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.
11 #import "MMVimController.h"
12 #import "MMWindowController.h"
13 #import "MMAppController.h"
14 #import "MMTextView.h"
15 #import "MMTextStorage.h"
18 //static NSString *AttentionToolbarItemID = @"Attention";
19 static NSString *DefaultToolbarImageName = @"Attention";
22 @interface MMVimController (Private)
23 - (void)handleMessage:(int)msgid data:(NSData *)data;
24 - (void)performBatchDrawWithData:(NSData *)data;
25 - (void)panelDidEnd:(NSSavePanel *)panel code:(int)code
26 context:(void *)context;
27 - (NSMenuItem *)menuItemForTag:(int)tag;
28 - (NSMenu *)menuForTag:(int)tag;
29 - (void)addMenuWithTag:(int)tag parent:(NSMenu *)parent title:(NSString *)title
31 - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent
32 title:(NSString *)title tip:(NSString *)tip
33 keyEquivalent:(int)key modifiers:(int)mask
34 action:(NSString *)action atIndex:(int)idx;
35 - (void)updateMainMenu;
36 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index;
37 - (IBAction)toolbarAction:(id)sender;
38 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
39 toolTip:(NSString *)tip icon:(NSString *)icon;
40 - (void)addToolbarItemWithTag:(int)tag label:(NSString *)label
41 tip:(NSString *)tip icon:(NSString *)icon
44 - (void)connectionDidDie:(NSNotification *)notification;
46 - (BOOL)executeActionWithName:(NSString *)name;
51 // TODO: Move to separate file
52 @interface NSColor (MMProtocol)
53 + (NSColor *)colorWithRgbInt:(int)rgb;
58 static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag)
61 NSMenuItem *item = [root itemWithTag:tag];
62 if (item) return item;
64 NSArray *items = [root itemArray];
65 unsigned i, count = [items count];
66 for (i = 0; i < count; ++i) {
67 item = [items objectAtIndex:i];
68 if ([item hasSubmenu]) {
69 item = findMenuItemWithTagInMenu([item submenu], tag);
70 if (item) return item;
80 @implementation MMVimController
83 - (id)initWithBackend:(id)backend
85 - (id)initWithPort:(NSPort *)port
88 if ((self = [super init])) {
90 [[MMWindowController alloc] initWithVimController:self];
92 backendProxy = [backend retain];
93 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
94 sendQueue = [NSMutableArray new];
97 NSConnection *connection = [backendProxy connectionForProxy];
98 [[NSNotificationCenter defaultCenter] addObserver:self
99 selector:@selector(connectionDidDie:)
100 name:NSConnectionDidDieNotification object:connection];
102 sendPort = [port retain];
104 // Init receive port and send connected message to VimTask
105 receivePort = [NSMachPort new];
106 [receivePort setDelegate:self];
108 // Add to the default run loop mode as well as the event tracking mode;
109 // the latter ensures that updates from the VimTask reaches
110 // MMVimController whilst the user resizes a window with the mouse.
111 [[NSRunLoop currentRunLoop] addPort:receivePort
112 forMode:NSDefaultRunLoopMode];
113 [[NSRunLoop currentRunLoop] addPort:receivePort
114 forMode:NSEventTrackingRunLoopMode];
116 [NSPortMessage sendMessage:ConnectedMsgID withSendPort:sendPort
117 receivePort:receivePort wait:YES];
120 mainMenuItems = [[NSMutableArray alloc] init];
122 toolbarItemDict = [[NSMutableDictionary alloc] init];
123 //[self addToolbarItemToDictionaryWithTag:0 label:@"Attention"
124 // toolTip:@"A toolbar item is missing"
125 // icon:@"Attention"];
127 [[NSNotificationCenter defaultCenter]
129 selector:@selector(windowWillClose:)
130 name:NSWindowWillCloseNotification
131 object:[windowController window]];
132 [[NSNotificationCenter defaultCenter]
134 selector:@selector(windowDidBecomeMain:)
135 name:NSWindowDidBecomeMainNotification
136 object:[windowController window]];
145 //NSLog(@"%@ %s", [self className], _cmd);
147 [[NSNotificationCenter defaultCenter] removeObserver:self];
150 [backendProxy release];
151 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
156 // Kill task immediately
157 [NSPortMessage sendMessage:KillTaskMsgID withSendPort:sendPort
158 receivePort:receivePort wait:NO];
162 [receivePort release];
165 [toolbarItemDict release];
167 [mainMenuItems release];
168 [windowController release];
173 - (MMWindowController *)windowController
175 return windowController;
178 - (void)sendMessage:(int)msgid data:(NSData *)data wait:(BOOL)wait
181 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
182 if (inProcessCommandQueue) {
183 //NSLog(@"In process command queue; delaying message send.");
184 [sendQueue addObject:[NSNumber numberWithInt:msgid]];
186 [sendQueue addObject:data];
188 [sendQueue addObject:[NSNull null]];
193 [backendProxy processInput:msgid data:data];
195 // Do not wait for the message to be sent, i.e. drop the message if it
196 // can't be delivered immediately.
197 NSConnection *connection = [backendProxy connectionForProxy];
199 NSTimeInterval req = [connection requestTimeout];
200 [connection setRequestTimeout:0];
202 [backendProxy processInput:msgid data:data];
204 @catch (NSException *e) {
205 // Connection timed out, just ignore this.
206 //NSLog(@"WARNING! Connection timed out in %s", _cmd);
209 [connection setRequestTimeout:req];
214 [NSPortMessage sendMessage:msgid withSendPort:sendPort data:data
225 - (oneway void)showSavePanelForDirectory:(in bycopy NSString *)dir
226 title:(in bycopy NSString *)title
229 [windowController setStatusText:title];
232 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
233 modalForWindow:[windowController window]
235 didEndSelector:@selector(panelDidEnd:code:context:)
238 NSOpenPanel *panel = [NSOpenPanel openPanel];
239 [panel setAllowsMultipleSelection:NO];
240 [panel beginSheetForDirectory:dir file:nil types:nil
241 modalForWindow:[windowController window]
243 didEndSelector:@selector(panelDidEnd:code:context:)
248 - (oneway void)processCommandQueue:(in NSArray *)queue
250 unsigned i, count = [queue count];
252 NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
253 "message; ignoring this message.", count);
257 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
258 inProcessCommandQueue = YES;
261 //NSLog(@"======== %s BEGIN ========", _cmd);
262 for (i = 0; i < count; i += 2) {
263 NSData *value = [queue objectAtIndex:i];
264 NSData *data = [queue objectAtIndex:i+1];
266 int msgid = *((int*)[value bytes]);
268 if (msgid != EnableMenuItemMsgID && msgid != AddMenuItemMsgID
269 && msgid != AddMenuMsgID) {
270 NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
274 [self handleMessage:msgid data:data];
276 //NSLog(@"======== %s END ========", _cmd);
278 if (shouldUpdateMainMenu) {
279 [self updateMainMenu];
282 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
283 inProcessCommandQueue = NO;
285 count = [sendQueue count];
287 if (count % 2 == 0) {
288 //NSLog(@"%s Sending %d queued messages", _cmd, count/2);
290 for (i = 0; i < count; i += 2) {
291 int msgid = [[sendQueue objectAtIndex:i] intValue];
292 id data = [sendQueue objectAtIndex:i+1];
293 if ([data isEqual:[NSNull null]])
296 [backendProxy processInput:msgid data:data];
300 [sendQueue removeAllObjects];
312 - (void)handlePortMessage:(NSPortMessage *)portMessage
314 //NSLog(@"%@ %s %@", [self className], _cmd, portMessage);
316 NSArray *components = [portMessage components];
317 unsigned msgid = [portMessage msgid];
319 //NSLog(@"%s%d", _cmd, msgid);
321 if (FlushQueueMsgID == msgid) {
322 unsigned i, count = [components count];
324 NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
325 "message; ignoring this message.", count);
329 for (i = 0; i < count; i += 2) {
330 NSData *value = [components objectAtIndex:i];
331 NSData *data = [components objectAtIndex:i+1];
333 [self handleMessage:*((int*)[value bytes]) data:data];
337 if ([components count] > 0)
338 data = [components objectAtIndex:0];
340 [self handleMessage:msgid data:data];
343 if (shouldUpdateMainMenu) {
344 [self updateMainMenu];
349 - (void)windowWillClose:(NSNotification *)notification
351 // NOTE! This causes the call to removeVimController: to be delayed.
353 performSelectorOnMainThread:@selector(removeVimController:)
354 withObject:self waitUntilDone:NO];
357 - (void)windowDidBecomeMain:(NSNotification *)notification
359 [self updateMainMenu];
362 - (NSToolbarItem *)toolbar:(NSToolbar *)theToolbar
363 itemForItemIdentifier:(NSString *)itemId
364 willBeInsertedIntoToolbar:(BOOL)flag
366 //NSLog(@"%s", _cmd);
368 NSToolbarItem *item = [toolbarItemDict objectForKey:itemId];
370 NSLog(@"WARNING: No toolbar item with id '%@'", itemId);
376 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)theToolbar
378 //NSLog(@"%s", _cmd);
382 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)theToolbar
384 //NSLog(@"%s", _cmd);
388 @end // MMVimController
392 @implementation MMVimController (Private)
394 - (void)handleMessage:(int)msgid data:(NSData *)data
396 //NSLog(@"%@ %s", [self className], _cmd);
398 if (OpenVimWindowMsgID == msgid) {
399 [windowController openWindow];
402 else if (TaskExitedMsgID == msgid) {
403 //NSLog(@"Received task exited message from VimTask; closing window.");
405 // Release sendPort immediately to avoid dealloc trying to send a 'kill
406 // task' message to the task.
407 [sendPort release]; sendPort = nil;
408 // NOTE! This causes windowWillClose: to be called, which in turn asks
409 // the MMAppController to remove this MMVimController.
410 [windowController close];
412 // HACK! Make sure no menu updating is done, we're about to close.
413 shouldUpdateMainMenu = NO;
416 else if (BatchDrawMsgID == msgid) {
417 //NSLog(@"Received batch draw message from VimTask.");
419 [self performBatchDrawWithData:data];
420 } else if (SelectTabMsgID == msgid) {
421 #if 0 // NOTE: Tab selection is done inside updateTabsWithData:.
422 const void *bytes = [data bytes];
423 int idx = *((int*)bytes);
424 //NSLog(@"Selecting tab with index %d", idx);
425 [windowController selectTabWithIndex:idx];
427 } else if (UpdateTabBarMsgID == msgid) {
428 //NSLog(@"Updating tabs");
429 [windowController updateTabsWithData:data];
430 } else if (ShowTabBarMsgID == msgid) {
431 //NSLog(@"Showing tab bar");
433 // HACK! Vim sends several draw commands etc. after the show message
434 // and these can mess up the display when showing the tab bar results
435 // in the window having to resize to fit the screen; delaying this
436 // message alleviates this problem.
437 [windowController performSelectorOnMainThread:@selector(showTabBar:)
438 withObject:self waitUntilDone:NO];
439 //[windowController showTabBar:self];
440 } else if (HideTabBarMsgID == msgid) {
441 //NSLog(@"Hiding tab bar");
442 [windowController hideTabBar:self];
443 } else if (SetTextDimensionsMsgID == msgid) {
444 const void *bytes = [data bytes];
445 int rows = *((int*)bytes); bytes += sizeof(int);
446 int cols = *((int*)bytes); bytes += sizeof(int);
448 [windowController setTextDimensionsWithRows:rows columns:cols];
449 } else if (SetVimWindowTitleMsgID == msgid) {
450 const void *bytes = [data bytes];
451 int len = *((int*)bytes); bytes += sizeof(int);
454 // BUG! Using this call leads to ALL windows getting the same title
455 // and then the app crashes if you :q a window.
456 NSString *string = [[NSString alloc]
457 initWithBytesNoCopy:(void*)bytes
459 encoding:NSUTF8StringEncoding
462 NSString *string = [[NSString alloc] initWithBytes:(void*)bytes
463 length:len encoding:NSUTF8StringEncoding];
466 [[windowController window] setTitle:string];
469 } else if (BrowseForFileMsgID == msgid) {
470 const void *bytes = [data bytes];
471 int save = *((int*)bytes); bytes += sizeof(int);
473 int len = *((int*)bytes); bytes += sizeof(int);
476 dir = [[NSString alloc] initWithBytes:(void*)bytes
478 encoding:NSUTF8StringEncoding];
482 len = *((int*)bytes); bytes += sizeof(int);
484 NSString *title = [[NSString alloc]
485 initWithBytes:(void*)bytes length:len
486 encoding:NSUTF8StringEncoding];
489 [windowController setStatusText:title];
494 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
495 modalForWindow:[windowController window]
497 didEndSelector:@selector(panelDidEnd:code:context:)
500 NSOpenPanel *panel = [NSOpenPanel openPanel];
501 [panel setAllowsMultipleSelection:NO];
502 [panel beginSheetForDirectory:dir file:nil types:nil
503 modalForWindow:[windowController window]
505 didEndSelector:@selector(panelDidEnd:code:context:)
510 } else if (UpdateInsertionPointMsgID == msgid) {
511 const void *bytes = [data bytes];
512 int color = *((int*)bytes); bytes += sizeof(int);
513 int row = *((int*)bytes); bytes += sizeof(int);
514 int col = *((int*)bytes); bytes += sizeof(int);
515 int state = *((int*)bytes); bytes += sizeof(int);
517 // TODO! Move to window controller.
518 MMTextView *textView = [windowController textView];
520 MMTextStorage *textStorage = (MMTextStorage*)[textView textStorage];
521 unsigned off = [textStorage offsetFromRow:row column:col];
523 [textView setInsertionPointColor:[NSColor colorWithRgbInt:color]];
524 [textView setSelectedRange:NSMakeRange(off, 0)];
525 [textView setShouldDrawInsertionPoint:state];
527 } else if (AddMenuMsgID == msgid) {
528 NSString *title = nil;
529 const void *bytes = [data bytes];
530 int tag = *((int*)bytes); bytes += sizeof(int);
531 int parentTag = *((int*)bytes); bytes += sizeof(int);
532 int len = *((int*)bytes); bytes += sizeof(int);
534 title = [[NSString alloc] initWithBytes:(void*)bytes length:len
535 encoding:NSUTF8StringEncoding];
538 int idx = *((int*)bytes); bytes += sizeof(int);
540 if (MenuToolbarType == parentTag) {
542 NSString *ident = [NSString stringWithFormat:@"%d.%d",
544 //NSLog(@"Creating toolbar with identifier %@", ident);
545 toolbar = [[NSToolbar alloc] initWithIdentifier:ident];
547 [toolbar setShowsBaselineSeparator:NO];
548 [toolbar setDelegate:self];
549 [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
550 [toolbar setSizeMode:NSToolbarSizeModeSmall];
552 [[windowController window] setToolbar:toolbar];
554 } else if (MenuPopupType == parentTag) {
557 NSMenu *parent = [self menuForTag:parentTag];
558 [self addMenuWithTag:tag parent:parent title:title atIndex:idx];
562 } else if (AddMenuItemMsgID == msgid) {
563 NSString *title = nil, *tip = nil, *icon = nil, *action = nil;
564 const void *bytes = [data bytes];
565 int tag = *((int*)bytes); bytes += sizeof(int);
566 int parentTag = *((int*)bytes); bytes += sizeof(int);
567 int namelen = *((int*)bytes); bytes += sizeof(int);
569 title = [[NSString alloc] initWithBytes:(void*)bytes length:namelen
570 encoding:NSUTF8StringEncoding];
573 int tiplen = *((int*)bytes); bytes += sizeof(int);
575 tip = [[NSString alloc] initWithBytes:(void*)bytes length:tiplen
576 encoding:NSUTF8StringEncoding];
579 int iconlen = *((int*)bytes); bytes += sizeof(int);
581 icon = [[NSString alloc] initWithBytes:(void*)bytes length:iconlen
582 encoding:NSUTF8StringEncoding];
585 int actionlen = *((int*)bytes); bytes += sizeof(int);
587 action = [[NSString alloc] initWithBytes:(void*)bytes
589 encoding:NSUTF8StringEncoding];
592 int idx = *((int*)bytes); bytes += sizeof(int);
593 if (idx < 0) idx = 0;
594 int key = *((int*)bytes); bytes += sizeof(int);
595 int mask = *((int*)bytes); bytes += sizeof(int);
597 NSString *ident = [NSString stringWithFormat:@"%d.%d",
598 (int)self, parentTag];
599 if (toolbar && [[toolbar identifier] isEqual:ident]) {
600 [self addToolbarItemWithTag:tag label:title tip:tip icon:icon
603 NSMenu *parent = [self menuForTag:parentTag];
604 [self addMenuItemWithTag:tag parent:parent title:title tip:tip
605 keyEquivalent:key modifiers:mask action:action
613 } else if (RemoveMenuItemMsgID == msgid) {
614 const void *bytes = [data bytes];
615 int tag = *((int*)bytes); bytes += sizeof(int);
617 // TODO: Search for tag in popup menus.
620 if ((item = [self toolbarItemForTag:tag index:&idx])) {
621 [toolbar removeItemAtIndex:idx];
622 } else if ((item = [self menuItemForTag:tag])) {
623 if ([item menu] == [NSApp mainMenu]) {
624 NSLog(@"Removing menu: %@", item);
625 [mainMenuItems removeObject:item];
627 [[item menu] removeItem:item];
629 } else if (EnableMenuItemMsgID == msgid) {
630 const void *bytes = [data bytes];
631 int tag = *((int*)bytes); bytes += sizeof(int);
632 int state = *((int*)bytes); bytes += sizeof(int);
634 // TODO: Search for tag in popup menus.
635 id item = [self toolbarItemForTag:tag index:NULL];
637 item = [self menuItemForTag:tag];
639 [item setEnabled:state];
640 } else if (ShowToolbarMsgID == msgid) {
641 const void *bytes = [data bytes];
642 int enable = *((int*)bytes); bytes += sizeof(int);
643 int flags = *((int*)bytes); bytes += sizeof(int);
645 int mode = NSToolbarDisplayModeDefault;
646 if (flags & ToolbarLabelFlag) {
647 mode = flags & ToolbarIconFlag ? NSToolbarDisplayModeIconAndLabel
648 : NSToolbarDisplayModeLabelOnly;
649 } else if (flags & ToolbarIconFlag) {
650 mode = NSToolbarDisplayModeIconOnly;
653 int size = flags & ToolbarSizeRegularFlag ? NSToolbarSizeModeRegular
654 : NSToolbarSizeModeSmall;
656 [toolbar setSizeMode:size];
657 [toolbar setDisplayMode:mode];
658 [toolbar setVisible:enable];
659 } else if (CreateScrollbarMsgID == msgid) {
660 const void *bytes = [data bytes];
661 long ident = *((long*)bytes); bytes += sizeof(long);
662 int type = *((int*)bytes); bytes += sizeof(int);
664 [windowController createScrollbarWithIdentifier:ident type:type];
665 } else if (DestroyScrollbarMsgID == msgid) {
666 const void *bytes = [data bytes];
667 long ident = *((long*)bytes); bytes += sizeof(long);
669 [windowController destroyScrollbarWithIdentifier:ident];
670 } else if (ShowScrollbarMsgID == msgid) {
671 const void *bytes = [data bytes];
672 long ident = *((long*)bytes); bytes += sizeof(long);
673 int visible = *((int*)bytes); bytes += sizeof(int);
675 [windowController showScrollbarWithIdentifier:ident state:visible];
676 } else if (SetScrollbarPositionMsgID == msgid) {
677 const void *bytes = [data bytes];
678 long ident = *((long*)bytes); bytes += sizeof(long);
679 int pos = *((int*)bytes); bytes += sizeof(int);
680 int len = *((int*)bytes); bytes += sizeof(int);
682 [windowController setScrollbarPosition:pos length:len
684 } else if (SetScrollbarThumbMsgID == msgid) {
685 const void *bytes = [data bytes];
686 long ident = *((long*)bytes); bytes += sizeof(long);
687 float val = *((float*)bytes); bytes += sizeof(float);
688 float prop = *((float*)bytes); bytes += sizeof(float);
690 [windowController setScrollbarThumbValue:val proportion:prop
692 } else if (SetFontMsgID == msgid) {
693 const void *bytes = [data bytes];
694 float size = *((float*)bytes); bytes += sizeof(float);
695 int len = *((int*)bytes); bytes += sizeof(int);
696 NSString *name = [[NSString alloc]
697 initWithBytes:(void*)bytes length:len
698 encoding:NSUTF8StringEncoding];
699 NSFont *font = [NSFont fontWithName:name size:size];
702 [windowController setFont:font];
705 } else if (SetDefaultColorsMsgID == msgid) {
706 const void *bytes = [data bytes];
707 int bg = *((int*)bytes); bytes += sizeof(int);
708 int fg = *((int*)bytes); bytes += sizeof(int);
709 NSColor *back = [NSColor colorWithRgbInt:bg];
710 NSColor *fore = [NSColor colorWithRgbInt:fg];
712 [windowController setDefaultColorsBackground:back foreground:fore];
713 } else if (ExecuteActionMsgID == msgid) {
714 const void *bytes = [data bytes];
715 int len = *((int*)bytes); bytes += sizeof(int);
716 NSString *actionName = [[NSString alloc]
717 initWithBytesNoCopy:(void*)bytes
719 encoding:NSUTF8StringEncoding
722 SEL sel = NSSelectorFromString(actionName);
723 [NSApp sendAction:sel to:nil from:self];
725 [actionName release];
727 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
731 - (void)performBatchDrawWithData:(NSData *)data
733 // TODO! Move to window controller.
734 MMTextStorage *textStorage = [windowController textStorage];
738 const void *bytes = [data bytes];
739 const void *end = bytes + [data length];
741 [textStorage beginEditing];
744 // 1. Sanity check input
745 // 2. Cache rgb -> NSColor lookups?
747 while (bytes < end) {
748 int type = *((int*)bytes); bytes += sizeof(int);
750 if (ClearAllDrawType == type) {
751 int color = *((int*)bytes); bytes += sizeof(int);
753 [textStorage clearAllWithColor:[NSColor colorWithRgbInt:color]];
754 } else if (ClearBlockDrawType == type) {
755 int color = *((int*)bytes); bytes += sizeof(int);
756 int row1 = *((int*)bytes); bytes += sizeof(int);
757 int col1 = *((int*)bytes); bytes += sizeof(int);
758 int row2 = *((int*)bytes); bytes += sizeof(int);
759 int col2 = *((int*)bytes); bytes += sizeof(int);
761 [textStorage clearBlockFromRow:row1 column:col1
762 toRow:row2 column:col2
763 color:[NSColor colorWithRgbInt:color]];
764 } else if (DeleteLinesDrawType == type) {
765 int color = *((int*)bytes); bytes += sizeof(int);
766 int row = *((int*)bytes); bytes += sizeof(int);
767 int count = *((int*)bytes); bytes += sizeof(int);
768 int bot = *((int*)bytes); bytes += sizeof(int);
769 int left = *((int*)bytes); bytes += sizeof(int);
770 int right = *((int*)bytes); bytes += sizeof(int);
772 [textStorage deleteLinesFromRow:row lineCount:count
773 scrollBottom:bot left:left right:right
774 color:[NSColor colorWithRgbInt:color]];
775 } else if (ReplaceStringDrawType == type) {
776 int bg = *((int*)bytes); bytes += sizeof(int);
777 int fg = *((int*)bytes); bytes += sizeof(int);
778 int row = *((int*)bytes); bytes += sizeof(int);
779 int col = *((int*)bytes); bytes += sizeof(int);
780 int flags = *((int*)bytes); bytes += sizeof(int);
781 int len = *((int*)bytes); bytes += sizeof(int);
782 NSString *string = [[NSString alloc]
783 initWithBytesNoCopy:(void*)bytes
785 encoding:NSUTF8StringEncoding
789 [textStorage replaceString:string
792 foregroundColor:[NSColor colorWithRgbInt:fg]
793 backgroundColor:[NSColor colorWithRgbInt:bg]];
796 } else if (InsertLinesDrawType == type) {
797 int color = *((int*)bytes); bytes += sizeof(int);
798 int row = *((int*)bytes); bytes += sizeof(int);
799 int count = *((int*)bytes); bytes += sizeof(int);
800 int bot = *((int*)bytes); bytes += sizeof(int);
801 int left = *((int*)bytes); bytes += sizeof(int);
802 int right = *((int*)bytes); bytes += sizeof(int);
804 [textStorage insertLinesAtRow:row lineCount:count
805 scrollBottom:bot left:left right:right
806 color:[NSColor colorWithRgbInt:color]];
808 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
812 [textStorage endEditing];
815 - (void)panelDidEnd:(NSSavePanel *)panel code:(int)code context:(void *)context
818 [windowController setStatusText:@""];
820 NSString *string = (code == NSOKButton) ? [panel filename] : nil;
821 [backendProxy setBrowseForFileString:string];
823 NSMutableData *data = [NSMutableData data];
824 int ok = (code == NSOKButton);
825 NSString *filename = [panel filename];
826 int len = [filename lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
828 [data appendBytes:&ok length:sizeof(int)];
829 [data appendBytes:&len length:sizeof(int)];
831 [data appendBytes:[filename UTF8String] length:len];
833 if (![NSPortMessage sendMessage:BrowseForFileReplyMsgID
834 withSendPort:sendPort data:data wait:YES]) {
835 NSLog(@"WARNING: Failed to send browse for files reply back to "
839 [windowController setStatusText:@""];
843 - (NSMenuItem *)menuItemForTag:(int)tag
845 int i, count = [mainMenuItems count];
846 for (i = 0; i < count; ++i) {
847 NSMenuItem *item = [mainMenuItems objectAtIndex:i];
848 if ([item tag] == tag) return item;
849 item = findMenuItemWithTagInMenu([item submenu], tag);
850 if (item) return item;
856 - (NSMenu *)menuForTag:(int)tag
858 return [[self menuItemForTag:tag] submenu];
861 - (void)addMenuWithTag:(int)tag parent:(NSMenu *)parent title:(NSString *)title
864 NSMenuItem *item = [[NSMenuItem alloc] init];
865 NSMenu *menu = [[NSMenu alloc] initWithTitle:title];
867 [menu setAutoenablesItems:NO];
869 [item setTitle:title];
870 [item setSubmenu:menu];
873 if ([parent numberOfItems] <= idx) {
874 [parent addItem:item];
876 [parent insertItem:item atIndex:idx];
879 if ([mainMenuItems count] <= idx) {
880 [mainMenuItems addObject:item];
882 [mainMenuItems insertObject:item atIndex:idx];
885 shouldUpdateMainMenu = YES;
892 - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent
893 title:(NSString *)title tip:(NSString *)tip
894 keyEquivalent:(int)key modifiers:(int)mask
895 action:(NSString *)action atIndex:(int)idx
898 NSMenuItem *item = nil;
900 item = [[[NSMenuItem alloc] init] autorelease];
901 [item setTitle:title];
902 // TODO: Check that 'action' is a valid action (nothing will happen
903 // if it isn't, but it would be nice with a warning).
904 if (action) [item setAction:NSSelectorFromString(action)];
905 else [item setAction:@selector(vimMenuItemAction:)];
906 if (tip) [item setToolTip:tip];
909 NSString *keyString =
910 [NSString stringWithFormat:@"%C", key];
911 [item setKeyEquivalent:keyString];
912 [item setKeyEquivalentModifierMask:mask];
915 item = [NSMenuItem separatorItem];
918 // NOTE! The tag is used to idenfity which menu items were
919 // added by Vim (tag != 0) and which were added by the AppKit
923 if ([parent numberOfItems] <= idx) {
924 [parent addItem:item];
926 [parent insertItem:item atIndex:idx];
931 - (void)updateMainMenu
933 NSMenu *mainMenu = [NSApp mainMenu];
935 // Stop NSApp from updating the Window menu.
936 [NSApp setWindowsMenu:nil];
938 // Remove all menus from main menu (except the MacVim menu).
939 int i, count = [mainMenu numberOfItems];
940 for (i = count-1; i > 0; --i) {
941 [mainMenu removeItemAtIndex:i];
944 // Add menus from 'mainMenuItems' to main menu.
945 count = [mainMenuItems count];
946 for (i = 0; i < count; ++i) {
947 [mainMenu addItem:[mainMenuItems objectAtIndex:i]];
950 // Set the new Window menu.
951 // TODO! Need to look for 'Window' in all localized languages.
952 NSMenu *windowMenu = [[mainMenu itemWithTitle:@"Window"] submenu];
954 // Remove all AppKit owned menu items (tag == 0); they will be added
955 // again when setWindowsMenu: is called.
956 count = [windowMenu numberOfItems];
957 for (i = count-1; i >= 0; --i) {
958 NSMenuItem *item = [windowMenu itemAtIndex:i];
960 [windowMenu removeItem:item];
964 [NSApp setWindowsMenu:windowMenu];
967 shouldUpdateMainMenu = NO;
970 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index
972 if (!toolbar) return nil;
974 NSArray *items = [toolbar items];
975 int i, count = [items count];
976 for (i = 0; i < count; ++i) {
977 NSToolbarItem *item = [items objectAtIndex:i];
978 if ([item tag] == tag) {
979 if (index) *index = i;
987 - (IBAction)toolbarAction:(id)sender
989 NSLog(@"%s%@", _cmd, sender);
992 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
993 toolTip:(NSString *)tip icon:(NSString *)icon
995 // NOTE! 'title' is nul for separator item. Since this is already defined
996 // by Coca, we don't need to do anything here.
999 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:title];
1001 [item setLabel:title];
1002 [item setToolTip:tip];
1003 [item setAction:@selector(vimMenuItemAction:)];
1004 [item setAutovalidates:NO];
1006 NSImage *img = [NSImage imageNamed:icon];
1008 NSLog(@"WARNING: Could not find image with name '%@' to use as toolbar"
1009 " image for identifier '%@';"
1010 " using default toolbar icon '%@' instead.",
1011 icon, title, DefaultToolbarImageName);
1013 img = [NSImage imageNamed:DefaultToolbarImageName];
1016 [item setImage:img];
1018 [toolbarItemDict setObject:item forKey:title];
1023 - (void)addToolbarItemWithTag:(int)tag label:(NSString *)label tip:(NSString
1024 *)tip icon:(NSString *)icon atIndex:(int)idx
1026 if (!toolbar) return;
1028 [self addToolbarItemToDictionaryWithTag:tag label:label toolTip:tip
1031 int maxIdx = [[toolbar items] count];
1032 if (maxIdx < idx) idx = maxIdx;
1034 // If 'label' is nul, insert a separator.
1035 if (!label) label = NSToolbarSeparatorItemIdentifier;
1036 [toolbar insertItemWithItemIdentifier:label atIndex:idx];
1040 - (void)connectionDidDie:(NSNotification *)notification
1042 //NSLog(@"A MMVimController lost its connection to the backend; "
1043 // "closing the controller.");
1044 [windowController close];
1048 - (BOOL)executeActionWithName:(NSString *)name
1051 static NSDictionary *actionDict = nil;
1054 NSBundle *mainBundle = [NSBundle mainBundle];
1055 NSString *path = [mainBundle pathForResource:@"Actions"
1058 actionDict = [[NSDictionary alloc] initWithContentsOfFile:path];
1059 NSLog(@"Actions = %@", actionDict);
1061 NSLog(@"WARNING: Failed to load dictionary of actions "
1062 "(Actions.plist).");
1067 if ([actionDict objectForKey:name]) {
1068 NSLog(@"Executing action %@", name);
1069 SEL sel = NSSelectorFromString(name);
1071 if ([NSApp sendAction:sel to:nil from:self])
1074 NSLog(@"WARNING: Failed to send action");
1076 NSLog(@"WARNING: Action with name '%@' cannot be executed.", name);
1083 @end // MMVimController (Private)
1087 @implementation NSColor (MMProtocol)
1089 + (NSColor *)colorWithRgbInt:(int)rgb
1091 float r = ((rgb>>16) & 0xff)/255.0f;
1092 float g = ((rgb>>8) & 0xff)/255.0f;
1093 float b = (rgb & 0xff)/255.0f;
1095 return [NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0f];
1098 @end // NSColor (MMProtocol)