Big rename part 2: 'config' etc. to 'mapping.
authorJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 3 Mar 2013 21:08:41 +0000 (22:08 +0100)
committerJoe Wreschnig <joe.wreschnig@gmail.com>
Sun, 3 Mar 2013 21:08:41 +0000 (22:08 +0100)
25 files changed:
ApplicationController.h
ApplicationController.m
Config.h [deleted file]
Config.m [deleted file]
ConfigsController.h [deleted file]
ConfigsController.m [deleted file]
English.lproj/MainMenu.xib
Enjoyable.xcodeproj/project.pbxproj
NJInputController.h
NJInputController.m
NJMapping.h [new file with mode: 0644]
NJMapping.m [new file with mode: 0644]
NJMappingsController.h [new file with mode: 0644]
NJMappingsController.m [new file with mode: 0644]
Target.h
Target.m
TargetConfig.h
TargetConfig.m
TargetController.h
TargetController.m
TargetKeyboard.m
TargetMouseBtn.m
TargetMouseMove.m
TargetMouseScroll.m
TargetToggleMouseScope.m

index 5dae0cc04a525b0d295aba4ec8bc3d36c90b8f23..88c9d29c72fc7b5c3d5607ba92740ee2713b5b36 100644 (file)
@@ -8,7 +8,7 @@
 
 @class NJInputController;
 @class TargetController;
-@class ConfigsController;
+@class NJMappingsController;
 
 @interface ApplicationController : NSObject <NSApplicationDelegate> {
     IBOutlet NSDrawer *drawer;
     IBOutlet NSMenu *dockMenuBase;
 }
 
-@property (nonatomic, strong) IBOutlet NJInputController *jsController;
+@property (nonatomic, strong) IBOutlet NJInputController *inputController;
 @property (nonatomic, strong) IBOutlet TargetController *targetController;
-@property (nonatomic, strong) IBOutlet ConfigsController *configsController;
+@property (nonatomic, strong) IBOutlet NJMappingsController *mappingsController;
 
 - (IBAction)toggleActivity:(id)sender;
-- (void)configsChanged;
+- (void)mappingsChanged;
 
 @end
index bc7c4689c3fb75d6eb942123a58f17f723400a7c..50f9c295393fdf64e03269820b910c064155a431 100644 (file)
@@ -7,8 +7,8 @@
 
 #import "ApplicationController.h"
 
-#import "Config.h"
-#import "ConfigsController.h"
+#import "NJMapping.h"
+#import "NJMappingsController.h"
 #import "NJInputController.h"
 #import "TargetController.h"
 #import "NJEvents.h"
 
 - (void)didSwitchApplication:(NSNotification *)notification {
     NSRunningApplication *currentApp = notification.userInfo[NSWorkspaceApplicationKey];
-    [self.configsController activateConfigForProcess:currentApp.localizedName];
+    [self.mappingsController activateMappingForProcess:currentApp.localizedName];
 }
 
 - (void)applicationDidFinishLaunching:(NSNotification *)notification {
     [drawer open];
     self.targetController.enabled = NO;
-    [self.jsController setup];
-    [self.configsController load];
+    [self.inputController setup];
+    [self.mappingsController load];
     [NSNotificationCenter.defaultCenter
      addObserver:self
      selector:@selector(mappingDidChange:)
 }
 
 - (IBAction)toggleActivity:(id)sender {
-    self.jsController.translatingEvents = !self.jsController.translatingEvents;
+    self.inputController.translatingEvents = !self.inputController.translatingEvents;
 }
 
-- (NSInteger)firstConfigMenuIndex {
+- (NSInteger)firstMappingMenuIndex {
     for (NSInteger i = dockMenuBase.numberOfItems - 1; i >= 0; --i)
         if ([dockMenuBase itemAtIndex:i].isSeparatorItem)
             return i + 1;
     return dockMenuBase.numberOfItems;
 }
 
-- (void)configsChanged {
-    NSInteger removeFrom = self.firstConfigMenuIndex;
+- (void)mappingsChanged {
+    NSInteger removeFrom = self.firstMappingMenuIndex;
     while (dockMenuBase.numberOfItems > removeFrom)
         [dockMenuBase removeItemAtIndex:dockMenuBase.numberOfItems - 1];
     int added = 0;
-    for (Config *config in self.configsController.configs) {
+    for (NJMapping *mapping in self.mappingsController.mappings) {
         NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @"";
-        [dockMenuBase addItemWithTitle:config.name
-                                action:@selector(chooseConfig:)
+        [dockMenuBase addItemWithTitle:mapping.name
+                                action:@selector(chooseMapping:)
                          keyEquivalent:keyEquiv];
         
     }
-    [_targetController refreshConfigs];
+    [_targetController refreshMappings];
 }
 
 - (void)mappingDidChange:(NSNotification *)note {
-    NSInteger firstConfig = self.firstConfigMenuIndex;
-    Config *current = note.object;
-    NSArray *configs = self.configsController.configs;
-    for (NSUInteger i = 0; i < configs.count; ++i)
-        [dockMenuBase itemAtIndex:i + firstConfig].state = configs[i] == current;
+    NSInteger firstMapping = self.firstMappingMenuIndex;
+    NJMapping *current = note.object;
+    NSArray *mappings = self.mappingsController.mappings;
+    for (NSUInteger i = 0; i < mappings.count; ++i)
+        [dockMenuBase itemAtIndex:i + firstMapping].state = mappings[i] == current;
 }
 
-- (void)chooseConfig:(id)sender {
-    NSInteger idx = [dockMenuBase indexOfItem:sender] - self.firstConfigMenuIndex;
-    Config *chosen = self.configsController.configs[idx];
-    [_configsController activateConfig:chosen];
+- (void)chooseMapping:(id)sender {
+    NSInteger idx = [dockMenuBase indexOfItem:sender] - self.firstMappingMenuIndex;
+    NJMapping *chosen = self.mappingsController.mappings[idx];
+    [_mappingsController activateMapping:chosen];
 }
 @end
diff --git a/Config.h b/Config.h
deleted file mode 100644 (file)
index a2ba55b..0000000
--- a/Config.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-//  Config.h
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//  Copyright 2009 University of Otago. All rights reserved.
-//
-
-@class Target;
-@class NJInput;
-
-@interface Config : NSObject
-
-@property (nonatomic, copy) NSString *name;
-@property (nonatomic, readonly) NSMutableDictionary *entries;
-
-- (id)initWithName:(NSString *)name;
-- (Target *)objectForKeyedSubscript:(NJInput *)input;
-- (void)setObject:(Target *)target forKeyedSubscript:(NJInput *)input;
-- (NSDictionary *)serialize;
-
-@end
diff --git a/Config.m b/Config.m
deleted file mode 100644 (file)
index 1897734..0000000
--- a/Config.m
+++ /dev/null
@@ -1,45 +0,0 @@
-//
-//  Config.m
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//
-
-#import "Config.h"
-
-#import "NJInput.h"
-
-@implementation Config
-
-- (id)initWithName:(NSString *)name {
-    if ((self = [super init])) {
-        self.name = name ? name : @"Untitled";
-        _entries = [[NSMutableDictionary alloc] init];
-    }
-    return self;
-}
-
-- (Target *)objectForKeyedSubscript:(NJInput *)input {
-    return input ? _entries[input.uid] : nil;
-}
-
-- (void)setObject:(Target *)target forKeyedSubscript:(NJInput *)input {
-    if (input) {
-        if (target)
-            _entries[input.uid] = target;
-        else
-            [_entries removeObjectForKey:input.uid];
-    }
-}
-
-- (NSDictionary *)serialize {
-    NSMutableDictionary* cfgEntries = [[NSMutableDictionary alloc] initWithCapacity:_entries.count];
-    for (id key in _entries) {
-        id serialized = [_entries[key] serialize];
-        if (serialized)
-            cfgEntries[key] = serialized;
-    }
-    return @{ @"name": _name, @"entries": cfgEntries };
-}
-
-@end
diff --git a/ConfigsController.h b/ConfigsController.h
deleted file mode 100644 (file)
index 71fce23..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-//  ConfigsController.h
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//  Copyright 2009 University of Otago. All rights reserved.
-//
-
-@class Config;
-@class TargetController;
-
-@interface ConfigsController : NSObject <NSTableViewDataSource, NSTableViewDelegate, NSOpenSavePanelDelegate> {
-    IBOutlet NSButton *removeButton;
-    IBOutlet NSTableView *tableView;
-    IBOutlet TargetController *targetController;
-}
-
-@property (nonatomic, readonly) Config *currentConfig;
-@property (nonatomic, readonly) NSArray *configs;
-
-- (Config *)objectForKeyedSubscript:(NSString *)name;
-
-
-- (IBAction)addPressed:(id)sender;
-- (IBAction)removePressed:(id)sender;
-- (IBAction)importPressed:(id)sender;
-- (IBAction)exportPressed:(id)sender;
-- (void)activateConfig:(Config *)config;
-- (void)activateConfigForProcess:(NSString *)processName;
-
-- (void)save;
-- (void)load;
-
-@end
diff --git a/ConfigsController.m b/ConfigsController.m
deleted file mode 100644 (file)
index fb50bc3..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-//
-//  ConfigsController.m
-//  Enjoy
-//
-//  Created by Sam McCall on 4/05/09.
-//
-
-#import "ConfigsController.h"
-
-#import "ApplicationController.h"
-#import "Config.h"
-#import "ConfigsController.h"
-#import "Target.h"
-#import "TargetController.h"
-#import "NJEvents.h"
-
-@implementation ConfigsController {
-    NSMutableArray *_configs;
-    Config *manualConfig;
-}
-
-- (id)init {
-    if ((self = [super init])) {
-        _configs = [[NSMutableArray alloc] init];
-        _currentConfig = [[Config alloc] initWithName:@"(default)"];
-        manualConfig = _currentConfig;
-        [_configs addObject:_currentConfig];
-    }
-    return self;
-}
-
-- (Config *)objectForKeyedSubscript:(NSString *)name {
-    for (Config *config in _configs)
-        if ([name isEqualToString:config.name])
-            return config;
-    return nil;
-}
-
-- (void)activateConfigForProcess:(NSString *)processName {
-    Config *oldConfig = manualConfig;
-    Config *newConfig = self[processName];
-    if (!newConfig)
-        newConfig = oldConfig;
-    if (newConfig != _currentConfig)
-        [self activateConfig:newConfig];
-    manualConfig = oldConfig;
-}
-
-- (void)activateConfig:(Config *)config {
-    if (!config)
-        config = manualConfig;
-    NSLog(@"Switching to mapping %@.", config.name);
-    manualConfig = config;
-    _currentConfig = config;
-    [removeButton setEnabled:_configs[0] != config];
-    [targetController loadCurrent];
-    [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged
-                                                      object:_currentConfig];
-    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:[_configs indexOfObject:config]] byExtendingSelection:NO];
-}
-
-- (IBAction)addPressed:(id)sender {
-    Config *newConfig = [[Config alloc] initWithName:@"Untitled"];
-    [_configs addObject:newConfig];
-    [(ApplicationController *)NSApplication.sharedApplication.delegate configsChanged];
-    [tableView reloadData];
-    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_configs.count - 1] byExtendingSelection:NO];
-    [tableView editColumn:0 row:_configs.count - 1 withEvent:nil select:YES];
-    [self activateConfig:newConfig];
-}
-
-- (IBAction)removePressed:(id)sender {
-    if (tableView.selectedRow == 0)
-        return;
-    
-    [_configs removeObjectAtIndex:tableView.selectedRow];
-    [tableView reloadData];
-    [(ApplicationController *)NSApplication.sharedApplication.delegate configsChanged];
-    [self activateConfig:_configs[0]];
-    [self save];
-}
-
--(void)tableViewSelectionDidChange:(NSNotification *)notify {
-    if (tableView.selectedRow >= 0)
-        [self activateConfig:_configs[tableView.selectedRow]];
-}
-
-- (id)tableView:(NSTableView *)view objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)index {
-    return [_configs[index] name];
-}
-
-- (void)tableView:(NSTableView *)view setObjectValue:(NSString *)obj forTableColumn:(NSTableColumn *)col row:(NSInteger)index {
-    [(Config *)_configs[index] setName:obj];
-    [tableView reloadData];
-    [(ApplicationController *)NSApplication.sharedApplication.delegate configsChanged];
-}
-
-- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
-    return _configs.count;
-}
-
-- (BOOL)tableView:(NSTableView *)view shouldEditTableColumn:(NSTableColumn *)column row:(NSInteger)index {
-    return index > 0;
-}
-
-- (void)save {
-    NSLog(@"Saving mappings to defaults.");
-    [NSUserDefaults.standardUserDefaults setObject:[self dumpAll] forKey:@"configurations"];
-}
-
-- (void)load {
-    [self loadAllFrom:[NSUserDefaults.standardUserDefaults objectForKey:@"configurations"]];
-}
-
-- (NSDictionary *)dumpAll {
-    NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_configs.count];
-    for (Config *config in _configs)
-        [ary addObject:[config serialize]];
-    NSUInteger current = _currentConfig ? [_configs indexOfObject:_currentConfig] : 0;
-    return @{ @"configurations": ary, @"selected": @(current) };
-}
-
-- (void)loadAllFrom:(NSDictionary*) envelope{
-    NSArray *storedConfigs = envelope[@"configurations"];
-    NSMutableArray* newConfigs = [[NSMutableArray alloc] initWithCapacity:storedConfigs.count];
-
-    // have to do two passes in case config1 refers to config2 via a TargetConfig
-    for (NSDictionary *storedConfig in storedConfigs) {
-        Config *cfg = [[Config alloc] initWithName:storedConfig[@"name"]];
-        [newConfigs addObject:cfg];
-    }
-
-    for (unsigned i = 0; i < storedConfigs.count; ++i) {
-        NSDictionary *entries = storedConfigs[i][@"entries"];
-        Config *config = newConfigs[i];
-        for (id key in entries) {
-            Target *target = [Target targetDeserialize:entries[key]
-                                            withConfigs:newConfigs];
-            if (target)
-                config.entries[key] = target;
-        }
-    }
-    
-    if (newConfigs.count) {
-        unsigned current = [envelope[@"selected"] unsignedIntValue];
-        if (current >= newConfigs.count)
-            current = 0;
-        _configs = newConfigs;
-        [tableView reloadData];
-        [(ApplicationController *)NSApplication.sharedApplication.delegate configsChanged];
-        [self activateConfig:_configs[current]];
-    }
-}
-
-- (Config *)configWithURL:(NSURL *)url error:(NSError **)error {
-    NSInputStream *stream = [NSInputStream inputStreamWithURL:url];
-    [stream open];
-    NSDictionary *serialization = !*error
-        ? [NSJSONSerialization JSONObjectWithStream:stream options:0 error:error]
-        : nil;
-    [stream close];
-    
-    if (!([serialization isKindOfClass:NSDictionary.class]
-          && [serialization[@"name"] isKindOfClass:NSString.class]
-          && [serialization[@"entries"] isKindOfClass:NSDictionary.class])) {
-        *error = [NSError errorWithDomain:@"Enjoyable"
-                                     code:0
-                              description:@"This isn't a valid mapping file."];
-        return nil;
-    }
-
-    NSDictionary *entries = serialization[@"entries"];
-    Config *cfg = [[Config alloc] initWithName:serialization[@"name"]];
-    for (id key in entries) {
-        NSDictionary *value = entries[key];
-        if ([key isKindOfClass:NSString.class]) {
-            Target *target = [Target targetDeserialize:value
-                                           withConfigs:_configs];
-            if (target)
-                cfg.entries[key] = target;
-        }
-    }
-    return cfg;
-}
-
-- (void)importPressed:(id)sender {
-    NSOpenPanel *panel = [NSOpenPanel openPanel];
-    panel.allowedFileTypes = @[ @"enjoyable", @"json", @"txt" ];
-    NSWindow *window = NSApplication.sharedApplication.keyWindow;
-    [panel beginSheetModalForWindow:window
-                  completionHandler:^(NSInteger result) {
-                      if (result != NSFileHandlingPanelOKButton)
-                          return;
-
-                      [panel close];
-                      NSError *error;
-                      Config *cfg = [self configWithURL:panel.URL error:&error];
-                      
-                      if (!error) {
-                          BOOL conflict;
-                          Config *mergeInto = self[cfg.name];
-                          for (id key in cfg.entries) {
-                              if (mergeInto.entries[key]
-                                  && ![mergeInto.entries[key] isEqual:cfg.entries[key]]) {
-                                  conflict = YES;
-                                  break;
-                              }
-                          }
-                          
-                          if (conflict) {
-                              NSAlert *conflictAlert = [[NSAlert alloc] init];
-                              conflictAlert.messageText = @"Replace existing mappings?";
-                              conflictAlert.informativeText =
-                                  [NSString stringWithFormat:
-                                   @"This file contains inputs you've already mapped in \"%@\". Do you "
-                                   @"want to merge them and replace your existing mappings, or import this "
-                                   @"as a separate mapping?", cfg.name];
-                              [conflictAlert addButtonWithTitle:@"Merge"];
-                              [conflictAlert addButtonWithTitle:@"Cancel"];
-                              [conflictAlert addButtonWithTitle:@"New Mapping"];
-                              NSInteger res = [conflictAlert runModal];
-                              if (res == NSAlertSecondButtonReturn)
-                                  return;
-                              else if (res == NSAlertThirdButtonReturn)
-                                  mergeInto = nil;
-                          }
-                          
-                          if (mergeInto) {
-                              [mergeInto.entries addEntriesFromDictionary:cfg.entries];
-                              cfg = mergeInto;
-                          } else {
-                              [_configs addObject:cfg];
-                              [tableView reloadData];
-                          }
-                          
-                          [self save];
-                          [(ApplicationController *)NSApplication.sharedApplication.delegate configsChanged];
-                          [self activateConfig:cfg];
-                          [targetController loadCurrent];
-                          
-                          if (conflict && !mergeInto) {
-                              [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_configs.count - 1] byExtendingSelection:NO];
-                              [tableView editColumn:0 row:_configs.count - 1 withEvent:nil select:YES];
-                          }
-                      }
-                      
-                      if (error) {
-                          [window presentError:error
-                                modalForWindow:window
-                                      delegate:nil
-                            didPresentSelector:nil
-                                   contextInfo:nil];
-                      }
-                  }];
-     
-}
-
-- (void)exportPressed:(id)sender {
-    NSSavePanel *panel = [NSSavePanel savePanel];
-    panel.allowedFileTypes = @[ @"enjoyable" ];
-    Config *cfg = _currentConfig;
-    panel.nameFieldStringValue = cfg.name;
-    NSWindow *window = NSApplication.sharedApplication.keyWindow;
-    [panel beginSheetModalForWindow:window
-                  completionHandler:^(NSInteger result) {
-                      if (result != NSFileHandlingPanelOKButton)
-                          return;
-                      [panel close];
-                      NSError *error;
-                      NSDictionary *serialization = [cfg serialize];
-                      NSData *json = [NSJSONSerialization dataWithJSONObject:serialization
-                                                                     options:NSJSONWritingPrettyPrinted
-                                                                       error:&error];
-                      if (!error)
-                          [json writeToURL:panel.URL options:NSDataWritingAtomic error:&error];
-                      
-                      if (error) {
-                          [window presentError:error
-                                modalForWindow:window
-                                      delegate:nil
-                            didPresentSelector:nil
-                                   contextInfo:nil];
-                      }
-                  }];
-}
-
-@end
index e1e7aa1304c737843d042be4e8430f6444383959..c2ebf64845f91c20d37da52ed1cbe2352d88dfe3 100644 (file)
                                <string key="NSClassName">ApplicationController</string>
                        </object>
                        <object class="NSCustomObject" id="468285243">
-                               <string key="NSClassName">ConfigsController</string>
+                               <string key="NSClassName">NJMappingsController</string>
                        </object>
                        <object class="NSCustomObject" id="1007832501">
                                <string key="NSClassName">NJInputController</string>
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
-                                               <string key="label">configsController</string>
+                                               <string key="label">mappingsController</string>
                                                <reference key="source" ref="1007832501"/>
                                                <reference key="destination" ref="468285243"/>
                                        </object>
-                                       <int key="connectionID">717</int>
-                               </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">jsController</string>
-                                               <reference key="source" ref="207406104"/>
-                                               <reference key="destination" ref="1007832501"/>
-                                       </object>
-                                       <int key="connectionID">484</int>
+                                       <int key="connectionID">822</int>
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
-                                               <string key="label">configsController</string>
+                                               <string key="label">dockMenuBase</string>
                                                <reference key="source" ref="207406104"/>
-                                               <reference key="destination" ref="468285243"/>
+                                               <reference key="destination" ref="720053764"/>
                                        </object>
-                                       <int key="connectionID">716</int>
+                                       <int key="connectionID">726</int>
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
-                                               <string key="label">dockMenuBase</string>
+                                               <string key="label">inputController</string>
                                                <reference key="source" ref="207406104"/>
-                                               <reference key="destination" ref="720053764"/>
+                                               <reference key="destination" ref="1007832501"/>
                                        </object>
-                                       <int key="connectionID">726</int>
+                                       <int key="connectionID">819</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBOutletConnection" key="connection">
+                                               <string key="label">mappingsController</string>
+                                               <reference key="source" ref="207406104"/>
+                                               <reference key="destination" ref="468285243"/>
+                                       </object>
+                                       <int key="connectionID">820</int>
                                </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBActionConnection" key="connection">
                                        </object>
                                        <int key="connectionID">692</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">configsController</string>
-                                               <reference key="source" ref="801536542"/>
-                                               <reference key="destination" ref="468285243"/>
-                                       </object>
-                                       <int key="connectionID">693</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">joystickController</string>
                                        </object>
                                        <int key="connectionID">709</int>
                                </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBOutletConnection" key="connection">
-                                               <string key="label">configPopup</string>
-                                               <reference key="source" ref="801536542"/>
-                                               <reference key="destination" ref="194275224"/>
-                                       </object>
-                                       <int key="connectionID">713</int>
-                               </object>
-                               <object class="IBConnectionRecord">
-                                       <object class="IBActionConnection" key="connection">
-                                               <string key="label">configChosen:</string>
-                                               <reference key="source" ref="801536542"/>
-                                               <reference key="destination" ref="194275224"/>
-                                       </object>
-                                       <int key="connectionID">714</int>
-                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBActionConnection" key="connection">
                                                <string key="label">radioChanged:</string>
                                        </object>
                                        <int key="connectionID">781</int>
                                </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBOutletConnection" key="connection">
+                                               <string key="label">mappingsController</string>
+                                               <reference key="source" ref="801536542"/>
+                                               <reference key="destination" ref="468285243"/>
+                                       </object>
+                                       <int key="connectionID">821</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBOutletConnection" key="connection">
+                                               <string key="label">mappingPopup</string>
+                                               <reference key="source" ref="801536542"/>
+                                               <reference key="destination" ref="194275224"/>
+                                       </object>
+                                       <int key="connectionID">823</int>
+                               </object>
+                               <object class="IBConnectionRecord">
+                                       <object class="IBActionConnection" key="connection">
+                                               <string key="label">mappingChosen:</string>
+                                               <reference key="source" ref="801536542"/>
+                                               <reference key="destination" ref="194275224"/>
+                                       </object>
+                                       <int key="connectionID">824</int>
+                               </object>
                                <object class="IBConnectionRecord">
                                        <object class="IBOutletConnection" key="connection">
                                                <string key="label">keyDelegate</string>
                        <nil key="activeLocalization"/>
                        <dictionary class="NSMutableDictionary" key="localizations"/>
                        <nil key="sourceID"/>
-                       <int key="maxID">818</int>
+                       <int key="maxID">824</int>
                </object>
                <object class="IBClassDescriber" key="IBDocument.Classes">
                        <array class="NSMutableArray" key="referencedPartialClassDescriptions">
index 5b21e9922e9dd1f02cc6b2496f341a7b497e0c69..cbcf390649283d89c9584e8eaf7002eb48e3a2df 100644 (file)
@@ -16,8 +16,8 @@
                8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
                8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
                D549CA4C0FBB441B00BC8203 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = D549CA4B0FBB441B00BC8203 /* Credits.rtf */; };
-               D5617A360FAEB74000928B3A /* ConfigsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617A350FAEB74000928B3A /* ConfigsController.m */; };
-               D5617A390FAEBA1800928B3A /* Config.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617A380FAEBA1800928B3A /* Config.m */; };
+               D5617A360FAEB74000928B3A /* NJMappingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617A350FAEB74000928B3A /* NJMappingsController.m */; };
+               D5617A390FAEBA1800928B3A /* NJMapping.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617A380FAEBA1800928B3A /* NJMapping.m */; };
                D5617D1A0FAF568100928B3A /* NJInputButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617D190FAF568100928B3A /* NJInputButton.m */; };
                D5617D260FAF579300928B3A /* NJInputHat.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617D250FAF579300928B3A /* NJInputHat.m */; };
                D5617D2B0FAF579A00928B3A /* NJInputAnalog.m in Sources */ = {isa = PBXBuildFile; fileRef = D5617D2A0FAF579A00928B3A /* NJInputAnalog.m */; };
                8D1107320486CEB800E47090 /* Enjoyable.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Enjoyable.app; sourceTree = BUILT_PRODUCTS_DIR; };
                D549CA4B0FBB441B00BC8203 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
                D5617A080FAEAF8300928B3A /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = "<group>"; };
-               D5617A340FAEB74000928B3A /* ConfigsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigsController.h; sourceTree = "<group>"; };
-               D5617A350FAEB74000928B3A /* ConfigsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConfigsController.m; sourceTree = "<group>"; };
-               D5617A370FAEBA1800928B3A /* Config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Config.h; sourceTree = "<group>"; };
-               D5617A380FAEBA1800928B3A /* Config.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Config.m; sourceTree = "<group>"; };
+               D5617A340FAEB74000928B3A /* NJMappingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJMappingsController.h; sourceTree = "<group>"; };
+               D5617A350FAEB74000928B3A /* NJMappingsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJMappingsController.m; sourceTree = "<group>"; };
+               D5617A370FAEBA1800928B3A /* NJMapping.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJMapping.h; sourceTree = "<group>"; };
+               D5617A380FAEBA1800928B3A /* NJMapping.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJMapping.m; sourceTree = "<group>"; };
                D5617D180FAF568100928B3A /* NJInputButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJInputButton.h; sourceTree = "<group>"; };
                D5617D190FAF568100928B3A /* NJInputButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NJInputButton.m; sourceTree = "<group>"; };
                D5617D240FAF579300928B3A /* NJInputHat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NJInputHat.h; sourceTree = "<group>"; };
                                D594BEF80FAE6FF2007A85F2 /* NJInputController.m */,
                                D594BF810FAE9661007A85F2 /* ApplicationController.h */,
                                D594BF820FAE9661007A85F2 /* ApplicationController.m */,
-                               D5617A340FAEB74000928B3A /* ConfigsController.h */,
-                               D5617A350FAEB74000928B3A /* ConfigsController.m */,
-                               D5617A370FAEBA1800928B3A /* Config.h */,
-                               D5617A380FAEBA1800928B3A /* Config.m */,
+                               D5617A340FAEB74000928B3A /* NJMappingsController.h */,
+                               D5617A350FAEB74000928B3A /* NJMappingsController.m */,
+                               D5617A370FAEBA1800928B3A /* NJMapping.h */,
+                               D5617A380FAEBA1800928B3A /* NJMapping.m */,
                                D5F8096F0FB093400006A4DE /* TargetConfig.h */,
                                D5F809700FB093400006A4DE /* TargetConfig.m */,
                                D5617FD40FAFD06000928B3A /* Target.h */,
                                D594BE8A0FAE64AD007A85F2 /* NJInput.m in Sources */,
                                D594BEF90FAE6FF2007A85F2 /* NJInputController.m in Sources */,
                                D594BF830FAE9661007A85F2 /* ApplicationController.m in Sources */,
-                               D5617A360FAEB74000928B3A /* ConfigsController.m in Sources */,
-                               D5617A390FAEBA1800928B3A /* Config.m in Sources */,
+                               D5617A360FAEB74000928B3A /* NJMappingsController.m in Sources */,
+                               D5617A390FAEBA1800928B3A /* NJMapping.m in Sources */,
                                D5617D1A0FAF568100928B3A /* NJInputButton.m in Sources */,
                                D5617D260FAF579300928B3A /* NJInputHat.m in Sources */,
                                D5617D2B0FAF579A00928B3A /* NJInputAnalog.m in Sources */,
index 1c4853cc1c1ec1dcdf847d843e40d7e34a223f1d..feea2e26e10d05e60df233d17a4d94fbbddda3a5 100644 (file)
@@ -8,13 +8,13 @@
 
 @class NJDevice;
 @class NJInput;
-@class ConfigsController;
+@class NJMappingsController;
 @class TargetController;
 
 @interface NJInputController : NSObject <NSOutlineViewDataSource, NSOutlineViewDelegate> {
        IBOutlet NSOutlineView *outlineView;
        IBOutlet TargetController *targetController;
-       IBOutlet ConfigsController *configsController;
+       IBOutlet NJMappingsController *mappingsController;
 }
 
 - (void)setup;
index 9ee6d0487eb83befa5c0eb6ff8b51c047dd75219..a708d82aab8f19bc33b7b87cb0c3e7ee76558ad1 100644 (file)
@@ -7,8 +7,8 @@
 
 #import "NJInputController.h"
 
-#import "Config.h"
-#import "ConfigsController.h"
+#import "NJMapping.h"
+#import "NJMappingsController.h"
 #import "NJDevice.h"
 #import "NJInput.h"
 #import "Target.h"
 }
 
 - (void)runTargetForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
-    NJDevice *js = [self findJoystickByRef:device];
-    NJInput *mainInput = [js inputForEvent:value];
+    NJDevice *dev = [self findJoystickByRef:device];
+    NJInput *mainInput = [dev inputForEvent:value];
     [mainInput notifyEvent:value];
     NSArray *children = mainInput.children ? mainInput.children : mainInput ? @[mainInput] : @[];
     for (NJInput *subInput in children) {
-        Target *target = configsController.currentConfig[subInput];
+        Target *target = mappingsController.currentMapping[subInput];
         target.magnitude = mainInput.magnitude;
         target.running = subInput.active;
         if (target.running && target.isContinuous)
@@ -72,8 +72,8 @@
 }
 
 - (void)showTargetForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value {
-    NJDevice *js = [self findJoystickByRef:device];
-    NJInput *handler = [js handlerForEvent:value];
+    NJDevice *dev = [self findJoystickByRef:device];
+    NJInput *handler = [dev handlerForEvent:value];
     if (!handler)
         return;
     
@@ -93,11 +93,11 @@ static void input_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDVa
     }
 }
 
-static int findAvailableIndex(NSArray *list, NJDevice *js) {
+static int findAvailableIndex(NSArray *list, NJDevice *dev) {
     for (int index = 1; ; index++) {
         BOOL available = YES;
         for (NJDevice *used in list) {
-            if ([used.productName isEqualToString:js.productName] && used.index == index) {
+            if ([used.productName isEqualToString:dev.productName] && used.index == index) {
                 available = NO;
                 break;
             }
@@ -109,9 +109,9 @@ static int findAvailableIndex(NSArray *list, NJDevice *js) {
 
 - (void)addJoystickForDevice:(IOHIDDeviceRef)device {
     IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)self);
-    NJDevice *js = [[NJDevice alloc] initWithDevice:device];
-    js.index = findAvailableIndex(_joysticks, js);
-    [_joysticks addObject:js];
+    NJDevice *dev = [[NJDevice alloc] initWithDevice:device];
+    dev.index = findAvailableIndex(_joysticks, dev);
+    [_joysticks addObject:dev];
     [outlineView reloadData];
 }
 
@@ -121,9 +121,9 @@ static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDevi
 }
 
 - (NJDevice *)findJoystickByRef:(IOHIDDeviceRef)device {
-    for (NJDevice *js in _joysticks)
-        if (js.device == device)
-            return js;
+    for (NJDevice *dev in _joysticks)
+        if (dev.device == device)
+            return dev;
     return nil;
 }
 
diff --git a/NJMapping.h b/NJMapping.h
new file mode 100644 (file)
index 0000000..a4068ff
--- /dev/null
@@ -0,0 +1,22 @@
+//
+//  NJMapping.h
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//  Copyright 2009 University of Otago. All rights reserved.
+//
+
+@class Target;
+@class NJInput;
+
+@interface NJMapping : NSObject
+
+@property (nonatomic, copy) NSString *name;
+@property (nonatomic, readonly) NSMutableDictionary *entries;
+
+- (id)initWithName:(NSString *)name;
+- (Target *)objectForKeyedSubscript:(NJInput *)input;
+- (void)setObject:(Target *)target forKeyedSubscript:(NJInput *)input;
+- (NSDictionary *)serialize;
+
+@end
diff --git a/NJMapping.m b/NJMapping.m
new file mode 100644 (file)
index 0000000..a5f810c
--- /dev/null
@@ -0,0 +1,45 @@
+//
+//  NJMapping.m
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//
+
+#import "NJMapping.h"
+
+#import "NJInput.h"
+
+@implementation NJMapping
+
+- (id)initWithName:(NSString *)name {
+    if ((self = [super init])) {
+        self.name = name ? name : @"Untitled";
+        _entries = [[NSMutableDictionary alloc] init];
+    }
+    return self;
+}
+
+- (Target *)objectForKeyedSubscript:(NJInput *)input {
+    return input ? _entries[input.uid] : nil;
+}
+
+- (void)setObject:(Target *)target forKeyedSubscript:(NJInput *)input {
+    if (input) {
+        if (target)
+            _entries[input.uid] = target;
+        else
+            [_entries removeObjectForKey:input.uid];
+    }
+}
+
+- (NSDictionary *)serialize {
+    NSMutableDictionary *entries = [[NSMutableDictionary alloc] initWithCapacity:_entries.count];
+    for (id key in _entries) {
+        id serialized = [_entries[key] serialize];
+        if (serialized)
+            entries[key] = serialized;
+    }
+    return @{ @"name": _name, @"entries": entries };
+}
+
+@end
diff --git a/NJMappingsController.h b/NJMappingsController.h
new file mode 100644 (file)
index 0000000..cda477f
--- /dev/null
@@ -0,0 +1,36 @@
+//
+//  NJMappingsController.h
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//  Copyright 2009 University of Otago. All rights reserved.
+//
+
+@class NJMapping;
+@class TargetController;
+
+@interface NJMappingsController : NSObject <NSTableViewDataSource,
+                                            NSTableViewDelegate,
+                                            NSOpenSavePanelDelegate> {
+    IBOutlet NSButton *removeButton;
+    IBOutlet NSTableView *tableView;
+    IBOutlet TargetController *targetController;
+}
+
+@property (nonatomic, readonly) NJMapping *currentMapping;
+@property (nonatomic, readonly) NSArray *mappings;
+
+- (NJMapping *)objectForKeyedSubscript:(NSString *)name;
+
+
+- (IBAction)addPressed:(id)sender;
+- (IBAction)removePressed:(id)sender;
+- (IBAction)importPressed:(id)sender;
+- (IBAction)exportPressed:(id)sender;
+- (void)activateMapping:(NJMapping *)mapping;
+- (void)activateMappingForProcess:(NSString *)processName;
+
+- (void)save;
+- (void)load;
+
+@end
diff --git a/NJMappingsController.m b/NJMappingsController.m
new file mode 100644 (file)
index 0000000..e5b2b7d
--- /dev/null
@@ -0,0 +1,287 @@
+//
+//  NJMappingsController.m
+//  Enjoy
+//
+//  Created by Sam McCall on 4/05/09.
+//
+
+#import "NJMappingsController.h"
+
+#import "ApplicationController.h"
+#import "NJMapping.h"
+#import "NJMappingsController.h"
+#import "Target.h"
+#import "TargetController.h"
+#import "NJEvents.h"
+
+@implementation NJMappingsController {
+    NSMutableArray *_mappings;
+    NJMapping *manualMapping;
+}
+
+- (id)init {
+    if ((self = [super init])) {
+        _mappings = [[NSMutableArray alloc] init];
+        _currentMapping = [[NJMapping alloc] initWithName:@"(default)"];
+        manualMapping = _currentMapping;
+        [_mappings addObject:_currentMapping];
+    }
+    return self;
+}
+
+- (NJMapping *)objectForKeyedSubscript:(NSString *)name {
+    for (NJMapping *mapping in _mappings)
+        if ([name isEqualToString:mapping.name])
+            return mapping;
+    return nil;
+}
+
+- (void)activateMappingForProcess:(NSString *)processName {
+    NJMapping *oldMapping = manualMapping;
+    NJMapping *newMapping = self[processName];
+    if (!newMapping)
+        newMapping = oldMapping;
+    if (newMapping != _currentMapping)
+        [self activateMapping:newMapping];
+    manualMapping = oldMapping;
+}
+
+- (void)activateMapping:(NJMapping *)mapping {
+    if (!mapping)
+        mapping = manualMapping;
+    NSLog(@"Switching to mapping %@.", mapping.name);
+    manualMapping = mapping;
+    _currentMapping = mapping;
+    [removeButton setEnabled:_mappings[0] != mapping];
+    [targetController loadCurrent];
+    [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged
+                                                      object:_currentMapping];
+    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:[_mappings indexOfObject:mapping]] byExtendingSelection:NO];
+}
+
+- (IBAction)addPressed:(id)sender {
+    NJMapping *newMapping = [[NJMapping alloc] initWithName:@"Untitled"];
+    [_mappings addObject:newMapping];
+    [(ApplicationController *)NSApplication.sharedApplication.delegate mappingsChanged];
+    [tableView reloadData];
+    [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_mappings.count - 1] byExtendingSelection:NO];
+    [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES];
+    [self activateMapping:newMapping];
+}
+
+- (IBAction)removePressed:(id)sender {
+    if (tableView.selectedRow == 0)
+        return;
+    
+    [_mappings removeObjectAtIndex:tableView.selectedRow];
+    [tableView reloadData];
+    [(ApplicationController *)NSApplication.sharedApplication.delegate mappingsChanged];
+    [self activateMapping:_mappings[0]];
+    [self save];
+}
+
+-(void)tableViewSelectionDidChange:(NSNotification *)notify {
+    if (tableView.selectedRow >= 0)
+        [self activateMapping:_mappings[tableView.selectedRow]];
+}
+
+- (id)tableView:(NSTableView *)view objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)index {
+    return [_mappings[index] name];
+}
+
+- (void)tableView:(NSTableView *)view setObjectValue:(NSString *)obj forTableColumn:(NSTableColumn *)col row:(NSInteger)index {
+    [(NJMapping *)_mappings[index] setName:obj];
+    [tableView reloadData];
+    [(ApplicationController *)NSApplication.sharedApplication.delegate mappingsChanged];
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
+    return _mappings.count;
+}
+
+- (BOOL)tableView:(NSTableView *)view shouldEditTableColumn:(NSTableColumn *)column row:(NSInteger)index {
+    return index > 0;
+}
+
+- (void)save {
+    NSLog(@"Saving mappings to defaults.");
+    [NSUserDefaults.standardUserDefaults setValuesForKeysWithDictionary:[self dumpAll]];
+}
+
+- (void)load {
+    [self loadAllFrom:NSUserDefaults.standardUserDefaults.dictionaryRepresentation];
+}
+
+- (NSDictionary *)dumpAll {
+    NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_mappings.count];
+    for (NJMapping *mapping in _mappings)
+        [ary addObject:[mapping serialize]];
+    NSUInteger current = _currentMapping ? [_mappings indexOfObject:_currentMapping] : 0;
+    return @{ @"mappings": ary, @"selected": @(current) };
+}
+
+- (void)loadAllFrom:(NSDictionary*)envelope {
+    NSArray *storedMappings = envelope[@"mappings"];
+    NSMutableArray* newMappings = [[NSMutableArray alloc] initWithCapacity:storedMappings.count];
+
+    // have to do two passes in case mapping1 refers to mapping2 via a TargetMapping
+    for (NSDictionary *storedMapping in storedMappings) {
+        NJMapping *mapping = [[NJMapping alloc] initWithName:storedMapping[@"name"]];
+        [newMappings addObject:mapping];
+    }
+
+    for (unsigned i = 0; i < storedMappings.count; ++i) {
+        NSDictionary *entries = storedMappings[i][@"entries"];
+        NJMapping *mapping = newMappings[i];
+        for (id key in entries) {
+            Target *target = [Target targetDeserialize:entries[key]
+                                            withMappings:newMappings];
+            if (target)
+                mapping.entries[key] = target;
+        }
+    }
+    
+    if (newMappings.count) {
+        unsigned current = [envelope[@"selected"] unsignedIntValue];
+        if (current >= newMappings.count)
+            current = 0;
+        _mappings = newMappings;
+        [tableView reloadData];
+        [(ApplicationController *)NSApplication.sharedApplication.delegate mappingsChanged];
+        [self activateMapping:_mappings[current]];
+    }
+}
+
+- (NJMapping *)mappingWithURL:(NSURL *)url error:(NSError **)error {
+    NSInputStream *stream = [NSInputStream inputStreamWithURL:url];
+    [stream open];
+    NSDictionary *serialization = !*error
+        ? [NSJSONSerialization JSONObjectWithStream:stream options:0 error:error]
+        : nil;
+    [stream close];
+    
+    if (!([serialization isKindOfClass:NSDictionary.class]
+          && [serialization[@"name"] isKindOfClass:NSString.class]
+          && [serialization[@"entries"] isKindOfClass:NSDictionary.class])) {
+        *error = [NSError errorWithDomain:@"Enjoyable"
+                                     code:0
+                              description:@"This isn't a valid mapping file."];
+        return nil;
+    }
+
+    NSDictionary *entries = serialization[@"entries"];
+    NJMapping *mapping = [[NJMapping alloc] initWithName:serialization[@"name"]];
+    for (id key in entries) {
+        NSDictionary *value = entries[key];
+        if ([key isKindOfClass:NSString.class]) {
+            Target *target = [Target targetDeserialize:value
+                                           withMappings:_mappings];
+            if (target)
+                mapping.entries[key] = target;
+        }
+    }
+    return mapping;
+}
+
+- (void)importPressed:(id)sender {
+    NSOpenPanel *panel = [NSOpenPanel openPanel];
+    panel.allowedFileTypes = @[ @"enjoyable", @"json", @"txt" ];
+    NSWindow *window = NSApplication.sharedApplication.keyWindow;
+    [panel beginSheetModalForWindow:window
+                  completionHandler:^(NSInteger result) {
+                      if (result != NSFileHandlingPanelOKButton)
+                          return;
+
+                      [panel close];
+                      NSError *error;
+                      NJMapping *mapping = [self mappingWithURL:panel.URL error:&error];
+                      
+                      if (!error) {
+                          BOOL conflict = NO;
+                          NJMapping *mergeInto = self[mapping.name];
+                          for (id key in mapping.entries) {
+                              if (mergeInto.entries[key]
+                                  && ![mergeInto.entries[key] isEqual:mapping.entries[key]]) {
+                                  conflict = YES;
+                                  break;
+                              }
+                          }
+                          
+                          if (conflict) {
+                              NSAlert *conflictAlert = [[NSAlert alloc] init];
+                              conflictAlert.messageText = @"Replace existing mappings?";
+                              conflictAlert.informativeText =
+                                  [NSString stringWithFormat:
+                                   @"This file contains inputs you've already mapped in \"%@\". Do you "
+                                   @"want to merge them and replace your existing mappings, or import this "
+                                   @"as a separate mapping?", mapping.name];
+                              [conflictAlert addButtonWithTitle:@"Merge"];
+                              [conflictAlert addButtonWithTitle:@"Cancel"];
+                              [conflictAlert addButtonWithTitle:@"New Mapping"];
+                              NSInteger res = [conflictAlert runModal];
+                              if (res == NSAlertSecondButtonReturn)
+                                  return;
+                              else if (res == NSAlertThirdButtonReturn)
+                                  mergeInto = nil;
+                          }
+                          
+                          if (mergeInto) {
+                              [mergeInto.entries addEntriesFromDictionary:mapping.entries];
+                              mapping = mergeInto;
+                          } else {
+                              [_mappings addObject:mapping];
+                              [tableView reloadData];
+                          }
+                          
+                          [self save];
+                          [(ApplicationController *)NSApplication.sharedApplication.delegate mappingsChanged];
+                          [self activateMapping:mapping];
+                          [targetController loadCurrent];
+                          
+                          if (conflict && !mergeInto) {
+                              [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_mappings.count - 1] byExtendingSelection:NO];
+                              [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES];
+                          }
+                      }
+                      
+                      if (error) {
+                          [window presentError:error
+                                modalForWindow:window
+                                      delegate:nil
+                            didPresentSelector:nil
+                                   contextInfo:nil];
+                      }
+                  }];
+     
+}
+
+- (void)exportPressed:(id)sender {
+    NSSavePanel *panel = [NSSavePanel savePanel];
+    panel.allowedFileTypes = @[ @"enjoyable" ];
+    NJMapping *mapping = _currentMapping;
+    panel.nameFieldStringValue = mapping.name;
+    NSWindow *window = NSApplication.sharedApplication.keyWindow;
+    [panel beginSheetModalForWindow:window
+                  completionHandler:^(NSInteger result) {
+                      if (result != NSFileHandlingPanelOKButton)
+                          return;
+                      [panel close];
+                      NSError *error;
+                      NSDictionary *serialization = [mapping serialize];
+                      NSData *json = [NSJSONSerialization dataWithJSONObject:serialization
+                                                                     options:NSJSONWritingPrettyPrinted
+                                                                       error:&error];
+                      if (!error)
+                          [json writeToURL:panel.URL options:NSDataWritingAtomic error:&error];
+                      
+                      if (error) {
+                          [window presentError:error
+                                modalForWindow:window
+                                      delegate:nil
+                            didPresentSelector:nil
+                                   contextInfo:nil];
+                      }
+                  }];
+}
+
+@end
index d2341621aaef39a2d0ad1210123ab97d961c8e7f..1a37f01e81d90b7c9e6d75b84d63ced72478530e 100644 (file)
--- a/Target.h
+++ b/Target.h
@@ -20,7 +20,7 @@
 
 - (NSDictionary *)serialize;
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs;
+                  withMappings:(NSArray *)mappings;
 + (NSString *)serializationCode;
 
 @end
index 42d004c6cd94e2ca67f26eff49896e71d4e0f2db..097562e52f3237a5b4788d19132a33476e6c6aef 100644 (file)
--- a/Target.m
+++ b/Target.m
@@ -38,8 +38,8 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
-    // Don't crash loading old configs (but don't load them either).
+                  withMappings:(NSArray *)mappings {
+    // Don't crash loading old/bad mappings (but don't load them either).
     if (![serialization isKindOfClass:NSDictionary.class])
         return nil;
     NSString *type = serialization[@"type"];
@@ -51,7 +51,7 @@
                         TargetToggleMouseScope.class
          ]) {
         if ([type isEqualToString:cls.serializationCode])
-            return [cls targetDeserialize:serialization withConfigs:configs];
+            return [cls targetDeserialize:serialization withMappings:mappings];
     }
     
     return nil;
index c124618b79054f29114767236753211ec3f09fa5..1018e5d3e26b576dea9a3d13960bb8dc4d464642 100644 (file)
@@ -8,10 +8,10 @@
 
 #import "Target.h"
 
-@class Config;
+@class NJMapping;
 
 @interface TargetConfig : Target
 
-@property (nonatomic, weak) Config *config;
+@property (nonatomic, weak) NJMapping *mapping;
 
 @end
index 055f7822cecc51d49666af84d824248d535a9391..0f892c68681ac939695a57089538bc903c1cf06b 100644 (file)
@@ -8,28 +8,28 @@
 #import "TargetConfig.h"
 
 #import "ApplicationController.h"
-#import "Config.h"
-#import "ConfigsController.h"
+#import "NJMapping.h"
+#import "NJMappingsController.h"
 
 @implementation TargetConfig
 
 + (NSString *)serializationCode {
-    return @"cfg";
+    return @"mapping";
 }
 
 - (NSDictionary *)serialize {
-    return _config
-        ? @{ @"type": @"cfg", @"name": _config.name }
+    return _mapping
+        ? @{ @"type": @"mapping", @"name": _mapping.name }
         : nil;
 }
 
 + (TargetConfig *)targetDeserialize:(NSDictionary *)serialization
-                        withConfigs:(NSArray *)configs {
+                        withMappings:(NSArray *)mappings {
     NSString *name = serialization[@"name"];
     TargetConfig *target = [[TargetConfig alloc] init];
-    for (Config *config in configs) {
-        if ([config.name isEqualToString:name]) {
-            target.config = config;
+    for (NJMapping *mapping in mappings) {
+        if ([mapping.name isEqualToString:name]) {
+            target.mapping = mapping;
             return target;
         }
     }
@@ -38,7 +38,7 @@
 
 - (void)trigger {
     ApplicationController *ctrl = NSApplication.sharedApplication.delegate;
-    [ctrl.configsController activateConfig:_config];
+    [ctrl.mappingsController activateMapping:_mapping];
 }
 
 @end
index 7f9e1590c88259cacac70a9603c997c2a8cb983e..da7974c4684c9df6e9d55333c3e8e3bde14a69e2 100644 (file)
@@ -8,7 +8,7 @@
 
 #import "NJKeyInputField.h"
 
-@class ConfigsController;
+@class NJMappingsController;
 @class NJInputController;
 @class Target;
 @class TargetMouseMove;
     IBOutlet NSSegmentedControl *mouseBtnSelect;
     IBOutlet NSSegmentedControl *scrollDirSelect;
     IBOutlet NSTextField *title;
-    IBOutlet NSPopUpButton *configPopup;
-    IBOutlet ConfigsController *configsController;
+    IBOutlet NSPopUpButton *mappingPopup;
+    IBOutlet NJMappingsController *mappingsController;
     IBOutlet NJInputController *joystickController;
 }
 
 @property (assign) BOOL enabled;
 
 - (void)loadCurrent;
-- (void)refreshConfigs;
-- (IBAction)configChosen:(id)sender;
+- (void)refreshMappings;
 - (IBAction)radioChanged:(id)sender;
 - (IBAction)mdirChanged:(id)sender;
 - (IBAction)mbtnChanged:(id)sender;
index 2e539179dbe3ac5149f9955984959017967dc941..1da2c99a6e7157f038ac8d13a5eb6fa8a6cacab3 100644 (file)
@@ -7,8 +7,8 @@
 
 #import "TargetController.h"
 
-#import "ConfigsController.h"
-#import "Config.h"
+#import "NJMappingsController.h"
+#import "NJMapping.h"
 #import "NJInput.h"
 #import "NJInputController.h"
 #import "NJKeyInputField.h"
     }
     
     if (row != 2) {
-        [configPopup selectItemAtIndex:-1];
-        [configPopup resignIfFirstResponder];
-    } else if (!configPopup.selectedItem)
-        [configPopup selectItemAtIndex:0];
+        [mappingPopup selectItemAtIndex:-1];
+        [mappingPopup resignIfFirstResponder];
+    } else if (!mappingPopup.selectedItem)
+        [mappingPopup selectItemAtIndex:0];
     
     if (row != 3) {
         mouseDirSelect.selectedSegment = -1;
@@ -73,9 +73,9 @@
     [self commit];
 }
 
-- (void)configChosen:(id)sender {
+- (void)mappingChosen:(id)sender {
     [radioButtons selectCellAtRow:2 column:0];
-    [configPopup.window makeFirstResponder:configPopup];
+    [mappingPopup.window makeFirstResponder:mappingPopup];
     [self commit];
 }
 
@@ -98,7 +98,7 @@
 }
 
 - (Target *)currentTarget {
-    return configsController.currentConfig[joystickController.selectedInput];
+    return mappingsController.currentMapping[joystickController.selectedInput];
 }
 
 - (Target *)makeTarget {
             break;
         case 2: {
             TargetConfig *c = [[TargetConfig alloc] init];
-            c.config = configsController.configs[configPopup.indexOfSelectedItem];
+            c.mapping = mappingsController.mappings[mappingPopup.indexOfSelectedItem];
             return c;
         }
         case 3: {
 
 - (void)commit {
     [self cleanUpInterface];
-    configsController.currentConfig[joystickController.selectedInput] = [self makeTarget];
-    [configsController save];
+    mappingsController.currentMapping[joystickController.selectedInput] = [self makeTarget];
+    [mappingsController save];
 }
 
 - (BOOL)enabled {
 - (void)setEnabled:(BOOL)enabled {
     [radioButtons setEnabled:enabled];
     [keyInput setEnabled:enabled];
-    [configPopup setEnabled:enabled];
+    [mappingPopup setEnabled:enabled];
     [mouseDirSelect setEnabled:enabled];
     [mouseBtnSelect setEnabled:enabled];
     [scrollDirSelect setEnabled:enabled];
         for (id <NJInputPathElement> cur = input.base; cur; cur = cur.base) {
             inpFullName = [[NSString alloc] initWithFormat:@"%@ > %@", cur.name, inpFullName];
         }
-        title.stringValue = [[NSString alloc] initWithFormat:@"%@ > %@", configsController.currentConfig.name, inpFullName];
+        title.stringValue = [[NSString alloc] initWithFormat:@"%@ > %@", mappingsController.currentMapping.name, inpFullName];
     }
 
     if ([target isKindOfClass:TargetKeyboard.class]) {
         keyInput.keyCode = [(TargetKeyboard*)target vk];
     } else if ([target isKindOfClass:TargetConfig.class]) {
         [radioButtons selectCellAtRow:2 column:0];
-        NSUInteger idx = [configsController.configs
-                          indexOfObject:[(TargetConfig *)target config]];
+        NSUInteger idx = [mappingsController.mappings
+                          indexOfObject:[(TargetConfig *)target mapping]];
         if (idx == NSNotFound) {
             [radioButtons selectCellAtRow:self.enabled ? 0 : -1 column:0];
-            [configPopup selectItemAtIndex:-1];
+            [mappingPopup selectItemAtIndex:-1];
         } else
-            [configPopup selectItemAtIndex:idx];
+            [mappingPopup selectItemAtIndex:idx];
     }
     else if ([target isKindOfClass:TargetMouseMove.class]) {
         [radioButtons selectCellAtRow:3 column:0];
         [keyInput resignIfFirstResponder];
 }
 
-- (void)refreshConfigs {
-    NSInteger initialIndex = configPopup.indexOfSelectedItem;
-    [configPopup.menu removeAllItems];
-    for (Config *config in configsController.configs) {
-        NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:config.name
-                                                      action:@selector(configChosen:)
+- (void)refreshMappings {
+    NSInteger initialIndex = mappingPopup.indexOfSelectedItem;
+    [mappingPopup.menu removeAllItems];
+    for (NJMapping *mapping in mappingsController.mappings) {
+        NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name
+                                                      action:@selector(mappingChosen:)
                                                keyEquivalent:@""];
         item.target = self;
-        [configPopup.menu addItem:item];
+        [mappingPopup.menu addItem:item];
     }
-    [configPopup selectItemAtIndex:initialIndex];
+    [mappingPopup selectItemAtIndex:initialIndex];
 }
 
 @end
index eab2d4e8e080608c3f4af3f0f93441cd1d0e5143..eaabd070f93ccaf990efdf0dc45459c6fba565c6 100644 (file)
@@ -20,7 +20,7 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
+                  withMappings:(NSArray *)mappings {
     TargetKeyboard *target = [[TargetKeyboard alloc] init];
     target.vk = [serialization[@"key"] intValue];
     return target;
index 951715d8c6ed614c8e917d0bca1f73a53e550a28..a19b484475ccf42fcde78a435834279e4247676b 100644 (file)
@@ -18,7 +18,7 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
+                  withMappings:(NSArray *)mappings {
        TargetMouseBtn *target = [[TargetMouseBtn alloc] init];
     target.button = [serialization[@"button"] intValue];
        return target;
index 2806827588daaa6c330888722dbf0aacdac7d1ad..b1d52a01c1d881814a713831cc2cd243a779b8f8 100644 (file)
@@ -26,7 +26,7 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
+                  withMappings:(NSArray *)mappings {
        TargetMouseMove *target = [[TargetMouseMove alloc] init];
     target.axis = [serialization[@"axis"] intValue];
        return target;
index ac3b852511d37a92f7ae744518e7b62bfadee5e9..8f87c02e32557ee7d0b99a2e2c20dc03b580b177 100644 (file)
@@ -20,7 +20,7 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
+                  withMappings:(NSArray *)mappings {
        TargetMouseScroll *target = [[TargetMouseScroll alloc] init];
     target.amount = [serialization[@"amount"] intValue];
        return target;
index 499e9c9a3e768d51441deb99fa93d576fe2aadea..0413b731d937a0b237d75c782dca1b6b359ddd06 100644 (file)
@@ -21,7 +21,7 @@
 }
 
 + (Target *)targetDeserialize:(NSDictionary *)serialization
-                  withConfigs:(NSArray *)configs {
+                  withMappings:(NSArray *)mappings {
        TargetToggleMouseScope *target = [[TargetToggleMouseScope alloc] init];
        return target;
 }
@@ -29,7 +29,7 @@
     // FIXME: It's hacky to get at the controller this way, but it's
     // also hacky to pass it. Shouldn't need to do either.
     ApplicationController *ac = NSApplication.sharedApplication.delegate;
-    NJInputController *jc = ac.jsController;
+    NJInputController *jc = ac.inputController;
     jc.frontWindowOnly = !jc.frontWindowOnly;
 }