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.
13 * MMAppController is the delegate of NSApp and as such handles file open
14 * requests, application termination, etc. It sets up a named NSConnection on
15 * which it listens to incoming connections from Vim processes. It also
16 * coordinates all MMVimControllers.
18 * A new Vim process is started by calling launchVimProcessWithArguments:.
19 * When the Vim process is initialized it notifies the app controller by
20 * sending a connectBackend:pid: message. At this point a new MMVimController
21 * is allocated. Afterwards, the Vim process communicates directly with its
24 * A Vim process started from the command line connects directly by sending the
25 * connectBackend:pid: message (launchVimProcessWithArguments: is never called
29 #import "MMAppController.h"
30 #import "MMVimController.h"
31 #import "MMWindowController.h"
36 // Default timeout intervals on all connections.
37 static NSTimeInterval MMRequestTimeout = 5;
38 static NSTimeInterval MMReplyTimeout = 5;
42 @interface MMAppController (MMServices)
43 - (void)openSelection:(NSPasteboard *)pboard userData:(NSString *)userData
44 error:(NSString **)error;
45 - (void)openFile:(NSPasteboard *)pboard userData:(NSString *)userData
46 error:(NSString **)error;
50 @interface MMAppController (Private)
51 - (MMVimController *)keyVimController;
52 - (MMVimController *)topmostVimController;
53 - (int)launchVimProcessWithArguments:(NSArray *)args;
54 - (NSArray *)filterFilesAndNotify:(NSArray *)files;
55 - (NSArray *)filterOpenFiles:(NSArray *)filenames remote:(OSType)theID
57 token:(NSAppleEventDescriptor *)token;
58 - (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event
59 replyEvent:(NSAppleEventDescriptor *)reply;
62 @interface NSMenu (MMExtras)
63 - (void)recurseSetAutoenablesItems:(BOOL)on;
66 @interface NSNumber (MMExtras)
72 @implementation MMAppController
76 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
77 [NSNumber numberWithBool:NO], MMNoWindowKey,
78 [NSNumber numberWithInt:64], MMTabMinWidthKey,
79 [NSNumber numberWithInt:6*64], MMTabMaxWidthKey,
80 [NSNumber numberWithInt:132], MMTabOptimumWidthKey,
81 [NSNumber numberWithInt:2], MMTextInsetLeftKey,
82 [NSNumber numberWithInt:1], MMTextInsetRightKey,
83 [NSNumber numberWithInt:1], MMTextInsetTopKey,
84 [NSNumber numberWithInt:1], MMTextInsetBottomKey,
85 [NSNumber numberWithBool:NO], MMTerminateAfterLastWindowClosedKey,
86 @"MMTypesetter", MMTypesetterKey,
87 [NSNumber numberWithFloat:1], MMCellWidthMultiplierKey,
88 [NSNumber numberWithFloat:-1], MMBaselineOffsetKey,
89 [NSNumber numberWithBool:YES], MMTranslateCtrlClickKey,
90 [NSNumber numberWithBool:NO], MMOpenFilesInTabsKey,
91 [NSNumber numberWithBool:NO], MMNoFontSubstitutionKey,
92 [NSNumber numberWithBool:NO], MMLoginShellKey,
95 [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
97 NSArray *types = [NSArray arrayWithObject:NSStringPboardType];
98 [NSApp registerServicesMenuSendTypes:types returnTypes:types];
103 if ((self = [super init])) {
104 fontContainerRef = loadFonts();
106 vimControllers = [NSMutableArray new];
107 pidArguments = [NSMutableDictionary new];
109 // NOTE! If the name of the connection changes here it must also be
110 // updated in MMBackend.m.
111 NSConnection *connection = [NSConnection defaultConnection];
112 NSString *name = [NSString stringWithFormat:@"%@-connection",
113 [[NSBundle mainBundle] bundleIdentifier]];
114 //NSLog(@"Registering connection with name '%@'", name);
115 if ([connection registerName:name]) {
116 [connection setRequestTimeout:MMRequestTimeout];
117 [connection setReplyTimeout:MMReplyTimeout];
118 [connection setRootObject:self];
120 // NOTE: When the user is resizing the window the AppKit puts the
121 // run loop in event tracking mode. Unless the connection listens
122 // to request in this mode, live resizing won't work.
123 [connection addRequestMode:NSEventTrackingRunLoopMode];
125 NSLog(@"WARNING: Failed to register connection with name '%@'",
135 //NSLog(@"MMAppController dealloc");
137 [pidArguments release];
138 [vimControllers release];
139 [openSelectionString release];
144 - (void)applicationWillFinishLaunching:(NSNotification *)notification
146 [[NSAppleEventManager sharedAppleEventManager]
148 andSelector:@selector(handleXcodeModEvent:replyEvent:)
153 - (void)applicationDidFinishLaunching:(NSNotification *)notification
155 [NSApp setServicesProvider:self];
158 - (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
160 // NOTE! This way it possible to start the app with the command-line
161 // argument '-nowindow yes' and no window will be opened by default.
162 untitledWindowOpening =
163 ![[NSUserDefaults standardUserDefaults] boolForKey:MMNoWindowKey];
164 return untitledWindowOpening;
167 - (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
169 [self newWindow:self];
173 - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
176 NSString *remotePath;
177 NSAppleEventDescriptor *remoteToken;
178 NSAppleEventDescriptor *odbdesc =
179 [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent];
181 if (![odbdesc paramDescriptorForKeyword:keyFileSender]) {
182 // The ODB paramaters may hide inside the 'keyAEPropData' descriptor.
183 odbdesc = [odbdesc paramDescriptorForKeyword:keyAEPropData];
184 if (![odbdesc paramDescriptorForKeyword:keyFileSender])
189 remoteID = [[odbdesc paramDescriptorForKeyword:keyFileSender]
191 remotePath = [[odbdesc paramDescriptorForKeyword:keyFileCustomPath]
193 remoteToken = [[odbdesc paramDescriptorForKeyword:keyFileSenderToken]
196 //NSLog(@"ODB parameters: ID=0x%x path=%@ token=%@",
197 // remoteID, remotePath, remoteToken);
200 filenames = [self filterOpenFiles:filenames remote:remoteID path:remotePath
202 if ([filenames count]) {
204 BOOL openInTabs = [[NSUserDefaults standardUserDefaults]
205 boolForKey:MMOpenFilesInTabsKey];
207 if (openInTabs && (vc = [self topmostVimController])) {
208 // Open files in tabs in the topmost window.
209 [vc dropFiles:filenames forceOpen:YES];
211 [vc odbEdit:filenames server:remoteID path:remotePath
214 // Open files in tabs in a new window.
215 NSMutableArray *args = [NSMutableArray arrayWithObject:@"-p"];
216 [args addObjectsFromArray:filenames];
217 int pid = [self launchVimProcessWithArguments:args];
219 // The Vim process starts asynchronously. Some arguments cannot be
220 // on the command line, so store them in a dictionary and pass them
221 // to the process once it has started.
223 // TODO: If the Vim process fails to start, or if it changes PID,
224 // then the memory allocated for these parameters will leak.
225 // Ensure that this cannot happen or somehow detect it.
227 // The remote token can be arbitrary data so it is cannot
228 // (without encoding it as text) be passed on the command line.
229 NSMutableDictionary *args =
230 [NSMutableDictionary dictionaryWithObjectsAndKeys:
231 filenames, @"filenames",
232 [NSNumber numberWithUnsignedInt:remoteID], @"remoteID",
235 [args setObject:remotePath forKey:@"remotePath"];
237 [args setObject:remoteToken forKey:@"remoteToken"];
239 [pidArguments setObject:args
240 forKey:[NSNumber numberWithInt:pid]];
245 [NSApp replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
246 // NSApplicationDelegateReplySuccess = 0,
247 // NSApplicationDelegateReplyCancel = 1,
248 // NSApplicationDelegateReplyFailure = 2
251 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
253 return [[NSUserDefaults standardUserDefaults]
254 boolForKey:MMTerminateAfterLastWindowClosedKey];
257 - (NSApplicationTerminateReply)applicationShouldTerminate:
258 (NSApplication *)sender
260 // TODO: Follow Apple's guidelines for 'Graceful Application Termination'
261 // (in particular, allow user to review changes and save).
262 int reply = NSTerminateNow;
263 BOOL modifiedBuffers = NO;
265 // Go through windows, checking for modified buffers. (Each Vim process
266 // tells MacVim when any buffer has been modified and MacVim sets the
267 // 'documentEdited' flag of the window correspondingly.)
268 NSEnumerator *e = [[NSApp windows] objectEnumerator];
270 while ((window = [e nextObject])) {
271 if ([window isDocumentEdited]) {
272 modifiedBuffers = YES;
277 if (modifiedBuffers) {
278 NSAlert *alert = [[NSAlert alloc] init];
279 [alert addButtonWithTitle:@"Quit"];
280 [alert addButtonWithTitle:@"Cancel"];
281 [alert setMessageText:@"Quit without saving?"];
282 [alert setInformativeText:@"There are modified buffers, "
283 "if you quit now all changes will be lost. Quit anyway?"];
284 [alert setAlertStyle:NSWarningAlertStyle];
286 if ([alert runModal] != NSAlertFirstButtonReturn)
287 reply = NSTerminateCancel;
292 // Tell all Vim processes to terminate now (otherwise they'll leave swap
294 if (NSTerminateNow == reply) {
295 e = [vimControllers objectEnumerator];
297 while ((vc = [e nextObject]))
298 [vc sendMessage:TerminateNowMsgID data:nil];
304 - (void)applicationWillTerminate:(NSNotification *)notification
306 [[NSAppleEventManager sharedAppleEventManager]
307 removeEventHandlerForEventClass:'KAHL'
310 // This will invalidate all connections (since they were spawned from the
311 // default connection).
312 [[NSConnection defaultConnection] invalidate];
314 // Send a SIGINT to all running Vim processes, so that they are sure to
315 // receive the connectionDidDie: notification (a process has to be checking
316 // the run-loop for this to happen).
317 unsigned i, count = [vimControllers count];
318 for (i = 0; i < count; ++i) {
319 MMVimController *controller = [vimControllers objectAtIndex:i];
320 int pid = [controller pid];
325 if (fontContainerRef) {
326 ATSFontDeactivate(fontContainerRef, NULL, kATSOptionFlagsDefault);
327 fontContainerRef = 0;
330 // TODO: Is this a correct way of releasing the MMAppController?
331 // (It doesn't seem like dealloc is ever called.)
332 [NSApp setDelegate:nil];
336 - (void)removeVimController:(id)controller
338 //NSLog(@"%s%@", _cmd, controller);
340 [[controller windowController] close];
342 [vimControllers removeObject:controller];
344 if (![vimControllers count]) {
345 // Turn on autoenabling of menus (because no Vim is open to handle it),
346 // but do not touch the MacVim menu. Note that the menus must be
347 // enabled first otherwise autoenabling does not work.
348 NSMenu *mainMenu = [NSApp mainMenu];
349 int i, count = [mainMenu numberOfItems];
350 for (i = 1; i < count; ++i) {
351 NSMenuItem *item = [mainMenu itemAtIndex:i];
352 [item setEnabled:YES];
353 [[item submenu] recurseSetAutoenablesItems:YES];
358 - (void)windowControllerWillOpen:(MMWindowController *)windowController
360 NSPoint topLeft = NSZeroPoint;
361 NSWindow *keyWin = [NSApp keyWindow];
362 NSWindow *win = [windowController window];
366 // If there is a key window, cascade from it, otherwise use the autosaved
367 // window position (if any).
369 NSRect frame = [keyWin frame];
370 topLeft = NSMakePoint(frame.origin.x, NSMaxY(frame));
372 NSString *topLeftString = [[NSUserDefaults standardUserDefaults]
373 stringForKey:MMTopLeftPointKey];
375 topLeft = NSPointFromString(topLeftString);
378 if (!NSEqualPoints(topLeft, NSZeroPoint)) {
380 topLeft = [win cascadeTopLeftFromPoint:topLeft];
382 [win setFrameTopLeftPoint:topLeft];
385 if (openSelectionString) {
386 // There is some text to paste into this window as a result of the
387 // services menu "Open selection ..." being used.
388 [[windowController vimController] dropString:openSelectionString];
389 [openSelectionString release];
390 openSelectionString = nil;
394 - (IBAction)newWindow:(id)sender
396 [self launchVimProcessWithArguments:nil];
399 - (IBAction)selectNextWindow:(id)sender
401 unsigned i, count = [vimControllers count];
404 NSWindow *keyWindow = [NSApp keyWindow];
405 for (i = 0; i < count; ++i) {
406 MMVimController *vc = [vimControllers objectAtIndex:i];
407 if ([[[vc windowController] window] isEqual:keyWindow])
414 MMVimController *vc = [vimControllers objectAtIndex:i];
415 [[vc windowController] showWindow:self];
419 - (IBAction)selectPreviousWindow:(id)sender
421 unsigned i, count = [vimControllers count];
424 NSWindow *keyWindow = [NSApp keyWindow];
425 for (i = 0; i < count; ++i) {
426 MMVimController *vc = [vimControllers objectAtIndex:i];
427 if ([[[vc windowController] window] isEqual:keyWindow])
437 MMVimController *vc = [vimControllers objectAtIndex:i];
438 [[vc windowController] showWindow:self];
442 - (IBAction)fontSizeUp:(id)sender
444 [[NSFontManager sharedFontManager] modifyFont:
445 [NSNumber numberWithInt:NSSizeUpFontAction]];
448 - (IBAction)fontSizeDown:(id)sender
450 [[NSFontManager sharedFontManager] modifyFont:
451 [NSNumber numberWithInt:NSSizeDownFontAction]];
454 - (byref id <MMFrontendProtocol>)
455 connectBackend:(byref in id <MMBackendProtocol>)backend
458 //NSLog(@"Connect backend (pid=%d)", pid);
460 [(NSDistantObject*)backend
461 setProtocolForProxy:@protocol(MMBackendProtocol)];
463 MMVimController *vc = [[[MMVimController alloc]
464 initWithBackend:backend pid:pid] autorelease];
466 if (![vimControllers count]) {
467 // The first window autosaves its position. (The autosaving features
468 // of Cocoa are not used because we need more control over what is
469 // autosaved and when it is restored.)
470 [[vc windowController] setWindowAutosaveKey:MMTopLeftPointKey];
473 [vimControllers addObject:vc];
475 // HACK! MacVim does not get activated if it is launched from the
476 // terminal, so we forcibly activate here unless it is an untitled window
477 // opening (i.e. MacVim was opened from the Finder). Untitled windows are
478 // treated differently, else MacVim would steal the focus if another app
479 // was activated while the untitled window was loading.
480 if (!untitledWindowOpening)
481 [NSApp activateIgnoringOtherApps:YES];
483 untitledWindowOpening = NO;
485 // Arguments to a new Vim process that cannot be passed on the command line
486 // are stored in a dictionary and passed to the Vim process here.
487 NSNumber *key = [NSNumber numberWithInt:pid];
488 NSDictionary *args = [pidArguments objectForKey:key];
490 if ([args objectForKey:@"remoteID"]) {
491 [vc odbEdit:[args objectForKey:@"filenames"]
492 server:[[args objectForKey:@"remoteID"] unsignedIntValue]
493 path:[args objectForKey:@"remotePath"]
494 token:[args objectForKey:@"remoteToken"]];
497 [pidArguments removeObjectForKey:key];
503 - (NSArray *)serverList
505 NSMutableArray *array = [NSMutableArray array];
507 unsigned i, count = [vimControllers count];
508 for (i = 0; i < count; ++i) {
509 MMVimController *controller = [vimControllers objectAtIndex:i];
510 if ([controller serverName])
511 [array addObject:[controller serverName]];
517 @end // MMAppController
522 @implementation MMAppController (MMServices)
524 - (void)openSelection:(NSPasteboard *)pboard userData:(NSString *)userData
525 error:(NSString **)error
527 if (![[pboard types] containsObject:NSStringPboardType]) {
528 NSLog(@"WARNING: Pasteboard contains no object of type "
529 "NSStringPboardType");
533 MMVimController *vc = [self topmostVimController];
535 // Open a new tab first, since dropString: does not do this.
536 [vc sendMessage:AddNewTabMsgID data:nil];
537 [vc dropString:[pboard stringForType:NSStringPboardType]];
539 // NOTE: There is no window to paste the selection into, so save the
540 // text, open a new window, and paste the text when the next window
541 // opens. (If this is called several times in a row, then all but the
542 // last call might be ignored.)
543 if (openSelectionString) [openSelectionString release];
544 openSelectionString = [[pboard stringForType:NSStringPboardType] copy];
546 [self newWindow:self];
550 - (void)openFile:(NSPasteboard *)pboard userData:(NSString *)userData
551 error:(NSString **)error
553 if (![[pboard types] containsObject:NSStringPboardType]) {
554 NSLog(@"WARNING: Pasteboard contains no object of type "
555 "NSStringPboardType");
559 // TODO: Parse multiple filenames and create array with names.
560 NSString *string = [pboard stringForType:NSStringPboardType];
561 string = [string stringByTrimmingCharactersInSet:
562 [NSCharacterSet whitespaceAndNewlineCharacterSet]];
563 string = [string stringByStandardizingPath];
565 NSArray *filenames = [self filterFilesAndNotify:
566 [NSArray arrayWithObject:string]];
567 if ([filenames count] > 0) {
568 MMVimController *vc = nil;
569 if (userData && [userData isEqual:@"Tab"])
570 vc = [self topmostVimController];
573 [vc dropFiles:filenames forceOpen:YES];
575 [self application:NSApp openFiles:filenames];
580 @end // MMAppController (MMServices)
585 @implementation MMAppController (Private)
587 - (MMVimController *)keyVimController
589 NSWindow *keyWindow = [NSApp keyWindow];
591 unsigned i, count = [vimControllers count];
592 for (i = 0; i < count; ++i) {
593 MMVimController *vc = [vimControllers objectAtIndex:i];
594 if ([[[vc windowController] window] isEqual:keyWindow])
602 - (MMVimController *)topmostVimController
604 NSArray *windows = [NSApp orderedWindows];
605 if ([windows count] > 0) {
606 NSWindow *window = [windows objectAtIndex:0];
607 unsigned i, count = [vimControllers count];
608 for (i = 0; i < count; ++i) {
609 MMVimController *vc = [vimControllers objectAtIndex:i];
610 if ([[[vc windowController] window] isEqual:window])
618 - (int)launchVimProcessWithArguments:(NSArray *)args
620 NSString *taskPath = nil;
621 NSArray *taskArgs = nil;
622 NSString *path = [[NSBundle mainBundle] pathForAuxiliaryExecutable:@"Vim"];
625 NSLog(@"ERROR: Vim executable could not be found inside app bundle!");
629 if ([[NSUserDefaults standardUserDefaults] boolForKey:MMLoginShellKey]) {
630 // Run process with a login shell
631 // $SHELL -l -c "exec Vim args"
633 NSMutableString *execArg = [NSMutableString
634 stringWithFormat:@"exec \"%@\" -g", path];
636 // Append all arguments while making sure that arguments containing
637 // spaces are enclosed in quotes.
638 NSCharacterSet *space = [NSCharacterSet whitespaceCharacterSet];
639 unsigned i, count = [args count];
641 for (i = 0; i < count; ++i) {
642 NSString *arg = [args objectAtIndex:i];
643 if (NSNotFound != [arg rangeOfCharacterFromSet:space].location)
644 [execArg appendFormat:@" \"%@\"", arg];
646 [execArg appendFormat:@" %@", arg];
650 // Launch the process with a login shell so that users environment
651 // settings get sourced. This does not always happen when MacVim is
653 taskArgs = [NSArray arrayWithObjects:@"-l", @"-c", execArg, nil];
654 taskPath = [[[NSProcessInfo processInfo] environment]
655 objectForKey:@"SHELL"];
657 taskPath = @"/bin/sh";
659 // Run process directly:
662 taskArgs = [NSArray arrayWithObject:@"-g"];
664 taskArgs = [taskArgs arrayByAddingObjectsFromArray:args];
667 NSTask *task =[NSTask launchedTaskWithLaunchPath:taskPath
669 //NSLog(@"launch %@ with args=%@ (pid=%d)", [task processIdentifier],
670 // taskPath, taskArgs);
672 return [task processIdentifier];
675 - (NSArray *)filterFilesAndNotify:(NSArray *)filenames
677 // Go trough 'filenames' array and make sure each file exists. Present
678 // warning dialog if some file was missing.
680 NSString *firstMissingFile = nil;
681 NSMutableArray *files = [NSMutableArray array];
682 unsigned i, count = [filenames count];
684 for (i = 0; i < count; ++i) {
685 NSString *name = [filenames objectAtIndex:i];
686 if ([[NSFileManager defaultManager] fileExistsAtPath:name]) {
687 [files addObject:name];
688 } else if (!firstMissingFile) {
689 firstMissingFile = name;
693 if (firstMissingFile) {
694 NSAlert *alert = [[NSAlert alloc] init];
695 [alert addButtonWithTitle:@"OK"];
698 if ([files count] >= count-1) {
699 [alert setMessageText:@"File not found"];
700 text = [NSString stringWithFormat:@"Could not open file with "
701 "name %@.", firstMissingFile];
703 [alert setMessageText:@"Multiple files not found"];
704 text = [NSString stringWithFormat:@"Could not open file with "
705 "name %@, and %d other files.", firstMissingFile,
706 count-[files count]-1];
709 [alert setInformativeText:text];
710 [alert setAlertStyle:NSWarningAlertStyle];
715 [NSApp replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
721 - (NSArray *)filterOpenFiles:(NSArray *)filenames remote:(OSType)theID
722 path:(NSString *)path
723 token:(NSAppleEventDescriptor *)token
725 // Check if any of the files in the 'filenames' array are open in any Vim
726 // process. Remove the files that are open from the 'filenames' array and
727 // return it. If all files were filtered out, then raise the first file in
728 // the Vim process it is open. Files that are filtered are sent an odb
729 // open event in case theID is not zero.
731 MMVimController *raiseController = nil;
732 NSString *raiseFile = nil;
733 NSMutableArray *files = [filenames mutableCopy];
734 NSString *expr = [NSString stringWithFormat:
735 @"map([\"%@\"],\"bufloaded(v:val)\")",
736 [files componentsJoinedByString:@"\",\""]];
737 unsigned i, count = [vimControllers count];
739 for (i = 0; i < count && [files count]; ++i) {
740 MMVimController *controller = [vimControllers objectAtIndex:i];
741 id proxy = [controller backendProxy];
744 NSString *eval = [proxy evaluateExpression:expr];
745 NSIndexSet *idxSet = [NSIndexSet indexSetWithVimList:eval];
746 if ([idxSet count]) {
748 // Remember the file and which Vim that has it open so that
749 // we can raise it later on.
750 raiseController = controller;
751 raiseFile = [files objectAtIndex:[idxSet firstIndex]];
752 [[raiseFile retain] autorelease];
755 // Send an odb open event to the Vim process.
757 [controller odbEdit:[files objectsAtIndexes:idxSet]
758 server:theID path:path token:token];
760 // Remove all the files that were open in this Vim process and
761 // create a new expression to evaluate.
762 [files removeObjectsAtIndexes:idxSet];
763 expr = [NSString stringWithFormat:
764 @"map([\"%@\"],\"bufloaded(v:val)\")",
765 [files componentsJoinedByString:@"\",\""]];
768 @catch (NSException *e) {
773 if (![files count] && raiseFile) {
774 // Raise the window containing the first file that was already open,
775 // and make sure that the tab containing that file is selected. Only
776 // do this if there are no more files to open, otherwise sometimes the
777 // window with 'raiseFile' will be raised, other times it might be the
778 // window that will open with the files in the 'files' array.
779 raiseFile = [raiseFile stringByEscapingSpecialFilenameCharacters];
780 NSString *input = [NSString stringWithFormat:@"<C-\\><C-N>"
781 ":let oldswb=&swb|let &swb=\"useopen,usetab\"|"
782 "tab sb %@|let &swb=oldswb|unl oldswb|"
783 "cal foreground()|redr|f<CR>", raiseFile];
784 [raiseController addVimInput:input];
790 - (void)handleXcodeModEvent:(NSAppleEventDescriptor *)event
791 replyEvent:(NSAppleEventDescriptor *)reply
794 // Xcode sends this event to query MacVim which open files have been
796 NSLog(@"reply:%@", reply);
797 NSLog(@"event:%@", event);
799 NSEnumerator *e = [vimControllers objectEnumerator];
801 while ((vc = [e nextObject])) {
802 DescType type = [reply descriptorType];
803 unsigned len = [[type data] length];
804 NSMutableData *data = [NSMutableData data];
806 [data appendBytes:&type length:sizeof(DescType)];
807 [data appendBytes:&len length:sizeof(unsigned)];
808 [data appendBytes:[reply data] length:len];
810 [vc sendMessage:XcodeModMsgID data:data];
815 @end // MMAppController (Private)
820 @implementation NSMenu (MMExtras)
822 - (void)recurseSetAutoenablesItems:(BOOL)on
824 [self setAutoenablesItems:on];
826 int i, count = [self numberOfItems];
827 for (i = 0; i < count; ++i) {
828 NSMenuItem *item = [self itemAtIndex:i];
829 [item setEnabled:YES];
830 NSMenu *submenu = [item submenu];
832 [submenu recurseSetAutoenablesItems:on];
837 @end // NSMenu (MMExtras)
842 @implementation NSNumber (MMExtras)
845 return [self intValue];
847 @end // NSNumber (MMExtras)