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)updateMainMenu;
30 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index;
31 - (IBAction)toolbarAction:(id)sender;
32 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
33 toolTip:(NSString *)tip icon:(NSString *)icon;
35 - (void)connectionDidDie:(NSNotification *)notification;
37 - (BOOL)executeActionWithName:(NSString *)name;
42 // TODO: Move to separate file
43 @interface NSColor (MMProtocol)
44 + (NSColor *)colorWithRgbInt:(int)rgb;
49 static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag)
52 NSMenuItem *item = [root itemWithTag:tag];
53 if (item) return item;
55 NSArray *items = [root itemArray];
56 unsigned i, count = [items count];
57 for (i = 0; i < count; ++i) {
58 item = [items objectAtIndex:i];
59 if ([item hasSubmenu]) {
60 item = findMenuItemWithTagInMenu([item submenu], tag);
61 if (item) return item;
71 @implementation MMVimController
74 - (id)initWithBackend:(id)backend
76 - (id)initWithPort:(NSPort *)port
79 if ((self = [super init])) {
81 [[MMWindowController alloc] initWithVimController:self];
83 backendProxy = [backend retain];
84 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
85 sendQueue = [NSMutableArray new];
88 NSConnection *connection = [backendProxy connectionForProxy];
89 [[NSNotificationCenter defaultCenter] addObserver:self
90 selector:@selector(connectionDidDie:)
91 name:NSConnectionDidDieNotification object:connection];
93 sendPort = [port retain];
95 // Init receive port and send connected message to VimTask
96 receivePort = [NSMachPort new];
97 [receivePort setDelegate:self];
99 // Add to the default run loop mode as well as the event tracking mode;
100 // the latter ensures that updates from the VimTask reaches
101 // MMVimController whilst the user resizes a window with the mouse.
102 [[NSRunLoop currentRunLoop] addPort:receivePort
103 forMode:NSDefaultRunLoopMode];
104 [[NSRunLoop currentRunLoop] addPort:receivePort
105 forMode:NSEventTrackingRunLoopMode];
107 [NSPortMessage sendMessage:ConnectedMsgID withSendPort:sendPort
108 receivePort:receivePort wait:YES];
111 mainMenuItems = [[NSMutableArray alloc] init];
113 toolbarItemDict = [[NSMutableDictionary alloc] init];
114 //[self addToolbarItemToDictionaryWithTag:0 label:@"Attention"
115 // toolTip:@"A toolbar item is missing"
116 // icon:@"Attention"];
118 [[NSNotificationCenter defaultCenter]
120 selector:@selector(windowWillClose:)
121 name:NSWindowWillCloseNotification
122 object:[windowController window]];
123 [[NSNotificationCenter defaultCenter]
125 selector:@selector(windowDidBecomeMain:)
126 name:NSWindowDidBecomeMainNotification
127 object:[windowController window]];
136 //NSLog(@"%@ %s", [self className], _cmd);
138 [[NSNotificationCenter defaultCenter] removeObserver:self];
141 [backendProxy release];
142 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
147 // Kill task immediately
148 [NSPortMessage sendMessage:KillTaskMsgID withSendPort:sendPort
149 receivePort:receivePort wait:NO];
153 [receivePort release];
156 [toolbarItemDict release];
158 [mainMenuItems release];
159 [windowController release];
164 - (MMWindowController *)windowController
166 return windowController;
169 - (void)sendMessage:(int)msgid data:(NSData *)data wait:(BOOL)wait
172 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
173 if (inProcessCommandQueue) {
174 //NSLog(@"In process command queue; delaying message send.");
175 [sendQueue addObject:[NSNumber numberWithInt:msgid]];
177 [sendQueue addObject:data];
179 [sendQueue addObject:[NSNull null]];
184 [backendProxy processInput:msgid data:data];
186 // Do not wait for the message to be sent, i.e. drop the message if it
187 // can't be delivered immediately.
188 NSConnection *connection = [backendProxy connectionForProxy];
190 NSTimeInterval req = [connection requestTimeout];
191 [connection setRequestTimeout:0];
193 [backendProxy processInput:msgid data:data];
195 @catch (NSException *e) {
196 // Connection timed out, just ignore this.
197 //NSLog(@"WARNING! Connection timed out in %s", _cmd);
200 [connection setRequestTimeout:req];
205 [NSPortMessage sendMessage:msgid withSendPort:sendPort data:data
216 - (oneway void)showSavePanelForDirectory:(in bycopy NSString *)dir
217 title:(in bycopy NSString *)title
220 [windowController setStatusText:title];
223 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
224 modalForWindow:[windowController window]
226 didEndSelector:@selector(panelDidEnd:code:context:)
229 NSOpenPanel *panel = [NSOpenPanel openPanel];
230 [panel setAllowsMultipleSelection:NO];
231 [panel beginSheetForDirectory:dir file:nil types:nil
232 modalForWindow:[windowController window]
234 didEndSelector:@selector(panelDidEnd:code:context:)
239 - (oneway void)processCommandQueue:(in NSArray *)queue
241 unsigned i, count = [queue count];
243 NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
244 "message; ignoring this message.", count);
248 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
249 inProcessCommandQueue = YES;
252 //NSLog(@"======== %s BEGIN ========", _cmd);
253 for (i = 0; i < count; i += 2) {
254 NSData *value = [queue objectAtIndex:i];
255 NSData *data = [queue objectAtIndex:i+1];
257 int msgid = *((int*)[value bytes]);
259 if (msgid != EnableMenuItemMsgID && msgid != AddMenuItemMsgID
260 && msgid != AddMenuMsgID) {
261 NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
265 [self handleMessage:msgid data:data];
267 //NSLog(@"======== %s END ========", _cmd);
269 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
270 inProcessCommandQueue = NO;
272 count = [sendQueue count];
274 if (count % 2 == 0) {
275 //NSLog(@"%s Sending %d queued messages", _cmd, count/2);
277 for (i = 0; i < count; i += 2) {
278 int msgid = [[sendQueue objectAtIndex:i] intValue];
279 id data = [sendQueue objectAtIndex:i+1];
280 if ([data isEqual:[NSNull null]])
283 [backendProxy processInput:msgid data:data];
287 [sendQueue removeAllObjects];
299 - (void)handlePortMessage:(NSPortMessage *)portMessage
301 //NSLog(@"%@ %s %@", [self className], _cmd, portMessage);
303 NSArray *components = [portMessage components];
304 unsigned msgid = [portMessage msgid];
306 //NSLog(@"%s%d", _cmd, msgid);
308 if (FlushQueueMsgID == msgid) {
309 unsigned i, count = [components count];
311 NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
312 "message; ignoring this message.", count);
316 for (i = 0; i < count; i += 2) {
317 NSData *value = [components objectAtIndex:i];
318 NSData *data = [components objectAtIndex:i+1];
320 [self handleMessage:*((int*)[value bytes]) data:data];
324 if ([components count] > 0)
325 data = [components objectAtIndex:0];
327 [self handleMessage:msgid data:data];
330 if (shouldUpdateMainMenu)
331 [self updateMainMenu];
335 - (void)windowWillClose:(NSNotification *)notification
337 // NOTE! This causes the call to removeVimController: to be delayed.
339 performSelectorOnMainThread:@selector(removeVimController:)
340 withObject:self waitUntilDone:NO];
343 - (void)windowDidBecomeMain:(NSNotification *)notification
345 [self updateMainMenu];
348 - (NSToolbarItem *)toolbar:(NSToolbar *)theToolbar
349 itemForItemIdentifier:(NSString *)itemId
350 willBeInsertedIntoToolbar:(BOOL)flag
352 //NSLog(@"%s", _cmd);
354 NSToolbarItem *item = [toolbarItemDict objectForKey:itemId];
356 NSLog(@"WARNING: No toolbar item with id '%@'", itemId);
362 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)theToolbar
364 //NSLog(@"%s", _cmd);
368 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)theToolbar
370 //NSLog(@"%s", _cmd);
374 @end // MMVimController
378 @implementation MMVimController (Private)
380 - (void)handleMessage:(int)msgid data:(NSData *)data
382 //NSLog(@"%@ %s", [self className], _cmd);
384 if (OpenVimWindowMsgID == msgid) {
385 [windowController openWindow];
388 else if (TaskExitedMsgID == msgid) {
389 //NSLog(@"Received task exited message from VimTask; closing window.");
391 // Release sendPort immediately to avoid dealloc trying to send a 'kill
392 // task' message to the task.
393 [sendPort release]; sendPort = nil;
394 // NOTE! This causes windowWillClose: to be called, which in turn asks
395 // the MMAppController to remove this MMVimController.
396 [windowController close];
398 // HACK! Make sure no menu updating is done, we're about to close.
399 shouldUpdateMainMenu = NO;
402 else if (BatchDrawMsgID == msgid) {
403 //NSLog(@"Received batch draw message from VimTask.");
405 [self performBatchDrawWithData:data];
406 } else if (SelectTabMsgID == msgid) {
407 const void *bytes = [data bytes];
408 int idx = *((int*)bytes);
409 //NSLog(@"Selecting tab with index %d", idx);
410 [windowController selectTabWithIndex:idx];
411 } else if (UpdateTabBarMsgID == msgid) {
412 //NSLog(@"Updating tabs");
413 [windowController updateTabsWithData:data];
414 } else if (ShowTabBarMsgID == msgid) {
415 //NSLog(@"Showing tab bar");
417 // The tab bar has it's own baseline separator, so hide the one
418 // belonging to the toolbar whenever the tab bar is visible.
419 // BUG: The window auto shows the separator when clicking the show/hide
421 [toolbar setShowsBaselineSeparator:NO];
423 // HACK! Vim sends several draw commands etc. after the show message
424 // and these can mess up the display when showing the tab bar results
425 // in the window having to resize to fit the screen; delaying this
426 // message alleviates this problem.
427 [windowController performSelectorOnMainThread:@selector(showTabBar:)
428 withObject:self waitUntilDone:NO];
429 //[windowController showTabBar:self];
430 } else if (HideTabBarMsgID == msgid) {
431 //NSLog(@"Hiding tab bar");
433 // The tab bar has it's own baseline separator, so hide the one
434 // belonging to the toolbar whenever the tab bar is visible.
435 // BUG: The window auto shows the separator when clicking the show/hide
437 [toolbar setShowsBaselineSeparator:YES];
439 [windowController hideTabBar:self];
440 } else if (SetTextDimensionsMsgID == msgid) {
441 const void *bytes = [data bytes];
442 int rows = *((int*)bytes); bytes += sizeof(int);
443 int cols = *((int*)bytes); bytes += sizeof(int);
445 [windowController setTextDimensionsWithRows:rows columns:cols];
446 } else if (SetVimWindowTitleMsgID == msgid) {
447 const void *bytes = [data bytes];
448 int len = *((int*)bytes); bytes += sizeof(int);
451 // BUG! Using this call leads to ALL windows getting the same title
452 // and then the app crashes if you :q a window.
453 NSString *string = [[NSString alloc]
454 initWithBytesNoCopy:(void*)bytes
456 encoding:NSUTF8StringEncoding
459 NSString *string = [[NSString alloc] initWithBytes:(void*)bytes
460 length:len encoding:NSUTF8StringEncoding];
463 [[windowController window] setTitle:string];
466 } else if (BrowseForFileMsgID == msgid) {
467 const void *bytes = [data bytes];
468 int save = *((int*)bytes); bytes += sizeof(int);
470 int len = *((int*)bytes); bytes += sizeof(int);
473 dir = [[NSString alloc] initWithBytes:(void*)bytes
475 encoding:NSUTF8StringEncoding];
479 len = *((int*)bytes); bytes += sizeof(int);
481 NSString *title = [[NSString alloc]
482 initWithBytes:(void*)bytes length:len
483 encoding:NSUTF8StringEncoding];
486 [windowController setStatusText:title];
491 [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
492 modalForWindow:[windowController window]
494 didEndSelector:@selector(panelDidEnd:code:context:)
497 NSOpenPanel *panel = [NSOpenPanel openPanel];
498 [panel setAllowsMultipleSelection:NO];
499 [panel beginSheetForDirectory:dir file:nil types:nil
500 modalForWindow:[windowController window]
502 didEndSelector:@selector(panelDidEnd:code:context:)
507 } else if (UpdateInsertionPointMsgID == msgid) {
508 const void *bytes = [data bytes];
509 int color = *((int*)bytes); bytes += sizeof(int);
510 int row = *((int*)bytes); bytes += sizeof(int);
511 int col = *((int*)bytes); bytes += sizeof(int);
512 int state = *((int*)bytes); bytes += sizeof(int);
514 // TODO! Move to window controller.
515 MMTextView *textView = [windowController textView];
517 MMTextStorage *textStorage = (MMTextStorage*)[textView textStorage];
518 unsigned off = [textStorage offsetFromRow:row column:col];
520 [textView setInsertionPointColor:[NSColor colorWithRgbInt:color]];
521 [textView setSelectedRange:NSMakeRange(off, 0)];
522 [textView setShouldDrawInsertionPoint:state];
524 } else if (AddMenuMsgID == msgid) {
525 NSString *title = nil;
526 const void *bytes = [data bytes];
527 int tag = *((int*)bytes); bytes += sizeof(int);
528 int parentTag = *((int*)bytes); bytes += sizeof(int);
529 int len = *((int*)bytes); bytes += sizeof(int);
531 title = [[NSString alloc] initWithBytes:(void*)bytes length:len
532 encoding:NSUTF8StringEncoding];
535 int idx = *((int*)bytes); bytes += sizeof(int);
537 if (MenuToolbarType == parentTag) {
539 NSString *ident = [NSString stringWithFormat:@"%d.%d",
541 //NSLog(@"Creating toolbar with identifier %@", ident);
542 toolbar = [[NSToolbar alloc] initWithIdentifier:ident];
544 [toolbar setDelegate:self];
545 [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
546 [toolbar setSizeMode:NSToolbarSizeModeSmall];
548 [[windowController window] setToolbar:toolbar];
550 } else if (MenuPopupType == parentTag) {
553 NSMenuItem *item = [[NSMenuItem alloc] init];
554 NSMenu *menu = [[NSMenu alloc] initWithTitle:title];
556 [menu setAutoenablesItems:NO];
558 [item setTitle:title];
559 [item setSubmenu:menu];
561 NSMenu *parent = [self menuForTag:parentTag];
563 if ([parent numberOfItems] <= idx) {
564 [parent addItem:item];
566 [parent insertItem:item atIndex:idx];
569 if ([mainMenuItems count] <= idx) {
570 [mainMenuItems addObject:item];
572 [mainMenuItems insertObject:item atIndex:idx];
575 shouldUpdateMainMenu = YES;
583 } else if (AddMenuItemMsgID == msgid) {
584 NSString *title = nil, *tip = nil, *icon = nil;
585 const void *bytes = [data bytes];
586 int tag = *((int*)bytes); bytes += sizeof(int);
587 int parentTag = *((int*)bytes); bytes += sizeof(int);
588 int namelen = *((int*)bytes); bytes += sizeof(int);
590 title = [[NSString alloc] initWithBytes:(void*)bytes length:namelen
591 encoding:NSUTF8StringEncoding];
594 int tiplen = *((int*)bytes); bytes += sizeof(int);
596 tip = [[NSString alloc] initWithBytes:(void*)bytes length:tiplen
597 encoding:NSUTF8StringEncoding];
600 int iconlen = *((int*)bytes); bytes += sizeof(int);
602 icon = [[NSString alloc] initWithBytes:(void*)bytes length:iconlen
603 encoding:NSUTF8StringEncoding];
606 int idx = *((int*)bytes); bytes += sizeof(int);
607 if (idx < 0) idx = 0;
608 int key = *((int*)bytes); bytes += sizeof(int);
609 int mask = *((int*)bytes); bytes += sizeof(int);
611 NSString *ident = [NSString stringWithFormat:@"%d.%d",
612 (int)self, parentTag];
613 if (toolbar && [[toolbar identifier] isEqual:ident]) {
614 //NSLog(@"Toolbar add: title=%@ icon=%@ tip=%@", title, icon, tip);
615 [self addToolbarItemToDictionaryWithTag:tag label:title toolTip:tip
618 int maxIdx = [[toolbar items] count];
619 if (maxIdx < idx) idx = maxIdx;
621 // If 'title' is nul, insert a separator.
622 if (!title) title = NSToolbarSeparatorItemIdentifier;
623 [toolbar insertItemWithItemIdentifier:title atIndex:idx];
625 NSMenu *parent = [self menuForTag:parentTag];
627 NSMenuItem *item = nil;
629 item = [[[NSMenuItem alloc] init] autorelease];
631 [item setTitle:title];
632 [item setAction:@selector(vimMenuItemAction:)];
633 if (tip) [item setToolTip:tip];
636 NSString *keyString =
637 [NSString stringWithFormat:@"%C", key];
638 //NSLog(@"Set key equivalent %@ (code=0x%x, mods=%d)",
639 // keyString, key, mask);
640 [item setKeyEquivalent:keyString];
641 [item setKeyEquivalentModifierMask:mask];
644 item = [NSMenuItem separatorItem];
647 if ([parent numberOfItems] <= idx) {
648 [parent addItem:item];
650 [parent insertItem:item atIndex:idx];
658 } else if (RemoveMenuItemMsgID == msgid) {
659 const void *bytes = [data bytes];
660 int tag = *((int*)bytes); bytes += sizeof(int);
662 // TODO: Search for tag in popup menus.
665 if ((item = [self toolbarItemForTag:tag index:&idx])) {
666 [toolbar removeItemAtIndex:idx];
667 } else if ((item = [self menuItemForTag:tag])) {
668 if ([item menu] == [NSApp mainMenu]) {
669 [mainMenuItems removeObject:item];
671 [[item menu] removeItem:item];
673 } else if (EnableMenuItemMsgID == msgid) {
674 const void *bytes = [data bytes];
675 int tag = *((int*)bytes); bytes += sizeof(int);
676 int state = *((int*)bytes); bytes += sizeof(int);
678 // TODO: Search for tag in popup menus.
679 id item = [self toolbarItemForTag:tag index:NULL];
681 item = [self menuItemForTag:tag];
683 [item setEnabled:state];
684 } else if (ShowToolbarMsgID == msgid) {
685 const void *bytes = [data bytes];
686 int enable = *((int*)bytes); bytes += sizeof(int);
687 int flags = *((int*)bytes); bytes += sizeof(int);
689 int mode = NSToolbarDisplayModeDefault;
690 if (flags & ToolbarLabelFlag) {
691 mode = flags & ToolbarIconFlag ? NSToolbarDisplayModeIconAndLabel
692 : NSToolbarDisplayModeLabelOnly;
693 } else if (flags & ToolbarIconFlag) {
694 mode = NSToolbarDisplayModeIconOnly;
697 int size = flags & ToolbarSizeRegularFlag ? NSToolbarSizeModeRegular
698 : NSToolbarSizeModeSmall;
700 [toolbar setSizeMode:size];
701 [toolbar setDisplayMode:mode];
702 [toolbar setVisible:enable];
703 } else if (CreateScrollbarMsgID == msgid) {
704 const void *bytes = [data bytes];
705 long ident = *((long*)bytes); bytes += sizeof(long);
706 int type = *((int*)bytes); bytes += sizeof(int);
708 [windowController createScrollbarWithIdentifier:ident type:type];
709 } else if (DestroyScrollbarMsgID == msgid) {
710 const void *bytes = [data bytes];
711 long ident = *((long*)bytes); bytes += sizeof(long);
713 [windowController destroyScrollbarWithIdentifier:ident];
714 } else if (ShowScrollbarMsgID == msgid) {
715 const void *bytes = [data bytes];
716 long ident = *((long*)bytes); bytes += sizeof(long);
717 int visible = *((int*)bytes); bytes += sizeof(int);
719 [windowController showScrollbarWithIdentifier:ident state:visible];
720 } else if (SetScrollbarPositionMsgID == msgid) {
721 const void *bytes = [data bytes];
722 long ident = *((long*)bytes); bytes += sizeof(long);
723 int pos = *((int*)bytes); bytes += sizeof(int);
724 int len = *((int*)bytes); bytes += sizeof(int);
726 [windowController setScrollbarPosition:pos length:len
728 } else if (SetScrollbarThumbMsgID == msgid) {
729 const void *bytes = [data bytes];
730 long ident = *((long*)bytes); bytes += sizeof(long);
731 float val = *((float*)bytes); bytes += sizeof(float);
732 float prop = *((float*)bytes); bytes += sizeof(float);
734 [windowController setScrollbarThumbValue:val proportion:prop
736 } else if (SetFontMsgID == msgid) {
737 const void *bytes = [data bytes];
738 float size = *((float*)bytes); bytes += sizeof(float);
739 int len = *((int*)bytes); bytes += sizeof(int);
740 NSString *name = [[NSString alloc]
741 initWithBytes:(void*)bytes length:len
742 encoding:NSUTF8StringEncoding];
743 NSFont *font = [NSFont fontWithName:name size:size];
746 [windowController setFont:font];
749 } else if (SetDefaultColorsMsgID == msgid) {
750 const void *bytes = [data bytes];
751 int bg = *((int*)bytes); bytes += sizeof(int);
752 int fg = *((int*)bytes); bytes += sizeof(int);
753 NSColor *back = [NSColor colorWithRgbInt:bg];
754 NSColor *fore = [NSColor colorWithRgbInt:fg];
756 [windowController setDefaultColorsBackground:back foreground:fore];
757 } else if (ExecuteActionMsgID == msgid) {
758 const void *bytes = [data bytes];
759 int len = *((int*)bytes); bytes += sizeof(int);
760 NSString *actionName = [[NSString alloc]
761 initWithBytesNoCopy:(void*)bytes
763 encoding:NSUTF8StringEncoding
766 SEL sel = NSSelectorFromString(actionName);
767 [NSApp sendAction:sel to:nil from:self];
769 [actionName release];
771 NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
775 - (void)performBatchDrawWithData:(NSData *)data
777 // TODO! Move to window controller.
778 MMTextStorage *textStorage = [windowController textStorage];
782 const void *bytes = [data bytes];
783 const void *end = bytes + [data length];
785 [textStorage beginEditing];
788 // 1. Sanity check input
789 // 2. Cache rgb -> NSColor lookups?
791 while (bytes < end) {
792 int type = *((int*)bytes); bytes += sizeof(int);
794 if (ClearAllDrawType == type) {
795 int color = *((int*)bytes); bytes += sizeof(int);
797 [textStorage clearAllWithColor:[NSColor colorWithRgbInt:color]];
798 } else if (ClearBlockDrawType == type) {
799 int color = *((int*)bytes); bytes += sizeof(int);
800 int row1 = *((int*)bytes); bytes += sizeof(int);
801 int col1 = *((int*)bytes); bytes += sizeof(int);
802 int row2 = *((int*)bytes); bytes += sizeof(int);
803 int col2 = *((int*)bytes); bytes += sizeof(int);
805 [textStorage clearBlockFromRow:row1 column:col1
806 toRow:row2 column:col2
807 color:[NSColor colorWithRgbInt:color]];
808 } else if (DeleteLinesDrawType == type) {
809 int color = *((int*)bytes); bytes += sizeof(int);
810 int row = *((int*)bytes); bytes += sizeof(int);
811 int count = *((int*)bytes); bytes += sizeof(int);
812 int bot = *((int*)bytes); bytes += sizeof(int);
813 int left = *((int*)bytes); bytes += sizeof(int);
814 int right = *((int*)bytes); bytes += sizeof(int);
816 [textStorage deleteLinesFromRow:row lineCount:count
817 scrollBottom:bot left:left right:right
818 color:[NSColor colorWithRgbInt:color]];
819 } else if (ReplaceStringDrawType == type) {
820 int bg = *((int*)bytes); bytes += sizeof(int);
821 int fg = *((int*)bytes); bytes += sizeof(int);
822 int row = *((int*)bytes); bytes += sizeof(int);
823 int col = *((int*)bytes); bytes += sizeof(int);
824 int flags = *((int*)bytes); bytes += sizeof(int);
825 int len = *((int*)bytes); bytes += sizeof(int);
826 NSString *string = [[NSString alloc]
827 initWithBytesNoCopy:(void*)bytes
829 encoding:NSUTF8StringEncoding
833 [textStorage replaceString:string
836 foregroundColor:[NSColor colorWithRgbInt:fg]
837 backgroundColor:[NSColor colorWithRgbInt:bg]];
840 } else if (InsertLinesDrawType == type) {
841 int color = *((int*)bytes); bytes += sizeof(int);
842 int row = *((int*)bytes); bytes += sizeof(int);
843 int count = *((int*)bytes); bytes += sizeof(int);
844 int bot = *((int*)bytes); bytes += sizeof(int);
845 int left = *((int*)bytes); bytes += sizeof(int);
846 int right = *((int*)bytes); bytes += sizeof(int);
848 [textStorage insertLinesAtRow:row lineCount:count
849 scrollBottom:bot left:left right:right
850 color:[NSColor colorWithRgbInt:color]];
852 NSLog(@"WARNING: Unknown draw type (type=%d)", type);
856 [textStorage endEditing];
859 - (void)panelDidEnd:(NSSavePanel *)panel code:(int)code context:(void *)context
862 [windowController setStatusText:@""];
864 NSString *string = (code == NSOKButton) ? [panel filename] : nil;
865 [backendProxy setBrowseForFileString:string];
867 NSMutableData *data = [NSMutableData data];
868 int ok = (code == NSOKButton);
869 NSString *filename = [panel filename];
870 int len = [filename lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
872 [data appendBytes:&ok length:sizeof(int)];
873 [data appendBytes:&len length:sizeof(int)];
875 [data appendBytes:[filename UTF8String] length:len];
877 if (![NSPortMessage sendMessage:BrowseForFileReplyMsgID
878 withSendPort:sendPort data:data wait:YES]) {
879 NSLog(@"WARNING: Failed to send browse for files reply back to "
883 [windowController setStatusText:@""];
887 - (NSMenuItem *)menuItemForTag:(int)tag
889 int i, count = [mainMenuItems count];
890 for (i = 0; i < count; ++i) {
891 NSMenuItem *item = [mainMenuItems objectAtIndex:i];
892 if ([item tag] == tag) return item;
893 item = findMenuItemWithTagInMenu([item submenu], tag);
894 if (item) return item;
900 - (NSMenu *)menuForTag:(int)tag
902 return [[self menuItemForTag:tag] submenu];
905 - (void)updateMainMenu
907 shouldUpdateMainMenu = NO;
909 // HACK! Add the vim menu named 'Window' as the submenu with index 3 of an
910 // already existing menu with the same name. The 'Window' menu is set up
911 // in Interface Builder.
912 NSMenu *mainMenu = [NSApp mainMenu];
913 NSMenu *windowMenu = nil;
915 // Remove all existing menus, except for 'Window'.
916 int i, count = [mainMenu numberOfItems];
917 for (i = count-1; i > 0; --i) {
918 NSMenuItem *item = [mainMenu itemAtIndex:i];
919 NSMenu *submenu = [item submenu];
920 if ([[submenu title] isEqual:@"Window"]) {
921 windowMenu = submenu;
923 [mainMenu removeItem:item];
927 // Add menus from 'mainMenuItems'
928 count = [mainMenuItems count];
929 for (i = 0; i < count; ++i) {
930 NSMenuItem *item = [mainMenuItems objectAtIndex:i];
932 if (windowMenu && [windowMenu numberOfItems] > 4
933 && [[item title] isEqual:@"Window"]) {
934 // Item 3 of the Window menu is replaced with vim's Window menu.
935 [windowMenu removeItemAtIndex:3];
936 [windowMenu insertItem:item atIndex:3];
938 [mainMenu insertItem:item atIndex:i+1];
943 - (NSToolbarItem *)toolbarItemForTag:(int)tag index:(int *)index
945 if (!toolbar) return nil;
947 NSArray *items = [toolbar items];
948 int i, count = [items count];
949 for (i = 0; i < count; ++i) {
950 NSToolbarItem *item = [items objectAtIndex:i];
951 if ([item tag] == tag) {
952 if (index) *index = i;
960 - (IBAction)toolbarAction:(id)sender
962 NSLog(@"%s%@", _cmd, sender);
965 - (void)addToolbarItemToDictionaryWithTag:(int)tag label:(NSString *)title
966 toolTip:(NSString *)tip icon:(NSString *)icon
968 // NOTE! 'title' is nul for separator item. Since this is already defined
969 // by Coca, we don't need to do anything here.
972 NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:title];
974 [item setLabel:title];
975 [item setToolTip:tip];
976 [item setAction:@selector(vimMenuItemAction:)];
977 [item setAutovalidates:NO];
979 NSImage *img = [NSImage imageNamed:icon];
981 NSLog(@"WARNING: Could not find image with name '%@' to use as toolbar"
982 " image for identifier '%@';"
983 " using default toolbar icon '%@' instead.",
984 icon, title, DefaultToolbarImageName);
986 img = [NSImage imageNamed:DefaultToolbarImageName];
991 [toolbarItemDict setObject:item forKey:title];
997 - (void)connectionDidDie:(NSNotification *)notification
999 //NSLog(@"A MMVimController lost its connection to the backend; "
1000 // "closing the controller.");
1001 [windowController close];
1005 - (BOOL)executeActionWithName:(NSString *)name
1008 static NSDictionary *actionDict = nil;
1011 NSBundle *mainBundle = [NSBundle mainBundle];
1012 NSString *path = [mainBundle pathForResource:@"Actions"
1015 actionDict = [[NSDictionary alloc] initWithContentsOfFile:path];
1016 NSLog(@"Actions = %@", actionDict);
1018 NSLog(@"WARNING: Failed to load dictionary of actions "
1019 "(Actions.plist).");
1024 if ([actionDict objectForKey:name]) {
1025 NSLog(@"Executing action %@", name);
1026 SEL sel = NSSelectorFromString(name);
1028 if ([NSApp sendAction:sel to:nil from:self])
1031 NSLog(@"WARNING: Failed to send action");
1033 NSLog(@"WARNING: Action with name '%@' cannot be executed.", name);
1040 @end // MMVimController (Private)
1044 @implementation NSColor (MMProtocol)
1046 + (NSColor *)colorWithRgbInt:(int)rgb
1048 float r = ((rgb>>16) & 0xff)/255.0f;
1049 float g = ((rgb>>8) & 0xff)/255.0f;
1050 float b = (rgb & 0xff)/255.0f;
1052 return [NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0f];
1055 @end // NSColor (MMProtocol)