// // SGKeyCombo.m // SGHotKeyCenter // // Created by Justin Williams on 7/26/09. // Copyright 2009 Second Gear. All rights reserved. // #import #import "SGKeyCombo.h" #import "SGKeyCodeTranslator.h" NSString * const kKeyCodeDictionaryKey = @"keyCode"; NSString * const kModifiersDictionaryKey = @"modifiers"; @implementation SGKeyCombo @synthesize keyCode; @synthesize modifiers; + (id)clearKeyCombo { return [self keyComboWithKeyCode:-1 modifiers:-1]; } + (id)keyComboWithKeyCode:(NSInteger)theKeyCode modifiers:(NSInteger)theModifiers { return [[[self alloc] initWithKeyCode:theKeyCode modifiers:theModifiers] autorelease]; } - (id)initWithKeyCode:(NSInteger)theKeyCode modifiers:(NSInteger)theModifiers { if (self = [super init]) { keyCode = theKeyCode; modifiers = theModifiers; } return self; } - (id)initWithPlistRepresentation:(id)thePlist { NSInteger theKeyCode; NSInteger theModifiers; if (!thePlist || ![thePlist count]) { theKeyCode = -1; theModifiers = -1; } else { theKeyCode = [[thePlist objectForKey:kKeyCodeDictionaryKey] integerValue]; if (theKeyCode <= 0) theKeyCode = -1; theModifiers = [[thePlist objectForKey:kModifiersDictionaryKey] integerValue]; if (theModifiers <= 0) theModifiers = -1; } return [self initWithKeyCode:theKeyCode modifiers:theModifiers]; } - (id)plistRepresentation { return [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger:self.keyCode], kKeyCodeDictionaryKey, [NSNumber numberWithInteger:self.modifiers], kModifiersDictionaryKey, nil]; } - (BOOL)isEqual:(SGKeyCombo *)theCombo { return self.keyCode == theCombo.keyCode && self.modifiers == theCombo.modifiers; } - (BOOL)isClearCombo { return keyCode == -1 && modifiers == -1; } - (BOOL)isValidHotKeyCombo { return keyCode >= 0 && modifiers > 0; } + (NSString*)_stringForModifiers:(NSInteger)theModifiers { static unichar modToChar[4][2] = { { cmdKey, kCommandUnicode }, { optionKey, kOptionUnicode }, { controlKey, kControlUnicode }, { shiftKey, kShiftUnicode } }; NSString *string = [NSString string]; long i; for( i = 0; i < 4; i++ ) { if (theModifiers & modToChar[i][0] ) string = [string stringByAppendingString: [NSString stringWithCharacters: &modToChar[i][1] length: 1]]; } return string; } + (NSDictionary *)_keyCodesDictionary { static NSDictionary *keyCodes = nil; if(keyCodes == nil) { NSString *path; NSString *contents; NSError *error; path = [[NSBundle bundleForClass:self] pathForResource:@"SGKeyCodes" ofType:@"plist"]; contents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; NSAssert(contents != nil, @"Contents of SGKeyCodes is nil"); keyCodes = [[contents propertyList] retain]; } return keyCodes; } + (NSString*)_stringForKeyCode: (short)theKeyCode legacyKeyCodeMap: (NSDictionary*)theDictionary { id key; NSString *string; key = [NSString stringWithFormat:@"%d", theKeyCode]; string = [theDictionary objectForKey:key]; if( !string ) string = [NSString stringWithFormat:@"%X", theKeyCode]; return string; } + (NSString*)_stringForKeyCode:(short)theKeyCode newKeyCodeMap:(NSDictionary*)theDictionary { NSString *result; NSString *keyCodeString; NSDictionary *unmappedKeys; NSArray *padKeys; keyCodeString = [NSString stringWithFormat: @"%d", theKeyCode]; //Handled if its not handled by translator unmappedKeys = [theDictionary objectForKey:@"unmappedKeys"]; result = [unmappedKeys objectForKey:keyCodeString]; if( result ) return result; //Translate it result = [[[SGKeyCodeTranslator currentTranslator] translateKeyCode:theKeyCode] uppercaseString]; //Handle if its a key-pad key padKeys = [theDictionary objectForKey:@"padKeys"]; if([padKeys indexOfObject:keyCodeString] != NSNotFound) { result = [NSString stringWithFormat:@"%@ %@", [theDictionary objectForKey:@"padKeyString"], result]; } return result; } + (NSString *)_stringForKeyCode:(short)theKeyCode { NSDictionary *dict; dict = [self _keyCodesDictionary]; if([[dict objectForKey:@"version"] integerValue] <= 0) return [self _stringForKeyCode:theKeyCode legacyKeyCodeMap:dict]; return [self _stringForKeyCode:theKeyCode newKeyCodeMap:dict]; } - (NSString*)keyCodeString { // special case: the modifiers for the "clear" key are 0x0 if ( [self isClearCombo] ) return @""; return [[self class] _stringForKeyCode:self.keyCode]; } - (NSUInteger)modifierMask { // special case: the modifiers for the "clear" key are 0x0 if ([self isClearCombo]) return 0; static NSUInteger modToChar[4][2] = { { cmdKey, NSCommandKeyMask }, { optionKey, NSAlternateKeyMask }, { controlKey, NSControlKeyMask }, { shiftKey, NSShiftKeyMask } }; NSUInteger i, ret = 0; for (i = 0; i < 4; i++) { if (self.modifiers & modToChar[i][0]) { ret |= modToChar[i][1]; } } return ret; } - (NSString *)description { NSString *desc; if ([self isValidHotKeyCombo]) { desc = [NSString stringWithFormat: @"%@%@", [[self class] _stringForModifiers:self.modifiers], [[self class] _stringForKeyCode:self.keyCode]]; } else { desc = NSLocalizedString(@"(None)", @"Hot Keys: Key Combo text for 'empty' combo" ); } return desc; } @end