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 "MMTextView.h"
14 #import "MMAppController.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 - (NSMenu *)topLevelMenuForTitle:(NSString *)title;
30 - (void)addMenuWithTag:(int)tag parent:(int)parentTag title:(NSString *)title
32 - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent
33 title:(NSString *)title tip:(NSString *)tip
34 keyEquivalent:(int)key modifiers:(int)mask
35 action:(NSString *)action atIndex:(int)idx;
36 - (void)updateMainMenu;
37 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index;
38 - (IBAction)toolbarAction:(id)sender;
39 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
40 toolTip:(NSString *)tip icon:(NSString *)icon;
41 - (void)addToolbarItemWithTag:(int)tag label:(NSString *)label
42 tip:(NSString *)tip icon:(NSString *)icon
44 - (void)connectionDidDie:(NSNotification *)notification;
45 - (BOOL)executeActionWithName:(NSString *)name;
50 // TODO: Move to separate file
51 @interface NSColor (MMProtocol)
52 + (NSColor *)colorWithRgbInt:(int)rgb;
57 static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag)
60 NSMenuItem *item = [root itemWithTag:tag];
61 if (item) return item;
63 NSArray *items = [root itemArray];
64 unsigned i, count = [items count];
65 for (i = 0; i < count; ++i) {
66 item = [items objectAtIndex:i];
67 if ([item hasSubmenu]) {
68 item = findMenuItemWithTagInMenu([item submenu], tag);
69 if (item) return item;
79 @implementation MMVimController
81 - (id)initWithBackend:(id)backend
83 if ((self = [super init])) {
85 [[MMWindowController alloc] initWithVimController:self];
86 backendProxy = [backend retain];
87 sendQueue = [NSMutableArray new];
88 mainMenuItems = [[NSMutableArray alloc] init];
89 popupMenuItems = [[NSMutableArray alloc] init];
90 toolbarItemDict = [[NSMutableDictionary alloc] init];
92 NSConnection *connection = [backendProxy connectionForProxy];
93 [[NSNotificationCenter defaultCenter] addObserver:self
94 selector:@selector(connectionDidDie:)
95 name:NSConnectionDidDieNotification object:connection];
103 //NSLog(@"%@ %s", [self className], _cmd);
105 [[NSNotificationCenter defaultCenter] removeObserver:self];
107 [backendProxy release];
110 [toolbarItemDict release];
112 [popupMenuItems release];
113 [mainMenuItems release];
114 [windowController release];
119 - (MMWindowController *)windowController
121 return windowController;
124 - (void)sendMessage:(int)msgid data:(NSData *)data wait:(BOOL)wait
126 if (inProcessCommandQueue) {
127 //NSLog(@"In process command queue; delaying message send.");
128 [sendQueue addObject:[NSNumber numberWithInt:msgid]];
130 [sendQueue addObject:data];
132 [sendQueue addObject:[NSNull null]];
137 [backendProxy processInput:msgid data:data];
139 // Do not wait for the message to be sent, i.e. drop the message if it
140 // can't be delivered immediately.
141 NSConnection *connection = [backendProxy connectionForProxy];
143 NSTimeInterval req = [connection requestTimeout];
144 [connection setRequestTimeout:0];
146 [backendProxy processInput:msgid data:data];
148 @catch (NSException *e) {
149 // Connection timed out, just ignore this.
150 //NSLog(@"WARNING! Connection timed out in %s", _cmd);
153 [connection setRequestTimeout:req];
164 - (oneway void)showSavePanelForDirectory:(in bycopy NSString *)dir
165 title:(in bycopy NSString *)title
168 [windowController setStatusText:title];
171 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
172 modalForWindow:[windowController window]
174 didEndSelector:@selector(panelDidEnd:code:context:)
177 NSOpenPanel *panel = [NSOpenPanel openPanel];
178 [panel setAllowsMultipleSelection:NO];
179 [panel beginSheetForDirectory:dir file:nil types:nil
180 modalForWindow:[windowController window]
182 didEndSelector:@selector(panelDidEnd:code:context:)
187 - (oneway void)processCommandQueue:(in NSArray *)queue
189 unsigned i, count = [queue count];
191 NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
192 "message; ignoring this message.", count);
196 inProcessCommandQueue = YES;
198 //NSLog(@"======== %s BEGIN ========", _cmd);
199 for (i = 0; i < count; i += 2) {
200 NSData *value = [queue objectAtIndex:i];
201 NSData *data = [queue objectAtIndex:i+1];
203 int msgid = *((int*)[value bytes]);
205 if (msgid != EnableMenuItemMsgID && msgid != AddMenuItemMsgID
206 && msgid != AddMenuMsgID) {
207 NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
211 [self handleMessage:msgid data:data];
213 //NSLog(@"======== %s END ========", _cmd);
215 if (shouldUpdateMainMenu) {
216 [self updateMainMenu];
219 [windowController processCommandQueueDidFinish];
221 inProcessCommandQueue = NO;
223 count = [sendQueue count];
225 if (count % 2 == 0) {
226 //NSLog(@"%s Sending %d queued messages", _cmd, count/2);
228 for (i = 0; i < count; i += 2) {
229 int msgid = [[sendQueue objectAtIndex:i] intValue];
230 id data = [sendQueue objectAtIndex:i+1];
231 if ([data isEqual:[NSNull null]])
234 [backendProxy processInput:msgid data:data];
238 [sendQueue removeAllObjects];
242 - (void)windowWillClose:(NSNotification *)notification
244 // NOTE! This causes the call to removeVimController: to be delayed.
246 performSelectorOnMainThread:@selector(removeVimController:)
247 withObject:self waitUntilDone:NO];
250 - (void)windowDidBecomeMain:(NSNotification *)notification
252 [self updateMainMenu];
255 - (NSToolbarItem *)toolbar:(NSToolbar *)theToolbar
256 itemForItemIdentifier:(NSString *)itemId
257 willBeInsertedIntoToolbar:(BOOL)flag
259 //NSLog(@"%s", _cmd);
261 NSToolbarItem *item = [toolbarItemDict objectForKey:itemId];
263 NSLog(@"WARNING: No toolbar item with id '%@'", itemId);
269 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)theToolbar
271 //NSLog(@"%s", _cmd);
275 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)theToolbar
277 //NSLog(@"%s", _cmd);
281 @end // MMVimController
285 @implementation MMVimController (Private)
287 - (void)handleMessage:(int)msgid data:(NSData *)data
289 //NSLog(@"%@ %s", [self className], _cmd);
291 if (OpenVimWindowMsgID == msgid) {
292 [windowController openWindow];
293 } else if (BatchDrawMsgID == msgid) {
294 //NSLog(@"Received batch draw message from VimTask.");
296 [self performBatchDrawWithData:data];
297 } else if (SelectTabMsgID == msgid) {
298 #if 0 // NOTE: Tab selection is done inside updateTabsWithData:.
299 const void *bytes = [data bytes];
300 int idx = *((int*)bytes);
301 //NSLog(@"Selecting tab with index %d", idx);
302 [windowController selectTabWithIndex:idx];
304 } else if (UpdateTabBarMsgID == msgid) {
305 //NSLog(@"Updating tabs");
306 [windowController updateTabsWithData:data];
307 } else if (ShowTabBarMsgID == msgid) {
308 //NSLog(@"Showing tab bar");
309 [windowController showTabBar:self];
310 } else if (HideTabBarMsgID == msgid) {
311 //NSLog(@"Hiding tab bar");
312 [windowController hideTabBar:self];
313 } else if (SetTextDimensionsMsgID == msgid) {
314 const void *bytes = [data bytes];
315 int rows = *((int*)bytes); bytes += sizeof(int);
316 int cols = *((int*)bytes); bytes += sizeof(int);
318 [windowController setTextDimensionsWithRows:rows columns:cols];
319 } else if (SetVimWindowTitleMsgID == msgid) {
320 const void *bytes = [data bytes];
321 int len = *((int*)bytes); bytes += sizeof(int);
323 NSString *string = [[NSString alloc] initWithBytes:(void*)bytes
324 length:len encoding:NSUTF8StringEncoding];
326 [[windowController window] setTitle:string];
329 } else if (BrowseForFileMsgID == msgid) {
330 const void *bytes = [data bytes];
331 int save = *((int*)bytes); bytes += sizeof(int);
333 int len = *((int*)bytes); bytes += sizeof(int);
336 dir = [[NSString alloc] initWithBytes:(void*)bytes
338 encoding:NSUTF8StringEncoding];
342 len = *((int*)bytes); bytes += sizeof(int);
344 NSString *title = [[NSString alloc]
345 initWithBytes:(void*)bytes length:len
346 encoding:NSUTF8StringEncoding];
349 [windowController setStatusText:title];
354 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
355 modalForWindow:[windowController window]
357 didEndSelector:@selector(panelDidEnd:code:context:)
360 NSOpenPanel *panel = [NSOpenPanel openPanel];
361 [panel setAllowsMultipleSelection:NO];
362 [panel beginSheetForDirectory:dir file:nil types:nil
363 modalForWindow:[windowController window]
365 didEndSelector:@selector(panelDidEnd:code:context:)
370 } else if (UpdateInsertionPointMsgID == msgid) {
371 const void *bytes = [data bytes];
372 int color = *((int*)bytes); bytes += sizeof(int);
373 int row = *((int*)bytes); bytes += sizeof(int);
374 int col = *((int*)bytes); bytes += sizeof(int);
375 int state = *((int*)bytes); bytes += sizeof(int);
377 // TODO! Move to window controller.
378 MMTextView *textView = [windowController textView];
380 MMTextStorage *textStorage = (MMTextStorage*)[textView textStorage];
381 unsigned off = [textStorage offsetFromRow:row column:col];
383 [textView setInsertionPointColor:[NSColor colorWithRgbInt:color]];
384 [textView setSelectedRange:NSMakeRange(off, 0)];
385 [textView setShouldDrawInsertionPoint:state];
387 } else if (AddMenuMsgID == msgid) {
388 NSString *title = nil;
389 const void *bytes = [data bytes];
390 int tag = *((int*)bytes); bytes += sizeof(int);
391 int parentTag = *((int*)bytes); bytes += sizeof(int);
392 int len = *((int*)bytes); bytes += sizeof(int);
394 title = [[NSString alloc] initWithBytes:(void*)bytes length:len
395 encoding:NSUTF8StringEncoding];
398 int idx = *((int*)bytes); bytes += sizeof(int);
400 if (MenuToolbarType == parentTag) {
402 NSString *ident = [NSString stringWithFormat:@"%d.%d",
404 //NSLog(@"Creating toolbar with identifier %@", ident);
405 toolbar = [[NSToolbar alloc] initWithIdentifier:ident];
407 [toolbar setShowsBaselineSeparator:NO];
408 [toolbar setDelegate:self];
409 [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
410 [toolbar setSizeMode:NSToolbarSizeModeSmall];
412 [[windowController window] setToolbar:toolbar];
415 [self addMenuWithTag:tag parent:parentTag title:title atIndex:idx];
419 } else if (AddMenuItemMsgID == msgid) {
420 NSString *title = nil, *tip = nil, *icon = nil, *action = nil;
421 const void *bytes = [data bytes];
422 int tag = *((int*)bytes); bytes += sizeof(int);
423 int parentTag = *((int*)bytes); bytes += sizeof(int);
424 int namelen = *((int*)bytes); bytes += sizeof(int);
426 title = [[NSString alloc] initWithBytes:(void*)bytes length:namelen
427 encoding:NSUTF8StringEncoding];
430 int tiplen = *((int*)bytes); bytes += sizeof(int);
432 tip = [[NSString alloc] initWithBytes:(void*)bytes length:tiplen
433 encoding:NSUTF8StringEncoding];
436 int iconlen = *((int*)bytes); bytes += sizeof(int);
438 icon = [[NSString alloc] initWithBytes:(void*)bytes length:iconlen
439 encoding:NSUTF8StringEncoding];
442 int actionlen = *((int*)bytes); bytes += sizeof(int);
444 action = [[NSString alloc] initWithBytes:(void*)bytes
446 encoding:NSUTF8StringEncoding];
449 int idx = *((int*)bytes); bytes += sizeof(int);
450 if (idx < 0) idx = 0;
451 int key = *((int*)bytes); bytes += sizeof(int);
452 int mask = *((int*)bytes); bytes += sizeof(int);
454 NSString *ident = [NSString stringWithFormat:@"%d.%d",
455 (int)self, parentTag];
456 if (toolbar && [[toolbar identifier] isEqual:ident]) {
457 [self addToolbarItemWithTag:tag label:title tip:tip icon:icon
460 NSMenu *parent = [self menuForTag:parentTag];
461 [self addMenuItemWithTag:tag parent:parent title:title tip:tip
462 keyEquivalent:key modifiers:mask action:action
470 } else if (RemoveMenuItemMsgID == msgid) {
471 const void *bytes = [data bytes];
472 int tag = *((int*)bytes); bytes += sizeof(int);
476 if ((item = [self toolbarItemForTag:tag index:&idx])) {
477 [toolbar removeItemAtIndex:idx];
478 } else if ((item = [self menuItemForTag:tag])) {
481 if ([item menu] == [NSApp mainMenu] || ![item menu]) {
482 //NSLog(@"Removing menu: %@", item);
483 // NOTE: To be on the safe side we try to remove the item from
484 // both arrays (it is ok to call removeObject: even if an array
485 // does not contain the object to remove).
486 [mainMenuItems removeObject:item];
487 [popupMenuItems removeObject:item];
491 [[item menu] removeItem:item];
495 } else if (EnableMenuItemMsgID == msgid) {
496 const void *bytes = [data bytes];
497 int tag = *((int*)bytes); bytes += sizeof(int);
498 int state = *((int*)bytes); bytes += sizeof(int);
500 id item = [self toolbarItemForTag:tag index:NULL];
502 item = [self menuItemForTag:tag];
504 [item setEnabled:state];
505 } else if (ShowToolbarMsgID == msgid) {
506 const void *bytes = [data bytes];
507 int enable = *((int*)bytes); bytes += sizeof(int);
508 int flags = *((int*)bytes); bytes += sizeof(int);
510 int mode = NSToolbarDisplayModeDefault;
511 if (flags & ToolbarLabelFlag) {
512 mode = flags & ToolbarIconFlag ? NSToolbarDisplayModeIconAndLabel
513 : NSToolbarDisplayModeLabelOnly;
514 } else if (flags & ToolbarIconFlag) {
515 mode = NSToolbarDisplayModeIconOnly;
518 int size = flags & ToolbarSizeRegularFlag ? NSToolbarSizeModeRegular
519 : NSToolbarSizeModeSmall;
521 [toolbar setSizeMode:size];
522 [toolbar setDisplayMode:mode];
523 [toolbar setVisible:enable];
524 } else if (CreateScrollbarMsgID == msgid) {
525 const void *bytes = [data bytes];
526 long ident = *((long*)bytes); bytes += sizeof(long);
527 int type = *((int*)bytes); bytes += sizeof(int);
529 [windowController createScrollbarWithIdentifier:ident type:type];
530 } else if (DestroyScrollbarMsgID == msgid) {
531 const void *bytes = [data bytes];
532 long ident = *((long*)bytes); bytes += sizeof(long);
534 [windowController destroyScrollbarWithIdentifier:ident];
535 } else if (ShowScrollbarMsgID == msgid) {
536 const void *bytes = [data bytes];
537 long ident = *((long*)bytes); bytes += sizeof(long);
538 int visible = *((int*)bytes); bytes += sizeof(int);
540 [windowController showScrollbarWithIdentifier:ident state:visible];
541 } else if (SetScrollbarPositionMsgID == msgid) {
542 const void *bytes = [data bytes];
543 long ident = *((long*)bytes); bytes += sizeof(long);
544 int pos = *((int*)bytes); bytes += sizeof(int);
545 int len = *((int*)bytes); bytes += sizeof(int);
547 [windowController setScrollbarPosition:pos length:len
549 } else if (SetScrollbarThumbMsgID == msgid) {
550 const void *bytes = [data bytes];
551 long ident = *((long*)bytes); bytes += sizeof(long);
552 float val = *((float*)bytes); bytes += sizeof(float);
553 float prop = *((float*)bytes); bytes += sizeof(float);
555 [windowController setScrollbarThumbValue:val proportion:prop
557 } else if (SetFontMsgID == msgid) {
558 const void *bytes = [data bytes];
559 float size = *((float*)bytes); bytes += sizeof(float);
560 int len = *((int*)bytes); bytes += sizeof(int);
561 NSString *name = [[NSString alloc]
562 initWithBytes:(void*)bytes length:len
563 encoding:NSUTF8StringEncoding];
564 NSFont *font = [NSFont fontWithName:name size:size];
567 [windowController setFont:font];
570 } else if (SetDefaultColorsMsgID == msgid) {
571 const void *bytes = [data bytes];
572 int bg = *((int*)bytes); bytes += sizeof(int);
573 int fg = *((int*)bytes); bytes += sizeof(int);
574 NSColor *back = [NSColor colorWithRgbInt:bg];
575 NSColor *fore = [NSColor colorWithRgbInt:fg];
577 [windowController setDefaultColorsBackground:back foreground:fore];
578 } else if (ExecuteActionMsgID == msgid) {
579 const void *bytes = [data bytes];
580 int len = *((int*)bytes); bytes += sizeof(int);
581 NSString *actionName = [[NSString alloc]
582 initWithBytesNoCopy:(void*)bytes
584 encoding:NSUTF8StringEncoding
587 SEL sel = NSSelectorFromString(actionName);
588 [NSApp sendAction:sel to:nil from:self];
590 [actionName release];
591 } else if (ShowPopupMenuMsgID == msgid) {
592 const void *bytes = [data bytes];
593 int row = *((int*)bytes); bytes += sizeof(int);
594 int col = *((int*)bytes); bytes += sizeof(int);
595 int len = *((int*)bytes); bytes += sizeof(int);
596 NSString *title = [[NSString alloc]
597 initWithBytesNoCopy:(void*)bytes
599 encoding:NSUTF8StringEncoding
602 NSMenu *menu = [self topLevelMenuForTitle:title];
604 [windowController popupMenu:menu atRow:row column:col];
606 NSLog(@"WARNING: Cannot popup menu with title %@; no such menu.",
612 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
616 - (void)performBatchDrawWithData:(NSData *)data
618 // TODO! Move to window controller.
619 MMTextStorage *textStorage = [windowController textStorage];
623 const void *bytes = [data bytes];
624 const void *end = bytes + [data length];
626 [textStorage beginEditing];
629 // 1. Sanity check input
630 // 2. Cache rgb -> NSColor lookups?
632 while (bytes < end) {
633 int type = *((int*)bytes); bytes += sizeof(int);
635 if (ClearAllDrawType == type) {
636 int color = *((int*)bytes); bytes += sizeof(int);
638 [textStorage clearAllWithColor:[NSColor colorWithRgbInt:color]];
639 } else if (ClearBlockDrawType == type) {
640 int color = *((int*)bytes); bytes += sizeof(int);
641 int row1 = *((int*)bytes); bytes += sizeof(int);
642 int col1 = *((int*)bytes); bytes += sizeof(int);
643 int row2 = *((int*)bytes); bytes += sizeof(int);
644 int col2 = *((int*)bytes); bytes += sizeof(int);
646 [textStorage clearBlockFromRow:row1 column:col1
647 toRow:row2 column:col2
648 color:[NSColor colorWithRgbInt:color]];
649 } else if (DeleteLinesDrawType == type) {
650 int color = *((int*)bytes); bytes += sizeof(int);
651 int row = *((int*)bytes); bytes += sizeof(int);
652 int count = *((int*)bytes); bytes += sizeof(int);
653 int bot = *((int*)bytes); bytes += sizeof(int);
654 int left = *((int*)bytes); bytes += sizeof(int);
655 int right = *((int*)bytes); bytes += sizeof(int);
657 [textStorage deleteLinesFromRow:row lineCount:count
658 scrollBottom:bot left:left right:right
659 color:[NSColor colorWithRgbInt:color]];
660 } else if (ReplaceStringDrawType == type) {
661 int bg = *((int*)bytes); bytes += sizeof(int);
662 int fg = *((int*)bytes); bytes += sizeof(int);
663 int row = *((int*)bytes); bytes += sizeof(int);
664 int col = *((int*)bytes); bytes += sizeof(int);
665 int flags = *((int*)bytes); bytes += sizeof(int);
666 int len = *((int*)bytes); bytes += sizeof(int);
667 NSString *string = [[NSString alloc]
668 initWithBytesNoCopy:(void*)bytes
670 encoding:NSUTF8StringEncoding
674 [textStorage replaceString:string
677 foregroundColor:[NSColor colorWithRgbInt:fg]
678 backgroundColor:[NSColor colorWithRgbInt:bg]];
681 } else if (InsertLinesDrawType == type) {
682 int color = *((int*)bytes); bytes += sizeof(int);
683 int row = *((int*)bytes); bytes += sizeof(int);
684 int count = *((int*)bytes); bytes += sizeof(int);
685 int bot = *((int*)bytes); bytes += sizeof(int);
686 int left = *((int*)bytes); bytes += sizeof(int);
687 int right = *((int*)bytes); bytes += sizeof(int);
689 [textStorage insertLinesAtRow:row lineCount:count
690 scrollBottom:bot left:left right:right
691 color:[NSColor colorWithRgbInt:color]];
693 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
697 [textStorage endEditing];
700 - (void)panelDidEnd:(NSSavePanel *)panel code:(int)code context:(void *)context
702 [windowController setStatusText:@""];
704 NSString *string = (code == NSOKButton) ? [panel filename] : nil;
705 [backendProxy setBrowseForFileString:string];
708 - (NSMenuItem *)menuItemForTag:(int)tag
710 // Search the main menu.
711 int i, count = [mainMenuItems count];
712 for (i = 0; i < count; ++i) {
713 NSMenuItem *item = [mainMenuItems objectAtIndex:i];
714 if ([item tag] == tag) return item;
715 item = findMenuItemWithTagInMenu([item submenu], tag);
716 if (item) return item;
719 // Search the popup menus.
720 count = [popupMenuItems count];
721 for (i = 0; i < count; ++i) {
722 NSMenuItem *item = [popupMenuItems objectAtIndex:i];
723 if ([item tag] == tag) return item;
724 item = findMenuItemWithTagInMenu([item submenu], tag);
725 if (item) return item;
731 - (NSMenu *)menuForTag:(int)tag
733 return [[self menuItemForTag:tag] submenu];
736 - (NSMenu *)topLevelMenuForTitle:(NSString *)title
738 // Search only the top-level menus.
740 unsigned i, count = [popupMenuItems count];
741 for (i = 0; i < count; ++i) {
742 NSMenuItem *item = [popupMenuItems objectAtIndex:i];
743 if ([title isEqual:[item title]])
744 return [item submenu];
747 count = [mainMenuItems count];
748 for (i = 0; i < count; ++i) {
749 NSMenuItem *item = [mainMenuItems objectAtIndex:i];
750 if ([title isEqual:[item title]])
751 return [item submenu];
757 - (void)addMenuWithTag:(int)tag parent:(int)parentTag title:(NSString *)title
760 NSMenu *parent = [self menuForTag:parentTag];
761 NSMenuItem *item = [[NSMenuItem alloc] init];
762 NSMenu *menu = [[NSMenu alloc] initWithTitle:title];
764 [menu setAutoenablesItems:NO];
766 [item setTitle:title];
767 [item setSubmenu:menu];
770 if ([parent numberOfItems] <= idx) {
771 [parent addItem:item];
773 [parent insertItem:item atIndex:idx];
776 NSMutableArray *items = (MenuPopupType == parentTag)
777 ? popupMenuItems : mainMenuItems;
778 if ([items count] <= idx) {
779 [items addObject:item];
781 [items insertObject:item atIndex:idx];
784 shouldUpdateMainMenu = (MenuPopupType != parentTag);
791 - (void)addMenuItemWithTag:(int)tag parent:(NSMenu *)parent
792 title:(NSString *)title tip:(NSString *)tip
793 keyEquivalent:(int)key modifiers:(int)mask
794 action:(NSString *)action atIndex:(int)idx
797 NSMenuItem *item = nil;
799 item = [[[NSMenuItem alloc] init] autorelease];
800 [item setTitle:title];
801 // TODO: Check that 'action' is a valid action (nothing will happen
802 // if it isn't, but it would be nice with a warning).
803 if (action) [item setAction:NSSelectorFromString(action)];
804 else [item setAction:@selector(vimMenuItemAction:)];
805 if (tip) [item setToolTip:tip];
808 NSString *keyString =
809 [NSString stringWithFormat:@"%C", key];
810 [item setKeyEquivalent:keyString];
811 [item setKeyEquivalentModifierMask:mask];
814 item = [NSMenuItem separatorItem];
817 // NOTE! The tag is used to idenfity which menu items were
818 // added by Vim (tag != 0) and which were added by the AppKit
822 if ([parent numberOfItems] <= idx) {
823 [parent addItem:item];
825 [parent insertItem:item atIndex:idx];
828 NSLog(@"WARNING: Menu item '%@' (tag=%d) has no parent.", title, tag);
832 - (void)updateMainMenu
834 NSMenu *mainMenu = [NSApp mainMenu];
836 // Stop NSApp from updating the Window menu.
837 [NSApp setWindowsMenu:nil];
839 // Remove all menus from main menu (except the MacVim menu).
840 int i, count = [mainMenu numberOfItems];
841 for (i = count-1; i > 0; --i) {
842 [mainMenu removeItemAtIndex:i];
845 // Add menus from 'mainMenuItems' to main menu.
846 count = [mainMenuItems count];
847 for (i = 0; i < count; ++i) {
848 [mainMenu addItem:[mainMenuItems objectAtIndex:i]];
851 // Set the new Window menu.
852 // TODO! Need to look for 'Window' in all localized languages.
853 NSMenu *windowMenu = [[mainMenu itemWithTitle:@"Window"] submenu];
855 // Remove all AppKit owned menu items (tag == 0); they will be added
856 // again when setWindowsMenu: is called.
857 count = [windowMenu numberOfItems];
858 for (i = count-1; i >= 0; --i) {
859 NSMenuItem *item = [windowMenu itemAtIndex:i];
861 [windowMenu removeItem:item];
865 [NSApp setWindowsMenu:windowMenu];
868 shouldUpdateMainMenu = NO;
871 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index
873 if (!toolbar) return nil;
875 NSArray *items = [toolbar items];
876 int i, count = [items count];
877 for (i = 0; i < count; ++i) {
878 NSToolbarItem *item = [items objectAtIndex:i];
879 if ([item tag] == tag) {
880 if (index) *index = i;
888 - (IBAction)toolbarAction:(id)sender
890 NSLog(@"%s%@", _cmd, sender);
893 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
894 toolTip:(NSString *)tip icon:(NSString *)icon
896 // NOTE! 'title' is nul for separator item. Since this is already defined
897 // by Coca, we don't need to do anything here.
900 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:title];
902 [item setLabel:title];
903 [item setToolTip:tip];
904 [item setAction:@selector(vimMenuItemAction:)];
905 [item setAutovalidates:NO];
907 NSImage *img = [NSImage imageNamed:icon];
909 NSLog(@"WARNING: Could not find image with name '%@' to use as toolbar"
910 " image for identifier '%@';"
911 " using default toolbar icon '%@' instead.",
912 icon, title, DefaultToolbarImageName);
914 img = [NSImage imageNamed:DefaultToolbarImageName];
919 [toolbarItemDict setObject:item forKey:title];
924 - (void)addToolbarItemWithTag:(int)tag label:(NSString *)label tip:(NSString
925 *)tip icon:(NSString *)icon atIndex:(int)idx
927 if (!toolbar) return;
929 [self addToolbarItemToDictionaryWithTag:tag label:label toolTip:tip
932 int maxIdx = [[toolbar items] count];
933 if (maxIdx < idx) idx = maxIdx;
935 // If 'label' is nul, insert a separator.
936 if (!label) label = NSToolbarSeparatorItemIdentifier;
937 [toolbar insertItemWithItemIdentifier:label atIndex:idx];
940 - (void)connectionDidDie:(NSNotification *)notification
942 //NSLog(@"A MMVimController lost its connection to the backend; "
943 // "closing the controller.");
944 [windowController close];
947 - (BOOL)executeActionWithName:(NSString *)name
950 static NSDictionary *actionDict = nil;
953 NSBundle *mainBundle = [NSBundle mainBundle];
954 NSString *path = [mainBundle pathForResource:@"Actions"
957 actionDict = [[NSDictionary alloc] initWithContentsOfFile:path];
958 NSLog(@"Actions = %@", actionDict);
960 NSLog(@"WARNING: Failed to load dictionary of actions "
966 if ([actionDict objectForKey:name]) {
967 NSLog(@"Executing action %@", name);
968 SEL sel = NSSelectorFromString(name);
970 if ([NSApp sendAction:sel to:nil from:self])
973 NSLog(@"WARNING: Failed to send action");
975 NSLog(@"WARNING: Action with name '%@' cannot be executed.", name);
982 @end // MMVimController (Private)
986 @implementation NSColor (MMProtocol)
988 + (NSColor *)colorWithRgbInt:(int)rgb
990 float r = ((rgb>>16) & 0xff)/255.0f;
991 float g = ((rgb>>8) & 0xff)/255.0f;
992 float b = (rgb & 0xff)/255.0f;
994 return [NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0f];
997 @end // NSColor (MMProtocol)