1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
13 * Handles resizing of windows, acts as an mediator between MMVimView and
16 * Resizing in windowed mode:
18 * In windowed mode resizing can occur either due to the window frame changing
19 * size (e.g. when the user drags to resize), or due to Vim changing the number
20 * of (rows,columns). The former case is dealt with by letting the vim view
21 * fill the entire content view when the window has resized. In the latter
22 * case we ensure that vim view fits on the screen.
24 * The vim view notifies Vim if the number of (rows,columns) does not match the
25 * current number whenver the view size is about to change. Upon receiving a
26 * dimension change message, Vim notifies the window controller and the window
27 * resizes. However, the window is never resized programmatically during a
28 * live resize (in order to avoid jittering).
30 * The window size is constrained to not become too small during live resize,
31 * and it is also constrained to always fit an integer number of
34 * In windowed mode we have to manually draw a tabline separator (due to bugs
35 * in the way Cocoa deals with the toolbar separator) when certain conditions
36 * are met. The rules for this are as follows:
38 * Tabline visible & Toolbar visible => Separator visible
39 * =====================================================================
40 * NO & NO => YES, if the window is textured
47 * Resizing in full-screen mode:
49 * The window never resizes since it fills the screen, however the vim view may
50 * change size, e.g. when the user types ":set lines=60", or when a scrollbar
53 * It is ensured that the vim view never becomes larger than the screen size
54 * and that it always stays in the center of the screen.
58 #import "MMAppController.h"
59 #import "MMAtsuiTextView.h"
60 #import "MMFindReplaceController.h"
61 #import "MMFullscreenWindow.h"
62 #import "MMTextView.h"
63 #import "MMTypesetter.h"
64 #import "MMVimController.h"
67 #import "MMWindowController.h"
68 #import "Miscellaneous.h"
69 #import <PSMTabBarControl.h>
73 @interface MMWindowController (Private)
74 - (NSSize)contentSize;
75 - (void)resizeWindowToFitContentSize:(NSSize)contentSize;
76 - (NSSize)constrainContentSizeToScreenSize:(NSSize)contentSize;
77 - (void)updateResizeConstraints;
78 - (NSTabViewItem *)addNewTabViewItem;
79 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb;
80 - (void)hideTablineSeparator:(BOOL)hide;
81 - (void)doFindNext:(BOOL)next;
85 @interface NSWindow (NSWindowPrivate)
86 // Note: This hack allows us to set content shadowing separately from
87 // the window shadow. This is apparently what webkit and terminal do.
88 - (void)_setContentHasShadow:(BOOL)shadow; // new Tiger private method
90 // This is a private api that makes textured windows not have rounded corners.
91 // We want this on Leopard.
92 - (void)setBottomCornerRounded:(BOOL)rounded;
96 @interface NSWindow (NSLeopardOnly)
97 // Note: These functions are Leopard-only, use -[NSObject respondsToSelector:]
98 // before calling them to make sure everything works on Tiger too.
100 #ifndef CGFLOAT_DEFINED
101 // On Leopard, CGFloat is float on 32bit and double on 64bit. On Tiger,
102 // we can't use this anyways, so it's just here to keep the compiler happy.
103 // However, when we're compiling for Tiger and running on Leopard, we
104 // might need the correct typedef, so this piece is copied from ATSTypes.h
106 typedef double CGFloat;
108 typedef float CGFloat;
111 - (void)setAutorecalculatesContentBorderThickness:(BOOL)b forEdge:(NSRectEdge)e;
112 - (void)setContentBorderThickness:(CGFloat)b forEdge:(NSRectEdge)e;
118 @implementation MMWindowController
120 - (id)initWithVimController:(MMVimController *)controller
122 #ifndef NSAppKitVersionNumber10_4 // needed for non-10.5 sdk
123 # define NSAppKitVersionNumber10_4 824
125 unsigned styleMask = NSTitledWindowMask | NSClosableWindowMask
126 | NSMiniaturizableWindowMask | NSResizableWindowMask
127 | NSUnifiedTitleAndToolbarWindowMask;
129 // Use textured background on Leopard or later (skip the 'if' on Tiger for
130 // polished metal window).
131 if ([[NSUserDefaults standardUserDefaults] boolForKey:MMTexturedWindowKey]
132 || (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4))
133 styleMask |= NSTexturedBackgroundWindowMask;
135 // NOTE: The content rect is only used the very first time MacVim is
136 // started (or rather, when ~/Library/Preferences/org.vim.MacVim.plist does
137 // not exist). The chosen values will put the window somewhere near the
138 // top and in the middle of a 1024x768 screen.
139 MMWindow *win = [[MMWindow alloc]
140 initWithContentRect:NSMakeRect(242,364,480,360)
142 backing:NSBackingStoreBuffered
146 self = [super initWithWindow:win];
147 if (!self) return nil;
149 vimController = controller;
150 decoratedWindow = [win retain];
152 // Window cascading is handled by MMAppController.
153 [self setShouldCascadeWindows:NO];
155 // NOTE: Autoresizing is enabled for the content view, but only used
156 // for the tabline separator. The vim view must be resized manually
157 // because of full-screen considerations, and because its size depends
158 // on whether the tabline separator is visible or not.
159 NSView *contentView = [win contentView];
160 [contentView setAutoresizesSubviews:YES];
162 vimView = [[MMVimView alloc] initWithFrame:[contentView frame]
163 vimController:vimController];
164 [vimView setAutoresizingMask:NSViewNotSizable];
165 [contentView addSubview:vimView];
167 [win setDelegate:self];
168 [win setInitialFirstResponder:[vimView textView]];
170 if ([win styleMask] & NSTexturedBackgroundWindowMask) {
171 // On Leopard, we want to have a textured window to have nice
172 // looking tabs. But the textured window look implies rounded
173 // corners, which looks really weird -- disable them. This is a
174 // private api, though.
175 if ([win respondsToSelector:@selector(setBottomCornerRounded:)])
176 [win setBottomCornerRounded:NO];
178 // When the tab bar is toggled, it changes color for the fraction
179 // of a second, probably because vim sends us events in a strange
180 // order, confusing appkit's content border heuristic for a short
181 // while. This can be worked around with these two methods. There
182 // might be a better way, but it's good enough.
183 if ([win respondsToSelector:@selector(
184 setAutorecalculatesContentBorderThickness:forEdge:)])
185 [win setAutorecalculatesContentBorderThickness:NO
187 if ([win respondsToSelector:
188 @selector(setContentBorderThickness:forEdge:)])
189 [win setContentBorderThickness:0 forEdge:NSMaxYEdge];
192 // Make us safe on pre-tiger OSX
193 if ([win respondsToSelector:@selector(_setContentHasShadow:)])
194 [win _setContentHasShadow:NO];
203 [decoratedWindow release]; decoratedWindow = nil;
204 [windowAutosaveKey release]; windowAutosaveKey = nil;
205 [vimView release]; vimView = nil;
210 - (NSString *)description
213 @"%@ : setupDone=%d windowAutosaveKey=%@ vimController=%@";
214 return [NSString stringWithFormat:format,
215 [self className], setupDone, windowAutosaveKey, vimController];
218 - (MMVimController *)vimController
220 return vimController;
223 - (MMVimView *)vimView
228 - (NSString *)windowAutosaveKey
230 return windowAutosaveKey;
233 - (void)setWindowAutosaveKey:(NSString *)key
235 [windowAutosaveKey autorelease];
236 windowAutosaveKey = [key copy];
241 //NSLog(@"%@ %s", [self className], _cmd);
243 if (fullscreenEnabled) {
244 // If we are closed while still in fullscreen, end fullscreen mode,
245 // release ourselves (because this won't happen in MMWindowController)
246 // and perform close operation on the original window.
247 [self leaveFullscreen];
253 [vimView removeFromSuperviewWithoutNeedingDisplay];
256 // It is feasible (though unlikely) that the user quits before the window
257 // controller is released, make sure the edit flag is cleared so no warning
258 // dialog is displayed.
259 [decoratedWindow setDocumentEdited:NO];
261 [[self window] orderOut:self];
266 // Indicates that the window is ready to be displayed, but do not display
267 // (or place) it yet -- that is done in showWindow.
269 [self addNewTabViewItem];
273 [self updateResizeConstraints];
274 [self resizeWindowToFitContentSize:[vimView desiredSize]];
279 // Actually show the window on screen. However, if openWindow hasn't
280 // already been called nothing will happen (the window will be displayed
282 if (!setupDone) return;
284 [[MMAppController sharedInstance] windowControllerWillOpen:self];
285 [[self window] makeKeyAndOrderFront:self];
288 - (void)updateTabsWithData:(NSData *)data
290 [vimView updateTabsWithData:data];
293 - (void)selectTabWithIndex:(int)idx
295 [vimView selectTabWithIndex:idx];
298 - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols live:(BOOL)live
300 //NSLog(@"setTextDimensionsWithRows:%d columns:%d live:%s", rows, cols,
301 // live ? "YES" : "NO");
303 // NOTE: The only place where the (rows,columns) of the vim view are
304 // modified is here and when entering/leaving full-screen. Setting these
305 // values have no immediate effect, the actual resizing of the view is done
306 // in processCommandQueueDidFinish.
308 // The 'live' flag indicates that this resize originated from a live
309 // resize; it may very well happen that the view is no longer in live
310 // resize when this message is received. We refrain from changing the view
311 // size when this flag is set, otherwise the window might jitter when the
312 // user drags to resize the window.
314 [vimView setDesiredRows:rows columns:cols];
316 if (setupDone && !live)
317 shouldResizeVimView = YES;
320 - (void)setTitle:(NSString *)title
323 [decoratedWindow setTitle:title];
326 - (void)setDocumentFilename:(NSString *)filename
331 // Ensure file really exists or the path to the proxy icon will look weird.
332 // If the file does not exists, don't show a proxy icon.
333 if (![[NSFileManager defaultManager] fileExistsAtPath:filename])
336 [decoratedWindow setRepresentedFilename:filename];
339 - (void)setToolbar:(NSToolbar *)toolbar
341 // The full-screen window has no toolbar.
342 [decoratedWindow setToolbar:toolbar];
344 // HACK! Redirect the pill button so that we can ask Vim to hide the
346 NSButton *pillButton = [decoratedWindow
347 standardWindowButton:NSWindowToolbarButton];
349 [pillButton setAction:@selector(toggleToolbar:)];
350 [pillButton setTarget:self];
354 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
356 [vimView createScrollbarWithIdentifier:ident type:type];
359 - (BOOL)destroyScrollbarWithIdentifier:(long)ident
361 BOOL scrollbarHidden = [vimView destroyScrollbarWithIdentifier:ident];
362 shouldResizeVimView = shouldResizeVimView || scrollbarHidden;
364 return scrollbarHidden;
367 - (BOOL)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
369 BOOL scrollbarToggled = [vimView showScrollbarWithIdentifier:ident
371 shouldResizeVimView = shouldResizeVimView || scrollbarToggled;
373 return scrollbarToggled;
376 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
378 [vimView setScrollbarPosition:pos length:len identifier:ident];
381 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
382 identifier:(long)ident
384 [vimView setScrollbarThumbValue:val proportion:prop identifier:ident];
387 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
389 // NOTE: This is called when the transparency changes so set the opacity
390 // flag on the window here (should be faster if the window is opaque).
391 BOOL isOpaque = [back alphaComponent] == 1.0f;
392 [decoratedWindow setOpaque:isOpaque];
393 if (fullscreenEnabled)
394 [fullscreenWindow setOpaque:isOpaque];
396 [vimView setDefaultColorsBackground:back foreground:fore];
399 - (void)setFont:(NSFont *)font
401 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
402 [[vimView textView] setFont:font];
403 [self updateResizeConstraints];
406 - (void)setWideFont:(NSFont *)font
408 [[vimView textView] setWideFont:font];
411 - (void)processCommandQueueDidFinish
413 // IMPORTANT! No synchronous DO calls are allowed in this method. They
414 // may cause the command queue to get processed out of order.
416 // NOTE: Resizing is delayed until after all commands have been processed
417 // since it often happens that more than one command will cause a resize.
418 // If we were to immediately resize then the vim view size would jitter
419 // (e.g. hiding/showing scrollbars often happens several time in one
422 if (shouldResizeVimView) {
423 shouldResizeVimView = NO;
425 NSSize contentSize = [vimView desiredSize];
426 contentSize = [self constrainContentSizeToScreenSize:contentSize];
427 contentSize = [vimView constrainRows:NULL columns:NULL
429 [vimView setFrameSize:contentSize];
431 if (fullscreenEnabled) {
432 [[fullscreenWindow contentView] setNeedsDisplay:YES];
433 [fullscreenWindow centerView];
435 [self resizeWindowToFitContentSize:contentSize];
440 - (void)showTabBar:(BOOL)on
442 [[vimView tabBarControl] setHidden:!on];
444 // Showing the tabline may result in the tabline separator being hidden or
445 // shown; this does not apply to full-screen mode.
447 NSToolbar *toolbar = [decoratedWindow toolbar];
448 if (([decoratedWindow styleMask] & NSTexturedBackgroundWindowMask)
450 [self hideTablineSeparator:![toolbar isVisible]];
452 [self hideTablineSeparator:NO];
455 if (([decoratedWindow styleMask] & NSTexturedBackgroundWindowMask)
457 [self hideTablineSeparator:on];
459 [self hideTablineSeparator:YES];
464 - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode
466 NSToolbar *toolbar = [decoratedWindow toolbar];
467 if (!toolbar) return;
469 [toolbar setSizeMode:size];
470 [toolbar setDisplayMode:mode];
471 [toolbar setVisible:on];
473 if (([decoratedWindow styleMask] & NSTexturedBackgroundWindowMask) == 0) {
475 [self hideTablineSeparator:YES];
477 [self hideTablineSeparator:![[vimView tabBarControl] isHidden]];
480 // Textured windows don't have a line below there title bar, so we
481 // need the separator in this case as well. In fact, the only case
482 // where we don't need the separator is when the tab bar control
483 // is visible (because it brings its own separator).
484 [self hideTablineSeparator:![[vimView tabBarControl] isHidden]];
488 - (void)setMouseShape:(int)shape
490 [[vimView textView] setMouseShape:shape];
493 - (void)adjustLinespace:(int)linespace
495 if (vimView && [vimView textView]) {
496 [[vimView textView] setLinespace:(float)linespace];
497 shouldResizeVimView = YES;
501 - (void)liveResizeWillStart
503 // Save the original title, if we haven't already.
504 if (lastSetTitle == nil) {
505 lastSetTitle = [[decoratedWindow title] retain];
509 - (void)liveResizeDidEnd
511 if (!setupDone) return;
513 // NOTE: During live resize messages from MacVim to Vim are often dropped
514 // (because too many messages are sent at once). This may lead to
515 // inconsistent states between Vim and MacVim; to avoid this we send a
516 // synchronous resize message to Vim now (this is not fool-proof, but it
517 // does seem to work quite well).
520 NSSize textViewSize = [[vimView textView] frame].size;
521 [[vimView textView] constrainRows:&constrained[0] columns:&constrained[1]
522 toSize:textViewSize];
524 //NSLog(@"End of live resize, notify Vim that text dimensions are %dx%d",
525 // constrained[1], constrained[0]);
527 NSData *data = [NSData dataWithBytes:constrained length:2*sizeof(int)];
528 BOOL sendOk = [vimController sendMessageNow:SetTextDimensionsMsgID
533 // Sending of synchronous message failed. Force the window size to
534 // match the last dimensions received from Vim, otherwise we end up
535 // with inconsistent states.
536 [self resizeWindowToFitContentSize:[vimView desiredSize]];
539 // If we saved the original title while resizing, restore it.
540 if (lastSetTitle != nil) {
541 [decoratedWindow setTitle:lastSetTitle];
542 [lastSetTitle release];
547 - (void)enterFullscreen:(int)fuoptions backgroundColor:(NSColor *)back
549 if (fullscreenEnabled) return;
551 fullscreenWindow = [[MMFullscreenWindow alloc]
552 initWithWindow:decoratedWindow view:vimView backgroundColor:back];
553 [fullscreenWindow enterFullscreen:fuoptions];
554 [fullscreenWindow setDelegate:self];
555 fullscreenEnabled = YES;
557 // The resize handle disappears so the vim view needs to update the
559 shouldResizeVimView = YES;
562 - (void)leaveFullscreen
564 if (!fullscreenEnabled) return;
566 fullscreenEnabled = NO;
567 [fullscreenWindow leaveFullscreen];
568 [fullscreenWindow release];
569 fullscreenWindow = nil;
571 // The vim view may be too large to fit the screen, so update it.
572 shouldResizeVimView = YES;
575 - (void)setFullscreenBackgroundColor:(NSColor *)back
577 if (fullscreenEnabled)
578 [fullscreenWindow setBackgroundColor:back];
581 - (void)setBuffersModified:(BOOL)mod
583 // NOTE: We only set the document edited flag on the decorated window since
584 // the full-screen window has no close button anyway. (It also saves us
585 // from keeping track of the flag in two different places.)
586 [decoratedWindow setDocumentEdited:mod];
590 - (IBAction)addNewTab:(id)sender
592 [vimView addNewTab:sender];
595 - (IBAction)toggleToolbar:(id)sender
597 [vimController sendMessage:ToggleToolbarMsgID data:nil];
600 - (IBAction)performClose:(id)sender
602 // NOTE: With the introduction of :macmenu it is possible to bind
603 // File.Close to ":conf q" but at the same time have it send off the
604 // performClose: action. For this reason we no longer need the CloseMsgID
605 // message. However, we still need File.Close to send performClose:
606 // otherwise Cmd-w will not work on dialogs.
607 [self vimMenuItemAction:sender];
610 - (IBAction)findNext:(id)sender
612 [self doFindNext:YES];
615 - (IBAction)findPrevious:(id)sender
617 [self doFindNext:NO];
620 - (IBAction)vimMenuItemAction:(id)sender
622 if (![sender isKindOfClass:[NSMenuItem class]]) return;
624 // TODO: Make into category on NSMenuItem which returns descriptor.
625 NSMenuItem *item = (NSMenuItem*)sender;
626 NSMutableArray *desc = [NSMutableArray arrayWithObject:[item title]];
628 NSMenu *menu = [item menu];
630 [desc insertObject:[menu title] atIndex:0];
631 menu = [menu supermenu];
634 // The "MainMenu" item is part of the Cocoa menu and should not be part of
636 if ([[desc objectAtIndex:0] isEqual:@"MainMenu"])
637 [desc removeObjectAtIndex:0];
639 NSDictionary *attrs = [NSDictionary dictionaryWithObject:desc
640 forKey:@"descriptor"];
641 [vimController sendMessage:ExecuteMenuMsgID data:[attrs dictionaryAsData]];
644 - (IBAction)vimToolbarItemAction:(id)sender
646 NSArray *desc = [NSArray arrayWithObjects:@"ToolBar", [sender label], nil];
647 NSDictionary *attrs = [NSDictionary dictionaryWithObject:desc
648 forKey:@"descriptor"];
649 [vimController sendMessage:ExecuteMenuMsgID data:[attrs dictionaryAsData]];
652 - (IBAction)fontSizeUp:(id)sender
654 [[NSFontManager sharedFontManager] modifyFont:
655 [NSNumber numberWithInt:NSSizeUpFontAction]];
658 - (IBAction)fontSizeDown:(id)sender
660 [[NSFontManager sharedFontManager] modifyFont:
661 [NSNumber numberWithInt:NSSizeDownFontAction]];
664 - (IBAction)findAndReplace:(id)sender
666 int tag = [sender tag];
667 MMFindReplaceController *fr = [MMFindReplaceController sharedInstance];
670 // NOTE: The 'flags' values must match the FRD_ defines in gui.h (except
671 // for 0x100 which we use to indicate a backward search).
673 case 1: flags = 0x100; break;
674 case 2: flags = 3; break;
675 case 3: flags = 4; break;
680 if (![fr ignoreCase])
683 NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
684 [fr findString], @"find",
685 [fr replaceString], @"replace",
686 [NSNumber numberWithInt:flags], @"flags",
689 [vimController sendMessage:FindReplaceMsgID data:[args dictionaryAsData]];
692 - (BOOL)validateMenuItem:(NSMenuItem *)item
694 if ([item action] == @selector(vimMenuItemAction:)
695 || [item action] == @selector(performClose:))
701 // -- NSWindow delegate ------------------------------------------------------
703 - (void)windowDidBecomeMain:(NSNotification *)notification
705 [[MMAppController sharedInstance] setMainMenu:[vimController mainMenu]];
706 [vimController sendMessage:GotFocusMsgID data:nil];
708 if ([vimView textView]) {
709 NSFontManager *fm = [NSFontManager sharedFontManager];
710 [fm setSelectedFont:[[vimView textView] font] isMultiple:NO];
714 - (void)windowDidResignMain:(NSNotification *)notification
716 [vimController sendMessage:LostFocusMsgID data:nil];
719 - (BOOL)windowShouldClose:(id)sender
721 // Don't close the window now; Instead let Vim decide whether to close the
723 [vimController sendMessage:VimShouldCloseMsgID data:nil];
727 - (void)windowDidMove:(NSNotification *)notification
729 if (setupDone && windowAutosaveKey) {
730 NSRect frame = [decoratedWindow frame];
731 NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
732 NSString *topLeftString = NSStringFromPoint(topLeft);
734 [[NSUserDefaults standardUserDefaults]
735 setObject:topLeftString forKey:windowAutosaveKey];
739 - (NSSize)windowWillResize:(NSWindow *)win toSize:(NSSize)proposedFrameSize
741 // Make sure the window isn't resized to be larger than the "visible frame"
742 // for the current screen. Do this here instead of setting the window max
743 // size in updateResizeConstraints since the screen's visible frame may
744 // change at any time (dock could move, resolution could change, window
745 // could be moved to another screen, ...).
747 NSRect maxFrame = [[win screen] visibleFrame];
748 NSRect rect = [win contentRectForFrameRect:maxFrame];
749 rect.size = [vimView constrainRows:NULL columns:NULL toSize:rect.size];
750 maxFrame = [win frameRectForContentRect:rect];
752 if (proposedFrameSize.width > maxFrame.size.width)
753 proposedFrameSize.width = maxFrame.size.width;
754 if (proposedFrameSize.height > maxFrame.size.height)
755 proposedFrameSize.height = maxFrame.size.height;
757 return proposedFrameSize;
760 - (void)windowDidResize:(id)sender
762 if (!setupDone || fullscreenEnabled) return;
764 // NOTE: Since we have no control over when the window may resize (Cocoa
765 // may resize automatically) we simply set the view to fill the entire
766 // window. The vim view takes care of notifying Vim if the number of
767 // (rows,columns) changed.
768 [vimView setFrameSize:[self contentSize]];
771 - (NSRect)windowWillUseStandardFrame:(NSWindow *)win
772 defaultFrame:(NSRect)frame
774 // By default the window is maximized in the vertical direction only.
775 // Holding down the Cmd key maximizes the window in the horizontal
776 // direction. If the MMZoomBoth user default is set, then the window
777 // maximizes in both directions by default, unless the Cmd key is held in
778 // which case the window only maximizes in the vertical direction.
780 NSEvent *event = [NSApp currentEvent];
781 BOOL cmdLeftClick = [event type] == NSLeftMouseUp
782 && [event modifierFlags] & NSCommandKeyMask;
783 BOOL zoomBoth = [[NSUserDefaults standardUserDefaults]
784 boolForKey:MMZoomBothKey];
786 // The "default frame" represents the maximal size of a zoomed window.
787 // Constrain this frame so that the content fits an even number of rows and
789 NSRect contentRect = [decoratedWindow contentRectForFrameRect:frame];
790 NSSize constrainedSize = [vimView constrainRows:NULL
792 toSize:contentRect.size];
794 contentRect.origin.y += contentRect.size.height - constrainedSize.height;
795 contentRect.size = constrainedSize;
797 frame = [decoratedWindow frameRectForContentRect:contentRect];
799 if (!((zoomBoth && !cmdLeftClick) || (!zoomBoth && cmdLeftClick))) {
800 // Zoom in horizontal direction only.
801 NSRect currentFrame = [win frame];
802 frame.size.width = currentFrame.size.width;
803 frame.origin.x = currentFrame.origin.x;
812 // -- Services menu delegate -------------------------------------------------
814 - (id)validRequestorForSendType:(NSString *)sendType
815 returnType:(NSString *)returnType
817 if ([sendType isEqual:NSStringPboardType]
818 && [self askBackendForStarRegister:nil])
821 return [super validRequestorForSendType:sendType returnType:returnType];
824 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
825 types:(NSArray *)types
827 if (![types containsObject:NSStringPboardType])
830 return [self askBackendForStarRegister:pboard];
833 @end // MMWindowController
837 @implementation MMWindowController (Private)
839 - (NSSize)contentSize
841 // NOTE: Never query the content view directly for its size since it may
842 // not return the same size as contentRectForFrameRect: (e.g. when in
843 // windowed mode and the tabline separator is visible)!
844 NSWindow *win = [self window];
845 return [win contentRectForFrameRect:[win frame]].size;
848 - (void)resizeWindowToFitContentSize:(NSSize)contentSize
850 NSRect frame = [decoratedWindow frame];
851 NSRect contentRect = [decoratedWindow contentRectForFrameRect:frame];
853 // Keep top-left corner of the window fixed when resizing.
854 contentRect.origin.y -= contentSize.height - contentRect.size.height;
855 contentRect.size = contentSize;
857 frame = [decoratedWindow frameRectForContentRect:contentRect];
858 [decoratedWindow setFrame:frame display:YES];
861 - (NSSize)constrainContentSizeToScreenSize:(NSSize)contentSize
863 NSWindow *win = [self window];
864 NSRect rect = [win contentRectForFrameRect:[[win screen] visibleFrame]];
866 if (contentSize.height > rect.size.height)
867 contentSize.height = rect.size.height;
868 if (contentSize.width > rect.size.width)
869 contentSize.width = rect.size.width;
874 - (void)updateResizeConstraints
876 if (!setupDone) return;
878 // Set the resize increments to exactly match the font size; this way the
879 // window will always hold an integer number of (rows,columns).
880 NSSize cellSize = [[vimView textView] cellSize];
881 [decoratedWindow setContentResizeIncrements:cellSize];
883 NSSize minSize = [vimView minSize];
884 [decoratedWindow setContentMinSize:minSize];
887 - (NSTabViewItem *)addNewTabViewItem
889 return [vimView addNewTabViewItem];
892 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb
894 // TODO: Can this be done with evaluateExpression: instead?
896 id backendProxy = [vimController backendProxy];
900 reply = [backendProxy starRegisterToPasteboard:pb];
902 @catch (NSException *e) {
903 NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
910 - (void)hideTablineSeparator:(BOOL)hide
912 // The full-screen window has no tabline separator so we operate on
913 // decoratedWindow instead of [self window].
914 if ([decoratedWindow hideTablineSeparator:hide]) {
915 // The tabline separator was toggled so the content view must change
917 [self updateResizeConstraints];
918 shouldResizeVimView = YES;
922 - (void)doFindNext:(BOOL)next
924 NSString *query = nil;
927 // Use current query if the search field is selected.
928 id searchField = [[self searchFieldItem] view];
929 if (searchField && [[searchField stringValue] length] > 0 &&
930 [decoratedWindow firstResponder] == [searchField currentEditor])
931 query = [searchField stringValue];
935 // Use find pasteboard for next query.
936 NSPasteboard *pb = [NSPasteboard pasteboardWithName:NSFindPboard];
937 NSArray *types = [NSArray arrayWithObject:NSStringPboardType];
938 if ([pb availableTypeFromArray:types])
939 query = [pb stringForType:NSStringPboardType];
942 NSString *input = nil;
944 // NOTE: The '/' register holds the last search string. By setting it
945 // (using the '@/' syntax) we fool Vim into thinking that it has
946 // already searched for that string and then we can simply use 'n' or
947 // 'N' to find the next/previous match.
948 input = [NSString stringWithFormat:@"<C-\\><C-N>:let @/='%@'<CR>%c",
949 query, next ? 'n' : 'N'];
951 input = next ? @"<C-\\><C-N>n" : @"<C-\\><C-N>N";
954 [vimController addVimInput:input];
957 @end // MMWindowController (Private)