Avoid false 'documented edited' warning when quitting
[MacVim.git] / src / MacVim / MMWindowController.m
blob2812d7dfea3c07a27030e0a7bab48ec35a8aa820
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
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.
9  */
11  * MMWindowController
12  *
13  * Handles resizing of windows, acts as an mediator between MMVimView and
14  * MMVimController.
15  */
17 #import "MMWindowController.h"
18 #import <PSMTabBarControl.h>
19 #import "MMTextView.h"
20 #import "MMTextStorage.h"
21 #import "MMVimController.h"
22 #import "MacVim.h"
23 #import "MMAppController.h"
24 #import "MMTypesetter.h"
25 #import "MMFullscreenWindow.h"
26 #import "MMVimView.h"
30 @interface MMWindowController (Private)
31 - (NSSize)contentSize;
32 - (NSRect)contentRectForFrameRect:(NSRect)frame;
33 - (NSRect)frameRectForContentRect:(NSRect)contentRect;
34 - (void)resizeWindowToFit:(id)sender;
35 - (NSRect)fitWindowToFrame:(NSRect)frame;
36 - (void)updateResizeIncrements;
37 - (NSTabViewItem *)addNewTabViewItem;
38 - (IBAction)vimMenuItemAction:(id)sender;
39 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb;
40 - (void)checkWindowNeedsResizing;
41 - (NSSize)resizeVimViewToFitSize:(NSSize)size;
42 @end
46 #if 0
47 NSString *buildMenuItemDescriptor(NSMenu *menu, NSString *tail)
49     return menu ? buildMenuItemDescriptor([menu supermenu], [[menu title]
50                     stringByAppendingString:tail])
51                 : tail;
54 NSMutableArray *buildMenuAddress(NSMenu *menu)
56     NSMutableArray *addr;
57     if (menu) {
58         addr = buildMenuAddress([menu supermenu]);
59         [addr addObject:[menu title]];
60     } else {
61         addr = [NSMutableArray array];
62     }
64     return addr;
66 #endif
68 @interface NSWindow (NSWindowPrivate)
69 // Note: This hack allows us to set content shadowing separately from
70 // the window shadow.  This is apparently what webkit and terminal do.
71 - (void)_setContentHasShadow:(BOOL)shadow; // new Tiger private method
73 // This is a private api that makes textured windows not have rounded corners.
74 // We want this on Leopard.
75 - (void)setBottomCornerRounded:(BOOL)rounded;
76 @end
78 @interface NSWindow (NSLeopardOnly)
79 // Note: These functions are Leopard-only, use -[NSObject respondsToSelector:]
80 // before calling them to make sure everything works on Tiger too.
82 #ifndef CGFLOAT_DEFINED
83     // On Leopard, CGFloat is float on 32bit and double on 64bit. On Tiger,
84     // we can't use this anyways, so it's just here to keep the compiler happy.
85     // However, when we're compiling for Tiger and running on Leopard, we
86     // might need the correct typedef, so this piece is copied from ATSTypes.h
87 # ifdef __LP64__
88     typedef double CGFloat;
89 # else
90     typedef float CGFloat;
91 # endif
92 #endif
93 - (void)setAutorecalculatesContentBorderThickness:(BOOL)b forEdge:(NSRectEdge)e;
94 - (void)setContentBorderThickness:(CGFloat)b forEdge:(NSRectEdge)e;
95 @end
100 @implementation MMWindowController
102 - (id)initWithVimController:(MMVimController *)controller
104 #ifndef NSAppKitVersionNumber10_4  // needed for non-10.5 sdk
105 # define NSAppKitVersionNumber10_4 824
106 #endif
107     unsigned styleMask = NSTitledWindowMask | NSClosableWindowMask
108             | NSMiniaturizableWindowMask | NSResizableWindowMask
109             | NSUnifiedTitleAndToolbarWindowMask;
111     // Use textured background on Leopard or later (skip the 'if' on Tiger for
112     // polished metal window).
113     if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4)
114         styleMask |= NSTexturedBackgroundWindowMask;
116     // NOTE: The content rect is only used the very first time MacVim is
117     // started (or rather, when ~/Library/Preferences/org.vim.MacVim.plist does
118     // not exist).  The chosen values will put the window somewhere near the
119     // top and in the middle of a 1024x768 screen.
120     NSWindow *win = [[NSWindow alloc]
121             initWithContentRect:NSMakeRect(242,364,480,360)
122                       styleMask:styleMask
123                         backing:NSBackingStoreBuffered
124                           defer:YES];
126     if ((self = [super initWithWindow:win])) {
127         vimController = controller;
129         // Window cascading is handled by MMAppController.
130         [self setShouldCascadeWindows:NO];
132         NSView *contentView = [win contentView];
133         vimView = [[MMVimView alloc] initWithFrame:[contentView frame]
134                                      vimController:vimController];
135         [contentView addSubview:vimView];
137         // Create the tabline separator (which may be visible when the tabline
138         // is hidden).  See showTabBar: for circumstances when the separator
139         // should be hidden.
140         NSRect tabSepRect = [contentView frame];
141         tabSepRect.origin.y = NSMaxY(tabSepRect)-1;
142         tabSepRect.size.height = 1;
143         tablineSeparator = [[NSBox alloc] initWithFrame:tabSepRect];
144         
145         [tablineSeparator setBoxType:NSBoxSeparator];
146         [tablineSeparator setHidden:NO];
147         [tablineSeparator setAutoresizingMask:NSViewWidthSizable
148             | NSViewMinYMargin];
150         [contentView setAutoresizesSubviews:YES];
151         [contentView addSubview:tablineSeparator];
153         [win setDelegate:self];
154         [win setInitialFirstResponder:[vimView textView]];
155         
156         if ([win styleMask] & NSTexturedBackgroundWindowMask) {
157             // On Leopard, we want to have a textured window to have nice
158             // looking tabs. But the textured window look implies rounded
159             // corners, which looks really weird -- disable them. This is a
160             // private api, though.
161             if ([win respondsToSelector:@selector(setBottomCornerRounded:)])
162                 [win setBottomCornerRounded:NO];
164             // When the tab bar is toggled, it changes color for the fraction
165             // of a second, probably because vim sends us events in a strange
166             // order, confusing appkit's content border heuristic for a short
167             // while.  This can be worked around with these two methods.  There
168             // might be a better way, but it's good enough.
169             if ([win respondsToSelector:@selector(
170                     setAutorecalculatesContentBorderThickness:forEdge:)])
171                 [win setAutorecalculatesContentBorderThickness:NO
172                                                        forEdge:NSMaxYEdge];
173             if ([win respondsToSelector:
174                     @selector(setContentBorderThickness:forEdge:)])
175                 [win setContentBorderThickness:0 forEdge:NSMaxYEdge];
176         }
178         // Make us safe on pre-tiger OSX
179         if ([win respondsToSelector:@selector(_setContentHasShadow:)])
180             [win _setContentHasShadow:NO];
181     }
183     [win release];
185     return self;
188 - (void)dealloc
190     //NSLog(@"%@ %s", [self className], _cmd);
192     [tablineSeparator release];  tablineSeparator = nil;
193     [windowAutosaveKey release];  windowAutosaveKey = nil;
194     [vimView release];  vimView = nil;
196     [super dealloc];
199 - (NSString *)description
201     NSString *format =
202         @"%@ : setupDone=%d windowAutosaveKey=%@ vimController=%@";
203     return [NSString stringWithFormat:format,
204         [self className], setupDone, windowAutosaveKey, vimController];
207 - (MMVimController *)vimController
209     return vimController;
212 - (MMTextView *)textView
214     return [vimView textView];
217 - (MMTextStorage *)textStorage
219     return [vimView textStorage];
222 - (MMVimView *)vimView
224     return vimView;
227 - (NSString *)windowAutosaveKey
229     return windowAutosaveKey;
232 - (void)setWindowAutosaveKey:(NSString *)key
234     [windowAutosaveKey autorelease];
235     windowAutosaveKey = [key copy];
238 - (void)cleanup
240     //NSLog(@"%@ %s", [self className], _cmd);
242     if (fullscreenWindow != nil) {
243         // if we are closed while still in fullscreen, end fullscreen mode,
244         // release ourselves (because this won't happen in MMWindowController)
245         // and perform close operation on the original window
246         [self leaveFullscreen];
247     }
249     setupDone = NO;
250     vimController = nil;
252     [tablineSeparator removeFromSuperviewWithoutNeedingDisplay];
253     [vimView removeFromSuperviewWithoutNeedingDisplay];
254     [vimView cleanup];  // TODO: is this necessary?
256     // It is feasible that the user quits before the window controller is
257     // released, make sure the edit flag is cleared so no warning dialog is
258     // displayed.
259     [[self window] setDocumentEdited:NO];
261     [[self window] orderOut:self];
264 - (void)openWindow
266     [[NSApp delegate] windowControllerWillOpen:self];
268     [self addNewTabViewItem];
270     setupDone = YES;
272     [self updateResizeIncrements];
273     [self resizeWindowToFit:self];
274     [[self window] makeKeyAndOrderFront:self];
277 - (void)updateTabsWithData:(NSData *)data
279     [vimView updateTabsWithData:data];
282 - (void)selectTabWithIndex:(int)idx
284     [vimView selectTabWithIndex:idx];
287 - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols
289     //NSLog(@"setTextDimensionsWithRows:%d columns:%d", rows, cols);
291     [vimView setActualRows:rows columns:cols];
293     if (setupDone && ![vimView inLiveResize])
294         shouldUpdateWindowSize = YES;
297 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
299     [vimView createScrollbarWithIdentifier:ident type:type];
302 - (void)destroyScrollbarWithIdentifier:(long)ident
304     [vimView destroyScrollbarWithIdentifier:ident];   
305     [self checkWindowNeedsResizing];
308 - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
310     [vimView showScrollbarWithIdentifier:ident state:visible];
311     [self checkWindowNeedsResizing];
314 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
316     [vimView setScrollbarPosition:pos length:len identifier:ident];
319 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
320                     identifier:(long)ident
322     [vimView setScrollbarThumbValue:val proportion:prop identifier:ident];
325 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
327     // NOTE: This is called when the transparency changes so set the opacity
328     // flag on the window here (should be faster if the window is opaque).
329     BOOL isOpaque = [back alphaComponent] == 1.0f;
330     [[self window] setOpaque:isOpaque];
332     [vimView setDefaultColorsBackground:back foreground:fore];
335 - (void)setFont:(NSFont *)font
337     [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
338     [[vimView textStorage] setFont:font];
339     [self updateResizeIncrements];
342 - (void)setWideFont:(NSFont *)font
344     [[vimView textStorage] setWideFont:font];
347 - (void)processCommandQueueDidFinish
349     // XXX: If not in live resize and vimview's desired size differs from actual
350     // size, resize ourselves
351     if (shouldUpdateWindowSize) {
352         shouldUpdateWindowSize = NO;
353         [vimView setShouldUpdateWindowSize:NO];
354         [self resizeWindowToFit:self];
355     }
358 - (void)popupMenu:(NSMenu *)menu atRow:(int)row column:(int)col
360     if (!setupDone) return;
362     NSEvent *event;
363     if (row >= 0 && col >= 0) {
364         NSSize cellSize = [[vimView textStorage] cellSize];
365         NSPoint pt = { (col+1)*cellSize.width, (row+1)*cellSize.height };
366         pt = [[vimView textView] convertPoint:pt toView:nil];
368         event = [NSEvent mouseEventWithType:NSRightMouseDown
369                                    location:pt
370                               modifierFlags:0
371                                   timestamp:0
372                                windowNumber:[[self window] windowNumber]
373                                     context:nil
374                                 eventNumber:0
375                                  clickCount:0
376                                    pressure:1.0];
377     } else {
378         event = [[vimView textView] lastMouseDownEvent];
379     }
381     [NSMenu popUpContextMenu:menu withEvent:event forView:[vimView textView]];
384 - (void)showTabBar:(BOOL)on
386     [[vimView tabBarControl] setHidden:!on];
388     // Rules for when to show tabline separator:
389     //
390     // Tabline visible & Toolbar visible  =>  Separator visible
391     // ================================================================
392     //       NO        &        NO        =>  NO (Tiger), YES (Leopard)
393     //       NO        &       YES        =>  YES
394     //      YES        &        NO        =>  NO
395     //      YES        &       YES        =>  NO
396     //
397     // XXX: This is ignored if called while in fullscreen mode
398     if (!on) {
399         NSToolbar *toolbar = [[self window] toolbar]; 
400         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
401             [tablineSeparator setHidden:![toolbar isVisible]];
402         } else {
403             [tablineSeparator setHidden:NO];
404         }
405     } else {
406         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
407             [tablineSeparator setHidden:on];
408         } else {
409             [tablineSeparator setHidden:YES];
410         }
411     }
413     //if (setupDone)
414     //    shouldUpdateWindowSize = YES;
417 - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode
419     NSToolbar *toolbar = [[self window] toolbar];
420     if (!toolbar) return;
422     [toolbar setSizeMode:size];
423     [toolbar setDisplayMode:mode];
424     [toolbar setVisible:on];
426     // See showTabBar: for circumstances when the separator should be hidden.
427     if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
428         if (!on) {
429             [tablineSeparator setHidden:YES];
430         } else {
431             [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
432         }
433     } else {
434         // Textured windows don't have a line below there title bar, so we
435         // need the separator in this case as well. In fact, the only case
436         // where we don't need the separator is when the tab bar control
437         // is visible (because it brings its own separator).
438         [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
439     }
442 - (void)setMouseShape:(int)shape
444     // This switch should match mshape_names[] in misc2.c.
445     //
446     // TODO: Add missing cursor shapes.
447     switch (shape) {
448         case 2: [[NSCursor IBeamCursor] set]; break;
449         case 3: case 4: [[NSCursor resizeUpDownCursor] set]; break;
450         case 5: case 6: [[NSCursor resizeLeftRightCursor] set]; break;
451         case 9: [[NSCursor crosshairCursor] set]; break;
452         case 10: [[NSCursor pointingHandCursor] set]; break;
453         case 11: [[NSCursor openHandCursor] set]; break;
454         default:
455             [[NSCursor arrowCursor] set]; break;
456     }
458     // Shape 1 indicates that the mouse cursor should be hidden.
459     if (1 == shape)
460         [NSCursor setHiddenUntilMouseMoves:YES];
463 - (void)adjustLinespace:(int)linespace
465     if (vimView && [vimView textStorage]) {
466         [[vimView textStorage] setLinespace:(float)linespace];
467         shouldUpdateWindowSize = YES;
468     }
471 - (void)liveResizeWillStart
473     // Save the original title, if we haven't already.
474     if (lastSetTitle == nil) {
475         lastSetTitle = [[[self window] title] retain];
476     }
479 - (void)liveResizeDidEnd
481     if (!setupDone) return;
483     // NOTE: During live resize the window is not constrained to lie inside the
484     // screen (because we must not programmatically alter the window size
485     // during live resize or it will 'jitter'), so at the end of live resize we
486     // make sure a final SetTextDimensionsMsgID message is sent to ensure that
487     // resizeWindowToFit does get called.  For this reason and also because we
488     // want to ensure that Vim and MacVim have consistent states, this resize
489     // message is sent synchronously.  (If the states were inconsistent the
490     // text view may become too large or too small to fit the window.)
492     NSSize contentSize = [self contentSize];
494     int desiredSize[2];
495     [vimView getDesiredRows:&desiredSize[0] columns:&desiredSize[1]
496                     forSize:contentSize];
498     NSData *data = [NSData dataWithBytes:desiredSize length:2*sizeof(int)];
500     BOOL resizeOk = [vimController sendMessageNow:SetTextDimensionsMsgID
501                                              data:data
502                                           timeout:.5];
504     if (!resizeOk) {
505         // Force the window size to match the text view size otherwise Vim and
506         // MacVim will have inconsistent states.
507         [self resizeWindowToFit:self];
508     }
510     // If we saved the original title while resizing, restore it.
511     if (lastSetTitle != nil) {
512         [[self window] setTitle:lastSetTitle];
513         [lastSetTitle release];
514         lastSetTitle = nil;
515     }
518 - (void)placeViews
520     if (!setupDone) return;
522     NSRect vimViewRect;
523     vimViewRect.origin = NSMakePoint(0, 0);
524     vimViewRect.size = [vimView getDesiredRows:NULL columns:NULL
525                                        forSize:[self contentSize]];
527      // HACK! If the window does resize, then windowDidResize is called which in
528      // turn calls placeViews.  In case the computed new size of the window is
529      // no different from the current size, then we need to call placeViews
530      // manually.
531      if (NSEqualRects(vimViewRect, [vimView frame])) {
532          [vimView placeViews];
533      } else {
534          [vimView setFrame:vimViewRect];
535      }
538 - (void)enterFullscreen
540     fullscreenWindow = [[MMFullscreenWindow alloc] initWithWindow:[self window]
541                                                              view:vimView];
542     [fullscreenWindow enterFullscreen];    
543       
544     [fullscreenWindow setDelegate:self];
547 - (void)leaveFullscreen
549     [fullscreenWindow leaveFullscreen];    
550     [fullscreenWindow release];
551     fullscreenWindow = nil;
555 - (IBAction)addNewTab:(id)sender
557     [vimView addNewTab:sender];
560 - (IBAction)toggleToolbar:(id)sender
562     [vimController sendMessage:ToggleToolbarMsgID data:nil];
567 // -- NSWindow delegate ------------------------------------------------------
569 - (void)windowDidBecomeMain:(NSNotification *)notification
571     [vimController sendMessage:GotFocusMsgID data:nil];
573     if ([vimView textStorage]) {
574         NSFontManager *fontManager = [NSFontManager sharedFontManager];
575         [fontManager setSelectedFont:[[vimView textStorage] font]
576                           isMultiple:NO];
577     }
580 - (void)windowDidResignMain:(NSNotification *)notification
582     [vimController sendMessage:LostFocusMsgID data:nil];
584     if ([vimView textView])
585         [[vimView textView] hideMarkedTextField];
588 - (BOOL)windowShouldClose:(id)sender
590     [vimController sendMessage:VimShouldCloseMsgID data:nil];
591     return NO;
594 - (void)windowDidMove:(NSNotification *)notification
596     if (setupDone && windowAutosaveKey) {
597         NSRect frame = [[self window] frame];
598         NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
599         NSString *topLeftString = NSStringFromPoint(topLeft);
601         [[NSUserDefaults standardUserDefaults]
602             setObject:topLeftString forKey:windowAutosaveKey];
603     }
606 - (void)windowDidResize:(id)sender
608     if (!setupDone) return;
610     // Live resizing works as follows:
611     // VimView's size is changed immediatly, and a resize message to the
612     // remote vim instance is sent. The remote vim instance sends a
613     // "vim content size changed" right back, but in live resize mode this
614     // doesn't change the VimView (because we assume that it already has the
615     // correct size because we set the resize increments correctly). Afterward,
616     // the remote vim view sends a batch draw for the text visible in the
617     // resized text area.
619     NSSize contentSize = [self contentSize];
620     [self resizeVimViewToFitSize:contentSize];
622     NSRect frame;
623     frame.origin = NSMakePoint(0, 0);
624     frame.size = contentSize;
625     [vimView setFrame:frame];
628 - (NSRect)windowWillUseStandardFrame:(NSWindow *)win
629                         defaultFrame:(NSRect)frame
631     // HACK!  For some reason 'frame' is not always constrained to fit on the
632     // screen (e.g. it may overlap the menu bar), so first constrain it to the
633     // screen; otherwise the new frame we compute may be too large and this
634     // will mess up the display after the window resizes.
635     frame = [win constrainFrameRect:frame toScreen:[win screen]];
637     // HACK!  If the top of 'frame' is lower than the current window frame,
638     // increase 'frame' so that their tops align.  Really, 'frame' should
639     // already have its top at least as high as the current window frame, but
640     // for some reason this is not always the case.
641     // (See resizeWindowToFit: for a similar hack.)
642     NSRect cur = [win frame];
643     if (NSMaxY(cur) > NSMaxY(frame)) {
644         frame.size.height = cur.origin.y - frame.origin.y + cur.size.height;
645     }
647     frame = [self fitWindowToFrame:frame];
649     // Keep old width and horizontal position unless user clicked while the
650     // Command key is held down.
651     NSEvent *event = [NSApp currentEvent];
652     if (!([event type] == NSLeftMouseUp
653             && [event modifierFlags] & NSCommandKeyMask)) {
654         NSRect currentFrame = [win frame];
655         frame.size.width = currentFrame.size.width;
656         frame.origin.x = currentFrame.origin.x;
657     }
659     return frame;
665 // -- Services menu delegate -------------------------------------------------
667 - (id)validRequestorForSendType:(NSString *)sendType
668                      returnType:(NSString *)returnType
670     if ([sendType isEqual:NSStringPboardType]
671             && [self askBackendForStarRegister:nil])
672         return self;
674     return [super validRequestorForSendType:sendType returnType:returnType];
677 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
678                              types:(NSArray *)types
680     if (![types containsObject:NSStringPboardType])
681         return NO;
683     return [self askBackendForStarRegister:pboard];
686 @end // MMWindowController
690 @implementation MMWindowController (Private)
692 - (NSRect)contentRectForFrameRect:(NSRect)frame
694     NSRect result = [[self window] contentRectForFrameRect:frame];
695     if (![tablineSeparator isHidden])
696         --result.size.height;
697     return result;
700 - (NSRect)frameRectForContentRect:(NSRect)contentRect
702     if (![tablineSeparator isHidden])
703         ++contentRect.size.height;
704     return [[self window] frameRectForContentRect:contentRect];
707 - (NSSize)contentSize
709     return [self contentRectForFrameRect:[[self window] frame]].size;
712 - (void)resizeWindowToFit:(id)sender
714     // Makes the window large enough to contain the vim view, called after the
715     // vim view's size was changed. If the window had to become to big, the
716     // vim view is made smaller.
718     // NOTE: Be very careful when you call this method!  Do not call while
719     // processing command queue, instead set 'shouldUpdateWindowSize' to YES.
720     // The only other place it is currently called is when live resize ends.
721     // This is done to ensure that the text view and window sizes match up
722     // (they may become out of sync if a SetTextDimensionsMsgID message to the
723     // backend is dropped).
725     if (!setupDone) return;
727     // Get size of text view, adapt window size to it
728     NSWindow *win = [self window];
729     NSRect frame = [win frame];
730     NSRect contentRect = [self contentRectForFrameRect:frame];
731     NSSize newSize = [vimView desiredSizeForActualRowsAndColumns];
733     // Keep top-left corner of the window fixed when resizing.
734     contentRect.origin.y -= newSize.height - contentRect.size.height;
735     contentRect.size = newSize;
737     frame = [self frameRectForContentRect:contentRect];
738     NSRect maxFrame = [win constrainFrameRect:frame toScreen:[win screen]];
740     // HACK!  Assuming the window frame cannot already be placed too high,
741     // adjust 'maxFrame' so that it at least as high up as the current frame.
742     // The reason for doing this is that constrainFrameRect:toScreen: does not
743     // always seem to utilize as much area as possible.
744     if (NSMaxY(frame) > NSMaxY(maxFrame)) {
745         maxFrame.size.height = frame.origin.y - maxFrame.origin.y
746                 + frame.size.height;
747     }
749     if (!NSEqualRects(maxFrame, frame)) {
750         // The new window frame is too big to fit on the screen, so fit the
751         // text storage to the biggest frame which will fit on the screen.
752         //NSLog(@"Proposed window frame does not fit on the screen!");
753         frame = [self fitWindowToFrame:maxFrame];
754         [self resizeVimViewToFitSize:[self contentRectForFrameRect:frame].size];
755     }
757     // NSLog(@"%s %@", _cmd, NSStringFromRect(frame));
759     // HACK! If the window does resize, then windowDidResize is called which in
760     // turn calls placeViews.  In case the computed new size of the window is
761     // no different from the current size, then we need to call placeViews
762     // manually.
763     if (NSEqualRects(frame, [win frame])) {
764         [self placeViews];
765     } else {
766         [win setFrame:frame display:YES];
767     }
770 - (NSRect)fitWindowToFrame:(NSRect)frame
772     if (!setupDone) return frame;
774     NSRect contentRect = [self contentRectForFrameRect:frame];
775     NSSize size = [vimView getDesiredRows:NULL columns:NULL
776                                   forSize:contentRect.size];
778     // Keep top-left corner of 'frame' fixed.
779     contentRect.origin.y -= size.height - contentRect.size.height;
780     contentRect.size = size;
782     return [self frameRectForContentRect:contentRect];
785 - (void)updateResizeIncrements
787     if (!setupDone) return;
789     NSSize size = [[vimView textStorage] cellSize];
790     [[self window] setContentResizeIncrements:size];
793 - (NSTabViewItem *)addNewTabViewItem
795     return [vimView addNewTabViewItem];
798 - (IBAction)vimMenuItemAction:(id)sender
800     int tag = [sender tag];
802     NSMutableData *data = [NSMutableData data];
803     [data appendBytes:&tag length:sizeof(int)];
805     [vimController sendMessage:ExecuteMenuMsgID data:data];
808 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb
810     BOOL reply = NO;
811     id backendProxy = [vimController backendProxy];
813     if (backendProxy) {
814         @try {
815             reply = [backendProxy starRegisterToPasteboard:pb];
816         }
817         @catch (NSException *e) {
818             NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
819         }
820     }
822     return reply;
825 - (void)checkWindowNeedsResizing
827     shouldUpdateWindowSize =
828         shouldUpdateWindowSize || [vimView shouldUpdateWindowSize];
831 - (NSSize)resizeVimViewToFitSize:(NSSize)size
833     // If our optimal (rows,cols) do not match our current (rows,cols), resize
834     // ourselves and tell the Vim process to sync up.
835     int desired[2];
836     NSSize newSize = [vimView getDesiredRows:&desired[0] columns:&desired[1]
837                               forSize:size];
839     int rows, columns;
840     [vimView getActualRows:&rows columns:&columns];
842     if (desired[0] != rows || desired[1] != columns) {
843         // NSLog(@"Notify Vim that text storage dimensions changed from %dx%d "
844         //       @"to %dx%d", columns, rows, desired[0], desired[1]);
845         NSData *data = [NSData dataWithBytes:desired length:2*sizeof(int)];
847         [vimController sendMessage:SetTextDimensionsMsgID data:data];
849         // We only want to set the window title if this resize came from
850         // a live-resize, not (for example) setting 'columns' or 'lines'.
851         if ([[self textView] inLiveResize]) {
852             [[self window] setTitle:[NSString stringWithFormat:@"%dx%d",
853                     desired[1], desired[0]]];
854         }
855     }
857     return newSize;
861 @end // MMWindowController (Private)