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.
16 // This constant controls how often the command queue may be flushed. If it is
17 // too small the app might feel unresponsive; if it is too large there might be
18 // long periods without the screen updating (e.g. when sourcing a large session
19 // file). (The unit is seconds.)
20 static float MMFlushTimeoutInterval = 0.1f;
23 // TODO: Move to separate file.
24 static int eventModifierFlagsToVimModMask(int modifierFlags);
25 static int vimModMaskToEventModifierFlags(int mods);
26 static int eventModifierFlagsToVimMouseModMask(int modifierFlags);
27 static int eventButtonNumberToVimMouseButton(int buttonNumber);
28 static int specialKeyToNSKey(int key);
37 @interface MMBackend (Private)
38 - (void)handleMessage:(int)msgid data:(NSData *)data;
39 + (NSDictionary *)specialKeys;
40 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods;
41 - (void)queueMessage:(int)msgid data:(NSData *)data;
42 - (void)connectionDidDie:(NSNotification *)notification;
43 - (void)blinkTimerFired:(NSTimer *)timer;
44 - (void)focusChange:(BOOL)on;
45 - (void)processInputBegin;
46 - (void)processInputEnd;
51 @implementation MMBackend
53 + (MMBackend *)sharedInstance
55 static MMBackend *singleton = nil;
56 return singleton ? singleton : (singleton = [MMBackend new]);
61 if ((self = [super init])) {
62 queue = [[NSMutableArray alloc] init];
63 #if MM_USE_INPUT_QUEUE
64 inputQueue = [[NSMutableArray alloc] init];
66 drawData = [[NSMutableData alloc] initWithCapacity:1024];
68 NSString *path = [[NSBundle mainBundle] pathForResource:@"Colors"
71 colorDict = [[NSDictionary dictionaryWithContentsOfFile:path]
74 NSLog(@"WARNING: Could not locate Colors.plist.");
77 path = [[NSBundle mainBundle] pathForResource:@"SystemColors"
80 sysColorDict = [[NSDictionary dictionaryWithContentsOfFile:path]
83 NSLog(@"WARNING: Could not locate SystemColors.plist.");
92 //NSLog(@"%@ %s", [self className], _cmd);
94 [[NSNotificationCenter defaultCenter] removeObserver:self];
96 [blinkTimer release]; blinkTimer = nil;
97 #if MM_USE_INPUT_QUEUE
98 [inputQueue release]; inputQueue = nil;
100 [queue release]; queue = nil;
101 [drawData release]; drawData = nil;
102 [frontendProxy release]; frontendProxy = nil;
103 [connection release]; connection = nil;
104 [sysColorDict release]; sysColorDict = nil;
105 [colorDict release]; colorDict = nil;
110 - (void)setBackgroundColor:(int)color
112 backgroundColor = color;
115 - (void)setForegroundColor:(int)color
117 foregroundColor = color;
120 - (void)setSpecialColor:(int)color
122 specialColor = color;
125 - (void)setDefaultColorsBackground:(int)bg foreground:(int)fg
127 defaultBackgroundColor = bg;
128 defaultForegroundColor = fg;
130 NSMutableData *data = [NSMutableData data];
132 [data appendBytes:&bg length:sizeof(int)];
133 [data appendBytes:&fg length:sizeof(int)];
135 [self queueMessage:SetDefaultColorsMsgID data:data];
138 - (NSString *)macVimConnectionName
140 // NOTE! If the name of the connection changes here it must also be
141 // updated in MMAppController.m.
142 return [NSString stringWithFormat:@"%@-connection",
143 [[NSBundle mainBundle] bundleIdentifier]];
148 NSBundle *mainBundle = [NSBundle mainBundle];
150 NSString *name = [self macVimConnectionName];
151 connection = [NSConnection connectionWithRegisteredName:name host:nil];
154 NSString *path = [mainBundle bundlePath];
155 if (![[NSWorkspace sharedWorkspace] launchApplication:path]) {
156 NSLog(@"WARNING: Failed to launch GUI with path %@", path);
160 // HACK! It would be preferable to launch the GUI using NSWorkspace,
161 // however I have not managed to figure out how to pass arguments using
164 // NOTE! Using NSTask to launch the GUI has the negative side-effect
165 // that the GUI won't be activated (or raised) so there is a hack in
166 // MMWindowController which always raises the app when a new window is
168 NSMutableArray *args = [NSMutableArray arrayWithObjects:
169 [NSString stringWithFormat:@"-%@", MMNoWindowKey], @"yes", nil];
170 NSString *exeName = [[mainBundle infoDictionary]
171 objectForKey:@"CFBundleExecutable"];
172 NSString *path = [mainBundle pathForAuxiliaryExecutable:exeName];
174 NSLog(@"ERROR: Could not find MacVim executable in bundle");
178 [NSTask launchedTaskWithLaunchPath:path arguments:args];
181 // HACK! The NSWorkspaceDidLaunchApplicationNotification does not work
182 // for tasks like this, so poll the mach bootstrap server until it
183 // returns a valid connection. Also set a time-out date so that we
184 // don't get stuck doing this forever.
185 NSDate *timeOutDate = [NSDate dateWithTimeIntervalSinceNow:15];
186 while (!connection &&
187 NSOrderedDescending == [timeOutDate compare:[NSDate date]])
189 [[NSRunLoop currentRunLoop]
190 runMode:NSDefaultRunLoopMode
191 beforeDate:[NSDate dateWithTimeIntervalSinceNow:1]];
193 connection = [NSConnection connectionWithRegisteredName:name
198 NSLog(@"WARNING: Timed-out waiting for GUI to launch.");
203 id proxy = [connection rootProxy];
204 [proxy setProtocolForProxy:@protocol(MMAppProtocol)];
206 [[NSNotificationCenter defaultCenter] addObserver:self
207 selector:@selector(connectionDidDie:)
208 name:NSConnectionDidDieNotification object:connection];
210 int pid = [[NSProcessInfo processInfo] processIdentifier];
213 frontendProxy = [(NSDistantObject*)[proxy connectBackend:self
216 @catch (NSException *e) {
217 NSLog(@"Exception caught when trying to connect backend: \"%@\"", e);
221 [frontendProxy setProtocolForProxy:@protocol(MMAppProtocol)];
224 return connection && frontendProxy;
227 - (BOOL)openVimWindow
229 [self queueMessage:OpenVimWindowMsgID data:nil];
235 int type = ClearAllDrawType;
237 // Any draw commands in queue are effectively obsolete since this clearAll
238 // will negate any effect they have, therefore we may as well clear the
240 [drawData setLength:0];
242 [drawData appendBytes:&type length:sizeof(int)];
244 [drawData appendBytes:&defaultBackgroundColor length:sizeof(int)];
247 - (void)clearBlockFromRow:(int)row1 column:(int)col1
248 toRow:(int)row2 column:(int)col2
250 int type = ClearBlockDrawType;
252 [drawData appendBytes:&type length:sizeof(int)];
254 [drawData appendBytes:&defaultBackgroundColor length:sizeof(int)];
255 [drawData appendBytes:&row1 length:sizeof(int)];
256 [drawData appendBytes:&col1 length:sizeof(int)];
257 [drawData appendBytes:&row2 length:sizeof(int)];
258 [drawData appendBytes:&col2 length:sizeof(int)];
261 - (void)deleteLinesFromRow:(int)row count:(int)count
262 scrollBottom:(int)bottom left:(int)left right:(int)right
264 int type = DeleteLinesDrawType;
266 [drawData appendBytes:&type length:sizeof(int)];
268 [drawData appendBytes:&defaultBackgroundColor length:sizeof(int)];
269 [drawData appendBytes:&row length:sizeof(int)];
270 [drawData appendBytes:&count length:sizeof(int)];
271 [drawData appendBytes:&bottom length:sizeof(int)];
272 [drawData appendBytes:&left length:sizeof(int)];
273 [drawData appendBytes:&right length:sizeof(int)];
276 - (void)replaceString:(char*)s length:(int)len row:(int)row column:(int)col
279 if (len <= 0) return;
281 int type = ReplaceStringDrawType;
283 [drawData appendBytes:&type length:sizeof(int)];
285 [drawData appendBytes:&backgroundColor length:sizeof(int)];
286 [drawData appendBytes:&foregroundColor length:sizeof(int)];
287 [drawData appendBytes:&specialColor length:sizeof(int)];
288 [drawData appendBytes:&row length:sizeof(int)];
289 [drawData appendBytes:&col length:sizeof(int)];
290 [drawData appendBytes:&flags length:sizeof(int)];
291 [drawData appendBytes:&len length:sizeof(int)];
292 [drawData appendBytes:s length:len];
295 - (void)insertLinesFromRow:(int)row count:(int)count
296 scrollBottom:(int)bottom left:(int)left right:(int)right
298 int type = InsertLinesDrawType;
300 [drawData appendBytes:&type length:sizeof(int)];
302 [drawData appendBytes:&defaultBackgroundColor length:sizeof(int)];
303 [drawData appendBytes:&row length:sizeof(int)];
304 [drawData appendBytes:&count length:sizeof(int)];
305 [drawData appendBytes:&bottom length:sizeof(int)];
306 [drawData appendBytes:&left length:sizeof(int)];
307 [drawData appendBytes:&right length:sizeof(int)];
310 - (void)drawCursorAtRow:(int)row column:(int)col shape:(int)shape
311 fraction:(int)percent color:(int)color
313 int type = DrawCursorDrawType;
315 [drawData appendBytes:&type length:sizeof(int)];
317 [drawData appendBytes:&color length:sizeof(int)];
318 [drawData appendBytes:&row length:sizeof(int)];
319 [drawData appendBytes:&col length:sizeof(int)];
320 [drawData appendBytes:&shape length:sizeof(int)];
321 [drawData appendBytes:&percent length:sizeof(int)];
324 - (void)flushQueue:(BOOL)force
326 // NOTE! This method gets called a lot; if we were to flush every time it
327 // was called MacVim would feel unresponsive. So there is a time out which
328 // ensures that the queue isn't flushed too often.
329 if (!force && lastFlushDate && -[lastFlushDate timeIntervalSinceNow]
330 < MMFlushTimeoutInterval)
333 if ([drawData length] > 0) {
334 [self queueMessage:BatchDrawMsgID data:[drawData copy]];
335 [drawData setLength:0];
338 if ([queue count] > 0) {
340 [frontendProxy processCommandQueue:queue];
342 @catch (NSException *e) {
343 NSLog(@"Exception caught when processing command queue: \"%@\"", e);
346 [queue removeAllObjects];
348 [lastFlushDate release];
349 lastFlushDate = [[NSDate date] retain];
353 - (BOOL)waitForInput:(int)milliseconds
355 NSDate *date = milliseconds > 0 ?
356 [NSDate dateWithTimeIntervalSinceNow:.001*milliseconds] :
357 [NSDate distantFuture];
359 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:date];
361 // I know of no way to figure out if the run loop exited because input was
362 // found or because of a time out, so I need to manually indicate when
363 // input was received in processInput:data: and then reset it every time
365 BOOL yn = inputReceived;
373 #ifdef MAC_CLIENTSERVER
374 // The default connection is used for the client/server code.
375 [[NSConnection defaultConnection] setRootObject:nil];
376 [[NSConnection defaultConnection] invalidate];
379 // By invalidating the NSConnection the MMWindowController immediately
380 // finds out that the connection is down and as a result
381 // [MMWindowController connectionDidDie:] is invoked.
382 //NSLog(@"%@ %s", [self className], _cmd);
383 [[NSNotificationCenter defaultCenter] removeObserver:self];
384 [connection invalidate];
387 - (void)selectTab:(int)index
389 //NSLog(@"%s%d", _cmd, index);
392 NSData *data = [NSData dataWithBytes:&index length:sizeof(int)];
393 [self queueMessage:SelectTabMsgID data:data];
398 //NSLog(@"%s", _cmd);
400 NSMutableData *data = [NSMutableData data];
402 int idx = tabpage_index(curtab) - 1;
403 [data appendBytes:&idx length:sizeof(int)];
406 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
407 // This function puts the label of the tab in the global 'NameBuff'.
408 get_tabline_label(tp, FALSE);
409 int len = strlen((char*)NameBuff);
410 if (len <= 0) continue;
412 // Count the number of windows in the tabpage.
413 //win_T *wp = tp->tp_firstwin;
415 //for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount);
417 //[data appendBytes:&wincount length:sizeof(int)];
418 [data appendBytes:&len length:sizeof(int)];
419 [data appendBytes:NameBuff length:len];
422 [self queueMessage:UpdateTabBarMsgID data:data];
425 - (BOOL)tabBarVisible
427 return tabBarVisible;
430 - (void)showTabBar:(BOOL)enable
432 tabBarVisible = enable;
434 int msgid = enable ? ShowTabBarMsgID : HideTabBarMsgID;
435 [self queueMessage:msgid data:nil];
438 - (void)setRows:(int)rows columns:(int)cols
440 //NSLog(@"[VimTask] setRows:%d columns:%d", rows, cols);
442 int dim[] = { rows, cols };
443 NSData *data = [NSData dataWithBytes:&dim length:2*sizeof(int)];
445 [self queueMessage:SetTextDimensionsMsgID data:data];
448 - (void)setVimWindowTitle:(char *)title
450 NSMutableData *data = [NSMutableData data];
451 int len = strlen(title);
452 if (len <= 0) return;
454 [data appendBytes:&len length:sizeof(int)];
455 [data appendBytes:title length:len];
457 [self queueMessage:SetVimWindowTitleMsgID data:data];
460 - (char *)browseForFileInDirectory:(char *)dir title:(char *)title
463 //NSLog(@"browseForFileInDirectory:%s title:%s saving:%d", dir, title,
468 ? [NSString stringWithCString:dir encoding:NSUTF8StringEncoding]
471 ? [NSString stringWithCString:title encoding:NSUTF8StringEncoding]
474 [frontendProxy showSavePanelForDirectory:ds title:ts saving:saving];
476 // Wait until a reply is sent from MMVimController.
477 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
478 beforeDate:[NSDate distantFuture]];
480 if (dialogReturn && [dialogReturn isKindOfClass:[NSString class]]) {
481 s = vim_strsave((char_u*)[dialogReturn UTF8String]);
484 [dialogReturn release]; dialogReturn = nil;
486 @catch (NSException *e) {
487 NSLog(@"Exception caught when showing save panel: \"%@\"", e);
493 - (oneway void)setDialogReturn:(in bycopy id)obj
495 // NOTE: This is called by
496 // - [MMVimController panelDidEnd:::], and
497 // - [MMVimController alertDidEnd:::],
498 // to indicate that a save/open panel or alert has finished.
500 if (obj != dialogReturn) {
501 [dialogReturn release];
502 dialogReturn = [obj retain];
506 - (int)presentDialogWithType:(int)type title:(char *)title message:(char *)msg
507 buttons:(char *)btns textField:(char *)txtfield
510 NSString *message = nil, *text = nil, *textFieldString = nil;
511 NSArray *buttons = nil;
512 int style = NSInformationalAlertStyle;
514 if (VIM_WARNING == type) style = NSWarningAlertStyle;
515 else if (VIM_ERROR == type) style = NSCriticalAlertStyle;
518 NSString *btnString = [NSString stringWithUTF8String:btns];
519 buttons = [btnString componentsSeparatedByString:@"\n"];
522 message = [NSString stringWithUTF8String:title];
524 text = [NSString stringWithUTF8String:msg];
526 // HACK! If there is a '\n\n' or '\n' sequence in the message, then
527 // make the part up to there into the title. We only do this
528 // because Vim has lots of dialogs without a title and they look
530 // TODO: Fix the actual dialog texts.
531 NSRange eolRange = [text rangeOfString:@"\n\n"];
532 if (NSNotFound == eolRange.location)
533 eolRange = [text rangeOfString:@"\n"];
534 if (NSNotFound != eolRange.location) {
535 message = [text substringToIndex:eolRange.location];
536 text = [text substringFromIndex:NSMaxRange(eolRange)];
541 textFieldString = [NSString stringWithUTF8String:txtfield];
544 [frontendProxy presentDialogWithStyle:style message:message
545 informativeText:text buttonTitles:buttons
546 textFieldString:textFieldString];
548 // Wait until a reply is sent from MMVimController.
549 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
550 beforeDate:[NSDate distantFuture]];
552 if (dialogReturn && [dialogReturn isKindOfClass:[NSArray class]]
553 && [dialogReturn count]) {
554 retval = [[dialogReturn objectAtIndex:0] intValue];
555 if (txtfield && [dialogReturn count] > 1) {
556 NSString *retString = [dialogReturn objectAtIndex:1];
557 vim_strncpy((char_u*)txtfield, (char_u*)[retString UTF8String],
562 [dialogReturn release]; dialogReturn = nil;
564 @catch (NSException *e) {
565 NSLog(@"Exception caught while showing alert dialog: \"%@\"", e);
571 - (void)addMenuWithTag:(int)tag parent:(int)parentTag name:(char *)name
574 //NSLog(@"addMenuWithTag:%d parent:%d name:%s atIndex:%d", tag, parentTag,
577 int namelen = name ? strlen(name) : 0;
578 NSMutableData *data = [NSMutableData data];
580 [data appendBytes:&tag length:sizeof(int)];
581 [data appendBytes:&parentTag length:sizeof(int)];
582 [data appendBytes:&namelen length:sizeof(int)];
583 if (namelen > 0) [data appendBytes:name length:namelen];
584 [data appendBytes:&index length:sizeof(int)];
586 [self queueMessage:AddMenuMsgID data:data];
589 - (void)addMenuItemWithTag:(int)tag parent:(int)parentTag name:(char *)name
590 tip:(char *)tip icon:(char *)icon
591 keyEquivalent:(int)key modifiers:(int)mods
592 action:(NSString *)action atIndex:(int)index
594 //NSLog(@"addMenuItemWithTag:%d parent:%d name:%s tip:%s atIndex:%d", tag,
595 // parentTag, name, tip, index);
597 int namelen = name ? strlen(name) : 0;
598 int tiplen = tip ? strlen(tip) : 0;
599 int iconlen = icon ? strlen(icon) : 0;
600 int eventFlags = vimModMaskToEventModifierFlags(mods);
601 int actionlen = [action lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
602 NSMutableData *data = [NSMutableData data];
604 key = specialKeyToNSKey(key);
606 [data appendBytes:&tag length:sizeof(int)];
607 [data appendBytes:&parentTag length:sizeof(int)];
608 [data appendBytes:&namelen length:sizeof(int)];
609 if (namelen > 0) [data appendBytes:name length:namelen];
610 [data appendBytes:&tiplen length:sizeof(int)];
611 if (tiplen > 0) [data appendBytes:tip length:tiplen];
612 [data appendBytes:&iconlen length:sizeof(int)];
613 if (iconlen > 0) [data appendBytes:icon length:iconlen];
614 [data appendBytes:&actionlen length:sizeof(int)];
615 if (actionlen > 0) [data appendBytes:[action UTF8String] length:actionlen];
616 [data appendBytes:&index length:sizeof(int)];
617 [data appendBytes:&key length:sizeof(int)];
618 [data appendBytes:&eventFlags length:sizeof(int)];
620 [self queueMessage:AddMenuItemMsgID data:data];
623 - (void)removeMenuItemWithTag:(int)tag
625 NSMutableData *data = [NSMutableData data];
626 [data appendBytes:&tag length:sizeof(int)];
628 [self queueMessage:RemoveMenuItemMsgID data:data];
631 - (void)enableMenuItemWithTag:(int)tag state:(int)enabled
633 NSMutableData *data = [NSMutableData data];
635 [data appendBytes:&tag length:sizeof(int)];
636 [data appendBytes:&enabled length:sizeof(int)];
638 [self queueMessage:EnableMenuItemMsgID data:data];
641 - (void)showPopupMenuWithName:(char *)name atMouseLocation:(BOOL)mouse
643 int len = strlen(name);
644 int row = -1, col = -1;
646 if (len <= 0) return;
648 if (!mouse && curwin) {
649 row = curwin->w_wrow;
650 col = curwin->w_wcol;
653 NSMutableData *data = [NSMutableData data];
655 [data appendBytes:&row length:sizeof(int)];
656 [data appendBytes:&col length:sizeof(int)];
657 [data appendBytes:&len length:sizeof(int)];
658 [data appendBytes:name length:len];
660 [self queueMessage:ShowPopupMenuMsgID data:data];
663 - (void)showToolbar:(int)enable flags:(int)flags
665 NSMutableData *data = [NSMutableData data];
667 [data appendBytes:&enable length:sizeof(int)];
668 [data appendBytes:&flags length:sizeof(int)];
670 [self queueMessage:ShowToolbarMsgID data:data];
673 - (void)createScrollbarWithIdentifier:(long)ident type:(int)type
675 NSMutableData *data = [NSMutableData data];
677 [data appendBytes:&ident length:sizeof(long)];
678 [data appendBytes:&type length:sizeof(int)];
680 [self queueMessage:CreateScrollbarMsgID data:data];
683 - (void)destroyScrollbarWithIdentifier:(long)ident
685 NSMutableData *data = [NSMutableData data];
686 [data appendBytes:&ident length:sizeof(long)];
688 [self queueMessage:DestroyScrollbarMsgID data:data];
691 - (void)showScrollbarWithIdentifier:(long)ident state:(int)visible
693 NSMutableData *data = [NSMutableData data];
695 [data appendBytes:&ident length:sizeof(long)];
696 [data appendBytes:&visible length:sizeof(int)];
698 [self queueMessage:ShowScrollbarMsgID data:data];
701 - (void)setScrollbarPosition:(int)pos length:(int)len identifier:(long)ident
703 NSMutableData *data = [NSMutableData data];
705 [data appendBytes:&ident length:sizeof(long)];
706 [data appendBytes:&pos length:sizeof(int)];
707 [data appendBytes:&len length:sizeof(int)];
709 [self queueMessage:SetScrollbarPositionMsgID data:data];
712 - (void)setScrollbarThumbValue:(long)val size:(long)size max:(long)max
713 identifier:(long)ident
715 float fval = max-size+1 > 0 ? (float)val/(max-size+1) : 0;
716 float prop = (float)size/(max+1);
717 if (fval < 0) fval = 0;
718 else if (fval > 1.0f) fval = 1.0f;
719 if (prop < 0) prop = 0;
720 else if (prop > 1.0f) prop = 1.0f;
722 NSMutableData *data = [NSMutableData data];
724 [data appendBytes:&ident length:sizeof(long)];
725 [data appendBytes:&fval length:sizeof(float)];
726 [data appendBytes:&prop length:sizeof(float)];
728 [self queueMessage:SetScrollbarThumbMsgID data:data];
731 - (BOOL)setFontWithName:(char *)name
735 BOOL parseFailed = NO;
738 fontName = [[[NSString alloc] initWithCString:name
739 encoding:NSUTF8StringEncoding] autorelease];
740 NSArray *components = [fontName componentsSeparatedByString:@":"];
741 if ([components count] == 2) {
742 NSString *sizeString = [components lastObject];
743 if ([sizeString length] > 0
744 && [sizeString characterAtIndex:0] == 'h') {
745 sizeString = [sizeString substringFromIndex:1];
746 if ([sizeString length] > 0) {
747 size = [sizeString floatValue];
748 fontName = [components objectAtIndex:0];
753 } else if ([components count] > 2) {
757 fontName = [[NSFont userFixedPitchFontOfSize:0] displayName];
760 if (!parseFailed && [fontName length] > 0) {
761 if (size < 6 || size > 100) {
762 // Font size 0.0 tells NSFont to use the 'user default size'.
766 NSFont *font = [NSFont fontWithName:fontName size:size];
768 //NSLog(@"Setting font '%@' of size %.2f", fontName, size);
770 lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
772 NSMutableData *data = [NSMutableData data];
774 [data appendBytes:&size length:sizeof(float)];
775 [data appendBytes:&len length:sizeof(int)];
776 [data appendBytes:[fontName UTF8String] length:len];
778 [self queueMessage:SetFontMsgID data:data];
784 NSLog(@"WARNING: Cannot set font with name '%@' of size %.2f",
789 - (void)executeActionWithName:(NSString *)name
791 int len = [name lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
794 NSMutableData *data = [NSMutableData data];
796 [data appendBytes:&len length:sizeof(int)];
797 [data appendBytes:[name UTF8String] length:len];
799 [self queueMessage:ExecuteActionMsgID data:data];
803 - (void)setMouseShape:(int)shape
805 NSMutableData *data = [NSMutableData data];
806 [data appendBytes:&shape length:sizeof(int)];
807 [self queueMessage:SetMouseShapeMsgID data:data];
810 - (void)setBlinkWait:(int)wait on:(int)on off:(int)off
812 // Vim specifies times in milliseconds, whereas Cocoa wants them in
814 blinkWaitInterval = .001f*wait;
815 blinkOnInterval = .001f*on;
816 blinkOffInterval = .001f*off;
822 [blinkTimer invalidate];
823 [blinkTimer release];
827 if (blinkWaitInterval > 0 && blinkOnInterval > 0 && blinkOffInterval > 0
829 blinkState = MMBlinkStateOn;
831 [[NSTimer scheduledTimerWithTimeInterval:blinkWaitInterval
833 selector:@selector(blinkTimerFired:)
834 userInfo:nil repeats:NO] retain];
835 gui_update_cursor(TRUE, FALSE);
836 [self flushQueue:YES];
842 if (MMBlinkStateOff == blinkState) {
843 gui_update_cursor(TRUE, FALSE);
844 [self flushQueue:YES];
847 blinkState = MMBlinkStateNone;
850 - (void)adjustLinespace:(int)linespace
852 NSMutableData *data = [NSMutableData data];
853 [data appendBytes:&linespace length:sizeof(int)];
854 [self queueMessage:AdjustLinespaceMsgID data:data];
859 [self queueMessage:ActivateMsgID data:nil];
862 - (int)lookupColorWithKey:(NSString *)key
864 if (!(key && [key length] > 0))
867 NSString *stripKey = [[[[key lowercaseString]
868 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]
869 componentsSeparatedByString:@" "]
870 componentsJoinedByString:@""];
872 if (stripKey && [stripKey length] > 0) {
873 // First of all try to lookup key in the color dictionary; note that
874 // all keys in this dictionary are lowercase with no whitespace.
875 id obj = [colorDict objectForKey:stripKey];
876 if (obj) return [obj intValue];
878 // The key was not in the dictionary; is it perhaps of the form
880 if ([stripKey length] > 1 && [stripKey characterAtIndex:0] == '#') {
881 NSScanner *scanner = [NSScanner scannerWithString:stripKey];
882 [scanner setScanLocation:1];
884 if ([scanner scanHexInt:&hex]) {
889 // As a last resort, check if it is one of the system defined colors.
890 // The keys in this dictionary are also lowercase with no whitespace.
891 obj = [sysColorDict objectForKey:stripKey];
893 NSColor *col = [NSColor performSelector:NSSelectorFromString(obj)];
896 col = [col colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
897 [col getRed:&r green:&g blue:&b alpha:&a];
898 return (((int)(r*255+.5f) & 0xff) << 16)
899 + (((int)(g*255+.5f) & 0xff) << 8)
900 + ((int)(b*255+.5f) & 0xff);
905 NSLog(@"WARNING: No color with key %@ found.", stripKey);
909 - (BOOL)hasSpecialKeyWithValue:(NSString *)value
911 NSEnumerator *e = [[MMBackend specialKeys] objectEnumerator];
914 while ((obj = [e nextObject])) {
915 if ([value isEqual:obj])
922 - (oneway void)processInput:(int)msgid data:(in NSData *)data
924 // NOTE: This method might get called whenever the run loop is tended to.
925 // Thus it might get called whilst input is being processed. Normally this
926 // is not a problem, but if it gets called often then it might become
927 // dangerous. E.g. when a focus messages is received the screen is redrawn
928 // because the selection color changes and if another focus message is
929 // received whilst the first one is being processed Vim might crash. To
930 // deal with this problem at the moment, we simply drop messages that are
931 // received while other input is being processed.
932 if (inProcessInput) {
933 #if MM_USE_INPUT_QUEUE
934 [inputQueue addObject:[NSNumber numberWithInt:msgid]];
935 [inputQueue addObject:data];
937 // Just drop the input
938 NSLog(@"WARNING: Dropping input in %s", _cmd);
941 [self processInputBegin];
942 [self handleMessage:msgid data:data];
943 [self processInputEnd];
947 - (oneway void)processInputAndData:(in NSArray *)messages
949 // NOTE: See comment in processInput:data:.
950 unsigned i, count = [messages count];
952 NSLog(@"WARNING: [messages count] is odd in %s", _cmd);
956 if (inProcessInput) {
957 #if MM_USE_INPUT_QUEUE
958 [inputQueue addObjectsFromArray:messages];
960 // Just drop the input
961 NSLog(@"WARNING: Dropping input in %s", _cmd);
964 [self processInputBegin];
966 for (i = 0; i < count; i += 2) {
967 int msgid = [[messages objectAtIndex:i] intValue];
968 id data = [messages objectAtIndex:i+1];
969 if ([data isEqual:[NSNull null]])
972 [self handleMessage:msgid data:data];
975 [self processInputEnd];
979 - (BOOL)checkForModifiedBuffers
982 for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
983 if (bufIsChanged(buf)) {
991 - (BOOL)starRegisterToPasteboard:(byref NSPasteboard *)pboard
993 if (VIsual_active && (State & NORMAL) && clip_star.available) {
994 // If there is no pasteboard, return YES to indicate that there is text
999 clip_copy_selection();
1001 // Get the text to put on the pasteboard.
1002 long_u len = 0; char_u *str = 0;
1003 int type = clip_convert_selection(&str, &len, &clip_star);
1007 NSString *string = [[NSString alloc]
1008 initWithBytes:str length:len encoding:NSUTF8StringEncoding];
1010 NSArray *types = [NSArray arrayWithObject:NSStringPboardType];
1011 [pboard declareTypes:types owner:nil];
1012 BOOL ok = [pboard setString:string forType:NSStringPboardType];
1023 - (NSString *)evaluateExpression:(in bycopy NSString *)expr
1025 NSString *eval = nil;
1026 char_u *res = eval_client_expr_to_string((char_u*)[expr UTF8String]);
1029 eval = [NSString stringWithUTF8String:(char*)res];
1040 @implementation MMBackend (Private)
1042 - (void)handleMessage:(int)msgid data:(NSData *)data
1044 if (InsertTextMsgID == msgid) {
1046 NSString *key = [[NSString alloc] initWithData:data
1047 encoding:NSUTF8StringEncoding];
1048 char_u *str = (char_u*)[key UTF8String];
1049 int i, len = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1052 char_u *conv_str = NULL;
1053 if (input_conv.vc_type != CONV_NONE) {
1054 conv_str = string_convert(&input_conv, str, &len);
1060 for (i = 0; i < len; ++i) {
1061 add_to_input_buf(str+i, 1);
1062 if (CSI == str[i]) {
1063 // NOTE: If the converted string contains the byte CSI, then it
1064 // must be followed by the bytes KS_EXTRA, KE_CSI or things
1066 static char_u extra[2] = { KS_EXTRA, KE_CSI };
1067 add_to_input_buf(extra, 2);
1076 } else if (KeyDownMsgID == msgid || CmdKeyMsgID == msgid) {
1078 const void *bytes = [data bytes];
1079 int mods = *((int*)bytes); bytes += sizeof(int);
1080 int len = *((int*)bytes); bytes += sizeof(int);
1081 NSString *key = [[NSString alloc] initWithBytes:bytes length:len
1082 encoding:NSUTF8StringEncoding];
1083 mods = eventModifierFlagsToVimModMask(mods);
1085 [self handleKeyDown:key modifiers:mods];
1088 } else if (SelectTabMsgID == msgid) {
1090 const void *bytes = [data bytes];
1091 int idx = *((int*)bytes) + 1;
1092 //NSLog(@"Selecting tab %d", idx);
1093 send_tabline_event(idx);
1094 } else if (CloseTabMsgID == msgid) {
1096 const void *bytes = [data bytes];
1097 int idx = *((int*)bytes) + 1;
1098 //NSLog(@"Closing tab %d", idx);
1099 send_tabline_menu_event(idx, TABLINE_MENU_CLOSE);
1100 } else if (AddNewTabMsgID == msgid) {
1101 //NSLog(@"Adding new tab");
1102 send_tabline_menu_event(0, TABLINE_MENU_NEW);
1103 } else if (DraggedTabMsgID == msgid) {
1105 const void *bytes = [data bytes];
1106 // NOTE! The destination index is 0 based, so do not add 1 to make it 1
1108 int idx = *((int*)bytes);
1111 } else if (ScrollWheelMsgID == msgid) {
1113 const void *bytes = [data bytes];
1115 int row = *((int*)bytes); bytes += sizeof(int);
1116 int col = *((int*)bytes); bytes += sizeof(int);
1117 int flags = *((int*)bytes); bytes += sizeof(int);
1118 float dy = *((float*)bytes); bytes += sizeof(float);
1120 int button = MOUSE_5;
1121 if (dy > 0) button = MOUSE_4;
1123 flags = eventModifierFlagsToVimMouseModMask(flags);
1125 gui_send_mouse_event(button, col, row, NO, flags);
1126 } else if (MouseDownMsgID == msgid) {
1128 const void *bytes = [data bytes];
1130 int row = *((int*)bytes); bytes += sizeof(int);
1131 int col = *((int*)bytes); bytes += sizeof(int);
1132 int button = *((int*)bytes); bytes += sizeof(int);
1133 int flags = *((int*)bytes); bytes += sizeof(int);
1134 int count = *((int*)bytes); bytes += sizeof(int);
1136 button = eventButtonNumberToVimMouseButton(button);
1137 flags = eventModifierFlagsToVimMouseModMask(flags);
1139 gui_send_mouse_event(button, col, row, 0 != count, flags);
1140 } else if (MouseUpMsgID == msgid) {
1142 const void *bytes = [data bytes];
1144 int row = *((int*)bytes); bytes += sizeof(int);
1145 int col = *((int*)bytes); bytes += sizeof(int);
1146 int flags = *((int*)bytes); bytes += sizeof(int);
1148 flags = eventModifierFlagsToVimMouseModMask(flags);
1150 gui_send_mouse_event(MOUSE_RELEASE, col, row, NO, flags);
1151 } else if (MouseDraggedMsgID == msgid) {
1153 const void *bytes = [data bytes];
1155 int row = *((int*)bytes); bytes += sizeof(int);
1156 int col = *((int*)bytes); bytes += sizeof(int);
1157 int flags = *((int*)bytes); bytes += sizeof(int);
1159 flags = eventModifierFlagsToVimMouseModMask(flags);
1161 gui_send_mouse_event(MOUSE_DRAG, col, row, NO, flags);
1162 } else if (SetTextDimensionsMsgID == msgid) {
1164 const void *bytes = [data bytes];
1165 int rows = *((int*)bytes); bytes += sizeof(int);
1166 int cols = *((int*)bytes); bytes += sizeof(int);
1168 // NOTE! Vim doesn't call gui_mch_set_shellsize() after
1169 // gui_resize_shell(), so we have to manually set the rows and columns
1170 // here. (MacVim doesn't change the rows and columns to avoid
1171 // inconsistent states between Vim and MacVim.)
1172 [self setRows:rows columns:cols];
1174 //NSLog(@"[VimTask] Resizing shell to %dx%d.", cols, rows);
1175 gui_resize_shell(cols, rows);
1176 } else if (ExecuteMenuMsgID == msgid) {
1178 const void *bytes = [data bytes];
1179 int tag = *((int*)bytes); bytes += sizeof(int);
1181 vimmenu_T *menu = (vimmenu_T*)tag;
1182 // TODO! Make sure 'menu' is a valid menu pointer!
1186 } else if (ToggleToolbarMsgID == msgid) {
1187 char_u go[sizeof(GO_ALL)+2];
1192 p = vim_strchr(go, GO_TOOLBAR);
1196 char_u *end = go + len;
1202 go[len] = GO_TOOLBAR;
1206 set_option_value((char_u*)"guioptions", 0, go, 0);
1208 // Force screen redraw (does it have to be this complicated?).
1209 redraw_all_later(CLEAR);
1210 update_screen(NOT_VALID);
1213 gui_update_cursor(FALSE, FALSE);
1215 } else if (ScrollbarEventMsgID == msgid) {
1217 const void *bytes = [data bytes];
1218 long ident = *((long*)bytes); bytes += sizeof(long);
1219 int hitPart = *((int*)bytes); bytes += sizeof(int);
1220 float fval = *((float*)bytes); bytes += sizeof(float);
1221 scrollbar_T *sb = gui_find_scrollbar(ident);
1224 scrollbar_T *sb_info = sb->wp ? &sb->wp->w_scrollbars[0] : sb;
1225 long value = sb_info->value;
1226 long size = sb_info->size;
1227 long max = sb_info->max;
1228 BOOL isStillDragging = NO;
1229 BOOL updateKnob = YES;
1232 case NSScrollerDecrementPage:
1233 value -= (size > 2 ? size - 2 : 1);
1235 case NSScrollerIncrementPage:
1236 value += (size > 2 ? size - 2 : 1);
1238 case NSScrollerDecrementLine:
1241 case NSScrollerIncrementLine:
1244 case NSScrollerKnob:
1245 isStillDragging = YES;
1247 case NSScrollerKnobSlot:
1248 value = (long)(fval * (max - size + 1));
1255 //NSLog(@"value %d -> %d", sb_info->value, value);
1256 gui_drag_scrollbar(sb, value, isStillDragging);
1259 // Dragging the knob or option+clicking automatically updates
1260 // the knob position (on the actual NSScroller), so we only
1261 // need to set the knob position in the other cases.
1263 // Update both the left&right vertical scrollbars.
1264 long identLeft = sb->wp->w_scrollbars[SBAR_LEFT].ident;
1265 long identRight = sb->wp->w_scrollbars[SBAR_RIGHT].ident;
1266 [self setScrollbarThumbValue:value size:size max:max
1267 identifier:identLeft];
1268 [self setScrollbarThumbValue:value size:size max:max
1269 identifier:identRight];
1271 // Update the horizontal scrollbar.
1272 [self setScrollbarThumbValue:value size:size max:max
1277 } else if (SetFontMsgID == msgid) {
1279 const void *bytes = [data bytes];
1280 float pointSize = *((float*)bytes); bytes += sizeof(float);
1281 //unsigned len = *((unsigned*)bytes); bytes += sizeof(unsigned);
1282 bytes += sizeof(unsigned); // len not used
1284 NSMutableString *name = [NSMutableString stringWithUTF8String:bytes];
1285 [name appendString:[NSString stringWithFormat:@":h%.2f", pointSize]];
1287 set_option_value((char_u*)"gfn", 0, (char_u*)[name UTF8String], 0);
1289 // Force screen redraw (does it have to be this complicated?).
1290 redraw_all_later(CLEAR);
1291 update_screen(NOT_VALID);
1294 gui_update_cursor(FALSE, FALSE);
1296 } else if (VimShouldCloseMsgID == msgid) {
1298 } else if (DropFilesMsgID == msgid) {
1300 const void *bytes = [data bytes];
1301 int n = *((int*)bytes); bytes += sizeof(int);
1304 int row = *((int*)bytes); bytes += sizeof(int);
1305 int col = *((int*)bytes); bytes += sizeof(int);
1307 char_u **fnames = (char_u **)alloc(n * sizeof(char_u *));
1309 const void *end = [data bytes] + [data length];
1311 while (bytes < end && i < n) {
1312 int len = *((int*)bytes); bytes += sizeof(int);
1313 fnames[i++] = vim_strnsave((char_u*)bytes, len);
1317 // NOTE! This function will free 'fnames'.
1318 gui_handle_drop(col, row, 0, fnames, i < n ? i : n);
1321 // HACK! I'm not sure how to get Vim to open a list of files in tabs,
1322 // so instead I create a ':tab drop' command with all the files to open
1324 NSMutableString *cmd = (n > 1)
1325 ? [NSMutableString stringWithString:@":tab drop"]
1326 : [NSMutableString stringWithString:@":drop"];
1328 const void *end = [data bytes] + [data length];
1330 for (i = 0; i < n && bytes < end; ++i) {
1331 int len = *((int*)bytes); bytes += sizeof(int);
1332 NSMutableString *file =
1333 [NSMutableString stringWithUTF8String:bytes];
1334 [file replaceOccurrencesOfString:@" "
1337 range:NSMakeRange(0, [file length])];
1340 [cmd appendString:@" "];
1341 [cmd appendString:file];
1344 // By going to the last tabpage we ensure that the new tabs will appear
1345 // last (if this call is left out, the taborder becomes messy).
1348 do_cmdline_cmd((char_u*)[cmd UTF8String]);
1350 // Force screen redraw (does it have to be this complicated?).
1351 // (This code was taken from the end of gui_handle_drop().)
1352 update_screen(NOT_VALID);
1355 gui_update_cursor(FALSE, FALSE);
1359 } else if (DropStringMsgID == msgid) {
1361 char_u dropkey[3] = { CSI, KS_EXTRA, (char_u)KE_DROP };
1362 const void *bytes = [data bytes];
1363 int len = *((int*)bytes); bytes += sizeof(int);
1364 NSMutableString *string = [NSMutableString stringWithUTF8String:bytes];
1366 // Replace unrecognized end-of-line sequences with \x0a (line feed).
1367 NSRange range = { 0, [string length] };
1368 unsigned n = [string replaceOccurrencesOfString:@"\x0d\x0a"
1369 withString:@"\x0a" options:0
1372 n = [string replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
1373 options:0 range:range];
1376 len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1377 dnd_yank_drag_data((char_u*)[string UTF8String], len);
1378 add_to_input_buf(dropkey, sizeof(dropkey));
1380 } else if (GotFocusMsgID == msgid) {
1382 [self focusChange:YES];
1383 } else if (LostFocusMsgID == msgid) {
1385 [self focusChange:NO];
1386 } else if (MouseMovedMsgID == msgid) {
1387 const void *bytes = [data bytes];
1388 int row = *((int*)bytes); bytes += sizeof(int);
1389 int col = *((int*)bytes); bytes += sizeof(int);
1391 gui_mouse_moved(col, row);
1392 } else if (SetMouseShapeMsgID == msgid) {
1393 const void *bytes = [data bytes];
1394 int shape = *((int*)bytes); bytes += sizeof(int);
1395 update_mouseshape(shape);
1396 } else if (ServerAddInputMsgID == msgid) {
1397 const void *bytes = [data bytes];
1398 /*int len = *((int*)bytes);*/ bytes += sizeof(int);
1399 char_u *cmd = (char_u*)bytes;
1401 server_to_input_buf(cmd);
1403 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
1407 + (NSDictionary *)specialKeys
1409 static NSDictionary *specialKeys = nil;
1412 NSBundle *mainBundle = [NSBundle mainBundle];
1413 NSString *path = [mainBundle pathForResource:@"SpecialKeys"
1415 specialKeys = [[NSDictionary alloc] initWithContentsOfFile:path];
1421 - (void)handleKeyDown:(NSString *)key modifiers:(int)mods
1428 // Special keys (arrow keys, function keys, etc.) are stored in a plist so
1429 // that new keys can easily be added.
1430 NSString *specialString = [[MMBackend specialKeys]
1432 if (specialString && [specialString length] > 1) {
1433 //NSLog(@"special key: %@", specialString);
1434 int ikey = TO_SPECIAL([specialString characterAtIndex:0],
1435 [specialString characterAtIndex:1]);
1437 ikey = simplify_key(ikey, &mods);
1442 special[1] = K_SECOND(ikey);
1443 special[2] = K_THIRD(ikey);
1447 } else if ([key length] > 0) {
1448 chars = (char_u*)[key UTF8String];
1449 length = [key lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1450 unichar c = [key characterAtIndex:0];
1452 //NSLog(@"non-special: %@ (hex=%x, mods=%d)", key,
1453 // [key characterAtIndex:0], mods);
1455 if (length == 1 && ((c == Ctrl_C && ctrl_c_interrupts)
1456 || (c == intr_char && intr_char != Ctrl_C))) {
1461 // HACK! In most circumstances the Ctrl and Shift modifiers should be
1462 // cleared since they are already added to the key by the AppKit.
1463 // Unfortunately, the only way to deal with when to clear the modifiers
1464 // or not seems to be to have hard-wired rules like this.
1465 if ( !((' ' == c) || (0xa0 == c) || (mods & MOD_MASK_CMD)) ) {
1466 mods &= ~MOD_MASK_SHIFT;
1467 mods &= ~MOD_MASK_CTRL;
1468 //NSLog(@"clear shift ctrl");
1471 // HACK! All Option+key presses go via 'insert text' messages, except
1472 // for <M-Space>. If the Alt flag is not cleared for <M-Space> it does
1473 // not work to map to it.
1474 if (0xa0 == c && !(mods & MOD_MASK_CMD)) {
1475 //NSLog(@"clear alt");
1476 mods &= ~MOD_MASK_ALT;
1480 if (chars && length > 0) {
1482 //NSLog(@"adding mods: %d", mods);
1484 modChars[1] = KS_MODIFIER;
1486 add_to_input_buf(modChars, 3);
1489 //NSLog(@"add to input buf: 0x%x", chars[0]);
1490 // TODO: Check for CSI bytes?
1491 add_to_input_buf(chars, length);
1495 - (void)queueMessage:(int)msgid data:(NSData *)data
1497 [queue addObject:[NSData dataWithBytes:&msgid length:sizeof(int)]];
1499 [queue addObject:data];
1501 [queue addObject:[NSData data]];
1504 - (void)connectionDidDie:(NSNotification *)notification
1506 // If the main connection to MacVim is lost this means that MacVim was
1507 // either quit (by the user chosing Quit on the MacVim menu), or it has
1508 // crashed. In either case our only option is to quit now.
1509 // TODO: Write backup file?
1511 //NSLog(@"A Vim process lots its connection to MacVim; quitting.");
1515 - (void)blinkTimerFired:(NSTimer *)timer
1517 NSTimeInterval timeInterval = 0;
1519 [blinkTimer release];
1522 if (MMBlinkStateOn == blinkState) {
1523 gui_undraw_cursor();
1524 blinkState = MMBlinkStateOff;
1525 timeInterval = blinkOffInterval;
1526 } else if (MMBlinkStateOff == blinkState) {
1527 gui_update_cursor(TRUE, FALSE);
1528 blinkState = MMBlinkStateOn;
1529 timeInterval = blinkOnInterval;
1532 if (timeInterval > 0) {
1534 [[NSTimer scheduledTimerWithTimeInterval:timeInterval target:self
1535 selector:@selector(blinkTimerFired:)
1536 userInfo:nil repeats:NO] retain];
1537 [self flushQueue:YES];
1541 - (void)focusChange:(BOOL)on
1543 gui_focus_change(on);
1546 - (void)processInputBegin
1548 inProcessInput = YES;
1549 [lastFlushDate release];
1550 lastFlushDate = [[NSDate date] retain];
1553 - (void)processInputEnd
1555 #if MM_USE_INPUT_QUEUE
1556 int count = [inputQueue count];
1558 // TODO: This is troubling, but it is not hard to get Vim to end up
1559 // here. Why does this happen?
1560 NSLog(@"WARNING: inputQueue has odd number of objects (%d)", count);
1561 [inputQueue removeAllObjects];
1562 } else if (count > 0) {
1563 // TODO: Dispatch these messages? Maybe not; usually when the
1564 // 'inputQueue' is non-empty it means that a LOT of messages has been
1565 // sent simultaneously. The only way this happens is when Vim is being
1566 // tormented, e.g. if the user holds down <D-`> to rapidly switch
1569 for (i = 0; i < count; i+=2) {
1570 int msgid = [[inputQueue objectAtIndex:i] intValue];
1571 NSLog(@"%s: Dropping message %s", _cmd, MessageStrings[msgid]);
1574 [inputQueue removeAllObjects];
1578 #if 0 // This does not work...for now, just don't care if a focus msg was lost.
1579 // HACK! A focus message might get lost, but whenever we get here the GUI
1582 [self focusChange:TRUE];
1585 inputReceived = YES;
1586 inProcessInput = NO;
1589 @end // MMBackend (Private)
1594 static int eventModifierFlagsToVimModMask(int modifierFlags)
1598 if (modifierFlags & NSShiftKeyMask)
1599 modMask |= MOD_MASK_SHIFT;
1600 if (modifierFlags & NSControlKeyMask)
1601 modMask |= MOD_MASK_CTRL;
1602 if (modifierFlags & NSAlternateKeyMask)
1603 modMask |= MOD_MASK_ALT;
1604 if (modifierFlags & NSCommandKeyMask)
1605 modMask |= MOD_MASK_CMD;
1610 static int vimModMaskToEventModifierFlags(int mods)
1614 if (mods & MOD_MASK_SHIFT)
1615 flags |= NSShiftKeyMask;
1616 if (mods & MOD_MASK_CTRL)
1617 flags |= NSControlKeyMask;
1618 if (mods & MOD_MASK_ALT)
1619 flags |= NSAlternateKeyMask;
1620 if (mods & MOD_MASK_CMD)
1621 flags |= NSCommandKeyMask;
1626 static int eventModifierFlagsToVimMouseModMask(int modifierFlags)
1630 if (modifierFlags & NSShiftKeyMask)
1631 modMask |= MOUSE_SHIFT;
1632 if (modifierFlags & NSControlKeyMask)
1633 modMask |= MOUSE_CTRL;
1634 if (modifierFlags & NSAlternateKeyMask)
1635 modMask |= MOUSE_ALT;
1640 static int eventButtonNumberToVimMouseButton(int buttonNumber)
1642 static int mouseButton[] = { MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE,
1643 MOUSE_X1, MOUSE_X2 };
1645 return mouseButton[buttonNumber < 5 ? buttonNumber : 0];
1648 static int specialKeyToNSKey(int key)
1650 if (!IS_SPECIAL(key))
1657 { K_UP, NSUpArrowFunctionKey },
1658 { K_DOWN, NSDownArrowFunctionKey },
1659 { K_LEFT, NSLeftArrowFunctionKey },
1660 { K_RIGHT, NSRightArrowFunctionKey },
1661 { K_F1, NSF1FunctionKey },
1662 { K_F2, NSF2FunctionKey },
1663 { K_F3, NSF3FunctionKey },
1664 { K_F4, NSF4FunctionKey },
1665 { K_F5, NSF5FunctionKey },
1666 { K_F6, NSF6FunctionKey },
1667 { K_F7, NSF7FunctionKey },
1668 { K_F8, NSF8FunctionKey },
1669 { K_F9, NSF9FunctionKey },
1670 { K_F10, NSF10FunctionKey },
1671 { K_F11, NSF11FunctionKey },
1672 { K_F12, NSF12FunctionKey },
1673 { K_F13, NSF13FunctionKey },
1674 { K_F14, NSF14FunctionKey },
1675 { K_F15, NSF15FunctionKey },
1676 { K_F16, NSF16FunctionKey },
1677 { K_F17, NSF17FunctionKey },
1678 { K_F18, NSF18FunctionKey },
1679 { K_F19, NSF19FunctionKey },
1680 { K_F20, NSF20FunctionKey },
1681 { K_F21, NSF21FunctionKey },
1682 { K_F22, NSF22FunctionKey },
1683 { K_F23, NSF23FunctionKey },
1684 { K_F24, NSF24FunctionKey },
1685 { K_F25, NSF25FunctionKey },
1686 { K_F26, NSF26FunctionKey },
1687 { K_F27, NSF27FunctionKey },
1688 { K_F28, NSF28FunctionKey },
1689 { K_F29, NSF29FunctionKey },
1690 { K_F30, NSF30FunctionKey },
1691 { K_F31, NSF31FunctionKey },
1692 { K_F32, NSF32FunctionKey },
1693 { K_F33, NSF33FunctionKey },
1694 { K_F34, NSF34FunctionKey },
1695 { K_F35, NSF35FunctionKey },
1696 { K_DEL, NSBackspaceCharacter },
1697 { K_BS, NSDeleteCharacter },
1698 { K_HOME, NSHomeFunctionKey },
1699 { K_END, NSEndFunctionKey },
1700 { K_PAGEUP, NSPageUpFunctionKey },
1701 { K_PAGEDOWN, NSPageDownFunctionKey }
1705 for (i = 0; i < sizeof(sp2ns)/sizeof(sp2ns[0]); ++i) {
1706 if (sp2ns[i].special == key)
1707 return sp2ns[i].nskey;