1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * MacVim GUI port by Bjorn Winckler
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
13 * MMBackend communicates with the frontend (MacVim). It maintains a queue of
14 * output which is flushed to the frontend under controlled circumstances (so
15 * as to maintain a steady framerate). Input from the frontend is also handled
18 * The frontend communicates with the backend via the MMBackendProtocol. In
19 * particular, input is sent to the backend via processInput:data: and Vim
20 * state can be queried from the frontend with evaluateExpression:.
22 * It is very important to realize that all state is held by the backend, the
23 * frontend must either ask for state [MMBackend evaluateExpression:] or wait
24 * for the backend to update [MMVimController processCommandQueue:].
26 * The client/server functionality of Vim is handled by the backend. It sets
27 * up a named NSConnection to which other Vim processes can connect.
34 // NOTE: Colors in MMBackend are stored as unsigned ints on the form 0xaarrggbb
35 // whereas colors in Vim are int without the alpha component. Also note that
36 // 'transp' is assumed to be a value between 0 and 100.
37 #define MM_COLOR(col) ((unsigned)( ((col)&0xffffff) | 0xff000000 ))
38 #define MM_COLOR_WITH_TRANSP(col,transp) \
39 ((unsigned)( ((col)&0xffffff) \
40 | ((((unsigned)((((100-(transp))*255)/100)+.5f))&0xff)<<24) ))
43 // This constant controls how often the command queue may be flushed. If it is
44 // too small the app might feel unresponsive; if it is too large there might be
45 // long periods without the screen updating (e.g. when sourcing a large session
46 // file). (The unit is seconds.)
47 static float MMFlushTimeoutInterval = 0.1f;
48 static int MMFlushQueueLenHint = 80*40;
50 static unsigned MMServerMax = 1000;
52 // TODO: Move to separate file.
53 static int eventModifierFlagsToVimModMask(int modifierFlags);
54 static int vimModMaskToEventModifierFlags(int mods);
55 static int eventModifierFlagsToVimMouseModMask(int modifierFlags);
56 static int eventButtonNumberToVimMouseButton(int buttonNumber);
57 static int specialKeyToNSKey(int key);
65 static NSString *MMSymlinkWarningString =
66 @"\n\n\tMost likely this is because you have symlinked directly to\n"
67 "\tthe Vim binary, which Cocoa does not allow. Please use an\n"
68 "\talias or the mvim shell script instead. If you have not used\n"
69 "\ta symlink, then your MacVim.app bundle is incomplete.\n\n";
73 @interface NSString (MMServerNameCompare)
74 - (NSComparisonResult)serverNameCompare:(NSString *)string;
79 @interface MMBackend (Private)
80 - (void)queueVimStateMessage;
81 - (void)processInputQueue;
82 - (void)handleInputEvent:(int)msgid data:(NSData *)data;
83 + (NSDictionary *)specialKeys;
84 - (void)handleInsertText:(NSData *)data;
85 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods;
86 - (void)queueMessage:(int)msgid data:(NSData *)data;
87 - (void)connectionDidDie:(NSNotification *)notification;
88 - (void)blinkTimerFired:(NSTimer *)timer;
89 - (void)focusChange:(BOOL)on;
90 - (void)handleToggleToolbar;
91 - (void)handleScrollbarEvent:(NSData *)data;
92 - (void)handleSetFont:(NSData *)data;
93 - (void)handleDropFiles:(NSData *)data;
94 - (void)handleDropString:(NSData *)data;
95 - (void)handleOdbEdit:(NSData *)data;
96 - (void)handleXcodeMod:(NSData *)data;
97 - (BOOL)checkForModifiedBuffers;
98 - (void)addInput:(NSString *)input;
103 @interface MMBackend (ClientServer)
104 - (NSString *)connectionNameFromServerName:(NSString *)name;
105 - (NSConnection *)connectionForServerName:(NSString *)name;
106 - (NSConnection *)connectionForServerPort:(int)port;
107 - (void)serverConnectionDidDie:(NSNotification *)notification;
108 - (void)addClient:(NSDistantObject *)client;
109 - (NSString *)alternateServerNameForName:(NSString *)name;
114 @implementation MMBackend
116 + (MMBackend *)sharedInstance
118 static MMBackend *singleton = nil;
119 return singleton ? singleton : (singleton = [MMBackend new]);
125 if (!self) return nil;
127 fontContainerRef = loadFonts();
129 outputQueue = [[NSMutableArray alloc] init];
130 inputQueue = [[NSMutableArray alloc] init];
131 drawData = [[NSMutableData alloc] initWithCapacity:1024];
132 connectionNameDict = [[NSMutableDictionary alloc] init];
133 clientProxyDict = [[NSMutableDictionary alloc] init];
134 serverReplyDict = [[NSMutableDictionary alloc] init];
136 NSBundle *mainBundle = [NSBundle mainBundle];
137 NSString *path = [mainBundle pathForResource:@"Colors" ofType:@"plist"];
139 colorDict = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
141 path = [mainBundle pathForResource:@"SystemColors" ofType:@"plist"];
143 sysColorDict = [[NSDictionary dictionaryWithContentsOfFile:path]
146 path = [mainBundle pathForResource:@"Actions" ofType:@"plist"];
148 actionDict = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
150 if (!(colorDict && sysColorDict && actionDict))
151 NSLog(@"ERROR: Failed to load dictionaries.%@",
152 MMSymlinkWarningString);
159 //NSLog(@"%@ %s", [self className], _cmd);
160 [[NSNotificationCenter defaultCenter] removeObserver:self];
162 [oldWideFont release]; oldWideFont = nil;
163 [blinkTimer release]; blinkTimer = nil;
164 [alternateServerName release]; alternateServerName = nil;
165 [serverReplyDict release]; serverReplyDict = nil;
166 [clientProxyDict release]; clientProxyDict = nil;
167 [connectionNameDict release]; connectionNameDict = nil;
168 [inputQueue release]; inputQueue = nil;
169 [outputQueue release]; outputQueue = nil;
170 [drawData release]; drawData = nil;
171 [frontendProxy release]; frontendProxy = nil;
172 [connection release]; connection = nil;
173 [actionDict release]; actionDict = nil;
174 [sysColorDict release]; sysColorDict = nil;
175 [colorDict release]; colorDict = nil;
180 - (void)setBackgroundColor:(int)color
182 backgroundColor = MM_COLOR_WITH_TRANSP(color,p_transp);
185 - (void)setForegroundColor:(int)color
187 foregroundColor = MM_COLOR(color);
190 - (void)setSpecialColor:(int)color
192 specialColor = MM_COLOR(color);
195 - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg
197 defaultBackgroundColor = MM_COLOR_WITH_TRANSP(bg,p_transp);
198 defaultForegroundColor = MM_COLOR(fg);
200 NSMutableData *data = [NSMutableData data];
202 [data appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
203 [data appendBytes:&defaultForegroundColor length:sizeof(unsigned)];
205 [self queueMessage:SetDefaultColorsMsgID data:data];
208 - (NSConnection *)connection
211 // NOTE! If the name of the connection changes here it must also be
212 // updated in MMAppController.m.
213 NSString *name = [NSString stringWithFormat:@"%@-connection",
214 [[NSBundle mainBundle] bundleIdentifier]];
216 connection = [NSConnection connectionWithRegisteredName:name host:nil];
220 // NOTE: 'connection' may be nil here.
224 - (NSDictionary *)actionDict
231 if (![self connection]) {
232 NSBundle *mainBundle = [NSBundle mainBundle];
237 // Launch MacVim using Launch Services (NSWorkspace would be nicer, but
238 // the API to pass Apple Event parameters is broken on 10.4).
239 NSString *path = [mainBundle bundlePath];
240 status = FSPathMakeRef((const UInt8 *)[path UTF8String], &ref, NULL);
241 if (noErr == status) {
242 // Pass parameter to the 'Open' Apple Event that tells MacVim not
243 // to open an untitled window.
244 NSAppleEventDescriptor *desc =
245 [NSAppleEventDescriptor recordDescriptor];
246 [desc setParamDescriptor:
247 [NSAppleEventDescriptor descriptorWithBoolean:NO]
248 forKeyword:keyMMUntitledWindow];
250 LSLaunchFSRefSpec spec = { &ref, 0, NULL, [desc aeDesc],
251 kLSLaunchDefaults, NULL };
252 status = LSOpenFromRefSpec(&spec, NULL);
255 if (noErr != status) {
256 NSLog(@"ERROR: Failed to launch MacVim (path=%@).%@",
257 path, MMSymlinkWarningString);
261 // Launch MacVim using NSTask. For some reason the above code using
262 // Launch Services sometimes fails on LSOpenFromRefSpec() (when it
263 // fails, the dock icon starts bouncing and never stops). It seems
264 // like rebuilding the Launch Services database takes care of this
265 // problem, but the NSTask way seems more stable so stick with it.
267 // NOTE! Using NSTask to launch the GUI has the negative side-effect
268 // that the GUI won't be activated (or raised) so there is a hack in
269 // MMAppController which raises the app when a new window is opened.
270 NSMutableArray *args = [NSMutableArray arrayWithObjects:
271 [NSString stringWithFormat:@"-%@", MMNoWindowKey], @"yes", nil];
272 NSString *exeName = [[mainBundle infoDictionary]
273 objectForKey:@"CFBundleExecutable"];
274 NSString *path = [mainBundle pathForAuxiliaryExecutable:exeName];
276 NSLog(@"ERROR: Could not find MacVim executable in bundle.%@",
277 MMSymlinkWarningString);
281 [NSTask launchedTaskWithLaunchPath:path arguments:args];
284 // HACK! Poll the mach bootstrap server until it returns a valid
285 // connection to detect that MacVim has finished launching. Also set a
286 // time-out date so that we don't get stuck doing this forever.
287 NSDate *timeOutDate = [NSDate dateWithTimeIntervalSinceNow:10];
288 while (![self connection] &&
289 NSOrderedDescending == [timeOutDate compare:[NSDate date]])
290 [[NSRunLoop currentRunLoop]
291 runMode:NSDefaultRunLoopMode
292 beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
294 // NOTE: [self connection] will set 'connection' as a side-effect.
296 NSLog(@"WARNING: Timed-out waiting for GUI to launch.");
303 [[NSNotificationCenter defaultCenter] addObserver:self
304 selector:@selector(connectionDidDie:)
305 name:NSConnectionDidDieNotification object:connection];
307 id proxy = [connection rootProxy];
308 [proxy setProtocolForProxy:@protocol(MMAppProtocol)];
310 int pid = [[NSProcessInfo processInfo] processIdentifier];
312 frontendProxy = [proxy connectBackend:self pid:pid];
314 [frontendProxy retain];
315 [frontendProxy setProtocolForProxy:@protocol(MMAppProtocol)];
319 @catch (NSException *e) {
320 NSLog(@"Exception caught when trying to connect backend: \"%@\"", e);
326 - (BOOL)openVimWindow
328 [self queueMessage:OpenVimWindowMsgID data:nil];
334 int type = ClearAllDrawType;
336 // Any draw commands in queue are effectively obsolete since this clearAll
337 // will negate any effect they have, therefore we may as well clear the
339 [drawData setLength:0];
341 [drawData appendBytes:&type length:sizeof(int)];
344 - (void)clearBlockFromRow:(int)row1 column:(int)col1
345 toRow:(int)row2 column:(int)col2
347 int type = ClearBlockDrawType;
349 [drawData appendBytes:&type length:sizeof(int)];
351 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
352 [drawData appendBytes:&row1 length:sizeof(int)];
353 [drawData appendBytes:&col1 length:sizeof(int)];
354 [drawData appendBytes:&row2 length:sizeof(int)];
355 [drawData appendBytes:&col2 length:sizeof(int)];
358 - (void)deleteLinesFromRow:(int)row count:(int)count
359 scrollBottom:(int)bottom left:(int)left right:(int)right
361 int type = DeleteLinesDrawType;
363 [drawData appendBytes:&type length:sizeof(int)];
365 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
366 [drawData appendBytes:&row length:sizeof(int)];
367 [drawData appendBytes:&count length:sizeof(int)];
368 [drawData appendBytes:&bottom length:sizeof(int)];
369 [drawData appendBytes:&left length:sizeof(int)];
370 [drawData appendBytes:&right length:sizeof(int)];
373 - (void)drawString:(char*)s length:(int)len row:(int)row column:(int)col
374 cells:(int)cells flags:(int)flags
376 if (len <= 0 || cells <= 0) return;
378 int type = DrawStringDrawType;
380 [drawData appendBytes:&type length:sizeof(int)];
382 [drawData appendBytes:&backgroundColor length:sizeof(unsigned)];
383 [drawData appendBytes:&foregroundColor length:sizeof(unsigned)];
384 [drawData appendBytes:&specialColor length:sizeof(unsigned)];
385 [drawData appendBytes:&row length:sizeof(int)];
386 [drawData appendBytes:&col length:sizeof(int)];
387 [drawData appendBytes:&cells length:sizeof(int)];
388 [drawData appendBytes:&flags length:sizeof(int)];
389 [drawData appendBytes:&len length:sizeof(int)];
390 [drawData appendBytes:s length:len];
393 - (void)insertLinesFromRow:(int)row count:(int)count
394 scrollBottom:(int)bottom left:(int)left right:(int)right
396 int type = InsertLinesDrawType;
398 [drawData appendBytes:&type length:sizeof(int)];
400 [drawData appendBytes:&defaultBackgroundColor length:sizeof(unsigned)];
401 [drawData appendBytes:&row length:sizeof(int)];
402 [drawData appendBytes:&count length:sizeof(int)];
403 [drawData appendBytes:&bottom length:sizeof(int)];
404 [drawData appendBytes:&left length:sizeof(int)];
405 [drawData appendBytes:&right length:sizeof(int)];
408 - (void)drawCursorAtRow:(int)row column:(int)col shape:(int)shape
409 fraction:(int)percent color:(int)color
411 int type = DrawCursorDrawType;
412 unsigned uc = MM_COLOR(color);
414 [drawData appendBytes:&type length:sizeof(int)];
416 [drawData appendBytes:&uc length:sizeof(unsigned)];
417 [drawData appendBytes:&row length:sizeof(int)];
418 [drawData appendBytes:&col length:sizeof(int)];
419 [drawData appendBytes:&shape length:sizeof(int)];
420 [drawData appendBytes:&percent length:sizeof(int)];
425 // Tend to the run loop, returning immediately if there are no events
427 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
428 beforeDate:[NSDate distantPast]];
431 // Keyboard and mouse input is handled directly, other input is queued and
432 // processed here. This call may enter a blocking loop.
433 if ([inputQueue count] > 0)
434 [self processInputQueue];
438 - (void)flushQueue:(BOOL)force
440 // NOTE! This method gets called a lot; if we were to flush every time it
441 // got called MacVim would feel unresponsive. So there is a time out which
442 // ensures that the queue isn't flushed too often.
444 (!force && lastFlushDate &&
445 -[lastFlushDate timeIntervalSinceNow] < MMFlushTimeoutInterval &&
446 [drawData length] < MMFlushQueueLenHint))
449 if ([drawData length] > 0) {
450 // HACK! Detect changes to 'guifontwide'.
451 if (gui.wide_font != (GuiFont)oldWideFont) {
452 [oldWideFont release];
453 oldWideFont = [(NSFont*)gui.wide_font retain];
454 [self setWideFont:oldWideFont];
457 int type = SetCursorPosDrawType;
458 [drawData appendBytes:&type length:sizeof(type)];
459 [drawData appendBytes:&gui.row length:sizeof(gui.row)];
460 [drawData appendBytes:&gui.col length:sizeof(gui.col)];
462 [self queueMessage:BatchDrawMsgID data:[drawData copy]];
463 [drawData setLength:0];
466 if ([outputQueue count] > 0 || force) {
467 // When 'force' is set we always update the Vim state to ensure that
468 // MacVim has a copy of the latest state (since 'force' is typically
469 // set just before Vim takes a nap whilst waiting for input).
470 [self queueVimStateMessage];
473 [frontendProxy processCommandQueue:outputQueue];
475 @catch (NSException *e) {
476 NSLog(@"Exception caught when processing command queue: \"%@\"", e);
479 [outputQueue removeAllObjects];
481 [lastFlushDate release];
482 lastFlushDate = [[NSDate date] retain];
486 - (BOOL)waitForInput:(int)milliseconds
488 //NSLog(@"|ENTER| %s%d", _cmd, milliseconds);
490 // Only start the run loop if the input queue is empty, otherwise process
491 // the input first so that the input on queue isn't delayed.
492 if ([inputQueue count]) {
495 NSDate *date = milliseconds > 0 ?
496 [NSDate dateWithTimeIntervalSinceNow:.001*milliseconds] :
497 [NSDate distantFuture];
499 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
503 // I know of no way to figure out if the run loop exited because input was
504 // found or because of a time out, so I need to manually indicate when
505 // input was received in processInput:data: and then reset it every time
507 BOOL yn = inputReceived;
510 // Keyboard and mouse input is handled directly, other input is queued and
511 // processed here. This call may enter a blocking loop.
512 if ([inputQueue count] > 0)
513 [self processInputQueue];
515 //NSLog(@"|LEAVE| %s input=%d", _cmd, yn);
521 #ifdef MAC_CLIENTSERVER
522 // The default connection is used for the client/server code.
523 [[NSConnection defaultConnection] setRootObject:nil];
524 [[NSConnection defaultConnection] invalidate];
527 // By invalidating the NSConnection the MMWindowController immediately
528 // finds out that the connection is down and as a result
529 // [MMWindowController connectionDidDie:] is invoked.
530 //NSLog(@"%@ %s", [self className], _cmd);
531 [[NSNotificationCenter defaultCenter] removeObserver:self];
532 [connection invalidate];
534 if (fontContainerRef) {
535 ATSFontDeactivate(fontContainerRef, NULL, kATSOptionFlagsDefault);
536 fontContainerRef = 0;
541 - (void)selectTab:(int)index
543 //NSLog(@"%s%d", _cmd, index);
546 NSData *data = [NSData dataWithBytes:&index length:sizeof(int)];
547 [self queueMessage:SelectTabMsgID data:data];
552 //NSLog(@"%s", _cmd);
554 NSMutableData *data = [NSMutableData data];
556 int idx = tabpage_index(curtab) - 1;
557 [data appendBytes:&idx length:sizeof(int)];
560 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
561 // This function puts the label of the tab in the global 'NameBuff'.
562 get_tabline_label(tp, FALSE);
563 char_u *s = NameBuff;
565 if (len <= 0) continue;
568 s = CONVERT_TO_UTF8(s);
571 // Count the number of windows in the tabpage.
572 //win_T *wp = tp->tp_firstwin;
574 //for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount);
576 //[data appendBytes:&wincount length:sizeof(int)];
577 [data appendBytes:&len length:sizeof(int)];
578 [data appendBytes:s length:len];
581 CONVERT_TO_UTF8_FREE(s);
585 [self queueMessage:UpdateTabBarMsgID data:data];
588 - (BOOL)tabBarVisible
590 return tabBarVisible;
593 - (void)showTabBar:(BOOL)enable
595 tabBarVisible = enable;
597 int msgid = enable ? ShowTabBarMsgID : HideTabBarMsgID;
598 [self queueMessage:msgid data:nil];
601 - (void)setRows:(int)rows columns:(int)cols
603 //NSLog(@"[VimTask] setRows:%d columns:%d", rows, cols);
605 int dim[] = { rows, cols };
606 NSData *data = [NSData dataWithBytes:&dim length:2*sizeof(int)];
608 [self queueMessage:SetTextDimensionsMsgID data:data];
611 - (void)setWindowTitle:(char *)title
613 NSMutableData *data = [NSMutableData data];
614 int len = strlen(title);
615 if (len <= 0) return;
617 [data appendBytes:&len length:sizeof(int)];
618 [data appendBytes:title length:len];
620 [self queueMessage:SetWindowTitleMsgID data:data];
623 - (char *)browseForFileInDirectory:(char *)dir title:(char *)title
626 //NSLog(@"browseForFileInDirectory:%s title:%s saving:%d", dir, title,
630 NSString *ds = dir ? [NSString stringWithUTF8String:dir] : nil;
631 NSString *ts = title ? [NSString stringWithUTF8String:title] : nil;
633 [frontendProxy showSavePanelForDirectory:ds title:ts saving:saving];
635 // Wait until a reply is sent from MMVimController.
636 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
637 beforeDate:[NSDate distantFuture]];
639 if (dialogReturn && [dialogReturn isKindOfClass:[NSString class]]) {
640 char_u *ret = (char_u*)[dialogReturn UTF8String];
642 ret = CONVERT_FROM_UTF8(ret);
644 s = vim_strsave(ret);
646 CONVERT_FROM_UTF8_FREE(ret);
650 [dialogReturn release]; dialogReturn = nil;
652 @catch (NSException *e) {
653 NSLog(@"Exception caught when showing save panel: \"%@\"", e);
659 - (oneway void)setDialogReturn:(in bycopy id)obj
661 // NOTE: This is called by
662 // - [MMVimController panelDidEnd:::], and
663 // - [MMVimController alertDidEnd:::],
664 // to indicate that a save/open panel or alert has finished.
666 if (obj != dialogReturn) {
667 [dialogReturn release];
668 dialogReturn = [obj retain];
672 - (int)presentDialogWithType:(int)type title:(char *)title message:(char *)msg
673 buttons:(char *)btns textField:(char *)txtfield
676 NSString *message = nil, *text = nil, *textFieldString = nil;
677 NSArray *buttons = nil;
678 int style = NSInformationalAlertStyle;
680 if (VIM_WARNING == type) style = NSWarningAlertStyle;
681 else if (VIM_ERROR == type) style = NSCriticalAlertStyle;
684 NSString *btnString = [NSString stringWithUTF8String:btns];
685 buttons = [btnString componentsSeparatedByString:@"\n"];
688 message = [NSString stringWithUTF8String:title];
690 text = [NSString stringWithUTF8String:msg];
692 // HACK! If there is a '\n\n' or '\n' sequence in the message, then
693 // make the part up to there into the title. We only do this
694 // because Vim has lots of dialogs without a title and they look
696 // TODO: Fix the actual dialog texts.
697 NSRange eolRange = [text rangeOfString:@"\n\n"];
698 if (NSNotFound == eolRange.location)
699 eolRange = [text rangeOfString:@"\n"];
700 if (NSNotFound != eolRange.location) {
701 message = [text substringToIndex:eolRange.location];
702 text = [text substringFromIndex:NSMaxRange(eolRange)];
707 textFieldString = [NSString stringWithUTF8String:txtfield];
710 [frontendProxy presentDialogWithStyle:style message:message
711 informativeText:text buttonTitles:buttons
712 textFieldString:textFieldString];
714 // Wait until a reply is sent from MMVimController.
715 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
716 beforeDate:[NSDate distantFuture]];
718 if (dialogReturn && [dialogReturn isKindOfClass:[NSArray class]]
719 && [dialogReturn count]) {
720 retval = [[dialogReturn objectAtIndex:0] intValue];
721 if (txtfield && [dialogReturn count] > 1) {
722 NSString *retString = [dialogReturn objectAtIndex:1];
723 char_u *ret = (char_u*)[retString UTF8String];
725 ret = CONVERT_FROM_UTF8(ret);
727 vim_strncpy((char_u*)txtfield, ret, IOSIZE - 1);
729 CONVERT_FROM_UTF8_FREE(ret);
734 [dialogReturn release]; dialogReturn = nil;
736 @catch (NSException *e) {
737 NSLog(@"Exception caught while showing alert dialog: \"%@\"", e);
743 - (void)addMenuWithTag:(int)tag parent:(int)parentTag name:(char *)name
746 //NSLog(@"addMenuWithTag:%d parent:%d name:%s atIndex:%d", tag, parentTag,
749 int namelen = name ? strlen(name) : 0;
750 NSMutableData *data = [NSMutableData data];
752 [data appendBytes:&tag length:sizeof(int)];
753 [data appendBytes:&parentTag length:sizeof(int)];
754 [data appendBytes:&namelen length:sizeof(int)];
755 if (namelen > 0) [data appendBytes:name length:namelen];
756 [data appendBytes:&index length:sizeof(int)];
758 [self queueMessage:AddMenuMsgID data:data];
761 - (void)addMenuItemWithTag:(int)tag parent:(int)parentTag name:(char *)name
762 tip:(char *)tip icon:(char *)icon
763 keyEquivalent:(int)key modifiers:(int)mods
764 action:(char *)action isAlternate:(int)isAlt
767 //NSLog(@"addMenuItemWithTag:%d parent:%d name:%s tip:%s atIndex:%d", tag,
768 // parentTag, name, tip, index);
770 int namelen = name ? strlen(name) : 0;
771 int tiplen = tip ? strlen(tip) : 0;
772 int iconlen = icon ? strlen(icon) : 0;
773 int eventFlags = vimModMaskToEventModifierFlags(mods);
774 int actionlen = action ? strlen(action) : 0;
775 NSMutableData *data = [NSMutableData data];
777 key = specialKeyToNSKey(key);
779 [data appendBytes:&tag length:sizeof(int)];
780 [data appendBytes:&parentTag length:sizeof(int)];
781 [data appendBytes:&namelen length:sizeof(int)];
782 if (namelen > 0) [data appendBytes:name length:namelen];
783 [data appendBytes:&tiplen length:sizeof(int)];
784 if (tiplen > 0) [data appendBytes:tip length:tiplen];
785 [data appendBytes:&iconlen length:sizeof(int)];
786 if (iconlen > 0) [data appendBytes:icon length:iconlen];
787 [data appendBytes:&actionlen length:sizeof(int)];
788 if (actionlen > 0) [data appendBytes:action length:actionlen];
789 [data appendBytes:&index length:sizeof(int)];
790 [data appendBytes:&key length:sizeof(int)];
791 [data appendBytes:&eventFlags length:sizeof(int)];
792 [data appendBytes:&isAlt length:sizeof(int)];
794 [self queueMessage:AddMenuItemMsgID data:data];
797 - (void)removeMenuItemWithTag:(int)tag
799 NSMutableData *data = [NSMutableData data];
800 [data appendBytes:&tag length:sizeof(int)];
802 [self queueMessage:RemoveMenuItemMsgID data:data];
805 - (void)enableMenuItemWithTag:(int)tag state:(int)enabled
807 NSMutableData *data = [NSMutableData data];
809 [data appendBytes:&tag length:sizeof(int)];
810 [data appendBytes:&enabled length:sizeof(int)];
812 [self queueMessage:EnableMenuItemMsgID data:data];
815 - (void)showPopupMenuWithName:(char *)name atMouseLocation:(BOOL)mouse
817 int len = strlen(name);
818 int row = -1, col = -1;
820 if (len <= 0) return;
822 if (!mouse && curwin) {
823 row = curwin->w_wrow;
824 col = curwin->w_wcol;
827 NSMutableData *data = [NSMutableData data];
829 [data appendBytes:&row length:sizeof(int)];
830 [data appendBytes:&col length:sizeof(int)];
831 [data appendBytes:&len length:sizeof(int)];
832 [data appendBytes:name length:len];
834 [self queueMessage:ShowPopupMenuMsgID data:data];
837 - (void)showToolbar:(int)enable flags:(int)flags
839 NSMutableData *data = [NSMutableData data];
841 [data appendBytes:&enable length:sizeof(int)];
842 [data appendBytes:&flags length:sizeof(int)];
844 [self queueMessage:ShowToolbarMsgID data:data];
847 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
849 NSMutableData *data = [NSMutableData data];
851 [data appendBytes:&ident length:sizeof(long)];
852 [data appendBytes:&type length:sizeof(int)];
854 [self queueMessage:CreateScrollbarMsgID data:data];
857 - (void)destroyScrollbarWithIdentifier:(long)ident
859 NSMutableData *data = [NSMutableData data];
860 [data appendBytes:&ident length:sizeof(long)];
862 [self queueMessage:DestroyScrollbarMsgID data:data];
865 - (void)showScrollbarWithIdentifier:(long)ident state:(int)visible
867 NSMutableData *data = [NSMutableData data];
869 [data appendBytes:&ident length:sizeof(long)];
870 [data appendBytes:&visible length:sizeof(int)];
872 [self queueMessage:ShowScrollbarMsgID data:data];
875 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
877 NSMutableData *data = [NSMutableData data];
879 [data appendBytes:&ident length:sizeof(long)];
880 [data appendBytes:&pos length:sizeof(int)];
881 [data appendBytes:&len length:sizeof(int)];
883 [self queueMessage:SetScrollbarPositionMsgID data:data];
886 - (void)setScrollbarThumbValue:(long)val size:(long)size max:(long)max
887 identifier:(long)ident
889 float fval = max-size+1 > 0 ? (float)val/(max-size+1) : 0;
890 float prop = (float)size/(max+1);
891 if (fval < 0) fval = 0;
892 else if (fval > 1.0f) fval = 1.0f;
893 if (prop < 0) prop = 0;
894 else if (prop > 1.0f) prop = 1.0f;
896 NSMutableData *data = [NSMutableData data];
898 [data appendBytes:&ident length:sizeof(long)];
899 [data appendBytes:&fval length:sizeof(float)];
900 [data appendBytes:&prop length:sizeof(float)];
902 [self queueMessage:SetScrollbarThumbMsgID data:data];
905 - (void)setFont:(NSFont *)font
907 NSString *fontName = [font displayName];
908 float size = [font pointSize];
909 int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
911 NSMutableData *data = [NSMutableData data];
913 [data appendBytes:&size length:sizeof(float)];
914 [data appendBytes:&len length:sizeof(int)];
915 [data appendBytes:[fontName UTF8String] length:len];
917 [self queueMessage:SetFontMsgID data:data];
921 - (void)setWideFont:(NSFont *)font
923 NSString *fontName = [font displayName];
924 float size = [font pointSize];
925 int len = [fontName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
926 NSMutableData *data = [NSMutableData data];
928 [data appendBytes:&size length:sizeof(float)];
929 [data appendBytes:&len length:sizeof(int)];
931 [data appendBytes:[fontName UTF8String] length:len];
933 [self queueMessage:SetWideFontMsgID data:data];
936 - (void)executeActionWithName:(NSString *)name
938 int len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
941 NSMutableData *data = [NSMutableData data];
943 [data appendBytes:&len length:sizeof(int)];
944 [data appendBytes:[name UTF8String] length:len];
946 [self queueMessage:ExecuteActionMsgID data:data];
950 - (void)setMouseShape:(int)shape
952 NSMutableData *data = [NSMutableData data];
953 [data appendBytes:&shape length:sizeof(int)];
954 [self queueMessage:SetMouseShapeMsgID data:data];
957 - (void)setBlinkWait:(int)wait on:(int)on off:(int)off
959 // Vim specifies times in milliseconds, whereas Cocoa wants them in
961 blinkWaitInterval = .001f*wait;
962 blinkOnInterval = .001f*on;
963 blinkOffInterval = .001f*off;
969 [blinkTimer invalidate];
970 [blinkTimer release];
974 if (blinkWaitInterval > 0 && blinkOnInterval > 0 && blinkOffInterval > 0
976 blinkState = MMBlinkStateOn;
978 [[NSTimer scheduledTimerWithTimeInterval:blinkWaitInterval
980 selector:@selector(blinkTimerFired:)
981 userInfo:nil repeats:NO] retain];
982 gui_update_cursor(TRUE, FALSE);
983 [self flushQueue:YES];
989 if (MMBlinkStateOff == blinkState) {
990 gui_update_cursor(TRUE, FALSE);
991 [self flushQueue:YES];
994 blinkState = MMBlinkStateNone;
997 - (void)adjustLinespace:(int)linespace
999 NSMutableData *data = [NSMutableData data];
1000 [data appendBytes:&linespace length:sizeof(int)];
1001 [self queueMessage:AdjustLinespaceMsgID data:data];
1006 [self queueMessage:ActivateMsgID data:nil];
1009 - (void)setPreEditRow:(int)row column:(int)col
1011 NSMutableData *data = [NSMutableData data];
1012 [data appendBytes:&row length:sizeof(int)];
1013 [data appendBytes:&col length:sizeof(int)];
1014 [self queueMessage:SetPreEditPositionMsgID data:data];
1017 - (int)lookupColorWithKey:(NSString *)key
1019 if (!(key && [key length] > 0))
1022 NSString *stripKey = [[[[key lowercaseString]
1023 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]
1024 componentsSeparatedByString:@" "]
1025 componentsJoinedByString:@""];
1027 if (stripKey && [stripKey length] > 0) {
1028 // First of all try to lookup key in the color dictionary; note that
1029 // all keys in this dictionary are lowercase with no whitespace.
1030 id obj = [colorDict objectForKey:stripKey];
1031 if (obj) return [obj intValue];
1033 // The key was not in the dictionary; is it perhaps of the form
1035 if ([stripKey length] > 1 && [stripKey characterAtIndex:0] == '#') {
1036 NSScanner *scanner = [NSScanner scannerWithString:stripKey];
1037 [scanner setScanLocation:1];
1039 if ([scanner scanHexInt:&hex]) {
1044 // As a last resort, check if it is one of the system defined colors.
1045 // The keys in this dictionary are also lowercase with no whitespace.
1046 obj = [sysColorDict objectForKey:stripKey];
1048 NSColor *col = [NSColor performSelector:NSSelectorFromString(obj)];
1051 col = [col colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
1052 [col getRed:&r green:&g blue:&b alpha:&a];
1053 return (((int)(r*255+.5f) & 0xff) << 16)
1054 + (((int)(g*255+.5f) & 0xff) << 8)
1055 + ((int)(b*255+.5f) & 0xff);
1060 //NSLog(@"WARNING: No color with key %@ found.", stripKey);
1064 - (BOOL)hasSpecialKeyWithValue:(NSString *)value
1066 NSEnumerator *e = [[MMBackend specialKeys] objectEnumerator];
1069 while ((obj = [e nextObject])) {
1070 if ([value isEqual:obj])
1077 - (void)enterFullscreen:(int)fuoptions background:(int)bg
1079 NSMutableData *data = [NSMutableData data];
1080 [data appendBytes:&fuoptions length:sizeof(int)];
1082 [data appendBytes:&bg length:sizeof(int)];
1083 [self queueMessage:EnterFullscreenMsgID data:data];
1086 - (void)leaveFullscreen
1088 [self queueMessage:LeaveFullscreenMsgID data:nil];
1091 - (void)setAntialias:(BOOL)antialias
1093 int msgid = antialias ? EnableAntialiasMsgID : DisableAntialiasMsgID;
1095 [self queueMessage:msgid data:nil];
1098 - (void)updateModifiedFlag
1100 // Notify MacVim if _any_ buffer has changed from unmodified to modified or
1102 int msgid = [self checkForModifiedBuffers]
1103 ? BuffersModifiedMsgID : BuffersNotModifiedMsgID;
1105 [self queueMessage:msgid data:nil];
1108 - (oneway void)processInput:(int)msgid data:(in bycopy NSData *)data
1110 // NOTE: This method might get called whenever the run loop is tended to.
1111 // Normal keyboard and mouse input is added to input buffers, so there is
1112 // no risk in handling these events directly (they return immediately, and
1113 // do not call any other Vim functions). However, other events such
1114 // as 'VimShouldCloseMsgID' may enter blocking loops that wait for key
1115 // events which would cause this method to be called recursively. This
1116 // in turn leads to various difficulties that we do not want to have to
1117 // deal with. To avoid recursive calls here we add all events except
1118 // keyboard and mouse events to an input queue which is processed whenever
1119 // gui_mch_update() is called (see processInputQueue).
1121 //NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
1123 // Don't flush too soon after receiving input or update speed will suffer.
1124 [lastFlushDate release];
1125 lastFlushDate = [[NSDate date] retain];
1127 // Handle keyboard and mouse input now. All other events are queued.
1128 if (InsertTextMsgID == msgid) {
1129 [self handleInsertText:data];
1130 } else if (KeyDownMsgID == msgid || CmdKeyMsgID == msgid) {
1132 const void *bytes = [data bytes];
1133 int mods = *((int*)bytes); bytes += sizeof(int);
1134 int len = *((int*)bytes); bytes += sizeof(int);
1135 NSString *key = [[NSString alloc] initWithBytes:bytes length:len
1136 encoding:NSUTF8StringEncoding];
1137 mods = eventModifierFlagsToVimModMask(mods);
1139 [self handleKeyDown:key modifiers:mods];
1142 } else if (ScrollWheelMsgID == msgid) {
1144 const void *bytes = [data bytes];
1146 int row = *((int*)bytes); bytes += sizeof(int);
1147 int col = *((int*)bytes); bytes += sizeof(int);
1148 int flags = *((int*)bytes); bytes += sizeof(int);
1149 float dy = *((float*)bytes); bytes += sizeof(float);
1151 int button = MOUSE_5;
1152 if (dy > 0) button = MOUSE_4;
1154 flags = eventModifierFlagsToVimMouseModMask(flags);
1156 int numLines = (int)round(dy);
1157 if (numLines < 0) numLines = -numLines;
1158 if (numLines == 0) numLines = 1;
1160 #ifdef FEAT_GUI_SCROLL_WHEEL_FORCE
1161 gui.scroll_wheel_force = numLines;
1164 gui_send_mouse_event(button, col, row, NO, flags);
1165 } else if (MouseDownMsgID == msgid) {
1167 const void *bytes = [data bytes];
1169 int row = *((int*)bytes); bytes += sizeof(int);
1170 int col = *((int*)bytes); bytes += sizeof(int);
1171 int button = *((int*)bytes); bytes += sizeof(int);
1172 int flags = *((int*)bytes); bytes += sizeof(int);
1173 int count = *((int*)bytes); bytes += sizeof(int);
1175 button = eventButtonNumberToVimMouseButton(button);
1177 flags = eventModifierFlagsToVimMouseModMask(flags);
1178 gui_send_mouse_event(button, col, row, count>1, flags);
1180 } else if (MouseUpMsgID == msgid) {
1182 const void *bytes = [data bytes];
1184 int row = *((int*)bytes); bytes += sizeof(int);
1185 int col = *((int*)bytes); bytes += sizeof(int);
1186 int flags = *((int*)bytes); bytes += sizeof(int);
1188 flags = eventModifierFlagsToVimMouseModMask(flags);
1190 gui_send_mouse_event(MOUSE_RELEASE, col, row, NO, flags);
1191 } else if (MouseDraggedMsgID == msgid) {
1193 const void *bytes = [data bytes];
1195 int row = *((int*)bytes); bytes += sizeof(int);
1196 int col = *((int*)bytes); bytes += sizeof(int);
1197 int flags = *((int*)bytes); bytes += sizeof(int);
1199 flags = eventModifierFlagsToVimMouseModMask(flags);
1201 gui_send_mouse_event(MOUSE_DRAG, col, row, NO, flags);
1202 } else if (MouseMovedMsgID == msgid) {
1203 const void *bytes = [data bytes];
1204 int row = *((int*)bytes); bytes += sizeof(int);
1205 int col = *((int*)bytes); bytes += sizeof(int);
1207 gui_mouse_moved(col, row);
1208 } else if (AddInputMsgID == msgid) {
1209 NSString *string = [[NSString alloc] initWithData:data
1210 encoding:NSUTF8StringEncoding];
1212 [self addInput:string];
1215 } else if (TerminateNowMsgID == msgid) {
1216 isTerminating = YES;
1218 // Not keyboard or mouse event, queue it and handle later.
1219 //NSLog(@"Add event %s to input event queue", MessageStrings[msgid]);
1220 [inputQueue addObject:[NSNumber numberWithInt:msgid]];
1221 [inputQueue addObject:(data ? (id)data : (id)[NSNull null])];
1224 // See waitForInput: for an explanation of this flag.
1225 inputReceived = YES;
1228 - (oneway void)processInputAndData:(in bycopy NSArray *)messages
1230 // TODO: Get rid of this method?
1231 //NSLog(@"%s%@", _cmd, messages);
1233 unsigned i, count = [messages count];
1234 for (i = 0; i < count; i += 2) {
1235 int msgid = [[messages objectAtIndex:i] intValue];
1236 id data = [messages objectAtIndex:i+1];
1237 if ([data isEqual:[NSNull null]])
1240 [self processInput:msgid data:data];
1244 - (NSString *)evaluateExpression:(in bycopy NSString *)expr
1246 NSString *eval = nil;
1247 char_u *s = (char_u*)[expr UTF8String];
1250 s = CONVERT_FROM_UTF8(s);
1253 char_u *res = eval_client_expr_to_string(s);
1256 CONVERT_FROM_UTF8_FREE(s);
1262 s = CONVERT_TO_UTF8(s);
1264 eval = [NSString stringWithUTF8String:(char*)s];
1266 CONVERT_TO_UTF8_FREE(s);
1274 - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard
1276 // TODO: This method should share code with clip_mch_request_selection().
1278 if (VIsual_active && (State & NORMAL) && clip_star.available) {
1279 // If there is no pasteboard, return YES to indicate that there is text
1284 clip_copy_selection();
1286 // Get the text to put on the pasteboard.
1287 long_u llen = 0; char_u *str = 0;
1288 int type = clip_convert_selection(&str, &llen, &clip_star);
1292 // TODO: Avoid overflow.
1293 int len = (int)llen;
1295 if (output_conv.vc_type != CONV_NONE) {
1296 char_u *conv_str = string_convert(&output_conv, str, &len);
1304 NSString *string = [[NSString alloc]
1305 initWithBytes:str length:len encoding:NSUTF8StringEncoding];
1307 NSArray *types = [NSArray arrayWithObject:NSStringPboardType];
1308 [pboard declareTypes:types owner:nil];
1309 BOOL ok = [pboard setString:string forType:NSStringPboardType];
1320 - (oneway void)addReply:(in bycopy NSString *)reply
1321 server:(in byref id <MMVimServerProtocol>)server
1323 //NSLog(@"addReply:%@ server:%@", reply, (id)server);
1325 // Replies might come at any time and in any order so we keep them in an
1326 // array inside a dictionary with the send port used as key.
1328 NSConnection *conn = [(NSDistantObject*)server connectionForProxy];
1329 // HACK! Assume connection uses mach ports.
1330 int port = [(NSMachPort*)[conn sendPort] machPort];
1331 NSNumber *key = [NSNumber numberWithInt:port];
1333 NSMutableArray *replies = [serverReplyDict objectForKey:key];
1335 replies = [NSMutableArray array];
1336 [serverReplyDict setObject:replies forKey:key];
1339 [replies addObject:reply];
1342 - (void)addInput:(in bycopy NSString *)input
1343 client:(in byref id <MMVimClientProtocol>)client
1345 //NSLog(@"addInput:%@ client:%@", input, (id)client);
1347 [self addInput:input];
1348 [self addClient:(id)client];
1350 inputReceived = YES;
1353 - (NSString *)evaluateExpression:(in bycopy NSString *)expr
1354 client:(in byref id <MMVimClientProtocol>)client
1356 [self addClient:(id)client];
1357 return [self evaluateExpression:expr];
1360 - (void)registerServerWithName:(NSString *)name
1362 NSString *svrName = name;
1363 NSConnection *svrConn = [NSConnection defaultConnection];
1366 for (i = 0; i < MMServerMax; ++i) {
1367 NSString *connName = [self connectionNameFromServerName:svrName];
1369 if ([svrConn registerName:connName]) {
1370 //NSLog(@"Registered server with name: %@", svrName);
1372 // TODO: Set request/reply time-outs to something else?
1374 // Don't wait for requests (time-out means that the message is
1376 [svrConn setRequestTimeout:0];
1377 //[svrConn setReplyTimeout:MMReplyTimeout];
1378 [svrConn setRootObject:self];
1380 char_u *s = (char_u*)[svrName UTF8String];
1382 s = CONVERT_FROM_UTF8(s);
1384 // NOTE: 'serverName' is a global variable
1385 serverName = vim_strsave(s);
1387 CONVERT_FROM_UTF8_FREE(s);
1390 set_vim_var_string(VV_SEND_SERVER, serverName, -1);
1393 need_maketitle = TRUE;
1395 [self queueMessage:SetServerNameMsgID data:
1396 [svrName dataUsingEncoding:NSUTF8StringEncoding]];
1400 svrName = [NSString stringWithFormat:@"%@%d", name, i+1];
1404 - (BOOL)sendToServer:(NSString *)name string:(NSString *)string
1405 reply:(char_u **)reply port:(int *)port expression:(BOOL)expr
1408 // NOTE: If 'name' equals 'serverName' then the request is local (client
1409 // and server are the same). This case is not handled separately, so a
1410 // connection will be set up anyway (this simplifies the code).
1412 NSConnection *conn = [self connectionForServerName:name];
1415 char_u *s = (char_u*)[name UTF8String];
1417 s = CONVERT_FROM_UTF8(s);
1419 EMSG2(_(e_noserver), s);
1421 CONVERT_FROM_UTF8_FREE(s);
1428 // HACK! Assume connection uses mach ports.
1429 *port = [(NSMachPort*)[conn sendPort] machPort];
1432 id proxy = [conn rootProxy];
1433 [proxy setProtocolForProxy:@protocol(MMVimServerProtocol)];
1437 NSString *eval = [proxy evaluateExpression:string client:self];
1440 char_u *r = (char_u*)[eval UTF8String];
1442 r = CONVERT_FROM_UTF8(r);
1444 *reply = vim_strsave(r);
1446 CONVERT_FROM_UTF8_FREE(r);
1449 *reply = vim_strsave((char_u*)_(e_invexprmsg));
1456 [proxy addInput:string client:self];
1459 @catch (NSException *e) {
1460 NSLog(@"WARNING: Caught exception in %s: \"%@\"", _cmd, e);
1467 - (NSArray *)serverList
1469 NSArray *list = nil;
1471 if ([self connection]) {
1472 id proxy = [connection rootProxy];
1473 [proxy setProtocolForProxy:@protocol(MMAppProtocol)];
1476 list = [proxy serverList];
1478 @catch (NSException *e) {
1479 NSLog(@"Exception caught when listing servers: \"%@\"", e);
1482 EMSG(_("E???: No connection to MacVim, server listing not possible."));
1488 - (NSString *)peekForReplyOnPort:(int)port
1490 //NSLog(@"%s%d", _cmd, port);
1492 NSNumber *key = [NSNumber numberWithInt:port];
1493 NSMutableArray *replies = [serverReplyDict objectForKey:key];
1494 if (replies && [replies count]) {
1495 //NSLog(@" %d replies, topmost is: %@", [replies count],
1496 // [replies objectAtIndex:0]);
1497 return [replies objectAtIndex:0];
1500 //NSLog(@" No replies");
1504 - (NSString *)waitForReplyOnPort:(int)port
1506 //NSLog(@"%s%d", _cmd, port);
1508 NSConnection *conn = [self connectionForServerPort:port];
1512 NSNumber *key = [NSNumber numberWithInt:port];
1513 NSMutableArray *replies = nil;
1514 NSString *reply = nil;
1516 // Wait for reply as long as the connection to the server is valid (unless
1517 // user interrupts wait with Ctrl-C).
1518 while (!got_int && [conn isValid] &&
1519 !(replies = [serverReplyDict objectForKey:key])) {
1520 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
1521 beforeDate:[NSDate distantFuture]];
1525 if ([replies count] > 0) {
1526 reply = [[replies objectAtIndex:0] retain];
1527 //NSLog(@" Got reply: %@", reply);
1528 [replies removeObjectAtIndex:0];
1529 [reply autorelease];
1532 if ([replies count] == 0)
1533 [serverReplyDict removeObjectForKey:key];
1539 - (BOOL)sendReply:(NSString *)reply toPort:(int)port
1541 id client = [clientProxyDict objectForKey:[NSNumber numberWithInt:port]];
1544 //NSLog(@"sendReply:%@ toPort:%d", reply, port);
1545 [client addReply:reply server:self];
1548 @catch (NSException *e) {
1549 NSLog(@"WARNING: Exception caught in %s: \"%@\"", _cmd, e);
1552 EMSG2(_("E???: server2client failed; no client with id 0x%x"), port);
1562 @implementation MMBackend (Private)
1564 - (void)queueVimStateMessage
1566 // NOTE: This is the place to add Vim state that needs to be accessed from
1567 // MacVim. Do not add state that could potentially require lots of memory
1568 // since this message gets sent each time the output queue is forcibly
1569 // flushed (e.g. storing the currently selected text would be a bad idea).
1570 // We take this approach of "pushing" the state to MacVim to avoid having
1571 // to make synchronous calls from MacVim to Vim in order to get state.
1572 NSDictionary *vimState = [NSDictionary dictionaryWithObjectsAndKeys:
1573 [[NSFileManager defaultManager] currentDirectoryPath], @"pwd",
1576 [self queueMessage:SetVimStateMsgID data:[vimState dictionaryAsData]];
1579 - (void)processInputQueue
1581 // NOTE: One of the input events may cause this method to be called
1582 // recursively, so copy the input queue to a local variable and clear it
1583 // before starting to process input events (otherwise we could get stuck in
1584 // an endless loop).
1585 NSArray *q = [inputQueue copy];
1586 unsigned i, count = [q count];
1588 [inputQueue removeAllObjects];
1590 for (i = 0; i < count-1; i += 2) {
1591 int msgid = [[q objectAtIndex:i] intValue];
1592 id data = [q objectAtIndex:i+1];
1593 if ([data isEqual:[NSNull null]])
1596 //NSLog(@"(%d) %s:%s", i, _cmd, MessageStrings[msgid]);
1597 [self handleInputEvent:msgid data:data];
1601 //NSLog(@"Clear input event queue");
1604 - (void)handleInputEvent:(int)msgid data:(NSData *)data
1606 // NOTE: Be careful with what you do in this method. Ideally, a message
1607 // should be handled by adding something to the input buffer and returning
1608 // immediately. If you call a Vim function then it should not enter a loop
1609 // waiting for key presses or in any other way block the process. The
1610 // reason for this being that only one message can be processed at a time,
1611 // so if another message is received while processing, then the new message
1612 // is dropped. See also the comment in processInput:data:.
1614 //NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
1616 if (SelectTabMsgID == msgid) {
1618 const void *bytes = [data bytes];
1619 int idx = *((int*)bytes) + 1;
1620 //NSLog(@"Selecting tab %d", idx);
1621 send_tabline_event(idx);
1622 } else if (CloseTabMsgID == msgid) {
1624 const void *bytes = [data bytes];
1625 int idx = *((int*)bytes) + 1;
1626 //NSLog(@"Closing tab %d", idx);
1627 send_tabline_menu_event(idx, TABLINE_MENU_CLOSE);
1628 } else if (AddNewTabMsgID == msgid) {
1629 //NSLog(@"Adding new tab");
1630 send_tabline_menu_event(0, TABLINE_MENU_NEW);
1631 } else if (DraggedTabMsgID == msgid) {
1633 const void *bytes = [data bytes];
1634 // NOTE! The destination index is 0 based, so do not add 1 to make it 1
1636 int idx = *((int*)bytes);
1639 } else if (SetTextDimensionsMsgID == msgid || LiveResizeMsgID == msgid) {
1641 const void *bytes = [data bytes];
1642 int rows = *((int*)bytes); bytes += sizeof(int);
1643 int cols = *((int*)bytes); bytes += sizeof(int);
1645 // NOTE! Vim doesn't call gui_mch_set_shellsize() after
1646 // gui_resize_shell(), so we have to manually set the rows and columns
1647 // here. (MacVim doesn't change the rows and columns to avoid
1648 // inconsistent states between Vim and MacVim.)
1649 [self queueMessage:msgid data:data];
1651 //NSLog(@"[VimTask] Resizing shell to %dx%d.", cols, rows);
1652 gui_resize_shell(cols, rows);
1653 } else if (ExecuteMenuMsgID == msgid) {
1655 const void *bytes = [data bytes];
1656 int tag = *((int*)bytes); bytes += sizeof(int);
1658 vimmenu_T *menu = (vimmenu_T*)tag;
1659 // TODO! Make sure 'menu' is a valid menu pointer!
1663 } else if (ToggleToolbarMsgID == msgid) {
1664 [self handleToggleToolbar];
1665 } else if (ScrollbarEventMsgID == msgid) {
1666 [self handleScrollbarEvent:data];
1667 } else if (SetFontMsgID == msgid) {
1668 [self handleSetFont:data];
1669 } else if (VimShouldCloseMsgID == msgid) {
1671 } else if (DropFilesMsgID == msgid) {
1672 [self handleDropFiles:data];
1673 } else if (DropStringMsgID == msgid) {
1674 [self handleDropString:data];
1675 } else if (GotFocusMsgID == msgid) {
1677 [self focusChange:YES];
1678 } else if (LostFocusMsgID == msgid) {
1680 [self focusChange:NO];
1681 } else if (SetMouseShapeMsgID == msgid) {
1682 const void *bytes = [data bytes];
1683 int shape = *((int*)bytes); bytes += sizeof(int);
1684 update_mouseshape(shape);
1685 } else if (ODBEditMsgID == msgid) {
1686 [self handleOdbEdit:data];
1687 } else if (XcodeModMsgID == msgid) {
1688 [self handleXcodeMod:data];
1690 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
1694 + (NSDictionary *)specialKeys
1696 static NSDictionary *specialKeys = nil;
1699 NSBundle *mainBundle = [NSBundle mainBundle];
1700 NSString *path = [mainBundle pathForResource:@"SpecialKeys"
1702 specialKeys = [[NSDictionary alloc] initWithContentsOfFile:path];
1708 - (void)handleInsertText:(NSData *)data
1712 NSString *key = [[NSString alloc] initWithData:data
1713 encoding:NSUTF8StringEncoding];
1714 char_u *str = (char_u*)[key UTF8String];
1715 int i, len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1718 char_u *conv_str = NULL;
1719 if (input_conv.vc_type != CONV_NONE) {
1720 conv_str = string_convert(&input_conv, str, &len);
1726 if (len == 1 && ((str[0] == Ctrl_C && ctrl_c_interrupts)
1727 || (str[0] == intr_char && intr_char != Ctrl_C))) {
1732 for (i = 0; i < len; ++i) {
1733 add_to_input_buf(str+i, 1);
1734 if (CSI == str[i]) {
1735 // NOTE: If the converted string contains the byte CSI, then it
1736 // must be followed by the bytes KS_EXTRA, KE_CSI or things
1738 static char_u extra[2] = { KS_EXTRA, KE_CSI };
1739 add_to_input_buf(extra, 2);
1750 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods
1754 char_u *chars = (char_u*)[key UTF8String];
1756 char_u *conv_str = NULL;
1758 int length = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1760 // Special keys (arrow keys, function keys, etc.) are stored in a plist so
1761 // that new keys can easily be added.
1762 NSString *specialString = [[MMBackend specialKeys]
1764 if (specialString && [specialString length] > 1) {
1765 //NSLog(@"special key: %@", specialString);
1766 int ikey = TO_SPECIAL([specialString characterAtIndex:0],
1767 [specialString characterAtIndex:1]);
1769 ikey = simplify_key(ikey, &mods);
1774 special[1] = K_SECOND(ikey);
1775 special[2] = K_THIRD(ikey);
1779 } else if (1 == length && TAB == chars[0]) {
1780 // Tab is a trouble child:
1781 // - <Tab> is added to the input buffer as is
1782 // - <S-Tab> is translated to, {CSI,'k','B'} (i.e. 'Back-tab')
1783 // - <M-Tab> should be 0x80|TAB but this is not valid utf-8 so it needs
1784 // to be converted to utf-8
1785 // - <S-M-Tab> is translated to <S-Tab> with ALT modifier
1786 // - <C-Tab> is reserved by Mac OS X
1787 // - <D-Tab> is reserved by Mac OS X
1792 if (mods & MOD_MASK_SHIFT) {
1793 mods &= ~MOD_MASK_SHIFT;
1795 special[1] = K_SECOND(K_S_TAB);
1796 special[2] = K_THIRD(K_S_TAB);
1798 } else if (mods & MOD_MASK_ALT) {
1799 int mtab = 0x80 | TAB;
1803 special[0] = (mtab >> 6) + 0xc0;
1804 special[1] = mtab & 0xbf;
1812 mods &= ~MOD_MASK_ALT;
1814 } else if (length > 0) {
1815 unichar c = [key characterAtIndex:0];
1817 //NSLog(@"non-special: %@ (hex=%x, mods=%d)", key,
1818 // [key characterAtIndex:0], mods);
1820 if (length == 1 && ((c == Ctrl_C && ctrl_c_interrupts)
1821 || (c == intr_char && intr_char != Ctrl_C))) {
1826 // HACK! In most circumstances the Ctrl and Shift modifiers should be
1827 // cleared since they are already added to the key by the AppKit.
1828 // Unfortunately, the only way to deal with when to clear the modifiers
1829 // or not seems to be to have hard-wired rules like this.
1830 if ( !((' ' == c) || (0xa0 == c) || (mods & MOD_MASK_CMD)
1831 || 0x9 == c || 0xd == c || ESC == c) ) {
1832 mods &= ~MOD_MASK_SHIFT;
1833 mods &= ~MOD_MASK_CTRL;
1834 //NSLog(@"clear shift ctrl");
1837 // HACK! All Option+key presses go via 'insert text' messages, except
1838 // for <M-Space>. If the Alt flag is not cleared for <M-Space> it does
1839 // not work to map to it.
1840 if (0xa0 == c && !(mods & MOD_MASK_CMD)) {
1841 //NSLog(@"clear alt");
1842 mods &= ~MOD_MASK_ALT;
1846 if (input_conv.vc_type != CONV_NONE) {
1847 conv_str = string_convert(&input_conv, chars, &length);
1854 if (chars && length > 0) {
1856 //NSLog(@"adding mods: %d", mods);
1858 modChars[1] = KS_MODIFIER;
1860 add_to_input_buf(modChars, 3);
1863 //NSLog(@"add to input buf: 0x%x", chars[0]);
1864 // TODO: Check for CSI bytes?
1865 add_to_input_buf(chars, length);
1874 - (void)queueMessage:(int)msgid data:(NSData *)data
1876 [outputQueue addObject:[NSData dataWithBytes:&msgid length:sizeof(int)]];
1878 [outputQueue addObject:data];
1880 [outputQueue addObject:[NSData data]];
1883 - (void)connectionDidDie:(NSNotification *)notification
1885 // If the main connection to MacVim is lost this means that MacVim was
1886 // either quit (by the user chosing Quit on the MacVim menu), or it has
1887 // crashed. In the former case the flag 'isTerminating' is set and we then
1888 // quit cleanly; in the latter case we make sure the swap files are left
1891 //NSLog(@"%s isTerminating=%d", _cmd, isTerminating);
1895 getout_preserve_modified(1);
1898 - (void)blinkTimerFired:(NSTimer *)timer
1900 NSTimeInterval timeInterval = 0;
1902 [blinkTimer release];
1905 if (MMBlinkStateOn == blinkState) {
1906 gui_undraw_cursor();
1907 blinkState = MMBlinkStateOff;
1908 timeInterval = blinkOffInterval;
1909 } else if (MMBlinkStateOff == blinkState) {
1910 gui_update_cursor(TRUE, FALSE);
1911 blinkState = MMBlinkStateOn;
1912 timeInterval = blinkOnInterval;
1915 if (timeInterval > 0) {
1917 [[NSTimer scheduledTimerWithTimeInterval:timeInterval target:self
1918 selector:@selector(blinkTimerFired:)
1919 userInfo:nil repeats:NO] retain];
1920 [self flushQueue:YES];
1924 - (void)focusChange:(BOOL)on
1926 gui_focus_change(on);
1929 - (void)handleToggleToolbar
1931 // If 'go' contains 'T', then remove it, else add it.
1933 char_u go[sizeof(GO_ALL)+2];
1938 p = vim_strchr(go, GO_TOOLBAR);
1942 char_u *end = go + len;
1948 go[len] = GO_TOOLBAR;
1952 set_option_value((char_u*)"guioptions", 0, go, 0);
1954 // Force screen redraw (does it have to be this complicated?).
1955 redraw_all_later(CLEAR);
1956 update_screen(NOT_VALID);
1959 gui_update_cursor(FALSE, FALSE);
1963 - (void)handleScrollbarEvent:(NSData *)data
1967 const void *bytes = [data bytes];
1968 long ident = *((long*)bytes); bytes += sizeof(long);
1969 int hitPart = *((int*)bytes); bytes += sizeof(int);
1970 float fval = *((float*)bytes); bytes += sizeof(float);
1971 scrollbar_T *sb = gui_find_scrollbar(ident);
1974 scrollbar_T *sb_info = sb->wp ? &sb->wp->w_scrollbars[0] : sb;
1975 long value = sb_info->value;
1976 long size = sb_info->size;
1977 long max = sb_info->max;
1978 BOOL isStillDragging = NO;
1979 BOOL updateKnob = YES;
1982 case NSScrollerDecrementPage:
1983 value -= (size > 2 ? size - 2 : 1);
1985 case NSScrollerIncrementPage:
1986 value += (size > 2 ? size - 2 : 1);
1988 case NSScrollerDecrementLine:
1991 case NSScrollerIncrementLine:
1994 case NSScrollerKnob:
1995 isStillDragging = YES;
1997 case NSScrollerKnobSlot:
1998 value = (long)(fval * (max - size + 1));
2005 //NSLog(@"value %d -> %d", sb_info->value, value);
2006 gui_drag_scrollbar(sb, value, isStillDragging);
2009 // Dragging the knob or option+clicking automatically updates
2010 // the knob position (on the actual NSScroller), so we only
2011 // need to set the knob position in the other cases.
2013 // Update both the left&right vertical scrollbars.
2014 long identLeft = sb->wp->w_scrollbars[SBAR_LEFT].ident;
2015 long identRight = sb->wp->w_scrollbars[SBAR_RIGHT].ident;
2016 [self setScrollbarThumbValue:value size:size max:max
2017 identifier:identLeft];
2018 [self setScrollbarThumbValue:value size:size max:max
2019 identifier:identRight];
2021 // Update the horizontal scrollbar.
2022 [self setScrollbarThumbValue:value size:size max:max
2029 - (void)handleSetFont:(NSData *)data
2033 const void *bytes = [data bytes];
2034 float pointSize = *((float*)bytes); bytes += sizeof(float);
2035 //unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned);
2036 bytes += sizeof(unsigned); // len not used
2038 NSMutableString *name = [NSMutableString stringWithUTF8String:bytes];
2039 [name appendString:[NSString stringWithFormat:@":h%.2f", pointSize]];
2040 char_u *s = (char_u*)[name UTF8String];
2043 s = CONVERT_FROM_UTF8(s);
2046 set_option_value((char_u*)"guifont", 0, s, 0);
2049 CONVERT_FROM_UTF8_FREE(s);
2052 // Force screen redraw (does it have to be this complicated?).
2053 redraw_all_later(CLEAR);
2054 update_screen(NOT_VALID);
2057 gui_update_cursor(FALSE, FALSE);
2061 - (void)handleDropFiles:(NSData *)data
2063 // TODO: Get rid of this method; instead use Vim script directly. At the
2064 // moment I know how to do this to open files in tabs, but I'm not sure how
2065 // to add the filenames to the command line when in command line mode.
2070 const void *bytes = [data bytes];
2071 const void *end = [data bytes] + [data length];
2072 BOOL forceOpen = *((BOOL*)bytes); bytes += sizeof(BOOL);
2073 int n = *((int*)bytes); bytes += sizeof(int);
2075 if (!forceOpen && (State & CMDLINE)) {
2076 // HACK! If Vim is in command line mode then the files names
2077 // should be added to the command line, instead of opening the
2078 // files in tabs (unless forceOpen is set). This is taken care of by
2079 // gui_handle_drop().
2080 char_u **fnames = (char_u **)alloc(n * sizeof(char_u *));
2083 while (bytes < end && i < n) {
2084 int len = *((int*)bytes); bytes += sizeof(int);
2085 char_u *s = (char_u*)bytes;
2087 s = CONVERT_FROM_UTF8(s);
2089 fnames[i++] = vim_strsave(s);
2091 CONVERT_FROM_UTF8_FREE(s);
2096 // NOTE! This function will free 'fnames'.
2097 // HACK! It is assumed that the 'x' and 'y' arguments are
2098 // unused when in command line mode.
2099 gui_handle_drop(0, 0, 0, fnames, i < n ? i : n);
2102 // HACK! I'm not sure how to get Vim to open a list of files in
2103 // tabs, so instead I create a ':tab drop' command with all the
2104 // files to open and execute it.
2105 NSMutableString *cmd = [NSMutableString stringWithString:@":tab drop"];
2108 for (i = 0; i < n && bytes < end; ++i) {
2109 int len = *((int*)bytes); bytes += sizeof(int);
2110 NSString *file = [NSString stringWithUTF8String:bytes];
2111 file = [file stringByEscapingSpecialFilenameCharacters];
2114 [cmd appendString:@" "];
2115 [cmd appendString:file];
2118 // By going to the last tabpage we ensure that the new tabs will
2119 // appear last (if this call is left out, the taborder becomes
2123 char_u *s = (char_u*)[cmd UTF8String];
2125 s = CONVERT_FROM_UTF8(s);
2129 CONVERT_FROM_UTF8_FREE(s);
2132 // Force screen redraw (does it have to be this complicated?).
2133 // (This code was taken from the end of gui_handle_drop().)
2134 update_screen(NOT_VALID);
2137 gui_update_cursor(FALSE, FALSE);
2144 - (void)handleDropString:(NSData *)data
2149 char_u dropkey[3] = { CSI, KS_EXTRA, (char_u)KE_DROP };
2150 const void *bytes = [data bytes];
2151 int len = *((int*)bytes); bytes += sizeof(int);
2152 NSMutableString *string = [NSMutableString stringWithUTF8String:bytes];
2154 // Replace unrecognized end-of-line sequences with \x0a (line feed).
2155 NSRange range = { 0, [string length] };
2156 unsigned n = [string replaceOccurrencesOfString:@"\x0d\x0a"
2157 withString:@"\x0a" options:0
2160 n = [string replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
2161 options:0 range:range];
2164 len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
2165 char_u *s = (char_u*)[string UTF8String];
2167 if (input_conv.vc_type != CONV_NONE)
2168 s = string_convert(&input_conv, s, &len);
2170 dnd_yank_drag_data(s, len);
2172 if (input_conv.vc_type != CONV_NONE)
2175 add_to_input_buf(dropkey, sizeof(dropkey));
2179 - (void)handleOdbEdit:(NSData *)data
2181 #ifdef FEAT_ODB_EDITOR
2182 const void *bytes = [data bytes];
2184 OSType serverID = *((OSType*)bytes); bytes += sizeof(OSType);
2186 char_u *path = NULL;
2187 int pathLen = *((int*)bytes); bytes += sizeof(int);
2189 path = (char_u*)bytes;
2192 path = CONVERT_FROM_UTF8(path);
2196 NSAppleEventDescriptor *token = nil;
2197 DescType tokenType = *((DescType*)bytes); bytes += sizeof(DescType);
2198 int descLen = *((int*)bytes); bytes += sizeof(int);
2200 token = [NSAppleEventDescriptor descriptorWithDescriptorType:tokenType
2206 unsigned i, numFiles = *((unsigned*)bytes); bytes += sizeof(unsigned);
2207 for (i = 0; i < numFiles; ++i) {
2208 int len = *((int*)bytes); bytes += sizeof(int);
2209 char_u *filename = (char_u*)bytes;
2211 filename = CONVERT_FROM_UTF8(filename);
2213 buf_T *buf = buflist_findname(filename);
2215 if (buf->b_odb_token) {
2216 [(NSAppleEventDescriptor*)(buf->b_odb_token) release];
2217 buf->b_odb_token = NULL;
2220 if (buf->b_odb_fname) {
2221 vim_free(buf->b_odb_fname);
2222 buf->b_odb_fname = NULL;
2225 buf->b_odb_server_id = serverID;
2228 buf->b_odb_token = [token retain];
2230 buf->b_odb_fname = vim_strsave(path);
2232 NSLog(@"WARNING: Could not find buffer '%s' for ODB editing.",
2237 CONVERT_FROM_UTF8_FREE(filename);
2242 CONVERT_FROM_UTF8_FREE(path);
2244 #endif // FEAT_ODB_EDITOR
2247 - (void)handleXcodeMod:(NSData *)data
2250 const void *bytes = [data bytes];
2251 DescType type = *((DescType*)bytes); bytes += sizeof(DescType);
2252 unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned);
2256 NSAppleEventDescriptor *replyEvent = [NSAppleEventDescriptor
2257 descriptorWithDescriptorType:type
2263 - (BOOL)checkForModifiedBuffers
2266 for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
2267 if (bufIsChanged(buf)) {
2275 - (void)addInput:(NSString *)input
2277 char_u *s = (char_u*)[input UTF8String];
2280 s = CONVERT_FROM_UTF8(s);
2283 server_to_input_buf(s);
2286 CONVERT_FROM_UTF8_FREE(s);
2290 @end // MMBackend (Private)
2295 @implementation MMBackend (ClientServer)
2297 - (NSString *)connectionNameFromServerName:(NSString *)name
2299 NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
2301 return [[NSString stringWithFormat:@"%@.%@", bundleIdentifier, name]
2305 - (NSConnection *)connectionForServerName:(NSString *)name
2307 // TODO: Try 'name%d' if 'name' fails.
2308 NSString *connName = [self connectionNameFromServerName:name];
2309 NSConnection *svrConn = [connectionNameDict objectForKey:connName];
2312 svrConn = [NSConnection connectionWithRegisteredName:connName
2314 // Try alternate server...
2315 if (!svrConn && alternateServerName) {
2316 //NSLog(@" trying to connect to alternate server: %@",
2317 // alternateServerName);
2318 connName = [self connectionNameFromServerName:alternateServerName];
2319 svrConn = [NSConnection connectionWithRegisteredName:connName
2323 // Try looking for alternate servers...
2325 //NSLog(@" looking for alternate servers...");
2326 NSString *alt = [self alternateServerNameForName:name];
2327 if (alt != alternateServerName) {
2328 //NSLog(@" found alternate server: %@", string);
2329 [alternateServerName release];
2330 alternateServerName = [alt copy];
2334 // Try alternate server again...
2335 if (!svrConn && alternateServerName) {
2336 //NSLog(@" trying to connect to alternate server: %@",
2337 // alternateServerName);
2338 connName = [self connectionNameFromServerName:alternateServerName];
2339 svrConn = [NSConnection connectionWithRegisteredName:connName
2344 [connectionNameDict setObject:svrConn forKey:connName];
2346 //NSLog(@"Adding %@ as connection observer for %@", self, svrConn);
2347 [[NSNotificationCenter defaultCenter] addObserver:self
2348 selector:@selector(serverConnectionDidDie:)
2349 name:NSConnectionDidDieNotification object:svrConn];
2356 - (NSConnection *)connectionForServerPort:(int)port
2359 NSEnumerator *e = [connectionNameDict objectEnumerator];
2361 while ((conn = [e nextObject])) {
2362 // HACK! Assume connection uses mach ports.
2363 if (port == [(NSMachPort*)[conn sendPort] machPort])
2370 - (void)serverConnectionDidDie:(NSNotification *)notification
2372 //NSLog(@"%s%@", _cmd, notification);
2374 NSConnection *svrConn = [notification object];
2376 //NSLog(@"Removing %@ as connection observer from %@", self, svrConn);
2377 [[NSNotificationCenter defaultCenter]
2379 name:NSConnectionDidDieNotification
2382 [connectionNameDict removeObjectsForKeys:
2383 [connectionNameDict allKeysForObject:svrConn]];
2385 // HACK! Assume connection uses mach ports.
2386 int port = [(NSMachPort*)[svrConn sendPort] machPort];
2387 NSNumber *key = [NSNumber numberWithInt:port];
2389 [clientProxyDict removeObjectForKey:key];
2390 [serverReplyDict removeObjectForKey:key];
2393 - (void)addClient:(NSDistantObject *)client
2395 NSConnection *conn = [client connectionForProxy];
2396 // HACK! Assume connection uses mach ports.
2397 int port = [(NSMachPort*)[conn sendPort] machPort];
2398 NSNumber *key = [NSNumber numberWithInt:port];
2400 if (![clientProxyDict objectForKey:key]) {
2401 [client setProtocolForProxy:@protocol(MMVimClientProtocol)];
2402 [clientProxyDict setObject:client forKey:key];
2405 // NOTE: 'clientWindow' is a global variable which is used by <client>
2406 clientWindow = port;
2409 - (NSString *)alternateServerNameForName:(NSString *)name
2411 if (!(name && [name length] > 0))
2414 // Only look for alternates if 'name' doesn't end in a digit.
2415 unichar lastChar = [name characterAtIndex:[name length]-1];
2416 if (lastChar >= '0' && lastChar <= '9')
2419 // Look for alternates among all current servers.
2420 NSArray *list = [self serverList];
2421 if (!(list && [list count] > 0))
2424 // Filter out servers starting with 'name' and ending with a number. The
2425 // (?i) pattern ensures that the match is case insensitive.
2426 NSString *pat = [NSString stringWithFormat:@"(?i)%@[0-9]+\\z", name];
2427 NSPredicate *pred = [NSPredicate predicateWithFormat:
2428 @"SELF MATCHES %@", pat];
2429 list = [list filteredArrayUsingPredicate:pred];
2430 if ([list count] > 0) {
2431 list = [list sortedArrayUsingSelector:@selector(serverNameCompare:)];
2432 return [list objectAtIndex:0];
2438 @end // MMBackend (ClientServer)
2443 @implementation NSString (MMServerNameCompare)
2444 - (NSComparisonResult)serverNameCompare:(NSString *)string
2446 return [self compare:string
2447 options:NSCaseInsensitiveSearch|NSNumericSearch];
2454 static int eventModifierFlagsToVimModMask(int modifierFlags)
2458 if (modifierFlags & NSShiftKeyMask)
2459 modMask |= MOD_MASK_SHIFT;
2460 if (modifierFlags & NSControlKeyMask)
2461 modMask |= MOD_MASK_CTRL;
2462 if (modifierFlags & NSAlternateKeyMask)
2463 modMask |= MOD_MASK_ALT;
2464 if (modifierFlags & NSCommandKeyMask)
2465 modMask |= MOD_MASK_CMD;
2470 static int vimModMaskToEventModifierFlags(int mods)
2474 if (mods & MOD_MASK_SHIFT)
2475 flags |= NSShiftKeyMask;
2476 if (mods & MOD_MASK_CTRL)
2477 flags |= NSControlKeyMask;
2478 if (mods & MOD_MASK_ALT)
2479 flags |= NSAlternateKeyMask;
2480 if (mods & MOD_MASK_CMD)
2481 flags |= NSCommandKeyMask;
2486 static int eventModifierFlagsToVimMouseModMask(int modifierFlags)
2490 if (modifierFlags & NSShiftKeyMask)
2491 modMask |= MOUSE_SHIFT;
2492 if (modifierFlags & NSControlKeyMask)
2493 modMask |= MOUSE_CTRL;
2494 if (modifierFlags & NSAlternateKeyMask)
2495 modMask |= MOUSE_ALT;
2500 static int eventButtonNumberToVimMouseButton(int buttonNumber)
2502 static int mouseButton[] = { MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE };
2504 return (buttonNumber >= 0 && buttonNumber < 3)
2505 ? mouseButton[buttonNumber] : -1;
2508 static int specialKeyToNSKey(int key)
2510 if (!IS_SPECIAL(key))
2517 { K_UP, NSUpArrowFunctionKey },
2518 { K_DOWN, NSDownArrowFunctionKey },
2519 { K_LEFT, NSLeftArrowFunctionKey },
2520 { K_RIGHT, NSRightArrowFunctionKey },
2521 { K_F1, NSF1FunctionKey },
2522 { K_F2, NSF2FunctionKey },
2523 { K_F3, NSF3FunctionKey },
2524 { K_F4, NSF4FunctionKey },
2525 { K_F5, NSF5FunctionKey },
2526 { K_F6, NSF6FunctionKey },
2527 { K_F7, NSF7FunctionKey },
2528 { K_F8, NSF8FunctionKey },
2529 { K_F9, NSF9FunctionKey },
2530 { K_F10, NSF10FunctionKey },
2531 { K_F11, NSF11FunctionKey },
2532 { K_F12, NSF12FunctionKey },
2533 { K_F13, NSF13FunctionKey },
2534 { K_F14, NSF14FunctionKey },
2535 { K_F15, NSF15FunctionKey },
2536 { K_F16, NSF16FunctionKey },
2537 { K_F17, NSF17FunctionKey },
2538 { K_F18, NSF18FunctionKey },
2539 { K_F19, NSF19FunctionKey },
2540 { K_F20, NSF20FunctionKey },
2541 { K_F21, NSF21FunctionKey },
2542 { K_F22, NSF22FunctionKey },
2543 { K_F23, NSF23FunctionKey },
2544 { K_F24, NSF24FunctionKey },
2545 { K_F25, NSF25FunctionKey },
2546 { K_F26, NSF26FunctionKey },
2547 { K_F27, NSF27FunctionKey },
2548 { K_F28, NSF28FunctionKey },
2549 { K_F29, NSF29FunctionKey },
2550 { K_F30, NSF30FunctionKey },
2551 { K_F31, NSF31FunctionKey },
2552 { K_F32, NSF32FunctionKey },
2553 { K_F33, NSF33FunctionKey },
2554 { K_F34, NSF34FunctionKey },
2555 { K_F35, NSF35FunctionKey },
2556 { K_DEL, NSBackspaceCharacter },
2557 { K_BS, NSDeleteCharacter },
2558 { K_HOME, NSHomeFunctionKey },
2559 { K_END, NSEndFunctionKey },
2560 { K_PAGEUP, NSPageUpFunctionKey },
2561 { K_PAGEDOWN, NSPageDownFunctionKey }
2565 for (i = 0; i < sizeof(sp2ns)/sizeof(sp2ns[0]); ++i) {
2566 if (sp2ns[i].special == key)
2567 return sp2ns[i].nskey;