You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
12 KiB

//
// 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