From 6d304bcfdf6e57f560fcb1f7b06802bc2d7f5b8a Mon Sep 17 00:00:00 2001 From: "gtm.daemon" Date: Tue, 21 Sep 2010 17:38:37 +0000 Subject: [Author: dmaclach] Revamped GTMHotKeyTextField so that it is far more of the standard control/cell architecture. Cells can now be used in tableviews. Binding is done correctly. You can now use the "delete" key to clear the hotkey field. Generally simplified the interface. R=thomasvl DELTA=750 (154 added, 423 deleted, 173 changed) --- AppKit/GTMHotKeyTextField.h | 86 ++++--- AppKit/GTMHotKeyTextField.m | 455 ++++++++++---------------------------- AppKit/GTMHotKeyTextFieldTest.m | 382 +++++++++++++------------------- AppKit/GTMHotKeyTextFieldTest.xib | 42 +++- 4 files changed, 348 insertions(+), 617 deletions(-) (limited to 'AppKit') diff --git a/AppKit/GTMHotKeyTextField.h b/AppKit/GTMHotKeyTextField.h index 4177f49..cd671f0 100644 --- a/AppKit/GTMHotKeyTextField.h +++ b/AppKit/GTMHotKeyTextField.h @@ -1,14 +1,14 @@ // // GTMHotKeyTextField.h // -// Copyright 2006-2008 Google Inc. +// 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 @@ -26,72 +26,62 @@ #import #import "GTMDefines.h" -// Dictionary key for hot key configuration information modifier flags. -// NSNumber of a unsigned int. Modifier flags are stored using Cocoa constants -// (same as NSEvent) you will need to translate them to Carbon modifier flags -// for use with RegisterEventHotKey() -#define kGTMHotKeyModifierFlagsKey @"Modifiers" +@interface GTMHotKey : NSObject { + @private + NSUInteger modifiers_; + NSUInteger keyCode_; + BOOL doubledModifier_; +} -// Dictionary key for hot key configuration of virtual key code. NSNumber of -// unsigned int. For double-modifier hotkeys (see below) this value is ignored. -#define kGTMHotKeyKeyCodeKey @"KeyCode" +// Modifier flags are stored using Cocoa constants (same as NSEvent) you will +// need to translate them to Carbon modifier flags for use with +// RegisterEventHotKey() +@property (readonly) NSUInteger modifiers; +// Virtual keycode. For double-modifier hotkeys this value is ignored. +@property (readonly) NSUInteger keyCode; +//Double-tap modifier keys cannot be used with RegisterEventHotKey(), you must +// implement your own Carbon event handler. +@property (readonly) BOOL doubledModifier; -// Dictionary key for hot key configuration of double-modifier tap. NSNumber -// BOOL value. Double-tap modifier keys cannot be used with -// RegisterEventHotKey(), you must implement your own Carbon event handler. -#define kGTMHotKeyDoubledModifierKey @"DoubleModifier" ++ (id)hotKeyWithKeyCode:(NSUInteger)keyCode + modifiers:(NSUInteger)modifiers + useDoubledModifier:(BOOL)doubledModifier; -// Custom text field class used for hot key entry. In order to use this class -// you will need to configure your window's delegate, to return the related -// field editor. -// -// Sample window delegate method: -// -// -(id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)anObject { -// -// if ([anObject isKindOfClass:[GTMHotKeyTextField class]]) { -// return [GTMHotKeyFieldEditor sharedHotKeyFieldEditor]; -// } else { -// return nil; // Window will use the AppKit shared editor -// } -// -// } -// -// -// Other notes: +- (id)initWithKeyCode:(NSUInteger)keyCode + modifiers:(NSUInteger)modifiers + useDoubledModifier:(BOOL)doubledModifier; +@end + +// Notes: // - Though you are free to implement control:textShouldEndEditing: in your // delegate its return is always ignored. The field always accepts only // one hotkey keystroke before editing ends. // - The "value" binding of this control is to the dictionary describing the -// hotkey. At this time binding options are not supported. +// hotkey. // - The field does not attempt to consume all hotkeys. Hotkeys which are // already bound in Apple prefs or other applications will have their // normal effect. // -@interface GTMHotKeyTextField : NSTextField { +@interface GTMHotKeyTextField : NSTextField +@end + +@interface GTMHotKeyTextFieldCell : NSTextFieldCell { @private - NSDictionary *hotKeyDict_; - // Bindings - NSObject *boundObject_; - NSString *boundKeyPath_; + GTMHotKey *hotKey_; } -// Set/Get the hot key dictionary for the field. See above for key names. -- (void)setHotKeyValue:(NSDictionary *)hotKey; -- (NSDictionary *)hotKeyValue; - // Convert Cocoa modifier flags (-[NSEvent modifierFlags]) into a string for // display. Modifiers are represented in the string in the same order they would // appear in the Menu Manager. // -// Args: +// Args: // flags: -[NSEvent modifierFlags] // // Returns: // Autoreleased NSString // -+ (NSString *)stringForModifierFlags:(unsigned int)flags; ++ (NSString *)stringForModifierFlags:(NSUInteger)flags; // Convert a keycode into a string that would result from typing the keycode in // the current keyboard layout. This may be one or more characters. @@ -107,7 +97,7 @@ // Returns: // Autoreleased NSString // -+ (NSString *)stringForKeycode:(UInt16)keycode ++ (NSString *)stringForKeycode:(UInt16)keycode useGlyph:(BOOL)useGlyph resourceBundle:(NSBundle *)bundle; @@ -118,9 +108,11 @@ // delegate. @interface GTMHotKeyFieldEditor : NSTextView { @private - NSDictionary *hotKeyDict_; // strong + GTMHotKeyTextFieldCell *cell_; } +@property (retain) GTMHotKeyTextFieldCell *cell; + // Get the shared field editor for all hot key fields + (GTMHotKeyFieldEditor *)sharedHotKeyFieldEditor; diff --git a/AppKit/GTMHotKeyTextField.m b/AppKit/GTMHotKeyTextField.m index afefeb4..27cd5b9 100644 --- a/AppKit/GTMHotKeyTextField.m +++ b/AppKit/GTMHotKeyTextField.m @@ -1,6 +1,6 @@ // GTMHotKeyTextField.m // -// Copyright 2006-2008 Google Inc. +// 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 @@ -18,204 +18,133 @@ #import "GTMHotKeyTextField.h" #import -#import "GTMSystemVersion.h" #import "GTMObjectSingleton.h" -#import "GTMNSObject+KeyValueObserving.h" -#import "GTMTypeCasting.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 GTMHotKeyTextField (PrivateMethods) +@interface GTMHotKeyTextFieldCell (PrivateMethods) - (void)setupBinding:(id)bound withPath:(NSString *)path; - (void)updateDisplayedPrettyString; -- (void)hotKeyValueChanged:(GTMKeyValueChangeNotification *)note; -+ (BOOL)isValidHotKey:(NSDictionary *)hotKey; -+ (NSString *)displayStringForHotKey:(NSDictionary *)hotKey; ++ (NSString *)displayStringForHotKey:(GTMHotKey *)hotKey; + (BOOL)doesKeyCodeRequireModifier:(UInt16)keycode; @end @interface GTMHotKeyFieldEditor (PrivateMethods) -- (NSDictionary *)hotKeyDictionary; -- (void)setHotKeyDictionary:(NSDictionary *)hotKey; +- (GTMHotKeyTextFieldCell *)cell; +- (void)setCell:(GTMHotKeyTextFieldCell *)cell; - (BOOL)shouldBypassEvent:(NSEvent *)theEvent; - (void)processEventToHotKeyAndString:(NSEvent *)theEvent; - (void)windowResigned:(NSNotification *)notification; -- (NSDictionary *)hotKeyDictionaryForEvent:(NSEvent *)event; +- (GTMHotKey *)hotKeyForEvent:(NSEvent *)event; @end -@implementation GTMHotKeyTextField +@implementation GTMHotKey -#if GTM_SUPPORT_GC -- (void)finalize { - if (boundObject_ && boundKeyPath_) { - [boundObject_ gtm_removeObserver:self - forKeyPath:boundKeyPath_ - selector:@selector(hotKeyValueChanged:)]; - } - [super finalize]; -} -#endif +@synthesize modifiers = modifiers_; +@synthesize keyCode = keyCode_; +@synthesize doubledModifier = doubledModifier_; -- (void)dealloc { - if (boundObject_ && boundKeyPath_) { - [boundObject_ gtm_removeObserver:self - forKeyPath:boundKeyPath_ - selector:@selector(hotKeyValueChanged:)]; - } - [boundObject_ release]; - [boundKeyPath_ release]; - [hotKeyDict_ release]; - [super dealloc]; ++ (id)hotKeyWithKeyCode:(NSUInteger)keyCode + modifiers:(NSUInteger)modifiers + useDoubledModifier:(BOOL)doubledModifier { + return [[[[self class] alloc] initWithKeyCode:keyCode + modifiers:modifiers + useDoubledModifier:doubledModifier] autorelease]; } -#pragma mark Bindings - - -- (void)bind:(NSString *)binding toObject:(id)observableController - withKeyPath:(NSString *)keyPath - options:(NSDictionary *)options { - if ([binding isEqualToString:NSValueBinding]) { - // Update to our new binding - [self setupBinding:observableController withPath:keyPath]; - // TODO: Should deal with the bind options - } - [super bind:binding - toObject:observableController - withKeyPath:keyPath - options:options]; -} - -- (void)unbind:(NSString *)binding { - // Clean up value on unbind - if ([binding isEqualToString:NSValueBinding]) { - if (boundObject_ && boundKeyPath_) { - [boundObject_ gtm_removeObserver:self - forKeyPath:boundKeyPath_ - selector:@selector(hotKeyValueChanged:)]; - } - [boundObject_ release]; - boundObject_ = nil; - [boundKeyPath_ release]; - boundKeyPath_ = nil; - } - [super unbind:binding]; -} - -- (void)hotKeyValueChanged:(GTMKeyValueChangeNotification *)note { - NSDictionary *change = [note change]; - // Our binding has changed, update - id changedValue = [change objectForKey:NSKeyValueChangeNewKey]; - // NSUserDefaultsController does not appear to pass on the new object and, - // perhaps other controllers may not, so if we get a nil or NSNull back - // here let's directly retrieve the hotKeyDict_ from the object. - if (!changedValue || changedValue == [NSNull null]) { - id object = [note object]; - NSString *keyPath = [note keyPath]; - changedValue = [object valueForKeyPath:keyPath]; +- (id)initWithKeyCode:(NSUInteger)keyCode + modifiers:(NSUInteger)modifiers + useDoubledModifier:(BOOL)doubledModifier { + if ((self = [super init])) { + modifiers_ = modifiers; + keyCode_ = keyCode; + doubledModifier_ = doubledModifier; } - [hotKeyDict_ autorelease]; - hotKeyDict_ = [changedValue copy]; - [self updateDisplayedPrettyString]; + return self; } - -// Private convenience method for attaching to a new binding -- (void)setupBinding:(id)bound withPath:(NSString *)path { - // Release previous - if (boundObject_ && boundKeyPath_) { - [boundObject_ gtm_removeObserver:self - forKeyPath:boundKeyPath_ - selector:@selector(hotKeyValueChanged:)]; - } - [boundObject_ release]; - [boundKeyPath_ release]; - // Set new - boundObject_ = [bound retain]; - boundKeyPath_ = [path copy]; - // Make ourself an observer - [boundObject_ gtm_addObserver:self - forKeyPath:boundKeyPath_ - selector:@selector(hotKeyValueChanged:) - userInfo:nil - options:NSKeyValueObservingOptionNew]; - // Pull in any current value - [hotKeyDict_ autorelease]; - hotKeyDict_ = [[boundObject_ valueForKeyPath:boundKeyPath_] copy]; - // Update the display string - [self updateDisplayedPrettyString]; +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[GTMHotKey class]] + && [object modifiers] == [self modifiers] + && [(GTMHotKey *)object keyCode] == [self keyCode] + && [object doubledModifier] == [self doubledModifier]; } -#pragma mark Defeating NSControl - -- (int)logBadValueAccess { - // Defeating NSControl - _GTMDevLog(@"Hot key fields don't take numbers."); - return 0; +- (NSUInteger)hash { + return [self modifiers] + [self keyCode] + [self doubledModifier]; } -- (void)logBadStringValueAccess { - // Defeating NSControl - _GTMDevLog(@"Hot key fields want dictionaries, not strings."); +- (id)copyWithZone:(NSZone *)zone { + return NSCopyObject(self, 0, zone); } +@end -- (double)doubleValue { - return [self logBadValueAccess]; -} - -- (void)setDoubleValue:(double)value { - [self logBadValueAccess]; -} +@implementation GTMHotKeyTextField -- (float)floatValue { - return [self logBadValueAccess]; ++ (Class)cellClass { + return [GTMHotKeyTextFieldCell class]; } -- (void)setFloatValue:(float)value { - [self logBadValueAccess]; -} +@end -- (int)intValue { - return [self logBadValueAccess]; +@implementation GTMHotKeyTextFieldCell +- (void)dealloc { + [hotKey_ release]; + [super dealloc]; } -- (void)setIntValue:(int)value { - [self logBadValueAccess]; +- (id)copyWithZone:(NSZone *)zone { + GTMHotKeyTextFieldCell *copy = [super copyWithZone:zone]; + copy->hotKey_ = nil; + [copy setObjectValue:[self objectValue]]; + return copy; } -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 - -- (NSInteger)integerValue { - return [self logBadValueAccess]; -} +#pragma mark Defeating NSCell -- (void)setIntegerValue:(NSInteger)value { - [self logBadValueAccess]; +- (void)logBadValueAccess { + _GTMDevLog(@"Hot key fields want hot key dictionaries as object values."); } -#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 - - (id)objectValue { - return [self hotKeyValue]; + return hotKey_; } - (void)setObjectValue:(id)object { - [self setHotKeyValue: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 { - return [[self class] displayStringForHotKey:hotKeyDict_]; + return [[self class] displayStringForHotKey:hotKey_]; } - (void)setStringValue:(NSString *)string { - [self logBadStringValueAccess]; + // 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 { @@ -229,112 +158,55 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; } - (void)setAttributedStringValue:(NSAttributedString *)string { - [self logBadStringValueAccess]; -} - -- (void)takeDoubleValueFrom:(id)sender { [self logBadValueAccess]; } -- (void)takeFloatValueFrom:(id)sender { - [self logBadValueAccess]; -} - -- (void)takeIntValueFrom:(id)sender { - [self logBadValueAccess]; -} - -- (void)takeObjectValueFrom:(id)sender { - // Defeating NSControl - _GTMDevLog(@"Hot key fields want dictionaries via bindings, " - @"not from controls."); -} - -- (void)takeStringValueFrom:(id)sender { - [self logBadStringValueAccess]; -} - - (id)formatter { return nil; } - (void)setFormatter:(NSFormatter *)newFormatter { - // Defeating NSControl - _GTMDevLog(@"Hot key fields don't accept formatters."); -} - -#pragma mark Hot Key Support - -+ (BOOL)isValidHotKey:(NSDictionary *)hotKeyDict { - if (!hotKeyDict || - ![hotKeyDict isKindOfClass:[NSDictionary class]] || - ![hotKeyDict objectForKey:kGTMHotKeyModifierFlagsKey] || - ![hotKeyDict objectForKey:kGTMHotKeyKeyCodeKey] || - ![hotKeyDict objectForKey:kGTMHotKeyDoubledModifierKey]) { - return NO; + if (newFormatter) { + // Defeating NSCell + _GTMDevLog(@"Hot key fields don't accept formatters."); } - return YES; } -- (void)setHotKeyValue:(NSDictionary *)hotKey { - // Sanity only if set, nil is OK - if (hotKey && ![[self class] isValidHotKey:hotKey]) { - return; - } - - // If we are bound we want to round trip through that interface - if (boundObject_ && boundKeyPath_) { - // If the change is accepted this will call us back as an observer - [boundObject_ setValue:hotKey forKeyPath:boundKeyPath_]; - } else { - // Otherwise we directly update ourself - [hotKeyDict_ autorelease]; - hotKeyDict_ = [hotKey copy]; - [self updateDisplayedPrettyString]; - } +- (id)_fieldEditor { + GTMHotKeyFieldEditor *editor = [GTMHotKeyFieldEditor sharedHotKeyFieldEditor]; + [editor setCell:self]; + return editor; } -- (NSDictionary *)hotKeyValue { - return hotKeyDict_; -} +#pragma mark Hot Key Support // Private method to update the displayed text of the field with the // user-readable representation. - (void)updateDisplayedPrettyString { - // Basic validation - if (![[self class] isValidHotKey:hotKeyDict_]) { - [super setStringValue:@""]; - return; - } - // Pretty string - NSString *prettyString = [[self class] displayStringForHotKey:hotKeyDict_]; + NSString *prettyString = [[self class] displayStringForHotKey:hotKey_]; if (!prettyString) { prettyString = @""; } - [super setStringValue:prettyString]; + [super setObjectValue:prettyString]; } -+ (NSString *)displayStringForHotKey:(NSDictionary *)hotKeyDict { - if (!hotKeyDict) return nil; ++ (NSString *)displayStringForHotKey:(GTMHotKey *)hotKey { + if (!hotKey) return nil; NSBundle *bundle = [NSBundle bundleForClass:[self class]]; // Modifiers - unsigned int flags - = [[hotKeyDict objectForKey:kGTMHotKeyModifierFlagsKey] unsignedIntValue]; - NSString *mods = [[self class] stringForModifierFlags:flags]; - if (flags && ![mods length]) return nil; + NSUInteger modifiers = [hotKey modifiers]; + NSString *mods = [[self class] stringForModifierFlags:modifiers]; + if (modifiers && ![mods length]) return nil; // Handle double modifier case - if ([[hotKeyDict objectForKey:kGTMHotKeyDoubledModifierKey] boolValue]) { + if ([hotKey doubledModifier]) { return [NSString stringWithFormat:@"%@ + %@", mods, mods]; } // Keycode - NSNumber *keyCodeNumber = [hotKeyDict objectForKey:kGTMHotKeyKeyCodeKey]; - if (!keyCodeNumber) return nil; - unsigned int keycode - = [keyCodeNumber unsignedIntValue]; + NSUInteger keycode = [hotKey keyCode]; NSString *keystroke = [[self class] stringForKeycode:keycode useGlyph:NO resourceBundle:bundle]; @@ -347,70 +219,6 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; return [NSString stringWithFormat:@"%@%@", mods, keystroke]; } - -#pragma mark Field Editor Callbacks - -- (BOOL)textShouldBeginEditing:(GTMHotKeyFieldEditor *)fieldEditor { - // Sanity - if (![fieldEditor isKindOfClass:[GTMHotKeyFieldEditor class]]) { - _GTMDevLog(@"Field editor not appropriate for field, check window delegate"); - return NO; - } - - // We don't call super from here, because we are defeating default behavior - // as a result we have to call the delegate ourself. - id myDelegate = [self delegate]; - SEL selector = @selector(control:textShouldBeginEditing:); - if ([myDelegate respondsToSelector:selector]) { - if (![myDelegate control:self textShouldBeginEditing:fieldEditor]) return NO; - } - - // Update the field editor internal hotkey representation - [fieldEditor setHotKeyDictionary:hotKeyDict_]; // OK if its nil - return YES; -} - -- (void)textDidChange:(NSNotification *)notification { - // Sanity - GTMHotKeyFieldEditor *fieldEditor = GTM_STATIC_CAST(GTMHotKeyFieldEditor, - [notification object]); - if (![fieldEditor isKindOfClass:[GTMHotKeyFieldEditor class]]) { - _GTMDevLog(@"Field editor not appropriate for field, check window delegate"); - return; - } - - // When the field changes we want to read in the current hotkey value so - // bindings can validate - [self setHotKeyValue:[fieldEditor hotKeyDictionary]]; - - // Let super handle the notifications - [super textDidChange:notification]; -} - -- (BOOL)textShouldEndEditing:(GTMHotKeyFieldEditor *)fieldEditor { - // Sanity - if (![fieldEditor isKindOfClass:[GTMHotKeyFieldEditor class]]) { - _GTMDevLog(@"Field editor not appropriate for field, check window delegate"); - return NO; - } - - // Again we are defeating default behavior so we have to do delegate handling - // ourself. In this case our goal is simply to prevent the superclass from - // doing its own KVO, but we can also skip [[self cell] isEntryAcceptable:]. - // We'll also ignore the delegate control:textShouldEndEditing:. The field - // editor is done whether they like it or not. - id myDelegate = [self delegate]; - SEL selector = @selector(control:textShouldEndEditing:); - if ([myDelegate respondsToSelector:selector]) { - [myDelegate control:self textShouldEndEditing:fieldEditor]; - } - - // The end is always allowed, so set new value - [self setHotKeyValue:[fieldEditor hotKeyDictionary]]; - - return YES; -} - #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 @@ -473,7 +281,7 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; // #define the class name to something else, and then you won't have any // conflicts. -+ (NSString *)stringForModifierFlags:(unsigned int)flags { ++ (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 @@ -706,9 +514,10 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; [str1 uppercaseString], [str2 uppercaseString]]; } else { - keystrokeString = [[[NSString alloc] initWithBytes:&secondChar - length:1 - encoding:NSMacOSRomanStringEncoding] autorelease]; + keystrokeString + = [[[NSString alloc] initWithBytes:&secondChar + length:1 + encoding:NSMacOSRomanStringEncoding] autorelease]; [keystrokeString uppercaseString]; } } @@ -745,6 +554,8 @@ static CFStringRef kGTM_TISPropertyUnicodeKeyLayoutData = NULL; GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) +@synthesize cell = cell_; + - (id)init { if ((self = [super init])) { [self setFieldEditor:YES]; // We are a field editor @@ -755,7 +566,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) // COV_NF_START // Singleton so never called. - (void)dealloc { - [hotKeyDict_ release]; + [cell_ release]; [super dealloc]; } // COV_NF_END @@ -863,15 +674,23 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) // hotkey plumbing. - (void)processEventToHotKeyAndString:(NSEvent *)theEvent { // Construct a dictionary of the event as a hotkey pref - NSDictionary *newHotKey = [self hotKeyDictionaryForEvent:theEvent]; - if (!newHotKey) { - NSBeep(); - return; // No action, but don't give up focus - } - NSString *prettyString = [GTMHotKeyTextField displayStringForHotKey:newHotKey]; - if (!prettyString) { - NSBeep(); - return; + 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 @@ -885,12 +704,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) return; } - // Replacement was allowed, update - [hotKeyDict_ autorelease]; - hotKeyDict_ = [newHotKey retain]; - - // Set string on self, allowing super to handle attribute copying - [self setString:prettyString]; + [[self cell] setObjectValue:newHotKey]; // Finish the change [self didChangeText]; @@ -898,31 +712,10 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) // 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. - id delegate = [self delegate]; - if ([delegate respondsToSelector:@selector(cell)]) { - [[delegate cell] endEditing:self]; - } -} - -- (NSDictionary *)hotKeyDictionary { - return hotKeyDict_; + [[self window] makeFirstResponder:nil]; } -- (void)setHotKeyDictionary:(NSDictionary *)hotKey { - [hotKeyDict_ autorelease]; - hotKeyDict_ = [hotKey copy]; - // Update content - NSString *prettyString = nil; - if (hotKeyDict_) { - prettyString = [GTMHotKeyTextField displayStringForHotKey:hotKey]; - } - if (!prettyString) { - prettyString = @""; - } - [self setString:prettyString]; -} - -- (NSDictionary *)hotKeyDictionaryForEvent:(NSEvent *)event { +- (GTMHotKey *)hotKeyForEvent:(NSEvent *)event { if (!event) return nil; // Check event @@ -933,7 +726,7 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) NSControlKeyMask | NSShiftKeyMask); BOOL requiresModifiers - = [GTMHotKeyTextField doesKeyCodeRequireModifier:keycode]; + = [GTMHotKeyTextFieldCell doesKeyCodeRequireModifier:keycode]; if (requiresModifiers) { // If we aren't a function key, and have no modifiers do nothing. if (!(flags & allModifiers)) return nil; @@ -947,15 +740,9 @@ GTMOBJECT_SINGLETON_BOILERPLATE(GTMHotKeyFieldEditor, sharedHotKeyFieldEditor) if (flags & NSAlternateKeyMask) cleanFlags |= NSAlternateKeyMask; if (flags & NSControlKeyMask) cleanFlags |= NSControlKeyMask; if (flags & NSShiftKeyMask) cleanFlags |= NSShiftKeyMask; - - return [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:keycode], - kGTMHotKeyKeyCodeKey, - [NSNumber numberWithUnsignedInt:cleanFlags], - kGTMHotKeyModifierFlagsKey, - nil]; + return [GTMHotKey hotKeyWithKeyCode:keycode + modifiers:cleanFlags + useDoubledModifier:NO]; } @end diff --git a/AppKit/GTMHotKeyTextFieldTest.m b/AppKit/GTMHotKeyTextFieldTest.m index e48ee94..59c46f8 100644 --- a/AppKit/GTMHotKeyTextFieldTest.m +++ b/AppKit/GTMHotKeyTextFieldTest.m @@ -1,13 +1,13 @@ // GTMHotKeyTextFieldTest.m // -// Copyright 2006-2008 Google Inc. +// 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 @@ -21,32 +21,28 @@ #import "GTMUnitTestDevLog.h" #import -@interface GTMHotKeyTextField (PrivateMethods) +@interface GTMHotKeyTextFieldCell (PrivateMethods) // Private methods which we want to access to test -+ (BOOL)isValidHotKey:(NSDictionary *)hotKey; -+ (NSString *)displayStringForHotKey:(NSDictionary *)hotKey; ++ (NSString *)displayStringForHotKey:(GTMHotKey *)hotKey; @end @interface GTMHotKeyTextFieldTest : GTMTestCase { @private GTMHotKeyTextFieldTestController *controller_; - NSMutableDictionary *hotkeyValues_; + GTMHotKey *hotKey_; } -- (NSDictionary *)hotkeyValues; -- (void)setHotkeyValues:(NSDictionary*)values; +- (GTMHotKey *)hotKey; +- (void)setHotKey:(GTMHotKey*)hotkey; @end @implementation GTMHotKeyTextFieldTest - (void)setUp { controller_ = [[GTMHotKeyTextFieldTestController alloc] init]; - hotkeyValues_ - = [[NSMutableDictionary alloc] initWithObjectsAndKeys: - [NSNumber numberWithInt:NSCommandKeyMask], kGTMHotKeyModifierFlagsKey, - [NSNumber numberWithInt:42], kGTMHotKeyKeyCodeKey, - [NSNumber numberWithBool:NO], kGTMHotKeyDoubledModifierKey, - nil]; - STAssertNotNil(hotkeyValues_, nil); + hotKey_ = [[GTMHotKey alloc] initWithKeyCode:42 + modifiers:NSCommandKeyMask + useDoubledModifier:NO]; + STAssertNotNil(hotKey_, nil); STAssertNotNil(controller_, nil); STAssertNotNil([controller_ window], nil); } @@ -54,62 +50,62 @@ - (void)tearDown { [controller_ close]; [controller_ release]; - [hotkeyValues_ release]; + [hotKey_ release]; } -- (NSDictionary *)hotkeyValues { - return hotkeyValues_; +- (GTMHotKey *)hotKey { + return hotKey_; } -- (void)setHotkeyValues:(NSDictionary*)values { - [hotkeyValues_ autorelease]; - hotkeyValues_ = [values mutableCopy]; +- (void)setHotKey:(GTMHotKey*)hotKey { + [hotKey_ autorelease]; + hotKey_ = [hotKey retain]; } - (void)testStringForModifierFlags { - + // Make sure only the flags we expect generate things in their strings - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSAlphaShiftKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSAlphaShiftKeyMask] length], (NSUInteger)0, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSShiftKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSShiftKeyMask] length], (NSUInteger)1, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSControlKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSControlKeyMask] length], (NSUInteger)1, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSAlternateKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSAlternateKeyMask] length], (NSUInteger)1, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSCommandKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSCommandKeyMask] length], (NSUInteger)1, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSNumericPadKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSNumericPadKeyMask] length], (NSUInteger)0, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSHelpKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSHelpKeyMask] length], (NSUInteger)0, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:NSFunctionKeyMask] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:NSFunctionKeyMask] length], (NSUInteger)0, nil); - + // And some quick checks combining flags to make sure the string gets longer - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:(NSShiftKeyMask | - NSAlternateKeyMask)] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:(NSShiftKeyMask | + NSAlternateKeyMask)] length], (NSUInteger)2, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:(NSShiftKeyMask | - NSAlternateKeyMask | - NSCommandKeyMask)] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:(NSShiftKeyMask | + NSAlternateKeyMask | + NSCommandKeyMask)] length], (NSUInteger)3, nil); - STAssertEquals([[GTMHotKeyTextField stringForModifierFlags:(NSShiftKeyMask | - NSAlternateKeyMask | - NSCommandKeyMask | - NSControlKeyMask)] length], + STAssertEquals([[GTMHotKeyTextFieldCell stringForModifierFlags:(NSShiftKeyMask | + NSAlternateKeyMask | + NSCommandKeyMask | + NSControlKeyMask)] length], (NSUInteger)4, nil); - + } - (void)testStringForKeycode_useGlyph_resourceBundle { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; STAssertNotNil(bundle, @"failed to get our bundle?"); NSString *str; - + // We need a better test, but for now, we'll just loop through things we know // we handle. - + // TODO: we need to force the pre leopard code path during tests. UInt16 testData[] = { @@ -120,10 +116,10 @@ for (int useGlyph = 0 ; useGlyph < 2 ; ++useGlyph) { for (size_t i = 0; i < (sizeof(testData) / sizeof(UInt16)); ++i) { UInt16 keycode = testData[i]; - - str = [GTMHotKeyTextField stringForKeycode:keycode - useGlyph:useGlyph - resourceBundle:bundle]; + + str = [GTMHotKeyTextFieldCell stringForKeycode:keycode + useGlyph:useGlyph + resourceBundle:bundle]; STAssertNotNil(str, @"failed to get a string for keycode %u (useGlyph:%@)", keycode, (useGlyph ? @"YES" : @"NO")); @@ -135,117 +131,29 @@ } - (void)testGTMHotKeyPrettyString { - NSDictionary *hkDict; - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:114], - kGTMHotKeyKeyCodeKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertNotNil([GTMHotKeyTextField displayStringForHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithUnsignedInt:114], - kGTMHotKeyKeyCodeKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertNotNil([GTMHotKeyTextField displayStringForHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertNil([GTMHotKeyTextField displayStringForHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:'A'], - kGTMHotKeyKeyCodeKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertNil([GTMHotKeyTextField displayStringForHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionary]; - STAssertNotNil(hkDict, nil); - STAssertNil([GTMHotKeyTextField displayStringForHotKey:hkDict], nil); - - STAssertNil([GTMHotKeyTextField displayStringForHotKey:nil], nil); - -} + GTMHotKey *hotKey = [GTMHotKey hotKeyWithKeyCode:114 + modifiers:NSCommandKeyMask + useDoubledModifier:NO]; + STAssertNotNil(hotKey, nil); + STAssertNotNil([GTMHotKeyTextFieldCell displayStringForHotKey:hotKey], nil); + + hotKey = [GTMHotKey hotKeyWithKeyCode:1 + modifiers:0 + useDoubledModifier:NO]; -- (void)testGTMHotKeyDictionaryAppearsValid { - NSDictionary *hkDict; - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:114], - kGTMHotKeyKeyCodeKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertTrue([GTMHotKeyTextField isValidHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithUnsignedInt:114], - kGTMHotKeyKeyCodeKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertFalse([GTMHotKeyTextField isValidHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:NSCommandKeyMask], - kGTMHotKeyModifierFlagsKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertFalse([GTMHotKeyTextField isValidHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], - kGTMHotKeyDoubledModifierKey, - [NSNumber numberWithUnsignedInt:114], - kGTMHotKeyKeyCodeKey, - nil]; - STAssertNotNil(hkDict, nil); - STAssertFalse([GTMHotKeyTextField isValidHotKey:hkDict], nil); - - hkDict = [NSDictionary dictionary]; - STAssertNotNil(hkDict, nil); - STAssertFalse([GTMHotKeyTextField isValidHotKey:hkDict], nil); - - STAssertFalse([GTMHotKeyTextField isValidHotKey:nil], nil); - - // Make sure it doesn't choke w/ an object of the wrong time (since the dicts - // have to be saved/reloaded. - hkDict = (id)[NSString string]; - STAssertNotNil(hkDict, nil); - STAssertFalse([GTMHotKeyTextField isValidHotKey:hkDict], nil); + STAssertNotNil(hotKey, nil); + STAssertNil([GTMHotKeyTextFieldCell displayStringForHotKey:hotKey], nil); } - (void)testFieldEditorSettersAndGetters { NSWindow *window = [controller_ window]; GTMHotKeyTextField *field = [controller_ view]; STAssertNotNil(field, nil); - GTMHotKeyFieldEditor *editor + GTMHotKeyFieldEditor *editor = (GTMHotKeyFieldEditor *)[window fieldEditor:YES forObject:field]; STAssertTrue([editor isMemberOfClass:[GTMHotKeyFieldEditor class]], nil); - STAssertEqualObjects(editor, - [GTMHotKeyFieldEditor sharedHotKeyFieldEditor], + STAssertEqualObjects(editor, + [GTMHotKeyFieldEditor sharedHotKeyFieldEditor], nil); SEL selectors[] = { @@ -256,7 +164,7 @@ for (size_t i = 0; i < sizeof(selectors) / sizeof(selectors[0]); ++i) { NSArray *array = [editor performSelector:selectors[i]]; STAssertNotNil(array, nil); - STAssertEquals([array count], (NSUInteger)0, + STAssertEquals([array count], (NSUInteger)0, @"Failed Selector: %@", NSStringFromSelector(selectors[i])); } } @@ -264,8 +172,9 @@ - (void)testTextFieldSettersAndGetters { GTMHotKeyTextField *field = [controller_ view]; STAssertNotNil(field, nil); - NSString *expectedNumberString = @"Hot key fields don't take numbers."; - [GTMUnitTestDevLog expect:6 casesOfString:@"%@", expectedNumberString]; + NSString *expectedString + = @"Hot key fields want hot key dictionaries as object values."; + [GTMUnitTestDevLog expect:3 casesOfString:@"%@", expectedString]; [field setDoubleValue:2]; [field setIntValue:-1]; [field setFloatValue:0]; @@ -273,7 +182,7 @@ STAssertEquals([field intValue], 0, nil); STAssertEquals([field floatValue], 0.0f, nil); #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 - [GTMUnitTestDevLog expect:2 casesOfString:@"%@", expectedNumberString]; + [GTMUnitTestDevLog expectString:expectedString]; [field setIntegerValue:5]; STAssertEquals([field integerValue], (NSInteger)0, nil); #endif @@ -283,61 +192,62 @@ @selector(takeFloatValueFrom:), @selector(takeIntValueFrom:) }; - for (size_t i = 0; + for (size_t i = 0; i < sizeof(takeNumberSels) / sizeof(takeNumberSels[0]); ++i) { - [GTMUnitTestDevLog expect:2 casesOfString:@"%@", expectedNumberString]; + [GTMUnitTestDevLog expect:2 casesOfString:@"%@", expectedString]; [field performSelector:takeNumberSels[i] withObject:self]; [field performSelector:takeNumberSels[i] withObject:nil]; } - NSString *expectedStringString - = @"Hot key fields want dictionaries, not strings."; - [GTMUnitTestDevLog expect:6 casesOfString:@"%@", expectedStringString]; + [GTMUnitTestDevLog expect:6 casesOfString:@"%@", expectedString]; [field takeStringValueFrom:self]; [field takeStringValueFrom:nil]; [field setStringValue:nil]; [field setStringValue:@"foo"]; - - NSAttributedString *attrString + + NSAttributedString *attrString = [[[NSAttributedString alloc] initWithString:@"foo"] autorelease]; [field setAttributedStringValue:nil]; [field setAttributedStringValue:attrString]; - + STAssertNil([field formatter], nil); - [GTMUnitTestDevLog expectString:@"Hot key fields don't accept formatters."]; [field setFormatter:nil]; - - [GTMUnitTestDevLog expect:2 casesOfString: - @"Hot key fields want dictionaries via bindings, not from controls."]; + NSFormatter *testFormatter = [[[NSFormatter alloc] init] autorelease]; + [GTMUnitTestDevLog expectString:@"Hot key fields don't accept formatters."]; + [field setFormatter:testFormatter]; + + [GTMUnitTestDevLog expectString:expectedString]; [field takeObjectValueFrom:self]; + + // Setting to nil is valid. [field takeObjectValueFrom:nil]; } -- (void)pressKey:(NSString *)key code:(NSInteger)code +- (void)pressKey:(NSString *)key code:(NSInteger)code modifierFlags:(NSInteger)flags window:(NSWindow *)window { NSInteger windNum = [window windowNumber]; NSGraphicsContext *context = [NSGraphicsContext currentContext]; EventTime evtTime = GetCurrentEventTime(); NSPoint loc = [NSEvent mouseLocation]; - NSEvent *keyDownEvt = [NSEvent keyEventWithType:NSKeyDown - location:loc - modifierFlags:flags + NSEvent *keyDownEvt = [NSEvent keyEventWithType:NSKeyDown + location:loc + modifierFlags:flags timestamp:evtTime - windowNumber:windNum - context:context - characters:key - charactersIgnoringModifiers:key - isARepeat:NO + windowNumber:windNum + context:context + characters:key + charactersIgnoringModifiers:key + isARepeat:NO keyCode:code]; - NSEvent *keyUpEvt = [NSEvent keyEventWithType:NSKeyUp - location:loc + NSEvent *keyUpEvt = [NSEvent keyEventWithType:NSKeyUp + location:loc modifierFlags:flags timestamp:evtTime - windowNumber:windNum - context:context - characters:key - charactersIgnoringModifiers:key - isARepeat:NO + windowNumber:windNum + context:context + characters:key + charactersIgnoringModifiers:key + isARepeat:NO keyCode:code]; STAssertNotNil(keyDownEvt, nil); STAssertNotNil(keyUpEvt, nil); @@ -346,107 +256,131 @@ } - (void)testTextFieldBindings { - NSObjectController *controller + NSObjectController *controller = [[[NSObjectController alloc] init] autorelease]; - [controller bind:NSContentBinding - toObject:self - withKeyPath:@"self" + [controller setObjectClass:[self class]]; + [controller bind:NSContentBinding + toObject:self + withKeyPath:@"self" options:nil]; STAssertNotNil(controller, nil); GTMHotKeyTextField *field = [controller_ view]; STAssertNotNil(field, nil); - [field bind:NSValueBinding - toObject:controller - withKeyPath:@"selection.hotkeyValues" + [field bind:NSValueBinding + toObject:controller + withKeyPath:@"selection.hotKey" options:nil]; id value = [field objectValue]; - STAssertEqualObjects(value, hotkeyValues_, nil); + STAssertEqualObjects(value, hotKey_, nil); NSString *stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌘\\", nil); NSAttributedString *attrStringValue = [field attributedStringValue]; STAssertEqualObjects([attrStringValue string], stringValue, nil); - // Try changing some values - [self willChangeValueForKey:@"hotkeyValues"]; - [hotkeyValues_ setObject:[NSNumber numberWithInt:43] - forKey:kGTMHotKeyKeyCodeKey]; - [self didChangeValueForKey:@"hotkeyValues"]; + GTMHotKey *newHotKey = [GTMHotKey hotKeyWithKeyCode:43 + modifiers:NSCommandKeyMask + useDoubledModifier:NO]; + [self setHotKey:newHotKey]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌘,", nil); - + // Now try some typing NSWindow *window = [controller_ window]; STAssertTrue([window makeFirstResponder:field], nil); [self pressKey:@"A" code:0 modifierFlags:NSShiftKeyMask window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⇧A", nil); - + // field is supposed to give up first responder when editing is done. STAssertNotEqualObjects([window firstResponder], field, nil); - + // Do NOT attempt to set the key via pressKey to the same cmd-key combo // as a menu item. This works fine on Leopard, but fails on Tiger (and fails // on Leopard if you have linked to the Tiger libs). I hope control-shift-opt // J won't be used in our simple test app. STAssertTrue([window makeFirstResponder:field], nil); - [self pressKey:@"J" - code:38 - modifierFlags:NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask + int modifiers = NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask; + [self pressKey:@"J" + code:38 + modifierFlags:modifiers window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌃⌥⇧J", nil); - + GTMHotKey *cmdJHotKey = [GTMHotKey hotKeyWithKeyCode:38 + modifiers:modifiers + useDoubledModifier:NO]; + STAssertEqualObjects(cmdJHotKey, hotKey_, nil); + // Try without a modifier. This should fail. STAssertTrue([window makeFirstResponder:field], nil); [self pressKey:@"j" code:38 modifierFlags:0 window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌃⌥⇧J", nil); - + // Try cmd-q this should fail STAssertTrue([window makeFirstResponder:field], nil); [self pressKey:@"Q" code:12 modifierFlags:NSCommandKeyMask window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌃⌥⇧J", nil); - + // Try cmd-w this should fail STAssertTrue([window makeFirstResponder:field], nil); [self pressKey:@"W" code:13 modifierFlags:NSCommandKeyMask window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌃⌥⇧J", nil); - + // Try cmd-tab this should fail STAssertTrue([window makeFirstResponder:field], nil); [self pressKey:@"\t" code:48 modifierFlags:NSCommandKeyMask window:window]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌃⌥⇧J", nil); - - // Do it by dictionary - NSDictionary *cmdSHotKey - = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:NSCommandKeyMask], kGTMHotKeyModifierFlagsKey, - [NSNumber numberWithInt:1], kGTMHotKeyKeyCodeKey, - [NSNumber numberWithBool:NO], kGTMHotKeyDoubledModifierKey, - nil]; - [field setObjectValue:cmdSHotKey]; + + // Do it by hotkey + GTMHotKey *cmdSHotKey = [GTMHotKey hotKeyWithKeyCode:1 + modifiers:NSCommandKeyMask + useDoubledModifier:NO]; + [controller setValue:cmdSHotKey forKeyPath:@"selection.hotKey"]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌘S", nil); - + // Check to make sure the binding stuck - STAssertEqualObjects(cmdSHotKey, hotkeyValues_, nil); - + STAssertEqualObjects(cmdSHotKey, hotKey_, nil); + + // Check to make sure that programatic edits don't modify our model + [field setObjectValue:cmdJHotKey]; + STAssertEqualObjects(cmdSHotKey, hotKey_, nil); + [field unbind:NSValueBinding]; [controller unbind:NSContentBinding]; - - NSDictionary *cmdDHotKey - = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:NSCommandKeyMask], kGTMHotKeyModifierFlagsKey, - [NSNumber numberWithInt:2], kGTMHotKeyKeyCodeKey, - [NSNumber numberWithBool:NO], kGTMHotKeyDoubledModifierKey, - nil]; + + GTMHotKey *cmdDHotKey = [GTMHotKey hotKeyWithKeyCode:2 + modifiers:NSCommandKeyMask + useDoubledModifier:NO]; [field setObjectValue:cmdDHotKey]; stringValue = [field stringValue]; STAssertEqualObjects(stringValue, @"⌘D", nil); } + +- (int)intValue { + return 2; +} + +- (float)floatValue { + return [self intValue]; +} + +- (double)doubleValue { + return [self floatValue]; +} + +- (NSString *)stringValue { + return @"GTMHotKeyTextFieldTestStringValue"; +} + +- (id)objectValue { + return self; +} + @end @implementation GTMHotKeyTextFieldTestController diff --git a/AppKit/GTMHotKeyTextFieldTest.xib b/AppKit/GTMHotKeyTextFieldTest.xib index c95b39d..c4d0726 100644 --- a/AppKit/GTMHotKeyTextFieldTest.xib +++ b/AppKit/GTMHotKeyTextFieldTest.xib @@ -2,13 +2,13 @@ 1050 - 9G55 - 677 - 949.43 + 9L31a + 680 + 949.54 353.00 YES - + YES @@ -217,6 +217,7 @@ 2.IBPluginDependency 4.CustomClassName 4.IBPluginDependency + 5.CustomClassName 5.IBPluginDependency @@ -234,6 +235,7 @@ com.apple.InterfaceBuilder.CocoaPlugin GTMHotKeyTextField com.apple.InterfaceBuilder.CocoaPlugin + GTMHotKeyTextFieldCell com.apple.InterfaceBuilder.CocoaPlugin @@ -265,11 +267,16 @@ GTMHotKeyTextField NSTextField - + IBProjectSource AppKit/GTMHotKeyTextField.h + + GTMHotKeyTextFieldCell + NSTextFieldCell + + GTMHotKeyTextFieldTestController NSWindowController @@ -294,6 +301,13 @@ UnitTesting/GTMAppKit+UnitTesting.h + + NSApplication + + IBProjectSource + UnitTesting/GTMAppKitUnitTestingUtilities.h + + NSCell @@ -320,13 +334,6 @@ AppKit/GTMDelegatingTableColumn.h - - NSObject - - IBProjectSource - Foundation/GTMHTTPServer.h - - NSObject @@ -366,10 +373,21 @@ NSTextField + + NSView + + IBProjectSource + AppKit/GTMNSAnimatablePropertyContainer.h + + NSView + + NSWindow + + NSWindow -- cgit v1.2.3