X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=blobdiff_plain;f=Classes%2FEnjoyableApplicationDelegate.m;h=25b9937216283f5820ea401ede9f6496690c6846;hp=7c9ae7e7964689ed8ff0d1656266376b905fae7e;hb=a58f23691951d4d1e1961dddd514a7e9f3748203;hpb=4a8e67a7294e8e527e2be9d8f5f39aae60691697 diff --git a/Classes/EnjoyableApplicationDelegate.m b/Classes/EnjoyableApplicationDelegate.m index 7c9ae7e..25b9937 100644 --- a/Classes/EnjoyableApplicationDelegate.m +++ b/Classes/EnjoyableApplicationDelegate.m @@ -10,17 +10,18 @@ #import "EnjoyableApplicationDelegate.h" #import "NJMapping.h" -#import "NJMappingsController.h" +#import "NJInput.h" #import "NJEvents.h" @implementation EnjoyableApplicationDelegate { NSStatusItem *statusItem; + NSMutableArray *_errors; } - (void)didSwitchApplication:(NSNotification *)note { NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey]; if (activeApp) - [self.mappingsController activateMappingForProcess:activeApp]; + [self.ic activateMappingForProcess:activeApp]; } - (void)applicationWillFinishLaunching:(NSNotification *)notification { @@ -40,16 +41,16 @@ name:NJEventSimulationStopped object:nil]; - [self.mappingsController load]; + [self.ic load]; [self.mvc.mappingList reloadData]; [self.mvc changedActiveMappingToIndex: - [self.mappingsController indexOfMapping: - self.mappingsController.currentMapping]]; + [self.ic indexOfMapping: + self.ic.currentMapping]]; statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:36]; statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; statusItem.highlightMode = YES; - statusItem.menu = statusItemMenu; + statusItem.menu = self.statusItemMenu; statusItem.target = self; } @@ -58,7 +59,7 @@ && NSRunningApplication.currentApplication.wasLaunchedAsLoginItemOrResume) [self transformIntoElement:nil]; else - [window makeKeyAndOrderFront:nil]; + [self.window makeKeyAndOrderFront:nil]; } - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication @@ -71,7 +72,7 @@ ProcessSerialNumber psn = { 0, kCurrentProcess }; TransformProcessType(&psn, kProcessTransformToForegroundApplication); [NSApplication.sharedApplication activateIgnoringOtherApps:YES]; - [window makeKeyAndOrderFront:sender]; + [self.window makeKeyAndOrderFront:sender]; [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(transformIntoElement:) object:self]; @@ -79,7 +80,7 @@ } - (void)applicationWillBecomeActive:(NSNotification *)notification { - if (window.isVisible) + if (self.window.isVisible) [self restoreToForeground:notification]; } @@ -109,7 +110,10 @@ } - (void)eventSimulationStarted:(NSNotification *)note { + self.simulatingEventsButton.state = NSOnState; statusItem.image = [NSImage imageNamed:@"Status Menu Icon"]; + [NSProcessInfo.processInfo + disableAutomaticTermination:@"Event simulation running."]; [NSWorkspace.sharedWorkspace.notificationCenter addObserver:self selector:@selector(didSwitchApplication:) @@ -118,7 +122,10 @@ } - (void)eventSimulationStopped:(NSNotification *)note { + self.simulatingEventsButton.state = NSOffState; statusItem.image = [NSImage imageNamed:@"Status Menu Icon Disabled"]; + [NSProcessInfo.processInfo + enableAutomaticTermination:@"Event simulation running."]; [NSWorkspace.sharedWorkspace.notificationCenter removeObserver:self name:NSWorkspaceDidActivateApplicationNotification @@ -129,15 +136,42 @@ NSUInteger idx = [note.userInfo[NJMappingIndexKey] intValue]; [self.mvc changedActiveMappingToIndex:idx]; - if (!window.isVisible) + if (!self.window.isVisible) for (int i = 0; i < 4; ++i) [self performSelector:@selector(flashStatusItem) withObject:self afterDelay:0.2 * i]; + [self loadOutputForInput:self.dvc.selectedHandler]; } - (NSMenu *)applicationDockMenu:(NSApplication *)sender { - return dockMenu; + return self.dockMenu; +} + +- (void)showNextError { + if (!self.window.attachedSheet && _errors.count) { + NSError *error = _errors.lastObject; + [_errors removeLastObject]; + [NSApplication.sharedApplication activateIgnoringOtherApps:YES]; + [self.window makeKeyAndOrderFront:nil]; + [self.window presentError:error + modalForWindow:self.window + delegate:self + didPresentSelector:@selector(didPresentErrorWithRecovery:contextInfo:) + contextInfo:nil]; + } +} + +- (void)didPresentErrorWithRecovery:(BOOL)didRecover + contextInfo:(void *)contextInfo { + [self showNextError]; +} + +- (void)presentErrorSheet:(NSError *)error { + if (!_errors) + _errors = [[NSMutableArray alloc] initWithCapacity:1]; + [_errors insertObject:error atIndex:0]; + [self showNextError]; } - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { @@ -146,24 +180,24 @@ NSURL *URL = [NSURL fileURLWithPath:filename]; NJMapping *mapping = [NJMapping mappingWithContentsOfURL:URL error:&error]; - if ([self.mappingsController[mapping.name] hasConflictWith:mapping]) { - [self promptForMapping:mapping atIndex:self.mappingsController.count]; - } else if (self.mappingsController[mapping.name]) { - [self.mappingsController[mapping.name] mergeEntriesFrom:mapping]; + if ([[self.ic mappingForKey:mapping.name] hasConflictWith:mapping]) { + [self promptForMapping:mapping atIndex:self.ic.mappings.count]; + } else if ([self.ic mappingForKey:mapping.name]) { + [[self.ic mappingForKey:mapping.name] mergeEntriesFrom:mapping]; } else if (mapping) { - [self.mappingsController addMapping:mapping]; + [self.mvc beginUpdates]; + [self.ic addMapping:mapping]; + [self.mvc addedMappingAtIndex:self.ic.mappings.count - 1 startEditing:NO]; + [self.mvc endUpdates]; + [self.ic activateMapping:mapping]; } else { - [window presentError:error - modalForWindow:window - delegate:nil - didPresentSelector:nil - contextInfo:nil]; + [self presentErrorSheet:error]; } return !!mapping; } - (void)mappingWasChosen:(NJMapping *)mapping { - [self.mappingsController activateMapping:mapping]; + [self.ic activateMapping:mapping]; } - (void)mappingListShouldOpen { @@ -171,51 +205,10 @@ [self.mvc mappingTriggerClicked: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; -} - - (void)importMappingClicked:(id)sender { NSOpenPanel *panel = [NSOpenPanel openPanel]; panel.allowedFileTypes = @[ @"enjoyable", @"json", @"txt" ]; - [panel beginSheetModalForWindow:window + [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) { if (result != NSFileHandlingPanelOKButton) return; @@ -223,18 +216,14 @@ NSError *error; NJMapping *mapping = [NJMapping mappingWithContentsOfURL:panel.URL error:&error]; - if ([self.mappingsController[mapping.name] hasConflictWith:mapping]) { - [self promptForMapping:mapping atIndex:self.mappingsController.count]; - } else if (self.mappingsController[mapping.name]) { - [self.mappingsController[mapping.name] mergeEntriesFrom:mapping]; + if ([[self.ic mappingForKey:mapping.name] hasConflictWith:mapping]) { + [self promptForMapping:mapping atIndex:self.ic.mappings.count]; + } else if ([self.ic mappingForKey:mapping.name]) { + [[self.ic mappingForKey:mapping.name] mergeEntriesFrom:mapping]; } else if (mapping) { - [self.mappingsController addMapping:mapping]; + [self.ic addMapping:mapping]; } else { - [window presentError:error - modalForWindow:window - delegate:nil - didPresentSelector:nil - contextInfo:nil]; + [self presentErrorSheet:error]; } }]; @@ -243,20 +232,16 @@ - (void)exportMappingClicked:(id)sender { NSSavePanel *panel = [NSSavePanel savePanel]; panel.allowedFileTypes = @[ @"enjoyable" ]; - NJMapping *mapping = self.mappingsController.currentMapping; + NJMapping *mapping = self.ic.currentMapping; panel.nameFieldStringValue = [mapping.name stringByFixingPathComponent]; - [panel beginSheetModalForWindow:window + [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) { if (result != NSFileHandlingPanelOKButton) return; [panel close]; NSError *error; if (![mapping writeToURL:panel.URL error:&error]) { - [window presentError:error - modalForWindow:window - delegate:nil - didPresentSelector:nil - contextInfo:nil]; + [self presentErrorSheet:error]; } }]; } @@ -271,15 +256,15 @@ [alert.window orderOut:nil]; switch (returnCode) { case NSAlertFirstButtonReturn: // Merge - [self.mappingsController mergeMapping:newMapping intoMapping:oldMapping]; - [self.mappingsController activateMapping:oldMapping]; + [self.ic mergeMapping:newMapping intoMapping:oldMapping]; + [self.ic activateMapping:oldMapping]; break; case NSAlertThirdButtonReturn: // New Mapping [self.mvc beginUpdates]; - [self.mappingsController addMapping:newMapping]; + [self.ic addMapping:newMapping]; [self.mvc addedMappingAtIndex:idx startEditing:YES]; [self.mvc endUpdates]; - [self.mappingsController activateMapping:newMapping]; + [self.ic activateMapping:newMapping]; break; default: // Cancel, other. break; @@ -287,7 +272,7 @@ } - (void)promptForMapping:(NJMapping *)mapping atIndex:(NSInteger)idx { - NJMapping *mergeInto = self.mappingsController[mapping.name]; + NJMapping *mergeInto = [self.ic mappingForKey:mapping.name]; NSAlert *conflictAlert = [[NSAlert alloc] init]; conflictAlert.messageText = NSLocalizedString(@"import conflict prompt", @"Title of import conflict alert"); conflictAlert.informativeText = @@ -296,7 +281,7 @@ [conflictAlert addButtonWithTitle:NSLocalizedString(@"import and merge", @"button to merge imported mappings")]; [conflictAlert addButtonWithTitle:NSLocalizedString(@"cancel import", @"button to cancel import")]; [conflictAlert addButtonWithTitle:NSLocalizedString(@"import new mapping", @"button to import as new mapping")]; - [conflictAlert beginSheetModalForWindow:window + [conflictAlert beginSheetModalForWindow:self.window modalDelegate:self didEndSelector:@selector(mappingConflictDidResolve:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(@{ @"index": @(idx), @@ -305,26 +290,25 @@ } - (NSInteger)numberOfMappings:(NJMappingsViewController *)mvc { - return self.mappingsController.count; + return self.ic.mappings.count; } - (NJMapping *)mappingsViewController:(NJMappingsViewController *)mvc mappingForIndex:(NSUInteger)idx { - return self.mappingsController[idx]; + return self.ic.mappings[idx]; } - (void)mappingsViewController:(NJMappingsViewController *)mvc renameMappingAtIndex:(NSInteger)index toName:(NSString *)name { - [self.mappingsController renameMapping:self.mappingsController[index] - to:name]; + [self.ic renameMapping:self.ic.mappings[index] to:name]; } - (BOOL)mappingsViewController:(NJMappingsViewController *)mvc canMoveMappingFromIndex:(NSInteger)fromIdx toIndex:(NSInteger)toIdx { return fromIdx != toIdx && fromIdx != 0 && toIdx != 0 - && toIdx < (NSInteger)self.mappingsController.count; + && toIdx < (NSInteger)self.ic.mappings.count; } - (void)mappingsViewController:(NJMappingsViewController *)mvc @@ -332,7 +316,7 @@ toIndex:(NSInteger)toIdx { [mvc beginUpdates]; [mvc.mappingList moveRowAtIndex:fromIdx toIndex:toIdx]; - [self.mappingsController moveMoveMappingFromIndex:fromIdx toIndex:toIdx]; + [self.ic moveMoveMappingFromIndex:fromIdx toIndex:toIdx]; [mvc endUpdates]; } @@ -345,7 +329,7 @@ removeMappingAtIndex:(NSInteger)idx { [mvc beginUpdates]; [mvc removedMappingAtIndex:idx]; - [self.mappingsController removeMappingAtIndex:idx]; + [self.ic removeMappingAtIndex:idx]; [mvc endUpdates]; } @@ -355,12 +339,15 @@ error:(NSError **)error { NJMapping *mapping = [NJMapping mappingWithContentsOfURL:url error:error]; - if ([self.mappingsController[mapping.name] hasConflictWith:mapping]) { + if ([[self.ic mappingForKey:mapping.name] hasConflictWith:mapping]) { [self promptForMapping:mapping atIndex:index]; - } else if (self.mappingsController[mapping.name]) { - [self.mappingsController[mapping.name] mergeEntriesFrom:mapping]; + } else if ([self.ic mappingForKey:mapping.name]) { + [[self.ic mappingForKey:mapping.name] mergeEntriesFrom:mapping]; } else if (mapping) { - [self.mappingsController insertMapping:mapping atIndex:index]; + [self.mvc beginUpdates]; + [self.mvc addedMappingAtIndex:index startEditing:NO]; + [self.ic insertMapping:mapping atIndex:index]; + [self.mvc endUpdates]; } return !!mapping; } @@ -368,15 +355,97 @@ - (void)mappingsViewController:(NJMappingsViewController *)mvc addMapping:(NJMapping *)mapping { [mvc beginUpdates]; - [mvc addedMappingAtIndex:self.mappingsController.count startEditing:YES]; - [self.mappingsController addMapping:mapping]; + [mvc addedMappingAtIndex:self.ic.mappings.count startEditing:YES]; + [self.ic addMapping:mapping]; [mvc endUpdates]; - [self.mappingsController activateMapping:mapping]; + [self.ic activateMapping:mapping]; } - (void)mappingsViewController:(NJMappingsViewController *)mvc choseMappingAtIndex:(NSInteger)idx { - [self.mappingsController activateMapping:self.mappingsController[idx]]; + [self.ic activateMapping:self.ic.mappings[idx]]; +} + +- (id)deviceViewController:(NJDeviceViewController *)dvc + elementForUID:(NSString *)uid { + return [self.ic elementForUID:uid]; +} + +- (void)loadOutputForInput:(NJInput *)input { + NJOutput *output = self.ic.currentMapping[input]; + [self.oc loadOutput:output forInput:input]; +} + +- (void)deviceViewControllerDidSelectNothing:(NJDeviceViewController *)dvc { + [self loadOutputForInput:nil]; +} + +- (void)deviceViewController:(NJDeviceViewController *)dvc + didSelectBranch:(NJInputPathElement *)handler { + [self loadOutputForInput:dvc.selectedHandler]; +} + +- (void)deviceViewController:(NJDeviceViewController *)dvc + didSelectHandler:(NJInputPathElement *)handler { + [self loadOutputForInput:dvc.selectedHandler]; +} + +- (void)deviceViewController:(NJDeviceViewController *)dvc + didSelectDevice:(NJInputPathElement *)device { + [self loadOutputForInput:dvc.selectedHandler]; +} + +- (void)inputController:(NJInputController *)ic + didAddDevice:(NJDevice *)device { + [self.dvc addedDevice:device atIndex:ic.devices.count - 1]; +} + +- (void)inputController:(NJInputController *)ic + didRemoveDeviceAtIndex:(NSInteger)idx { + [self.dvc removedDeviceAtIndex:idx]; +} + +- (void)inputControllerDidStartHID:(NJInputController *)ic { + [self.dvc hidStarted]; +} + +- (void)inputControllerDidStopHID:(NJInputController *)ic { + [self.dvc hidStopped]; +} + +- (void)inputController:(NJInputController *)ic didInput:(NJInput *)input { + [self.dvc expandAndSelectItem:input]; + [self loadOutputForInput:input]; + [self.oc focusKey]; +} + +- (void)inputController:(NJInputController *)ic didError:(NSError *)error { + [self presentErrorSheet:error]; +} + +- (NSInteger)numberOfDevicesInDeviceList:(NJDeviceViewController *)dvc { + return self.ic.devices.count; +} + +- (NJDevice *)deviceViewController:(NJDeviceViewController *)dvc + deviceForIndex:(NSUInteger)idx { + return self.ic.devices[idx]; +} + +- (IBAction)simulatingEventsChanged:(NSButton *)sender { + self.ic.simulatingEvents = sender.state == NSOnState; +} + +- (void)outputViewController:(NJOutputViewController *)ovc + setOutput:(NJOutput *)output + forInput:(NJInput *)input { + self.ic.currentMapping[input] = output; + [self.ic save]; +} + +- (NJMapping *)outputViewController:(NJOutputViewController *)ovc + mappingForIndex:(NSUInteger)index { + return self.ic.mappings[index]; } @end