X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=blobdiff_plain;f=Classes%2FEnjoyableApplicationDelegate.m;h=b1d9907fb850cd85f7f05679c5acdbb8d6ce3224;hp=78b930b80f1f3a4350f8624e951e5e13bc637b8f;hb=b417eb4ae97804070277eb19cc596bdb6bb9d281;hpb=7e5568674713bedf9318e83b9fb13abbd122382c diff --git a/Classes/EnjoyableApplicationDelegate.m b/Classes/EnjoyableApplicationDelegate.m index 78b930b..b1d9907 100644 --- a/Classes/EnjoyableApplicationDelegate.m +++ b/Classes/EnjoyableApplicationDelegate.m @@ -5,15 +5,17 @@ // Created by Sam McCall on 4/05/09. // +#import + #import "EnjoyableApplicationDelegate.h" #import "NJMapping.h" #import "NJMappingsController.h" -#import "NJDeviceController.h" -#import "NJOutputController.h" #import "NJEvents.h" -@implementation EnjoyableApplicationDelegate +@implementation EnjoyableApplicationDelegate { + NSStatusItem *statusItem; +} - (void)didSwitchApplication:(NSNotification *)note { NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey]; @@ -21,17 +23,12 @@ [self.mappingsController activateMappingForProcess:activeApp]; } -- (void)applicationDidFinishLaunching:(NSNotification *)notification { +- (void)applicationWillFinishLaunching:(NSNotification *)notification { [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(mappingDidChange:) name:NJEventMappingChanged object:nil]; - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(mappingListDidChange:) - name:NJEventMappingListChanged - object:nil]; [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(eventTranslationActivated:) @@ -43,87 +40,155 @@ name:NJEventTranslationDeactivated object:nil]; - [self.inputController setup]; [self.mappingsController load]; + + statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:36]; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; + statusItem.highlightMode = YES; + statusItem.menu = statusItemMenu; + statusItem.target = self; } -- (void)applicationDidBecomeActive:(NSNotification *)notification { - [window makeKeyAndOrderFront:nil]; +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + if ([NSUserDefaults.standardUserDefaults boolForKey:@"hidden in status item"] + && NSRunningApplication.currentApplication.wasLaunchedAsLoginItemOrResume) + [self transformIntoElement:nil]; + else + [window makeKeyAndOrderFront:nil]; } - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag { - [window makeKeyAndOrderFront:nil]; + [self restoreToForeground:theApplication]; + return NO; +} + +- (void)restoreToForeground:(id)sender { + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + [NSApplication.sharedApplication activateIgnoringOtherApps:YES]; + [window makeKeyAndOrderFront:sender]; + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(transformIntoElement:) + object:self]; + [NSUserDefaults.standardUserDefaults setBool:NO forKey:@"hidden in status item"]; +} + +- (void)applicationWillBecomeActive:(NSNotification *)notification { + if (window.isVisible) + [self restoreToForeground:notification]; +} + +- (void)transformIntoElement:(id)sender { + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToUIElementApplication); + [NSUserDefaults.standardUserDefaults setBool:YES forKey:@"hidden in status item"]; +} + +- (void)flashStatusItem { + if ([statusItem.image.name isEqualToString:@"Status Menu Icon"]) { + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; + } else { + statusItem.image = [NSImage imageNamed:@"Status Menu Icon"]; + } + +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication { + [theApplication hide:theApplication]; + // If we turn into a UIElement right away, the application cancels + // the deactivation events. The dock icon disappears, but an + // unresponsive menu bar remains until the user clicks somewhere. + // So delay just long enough to be past the end handling that. + [self performSelector:@selector(transformIntoElement:) withObject:self afterDelay:0.001]; return NO; } - (void)eventTranslationActivated:(NSNotification *)note { - [NSProcessInfo.processInfo disableAutomaticTermination:@"Input translation is active."]; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon"]; [NSWorkspace.sharedWorkspace.notificationCenter addObserver:self selector:@selector(didSwitchApplication:) name:NSWorkspaceDidActivateApplicationNotification object:nil]; - NSLog(@"Listening for application changes."); } - (void)eventTranslationDeactivated:(NSNotification *)note { - [NSProcessInfo.processInfo enableAutomaticTermination:@"Input translation is active."]; + statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; [NSWorkspace.sharedWorkspace.notificationCenter removeObserver:self name:NSWorkspaceDidActivateApplicationNotification object:nil]; - NSLog(@"Ignoring application changes."); -} - -- (void)mappingListDidChange:(NSNotification *)note { - NSArray *mappings = note.object; - while (dockMenuBase.lastItem.representedObject) - [dockMenuBase removeLastItem]; - int added = 0; - for (NJMapping *mapping in mappings) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(chooseMapping:) - keyEquivalent:keyEquiv]; - item.representedObject = mapping; - item.state = mapping == self.mappingsController.currentMapping; - [dockMenuBase addItem:item]; - } } - (void)mappingDidChange:(NSNotification *)note { - NJMapping *current = note.object; - for (NSMenuItem *item in dockMenuBase.itemArray) - if (item.representedObject) - item.state = item.representedObject == current; -} - -- (void)chooseMapping:(NSMenuItem *)sender { - NJMapping *chosen = sender.representedObject; - [self.mappingsController activateMapping:chosen]; + if (!window.isVisible) + for (int i = 0; i < 4; ++i) + [self performSelector:@selector(flashStatusItem) + withObject:self + afterDelay:0.2 * i]; } - (NSMenu *)applicationDockMenu:(NSApplication *)sender { - NSMenu *menu = [[NSMenu alloc] init]; - int added = 0; - for (NJMapping *mapping in self.mappingsController) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(chooseMapping:) - keyEquivalent:keyEquiv]; - item.representedObject = mapping; - item.state = mapping == self.mappingsController.currentMapping; - [menu addItem:item]; - } - return menu; + return dockMenu; } - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + [self restoreToForeground:sender]; NSURL *url = [NSURL fileURLWithPath:filename]; [self.mappingsController addMappingWithContentsOfURL:url]; return YES; } +- (void)mappingWasChosen:(NJMapping *)mapping { + [self.mappingsController activateMapping:mapping]; +} + +- (void)mappingListShouldOpen { + [self restoreToForeground:self]; + [self.mappingsController mappingPressed:self]; +} + +- (void)loginItemPromptDidEnd:(NSWindow *)sheet + returnCode:(int)returnCode + contextInfo:(void *)contextInfo { + if (returnCode == NSAlertDefaultReturn) { + [NSRunningApplication.currentApplication addToLoginItems]; + // If we're going to automatically start, don't bug the user + // about automatic updates next boot - they probably want it, + // and if they don't they probably want a prompt for it less. + SUUpdater.sharedUpdater.automaticallyChecksForUpdates = YES; + } +} + +- (void)loginItemPromptDidDismiss:(NSWindow *)sheet + returnCode:(int)returnCode + contextInfo:(void *)contextInfo { + [NSUserDefaults.standardUserDefaults setBool:YES forKey:@"explained login items"]; + [window performClose:sheet]; +} + +- (BOOL)windowShouldClose:(NSWindow *)sender { + if (sender != window + || NSRunningApplication.currentApplication.isLoginItem + || [NSUserDefaults.standardUserDefaults boolForKey:@"explained login items"]) + return YES; + NSBeginAlertSheet( + NSLocalizedString(@"login items prompt", @"alert prompt for adding to login items"), + NSLocalizedString(@"login items add button", @"button to add to login items"), + NSLocalizedString(@"login items don't add button", @"button to not add to login items"), + nil, window, self, + @selector(loginItemPromptDidEnd:returnCode:contextInfo:), + @selector(loginItemPromptDidDismiss:returnCode:contextInfo:), + NULL, + NSLocalizedString(@"login items explanation", @"a brief explanation of login items") + ); + for (int i = 0; i < 10; ++i) + [self performSelector:@selector(flashStatusItem) + withObject:self + afterDelay:0.5 * i]; + return NO; +} + @end