#import "NJMapping.h"
#import "NJMappingsController.h"
#import "NJOutput.h"
-#import "NJOutputController.h"
#import "NJEvents.h"
#define PB_ROW @"com.yukkurigames.Enjoyable.MappingRow"
@implementation NJMappingsController {
NSMutableArray *_mappings;
- NJMapping *manualMapping;
- NSString *draggingName;
+ NJMapping *_manualMapping;
}
- (id)init {
if ((self = [super init])) {
_mappings = [[NSMutableArray alloc] init];
_currentMapping = [[NJMapping alloc] initWithName:@"(default)"];
- manualMapping = _currentMapping;
+ _manualMapping = _currentMapping;
[_mappings addObject:_currentMapping];
}
return self;
- (void)mappingsChanged {
[self save];
[tableView reloadData];
- popoverActivate.title = _currentMapping.name;
[self updateInterfaceForCurrentMapping];
[NSNotificationCenter.defaultCenter
postNotificationName:NJEventMappingListChanged
- object:_mappings];
+ object:self
+ userInfo:@{ NJMappingListKey: _mappings,
+ NJMappingKey: _currentMapping }];
}
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
}
- (void)activateMappingForProcess:(NSRunningApplication *)app {
- NJMapping *oldMapping = manualMapping;
+ NJMapping *oldMapping = _manualMapping;
NSArray *names = app.possibleMappingNames;
BOOL found = NO;
for (NSString *name in names) {
NJMapping *mapping = self[name];
if (mapping) {
- [self activateMapping:self[name]];
+ [self activateMapping:mapping];
found = YES;
break;
}
[self mappingsChanged];
}
}
- manualMapping = oldMapping;
+ _manualMapping = oldMapping;
}
- (void)updateInterfaceForCurrentMapping {
NSUInteger selected = [_mappings indexOfObject:_currentMapping];
- [removeButton setEnabled:selected != 0];
- [moveDown setEnabled:selected && selected != _mappings.count - 1];
- [moveUp setEnabled:selected > 1];
+ removeButton.enabled = selected != 0;
+ moveUp.enabled = selected > 1;
+ moveDown.enabled = selected && selected != _mappings.count - 1;
popoverActivate.title = _currentMapping.name;
[tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selected] byExtendingSelection:NO];
[NSUserDefaults.standardUserDefaults setInteger:selected forKey:@"selected"];
- (void)activateMapping:(NJMapping *)mapping {
if (!mapping)
- mapping = manualMapping;
+ mapping = _manualMapping;
if (mapping == _currentMapping)
return;
NSLog(@"Switching to mapping %@.", mapping.name);
- manualMapping = mapping;
+ _manualMapping = mapping;
_currentMapping = mapping;
[self updateInterfaceForCurrentMapping];
- [outputController loadCurrent];
- [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged
- object:_currentMapping];
+ [NSNotificationCenter.defaultCenter
+ postNotificationName:NJEventMappingChanged
+ object:self
+ userInfo:@{ NJMappingKey : _currentMapping }];
}
- (IBAction)addPressed:(id)sender {
- (void)loadAllFrom:(NSArray *)storedMappings andActivate:(NSUInteger)selected {
NSMutableArray* newMappings = [[NSMutableArray alloc] initWithCapacity:storedMappings.count];
- // have to do two passes in case mapping1 refers to mapping2 via a NJOutputMapping
+ // Requires two passes to deal with inter-mapping references. First make
+ // an empty mapping for each serialized mapping. Then, deserialize the
+ // data pointing to the empty mappings. Then merge that data back into
+ // its equivalent empty one, which is the one we finally use.
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) {
- NJOutput *output = [NJOutput outputDeserialize:entries[key]
- withMappings:newMappings];
- if (output)
- mapping.entries[key] = output;
- }
+ NJMapping *realMapping = [[NJMapping alloc] initWithSerialization:storedMappings[i]
+ mappings:newMappings];
+ [newMappings[i] mergeEntriesFrom:realMapping];
}
if (newMappings.count) {
}
}
-- (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]) {
- NJOutput *output = [NJOutput outputDeserialize:value
- withMappings:_mappings];
- if (output)
- mapping.entries[key] = output;
- }
+- (void)mappingConflictDidResolve:(NSAlert *)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void *)contextInfo {
+ NSDictionary *userInfo = CFBridgingRelease(contextInfo);
+ NJMapping *oldMapping = userInfo[@"old mapping"];
+ NJMapping *newMapping = userInfo[@"new mapping"];
+ switch (returnCode) {
+ case NSAlertFirstButtonReturn: // Merge
+ [oldMapping mergeEntriesFrom:newMapping];
+ [self activateMapping:oldMapping];
+ [self mappingsChanged];
+ break;
+ case NSAlertThirdButtonReturn: // New Mapping
+ [_mappings addObject:newMapping];
+ [self activateMapping:newMapping];
+ [self mappingsChanged];
+ [self mappingPressed:alert];
+ [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_mappings.count - 1] byExtendingSelection:NO];
+ [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES];
+ break;
+ default: // Cancel, other.
+ break;
}
- return mapping;
}
- (void)addMappingWithContentsOfURL:(NSURL *)url {
error:&error];
if (mapping && !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) {
+ if ([mergeInto hasConflictWith:mapping]) {
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];
+ @"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;
+ [conflictAlert beginSheetModalForWindow:popoverActivate.window
+ modalDelegate:self
+ didEndSelector:@selector(mappingConflictDidResolve:returnCode:contextInfo:)
+ contextInfo:(void *)CFBridgingRetain(@{ @"old mapping": mergeInto,
+ @"new mapping": mapping })];
} else {
[_mappings addObject:mapping];
- }
-
- [self activateMapping:mapping];
- [self mappingsChanged];
-
- if (conflict && !mergeInto) {
- [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:_mappings.count - 1] byExtendingSelection:NO];
- [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES];
+ [self activateMapping:mapping];
+ [self mappingsChanged];
}
}
-
+
if (error) {
[window presentError:error
modalForWindow:window
}
- (IBAction)moveUpPressed:(id)sender {
- NSUInteger idx = [_mappings indexOfObject:_currentMapping];
- if (idx > 1 && idx != NSNotFound) {
- [_mappings exchangeObjectAtIndex:idx withObjectAtIndex:idx - 1];
+ if ([_mappings moveFirstwards:_currentMapping upTo:1])
[self mappingsChanged];
- }
}
- (IBAction)moveDownPressed:(id)sender {
- NSUInteger idx = [_mappings indexOfObject:_currentMapping];
- if (idx < _mappings.count - 1) {
- [_mappings exchangeObjectAtIndex:idx withObjectAtIndex:idx + 1];
+ if ([_mappings moveLastwards:_currentMapping])
[self mappingsChanged];
- }
}
- (BOOL)tableView:(NSTableView *)tableView_