#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];
- NSString *name = activeApp.localizedName;
- if (!name)
- name = activeApp.bundleIdentifier;
- if (name && ![name isEqualToString:NSRunningApplication.currentApplication.localizedName])
- [self.mappingsController activateMappingForProcess:name];
+ if (activeApp)
+ [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:)
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 {
+- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[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];
+}
+
+- (void)transformIntoElement:(id)sender {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ TransformProcessType(&psn, kProcessTransformToUIElementApplication);
+}
+
+- (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];
+}
+
@end