aboutsummaryrefslogtreecommitdiff
path: root/AppKit/GTMHotKeyTextField.m
diff options
context:
space:
mode:
Diffstat (limited to 'AppKit/GTMHotKeyTextField.m')
-rw-r--r--AppKit/GTMHotKeyTextField.m781
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(&currentLayout);
- 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 **)&currentLayoutKind);
- 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