|
|
|
|
//
|
|
|
|
|
// SRKeyCodeTransformer.h
|
|
|
|
|
// ShortcutRecorder
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2006-2012 Contributors. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
// License: BSD
|
|
|
|
|
//
|
|
|
|
|
// Contributors:
|
|
|
|
|
// David Dauer
|
|
|
|
|
// Jesper
|
|
|
|
|
// Jamie Kirkpatrick
|
|
|
|
|
// Ilya Kulakov
|
|
|
|
|
// Silvio Rizzi
|
|
|
|
|
|
|
|
|
|
#import "SRKeyCodeTransformer.h"
|
|
|
|
|
#import "SRCommon.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FOUNDATION_STATIC_INLINE NSString* _SRUnicharToString(unichar aChar)
|
|
|
|
|
{
|
|
|
|
|
return [NSString stringWithFormat: @"%C", aChar];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation SRKeyCodeTransformer
|
|
|
|
|
|
|
|
|
|
- (instancetype)initWithASCIICapableKeyboardInputSource:(BOOL)aUsesASCII plainStrings:(BOOL)aUsesPlainStrings
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
_usesASCIICapableKeyboardInputSource = aUsesASCII;
|
|
|
|
|
_usesPlainStrings = aUsesPlainStrings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)init
|
|
|
|
|
{
|
|
|
|
|
return [self initWithASCIICapableKeyboardInputSource:NO plainStrings:NO];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark Methods
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedTransformer
|
|
|
|
|
{
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static SRKeyCodeTransformer *Transformer = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Transformer = [[self alloc] initWithASCIICapableKeyboardInputSource:NO
|
|
|
|
|
plainStrings:NO];
|
|
|
|
|
});
|
|
|
|
|
return Transformer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedASCIITransformer
|
|
|
|
|
{
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static SRKeyCodeTransformer *Transformer = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Transformer = [[self alloc] initWithASCIICapableKeyboardInputSource:YES
|
|
|
|
|
plainStrings:NO];
|
|
|
|
|
});
|
|
|
|
|
return Transformer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (instancetype)sharedPlainTransformer
|
|
|
|
|
{
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static SRKeyCodeTransformer *Transformer = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Transformer = [[self alloc] initWithASCIICapableKeyboardInputSource:NO
|
|
|
|
|
plainStrings:YES];
|
|
|
|
|
});
|
|
|
|
|
return Transformer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (SRKeyCodeTransformer *)sharedPlainASCIITransformer
|
|
|
|
|
{
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static SRKeyCodeTransformer *Transformer = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Transformer = [[self alloc] initWithASCIICapableKeyboardInputSource:YES
|
|
|
|
|
plainStrings:YES];
|
|
|
|
|
});
|
|
|
|
|
return Transformer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (NSDictionary *)specialKeyCodesToUnicodeCharactersMapping
|
|
|
|
|
{
|
|
|
|
|
// Most of these keys are system constans.
|
|
|
|
|
// Values for rest of the keys were given by setting key equivalents in IB.
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static NSDictionary *Mapping = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Mapping = @{
|
|
|
|
|
@(kVK_F1): _SRUnicharToString(NSF1FunctionKey),
|
|
|
|
|
@(kVK_F2): _SRUnicharToString(NSF2FunctionKey),
|
|
|
|
|
@(kVK_F3): _SRUnicharToString(NSF3FunctionKey),
|
|
|
|
|
@(kVK_F4): _SRUnicharToString(NSF4FunctionKey),
|
|
|
|
|
@(kVK_F5): _SRUnicharToString(NSF5FunctionKey),
|
|
|
|
|
@(kVK_F6): _SRUnicharToString(NSF6FunctionKey),
|
|
|
|
|
@(kVK_F7): _SRUnicharToString(NSF7FunctionKey),
|
|
|
|
|
@(kVK_F8): _SRUnicharToString(NSF8FunctionKey),
|
|
|
|
|
@(kVK_F9): _SRUnicharToString(NSF9FunctionKey),
|
|
|
|
|
@(kVK_F10): _SRUnicharToString(NSF10FunctionKey),
|
|
|
|
|
@(kVK_F11): _SRUnicharToString(NSF11FunctionKey),
|
|
|
|
|
@(kVK_F12): _SRUnicharToString(NSF12FunctionKey),
|
|
|
|
|
@(kVK_F13): _SRUnicharToString(NSF13FunctionKey),
|
|
|
|
|
@(kVK_F14): _SRUnicharToString(NSF14FunctionKey),
|
|
|
|
|
@(kVK_F15): _SRUnicharToString(NSF15FunctionKey),
|
|
|
|
|
@(kVK_F16): _SRUnicharToString(NSF16FunctionKey),
|
|
|
|
|
@(kVK_F17): _SRUnicharToString(NSF17FunctionKey),
|
|
|
|
|
@(kVK_F18): _SRUnicharToString(NSF18FunctionKey),
|
|
|
|
|
@(kVK_F19): _SRUnicharToString(NSF19FunctionKey),
|
|
|
|
|
@(kVK_F20): _SRUnicharToString(NSF20FunctionKey),
|
|
|
|
|
@(kVK_Space): _SRUnicharToString(' '),
|
|
|
|
|
@(kVK_Delete): _SRUnicharToString(NSBackspaceCharacter),
|
|
|
|
|
@(kVK_ForwardDelete): _SRUnicharToString(NSDeleteCharacter),
|
|
|
|
|
@(kVK_ANSI_KeypadClear): _SRUnicharToString(NSClearLineFunctionKey),
|
|
|
|
|
@(kVK_LeftArrow): _SRUnicharToString(NSLeftArrowFunctionKey),
|
|
|
|
|
@(kVK_RightArrow): _SRUnicharToString(NSRightArrowFunctionKey),
|
|
|
|
|
@(kVK_UpArrow): _SRUnicharToString(NSUpArrowFunctionKey),
|
|
|
|
|
@(kVK_DownArrow): _SRUnicharToString(NSDownArrowFunctionKey),
|
|
|
|
|
@(kVK_End): _SRUnicharToString(NSEndFunctionKey),
|
|
|
|
|
@(kVK_Home): _SRUnicharToString(NSHomeFunctionKey),
|
|
|
|
|
@(kVK_Escape): _SRUnicharToString('\e'),
|
|
|
|
|
@(kVK_PageDown): _SRUnicharToString(NSPageDownFunctionKey),
|
|
|
|
|
@(kVK_PageUp): _SRUnicharToString(NSPageUpFunctionKey),
|
|
|
|
|
@(kVK_Return): _SRUnicharToString(NSCarriageReturnCharacter),
|
|
|
|
|
@(kVK_ANSI_KeypadEnter): _SRUnicharToString(NSEnterCharacter),
|
|
|
|
|
@(kVK_Tab): _SRUnicharToString(NSTabCharacter),
|
|
|
|
|
@(kVK_Help): _SRUnicharToString(NSHelpFunctionKey)
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
return Mapping;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (NSDictionary *)specialKeyCodesToPlainStringsMapping
|
|
|
|
|
{
|
|
|
|
|
static dispatch_once_t OnceToken;
|
|
|
|
|
static NSDictionary *Mapping = nil;
|
|
|
|
|
dispatch_once(&OnceToken, ^{
|
|
|
|
|
Mapping = @{
|
|
|
|
|
@(kVK_F1): @"F1",
|
|
|
|
|
@(kVK_F2): @"F2",
|
|
|
|
|
@(kVK_F3): @"F3",
|
|
|
|
|
@(kVK_F4): @"F4",
|
|
|
|
|
@(kVK_F5): @"F5",
|
|
|
|
|
@(kVK_F6): @"F6",
|
|
|
|
|
@(kVK_F7): @"F7",
|
|
|
|
|
@(kVK_F8): @"F8",
|
|
|
|
|
@(kVK_F9): @"F9",
|
|
|
|
|
@(kVK_F10): @"F10",
|
|
|
|
|
@(kVK_F11): @"F11",
|
|
|
|
|
@(kVK_F12): @"F12",
|
|
|
|
|
@(kVK_F13): @"F13",
|
|
|
|
|
@(kVK_F14): @"F14",
|
|
|
|
|
@(kVK_F15): @"F15",
|
|
|
|
|
@(kVK_F16): @"F16",
|
|
|
|
|
@(kVK_F17): @"F17",
|
|
|
|
|
@(kVK_F18): @"F18",
|
|
|
|
|
@(kVK_F19): @"F19",
|
|
|
|
|
@(kVK_F20): @"F20",
|
|
|
|
|
@(kVK_Space): SRLoc(@"Space"),
|
|
|
|
|
@(kVK_Delete): _SRUnicharToString(SRKeyCodeGlyphDeleteLeft),
|
|
|
|
|
@(kVK_ForwardDelete): _SRUnicharToString(SRKeyCodeGlyphDeleteRight),
|
|
|
|
|
@(kVK_ANSI_KeypadClear): _SRUnicharToString(SRKeyCodeGlyphPadClear),
|
|
|
|
|
@(kVK_LeftArrow): _SRUnicharToString(SRKeyCodeGlyphLeftArrow),
|
|
|
|
|
@(kVK_RightArrow): _SRUnicharToString(SRKeyCodeGlyphRightArrow),
|
|
|
|
|
@(kVK_UpArrow): _SRUnicharToString(SRKeyCodeGlyphUpArrow),
|
|
|
|
|
@(kVK_DownArrow): _SRUnicharToString(SRKeyCodeGlyphDownArrow),
|
|
|
|
|
@(kVK_End): _SRUnicharToString(SRKeyCodeGlyphSoutheastArrow),
|
|
|
|
|
@(kVK_Home): _SRUnicharToString(SRKeyCodeGlyphNorthwestArrow),
|
|
|
|
|
@(kVK_Escape): _SRUnicharToString(SRKeyCodeGlyphEscape),
|
|
|
|
|
@(kVK_PageDown): _SRUnicharToString(SRKeyCodeGlyphPageDown),
|
|
|
|
|
@(kVK_PageUp): _SRUnicharToString(SRKeyCodeGlyphPageUp),
|
|
|
|
|
@(kVK_Return): _SRUnicharToString(SRKeyCodeGlyphReturnR2L),
|
|
|
|
|
@(kVK_ANSI_KeypadEnter): _SRUnicharToString(SRKeyCodeGlyphReturn),
|
|
|
|
|
@(kVK_Tab): _SRUnicharToString(SRKeyCodeGlyphTabRight),
|
|
|
|
|
@(kVK_Help): @"?⃝"
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
return Mapping;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isKeyCodeSpecial:(unsigned short)aKeyCode
|
|
|
|
|
{
|
|
|
|
|
switch (aKeyCode)
|
|
|
|
|
{
|
|
|
|
|
case kVK_F1:
|
|
|
|
|
case kVK_F2:
|
|
|
|
|
case kVK_F3:
|
|
|
|
|
case kVK_F4:
|
|
|
|
|
case kVK_F5:
|
|
|
|
|
case kVK_F6:
|
|
|
|
|
case kVK_F7:
|
|
|
|
|
case kVK_F8:
|
|
|
|
|
case kVK_F9:
|
|
|
|
|
case kVK_F10:
|
|
|
|
|
case kVK_F11:
|
|
|
|
|
case kVK_F12:
|
|
|
|
|
case kVK_F13:
|
|
|
|
|
case kVK_F14:
|
|
|
|
|
case kVK_F15:
|
|
|
|
|
case kVK_F16:
|
|
|
|
|
case kVK_F17:
|
|
|
|
|
case kVK_F18:
|
|
|
|
|
case kVK_F19:
|
|
|
|
|
case kVK_F20:
|
|
|
|
|
case kVK_Space:
|
|
|
|
|
case kVK_Delete:
|
|
|
|
|
case kVK_ForwardDelete:
|
|
|
|
|
case kVK_ANSI_KeypadClear:
|
|
|
|
|
case kVK_LeftArrow:
|
|
|
|
|
case kVK_RightArrow:
|
|
|
|
|
case kVK_UpArrow:
|
|
|
|
|
case kVK_DownArrow:
|
|
|
|
|
case kVK_End:
|
|
|
|
|
case kVK_Home:
|
|
|
|
|
case kVK_Escape:
|
|
|
|
|
case kVK_PageDown:
|
|
|
|
|
case kVK_PageUp:
|
|
|
|
|
case kVK_Return:
|
|
|
|
|
case kVK_ANSI_KeypadEnter:
|
|
|
|
|
case kVK_Tab:
|
|
|
|
|
case kVK_Help:
|
|
|
|
|
return YES;
|
|
|
|
|
default:
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark NSValueTransformer
|
|
|
|
|
|
|
|
|
|
+ (BOOL)allowsReverseTransformation
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (Class)transformedValueClass;
|
|
|
|
|
{
|
|
|
|
|
return [NSString class];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)transformedValue:(NSNumber *)aValue
|
|
|
|
|
{
|
|
|
|
|
return [self transformedValue:aValue withModifierFlags:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)transformedValue:(NSNumber *)aValue withModifierFlags:(NSNumber *)aModifierFlags
|
|
|
|
|
{
|
|
|
|
|
return [self transformedValue:aValue withImplicitModifierFlags:aModifierFlags explicitModifierFlags:nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)transformedValue:(NSNumber *)aValue withImplicitModifierFlags:(NSNumber *)anImplicitModifierFlags explicitModifierFlags:(NSNumber *)anExplicitModifierFlags
|
|
|
|
|
{
|
|
|
|
|
if ([anImplicitModifierFlags unsignedIntegerValue] & [anExplicitModifierFlags unsignedIntegerValue] & SRCocoaModifierFlagsMask)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise:NSInvalidArgumentException format:@"anImplicitModifierFlags and anExplicitModifierFlags MUST NOT have common elements"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (![aValue isKindOfClass:[NSNumber class]])
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
// Some key codes cannot be translated directly.
|
|
|
|
|
NSString *unmappedString = [self transformedSpecialKeyCode:aValue withExplicitModifierFlags:anExplicitModifierFlags];
|
|
|
|
|
|
|
|
|
|
if (unmappedString)
|
|
|
|
|
return unmappedString;
|
|
|
|
|
|
|
|
|
|
CFDataRef layoutData = NULL;
|
|
|
|
|
|
|
|
|
|
if (self.usesASCIICapableKeyboardInputSource)
|
|
|
|
|
{
|
|
|
|
|
TISInputSourceRef tisSource = TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
|
|
|
|
|
|
|
|
|
|
if (!tisSource)
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
layoutData = (CFDataRef)TISGetInputSourceProperty(tisSource, kTISPropertyUnicodeKeyLayoutData);
|
|
|
|
|
CFRelease(tisSource);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TISInputSourceRef tisSource = TISCopyCurrentKeyboardLayoutInputSource();
|
|
|
|
|
|
|
|
|
|
if (!tisSource)
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
layoutData = (CFDataRef)TISGetInputSourceProperty(tisSource, kTISPropertyUnicodeKeyLayoutData);
|
|
|
|
|
CFRelease(tisSource);
|
|
|
|
|
|
|
|
|
|
// For non-unicode layouts such as Chinese, Japanese, and Korean, get the ASCII capable layout
|
|
|
|
|
if (!layoutData)
|
|
|
|
|
{
|
|
|
|
|
tisSource = TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
|
|
|
|
|
|
|
|
|
|
if (!tisSource)
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
layoutData = (CFDataRef)TISGetInputSourceProperty(tisSource, kTISPropertyUnicodeKeyLayoutData);
|
|
|
|
|
CFRelease(tisSource);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!layoutData)
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
const UCKeyboardLayout *keyLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
|
|
|
|
|
|
|
|
|
|
static const UniCharCount MaxLength = 255;
|
|
|
|
|
UniCharCount actualLength = 0;
|
|
|
|
|
UniChar chars[MaxLength] = {0};
|
|
|
|
|
|
|
|
|
|
UInt32 deadKeyState = 0;
|
|
|
|
|
OSStatus err = UCKeyTranslate(keyLayout,
|
|
|
|
|
[aValue unsignedShortValue],
|
|
|
|
|
kUCKeyActionDisplay,
|
|
|
|
|
SRCocoaToCarbonFlags([anImplicitModifierFlags unsignedIntegerValue]) >> 8,
|
|
|
|
|
LMGetKbdType(),
|
|
|
|
|
kUCKeyTranslateNoDeadKeysBit,
|
|
|
|
|
&deadKeyState,
|
|
|
|
|
sizeof(chars) / sizeof(UniChar),
|
|
|
|
|
&actualLength,
|
|
|
|
|
chars);
|
|
|
|
|
if (err != noErr)
|
|
|
|
|
return @"";
|
|
|
|
|
|
|
|
|
|
if (self.usesPlainStrings)
|
|
|
|
|
return [[NSString stringWithCharacters:chars length:actualLength] uppercaseString];
|
|
|
|
|
else
|
|
|
|
|
return [NSString stringWithCharacters:chars length:actualLength];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)transformedSpecialKeyCode:(NSNumber *)aKeyCode withExplicitModifierFlags:(NSNumber *)anExplicitModifierFlags
|
|
|
|
|
{
|
|
|
|
|
if ([anExplicitModifierFlags unsignedIntegerValue] & NSShiftKeyMask && [aKeyCode unsignedShortValue] == kVK_Tab)
|
|
|
|
|
{
|
|
|
|
|
if (self.usesPlainStrings)
|
|
|
|
|
return _SRUnicharToString(SRKeyCodeGlyphTabLeft);
|
|
|
|
|
else
|
|
|
|
|
return _SRUnicharToString(NSBackTabCharacter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self.usesPlainStrings)
|
|
|
|
|
return [[self class] specialKeyCodesToPlainStringsMapping][aKeyCode];
|
|
|
|
|
else
|
|
|
|
|
return [[self class] specialKeyCodesToUnicodeCharactersMapping][aKeyCode];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|