From: Joe Wreschnig Date: Thu, 7 Mar 2013 13:56:08 +0000 (+0100) Subject: Clean up root folder. X-Git-Tag: version-1.0~6 X-Git-Url: https://git.yukkurigames.com/?p=enjoyable.git;a=commitdiff_plain;h=0064c1fbff36795885a9724081af2a17d83c20a3 Clean up root folder. --- diff --git a/Categories/NSError+Description.h b/Categories/NSError+Description.h new file mode 100644 index 0000000..a722e9e --- /dev/null +++ b/Categories/NSError+Description.h @@ -0,0 +1,9 @@ +#import + +@interface NSError (Description) + ++ (NSError *)errorWithDomain:(NSString *)domain + code:(NSInteger)code + description:(NSString *)description; + +@end diff --git a/Categories/NSError+Description.m b/Categories/NSError+Description.m new file mode 100644 index 0000000..126bb17 --- /dev/null +++ b/Categories/NSError+Description.m @@ -0,0 +1,13 @@ +#import "NSError+Description.h" + +@implementation NSError (Description) + ++ (NSError *)errorWithDomain:(NSString *)domain + code:(NSInteger)code + description:(NSString *)description { + NSDictionary *errorDict = @{ NSLocalizedDescriptionKey : description }; + return [NSError errorWithDomain:domain code:code userInfo:errorDict]; + +} + +@end diff --git a/Categories/NSFileManager+UniqueNames.h b/Categories/NSFileManager+UniqueNames.h new file mode 100644 index 0000000..82c57d4 --- /dev/null +++ b/Categories/NSFileManager+UniqueNames.h @@ -0,0 +1,26 @@ +// +// NSFileManager+UniqueNames.h +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import + +@interface NSFileManager (UniqueNames) + +- (NSURL *)generateUniqueURLWithBase:(NSURL *)canonical; + // Generate a probably-unique URL by trying sequential indices, e.g. + // file://Test.txt + // file://Test (1).txt + // file://Test (2).txt + // and so on. + // + // The URL is only probably unique. It is subject to the usual + // race conditions associated with generating a filename before + // actually opening it. It also does not check remote resources, + // as it operates synchronously. Finally, it gives up after 10,000 + // indices. + +@end diff --git a/Categories/NSFileManager+UniqueNames.m b/Categories/NSFileManager+UniqueNames.m new file mode 100644 index 0000000..c7eb768 --- /dev/null +++ b/Categories/NSFileManager+UniqueNames.m @@ -0,0 +1,31 @@ +// +// NSFileManager+UniqueNames.m +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import "NSFileManager+UniqueNames.h" + +@implementation NSFileManager (UniqueNames) + +- (NSURL *)generateUniqueURLWithBase:(NSURL *)canonical { + // Punt for cases that are just too hard. + if (!canonical.isFileURL) + return canonical; + + NSString *trying = canonical.path; + NSString *dirname = [trying stringByDeletingLastPathComponent]; + NSString *basename = [trying.lastPathComponent stringByDeletingPathExtension]; + NSString *extension = trying.pathExtension; + int index = 1; + while ([self fileExistsAtPath:trying] && index < 10000) { + NSString *indexName = [NSString stringWithFormat:@"%@ (%d)", basename, index++]; + indexName = [indexName stringByAppendingPathExtension:extension]; + trying = [dirname stringByAppendingPathComponent:indexName]; + } + return [NSURL fileURLWithPath:trying]; +} + +@end diff --git a/Categories/NSMenu+RepresentedObjectAccessors.h b/Categories/NSMenu+RepresentedObjectAccessors.h new file mode 100644 index 0000000..ecb28a6 --- /dev/null +++ b/Categories/NSMenu+RepresentedObjectAccessors.h @@ -0,0 +1,47 @@ +// +// NSMenu+RepresentedObjectAccessors.h +// Enjoyable +// +// Created by Joe Wreschnig on 3/4/13. +// +// + +#import + +@interface NSMenu (RepresentedObjectAccessors) + // Helpers for using represented objects in menu items. + +- (NSMenuItem *)itemWithRepresentedObject:(id)object; + // Returns the first menu item in the receiver that has a given + // represented object. + +- (void)removeItemWithRepresentedObject:(id)object; + // Removes the first menu item representing the given object in the + // receiver. + // + // After it removes the menu item, this method posts an + // NSMenuDidRemoveItemNotification. + +- (NSMenuItem *)lastItem; + // Return the last menu item in the receiver, or nil if the menu + // has no items. + +- (void)removeLastItem; + // Removes the last menu item in the receiver, if there is one. + // + // After and if it removes the menu item, this method posts an + // NSMenuDidRemoveItemNotification. + +@end + +@interface NSPopUpButton (RepresentedObjectAccessors) + +- (NSMenuItem *)itemWithRepresentedObject:(id)object; + // Returns the first item in the receiver's menu that has a given + // represented object. + +- (void)selectItemWithRepresentedObject:(id)object; + // Selects the first item in the receiver's menu that has a give + // represented object. + +@end diff --git a/Categories/NSMenu+RepresentedObjectAccessors.m b/Categories/NSMenu+RepresentedObjectAccessors.m new file mode 100644 index 0000000..a63f083 --- /dev/null +++ b/Categories/NSMenu+RepresentedObjectAccessors.m @@ -0,0 +1,48 @@ +// +// NSMenu+RepresentedObjectAccessors.m +// Enjoyable +// +// Created by Joe Wreschnig on 3/4/13. +// +// + +#import "NSMenu+RepresentedObjectAccessors.h" + +@implementation NSMenu (RepresentedObjectAccessors) + +- (NSMenuItem *)itemWithRepresentedObject:(id)object { + for (NSMenuItem *item in self.itemArray) + if ([item.representedObject isEqual:object]) + return item; + return nil; +} + +- (void)removeItemWithRepresentedObject:(id)object { + NSInteger idx = [self indexOfItemWithRepresentedObject:object]; + if (idx != -1) + [self removeItemAtIndex:idx]; +} + +- (NSMenuItem *)lastItem { + return self.itemArray.lastObject; +} + +- (void)removeLastItem { + if (self.numberOfItems) + [self removeItemAtIndex:self.numberOfItems - 1]; +} + +@end + +@implementation NSPopUpButton (RepresentedObjectAccessors) + +- (NSMenuItem *)itemWithRepresentedObject:(id)object { + return [self.menu itemWithRepresentedObject:object]; +} + +- (void)selectItemWithRepresentedObject:(id)object { + [self selectItemAtIndex:[self indexOfItemWithRepresentedObject:object]]; +} + + +@end diff --git a/Categories/NSMutableArray+MoveObject.h b/Categories/NSMutableArray+MoveObject.h new file mode 100644 index 0000000..c1c60d2 --- /dev/null +++ b/Categories/NSMutableArray+MoveObject.h @@ -0,0 +1,15 @@ +// +// NSMutableArray+MoveObject.h +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import + +@interface NSMutableArray (MoveObject) + +- (void)moveObjectAtIndex:(NSUInteger)src toIndex:(NSUInteger)dst; + +@end diff --git a/Categories/NSMutableArray+MoveObject.m b/Categories/NSMutableArray+MoveObject.m new file mode 100644 index 0000000..8d9a4ee --- /dev/null +++ b/Categories/NSMutableArray+MoveObject.m @@ -0,0 +1,19 @@ +// +// NSMutableArray+MoveObject.m +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import "NSMutableArray+MoveObject.h" + +@implementation NSMutableArray (MoveObject) + +- (void)moveObjectAtIndex:(NSUInteger)src toIndex:(NSUInteger)dst { + id obj = self[src]; + [self removeObjectAtIndex:src]; + [self insertObject:obj atIndex:dst > src ? dst - 1 : dst]; +} + +@end diff --git a/Categories/NSString+FixFilename.h b/Categories/NSString+FixFilename.h new file mode 100644 index 0000000..b3e318f --- /dev/null +++ b/Categories/NSString+FixFilename.h @@ -0,0 +1,28 @@ +// +// NSString+FixFilename.h +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import + +@interface NSCharacterSet (FixFilename) + ++ (NSCharacterSet *)invalidPathComponentCharacterSet; + // A character set containing the characters that are invalid to + // use in path components on common filesystems. + +@end + +@interface NSString (FixFilename) + +- (NSString *)stringByFixingPathComponent; + // Does various operations to make this string suitable for use as + // a single path component of a normal filename. Removes + // characters that are invalid. Strips whitespace from the + // beginning and end. If the first character is a . or a -, a _ is + // added to the front. + +@end diff --git a/Categories/NSString+FixFilename.m b/Categories/NSString+FixFilename.m new file mode 100644 index 0000000..06a837c --- /dev/null +++ b/Categories/NSString+FixFilename.m @@ -0,0 +1,35 @@ +// +// NSString+FixFilename.m +// Enjoyable +// +// Created by Joe Wreschnig on 3/7/13. +// +// + +#import "NSString+FixFilename.h" + +@implementation NSCharacterSet (FixFilename) + ++ (NSCharacterSet *)invalidPathComponentCharacterSet { + return [NSCharacterSet characterSetWithCharactersInString:@"\"\\/:*?<>|"]; +} + +@end + +@implementation NSString (FixFilename) + +- (NSString *)stringByFixingPathComponent { + NSCharacterSet *invalid = NSCharacterSet.invalidPathComponentCharacterSet; + NSCharacterSet *whitespace = NSCharacterSet.whitespaceAndNewlineCharacterSet; + NSArray *parts = [self componentsSeparatedByCharactersInSet:invalid]; + NSString *name = [parts componentsJoinedByString:@"_"]; + name = [name stringByTrimmingCharactersInSet:whitespace]; + if (!name.length) + return @"_"; + unichar first = [name characterAtIndex:0]; + if (first == '.' || first == '-') + name = [@"_" stringByAppendingString:name]; + return name; +} + +@end diff --git a/Categories/NSView+FirstResponder.h b/Categories/NSView+FirstResponder.h new file mode 100644 index 0000000..cd89aa5 --- /dev/null +++ b/Categories/NSView+FirstResponder.h @@ -0,0 +1,11 @@ +#import + +@interface NSView (FirstResponder) + +- (BOOL)resignIfFirstResponder; + // Resign first responder status if this view is the active first + // responder in its window. Returns whether first responder status + // was resigned; YES if it was and NO if refused or the view was + // not the first responder. + +@end diff --git a/Categories/NSView+FirstResponder.m b/Categories/NSView+FirstResponder.m new file mode 100644 index 0000000..0575b0d --- /dev/null +++ b/Categories/NSView+FirstResponder.m @@ -0,0 +1,12 @@ +#import "NSView+FirstResponder.h" + +@implementation NSView (FirstResponder) + +- (BOOL)resignIfFirstResponder { + NSWindow *window = self.window; + return window.firstResponder == self + ? [window makeFirstResponder:nil] + : NO; +} + +@end diff --git a/Classes/EnjoyableApplicationDelegate.h b/Classes/EnjoyableApplicationDelegate.h new file mode 100644 index 0000000..b795a92 --- /dev/null +++ b/Classes/EnjoyableApplicationDelegate.h @@ -0,0 +1,23 @@ +// +// EnjoyableApplicationDelegate.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +@class NJDeviceController; +@class NJOutputController; +@class NJMappingsController; + +@interface EnjoyableApplicationDelegate : NSObject +{ + IBOutlet NSMenu *dockMenuBase; + IBOutlet NSWindow *window; +} + +@property (nonatomic, strong) IBOutlet NJDeviceController *inputController; +@property (nonatomic, strong) IBOutlet NJMappingsController *mappingsController; + +@end diff --git a/Classes/EnjoyableApplicationDelegate.m b/Classes/EnjoyableApplicationDelegate.m new file mode 100644 index 0000000..c477511 --- /dev/null +++ b/Classes/EnjoyableApplicationDelegate.m @@ -0,0 +1,155 @@ +// +// EnjoyableApplicationDelegate.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "EnjoyableApplicationDelegate.h" + +#import "NJMapping.h" +#import "NJMappingsController.h" +#import "NJDeviceController.h" +#import "NJOutputController.h" +#import "NJEvents.h" + +@implementation EnjoyableApplicationDelegate + +- (void)didSwitchApplication:(NSNotification *)note { + NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey]; + NSString *name = activeApp.localizedName; + if (!name) + name = activeApp.bundleIdentifier; + if (name && ![name isEqualToString:NSRunningApplication.currentApplication.localizedName]) + [self.mappingsController activateMappingForProcess:name]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(mappingDidChange:) + name:NJEventMappingChanged + object:nil]; + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(mappingListDidChange:) + name:NJEventMappingListChanged + object:nil]; + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(eventTranslationActivated:) + name:NJEventTranslationActivated + object:nil]; + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(eventTranslationDeactivated:) + name:NJEventTranslationDeactivated + object:nil]; + + [self.inputController setup]; + [self.mappingsController load]; +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification { + [window makeKeyAndOrderFront:nil]; +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication + hasVisibleWindows:(BOOL)flag { + [window makeKeyAndOrderFront:nil]; + return NO; +} + +- (void)eventTranslationActivated:(NSNotification *)note { + [NSProcessInfo.processInfo disableAutomaticTermination:@"Input translation is active."]; + [NSWorkspace.sharedWorkspace.notificationCenter + addObserver:self + selector:@selector(didSwitchApplication:) + name:NSWorkspaceDidActivateApplicationNotification + object:nil]; + NSLog(@"Listening for application changes."); +} + +- (void)eventTranslationDeactivated:(NSNotification *)note { + [NSProcessInfo.processInfo enableAutomaticTermination:@"Input translation is active."]; + [NSWorkspace.sharedWorkspace.notificationCenter + removeObserver:self + name:NSWorkspaceDidActivateApplicationNotification + object:nil]; + NSLog(@"Ignoring application changes."); +} + +- (void)mappingListDidChange:(NSNotification *)note { + NSArray *mappings = note.object; + while (dockMenuBase.lastItem.representedObject) + [dockMenuBase removeLastItem]; + int added = 0; + for (NJMapping *mapping in mappings) { + NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name + action:@selector(chooseMapping:) + keyEquivalent:keyEquiv]; + item.representedObject = mapping; + item.state = mapping == self.mappingsController.currentMapping; + [dockMenuBase addItem:item]; + } +} + +- (void)mappingDidChange:(NSNotification *)note { + NJMapping *current = note.object; + for (NSMenuItem *item in dockMenuBase.itemArray) + if (item.representedObject) + item.state = item.representedObject == current; +} + +- (void)chooseMapping:(NSMenuItem *)sender { + NJMapping *chosen = sender.representedObject; + [self.mappingsController activateMapping:chosen]; +} + +#define OUTPUT_PANE_MIN_WIDTH 390 + +- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset { + return proposedMax - OUTPUT_PANE_MIN_WIDTH; +} + +- (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize { + NSView *inputView = splitView.subviews[0]; + NSView *outputView = splitView.subviews[1]; + if (outputView.frame.size.width < OUTPUT_PANE_MIN_WIDTH) { + NSSize frameSize = splitView.frame.size; + CGFloat inputWidth = frameSize.width - OUTPUT_PANE_MIN_WIDTH - splitView.dividerThickness; + inputView.frame = NSMakeRect(inputWidth, frameSize.height, + inputView.frame.size.width, + inputView.frame.size.height); + outputView.frame = NSMakeRect(inputWidth + splitView.dividerThickness, + 0, + OUTPUT_PANE_MIN_WIDTH, + frameSize.height); + } else + [splitView adjustSubviews]; +} + +- (NSMenu *)applicationDockMenu:(NSApplication *)sender { + NSMenu *menu = [[NSMenu alloc] init]; + int added = 0; + for (NJMapping *mapping in self.mappingsController) { + NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name + action:@selector(chooseMapping:) + keyEquivalent:keyEquiv]; + item.representedObject = mapping; + item.state = mapping == self.mappingsController.currentMapping; + [menu addItem:item]; + } + return menu; +} + +- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { + NSURL *url = [NSURL fileURLWithPath:filename]; + [self.mappingsController addMappingWithContentsOfURL:url]; + return YES; +} + + +@end diff --git a/Classes/NJDevice.h b/Classes/NJDevice.h new file mode 100644 index 0000000..ec607ac --- /dev/null +++ b/Classes/NJDevice.h @@ -0,0 +1,26 @@ +// +// NJDevice.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJInputPathElement.h" + +@class NJInput; + +@interface NJDevice : NSObject + +@property (nonatomic, assign) int index; +@property (nonatomic, copy) NSString *productName; +@property (nonatomic, assign) IOHIDDeviceRef device; +@property (nonatomic, copy) NSArray *children; +@property (nonatomic, readonly) NSString *name; +@property (readonly) NSString *uid; + +- (id)initWithDevice:(IOHIDDeviceRef)device; +- (NJInput *)handlerForEvent:(IOHIDValueRef)value; +- (NJInput *)inputForEvent:(IOHIDValueRef)value; + +@end diff --git a/Classes/NJDevice.m b/Classes/NJDevice.m new file mode 100644 index 0000000..69775d8 --- /dev/null +++ b/Classes/NJDevice.m @@ -0,0 +1,109 @@ +// +// NJDevice.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "NJDevice.h" + +#import "NJInput.h" +#import "NJInputAnalog.h" +#import "NJInputHat.h" +#import "NJInputButton.h" + +static NSArray *InputsForElement(IOHIDDeviceRef device, id base) { + CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); + NSMutableArray *children = [NSMutableArray arrayWithCapacity:CFArrayGetCount(elements)]; + + int buttons = 0; + int axes = 0; + int hats = 0; + + for (int i = 0; i < CFArrayGetCount(elements); i++) { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); + int type = IOHIDElementGetType(element); + unsigned usage = IOHIDElementGetUsage(element); + unsigned usagePage = IOHIDElementGetUsagePage(element); + long max = IOHIDElementGetPhysicalMax(element); + long min = IOHIDElementGetPhysicalMin(element); + CFStringRef elName = IOHIDElementGetName(element); + + NJInput *input = nil; + + if (!(type == kIOHIDElementTypeInput_Misc + || type == kIOHIDElementTypeInput_Axis + || type == kIOHIDElementTypeInput_Button)) + continue; + + if (max - min == 1 || usagePage == kHIDPage_Button || type == kIOHIDElementTypeInput_Button) { + input = [[NJInputButton alloc] initWithName:(__bridge NSString *)elName + idx:++buttons + max:max]; + } else if (usage == kHIDUsage_GD_Hatswitch) { + input = [[NJInputHat alloc] initWithIndex:++hats]; + } else if (usage >= kHIDUsage_GD_X && usage <= kHIDUsage_GD_Rz) { + input = [[NJInputAnalog alloc] initWithIndex:++axes + rawMin:min + rawMax:max]; + } else { + continue; + } + + // TODO(jfw): Should be moved into better constructors. + input.base = base; + input.cookie = IOHIDElementGetCookie(element); + [children addObject:input]; + } + + CFRelease(elements); + return children; +} + +@implementation NJDevice { + int vendorId; + int productId; +} + +- (id)initWithDevice:(IOHIDDeviceRef)dev { + if ((self = [super init])) { + self.device = dev; + self.productName = (__bridge NSString *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductKey)); + vendorId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey)) intValue]; + productId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey)) intValue]; + self.children = InputsForElement(dev, self); + } + return self; +} + +- (NSString *)name { + return [NSString stringWithFormat:@"%@ #%d", _productName, _index]; +} + +- (id)base { + return nil; +} + +- (NSString *)uid { + return [NSString stringWithFormat: @"%d:%d:%d", vendorId, productId, _index]; +} + +- (NJInput *)findInputByCookie:(IOHIDElementCookie)cookie { + for (NJInput *child in _children) + if (child.cookie == cookie) + return child; + return nil; +} + +- (NJInput *)handlerForEvent:(IOHIDValueRef)value { + NJInput *mainInput = [self inputForEvent:value]; + return [mainInput findSubInputForValue:value]; +} + +- (NJInput *)inputForEvent:(IOHIDValueRef)value { + IOHIDElementRef elt = IOHIDValueGetElement(value); + IOHIDElementCookie cookie = IOHIDElementGetCookie(elt); + return [self findInputByCookie:cookie]; +} + +@end diff --git a/Classes/NJDeviceController.h b/Classes/NJDeviceController.h new file mode 100644 index 0000000..7658c64 --- /dev/null +++ b/Classes/NJDeviceController.h @@ -0,0 +1,31 @@ +// +// NJDeviceController.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +@class NJDevice; +@class NJInput; +@class NJMappingsController; +@class NJOutputController; + +@interface NJDeviceController : NSObject { + IBOutlet NSOutlineView *outlineView; + IBOutlet NJOutputController *outputController; + IBOutlet NJMappingsController *mappingsController; + IBOutlet NSButton *translatingEventsButton; + IBOutlet NSMenuItem *translatingEventsMenu; +} + +@property (nonatomic, readonly) NJInput *selectedInput; +@property (nonatomic, assign) NSPoint mouseLoc; +@property (nonatomic, assign) BOOL translatingEvents; + +- (void)setup; +- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device; + +- (IBAction)translatingEventsChanged:(id)sender; + +@end diff --git a/Classes/NJDeviceController.m b/Classes/NJDeviceController.m new file mode 100644 index 0000000..d553d7d --- /dev/null +++ b/Classes/NJDeviceController.m @@ -0,0 +1,243 @@ +// +// NJDeviceController.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "NJDeviceController.h" + +#import "NJMapping.h" +#import "NJMappingsController.h" +#import "NJDevice.h" +#import "NJInput.h" +#import "NJOutput.h" +#import "NJOutputController.h" +#import "NJEvents.h" + +@implementation NJDeviceController { + IOHIDManagerRef hidManager; + NSTimer *continuousTimer; + NSMutableArray *runningOutputs; + NSMutableArray *_devices; +} + +- (id)init { + if ((self = [super init])) { + _devices = [[NSMutableArray alloc] initWithCapacity:16]; + runningOutputs = [[NSMutableArray alloc] initWithCapacity:32]; + } + return self; +} + +- (void)dealloc { + [continuousTimer invalidate]; + IOHIDManagerClose(hidManager, kIOHIDOptionsTypeNone); + CFRelease(hidManager); +} + +- (void)expandRecursive:(id )pathElement { + if (pathElement) { + [self expandRecursive:pathElement.base]; + [outlineView expandItem:pathElement]; + } +} + +- (void)addRunningOutput:(NJOutput *)output { + if (![runningOutputs containsObject:output]) { + [runningOutputs addObject:output]; + } + if (!continuousTimer) { + continuousTimer = [NSTimer scheduledTimerWithTimeInterval:1.f/60.f + target:self + selector:@selector(updateContinuousInputs:) + userInfo:nil + repeats:YES]; + NSLog(@"Scheduled continuous output timer."); + } +} + +- (void)runOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value { + NJDevice *dev = [self findDeviceByRef:device]; + NJInput *mainInput = [dev inputForEvent:value]; + [mainInput notifyEvent:value]; + NSArray *children = mainInput.children ? mainInput.children : mainInput ? @[mainInput] : @[]; + for (NJInput *subInput in children) { + NJOutput *output = mappingsController.currentMapping[subInput]; + output.magnitude = subInput.magnitude; + output.running = subInput.active; + if ((output.running || output.magnitude) && output.isContinuous) + [self addRunningOutput:output]; + } +} + +- (void)showOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value { + NJDevice *dev = [self findDeviceByRef:device]; + NJInput *handler = [dev handlerForEvent:value]; + if (!handler) + return; + + [self expandRecursive:handler]; + [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[outlineView rowForItem:handler]] byExtendingSelection: NO]; + [outputController focusKey]; +} + +static void input_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDValueRef value) { + NJDeviceController *controller = (__bridge NJDeviceController *)ctx; + IOHIDDeviceRef device = IOHIDQueueGetDevice(inSender); + + if (controller.translatingEvents) { + [controller runOutputForDevice:device value:value]; + } else if ([NSApplication sharedApplication].mainWindow.isVisible) { + [controller showOutputForDevice:device value:value]; + } +} + +static int findAvailableIndex(NSArray *list, NJDevice *dev) { + for (int index = 1; ; index++) { + BOOL available = YES; + for (NJDevice *used in list) { + if ([used.productName isEqualToString:dev.productName] && used.index == index) { + available = NO; + break; + } + } + if (available) + return index; + } +} + +- (void)addDeviceForDevice:(IOHIDDeviceRef)device { + IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)self); + NJDevice *dev = [[NJDevice alloc] initWithDevice:device]; + dev.index = findAvailableIndex(_devices, dev); + [_devices addObject:dev]; + [outlineView reloadData]; +} + +static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) { + NJDeviceController *controller = (__bridge NJDeviceController *)ctx; + [controller addDeviceForDevice:device]; +} + +- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device { + for (NJDevice *dev in _devices) + if (dev.device == device) + return dev; + return nil; +} + +static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) { + NJDeviceController *controller = (__bridge NJDeviceController *)ctx; + [controller removeDeviceForDevice:device]; +} + +- (void)removeDeviceForDevice:(IOHIDDeviceRef)device { + NJDevice *match = [self findDeviceByRef:device]; + IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL); + if (match) { + [_devices removeObject:match]; + [outlineView reloadData]; + } + +} + +- (void)updateContinuousInputs:(NSTimer *)timer { + self.mouseLoc = [NSEvent mouseLocation]; + for (NJOutput *output in [runningOutputs copy]) { + if (![output update:self]) { + [runningOutputs removeObject:output]; + } + } + if (!runningOutputs.count) { + [continuousTimer invalidate]; + continuousTimer = nil; + NSLog(@"Unscheduled continuous output timer."); + } +} + +#define NSSTR(e) ((NSString *)CFSTR(e)) + +- (void)setup { + hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + NSArray *criteria = @[ @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), + NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick) }, + @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), + NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_GamePad) }, + @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), + NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_MultiAxisController) } + ]; + IOHIDManagerSetDeviceMatchingMultiple(hidManager, (__bridge CFArrayRef)criteria); + + IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOReturn ret = IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); + if (ret != kIOReturnSuccess) { + [[NSAlert alertWithMessageText:@"Input devices are unavailable" + defaultButton:nil + alternateButton:nil + otherButton:nil + informativeTextWithFormat:@"Error 0x%08x occured trying to access your devices. " + @"Input may not be correctly detected or mapped.", + ret] + beginSheetModalForWindow:outlineView.window + modalDelegate:nil + didEndSelector:nil + contextInfo:nil]; + } + + IOHIDManagerRegisterDeviceMatchingCallback(hidManager, add_callback, (__bridge void *)self); + IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (__bridge void *)self); +} + +- (NJInput *)selectedInput { + id item = [outlineView itemAtRow:outlineView.selectedRow]; + return (!item.children && item.base) ? item : nil; +} + +- (NSInteger)outlineView:(NSOutlineView *)outlineView + numberOfChildrenOfItem:(id )item { + return item ? item.children.count : _devices.count; +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView + isItemExpandable:(id )item { + return item ? [[item children] count] > 0: YES; +} + +- (id)outlineView:(NSOutlineView *)outlineView + child:(NSInteger)index + ofItem:(id )item { + return item ? item.children[index] : _devices[index]; +} + +- (id)outlineView:(NSOutlineView *)outlineView +objectValueForTableColumn:(NSTableColumn *)tableColumn + byItem:(id )item { + return item ? item.name : @"root"; +} + +- (void)outlineViewSelectionDidChange:(NSNotification *)notification { + + [outputController loadCurrent]; +} + +- (void)setTranslatingEvents:(BOOL)translatingEvents { + if (translatingEvents != _translatingEvents) { + _translatingEvents = translatingEvents; + NSInteger state = translatingEvents ? NSOnState : NSOffState; + translatingEventsButton.state = state; + translatingEventsMenu.title = translatingEvents ? @"Disable" : @"Enable"; + NSString *name = translatingEvents + ? NJEventTranslationActivated + : NJEventTranslationDeactivated; + [NSNotificationCenter.defaultCenter postNotificationName:name + object:self]; + } +} + +- (IBAction)translatingEventsChanged:(NSButton *)sender { + self.translatingEvents = sender.state == NSOnState; +} + + +@end diff --git a/Classes/NJInput.h b/Classes/NJInput.h new file mode 100644 index 0000000..380e858 --- /dev/null +++ b/Classes/NJInput.h @@ -0,0 +1,26 @@ +// +// NJInput.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJInputPathElement.h" + +@interface NJInput : NSObject + +@property (nonatomic, assign) IOHIDElementCookie cookie; +@property (nonatomic, copy) NSArray *children; +@property (nonatomic, weak) id base; +@property (nonatomic, copy) NSString *name; +@property (nonatomic, assign) BOOL active; +@property (nonatomic, assign) float magnitude; +@property (readonly) NSString *uid; + +- (id)initWithName:(NSString *)newName base:(id )newBase; + +- (void)notifyEvent:(IOHIDValueRef)value; +- (id)findSubInputForValue:(IOHIDValueRef)value; + +@end diff --git a/Classes/NJInput.m b/Classes/NJInput.m new file mode 100644 index 0000000..077dac8 --- /dev/null +++ b/Classes/NJInput.m @@ -0,0 +1,32 @@ +// +// NJInput.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "NJInput.h" + +@implementation NJInput + +- (id)initWithName:(NSString *)newName base:(id )newBase { + if ((self = [super init])) { + self.name = newName; + self.base = newBase; + } + return self; +} + +- (id)findSubInputForValue:(IOHIDValueRef)value { + return NULL; +} + +- (NSString *)uid { + return [NSString stringWithFormat:@"%@~%@", [_base uid], _name]; +} + +- (void)notifyEvent:(IOHIDValueRef)value { + [self doesNotRecognizeSelector:_cmd]; +} + +@end diff --git a/Classes/NJInputAnalog.h b/Classes/NJInputAnalog.h new file mode 100644 index 0000000..e79ed89 --- /dev/null +++ b/Classes/NJInputAnalog.h @@ -0,0 +1,17 @@ +// +// NJInputAnalog.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import + +#import "NJInput.h" + +@interface NJInputAnalog : NJInput + +- (id)initWithIndex:(int)index rawMin:(long)rawMin rawMax:(long)rawMax; + +@end diff --git a/Classes/NJInputAnalog.m b/Classes/NJInputAnalog.m new file mode 100644 index 0000000..7b4b15a --- /dev/null +++ b/Classes/NJInputAnalog.m @@ -0,0 +1,55 @@ +// +// NJInputAnalog.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#define DEAD_ZONE 0.3 + +#import "NJInputAnalog.h" + +static float normalize(long p, long min, long max) { + return 2 * (p - min) / (float)(max - min) - 1; +} + +@implementation NJInputAnalog { + float magnitude; + long rawMin; + long rawMax; +} + +- (id)initWithIndex:(int)index rawMin:(long)rawMin_ rawMax:(long)rawMax_ { + if ((self = [super init])) { + self.name = [[NSString alloc] initWithFormat: @"Axis %d", index]; + self.children = @[[[NJInput alloc] initWithName:@"Low" base:self], + [[NJInput alloc] initWithName:@"High" base:self]]; + rawMax = rawMax_; + rawMin = rawMin_; + } + return self; +} + +- (id)findSubInputForValue:(IOHIDValueRef)value { + float mag = normalize(IOHIDValueGetIntegerValue(value), rawMin, rawMax); + if (mag < -DEAD_ZONE) + return self.children[0]; + else if (mag > DEAD_ZONE) + return self.children[1]; + else + return nil; +} + +- (void)notifyEvent:(IOHIDValueRef)value { + magnitude = normalize(IOHIDValueGetIntegerValue(value), rawMin, rawMax); + [self.children[0] setMagnitude:fabsf(MIN(magnitude, 0))]; + [self.children[1] setMagnitude:fabsf(MAX(magnitude, 0))]; + [self.children[0] setActive:magnitude < -DEAD_ZONE]; + [self.children[1] setActive:magnitude > DEAD_ZONE]; +} + +- (float)magnitude { + return magnitude; +} + +@end diff --git a/Classes/NJInputButton.h b/Classes/NJInputButton.h new file mode 100644 index 0000000..31cba73 --- /dev/null +++ b/Classes/NJInputButton.h @@ -0,0 +1,15 @@ +// +// NJInputButton.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJInput.h" + +@interface NJInputButton : NJInput + +- (id)initWithName:(NSString *)name idx:(int)idx max:(long)max; + +@end diff --git a/Classes/NJInputButton.m b/Classes/NJInputButton.m new file mode 100644 index 0000000..0c3b0ce --- /dev/null +++ b/Classes/NJInputButton.m @@ -0,0 +1,34 @@ +// +// NJInputButton.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#import "NJInputButton.h" + +@implementation NJInputButton { + long _max; +} + +- (id)initWithName:(NSString *)name idx:(int)idx max:(long)max { + if ((self = [super init])) { + _max = max; + if (name.length) + self.name = [NSString stringWithFormat:@"Button %d - %@", idx, name]; + else + self.name = [NSString stringWithFormat:@"Button %d", idx]; + } + return self; +} + +- (id)findSubInputForValue:(IOHIDValueRef)val { + return (IOHIDValueGetIntegerValue(val) == _max) ? self : nil; +} + +- (void)notifyEvent:(IOHIDValueRef)value { + self.active = IOHIDValueGetIntegerValue(value) == _max; + self.magnitude = IOHIDValueGetIntegerValue(value) / (float)_max; +} + +@end diff --git a/Classes/NJInputHat.h b/Classes/NJInputHat.h new file mode 100644 index 0000000..5290d71 --- /dev/null +++ b/Classes/NJInputHat.h @@ -0,0 +1,15 @@ +// +// NJInputHat.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJInput.h" + +@interface NJInputHat : NJInput + +- (id)initWithIndex:(int)index; + +@end diff --git a/Classes/NJInputHat.m b/Classes/NJInputHat.m new file mode 100644 index 0000000..e0e127f --- /dev/null +++ b/Classes/NJInputHat.m @@ -0,0 +1,99 @@ +// +// NJInputHat.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#import "NJInputHat.h" + +static BOOL active_eightway[36] = { + NO, NO, NO, NO , // center + YES, NO, NO, NO , // N + YES, NO, NO, YES, // NE + NO, NO, NO, YES, // E + NO, YES, NO, YES, // SE + NO, YES, NO, NO , // S + NO, YES, YES, NO , // SW + NO, NO, YES, NO , // W + YES, NO, YES, NO , // NW +}; + +static BOOL active_fourway[20] = { + NO, NO, NO, NO , // center + YES, NO, NO, NO , // N + NO, NO, NO, YES, // E + NO, YES, NO, NO , // S + NO, NO, YES, NO , // W +}; + +@implementation NJInputHat + +- (id)initWithIndex:(int)index { + if ((self = [super init])) { + self.children = @[[[NJInput alloc] initWithName:@"Up" base:self], + [[NJInput alloc] initWithName:@"Down" base:self], + [[NJInput alloc] initWithName:@"Left" base:self], + [[NJInput alloc] initWithName:@"Right" base:self]]; + self.name = [NSString stringWithFormat:@"Hat Switch %d", index]; + } + return self; +} + +- (id)findSubInputForValue:(IOHIDValueRef)value { + long parsed = IOHIDValueGetIntegerValue(value); + switch (IOHIDElementGetLogicalMax(IOHIDValueGetElement(value))) { + case 7: // 8-way switch, 0-7. + switch (parsed) { + case 0: return self.children[0]; + case 4: return self.children[1]; + case 6: return self.children[2]; + case 2: return self.children[3]; + default: return nil; + } + case 8: // 8-way switch, 1-8 (neutral 0). + switch (parsed) { + case 1: return self.children[0]; + case 5: return self.children[1]; + case 7: return self.children[2]; + case 3: return self.children[3]; + default: return nil; + } + case 3: // 4-way switch, 0-3. + switch (parsed) { + case 0: return self.children[0]; + case 2: return self.children[1]; + case 3: return self.children[2]; + case 1: return self.children[3]; + default: return nil; + } + case 4: // 4-way switch, 1-4 (neutral 0). + switch (parsed) { + case 1: return self.children[0]; + case 3: return self.children[1]; + case 4: return self.children[2]; + case 2: return self.children[3]; + default: return nil; + } + default: + return nil; + } +} + +- (void)notifyEvent:(IOHIDValueRef)value { + long parsed = IOHIDValueGetIntegerValue(value); + long size = IOHIDElementGetLogicalMax(IOHIDValueGetElement(value)); + // Skip first row in table if 0 is not neutral. + if (size & 1) { + parsed++; + size++; + } + BOOL *activechildren = (size == 8) ? active_eightway : active_fourway; + for (unsigned i = 0; i < 4; i++) { + BOOL active = activechildren[parsed * 4 + i]; + [self.children[i] setActive:active]; + [self.children[i] setMagnitude:active]; + } +} + +@end diff --git a/Classes/NJInputPathElement.h b/Classes/NJInputPathElement.h new file mode 100644 index 0000000..8fe5c65 --- /dev/null +++ b/Classes/NJInputPathElement.h @@ -0,0 +1,9 @@ +#import + +@protocol NJInputPathElement + +- (NSArray *)children; +- (id ) base; +- (NSString *)name; + +@end diff --git a/Classes/NJKeyInputField.h b/Classes/NJKeyInputField.h new file mode 100644 index 0000000..ad026d3 --- /dev/null +++ b/Classes/NJKeyInputField.h @@ -0,0 +1,45 @@ +// +// NJKeyInputField.h +// Enjoyable +// +// Copyright 2013 Joe Wreschnig. +// + +#import + +extern CGKeyCode NJKeyInputFieldEmpty; + +@protocol NJKeyInputFieldDelegate; + +@interface NJKeyInputField : NSTextField + // An NJKeyInputField is a NSTextField-like widget that receives + // exactly one key press, and displays the name of that key, then + // resigns its first responder status. It can also inform a + // special delegate when its content changes. + ++ (NSString *)stringForKeyCode:(CGKeyCode)keyCode; + // Give the string name for a virtual key code. + +@property (nonatomic, weak) IBOutlet id keyDelegate; + +@property (nonatomic, assign) CGKeyCode keyCode; + // The currently displayed key code, or NJKeyInputFieldEmpty if no + // key is active. Changing this will update the display but not + // inform the delegate. + +@property (nonatomic, readonly) BOOL hasKeyCode; + // True if any key is active, false otherwise. + +- (void)clear; + // Clear the currently active key and call the delegate. + +@end + +@protocol NJKeyInputFieldDelegate + +- (void)keyInputField:(NJKeyInputField *)keyInput + didChangeKey:(CGKeyCode)keyCode; +- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput; + +@end + diff --git a/Classes/NJKeyInputField.m b/Classes/NJKeyInputField.m new file mode 100644 index 0000000..ec08150 --- /dev/null +++ b/Classes/NJKeyInputField.m @@ -0,0 +1,213 @@ +// +// NJKeyInputField.h +// Enjoyable +// +// Copyright 2013 Joe Wreschnig. +// + +#import "NJKeyInputField.h" + +CGKeyCode NJKeyInputFieldEmpty = 0xFFFF; + +@implementation NJKeyInputField + +- (id)initWithFrame:(NSRect)frameRect { + if ((self = [super initWithFrame:frameRect])) { + self.alignment = NSCenterTextAlignment; + [self setEditable:NO]; + [self setSelectable:NO]; + } + return self; +} + +- (void)clear { + self.keyCode = NJKeyInputFieldEmpty; + [self.keyDelegate keyInputFieldDidClear:self]; + [self resignIfFirstResponder]; +} + +- (BOOL)hasKeyCode { + return self.keyCode != NJKeyInputFieldEmpty; +} + ++ (NSString *)stringForKeyCode:(CGKeyCode)keyCode { + switch (keyCode) { + case 0xffff: return @""; + case 0x7a: return @"F1"; + case 0x78: return @"F2"; + case 0x63: return @"F3"; + case 0x76: return @"F4"; + case 0x60: return @"F5"; + case 0x61: return @"F6"; + case 0x62: return @"F7"; + case 0x64: return @"F8"; + case 0x65: return @"F9"; + case 0x6d: return @"F10"; + case 0x67: return @"F11"; + case 0x6f: return @"F12"; + case 0x69: return @"F13"; + case 0x6b: return @"F14"; + case 0x71: return @"F15"; + case 0x6a: return @"F16"; + case 0x40: return @"F17"; + case 0x4f: return @"F18"; + case 0x50: return @"F19"; + + case 0x35: return @"Esc"; + case 0x32: return @"`"; + + case 0x12: return @"1"; + case 0x13: return @"2"; + case 0x14: return @"3"; + case 0x15: return @"4"; + case 0x17: return @"5"; + case 0x16: return @"6"; + case 0x1a: return @"7"; + case 0x1c: return @"8"; + case 0x19: return @"9"; + case 0x1d: return @"0"; + case 0x1b: return @"-"; + case 0x18: return @"="; + + case 0x3f: return @"Fn"; + case 0x36: return @"Right Command"; + case 0x37: return @"Left Command"; + case 0x38: return @"Left Shift"; + case 0x39: return @"Caps Lock"; + case 0x3a: return @"Left Option"; + case 0x3b: return @"Left Control"; + case 0x3c: return @"Right Shift"; + case 0x3d: return @"Right Option"; + case 0x3e: return @"Right Control"; + + case 0x73: return @"Home"; + case 0x74: return @"Page Up"; + case 0x75: return @"Delete"; + case 0x77: return @"End"; + case 0x79: return @"Page Down"; + + case 0x30: return @"Tab"; + case 0x33: return @"Backspace"; + case 0x24: return @"Return"; + case 0x31: return @"Space"; + + case 0x0c: return @"Q"; + case 0x0d: return @"W"; + case 0x0e: return @"E"; + case 0x0f: return @"R"; + case 0x11: return @"T"; + case 0x10: return @"Y"; + case 0x20: return @"U"; + case 0x22: return @"I"; + case 0x1f: return @"O"; + case 0x23: return @"P"; + case 0x21: return @"["; + case 0x1e: return @"]"; + case 0x2a: return @"\\"; + case 0x00: return @"A"; + case 0x01: return @"S"; + case 0x02: return @"D"; + case 0x03: return @"F"; + case 0x05: return @"G"; + case 0x04: return @"H"; + case 0x26: return @"J"; + case 0x28: return @"K"; + case 0x25: return @"L"; + case 0x29: return @";"; + case 0x27: return @"'"; + case 0x06: return @"Z"; + case 0x07: return @"X"; + case 0x08: return @"C"; + case 0x09: return @"V"; + case 0x0b: return @"B"; + case 0x2d: return @"N"; + case 0x2e: return @"M"; + case 0x2b: return @","; + case 0x2f: return @"."; + case 0x2c: return @"/"; + + case 0x47: return @"Clear"; + case 0x51: return @"Keypad ="; + case 0x4b: return @"Keypad /"; + case 0x43: return @"Keypad *"; + case 0x59: return @"Keypad 7"; + case 0x5b: return @"Keypad 8"; + case 0x5c: return @"Keypad 9"; + case 0x4e: return @"Keypad -"; + case 0x56: return @"Keypad 4"; + case 0x57: return @"Keypad 5"; + case 0x58: return @"Keypad 6"; + case 0x45: return @"Keypad +"; + case 0x53: return @"Keypad 1"; + case 0x54: return @"Keypad 2"; + case 0x55: return @"Keypad 3"; + case 0x52: return @"Keypad 0"; + case 0x41: return @"Keypad ."; + case 0x4c: return @"Enter"; + + case 0x7e: return @"Up"; + case 0x7d: return @"Down"; + case 0x7b: return @"Left"; + case 0x7c: return @"Right"; + default: + return [[NSString alloc] initWithFormat:@"Key 0x%x", keyCode]; + } +} + +- (BOOL)acceptsFirstResponder { + return self.isEnabled; +} + +- (BOOL)becomeFirstResponder { + self.backgroundColor = NSColor.selectedTextBackgroundColor; + return [super becomeFirstResponder]; +} + +- (BOOL)resignFirstResponder { + self.backgroundColor = NSColor.textBackgroundColor; + return [super resignFirstResponder]; +} + +- (void)setKeyCode:(CGKeyCode)keyCode { + _keyCode = keyCode; + self.stringValue = [NJKeyInputField stringForKeyCode:keyCode]; +} + +- (void)keyDown:(NSEvent *)theEvent { + if (!theEvent.isARepeat) { + if ((theEvent.modifierFlags & NSAlternateKeyMask) + && theEvent.keyCode == 0x33) { + // Allow Alt+Backspace to clear the field. + self.keyCode = NJKeyInputFieldEmpty; + [self.keyDelegate keyInputFieldDidClear:self]; + } else if ((theEvent.modifierFlags & NSAlternateKeyMask) + && theEvent.keyCode == 0x35) { + // Allow Alt+Escape to cancel. + ; + } else { + self.keyCode = theEvent.keyCode; + [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; + } + [self resignIfFirstResponder]; + } +} + +- (void)mouseDown:(NSEvent *)theEvent { + if (self.acceptsFirstResponder) + [self.window makeFirstResponder:self]; +} + +- (void)flagsChanged:(NSEvent *)theEvent { + // Many keys are only available on MacBook keyboards by using the + // Fn modifier key (e.g. Fn+Left for Home), so delay processing + // modifiers until the up event is received in order to let the + // user type these virtual keys. However, there is no actual event + // for modifier key up - so detect it by checking to see if any + // modifiers are still down. + if (!(theEvent.modifierFlags & NSDeviceIndependentModifierFlagsMask)) { + self.keyCode = theEvent.keyCode; + [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; + } +} + +@end diff --git a/Classes/NJMapping.h b/Classes/NJMapping.h new file mode 100644 index 0000000..5653745 --- /dev/null +++ b/Classes/NJMapping.h @@ -0,0 +1,25 @@ +// +// NJMapping.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +@class NJOutput; +@class NJInput; + +@interface NJMapping : NSObject + +@property (nonatomic, copy) NSString *name; +@property (nonatomic, readonly) NSMutableDictionary *entries; + +- (id)initWithName:(NSString *)name; +- (NJOutput *)objectForKeyedSubscript:(NJInput *)input; +- (void)setObject:(NJOutput *)output forKeyedSubscript:(NJInput *)input; +- (NSDictionary *)serialize; +- (BOOL)writeToURL:(NSURL *)url error:(NSError **)error; + ++ (id)mappingWithContentsOfURL:(NSURL *)url mappings:(NSArray *)mappings error:(NSError **)error; + +@end diff --git a/Classes/NJMapping.m b/Classes/NJMapping.m new file mode 100644 index 0000000..8cc5724 --- /dev/null +++ b/Classes/NJMapping.m @@ -0,0 +1,91 @@ +// +// NJMapping.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "NJMapping.h" + +#import "NJInput.h" +#import "NJOutput.h" + +@implementation NJMapping + +- (id)initWithName:(NSString *)name { + if ((self = [super init])) { + self.name = name ? name : @"Untitled"; + _entries = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NJOutput *)objectForKeyedSubscript:(NJInput *)input { + return input ? _entries[input.uid] : nil; +} + +- (void)setObject:(NJOutput *)output forKeyedSubscript:(NJInput *)input { + if (input) { + if (output) + _entries[input.uid] = output; + else + [_entries removeObjectForKey:input.uid]; + } +} + +- (NSDictionary *)serialize { + NSMutableDictionary *entries = [[NSMutableDictionary alloc] initWithCapacity:_entries.count]; + for (id key in _entries) { + id serialized = [_entries[key] serialize]; + if (serialized) + entries[key] = serialized; + } + return @{ @"name": _name, @"entries": entries }; +} + +- (BOOL)writeToURL:(NSURL *)url error:(NSError **)error { + [NSProcessInfo.processInfo disableSuddenTermination]; + NSDictionary *serialization = [self serialize]; + NSData *json = [NSJSONSerialization dataWithJSONObject:serialization + options:NSJSONWritingPrettyPrinted + error:error]; + BOOL success = json && [json writeToURL:url options:NSDataWritingAtomic error:error]; + [NSProcessInfo.processInfo enableSuddenTermination]; + 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/Classes/NJMappingsController.h b/Classes/NJMappingsController.h new file mode 100644 index 0000000..b252904 --- /dev/null +++ b/Classes/NJMappingsController.h @@ -0,0 +1,46 @@ +// +// NJMappingsController.h +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +@class NJMapping; +@class NJOutputController; + +@interface NJMappingsController : NSObject +{ + IBOutlet NSButton *removeButton; + IBOutlet NSTableView *tableView; + IBOutlet NJOutputController *outputController; + IBOutlet NSButton *popoverActivate; + IBOutlet NSPopover *popover; + IBOutlet NSButton *moveUp; + IBOutlet NSButton *moveDown; +} + +@property (nonatomic, readonly) NJMapping *currentMapping; +@property (nonatomic, readonly) NSArray *mappings; + +- (NJMapping *)objectForKeyedSubscript:(NSString *)name; +- (NJMapping *)objectAtIndexedSubscript:(NSUInteger)idx; +- (void)addMappingWithContentsOfURL:(NSURL *)url; +- (void)activateMapping:(NJMapping *)mapping; +- (void)activateMappingForProcess:(NSString *)processName; +- (void)save; +- (void)load; + +- (IBAction)mappingPressed:(id)sender; +- (IBAction)addPressed:(id)sender; +- (IBAction)removePressed:(id)sender; +- (IBAction)moveUpPressed:(id)sender; +- (IBAction)moveDownPressed:(id)sender; +- (IBAction)importPressed:(id)sender; +- (IBAction)exportPressed:(id)sender; + +@end diff --git a/Classes/NJMappingsController.m b/Classes/NJMappingsController.m new file mode 100644 index 0000000..ea8137f --- /dev/null +++ b/Classes/NJMappingsController.m @@ -0,0 +1,436 @@ +// +// NJMappingsController.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import "NJMappingsController.h" + +#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; +} + +- (id)init { + if ((self = [super init])) { + _mappings = [[NSMutableArray alloc] init]; + _currentMapping = [[NJMapping alloc] initWithName:@"(default)"]; + manualMapping = _currentMapping; + [_mappings addObject:_currentMapping]; + } + 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]) + return mapping; + return nil; +} + +- (NJMapping *)objectAtIndexedSubscript:(NSUInteger)idx { + return idx < _mappings.count ? _mappings[idx] : nil; +} + +- (void)mappingsChanged { + [self save]; + [tableView reloadData]; + popoverActivate.title = _currentMapping.name; + [self updateInterfaceForCurrentMapping]; + [NSNotificationCenter.defaultCenter + postNotificationName:NJEventMappingListChanged + object:_mappings]; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(__unsafe_unretained id [])buffer + count:(NSUInteger)len { + return [_mappings countByEnumeratingWithState:state + objects:buffer + count:len]; +} + +- (void)activateMappingForProcess:(NSString *)processName { + if ([manualMapping.name.lowercaseString isEqualToString:@"@application"]) { + manualMapping.name = processName; + [self mappingsChanged]; + } else { + NJMapping *oldMapping = manualMapping; + NJMapping *newMapping = self[processName]; + if (!newMapping) + newMapping = oldMapping; + if (newMapping != _currentMapping) + [self activateMapping:newMapping]; + manualMapping = oldMapping; + } +} + +- (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; + if (mapping == _currentMapping) + return; + NSLog(@"Switching to mapping %@.", mapping.name); + manualMapping = mapping; + _currentMapping = mapping; + [self updateInterfaceForCurrentMapping]; + [outputController loadCurrent]; + [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged + object:_currentMapping]; +} + +- (IBAction)addPressed:(id)sender { + NJMapping *newMapping = [[NJMapping alloc] initWithName:@"Untitled"]; + [_mappings addObject:newMapping]; + [self activateMapping:newMapping]; + [self mappingsChanged]; + [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES]; +} + +- (IBAction)removePressed:(id)sender { + if (tableView.selectedRow == 0) + return; + + NSInteger selectedRow = tableView.selectedRow; + [_mappings removeObjectAtIndex:selectedRow]; + [self activateMapping:_mappings[MIN(selectedRow, _mappings.count - 1)]]; + [self mappingsChanged]; +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notify { + [self activateMapping:self[tableView.selectedRow]]; +} + +- (id)tableView:(NSTableView *)view objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)index { + return self[index].name; +} + +- (void)tableView:(NSTableView *)view + setObjectValue:(NSString *)obj + forTableColumn:(NSTableColumn *)col + row:(NSInteger)index { + self[index].name = obj; + [self mappingsChanged]; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return _mappings.count; +} + +- (BOOL)tableView:(NSTableView *)view shouldEditTableColumn:(NSTableColumn *)column row:(NSInteger)index { + return YES; +} + +- (void)save { + NSLog(@"Saving mappings to defaults."); + NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_mappings.count]; + for (NJMapping *mapping in _mappings) + [ary addObject:[mapping serialize]]; + [NSUserDefaults.standardUserDefaults setObject:ary forKey:@"mappings"]; +} + +- (void)load { + NSUInteger selected = [NSUserDefaults.standardUserDefaults integerForKey:@"selected"]; + NSArray *mappings = [NSUserDefaults.standardUserDefaults arrayForKey:@"mappings"]; + [self loadAllFrom:mappings andActivate:selected]; +} + +- (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 + 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; + } + } + + if (newMappings.count) { + _mappings = newMappings; + if (selected >= newMappings.count) + selected = 0; + [self activateMapping:_mappings[selected]]; + [self mappingsChanged]; + } +} + +- (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; + } + } + 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" ]; + NSWindow *window = NSApplication.sharedApplication.keyWindow; + [panel beginSheetModalForWindow:window + completionHandler:^(NSInteger result) { + if (result != NSFileHandlingPanelOKButton) + return; + [panel close]; + [self addMappingWithContentsOfURL:panel.URL]; + }]; + +} + +- (void)exportPressed:(id)sender { + NSSavePanel *panel = [NSSavePanel savePanel]; + panel.allowedFileTypes = @[ @"enjoyable" ]; + NJMapping *mapping = _currentMapping; + panel.nameFieldStringValue = [mapping.name stringByFixingPathComponent]; + NSWindow *window = NSApplication.sharedApplication.keyWindow; + [panel beginSheetModalForWindow:window + completionHandler:^(NSInteger result) { + if (result != NSFileHandlingPanelOKButton) + return; + [panel close]; + NSError *error; + [mapping writeToURL:panel.URL error:&error]; + if (error) { + [window presentError:error + modalForWindow:window + delegate:nil + didPresentSelector:nil + contextInfo:nil]; + } + }]; +} + +- (IBAction)mappingPressed:(id)sender { + [popover showRelativeToRect:popoverActivate.bounds + ofView:popoverActivate + preferredEdge:NSMinXEdge]; +} + +- (void)popoverWillShow:(NSNotification *)notification { + popoverActivate.state = NSOnState; +} + +- (void)popoverWillClose:(NSNotification *)notification { + 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 if (rowIndexes.count == 1 && rowIndexes.firstIndex == 0) { + [pboard declareTypes:@[NSFilesPromisePboardType] owner:nil]; + [pboard setPropertyList:@[@"enjoyable"] forType:NSFilesPromisePboardType]; + return YES; + } else { + return NO; + } +} + +@end diff --git a/Classes/NJOutput.h b/Classes/NJOutput.h new file mode 100644 index 0000000..b82c4e7 --- /dev/null +++ b/Classes/NJOutput.h @@ -0,0 +1,26 @@ +// +// NJOutput.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +@class NJDeviceController; + +@interface NJOutput : NSObject + +@property (nonatomic, assign) float magnitude; +@property (nonatomic, assign) BOOL running; +@property (nonatomic, readonly) BOOL isContinuous; + +- (void)trigger; +- (void)untrigger; +- (BOOL)update:(NJDeviceController *)jc; + +- (NSDictionary *)serialize; ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings; ++ (NSString *)serializationCode; + +@end diff --git a/Classes/NJOutput.m b/Classes/NJOutput.m new file mode 100644 index 0000000..409b3e6 --- /dev/null +++ b/Classes/NJOutput.m @@ -0,0 +1,87 @@ +// +// NJOutput.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#import "NJOutput.h" + +#import "NJOutputKeyPress.h" +#import "NJOutputMapping.h" +#import "NJOutputMouseMove.h" +#import "NJOutputMouseButton.h" +#import "NJOutputMouseScroll.h" + +@implementation NJOutput { + BOOL running; +} + ++ (NSString *)serializationCode { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (NSDictionary *)serialize { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:NJOutput.class] + && [[self serialize] isEqual:[object serialize]]; +} + +- (NSUInteger)hash { + return [[self serialize] hash]; +} + ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + // Don't crash loading old/bad mappings (but don't load them either). + if (![serialization isKindOfClass:NSDictionary.class]) + return nil; + NSString *type = serialization[@"type"]; + for (Class cls in @[NJOutputKeyPress.class, + NJOutputMapping.class, + NJOutputMouseMove.class, + NJOutputMouseButton.class, + NJOutputMouseScroll.class + ]) { + if ([type isEqualToString:cls.serializationCode]) + return [cls outputDeserialize:serialization withMappings:mappings]; + } + + return nil; +} + +- (void)trigger { +} + +- (void)untrigger { +} + +- (BOOL)update:(NJDeviceController *)jc { + return NO; +} + +- (BOOL)isContinuous { + return NO; +} + +- (BOOL)running { + return running; +} + +- (void)setRunning:(BOOL)newRunning { + if (running != newRunning) { + running = newRunning; + if (running) + [self trigger]; + else + [self untrigger]; + } +} + + +@end diff --git a/Classes/NJOutputController.h b/Classes/NJOutputController.h new file mode 100644 index 0000000..f8052e7 --- /dev/null +++ b/Classes/NJOutputController.h @@ -0,0 +1,42 @@ +// +// NJOutputController.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJKeyInputField.h" + +@class NJMappingsController; +@class NJDeviceController; +@class NJOutput; +@class NJOutputMouseMove; + +@interface NJOutputController : NSObject { + IBOutlet NJKeyInputField *keyInput; + IBOutlet NSMatrix *radioButtons; + IBOutlet NSSegmentedControl *mouseDirSelect; + IBOutlet NSSlider *mouseSpeedSlider; + IBOutlet NSSegmentedControl *mouseBtnSelect; + IBOutlet NSSegmentedControl *scrollDirSelect; + IBOutlet NSSlider *scrollSpeedSlider; + IBOutlet NSTextField *title; + IBOutlet NSPopUpButton *mappingPopup; + IBOutlet NJMappingsController *mappingsController; + IBOutlet NJDeviceController *inputController; +} + +@property (assign) BOOL enabled; + +- (void)loadCurrent; +- (IBAction)radioChanged:(id)sender; +- (IBAction)mdirChanged:(id)sender; +- (IBAction)mbtnChanged:(id)sender; +- (IBAction)sdirChanged:(id)sender; +- (IBAction)mouseSpeedChanged:(id)sender; +- (IBAction)scrollSpeedChanged:(id)sender; + +- (void)focusKey; + +@end diff --git a/Classes/NJOutputController.m b/Classes/NJOutputController.m new file mode 100644 index 0000000..bcafe0d --- /dev/null +++ b/Classes/NJOutputController.m @@ -0,0 +1,283 @@ +// +// NJOutputController.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#import "NJOutputController.h" + +#import "NJMappingsController.h" +#import "NJMapping.h" +#import "NJInput.h" +#import "NJEvents.h" +#import "NJDeviceController.h" +#import "NJKeyInputField.h" +#import "NJOutputMapping.h" +#import "NJOutputController.h" +#import "NJOutputKeyPress.h" +#import "NJOutputMouseButton.h" +#import "NJOutputMouseMove.h" +#import "NJOutputMouseScroll.h" + +@implementation NJOutputController + +- (id)init { + if ((self = [super init])) { + [NSNotificationCenter.defaultCenter + addObserver:self + selector:@selector(mappingListDidChange:) + name:NJEventMappingListChanged + object:nil]; + } + return self; +} + +- (void)dealloc { + [NSNotificationCenter.defaultCenter removeObserver:self]; +} + +- (void)cleanUpInterface { + NSInteger row = radioButtons.selectedRow; + + if (row != 1) { + keyInput.keyCode = NJKeyInputFieldEmpty; + [keyInput resignIfFirstResponder]; + } + + if (row != 2) { + [mappingPopup selectItemAtIndex:-1]; + [mappingPopup resignIfFirstResponder]; + } else if (!mappingPopup.selectedItem) + [mappingPopup selectItemAtIndex:0]; + + if (row != 3) { + mouseDirSelect.selectedSegment = -1; + mouseSpeedSlider.floatValue = mouseSpeedSlider.minValue; + [mouseDirSelect resignIfFirstResponder]; + } else { + if (mouseDirSelect.selectedSegment == -1) + mouseDirSelect.selectedSegment = 0; + if (!mouseSpeedSlider.floatValue) + mouseSpeedSlider.floatValue = 4; + } + + if (row != 4) { + mouseBtnSelect.selectedSegment = -1; + [mouseBtnSelect resignIfFirstResponder]; + } else if (mouseBtnSelect.selectedSegment == -1) + mouseBtnSelect.selectedSegment = 0; + + if (row != 5) { + scrollDirSelect.selectedSegment = -1; + scrollSpeedSlider.floatValue = scrollSpeedSlider.minValue; + [scrollDirSelect resignIfFirstResponder]; + } else { + if (scrollDirSelect.selectedSegment == -1) + scrollDirSelect.selectedSegment = 0; + if (scrollDirSelect.selectedSegment < 2 + && !scrollSpeedSlider.floatValue) + scrollSpeedSlider.floatValue = 15.f; + else if (scrollDirSelect.selectedSegment >= 2 + && scrollSpeedSlider.floatValue) + scrollSpeedSlider.floatValue = scrollSpeedSlider.minValue; + } + +} + +- (IBAction)radioChanged:(NSView *)sender { + [sender.window makeFirstResponder:sender]; + if (radioButtons.selectedRow == 1) + [keyInput.window makeFirstResponder:keyInput]; + [self commit]; +} + +- (void)keyInputField:(NJKeyInputField *)keyInput didChangeKey:(CGKeyCode)keyCode { + [radioButtons selectCellAtRow:1 column:0]; + [radioButtons.window makeFirstResponder:radioButtons]; + [self commit]; +} + +- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput { + [radioButtons selectCellAtRow:0 column:0]; + [self commit]; +} + +- (void)mappingChosen:(id)sender { + [radioButtons selectCellAtRow:2 column:0]; + [mappingPopup.window makeFirstResponder:mappingPopup]; + [self commit]; +} + +- (void)mdirChanged:(NSView *)sender { + [radioButtons selectCellAtRow:3 column:0]; + [sender.window makeFirstResponder:sender]; + [self commit]; +} + +- (void)mouseSpeedChanged:(NSSlider *)sender { + [radioButtons selectCellAtRow:3 column:0]; + [sender.window makeFirstResponder:sender]; + [self commit]; +} + +- (void)mbtnChanged:(NSView *)sender { + [radioButtons selectCellAtRow:4 column:0]; + [sender.window makeFirstResponder:sender]; + [self commit]; +} + +- (void)sdirChanged:(NSView *)sender { + [radioButtons selectCellAtRow:5 column:0]; + [sender.window makeFirstResponder:sender]; + [self commit]; +} + +- (void)scrollSpeedChanged:(NSSlider *)sender { + [radioButtons selectCellAtRow:5 column:0]; + [sender.window makeFirstResponder:sender]; + if (!sender.floatValue && scrollDirSelect.selectedSegment < 2) + scrollDirSelect.selectedSegment += 2; + else if (sender.floatValue && scrollDirSelect.selectedSegment >= 2) + scrollDirSelect.selectedSegment -= 2; + [self commit]; +} + +- (NJOutput *)currentOutput { + return mappingsController.currentMapping[inputController.selectedInput]; +} + +- (NJOutput *)makeOutput { + switch (radioButtons.selectedRow) { + case 0: + return nil; + case 1: + if (keyInput.hasKeyCode) { + NJOutputKeyPress *k = [[NJOutputKeyPress alloc] init]; + k.vk = keyInput.keyCode; + return k; + } else { + return nil; + } + break; + case 2: { + NJOutputMapping *c = [[NJOutputMapping alloc] init]; + c.mapping = mappingsController[mappingPopup.indexOfSelectedItem]; + return c; + } + case 3: { + NJOutputMouseMove *mm = [[NJOutputMouseMove alloc] init]; + mm.axis = mouseDirSelect.selectedSegment; + mm.speed = mouseSpeedSlider.floatValue; + return mm; + } + case 4: { + NJOutputMouseButton *mb = [[NJOutputMouseButton alloc] init]; + mb.button = mouseBtnSelect.selectedSegment == 0 ? kCGMouseButtonLeft : kCGMouseButtonRight; + return mb; + } + case 5: { + NJOutputMouseScroll *ms = [[NJOutputMouseScroll alloc] init]; + ms.direction = (scrollDirSelect.selectedSegment & 1) ? 1 : -1; + ms.speed = scrollDirSelect.selectedSegment < 2 + ? scrollSpeedSlider.floatValue + : 0.f; + return ms; + } + default: + return nil; + } +} + +- (void)commit { + [self cleanUpInterface]; + mappingsController.currentMapping[inputController.selectedInput] = [self makeOutput]; + [mappingsController save]; +} + +- (BOOL)enabled { + return [radioButtons isEnabled]; +} + +- (void)setEnabled:(BOOL)enabled { + [radioButtons setEnabled:enabled]; + [keyInput setEnabled:enabled]; + [mappingPopup setEnabled:enabled]; + [mouseDirSelect setEnabled:enabled]; + [mouseSpeedSlider setEnabled:enabled]; + [mouseBtnSelect setEnabled:enabled]; + [scrollDirSelect setEnabled:enabled]; + [scrollSpeedSlider setEnabled:enabled]; +} + +- (void)loadOutput:(NJOutput *)output forInput:(NJInput *)input { + if (!input) { + self.enabled = NO; + title.stringValue = @""; + } else { + self.enabled = YES; + NSString *inpFullName = input.name; + for (id cur = input.base; cur; cur = cur.base) { + inpFullName = [[NSString alloc] initWithFormat:@"%@ > %@", cur.name, inpFullName]; + } + title.stringValue = inpFullName; + } + + if ([output isKindOfClass:NJOutputKeyPress.class]) { + [radioButtons selectCellAtRow:1 column:0]; + keyInput.keyCode = [(NJOutputKeyPress*)output vk]; + } else if ([output isKindOfClass:NJOutputMapping.class]) { + [radioButtons selectCellAtRow:2 column:0]; + NSMenuItem *item = [mappingPopup itemWithRepresentedObject:[(NJOutputMapping *)output mapping]]; + [mappingPopup selectItem:item]; + if (!item) + [radioButtons selectCellAtRow:self.enabled ? 0 : -1 column:0]; + } + else if ([output isKindOfClass:NJOutputMouseMove.class]) { + [radioButtons selectCellAtRow:3 column:0]; + mouseDirSelect.selectedSegment = [(NJOutputMouseMove *)output axis]; + mouseSpeedSlider.floatValue = [(NJOutputMouseMove *)output speed]; + } + else if ([output isKindOfClass:NJOutputMouseButton.class]) { + [radioButtons selectCellAtRow:4 column:0]; + mouseBtnSelect.selectedSegment = [(NJOutputMouseButton *)output button] == kCGMouseButtonLeft ? 0 : 1; + } + else if ([output isKindOfClass:NJOutputMouseScroll.class]) { + [radioButtons selectCellAtRow:5 column:0]; + int direction = [(NJOutputMouseScroll *)output direction]; + float speed = [(NJOutputMouseScroll *)output speed]; + scrollDirSelect.selectedSegment = (direction > 0) + !speed * 2; + scrollSpeedSlider.floatValue = speed; + } else { + [radioButtons selectCellAtRow:self.enabled ? 0 : -1 column:0]; + } + [self cleanUpInterface]; +} + +- (void)loadCurrent { + [self loadOutput:self.currentOutput forInput:inputController.selectedInput]; +} + +- (void)focusKey { + if (radioButtons.selectedRow <= 1) + [keyInput.window makeFirstResponder:keyInput]; + else + [keyInput resignIfFirstResponder]; +} + +- (void)mappingListDidChange:(NSNotification *)note { + NSArray *mappings = note.object; + NJMapping *current = mappingPopup.selectedItem.representedObject; + [mappingPopup.menu removeAllItems]; + for (NJMapping *mapping in mappings) { + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name + action:@selector(mappingChosen:) + keyEquivalent:@""]; + item.target = self; + item.representedObject = mapping; + [mappingPopup.menu addItem:item]; + } + [mappingPopup selectItemWithRepresentedObject:current]; +} + +@end diff --git a/Classes/NJOutputKeyPress.h b/Classes/NJOutputKeyPress.h new file mode 100644 index 0000000..052b010 --- /dev/null +++ b/Classes/NJOutputKeyPress.h @@ -0,0 +1,15 @@ +// +// NJOutputKeyPress.h +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJOutput.h" + +@interface NJOutputKeyPress : NJOutput + +@property (nonatomic, assign) CGKeyCode vk; + +@end diff --git a/Classes/NJOutputKeyPress.m b/Classes/NJOutputKeyPress.m new file mode 100644 index 0000000..622cf41 --- /dev/null +++ b/Classes/NJOutputKeyPress.m @@ -0,0 +1,43 @@ +// +// NJOutputKeyPress.m +// Enjoy +// +// Created by Sam McCall on 5/05/09. +// + +#import "NJOutputKeyPress.h" + +#import "NJKeyInputField.h" + +@implementation NJOutputKeyPress + ++ (NSString *)serializationCode { + return @"key press"; +} + +- (NSDictionary *)serialize { + return _vk != NJKeyInputFieldEmpty + ? @{ @"type": self.class.serializationCode, @"key": @(_vk) } + : nil; +} + ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + NJOutputKeyPress *output = [[NJOutputKeyPress alloc] init]; + output.vk = [serialization[@"key"] intValue]; + return output; +} + +- (void)trigger { + CGEventRef keyDown = CGEventCreateKeyboardEvent(NULL, _vk, YES); + CGEventPost(kCGHIDEventTap, keyDown); + CFRelease(keyDown); +} + +- (void)untrigger { + CGEventRef keyUp = CGEventCreateKeyboardEvent(NULL, _vk, NO); + CGEventPost(kCGHIDEventTap, keyUp); + CFRelease(keyUp); +} + +@end diff --git a/Classes/NJOutputMapping.h b/Classes/NJOutputMapping.h new file mode 100644 index 0000000..e8c3029 --- /dev/null +++ b/Classes/NJOutputMapping.h @@ -0,0 +1,17 @@ +// +// NJOutputMapping.h +// Enjoy +// +// Created by Sam McCall on 6/05/09. +// Copyright 2009 University of Otago. All rights reserved. +// + +#import "NJOutput.h" + +@class NJMapping; + +@interface NJOutputMapping : NJOutput + +@property (nonatomic, weak) NJMapping *mapping; + +@end diff --git a/Classes/NJOutputMapping.m b/Classes/NJOutputMapping.m new file mode 100644 index 0000000..fa8fda1 --- /dev/null +++ b/Classes/NJOutputMapping.m @@ -0,0 +1,44 @@ +// +// NJOutputMapping.m +// Enjoy +// +// Created by Sam McCall on 6/05/09. +// + +#import "NJOutputMapping.h" + +#import "EnjoyableApplicationDelegate.h" +#import "NJMapping.h" +#import "NJMappingsController.h" + +@implementation NJOutputMapping + ++ (NSString *)serializationCode { + return @"mapping"; +} + +- (NSDictionary *)serialize { + return _mapping + ? @{ @"type": self.class.serializationCode, @"name": _mapping.name } + : nil; +} + ++ (NJOutputMapping *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + NSString *name = serialization[@"name"]; + NJOutputMapping *output = [[NJOutputMapping alloc] init]; + for (NJMapping *mapping in mappings) { + if ([mapping.name isEqualToString:name]) { + output.mapping = mapping; + return output; + } + } + return nil; +} + +- (void)trigger { + EnjoyableApplicationDelegate *ctrl = (EnjoyableApplicationDelegate *)NSApplication.sharedApplication.delegate; + [ctrl.mappingsController activateMapping:_mapping]; +} + +@end diff --git a/Classes/NJOutputMouseButton.h b/Classes/NJOutputMouseButton.h new file mode 100644 index 0000000..4d790e5 --- /dev/null +++ b/Classes/NJOutputMouseButton.h @@ -0,0 +1,14 @@ +// +// NJOutputMouseButton.h +// Enjoy +// +// Created by Yifeng Huang on 7/27/12. +// + +#import "NJOutput.h" + +@interface NJOutputMouseButton : NJOutput + +@property (nonatomic, assign) CGMouseButton button; + +@end diff --git a/Classes/NJOutputMouseButton.m b/Classes/NJOutputMouseButton.m new file mode 100644 index 0000000..d5b1f16 --- /dev/null +++ b/Classes/NJOutputMouseButton.m @@ -0,0 +1,51 @@ +// +// NJOutputMouseButton.m +// Enjoy +// +// Created by Yifeng Huang on 7/27/12. +// + +#import "NJOutputMouseButton.h" + +@implementation NJOutputMouseButton + ++ (NSString *)serializationCode { + return @"mouse button"; +} + +- (NSDictionary *)serialize { + return @{ @"type": self.class.serializationCode, @"button": @(_button) }; +} + ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + NJOutputMouseButton *output = [[NJOutputMouseButton alloc] init]; + output.button = [serialization[@"button"] intValue]; + return output; +} + +- (void)trigger { + CGFloat height = NSScreen.mainScreen.frame.size.height; + NSPoint mouseLoc = NSEvent.mouseLocation; + CGEventType eventType = (_button == kCGMouseButtonLeft) ? kCGEventLeftMouseDown : kCGEventRightMouseDown; + CGEventRef click = CGEventCreateMouseEvent(NULL, + eventType, + CGPointMake(mouseLoc.x, height - mouseLoc.y), + _button); + CGEventPost(kCGHIDEventTap, click); + CFRelease(click); +} + +- (void)untrigger { + CGFloat height = NSScreen.mainScreen.frame.size.height; + NSPoint mouseLoc = NSEvent.mouseLocation; + CGEventType eventType = (_button == kCGMouseButtonLeft) ? kCGEventLeftMouseUp : kCGEventRightMouseUp; + CGEventRef click = CGEventCreateMouseEvent(NULL, + eventType, + CGPointMake(mouseLoc.x, height - mouseLoc.y), + _button); + CGEventPost(kCGHIDEventTap, click); + CFRelease(click); +} + +@end diff --git a/Classes/NJOutputMouseMove.h b/Classes/NJOutputMouseMove.h new file mode 100644 index 0000000..428416f --- /dev/null +++ b/Classes/NJOutputMouseMove.h @@ -0,0 +1,15 @@ +// +// NJOutputMouseMove.h +// Enjoy +// +// Created by Yifeng Huang on 7/26/12. +// + +#import "NJOutput.h" + +@interface NJOutputMouseMove : NJOutput + +@property (nonatomic, assign) int axis; +@property (nonatomic, assign) float speed; + +@end diff --git a/Classes/NJOutputMouseMove.m b/Classes/NJOutputMouseMove.m new file mode 100644 index 0000000..884a3d3 --- /dev/null +++ b/Classes/NJOutputMouseMove.m @@ -0,0 +1,76 @@ +// +// NJOutputMouseMove.m +// Enjoy +// +// Created by Yifeng Huang on 7/26/12. +// + +#import "NJOutputMouseMove.h" + +#import "NJDeviceController.h" + +@implementation NJOutputMouseMove + ++ (NSString *)serializationCode { + return @"mouse move"; +} + +- (NSDictionary *)serialize { + return @{ @"type": self.class.serializationCode, + @"axis": @(_axis), + @"speed": @(_speed), + }; +} + ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + NJOutputMouseMove *output = [[NJOutputMouseMove alloc] init]; + output.axis = [serialization[@"axis"] intValue]; + output.speed = [serialization[@"speed"] floatValue]; + if (!output.speed) + output.speed = 4; + return output; +} + +- (BOOL)isContinuous { + return YES; +} + +- (BOOL)update:(NJDeviceController *)jc { + if (self.magnitude < 0.05) + return NO; // dead zone + + CGFloat height = NSScreen.mainScreen.frame.size.height; + + float dx = 0.f, dy = 0.f; + switch (_axis) { + case 0: + dx = -self.magnitude * _speed; + break; + case 1: + dx = self.magnitude * _speed; + break; + case 2: + dy = -self.magnitude * _speed; + break; + case 3: + dy = self.magnitude * _speed; + break; + } + NSPoint mouseLoc = jc.mouseLoc; + mouseLoc.x += dx; + mouseLoc.y -= dy; + jc.mouseLoc = mouseLoc; + + CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, + CGPointMake(mouseLoc.x, height - mouseLoc.y), + 0); + CGEventSetType(move, kCGEventMouseMoved); + CGEventSetIntegerValueField(move, kCGMouseEventDeltaX, (int)dx); + CGEventSetIntegerValueField(move, kCGMouseEventDeltaY, (int)dy); + CGEventPost(kCGHIDEventTap, move); + CFRelease(move); + return YES; +} + +@end diff --git a/Classes/NJOutputMouseScroll.h b/Classes/NJOutputMouseScroll.h new file mode 100644 index 0000000..dd0444a --- /dev/null +++ b/Classes/NJOutputMouseScroll.h @@ -0,0 +1,15 @@ +// +// NJOutputMouseScroll.h +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// + +#import "NJOutput.h" + +@interface NJOutputMouseScroll : NJOutput + +@property (nonatomic, assign) int direction; +@property (nonatomic, assign) float speed; + +@end diff --git a/Classes/NJOutputMouseScroll.m b/Classes/NJOutputMouseScroll.m new file mode 100644 index 0000000..812685f --- /dev/null +++ b/Classes/NJOutputMouseScroll.m @@ -0,0 +1,61 @@ +// +// NJOutputMouseScroll.m +// Enjoy +// +// Created by Yifeng Huang on 7/28/12. +// + +#import "NJOutputMouseScroll.h" + +@implementation NJOutputMouseScroll + ++ (NSString *)serializationCode { + return @"mouse scroll"; +} + +- (NSDictionary *)serialize { + return @{ @"type": self.class.serializationCode, + @"direction": @(_direction), + @"speed": @(_speed) + }; +} + ++ (NJOutput *)outputDeserialize:(NSDictionary *)serialization + withMappings:(NSArray *)mappings { + NJOutputMouseScroll *output = [[NJOutputMouseScroll alloc] init]; + output.direction = [serialization[@"direction"] intValue]; + output.speed = [serialization[@"direction"] floatValue]; + return output; +} + +- (BOOL)isContinuous { + return !!self.speed; +} + +- (void)trigger { + if (!self.speed) { + CGEventRef scroll = CGEventCreateScrollWheelEvent(NULL, + kCGScrollEventUnitLine, + 1, + _direction); + CGEventPost(kCGHIDEventTap, scroll); + CFRelease(scroll); + } +} + +- (BOOL)update:(NJDeviceController *)jc { + if (self.magnitude < 0.05f) + return NO; // dead zone + + int amount = (int)(_speed * self.magnitude * _direction); + CGEventRef scroll = CGEventCreateScrollWheelEvent(NULL, + kCGScrollEventUnitPixel, + 1, + amount); + CGEventPost(kCGHIDEventTap, scroll); + CFRelease(scroll); + + return YES; +} + +@end diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings deleted file mode 100644 index 84fe32f..0000000 Binary files a/English.lproj/InfoPlist.strings and /dev/null differ diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib deleted file mode 100644 index cd9a1b9..0000000 --- a/English.lproj/MainMenu.xib +++ /dev/null @@ -1,3084 +0,0 @@ - - - - 1080 - 12C2034 - 3084 - 1187.34 - 625.00 - - com.apple.InterfaceBuilder.CocoaPlugin - 3084 - - - NSBox - NSButton - NSButtonCell - NSCustomObject - NSCustomView - NSMatrix - NSMenu - NSMenuItem - NSOutlineView - NSPopUpButton - NSPopUpButtonCell - NSPopover - NSScrollView - NSScroller - NSSegmentedCell - NSSegmentedControl - NSSlider - NSSliderCell - NSSplitView - NSTableColumn - NSTableView - NSTextField - NSTextFieldCell - NSToolbar - NSToolbarFlexibleSpaceItem - NSToolbarItem - NSView - NSViewController - NSWindowTemplate - - - com.apple.InterfaceBuilder.CocoaPlugin - - - PluginDependencyRecalculationVersion - - - - - NSApplication - - - FirstResponder - - - NSApplication - - - AMainMenu - - - - Enjoyable - CA - 1048576 - 2147483647 - - NSImage - NSMenuCheckmark - - - NSImage - NSMenuMixedState - - submenuAction: - - Enjoyable - - - - About Enjoyable - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Services - - 1048576 - 2147483647 - - - submenuAction: - - Services - - _NSServicesMenu - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Hide Enjoyable - h - 1048576 - 2147483647 - - - - - - Hide Others - h - 1572864 - 2147483647 - - - - - - Show All - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Quit Enjoyable - q - 1048576 - 2147483647 - - - - - _NSAppleMenu - - - - - Mappings - - 1048576 - 2147483647 - - - submenuAction: - - Mappings - - - - Enable - r - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - - - - List - l - 1048576 - 2147483647 - - - - - - Import… - o - 1048576 - 2147483647 - - - - - - Export… - s - 1048576 - 2147483647 - - - - - - YES - YES - - - 2147483647 - - - 1 - - - - - - - Window - - 1048576 - 2147483647 - - - submenuAction: - - Window - - - - Minimize - m - 1048576 - 2147483647 - - - - - - Zoom - - 1048576 - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - - - - Bring All to Front - - 1048576 - 2147483647 - - - - - _NSWindowsMenu - - - - - Help - - 2147483647 - - - submenuAction: - - Help - - - - Enjoyable Help - ? - 1048576 - 2147483647 - - - - - _NSHelpMenu - - - - _NSMainMenu - - - 15 - 2 - {{355, 59}, {640, 320}} - 1685585920 - Enjoyable - NSWindow - - - AC1F5C48-4C16-4C9D-9779-B783AF35E2E1 - - - YES - YES - NO - YES - 2 - 1 - - - - 2CB21E35-9CF1-4C67-9670-31139C914D10 - - Enabled - Enabled - - - - 268 - {{7, 14}, {36, 25}} - _NS:9 - YES - - 67108864 - 134217728 - - - LucidaGrande - 13 - 1044 - - _NS:9 - - -1228128256 - 163 - - NSImage - NSRightFacingTriangleTemplate - - - - 200 - 25 - - NO - - - - - {36, 25} - {36, 25} - YES - YES - 0 - YES - 0 - - - - 4AC66688-76E8-47ED-AC0A-7462220A4019 - - Mapping Selector - Mapping Selector - - - - 268 - {{0, 14}, {140, 25}} - _NS:9 - YES - - 67108864 - 134217728 - (default) - - _NS:9 - - 918306816 - 163 - - NSImage - NSListViewTemplate - - - - 400 - 75 - - NO - - - - - {13, 25} - {141, 25} - YES - NO - 0 - YES - 0 - - - NSToolbarFlexibleSpaceItem - - Flexible Space - - - - - - {1, 5} - {20000, 32} - YES - YES - -1 - YES - 0 - - YES - YES - - - 1048576 - 2147483647 - - - - - - - - - - - - - - - - - - - {640, 320} - - - 256 - - - - 274 - - - - 256 - - - - 274 - - - - 2304 - - - - 256 - {200, 318} - - - - YES - NO - YES - - - 256 - {{474, 0}, {16, 17}} - - - - 197 - 16 - 1000 - - 75497536 - 2048 - - - LucidaGrande - 11 - 3100 - - - 3 - MC4zMzMzMzI5OQA - - - 6 - System - headerTextColor - - 3 - MAA - - - - - 67108928 - 2624 - Text Cell - - - - 6 - System - controlBackgroundColor - - 3 - MC42NjY2NjY2NjY3AA - - - - 6 - System - controlTextColor - - - - 1 - YES - - - - 3 - 2 - - 3 - MQA - - - 6 - System - gridColor - - 3 - MC41AA - - - 20 - 306184192 - - - 4 - 15 - 0 - YES - 0 - 1 - -1 - NO - - - {{1, 1}, {200, 318}} - - - - - - 4 - - - - -2147483392 - {{1, 1}, {8, 298}} - - - - NO - - _doScroller: - 0.4210526 - - - - -2147483392 - {{-100, -100}, {473, 15}} - - - - NO - 1 - - _doScroller: - 0.99789030000000001 - - - {202, 320} - - - - 150034 - - - - QSAAAEEgAABBsAAAQbAAAA - 0.25 - 4 - 1 - - - {202, 320} - - - - NSView - - - - 256 - - - - 265 - {{228, 21}, {130, 12}} - - - - _NS:9 - YES - - -2080374784 - 262144 - - _NS:9 - - 30 - 0.0 - 15 - 0.0 - 0 - 1 - NO - NO - - NO - - - - 265 - {{228, 107}, {176, 12}} - - - - _NS:9 - YES - - -2080374784 - 262144 - - _NS:9 - - 20 - 0.0 - 4 - 0.0 - 0 - 1 - NO - NO - - NO - - - - 265 - {{226, 117}, {180, 20}} - - - - _NS:9 - YES - - 67108864 - 131072 - - _NS:9 - - - - 44 - ← - YES - 0 - - - 44 - → - 1 - 0 - - - 42 - ↑ - 0 - - - 41 - ↓ - 0 - - - 1 - - NO - - - - 265 - {{226, 31}, {180, 20}} - - - - _NS:9 - YES - - 67108864 - 131072 - - _NS:9 - - - - 64 - ↑ - Scroll up continuously - YES - 0 - - - 63 - ↓ - Scroll down continuously - 1 - 0 - - - ⤒ - Scroll up one step - 0 - - - ⤓ - Scroll down one step - 0 - - - 1 - - NO - - - - 265 - {{226, 67}, {180, 24}} - - - - _NS:9 - YES - - 67108864 - 0 - - LucidaGrande - 13 - 16 - - _NS:9 - - - - 87 - Left - YES - 0 - - - 86 - Right - 1 - 0 - - - 1 - - NO - - - - 265 - {{228, 197}, {176, 24}} - - - - _NS:9 - NJKeyInputField - - - - 265 - {{225, 152}, {182, 26}} - - - - YES - - -1539309504 - 2048 - - - 109199360 - 129 - - - 400 - 75 - - YES - - OtherViews - - - -1 - 1 - YES - YES - 2 - - NO - - - - 268 - {{20, 16}, {200, 256}} - - - - NO - 6 - 1 - - - 603979776 - 0 - Do nothing - - - 1 - 1211912448 - 0 - - NSImage - NSRadioButton - - - NSRadioButton - - - - 200 - 25 - - - 603979776 - 0 - Press a key - - - 1211912448 - 0 - - - - 400 - 75 - - - 603979776 - 0 - Switch to mapping - - - 1211912448 - 0 - - - - 400 - 75 - - - 603979776 - 0 - Move the mouse - - - 1211912448 - 0 - - - - 400 - 75 - - - 603979776 - 0 - Press a mouse button - - - 1211912448 - 0 - - - - 400 - 75 - - - 603979776 - 0 - Scroll the mouse - - - 1211912448 - 0 - - - - 400 - 75 - - - {200, 41} - {4, 2} - 1353195520 - NSActionCell - - 603979776 - 0 - Radio - - 1211912448 - 0 - - - - 400 - 75 - - -1 - -1 - - 6 - System - controlColor - - - - - - - - 266 - {{0, 289}, {429, 17}} - - - - YES - - 67108928 - 138414656 - - - LucidaGrande-Bold - 13 - 16 - - No input selected - - - - - NO - - - - 10 - {{12, 278}, {405, 5}} - - - - {0, 0} - - 67108864 - 0 - Box - - - 6 - System - textBackgroundColor - - - - 3 - MCAwLjgwMDAwMDAxAA - - - 3 - 2 - 0 - NO - - - {{211, 0}, {429, 320}} - - - - NSView - - - {640, 320} - - - - YES - Main Split - - YES - - - - - - - - - - - - {640, 320} - - - - - {{0, 0}, {1440, 878}} - {640, 375} - {10000000000000, 10000000000000} - Enjoyable - YES - - - - 256 - - - - 274 - - - - 2304 - - - - 256 - {198, 198} - - - - YES - NO - YES - - - 256 - {{306, 0}, {16, 17}} - - - - 190 - 190 - 190 - - 75497536 - 2048 - - - - 3 - MC4zMzMzMzI5OQA - - - - - 337641536 - 2048 - Text Cell - - - - - - YES - - - - 3 - 2 - - - 20 - 44072960 - - - 1 - 15 - 0 - NO - 0 - 1 - -1 - - - {{1, 1}, {198, 198}} - - - - - - 4 - - - - -2147483392 - {{306, 1}, {15, 403}} - - - - NO - - _doScroller: - 0.99766359999999998 - - - - -2147483392 - {{-100, -100}, {366, 16}} - - - - NO - 1 - - _doScroller: - 0.98123324396782841 - - - {{0, 20}, {200, 200}} - - - - 150034 - - - - QSAAAEEgAABBsAAAQbAAAA - 0.25 - 4 - 1 - - - - 268 - {{66, -1}, {68, 23}} - - - - _NS:22 - YES - - -2080374784 - 168034304 - - - LucidaGrande - 9 - 3614 - - _NS:22 - - 1221349376 - 162 - - - 400 - 75 - - NO - - - - 292 - {{0, -1}, {34, 23}} - - - - YES - - 67108864 - 134479872 - - - - -2033958912 - 268435618 - - NSImage - NSAddTemplate - - - n - 400 - 75 - - NO - - - - 292 - {{166, -1}, {34, 23}} - - - - YES - - 67108864 - 134479872 - ⬇ - - - -2033434624 - 268435618 - -  - 400 - 75 - - NO - - - - 292 - {{133, -1}, {34, 23}} - - - - YES - - 67108864 - 134479872 - ⬆ - - - -2033434624 - 268435618 - -  - 400 - 75 - - NO - - - - 292 - {{33, -1}, {34, 23}} - - - - YES - - 67108864 - 134479872 - - - - -2033958912 - 268435618 - - NSImage - NSRemoveTemplate - - - CA - 400 - 75 - - NO - - - {200, 220} - - - - NSView - - - EnjoyableApplicationDelegate - - - NJMappingsController - - - NJDeviceController - - - NJOutputController - - - - - 0 - 1 - 0.0 - 0.0 - YES - - - - - - - terminate: - - - - 449 - - - - delegate - - - - 483 - - - - dockMenu - - - - 732 - - - - showHelp: - - - - 870 - - - - orderFrontStandardAboutPanel: - - - - 142 - - - - performMiniaturize: - - - - 37 - - - - arrangeInFront: - - - - 39 - - - - performZoom: - - - - 240 - - - - hide: - - - - 367 - - - - hideOtherApplications: - - - - 368 - - - - unhideAllApplications: - - - - 370 - - - - delegate - - - - 517 - - - - dataSource - - - - 518 - - - - outlineView - - - - 648 - - - - mappingsController - - - - 822 - - - - outputController - - - - 826 - - - - translatingEventsMenu - - - - 877 - - - - translatingEventsChanged: - - - - 878 - - - - translatingEventsButton - - - - 879 - - - - dockMenuBase - - - - 726 - - - - inputController - - - - 819 - - - - mappingsController - - - - 820 - - - - window - - - - 865 - - - - removePressed: - - - - 516 - - - - removeButton - - - - 519 - - - - tableView - - - - 520 - - - - exportPressed: - - - - 815 - - - - importPressed: - - - - 816 - - - - outputController - - - - 827 - - - - mappingPressed: - - - - 855 - - - - popover - - - - 856 - - - - popoverActivate - - - - 857 - - - - addPressed: - - - - 515 - - - - moveUpPressed: - - - - 899 - - - - moveDownPressed: - - - - 900 - - - - moveUp - - - - 901 - - - - moveDown - - - - 902 - - - - dataSource - - - - 647 - - - - delegate - - - - 696 - - - - delegate - - - - 892 - - - - radioButtons - - - - 692 - - - - title - - - - 709 - - - - radioChanged: - - - - 731 - - - - mouseBtnSelect - - - - 746 - - - - mbtnChanged: - - - - 747 - - - - scrollDirSelect - - - - 751 - - - - sdirChanged: - - - - 752 - - - - mouseDirSelect - - - - 756 - - - - mdirChanged: - - - - 757 - - - - keyInput - - - - 781 - - - - mappingsController - - - - 821 - - - - mappingPopup - - - - 823 - - - - inputController - - - - 828 - - - - mouseSpeedChanged: - - - - 885 - - - - mouseSpeedSlider - - - - 886 - - - - scrollSpeedChanged: - - - - 890 - - - - scrollSpeedSlider - - - - 891 - - - - keyDelegate - - - - 818 - - - - performClick: - - - - 871 - - - - view - - - - 854 - - - - contentViewController - - - - 852 - - - - delegate - - - - 853 - - - - performClick: - - - - 880 - - - - - - 0 - - - - - - -2 - - - File's Owner - - - -1 - - - First Responder - - - -3 - - - Application - - - 29 - - - - - - - - - Main Menu - - - 19 - - - - - - - - 56 - - - - - - - - 83 - - - - - - - - 81 - - - - - - - - - - - - - 57 - - - - - - - - - - - - - - - - 58 - - - - - 134 - - - - - 150 - - - - - 136 - - - Quit Enjoy - - - 144 - - - - - 236 - - - - - 131 - - - - - - - - 149 - - - - - 145 - - - - - 130 - - - - - 24 - - - - - - - - - - - 92 - - - - - 5 - - - - - 239 - - - - - 23 - - - - - 450 - - - - - - - - - 451 - - - - - - - - - - - Mapping List Popover Content - - - 453 - - - - - - - - 456 - - - - - - - - - - 457 - - - - - 458 - - - - - 459 - - - - - - Mapping List - - - 461 - - - - - - - - 464 - - - - - 482 - - - - - 487 - - - - - - - - - - 511 - - - - - - Remove Mapping - - - 512 - - - - - 514 - - - - - 479 - - - - - 606 - - - - - 652 - - - - - - - - - 653 - - - - - - Input Devices Pane - - - 634 - - - - - - - - - - 637 - - - - - - Input Device List - - - 636 - - - - - 635 - - - - - 639 - - - - - - Device/Input Name - - - 642 - - - - - 654 - - - - - - - - - - - - - - - Output Editor Pane - - - 686 - - - - - 656 - - - - - - - - - - - - Output Types - - - 657 - - - Disabled - - - 658 - - - Key Press - - - 699 - - - Switch Mapping - - - 700 - - - - - - Mapping Choice List - - - 701 - - - - - - - - 702 - - - - - - 706 - - - - - - Input Name - - - 707 - - - - - 708 - - - - - 723 - - - - - 733 - - - Mouse Movement - - - 734 - - - Mouse Button - - - 735 - - - Mouse Scroll - - - 659 - - - - - 744 - - - - - - Mouse Button Selector - - - 745 - - - - - 749 - - - - - - Mouse Scroll Selector - - - 750 - - - - - 754 - - - - - - Mouse Motion Selector - - - 755 - - - - - 778 - - - - - 812 - - - - - 813 - - - - - 814 - - - - - 837 - - - - - - Mapping Selector - - - 835 - - - - - - - - 836 - - - - - 849 - - - - - 850 - - - Popover View Controller - - - 851 - - - Mapping List Popover - - - 507 - - - - - - Add Mapping - - - 508 - - - - - 862 - - - - - - Gradient Space - - - 863 - - - - - 810 - - - - - 866 - - - - - - - - 867 - - - - - - - - 868 - - - - - 874 - - - - - - - - 872 - - - - - - - - 873 - - - - - 883 - - - - - - - - 884 - - - - - 887 - - - - - - - - 888 - - - - - 893 - - - - - - Move Mapping Up - - - 894 - - - - - 896 - - - - - - Move Mapping Down - - - 897 - - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - com.apple.InterfaceBuilder.CocoaPlugin - {{114, 276}, {770, 487}} - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Create a new mapping - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Remove the selected mapping - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Change the active mapping - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Enable mapped actions - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Maximum mouse speed - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Mouse scroll speed - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Move the selected mapping up the list - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - ToolTip - - ToolTip - - Move the selected mapping down the list - - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - - - - - 902 - - - - - EnjoyableApplicationDelegate - NSObject - - NSMenu - NJDeviceController - NJMappingsController - NSWindow - - - - dockMenuBase - NSMenu - - - inputController - NJDeviceController - - - mappingsController - NJMappingsController - - - window - NSWindow - - - - IBProjectSource - ./Classes/EnjoyableApplicationDelegate.h - - - - NJDeviceController - NSObject - - translatingEventsChanged: - id - - - translatingEventsChanged: - - translatingEventsChanged: - id - - - - NJMappingsController - NSOutlineView - NJOutputController - NSButton - NSMenuItem - - - - mappingsController - NJMappingsController - - - outlineView - NSOutlineView - - - outputController - NJOutputController - - - translatingEventsButton - NSButton - - - translatingEventsMenu - NSMenuItem - - - - IBProjectSource - ./Classes/NJDeviceController.h - - - - NJKeyInputField - NSTextField - - keyDelegate - id - - - keyDelegate - - keyDelegate - id - - - - IBProjectSource - ./Classes/NJKeyInputField.h - - - - NJMappingsController - NSObject - - id - id - id - id - id - id - id - - - - addPressed: - id - - - exportPressed: - id - - - importPressed: - id - - - mappingPressed: - id - - - moveDownPressed: - id - - - moveUpPressed: - id - - - removePressed: - id - - - - NSButton - NSButton - NJOutputController - NSPopover - NSButton - NSButton - NSTableView - - - - moveDown - NSButton - - - moveUp - NSButton - - - outputController - NJOutputController - - - popover - NSPopover - - - popoverActivate - NSButton - - - removeButton - NSButton - - - tableView - NSTableView - - - - IBProjectSource - ./Classes/NJMappingsController.h - - - - NJOutputController - NSObject - - id - id - id - id - id - id - - - - mbtnChanged: - id - - - mdirChanged: - id - - - mouseSpeedChanged: - id - - - radioChanged: - id - - - scrollSpeedChanged: - id - - - sdirChanged: - id - - - - NJDeviceController - NJKeyInputField - NSPopUpButton - NJMappingsController - NSSegmentedControl - NSSegmentedControl - NSSlider - NSMatrix - NSSegmentedControl - NSSlider - NSTextField - - - - inputController - NJDeviceController - - - keyInput - NJKeyInputField - - - mappingPopup - NSPopUpButton - - - mappingsController - NJMappingsController - - - mouseBtnSelect - NSSegmentedControl - - - mouseDirSelect - NSSegmentedControl - - - mouseSpeedSlider - NSSlider - - - radioButtons - NSMatrix - - - scrollDirSelect - NSSegmentedControl - - - scrollSpeedSlider - NSSlider - - - title - NSTextField - - - - IBProjectSource - ./Classes/NJOutputController.h - - - - - 0 - IBCocoaFramework - YES - 3 - - {8, 8} - {11, 10} - {11, 11} - {10, 3} - {16, 15} - {8, 8} - {9, 9} - - - diff --git a/EnjoyableApplicationDelegate.h b/EnjoyableApplicationDelegate.h deleted file mode 100644 index b795a92..0000000 --- a/EnjoyableApplicationDelegate.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// EnjoyableApplicationDelegate.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class NJDeviceController; -@class NJOutputController; -@class NJMappingsController; - -@interface EnjoyableApplicationDelegate : NSObject -{ - IBOutlet NSMenu *dockMenuBase; - IBOutlet NSWindow *window; -} - -@property (nonatomic, strong) IBOutlet NJDeviceController *inputController; -@property (nonatomic, strong) IBOutlet NJMappingsController *mappingsController; - -@end diff --git a/EnjoyableApplicationDelegate.m b/EnjoyableApplicationDelegate.m deleted file mode 100644 index c477511..0000000 --- a/EnjoyableApplicationDelegate.m +++ /dev/null @@ -1,155 +0,0 @@ -// -// EnjoyableApplicationDelegate.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "EnjoyableApplicationDelegate.h" - -#import "NJMapping.h" -#import "NJMappingsController.h" -#import "NJDeviceController.h" -#import "NJOutputController.h" -#import "NJEvents.h" - -@implementation EnjoyableApplicationDelegate - -- (void)didSwitchApplication:(NSNotification *)note { - NSRunningApplication *activeApp = note.userInfo[NSWorkspaceApplicationKey]; - NSString *name = activeApp.localizedName; - if (!name) - name = activeApp.bundleIdentifier; - if (name && ![name isEqualToString:NSRunningApplication.currentApplication.localizedName]) - [self.mappingsController activateMappingForProcess:name]; -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification { - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(mappingDidChange:) - name:NJEventMappingChanged - object:nil]; - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(mappingListDidChange:) - name:NJEventMappingListChanged - object:nil]; - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(eventTranslationActivated:) - name:NJEventTranslationActivated - object:nil]; - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(eventTranslationDeactivated:) - name:NJEventTranslationDeactivated - object:nil]; - - [self.inputController setup]; - [self.mappingsController load]; -} - -- (void)applicationDidBecomeActive:(NSNotification *)notification { - [window makeKeyAndOrderFront:nil]; -} - -- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication - hasVisibleWindows:(BOOL)flag { - [window makeKeyAndOrderFront:nil]; - return NO; -} - -- (void)eventTranslationActivated:(NSNotification *)note { - [NSProcessInfo.processInfo disableAutomaticTermination:@"Input translation is active."]; - [NSWorkspace.sharedWorkspace.notificationCenter - addObserver:self - selector:@selector(didSwitchApplication:) - name:NSWorkspaceDidActivateApplicationNotification - object:nil]; - NSLog(@"Listening for application changes."); -} - -- (void)eventTranslationDeactivated:(NSNotification *)note { - [NSProcessInfo.processInfo enableAutomaticTermination:@"Input translation is active."]; - [NSWorkspace.sharedWorkspace.notificationCenter - removeObserver:self - name:NSWorkspaceDidActivateApplicationNotification - object:nil]; - NSLog(@"Ignoring application changes."); -} - -- (void)mappingListDidChange:(NSNotification *)note { - NSArray *mappings = note.object; - while (dockMenuBase.lastItem.representedObject) - [dockMenuBase removeLastItem]; - int added = 0; - for (NJMapping *mapping in mappings) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(chooseMapping:) - keyEquivalent:keyEquiv]; - item.representedObject = mapping; - item.state = mapping == self.mappingsController.currentMapping; - [dockMenuBase addItem:item]; - } -} - -- (void)mappingDidChange:(NSNotification *)note { - NJMapping *current = note.object; - for (NSMenuItem *item in dockMenuBase.itemArray) - if (item.representedObject) - item.state = item.representedObject == current; -} - -- (void)chooseMapping:(NSMenuItem *)sender { - NJMapping *chosen = sender.representedObject; - [self.mappingsController activateMapping:chosen]; -} - -#define OUTPUT_PANE_MIN_WIDTH 390 - -- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset { - return proposedMax - OUTPUT_PANE_MIN_WIDTH; -} - -- (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize { - NSView *inputView = splitView.subviews[0]; - NSView *outputView = splitView.subviews[1]; - if (outputView.frame.size.width < OUTPUT_PANE_MIN_WIDTH) { - NSSize frameSize = splitView.frame.size; - CGFloat inputWidth = frameSize.width - OUTPUT_PANE_MIN_WIDTH - splitView.dividerThickness; - inputView.frame = NSMakeRect(inputWidth, frameSize.height, - inputView.frame.size.width, - inputView.frame.size.height); - outputView.frame = NSMakeRect(inputWidth + splitView.dividerThickness, - 0, - OUTPUT_PANE_MIN_WIDTH, - frameSize.height); - } else - [splitView adjustSubviews]; -} - -- (NSMenu *)applicationDockMenu:(NSApplication *)sender { - NSMenu *menu = [[NSMenu alloc] init]; - int added = 0; - for (NJMapping *mapping in self.mappingsController) { - NSString *keyEquiv = ++added < 10 ? @(added).stringValue : @""; - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(chooseMapping:) - keyEquivalent:keyEquiv]; - item.representedObject = mapping; - item.state = mapping == self.mappingsController.currentMapping; - [menu addItem:item]; - } - return menu; -} - -- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { - NSURL *url = [NSURL fileURLWithPath:filename]; - [self.mappingsController addMappingWithContentsOfURL:url]; - return YES; -} - - -@end diff --git a/Enjoyable_Prefix.pch b/Enjoyable_Prefix.pch deleted file mode 100644 index 4845e9d..0000000 --- a/Enjoyable_Prefix.pch +++ /dev/null @@ -1,16 +0,0 @@ -// -// Prefix header for all source files of the 'Enjoy' target in the 'Enjoy' project -// - -#ifdef __OBJC__ - #import -#endif - -#import - -#import "NSError+Description.h" -#import "NSMenu+RepresentedObjectAccessors.h" -#import "NSView+FirstResponder.h" -#import "NSMutableArray+MoveObject.h" -#import "NSFileManager+UniqueNames.h" -#import "NSString+FixFilename.h" diff --git a/Help/Contents/Info.plist b/Help/Contents/Info.plist deleted file mode 100644 index e62b80f..0000000 --- a/Help/Contents/Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - CFBundleIdentifier - com.yukkurigames.Enjoyable.help - CFBundleDevelopmentRegion - en_US - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Enjoyable - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1 - CFBundleSignature - hbwr - CFBundleVersion - 1 - HPDBookAccessPath - index.html - HPDBookIconPath - gfx/Icon.png - HPDBookIndexPath - Enjoyable.helpindex - HPDBookKBProduct - enjoyable1 - HPDBookTitle - Enjoyable Help - HPDBookType - 3 - - diff --git a/Help/Contents/Resources/English.lproj/Makefile b/Help/Contents/Resources/English.lproj/Makefile deleted file mode 100644 index 51a8466..0000000 --- a/Help/Contents/Resources/English.lproj/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/make -f - -HTML := *.html pgs/*.html - -all: Enjoyable.helpindex - -Enjoyable.helpindex: $(HTML) - hiutil -C -f $@ -g -a -s en . diff --git a/Help/Contents/Resources/English.lproj/gfx/Icon.png b/Help/Contents/Resources/English.lproj/gfx/Icon.png deleted file mode 100644 index 36c4254..0000000 Binary files a/Help/Contents/Resources/English.lproj/gfx/Icon.png and /dev/null differ diff --git a/Help/Contents/Resources/English.lproj/index.html b/Help/Contents/Resources/English.lproj/index.html deleted file mode 100644 index 1a52296..0000000 --- a/Help/Contents/Resources/English.lproj/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Enjoyable Help - - - - - - - - - - - - -
-
- Enjoyable Icon -
-
-

Enjoyable Help

-
-
- -

- Enjoyable helps you use a joystick or gamepad to control - applications which normally require a keyboard and mouse. -

- -
-

Quick Start

-
    -
  • Connect a joystick or gamepad.
  • -
  • Press a button on it, then the keyboard key you want to use.
  • -
  • Press the ▶ button in the upper-right.
  • -
  • Start up your game and use your gamepad!
  • - -
- -
-

- - Keyboard Events -
- Map buttons to keys on a keyboard. -

-

- - Mouse Events -
- Use axes and buttons to simulate a mouse. -

-

- - Application Mappings -
- Create and share mappings for different applications. -

-

- - Troubleshooting -
- Assistance for common problems. -

-
- -

- - license - - - - website -

- - - diff --git a/Help/Contents/Resources/English.lproj/pgs/boring.html b/Help/Contents/Resources/English.lproj/pgs/boring.html deleted file mode 100644 index 45c6719..0000000 --- a/Help/Contents/Resources/English.lproj/pgs/boring.html +++ /dev/null @@ -1,73 +0,0 @@ - - - - - License & Copyright - - - - - - - - - -
-
- Icon -
-

License & Copyright

-
- -

Copyright

-

- 2013 Joe Wreschnig
- 2012 Yifeng Huang
- 2009 Sam McCall & the University of Otago -

- -

License

-

- Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: -

-

- The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. -

-

- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -

-

- The joystick icon is from the Tango icon set and is public - domain. -

- - diff --git a/Help/Contents/Resources/English.lproj/pgs/keyboard.html b/Help/Contents/Resources/English.lproj/pgs/keyboard.html deleted file mode 100644 index dca6136..0000000 --- a/Help/Contents/Resources/English.lproj/pgs/keyboard.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Keyboard Events - - - - - - - - - -
-
- Enjoyable Icon -
-

Keyboard Events

-
- -

- Enjoyable supports mapping joystick buttons, hat switches, and - axis thresholds to simulate keyboard keys. First disable - mapping by deactivating the ▶ button in the top left. Then press - the button on the joystick you want to map. This will select it - on the left-hand side of the screen. -

- -

- If the button wasn't mapped or was mapped to a key press - already, the key input field activates and you can simply press - the key you want to use. Otherwise, click on the Press a - key label or input field, then press the key. -

- -

- To change a key without disabling mapping you can choose the - input's entry in the sidebar directly. -

- -

Clearing the Selection

-

- To clear a mapped key either select the Do nothing - option or press ⌥⌫ when the key input field is selected. -

- -

Cancelling the Selection

-

- If you select the key input field by mistake you can press ⌥⎋ - to cancel the selection without changing the current setting. -

- - diff --git a/Help/Contents/Resources/English.lproj/pgs/mappings.html b/Help/Contents/Resources/English.lproj/pgs/mappings.html deleted file mode 100644 index 3b181ff..0000000 --- a/Help/Contents/Resources/English.lproj/pgs/mappings.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - Application Mappings - - - - - - - - - -
-
- Icon -
-

Application Mappings

-
- -

- You can make many different mappings and switch between them - easily. To open the list of mappings click the button at the - top-left or press ⌘L. -

- -

- Click on a mapping to switch to it. Create a new mapping with - the + button or pressing ⌘N. Delete the current mapping with - the - button or ⌘⌫. Rename a mapping by double-clicking on it or - pressing Return while it's selected. -

- -

- You can also switch mappings with the Mappings menu, with - Enjoyable's dock menu, or by pressing ⌘1 through ⌘9. -

- -

- Switching mappings can also be mapped to an input. Select the - input you wish to use and then choose a mapping from - the Switch to mapping option. For example, you could have - one mapping for a game's menu and another for its main screen - and switch between them in-game without returning to Enjoyable. -

- -

Automatic Switching

-

- If you name a mapping after an application it will be - automatically chosen when you switch to that application. The - name of an application is usually shown on the dock when you - hover your mouse over it. If you don't know the name of the - application you want to create a mapping for, create a mapping - with the name @Application (note the @ at - the start). The mapping will automatically be renamed for the - next application you switch to while it's enabled. -

- -

Import and Export

-

- Mappings can be exported and shared with other people. To export - your current mapping choose Mappings > Export… and pick a - location to save the file. This file can be shared with anyone; - it doesn't contain any personal information other than the - product IDs of the input devices you used and what you mapped - them to. -

-

- To import a mapping choose Mappings > Import… and select - the file you want to import. Mapping files end - with .enjoyable (the default), .json, - or .txt. If the imported mapping conflicts with one - you already made, you can choose to merge the two mappings or - create a new one with the same name. -

-

- You can also import mappings by opening them in Finder or - dragging the files onto the mapping list. Similarly, you can - export mappings by dragging them from the mapping list to - Finder. -

- - diff --git a/Help/Contents/Resources/English.lproj/pgs/mouse.html b/Help/Contents/Resources/English.lproj/pgs/mouse.html deleted file mode 100644 index 0293fd0..0000000 --- a/Help/Contents/Resources/English.lproj/pgs/mouse.html +++ /dev/null @@ -1,102 +0,0 @@ - - - - - Mouse Events - - - - - - - - - -
-
- Icon -
-

Mouse Events

-
- -

- You can use Enjoyable to map input to mouse buttons, moving, and - scrolling. -

- -

Movement

-

- Select the direction you'd like the input to move the - mouse. Adjust the movement speed using the slider underneath. If - you are mapping an analog input then this is the maximum speed; - for a button it's a constant speed. -

-

- The speed is set independently for each input. You can have - faster horizontal movement than vertical movement, or map one - set of inputs to a fast speed and another set to a slow - speed. -

- -

Buttons

-

- Select the mouse button you'd like the input to simulate. -

- -

Scrolling

-

- Simulated scrolling can be continuous like the scrolling - gestures on a trackpad, or discrete like a mouse wheel that - clicks as you spin it. -

-

- To use continuous scrolling choose ↑ or ↓. Use the - slider underneath them to adjust the scrolling speed. If you are - mapping an analog input then this is the maximum speed; for a - button it's a constant speed. -

- To use discrete scrolling choose ⤒ or ⤓. The input - will trigger scrolling up or down by exactly one line and stop, - regardless of how long you hold the button down or how far - you move an analog input. -

-

- The arrows indicate the direction you would spin a mouse wheel - or move your fingers. Depending on settings this may mean you - need to choose a down arrow to scroll up and vice versa. You can - also change this globally in  > System Preferences… > - Mouse and  > System Preferences… > Trackpad. -

- -

Known Issues

-

- Mouse events are more fragile than keyboard ones. While Enjoyble - will work fine for most games, regular OS X (Cocoa) applications - require specially formatted mouse events. Features such as - click-and-drag or double-clicking will not work correctly, so - many applications will behave incorrectly if driven by an - Enjoyable simulated mouse. -

-

- If you find a non-Cocoa application that has problems with - Enjoyable's mouse - support please - file a ticket in the issue tracker. -

- - - diff --git a/Help/Contents/Resources/English.lproj/pgs/problems.html b/Help/Contents/Resources/English.lproj/pgs/problems.html deleted file mode 100644 index 8f3aa68..0000000 --- a/Help/Contents/Resources/English.lproj/pgs/problems.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Troubleshooting - - - - - - - - - -
-
- Icon -
-

Troubleshooting

-
- -

- - When I start Enjoyable, it says "Input devices are unavailable" -

-

- This happens if Enjoyable is refused access to your input - devices by Mac OS X. This usually happens if another application - has requested exclusive access to them. Try quitting any other - applications using your input devices. If that doesn't work, try - disconnecting and reconnecting the device, then restarting - Enjoyable. If it still doesn't work you may need to reboot. -

- -

- - Enjoyable never switches to my application mapping -

-

- Make sure it matches the name of the application exactly. If you - still have trouble, name the mapping @Application and - switch back to have Enjoyable try to deduce the correct name - automatically. -

- -

- Mouse clicks and drags don't work - -

-

- This is a known issue with Cocoa applications, as they require - more specially-crafted mouse events. We hope to fix it in a - future version. -

- - diff --git a/Help/Contents/Resources/English.lproj/sty/default.css b/Help/Contents/Resources/English.lproj/sty/default.css deleted file mode 100644 index 58b7747..0000000 --- a/Help/Contents/Resources/English.lproj/sty/default.css +++ /dev/null @@ -1 +0,0 @@ -body { font-size: 8pt; font-family: "Lucida Grande", Arial, sans-serif; line-height: 12pt; text-decoration: none; margin-right: 1em; margin-left: 1em; } #navbox { background-color: #f2f2f2; position: fixed; top: 0; left: 0; width: 100%; height: 1.5em; float: left; border-bottom: 1px solid #bfbfbf } #navleftbox { position: absolute; top: 1px; left: 15px } #navrightbox { background-color: #f2f2f2; padding-right: 25px; float: right; padding-bottom: 1px; border-left: 1px solid #bfbfbf } #navbox a { font-size: 8pt; color: #666; font-weight: normal; margin: -9px 0 -6px; } #headerbox { margin-top: 36px; padding-right: 6px; margin-bottom: 2em; } #iconbox { float: left; } h1 { margin-left: 40px; width: 88%; font-size: 15pt; line-height: 15pt; font-weight: bold; padding-top: 6px; margin-bottom: 0; } h2 { font-size: 11pt; line-height: 12pt; font-weight: bold; color: black; margin-top: 0; margin-bottom: 11px; } h3 { font-size: 8pt; font-weight: bold; letter-spacing: 0.1em; line-height: 8pt; color: #666; margin-top: 1em; margin-bottom: 0px; padding-bottom: 0.5em; } p { margin-left: 0px; margin-top: 0px; margin-bottom: 0.5em; } ul { margin-left: 2em; margin-top: 6px; margin-bottom: 0px; padding-left: 0px; } li { margin-left: 0px; } a { color: #778fbd; font-size: 9pt; font-weight: bold; text-decoration: none; } a:hover { text-decoration: underline; } .weblink { color: #666; font-weight: normal; } \ No newline at end of file diff --git a/NJDevice.h b/NJDevice.h deleted file mode 100644 index ec607ac..0000000 --- a/NJDevice.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// NJDevice.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJInputPathElement.h" - -@class NJInput; - -@interface NJDevice : NSObject - -@property (nonatomic, assign) int index; -@property (nonatomic, copy) NSString *productName; -@property (nonatomic, assign) IOHIDDeviceRef device; -@property (nonatomic, copy) NSArray *children; -@property (nonatomic, readonly) NSString *name; -@property (readonly) NSString *uid; - -- (id)initWithDevice:(IOHIDDeviceRef)device; -- (NJInput *)handlerForEvent:(IOHIDValueRef)value; -- (NJInput *)inputForEvent:(IOHIDValueRef)value; - -@end diff --git a/NJDevice.m b/NJDevice.m deleted file mode 100644 index 69775d8..0000000 --- a/NJDevice.m +++ /dev/null @@ -1,109 +0,0 @@ -// -// NJDevice.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "NJDevice.h" - -#import "NJInput.h" -#import "NJInputAnalog.h" -#import "NJInputHat.h" -#import "NJInputButton.h" - -static NSArray *InputsForElement(IOHIDDeviceRef device, id base) { - CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); - NSMutableArray *children = [NSMutableArray arrayWithCapacity:CFArrayGetCount(elements)]; - - int buttons = 0; - int axes = 0; - int hats = 0; - - for (int i = 0; i < CFArrayGetCount(elements); i++) { - IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); - int type = IOHIDElementGetType(element); - unsigned usage = IOHIDElementGetUsage(element); - unsigned usagePage = IOHIDElementGetUsagePage(element); - long max = IOHIDElementGetPhysicalMax(element); - long min = IOHIDElementGetPhysicalMin(element); - CFStringRef elName = IOHIDElementGetName(element); - - NJInput *input = nil; - - if (!(type == kIOHIDElementTypeInput_Misc - || type == kIOHIDElementTypeInput_Axis - || type == kIOHIDElementTypeInput_Button)) - continue; - - if (max - min == 1 || usagePage == kHIDPage_Button || type == kIOHIDElementTypeInput_Button) { - input = [[NJInputButton alloc] initWithName:(__bridge NSString *)elName - idx:++buttons - max:max]; - } else if (usage == kHIDUsage_GD_Hatswitch) { - input = [[NJInputHat alloc] initWithIndex:++hats]; - } else if (usage >= kHIDUsage_GD_X && usage <= kHIDUsage_GD_Rz) { - input = [[NJInputAnalog alloc] initWithIndex:++axes - rawMin:min - rawMax:max]; - } else { - continue; - } - - // TODO(jfw): Should be moved into better constructors. - input.base = base; - input.cookie = IOHIDElementGetCookie(element); - [children addObject:input]; - } - - CFRelease(elements); - return children; -} - -@implementation NJDevice { - int vendorId; - int productId; -} - -- (id)initWithDevice:(IOHIDDeviceRef)dev { - if ((self = [super init])) { - self.device = dev; - self.productName = (__bridge NSString *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductKey)); - vendorId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey)) intValue]; - productId = [(__bridge NSNumber *)IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey)) intValue]; - self.children = InputsForElement(dev, self); - } - return self; -} - -- (NSString *)name { - return [NSString stringWithFormat:@"%@ #%d", _productName, _index]; -} - -- (id)base { - return nil; -} - -- (NSString *)uid { - return [NSString stringWithFormat: @"%d:%d:%d", vendorId, productId, _index]; -} - -- (NJInput *)findInputByCookie:(IOHIDElementCookie)cookie { - for (NJInput *child in _children) - if (child.cookie == cookie) - return child; - return nil; -} - -- (NJInput *)handlerForEvent:(IOHIDValueRef)value { - NJInput *mainInput = [self inputForEvent:value]; - return [mainInput findSubInputForValue:value]; -} - -- (NJInput *)inputForEvent:(IOHIDValueRef)value { - IOHIDElementRef elt = IOHIDValueGetElement(value); - IOHIDElementCookie cookie = IOHIDElementGetCookie(elt); - return [self findInputByCookie:cookie]; -} - -@end diff --git a/NJDeviceController.h b/NJDeviceController.h deleted file mode 100644 index 7658c64..0000000 --- a/NJDeviceController.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// NJDeviceController.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class NJDevice; -@class NJInput; -@class NJMappingsController; -@class NJOutputController; - -@interface NJDeviceController : NSObject { - IBOutlet NSOutlineView *outlineView; - IBOutlet NJOutputController *outputController; - IBOutlet NJMappingsController *mappingsController; - IBOutlet NSButton *translatingEventsButton; - IBOutlet NSMenuItem *translatingEventsMenu; -} - -@property (nonatomic, readonly) NJInput *selectedInput; -@property (nonatomic, assign) NSPoint mouseLoc; -@property (nonatomic, assign) BOOL translatingEvents; - -- (void)setup; -- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device; - -- (IBAction)translatingEventsChanged:(id)sender; - -@end diff --git a/NJDeviceController.m b/NJDeviceController.m deleted file mode 100644 index d553d7d..0000000 --- a/NJDeviceController.m +++ /dev/null @@ -1,243 +0,0 @@ -// -// NJDeviceController.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "NJDeviceController.h" - -#import "NJMapping.h" -#import "NJMappingsController.h" -#import "NJDevice.h" -#import "NJInput.h" -#import "NJOutput.h" -#import "NJOutputController.h" -#import "NJEvents.h" - -@implementation NJDeviceController { - IOHIDManagerRef hidManager; - NSTimer *continuousTimer; - NSMutableArray *runningOutputs; - NSMutableArray *_devices; -} - -- (id)init { - if ((self = [super init])) { - _devices = [[NSMutableArray alloc] initWithCapacity:16]; - runningOutputs = [[NSMutableArray alloc] initWithCapacity:32]; - } - return self; -} - -- (void)dealloc { - [continuousTimer invalidate]; - IOHIDManagerClose(hidManager, kIOHIDOptionsTypeNone); - CFRelease(hidManager); -} - -- (void)expandRecursive:(id )pathElement { - if (pathElement) { - [self expandRecursive:pathElement.base]; - [outlineView expandItem:pathElement]; - } -} - -- (void)addRunningOutput:(NJOutput *)output { - if (![runningOutputs containsObject:output]) { - [runningOutputs addObject:output]; - } - if (!continuousTimer) { - continuousTimer = [NSTimer scheduledTimerWithTimeInterval:1.f/60.f - target:self - selector:@selector(updateContinuousInputs:) - userInfo:nil - repeats:YES]; - NSLog(@"Scheduled continuous output timer."); - } -} - -- (void)runOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value { - NJDevice *dev = [self findDeviceByRef:device]; - NJInput *mainInput = [dev inputForEvent:value]; - [mainInput notifyEvent:value]; - NSArray *children = mainInput.children ? mainInput.children : mainInput ? @[mainInput] : @[]; - for (NJInput *subInput in children) { - NJOutput *output = mappingsController.currentMapping[subInput]; - output.magnitude = subInput.magnitude; - output.running = subInput.active; - if ((output.running || output.magnitude) && output.isContinuous) - [self addRunningOutput:output]; - } -} - -- (void)showOutputForDevice:(IOHIDDeviceRef)device value:(IOHIDValueRef)value { - NJDevice *dev = [self findDeviceByRef:device]; - NJInput *handler = [dev handlerForEvent:value]; - if (!handler) - return; - - [self expandRecursive:handler]; - [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[outlineView rowForItem:handler]] byExtendingSelection: NO]; - [outputController focusKey]; -} - -static void input_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDValueRef value) { - NJDeviceController *controller = (__bridge NJDeviceController *)ctx; - IOHIDDeviceRef device = IOHIDQueueGetDevice(inSender); - - if (controller.translatingEvents) { - [controller runOutputForDevice:device value:value]; - } else if ([NSApplication sharedApplication].mainWindow.isVisible) { - [controller showOutputForDevice:device value:value]; - } -} - -static int findAvailableIndex(NSArray *list, NJDevice *dev) { - for (int index = 1; ; index++) { - BOOL available = YES; - for (NJDevice *used in list) { - if ([used.productName isEqualToString:dev.productName] && used.index == index) { - available = NO; - break; - } - } - if (available) - return index; - } -} - -- (void)addDeviceForDevice:(IOHIDDeviceRef)device { - IOHIDDeviceRegisterInputValueCallback(device, input_callback, (__bridge void*)self); - NJDevice *dev = [[NJDevice alloc] initWithDevice:device]; - dev.index = findAvailableIndex(_devices, dev); - [_devices addObject:dev]; - [outlineView reloadData]; -} - -static void add_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) { - NJDeviceController *controller = (__bridge NJDeviceController *)ctx; - [controller addDeviceForDevice:device]; -} - -- (NJDevice *)findDeviceByRef:(IOHIDDeviceRef)device { - for (NJDevice *dev in _devices) - if (dev.device == device) - return dev; - return nil; -} - -static void remove_callback(void *ctx, IOReturn inResult, void *inSender, IOHIDDeviceRef device) { - NJDeviceController *controller = (__bridge NJDeviceController *)ctx; - [controller removeDeviceForDevice:device]; -} - -- (void)removeDeviceForDevice:(IOHIDDeviceRef)device { - NJDevice *match = [self findDeviceByRef:device]; - IOHIDDeviceRegisterInputValueCallback(device, NULL, NULL); - if (match) { - [_devices removeObject:match]; - [outlineView reloadData]; - } - -} - -- (void)updateContinuousInputs:(NSTimer *)timer { - self.mouseLoc = [NSEvent mouseLocation]; - for (NJOutput *output in [runningOutputs copy]) { - if (![output update:self]) { - [runningOutputs removeObject:output]; - } - } - if (!runningOutputs.count) { - [continuousTimer invalidate]; - continuousTimer = nil; - NSLog(@"Unscheduled continuous output timer."); - } -} - -#define NSSTR(e) ((NSString *)CFSTR(e)) - -- (void)setup { - hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - NSArray *criteria = @[ @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), - NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick) }, - @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), - NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_GamePad) }, - @{ NSSTR(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), - NSSTR(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_MultiAxisController) } - ]; - IOHIDManagerSetDeviceMatchingMultiple(hidManager, (__bridge CFArrayRef)criteria); - - IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - IOReturn ret = IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone); - if (ret != kIOReturnSuccess) { - [[NSAlert alertWithMessageText:@"Input devices are unavailable" - defaultButton:nil - alternateButton:nil - otherButton:nil - informativeTextWithFormat:@"Error 0x%08x occured trying to access your devices. " - @"Input may not be correctly detected or mapped.", - ret] - beginSheetModalForWindow:outlineView.window - modalDelegate:nil - didEndSelector:nil - contextInfo:nil]; - } - - IOHIDManagerRegisterDeviceMatchingCallback(hidManager, add_callback, (__bridge void *)self); - IOHIDManagerRegisterDeviceRemovalCallback(hidManager, remove_callback, (__bridge void *)self); -} - -- (NJInput *)selectedInput { - id item = [outlineView itemAtRow:outlineView.selectedRow]; - return (!item.children && item.base) ? item : nil; -} - -- (NSInteger)outlineView:(NSOutlineView *)outlineView - numberOfChildrenOfItem:(id )item { - return item ? item.children.count : _devices.count; -} - -- (BOOL)outlineView:(NSOutlineView *)outlineView - isItemExpandable:(id )item { - return item ? [[item children] count] > 0: YES; -} - -- (id)outlineView:(NSOutlineView *)outlineView - child:(NSInteger)index - ofItem:(id )item { - return item ? item.children[index] : _devices[index]; -} - -- (id)outlineView:(NSOutlineView *)outlineView -objectValueForTableColumn:(NSTableColumn *)tableColumn - byItem:(id )item { - return item ? item.name : @"root"; -} - -- (void)outlineViewSelectionDidChange:(NSNotification *)notification { - - [outputController loadCurrent]; -} - -- (void)setTranslatingEvents:(BOOL)translatingEvents { - if (translatingEvents != _translatingEvents) { - _translatingEvents = translatingEvents; - NSInteger state = translatingEvents ? NSOnState : NSOffState; - translatingEventsButton.state = state; - translatingEventsMenu.title = translatingEvents ? @"Disable" : @"Enable"; - NSString *name = translatingEvents - ? NJEventTranslationActivated - : NJEventTranslationDeactivated; - [NSNotificationCenter.defaultCenter postNotificationName:name - object:self]; - } -} - -- (IBAction)translatingEventsChanged:(NSButton *)sender { - self.translatingEvents = sender.state == NSOnState; -} - - -@end diff --git a/NJEvents.h b/NJEvents.h deleted file mode 100644 index ce87b24..0000000 --- a/NJEvents.h +++ /dev/null @@ -1,12 +0,0 @@ -// -// NJEvents.h -// Enjoyable -// -// Created by Joe Wreschnig on 3/2/13. -// -// - -#define NJEventMappingChanged @"NJEventMappingChanged" -#define NJEventMappingListChanged @"NJEventMappingListChanged" -#define NJEventTranslationActivated @"NJEventTranslationActivated" -#define NJEventTranslationDeactivated @"NJEventTranslationDeactivated" diff --git a/NJInput.h b/NJInput.h deleted file mode 100644 index 380e858..0000000 --- a/NJInput.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// NJInput.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJInputPathElement.h" - -@interface NJInput : NSObject - -@property (nonatomic, assign) IOHIDElementCookie cookie; -@property (nonatomic, copy) NSArray *children; -@property (nonatomic, weak) id base; -@property (nonatomic, copy) NSString *name; -@property (nonatomic, assign) BOOL active; -@property (nonatomic, assign) float magnitude; -@property (readonly) NSString *uid; - -- (id)initWithName:(NSString *)newName base:(id )newBase; - -- (void)notifyEvent:(IOHIDValueRef)value; -- (id)findSubInputForValue:(IOHIDValueRef)value; - -@end diff --git a/NJInput.m b/NJInput.m deleted file mode 100644 index 077dac8..0000000 --- a/NJInput.m +++ /dev/null @@ -1,32 +0,0 @@ -// -// NJInput.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "NJInput.h" - -@implementation NJInput - -- (id)initWithName:(NSString *)newName base:(id )newBase { - if ((self = [super init])) { - self.name = newName; - self.base = newBase; - } - return self; -} - -- (id)findSubInputForValue:(IOHIDValueRef)value { - return NULL; -} - -- (NSString *)uid { - return [NSString stringWithFormat:@"%@~%@", [_base uid], _name]; -} - -- (void)notifyEvent:(IOHIDValueRef)value { - [self doesNotRecognizeSelector:_cmd]; -} - -@end diff --git a/NJInputAnalog.h b/NJInputAnalog.h deleted file mode 100644 index e79ed89..0000000 --- a/NJInputAnalog.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// NJInputAnalog.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import - -#import "NJInput.h" - -@interface NJInputAnalog : NJInput - -- (id)initWithIndex:(int)index rawMin:(long)rawMin rawMax:(long)rawMax; - -@end diff --git a/NJInputAnalog.m b/NJInputAnalog.m deleted file mode 100644 index 7b4b15a..0000000 --- a/NJInputAnalog.m +++ /dev/null @@ -1,55 +0,0 @@ -// -// NJInputAnalog.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#define DEAD_ZONE 0.3 - -#import "NJInputAnalog.h" - -static float normalize(long p, long min, long max) { - return 2 * (p - min) / (float)(max - min) - 1; -} - -@implementation NJInputAnalog { - float magnitude; - long rawMin; - long rawMax; -} - -- (id)initWithIndex:(int)index rawMin:(long)rawMin_ rawMax:(long)rawMax_ { - if ((self = [super init])) { - self.name = [[NSString alloc] initWithFormat: @"Axis %d", index]; - self.children = @[[[NJInput alloc] initWithName:@"Low" base:self], - [[NJInput alloc] initWithName:@"High" base:self]]; - rawMax = rawMax_; - rawMin = rawMin_; - } - return self; -} - -- (id)findSubInputForValue:(IOHIDValueRef)value { - float mag = normalize(IOHIDValueGetIntegerValue(value), rawMin, rawMax); - if (mag < -DEAD_ZONE) - return self.children[0]; - else if (mag > DEAD_ZONE) - return self.children[1]; - else - return nil; -} - -- (void)notifyEvent:(IOHIDValueRef)value { - magnitude = normalize(IOHIDValueGetIntegerValue(value), rawMin, rawMax); - [self.children[0] setMagnitude:fabsf(MIN(magnitude, 0))]; - [self.children[1] setMagnitude:fabsf(MAX(magnitude, 0))]; - [self.children[0] setActive:magnitude < -DEAD_ZONE]; - [self.children[1] setActive:magnitude > DEAD_ZONE]; -} - -- (float)magnitude { - return magnitude; -} - -@end diff --git a/NJInputButton.h b/NJInputButton.h deleted file mode 100644 index 31cba73..0000000 --- a/NJInputButton.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NJInputButton.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJInput.h" - -@interface NJInputButton : NJInput - -- (id)initWithName:(NSString *)name idx:(int)idx max:(long)max; - -@end diff --git a/NJInputButton.m b/NJInputButton.m deleted file mode 100644 index 0c3b0ce..0000000 --- a/NJInputButton.m +++ /dev/null @@ -1,34 +0,0 @@ -// -// NJInputButton.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#import "NJInputButton.h" - -@implementation NJInputButton { - long _max; -} - -- (id)initWithName:(NSString *)name idx:(int)idx max:(long)max { - if ((self = [super init])) { - _max = max; - if (name.length) - self.name = [NSString stringWithFormat:@"Button %d - %@", idx, name]; - else - self.name = [NSString stringWithFormat:@"Button %d", idx]; - } - return self; -} - -- (id)findSubInputForValue:(IOHIDValueRef)val { - return (IOHIDValueGetIntegerValue(val) == _max) ? self : nil; -} - -- (void)notifyEvent:(IOHIDValueRef)value { - self.active = IOHIDValueGetIntegerValue(value) == _max; - self.magnitude = IOHIDValueGetIntegerValue(value) / (float)_max; -} - -@end diff --git a/NJInputHat.h b/NJInputHat.h deleted file mode 100644 index 5290d71..0000000 --- a/NJInputHat.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NJInputHat.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJInput.h" - -@interface NJInputHat : NJInput - -- (id)initWithIndex:(int)index; - -@end diff --git a/NJInputHat.m b/NJInputHat.m deleted file mode 100644 index e0e127f..0000000 --- a/NJInputHat.m +++ /dev/null @@ -1,99 +0,0 @@ -// -// NJInputHat.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#import "NJInputHat.h" - -static BOOL active_eightway[36] = { - NO, NO, NO, NO , // center - YES, NO, NO, NO , // N - YES, NO, NO, YES, // NE - NO, NO, NO, YES, // E - NO, YES, NO, YES, // SE - NO, YES, NO, NO , // S - NO, YES, YES, NO , // SW - NO, NO, YES, NO , // W - YES, NO, YES, NO , // NW -}; - -static BOOL active_fourway[20] = { - NO, NO, NO, NO , // center - YES, NO, NO, NO , // N - NO, NO, NO, YES, // E - NO, YES, NO, NO , // S - NO, NO, YES, NO , // W -}; - -@implementation NJInputHat - -- (id)initWithIndex:(int)index { - if ((self = [super init])) { - self.children = @[[[NJInput alloc] initWithName:@"Up" base:self], - [[NJInput alloc] initWithName:@"Down" base:self], - [[NJInput alloc] initWithName:@"Left" base:self], - [[NJInput alloc] initWithName:@"Right" base:self]]; - self.name = [NSString stringWithFormat:@"Hat Switch %d", index]; - } - return self; -} - -- (id)findSubInputForValue:(IOHIDValueRef)value { - long parsed = IOHIDValueGetIntegerValue(value); - switch (IOHIDElementGetLogicalMax(IOHIDValueGetElement(value))) { - case 7: // 8-way switch, 0-7. - switch (parsed) { - case 0: return self.children[0]; - case 4: return self.children[1]; - case 6: return self.children[2]; - case 2: return self.children[3]; - default: return nil; - } - case 8: // 8-way switch, 1-8 (neutral 0). - switch (parsed) { - case 1: return self.children[0]; - case 5: return self.children[1]; - case 7: return self.children[2]; - case 3: return self.children[3]; - default: return nil; - } - case 3: // 4-way switch, 0-3. - switch (parsed) { - case 0: return self.children[0]; - case 2: return self.children[1]; - case 3: return self.children[2]; - case 1: return self.children[3]; - default: return nil; - } - case 4: // 4-way switch, 1-4 (neutral 0). - switch (parsed) { - case 1: return self.children[0]; - case 3: return self.children[1]; - case 4: return self.children[2]; - case 2: return self.children[3]; - default: return nil; - } - default: - return nil; - } -} - -- (void)notifyEvent:(IOHIDValueRef)value { - long parsed = IOHIDValueGetIntegerValue(value); - long size = IOHIDElementGetLogicalMax(IOHIDValueGetElement(value)); - // Skip first row in table if 0 is not neutral. - if (size & 1) { - parsed++; - size++; - } - BOOL *activechildren = (size == 8) ? active_eightway : active_fourway; - for (unsigned i = 0; i < 4; i++) { - BOOL active = activechildren[parsed * 4 + i]; - [self.children[i] setActive:active]; - [self.children[i] setMagnitude:active]; - } -} - -@end diff --git a/NJInputPathElement.h b/NJInputPathElement.h deleted file mode 100644 index 8fe5c65..0000000 --- a/NJInputPathElement.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@protocol NJInputPathElement - -- (NSArray *)children; -- (id ) base; -- (NSString *)name; - -@end diff --git a/NJKeyInputField.h b/NJKeyInputField.h deleted file mode 100644 index ad026d3..0000000 --- a/NJKeyInputField.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// NJKeyInputField.h -// Enjoyable -// -// Copyright 2013 Joe Wreschnig. -// - -#import - -extern CGKeyCode NJKeyInputFieldEmpty; - -@protocol NJKeyInputFieldDelegate; - -@interface NJKeyInputField : NSTextField - // An NJKeyInputField is a NSTextField-like widget that receives - // exactly one key press, and displays the name of that key, then - // resigns its first responder status. It can also inform a - // special delegate when its content changes. - -+ (NSString *)stringForKeyCode:(CGKeyCode)keyCode; - // Give the string name for a virtual key code. - -@property (nonatomic, weak) IBOutlet id keyDelegate; - -@property (nonatomic, assign) CGKeyCode keyCode; - // The currently displayed key code, or NJKeyInputFieldEmpty if no - // key is active. Changing this will update the display but not - // inform the delegate. - -@property (nonatomic, readonly) BOOL hasKeyCode; - // True if any key is active, false otherwise. - -- (void)clear; - // Clear the currently active key and call the delegate. - -@end - -@protocol NJKeyInputFieldDelegate - -- (void)keyInputField:(NJKeyInputField *)keyInput - didChangeKey:(CGKeyCode)keyCode; -- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput; - -@end - diff --git a/NJKeyInputField.m b/NJKeyInputField.m deleted file mode 100644 index ec08150..0000000 --- a/NJKeyInputField.m +++ /dev/null @@ -1,213 +0,0 @@ -// -// NJKeyInputField.h -// Enjoyable -// -// Copyright 2013 Joe Wreschnig. -// - -#import "NJKeyInputField.h" - -CGKeyCode NJKeyInputFieldEmpty = 0xFFFF; - -@implementation NJKeyInputField - -- (id)initWithFrame:(NSRect)frameRect { - if ((self = [super initWithFrame:frameRect])) { - self.alignment = NSCenterTextAlignment; - [self setEditable:NO]; - [self setSelectable:NO]; - } - return self; -} - -- (void)clear { - self.keyCode = NJKeyInputFieldEmpty; - [self.keyDelegate keyInputFieldDidClear:self]; - [self resignIfFirstResponder]; -} - -- (BOOL)hasKeyCode { - return self.keyCode != NJKeyInputFieldEmpty; -} - -+ (NSString *)stringForKeyCode:(CGKeyCode)keyCode { - switch (keyCode) { - case 0xffff: return @""; - case 0x7a: return @"F1"; - case 0x78: return @"F2"; - case 0x63: return @"F3"; - case 0x76: return @"F4"; - case 0x60: return @"F5"; - case 0x61: return @"F6"; - case 0x62: return @"F7"; - case 0x64: return @"F8"; - case 0x65: return @"F9"; - case 0x6d: return @"F10"; - case 0x67: return @"F11"; - case 0x6f: return @"F12"; - case 0x69: return @"F13"; - case 0x6b: return @"F14"; - case 0x71: return @"F15"; - case 0x6a: return @"F16"; - case 0x40: return @"F17"; - case 0x4f: return @"F18"; - case 0x50: return @"F19"; - - case 0x35: return @"Esc"; - case 0x32: return @"`"; - - case 0x12: return @"1"; - case 0x13: return @"2"; - case 0x14: return @"3"; - case 0x15: return @"4"; - case 0x17: return @"5"; - case 0x16: return @"6"; - case 0x1a: return @"7"; - case 0x1c: return @"8"; - case 0x19: return @"9"; - case 0x1d: return @"0"; - case 0x1b: return @"-"; - case 0x18: return @"="; - - case 0x3f: return @"Fn"; - case 0x36: return @"Right Command"; - case 0x37: return @"Left Command"; - case 0x38: return @"Left Shift"; - case 0x39: return @"Caps Lock"; - case 0x3a: return @"Left Option"; - case 0x3b: return @"Left Control"; - case 0x3c: return @"Right Shift"; - case 0x3d: return @"Right Option"; - case 0x3e: return @"Right Control"; - - case 0x73: return @"Home"; - case 0x74: return @"Page Up"; - case 0x75: return @"Delete"; - case 0x77: return @"End"; - case 0x79: return @"Page Down"; - - case 0x30: return @"Tab"; - case 0x33: return @"Backspace"; - case 0x24: return @"Return"; - case 0x31: return @"Space"; - - case 0x0c: return @"Q"; - case 0x0d: return @"W"; - case 0x0e: return @"E"; - case 0x0f: return @"R"; - case 0x11: return @"T"; - case 0x10: return @"Y"; - case 0x20: return @"U"; - case 0x22: return @"I"; - case 0x1f: return @"O"; - case 0x23: return @"P"; - case 0x21: return @"["; - case 0x1e: return @"]"; - case 0x2a: return @"\\"; - case 0x00: return @"A"; - case 0x01: return @"S"; - case 0x02: return @"D"; - case 0x03: return @"F"; - case 0x05: return @"G"; - case 0x04: return @"H"; - case 0x26: return @"J"; - case 0x28: return @"K"; - case 0x25: return @"L"; - case 0x29: return @";"; - case 0x27: return @"'"; - case 0x06: return @"Z"; - case 0x07: return @"X"; - case 0x08: return @"C"; - case 0x09: return @"V"; - case 0x0b: return @"B"; - case 0x2d: return @"N"; - case 0x2e: return @"M"; - case 0x2b: return @","; - case 0x2f: return @"."; - case 0x2c: return @"/"; - - case 0x47: return @"Clear"; - case 0x51: return @"Keypad ="; - case 0x4b: return @"Keypad /"; - case 0x43: return @"Keypad *"; - case 0x59: return @"Keypad 7"; - case 0x5b: return @"Keypad 8"; - case 0x5c: return @"Keypad 9"; - case 0x4e: return @"Keypad -"; - case 0x56: return @"Keypad 4"; - case 0x57: return @"Keypad 5"; - case 0x58: return @"Keypad 6"; - case 0x45: return @"Keypad +"; - case 0x53: return @"Keypad 1"; - case 0x54: return @"Keypad 2"; - case 0x55: return @"Keypad 3"; - case 0x52: return @"Keypad 0"; - case 0x41: return @"Keypad ."; - case 0x4c: return @"Enter"; - - case 0x7e: return @"Up"; - case 0x7d: return @"Down"; - case 0x7b: return @"Left"; - case 0x7c: return @"Right"; - default: - return [[NSString alloc] initWithFormat:@"Key 0x%x", keyCode]; - } -} - -- (BOOL)acceptsFirstResponder { - return self.isEnabled; -} - -- (BOOL)becomeFirstResponder { - self.backgroundColor = NSColor.selectedTextBackgroundColor; - return [super becomeFirstResponder]; -} - -- (BOOL)resignFirstResponder { - self.backgroundColor = NSColor.textBackgroundColor; - return [super resignFirstResponder]; -} - -- (void)setKeyCode:(CGKeyCode)keyCode { - _keyCode = keyCode; - self.stringValue = [NJKeyInputField stringForKeyCode:keyCode]; -} - -- (void)keyDown:(NSEvent *)theEvent { - if (!theEvent.isARepeat) { - if ((theEvent.modifierFlags & NSAlternateKeyMask) - && theEvent.keyCode == 0x33) { - // Allow Alt+Backspace to clear the field. - self.keyCode = NJKeyInputFieldEmpty; - [self.keyDelegate keyInputFieldDidClear:self]; - } else if ((theEvent.modifierFlags & NSAlternateKeyMask) - && theEvent.keyCode == 0x35) { - // Allow Alt+Escape to cancel. - ; - } else { - self.keyCode = theEvent.keyCode; - [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; - } - [self resignIfFirstResponder]; - } -} - -- (void)mouseDown:(NSEvent *)theEvent { - if (self.acceptsFirstResponder) - [self.window makeFirstResponder:self]; -} - -- (void)flagsChanged:(NSEvent *)theEvent { - // Many keys are only available on MacBook keyboards by using the - // Fn modifier key (e.g. Fn+Left for Home), so delay processing - // modifiers until the up event is received in order to let the - // user type these virtual keys. However, there is no actual event - // for modifier key up - so detect it by checking to see if any - // modifiers are still down. - if (!(theEvent.modifierFlags & NSDeviceIndependentModifierFlagsMask)) { - self.keyCode = theEvent.keyCode; - [self.keyDelegate keyInputField:self didChangeKey:_keyCode]; - } -} - -@end diff --git a/NJMapping.h b/NJMapping.h deleted file mode 100644 index 5653745..0000000 --- a/NJMapping.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// NJMapping.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class NJOutput; -@class NJInput; - -@interface NJMapping : NSObject - -@property (nonatomic, copy) NSString *name; -@property (nonatomic, readonly) NSMutableDictionary *entries; - -- (id)initWithName:(NSString *)name; -- (NJOutput *)objectForKeyedSubscript:(NJInput *)input; -- (void)setObject:(NJOutput *)output forKeyedSubscript:(NJInput *)input; -- (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 deleted file mode 100644 index 8cc5724..0000000 --- a/NJMapping.m +++ /dev/null @@ -1,91 +0,0 @@ -// -// NJMapping.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "NJMapping.h" - -#import "NJInput.h" -#import "NJOutput.h" - -@implementation NJMapping - -- (id)initWithName:(NSString *)name { - if ((self = [super init])) { - self.name = name ? name : @"Untitled"; - _entries = [[NSMutableDictionary alloc] init]; - } - return self; -} - -- (NJOutput *)objectForKeyedSubscript:(NJInput *)input { - return input ? _entries[input.uid] : nil; -} - -- (void)setObject:(NJOutput *)output forKeyedSubscript:(NJInput *)input { - if (input) { - if (output) - _entries[input.uid] = output; - else - [_entries removeObjectForKey:input.uid]; - } -} - -- (NSDictionary *)serialize { - NSMutableDictionary *entries = [[NSMutableDictionary alloc] initWithCapacity:_entries.count]; - for (id key in _entries) { - id serialized = [_entries[key] serialize]; - if (serialized) - entries[key] = serialized; - } - return @{ @"name": _name, @"entries": entries }; -} - -- (BOOL)writeToURL:(NSURL *)url error:(NSError **)error { - [NSProcessInfo.processInfo disableSuddenTermination]; - NSDictionary *serialization = [self serialize]; - NSData *json = [NSJSONSerialization dataWithJSONObject:serialization - options:NSJSONWritingPrettyPrinted - error:error]; - BOOL success = json && [json writeToURL:url options:NSDataWritingAtomic error:error]; - [NSProcessInfo.processInfo enableSuddenTermination]; - 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 deleted file mode 100644 index b252904..0000000 --- a/NJMappingsController.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// NJMappingsController.h -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class NJMapping; -@class NJOutputController; - -@interface NJMappingsController : NSObject -{ - IBOutlet NSButton *removeButton; - IBOutlet NSTableView *tableView; - IBOutlet NJOutputController *outputController; - IBOutlet NSButton *popoverActivate; - IBOutlet NSPopover *popover; - IBOutlet NSButton *moveUp; - IBOutlet NSButton *moveDown; -} - -@property (nonatomic, readonly) NJMapping *currentMapping; -@property (nonatomic, readonly) NSArray *mappings; - -- (NJMapping *)objectForKeyedSubscript:(NSString *)name; -- (NJMapping *)objectAtIndexedSubscript:(NSUInteger)idx; -- (void)addMappingWithContentsOfURL:(NSURL *)url; -- (void)activateMapping:(NJMapping *)mapping; -- (void)activateMappingForProcess:(NSString *)processName; -- (void)save; -- (void)load; - -- (IBAction)mappingPressed:(id)sender; -- (IBAction)addPressed:(id)sender; -- (IBAction)removePressed:(id)sender; -- (IBAction)moveUpPressed:(id)sender; -- (IBAction)moveDownPressed:(id)sender; -- (IBAction)importPressed:(id)sender; -- (IBAction)exportPressed:(id)sender; - -@end diff --git a/NJMappingsController.m b/NJMappingsController.m deleted file mode 100644 index ea8137f..0000000 --- a/NJMappingsController.m +++ /dev/null @@ -1,436 +0,0 @@ -// -// NJMappingsController.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import "NJMappingsController.h" - -#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; -} - -- (id)init { - if ((self = [super init])) { - _mappings = [[NSMutableArray alloc] init]; - _currentMapping = [[NJMapping alloc] initWithName:@"(default)"]; - manualMapping = _currentMapping; - [_mappings addObject:_currentMapping]; - } - 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]) - return mapping; - return nil; -} - -- (NJMapping *)objectAtIndexedSubscript:(NSUInteger)idx { - return idx < _mappings.count ? _mappings[idx] : nil; -} - -- (void)mappingsChanged { - [self save]; - [tableView reloadData]; - popoverActivate.title = _currentMapping.name; - [self updateInterfaceForCurrentMapping]; - [NSNotificationCenter.defaultCenter - postNotificationName:NJEventMappingListChanged - object:_mappings]; -} - -- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state - objects:(__unsafe_unretained id [])buffer - count:(NSUInteger)len { - return [_mappings countByEnumeratingWithState:state - objects:buffer - count:len]; -} - -- (void)activateMappingForProcess:(NSString *)processName { - if ([manualMapping.name.lowercaseString isEqualToString:@"@application"]) { - manualMapping.name = processName; - [self mappingsChanged]; - } else { - NJMapping *oldMapping = manualMapping; - NJMapping *newMapping = self[processName]; - if (!newMapping) - newMapping = oldMapping; - if (newMapping != _currentMapping) - [self activateMapping:newMapping]; - manualMapping = oldMapping; - } -} - -- (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; - if (mapping == _currentMapping) - return; - NSLog(@"Switching to mapping %@.", mapping.name); - manualMapping = mapping; - _currentMapping = mapping; - [self updateInterfaceForCurrentMapping]; - [outputController loadCurrent]; - [NSNotificationCenter.defaultCenter postNotificationName:NJEventMappingChanged - object:_currentMapping]; -} - -- (IBAction)addPressed:(id)sender { - NJMapping *newMapping = [[NJMapping alloc] initWithName:@"Untitled"]; - [_mappings addObject:newMapping]; - [self activateMapping:newMapping]; - [self mappingsChanged]; - [tableView editColumn:0 row:_mappings.count - 1 withEvent:nil select:YES]; -} - -- (IBAction)removePressed:(id)sender { - if (tableView.selectedRow == 0) - return; - - NSInteger selectedRow = tableView.selectedRow; - [_mappings removeObjectAtIndex:selectedRow]; - [self activateMapping:_mappings[MIN(selectedRow, _mappings.count - 1)]]; - [self mappingsChanged]; -} - -- (void)tableViewSelectionDidChange:(NSNotification *)notify { - [self activateMapping:self[tableView.selectedRow]]; -} - -- (id)tableView:(NSTableView *)view objectValueForTableColumn:(NSTableColumn *)column row:(NSInteger)index { - return self[index].name; -} - -- (void)tableView:(NSTableView *)view - setObjectValue:(NSString *)obj - forTableColumn:(NSTableColumn *)col - row:(NSInteger)index { - self[index].name = obj; - [self mappingsChanged]; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { - return _mappings.count; -} - -- (BOOL)tableView:(NSTableView *)view shouldEditTableColumn:(NSTableColumn *)column row:(NSInteger)index { - return YES; -} - -- (void)save { - NSLog(@"Saving mappings to defaults."); - NSMutableArray *ary = [[NSMutableArray alloc] initWithCapacity:_mappings.count]; - for (NJMapping *mapping in _mappings) - [ary addObject:[mapping serialize]]; - [NSUserDefaults.standardUserDefaults setObject:ary forKey:@"mappings"]; -} - -- (void)load { - NSUInteger selected = [NSUserDefaults.standardUserDefaults integerForKey:@"selected"]; - NSArray *mappings = [NSUserDefaults.standardUserDefaults arrayForKey:@"mappings"]; - [self loadAllFrom:mappings andActivate:selected]; -} - -- (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 - 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; - } - } - - if (newMappings.count) { - _mappings = newMappings; - if (selected >= newMappings.count) - selected = 0; - [self activateMapping:_mappings[selected]]; - [self mappingsChanged]; - } -} - -- (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; - } - } - 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" ]; - NSWindow *window = NSApplication.sharedApplication.keyWindow; - [panel beginSheetModalForWindow:window - completionHandler:^(NSInteger result) { - if (result != NSFileHandlingPanelOKButton) - return; - [panel close]; - [self addMappingWithContentsOfURL:panel.URL]; - }]; - -} - -- (void)exportPressed:(id)sender { - NSSavePanel *panel = [NSSavePanel savePanel]; - panel.allowedFileTypes = @[ @"enjoyable" ]; - NJMapping *mapping = _currentMapping; - panel.nameFieldStringValue = [mapping.name stringByFixingPathComponent]; - NSWindow *window = NSApplication.sharedApplication.keyWindow; - [panel beginSheetModalForWindow:window - completionHandler:^(NSInteger result) { - if (result != NSFileHandlingPanelOKButton) - return; - [panel close]; - NSError *error; - [mapping writeToURL:panel.URL error:&error]; - if (error) { - [window presentError:error - modalForWindow:window - delegate:nil - didPresentSelector:nil - contextInfo:nil]; - } - }]; -} - -- (IBAction)mappingPressed:(id)sender { - [popover showRelativeToRect:popoverActivate.bounds - ofView:popoverActivate - preferredEdge:NSMinXEdge]; -} - -- (void)popoverWillShow:(NSNotification *)notification { - popoverActivate.state = NSOnState; -} - -- (void)popoverWillClose:(NSNotification *)notification { - 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 if (rowIndexes.count == 1 && rowIndexes.firstIndex == 0) { - [pboard declareTypes:@[NSFilesPromisePboardType] owner:nil]; - [pboard setPropertyList:@[@"enjoyable"] forType:NSFilesPromisePboardType]; - return YES; - } else { - return NO; - } -} - -@end diff --git a/NJOutput.h b/NJOutput.h deleted file mode 100644 index b82c4e7..0000000 --- a/NJOutput.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// NJOutput.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -@class NJDeviceController; - -@interface NJOutput : NSObject - -@property (nonatomic, assign) float magnitude; -@property (nonatomic, assign) BOOL running; -@property (nonatomic, readonly) BOOL isContinuous; - -- (void)trigger; -- (void)untrigger; -- (BOOL)update:(NJDeviceController *)jc; - -- (NSDictionary *)serialize; -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings; -+ (NSString *)serializationCode; - -@end diff --git a/NJOutput.m b/NJOutput.m deleted file mode 100644 index 409b3e6..0000000 --- a/NJOutput.m +++ /dev/null @@ -1,87 +0,0 @@ -// -// NJOutput.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#import "NJOutput.h" - -#import "NJOutputKeyPress.h" -#import "NJOutputMapping.h" -#import "NJOutputMouseMove.h" -#import "NJOutputMouseButton.h" -#import "NJOutputMouseScroll.h" - -@implementation NJOutput { - BOOL running; -} - -+ (NSString *)serializationCode { - [self doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (NSDictionary *)serialize { - [self doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (BOOL)isEqual:(id)object { - return [object isKindOfClass:NJOutput.class] - && [[self serialize] isEqual:[object serialize]]; -} - -- (NSUInteger)hash { - return [[self serialize] hash]; -} - -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - // Don't crash loading old/bad mappings (but don't load them either). - if (![serialization isKindOfClass:NSDictionary.class]) - return nil; - NSString *type = serialization[@"type"]; - for (Class cls in @[NJOutputKeyPress.class, - NJOutputMapping.class, - NJOutputMouseMove.class, - NJOutputMouseButton.class, - NJOutputMouseScroll.class - ]) { - if ([type isEqualToString:cls.serializationCode]) - return [cls outputDeserialize:serialization withMappings:mappings]; - } - - return nil; -} - -- (void)trigger { -} - -- (void)untrigger { -} - -- (BOOL)update:(NJDeviceController *)jc { - return NO; -} - -- (BOOL)isContinuous { - return NO; -} - -- (BOOL)running { - return running; -} - -- (void)setRunning:(BOOL)newRunning { - if (running != newRunning) { - running = newRunning; - if (running) - [self trigger]; - else - [self untrigger]; - } -} - - -@end diff --git a/NJOutputController.h b/NJOutputController.h deleted file mode 100644 index f8052e7..0000000 --- a/NJOutputController.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// NJOutputController.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJKeyInputField.h" - -@class NJMappingsController; -@class NJDeviceController; -@class NJOutput; -@class NJOutputMouseMove; - -@interface NJOutputController : NSObject { - IBOutlet NJKeyInputField *keyInput; - IBOutlet NSMatrix *radioButtons; - IBOutlet NSSegmentedControl *mouseDirSelect; - IBOutlet NSSlider *mouseSpeedSlider; - IBOutlet NSSegmentedControl *mouseBtnSelect; - IBOutlet NSSegmentedControl *scrollDirSelect; - IBOutlet NSSlider *scrollSpeedSlider; - IBOutlet NSTextField *title; - IBOutlet NSPopUpButton *mappingPopup; - IBOutlet NJMappingsController *mappingsController; - IBOutlet NJDeviceController *inputController; -} - -@property (assign) BOOL enabled; - -- (void)loadCurrent; -- (IBAction)radioChanged:(id)sender; -- (IBAction)mdirChanged:(id)sender; -- (IBAction)mbtnChanged:(id)sender; -- (IBAction)sdirChanged:(id)sender; -- (IBAction)mouseSpeedChanged:(id)sender; -- (IBAction)scrollSpeedChanged:(id)sender; - -- (void)focusKey; - -@end diff --git a/NJOutputController.m b/NJOutputController.m deleted file mode 100644 index bcafe0d..0000000 --- a/NJOutputController.m +++ /dev/null @@ -1,283 +0,0 @@ -// -// NJOutputController.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#import "NJOutputController.h" - -#import "NJMappingsController.h" -#import "NJMapping.h" -#import "NJInput.h" -#import "NJEvents.h" -#import "NJDeviceController.h" -#import "NJKeyInputField.h" -#import "NJOutputMapping.h" -#import "NJOutputController.h" -#import "NJOutputKeyPress.h" -#import "NJOutputMouseButton.h" -#import "NJOutputMouseMove.h" -#import "NJOutputMouseScroll.h" - -@implementation NJOutputController - -- (id)init { - if ((self = [super init])) { - [NSNotificationCenter.defaultCenter - addObserver:self - selector:@selector(mappingListDidChange:) - name:NJEventMappingListChanged - object:nil]; - } - return self; -} - -- (void)dealloc { - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -- (void)cleanUpInterface { - NSInteger row = radioButtons.selectedRow; - - if (row != 1) { - keyInput.keyCode = NJKeyInputFieldEmpty; - [keyInput resignIfFirstResponder]; - } - - if (row != 2) { - [mappingPopup selectItemAtIndex:-1]; - [mappingPopup resignIfFirstResponder]; - } else if (!mappingPopup.selectedItem) - [mappingPopup selectItemAtIndex:0]; - - if (row != 3) { - mouseDirSelect.selectedSegment = -1; - mouseSpeedSlider.floatValue = mouseSpeedSlider.minValue; - [mouseDirSelect resignIfFirstResponder]; - } else { - if (mouseDirSelect.selectedSegment == -1) - mouseDirSelect.selectedSegment = 0; - if (!mouseSpeedSlider.floatValue) - mouseSpeedSlider.floatValue = 4; - } - - if (row != 4) { - mouseBtnSelect.selectedSegment = -1; - [mouseBtnSelect resignIfFirstResponder]; - } else if (mouseBtnSelect.selectedSegment == -1) - mouseBtnSelect.selectedSegment = 0; - - if (row != 5) { - scrollDirSelect.selectedSegment = -1; - scrollSpeedSlider.floatValue = scrollSpeedSlider.minValue; - [scrollDirSelect resignIfFirstResponder]; - } else { - if (scrollDirSelect.selectedSegment == -1) - scrollDirSelect.selectedSegment = 0; - if (scrollDirSelect.selectedSegment < 2 - && !scrollSpeedSlider.floatValue) - scrollSpeedSlider.floatValue = 15.f; - else if (scrollDirSelect.selectedSegment >= 2 - && scrollSpeedSlider.floatValue) - scrollSpeedSlider.floatValue = scrollSpeedSlider.minValue; - } - -} - -- (IBAction)radioChanged:(NSView *)sender { - [sender.window makeFirstResponder:sender]; - if (radioButtons.selectedRow == 1) - [keyInput.window makeFirstResponder:keyInput]; - [self commit]; -} - -- (void)keyInputField:(NJKeyInputField *)keyInput didChangeKey:(CGKeyCode)keyCode { - [radioButtons selectCellAtRow:1 column:0]; - [radioButtons.window makeFirstResponder:radioButtons]; - [self commit]; -} - -- (void)keyInputFieldDidClear:(NJKeyInputField *)keyInput { - [radioButtons selectCellAtRow:0 column:0]; - [self commit]; -} - -- (void)mappingChosen:(id)sender { - [radioButtons selectCellAtRow:2 column:0]; - [mappingPopup.window makeFirstResponder:mappingPopup]; - [self commit]; -} - -- (void)mdirChanged:(NSView *)sender { - [radioButtons selectCellAtRow:3 column:0]; - [sender.window makeFirstResponder:sender]; - [self commit]; -} - -- (void)mouseSpeedChanged:(NSSlider *)sender { - [radioButtons selectCellAtRow:3 column:0]; - [sender.window makeFirstResponder:sender]; - [self commit]; -} - -- (void)mbtnChanged:(NSView *)sender { - [radioButtons selectCellAtRow:4 column:0]; - [sender.window makeFirstResponder:sender]; - [self commit]; -} - -- (void)sdirChanged:(NSView *)sender { - [radioButtons selectCellAtRow:5 column:0]; - [sender.window makeFirstResponder:sender]; - [self commit]; -} - -- (void)scrollSpeedChanged:(NSSlider *)sender { - [radioButtons selectCellAtRow:5 column:0]; - [sender.window makeFirstResponder:sender]; - if (!sender.floatValue && scrollDirSelect.selectedSegment < 2) - scrollDirSelect.selectedSegment += 2; - else if (sender.floatValue && scrollDirSelect.selectedSegment >= 2) - scrollDirSelect.selectedSegment -= 2; - [self commit]; -} - -- (NJOutput *)currentOutput { - return mappingsController.currentMapping[inputController.selectedInput]; -} - -- (NJOutput *)makeOutput { - switch (radioButtons.selectedRow) { - case 0: - return nil; - case 1: - if (keyInput.hasKeyCode) { - NJOutputKeyPress *k = [[NJOutputKeyPress alloc] init]; - k.vk = keyInput.keyCode; - return k; - } else { - return nil; - } - break; - case 2: { - NJOutputMapping *c = [[NJOutputMapping alloc] init]; - c.mapping = mappingsController[mappingPopup.indexOfSelectedItem]; - return c; - } - case 3: { - NJOutputMouseMove *mm = [[NJOutputMouseMove alloc] init]; - mm.axis = mouseDirSelect.selectedSegment; - mm.speed = mouseSpeedSlider.floatValue; - return mm; - } - case 4: { - NJOutputMouseButton *mb = [[NJOutputMouseButton alloc] init]; - mb.button = mouseBtnSelect.selectedSegment == 0 ? kCGMouseButtonLeft : kCGMouseButtonRight; - return mb; - } - case 5: { - NJOutputMouseScroll *ms = [[NJOutputMouseScroll alloc] init]; - ms.direction = (scrollDirSelect.selectedSegment & 1) ? 1 : -1; - ms.speed = scrollDirSelect.selectedSegment < 2 - ? scrollSpeedSlider.floatValue - : 0.f; - return ms; - } - default: - return nil; - } -} - -- (void)commit { - [self cleanUpInterface]; - mappingsController.currentMapping[inputController.selectedInput] = [self makeOutput]; - [mappingsController save]; -} - -- (BOOL)enabled { - return [radioButtons isEnabled]; -} - -- (void)setEnabled:(BOOL)enabled { - [radioButtons setEnabled:enabled]; - [keyInput setEnabled:enabled]; - [mappingPopup setEnabled:enabled]; - [mouseDirSelect setEnabled:enabled]; - [mouseSpeedSlider setEnabled:enabled]; - [mouseBtnSelect setEnabled:enabled]; - [scrollDirSelect setEnabled:enabled]; - [scrollSpeedSlider setEnabled:enabled]; -} - -- (void)loadOutput:(NJOutput *)output forInput:(NJInput *)input { - if (!input) { - self.enabled = NO; - title.stringValue = @""; - } else { - self.enabled = YES; - NSString *inpFullName = input.name; - for (id cur = input.base; cur; cur = cur.base) { - inpFullName = [[NSString alloc] initWithFormat:@"%@ > %@", cur.name, inpFullName]; - } - title.stringValue = inpFullName; - } - - if ([output isKindOfClass:NJOutputKeyPress.class]) { - [radioButtons selectCellAtRow:1 column:0]; - keyInput.keyCode = [(NJOutputKeyPress*)output vk]; - } else if ([output isKindOfClass:NJOutputMapping.class]) { - [radioButtons selectCellAtRow:2 column:0]; - NSMenuItem *item = [mappingPopup itemWithRepresentedObject:[(NJOutputMapping *)output mapping]]; - [mappingPopup selectItem:item]; - if (!item) - [radioButtons selectCellAtRow:self.enabled ? 0 : -1 column:0]; - } - else if ([output isKindOfClass:NJOutputMouseMove.class]) { - [radioButtons selectCellAtRow:3 column:0]; - mouseDirSelect.selectedSegment = [(NJOutputMouseMove *)output axis]; - mouseSpeedSlider.floatValue = [(NJOutputMouseMove *)output speed]; - } - else if ([output isKindOfClass:NJOutputMouseButton.class]) { - [radioButtons selectCellAtRow:4 column:0]; - mouseBtnSelect.selectedSegment = [(NJOutputMouseButton *)output button] == kCGMouseButtonLeft ? 0 : 1; - } - else if ([output isKindOfClass:NJOutputMouseScroll.class]) { - [radioButtons selectCellAtRow:5 column:0]; - int direction = [(NJOutputMouseScroll *)output direction]; - float speed = [(NJOutputMouseScroll *)output speed]; - scrollDirSelect.selectedSegment = (direction > 0) + !speed * 2; - scrollSpeedSlider.floatValue = speed; - } else { - [radioButtons selectCellAtRow:self.enabled ? 0 : -1 column:0]; - } - [self cleanUpInterface]; -} - -- (void)loadCurrent { - [self loadOutput:self.currentOutput forInput:inputController.selectedInput]; -} - -- (void)focusKey { - if (radioButtons.selectedRow <= 1) - [keyInput.window makeFirstResponder:keyInput]; - else - [keyInput resignIfFirstResponder]; -} - -- (void)mappingListDidChange:(NSNotification *)note { - NSArray *mappings = note.object; - NJMapping *current = mappingPopup.selectedItem.representedObject; - [mappingPopup.menu removeAllItems]; - for (NJMapping *mapping in mappings) { - NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:mapping.name - action:@selector(mappingChosen:) - keyEquivalent:@""]; - item.target = self; - item.representedObject = mapping; - [mappingPopup.menu addItem:item]; - } - [mappingPopup selectItemWithRepresentedObject:current]; -} - -@end diff --git a/NJOutputKeyPress.h b/NJOutputKeyPress.h deleted file mode 100644 index 052b010..0000000 --- a/NJOutputKeyPress.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NJOutputKeyPress.h -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJOutput.h" - -@interface NJOutputKeyPress : NJOutput - -@property (nonatomic, assign) CGKeyCode vk; - -@end diff --git a/NJOutputKeyPress.m b/NJOutputKeyPress.m deleted file mode 100644 index 622cf41..0000000 --- a/NJOutputKeyPress.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// NJOutputKeyPress.m -// Enjoy -// -// Created by Sam McCall on 5/05/09. -// - -#import "NJOutputKeyPress.h" - -#import "NJKeyInputField.h" - -@implementation NJOutputKeyPress - -+ (NSString *)serializationCode { - return @"key press"; -} - -- (NSDictionary *)serialize { - return _vk != NJKeyInputFieldEmpty - ? @{ @"type": self.class.serializationCode, @"key": @(_vk) } - : nil; -} - -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - NJOutputKeyPress *output = [[NJOutputKeyPress alloc] init]; - output.vk = [serialization[@"key"] intValue]; - return output; -} - -- (void)trigger { - CGEventRef keyDown = CGEventCreateKeyboardEvent(NULL, _vk, YES); - CGEventPost(kCGHIDEventTap, keyDown); - CFRelease(keyDown); -} - -- (void)untrigger { - CGEventRef keyUp = CGEventCreateKeyboardEvent(NULL, _vk, NO); - CGEventPost(kCGHIDEventTap, keyUp); - CFRelease(keyUp); -} - -@end diff --git a/NJOutputMapping.h b/NJOutputMapping.h deleted file mode 100644 index e8c3029..0000000 --- a/NJOutputMapping.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// NJOutputMapping.h -// Enjoy -// -// Created by Sam McCall on 6/05/09. -// Copyright 2009 University of Otago. All rights reserved. -// - -#import "NJOutput.h" - -@class NJMapping; - -@interface NJOutputMapping : NJOutput - -@property (nonatomic, weak) NJMapping *mapping; - -@end diff --git a/NJOutputMapping.m b/NJOutputMapping.m deleted file mode 100644 index fa8fda1..0000000 --- a/NJOutputMapping.m +++ /dev/null @@ -1,44 +0,0 @@ -// -// NJOutputMapping.m -// Enjoy -// -// Created by Sam McCall on 6/05/09. -// - -#import "NJOutputMapping.h" - -#import "EnjoyableApplicationDelegate.h" -#import "NJMapping.h" -#import "NJMappingsController.h" - -@implementation NJOutputMapping - -+ (NSString *)serializationCode { - return @"mapping"; -} - -- (NSDictionary *)serialize { - return _mapping - ? @{ @"type": self.class.serializationCode, @"name": _mapping.name } - : nil; -} - -+ (NJOutputMapping *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - NSString *name = serialization[@"name"]; - NJOutputMapping *output = [[NJOutputMapping alloc] init]; - for (NJMapping *mapping in mappings) { - if ([mapping.name isEqualToString:name]) { - output.mapping = mapping; - return output; - } - } - return nil; -} - -- (void)trigger { - EnjoyableApplicationDelegate *ctrl = (EnjoyableApplicationDelegate *)NSApplication.sharedApplication.delegate; - [ctrl.mappingsController activateMapping:_mapping]; -} - -@end diff --git a/NJOutputMouseButton.h b/NJOutputMouseButton.h deleted file mode 100644 index 4d790e5..0000000 --- a/NJOutputMouseButton.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// NJOutputMouseButton.h -// Enjoy -// -// Created by Yifeng Huang on 7/27/12. -// - -#import "NJOutput.h" - -@interface NJOutputMouseButton : NJOutput - -@property (nonatomic, assign) CGMouseButton button; - -@end diff --git a/NJOutputMouseButton.m b/NJOutputMouseButton.m deleted file mode 100644 index d5b1f16..0000000 --- a/NJOutputMouseButton.m +++ /dev/null @@ -1,51 +0,0 @@ -// -// NJOutputMouseButton.m -// Enjoy -// -// Created by Yifeng Huang on 7/27/12. -// - -#import "NJOutputMouseButton.h" - -@implementation NJOutputMouseButton - -+ (NSString *)serializationCode { - return @"mouse button"; -} - -- (NSDictionary *)serialize { - return @{ @"type": self.class.serializationCode, @"button": @(_button) }; -} - -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - NJOutputMouseButton *output = [[NJOutputMouseButton alloc] init]; - output.button = [serialization[@"button"] intValue]; - return output; -} - -- (void)trigger { - CGFloat height = NSScreen.mainScreen.frame.size.height; - NSPoint mouseLoc = NSEvent.mouseLocation; - CGEventType eventType = (_button == kCGMouseButtonLeft) ? kCGEventLeftMouseDown : kCGEventRightMouseDown; - CGEventRef click = CGEventCreateMouseEvent(NULL, - eventType, - CGPointMake(mouseLoc.x, height - mouseLoc.y), - _button); - CGEventPost(kCGHIDEventTap, click); - CFRelease(click); -} - -- (void)untrigger { - CGFloat height = NSScreen.mainScreen.frame.size.height; - NSPoint mouseLoc = NSEvent.mouseLocation; - CGEventType eventType = (_button == kCGMouseButtonLeft) ? kCGEventLeftMouseUp : kCGEventRightMouseUp; - CGEventRef click = CGEventCreateMouseEvent(NULL, - eventType, - CGPointMake(mouseLoc.x, height - mouseLoc.y), - _button); - CGEventPost(kCGHIDEventTap, click); - CFRelease(click); -} - -@end diff --git a/NJOutputMouseMove.h b/NJOutputMouseMove.h deleted file mode 100644 index 428416f..0000000 --- a/NJOutputMouseMove.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NJOutputMouseMove.h -// Enjoy -// -// Created by Yifeng Huang on 7/26/12. -// - -#import "NJOutput.h" - -@interface NJOutputMouseMove : NJOutput - -@property (nonatomic, assign) int axis; -@property (nonatomic, assign) float speed; - -@end diff --git a/NJOutputMouseMove.m b/NJOutputMouseMove.m deleted file mode 100644 index 884a3d3..0000000 --- a/NJOutputMouseMove.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// NJOutputMouseMove.m -// Enjoy -// -// Created by Yifeng Huang on 7/26/12. -// - -#import "NJOutputMouseMove.h" - -#import "NJDeviceController.h" - -@implementation NJOutputMouseMove - -+ (NSString *)serializationCode { - return @"mouse move"; -} - -- (NSDictionary *)serialize { - return @{ @"type": self.class.serializationCode, - @"axis": @(_axis), - @"speed": @(_speed), - }; -} - -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - NJOutputMouseMove *output = [[NJOutputMouseMove alloc] init]; - output.axis = [serialization[@"axis"] intValue]; - output.speed = [serialization[@"speed"] floatValue]; - if (!output.speed) - output.speed = 4; - return output; -} - -- (BOOL)isContinuous { - return YES; -} - -- (BOOL)update:(NJDeviceController *)jc { - if (self.magnitude < 0.05) - return NO; // dead zone - - CGFloat height = NSScreen.mainScreen.frame.size.height; - - float dx = 0.f, dy = 0.f; - switch (_axis) { - case 0: - dx = -self.magnitude * _speed; - break; - case 1: - dx = self.magnitude * _speed; - break; - case 2: - dy = -self.magnitude * _speed; - break; - case 3: - dy = self.magnitude * _speed; - break; - } - NSPoint mouseLoc = jc.mouseLoc; - mouseLoc.x += dx; - mouseLoc.y -= dy; - jc.mouseLoc = mouseLoc; - - CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, - CGPointMake(mouseLoc.x, height - mouseLoc.y), - 0); - CGEventSetType(move, kCGEventMouseMoved); - CGEventSetIntegerValueField(move, kCGMouseEventDeltaX, (int)dx); - CGEventSetIntegerValueField(move, kCGMouseEventDeltaY, (int)dy); - CGEventPost(kCGHIDEventTap, move); - CFRelease(move); - return YES; -} - -@end diff --git a/NJOutputMouseScroll.h b/NJOutputMouseScroll.h deleted file mode 100644 index dd0444a..0000000 --- a/NJOutputMouseScroll.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NJOutputMouseScroll.h -// Enjoy -// -// Created by Yifeng Huang on 7/28/12. -// - -#import "NJOutput.h" - -@interface NJOutputMouseScroll : NJOutput - -@property (nonatomic, assign) int direction; -@property (nonatomic, assign) float speed; - -@end diff --git a/NJOutputMouseScroll.m b/NJOutputMouseScroll.m deleted file mode 100644 index 812685f..0000000 --- a/NJOutputMouseScroll.m +++ /dev/null @@ -1,61 +0,0 @@ -// -// NJOutputMouseScroll.m -// Enjoy -// -// Created by Yifeng Huang on 7/28/12. -// - -#import "NJOutputMouseScroll.h" - -@implementation NJOutputMouseScroll - -+ (NSString *)serializationCode { - return @"mouse scroll"; -} - -- (NSDictionary *)serialize { - return @{ @"type": self.class.serializationCode, - @"direction": @(_direction), - @"speed": @(_speed) - }; -} - -+ (NJOutput *)outputDeserialize:(NSDictionary *)serialization - withMappings:(NSArray *)mappings { - NJOutputMouseScroll *output = [[NJOutputMouseScroll alloc] init]; - output.direction = [serialization[@"direction"] intValue]; - output.speed = [serialization[@"direction"] floatValue]; - return output; -} - -- (BOOL)isContinuous { - return !!self.speed; -} - -- (void)trigger { - if (!self.speed) { - CGEventRef scroll = CGEventCreateScrollWheelEvent(NULL, - kCGScrollEventUnitLine, - 1, - _direction); - CGEventPost(kCGHIDEventTap, scroll); - CFRelease(scroll); - } -} - -- (BOOL)update:(NJDeviceController *)jc { - if (self.magnitude < 0.05f) - return NO; // dead zone - - int amount = (int)(_speed * self.magnitude * _direction); - CGEventRef scroll = CGEventCreateScrollWheelEvent(NULL, - kCGScrollEventUnitPixel, - 1, - amount); - CGEventPost(kCGHIDEventTap, scroll); - CFRelease(scroll); - - return YES; -} - -@end diff --git a/NSError+Description.h b/NSError+Description.h deleted file mode 100644 index a722e9e..0000000 --- a/NSError+Description.h +++ /dev/null @@ -1,9 +0,0 @@ -#import - -@interface NSError (Description) - -+ (NSError *)errorWithDomain:(NSString *)domain - code:(NSInteger)code - description:(NSString *)description; - -@end diff --git a/NSError+Description.m b/NSError+Description.m deleted file mode 100644 index 126bb17..0000000 --- a/NSError+Description.m +++ /dev/null @@ -1,13 +0,0 @@ -#import "NSError+Description.h" - -@implementation NSError (Description) - -+ (NSError *)errorWithDomain:(NSString *)domain - code:(NSInteger)code - description:(NSString *)description { - NSDictionary *errorDict = @{ NSLocalizedDescriptionKey : description }; - return [NSError errorWithDomain:domain code:code userInfo:errorDict]; - -} - -@end diff --git a/NSFileManager+UniqueNames.h b/NSFileManager+UniqueNames.h deleted file mode 100644 index 82c57d4..0000000 --- a/NSFileManager+UniqueNames.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// NSFileManager+UniqueNames.h -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import - -@interface NSFileManager (UniqueNames) - -- (NSURL *)generateUniqueURLWithBase:(NSURL *)canonical; - // Generate a probably-unique URL by trying sequential indices, e.g. - // file://Test.txt - // file://Test (1).txt - // file://Test (2).txt - // and so on. - // - // The URL is only probably unique. It is subject to the usual - // race conditions associated with generating a filename before - // actually opening it. It also does not check remote resources, - // as it operates synchronously. Finally, it gives up after 10,000 - // indices. - -@end diff --git a/NSFileManager+UniqueNames.m b/NSFileManager+UniqueNames.m deleted file mode 100644 index c7eb768..0000000 --- a/NSFileManager+UniqueNames.m +++ /dev/null @@ -1,31 +0,0 @@ -// -// NSFileManager+UniqueNames.m -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import "NSFileManager+UniqueNames.h" - -@implementation NSFileManager (UniqueNames) - -- (NSURL *)generateUniqueURLWithBase:(NSURL *)canonical { - // Punt for cases that are just too hard. - if (!canonical.isFileURL) - return canonical; - - NSString *trying = canonical.path; - NSString *dirname = [trying stringByDeletingLastPathComponent]; - NSString *basename = [trying.lastPathComponent stringByDeletingPathExtension]; - NSString *extension = trying.pathExtension; - int index = 1; - while ([self fileExistsAtPath:trying] && index < 10000) { - NSString *indexName = [NSString stringWithFormat:@"%@ (%d)", basename, index++]; - indexName = [indexName stringByAppendingPathExtension:extension]; - trying = [dirname stringByAppendingPathComponent:indexName]; - } - return [NSURL fileURLWithPath:trying]; -} - -@end diff --git a/NSMenu+RepresentedObjectAccessors.h b/NSMenu+RepresentedObjectAccessors.h deleted file mode 100644 index ecb28a6..0000000 --- a/NSMenu+RepresentedObjectAccessors.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// NSMenu+RepresentedObjectAccessors.h -// Enjoyable -// -// Created by Joe Wreschnig on 3/4/13. -// -// - -#import - -@interface NSMenu (RepresentedObjectAccessors) - // Helpers for using represented objects in menu items. - -- (NSMenuItem *)itemWithRepresentedObject:(id)object; - // Returns the first menu item in the receiver that has a given - // represented object. - -- (void)removeItemWithRepresentedObject:(id)object; - // Removes the first menu item representing the given object in the - // receiver. - // - // After it removes the menu item, this method posts an - // NSMenuDidRemoveItemNotification. - -- (NSMenuItem *)lastItem; - // Return the last menu item in the receiver, or nil if the menu - // has no items. - -- (void)removeLastItem; - // Removes the last menu item in the receiver, if there is one. - // - // After and if it removes the menu item, this method posts an - // NSMenuDidRemoveItemNotification. - -@end - -@interface NSPopUpButton (RepresentedObjectAccessors) - -- (NSMenuItem *)itemWithRepresentedObject:(id)object; - // Returns the first item in the receiver's menu that has a given - // represented object. - -- (void)selectItemWithRepresentedObject:(id)object; - // Selects the first item in the receiver's menu that has a give - // represented object. - -@end diff --git a/NSMenu+RepresentedObjectAccessors.m b/NSMenu+RepresentedObjectAccessors.m deleted file mode 100644 index a63f083..0000000 --- a/NSMenu+RepresentedObjectAccessors.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// NSMenu+RepresentedObjectAccessors.m -// Enjoyable -// -// Created by Joe Wreschnig on 3/4/13. -// -// - -#import "NSMenu+RepresentedObjectAccessors.h" - -@implementation NSMenu (RepresentedObjectAccessors) - -- (NSMenuItem *)itemWithRepresentedObject:(id)object { - for (NSMenuItem *item in self.itemArray) - if ([item.representedObject isEqual:object]) - return item; - return nil; -} - -- (void)removeItemWithRepresentedObject:(id)object { - NSInteger idx = [self indexOfItemWithRepresentedObject:object]; - if (idx != -1) - [self removeItemAtIndex:idx]; -} - -- (NSMenuItem *)lastItem { - return self.itemArray.lastObject; -} - -- (void)removeLastItem { - if (self.numberOfItems) - [self removeItemAtIndex:self.numberOfItems - 1]; -} - -@end - -@implementation NSPopUpButton (RepresentedObjectAccessors) - -- (NSMenuItem *)itemWithRepresentedObject:(id)object { - return [self.menu itemWithRepresentedObject:object]; -} - -- (void)selectItemWithRepresentedObject:(id)object { - [self selectItemAtIndex:[self indexOfItemWithRepresentedObject:object]]; -} - - -@end diff --git a/NSMutableArray+MoveObject.h b/NSMutableArray+MoveObject.h deleted file mode 100644 index c1c60d2..0000000 --- a/NSMutableArray+MoveObject.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// NSMutableArray+MoveObject.h -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import - -@interface NSMutableArray (MoveObject) - -- (void)moveObjectAtIndex:(NSUInteger)src toIndex:(NSUInteger)dst; - -@end diff --git a/NSMutableArray+MoveObject.m b/NSMutableArray+MoveObject.m deleted file mode 100644 index 8d9a4ee..0000000 --- a/NSMutableArray+MoveObject.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// NSMutableArray+MoveObject.m -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import "NSMutableArray+MoveObject.h" - -@implementation NSMutableArray (MoveObject) - -- (void)moveObjectAtIndex:(NSUInteger)src toIndex:(NSUInteger)dst { - id obj = self[src]; - [self removeObjectAtIndex:src]; - [self insertObject:obj atIndex:dst > src ? dst - 1 : dst]; -} - -@end diff --git a/NSString+FixFilename.h b/NSString+FixFilename.h deleted file mode 100644 index b3e318f..0000000 --- a/NSString+FixFilename.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// NSString+FixFilename.h -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import - -@interface NSCharacterSet (FixFilename) - -+ (NSCharacterSet *)invalidPathComponentCharacterSet; - // A character set containing the characters that are invalid to - // use in path components on common filesystems. - -@end - -@interface NSString (FixFilename) - -- (NSString *)stringByFixingPathComponent; - // Does various operations to make this string suitable for use as - // a single path component of a normal filename. Removes - // characters that are invalid. Strips whitespace from the - // beginning and end. If the first character is a . or a -, a _ is - // added to the front. - -@end diff --git a/NSString+FixFilename.m b/NSString+FixFilename.m deleted file mode 100644 index 06a837c..0000000 --- a/NSString+FixFilename.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// NSString+FixFilename.m -// Enjoyable -// -// Created by Joe Wreschnig on 3/7/13. -// -// - -#import "NSString+FixFilename.h" - -@implementation NSCharacterSet (FixFilename) - -+ (NSCharacterSet *)invalidPathComponentCharacterSet { - return [NSCharacterSet characterSetWithCharactersInString:@"\"\\/:*?<>|"]; -} - -@end - -@implementation NSString (FixFilename) - -- (NSString *)stringByFixingPathComponent { - NSCharacterSet *invalid = NSCharacterSet.invalidPathComponentCharacterSet; - NSCharacterSet *whitespace = NSCharacterSet.whitespaceAndNewlineCharacterSet; - NSArray *parts = [self componentsSeparatedByCharactersInSet:invalid]; - NSString *name = [parts componentsJoinedByString:@"_"]; - name = [name stringByTrimmingCharactersInSet:whitespace]; - if (!name.length) - return @"_"; - unichar first = [name characterAtIndex:0]; - if (first == '.' || first == '-') - name = [@"_" stringByAppendingString:name]; - return name; -} - -@end diff --git a/NSView+FirstResponder.h b/NSView+FirstResponder.h deleted file mode 100644 index cd89aa5..0000000 --- a/NSView+FirstResponder.h +++ /dev/null @@ -1,11 +0,0 @@ -#import - -@interface NSView (FirstResponder) - -- (BOOL)resignIfFirstResponder; - // Resign first responder status if this view is the active first - // responder in its window. Returns whether first responder status - // was resigned; YES if it was and NO if refused or the view was - // not the first responder. - -@end diff --git a/NSView+FirstResponder.m b/NSView+FirstResponder.m deleted file mode 100644 index 0575b0d..0000000 --- a/NSView+FirstResponder.m +++ /dev/null @@ -1,12 +0,0 @@ -#import "NSView+FirstResponder.h" - -@implementation NSView (FirstResponder) - -- (BOOL)resignIfFirstResponder { - NSWindow *window = self.window; - return window.firstResponder == self - ? [window makeFirstResponder:nil] - : NO; -} - -@end diff --git a/Other Sources/Enjoyable_Prefix.pch b/Other Sources/Enjoyable_Prefix.pch new file mode 100644 index 0000000..4845e9d --- /dev/null +++ b/Other Sources/Enjoyable_Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header for all source files of the 'Enjoy' target in the 'Enjoy' project +// + +#ifdef __OBJC__ + #import +#endif + +#import + +#import "NSError+Description.h" +#import "NSMenu+RepresentedObjectAccessors.h" +#import "NSView+FirstResponder.h" +#import "NSMutableArray+MoveObject.h" +#import "NSFileManager+UniqueNames.h" +#import "NSString+FixFilename.h" diff --git a/Other Sources/NJEvents.h b/Other Sources/NJEvents.h new file mode 100644 index 0000000..ce87b24 --- /dev/null +++ b/Other Sources/NJEvents.h @@ -0,0 +1,12 @@ +// +// NJEvents.h +// Enjoyable +// +// Created by Joe Wreschnig on 3/2/13. +// +// + +#define NJEventMappingChanged @"NJEventMappingChanged" +#define NJEventMappingListChanged @"NJEventMappingListChanged" +#define NJEventTranslationActivated @"NJEventTranslationActivated" +#define NJEventTranslationDeactivated @"NJEventTranslationDeactivated" diff --git a/Other Sources/main.m b/Other Sources/main.m new file mode 100644 index 0000000..9fbf37b --- /dev/null +++ b/Other Sources/main.m @@ -0,0 +1,13 @@ +// +// main.m +// Enjoy +// +// Created by Sam McCall on 4/05/09. +// + +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/Resources/English.lproj/InfoPlist.strings b/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..84fe32f Binary files /dev/null and b/Resources/English.lproj/InfoPlist.strings differ diff --git a/Resources/English.lproj/MainMenu.xib b/Resources/English.lproj/MainMenu.xib new file mode 100644 index 0000000..cd9a1b9 --- /dev/null +++ b/Resources/English.lproj/MainMenu.xib @@ -0,0 +1,3084 @@ + + + + 1080 + 12C2034 + 3084 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 3084 + + + NSBox + NSButton + NSButtonCell + NSCustomObject + NSCustomView + NSMatrix + NSMenu + NSMenuItem + NSOutlineView + NSPopUpButton + NSPopUpButtonCell + NSPopover + NSScrollView + NSScroller + NSSegmentedCell + NSSegmentedControl + NSSlider + NSSliderCell + NSSplitView + NSTableColumn + NSTableView + NSTextField + NSTextFieldCell + NSToolbar + NSToolbarFlexibleSpaceItem + NSToolbarItem + NSView + NSViewController + NSWindowTemplate + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + + + Enjoyable + CA + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + Enjoyable + + + + About Enjoyable + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide Enjoyable + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit Enjoyable + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Mappings + + 1048576 + 2147483647 + + + submenuAction: + + Mappings + + + + Enable + r + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + List + l + 1048576 + 2147483647 + + + + + + Import… + o + 1048576 + 2147483647 + + + + + + Export… + s + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + 1 + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + + + Enjoyable Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{355, 59}, {640, 320}} + 1685585920 + Enjoyable + NSWindow + + + AC1F5C48-4C16-4C9D-9779-B783AF35E2E1 + + + YES + YES + NO + YES + 2 + 1 + + + + 2CB21E35-9CF1-4C67-9670-31139C914D10 + + Enabled + Enabled + + + + 268 + {{7, 14}, {36, 25}} + _NS:9 + YES + + 67108864 + 134217728 + + + LucidaGrande + 13 + 1044 + + _NS:9 + + -1228128256 + 163 + + NSImage + NSRightFacingTriangleTemplate + + + + 200 + 25 + + NO + + + + + {36, 25} + {36, 25} + YES + YES + 0 + YES + 0 + + + + 4AC66688-76E8-47ED-AC0A-7462220A4019 + + Mapping Selector + Mapping Selector + + + + 268 + {{0, 14}, {140, 25}} + _NS:9 + YES + + 67108864 + 134217728 + (default) + + _NS:9 + + 918306816 + 163 + + NSImage + NSListViewTemplate + + + + 400 + 75 + + NO + + + + + {13, 25} + {141, 25} + YES + NO + 0 + YES + 0 + + + NSToolbarFlexibleSpaceItem + + Flexible Space + + + + + + {1, 5} + {20000, 32} + YES + YES + -1 + YES + 0 + + YES + YES + + + 1048576 + 2147483647 + + + + + + + + + + + + + + + + + + + {640, 320} + + + 256 + + + + 274 + + + + 256 + + + + 274 + + + + 2304 + + + + 256 + {200, 318} + + + + YES + NO + YES + + + 256 + {{474, 0}, {16, 17}} + + + + 197 + 16 + 1000 + + 75497536 + 2048 + + + LucidaGrande + 11 + 3100 + + + 3 + MC4zMzMzMzI5OQA + + + 6 + System + headerTextColor + + 3 + MAA + + + + + 67108928 + 2624 + Text Cell + + + + 6 + System + controlBackgroundColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + + + 1 + YES + + + + 3 + 2 + + 3 + MQA + + + 6 + System + gridColor + + 3 + MC41AA + + + 20 + 306184192 + + + 4 + 15 + 0 + YES + 0 + 1 + -1 + NO + + + {{1, 1}, {200, 318}} + + + + + + 4 + + + + -2147483392 + {{1, 1}, {8, 298}} + + + + NO + + _doScroller: + 0.4210526 + + + + -2147483392 + {{-100, -100}, {473, 15}} + + + + NO + 1 + + _doScroller: + 0.99789030000000001 + + + {202, 320} + + + + 150034 + + + + QSAAAEEgAABBsAAAQbAAAA + 0.25 + 4 + 1 + + + {202, 320} + + + + NSView + + + + 256 + + + + 265 + {{228, 21}, {130, 12}} + + + + _NS:9 + YES + + -2080374784 + 262144 + + _NS:9 + + 30 + 0.0 + 15 + 0.0 + 0 + 1 + NO + NO + + NO + + + + 265 + {{228, 107}, {176, 12}} + + + + _NS:9 + YES + + -2080374784 + 262144 + + _NS:9 + + 20 + 0.0 + 4 + 0.0 + 0 + 1 + NO + NO + + NO + + + + 265 + {{226, 117}, {180, 20}} + + + + _NS:9 + YES + + 67108864 + 131072 + + _NS:9 + + + + 44 + ← + YES + 0 + + + 44 + → + 1 + 0 + + + 42 + ↑ + 0 + + + 41 + ↓ + 0 + + + 1 + + NO + + + + 265 + {{226, 31}, {180, 20}} + + + + _NS:9 + YES + + 67108864 + 131072 + + _NS:9 + + + + 64 + ↑ + Scroll up continuously + YES + 0 + + + 63 + ↓ + Scroll down continuously + 1 + 0 + + + ⤒ + Scroll up one step + 0 + + + ⤓ + Scroll down one step + 0 + + + 1 + + NO + + + + 265 + {{226, 67}, {180, 24}} + + + + _NS:9 + YES + + 67108864 + 0 + + LucidaGrande + 13 + 16 + + _NS:9 + + + + 87 + Left + YES + 0 + + + 86 + Right + 1 + 0 + + + 1 + + NO + + + + 265 + {{228, 197}, {176, 24}} + + + + _NS:9 + NJKeyInputField + + + + 265 + {{225, 152}, {182, 26}} + + + + YES + + -1539309504 + 2048 + + + 109199360 + 129 + + + 400 + 75 + + YES + + OtherViews + + + -1 + 1 + YES + YES + 2 + + NO + + + + 268 + {{20, 16}, {200, 256}} + + + + NO + 6 + 1 + + + 603979776 + 0 + Do nothing + + + 1 + 1211912448 + 0 + + NSImage + NSRadioButton + + + NSRadioButton + + + + 200 + 25 + + + 603979776 + 0 + Press a key + + + 1211912448 + 0 + + + + 400 + 75 + + + 603979776 + 0 + Switch to mapping + + + 1211912448 + 0 + + + + 400 + 75 + + + 603979776 + 0 + Move the mouse + + + 1211912448 + 0 + + + + 400 + 75 + + + 603979776 + 0 + Press a mouse button + + + 1211912448 + 0 + + + + 400 + 75 + + + 603979776 + 0 + Scroll the mouse + + + 1211912448 + 0 + + + + 400 + 75 + + + {200, 41} + {4, 2} + 1353195520 + NSActionCell + + 603979776 + 0 + Radio + + 1211912448 + 0 + + + + 400 + 75 + + -1 + -1 + + 6 + System + controlColor + + + + + + + + 266 + {{0, 289}, {429, 17}} + + + + YES + + 67108928 + 138414656 + + + LucidaGrande-Bold + 13 + 16 + + No input selected + + + + + NO + + + + 10 + {{12, 278}, {405, 5}} + + + + {0, 0} + + 67108864 + 0 + Box + + + 6 + System + textBackgroundColor + + + + 3 + MCAwLjgwMDAwMDAxAA + + + 3 + 2 + 0 + NO + + + {{211, 0}, {429, 320}} + + + + NSView + + + {640, 320} + + + + YES + Main Split + + YES + + + + + + + + + + + + {640, 320} + + + + + {{0, 0}, {1440, 878}} + {640, 375} + {10000000000000, 10000000000000} + Enjoyable + YES + + + + 256 + + + + 274 + + + + 2304 + + + + 256 + {198, 198} + + + + YES + NO + YES + + + 256 + {{306, 0}, {16, 17}} + + + + 190 + 190 + 190 + + 75497536 + 2048 + + + + 3 + MC4zMzMzMzI5OQA + + + + + 337641536 + 2048 + Text Cell + + + + + + YES + + + + 3 + 2 + + + 20 + 44072960 + + + 1 + 15 + 0 + NO + 0 + 1 + -1 + + + {{1, 1}, {198, 198}} + + + + + + 4 + + + + -2147483392 + {{306, 1}, {15, 403}} + + + + NO + + _doScroller: + 0.99766359999999998 + + + + -2147483392 + {{-100, -100}, {366, 16}} + + + + NO + 1 + + _doScroller: + 0.98123324396782841 + + + {{0, 20}, {200, 200}} + + + + 150034 + + + + QSAAAEEgAABBsAAAQbAAAA + 0.25 + 4 + 1 + + + + 268 + {{66, -1}, {68, 23}} + + + + _NS:22 + YES + + -2080374784 + 168034304 + + + LucidaGrande + 9 + 3614 + + _NS:22 + + 1221349376 + 162 + + + 400 + 75 + + NO + + + + 292 + {{0, -1}, {34, 23}} + + + + YES + + 67108864 + 134479872 + + + + -2033958912 + 268435618 + + NSImage + NSAddTemplate + + + n + 400 + 75 + + NO + + + + 292 + {{166, -1}, {34, 23}} + + + + YES + + 67108864 + 134479872 + ⬇ + + + -2033434624 + 268435618 + +  + 400 + 75 + + NO + + + + 292 + {{133, -1}, {34, 23}} + + + + YES + + 67108864 + 134479872 + ⬆ + + + -2033434624 + 268435618 + +  + 400 + 75 + + NO + + + + 292 + {{33, -1}, {34, 23}} + + + + YES + + 67108864 + 134479872 + + + + -2033958912 + 268435618 + + NSImage + NSRemoveTemplate + + + CA + 400 + 75 + + NO + + + {200, 220} + + + + NSView + + + EnjoyableApplicationDelegate + + + NJMappingsController + + + NJDeviceController + + + NJOutputController + + + + + 0 + 1 + 0.0 + 0.0 + YES + + + + + + + terminate: + + + + 449 + + + + delegate + + + + 483 + + + + dockMenu + + + + 732 + + + + showHelp: + + + + 870 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + performZoom: + + + + 240 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + delegate + + + + 517 + + + + dataSource + + + + 518 + + + + outlineView + + + + 648 + + + + mappingsController + + + + 822 + + + + outputController + + + + 826 + + + + translatingEventsMenu + + + + 877 + + + + translatingEventsChanged: + + + + 878 + + + + translatingEventsButton + + + + 879 + + + + dockMenuBase + + + + 726 + + + + inputController + + + + 819 + + + + mappingsController + + + + 820 + + + + window + + + + 865 + + + + removePressed: + + + + 516 + + + + removeButton + + + + 519 + + + + tableView + + + + 520 + + + + exportPressed: + + + + 815 + + + + importPressed: + + + + 816 + + + + outputController + + + + 827 + + + + mappingPressed: + + + + 855 + + + + popover + + + + 856 + + + + popoverActivate + + + + 857 + + + + addPressed: + + + + 515 + + + + moveUpPressed: + + + + 899 + + + + moveDownPressed: + + + + 900 + + + + moveUp + + + + 901 + + + + moveDown + + + + 902 + + + + dataSource + + + + 647 + + + + delegate + + + + 696 + + + + delegate + + + + 892 + + + + radioButtons + + + + 692 + + + + title + + + + 709 + + + + radioChanged: + + + + 731 + + + + mouseBtnSelect + + + + 746 + + + + mbtnChanged: + + + + 747 + + + + scrollDirSelect + + + + 751 + + + + sdirChanged: + + + + 752 + + + + mouseDirSelect + + + + 756 + + + + mdirChanged: + + + + 757 + + + + keyInput + + + + 781 + + + + mappingsController + + + + 821 + + + + mappingPopup + + + + 823 + + + + inputController + + + + 828 + + + + mouseSpeedChanged: + + + + 885 + + + + mouseSpeedSlider + + + + 886 + + + + scrollSpeedChanged: + + + + 890 + + + + scrollSpeedSlider + + + + 891 + + + + keyDelegate + + + + 818 + + + + performClick: + + + + 871 + + + + view + + + + 854 + + + + contentViewController + + + + 852 + + + + delegate + + + + 853 + + + + performClick: + + + + 880 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + + + + + + + Main Menu + + + 19 + + + + + + + + 56 + + + + + + + + 83 + + + + + + + + 81 + + + + + + + + + + + + + 57 + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + Quit Enjoy + + + 144 + + + + + 236 + + + + + 131 + + + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 450 + + + + + + + + + 451 + + + + + + + + + + + Mapping List Popover Content + + + 453 + + + + + + + + 456 + + + + + + + + + + 457 + + + + + 458 + + + + + 459 + + + + + + Mapping List + + + 461 + + + + + + + + 464 + + + + + 482 + + + + + 487 + + + + + + + + + + 511 + + + + + + Remove Mapping + + + 512 + + + + + 514 + + + + + 479 + + + + + 606 + + + + + 652 + + + + + + + + + 653 + + + + + + Input Devices Pane + + + 634 + + + + + + + + + + 637 + + + + + + Input Device List + + + 636 + + + + + 635 + + + + + 639 + + + + + + Device/Input Name + + + 642 + + + + + 654 + + + + + + + + + + + + + + + Output Editor Pane + + + 686 + + + + + 656 + + + + + + + + + + + + Output Types + + + 657 + + + Disabled + + + 658 + + + Key Press + + + 699 + + + Switch Mapping + + + 700 + + + + + + Mapping Choice List + + + 701 + + + + + + + + 702 + + + + + + 706 + + + + + + Input Name + + + 707 + + + + + 708 + + + + + 723 + + + + + 733 + + + Mouse Movement + + + 734 + + + Mouse Button + + + 735 + + + Mouse Scroll + + + 659 + + + + + 744 + + + + + + Mouse Button Selector + + + 745 + + + + + 749 + + + + + + Mouse Scroll Selector + + + 750 + + + + + 754 + + + + + + Mouse Motion Selector + + + 755 + + + + + 778 + + + + + 812 + + + + + 813 + + + + + 814 + + + + + 837 + + + + + + Mapping Selector + + + 835 + + + + + + + + 836 + + + + + 849 + + + + + 850 + + + Popover View Controller + + + 851 + + + Mapping List Popover + + + 507 + + + + + + Add Mapping + + + 508 + + + + + 862 + + + + + + Gradient Space + + + 863 + + + + + 810 + + + + + 866 + + + + + + + + 867 + + + + + + + + 868 + + + + + 874 + + + + + + + + 872 + + + + + + + + 873 + + + + + 883 + + + + + + + + 884 + + + + + 887 + + + + + + + + 888 + + + + + 893 + + + + + + Move Mapping Up + + + 894 + + + + + 896 + + + + + + Move Mapping Down + + + 897 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin + {{114, 276}, {770, 487}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Create a new mapping + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Remove the selected mapping + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Change the active mapping + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Enable mapped actions + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Maximum mouse speed + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Mouse scroll speed + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Move the selected mapping up the list + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + ToolTip + + ToolTip + + Move the selected mapping down the list + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 902 + + + + + EnjoyableApplicationDelegate + NSObject + + NSMenu + NJDeviceController + NJMappingsController + NSWindow + + + + dockMenuBase + NSMenu + + + inputController + NJDeviceController + + + mappingsController + NJMappingsController + + + window + NSWindow + + + + IBProjectSource + ./Classes/EnjoyableApplicationDelegate.h + + + + NJDeviceController + NSObject + + translatingEventsChanged: + id + + + translatingEventsChanged: + + translatingEventsChanged: + id + + + + NJMappingsController + NSOutlineView + NJOutputController + NSButton + NSMenuItem + + + + mappingsController + NJMappingsController + + + outlineView + NSOutlineView + + + outputController + NJOutputController + + + translatingEventsButton + NSButton + + + translatingEventsMenu + NSMenuItem + + + + IBProjectSource + ./Classes/NJDeviceController.h + + + + NJKeyInputField + NSTextField + + keyDelegate + id + + + keyDelegate + + keyDelegate + id + + + + IBProjectSource + ./Classes/NJKeyInputField.h + + + + NJMappingsController + NSObject + + id + id + id + id + id + id + id + + + + addPressed: + id + + + exportPressed: + id + + + importPressed: + id + + + mappingPressed: + id + + + moveDownPressed: + id + + + moveUpPressed: + id + + + removePressed: + id + + + + NSButton + NSButton + NJOutputController + NSPopover + NSButton + NSButton + NSTableView + + + + moveDown + NSButton + + + moveUp + NSButton + + + outputController + NJOutputController + + + popover + NSPopover + + + popoverActivate + NSButton + + + removeButton + NSButton + + + tableView + NSTableView + + + + IBProjectSource + ./Classes/NJMappingsController.h + + + + NJOutputController + NSObject + + id + id + id + id + id + id + + + + mbtnChanged: + id + + + mdirChanged: + id + + + mouseSpeedChanged: + id + + + radioChanged: + id + + + scrollSpeedChanged: + id + + + sdirChanged: + id + + + + NJDeviceController + NJKeyInputField + NSPopUpButton + NJMappingsController + NSSegmentedControl + NSSegmentedControl + NSSlider + NSMatrix + NSSegmentedControl + NSSlider + NSTextField + + + + inputController + NJDeviceController + + + keyInput + NJKeyInputField + + + mappingPopup + NSPopUpButton + + + mappingsController + NJMappingsController + + + mouseBtnSelect + NSSegmentedControl + + + mouseDirSelect + NSSegmentedControl + + + mouseSpeedSlider + NSSlider + + + radioButtons + NSMatrix + + + scrollDirSelect + NSSegmentedControl + + + scrollSpeedSlider + NSSlider + + + title + NSTextField + + + + IBProjectSource + ./Classes/NJOutputController.h + + + + + 0 + IBCocoaFramework + YES + 3 + + {8, 8} + {11, 10} + {11, 11} + {10, 3} + {16, 15} + {8, 8} + {9, 9} + + + diff --git a/Resources/Help/Contents/Info.plist b/Resources/Help/Contents/Info.plist new file mode 100644 index 0000000..e62b80f --- /dev/null +++ b/Resources/Help/Contents/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleIdentifier + com.yukkurigames.Enjoyable.help + CFBundleDevelopmentRegion + en_US + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Enjoyable + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1 + CFBundleSignature + hbwr + CFBundleVersion + 1 + HPDBookAccessPath + index.html + HPDBookIconPath + gfx/Icon.png + HPDBookIndexPath + Enjoyable.helpindex + HPDBookKBProduct + enjoyable1 + HPDBookTitle + Enjoyable Help + HPDBookType + 3 + + diff --git a/Resources/Help/Contents/Resources/English.lproj/Makefile b/Resources/Help/Contents/Resources/English.lproj/Makefile new file mode 100644 index 0000000..51a8466 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/Makefile @@ -0,0 +1,8 @@ +#!/usr/bin/make -f + +HTML := *.html pgs/*.html + +all: Enjoyable.helpindex + +Enjoyable.helpindex: $(HTML) + hiutil -C -f $@ -g -a -s en . diff --git a/Resources/Help/Contents/Resources/English.lproj/gfx/Icon.png b/Resources/Help/Contents/Resources/English.lproj/gfx/Icon.png new file mode 100644 index 0000000..36c4254 Binary files /dev/null and b/Resources/Help/Contents/Resources/English.lproj/gfx/Icon.png differ diff --git a/Resources/Help/Contents/Resources/English.lproj/index.html b/Resources/Help/Contents/Resources/English.lproj/index.html new file mode 100644 index 0000000..1a52296 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/index.html @@ -0,0 +1,81 @@ + + + + + Enjoyable Help + + + + + + + + + + + + +
+
+ Enjoyable Icon +
+
+

Enjoyable Help

+
+
+ +

+ Enjoyable helps you use a joystick or gamepad to control + applications which normally require a keyboard and mouse. +

+ +
+

Quick Start

+
    +
  • Connect a joystick or gamepad.
  • +
  • Press a button on it, then the keyboard key you want to use.
  • +
  • Press the ▶ button in the upper-right.
  • +
  • Start up your game and use your gamepad!
  • + +
+ +
+

+ + Keyboard Events +
+ Map buttons to keys on a keyboard. +

+

+ + Mouse Events +
+ Use axes and buttons to simulate a mouse. +

+

+ + Application Mappings +
+ Create and share mappings for different applications. +

+

+ + Troubleshooting +
+ Assistance for common problems. +

+
+ +

+ + license + - + + website +

+ + + diff --git a/Resources/Help/Contents/Resources/English.lproj/pgs/boring.html b/Resources/Help/Contents/Resources/English.lproj/pgs/boring.html new file mode 100644 index 0000000..45c6719 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/pgs/boring.html @@ -0,0 +1,73 @@ + + + + + License & Copyright + + + + + + + + + +
+
+ Icon +
+

License & Copyright

+
+ +

Copyright

+

+ 2013 Joe Wreschnig
+ 2012 Yifeng Huang
+ 2009 Sam McCall & the University of Otago +

+ +

License

+

+ Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: +

+

+ The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. +

+

+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +

+

+ The joystick icon is from the Tango icon set and is public + domain. +

+ + diff --git a/Resources/Help/Contents/Resources/English.lproj/pgs/keyboard.html b/Resources/Help/Contents/Resources/English.lproj/pgs/keyboard.html new file mode 100644 index 0000000..dca6136 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/pgs/keyboard.html @@ -0,0 +1,68 @@ + + + + + Keyboard Events + + + + + + + + + +
+
+ Enjoyable Icon +
+

Keyboard Events

+
+ +

+ Enjoyable supports mapping joystick buttons, hat switches, and + axis thresholds to simulate keyboard keys. First disable + mapping by deactivating the ▶ button in the top left. Then press + the button on the joystick you want to map. This will select it + on the left-hand side of the screen. +

+ +

+ If the button wasn't mapped or was mapped to a key press + already, the key input field activates and you can simply press + the key you want to use. Otherwise, click on the Press a + key label or input field, then press the key. +

+ +

+ To change a key without disabling mapping you can choose the + input's entry in the sidebar directly. +

+ +

Clearing the Selection

+

+ To clear a mapped key either select the Do nothing + option or press ⌥⌫ when the key input field is selected. +

+ +

Cancelling the Selection

+

+ If you select the key input field by mistake you can press ⌥⎋ + to cancel the selection without changing the current setting. +

+ + diff --git a/Resources/Help/Contents/Resources/English.lproj/pgs/mappings.html b/Resources/Help/Contents/Resources/English.lproj/pgs/mappings.html new file mode 100644 index 0000000..3b181ff --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/pgs/mappings.html @@ -0,0 +1,98 @@ + + + + + Application Mappings + + + + + + + + + +
+
+ Icon +
+

Application Mappings

+
+ +

+ You can make many different mappings and switch between them + easily. To open the list of mappings click the button at the + top-left or press ⌘L. +

+ +

+ Click on a mapping to switch to it. Create a new mapping with + the + button or pressing ⌘N. Delete the current mapping with + the - button or ⌘⌫. Rename a mapping by double-clicking on it or + pressing Return while it's selected. +

+ +

+ You can also switch mappings with the Mappings menu, with + Enjoyable's dock menu, or by pressing ⌘1 through ⌘9. +

+ +

+ Switching mappings can also be mapped to an input. Select the + input you wish to use and then choose a mapping from + the Switch to mapping option. For example, you could have + one mapping for a game's menu and another for its main screen + and switch between them in-game without returning to Enjoyable. +

+ +

Automatic Switching

+

+ If you name a mapping after an application it will be + automatically chosen when you switch to that application. The + name of an application is usually shown on the dock when you + hover your mouse over it. If you don't know the name of the + application you want to create a mapping for, create a mapping + with the name @Application (note the @ at + the start). The mapping will automatically be renamed for the + next application you switch to while it's enabled. +

+ +

Import and Export

+

+ Mappings can be exported and shared with other people. To export + your current mapping choose Mappings > Export… and pick a + location to save the file. This file can be shared with anyone; + it doesn't contain any personal information other than the + product IDs of the input devices you used and what you mapped + them to. +

+

+ To import a mapping choose Mappings > Import… and select + the file you want to import. Mapping files end + with .enjoyable (the default), .json, + or .txt. If the imported mapping conflicts with one + you already made, you can choose to merge the two mappings or + create a new one with the same name. +

+

+ You can also import mappings by opening them in Finder or + dragging the files onto the mapping list. Similarly, you can + export mappings by dragging them from the mapping list to + Finder. +

+ + diff --git a/Resources/Help/Contents/Resources/English.lproj/pgs/mouse.html b/Resources/Help/Contents/Resources/English.lproj/pgs/mouse.html new file mode 100644 index 0000000..0293fd0 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/pgs/mouse.html @@ -0,0 +1,102 @@ + + + + + Mouse Events + + + + + + + + + +
+
+ Icon +
+

Mouse Events

+
+ +

+ You can use Enjoyable to map input to mouse buttons, moving, and + scrolling. +

+ +

Movement

+

+ Select the direction you'd like the input to move the + mouse. Adjust the movement speed using the slider underneath. If + you are mapping an analog input then this is the maximum speed; + for a button it's a constant speed. +

+

+ The speed is set independently for each input. You can have + faster horizontal movement than vertical movement, or map one + set of inputs to a fast speed and another set to a slow + speed. +

+ +

Buttons

+

+ Select the mouse button you'd like the input to simulate. +

+ +

Scrolling

+

+ Simulated scrolling can be continuous like the scrolling + gestures on a trackpad, or discrete like a mouse wheel that + clicks as you spin it. +

+

+ To use continuous scrolling choose ↑ or ↓. Use the + slider underneath them to adjust the scrolling speed. If you are + mapping an analog input then this is the maximum speed; for a + button it's a constant speed. +

+ To use discrete scrolling choose ⤒ or ⤓. The input + will trigger scrolling up or down by exactly one line and stop, + regardless of how long you hold the button down or how far + you move an analog input. +

+

+ The arrows indicate the direction you would spin a mouse wheel + or move your fingers. Depending on settings this may mean you + need to choose a down arrow to scroll up and vice versa. You can + also change this globally in  > System Preferences… > + Mouse and  > System Preferences… > Trackpad. +

+ +

Known Issues

+

+ Mouse events are more fragile than keyboard ones. While Enjoyble + will work fine for most games, regular OS X (Cocoa) applications + require specially formatted mouse events. Features such as + click-and-drag or double-clicking will not work correctly, so + many applications will behave incorrectly if driven by an + Enjoyable simulated mouse. +

+

+ If you find a non-Cocoa application that has problems with + Enjoyable's mouse + support please + file a ticket in the issue tracker. +

+ + + diff --git a/Resources/Help/Contents/Resources/English.lproj/pgs/problems.html b/Resources/Help/Contents/Resources/English.lproj/pgs/problems.html new file mode 100644 index 0000000..8f3aa68 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/pgs/problems.html @@ -0,0 +1,70 @@ + + + + + Troubleshooting + + + + + + + + + +
+
+ Icon +
+

Troubleshooting

+
+ +

+ + When I start Enjoyable, it says "Input devices are unavailable" +

+

+ This happens if Enjoyable is refused access to your input + devices by Mac OS X. This usually happens if another application + has requested exclusive access to them. Try quitting any other + applications using your input devices. If that doesn't work, try + disconnecting and reconnecting the device, then restarting + Enjoyable. If it still doesn't work you may need to reboot. +

+ +

+ + Enjoyable never switches to my application mapping +

+

+ Make sure it matches the name of the application exactly. If you + still have trouble, name the mapping @Application and + switch back to have Enjoyable try to deduce the correct name + automatically. +

+ +

+ Mouse clicks and drags don't work + +

+

+ This is a known issue with Cocoa applications, as they require + more specially-crafted mouse events. We hope to fix it in a + future version. +

+ + diff --git a/Resources/Help/Contents/Resources/English.lproj/sty/default.css b/Resources/Help/Contents/Resources/English.lproj/sty/default.css new file mode 100644 index 0000000..58b7747 --- /dev/null +++ b/Resources/Help/Contents/Resources/English.lproj/sty/default.css @@ -0,0 +1 @@ +body { font-size: 8pt; font-family: "Lucida Grande", Arial, sans-serif; line-height: 12pt; text-decoration: none; margin-right: 1em; margin-left: 1em; } #navbox { background-color: #f2f2f2; position: fixed; top: 0; left: 0; width: 100%; height: 1.5em; float: left; border-bottom: 1px solid #bfbfbf } #navleftbox { position: absolute; top: 1px; left: 15px } #navrightbox { background-color: #f2f2f2; padding-right: 25px; float: right; padding-bottom: 1px; border-left: 1px solid #bfbfbf } #navbox a { font-size: 8pt; color: #666; font-weight: normal; margin: -9px 0 -6px; } #headerbox { margin-top: 36px; padding-right: 6px; margin-bottom: 2em; } #iconbox { float: left; } h1 { margin-left: 40px; width: 88%; font-size: 15pt; line-height: 15pt; font-weight: bold; padding-top: 6px; margin-bottom: 0; } h2 { font-size: 11pt; line-height: 12pt; font-weight: bold; color: black; margin-top: 0; margin-bottom: 11px; } h3 { font-size: 8pt; font-weight: bold; letter-spacing: 0.1em; line-height: 8pt; color: #666; margin-top: 1em; margin-bottom: 0px; padding-bottom: 0.5em; } p { margin-left: 0px; margin-top: 0px; margin-bottom: 0.5em; } ul { margin-left: 2em; margin-top: 6px; margin-bottom: 0px; padding-left: 0px; } li { margin-left: 0px; } a { color: #778fbd; font-size: 9pt; font-weight: bold; text-decoration: none; } a:hover { text-decoration: underline; } .weblink { color: #666; font-weight: normal; } \ No newline at end of file diff --git a/Resources/Icon.icns b/Resources/Icon.icns new file mode 100644 index 0000000..c7e3f36 Binary files /dev/null and b/Resources/Icon.icns differ diff --git a/Resources/com.yukkurigames.Enjoyable.mapping.icns b/Resources/com.yukkurigames.Enjoyable.mapping.icns new file mode 100644 index 0000000..027b5e2 Binary files /dev/null and b/Resources/com.yukkurigames.Enjoyable.mapping.icns differ diff --git a/com.yukkurigames.Enjoyable.mapping.icns b/com.yukkurigames.Enjoyable.mapping.icns deleted file mode 100644 index 027b5e2..0000000 Binary files a/com.yukkurigames.Enjoyable.mapping.icns and /dev/null differ diff --git a/icon.icns b/icon.icns deleted file mode 100644 index c7e3f36..0000000 Binary files a/icon.icns and /dev/null differ diff --git a/main.m b/main.m deleted file mode 100644 index 9fbf37b..0000000 --- a/main.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// main.m -// Enjoy -// -// Created by Sam McCall on 4/05/09. -// - -#import - -int main(int argc, char *argv[]) -{ - return NSApplicationMain(argc, (const char **) argv); -}