Add support for :winpos
authorBjorn Winckler <bjorn.winckler@gmail.com>
Fri, 19 Feb 2010 23:40:23 +0000 (20 00:40 +0100)
committerBjorn Winckler <bjorn.winckler@gmail.com>
Sat, 20 Feb 2010 01:05:15 +0000 (20 02:05 +0100)
Note that window coordinates are specified in a coordinate system where
X points to the right and Y points upwards.

src/MacVim/MMAppController.m
src/MacVim/MMBackend.h
src/MacVim/MMBackend.m
src/MacVim/MMVimController.m
src/MacVim/MMWindowController.h
src/MacVim/MMWindowController.m
src/MacVim/MacVim.h
src/MacVim/MacVim.m
src/MacVim/gui_macvim.m

index 8b7dac9..5f2203b 100644 (file)
@@ -743,15 +743,23 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
 - (void)windowControllerWillOpen:(MMWindowController *)windowController
 {
     NSPoint topLeft = NSZeroPoint;
-    NSWindow *topWin = [[[self topmostVimController] windowController] window];
+    NSWindow *cascadeFrom = [[[self topmostVimController] windowController]
+                                                                    window];
     NSWindow *win = [windowController window];
 
     if (!win) return;
 
-    // If there is a window belonging to a Vim process, cascade from it,
-    // otherwise use the autosaved window position (if any).
-    if (topWin) {
-        NSRect frame = [topWin frame];
+    // Heuristic to determine where to position the window:
+    //   1. Use the default top left position (set using :winpos in .[g]vimrc)
+    //   2. Cascade from an existing window
+    //   3. Use autosaved position
+    // If all of the above fail, then the window position is not changed.
+    if ([windowController getDefaultTopLeft:&topLeft]) {
+        // Make sure the window is not cascaded (note that topLeft was set in
+        // the above call).
+        cascadeFrom = nil;
+    } else if (cascadeFrom) {
+        NSRect frame = [cascadeFrom frame];
         topLeft = NSMakePoint(frame.origin.x, NSMaxY(frame));
     } else {
         NSString *topLeftString = [[NSUserDefaults standardUserDefaults]
@@ -767,7 +775,7 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
         if (!screen)
             screen = [win screen];
 
-        if (topWin) {
+        if (cascadeFrom) {
             // Do manual cascading instead of using
             // -[MMWindow cascadeTopLeftFromPoint:] since it is rather
             // unpredictable.
index f4adbfa..ac9499c 100644 (file)
@@ -53,6 +53,8 @@
     BOOL                imState;
     CFSocketRef         netbeansSocket;
     CFRunLoopSourceRef  netbeansRunLoopSource;
+    int                 winposX;
+    int                 winposY;
 }
 
 + (MMBackend *)sharedInstance;
@@ -64,6 +66,8 @@
 - (NSConnection *)connection;
 - (NSDictionary *)actionDict;
 - (int)initialWindowLayout;
+- (void)getWindowPositionX:(int*)x Y:(int*)y;
+- (void)setWindowPositionX:(int)x Y:(int)y;
 
 - (void)queueMessage:(int)msgid properties:(NSDictionary *)props;
 - (BOOL)checkin;
index 4c6291e..bf5b58a 100644 (file)
@@ -326,6 +326,24 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
     return initialWindowLayout;
 }
 
+- (void)getWindowPositionX:(int*)x Y:(int*)y
+{
+    // NOTE: winposX and winposY are set by the SetWindowPositionMsgID message.
+    if (x) *x = winposX;
+    if (y) *y = winposY;
+}
+
+- (void)setWindowPositionX:(int)x Y:(int)y
+{
+    // NOTE: Setting the window position has no immediate effect on the cached
+    // variables winposX and winposY.  These are set by the frontend when the
+    // window actually moves (see SetWindowPositionMsgID).
+    ASLogDebug(@"x=%d y=%d", x, y);
+    int pos[2] = { x, y };
+    NSData *data = [NSData dataWithBytes:pos length:2*sizeof(int)];
+    [self queueMessage:SetWindowPositionMsgID data:data];
+}
+
 - (void)queueMessage:(int)msgid properties:(NSDictionary *)props
 {
     [self queueMessage:msgid data:[props dictionaryAsData]];
@@ -440,7 +458,21 @@ extern GuiFont gui_mch_retain_font(GuiFont font);
 
 - (BOOL)openGUIWindow
 {
+    if (gui_win_x != -1 && gui_win_y != -1) {
+        // NOTE: the gui_win_* coordinates are both set to -1 if no :winpos
+        // command is in .[g]vimrc.  (This way of detecting if :winpos has been
+        // used may cause problems if a second monitor is located to the left
+        // and underneath the main monitor as it will have negative
+        // coordinates.  However, this seems like a minor problem that is not
+        // worth fixing since all GUIs work this way.)
+        ASLogDebug(@"default x=%d y=%d", gui_win_x, gui_win_y);
+        int pos[2] = { gui_win_x, gui_win_y };
+        NSData *data = [NSData dataWithBytes:pos length:2*sizeof(int)];
+        [self queueMessage:SetWindowPositionMsgID data:data];
+    }
+
     [self queueMessage:OpenWindowMsgID data:nil];
+
     return YES;
 }
 
@@ -1952,6 +1984,12 @@ static void netbeansReadCallback(CFSocketRef s,
         // regarding resizing.)
         [self queueMessage:ZoomMsgID data:data];
         gui_resize_shell(cols, rows);
+    } else if (SetWindowPositionMsgID == msgid) {
+        if (!data) return;
+        const void *bytes = [data bytes];
+        winposX = *((int*)bytes);  bytes += sizeof(int);
+        winposY = *((int*)bytes);  bytes += sizeof(int);
+        ASLogDebug(@"SetWindowPositionMsgID: x=%d y=%d", winposX, winposY);
     } else {
         ASLogWarn(@"Unknown message received (msgid=%d)", msgid);
     }
index 279cf8f..f114bae 100644 (file)
@@ -824,6 +824,12 @@ static BOOL isUnsafeMessage(int msgid);
         [windowController zoomWithRows:rows
                                columns:cols
                                  state:state];
+    } else if (SetWindowPositionMsgID == msgid) {
+        const void *bytes = [data bytes];
+        int x = *((int*)bytes);  bytes += sizeof(int);
+        int y = *((int*)bytes);  bytes += sizeof(int);
+
+        [windowController setTopLeft:NSMakePoint(x,y)];
     // IMPORTANT: When adding a new message, make sure to update
     // isUnsafeMessage() if necessary!
     } else {
index 9b05d81..5ac4ade 100644 (file)
@@ -38,6 +38,7 @@
     int                 userRows;
     int                 userCols;
     NSPoint             userTopLeft;
+    NSPoint             defaultTopLeft;
 }
 
 - (id)initWithVimController:(MMVimController *)controller;
@@ -78,6 +79,8 @@
 - (void)setFullscreenBackgroundColor:(NSColor *)back;
 
 - (void)setBuffersModified:(BOOL)mod;
+- (void)setTopLeft:(NSPoint)pt;
+- (BOOL)getDefaultTopLeft:(NSPoint*)pt;
 
 - (IBAction)addNewTab:(id)sender;
 - (IBAction)toggleToolbar:(id)sender;
index 366041d..39c4512 100644 (file)
     [decoratedWindow setDocumentEdited:mod];
 }
 
+- (void)setTopLeft:(NSPoint)pt
+{
+    if (setupDone) {
+        [decoratedWindow setFrameTopLeftPoint:pt];
+    } else {
+        // Window has not been "opened" yet (see openWindow:) but remember this
+        // value to be used when the window opens.
+        defaultTopLeft = pt;
+    }
+}
+
+- (BOOL)getDefaultTopLeft:(NSPoint*)pt
+{
+    // A default top left point may be set in .[g]vimrc with the :winpos
+    // command.  (If this has not been done the top left point will be the zero
+    // point.)
+    if (pt && !NSEqualPoints(defaultTopLeft, NSZeroPoint)) {
+        *pt = defaultTopLeft;
+        return YES;
+    }
+
+    return NO;
+}
+
 
 - (IBAction)addNewTab:(id)sender
 {
         return;
     }
 
+    NSRect frame = [decoratedWindow frame];
+    NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
     if (windowAutosaveKey) {
-        NSRect frame = [decoratedWindow frame];
-        NSPoint topLeft = { frame.origin.x, NSMaxY(frame) };
         NSString *topLeftString = NSStringFromPoint(topLeft);
 
         [[NSUserDefaults standardUserDefaults]
             setObject:topLeftString forKey:windowAutosaveKey];
     }
+
+    // NOTE: This method is called when the user drags the window, but not when
+    // the top left point changes programmatically.
+    int pos[2] = { (int)topLeft.x, (int)topLeft.y };
+    NSData *data = [NSData dataWithBytes:pos length:2*sizeof(int)];
+    [vimController sendMessage:SetWindowPositionMsgID data:data];
 }
 
 - (void)windowDidResize:(id)sender
     }
 
     [decoratedWindow setFrame:newFrame display:YES];
+
+    NSPoint oldTopLeft = { frame.origin.x, NSMaxY(frame) };
+    NSPoint newTopLeft = { newFrame.origin.x, NSMaxY(newFrame) };
+    if (!NSEqualPoints(oldTopLeft, newTopLeft)) {
+        // NOTE: The window top left position may change due to the window
+        // being moved e.g. when the tabline is shown so we must tell Vim what
+        // the new window position is here.
+        int pos[2] = { (int)newTopLeft.x, (int)newTopLeft.y };
+        NSData *data = [NSData dataWithBytes:pos length:2*sizeof(int)];
+        [vimController sendMessage:SetWindowPositionMsgID data:data];
+    }
 }
 
 - (NSSize)constrainContentSizeToScreenSize:(NSSize)contentSize
index f07bfe5..927a887 100644 (file)
@@ -191,6 +191,7 @@ enum {
     NetBeansMsgID,
     SetMarkedTextMsgID,
     ZoomMsgID,
+    SetWindowPositionMsgID,
     LastMsgID   // NOTE: MUST BE LAST MESSAGE IN ENUM!
 };
 
index 9044900..ce060d2 100644 (file)
@@ -94,6 +94,7 @@ char *MessageStrings[] =
     "NetBeansMsgID",
     "SetMarkedTextMsgID",
     "ZoomMsgID",
+    "SetWindowPositionMsgID",
     "END OF MESSAGE IDs"     // NOTE: Must be last!
 };
 
index 71748ce..4d5e3b9 100644 (file)
@@ -1655,17 +1655,6 @@ gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
 
 
 /*
- * Get the position of the top left corner of the window.
- */
-    int
-gui_mch_get_winpos(int *x, int *y)
-{
-    *x = *y = 0;
-    return OK;
-}
-
-
-/*
  * Return OK if the key with the termcap name "name" is supported.
  */
     int
@@ -1714,17 +1703,30 @@ gui_mch_set_shellsize(
 }
 
 
+/*
+ * Set the position of the top left corner of the window to the given
+ * coordinates.
+ */
     void
-gui_mch_set_text_area_pos(int x, int y, int w, int h)
+gui_mch_set_winpos(int x, int y)
 {
+    [[MMBackend sharedInstance] setWindowPositionX:x Y:y];
 }
 
+
 /*
- * Set the position of the top left corner of the window to the given
- * coordinates.
+ * Get the position of the top left corner of the window.
  */
+    int
+gui_mch_get_winpos(int *x, int *y)
+{
+    [[MMBackend sharedInstance] getWindowPositionX:x Y:y];
+    return OK;
+}
+
+
     void
-gui_mch_set_winpos(int x, int y)
+gui_mch_set_text_area_pos(int x, int y, int w, int h)
 {
 }