Second try at login items. If the user hides Enjoyable in the status bar, explain...
[enjoyable.git] / Classes / EnjoyableApplicationDelegate.m
index 634f362..b1d9907 100644 (file)
@@ -5,12 +5,12 @@
 //  Created by Sam McCall on 4/05/09.
 //
 
+#import <Sparkle/Sparkle.h>
+
 #import "EnjoyableApplicationDelegate.h"
 
 #import "NJMapping.h"
 #import "NJMappingsController.h"
-#import "NJDeviceController.h"
-#import "NJOutputController.h"
 #import "NJEvents.h"
 
 @implementation EnjoyableApplicationDelegate {
         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:)
 }
 
 - (void)applicationDidFinishLaunching:(NSNotification *)notification {
-    [window makeKeyAndOrderFront:nil];
+    if ([NSUserDefaults.standardUserDefaults boolForKey:@"hidden in status item"]
+        && NSRunningApplication.currentApplication.wasLaunchedAsLoginItemOrResume)
+        [self transformIntoElement:nil];
+    else
+        [window makeKeyAndOrderFront:nil];
 }
 
 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication
     [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 {
 }
 
 - (void)eventTranslationActivated:(NSNotification *)note {
-    [dockMenu itemAtIndex:0].state = NSOnState;
-    [statusItemMenu itemAtIndex:0].state = NSOnState;
     statusItem.image = [NSImage imageNamed:@"Status Menu Icon"];
     [NSWorkspace.sharedWorkspace.notificationCenter
         addObserver:self
 }
 
 - (void)eventTranslationDeactivated:(NSNotification *)note {
-    [dockMenu itemAtIndex:0].state = NSOffState;
-    [statusItemMenu itemAtIndex:0].state = NSOffState;
     statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"];
     [NSWorkspace.sharedWorkspace.notificationCenter
         removeObserver:self
         object:nil];
 }
 
-- (void)restoreWindowAndShowMappings:(id)sender {
-    [self restoreToForeground:sender];
-    [self.mappingsController mappingPressed:sender];
-}
-
-- (void)addMappingsToMenu:(NSMenu *)menu withKeys:(BOOL)withKeys atIndex:(NSInteger)index {
-    static const NSUInteger MAXIMUM_ITEMS = 15;
-    int added = 0;
-    for (NJMapping *mapping in self.mappingsController) {
-        NSString *keyEquiv = (++added < 10 && withKeys) ? @(added).stringValue : @"";
-        NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name
-                                                      action:@selector(chooseMapping:)
-                                               keyEquivalent:keyEquiv];
-        item.representedObject = mapping;
-        item.state = mapping == self.mappingsController.currentMapping;
-        [menu insertItem:item atIndex:index++];
-        if (added == MAXIMUM_ITEMS && self.mappingsController.mappings.count > MAXIMUM_ITEMS + 1) {
-            NSString *msg = [NSString stringWithFormat:@"(and %lu moreā€¦)",
-                             self.mappingsController.mappings.count - MAXIMUM_ITEMS];
-            NSMenuItem *end = [[NSMenuItem alloc] initWithTitle:msg
-                                                         action:@selector(restoreWindowAndShowMappings:)
-                                                  keyEquivalent:@""];
-            // There must be a represented object here so the item gets
-            // removed correctly when the menus are regenerated.
-            end.representedObject = self.mappingsController.mappings;
-            end.target = self;
-            [menu insertItem:end atIndex:index++];
-            break;
-        }
-    }    
-}
-
-- (void)mappingListDidChange:(NSNotification *)note {
-    while (mappingsMenu.lastItem.representedObject)
-        [mappingsMenu removeLastItem];
-    [self addMappingsToMenu:mappingsMenu withKeys:YES atIndex:mappingsMenu.numberOfItems];
-    while ([statusItemMenu itemAtIndex:2].representedObject)
-        [statusItemMenu removeItemAtIndex:2];
-    [self addMappingsToMenu:statusItemMenu withKeys:NO atIndex:2];
-}
-
 - (void)mappingDidChange:(NSNotification *)note {
-    NJMapping *current = note.object;
-    for (NSMenuItem *item in mappingsMenu.itemArray)
-        if (item.representedObject)
-            item.state = item.representedObject == current;
-    for (NSMenuItem *item in statusItemMenu.itemArray)
-        if (item.representedObject)
-            item.state = item.representedObject == current;
-    
     if (!window.isVisible)
         for (int i = 0; i < 4; ++i)
             [self performSelector:@selector(flashStatusItem)
                        afterDelay:0.2 * i];
 }
 
-- (void)chooseMapping:(NSMenuItem *)sender {
-    NJMapping *chosen = sender.representedObject;
-    [self.mappingsController activateMapping:chosen];
-}
-
 - (NSMenu *)applicationDockMenu:(NSApplication *)sender {
-    while (dockMenu.lastItem.representedObject)
-        [dockMenu removeLastItem];
-    [self addMappingsToMenu:dockMenu withKeys:NO atIndex:dockMenu.numberOfItems];
     return dockMenu;
 }
 
     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