From: Joe Wreschnig Date: Thu, 7 Mar 2013 09:40:46 +0000 (+0100) Subject: Support opening/adding a mapping file directly to import it. X-Git-Tag: version-1.0~12 X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=commitdiff_plain;h=5c488091214f1aca932fa74623e024a4d0c245a2 Support opening/adding a mapping file directly to import it. --- diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings index 5e45963..84fe32f 100644 Binary files a/English.lproj/InfoPlist.strings and b/English.lproj/InfoPlist.strings differ diff --git a/Enjoyable.xcodeproj/project.pbxproj b/Enjoyable.xcodeproj/project.pbxproj index e8badf9..8342944 100644 --- a/Enjoyable.xcodeproj/project.pbxproj +++ b/Enjoyable.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ EEAA9CE116E808E600256B64 /* NSMutableArray+MoveObject.m in Sources */ = {isa = PBXBuildFile; fileRef = EEAA9CE016E808E600256B64 /* NSMutableArray+MoveObject.m */; }; EEAA9CE416E816C800256B64 /* NSFileManager+UniqueNames.m in Sources */ = {isa = PBXBuildFile; fileRef = EEAA9CE316E816C800256B64 /* NSFileManager+UniqueNames.m */; }; EEAA9CE716E81C8400256B64 /* NSString+FixFilename.m in Sources */ = {isa = PBXBuildFile; fileRef = EEAA9CE616E81C8400256B64 /* NSString+FixFilename.m */; }; + EEDFE52816E8957200CD27E0 /* com.yukkurigames.Enjoyable.mapping.icns in Resources */ = {isa = PBXBuildFile; fileRef = EEDFE52716E8957200CD27E0 /* com.yukkurigames.Enjoyable.mapping.icns */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -98,6 +99,7 @@ EEAA9CE316E816C800256B64 /* NSFileManager+UniqueNames.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+UniqueNames.m"; sourceTree = ""; }; EEAA9CE516E81C8400256B64 /* NSString+FixFilename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+FixFilename.h"; sourceTree = ""; }; EEAA9CE616E81C8400256B64 /* NSString+FixFilename.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+FixFilename.m"; sourceTree = ""; }; + EEDFE52716E8957200CD27E0 /* com.yukkurigames.Enjoyable.mapping.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = com.yukkurigames.Enjoyable.mapping.icns; sourceTree = ""; }; EEF86B7316E2241000674B87 /* NJInputPathElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NJInputPathElement.h; sourceTree = ""; }; EEF86B7416E298CD00674B87 /* NJEvents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NJEvents.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -209,6 +211,7 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + EEDFE52716E8957200CD27E0 /* com.yukkurigames.Enjoyable.mapping.icns */, EE03150C16E63481002B2DCE /* Help */, D5617A080FAEAF8300928B3A /* icon.icns */, 8D1107310486CEB800E47090 /* Info.plist */, @@ -304,6 +307,7 @@ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, D5F80A9D0FB0A2FF0006A4DE /* icon.icns in Resources */, EE03150D16E63481002B2DCE /* Help in Resources */, + EEDFE52816E8957200CD27E0 /* com.yukkurigames.Enjoyable.mapping.icns in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/EnjoyableApplicationDelegate.m b/EnjoyableApplicationDelegate.m index 5f79f20..59c3025 100644 --- a/EnjoyableApplicationDelegate.m +++ b/EnjoyableApplicationDelegate.m @@ -145,4 +145,11 @@ return menu; } +- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + NSURL *url = [NSURL fileURLWithPath:filename]; + [self.mappingsController addMappingWithContentsOfURL:url]; + return YES; +} + + @end diff --git a/Info.plist b/Info.plist index a27f386..c7a5027 100644 --- a/Info.plist +++ b/Info.plist @@ -2,6 +2,51 @@ + UTExportedTypeDeclarations + + + UTTypeIconFile + com.yukkurigames.Enjoyable.mapping.icns + UTTypeDescription + Enjoyable Mapping + UTTypeConformsTo + + public.data + + UTTypeIdentifier + com.yukkurigames.Enjoyable.mapping + UTTypeTagSpecification + + public.filename-extension + + enjoyable + + public.mime-type + application/json + + + + CFBundleDocumentTypes + + + CFBundleTypeIconFile + com.yukkurigames.Enjoyable.mapping.icns + CFBundleTypeExtensions + + enjoyable + + CFBundleTypeRole + Editor + LSItemContentTypes + + com.yukkurigames.Enjoyable.mapping + + CFBundleTypeName + com.yukkurigames.Enjoyable.mapping + LSHandlerRank + Owner + + CFBundleDevelopmentRegion English CFBundleExecutable diff --git a/NJMapping.h b/NJMapping.h index 069f132..5653745 100644 --- a/NJMapping.h +++ b/NJMapping.h @@ -20,4 +20,6 @@ - (NSDictionary *)serialize; - (BOOL)writeToURL:(NSURL *)url error:(NSError **)error; ++ (id)mappingWithContentsOfURL:(NSURL *)url mappings:(NSArray *)mappings error:(NSError **)error; + @end diff --git a/NJMapping.m b/NJMapping.m index 7df321c..8cc5724 100644 --- a/NJMapping.m +++ b/NJMapping.m @@ -8,6 +8,7 @@ #import "NJMapping.h" #import "NJInput.h" +#import "NJOutput.h" @implementation NJMapping @@ -53,4 +54,38 @@ return success; } ++ (id)mappingWithContentsOfURL:(NSURL *)url mappings:(NSArray *)mappings error:(NSError **)error { + NSInputStream *stream = [NSInputStream inputStreamWithURL:url]; + [stream open]; + NSDictionary *serialization = stream && !*error + ? [NSJSONSerialization JSONObjectWithStream:stream options:0 error:error] + : nil; + [stream close]; + + if (!serialization && error) + return nil; + + 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; + } + } + return mapping; +} + @end diff --git a/NJMappingsController.h b/NJMappingsController.h index 2f7ede0..b252904 100644 --- a/NJMappingsController.h +++ b/NJMappingsController.h @@ -29,6 +29,7 @@ - (NJMapping *)objectForKeyedSubscript:(NSString *)name; - (NJMapping *)objectAtIndexedSubscript:(NSUInteger)idx; +- (void)addMappingWithContentsOfURL:(NSURL *)url; - (void)activateMapping:(NJMapping *)mapping; - (void)activateMappingForProcess:(NSString *)processName; - (void)save; diff --git a/NJMappingsController.m b/NJMappingsController.m index ff9b42c..0adb1f9 100644 --- a/NJMappingsController.m +++ b/NJMappingsController.m @@ -32,8 +32,8 @@ } - (void)awakeFromNib { - [tableView registerForDraggedTypes:@[PB_ROW]]; - [tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; + [tableView registerForDraggedTypes:@[PB_ROW, NSURLPboardType]]; + [tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; } - (NJMapping *)objectForKeyedSubscript:(NSString *)name { @@ -219,6 +219,67 @@ return mapping; } +- (void)addMappingWithContentsOfURL:(NSURL *)url { + NSWindow *window = popoverActivate.window; + NSError *error; + NJMapping *mapping = [NJMapping mappingWithContentsOfURL:url + mappings:_mappings + 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) { + 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]; + } + + [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]; + } + } + + if (error) { + [window presentError:error + modalForWindow:window + delegate:nil + didPresentSelector:nil + contextInfo:nil]; + } +} + - (void)importPressed:(id)sender { NSOpenPanel *panel = [NSOpenPanel openPanel]; panel.allowedFileTypes = @[ @"enjoyable", @"json", @"txt" ]; @@ -227,65 +288,10 @@ 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]; - } - - [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]; - } - } - - if (error) { - [window presentError:error - modalForWindow:window - delegate:nil - didPresentSelector:nil - contextInfo:nil]; - } + [self addMappingWithContentsOfURL:panel.URL]; }]; - + } - (void)exportPressed:(id)sender { @@ -341,7 +347,7 @@ } } -- (BOOL)tableView:(NSTableView *)tableView +- (BOOL)tableView:(NSTableView *)tableView_ acceptDrop:(id )info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation { @@ -352,6 +358,20 @@ [_mappings moveObjectAtIndex:srcRow toIndex:row]; [self mappingsChanged]; return YES; + } else if ([pboard.types containsObject:NSURLPboardType]) { + NSURL *url = [NSURL URLFromPasteboard:pboard]; + NSError *error; + NJMapping *mapping = [NJMapping mappingWithContentsOfURL:url + mappings:_mappings + error:&error]; + if (error) { + [tableView_ presentError:error]; + return NO; + } else { + [_mappings insertObject:mapping atIndex:row]; + [self mappingsChanged]; + return YES; + } } else { return NO; } @@ -364,7 +384,15 @@ NSPasteboard *pboard = [info draggingPasteboard]; if ([pboard.types containsObject:PB_ROW]) { [tableView_ setDropRow:MAX(1, row) dropOperation:NSTableViewDropAbove]; - return NSDragOperationGeneric; + return NSDragOperationMove; + } else if ([pboard.types containsObject:NSURLPboardType]) { + NSURL *url = [NSURL URLFromPasteboard:pboard]; + if ([url.pathExtension isEqualToString:@"enjoyable"]) { + [tableView_ setDropRow:MAX(1, row) dropOperation:NSTableViewDropAbove]; + return NSDragOperationCopy; + } else { + return NSDragOperationNone; + } } else { return NSDragOperationNone; } diff --git a/com.yukkurigames.Enjoyable.mapping.icns b/com.yukkurigames.Enjoyable.mapping.icns new file mode 100644 index 0000000..027b5e2 Binary files /dev/null and b/com.yukkurigames.Enjoyable.mapping.icns differ diff --git a/icon.icns b/icon.icns index d9a900d..c7e3f36 100644 Binary files a/icon.icns and b/icon.icns differ