From 427c6d048f1c85c51979b3f224f2be1136a00021 Mon Sep 17 00:00:00 2001 From: "bjorn.winckler" Date: Tue, 9 Oct 2007 17:49:40 +0000 Subject: [PATCH] Merged Nico's fullscreen patch git-svn-id: http://macvim.googlecode.com/svn/trunk@304 96c4425d-ca35-0410-94e5-3396d5c13a8f --- MMBackend.h | 3 + MMBackend.m | 10 + MMFullscreenWindow.h | 32 ++ MMFullscreenWindow.m | 273 ++++++++++ MMVimController.m | 4 + MMWindowController.h => MMVimView.h | 63 ++- MMVimView.m | 747 +++++++++++++++++++++++++++ MMWindowController.h | 21 +- MMWindowController.m | 790 +++++------------------------ MacVim.h | 2 + MacVim.m | 2 + MacVim.xcodeproj/project.pbxproj | 20 + MacVim.xcodeproj/winckler.mode1 | 13 +- MacVim.xcodeproj/winckler.pbxuser | 4 +- PSMTabBarControl/source/PSMMetalTabStyle.m | 12 +- gui_macvim.m | 12 + 16 files changed, 1308 insertions(+), 700 deletions(-) create mode 100644 MMFullscreenWindow.h create mode 100644 MMFullscreenWindow.m copy MMWindowController.h => MMVimView.h (54%) create mode 100644 MMVimView.m diff --git a/MMBackend.h b/MMBackend.h index a7767aca..af48b5b6 100644 --- a/MMBackend.h +++ b/MMBackend.h @@ -116,6 +116,9 @@ - (int)lookupColorWithKey:(NSString *)key; - (BOOL)hasSpecialKeyWithValue:(NSString *)value; +- (void)enterFullscreen; +- (void)leaveFullscreen; + - (void)registerServerWithName:(NSString *)name; - (BOOL)sendToServer:(NSString *)name string:(NSString *)string reply:(char_u **)reply port:(int *)port expression:(BOOL)expr diff --git a/MMBackend.m b/MMBackend.m index 85fe1531..aa1a1ab9 100644 --- a/MMBackend.m +++ b/MMBackend.m @@ -993,6 +993,16 @@ enum { return NO; } +- (void)enterFullscreen +{ + [self queueMessage:EnterFullscreenMsgID data:nil]; +} + +- (void)leaveFullscreen +{ + [self queueMessage:LeaveFullscreenMsgID data:nil]; +} + - (oneway void)processInput:(int)msgid data:(in NSData *)data { // NOTE: This method might get called whenever the run loop is tended to. diff --git a/MMFullscreenWindow.h b/MMFullscreenWindow.h new file mode 100644 index 00000000..62c7da7d --- /dev/null +++ b/MMFullscreenWindow.h @@ -0,0 +1,32 @@ +/* vi:set ts=8 sts=4 sw=4 ft=objc: + * + * VIM - Vi IMproved by Bram Moolenaar + * MacVim GUI port by Bjorn Winckler + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#import + + + +@class MMVimView; + +@interface MMFullscreenWindow : NSWindow { + NSWindow* target; + MMVimView* view; + NSPoint oldPosition; + NSString *oldTabBarStyle; +} + +- (MMFullscreenWindow*)initWithWindow:(NSWindow*)t andView:(MMVimView*)v; + +- (void) enterFullscreen; +- (void) leaveFullscreen; + +- (BOOL)canBecomeKeyWindow; +- (BOOL)canBecomeMainWindow; + +@end diff --git a/MMFullscreenWindow.m b/MMFullscreenWindow.m new file mode 100644 index 00000000..1b0bf07a --- /dev/null +++ b/MMFullscreenWindow.m @@ -0,0 +1,273 @@ +/* vi:set ts=8 sts=4 sw=4 ft=objc: + * + * VIM - Vi IMproved by Bram Moolenaar + * MacVim GUI port by Bjorn Winckler + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#import "MMFullscreenWindow.h" +#import +#import "MMVimView.h" +#import "MMTextView.h" +#import "MMWindowController.h" +#import + + + +@implementation MMFullscreenWindow + +- (MMFullscreenWindow*)initWithWindow:(NSWindow*)t andView:(MMVimView*)v +{ + NSScreen* screen = [t screen]; + + // XXX: what if screen == nil? + + // you can't change the style of an existing window in cocoa. create a new + // window and move the MMTextView into it. + // (another way would be to make the existing window large enough that the + // title bar is off screen. but that doesn't work with multiple screens). + self = [super initWithContentRect:[screen frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES + screen:screen]; + + if(self == nil) + return nil; + + [self setHasShadow:NO]; + [self setShowsResizeIndicator:NO]; + [self setBackgroundColor:[NSColor blackColor]]; + [self setReleasedWhenClosed:NO]; + + target = t; [target retain]; + view = v; [view retain]; + + return self; +} + +- (void) dealloc +{ + [target release]; + [view release]; + + [super dealloc]; +} + +- (void) centerView +{ + NSRect outer = [self frame], inner = [view frame]; + //NSLog(@"%s %@%@", _cmd, NSStringFromRect(outer), NSStringFromRect(inner)); + + NSPoint origin = NSMakePoint((outer.size.width - inner.size.width)/2, + (outer.size.height - inner.size.height)/2); + [view setFrameOrigin:origin]; +} + +- (void) enterFullscreen +{ + // hide menu and dock, both appear on demand + SetSystemUIMode(kUIModeAllSuppressed, 0); //requires 10.3 + + // fade to black + Boolean didBlend = NO; + CGDisplayFadeReservationToken token; + if(CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) { + CGDisplayFade(token, .25, kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, .0, .0, .0, true); + didBlend = YES; + } + + // fool delegate + id delegate = [target delegate]; + [target setDelegate:nil]; + + // make target's window controller believe that it's now controlling us + [target retain]; // NSWindowController will release target once in the next + // line + [[target windowController] setWindow: self]; + + + oldTabBarStyle = [[view tabBarControl] styleName]; + [[view tabBarControl] setStyleNamed:@"Unified"]; + + // add text view + oldPosition = [view frame].origin; + + [[self contentView] addSubview:view]; + [self setInitialFirstResponder:[view textView]]; + + [self setTitle: [target title]]; + [self setOpaque:[target isOpaque]]; + + // make us visible and target invisible + [target orderOut:self]; + [self makeKeyAndOrderFront:self]; + + // don't set this sooner, so we don't get an additional focus gained message + [self setDelegate: delegate]; + + // update bottom right corner scrollbar (no resize handle in fu mode) + [[self windowController] placeViews]; + + // the call above moves the text view in the lower left corner, fix that + // XXX: still required? + [self centerView]; + [self display]; + + // fade back in + if(didBlend) { + CGDisplayFade(token, .25, kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, .0, .0, .0, false); + CGReleaseDisplayFadeReservation(token); + } +} + +- (void) leaveFullscreen +{ + // fade to black + Boolean didBlend = NO; + CGDisplayFadeReservationToken token; + if(CGAcquireDisplayFadeReservation(.5, &token) == kCGErrorSuccess) { + CGDisplayFade(token, .25, kCGDisplayBlendNormal, + kCGDisplayBlendSolidColor, .0, .0, .0, true); + didBlend = YES; + } + + // fix up target controller + [self retain]; // NSWindowController releases us once + [[self windowController] setWindow: target]; + + + [[view tabBarControl] setStyleNamed:oldTabBarStyle]; + + // fix delegate + id delegate = [self delegate]; + [self setDelegate:nil]; + + // move text view back to original window, hide fullscreen window, + // show original window + // do this _after_ resetting delegate and window controller, so the + // window controller doesn't get a focus lost message from the fullscreen + // window. + [[target contentView] addSubview: view]; + [view setFrameOrigin:oldPosition]; + [self close]; + [target makeKeyAndOrderFront:self]; + + // ...but we don't want a focus gained message either, so don't set this + // sooner + [target setDelegate: delegate]; + + + // update bottom right corner scrollbar (resize handle reappears) + // XXX: Doesn't work? + [[self windowController] placeViews]; + [view placeScrollbars]; + + + // fade back in + if(didBlend) { + CGDisplayFade(token, .25, kCGDisplayBlendSolidColor, + kCGDisplayBlendNormal, .0, .0, .0, false); + CGReleaseDisplayFadeReservation(token); + } + + // order menu and dock back in + SetSystemUIMode(kUIModeNormal, 0); +} + +// Title-less windows normally don't receive key presses, override this +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +// Title-less windows normally can't become main which means that another +// non-fullscreen window will have the "active" titlebar in expose. Bad, fix it. +- (BOOL)canBecomeMainWindow +{ + return YES; +} + + +#pragma mark Proxy/Decorator/whatever stuff + +// the window controller will send us messages that are meant for the original, +// non-fullscreen window. forward those, and interpret the messages that are +// interesting for us + +- (void)setTitle:(NSString*)title +{ + [target setTitle:title]; + [super setTitle:title]; +} + +// HACK: if the T flag in guioptions is changed in fu mode, the toolbar needs +// to be changed when nofu is set. MMWindowController gets the toolbar object, +// so we need to return a toolbar from this method, even if none is visible for +// the fullscreen window. Seems to work, though. +- (NSToolbar*)toolbar +{ + return [target toolbar]; +} + +- (void)setFrame:(NSRect)frame display:(BOOL)display +{ + // HACK: if the target window would resize, we have to call our own + // windowDidResize method so that placeViews in MMWindowController is called + if (!NSEqualRects(frame, [target frame])) + { + [target setFrame:frame display:NO]; + + // XXX: send this directly to MMVimView + if ([[self delegate] respondsToSelector:@selector(windowDidResize:)]) + [[self delegate] windowDidResize:nil]; + + [self centerView]; + [self display]; + } +} + +/*- (NSRect)frame +{ + return [target frame]; // really? needed by MMWindowController placeViews. + // but mucks up display +}*/ + +- (NSRect)contentRectForFrameRect:(NSRect)rect +{ + //return [target contentRectForFrameRect:rect]; + + // EVIL HACK: this is always called with [[self window] frame] as argument + // from MMWindowController. We can't let frame return the frame of target, + // so "fix" this here. + return [target contentRectForFrameRect:[target frame]]; +} + +- (NSRect)frameRectForContentRect:(NSRect)contentRect +{ + return [target frameRectForContentRect:contentRect]; +} + +- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)screen +{ + return [target constrainFrameRect:frameRect toScreen:screen]; +} + +- (void)setContentResizeIncrements:(NSSize)size +{ + [target setContentResizeIncrements:size]; +} + +- (void)setOpaque:(BOOL)isOpaque +{ + // XXX: Do we want transparency even in fullscreen mode? + [super setOpaque:isOpaque]; + [target setOpaque:isOpaque]; +} + +@end diff --git a/MMVimController.m b/MMVimController.m index 50d93563..43793fa2 100644 --- a/MMVimController.m +++ b/MMVimController.m @@ -786,6 +786,10 @@ static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag) encoding:NSUTF8StringEncoding]; [self setServerName:name]; [name release]; + } else if (EnterFullscreenMsgID == msgid) { + [windowController enterFullscreen]; + } else if (LeaveFullscreenMsgID == msgid) { + [windowController leaveFullscreen]; } else { NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid); } diff --git a/MMWindowController.h b/MMVimView.h similarity index 54% copy from MMWindowController.h copy to MMVimView.h index b472f843..be006671 100644 --- a/MMWindowController.h +++ b/MMVimView.h @@ -15,54 +15,73 @@ @class PSMTabBarControl; @class MMTextView; @class MMTextStorage; +@class MMScroller; @class MMVimController; -@interface MMWindowController : NSWindowController { +@interface MMVimView : NSView { PSMTabBarControl *tabBarControl; NSTabView *tabView; - NSBox *tablineSeparator; MMVimController *vimController; BOOL vimTaskSelectedTab; MMTextView *textView; MMTextStorage *textStorage; NSMutableArray *scrollbars; - BOOL setupDone; + + // This is temporary to make the refactoring easier BOOL shouldUpdateWindowSize; - NSString *windowAutosaveKey; } -- (id)initWithVimController:(MMVimController *)controller; -- (MMVimController *)vimController; +- (MMVimView *)initWithFrame:(NSRect)frame vimController:(MMVimController *) c; + - (MMTextView *)textView; - (MMTextStorage *)textStorage; -- (NSString *)windowAutosaveKey; -- (void)setWindowAutosaveKey:(NSString *)key; +- (NSMutableArray *)scrollbars; +- (BOOL)inLiveResize; - (void)cleanup; -- (void)openWindow; + + +- (PSMTabBarControl *)tabBarControl; +- (NSTabView *)tabView; +- (IBAction)addNewTab:(id)sender; - (void)updateTabsWithData:(NSData *)data; - (void)selectTabWithIndex:(int)idx; -- (void)setTextDimensionsWithRows:(int)rows columns:(int)cols; +- (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi; +- (NSTabViewItem *)addNewTabViewItem; + +- (BOOL)bottomScrollbarVisible; +- (BOOL)leftScrollbarVisible; +- (BOOL)rightScrollbarVisible; +- (void)placeScrollbars; - (void)createScrollbarWithIdentifier:(long)ident type:(int)type; - (void)destroyScrollbarWithIdentifier:(long)ident; - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible; -- (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident; - (void)setScrollbarThumbValue:(float)val proportion:(float)prop identifier:(long)ident; +- (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx; + - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore; -- (void)setFont:(NSFont *)font; -- (void)processCommandQueueDidFinish; -- (void)popupMenu:(NSMenu *)menu atRow:(int)row column:(int)col; -- (void)showTabBar:(BOOL)on; -- (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode; -- (void)setMouseShape:(int)shape; -- (void)adjustLinespace:(int)linespace; -- (void)liveResizeDidEnd; -- (IBAction)addNewTab:(id)sender; -- (IBAction)toggleToolbar:(id)sender; +- (BOOL)shouldUpdateWindowSize; +- (NSRect)textViewRectForContentSize:(NSSize)contentSize; +- (void)setShouldUpdateWindowSize:(BOOL)b; + +- (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize; +- (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize; +@end + +// TODO: Move! +@interface MMScroller : NSScroller { + long identifier; + int type; + NSRange range; +} +- (id)initWithIdentifier:(long)ident type:(int)type; +- (long)identifier; +- (int)type; +- (NSRange)range; +- (void)setRange:(NSRange)newRange; @end -// vim: set ft=objc: diff --git a/MMVimView.m b/MMVimView.m new file mode 100644 index 00000000..f5e8f68b --- /dev/null +++ b/MMVimView.m @@ -0,0 +1,747 @@ +/* vi:set ts=8 sts=4 sw=4 ft=objc: + * + * VIM - Vi IMproved by Bram Moolenaar + * MacVim GUI port by Bjorn Winckler + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#import "MMVimView.h" + +#import +#import "MacVim.h" +#import "MMTextView.h" +#import "MMTextStorage.h" +#import "MMTypesetter.h" +#import "MMVimController.h" + +#import "MMWindowController.h" // needed by MMScroller. TODO: remove + +// Scroller type; these must match SBAR_* in gui.h +enum { + MMScrollerTypeLeft = 0, + MMScrollerTypeRight, + MMScrollerTypeBottom +}; + +// TODO: Move! +@interface NSTabView (MMExtras) +- (void)removeAllTabViewItems; +@end + + +@interface MMVimView (Private) +- (BOOL)bottomScrollbarVisible; +- (BOOL)leftScrollbarVisible; +- (BOOL)rightScrollbarVisible; +- (void)placeScrollbars; +@end + + +@implementation MMVimView + +- (MMVimView *)initWithFrame:(NSRect)frame + vimController:(MMVimController *)controller { + if (![super initWithFrame:frame]) + return nil; + + vimController = controller; + scrollbars = [[NSMutableArray alloc] init]; + + // Set up a complete text system. + textStorage = [[MMTextStorage alloc] init]; + NSLayoutManager *lm = [[NSLayoutManager alloc] init]; + NSTextContainer *tc = [[NSTextContainer alloc] initWithContainerSize: + NSMakeSize(1.0e7,1.0e7)]; + + NSString *typesetterString = [[NSUserDefaults standardUserDefaults] + stringForKey:MMTypesetterKey]; + if (![typesetterString isEqual:@"NSTypesetter"]) { + MMTypesetter *typesetter = [[MMTypesetter alloc] init]; + [lm setTypesetter:typesetter]; + [typesetter release]; + } else { + // Only MMTypesetter supports different cell width multipliers. + [[NSUserDefaults standardUserDefaults] + setFloat:1.0 forKey:MMCellWidthMultiplierKey]; + } + + [tc setWidthTracksTextView:NO]; + [tc setHeightTracksTextView:NO]; + [tc setLineFragmentPadding:0]; + + [textStorage addLayoutManager:lm]; + [lm addTextContainer:tc]; + + textView = [[MMTextView alloc] initWithFrame:frame + textContainer:tc]; + + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + int left = [ud integerForKey:MMTextInsetLeftKey]; + int top = [ud integerForKey:MMTextInsetTopKey]; + [textView setTextContainerInset:NSMakeSize(left, top)]; + + [self addSubview:textView]; + + // The text storage retains the layout manager which in turn retains + // the text container. + [tc release]; + [lm release]; + + // Create the tab view (which is never visible, but the tab bar control + // needs it to function). + tabView = [[NSTabView alloc] initWithFrame:NSZeroRect]; + + // Create the tab bar control (which is responsible for actually + // drawing the tabline and tabs). + NSRect tabFrame = frame; + tabFrame.origin.y = NSMaxY(tabFrame) - 22; + tabFrame.size.height = 22; + tabBarControl = [[PSMTabBarControl alloc] initWithFrame:tabFrame]; + + [tabView setDelegate:tabBarControl]; + + [tabBarControl setTabView:tabView]; + [tabBarControl setDelegate:self]; + [tabBarControl setHidden:YES]; + [tabBarControl setAutoresizingMask:NSViewWidthSizable|NSViewMinYMargin]; + [tabBarControl setCellMinWidth:[ud integerForKey:MMTabMinWidthKey]]; + [tabBarControl setCellMaxWidth:[ud integerForKey:MMTabMaxWidthKey]]; + [tabBarControl setCellOptimumWidth: + [ud integerForKey:MMTabOptimumWidthKey]]; + [tabBarControl setShowAddTabButton:YES]; + [[tabBarControl addTabButton] setTarget:self]; + [[tabBarControl addTabButton] setAction:@selector(addNewTab:)]; + [tabBarControl setAllowsDragBetweenWindows:NO]; + + [tabBarControl setPartnerView:[self textView]]; + + [self addSubview:tabBarControl]; + + return self; +} + +- (void)dealloc +{ + [tabBarControl release]; tabBarControl = nil; + [tabView release]; tabView = nil; + [scrollbars release]; scrollbars = nil; + [textView release]; textView = nil; + [textStorage release]; textStorage = nil; + + [super dealloc]; +} + +- (MMTextView *)textView +{ + return textView; +} + +- (MMTextStorage *)textStorage +{ + return textStorage; +} + +- (NSMutableArray *)scrollbars +{ + return scrollbars; +} + +- (BOOL)inLiveResize +{ + return [textView inLiveResize]; +} + +- (PSMTabBarControl *)tabBarControl +{ + return tabBarControl; +} + +- (NSTabView *)tabView +{ + return tabView; +} + + +- (void)cleanup +{ + vimController = nil; + + // NOTE! There is a bug in PSMTabBarControl in that it retains the delegate + // (which is the MMWindowController) so reset the delegate here, otherwise + // the MMWindowController never gets released resulting in a pretty serious + // memory leak. + [tabView setDelegate:nil]; + [tabBarControl setDelegate:nil]; + [tabBarControl setTabView:nil]; + [[self window] setDelegate:nil]; + + // NOTE! There is another bug in PSMTabBarControl where the control is not + // removed as an observer, so remove it here (else lots of evil nasty bugs + // will come and gnaw at your feet while you are sleeping). + [[NSNotificationCenter defaultCenter] removeObserver:tabBarControl]; + + [tabBarControl removeFromSuperviewWithoutNeedingDisplay]; + [textView removeFromSuperviewWithoutNeedingDisplay]; + + unsigned i, count = [scrollbars count]; + for (i = 0; i < count; ++i) { + MMScroller *sb = [scrollbars objectAtIndex:i]; + [sb removeFromSuperviewWithoutNeedingDisplay]; + } + + [tabView removeAllTabViewItems]; +} + +- (IBAction)addNewTab:(id)sender +{ + // NOTE! This can get called a lot if the user holds down the key + // equivalent for this action, which causes the ports to fill up. If we + // wait for the message to be sent then the app might become unresponsive. + [vimController sendMessage:AddNewTabMsgID data:nil]; +} + +- (void)updateTabsWithData:(NSData *)data +{ + const void *p = [data bytes]; + const void *end = p + [data length]; + int tabIdx = 0; + + // HACK! Current tab is first in the message. This way it is not + // necessary to guess which tab should be the selected one (this can be + // problematic for instance when new tabs are created). + int curtabIdx = *((int*)p); p += sizeof(int); + + NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems]; + + while (p < end) { + //int wincount = *((int*)p); p += sizeof(int); + int length = *((int*)p); p += sizeof(int); + + NSString *label = [[NSString alloc] + initWithBytesNoCopy:(void*)p + length:length + encoding:NSUTF8StringEncoding + freeWhenDone:NO]; + p += length; + + // Set the label of the tab; add a new tab when needed. + NSTabViewItem *tvi = [[self tabView] numberOfTabViewItems] <= tabIdx + ? [self addNewTabViewItem] + : [tabViewItems objectAtIndex:tabIdx]; + + [tvi setLabel:label]; + + [label release]; + + ++tabIdx; + } + + // Remove unused tabs from the NSTabView. Note that when a tab is closed + // the NSTabView will automatically select another tab, but we want Vim to + // take care of which tab to select so set the vimTaskSelectedTab flag to + // prevent the tab selection message to be passed on to the VimTask. + vimTaskSelectedTab = YES; + int i, count = [[self tabView] numberOfTabViewItems]; + for (i = count-1; i >= tabIdx; --i) { + id tvi = [tabViewItems objectAtIndex:i]; + //NSLog(@"Removing tab with index %d", i); + [[self tabView] removeTabViewItem:tvi]; + } + vimTaskSelectedTab = NO; + + [self selectTabWithIndex:curtabIdx]; +} + +- (void)selectTabWithIndex:(int)idx +{ + //NSLog(@"%s%d", _cmd, idx); + + NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems]; + if (idx < 0 || idx >= [tabViewItems count]) { + NSLog(@"WARNING: No tab with index %d exists.", idx); + return; + } + + // Do not try to select a tab if already selected. + NSTabViewItem *tvi = [tabViewItems objectAtIndex:idx]; + if (tvi != [[self tabView] selectedTabViewItem]) { + vimTaskSelectedTab = YES; + [[self tabView] selectTabViewItem:tvi]; + vimTaskSelectedTab = NO; + } +} + +- (NSTabViewItem *)addNewTabViewItem +{ + // NOTE! A newly created tab is not by selected by default; the VimTask + // decides which tab should be selected at all times. However, the AppKit + // will automatically select the first tab added to a tab view. + + NSTabViewItem *tvi = [[NSTabViewItem alloc] initWithIdentifier:nil]; + + // NOTE: If this is the first tab it will be automatically selected. + vimTaskSelectedTab = YES; + [[self tabView] addTabViewItem:tvi]; + vimTaskSelectedTab = NO; + + [tvi release]; + + return tvi; +} + +- (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi +{ + NSArray *tabViewItems = [[self tabBarControl] representedTabViewItems]; + return [tabViewItems indexOfObject:tvi]; +} + +- (BOOL)bottomScrollbarVisible +{ + unsigned i, count = [scrollbars count]; + for (i = 0; i < count; ++i) { + MMScroller *scroller = [scrollbars objectAtIndex:i]; + if ([scroller type] == MMScrollerTypeBottom && ![scroller isHidden]) + return YES; + } + + return NO; +} + +- (BOOL)leftScrollbarVisible +{ + unsigned i, count = [scrollbars count]; + for (i = 0; i < count; ++i) { + MMScroller *scroller = [scrollbars objectAtIndex:i]; + if ([scroller type] == MMScrollerTypeLeft && ![scroller isHidden]) + return YES; + } + + return NO; +} + +- (BOOL)rightScrollbarVisible +{ + unsigned i, count = [scrollbars count]; + for (i = 0; i < count; ++i) { + MMScroller *scroller = [scrollbars objectAtIndex:i]; + if ([scroller type] == MMScrollerTypeRight && ![scroller isHidden]) + return YES; + } + + return NO; +} + +- (void)createScrollbarWithIdentifier:(long)ident type:(int)type +{ + //NSLog(@"Create scroller %d of type %d", ident, type); + + MMScroller *scroller = [[MMScroller alloc] initWithIdentifier:ident + type:type]; + [scroller setTarget:self]; + [scroller setAction:@selector(scroll:)]; + + [self addSubview:scroller]; + [[self scrollbars] addObject:scroller]; + [scroller release]; +} + +- (void)destroyScrollbarWithIdentifier:(long)ident +{ + //NSLog(@"Destroy scroller %d", ident); + + unsigned idx = 0; + MMScroller *scroller = [self scrollbarForIdentifier:ident index:&idx]; + if (scroller) { + [scroller removeFromSuperview]; + [[self scrollbars] removeObjectAtIndex:idx]; + + if (![scroller isHidden]) { + // A visible scroller was removed, so the window must resize to + // fit. + //NSLog(@"Visible scroller %d was destroyed, resizing window.", + // ident); + shouldUpdateWindowSize = YES; + } + } +} + +- (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible +{ + MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL]; + if (!scroller) return; + + BOOL wasVisible = ![scroller isHidden]; + //NSLog(@"%s scroller %d (was %svisible)", visible ? "Show" : "Hide", + // ident, wasVisible ? "" : "in"); + [scroller setHidden:!visible]; + + if (wasVisible != visible) { + // A scroller was hidden or shown, so the window must resize to fit. + //NSLog(@"%s scroller %d and resize.", visible ? "Show" : "Hide", + // ident); + shouldUpdateWindowSize = YES; + } +} + +- (void)setScrollbarThumbValue:(float)val proportion:(float)prop + identifier:(long)ident +{ + MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL]; + //NSLog(@"Set thumb value %.2f proportion %.2f for scroller %d", + // val, prop, ident); + [scroller setFloatValue:val knobProportion:prop]; +} + + +- (void)scroll:(id)sender +{ + NSMutableData *data = [NSMutableData data]; + long ident = [(MMScroller*)sender identifier]; + int hitPart = [sender hitPart]; + float value = [sender floatValue]; + + [data appendBytes:&ident length:sizeof(long)]; + [data appendBytes:&hitPart length:sizeof(int)]; + [data appendBytes:&value length:sizeof(float)]; + + [vimController sendMessage:ScrollbarEventMsgID data:data]; +} + +- (void)placeScrollbars +{ + NSRect textViewFrame = [textView frame]; + BOOL lsbVisible = [self leftScrollbarVisible]; + + // HACK! Find the lowest left&right vertical scrollbars, as well as the + // rightmost horizontal scrollbar. This hack continues further down. + // + // TODO! Can there be no more than one horizontal scrollbar? If so, the + // code can be simplified. + unsigned lowestLeftSbIdx = (unsigned)-1; + unsigned lowestRightSbIdx = (unsigned)-1; + unsigned rightmostSbIdx = (unsigned)-1; + unsigned rowMaxLeft = 0, rowMaxRight = 0, colMax = 0; + unsigned i, count = [scrollbars count]; + for (i = 0; i < count; ++i) { + MMScroller *scroller = [scrollbars objectAtIndex:i]; + if (![scroller isHidden]) { + NSRange range = [scroller range]; + if ([scroller type] == MMScrollerTypeLeft + && range.location >= rowMaxLeft) { + rowMaxLeft = range.location; + lowestLeftSbIdx = i; + } else if ([scroller type] == MMScrollerTypeRight + && range.location >= rowMaxRight) { + rowMaxRight = range.location; + lowestRightSbIdx = i; + } else if ([scroller type] == MMScrollerTypeBottom + && range.location >= colMax) { + colMax = range.location; + rightmostSbIdx = i; + } + } + } + + // Place the scrollbars. + for (i = 0; i < count; ++i) { + MMScroller *scroller = [scrollbars objectAtIndex:i]; + if ([scroller isHidden]) + continue; + + NSRect rect; + if ([scroller type] == MMScrollerTypeBottom) { + rect = [textStorage rectForColumnsInRange:[scroller range]]; + rect.size.height = [NSScroller scrollerWidth]; + if (lsbVisible) + rect.origin.x += [NSScroller scrollerWidth]; + + // HACK! Make sure the rightmost horizontal scrollbar covers the + // text view all the way to the right, otherwise it looks ugly when + // the user drags the window to resize. + if (i == rightmostSbIdx) { + float w = NSMaxX(textViewFrame) - NSMaxX(rect); + if (w > 0) + rect.size.width += w; + } + + // Make sure scrollbar rect is bounded by the text view frame. + if (rect.origin.x < textViewFrame.origin.x) + rect.origin.x = textViewFrame.origin.x; + else if (rect.origin.x > NSMaxX(textViewFrame)) + rect.origin.x = NSMaxX(textViewFrame); + if (NSMaxX(rect) > NSMaxX(textViewFrame)) + rect.size.width -= NSMaxX(rect) - NSMaxX(textViewFrame); + if (rect.size.width < 0) + rect.size.width = 0; + } else { + rect = [textStorage rectForRowsInRange:[scroller range]]; + // Adjust for the fact that text layout is flipped. + rect.origin.y = NSMaxY(textViewFrame) - rect.origin.y + - rect.size.height; + rect.size.width = [NSScroller scrollerWidth]; + if ([scroller type] == MMScrollerTypeRight) + rect.origin.x = NSMaxX(textViewFrame); + + // HACK! Make sure the lowest vertical scrollbar covers the text + // view all the way to the bottom. This is done because Vim only + // makes the scrollbar cover the (vim-)window it is associated with + // and this means there is always an empty gap in the scrollbar + // region next to the command line. + // TODO! Find a nicer way to do this. + if (i == lowestLeftSbIdx || i == lowestRightSbIdx) { + float h = rect.origin.y + rect.size.height + - textViewFrame.origin.y; + if (rect.size.height < h) { + rect.origin.y = textViewFrame.origin.y; + rect.size.height = h; + } + } + + // Vertical scrollers must not cover the resize box in the + // bottom-right corner of the window. + if ([[self window] showsResizeIndicator] // XXX: make this a flag + && rect.origin.y < [NSScroller scrollerWidth]) { + rect.size.height -= [NSScroller scrollerWidth] - rect.origin.y; + rect.origin.y = [NSScroller scrollerWidth]; + } + + // Make sure scrollbar rect is bounded by the text view frame. + if (rect.origin.y < textViewFrame.origin.y) { + rect.size.height -= textViewFrame.origin.y - rect.origin.y; + rect.origin.y = textViewFrame.origin.y; + } else if (rect.origin.y > NSMaxY(textViewFrame)) + rect.origin.y = NSMaxY(textViewFrame); + if (NSMaxY(rect) > NSMaxY(textViewFrame)) + rect.size.height -= NSMaxY(rect) - NSMaxY(textViewFrame); + if (rect.size.height < 0) + rect.size.height = 0; + } + + //NSLog(@"set scroller #%d frame = %@", i, NSStringFromRect(rect)); + NSRect oldRect = [scroller frame]; + if (!NSEqualRects(oldRect, rect)) { + [scroller setFrame:rect]; + // Clear behind the old scroller frame, or parts of the old + // scroller might still be visible after setFrame:. + [[[self window] contentView] setNeedsDisplayInRect:oldRect]; + [scroller setNeedsDisplay:YES]; + } + } +} + +- (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx +{ + unsigned i, count = [[self scrollbars] count]; + for (i = 0; i < count; ++i) { + MMScroller *scroller = [[self scrollbars] objectAtIndex:i]; + if ([scroller identifier] == ident) { + if (idx) *idx = i; + return scroller; + } + } + + return nil; +} + +- (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore +{ + [textStorage setDefaultColorsBackground:back foreground:fore]; + [textView setBackgroundColor:back]; +} + +- (BOOL)shouldUpdateWindowSize +{ + return shouldUpdateWindowSize; +} + +- (void)setShouldUpdateWindowSize:(BOOL)b +{ + shouldUpdateWindowSize = b; +} + +- (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize +{ + NSSize size = textViewSize; + + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + int right = [ud integerForKey:MMTextInsetRightKey]; + int bot = [ud integerForKey:MMTextInsetBottomKey]; + + size.width += [[self textView] textContainerOrigin].x + right; + size.height += [[self textView] textContainerOrigin].y + bot; + + if (![[self tabBarControl] isHidden]) + size.height += [[self tabBarControl] frame].size.height; + + if ([self bottomScrollbarVisible]) + size.height += [NSScroller scrollerWidth]; + if ([self leftScrollbarVisible]) + size.width += [NSScroller scrollerWidth]; + if ([self rightScrollbarVisible]) + size.width += [NSScroller scrollerWidth]; + + return size; +} + +- (NSRect)textViewRectForContentSize:(NSSize)contentSize +{ + NSRect rect = { 0, 0, contentSize.width, contentSize.height }; + + if (![[self tabBarControl] isHidden]) + rect.size.height -= [[self tabBarControl] frame].size.height; + + if ([self bottomScrollbarVisible]) { + rect.size.height -= [NSScroller scrollerWidth]; + rect.origin.y += [NSScroller scrollerWidth]; + } + if ([self leftScrollbarVisible]) { + rect.size.width -= [NSScroller scrollerWidth]; + rect.origin.x += [NSScroller scrollerWidth]; + } + if ([self rightScrollbarVisible]) + rect.size.width -= [NSScroller scrollerWidth]; + + return rect; +} + +- (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize +{ + NSSize size = textViewSize; + + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + int right = [ud integerForKey:MMTextInsetRightKey]; + int bot = [ud integerForKey:MMTextInsetBottomKey]; + + size.width -= [[self textView] textContainerOrigin].x + right; + size.height -= [[self textView] textContainerOrigin].y + bot; + + return size; +} + + +// -- PSMTabBarControl delegate ---------------------------------------------- + + +- (BOOL)tabView:(NSTabView *)theTabView shouldSelectTabViewItem: + (NSTabViewItem *)tabViewItem +{ + // NOTE: It would be reasonable to think that 'shouldSelect...' implies + // that this message only gets sent when the user clicks the tab. + // Unfortunately it is not so, which is why we need the + // 'vimTaskSelectedTab' flag. + // + // HACK! The selection message should not be propagated to the VimTask if + // the VimTask selected the tab (e.g. as opposed the user clicking the + // tab). The delegate method has no way of knowing who initiated the + // selection so a flag is set when the VimTask initiated the selection. + if (!vimTaskSelectedTab) { + // Propagate the selection message to the VimTask. + int idx = [self representedIndexOfTabViewItem:tabViewItem]; + if (NSNotFound != idx) { + NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)]; + [vimController sendMessage:SelectTabMsgID data:data]; + } + } + + // Unless Vim selected the tab, return NO, and let Vim decide if the tab + // should get selected or not. + return vimTaskSelectedTab; +} + +- (BOOL)tabView:(NSTabView *)theTabView shouldCloseTabViewItem: + (NSTabViewItem *)tabViewItem +{ + // HACK! This method is only called when the user clicks the close button + // on the tab. Instead of letting the tab bar close the tab, we return NO + // and pass a message on to Vim to let it handle the closing. + int idx = [self representedIndexOfTabViewItem:tabViewItem]; + //NSLog(@"Closing tab with index %d", idx); + NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)]; + [vimController sendMessage:CloseTabMsgID data:data]; + + return NO; +} + +- (void)tabView:(NSTabView *)theTabView didDragTabViewItem: + (NSTabViewItem *)tabViewItem toIndex:(int)idx +{ + NSMutableData *data = [NSMutableData data]; + [data appendBytes:&idx length:sizeof(int)]; + + [vimController sendMessage:DraggedTabMsgID data:data]; +} + +@end + + + + +@implementation NSTabView (MMExtras) + +- (void)removeAllTabViewItems +{ + NSArray *existingItems = [self tabViewItems]; + NSEnumerator *e = [existingItems objectEnumerator]; + NSTabViewItem *item; + while (item = [e nextObject]){ + [self removeTabViewItem:item]; + } +} + +@end // NSTabView (MMExtras) + + + + +@implementation MMScroller + +- (id)initWithIdentifier:(long)ident type:(int)theType +{ + // HACK! NSScroller creates a horizontal scroller if it is init'ed with a + // frame whose with exceeds its height; so create a bogus rect and pass it + // to initWithFrame. + NSRect frame = theType == MMScrollerTypeBottom + ? NSMakeRect(0, 0, 1, 0) + : NSMakeRect(0, 0, 0, 1); + + if ((self = [super initWithFrame:frame])) { + identifier = ident; + type = theType; + [self setHidden:YES]; + [self setEnabled:YES]; + } + + return self; +} + +- (long)identifier +{ + return identifier; +} + +- (int)type +{ + return type; +} + +- (NSRange)range +{ + return range; +} + +- (void)setRange:(NSRange)newRange +{ + range = newRange; +} + +- (void)scrollWheel:(NSEvent *)event +{ + // HACK! Pass message on to the text view. + MMWindowController *wc = [[self window] windowController]; + [[wc textView] scrollWheel:event]; +} + +@end // MMScroller diff --git a/MMWindowController.h b/MMWindowController.h index b472f843..9977bbe1 100644 --- a/MMWindowController.h +++ b/MMWindowController.h @@ -12,31 +12,28 @@ -@class PSMTabBarControl; -@class MMTextView; -@class MMTextStorage; +@class MMFullscreenWindow; @class MMVimController; - +@class MMTextStorage; +@class MMTextView; +@class MMVimView; @interface MMWindowController : NSWindowController { - PSMTabBarControl *tabBarControl; - NSTabView *tabView; NSBox *tablineSeparator; MMVimController *vimController; - BOOL vimTaskSelectedTab; - MMTextView *textView; - MMTextStorage *textStorage; - NSMutableArray *scrollbars; + MMVimView *vimView; BOOL setupDone; BOOL shouldUpdateWindowSize; NSString *windowAutosaveKey; + MMFullscreenWindow *fullscreenWindow; } - (id)initWithVimController:(MMVimController *)controller; - (MMVimController *)vimController; - (MMTextView *)textView; - (MMTextStorage *)textStorage; +- (MMVimView *)vimView; - (NSString *)windowAutosaveKey; - (void)setWindowAutosaveKey:(NSString *)key; - (void)cleanup; @@ -59,6 +56,10 @@ - (void)setMouseShape:(int)shape; - (void)adjustLinespace:(int)linespace; - (void)liveResizeDidEnd; +- (void)placeViews; + +- (void)enterFullscreen; +- (void)leaveFullscreen; - (IBAction)addNewTab:(id)sender; - (IBAction)toggleToolbar:(id)sender; diff --git a/MMWindowController.m b/MMWindowController.m index edac1ac2..8eb15455 100644 --- a/MMWindowController.m +++ b/MMWindowController.m @@ -16,35 +16,10 @@ #import "MacVim.h" #import "MMAppController.h" #import "MMTypesetter.h" +#import "MMFullscreenWindow.h" +#import "MMVimView.h" -// Scroller type; these must match SBAR_* in gui.h -enum { - MMScrollerTypeLeft = 0, - MMScrollerTypeRight, - MMScrollerTypeBottom -}; - - - -// TODO: Move! -@interface NSTabView (MMExtras) -- (void)removeAllTabViewItems; -@end - - -// TODO: Move! -@interface MMScroller : NSScroller { - long identifier; - int type; - NSRange range; -} -- (id)initWithIdentifier:(long)ident type:(int)type; -- (long)identifier; -- (int)type; -- (NSRange)range; -- (void)setRange:(NSRange)newRange; -@end @interface MMWindowController (Private) - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize; @@ -57,13 +32,8 @@ enum { - (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi; - (IBAction)vimMenuItemAction:(id)sender; - (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx; -- (BOOL)bottomScrollbarVisible; -- (BOOL)leftScrollbarVisible; -- (BOOL)rightScrollbarVisible; -- (void)placeScrollbars; -- (void)scroll:(id)sender; -- (void)placeViews; - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb; +- (void)checkWindowNeedsResizing; @end @@ -102,53 +72,20 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (id)initWithVimController:(MMVimController *)controller { if ((self = [super initWithWindowNibName:@"EmptyWindow"])) { + fullscreenWindow = nil; vimController = controller; - scrollbars = [[NSMutableArray alloc] init]; // Window cascading is handled by MMAppController. [self setShouldCascadeWindows:NO]; - // Setup a complete text system. - textStorage = [[MMTextStorage alloc] init]; - NSLayoutManager *lm = [[NSLayoutManager alloc] init]; - NSTextContainer *tc = [[NSTextContainer alloc] initWithContainerSize: - NSMakeSize(1.0e7,1.0e7)]; - - NSString *typesetterString = [[NSUserDefaults standardUserDefaults] - stringForKey:MMTypesetterKey]; - if (![typesetterString isEqual:@"NSTypesetter"]) { - MMTypesetter *typesetter = [[MMTypesetter alloc] init]; - [lm setTypesetter:typesetter]; - [typesetter release]; - } else { - // Only MMTypesetter supports different cell width multipliers. - [[NSUserDefaults standardUserDefaults] - setFloat:1.0 forKey:MMCellWidthMultiplierKey]; - } - - [tc setWidthTracksTextView:NO]; - [tc setHeightTracksTextView:NO]; - [tc setLineFragmentPadding:0]; - - [textStorage addLayoutManager:lm]; - [lm addTextContainer:tc]; - NSWindow *win = [self window]; NSView *contentView = [win contentView]; - textView = [[MMTextView alloc] initWithFrame:[contentView frame] - textContainer:tc]; - - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - int left = [ud integerForKey:MMTextInsetLeftKey]; - int top = [ud integerForKey:MMTextInsetTopKey]; - [textView setTextContainerInset:NSMakeSize(left, top)]; - - [contentView addSubview:textView]; - - // The text storage retains the layout manager which in turn retains - // the text container. - [tc release]; - [lm release]; + vimView = [[MMVimView alloc] initWithFrame:[contentView frame] + vimController:vimController]; + [contentView addSubview:vimView]; + //[vimView translateOriginToPoint: + // NSMakePoint([contentView frame].size.width, 0)]; + //[vimView rotateByAngle:45.f]; // Create the tabline separator (which may be visible when the tabline // is hidden). @@ -156,44 +93,17 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) tabSepRect.origin.y = NSMaxY(tabSepRect)-1; tabSepRect.size.height = 1; tablineSeparator = [[NSBox alloc] initWithFrame:tabSepRect]; - - // Create the tab view (which is never visible, but the tab bar control - // needs it to function). - tabView = [[NSTabView alloc] initWithFrame:NSZeroRect]; - - // Create the tab bar control (which is responsible for actually - // drawing the tabline and tabs). - NSRect tabFrame = [contentView frame]; - tabFrame.origin.y = NSMaxY(tabFrame) - 22; - tabFrame.size.height = 22; - tabBarControl = [[PSMTabBarControl alloc] initWithFrame:tabFrame]; - - [tabView setDelegate:tabBarControl]; - - [tabBarControl setTabView:tabView]; - [tabBarControl setDelegate:self]; - [tabBarControl setHidden:YES]; - [tabBarControl setAutoresizingMask:NSViewWidthSizable|NSViewMinYMargin]; - [tabBarControl setCellMinWidth:[ud integerForKey:MMTabMinWidthKey]]; - [tabBarControl setCellMaxWidth:[ud integerForKey:MMTabMaxWidthKey]]; - [tabBarControl setCellOptimumWidth: - [ud integerForKey:MMTabOptimumWidthKey]]; - [tabBarControl setShowAddTabButton:YES]; - [[tabBarControl addTabButton] setTarget:self]; - [[tabBarControl addTabButton] setAction:@selector(addNewTab:)]; - [tabBarControl setAllowsDragBetweenWindows:NO]; - + [tablineSeparator setBoxType:NSBoxSeparator]; [tablineSeparator setHidden:NO]; [tablineSeparator setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; [contentView setAutoresizesSubviews:YES]; - [contentView addSubview:tabBarControl]; [contentView addSubview:tablineSeparator]; [win setDelegate:self]; - [win setInitialFirstResponder:textView]; + [win setInitialFirstResponder:[vimView textView]]; // Make us safe on pre-tiger OSX if ([win respondsToSelector:@selector(_setContentHasShadow:)]) @@ -207,13 +117,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { //NSLog(@"%@ %s", [self className], _cmd); - [tabBarControl release]; tabBarControl = nil; - [tabView release]; tabView = nil; [tablineSeparator release]; tablineSeparator = nil; [windowAutosaveKey release]; windowAutosaveKey = nil; - [scrollbars release]; scrollbars = nil; - [textView release]; textView = nil; - [textStorage release]; textStorage = nil; [super dealloc]; } @@ -230,12 +135,17 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (MMTextView *)textView { - return textView; + return [vimView textView]; } - (MMTextStorage *)textStorage { - return textStorage; + return [vimView textStorage]; +} + +- (MMVimView *)vimView +{ + return vimView; } - (NSString *)windowAutosaveKey @@ -253,34 +163,20 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { //NSLog(@"%@ %s", [self className], _cmd); + if (fullscreenWindow != nil) { + // if we are closed while still in fullscreen, end fullscreen mode, + // release ourselves (because this won't happen in MMWindowController) + // and perform close operation on the original window + [self leaveFullscreen]; + } + + setupDone = NO; vimController = nil; - // NOTE! There is a bug in PSMTabBarControl in that it retains the delegate - // (which is the MMWindowController) so reset the delegate here, otherwise - // the MMWindowController never gets released resulting in a pretty serious - // memory leak. - [tabView setDelegate:nil]; - [tabBarControl setDelegate:nil]; - [tabBarControl setTabView:nil]; - [[self window] setDelegate:nil]; - - // NOTE! There is another bug in PSMTabBarControl where the control is not - // removed as an observer, so remove it here (else lots of evil nasty bugs - // will come and gnaw at your feet while you are sleeping). - [[NSNotificationCenter defaultCenter] removeObserver:tabBarControl]; - - [tabBarControl removeFromSuperviewWithoutNeedingDisplay]; [tablineSeparator removeFromSuperviewWithoutNeedingDisplay]; - [textView removeFromSuperviewWithoutNeedingDisplay]; - - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *sb = [scrollbars objectAtIndex:i]; - [sb removeFromSuperviewWithoutNeedingDisplay]; - } - - [tabView removeAllTabViewItems]; + [vimView removeFromSuperviewWithoutNeedingDisplay]; + [vimView cleanup]; // TODO: is this necessary? [[self window] orderOut:self]; } @@ -300,135 +196,39 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (void)updateTabsWithData:(NSData *)data { - const void *p = [data bytes]; - const void *end = p + [data length]; - int tabIdx = 0; - - // HACK! Current tab is first in the message. This way it is not - // necessary to guess which tab should be the selected one (this can be - // problematic for instance when new tabs are created). - int curtabIdx = *((int*)p); p += sizeof(int); - - NSArray *tabViewItems = [tabBarControl representedTabViewItems]; - - while (p < end) { - //int wincount = *((int*)p); p += sizeof(int); - int length = *((int*)p); p += sizeof(int); - - NSString *label = [[NSString alloc] - initWithBytesNoCopy:(void*)p - length:length - encoding:NSUTF8StringEncoding - freeWhenDone:NO]; - p += length; - - // Set the label of the tab; add a new tab when needed. - NSTabViewItem *tvi = [tabView numberOfTabViewItems] <= tabIdx - ? [self addNewTabViewItem] - : [tabViewItems objectAtIndex:tabIdx]; - - [tvi setLabel:label]; - - [label release]; - - ++tabIdx; - } - - // Remove unused tabs from the NSTabView. Note that when a tab is closed - // the NSTabView will automatically select another tab, but we want Vim to - // take care of which tab to select so set the vimTaskSelectedTab flag to - // prevent the tab selection message to be passed on to the VimTask. - vimTaskSelectedTab = YES; - int i, count = [tabView numberOfTabViewItems]; - for (i = count-1; i >= tabIdx; --i) { - id tvi = [tabViewItems objectAtIndex:i]; - //NSLog(@"Removing tab with index %d", i); - [tabView removeTabViewItem:tvi]; - } - vimTaskSelectedTab = NO; - - [self selectTabWithIndex:curtabIdx]; + [vimView updateTabsWithData:data]; } - (void)selectTabWithIndex:(int)idx { - //NSLog(@"%s%d", _cmd, idx); - - NSArray *tabViewItems = [tabBarControl representedTabViewItems]; - if (idx < 0 || idx >= [tabViewItems count]) { - NSLog(@"WARNING: No tab with index %d exists.", idx); - return; - } - - // Do not try to select a tab if already selected. - NSTabViewItem *tvi = [tabViewItems objectAtIndex:idx]; - if (tvi != [tabView selectedTabViewItem]) { - vimTaskSelectedTab = YES; - [tabView selectTabViewItem:tvi]; - vimTaskSelectedTab = NO; - } + [vimView selectTabWithIndex:idx]; } - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols { //NSLog(@"setTextDimensionsWithRows:%d columns:%d", rows, cols); - [textStorage setMaxRows:rows columns:cols]; + [[vimView textStorage] setMaxRows:rows columns:cols]; - if (setupDone && ![textView inLiveResize]) + if (setupDone && ![vimView inLiveResize]) shouldUpdateWindowSize = YES; } - (void)createScrollbarWithIdentifier:(long)ident type:(int)type { - //NSLog(@"Create scroller %d of type %d", ident, type); - - MMScroller *scroller = [[MMScroller alloc] initWithIdentifier:ident - type:type]; - [scroller setTarget:self]; - [scroller setAction:@selector(scroll:)]; - - [[[self window] contentView] addSubview:scroller]; - [scrollbars addObject:scroller]; - [scroller release]; + [vimView createScrollbarWithIdentifier:ident type:type]; } - (void)destroyScrollbarWithIdentifier:(long)ident { - //NSLog(@"Destroy scroller %d", ident); - - unsigned idx = 0; - MMScroller *scroller = [self scrollbarForIdentifier:ident index:&idx]; - if (scroller) { - [scroller removeFromSuperview]; - [scrollbars removeObjectAtIndex:idx]; - - if (![scroller isHidden]) { - // A visible scroller was removed, so the window must resize to - // fit. - //NSLog(@"Visible scroller %d was destroyed, resizing window.", - // ident); - shouldUpdateWindowSize = YES; - } - } + [vimView destroyScrollbarWithIdentifier:ident]; + [self checkWindowNeedsResizing]; } - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible { - MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL]; - if (!scroller) return; - - BOOL wasVisible = ![scroller isHidden]; - //NSLog(@"%s scroller %d (was %svisible)", visible ? "Show" : "Hide", - // ident, wasVisible ? "" : "in"); - [scroller setHidden:!visible]; - - if (wasVisible != visible) { - // A scroller was hidden or shown, so the window must resize to fit. - //NSLog(@"%s scroller %d and resize.", visible ? "Show" : "Hide", - // ident); - shouldUpdateWindowSize = YES; - } + [vimView showScrollbarWithIdentifier:ident state:visible]; + [self checkWindowNeedsResizing]; } - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident @@ -440,17 +240,16 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) // NSStringFromRange(range), ident); [scroller setRange:range]; // TODO! Should only do this once per update. - [self placeScrollbars]; + + if (setupDone) // TODO: probably not necessary + [vimView placeScrollbars]; } } - (void)setScrollbarThumbValue:(float)val proportion:(float)prop identifier:(long)ident { - MMScroller *scroller = [self scrollbarForIdentifier:ident index:NULL]; - //NSLog(@"Set thumb value %.2f proportion %.2f for scroller %d", - // val, prop, ident); - [scroller setFloatValue:val knobProportion:prop]; + [vimView setScrollbarThumbValue:val proportion:prop identifier:ident]; } - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore @@ -460,14 +259,13 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) BOOL isOpaque = [back alphaComponent] == 1.0f; [[self window] setOpaque:isOpaque]; - [textStorage setDefaultColorsBackground:back foreground:fore]; - [textView setBackgroundColor:back]; + [vimView setDefaultColorsBackground:back foreground:fore]; } - (void)setFont:(NSFont *)font { [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO]; - [textStorage setFont:font]; + [[vimView textStorage] setFont:font]; [self updateResizeIncrements]; } @@ -475,6 +273,7 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { if (shouldUpdateWindowSize) { shouldUpdateWindowSize = NO; + [vimView setShouldUpdateWindowSize:NO]; [self resizeWindowToFit:self]; } } @@ -485,8 +284,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) NSEvent *event; if (row >= 0 && col >= 0) { - NSSize cellSize = [textStorage cellSize]; - NSPoint pt = { (col+1)*cellSize.width, [textView frame].size.height + NSSize cellSize = [[vimView textStorage] cellSize]; + NSPoint pt = { (col+1)*cellSize.width, [[vimView textView] frame].size.height - (row+1)*cellSize.height }; event = [NSEvent mouseEventWithType:NSRightMouseDown @@ -499,15 +298,15 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) clickCount:0 pressure:1.0]; } else { - event = [textView lastMouseDownEvent]; + event = [[vimView textView] lastMouseDownEvent]; } - [NSMenu popUpContextMenu:menu withEvent:event forView:textView]; + [NSMenu popUpContextMenu:menu withEvent:event forView:[vimView textView]]; } - (void)showTabBar:(BOOL)on { - [tabBarControl setHidden:!on]; + [[vimView tabBarControl] setHidden:!on]; if (!on) { NSToolbar *toolbar = [[self window] toolbar]; @@ -516,8 +315,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) [tablineSeparator setHidden:on]; } - if (setupDone) - shouldUpdateWindowSize = YES; + //if (setupDone) + // shouldUpdateWindowSize = YES; } - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode @@ -532,7 +331,7 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) if (!on) { [tablineSeparator setHidden:YES]; } else { - [tablineSeparator setHidden:![tabBarControl isHidden]]; + [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]]; } } @@ -559,8 +358,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (void)adjustLinespace:(int)linespace { - if (textStorage) { - [textStorage setLinespace:(float)linespace]; + if (vimView && [vimView textStorage]) { + [[vimView textStorage] setLinespace:(float)linespace]; shouldUpdateWindowSize = YES; } } @@ -581,8 +380,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) NSSize tsSize = [self textStorageSizeForTextViewSize:textViewRect.size]; int dim[2], rows, cols; - [textStorage getMaxRows:&rows columns:&cols]; - [textStorage fitToSize:tsSize rows:&dim[0] columns:&dim[1]]; + [[vimView textStorage] getMaxRows:&rows columns:&cols]; + [[vimView textStorage] fitToSize:tsSize rows:&dim[0] columns:&dim[1]]; if (dim[0] != rows || dim[1] != cols) { NSData *data = [NSData dataWithBytes:dim length:2*sizeof(int)]; @@ -598,9 +397,9 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) timeout:.5]; } - [textView setFrame:textViewRect]; + [[vimView textView] setFrame:textViewRect]; - [self placeScrollbars]; + [vimView placeScrollbars]; if (resizeFailed) { // Force the window size to match the text view size otherwise Vim and @@ -609,73 +408,77 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) } } -- (IBAction)addNewTab:(id)sender +- (void)placeViews { - // NOTE! This can get called a lot if the user holds down the key - // equivalent for this action, which causes the ports to fill up. If we - // wait for the message to be sent then the app might become unresponsive. - [vimController sendMessage:AddNewTabMsgID data:nil]; -} + if (!setupDone) return; -- (IBAction)toggleToolbar:(id)sender -{ - [vimController sendMessage:ToggleToolbarMsgID data:nil]; -} + // NOTE! It is assumed that the window has been resized so that it will + // exactly fit the text storage (possibly after resizing it). If this is + // not the case the display might be messed up. + NSWindow *win = [self window]; + NSRect contentRect = [win contentRectForFrameRect:[win frame]]; + NSRect textViewRect = [self textViewRectForContentSize:contentRect.size]; + NSSize tsSize = [self textStorageSizeForTextViewSize:textViewRect.size]; + int dim[2], rows, cols; + [[vimView textStorage] getMaxRows:&rows columns:&cols]; + [[vimView textStorage] fitToSize:tsSize rows:&dim[0] columns:&dim[1]]; + if (dim[0] != rows || dim[1] != cols) { + //NSLog(@"Notify Vim that text storage dimensions changed to %dx%d", + // dim[0], dim[1]); + NSData *data = [NSData dataWithBytes:dim length:2*sizeof(int)]; -// -- PSMTabBarControl delegate ---------------------------------------------- + [vimController sendMessage:SetTextDimensionsMsgID data:data]; + } + + // XXX: put vimView resizing logic in vimView + [[vimView textView] setFrame:textViewRect]; + + NSRect vimViewRect = textViewRect; + vimViewRect.origin = NSMakePoint(0, 0); + if (![[vimView tabBarControl] isHidden]) + vimViewRect.size.height += [[vimView tabBarControl] frame].size.height; + if ([vimView bottomScrollbarVisible]) + vimViewRect.size.height += [NSScroller scrollerWidth]; + if ([vimView leftScrollbarVisible]) + vimViewRect.size.width += [NSScroller scrollerWidth]; + if ([vimView rightScrollbarVisible]) + vimViewRect.size.width += [NSScroller scrollerWidth]; -- (BOOL)tabView:(NSTabView *)theTabView shouldSelectTabViewItem: - (NSTabViewItem *)tabViewItem -{ - // NOTE: It would be reasonable to think that 'shouldSelect...' implies - // that this message only gets sent when the user clicks the tab. - // Unfortunately it is not so, which is why we need the - // 'vimTaskSelectedTab' flag. - // - // HACK! The selection message should not be propagated to the VimTask if - // the VimTask selected the tab (e.g. as opposed the user clicking the - // tab). The delegate method has no way of knowing who initiated the - // selection so a flag is set when the VimTask initiated the selection. - if (!vimTaskSelectedTab) { - // Propagate the selection message to the VimTask. - int idx = [self representedIndexOfTabViewItem:tabViewItem]; - if (NSNotFound != idx) { - NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)]; - [vimController sendMessage:SelectTabMsgID data:data]; - } - } - // Unless Vim selected the tab, return NO, and let Vim decide if the tab - // should get selected or not. - return vimTaskSelectedTab; + [vimView setFrame:vimViewRect]; + + [vimView placeScrollbars]; } -- (BOOL)tabView:(NSTabView *)theTabView shouldCloseTabViewItem: - (NSTabViewItem *)tabViewItem +- (void)enterFullscreen { - // HACK! This method is only called when the user clicks the close button - // on the tab. Instead of letting the tab bar close the tab, we return NO - // and pass a message on to Vim to let it handle the closing. - int idx = [self representedIndexOfTabViewItem:tabViewItem]; - //NSLog(@"Closing tab with index %d", idx); - NSData *data = [NSData dataWithBytes:&idx length:sizeof(int)]; - [vimController sendMessage:CloseTabMsgID data:data]; - - return NO; + fullscreenWindow = [[MMFullscreenWindow alloc] initWithWindow:[self window] + andView:vimView]; + [fullscreenWindow enterFullscreen]; + + [fullscreenWindow setDelegate:self]; } -- (void)tabView:(NSTabView *)theTabView didDragTabViewItem: - (NSTabViewItem *)tabViewItem toIndex:(int)idx +- (void)leaveFullscreen { - NSMutableData *data = [NSMutableData data]; - [data appendBytes:&idx length:sizeof(int)]; + [fullscreenWindow leaveFullscreen]; + [fullscreenWindow release]; + fullscreenWindow = nil; +} - [vimController sendMessage:DraggedTabMsgID data:data]; + +- (IBAction)addNewTab:(id)sender +{ + [vimView addNewTab:sender]; } +- (IBAction)toggleToolbar:(id)sender +{ + [vimController sendMessage:ToggleToolbarMsgID data:nil]; +} @@ -685,8 +488,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { [vimController sendMessage:GotFocusMsgID data:nil]; - if (textStorage) - [[NSFontManager sharedFontManager] setSelectedFont:[textStorage font] + if ([vimView textStorage]) + [[NSFontManager sharedFontManager] setSelectedFont:[[vimView textStorage] font] isMultiple:NO]; } @@ -694,8 +497,8 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { [vimController sendMessage:LostFocusMsgID data:nil]; - if (textView) - [textView hideMarkedTextField]; + if ([vimView textView]) + [[vimView textView] hideMarkedTextField]; } - (BOOL)windowShouldClose:(id)sender @@ -788,65 +591,24 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (NSSize)contentSizeForTextStorageSize:(NSSize)textViewSize { - NSSize size = textViewSize; - - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - int right = [ud integerForKey:MMTextInsetRightKey]; - int bot = [ud integerForKey:MMTextInsetBottomKey]; - - size.width += [textView textContainerOrigin].x + right; - size.height += [textView textContainerOrigin].y + bot; - + NSSize size = [vimView contentSizeForTextStorageSize:textViewSize]; if (![tablineSeparator isHidden]) ++size.height; - if (![tabBarControl isHidden]) - size.height += [tabBarControl frame].size.height; - - if ([self bottomScrollbarVisible]) - size.height += [NSScroller scrollerWidth]; - if ([self leftScrollbarVisible]) - size.width += [NSScroller scrollerWidth]; - if ([self rightScrollbarVisible]) - size.width += [NSScroller scrollerWidth]; - return size; } - (NSRect)textViewRectForContentSize:(NSSize)contentSize { - NSRect rect = { 0, 0, contentSize.width, contentSize.height }; - + NSSize size = { contentSize.width, contentSize.height }; if (![tablineSeparator isHidden]) - --rect.size.height; - if (![tabBarControl isHidden]) - rect.size.height -= [tabBarControl frame].size.height; + --size.height; - if ([self bottomScrollbarVisible]) { - rect.size.height -= [NSScroller scrollerWidth]; - rect.origin.y += [NSScroller scrollerWidth]; - } - if ([self leftScrollbarVisible]) { - rect.size.width -= [NSScroller scrollerWidth]; - rect.origin.x += [NSScroller scrollerWidth]; - } - if ([self rightScrollbarVisible]) - rect.size.width -= [NSScroller scrollerWidth]; - - return rect; + return [vimView textViewRectForContentSize:size]; } - (NSSize)textStorageSizeForTextViewSize:(NSSize)textViewSize { - NSSize size = textViewSize; - - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - int right = [ud integerForKey:MMTextInsetRightKey]; - int bot = [ud integerForKey:MMTextInsetBottomKey]; - - size.width -= [textView textContainerOrigin].x + right; - size.height -= [textView textContainerOrigin].y + bot; - - return size; + return [vimView textStorageSizeForTextViewSize:textViewSize]; } - (void)resizeWindowToFit:(id)sender @@ -863,7 +625,7 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) NSWindow *win = [self window]; NSRect frame = [win frame]; NSRect contentRect = [win contentRectForFrameRect:frame]; - NSSize newSize = [self contentSizeForTextStorageSize:[textStorage size]]; + NSSize newSize = [self contentSizeForTextStorageSize:[[vimView textStorage] size]]; // Keep top-left corner of the window fixed when resizing. contentRect.origin.y -= newSize.height - contentRect.size.height; @@ -909,7 +671,7 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) NSRect contentRect = [win contentRectForFrameRect:frame]; NSSize size = [self textViewRectForContentSize:contentRect.size].size; size = [self textStorageSizeForTextViewSize:size]; - size = [textStorage fitToSize:size]; + size = [[vimView textStorage] fitToSize:size]; size = [self contentSizeForTextStorageSize:size]; // Keep top-left corner of 'frame' fixed. @@ -923,32 +685,18 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) { if (!setupDone) return; - NSSize size = [textStorage cellSize]; + NSSize size = [[vimView textStorage] cellSize]; [[self window] setContentResizeIncrements:size]; } - (NSTabViewItem *)addNewTabViewItem { - // NOTE! A newly created tab is not by selected by default; the VimTask - // decides which tab should be selected at all times. However, the AppKit - // will automatically select the first tab added to a tab view. - - NSTabViewItem *tvi = [[NSTabViewItem alloc] initWithIdentifier:nil]; - - // NOTE: If this is the first tab it will be automatically selected. - vimTaskSelectedTab = YES; - [tabView addTabViewItem:tvi]; - vimTaskSelectedTab = NO; - - [tvi release]; - - return tvi; + return [vimView addNewTabViewItem]; } - (int)representedIndexOfTabViewItem:(NSTabViewItem *)tvi { - NSArray *tabViewItems = [tabBarControl representedTabViewItems]; - return [tabViewItems indexOfObject:tvi]; + return [vimView representedIndexOfTabViewItem:tvi]; } - (IBAction)vimMenuItemAction:(id)sender @@ -963,218 +711,7 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) - (MMScroller *)scrollbarForIdentifier:(long)ident index:(unsigned *)idx { - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if ([scroller identifier] == ident) { - if (idx) *idx = i; - return scroller; - } - } - - return nil; -} - -- (BOOL)bottomScrollbarVisible -{ - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if ([scroller type] == MMScrollerTypeBottom && ![scroller isHidden]) - return YES; - } - - return NO; -} - -- (BOOL)leftScrollbarVisible -{ - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if ([scroller type] == MMScrollerTypeLeft && ![scroller isHidden]) - return YES; - } - - return NO; -} - -- (BOOL)rightScrollbarVisible -{ - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if ([scroller type] == MMScrollerTypeRight && ![scroller isHidden]) - return YES; - } - - return NO; -} - -- (void)placeScrollbars -{ - if (!setupDone) return; - - NSRect textViewFrame = [textView frame]; - BOOL lsbVisible = [self leftScrollbarVisible]; - - // HACK! Find the lowest left&right vertical scrollbars, as well as the - // rightmost horizontal scrollbar. This hack continues further down. - // - // TODO! Can there be no more than one horizontal scrollbar? If so, the - // code can be simplified. - unsigned lowestLeftSbIdx = (unsigned)-1; - unsigned lowestRightSbIdx = (unsigned)-1; - unsigned rightmostSbIdx = (unsigned)-1; - unsigned rowMaxLeft = 0, rowMaxRight = 0, colMax = 0; - unsigned i, count = [scrollbars count]; - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if (![scroller isHidden]) { - NSRange range = [scroller range]; - if ([scroller type] == MMScrollerTypeLeft - && range.location >= rowMaxLeft) { - rowMaxLeft = range.location; - lowestLeftSbIdx = i; - } else if ([scroller type] == MMScrollerTypeRight - && range.location >= rowMaxRight) { - rowMaxRight = range.location; - lowestRightSbIdx = i; - } else if ([scroller type] == MMScrollerTypeBottom - && range.location >= colMax) { - colMax = range.location; - rightmostSbIdx = i; - } - } - } - - // Place the scrollbars. - for (i = 0; i < count; ++i) { - MMScroller *scroller = [scrollbars objectAtIndex:i]; - if ([scroller isHidden]) - continue; - - NSRect rect; - if ([scroller type] == MMScrollerTypeBottom) { - rect = [textStorage rectForColumnsInRange:[scroller range]]; - rect.size.height = [NSScroller scrollerWidth]; - if (lsbVisible) - rect.origin.x += [NSScroller scrollerWidth]; - - // HACK! Make sure the rightmost horizontal scrollbar covers the - // text view all the way to the right, otherwise it looks ugly when - // the user drags the window to resize. - if (i == rightmostSbIdx) { - float w = NSMaxX(textViewFrame) - NSMaxX(rect); - if (w > 0) - rect.size.width += w; - } - - // Make sure scrollbar rect is bounded by the text view frame. - if (rect.origin.x < textViewFrame.origin.x) - rect.origin.x = textViewFrame.origin.x; - else if (rect.origin.x > NSMaxX(textViewFrame)) - rect.origin.x = NSMaxX(textViewFrame); - if (NSMaxX(rect) > NSMaxX(textViewFrame)) - rect.size.width -= NSMaxX(rect) - NSMaxX(textViewFrame); - if (rect.size.width < 0) - rect.size.width = 0; - } else { - rect = [textStorage rectForRowsInRange:[scroller range]]; - // Adjust for the fact that text layout is flipped. - rect.origin.y = NSMaxY(textViewFrame) - rect.origin.y - - rect.size.height; - rect.size.width = [NSScroller scrollerWidth]; - if ([scroller type] == MMScrollerTypeRight) - rect.origin.x = NSMaxX(textViewFrame); - - // HACK! Make sure the lowest vertical scrollbar covers the text - // view all the way to the bottom. This is done because Vim only - // makes the scrollbar cover the (vim-)window it is associated with - // and this means there is always an empty gap in the scrollbar - // region next to the command line. - // TODO! Find a nicer way to do this. - if (i == lowestLeftSbIdx || i == lowestRightSbIdx) { - float h = rect.origin.y + rect.size.height - - textViewFrame.origin.y; - if (rect.size.height < h) { - rect.origin.y = textViewFrame.origin.y; - rect.size.height = h; - } - } - - // Vertical scrollers must not cover the resize box in the - // bottom-right corner of the window. - if (rect.origin.y < [NSScroller scrollerWidth]) { - rect.size.height -= [NSScroller scrollerWidth] - rect.origin.y; - rect.origin.y = [NSScroller scrollerWidth]; - } - - // Make sure scrollbar rect is bounded by the text view frame. - if (rect.origin.y < textViewFrame.origin.y) { - rect.size.height -= textViewFrame.origin.y - rect.origin.y; - rect.origin.y = textViewFrame.origin.y; - } else if (rect.origin.y > NSMaxY(textViewFrame)) - rect.origin.y = NSMaxY(textViewFrame); - if (NSMaxY(rect) > NSMaxY(textViewFrame)) - rect.size.height -= NSMaxY(rect) - NSMaxY(textViewFrame); - if (rect.size.height < 0) - rect.size.height = 0; - } - - //NSLog(@"set scroller #%d frame = %@", i, NSStringFromRect(rect)); - NSRect oldRect = [scroller frame]; - if (!NSEqualRects(oldRect, rect)) { - [scroller setFrame:rect]; - // Clear behind the old scroller frame, or parts of the old - // scroller might still be visible after setFrame:. - [[[self window] contentView] setNeedsDisplayInRect:oldRect]; - [scroller setNeedsDisplay:YES]; - } - } -} - -- (void)scroll:(id)sender -{ - NSMutableData *data = [NSMutableData data]; - long ident = [(MMScroller*)sender identifier]; - int hitPart = [sender hitPart]; - float value = [sender floatValue]; - - [data appendBytes:&ident length:sizeof(long)]; - [data appendBytes:&hitPart length:sizeof(int)]; - [data appendBytes:&value length:sizeof(float)]; - - [vimController sendMessage:ScrollbarEventMsgID data:data]; -} - -- (void)placeViews -{ - if (!setupDone) return; - - // NOTE! It is assumed that the window has been resized so that it will - // exactly fit the text storage (possibly after resizing it). If this is - // not the case the display might be messed up. - NSWindow *win = [self window]; - NSRect contentRect = [win contentRectForFrameRect:[win frame]]; - NSRect textViewRect = [self textViewRectForContentSize:contentRect.size]; - NSSize tsSize = [self textStorageSizeForTextViewSize:textViewRect.size]; - - int dim[2], rows, cols; - [textStorage getMaxRows:&rows columns:&cols]; - [textStorage fitToSize:tsSize rows:&dim[0] columns:&dim[1]]; - - if (dim[0] != rows || dim[1] != cols) { - //NSLog(@"Notify Vim that text storage dimensions changed to %dx%d", - // dim[0], dim[1]); - NSData *data = [NSData dataWithBytes:dim length:2*sizeof(int)]; - - [vimController sendMessage:SetTextDimensionsMsgID data:data]; - } - - [textView setFrame:textViewRect]; - - [self placeScrollbars]; + return [vimView scrollbarForIdentifier:ident index:idx]; } - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb @@ -1194,73 +731,10 @@ NSMutableArray *buildMenuAddress(NSMenu *menu) return reply; } -@end // MMWindowController (Private) - - - -@implementation NSTabView (MMExtras) - -- (void)removeAllTabViewItems -{ - NSArray *existingItems = [self tabViewItems]; - NSEnumerator *e = [existingItems objectEnumerator]; - NSTabViewItem *item; - while (item = [e nextObject]){ - [self removeTabViewItem:item]; - } -} - -@end // NSTabView (MMExtras) - - - - -@implementation MMScroller - -- (id)initWithIdentifier:(long)ident type:(int)theType -{ - // HACK! NSScroller creates a horizontal scroller if it is init'ed with a - // frame whose with exceeds its height; so create a bogus rect and pass it - // to initWithFrame. - NSRect frame = theType == MMScrollerTypeBottom - ? NSMakeRect(0, 0, 1, 0) - : NSMakeRect(0, 0, 0, 1); - - if ((self = [super initWithFrame:frame])) { - identifier = ident; - type = theType; - [self setHidden:YES]; - [self setEnabled:YES]; - } - - return self; -} - -- (long)identifier +- (void)checkWindowNeedsResizing { - return identifier; + shouldUpdateWindowSize = + shouldUpdateWindowSize || [vimView shouldUpdateWindowSize]; } -- (int)type -{ - return type; -} - -- (NSRange)range -{ - return range; -} - -- (void)setRange:(NSRange)newRange -{ - range = newRange; -} - -- (void)scrollWheel:(NSEvent *)event -{ - // HACK! Pass message on to the text view. - MMWindowController *wc = [[self window] windowController]; - [[wc textView] scrollWheel:event]; -} - -@end // MMScroller +@end // MMWindowController (Private) diff --git a/MacVim.h b/MacVim.h index 0838730d..29afbf09 100644 --- a/MacVim.h +++ b/MacVim.h @@ -152,6 +152,8 @@ enum { AdjustLinespaceMsgID, ActivateMsgID, SetServerNameMsgID, + EnterFullscreenMsgID, + LeaveFullscreenMsgID, }; diff --git a/MacVim.m b/MacVim.m index 84bf922c..b85fd253 100644 --- a/MacVim.m +++ b/MacVim.m @@ -59,6 +59,8 @@ char *MessageStrings[] = "AdjustLinespaceMsgID", "ActivateMsgID", "SetServerNameMsgID", + "EnterFullscreenMsgID", + "LeaveFullscreenMsgID", }; diff --git a/MacVim.xcodeproj/project.pbxproj b/MacVim.xcodeproj/project.pbxproj index b5527d8b..439589a4 100644 --- a/MacVim.xcodeproj/project.pbxproj +++ b/MacVim.xcodeproj/project.pbxproj @@ -32,6 +32,9 @@ 1D71ACBC0BC702AC002F2B60 /* doc-bm-txt.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1D71ACB10BC702AB002F2B60 /* doc-bm-txt.icns */; }; 1D71ACBD0BC702AC002F2B60 /* doc-bm-xml.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1D71ACB20BC702AB002F2B60 /* doc-bm-xml.icns */; }; 1D71ACBE0BC702AC002F2B60 /* doc-bm.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1D71ACB30BC702AB002F2B60 /* doc-bm.icns */; }; + 1D80FBD40CBBD3B700102A1C /* MMFullscreenWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D80FBD00CBBD3B700102A1C /* MMFullscreenWindow.m */; }; + 1D80FBD60CBBD3B700102A1C /* MMVimView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D80FBD20CBBD3B700102A1C /* MMVimView.m */; }; + 1D80FBE40CBBD6F200102A1C /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D80FBE30CBBD6F200102A1C /* Carbon.framework */; }; 1DD04DEC0C529C5E006CDC2B /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 1DD04DEB0C529C5E006CDC2B /* Credits.rtf */; }; 1DD0C20C0C60FFB4008CD84A /* gvimrc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1DD0C20A0C60FF9A008CD84A /* gvimrc */; }; 1DD66ECE0C803D3600EBDAB3 /* MMApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DD66ECC0C803D3600EBDAB3 /* MMApplication.m */; }; @@ -158,6 +161,11 @@ 1D71ACB10BC702AB002F2B60 /* doc-bm-txt.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "doc-bm-txt.icns"; sourceTree = ""; }; 1D71ACB20BC702AB002F2B60 /* doc-bm-xml.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "doc-bm-xml.icns"; sourceTree = ""; }; 1D71ACB30BC702AB002F2B60 /* doc-bm.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "doc-bm.icns"; sourceTree = ""; }; + 1D80FBCF0CBBD3B700102A1C /* MMFullscreenWindow.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MMFullscreenWindow.h; sourceTree = ""; }; + 1D80FBD00CBBD3B700102A1C /* MMFullscreenWindow.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = MMFullscreenWindow.m; sourceTree = ""; }; + 1D80FBD10CBBD3B700102A1C /* MMVimView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MMVimView.h; sourceTree = ""; }; + 1D80FBD20CBBD3B700102A1C /* MMVimView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = MMVimView.m; sourceTree = ""; }; + 1D80FBE30CBBD6F200102A1C /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; 1DD04DEB0C529C5E006CDC2B /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 1DD0C20A0C60FF9A008CD84A /* gvimrc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = gvimrc; sourceTree = ""; }; 1DD66ECB0C803D3600EBDAB3 /* MMApplication.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MMApplication.h; sourceTree = ""; }; @@ -206,6 +214,7 @@ files = ( 1DFE25A50C527BC4003000F7 /* PSMTabBarControl.framework in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 1D80FBE40CBBD6F200102A1C /* Carbon.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -215,6 +224,10 @@ 080E96DDFE201D6D7F000001 /* MacVim Source */ = { isa = PBXGroup; children = ( + 1D80FBCF0CBBD3B700102A1C /* MMFullscreenWindow.h */, + 1D80FBD00CBBD3B700102A1C /* MMFullscreenWindow.m */, + 1D80FBD10CBBD3B700102A1C /* MMVimView.h */, + 1D80FBD20CBBD3B700102A1C /* MMVimView.m */, 1DD66ECB0C803D3600EBDAB3 /* MMApplication.h */, 1DD66ECC0C803D3600EBDAB3 /* MMApplication.m */, 1D09AB3F0C6A4D520045497E /* MMTypesetter.h */, @@ -240,6 +253,7 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( + 1D80FBE30CBBD6F200102A1C /* Carbon.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; @@ -506,6 +520,8 @@ 1D1474BC0C567A910038FA2B /* MMWindowController.m in Sources */, 1D09AB420C6A4D520045497E /* MMTypesetter.m in Sources */, 1DD66ECE0C803D3600EBDAB3 /* MMApplication.m in Sources */, + 1D80FBD40CBBD3B700102A1C /* MMFullscreenWindow.m in Sources */, + 1D80FBD60CBBD3B700102A1C /* MMVimView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -557,8 +573,10 @@ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_2)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_3)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_2)", ); FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SRCROOT)/../../../../Library/Frameworks\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_2 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\""; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -583,8 +601,10 @@ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_2)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_3)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_2)", ); FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SRCROOT)/../../../../Library/Frameworks\""; + FRAMEWORK_SEARCH_PATHS_QUOTED_2 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\""; GCC_DYNAMIC_NO_PIC = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; diff --git a/MacVim.xcodeproj/winckler.mode1 b/MacVim.xcodeproj/winckler.mode1 index 772b46f9..ac12bc44 100644 --- a/MacVim.xcodeproj/winckler.mode1 +++ b/MacVim.xcodeproj/winckler.mode1 @@ -368,9 +368,9 @@ TableOfContents - 1DD15C5B0CA69E1F00C745CE + 1D80FBD80CBBD3DA00102A1C 1CE0B1FE06471DED0097A5F4 - 1DD15C5C0CA69E1F00C745CE + 1D80FBD90CBBD3DA00102A1C 1CE0B20306471E060097A5F4 1CE0B20506471E060097A5F4 @@ -504,9 +504,8 @@ 5 WindowOrderList - 1D16B9EF0BA33E3800A69B33 - 1C0AD2B3069F1EA900FABCE6 /Users/winckler/Projects/vim7/src/MacVim/MacVim.xcodeproj + 1D16B9EF0BA33E3800A69B33 WindowString 165 349 690 397 0 0 1024 746 @@ -547,6 +546,8 @@ 194pt + BecomeActive + ContentConfiguration PBXProjectModuleGUID @@ -586,7 +587,7 @@ TableOfContents 1D16B9EF0BA33E3800A69B33 - 1DD15C5D0CA69E1F00C745CE + 1D80FBCD0CBBD39300102A1C 1CD0528F0623707200166675 XCMainBuildResultsModuleGUID @@ -597,7 +598,7 @@ WindowToolGUID 1D16B9EF0BA33E3800A69B33 WindowToolIsVisible - + FirstTimeWindowDisplayed diff --git a/MacVim.xcodeproj/winckler.pbxuser b/MacVim.xcodeproj/winckler.pbxuser index 994d686a..e59fd9ed 100644 --- a/MacVim.xcodeproj/winckler.pbxuser +++ b/MacVim.xcodeproj/winckler.pbxuser @@ -438,8 +438,8 @@ PBXFileDataSource_Warnings_ColumnID, ); }; - PBXPerProjectTemplateStateSaveDate = 212246019; - PBXWorkspaceStateSaveDate = 212246019; + PBXPerProjectTemplateStateSaveDate = 213635967; + PBXWorkspaceStateSaveDate = 213635967; }; sourceControlManager = 1D16B9DD0BA33BB000A69B33 /* Source Control */; userBuildSettings = { diff --git a/PSMTabBarControl/source/PSMMetalTabStyle.m b/PSMTabBarControl/source/PSMMetalTabStyle.m index ca0051f1..2b2bce45 100644 --- a/PSMTabBarControl/source/PSMMetalTabStyle.m +++ b/PSMTabBarControl/source/PSMMetalTabStyle.m @@ -13,6 +13,14 @@ #define kPSMMetalObjectCounterRadius 7.0 #define kPSMMetalCounterMinWidth 20 +// NSDrawWindowBackground() is broken for borderless windows, see +// http://lists.apple.com/archives/cocoa-dev/2006/Feb/msg00130.html +void MyNSDrawWindowBackground(NSRect rect) +{ + [[NSColor windowBackgroundColor] set]; + NSRectFill( rect ); +} + @implementation PSMMetalTabStyle - (NSString *)name @@ -316,7 +324,7 @@ aRect.size.height -= 0.5; // background - NSDrawWindowBackground(aRect); + MyNSDrawWindowBackground(aRect); aRect.size.height+=0.5; @@ -452,7 +460,7 @@ - (void)drawTabBar:(PSMTabBarControl *)bar inRect:(NSRect)rect { - NSDrawWindowBackground(rect); + MyNSDrawWindowBackground(rect); [[NSColor colorWithCalibratedWhite:0.0 alpha:0.2] set]; NSRectFillUsingOperation(rect, NSCompositeSourceAtop); [[NSColor darkGrayColor] set]; diff --git a/gui_macvim.m b/gui_macvim.m index 84bef472..53673eaf 100644 --- a/gui_macvim.m +++ b/gui_macvim.m @@ -1367,6 +1367,18 @@ gui_macvim_is_valid_action(NSString *action) return [actionDict objectForKey:action] != nil; } + void +gui_mch_enter_fullscreen() +{ + [[MMBackend sharedInstance] enterFullscreen]; +} + + void +gui_mch_leave_fullscreen() +{ + [[MMBackend sharedInstance] leaveFullscreen]; +} + // -- Client/Server --------------------------------------------------------- -- 2.11.4.GIT