- Text view now drawn with inset
[MacVim/jjgod.git] / MMVimController.m
blob0c289d536d8ddd7eeeea923aa7255f25f85e53d5
1 /* vi:set ts=8 sts=4 sw=4 ft=objc:
2  *
3  * VIM - Vi IMproved            by Bram Moolenaar
4  *                              MacVim GUI port by Bjorn Winckler
5  *
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.
9  */
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;
34 #if MM_USE_DO
35 - (void)connectionDidDie:(NSNotification *)notification;
36 #endif
37 - (BOOL)executeActionWithName:(NSString *)name;
38 @end
42 // TODO: Move to separate file
43 @interface NSColor (MMProtocol)
44 + (NSColor *)colorWithRgbInt:(int)rgb;
45 @end
49 static NSMenuItem *findMenuItemWithTagInMenu(NSMenu *root, int tag)
51     if (root) {
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;
62             }
63         }
64     }
66     return nil;
71 @implementation MMVimController
73 #if MM_USE_DO
74 - (id)initWithBackend:(id)backend
75 #else
76 - (id)initWithPort:(NSPort *)port
77 #endif
79     if ((self = [super init])) {
80         windowController =
81             [[MMWindowController alloc] initWithVimController:self];
82 #if MM_USE_DO
83         backendProxy = [backend retain];
84 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
85         sendQueue = [NSMutableArray new];
86 # endif
88         NSConnection *connection = [backendProxy connectionForProxy];
89         [[NSNotificationCenter defaultCenter] addObserver:self
90                 selector:@selector(connectionDidDie:)
91                     name:NSConnectionDidDieNotification object:connection];
92 #else
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];
109 #endif
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]
119                 addObserver:self
120                    selector:@selector(windowWillClose:)
121                        name:NSWindowWillCloseNotification
122                      object:[windowController window]];
123         [[NSNotificationCenter defaultCenter]
124                 addObserver:self
125                    selector:@selector(windowDidBecomeMain:)
126                        name:NSWindowDidBecomeMainNotification
127                      object:[windowController window]];
129     }
131     return self;
134 - (void)dealloc
136     //NSLog(@"%@ %s", [self className], _cmd);
138     [[NSNotificationCenter defaultCenter] removeObserver:self];
140 #if MM_USE_DO
141     [backendProxy release];
142 # if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
143     [sendQueue release];
144 # endif
145 #else
146     if (sendPort) {
147         // Kill task immediately
148         [NSPortMessage sendMessage:KillTaskMsgID withSendPort:sendPort
149                        receivePort:receivePort wait:NO];
150     }
152     [sendPort release];
153     [receivePort release];
154 #endif
156     [toolbarItemDict release];
157     [toolbar release];
158     [mainMenuItems release];
159     [windowController release];
161     [super dealloc];
164 - (MMWindowController *)windowController
166     return windowController;
169 - (void)sendMessage:(int)msgid data:(NSData *)data wait:(BOOL)wait
171 #if MM_USE_DO
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]];
176         if (data)
177             [sendQueue addObject:data];
178         else
179             [sendQueue addObject:[NSNull null]];
180         return;
181     }
182 # endif
183     if (wait) {
184         [backendProxy processInput:msgid data:data];
185     } else {
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];
189         if (connection) {
190             NSTimeInterval req = [connection requestTimeout];
191             [connection setRequestTimeout:0];
192             @try {
193                 [backendProxy processInput:msgid data:data];
194             }
195             @catch (NSException *e) {
196                 // Connection timed out, just ignore this.
197                 //NSLog(@"WARNING! Connection timed out in %s", _cmd);
198             }
199             @finally {
200                 [connection setRequestTimeout:req];
201             }
202         }
203     }
204 #else
205     [NSPortMessage sendMessage:msgid withSendPort:sendPort data:data
206                           wait:wait];
207 #endif
210 #if MM_USE_DO
211 - (id)backendProxy
213     return backendProxy;
216 - (oneway void)showSavePanelForDirectory:(in bycopy NSString *)dir
217                                    title:(in bycopy NSString *)title
218                                   saving:(int)saving
220     [windowController setStatusText:title];
222     if (saving) {
223         [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
224                 modalForWindow:[windowController window]
225                  modalDelegate:self
226                 didEndSelector:@selector(panelDidEnd:code:context:)
227                    contextInfo:NULL];
228     } else {
229         NSOpenPanel *panel = [NSOpenPanel openPanel];
230         [panel setAllowsMultipleSelection:NO];
231         [panel beginSheetForDirectory:dir file:nil types:nil
232                 modalForWindow:[windowController window]
233                  modalDelegate:self
234                 didEndSelector:@selector(panelDidEnd:code:context:)
235                    contextInfo:NULL];
236     }
239 - (oneway void)processCommandQueue:(in NSArray *)queue
241     unsigned i, count = [queue count];
242     if (count % 2) {
243         NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
244                 "message; ignoring this message.", count);
245         return;
246     }
248 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
249     inProcessCommandQueue = YES;
250 #endif
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]);
258 #if 0
259         if (msgid != EnableMenuItemMsgID && msgid != AddMenuItemMsgID
260                 && msgid != AddMenuMsgID) {
261             NSLog(@"%s%s", _cmd, MessageStrings[msgid]);
262         }
263 #endif
265         [self handleMessage:msgid data:data];
266     }
267     //NSLog(@"======== %s  END  ========", _cmd);
269 #if MM_DELAY_SEND_IN_PROCESS_CMD_QUEUE
270     inProcessCommandQueue = NO;
272     count = [sendQueue count];
273     if (count > 0) {
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]])
281                     data = nil;
283                 [backendProxy processInput:msgid data:data];
284             }
285         }
287         [sendQueue removeAllObjects];
288     }
289 #endif
292 #else // MM_USE_DO
294 - (NSPort *)sendPort
296     return sendPort;
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];
310         if (count % 2) {
311             NSLog(@"WARNING: Uneven number of components (%d) in flush queue "
312                     "message; ignoring this message.", count);
313             return;
314         }
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];
321         }
322     } else {
323         NSData *data = nil;
324         if ([components count] > 0)
325             data = [components objectAtIndex:0];
327         [self handleMessage:msgid data:data];
328     }
330     if (shouldUpdateMainMenu)
331         [self updateMainMenu];
333 #endif // MM_USE_DO
335 - (void)windowWillClose:(NSNotification *)notification
337     // NOTE!  This causes the call to removeVimController: to be delayed.
338     [[NSApp delegate]
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];
355     if (!item) {
356         NSLog(@"WARNING:  No toolbar item with id '%@'", itemId);
357     }
359     return item;
362 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)theToolbar
364     //NSLog(@"%s", _cmd);
365     return nil;
368 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)theToolbar
370     //NSLog(@"%s", _cmd);
371     return nil;
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];
386     }
387 #if !MM_USE_DO
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;
400     }
401 #endif // !MM_USE_DO
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
420         // toolbar button.
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
436         // toolbar button.
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);
450 #if 0
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
455                              length:len
456                            encoding:NSUTF8StringEncoding
457                        freeWhenDone:NO];
458 #else
459         NSString *string = [[NSString alloc] initWithBytes:(void*)bytes
460                 length:len encoding:NSUTF8StringEncoding];
461 #endif
463         [[windowController window] setTitle:string];
465         [string release];
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);
471         NSString *dir = nil;
472         if (len > 0) {
473             dir = [[NSString alloc] initWithBytes:(void*)bytes
474                                            length:len
475                                          encoding:NSUTF8StringEncoding];
476             bytes += len;
477         }
479         len = *((int*)bytes);  bytes += sizeof(int);
480         if (len > 0) {
481             NSString *title = [[NSString alloc]
482                     initWithBytes:(void*)bytes length:len
483                          encoding:NSUTF8StringEncoding];
484             bytes += len;
486             [windowController setStatusText:title];
487             [title release];
488         }
490         if (save) {
491             [[NSSavePanel savePanel] beginSheetForDirectory:dir file:nil
492                 modalForWindow:[windowController window]
493                  modalDelegate:self
494                 didEndSelector:@selector(panelDidEnd:code:context:)
495                    contextInfo:NULL];
496         } else {
497             NSOpenPanel *panel = [NSOpenPanel openPanel];
498             [panel setAllowsMultipleSelection:NO];
499             [panel beginSheetForDirectory:dir file:nil types:nil
500                     modalForWindow:[windowController window]
501                      modalDelegate:self
502                     didEndSelector:@selector(panelDidEnd:code:context:)
503                        contextInfo:NULL];
504         }
506         [dir release];
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];
516         if (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];
523         }
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);
530         if (len > 0) {
531             title = [[NSString alloc] initWithBytes:(void*)bytes length:len
532                                            encoding:NSUTF8StringEncoding];
533             bytes += len;
534         }
535         int idx = *((int*)bytes);  bytes += sizeof(int);
537         if (MenuToolbarType == parentTag) {
538             if (!toolbar) {
539                 NSString *ident = [NSString stringWithFormat:@"%d.%d",
540                          (int)self, tag];
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];
549             }
550         } else if (MenuPopupType == parentTag) {
551             // TODO!
552         } else if (title) {
553             NSMenuItem *item = [[NSMenuItem alloc] init];
554             NSMenu *menu = [[NSMenu alloc] initWithTitle:title];
556             [menu setAutoenablesItems:NO];
557             [item setTag:tag];
558             [item setTitle:title];
559             [item setSubmenu:menu];
561             NSMenu *parent = [self menuForTag:parentTag];
562             if (parent) {
563                 if ([parent numberOfItems] <= idx) {
564                     [parent addItem:item];
565                 } else {
566                     [parent insertItem:item atIndex:idx];
567                 }
568             } else {
569                 if ([mainMenuItems count] <= idx) {
570                     [mainMenuItems addObject:item];
571                 } else {
572                     [mainMenuItems insertObject:item atIndex:idx];
573                 }
575                 shouldUpdateMainMenu = YES;
576             }
578             [item release];
579             [menu release];
580         }
582         [title release];
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);
589         if (namelen > 0) {
590             title = [[NSString alloc] initWithBytes:(void*)bytes length:namelen
591                                            encoding:NSUTF8StringEncoding];
592             bytes += namelen;
593         }
594         int tiplen = *((int*)bytes);  bytes += sizeof(int);
595         if (tiplen > 0) {
596             tip = [[NSString alloc] initWithBytes:(void*)bytes length:tiplen
597                                            encoding:NSUTF8StringEncoding];
598             bytes += tiplen;
599         }
600         int iconlen = *((int*)bytes);  bytes += sizeof(int);
601         if (iconlen > 0) {
602             icon = [[NSString alloc] initWithBytes:(void*)bytes length:iconlen
603                                            encoding:NSUTF8StringEncoding];
604             bytes += iconlen;
605         }
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
616                                                icon:icon];
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];
624         } else {
625             NSMenu *parent = [self menuForTag:parentTag];
626             if (parent) {
627                 NSMenuItem *item = nil;
628                 if (title) {
629                     item = [[[NSMenuItem alloc] init] autorelease];
630                     [item setTag:tag];
631                     [item setTitle:title];
632                     [item setAction:@selector(vimMenuItemAction:)];
633                     if (tip) [item setToolTip:tip];
635                     if (key != 0) {
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];
642                     }
643                 } else {
644                     item = [NSMenuItem separatorItem];
645                 }
647                 if ([parent numberOfItems] <= idx) {
648                     [parent addItem:item];
649                 } else {
650                     [parent insertItem:item atIndex:idx];
651                 }
652             }
653         }
655         [title release];
656         [tip release];
657         [icon release];
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.
663         id item;
664         int idx;
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];
670             }
671             [[item menu] removeItem:item];
672         }
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];
680         if (!item)
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;
695         }
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
727                                     identifier:ident];
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
735                                       identifier:ident];
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];
745         if (font)
746             [windowController setFont:font];
748         [name release];
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
762                              length:len
763                            encoding:NSUTF8StringEncoding
764                        freeWhenDone:NO];
766         SEL sel = NSSelectorFromString(actionName);
767         [NSApp sendAction:sel to:nil from:self];
769         [actionName release];
770     } else {
771         NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
772     }
775 - (void)performBatchDrawWithData:(NSData *)data
777     // TODO!  Move to window controller.
778     MMTextStorage *textStorage = [windowController textStorage];
779     if (!textStorage)
780         return;
782     const void *bytes = [data bytes];
783     const void *end = bytes + [data length];
785     [textStorage beginEditing];
787     // TODO:
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
828                                  length:len
829                                encoding:NSUTF8StringEncoding
830                            freeWhenDone:NO];
831             bytes += len;
833             [textStorage replaceString:string
834                                  atRow:row column:col
835                              withFlags:flags
836                        foregroundColor:[NSColor colorWithRgbInt:fg]
837                        backgroundColor:[NSColor colorWithRgbInt:bg]];
839             [string release];
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]];
851         } else {
852             NSLog(@"WARNING: Unknown draw type (type=%d)", type);
853         }
854     }
856     [textStorage endEditing];
859 - (void)panelDidEnd:(NSSavePanel *)panel code:(int)code context:(void *)context
861 #if MM_USE_DO
862     [windowController setStatusText:@""];
864     NSString *string = (code == NSOKButton) ? [panel filename] : nil;
865     [backendProxy setBrowseForFileString:string];
866 #else
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)];
874     if (len > 0)
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 "
880                 "VimTask.");
881     }
883     [windowController setStatusText:@""];
884 #endif // !MM_USE_DO
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;
895     }
897     return nil;
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;
922         } else {
923             [mainMenu removeItem:item];
924         }
925     }
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];
937         } else {
938             [mainMenu insertItem:item atIndex:i+1];
939         }
940     }
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;
953             return item;
954         }
955     }
957     return nil;
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.
970     if (!title) return;
972     NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:title];
973     [item setTag:tag];
974     [item setLabel:title];
975     [item setToolTip:tip];
976     [item setAction:@selector(vimMenuItemAction:)];
977     [item setAutovalidates:NO];
979     NSImage *img = [NSImage imageNamed:icon];
980     if (!img) {
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];
987     }
989     [item setImage:img];
991     [toolbarItemDict setObject:item forKey:title];
993     [item release];
996 #if MM_USE_DO
997 - (void)connectionDidDie:(NSNotification *)notification
999     //NSLog(@"A MMVimController lost its connection to the backend; "
1000     //       "closing the controller.");
1001     [windowController close];
1003 #endif // MM_USE_DO
1005 - (BOOL)executeActionWithName:(NSString *)name
1007 #if 0
1008     static NSDictionary *actionDict = nil;
1010     if (!actionDict) {
1011         NSBundle *mainBundle = [NSBundle mainBundle];
1012         NSString *path = [mainBundle pathForResource:@"Actions"
1013                                               ofType:@"plist"];
1014         if (path) {
1015             actionDict = [[NSDictionary alloc] initWithContentsOfFile:path];
1016             NSLog(@"Actions = %@", actionDict);
1017         } else {
1018             NSLog(@"WARNING: Failed to load dictionary of actions "
1019                     "(Actions.plist).");
1020             return NO;
1021         }
1022     }
1024     if ([actionDict objectForKey:name]) {
1025         NSLog(@"Executing action %@", name);
1026         SEL sel = NSSelectorFromString(name);
1028         if ([NSApp sendAction:sel to:nil from:self])
1029             return YES;
1031         NSLog(@"WARNING: Failed to send action");
1032     } else {
1033         NSLog(@"WARNING: Action with name '%@' cannot be executed.", name);
1034     }
1036 #endif
1037     return NO;
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)