|
|
// |
|
|
// SRCommon.m |
|
|
// ShortcutRecorder |
|
|
// |
|
|
// Copyright 2006-2012 Contributors. All rights reserved. |
|
|
// |
|
|
// License: BSD |
|
|
// |
|
|
// Contributors: |
|
|
// David Dauer |
|
|
// Jesper |
|
|
// Jamie Kirkpatrick |
|
|
// Andy Kim |
|
|
// Ilya Kulakov |
|
|
|
|
|
#import "SRCommon.h" |
|
|
#import "SRKeyCodeTransformer.h" |
|
|
|
|
|
|
|
|
NSBundle *SRBundle() |
|
|
{ |
|
|
static dispatch_once_t onceToken; |
|
|
static NSBundle *Bundle = nil; |
|
|
dispatch_once(&onceToken, ^{ |
|
|
Bundle = [NSBundle bundleWithIdentifier:@"com.kulakov.ShortcutRecorder"]; |
|
|
|
|
|
if (!Bundle) |
|
|
{ |
|
|
// Could be a CocoaPods framework with embedded resources bundle. |
|
|
// Look up "use_frameworks!" and "resources_bundle" in CocoaPods documentation. |
|
|
Bundle = [NSBundle bundleWithIdentifier:@"org.cocoapods.ShortcutRecorder"]; |
|
|
|
|
|
if (!Bundle) |
|
|
{ |
|
|
Class c = NSClassFromString(@"SRRecorderControl"); |
|
|
|
|
|
if (c) |
|
|
{ |
|
|
Bundle = [NSBundle bundleForClass:c]; |
|
|
} |
|
|
} |
|
|
|
|
|
if (Bundle) |
|
|
{ |
|
|
Bundle = [NSBundle bundleWithPath:[Bundle pathForResource:@"ShortcutRecorder" ofType:@"bundle"]]; |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
if (!Bundle) |
|
|
{ |
|
|
@throw [NSException exceptionWithName:NSInternalInconsistencyException |
|
|
reason:@"Unable to find bundle with resources." |
|
|
userInfo:nil]; |
|
|
} |
|
|
else |
|
|
{ |
|
|
return Bundle; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
NSString *SRLoc(NSString *aKey) |
|
|
{ |
|
|
return NSLocalizedStringFromTableInBundle(aKey, @"ShortcutRecorder", SRBundle(), nil); |
|
|
} |
|
|
|
|
|
|
|
|
NSImage *SRImage(NSString *anImageName) |
|
|
{ |
|
|
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_6) |
|
|
return [[NSImage alloc] initByReferencingURL:[SRBundle() URLForImageResource:anImageName]]; |
|
|
else |
|
|
return [SRBundle() imageForResource:anImageName]; |
|
|
} |
|
|
|
|
|
|
|
|
NSString *SRReadableStringForCocoaModifierFlagsAndKeyCode(NSEventModifierFlags aModifierFlags, unsigned short aKeyCode) |
|
|
{ |
|
|
SRKeyCodeTransformer *t = [SRKeyCodeTransformer sharedPlainTransformer]; |
|
|
NSString *c = [t transformedValue:@(aKeyCode)]; |
|
|
|
|
|
return [NSString stringWithFormat:@"%@%@%@%@%@", |
|
|
(aModifierFlags & NSCommandKeyMask ? SRLoc(@"Command-") : @""), |
|
|
(aModifierFlags & NSAlternateKeyMask ? SRLoc(@"Option-") : @""), |
|
|
(aModifierFlags & NSControlKeyMask ? SRLoc(@"Control-") : @""), |
|
|
(aModifierFlags & NSShiftKeyMask ? SRLoc(@"Shift-") : @""), |
|
|
c]; |
|
|
} |
|
|
|
|
|
|
|
|
NSString *SRReadableASCIIStringForCocoaModifierFlagsAndKeyCode(NSEventModifierFlags aModifierFlags, unsigned short aKeyCode) |
|
|
{ |
|
|
SRKeyCodeTransformer *t = [SRKeyCodeTransformer sharedPlainASCIITransformer]; |
|
|
NSString *c = [t transformedValue:@(aKeyCode)]; |
|
|
|
|
|
return [NSString stringWithFormat:@"%@%@%@%@%@", |
|
|
(aModifierFlags & NSCommandKeyMask ? SRLoc(@"Command-") : @""), |
|
|
(aModifierFlags & NSAlternateKeyMask ? SRLoc(@"Option-") : @""), |
|
|
(aModifierFlags & NSControlKeyMask ? SRLoc(@"Control-") : @""), |
|
|
(aModifierFlags & NSShiftKeyMask ? SRLoc(@"Shift-") : @""), |
|
|
c]; |
|
|
} |
|
|
|
|
|
|
|
|
static BOOL _SRKeyCodeWithFlagsEqualToKeyEquivalentWithFlags(unsigned short aKeyCode, |
|
|
NSEventModifierFlags aKeyCodeFlags, |
|
|
NSString *aKeyEquivalent, |
|
|
NSEventModifierFlags aKeyEquivalentModifierFlags, |
|
|
SRKeyCodeTransformer *aTransformer) |
|
|
{ |
|
|
if (!aKeyEquivalent) |
|
|
return NO; |
|
|
|
|
|
aKeyCodeFlags &= SRCocoaModifierFlagsMask; |
|
|
aKeyEquivalentModifierFlags &= SRCocoaModifierFlagsMask; |
|
|
|
|
|
if (aKeyCodeFlags == aKeyEquivalentModifierFlags) |
|
|
{ |
|
|
NSString *keyCodeRepresentation = [aTransformer transformedValue:@(aKeyCode) |
|
|
withImplicitModifierFlags:nil |
|
|
explicitModifierFlags:@(aKeyCodeFlags)]; |
|
|
return [keyCodeRepresentation isEqual:aKeyEquivalent]; |
|
|
} |
|
|
else if (!aKeyEquivalentModifierFlags || |
|
|
(aKeyCodeFlags & aKeyEquivalentModifierFlags) == aKeyEquivalentModifierFlags) |
|
|
{ |
|
|
// Some key equivalent modifier flags can be implicitly set by using special unicode characters. E.g. <EFBFBD> insetead of opt-a. |
|
|
// However all modifier flags explictily set in key equivalent MUST be also set in key code flags. |
|
|
// E.g. ctrl-<EFBFBD>/ctrl-opt-a and <EFBFBD>/opt-a match this condition, but cmd-<EFBFBD>/ctrl-opt-a doesn't. |
|
|
NSString *keyCodeRepresentation = [aTransformer transformedValue:@(aKeyCode) |
|
|
withImplicitModifierFlags:nil |
|
|
explicitModifierFlags:@(aKeyCodeFlags)]; |
|
|
|
|
|
if ([keyCodeRepresentation isEqual:aKeyEquivalent]) |
|
|
{ |
|
|
// Key code and key equivalent are not equal key code representation matches key equivalent, but modifier flags are not. |
|
|
return NO; |
|
|
} |
|
|
else |
|
|
{ |
|
|
NSEventModifierFlags possiblyImplicitFlags = aKeyCodeFlags & ~aKeyEquivalentModifierFlags; |
|
|
keyCodeRepresentation = [aTransformer transformedValue:@(aKeyCode) |
|
|
withImplicitModifierFlags:@(possiblyImplicitFlags) |
|
|
explicitModifierFlags:@(aKeyEquivalentModifierFlags)]; |
|
|
return [keyCodeRepresentation isEqual:aKeyEquivalent]; |
|
|
} |
|
|
} |
|
|
else |
|
|
return NO; |
|
|
} |
|
|
|
|
|
|
|
|
BOOL SRKeyCodeWithFlagsEqualToKeyEquivalentWithFlags(unsigned short aKeyCode, |
|
|
NSEventModifierFlags aKeyCodeFlags, |
|
|
NSString *aKeyEquivalent, |
|
|
NSEventModifierFlags aKeyEquivalentModifierFlags) |
|
|
{ |
|
|
BOOL isEqual = _SRKeyCodeWithFlagsEqualToKeyEquivalentWithFlags(aKeyCode, |
|
|
aKeyCodeFlags, |
|
|
aKeyEquivalent, |
|
|
aKeyEquivalentModifierFlags, |
|
|
[SRKeyCodeTransformer sharedASCIITransformer]); |
|
|
|
|
|
if (!isEqual) |
|
|
{ |
|
|
isEqual = _SRKeyCodeWithFlagsEqualToKeyEquivalentWithFlags(aKeyCode, |
|
|
aKeyCodeFlags, |
|
|
aKeyEquivalent, |
|
|
aKeyEquivalentModifierFlags, |
|
|
[SRKeyCodeTransformer sharedTransformer]); |
|
|
} |
|
|
|
|
|
return isEqual; |
|
|
}
|
|
|
|