Prepared for ATSUI renderer
[MacVim.git] / src / MacVim / MMWindowController.m
blob8071af9c0165153af29669fa1a934827667d8066
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"
27 #import "MMAtsuiTextView.h"
31 @interface MMWindowController (Private)
32 - (NSSize)contentSize;
33 - (NSRect)contentRectForFrameRect:(NSRect)frame;
34 - (NSRect)frameRectForContentRect:(NSRect)contentRect;
35 - (void)resizeWindowToFit:(id)sender;
36 - (NSRect)fitWindowToFrame:(NSRect)frame;
37 - (void)updateResizeIncrements;
38 - (NSTabViewItem *)addNewTabViewItem;
39 - (IBAction)vimMenuItemAction:(id)sender;
40 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb;
41 - (void)checkWindowNeedsResizing;
42 - (NSSize)resizeVimViewToFitSize:(NSSize)size;
43 @end
47 #if 0
48 NSString *buildMenuItemDescriptor(NSMenu *menu, NSString *tail)
50     return menu ? buildMenuItemDescriptor([menu supermenu], [[menu title]
51                     stringByAppendingString:tail])
52                 : tail;
55 NSMutableArray *buildMenuAddress(NSMenu *menu)
57     NSMutableArray *addr;
58     if (menu) {
59         addr = buildMenuAddress([menu supermenu]);
60         [addr addObject:[menu title]];
61     } else {
62         addr = [NSMutableArray array];
63     }
65     return addr;
67 #endif
69 @interface NSWindow (NSWindowPrivate)
70 // Note: This hack allows us to set content shadowing separately from
71 // the window shadow.  This is apparently what webkit and terminal do.
72 - (void)_setContentHasShadow:(BOOL)shadow; // new Tiger private method
74 // This is a private api that makes textured windows not have rounded corners.
75 // We want this on Leopard.
76 - (void)setBottomCornerRounded:(BOOL)rounded;
77 @end
79 @interface NSWindow (NSLeopardOnly)
80 // Note: These functions are Leopard-only, use -[NSObject respondsToSelector:]
81 // before calling them to make sure everything works on Tiger too.
83 #ifndef CGFLOAT_DEFINED
84     // On Leopard, CGFloat is float on 32bit and double on 64bit. On Tiger,
85     // we can't use this anyways, so it's just here to keep the compiler happy.
86     // However, when we're compiling for Tiger and running on Leopard, we
87     // might need the correct typedef, so this piece is copied from ATSTypes.h
88 # ifdef __LP64__
89     typedef double CGFloat;
90 # else
91     typedef float CGFloat;
92 # endif
93 #endif
94 - (void)setAutorecalculatesContentBorderThickness:(BOOL)b forEdge:(NSRectEdge)e;
95 - (void)setContentBorderThickness:(CGFloat)b forEdge:(NSRectEdge)e;
96 @end
101 @implementation MMWindowController
103 - (id)initWithVimController:(MMVimController *)controller
105 #ifndef NSAppKitVersionNumber10_4  // needed for non-10.5 sdk
106 # define NSAppKitVersionNumber10_4 824
107 #endif
108     unsigned styleMask = NSTitledWindowMask | NSClosableWindowMask
109             | NSMiniaturizableWindowMask | NSResizableWindowMask
110             | NSUnifiedTitleAndToolbarWindowMask;
112     // Use textured background on Leopard or later (skip the 'if' on Tiger for
113     // polished metal window).
114     if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4)
115         styleMask |= NSTexturedBackgroundWindowMask;
117     // NOTE: The content rect is only used the very first time MacVim is
118     // started (or rather, when ~/Library/Preferences/org.vim.MacVim.plist does
119     // not exist).  The chosen values will put the window somewhere near the
120     // top and in the middle of a 1024x768 screen.
121     NSWindow *win = [[NSWindow alloc]
122             initWithContentRect:NSMakeRect(242,364,480,360)
123                       styleMask:styleMask
124                         backing:NSBackingStoreBuffered
125                           defer:YES];
127     if ((self = [super initWithWindow:win])) {
128         vimController = controller;
130         // Window cascading is handled by MMAppController.
131         [self setShouldCascadeWindows:NO];
133         NSView *contentView = [win contentView];
134         vimView = [[MMVimView alloc] initWithFrame:[contentView frame]
135                                      vimController:vimController];
136         [contentView addSubview:vimView];
138         // Create the tabline separator (which may be visible when the tabline
139         // is hidden).  See showTabBar: for circumstances when the separator
140         // should be hidden.
141         NSRect tabSepRect = [contentView frame];
142         tabSepRect.origin.y = NSMaxY(tabSepRect)-1;
143         tabSepRect.size.height = 1;
144         tablineSeparator = [[NSBox alloc] initWithFrame:tabSepRect];
145         
146         [tablineSeparator setBoxType:NSBoxSeparator];
147         [tablineSeparator setHidden:NO];
148         [tablineSeparator setAutoresizingMask:NSViewWidthSizable
149             | NSViewMinYMargin];
151         [contentView setAutoresizesSubviews:YES];
152         [contentView addSubview:tablineSeparator];
154         [win setDelegate:self];
155         [win setInitialFirstResponder:[vimView textView]];
156         
157         if ([win styleMask] & NSTexturedBackgroundWindowMask) {
158             // On Leopard, we want to have a textured window to have nice
159             // looking tabs. But the textured window look implies rounded
160             // corners, which looks really weird -- disable them. This is a
161             // private api, though.
162             if ([win respondsToSelector:@selector(setBottomCornerRounded:)])
163                 [win setBottomCornerRounded:NO];
165             // When the tab bar is toggled, it changes color for the fraction
166             // of a second, probably because vim sends us events in a strange
167             // order, confusing appkit's content border heuristic for a short
168             // while.  This can be worked around with these two methods.  There
169             // might be a better way, but it's good enough.
170             if ([win respondsToSelector:@selector(
171                     setAutorecalculatesContentBorderThickness:forEdge:)])
172                 [win setAutorecalculatesContentBorderThickness:NO
173                                                        forEdge:NSMaxYEdge];
174             if ([win respondsToSelector:
175                     @selector(setContentBorderThickness:forEdge:)])
176                 [win setContentBorderThickness:0 forEdge:NSMaxYEdge];
177         }
179         // Make us safe on pre-tiger OSX
180         if ([win respondsToSelector:@selector(_setContentHasShadow:)])
181             [win _setContentHasShadow:NO];
182     }
184     [win release];
186     return self;
189 - (void)dealloc
191     //NSLog(@"%@ %s", [self className], _cmd);
193     [tablineSeparator release];  tablineSeparator = nil;
194     [windowAutosaveKey release];  windowAutosaveKey = nil;
195     [vimView release];  vimView = nil;
197     [super dealloc];
200 - (NSString *)description
202     NSString *format =
203         @"%@ : setupDone=%d windowAutosaveKey=%@ vimController=%@";
204     return [NSString stringWithFormat:format,
205         [self className], setupDone, windowAutosaveKey, vimController];
208 - (MMVimController *)vimController
210     return vimController;
213 - (MMTextView *)textView
215     return [vimView textView];
218 - (MMTextStorage *)textStorage
220     return [vimView textStorage];
223 - (MMVimView *)vimView
225     return vimView;
228 - (NSString *)windowAutosaveKey
230     return windowAutosaveKey;
233 - (void)setWindowAutosaveKey:(NSString *)key
235     [windowAutosaveKey autorelease];
236     windowAutosaveKey = [key copy];
239 - (void)cleanup
241     //NSLog(@"%@ %s", [self className], _cmd);
243     if (fullscreenWindow != nil) {
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];
248     }
250     setupDone = NO;
251     vimController = nil;
253     [tablineSeparator removeFromSuperviewWithoutNeedingDisplay];
254     [vimView removeFromSuperviewWithoutNeedingDisplay];
255     [vimView cleanup];  // TODO: is this necessary?
257     // It is feasible that the user quits before the window controller is
258     // released, make sure the edit flag is cleared so no warning dialog is
259     // displayed.
260     [[self window] setDocumentEdited:NO];
262     [[self window] orderOut:self];
265 - (void)openWindow
267     [[NSApp delegate] windowControllerWillOpen:self];
269     [self addNewTabViewItem];
271     setupDone = YES;
273     [self updateResizeIncrements];
274     [self resizeWindowToFit:self];
275     [[self window] makeKeyAndOrderFront:self];
278 - (void)updateTabsWithData:(NSData *)data
280     [vimView updateTabsWithData:data];
283 - (void)selectTabWithIndex:(int)idx
285     [vimView selectTabWithIndex:idx];
288 - (void)setTextDimensionsWithRows:(int)rows columns:(int)cols
290     //NSLog(@"setTextDimensionsWithRows:%d columns:%d", rows, cols);
292     [vimView setActualRows:rows columns:cols];
294     if (setupDone && ![vimView inLiveResize])
295         shouldUpdateWindowSize = YES;
298 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
300     [vimView createScrollbarWithIdentifier:ident type:type];
303 - (void)destroyScrollbarWithIdentifier:(long)ident
305     [vimView destroyScrollbarWithIdentifier:ident];   
306     [self checkWindowNeedsResizing];
309 - (void)showScrollbarWithIdentifier:(long)ident state:(BOOL)visible
311     [vimView showScrollbarWithIdentifier:ident state:visible];
312     [self checkWindowNeedsResizing];
315 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
317     [vimView setScrollbarPosition:pos length:len identifier:ident];
320 - (void)setScrollbarThumbValue:(float)val proportion:(float)prop
321                     identifier:(long)ident
323     [vimView setScrollbarThumbValue:val proportion:prop identifier:ident];
326 - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
328     // NOTE: This is called when the transparency changes so set the opacity
329     // flag on the window here (should be faster if the window is opaque).
330     BOOL isOpaque = [back alphaComponent] == 1.0f;
331     [[self window] setOpaque:isOpaque];
333     [vimView setDefaultColorsBackground:back foreground:fore];
336 - (void)setFont:(NSFont *)font
338     [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
339     [[vimView textStorage] setFont:font];
340     [self updateResizeIncrements];
343 - (void)setWideFont:(NSFont *)font
345     [[vimView textStorage] setWideFont:font];
348 - (void)processCommandQueueDidFinish
350     // XXX: If not in live resize and vimview's desired size differs from actual
351     // size, resize ourselves
352     if (shouldUpdateWindowSize) {
353         shouldUpdateWindowSize = NO;
354         [vimView setShouldUpdateWindowSize:NO];
355         [self resizeWindowToFit:self];
356     }
359 - (void)popupMenu:(NSMenu *)menu atRow:(int)row column:(int)col
361     if (!setupDone) return;
363     NSEvent *event;
364     if (row >= 0 && col >= 0) {
365         NSSize cellSize = [[vimView textStorage] cellSize];
366         NSPoint pt = { (col+1)*cellSize.width, (row+1)*cellSize.height };
367         pt = [[vimView textView] convertPoint:pt toView:nil];
369         event = [NSEvent mouseEventWithType:NSRightMouseDown
370                                    location:pt
371                               modifierFlags:0
372                                   timestamp:0
373                                windowNumber:[[self window] windowNumber]
374                                     context:nil
375                                 eventNumber:0
376                                  clickCount:0
377                                    pressure:1.0];
378     } else {
379         event = [[vimView textView] lastMouseDownEvent];
380     }
382     [NSMenu popUpContextMenu:menu withEvent:event forView:[vimView textView]];
385 - (void)showTabBar:(BOOL)on
387     [[vimView tabBarControl] setHidden:!on];
389     // Rules for when to show tabline separator:
390     //
391     // Tabline visible & Toolbar visible  =>  Separator visible
392     // ================================================================
393     //       NO        &        NO        =>  NO (Tiger), YES (Leopard)
394     //       NO        &       YES        =>  YES
395     //      YES        &        NO        =>  NO
396     //      YES        &       YES        =>  NO
397     //
398     // XXX: This is ignored if called while in fullscreen mode
399     if (!on) {
400         NSToolbar *toolbar = [[self window] toolbar]; 
401         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
402             [tablineSeparator setHidden:![toolbar isVisible]];
403         } else {
404             [tablineSeparator setHidden:NO];
405         }
406     } else {
407         if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
408             [tablineSeparator setHidden:on];
409         } else {
410             [tablineSeparator setHidden:YES];
411         }
412     }
414     //if (setupDone)
415     //    shouldUpdateWindowSize = YES;
418 - (void)showToolbar:(BOOL)on size:(int)size mode:(int)mode
420     NSToolbar *toolbar = [[self window] toolbar];
421     if (!toolbar) return;
423     [toolbar setSizeMode:size];
424     [toolbar setDisplayMode:mode];
425     [toolbar setVisible:on];
427     // See showTabBar: for circumstances when the separator should be hidden.
428     if (([[self window] styleMask] & NSTexturedBackgroundWindowMask) == 0) {
429         if (!on) {
430             [tablineSeparator setHidden:YES];
431         } else {
432             [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
433         }
434     } else {
435         // Textured windows don't have a line below there title bar, so we
436         // need the separator in this case as well. In fact, the only case
437         // where we don't need the separator is when the tab bar control
438         // is visible (because it brings its own separator).
439         [tablineSeparator setHidden:![[vimView tabBarControl] isHidden]];
440     }
443 - (void)setMouseShape:(int)shape
445     // This switch should match mshape_names[] in misc2.c.
446     //
447     // TODO: Add missing cursor shapes.
448     switch (shape) {
449         case 2: [[NSCursor IBeamCursor] set]; break;
450         case 3: case 4: [[NSCursor resizeUpDownCursor] set]; break;
451         case 5: case 6: [[NSCursor resizeLeftRightCursor] set]; break;
452         case 9: [[NSCursor crosshairCursor] set]; break;
453         case 10: [[NSCursor pointingHandCursor] set]; break;
454         case 11: [[NSCursor openHandCursor] set]; break;
455         default:
456             [[NSCursor arrowCursor] set]; break;
457     }
459     // Shape 1 indicates that the mouse cursor should be hidden.
460     if (1 == shape)
461         [NSCursor setHiddenUntilMouseMoves:YES];
464 - (void)adjustLinespace:(int)linespace
466     if (vimView && [vimView textStorage]) {
467         [[vimView textStorage] setLinespace:(float)linespace];
468         shouldUpdateWindowSize = YES;
469     }
472 - (void)liveResizeWillStart
474     // Save the original title, if we haven't already.
475     if (lastSetTitle == nil) {
476         lastSetTitle = [[[self window] title] retain];
477     }
480 - (void)liveResizeDidEnd
482     if (!setupDone) return;
484     // NOTE: During live resize the window is not constrained to lie inside the
485     // screen (because we must not programmatically alter the window size
486     // during live resize or it will 'jitter'), so at the end of live resize we
487     // make sure a final SetTextDimensionsMsgID message is sent to ensure that
488     // resizeWindowToFit does get called.  For this reason and also because we
489     // want to ensure that Vim and MacVim have consistent states, this resize
490     // message is sent synchronously.  (If the states were inconsistent the
491     // text view may become too large or too small to fit the window.)
493     NSSize contentSize = [self contentSize];
495     int desiredSize[2];
496     [vimView getDesiredRows:&desiredSize[0] columns:&desiredSize[1]
497                     forSize:contentSize];
499     NSData *data = [NSData dataWithBytes:desiredSize length:2*sizeof(int)];
501     BOOL resizeOk = [vimController sendMessageNow:SetTextDimensionsMsgID
502                                              data:data
503                                           timeout:.5];
505     if (!resizeOk) {
506         // Force the window size to match the text view size otherwise Vim and
507         // MacVim will have inconsistent states.
508         [self resizeWindowToFit:self];
509     }
511     // If we saved the original title while resizing, restore it.
512     if (lastSetTitle != nil) {
513         [[self window] setTitle:lastSetTitle];
514         [lastSetTitle release];
515         lastSetTitle = nil;
516     }
519 - (void)placeViews
521     if (!setupDone) return;
523     NSRect vimViewRect;
524     vimViewRect.origin = NSMakePoint(0, 0);
525     vimViewRect.size = [vimView getDesiredRows:NULL columns:NULL
526                                        forSize:[self contentSize]];
528      // HACK! If the window does resize, then windowDidResize is called which in
529      // turn calls placeViews.  In case the computed new size of the window is
530      // no different from the current size, then we need to call placeViews
531      // manually.
532      if (NSEqualRects(vimViewRect, [vimView frame])) {
533          [vimView placeViews];
534      } else {
535          [vimView setFrame:vimViewRect];
536      }
539 - (void)enterFullscreen
541     fullscreenWindow = [[MMFullscreenWindow alloc] initWithWindow:[self window]
542                                                              view:vimView];
543     [fullscreenWindow enterFullscreen];    
544       
545     [fullscreenWindow setDelegate:self];
548 - (void)leaveFullscreen
550     [fullscreenWindow leaveFullscreen];    
551     [fullscreenWindow release];
552     fullscreenWindow = nil;
556 - (IBAction)addNewTab:(id)sender
558     [vimView addNewTab:sender];
561 - (IBAction)toggleToolbar:(id)sender
563     [vimController sendMessage:ToggleToolbarMsgID data:nil];
568 // -- NSWindow delegate ------------------------------------------------------
570 - (void)windowDidBecomeMain:(NSNotification *)notification
572     [vimController sendMessage:GotFocusMsgID data:nil];
574     if ([vimView textStorage]) {
575         NSFontManager *fontManager = [NSFontManager sharedFontManager];
576         [fontManager setSelectedFont:[[vimView textStorage] font]
577                           isMultiple:NO];
578     }
581 - (void)windowDidResignMain:(NSNotification *)notification
583     [vimController sendMessage:LostFocusMsgID data:nil];
585     if ([vimView textView])
586         [[vimView textView] hideMarkedTextField];
589 - (BOOL)windowShouldClose:(id)sender
591     [vimController sendMessage:VimShouldCloseMsgID data:nil];
592     return NO;
595 - (void)windowDidMove:(NSNotification *)notification
597     if (setupDone && windowAutosaveKey) {
598         NSRect frame = [[self window] frame];
599         NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
600         NSString *topLeftString = NSStringFromPoint(topLeft);
602         [[NSUserDefaults standardUserDefaults]
603             setObject:topLeftString forKey:windowAutosaveKey];
604     }
607 - (void)windowDidResize:(id)sender
609     if (!setupDone) return;
611     // Live resizing works as follows:
612     // VimView's size is changed immediatly, and a resize message to the
613     // remote vim instance is sent. The remote vim instance sends a
614     // "vim content size changed" right back, but in live resize mode this
615     // doesn't change the VimView (because we assume that it already has the
616     // correct size because we set the resize increments correctly). Afterward,
617     // the remote vim view sends a batch draw for the text visible in the
618     // resized text area.
620     NSSize contentSize = [self contentSize];
621     [self resizeVimViewToFitSize:contentSize];
623     NSRect frame;
624     frame.origin = NSMakePoint(0, 0);
625     frame.size = contentSize;
626     [vimView setFrame:frame];
629 - (NSRect)windowWillUseStandardFrame:(NSWindow *)win
630                         defaultFrame:(NSRect)frame
632     // HACK!  For some reason 'frame' is not always constrained to fit on the
633     // screen (e.g. it may overlap the menu bar), so first constrain it to the
634     // screen; otherwise the new frame we compute may be too large and this
635     // will mess up the display after the window resizes.
636     frame = [win constrainFrameRect:frame toScreen:[win screen]];
638     // HACK!  If the top of 'frame' is lower than the current window frame,
639     // increase 'frame' so that their tops align.  Really, 'frame' should
640     // already have its top at least as high as the current window frame, but
641     // for some reason this is not always the case.
642     // (See resizeWindowToFit: for a similar hack.)
643     NSRect cur = [win frame];
644     if (NSMaxY(cur) > NSMaxY(frame)) {
645         frame.size.height = cur.origin.y - frame.origin.y + cur.size.height;
646     }
648     frame = [self fitWindowToFrame:frame];
650     // Keep old width and horizontal position unless user clicked while the
651     // Command key is held down.
652     NSEvent *event = [NSApp currentEvent];
653     if (!([event type] == NSLeftMouseUp
654             && [event modifierFlags] & NSCommandKeyMask)) {
655         NSRect currentFrame = [win frame];
656         frame.size.width = currentFrame.size.width;
657         frame.origin.x = currentFrame.origin.x;
658     }
660     return frame;
666 // -- Services menu delegate -------------------------------------------------
668 - (id)validRequestorForSendType:(NSString *)sendType
669                      returnType:(NSString *)returnType
671     if ([sendType isEqual:NSStringPboardType]
672             && [self askBackendForStarRegister:nil])
673         return self;
675     return [super validRequestorForSendType:sendType returnType:returnType];
678 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
679                              types:(NSArray *)types
681     if (![types containsObject:NSStringPboardType])
682         return NO;
684     return [self askBackendForStarRegister:pboard];
687 @end // MMWindowController
691 @implementation MMWindowController (Private)
693 - (NSRect)contentRectForFrameRect:(NSRect)frame
695     NSRect result = [[self window] contentRectForFrameRect:frame];
696     if (![tablineSeparator isHidden])
697         --result.size.height;
698     return result;
701 - (NSRect)frameRectForContentRect:(NSRect)contentRect
703     if (![tablineSeparator isHidden])
704         ++contentRect.size.height;
705     return [[self window] frameRectForContentRect:contentRect];
708 - (NSSize)contentSize
710     return [self contentRectForFrameRect:[[self window] frame]].size;
713 - (void)resizeWindowToFit:(id)sender
715     // Makes the window large enough to contain the vim view, called after the
716     // vim view's size was changed. If the window had to become to big, the
717     // vim view is made smaller.
719     // NOTE: Be very careful when you call this method!  Do not call while
720     // processing command queue, instead set 'shouldUpdateWindowSize' to YES.
721     // The only other place it is currently called is when live resize ends.
722     // This is done to ensure that the text view and window sizes match up
723     // (they may become out of sync if a SetTextDimensionsMsgID message to the
724     // backend is dropped).
726     if (!setupDone) return;
728     // Get size of text view, adapt window size to it
729     NSWindow *win = [self window];
730     NSRect frame = [win frame];
731     NSRect contentRect = [self contentRectForFrameRect:frame];
732     NSSize newSize = [vimView desiredSizeForActualRowsAndColumns];
734     // Keep top-left corner of the window fixed when resizing.
735     contentRect.origin.y -= newSize.height - contentRect.size.height;
736     contentRect.size = newSize;
738     frame = [self frameRectForContentRect:contentRect];
739     NSRect maxFrame = [win constrainFrameRect:frame toScreen:[win screen]];
741     // HACK!  Assuming the window frame cannot already be placed too high,
742     // adjust 'maxFrame' so that it at least as high up as the current frame.
743     // The reason for doing this is that constrainFrameRect:toScreen: does not
744     // always seem to utilize as much area as possible.
745     if (NSMaxY(frame) > NSMaxY(maxFrame)) {
746         maxFrame.size.height = frame.origin.y - maxFrame.origin.y
747                 + frame.size.height;
748     }
750     if (!NSEqualRects(maxFrame, frame)) {
751         // The new window frame is too big to fit on the screen, so fit the
752         // text storage to the biggest frame which will fit on the screen.
753         //NSLog(@"Proposed window frame does not fit on the screen!");
754         frame = [self fitWindowToFrame:maxFrame];
755         [self resizeVimViewToFitSize:[self contentRectForFrameRect:frame].size];
756     }
758     // NSLog(@"%s %@", _cmd, NSStringFromRect(frame));
760     // HACK! If the window does resize, then windowDidResize is called which in
761     // turn calls placeViews.  In case the computed new size of the window is
762     // no different from the current size, then we need to call placeViews
763     // manually.
764     if (NSEqualRects(frame, [win frame])) {
765         [self placeViews];
766     } else {
767         [win setFrame:frame display:YES];
768     }
771 - (NSRect)fitWindowToFrame:(NSRect)frame
773     if (!setupDone) return frame;
775     NSRect contentRect = [self contentRectForFrameRect:frame];
776     NSSize size = [vimView getDesiredRows:NULL columns:NULL
777                                   forSize:contentRect.size];
779     // Keep top-left corner of 'frame' fixed.
780     contentRect.origin.y -= size.height - contentRect.size.height;
781     contentRect.size = size;
783     return [self frameRectForContentRect:contentRect];
786 - (void)updateResizeIncrements
788     if (!setupDone) return;
790     NSSize size = [[vimView textStorage] cellSize];
791     [[self window] setContentResizeIncrements:size];
794 - (NSTabViewItem *)addNewTabViewItem
796     return [vimView addNewTabViewItem];
799 - (IBAction)vimMenuItemAction:(id)sender
801     int tag = [sender tag];
803     NSMutableData *data = [NSMutableData data];
804     [data appendBytes:&tag length:sizeof(int)];
806     [vimController sendMessage:ExecuteMenuMsgID data:data];
809 - (BOOL)askBackendForStarRegister:(NSPasteboard *)pb
811     BOOL reply = NO;
812     id backendProxy = [vimController backendProxy];
814     if (backendProxy) {
815         @try {
816             reply = [backendProxy starRegisterToPasteboard:pb];
817         }
818         @catch (NSException *e) {
819             NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
820         }
821     }
823     return reply;
826 - (void)checkWindowNeedsResizing
828     shouldUpdateWindowSize =
829         shouldUpdateWindowSize || [vimView shouldUpdateWindowSize];
832 - (NSSize)resizeVimViewToFitSize:(NSSize)size
834     // If our optimal (rows,cols) do not match our current (rows,cols), resize
835     // ourselves and tell the Vim process to sync up.
836     int desired[2];
837     NSSize newSize = [vimView getDesiredRows:&desired[0] columns:&desired[1]
838                               forSize:size];
840     int rows, columns;
841     [vimView getActualRows:&rows columns:&columns];
843     if (desired[0] != rows || desired[1] != columns) {
844         // NSLog(@"Notify Vim that text storage dimensions changed from %dx%d "
845         //       @"to %dx%d", columns, rows, desired[0], desired[1]);
846         NSData *data = [NSData dataWithBytes:desired length:2*sizeof(int)];
848         [vimController sendMessage:SetTextDimensionsMsgID data:data];
850         // We only want to set the window title if this resize came from
851         // a live-resize, not (for example) setting 'columns' or 'lines'.
852         if ([[self textView] inLiveResize]) {
853             [[self window] setTitle:[NSString stringWithFormat:@"%dx%d",
854                     desired[1], desired[0]]];
855         }
856     }
858     return newSize;
862 @end // MMWindowController (Private)