mirror of
https://github.com/macvim-dev/macvim.git
synced 2026-06-07 15:37:14 +02:00
def350e3c8
git-svn-id: http://macvim.googlecode.com/svn/trunk@77 96c4425d-ca35-0410-94e5-3396d5c13a8f
406 lines
13 KiB
Objective-C
406 lines
13 KiB
Objective-C
/* vi:set ts=8 sts=4 sw=4 ft=objc:
|
|
*
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
* MacVim GUI port by Bjorn Winckler
|
|
*
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
* See README.txt for an overview of the Vim source code.
|
|
*/
|
|
|
|
#import "MMAppController.h"
|
|
#import "MMVimController.h"
|
|
|
|
|
|
|
|
// NSUserDefaults keys
|
|
NSString *MMNoWindowKey = @"nowindow";
|
|
NSString *MMTabMinWidthKey = @"tabminwidth";
|
|
NSString *MMTabMaxWidthKey = @"tabmaxwidth";
|
|
NSString *MMTabOptimumWidthKey = @"taboptimumwidth";
|
|
NSString *MMStatuslineOffKey = @"statuslineoff";
|
|
NSString *MMTextInsetLeft = @"insetleft";
|
|
NSString *MMTextInsetRight = @"insetright";
|
|
NSString *MMTextInsetTop = @"insettop";
|
|
NSString *MMTextInsetBottom = @"insetbottom";
|
|
NSString *MMTerminateAfterLastWindowClosed = @"terminateafterlastwindowclosed";
|
|
|
|
|
|
|
|
@interface NSMenu (MMExtras)
|
|
- (void)recurseSetAutoenablesItems:(BOOL)on;
|
|
@end
|
|
|
|
|
|
@implementation MMAppController
|
|
|
|
+ (void)initialize
|
|
{
|
|
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSNumber numberWithBool:NO], MMNoWindowKey,
|
|
[NSNumber numberWithInt:64], MMTabMinWidthKey,
|
|
[NSNumber numberWithInt:6*64], MMTabMaxWidthKey,
|
|
[NSNumber numberWithInt:132], MMTabOptimumWidthKey,
|
|
[NSNumber numberWithBool:NO], MMStatuslineOffKey,
|
|
[NSNumber numberWithInt:2], MMTextInsetLeft,
|
|
[NSNumber numberWithInt:1], MMTextInsetRight,
|
|
[NSNumber numberWithInt:1], MMTextInsetTop,
|
|
[NSNumber numberWithInt:1], MMTextInsetBottom,
|
|
[NSNumber numberWithBool:NO], MMTerminateAfterLastWindowClosed,
|
|
nil];
|
|
|
|
[[NSUserDefaults standardUserDefaults] registerDefaults:dict];
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
if ((self = [super init])) {
|
|
vimControllers = [NSMutableArray new];
|
|
|
|
#if MM_USE_DO
|
|
// NOTE! If the name of the connection changes here it must also be
|
|
// updated in MMBackend.m.
|
|
NSConnection *connection = [NSConnection defaultConnection];
|
|
NSString *name = [NSString stringWithFormat:@"%@-connection",
|
|
[[NSBundle mainBundle] bundleIdentifier]];
|
|
//NSLog(@"Registering connection with name '%@'", name);
|
|
if ([connection registerName:name]) {
|
|
[connection setRootObject:self];
|
|
|
|
// NOTE: When the user is resizing the window the AppKit puts the
|
|
// run loop in event tracking mode. Unless the connection listens
|
|
// to request in this mode, live resizing won't work.
|
|
[connection addRequestMode:NSEventTrackingRunLoopMode];
|
|
} else {
|
|
NSLog(@"WARNING: Failed to register connection with name '%@'",
|
|
name);
|
|
}
|
|
#else
|
|
// Init named port for VimTasks to connect to
|
|
receivePort = [NSMachPort new];
|
|
[receivePort setDelegate:self];
|
|
|
|
[[NSRunLoop currentRunLoop] addPort:receivePort
|
|
forMode:NSDefaultRunLoopMode];
|
|
|
|
// NOTE! If the name of the port changes here it must also be updated
|
|
// in MMBackend.m.
|
|
NSString *portName = [NSString stringWithFormat:@"%@-taskport",
|
|
[[NSBundle mainBundle] bundleIdentifier]];
|
|
//NSLog(@"Starting mach bootstrap server: %@", portName);
|
|
if (![[NSMachBootstrapServer sharedInstance] registerPort:receivePort
|
|
name:portName]) {
|
|
NSLog(@"WARNING: Failed to start mach bootstrap server");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
//NSLog(@"MMAppController dealloc");
|
|
|
|
#if !MM_USE_DO
|
|
[receivePort release];
|
|
#endif
|
|
[vimControllers release];
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
|
{
|
|
// HACK! The GUI does not get activated if Vim is launched by MMBackend in
|
|
// checkin:. I have not been able to figure out any other way to get it to
|
|
// activate other than forcing it here. A better solution for launching
|
|
// the GUI would be good.
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
}
|
|
|
|
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
|
|
{
|
|
// NOTE! This way it possible to start the app with the command-line
|
|
// argument '-nowindow yes' and no window will be opened by default.
|
|
return ![[NSUserDefaults standardUserDefaults] boolForKey:MMNoWindowKey];
|
|
}
|
|
|
|
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender
|
|
{
|
|
//NSLog(@"%s NSapp=%@ theApp=%@", _cmd, NSApp, sender);
|
|
|
|
[self newVimWindow:self];
|
|
return YES;
|
|
}
|
|
|
|
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
|
|
{
|
|
NSMutableArray *args = [NSMutableArray arrayWithObjects:@"-g", @"-p", nil];
|
|
[args addObjectsFromArray:filenames];
|
|
|
|
NSString *path = [[NSBundle mainBundle]
|
|
pathForAuxiliaryExecutable:@"Vim"];
|
|
|
|
[NSTask launchedTaskWithLaunchPath:path arguments:args];
|
|
|
|
[NSApp replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
|
// NSApplicationDelegateReplySuccess = 0,
|
|
// NSApplicationDelegateReplyCancel = 1,
|
|
// NSApplicationDelegateReplyFailure = 2
|
|
}
|
|
|
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
|
|
{
|
|
return [[NSUserDefaults standardUserDefaults]
|
|
boolForKey:MMTerminateAfterLastWindowClosed];
|
|
}
|
|
|
|
- (NSApplicationTerminateReply)applicationShouldTerminate:
|
|
(NSApplication *)sender
|
|
{
|
|
#if MM_USE_DO
|
|
int reply = NSTerminateNow;
|
|
BOOL modifiedBuffers = NO;
|
|
|
|
unsigned i, count = [vimControllers count];
|
|
for (i = 0; i < count; ++i) {
|
|
MMVimController *controller = [vimControllers objectAtIndex:i];
|
|
id proxy = [controller backendProxy];
|
|
if (proxy && [proxy checkForModifiedBuffers]) {
|
|
modifiedBuffers = YES;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (modifiedBuffers) {
|
|
NSAlert *alert = [[NSAlert alloc] init];
|
|
[alert addButtonWithTitle:@"Quit"];
|
|
[alert addButtonWithTitle:@"Cancel"];
|
|
[alert setMessageText:@"Quit without saving?"];
|
|
[alert setInformativeText:@"There are modified buffers, "
|
|
" if you quit now all changes will be lost. Quit anyway?"];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
|
|
if ([alert runModal] != NSAlertFirstButtonReturn) {
|
|
reply = NSTerminateCancel;
|
|
}
|
|
|
|
[alert release];
|
|
}
|
|
|
|
return reply;
|
|
#else
|
|
int reply = NSTerminateNow;
|
|
|
|
// HACK! Send message to all vim tasks asking if they have modified
|
|
// buffers, then hang around for a while waiting for responses to come
|
|
// back. If any task has at least one modified buffer an alert dialog is
|
|
// displayed telling the user that there are modified buffers. The user
|
|
// can then choose whether to quit anyway, or cancel the termination.
|
|
// (NSTerminateLater is not supported.)
|
|
terminateNowCount = 0;
|
|
abortTermination = NO;
|
|
|
|
unsigned i, count = [vimControllers count];
|
|
for (i = 0; i < count; ++i) {
|
|
MMVimController *controller = [vimControllers objectAtIndex:i];
|
|
[NSPortMessage sendMessage:TaskShouldTerminateMsgID
|
|
withSendPort:[controller sendPort]
|
|
receivePort:receivePort
|
|
wait:NO];
|
|
}
|
|
|
|
NSDate *timeOutDate = [NSDate dateWithTimeIntervalSinceNow:15];
|
|
while (terminateNowCount < count && !abortTermination &&
|
|
NSOrderedDescending == [timeOutDate compare:[NSDate date]]) {
|
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
|
|
beforeDate:timeOutDate];
|
|
}
|
|
|
|
//NSLog(@"%s terminateNowCount=%d abortTermination=%s", _cmd,
|
|
// terminateNowCount, abortTermination ? "YES" : "NO");
|
|
|
|
if (abortTermination) {
|
|
NSAlert *alert = [[NSAlert alloc] init];
|
|
[alert addButtonWithTitle:@"Quit"];
|
|
[alert addButtonWithTitle:@"Cancel"];
|
|
[alert setMessageText:@"Quit without saving?"];
|
|
[alert setInformativeText:@"There are modified buffers, "
|
|
" if you quit now all changes will be lost. Quit anyway?"];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
|
|
if ([alert runModal] != NSAlertFirstButtonReturn) {
|
|
reply = NSTerminateCancel;
|
|
}
|
|
|
|
[alert release];
|
|
} else if (terminateNowCount < count) {
|
|
NSLog(@"WARNING: Not all tasks replied to TaskShouldTerminateMsgID,"
|
|
" quitting anyway.");
|
|
}
|
|
|
|
return reply;
|
|
#endif
|
|
}
|
|
|
|
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
|
{
|
|
// NOTE! Is this a correct way of releasing the MMAppController?
|
|
[NSApp setDelegate:nil];
|
|
[self autorelease];
|
|
}
|
|
|
|
#if !MM_USE_DO
|
|
- (void)handlePortMessage:(NSPortMessage *)portMessage
|
|
{
|
|
unsigned msgid = [portMessage msgid];
|
|
|
|
if (msgid == CheckinMsgID) {
|
|
//NSLog(@"Received checkin message from VimTask.");
|
|
MMVimController *wc = [[MMVimController alloc]
|
|
initWithPort:[portMessage sendPort]];
|
|
[vimControllers addObject:wc];
|
|
[wc release];
|
|
} else if (msgid == TerminateReplyYesMsgID) {
|
|
++terminateNowCount;
|
|
} else if (msgid == TerminateReplyNoMsgID) {
|
|
abortTermination = YES;
|
|
} else {
|
|
NSLog(@"WARNING: Unknown message received (msgid=%d)", msgid);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
- (void)removeVimController:(id)controller
|
|
{
|
|
[vimControllers removeObject:controller];
|
|
|
|
if (![vimControllers count]) {
|
|
// Turn on autoenabling of menus (because no Vim is open to handle it),
|
|
// but do not touch the MacVim menu.
|
|
NSMenu *mainMenu = [NSApp mainMenu];
|
|
int i, count = [mainMenu numberOfItems];
|
|
for (i = 1; i < count; ++i) {
|
|
NSMenu *submenu = [[mainMenu itemAtIndex:i] submenu];
|
|
[submenu recurseSetAutoenablesItems:YES];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (IBAction)newVimWindow:(id)sender
|
|
{
|
|
NSMutableArray *args = [NSMutableArray arrayWithObject:@"-g"];
|
|
NSString *path = [[NSBundle mainBundle]
|
|
pathForAuxiliaryExecutable:@"Vim"];
|
|
|
|
//NSLog(@"Launching a new VimTask...");
|
|
[NSTask launchedTaskWithLaunchPath:path arguments:args];
|
|
}
|
|
|
|
- (IBAction)selectNextWindow:(id)sender
|
|
{
|
|
#if 0
|
|
NSArray *windows = [NSApp orderedWindows];
|
|
unsigned idx = [windows indexOfObject:[NSApp keyWindow]];
|
|
if (NSNotFound != idx) {
|
|
if (++idx >= [windows count])
|
|
idx = 0;
|
|
[[windows objectAtIndex:idx] makeKeyAndOrderFront:self];
|
|
}
|
|
#else
|
|
unsigned i, count = [vimControllers count];
|
|
if (!count) return;
|
|
|
|
NSWindow *keyWindow = [NSApp keyWindow];
|
|
for (i = 0; i < count; ++i) {
|
|
MMWindowController *vc = [vimControllers objectAtIndex:i];
|
|
if ([[[vc windowController] window] isEqual:keyWindow])
|
|
break;
|
|
}
|
|
|
|
if (i < count) {
|
|
if (++i >= count)
|
|
i = 0;
|
|
MMWindowController *vc = [vimControllers objectAtIndex:i];
|
|
[[vc windowController] showWindow:self];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
- (IBAction)selectPreviousWindow:(id)sender
|
|
{
|
|
#if 0
|
|
NSArray *windows = [NSApp orderedWindows];
|
|
unsigned idx = [windows indexOfObject:[NSApp keyWindow]];
|
|
if (NSNotFound != idx) {
|
|
if (idx > 0) {
|
|
--idx;
|
|
} else {
|
|
idx = [windows count] - 1;
|
|
}
|
|
[[windows objectAtIndex:idx] makeKeyAndOrderFront:self];
|
|
}
|
|
#else
|
|
unsigned i, count = [vimControllers count];
|
|
if (!count) return;
|
|
|
|
NSWindow *keyWindow = [NSApp keyWindow];
|
|
for (i = 0; i < count; ++i) {
|
|
MMWindowController *vc = [vimControllers objectAtIndex:i];
|
|
if ([[[vc windowController] window] isEqual:keyWindow])
|
|
break;
|
|
}
|
|
|
|
if (i < count) {
|
|
if (i > 0) {
|
|
--i;
|
|
} else {
|
|
i = count - 1;
|
|
}
|
|
MMWindowController *vc = [vimControllers objectAtIndex:i];
|
|
[[vc windowController] showWindow:self];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#if MM_USE_DO
|
|
- (byref id <MMFrontendProtocol>)connectBackend:
|
|
(byref in id <MMBackendProtocol>)backend;
|
|
{
|
|
//NSLog(@"Frontend got connection request from backend...adding new "
|
|
// "MMVimController");
|
|
|
|
[(NSDistantObject*)backend
|
|
setProtocolForProxy:@protocol(MMBackendProtocol)];
|
|
|
|
MMVimController *wc = [[[MMVimController alloc] initWithBackend:backend]
|
|
autorelease];
|
|
[vimControllers addObject:wc];
|
|
|
|
return wc;
|
|
}
|
|
#endif
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSMenu (MMExtras)
|
|
|
|
- (void)recurseSetAutoenablesItems:(BOOL)on
|
|
{
|
|
[self setAutoenablesItems:on];
|
|
|
|
int i, count = [self numberOfItems];
|
|
for (i = 0; i < count; ++i) {
|
|
NSMenu *submenu = [[self itemAtIndex:i] submenu];
|
|
if (submenu) {
|
|
[submenu recurseSetAutoenablesItems:on];
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|