Generalize and share mapping menu (main, status, dock) behavior.
[enjoyable.git] / Classes / EnjoyableApplicationDelegate.m
1 //
2 // EnjoyableApplicationDelegate.m
3 // Enjoy
4 //
5 // Created by Sam McCall on 4/05/09.
6 //
7
8 #import "EnjoyableApplicationDelegate.h"
9
10 #import "NJMapping.h"
11 #import "NJMappingsController.h"
12 #import "NJDeviceController.h"
13 #import "NJOutputController.h"
14 #import "NJEvents.h"
15
16 @implementation EnjoyableApplicationDelegate {
17 NSStatusItem *statusItem;
18 }
19
20 - (void)didSwitchApplication:(NSNotification *)note {
21 NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey];
22 if (activeApp)
23 [self.mappingsController activateMappingForProcess:activeApp];
24 }
25
26 - (void)applicationWillFinishLaunching:(NSNotification *)notification {
27 [NSNotificationCenter.defaultCenter
28 addObserver:self
29 selector:@selector(mappingDidChange:)
30 name:NJEventMappingChanged
31 object:nil];
32 [NSNotificationCenter.defaultCenter
33 addObserver:self
34 selector:@selector(eventTranslationActivated:)
35 name:NJEventTranslationActivated
36 object:nil];
37 [NSNotificationCenter.defaultCenter
38 addObserver:self
39 selector:@selector(eventTranslationDeactivated:)
40 name:NJEventTranslationDeactivated
41 object:nil];
42
43 [self.mappingsController load];
44
45 statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:36];
46 statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"];
47 statusItem.highlightMode = YES;
48 statusItem.menu = statusItemMenu;
49 statusItem.target = self;
50 }
51
52 - (void)applicationDidFinishLaunching:(NSNotification *)notification {
53 [window makeKeyAndOrderFront:nil];
54 }
55
56 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication
57 hasVisibleWindows:(BOOL)flag {
58 [self restoreToForeground:theApplication];
59 return NO;
60 }
61
62 - (void)restoreToForeground:(id)sender {
63 ProcessSerialNumber psn = { 0, kCurrentProcess };
64 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
65 [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
66 [window makeKeyAndOrderFront:sender];
67 [NSObject cancelPreviousPerformRequestsWithTarget:self
68 selector:@selector(transformIntoElement:)
69 object:self];
70 }
71
72 - (void)transformIntoElement:(id)sender {
73 ProcessSerialNumber psn = { 0, kCurrentProcess };
74 TransformProcessType(&psn, kProcessTransformToUIElementApplication);
75 }
76
77 - (void)flashStatusItem {
78 if ([statusItem.image.name isEqualToString:@"Status Menu Icon"]) {
79 statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"];
80 } else {
81 statusItem.image = [NSImage imageNamed:@"Status Menu Icon"];
82 }
83
84 }
85
86 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
87 [theApplication hide:theApplication];
88 // If we turn into a UIElement right away, the application cancels
89 // the deactivation events. The dock icon disappears, but an
90 // unresponsive menu bar remains until the user clicks somewhere.
91 // So delay just long enough to be past the end handling that.
92 [self performSelector:@selector(transformIntoElement:) withObject:self afterDelay:0.001];
93 return NO;
94 }
95
96 - (void)eventTranslationActivated:(NSNotification *)note {
97 statusItem.image = [NSImage imageNamed:@"Status Menu Icon"];
98 [NSWorkspace.sharedWorkspace.notificationCenter
99 addObserver:self
100 selector:@selector(didSwitchApplication:)
101 name:NSWorkspaceDidActivateApplicationNotification
102 object:nil];
103 }
104
105 - (void)eventTranslationDeactivated:(NSNotification *)note {
106 statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"];
107 [NSWorkspace.sharedWorkspace.notificationCenter
108 removeObserver:self
109 name:NSWorkspaceDidActivateApplicationNotification
110 object:nil];
111 }
112
113 - (void)mappingDidChange:(NSNotification *)note {
114 if (!window.isVisible)
115 for (int i = 0; i < 4; ++i)
116 [self performSelector:@selector(flashStatusItem)
117 withObject:self
118 afterDelay:0.2 * i];
119 }
120
121 - (NSMenu *)applicationDockMenu:(NSApplication *)sender {
122 return dockMenu;
123 }
124
125 - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
126 [self restoreToForeground:sender];
127 NSURL *url = [NSURL fileURLWithPath:filename];
128 [self.mappingsController addMappingWithContentsOfURL:url];
129 return YES;
130 }
131
132 - (void)mappingWasChosen:(NJMapping *)mapping {
133 [self.mappingsController activateMapping:mapping];
134 }
135
136 - (void)mappingListShouldOpen {
137 [self restoreToForeground:self];
138 [self.mappingsController mappingPressed:self];
139 }
140
141
142 @end