X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=blobdiff_plain;f=Classes%2FNJMappingsController.m;h=500e90150ed1277b30145530e4272f9fd496488f;hp=2ee423905cde287893e165ada1e395d00ce25312;hb=235c087385a6e959ba7edefe4a3cbbbc00b2a534;hpb=7e5568674713bedf9318e83b9fb13abbd122382c diff --git a/Classes/NJMappingsController.m b/Classes/NJMappingsController.m index 2ee4239..500e901 100644 --- a/Classes/NJMappingsController.m +++ b/Classes/NJMappingsController.m @@ -10,22 +10,21 @@ #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; + _currentMapping = [[NJMapping alloc] initWithName: + NSLocalizedString(@"(default)", @"default name for first the mapping")]; + _manualMapping = _currentMapping; [_mappings addObject:_currentMapping]; } return self; @@ -50,11 +49,12 @@ - (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 @@ -66,13 +66,13 @@ } - (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; } @@ -80,19 +80,21 @@ if (!found) { [self activateMapping:oldMapping]; - if ([oldMapping.name.lowercaseString isEqualToString:@"@application"]) { + if ([oldMapping.name.lowercaseString isEqualToString:@"@application"] + || [oldMapping.name.lowercaseString isEqualToString: + NSLocalizedString(@"@Application", nil).lowercaseString]) { oldMapping.name = app.bestMappingName; [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"]; @@ -100,20 +102,21 @@ - (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 { - NJMapping *newMapping = [[NJMapping alloc] initWithName:@"Untitled"]; + NJMapping *newMapping = [[NJMapping alloc] init]; [_mappings addObject:newMapping]; [self activateMapping:newMapping]; [self mappingsChanged]; @@ -171,21 +174,19 @@ - (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) { @@ -197,35 +198,29 @@ } } -- (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 { @@ -236,50 +231,28 @@ 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.messageText = NSLocalizedString(@"import conflict prompt", @"Title of import conflict alert"); 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; + [NSString stringWithFormat:NSLocalizedString(@"import conflict in %@", @"Explanation of import conflict"), + mapping.name]; + [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: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 @@ -341,19 +314,13 @@ } - (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_