diff options
Diffstat (limited to 'AppKit/GTMHotKeyTextField.m')
-rw-r--r-- | AppKit/GTMHotKeyTextField.m | 781 |
1 files changed, 0 insertions, 781 deletions
diff --git a/AppKit/GTMHotKeyTextField.m b/AppKit/GTMHotKeyTextField.m deleted file mode 100644 index a33277a..0000000 --- a/AppKit/GTMHotKeyTextField.m +++ /dev/null @@ -1,781 +0,0 @@ -// GTMHotKeyTextField.m -// -// Copyright 2006-2010 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); you may not -// use this file except in compliance with the License. You may obtain a copy -// of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations under -// the License. -// - -#import "GTMHotKeyTextField.h" - -#import <Carbon/Carbon.h> - -#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 -#import "GTMSystemVersion.h" -typedef struct __TISInputSource* TISInputSourceRef; - -static TISInputSourceRef(*GTM_TISCopyCurrentKeyboardLayoutInputSource)(void) = NULL; -static void * (*GTM_TISGetInputSourceProperty)(TISInputSourceRef inputSource, - CFStringRef propertyKey) = NULL; -static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; -#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - -@interface GTMHotKeyTextFieldCell (PrivateMethods) -- (void)setupBinding:(id)bound withPath:(NSString *)path; -- (void)updateDisplayedPrettyString; -+ (NSString *)displayStringForHotKey:(GTMHotKey *)hotKey; -+ (BOOL)doesKeyCodeRequireModifier:(UInt16)keycode; -@end - -@interface GTMHotKeyFieldEditor (PrivateMethods) -- (GTMHotKeyTextFieldCell *)cell; -- (void)setCell:(GTMHotKeyTextFieldCell *)cell; -- (BOOL)shouldBypassEvent:(NSEvent *)theEvent; -- (void)processEventToHotKeyAndString:(NSEvent *)theEvent; -- (void)windowResigned:(NSNotification *)notification; -- (GTMHotKey *)hotKeyForEvent:(NSEvent *)event; -@end - -@implementation GTMHotKey - -+ (id)hotKeyWithKeyCode:(NSUInteger)keyCode - modifiers:(NSUInteger)modifiers - useDoubledModifier:(BOOL)doubledModifier { - return [[[self alloc] initWithKeyCode:keyCode - modifiers:modifiers - useDoubledModifier:doubledModifier] autorelease]; -} - -- (id)initWithKeyCode:(NSUInteger)keyCode - modifiers:(NSUInteger)modifiers - useDoubledModifier:(BOOL)doubledModifier { - if ((self = [super init])) { - modifiers_ = modifiers; - keyCode_ = keyCode; - doubledModifier_ = doubledModifier; - } - return self; -} - -- (NSUInteger)modifiers { - return modifiers_; -} - -- (NSUInteger)keyCode { - return keyCode_; -} - -- (BOOL)doubledModifier { - return doubledModifier_; -} - -- (BOOL)isEqual:(id)object { - return [object isKindOfClass:[GTMHotKey class]] - && [object modifiers] == [self modifiers] - && [(GTMHotKey *)object keyCode] == [self keyCode] - && [object doubledModifier] == [self doubledModifier]; -} - -- (NSUInteger)hash { - return [self modifiers] + [self keyCode] + [self doubledModifier]; -} - -- (id)copyWithZone:(NSZone *)zone { - return NSCopyObject(self, 0, zone); -} - -- (NSString *)description { - return [NSString stringWithFormat:@"<%@ %p> - %@", - [self class], self, - [GTMHotKeyTextFieldCell displayStringForHotKey:self]]; -} - -@end - -@implementation GTMHotKeyTextField - -+ (Class)cellClass { - return [GTMHotKeyTextFieldCell class]; -} - -@end - -@implementation GTMHotKeyTextFieldCell -- (void)dealloc { - [hotKey_ release]; - [super dealloc]; -} - -- (id)copyWithZone:(NSZone *)zone { - GTMHotKeyTextFieldCell *copy = [super copyWithZone:zone]; - copy->hotKey_ = nil; - [copy setObjectValue:[self objectValue]]; - return copy; -} - -#pragma mark Defeating NSCell - -- (void)logBadValueAccess { - _GTMDevLog(@"Hot key fields want hot key dictionaries as object values."); -} - -- (id)objectValue { - return hotKey_; -} - -- (void)setObjectValue:(id)object { - // Sanity only if set, nil is OK - if (object && ![object isKindOfClass:[GTMHotKey class]]) { - [self logBadValueAccess]; - return; - } - if (![hotKey_ isEqual:object]) { - // Otherwise we directly update ourself - [hotKey_ autorelease]; - hotKey_ = [object copy]; - [self updateDisplayedPrettyString]; - } -} - -- (NSString *)stringValue { - NSString *value = [[self class] displayStringForHotKey:hotKey_]; - if (!value) { - value = @""; - } - return value; -} - -- (void)setStringValue:(NSString *)string { - // Since we are a text cell, lots of AppKit objects will attempt to - // set out string value. Our Field editor should already have done - // that for us, so check to make sure what AppKit is setting us to is - // what we expect. - if (![string isEqual:[self stringValue]]) { - [self logBadValueAccess]; - } -} - -- (NSAttributedString *)attributedStringValue { - NSAttributedString *attrString = nil; - NSString *prettyString = [self stringValue]; - if (prettyString) { - attrString = [[[NSAttributedString alloc] - initWithString:prettyString] autorelease]; - } - return attrString; -} - -- (void)setAttributedStringValue:(NSAttributedString *)string { - [self logBadValueAccess]; -} - -- (id)formatter { - return nil; -} - -- (void)setFormatter:(NSFormatter *)newFormatter { - if (newFormatter) { - // Defeating NSCell - _GTMDevLog(@"Hot key fields don't accept formatters."); - } -} - -- (id)_fieldEditor { - GTMHotKeyFieldEditor *editor = [GTMHotKeyFieldEditor sharedHotKeyFieldEditor]; - [editor setCell:self]; - return editor; -} - -#pragma mark Hot Key Support - -// Private method to update the displayed text of the field with the -// user-readable representation. -- (void)updateDisplayedPrettyString { - // Pretty string - NSString *prettyString = [[self class] displayStringForHotKey:hotKey_]; - if (!prettyString) { - prettyString = @""; - } - [super setObjectValue:prettyString]; - -} - -+ (NSString *)displayStringForHotKey:(GTMHotKey *)hotKey { - if (!hotKey) return nil; - - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - - // Modifiers - NSUInteger modifiers = [hotKey modifiers]; - NSString *mods = [[self class] stringForModifierFlags:modifiers]; - if (modifiers && ![mods length]) return nil; - // Handle double modifier case - if ([hotKey doubledModifier]) { - return [NSString stringWithFormat:@"%@ + %@", mods, mods]; - } - // Keycode - NSUInteger keycode = [hotKey keyCode]; - NSString *keystroke = [[self class] stringForKeycode:keycode - useGlyph:NO - resourceBundle:bundle]; - if (!keystroke || ![keystroke length]) return nil; - if ([[self class] doesKeyCodeRequireModifier:keycode] - && ![mods length]) { - return nil; - } - - return [NSString stringWithFormat:@"%@%@", mods, keystroke]; -} - -#pragma mark Class methods building strings for use w/in the UI. - -#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 -+ (void)initialize { - if (!GTM_TISCopyCurrentKeyboardLayoutInputSource - && [GTMSystemVersion isLeopardOrGreater]) { - CFBundleRef hiToolbox - = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); - if (hiToolbox) { - kGTM_TISPropertyUnicodeKeyLayoutData - = *(CFStringRef*)CFBundleGetDataPointerForName(hiToolbox, - CFSTR("kTISPropertyUnicodeKeyLayoutData")); - GTM_TISCopyCurrentKeyboardLayoutInputSource - = CFBundleGetFunctionPointerForName(hiToolbox, - CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); - GTM_TISGetInputSourceProperty - = CFBundleGetFunctionPointerForName(hiToolbox, - CFSTR("TISGetInputSourceProperty")); - } - } -} -#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - -#pragma mark Useful String Class Methods - -+ (BOOL)doesKeyCodeRequireModifier:(UInt16)keycode { - BOOL doesRequire = YES; - switch(keycode) { - // These are the keycodes that map to the - //unichars in the associated comment. - case 122: // NSF1FunctionKey - case 120: // NSF2FunctionKey - case 99: // NSF3FunctionKey - case 118: // NSF4FunctionKey - case 96: // NSF5FunctionKey - case 97: // NSF6FunctionKey - case 98: // NSF7FunctionKey - case 100: // NSF8FunctionKey - case 101: // NSF9FunctionKey - case 109: // NSF10FunctionKey - case 103: // NSF11FunctionKey - case 111: // NSF12FunctionKey - case 105: // NSF13FunctionKey - case 107: // NSF14FunctionKey - case 113: // NSF15FunctionKey - case 106: // NSF16FunctionKey - doesRequire = NO; - break; - default: - doesRequire = YES; - break; - } - return doesRequire; -} - -// These are not in a category on NSString because this class could be used -// within multiple preference panes at the same time. If we put it in a category -// it would require setting up some magic so that the categories didn't conflict -// between the multiple pref panes. By putting it in the class, you can just -// #define the class name to something else, and then you won't have any -// conflicts. - -+ (NSString *)stringForModifierFlags:(NSUInteger)flags { - UniChar modChars[4]; // We only look for 4 flags - unsigned int charCount = 0; - // These are in the same order as the menu manager shows them - if (flags & NSControlKeyMask) modChars[charCount++] = kControlUnicode; - if (flags & NSAlternateKeyMask) modChars[charCount++] = kOptionUnicode; - if (flags & NSShiftKeyMask) modChars[charCount++] = kShiftUnicode; - if (flags & NSCommandKeyMask) modChars[charCount++] = kCommandUnicode; - if (charCount == 0) return @""; - return [NSString stringWithCharacters:modChars length:charCount]; -} - -+ (NSString *)stringForKeycode:(UInt16)keycode - useGlyph:(BOOL)useGlyph - resourceBundle:(NSBundle *)bundle { - // Some keys never move in any layout (to the best of our knowledge at least) - // so we can hard map them. - UniChar key = 0; - NSString *localizedKey = nil; - - switch (keycode) { - - // Of the hard mapped keys some can be represented with pretty and obvioous - // Unicode or simple strings without localization. - - // Arrow keys - case 123: key = NSLeftArrowFunctionKey; break; - case 124: key = NSRightArrowFunctionKey; break; - case 125: key = NSDownArrowFunctionKey; break; - case 126: key = NSUpArrowFunctionKey; break; - case 122: key = NSF1FunctionKey; localizedKey = @"F1"; break; - case 120: key = NSF2FunctionKey; localizedKey = @"F2"; break; - case 99: key = NSF3FunctionKey; localizedKey = @"F3"; break; - case 118: key = NSF4FunctionKey; localizedKey = @"F4"; break; - case 96: key = NSF5FunctionKey; localizedKey = @"F5"; break; - case 97: key = NSF6FunctionKey; localizedKey = @"F6"; break; - case 98: key = NSF7FunctionKey; localizedKey = @"F7"; break; - case 100: key = NSF8FunctionKey; localizedKey = @"F8"; break; - case 101: key = NSF9FunctionKey; localizedKey = @"F9"; break; - case 109: key = NSF10FunctionKey; localizedKey = @"F10"; break; - case 103: key = NSF11FunctionKey; localizedKey = @"F11"; break; - case 111: key = NSF12FunctionKey; localizedKey = @"F12"; break; - case 105: key = NSF13FunctionKey; localizedKey = @"F13"; break; - case 107: key = NSF14FunctionKey; localizedKey = @"F14"; break; - case 113: key = NSF15FunctionKey; localizedKey = @"F15"; break; - case 106: key = NSF16FunctionKey; localizedKey = @"F16"; break; - // Forward delete is a terrible name so we'll use the glyph Apple puts on - // their current keyboards - case 117: key = 0x2326; break; - - // Now we have keys that can be hard coded but don't have good glyph - // representations. Sure, the Apple menu manager has glyphs for them, but - // an informal poll of Google developers shows no one really knows what - // they mean, so its probably a good idea to use strings. Unfortunately - // this also means localization (*sigh*). We'll use the real English - // strings here as keys so that even if localization is missed we'll do OK - // in output. - - // Whitespace - case 36: key = '\r'; localizedKey = @"Return"; break; - case 76: key = 0x3; localizedKey = @"Enter"; break; - case 48: key = 0x9; localizedKey = @"Tab"; break; - // 0x2423 is the Open Box - case 49: key = 0x2423; localizedKey = @"Space"; break; - // Control keys - case 51: key = 0x8; localizedKey = @"Delete"; break; - case 71: key = NSClearDisplayFunctionKey; localizedKey = @"Clear"; break; - case 53: key = 0x1B; localizedKey = @"Esc"; break; - case 115: key = NSHomeFunctionKey; localizedKey = @"Home"; break; - case 116: key = NSPageUpFunctionKey; localizedKey = @"Page Up"; break; - case 119: key = NSEndFunctionKey; localizedKey = @"End"; break; - case 121: key = NSPageDownFunctionKey; localizedKey = @"Page Down"; break; - case 114: key = NSHelpFunctionKey; localizedKey = @"Help"; break; - // Keypad keys - // There is no good way we could find to glyph these. We tried a variety - // of Unicode glyphs, and the menu manager wouldn't take them. We tried - // subscript numbers, circled numbers and superscript numbers with no - // luck. It may be a bit confusing to the user, but we're happy to hear - // any suggestions. - case 65: key = '.'; localizedKey = @"Keypad ."; break; - case 67: key = '*'; localizedKey = @"Keypad *"; break; - case 69: key = '+'; localizedKey = @"Keypad +"; break; - case 75: key = '/'; localizedKey = @"Keypad /"; break; - case 78: key = '-'; localizedKey = @"Keypad -"; break; - case 81: key = '='; localizedKey = @"Keypad ="; break; - case 82: key = '0'; localizedKey = @"Keypad 0"; break; - case 83: key = '1'; localizedKey = @"Keypad 1"; break; - case 84: key = '2'; localizedKey = @"Keypad 2"; break; - case 85: key = '3'; localizedKey = @"Keypad 3"; break; - case 86: key = '4'; localizedKey = @"Keypad 4"; break; - case 87: key = '5'; localizedKey = @"Keypad 5"; break; - case 88: key = '6'; localizedKey = @"Keypad 6"; break; - case 89: key = '7'; localizedKey = @"Keypad 7"; break; - case 91: key = '8'; localizedKey = @"Keypad 8"; break; - case 92: key = '9'; localizedKey = @"Keypad 9"; break; - - } - - // If they asked for strings, and we have one return it. Otherwise, return - // any key we've picked. - if (!useGlyph && localizedKey) { - return NSLocalizedStringFromTableInBundle(localizedKey, - @"GTMHotKeyTextField", - bundle, - @""); - } else if (key != 0) { - return [NSString stringWithFormat:@"%C", key]; - } - - // Everything else should be printable so look it up in the current keyboard - UCKeyboardLayout *uchrData = NULL; - - OSStatus err = noErr; -#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - // layout - KeyboardLayoutRef currentLayout = NULL; - // Get the layout kind - SInt32 currentLayoutKind = -1; - if ([GTMSystemVersion isLeopardOrGreater] - && kGTM_TISPropertyUnicodeKeyLayoutData - && GTM_TISGetInputSourceProperty - && GTM_TISCopyCurrentKeyboardLayoutInputSource) { - // On Leopard we use the new improved TIS interfaces which work for input - // sources as well as keyboard layouts. - TISInputSourceRef inputSource - = GTM_TISCopyCurrentKeyboardLayoutInputSource(); - if (inputSource) { - CFDataRef uchrDataRef - = GTM_TISGetInputSourceProperty(inputSource, - kGTM_TISPropertyUnicodeKeyLayoutData); - if(uchrDataRef) { - uchrData = (UCKeyboardLayout*)CFDataGetBytePtr(uchrDataRef); - } - CFRelease(inputSource); - } - } else { - // Tiger we use keyboard layouts as it's the best we can officially do. - err = KLGetCurrentKeyboardLayout(¤tLayout); - if (err != noErr) { // COV_NF_START - _GTMDevLog(@"failed to fetch the keyboard layout, err=%d", err); - return nil; - } // COV_NF_END - - err = KLGetKeyboardLayoutProperty(currentLayout, - kKLKind, - (const void **)¤tLayoutKind); - if (err != noErr) { // COV_NF_START - _GTMDevLog(@"failed to fetch the keyboard layout kind property, err=%d", - err); - return nil; - } // COV_NF_END - - if (currentLayoutKind != kKLKCHRKind) { - err = KLGetKeyboardLayoutProperty(currentLayout, - kKLuchrData, - (const void **)&uchrData); - if (err != noErr) { // COV_NF_START - _GTMDevLog(@"failed to fetch the keyboard layout uchar data, err=%d", - err); - return nil; - } // COV_NF_END - } - } -#else - TISInputSourceRef inputSource = TISCopyCurrentKeyboardLayoutInputSource(); - if (inputSource) { - CFDataRef uchrDataRef - = TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData); - if(uchrDataRef) { - uchrData = (UCKeyboardLayout*)CFDataGetBytePtr(uchrDataRef); - } - CFRelease(inputSource); - } -#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - - NSString *keystrokeString = nil; - if (uchrData) { - // uchr layout data is available, this is our preference - UniCharCount uchrCharLength = 0; - UniChar uchrChars[256] = { 0 }; - UInt32 uchrDeadKeyState = 0; - err = UCKeyTranslate(uchrData, - keycode, - kUCKeyActionDisplay, - 0, // No modifiers - LMGetKbdType(), - kUCKeyTranslateNoDeadKeysMask, - &uchrDeadKeyState, - sizeof(uchrChars) / sizeof(UniChar), - &uchrCharLength, - uchrChars); - if (err != noErr) { - // COV_NF_START - _GTMDevLog(@"failed to translate the keycode, err=%d", (int)err); - return nil; - // COV_NF_END - } - if (uchrCharLength < 1) return nil; - keystrokeString = [NSString stringWithCharacters:uchrChars - length:uchrCharLength]; - } -#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - else if (currentLayoutKind == kKLKCHRKind) { - // Only KCHR layout data is available, go old school - void *KCHRData = NULL; - err = KLGetKeyboardLayoutProperty(currentLayout, kKLKCHRData, - (const void **)&KCHRData); - if (err != noErr) { // COV_NF_START - _GTMDevLog(@"failed to fetch the keyboard layout uchar data, err=%d", - err); - return nil; - } // COV_NF_END - // Turn into character code - UInt32 keyTranslateState = 0; - UInt32 twoKCHRChars = KeyTranslate(KCHRData, keycode, &keyTranslateState); - if (!twoKCHRChars) return nil; - // Unpack the fields - char firstChar = (char)((twoKCHRChars & 0x00FF0000) >> 16); - char secondChar = (char)(twoKCHRChars & 0x000000FF); - // May have one or two characters - if (firstChar && secondChar) { - NSString *str1 - = [[[NSString alloc] initWithBytes:&firstChar - length:1 - encoding:NSMacOSRomanStringEncoding] autorelease]; - NSString *str2 - = [[[NSString alloc] initWithBytes:&secondChar - length:1 - encoding:NSMacOSRomanStringEncoding] autorelease]; - keystrokeString = [NSString stringWithFormat:@"%@%@", - [str1 uppercaseString], - [str2 uppercaseString]]; - } else { - keystrokeString - = [[[NSString alloc] initWithBytes:&secondChar - length:1 - encoding:NSMacOSRomanStringEncoding] autorelease]; - [keystrokeString uppercaseString]; - } - } -#endif // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 - - // Sanity we got a stroke - if (!keystrokeString || ![keystrokeString length]) return nil; - - // Sanity check the keystroke string for unprintable characters - NSMutableCharacterSet *validChars = - [[[NSMutableCharacterSet alloc] init] autorelease]; - - [validChars formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]]; - [validChars formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; - [validChars formUnionWithCharacterSet:[NSCharacterSet symbolCharacterSet]]; - for (unsigned int i = 0; i < [keystrokeString length]; i++) { - if (![validChars characterIsMember:[keystrokeString characterAtIndex:i]]) { - return nil; - } - } - - if (!useGlyph) { - // menus want glyphs in the original lowercase forms, so we only upper this - // if we aren't using it as a glyph. - keystrokeString = [keystrokeString uppercaseString]; - } - - return keystrokeString; -} - -@end - -@implementation GTMHotKeyFieldEditor - -+ (GTMHotKeyFieldEditor *)sharedHotKeyFieldEditor { - static GTMHotKeyFieldEditor *obj; - if (!obj) { - obj = [[self alloc] init]; - } - return obj; -} - -- (id)init { - if ((self = [super init])) { - [self setFieldEditor:YES]; // We are a field editor - } - return self; -} - -// COV_NF_START -// Singleton so never called. -- (void)dealloc { - [cell_ release]; - [super dealloc]; -} -// COV_NF_END - -- (GTMHotKeyTextFieldCell *)cell { - return cell_; -} - -- (void)setCell:(GTMHotKeyTextFieldCell *)cell { - [cell_ autorelease]; - cell_ = [cell retain]; -} - -- (NSArray *)acceptableDragTypes { - // Don't take drags - return [NSArray array]; -} - -- (NSArray *)readablePasteboardTypes { - // No pasting - return [NSArray array]; -} - -- (NSArray *)writablePasteboardTypes { - // No copying - return [NSArray array]; -} - -- (BOOL)becomeFirstResponder { - // We need to lose focus any time the window is not key - NSNotificationCenter *dc = [NSNotificationCenter defaultCenter]; - [dc addObserver:self - selector:@selector(windowResigned:) - name:NSWindowDidResignKeyNotification - object:[self window]]; - return [super becomeFirstResponder]; -} - -- (BOOL)resignFirstResponder { - // No longer interested in window resign - [[NSNotificationCenter defaultCenter] removeObserver:self]; - return [super resignFirstResponder]; -} - -// Private method we use to get out of global hotkey capture when the window -// is no longer front -- (void)windowResigned:(NSNotification *)notification { - // Lose our focus - NSWindow *window = [self window]; - [window makeFirstResponder:window]; - -} - -- (BOOL)shouldDrawInsertionPoint { - // Show an insertion point, because we'll kill our own focus after - // each entry - return YES; -} - -- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange - granularity:(NSSelectionGranularity)granularity { - // Always select everything - return NSMakeRange(0, [[self textStorage] length]); -} - -- (void)keyDown:(NSEvent *)theEvent { - if ([self shouldBypassEvent:theEvent]) { - [super keyDown:theEvent]; - } else { - // Try to eat the event - [self processEventToHotKeyAndString:theEvent]; - } -} - -- (BOOL)performKeyEquivalent:(NSEvent *)theEvent { - if ([self shouldBypassEvent:theEvent]) { - return [super performKeyEquivalent:theEvent]; - } else { - // We always eat these key strokes while we have focus - [self processEventToHotKeyAndString:theEvent]; - return YES; - } -} - -// Private do method that tell us to ignore certain events -- (BOOL)shouldBypassEvent:(NSEvent *)theEvent { - BOOL bypass = NO; - UInt16 keyCode = [theEvent keyCode]; - NSUInteger modifierFlags - = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; - - if (keyCode == 48) { // Tab - // Ignore all events that the dock cares about - // Just to be extra clear if the user is trying to use Dock hotkeys beep - // at them - if ((modifierFlags == NSCommandKeyMask) || - (modifierFlags == (NSCommandKeyMask | NSShiftKeyMask))) { - NSBeep(); - bypass = YES; - } else if (modifierFlags == 0 || modifierFlags == NSShiftKeyMask) { - // Probably attempting to tab around the dialog. - bypass = YES; - } - - } else if ((keyCode == 12) && (modifierFlags == NSCommandKeyMask)) { - // Don't eat Cmd-Q. Users could have it as a hotkey, but its more likely - // they're trying to quit - bypass = YES; - } else if ((keyCode == 13) && (modifierFlags == NSCommandKeyMask)) { - // Same for Cmd-W, user is probably trying to close the window - bypass = YES; - } - return bypass; -} - -// Private method that turns events into strings and dictionaries for our -// hotkey plumbing. -- (void)processEventToHotKeyAndString:(NSEvent *)theEvent { - // Construct a dictionary of the event as a hotkey pref - GTMHotKey *newHotKey = nil; - NSString *prettyString = @""; - // 51 is "the delete key" - const NSUInteger allModifiers = (NSCommandKeyMask | NSAlternateKeyMask | - NSControlKeyMask | NSShiftKeyMask); - if (!(([theEvent keyCode] == 51 ) - && (([theEvent modifierFlags] & allModifiers)== 0))) { - newHotKey = [self hotKeyForEvent:theEvent]; - if (!newHotKey) { - NSBeep(); - return; // No action, but don't give up focus - } - prettyString = [GTMHotKeyTextFieldCell displayStringForHotKey:newHotKey]; - if (!prettyString) { - NSBeep(); - return; - } - } - - // Replacement range - NSRange replaceRange = NSMakeRange(0, [[self textStorage] length]); - - // Ask for permission to replace - if (![self shouldChangeTextInRange:replaceRange - replacementString:prettyString]) { - // If replacement was disallowed, change nothing, including hotKeyDict_ - NSBeep(); - return; - } - - [[self cell] setObjectValue:newHotKey]; - - // Finish the change - [self didChangeText]; - - // Force editing to end. This sends focus off into space slightly, but - // its better than constantly capturing user events. This is exactly - // like the Apple editor in their Keyboard pref pane. - [[self window] makeFirstResponder:nil]; -} - -- (GTMHotKey *)hotKeyForEvent:(NSEvent *)event { - if (!event) return nil; - - // Check event - NSUInteger flags = [event modifierFlags]; - UInt16 keycode = [event keyCode]; - // If the event has no modifiers do nothing - NSUInteger allModifiers = (NSCommandKeyMask | NSAlternateKeyMask | - NSControlKeyMask | NSShiftKeyMask); - - BOOL requiresModifiers - = [GTMHotKeyTextFieldCell doesKeyCodeRequireModifier:keycode]; - if (requiresModifiers) { - // If we aren't a function key, and have no modifiers do nothing. - if (!(flags & allModifiers)) return nil; - // If the event has high bits in keycode do nothing - if (keycode & 0xFF00) return nil; - } - - // Clean the flags to only contain things we care about - UInt32 cleanFlags = 0; - if (flags & NSCommandKeyMask) cleanFlags |= NSCommandKeyMask; - if (flags & NSAlternateKeyMask) cleanFlags |= NSAlternateKeyMask; - if (flags & NSControlKeyMask) cleanFlags |= NSControlKeyMask; - if (flags & NSShiftKeyMask) cleanFlags |= NSShiftKeyMask; - return [GTMHotKey hotKeyWithKeyCode:keycode - modifiers:cleanFlags - useDoubledModifier:NO]; -} - -@end |