diff --git a/ChangeLog b/ChangeLog index e34ac37ce..885a8ba03 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-01-27 Richard Frith-Macdonald + + * Source/NSApplication.m: pass more informative user info in + notifications. + * Source/NSWorkspace.m: implement filesystem based fallback mechanisms + for tracking launched and active appplications when there is no + workspace manager process to do it. + 2006-01-22 Fred Kiefer * Source/NSPopupButtonCell.m (-synchronizeTitleAndSelectedItem): diff --git a/Source/NSApplication.m b/Source/NSApplication.m index bc46ef110..9c53fa873 100644 --- a/Source/NSApplication.m +++ b/Source/NSApplication.m @@ -341,6 +341,7 @@ struct _NSModalSession { @interface NSApplication (Private) - _appIconInit; +- (NSDictionary*) _notificationUserInfo; - (void) _openDocument: (NSString*)name; - (void) _windowDidBecomeKey: (NSNotification*) notification; - (void) _windowDidBecomeMain: (NSNotification*) notification; @@ -913,7 +914,6 @@ static NSSize scaledIconSizeForSize(NSSize imageSize) NSString *mainModelFile; NSString *appIconFile; NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; - NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; NSString *filePath; NSArray *windows_list; unsigned count; @@ -1039,11 +1039,11 @@ static NSSize scaledIconSizeForSize(NSSize imageSize) [self terminate: self]; } else if (![defs boolForKey: @"autolaunch"] - && [_delegate respondsToSelector: - @selector(applicationShouldOpenUntitledFile:)] - && ([_delegate applicationShouldOpenUntitledFile: self]) - && [_delegate respondsToSelector: - @selector(applicationOpenUntitledFile:)]) + && [_delegate respondsToSelector: + @selector(applicationShouldOpenUntitledFile:)] + && ([_delegate applicationShouldOpenUntitledFile: self]) + && [_delegate respondsToSelector: + @selector(applicationOpenUntitledFile:)]) { [_delegate applicationOpenUntitledFile: self]; } @@ -1053,14 +1053,21 @@ static NSSize scaledIconSizeForSize(NSSize imageSize) object: self]; NS_DURING - [[workspace notificationCenter] - postNotificationName: NSWorkspaceDidLaunchApplicationNotification - object: workspace - userInfo: [workspace activeApplication]]; + { + NSWorkspace *workspace; + + workspace = [NSWorkspace sharedWorkspace]; + [[workspace notificationCenter] + postNotificationName: NSWorkspaceDidLaunchApplicationNotification + object: workspace + userInfo: [self _notificationUserInfo]]; + } NS_HANDLER - NSLog (_(@"Problem during launch app notification: %@"), - [localException reason]); - [localException raise]; + { + NSLog (_(@"Problem during launch app notification: %@"), + [localException reason]); + [localException raise]; + } NS_ENDHANDLER } @@ -1168,7 +1175,8 @@ static NSSize scaledIconSizeForSize(NSSize imageSize) } [nc postNotificationName: NSApplicationDidBecomeActiveNotification - object: self]; + object: self + userInfo: [self _notificationUserInfo]]; } } @@ -1230,7 +1238,8 @@ static NSSize scaledIconSizeForSize(NSSize imageSize) } [nc postNotificationName: NSApplicationDidResignActiveNotification - object: self]; + object: self + userInfo: [self _notificationUserInfo]]; } } @@ -3046,7 +3055,7 @@ image.

See Also: -applicationIconImage

{ if (shouldTerminate) { - NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; [nc postNotificationName: NSApplicationWillTerminateNotification object: self]; @@ -3063,7 +3072,7 @@ image.

See Also: -applicationIconImage

[[workspace notificationCenter] postNotificationName: NSWorkspaceDidTerminateApplicationNotification object: workspace - userInfo: [workspace activeApplication]]; + userInfo: [self _notificationUserInfo]]; /* Destroy the main run loop pool (this also destroys any nested pools which might have been created inside this one). */ @@ -3286,6 +3295,21 @@ image.

See Also: -applicationIconImage

return self; } +- (NSDictionary*) _notificationUserInfo +{ + NSNumber *processIdentifier; + NSDictionary *userInfo; + + processIdentifier = [NSNumber numberWithInt: + [[NSProcessInfo processInfo] processIdentifier]]; + userInfo = [NSDictionary dictionaryWithObjectsAndKeys: + [(GSServicesManager*)_listener port], @"NSApplicationName", + [[NSBundle mainBundle] bundlePath], @"NSApplicationPath", + processIdentifier, @"NSApplicationPprocessIdentifier", + nil]; + return userInfo; +} + - (void) _openDocument: (NSString*)filePath { [_listener application: self openFile: filePath]; diff --git a/Source/NSWorkspace.m b/Source/NSWorkspace.m index c2e638468..3b98f98f3 100644 --- a/Source/NSWorkspace.m +++ b/Source/NSWorkspace.m @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -78,10 +79,128 @@ static NSImage *unknownTool = nil; static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; +static NSArray *GSLaunched(NSDictionary *info, NSString *mode) +{ + static NSString *path = nil; + static NSDistributedLock *lock = nil; + NSMutableArray *apps = nil; + BOOL toBecomeActive; + BOOL toResignActive; + BOOL toLaunch; + BOOL toTerminate; + + toBecomeActive = [mode isEqualToString: + NSApplicationDidBecomeActiveNotification]; + toResignActive = [mode isEqualToString: + NSApplicationDidResignActiveNotification]; + toLaunch = [mode isEqualToString: + NSWorkspaceDidLaunchApplicationNotification]; + toTerminate = [mode isEqualToString: + NSWorkspaceDidTerminateApplicationNotification]; + + if (path == nil) + { + path = [NSTemporaryDirectory() + stringByAppendingPathComponent: @"GSLaunchedApplications"]; + RETAIN(path); + lock = [[NSDistributedLock alloc] initWithPath: + [path stringByAppendingPathExtension: @"lock"]]; + } + if ([lock tryLock] == NO) + { + if ([[lock lockDate] timeIntervalSinceNow] < -20.0) + { + NS_DURING + { + [lock breakLock]; + } + NS_HANDLER + { + NSLog(@"Unable to break lock %@ ... %@", lock, localException); + } + NS_ENDHANDLER + } + if ([lock tryLock] == NO) + { + NSLog(@"Unable to obtain lock %@", lock); + return nil; + } + } + + if ([[NSFileManager defaultManager] isReadableFileAtPath: path] == YES) + { + apps = [NSMutableArray arrayWithContentsOfFile: path]; + } + if (apps == nil) + { + apps = [NSMutableArray arrayWithCapacity: 1]; + } + + if (info != nil) + { + NSString *name = [info objectForKey: @"NSApplicationName"]; + unsigned i = [apps count]; + BOOL modified = NO; + BOOL wasActive = NO; + + while (i-- > 0) + { + NSDictionary *oldInfo; + NSString *oldName; + + oldInfo = [apps objectAtIndex: i]; + oldName = [oldInfo objectForKey: @"NSApplicationName"]; + + if ([name isEqualToString: oldName] == YES) + { + if ([oldInfo objectForKey: @"GSApplicationActive"] != nil) + { + wasActive = YES; + } + [apps removeObjectAtIndex: i]; + modified = YES; + } + else if (toBecomeActive == YES + && [oldInfo objectForKey: @"GSApplicationActive"] != nil) + { + NSMutableDictionary *m = [oldInfo mutableCopy]; + + [m removeObjectForKey: @"GSApplicationActive"]; + [apps replaceObjectAtIndex: i withObject: m]; + RELEASE(m); + modified = YES; + } + } + + if (toTerminate == NO) + { + if (toBecomeActive == YES + || (toResignActive == NO && wasActive == YES)) + { + NSMutableDictionary *m = [info mutableCopy]; + + [m setObject: @"YES" forKey: @"GSApplicationActive"]; + info = AUTORELEASE(m); + } + [apps addObject: info]; + modified = YES; + } + + if (modified == YES) + { + [apps writeToFile: path atomically: YES]; + } + } + [lock unlock]; + + return apps; +} + @interface _GSWorkspaceCenter: NSNotificationCenter { NSDistributedNotificationCenter *remote; } +- (void) _handleApplicationNotification: (NSNotification*)aNotification; - (void) _handleRemoteNotification: (NSNotification*)aNotification; - (void) _postLocal: (NSString*)name userInfo: (NSDictionary*)info; @end @@ -90,6 +209,14 @@ static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; - (void) dealloc { + [[NSNotificationCenter defaultCenter] + removeObserver: self + name: NSApplicationDidBecomeActiveNotification + object: nil]; + [[NSNotificationCenter defaultCenter] + removeObserver: self + name: NSApplicationDidResignActiveNotification + object: nil]; [remote removeObserver: self name: nil object: GSWorkspaceNotification]; RELEASE(remote); [super dealloc]; @@ -100,6 +227,17 @@ static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; self = [super init]; if (self != nil) { + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(_handleApplicationNotification:) + name: NSApplicationDidResignActiveNotification + object: nil]; + [[NSNotificationCenter defaultCenter] + addObserver: self + selector: @selector(_handleApplicationNotification:) + name: NSApplicationDidBecomeActiveNotification + object: nil]; + remote = RETAIN([NSDistributedNotificationCenter defaultCenter]); NS_DURING { @@ -135,10 +273,21 @@ static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; - (void) postNotification: (NSNotification*)aNotification { NSNotification *rem; + NSString *name = [aNotification name]; + NSDictionary *info = [aNotification userInfo]; - rem = [NSNotification notificationWithName: [aNotification name] + if ([name isEqual: NSWorkspaceDidTerminateApplicationNotification] == YES) + { + GSLaunched(info, name); + } + if ([name isEqual: NSWorkspaceDidLaunchApplicationNotification] == YES) + { + GSLaunched(info, name); + } + + rem = [NSNotification notificationWithName: name object: GSWorkspaceNotification - userInfo: [aNotification userInfo]]; + userInfo: info]; NS_DURING { [remote postNotification: rem]; @@ -176,6 +325,17 @@ static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; userInfo: info]]; } +- (void) _handleApplicationNotification: (NSNotification*)aNotification +{ + NSString *name = [aNotification name]; + + if ([name isEqualToString: NSApplicationDidBecomeActiveNotification] == YES + || [name isEqualToString: NSApplicationDidResignActiveNotification] == YES) + { + GSLaunched([aNotification userInfo], name); + } +} + /* * Forward a notification from a remote application to observers in this * application. @@ -215,7 +375,8 @@ static NSString *GSWorkspaceNotification = @"GSWorkspaceNotification"; // application communication - (BOOL) _launchApplication: (NSString*)appName - arguments: (NSArray*)args; + arguments: (NSArray*)args + locally: (BOOL)locally; - (id) _connectApplication: (NSString*)appName; - (id) _workspaceApplication; @@ -517,7 +678,7 @@ static NSString *_rootPath = @"/"; NSArray *args; args = [NSArray arrayWithObjects: @"-GSFilePath", fullPath, nil]; - return [self _launchApplication: appName arguments: args]; + return [self _launchApplication: appName arguments: args locally: NO]; } else { @@ -564,7 +725,7 @@ static NSString *_rootPath = @"/"; NSArray *args; args = [NSArray arrayWithObjects: @"-GSTempPath", fullPath, nil]; - return [self _launchApplication: appName arguments: args]; + return [self _launchApplication: appName arguments: args locally: NO]; } else { @@ -1135,7 +1296,7 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath { args = [NSArray arrayWithObjects: @"-autolaunch", @"YES", nil]; } - return [self _launchApplication: appName arguments: args]; + return [self _launchApplication: appName arguments: args locally: NO]; } else { @@ -1152,9 +1313,29 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath */ - (NSDictionary*) activeApplication { - NSProcessInfo *processInfo = [NSProcessInfo processInfo]; - NSString *appName = [[GSServicesManager manager] port]; + NSProcessInfo *processInfo; + NSString *appName; + NSArray *apps = GSLaunched(nil, nil); + unsigned i = [apps count]; + /* + * Try to find actrive app in launched applications. + */ + while (i-- > 0) + { + NSDictionary *info = [apps objectAtIndex: i]; + + if ([info objectForKey: @"GSApplicationActive"] != nil) + { + return info; + } + } + + /* + * Should never get here ... but assume this is the active app. + */ + processInfo = [NSProcessInfo processInfo]; + appName = [[GSServicesManager manager] port]; return [NSDictionary dictionaryWithObjectsAndKeys: appName, @"NSApplicationName", [[NSBundle mainBundle] bundlePath], @"NSApplicationPath", @@ -1164,24 +1345,14 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath } /** - * Returns an array listing all the applications know to have been - * launched. + * Returns an array listing all the applications known to have been + * launched. Each entry in the array is a dictionary providing + * the name, path and process identfier of an application.
+ * NB. The contents of this array are not guaranteed to be up to date. */ - (NSArray*) launchedApplications { - NSDictionary *dict; - NSString *app; - NSMutableArray *apps = [NSMutableArray array]; - NSEnumerator *enumerator = [_launched keyEnumerator]; - - while ((app = [enumerator nextObject]) != nil) - { - dict = [NSDictionary dictionaryWithObject: app - forKey: @"NSApplicationName"]; - [apps addObject: dict]; - } - - return apps; + return GSLaunched(nil, nil); } /* @@ -2071,19 +2242,21 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath } /** - * Launch an application ... if there is a workspace application, ask it - * to perform the launch for us. Otherwise we try to launch the app - * ourself as long as it is on the same host as we are. + * Launch an application ... if there is a workspace application and + * we have not been specifically asked to launch locally, ask the + * workspace application to perform the launch for us. + * Otherwise we try to launch the app ourself as long as it is on + * the same host as we are. */ - (BOOL) _launchApplication: (NSString*)appName arguments: (NSArray*)args + locally: (BOOL)locally { - id app = nil; + id app; - // app = [self _workspaceApplication]; - if (app != nil) + if (locally == NO && (app = [self _workspaceApplication]) != nil) { - return [app _launchApplication: appName arguments: args]; + return [app _launchApplication: appName arguments: args locally: YES]; } else { @@ -2267,10 +2440,6 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath { NSString *host; - /** - * We don't use -_launchApplication:arguents: here as that method - * calls -_workspaceApplication, and would cause recursion. - */ host = [[NSUserDefaults standardUserDefaults] stringForKey: @"NSHost"]; if (host == nil) { @@ -2293,7 +2462,9 @@ inFileViewerRootedAtPath: (NSString*)rootFullpath */ if ([host isEqual: @""] == YES) { - if ([self _launchApplication: appName arguments: nil] == YES) + if ([self _launchApplication: appName + arguments: nil + locally: YES] == YES) { app = [self _connectApplication: appName]; }