X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=blobdiff_plain;f=NJMappingsController.m;h=45085b5c79dc56abb97c1c7f2e494c753e9a276c;hp=567f2337930b242b38d8a080ec53f28501d9fb4d;hb=b8c30e0c06effb8f4a937378e5c4cf8a22f40c59;hpb=bf62b06504aeb5d111735f4a0f40b1b3f9e9e350 diff --git a/NJMappingsController.m b/NJMappingsController.m index 567f233..45085b5 100644 --- a/NJMappingsController.m +++ b/NJMappingsController.m @@ -13,9 +13,12 @@ #import "NJOutputController.h" #import "NJEvents.h" +#define PB_ROW @"com.yukkurigames.Enjoyable.MappingRow" + @implementation NJMappingsController { NSMutableArray *_mappings; NJMapping *manualMapping; + NSString *draggingName; } - (id)init { @@ -28,6 +31,11 @@ return self; } +- (void)awakeFromNib { + [tableView registerForDraggedTypes:@[PB_ROW, NSURLPboardType]]; + [tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; +} + - (NJMapping *)objectForKeyedSubscript:(NSString *)name { for (NJMapping *mapping in _mappings) if ([name isEqualToString:mapping.name]) @@ -43,6 +51,7 @@ [self save]; [tableView reloadData]; popoverActivate.title = _currentMapping.name; + [self updateInterfaceForCurrentMapping]; [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingListChanged object:_mappings]; @@ -71,6 +80,16 @@ } } +- (void)updateInterfaceForCurrentMapping { + NSUInteger selected = [_mappings indexOfObject:_currentMapping]; + [removeButton setEnabled:selected != 0]; + [moveDown setEnabled:selected && selected != _mappings.count - 1]; + [moveUp setEnabled:selected > 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; @@ -79,12 +98,8 @@ NSLog(@"Switching to mapping %@.", mapping.name); manualMapping = mapping; _currentMapping = mapping; - [removeButton setEnabled:_mappings[0] != mapping]; + [self updateInterfaceForCurrentMapping]; [outputController loadCurrent]; - popoverActivate.title = _currentMapping.name; - NSUInteger selected = [_mappings indexOfObject:mapping]; - [tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selected] byExtendingSelection:NO]; - [NSUserDefaults.standardUserDefaults setInteger:selected forKey:@"selected"]; [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged object:_currentMapping]; } @@ -92,8 +107,8 @@ - (IBAction)addPressed:(id)sender { NJMapping *newMapping = [[NJMapping alloc] initWithName:@"Untitled"]; [_mappings addObject:newMapping]; - [self mappingsChanged]; [self activateMapping:newMapping]; + [self mappingsChanged]; [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES]; } @@ -101,12 +116,13 @@ if (tableView.selectedRow == 0) return; - [_mappings removeObjectAtIndex:tableView.selectedRow]; + NSInteger selectedRow = tableView.selectedRow; + [_mappings removeObjectAtIndex:selectedRow]; + [self activateMapping:_mappings[MIN(selectedRow, _mappings.count - 1)]]; [self mappingsChanged]; - [self activateMapping:_mappings[0]]; } --(void)tableViewSelectionDidChange:(NSNotification *)notify { +- (void)tableViewSelectionDidChange:(NSNotification *)notify { [self activateMapping:self[tableView.selectedRow]]; } @@ -168,8 +184,8 @@ _mappings = newMappings; if (selected >= newMappings.count) selected = 0; - [self mappingsChanged]; [self activateMapping:_mappings[selected]]; + [self mappingsChanged]; } } @@ -204,6 +220,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" ]; @@ -212,66 +289,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 mappingsChanged]; - [self activateMapping:mapping]; - [outputController 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]; - } + [self addMappingWithContentsOfURL:panel.URL]; }]; - + } - (void)exportPressed:(id)sender { @@ -285,16 +306,8 @@ if (result != NSFileHandlingPanelOKButton) return; [panel close]; - [NSProcessInfo.processInfo disableSuddenTermination]; 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]; - - [NSProcessInfo.processInfo enableSuddenTermination]; + [mapping writeToURL:panel.URL error:&error]; if (error) { [window presentError:error modalForWindow:window @@ -306,7 +319,9 @@ } - (IBAction)mappingPressed:(id)sender { - [popover showRelativeToRect:popoverActivate.bounds ofView:popoverActivate preferredEdge:NSMinXEdge]; + [popover showRelativeToRect:popoverActivate.bounds + ofView:popoverActivate + preferredEdge:NSMinXEdge]; } - (void)popoverWillShow:(NSNotification *)notification { @@ -317,4 +332,101 @@ popoverActivate.state = NSOffState; } +- (IBAction)moveUpPressed:(id)sender { + NSUInteger idx = [_mappings indexOfObject:_currentMapping]; + if (idx > 1 && idx != NSNotFound) { + [_mappings exchangeObjectAtIndex:idx withObjectAtIndex:idx - 1]; + [self mappingsChanged]; + } +} + +- (IBAction)moveDownPressed:(id)sender { + NSUInteger idx = [_mappings indexOfObject:_currentMapping]; + if (idx < _mappings.count - 1) { + [_mappings exchangeObjectAtIndex:idx withObjectAtIndex:idx + 1]; + [self mappingsChanged]; + } +} + +- (BOOL)tableView:(NSTableView *)tableView_ + acceptDrop:(id )info + row:(NSInteger)row + dropOperation:(NSTableViewDropOperation)dropOperation { + NSPasteboard *pboard = [info draggingPasteboard]; + if ([pboard.types containsObject:PB_ROW]) { + NSString *value = [pboard stringForType:PB_ROW]; + NSUInteger srcRow = [value intValue]; + [_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; + } +} + +- (NSDragOperation)tableView:(NSTableView *)tableView_ + validateDrop:(id )info + proposedRow:(NSInteger)row + proposedDropOperation:(NSTableViewDropOperation)dropOperation { + NSPasteboard *pboard = [info draggingPasteboard]; + if ([pboard.types containsObject:PB_ROW]) { + [tableView_ setDropRow:MAX(1, row) dropOperation:NSTableViewDropAbove]; + 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; + } +} + +- (NSArray *)tableView:(NSTableView *)tableView_ +namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +forDraggedRowsWithIndexes:(NSIndexSet *)indexSet { + NJMapping *toSave = self[indexSet.firstIndex]; + NSString *filename = [[toSave.name stringByFixingPathComponent] + stringByAppendingPathExtension:@"enjoyable"]; + NSURL *dst = [dropDestination URLByAppendingPathComponent:filename]; + dst = [NSFileManager.defaultManager generateUniqueURLWithBase:dst]; + NSError *error; + if (![toSave writeToURL:dst error:&error]) { + [tableView_ presentError:error]; + return @[]; + } else { + return @[dst.lastPathComponent]; + } +} + +- (BOOL)tableView:(NSTableView *)tableView_ +writeRowsWithIndexes:(NSIndexSet *)rowIndexes + toPasteboard:(NSPasteboard *)pboard { + if (rowIndexes.count == 1 && rowIndexes.firstIndex != 0) { + [pboard declareTypes:@[PB_ROW, NSFilesPromisePboardType] owner:nil]; + [pboard setString:@(rowIndexes.firstIndex).stringValue forType:PB_ROW]; + [pboard setPropertyList:@[@"enjoyable"] forType:NSFilesPromisePboardType]; + return YES; + } else { + return NO; + } +} + @end