Fix memory leak in window controller
[MacVim.git] / src / MacVim / MMWindowController.m
blob0228c8a2479738e34e1326b56e192a919cdc01f7
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     [[self window] orderOut:self];
259 - (void)openWindow
261     [[NSApp delegate] windowControllerWillOpen:self];
263     [self addNewTabViewItem];
265     setupDone = YES;
267     [self updateResizeIncrements];
268     [self resizeWindowToFit:self];
269     [[self window] makeKeyAndOrderFront:self];
272 - (void)updateTabsWithData:(NSData *)data
274     [vimView updateTabsWithData:data];
277 - (void)selectTabWithIndex:(int)idx
279     [vimView selectTabWithIndex:idx];
282 - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols
284     //NSLog(@"setTextDimensionsWithRows:%d columns:%d", rows, cols);
286     [vimView setActualRows:rows columns:cols];
288     if (setupDone && ![vimView inLiveResize])
289         shouldUpdateWindowSize = YES;
292 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
294     [vimView createScrollbarWithIdentifier:ident type:type];
297 - (void)destroyScrollbarWithIdentifier:(long)ident
299     [vimView destroyScrollbarWithIdentifier:ident];   
300     [self checkWindowNeedsResizing];
303 - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
305     [vimView showScrollbarWithIdentifier:ident state:visible];
306     [self checkWindowNeedsResizing];
309 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
311     [vimView setScrollbarPosition:pos length:len identifier:ident];
314 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
315                     identifier:(long)ident
317     [vimView setScrollbarThumbValue:val proportion:prop identifier:ident];
320 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
322     // NOTE: This is called when the transparency changes so set the opacity
323     // flag on the window here (should be faster if the window is opaque).
324     BOOL isOpaque = [back alphaComponent] == 1.0f;
325     [[self window] setOpaque:isOpaque];
327     [vimView setDefaultColorsBackground:back foreground:fore];
330 - (void)setFont:(NSFont *)font
332     [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
333     [[vimView textStorage] setFont:font];
334     [self updateResizeIncrements];
337 - (void)setWideFont:(NSFont *)font
339     [[vimView textStorage] setWideFont:font];
342 - (void)processCommandQueueDidFinish
344     // XXX: If not in live resize and vimview's desired size differs from actual
345     // size, resize ourselves
346     if (shouldUpdateWindowSize) {
347         shouldUpdateWindowSize = NO;
348         [vimView setShouldUpdateWindowSize:NO];
349         [self resizeWindowToFit:self];
350     }
353 - (void)popupMenu:(NSMenu *)menu atRow:(int)row column:(int)col
355     if (!setupDone) return;
357     NSEvent *event;
358     if (row >= 0 && col >= 0) {
359         NSSize cellSize = [[vimView textStorage] cellSize];
360         NSPoint pt = { (col+1)*cellSize.width, (row+1)*cellSize.height };
361         pt = [[vimView textView] convertPoint:pt toView:nil];
363         event = [NSEvent mouseEventWithType:NSRightMouseDown
364                                    location:pt
365                               modifierFlags:0
366                                   timestamp:0
367                                windowNumber:[[self window] windowNumber]
368                                     context:nil
369                                 eventNumber:0
370                                  clickCount:0
371                                    pressure:1.0];
372     } else {
373         event = [[vimView textView] lastMouseDownEvent];
374     }
376     [NSMenu popUpContextMenu:menu withEvent:event forView:[vimView textView]];
379 - (void)showTabBar:(BOOL)on
381     [[vimView tabBarControl] setHidden:!on];
383     // Rules for when to show tabline separator:
384     //
385     // Tabline visible & Toolbar visible  =>  Separator visible
386     // ================================================================
387     //       NO        &        NO        =>  NO (Tiger), YES (Leopard)
388     //       NO        &       YES        =>  YES
389     //      YES        &        NO        =>  NO
390     //      YES        &       YES        =>  NO
391     //
392     // XXX: This is ignored if called while in fullscreen mode
393     if (!on) {
394         NSToolbar *toolbar = [[self window] toolbar]; 
395         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
396             [tablineSeparator setHidden:![toolbar isVisible]];
397         } else {
398             [tablineSeparator setHidden:NO];
399         }
400     } else {
401         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
402             [tablineSeparator setHidden:on];
403         } else {
404             [tablineSeparator setHidden:YES];
405         }
406     }
408     //if (setupDone)
409     //    shouldUpdateWindowSize = YES;
412 - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode
414     NSToolbar *toolbar = [[self window] toolbar];
415     if (!toolbar) return;
417     [toolbar setSizeMode:size];
418     [toolbar setDisplayMode:mode];
419     [toolbar setVisible:on];
421     // See showTabBar: for circumstances when the separator should be hidden.
422     if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
423         if (!on) {
424             [tablineSeparator setHidden:YES];
425         } else {
426             [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
427         }
428     } else {
429         // Textured windows don't have a line below there title bar, so we
430         // need the separator in this case as well. In fact, the only case
431         // where we don't need the separator is when the tab bar control
432         // is visible (because it brings its own separator).
433         [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
434     }
437 - (void)setMouseShape:(int)shape
439     // This switch should match mshape_names[] in misc2.c.
440     //
441     // TODO: Add missing cursor shapes.
442     switch (shape) {
443         case 2: [[NSCursor IBeamCursor] set]; break;
444         case 3: case 4: [[NSCursor resizeUpDownCursor] set]; break;
445         case 5: case 6: [[NSCursor resizeLeftRightCursor] set]; break;
446         case 9: [[NSCursor crosshairCursor] set]; break;
447         case 10: [[NSCursor pointingHandCursor] set]; break;
448         case 11: [[NSCursor openHandCursor] set]; break;
449         default:
450             [[NSCursor arrowCursor] set]; break;
451     }
453     // Shape 1 indicates that the mouse cursor should be hidden.
454     if (1 == shape)
455         [NSCursor setHiddenUntilMouseMoves:YES];
458 - (void)adjustLinespace:(int)linespace
460     if (vimView && [vimView textStorage]) {
461         [[vimView textStorage] setLinespace:(float)linespace];
462         shouldUpdateWindowSize = YES;
463     }
466 - (void)liveResizeWillStart
468     // Save the original title, if we haven't already.
469     if (lastSetTitle == nil) {
470         lastSetTitle = [[[self window] title] retain];
471     }
474 - (void)liveResizeDidEnd
476     if (!setupDone) return;
478     // NOTE: During live resize the window is not constrained to lie inside the
479     // screen (because we must not programmatically alter the window size
480     // during live resize or it will 'jitter'), so at the end of live resize we
481     // make sure a final SetTextDimensionsMsgID message is sent to ensure that
482     // resizeWindowToFit does get called.  For this reason and also because we
483     // want to ensure that Vim and MacVim have consistent states, this resize
484     // message is sent synchronously.  (If the states were inconsistent the
485     // text view may become too large or too small to fit the window.)
487     NSSize contentSize = [self contentSize];
489     int desiredSize[2];
490     [vimView getDesiredRows:&desiredSize[0] columns:&desiredSize[1]
491                     forSize:contentSize];
493     NSData *data = [NSData dataWithBytes:desiredSize length:2*sizeof(int)];
495     BOOL resizeOk = [vimController sendMessageNow:SetTextDimensionsMsgID
496                                              data:data
497                                           timeout:.5];
499     if (!resizeOk) {
500         // Force the window size to match the text view size otherwise Vim and
501         // MacVim will have inconsistent states.
502         [self resizeWindowToFit:self];
503     }
505     // If we saved the original title while resizing, restore it.
506     if (lastSetTitle != nil) {
507         [[self window] setTitle:lastSetTitle];
508         [lastSetTitle release];
509         lastSetTitle = nil;
510     }
513 - (void)placeViews
515     if (!setupDone) return;
517     NSRect vimViewRect;
518     vimViewRect.origin = NSMakePoint(0, 0);
519     vimViewRect.size = [vimView getDesiredRows:NULL columns:NULL
520                                        forSize:[self contentSize]];
522      // HACK! If the window does resize, then windowDidResize is called which in
523      // turn calls placeViews.  In case the computed new size of the window is
524      // no different from the current size, then we need to call placeViews
525      // manually.
526      if (NSEqualRects(vimViewRect, [vimView frame])) {
527          [vimView placeViews];
528      } else {
529          [vimView setFrame:vimViewRect];
530      }
533 - (void)enterFullscreen
535     fullscreenWindow = [[MMFullscreenWindow alloc] initWithWindow:[self window]
536                                                              view:vimView];
537     [fullscreenWindow enterFullscreen];    
538       
539     [fullscreenWindow setDelegate:self];
542 - (void)leaveFullscreen
544     [fullscreenWindow leaveFullscreen];    
545     [fullscreenWindow release];
546     fullscreenWindow = nil;
550 - (IBAction)addNewTab:(id)sender
552     [vimView addNewTab:sender];
555 - (IBAction)toggleToolbar:(id)sender
557     [vimController sendMessage:ToggleToolbarMsgID data:nil];
562 // -- NSWindow delegate ------------------------------------------------------
564 - (void)windowDidBecomeMain:(NSNotification *)notification
566     [vimController sendMessage:GotFocusMsgID data:nil];
568     if ([vimView textStorage]) {
569         NSFontManager *fontManager = [NSFontManager sharedFontManager];
570         [fontManager setSelectedFont:[[vimView textStorage] font]
571                           isMultiple:NO];
572     }
575 - (void)windowDidResignMain:(NSNotification *)notification
577     [vimController sendMessage:LostFocusMsgID data:nil];
579     if ([vimView textView])
580         [[vimView textView] hideMarkedTextField];
583 - (BOOL)windowShouldClose:(id)sender
585     [vimController sendMessage:VimShouldCloseMsgID data:nil];
586     return NO;
589 - (void)windowDidMove:(NSNotification *)notification
591     if (setupDone && windowAutosaveKey) {
592         NSRect frame = [[self window] frame];
593         NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
594         NSString *topLeftString = NSStringFromPoint(topLeft);
596         [[NSUserDefaults standardUserDefaults]
597             setObject:topLeftString forKey:windowAutosaveKey];
598     }
601 - (void)windowDidResize:(id)sender
603     if (!setupDone) return;
605     // Live resizing works as follows:
606     // VimView's size is changed immediatly, and a resize message to the
607     // remote vim instance is sent. The remote vim instance sends a
608     // "vim content size changed" right back, but in live resize mode this
609     // doesn't change the VimView (because we assume that it already has the
610     // correct size because we set the resize increments correctly). Afterward,
611     // the remote vim view sends a batch draw for the text visible in the
612     // resized text area.
614     NSSize contentSize = [self contentSize];
615     [self resizeVimViewToFitSize:contentSize];
617     NSRect frame;
618     frame.origin = NSMakePoint(0, 0);
619     frame.size = contentSize;
620     [vimView setFrame:frame];
623 - (NSRect)windowWillUseStandardFrame:(NSWindow *)win
624                         defaultFrame:(NSRect)frame
626     // HACK!  For some reason 'frame' is not always constrained to fit on the
627     // screen (e.g. it may overlap the menu bar), so first constrain it to the
628     // screen; otherwise the new frame we compute may be too large and this
629     // will mess up the display after the window resizes.
630     frame = [win constrainFrameRect:frame toScreen:[win screen]];
632     // HACK!  If the top of 'frame' is lower than the current window frame,
633     // increase 'frame' so that their tops align.  Really, 'frame' should
634     // already have its top at least as high as the current window frame, but
635     // for some reason this is not always the case.
636     // (See resizeWindowToFit: for a similar hack.)
637     NSRect cur = [win frame];
638     if (NSMaxY(cur) > NSMaxY(frame)) {
639         frame.size.height = cur.origin.y - frame.origin.y + cur.size.height;
640     }
642     frame = [self fitWindowToFrame:frame];
644     // Keep old width and horizontal position unless user clicked while the
645     // Command key is held down.
646     NSEvent *event = [NSApp currentEvent];
647     if (!([event type] == NSLeftMouseUp
648             && [event modifierFlags] & NSCommandKeyMask)) {
649         NSRect currentFrame = [win frame];
650         frame.size.width = currentFrame.size.width;
651         frame.origin.x = currentFrame.origin.x;
652     }
654     return frame;
660 // -- Services menu delegate -------------------------------------------------
662 - (id)validRequestorForSendType:(NSString *)sendType
663                      returnType:(NSString *)returnType
665     if ([sendType isEqual:NSStringPboardType]
666             && [self askBackendForStarRegister:nil])
667         return self;
669     return [super validRequestorForSendType:sendType returnType:returnType];
672 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
673                              types:(NSArray *)types
675     if (![types containsObject:NSStringPboardType])
676         return NO;
678     return [self askBackendForStarRegister:pboard];
681 @end // MMWindowController
685 @implementation MMWindowController (Private)
687 - (NSRect)contentRectForFrameRect:(NSRect)frame
689     NSRect result = [[self window] contentRectForFrameRect:frame];
690     if (![tablineSeparator isHidden])
691         --result.size.height;
692     return result;
695 - (NSRect)frameRectForContentRect:(NSRect)contentRect
697     if (![tablineSeparator isHidden])
698         ++contentRect.size.height;
699     return [[self window] frameRectForContentRect:contentRect];
702 - (NSSize)contentSize
704     return [self contentRectForFrameRect:[[self window] frame]].size;
707 - (void)resizeWindowToFit:(id)sender
709     // Makes the window large enough to contain the vim view, called after the
710     // vim view's size was changed. If the window had to become to big, the
711     // vim view is made smaller.
713     // NOTE: Be very careful when you call this method!  Do not call while
714     // processing command queue, instead set 'shouldUpdateWindowSize' to YES.
715     // The only other place it is currently called is when live resize ends.
716     // This is done to ensure that the text view and window sizes match up
717     // (they may become out of sync if a SetTextDimensionsMsgID message to the
718     // backend is dropped).
720     if (!setupDone) return;
722     // Get size of text view, adapt window size to it
723     NSWindow *win = [self window];
724     NSRect frame = [win frame];
725     NSRect contentRect = [self contentRectForFrameRect:frame];
726     NSSize newSize = [vimView desiredSizeForActualRowsAndColumns];
728     // Keep top-left corner of the window fixed when resizing.
729     contentRect.origin.y -= newSize.height - contentRect.size.height;
730     contentRect.size = newSize;
732     frame = [self frameRectForContentRect:contentRect];
733     NSRect maxFrame = [win constrainFrameRect:frame toScreen:[win screen]];
735     // HACK!  Assuming the window frame cannot already be placed too high,
736     // adjust 'maxFrame' so that it at least as high up as the current frame.
737     // The reason for doing this is that constrainFrameRect:toScreen: does not
738     // always seem to utilize as much area as possible.
739     if (NSMaxY(frame) > NSMaxY(maxFrame)) {
740         maxFrame.size.height = frame.origin.y - maxFrame.origin.y
741                 + frame.size.height;
742     }
744     if (!NSEqualRects(maxFrame, frame)) {
745         // The new window frame is too big to fit on the screen, so fit the
746         // text storage to the biggest frame which will fit on the screen.
747         //NSLog(@"Proposed window frame does not fit on the screen!");
748         frame = [self fitWindowToFrame:maxFrame];
749         [self resizeVimViewToFitSize:[self contentRectForFrameRect:frame].size];
750     }
752     // NSLog(@"%s %@", _cmd, NSStringFromRect(frame));
754     // HACK! If the window does resize, then windowDidResize is called which in
755     // turn calls placeViews.  In case the computed new size of the window is
756     // no different from the current size, then we need to call placeViews
757     // manually.
758     if (NSEqualRects(frame, [win frame])) {
759         [self placeViews];
760     } else {
761         [win setFrame:frame display:YES];
762     }
765 - (NSRect)fitWindowToFrame:(NSRect)frame
767     if (!setupDone) return frame;
769     NSRect contentRect = [self contentRectForFrameRect:frame];
770     NSSize size = [vimView getDesiredRows:NULL columns:NULL
771                                   forSize:contentRect.size];
773     // Keep top-left corner of 'frame' fixed.
774     contentRect.origin.y -= size.height - contentRect.size.height;
775     contentRect.size = size;
777     return [self frameRectForContentRect:contentRect];
780 - (void)updateResizeIncrements
782     if (!setupDone) return;
784     NSSize size = [[vimView textStorage] cellSize];
785     [[self window] setContentResizeIncrements:size];
788 - (NSTabViewItem *)addNewTabViewItem
790     return [vimView addNewTabViewItem];
793 - (IBAction)vimMenuItemAction:(id)sender
795     int tag = [sender tag];
797     NSMutableData *data = [NSMutableData data];
798     [data appendBytes:&tag length:sizeof(int)];
800     [vimController sendMessage:ExecuteMenuMsgID data:data];
803 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb
805     BOOL reply = NO;
806     id backendProxy = [vimController backendProxy];
808     if (backendProxy) {
809         @try {
810             reply = [backendProxy starRegisterToPasteboard:pb];
811         }
812         @catch (NSException *e) {
813             NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
814         }
815     }
817     return reply;
820 - (void)checkWindowNeedsResizing
822     shouldUpdateWindowSize =
823         shouldUpdateWindowSize || [vimView shouldUpdateWindowSize];
826 - (NSSize)resizeVimViewToFitSize:(NSSize)size
828     // If our optimal (rows,cols) do not match our current (rows,cols), resize
829     // ourselves and tell the Vim process to sync up.
830     int desired[2];
831     NSSize newSize = [vimView getDesiredRows:&desired[0] columns:&desired[1]
832                               forSize:size];
834     int rows, columns;
835     [vimView getActualRows:&rows columns:&columns];
837     if (desired[0] != rows || desired[1] != columns) {
838         // NSLog(@"Notify Vim that text storage dimensions changed from %dx%d "
839         //       @"to %dx%d", columns, rows, desired[0], desired[1]);
840         NSData *data = [NSData dataWithBytes:desired length:2*sizeof(int)];
842         [vimController sendMessage:SetTextDimensionsMsgID data:data];
844         // We only want to set the window title if this resize came from
845         // a live-resize, not (for example) setting 'columns' or 'lines'.
846         if ([[self textView] inLiveResize]) {
847             [[self window] setTitle:[NSString stringWithFormat:@"%dx%d",
848                     desired[1], desired[0]]];
849         }
850     }
852     return newSize;
856 @end // MMWindowController (Private)