diff options
Diffstat (limited to 'Framework/MASShortcut.m')
| -rw-r--r-- | Framework/MASShortcut.m | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/Framework/MASShortcut.m b/Framework/MASShortcut.m new file mode 100644 index 0000000..e6fa63d --- /dev/null +++ b/Framework/MASShortcut.m @@ -0,0 +1,241 @@ +#import "MASShortcut.h" + +static NSString *const MASShortcutKeyCode = @"KeyCode"; +static NSString *const MASShortcutModifierFlags = @"ModifierFlags"; + +@implementation MASShortcut + +#pragma mark Initialization + +- (instancetype)initWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags +{ + self = [super init]; + if (self) { + _keyCode = code; + _modifierFlags = MASPickCocoaModifiers(flags); + } + return self; +} + ++ (instancetype)shortcutWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags +{ + return [[self alloc] initWithKeyCode:code modifierFlags:flags]; +} + ++ (instancetype)shortcutWithEvent:(NSEvent *)event +{ + return [[self alloc] initWithKeyCode:event.keyCode modifierFlags:event.modifierFlags]; +} + +#pragma mark Shortcut Accessors + +- (UInt32)carbonKeyCode +{ + return (self.keyCode == NSNotFound ? 0 : (UInt32)self.keyCode); +} + +- (UInt32)carbonFlags +{ + return MASCarbonModifiersFromCocoaModifiers(self.modifierFlags); +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@%@", self.modifierFlagsString, self.keyCodeString]; +} + +- (NSString *)keyCodeStringForKeyEquivalent +{ + NSString *keyCodeString = self.keyCodeString; + if (keyCodeString.length > 1) { + switch (self.keyCode) { + case kVK_F1: return NSStringFromMASKeyCode(0xF704); + case kVK_F2: return NSStringFromMASKeyCode(0xF705); + case kVK_F3: return NSStringFromMASKeyCode(0xF706); + case kVK_F4: return NSStringFromMASKeyCode(0xF707); + case kVK_F5: return NSStringFromMASKeyCode(0xF708); + case kVK_F6: return NSStringFromMASKeyCode(0xF709); + case kVK_F7: return NSStringFromMASKeyCode(0xF70a); + case kVK_F8: return NSStringFromMASKeyCode(0xF70b); + case kVK_F9: return NSStringFromMASKeyCode(0xF70c); + case kVK_F10: return NSStringFromMASKeyCode(0xF70d); + case kVK_F11: return NSStringFromMASKeyCode(0xF70e); + case kVK_F12: return NSStringFromMASKeyCode(0xF70f); + // From this point down I am guessing F13 etc come sequentially, I don't have a keyboard to test. + case kVK_F13: return NSStringFromMASKeyCode(0xF710); + case kVK_F14: return NSStringFromMASKeyCode(0xF711); + case kVK_F15: return NSStringFromMASKeyCode(0xF712); + case kVK_F16: return NSStringFromMASKeyCode(0xF713); + case kVK_F17: return NSStringFromMASKeyCode(0xF714); + case kVK_F18: return NSStringFromMASKeyCode(0xF715); + case kVK_F19: return NSStringFromMASKeyCode(0xF716); + case kVK_Space: return NSStringFromMASKeyCode(0x20); + default: return @""; + } + } + return keyCodeString.lowercaseString; +} + +- (NSString *)keyCodeString +{ + // Some key codes don't have an equivalent + switch (self.keyCode) { + case NSNotFound: return @""; + case kVK_F1: return @"F1"; + case kVK_F2: return @"F2"; + case kVK_F3: return @"F3"; + case kVK_F4: return @"F4"; + case kVK_F5: return @"F5"; + case kVK_F6: return @"F6"; + case kVK_F7: return @"F7"; + case kVK_F8: return @"F8"; + case kVK_F9: return @"F9"; + case kVK_F10: return @"F10"; + case kVK_F11: return @"F11"; + case kVK_F12: return @"F12"; + case kVK_F13: return @"F13"; + case kVK_F14: return @"F14"; + case kVK_F15: return @"F15"; + case kVK_F16: return @"F16"; + case kVK_F17: return @"F17"; + case kVK_F18: return @"F18"; + case kVK_F19: return @"F19"; + case kVK_Space: return NSLocalizedString(@"Space", @"Shortcut glyph name for SPACE key"); + case kVK_Escape: return NSStringFromMASKeyCode(kMASShortcutGlyphEscape); + case kVK_Delete: return NSStringFromMASKeyCode(kMASShortcutGlyphDeleteLeft); + case kVK_ForwardDelete: return NSStringFromMASKeyCode(kMASShortcutGlyphDeleteRight); + case kVK_LeftArrow: return NSStringFromMASKeyCode(kMASShortcutGlyphLeftArrow); + case kVK_RightArrow: return NSStringFromMASKeyCode(kMASShortcutGlyphRightArrow); + case kVK_UpArrow: return NSStringFromMASKeyCode(kMASShortcutGlyphUpArrow); + case kVK_DownArrow: return NSStringFromMASKeyCode(kMASShortcutGlyphDownArrow); + case kVK_Help: return NSStringFromMASKeyCode(kMASShortcutGlyphHelp); + case kVK_PageUp: return NSStringFromMASKeyCode(kMASShortcutGlyphPageUp); + case kVK_PageDown: return NSStringFromMASKeyCode(kMASShortcutGlyphPageDown); + case kVK_Tab: return NSStringFromMASKeyCode(kMASShortcutGlyphTabRight); + case kVK_Return: return NSStringFromMASKeyCode(kMASShortcutGlyphReturnR2L); + + // Keypad + case kVK_ANSI_Keypad0: return @"0"; + case kVK_ANSI_Keypad1: return @"1"; + case kVK_ANSI_Keypad2: return @"2"; + case kVK_ANSI_Keypad3: return @"3"; + case kVK_ANSI_Keypad4: return @"4"; + case kVK_ANSI_Keypad5: return @"5"; + case kVK_ANSI_Keypad6: return @"6"; + case kVK_ANSI_Keypad7: return @"7"; + case kVK_ANSI_Keypad8: return @"8"; + case kVK_ANSI_Keypad9: return @"9"; + case kVK_ANSI_KeypadDecimal: return @"."; + case kVK_ANSI_KeypadMultiply: return @"*"; + case kVK_ANSI_KeypadPlus: return @"+"; + case kVK_ANSI_KeypadClear: return NSStringFromMASKeyCode(kMASShortcutGlyphPadClear); + case kVK_ANSI_KeypadDivide: return @"/"; + case kVK_ANSI_KeypadEnter: return NSStringFromMASKeyCode(kMASShortcutGlyphReturn); + case kVK_ANSI_KeypadMinus: return @"–"; + case kVK_ANSI_KeypadEquals: return @"="; + + // Hardcode + case 119: return NSStringFromMASKeyCode(kMASShortcutGlyphSoutheastArrow); + case 115: return NSStringFromMASKeyCode(kMASShortcutGlyphNorthwestArrow); + } + + // Everything else should be printable so look it up in the current keyboard + OSStatus error = noErr; + NSString *keystroke = nil; + TISInputSourceRef inputSource = TISCopyCurrentKeyboardLayoutInputSource(); + if (inputSource) { + CFDataRef layoutDataRef = TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData); + if (layoutDataRef) { + UCKeyboardLayout *layoutData = (UCKeyboardLayout *)CFDataGetBytePtr(layoutDataRef); + UniCharCount length = 0; + UniChar chars[256] = { 0 }; + UInt32 deadKeyState = 0; + error = UCKeyTranslate(layoutData, (UInt16)self.keyCode, kUCKeyActionDisplay, 0, // No modifiers + LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask, &deadKeyState, + sizeof(chars) / sizeof(UniChar), &length, chars); + keystroke = ((error == noErr) && length ? [NSString stringWithCharacters:chars length:length] : @""); + } + CFRelease(inputSource); + } + + // Validate keystroke + if (keystroke.length) { + static NSMutableCharacterSet *validChars = nil; + if (validChars == nil) { + validChars = [[NSMutableCharacterSet alloc] init]; + [validChars formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]]; + [validChars formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; + [validChars formUnionWithCharacterSet:[NSCharacterSet symbolCharacterSet]]; + } + for (NSUInteger i = 0, length = keystroke.length; i < length; i++) { + if (![validChars characterIsMember:[keystroke characterAtIndex:i]]) { + keystroke = @""; + break; + } + } + } + + // Finally, we've got a shortcut! + return keystroke.uppercaseString; +} + +- (NSString *)modifierFlagsString +{ + unichar chars[4]; + NSUInteger count = 0; + // These are in the same order as the menu manager shows them + if (self.modifierFlags & NSControlKeyMask) chars[count++] = kControlUnicode; + if (self.modifierFlags & NSAlternateKeyMask) chars[count++] = kOptionUnicode; + if (self.modifierFlags & NSShiftKeyMask) chars[count++] = kShiftUnicode; + if (self.modifierFlags & NSCommandKeyMask) chars[count++] = kCommandUnicode; + return (count ? [NSString stringWithCharacters:chars length:count] : @""); +} + +#pragma mark NSObject + +- (BOOL) isEqual: (MASShortcut*) object +{ + return [object isKindOfClass:[self class]] + && (object.keyCode == self.keyCode) + && (object.modifierFlags == self.modifierFlags); +} + +- (NSUInteger) hash +{ + return self.keyCode + self.modifierFlags; +} + +#pragma mark NSCoding + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInteger:(self.keyCode != NSNotFound ? (NSInteger)self.keyCode : - 1) forKey:MASShortcutKeyCode]; + [coder encodeInteger:(NSInteger)self.modifierFlags forKey:MASShortcutModifierFlags]; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + if (self) { + NSInteger code = [decoder decodeIntegerForKey:MASShortcutKeyCode]; + _keyCode = (code < 0 ? NSNotFound : (NSUInteger)code); + _modifierFlags = [decoder decodeIntegerForKey:MASShortcutModifierFlags]; + } + return self; +} + +#pragma mark NSSecureCoding + ++ (BOOL)supportsSecureCoding +{ + return YES; +} + +#pragma mark NSCopying + +- (instancetype) copyWithZone:(NSZone *)zone +{ + return [[self class] shortcutWithKeyCode:_keyCode modifierFlags:_modifierFlags]; +} + +@end |
