#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 {
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"];
- (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:)
- (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
[self performSelector:@selector(flashStatusItem)
withObject:self
afterDelay:0.2 * i];
+ [self loadOutputForInput:self.dvc.selectedHandler];
}
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
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 {
[self restoreToForeground:sender];
NSError *error;
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.mvc beginUpdates];
- [self.mappingsController addMapping:mapping];
- [self.mvc addedMappingAtIndex:self.mappingsController.count - 1 startEditing:NO];
+ [self.ic addMapping:mapping];
+ [self.mvc addedMappingAtIndex:self.ic.mappings.count - 1 startEditing:NO];
[self.mvc endUpdates];
- [self.mappingsController activateMapping:mapping];
+ [self.ic activateMapping:mapping];
} else {
- [self.window presentError:error
- modalForWindow:self.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 {
[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"];
- [self.window performClose:sheet];
-}
-
-- (BOOL)windowShouldClose:(NSWindow *)sender {
- if (sender != self.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, self.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" ];
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 {
- [self.window presentError:error
- modalForWindow:self.window
- delegate:nil
- didPresentSelector:nil
- contextInfo:nil];
+ [self presentErrorSheet:error];
}
}];
- (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:self.window
completionHandler:^(NSInteger result) {
[panel close];
NSError *error;
if (![mapping writeToURL:panel.URL error:&error]) {
- [self.window presentError:error
- modalForWindow:self.window
- delegate:nil
- didPresentSelector:nil
- contextInfo:nil];
+ [self presentErrorSheet:error];
}
}];
}
[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;
}
- (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 =
}
- (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
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];
}
removeMappingAtIndex:(NSInteger)idx {
[mvc beginUpdates];
[mvc removedMappingAtIndex:idx];
- [self.mappingsController removeMappingAtIndex:idx];
+ [self.ic removeMappingAtIndex:idx];
[mvc endUpdates];
}
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.mvc beginUpdates];
[self.mvc addedMappingAtIndex:index startEditing:NO];
- [self.mappingsController insertMapping:mapping atIndex:index];
+ [self.ic insertMapping:mapping atIndex:index];
[self.mvc endUpdates];
}
return !!mapping;
- (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.deviceController[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.outputController loadInput:dvc.selectedHandler];
+ [self loadOutputForInput:nil];
}
- (void)deviceViewController:(NJDeviceViewController *)dvc
didSelectBranch:(NJInputPathElement *)handler {
- [self.outputController loadInput:dvc.selectedHandler];
+ [self loadOutputForInput:dvc.selectedHandler];
}
- (void)deviceViewController:(NJDeviceViewController *)dvc
didSelectHandler:(NJInputPathElement *)handler {
- [self.outputController loadInput:dvc.selectedHandler];
+ [self loadOutputForInput:dvc.selectedHandler];
}
- (void)deviceViewController:(NJDeviceViewController *)dvc
didSelectDevice:(NJInputPathElement *)device {
- [self.outputController loadInput:dvc.selectedHandler];
+ [self loadOutputForInput:dvc.selectedHandler];
}
-- (void)deviceController:(NJDeviceController *)dc
- didAddDevice:(NJDevice *)device {
- [self.dvc addedDevice:device atIndex:dc.count - 1];
+- (void)inputController:(NJInputController *)ic
+ didAddDevice:(NJDevice *)device {
+ [self.dvc addedDevice:device atIndex:ic.devices.count - 1];
}
-- (void)deviceController:(NJDeviceController *)dc
- didRemoveDeviceAtIndex:(NSInteger)idx {
+- (void)inputController:(NJInputController *)ic
+ didRemoveDeviceAtIndex:(NSInteger)idx {
[self.dvc removedDeviceAtIndex:idx];
}
-- (void)deviceControllerDidStartHID:(NJDeviceController *)dc {
+- (void)inputControllerDidStartHID:(NJInputController *)ic {
[self.dvc hidStarted];
}
-- (void)deviceControllerDidStopHID:(NJDeviceController *)dc {
+- (void)inputControllerDidStopHID:(NJInputController *)ic {
[self.dvc hidStopped];
}
-- (void)deviceController:(NJDeviceController *)dc didInput:(NJInput *)input {
- [self.outputController loadInput:input];
- [self.outputController focusKey];
+- (void)inputController:(NJInputController *)ic didInput:(NJInput *)input {
+ [self.dvc expandAndSelectItem:input];
+ [self loadOutputForInput:input];
+ [self.oc focusKey];
}
-- (void)deviceController:(NJDeviceController *)dc didError:(NSError *)error {
- // Since the error shows the window, it can trigger another attempt
- // to re-open the HID manager, which will also probably fail and error,
- // so don't bother repeating ourselves.
- if (!self.window.attachedSheet) {
- [NSApplication.sharedApplication activateIgnoringOtherApps:YES];
- [self.window makeKeyAndOrderFront:nil];
- [self.window presentError:error
- modalForWindow:self.window
- delegate:nil
- didPresentSelector:nil
- contextInfo:nil];
- }
+- (void)inputController:(NJInputController *)ic didError:(NSError *)error {
+ [self presentErrorSheet:error];
}
- (NSInteger)numberOfDevicesInDeviceList:(NJDeviceViewController *)dvc {
- return self.deviceController.count;
+ return self.ic.devices.count;
}
- (NJDevice *)deviceViewController:(NJDeviceViewController *)dvc
deviceForIndex:(NSUInteger)idx {
- return self.deviceController[idx];
+ return self.ic.devices[idx];
}
- (IBAction)simulatingEventsChanged:(NSButton *)sender {
- self.deviceController.simulatingEvents = sender.state == NSOnState;
+ 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