Skip to content
This repository has been archived by the owner on Mar 5, 2023. It is now read-only.

Commit

Permalink
Enables support for exlusive Option modifier.
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadim Shpakovski committed Nov 23, 2012
1 parent bc56cdc commit a89afec
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 17 deletions.
5 changes: 5 additions & 0 deletions MASShortcut.h
Expand Up @@ -51,4 +51,9 @@ enum {

- (BOOL)isTakenError:(NSError **)error;

// The following API enable hotkeys with the Option key as the only modifier
// For example, Option-G will not generate © and Option-R will not paste ®
+ (void)setAllowsAnyHotkeyWithOptionModifier:(BOOL)allow;
+ (BOOL)allowsAnyHotkeyWithOptionModifier;

@end
65 changes: 48 additions & 17 deletions MASShortcut.m
@@ -1,7 +1,7 @@
#import "MASShortcut.h"

NSString *const kMASShortcutKeyCode = @"KeyCode";
NSString *const kMASShortcutModifierFlags = @"ModifierFlags";
NSString *const MASShortcutKeyCode = @"KeyCode";
NSString *const MASShortcutModifierFlags = @"ModifierFlags";

@implementation MASShortcut {
NSUInteger _keyCode; // NSNotFound if empty
Expand All @@ -15,17 +15,17 @@ @implementation MASShortcut {

- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeInteger:(self.keyCode != NSNotFound ? (NSInteger)self.keyCode : - 1) forKey:kMASShortcutKeyCode];
[coder encodeInteger:(NSInteger)self.modifierFlags forKey:kMASShortcutModifierFlags];
[coder encodeInteger:(self.keyCode != NSNotFound ? (NSInteger)self.keyCode : - 1) forKey:MASShortcutKeyCode];
[coder encodeInteger:(NSInteger)self.modifierFlags forKey:MASShortcutModifierFlags];
}

- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self) {
NSInteger code = [decoder decodeIntegerForKey:kMASShortcutKeyCode];
NSInteger code = [decoder decodeIntegerForKey:MASShortcutKeyCode];
self.keyCode = (code < 0 ? NSNotFound : (NSUInteger)code);
self.modifierFlags = [decoder decodeIntegerForKey:kMASShortcutModifierFlags];
self.modifierFlags = [decoder decodeIntegerForKey:MASShortcutModifierFlags];
}
return self;
}
Expand Down Expand Up @@ -232,19 +232,50 @@ - (BOOL)shouldBypass
return (self.modifierFlags == NSCommandKeyMask) && ([codeString isEqualToString:@"W"] || [codeString isEqualToString:@"Q"]);
}

BOOL MASShortcutAllowsAnyHotkeyWithOptionModifier = NO;

+ (void)setAllowsAnyHotkeyWithOptionModifier:(BOOL)allow
{
MASShortcutAllowsAnyHotkeyWithOptionModifier = allow;
}

+ (BOOL)allowsAnyHotkeyWithOptionModifier
{
return MASShortcutAllowsAnyHotkeyWithOptionModifier;
}

- (BOOL)isValid
{
BOOL hasFlags = (_modifierFlags > 0);
BOOL hasCommand = ((_modifierFlags & NSCommandKeyMask) > 0);
BOOL hasControl = ((_modifierFlags & NSControlKeyMask) > 0);
BOOL hasOption = ((_modifierFlags & NSAlternateKeyMask) > 0);
BOOL isFunction = ((_keyCode == kVK_F1) || (_keyCode == kVK_F2) || (_keyCode == kVK_F3) || (_keyCode == kVK_F4) ||
(_keyCode == kVK_F5) || (_keyCode == kVK_F6) || (_keyCode == kVK_F7) || (_keyCode == kVK_F8) ||
(_keyCode == kVK_F9) || (_keyCode == kVK_F10) || (_keyCode == kVK_F11) || (_keyCode == kVK_F12) ||
(_keyCode == kVK_F13) || (_keyCode == kVK_F14) || (_keyCode == kVK_F15) || (_keyCode == kVK_F16) ||
(_keyCode == kVK_F17) || (_keyCode == kVK_F18) || (_keyCode == kVK_F19) || (_keyCode == kVK_F20));
BOOL isSpecial = ((_keyCode == kVK_Space) || (_keyCode == kVK_Escape) || (_keyCode == kVK_Return));
return ((hasFlags && (hasCommand || hasControl || (hasOption && isSpecial))) || isFunction);
// Allow any function key with any combination of modifiers
BOOL includesFunctionKey = ((_keyCode == kVK_F1) || (_keyCode == kVK_F2) || (_keyCode == kVK_F3) || (_keyCode == kVK_F4) ||
(_keyCode == kVK_F5) || (_keyCode == kVK_F6) || (_keyCode == kVK_F7) || (_keyCode == kVK_F8) ||
(_keyCode == kVK_F9) || (_keyCode == kVK_F10) || (_keyCode == kVK_F11) || (_keyCode == kVK_F12) ||
(_keyCode == kVK_F13) || (_keyCode == kVK_F14) || (_keyCode == kVK_F15) || (_keyCode == kVK_F16) ||
(_keyCode == kVK_F17) || (_keyCode == kVK_F18) || (_keyCode == kVK_F19) || (_keyCode == kVK_F20));
if (includesFunctionKey) return YES;

// Do not allow any other key without modifiers
BOOL hasModifierFlags = (_modifierFlags > 0);
if (!hasModifierFlags) return NO;

// Allow any hotkey containing Control or Command modifier
BOOL includesCommand = ((_modifierFlags & NSCommandKeyMask) > 0);
BOOL includesControl = ((_modifierFlags & NSControlKeyMask) > 0);
if (includesCommand || includesControl) return YES;

// Allow Option key only in selected cases
BOOL includesOption = ((_modifierFlags & NSAlternateKeyMask) > 0);
if (includesOption) {

// Always allow Option-Space and Option-Escape because they do not have any bind system commands
if ((_keyCode == kVK_Space) || (_keyCode == kVK_Escape)) return YES;

// Allow Option modifier with any key even if it will break the system binding
if ([[self class] allowsAnyHotkeyWithOptionModifier]) return YES;
}

// The hotkey does not have any modifiers or violates system bindings
return NO;
}

- (BOOL)isKeyEquivalent:(NSString *)keyEquivalent flags:(NSUInteger)flags takenInMenu:(NSMenu *)menu error:(NSError **)outError
Expand Down

0 comments on commit a89afec

Please sign in to comment.