Show import conflict alert as a sheet rather than a separate window.
[enjoyable.git] / Classes / NJMappingsController.m
index de66fb8..5faa4bf 100644 (file)
@@ -51,7 +51,9 @@
     [self updateInterfaceForCurrentMapping];
     [NSNotificationCenter.defaultCenter
         postNotificationName:NJEventMappingListChanged
-        object:_mappings];
+                      object:self
+                    userInfo:@{ NJMappingListKey: _mappings,
+                                NJMappingKey: _currentMapping }];
 }
 
 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
     _manualMapping = mapping;
     _currentMapping = mapping;
     [self updateInterfaceForCurrentMapping];
-    [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) {
     }
 }
 
+- (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;
+    }
+}
+
 - (void)addMappingWithContentsOfURL:(NSURL *)url {
     NSWindow *window = popoverActivate.window;
     NSError *error;
                                                        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