aboutsummaryrefslogtreecommitdiff
path: root/AppKit
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2010-09-21 17:38:37 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2010-09-21 17:38:37 +0000
commit6d304bcfdf6e57f560fcb1f7b06802bc2d7f5b8a (patch)
tree749f7f449c82b1bbfa4e8293d9f94f74ba962b12 /AppKit
parent28ef947c2b07cb24cef5c3ae14c0e1974e60bf92 (diff)
[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)
Diffstat (limited to 'AppKit')
-rw-r--r--AppKit/GTMHotKeyTextField.h86
-rw-r--r--AppKit/GTMHotKeyTextField.m455
-rw-r--r--AppKit/GTMHotKeyTextFieldTest.m382
-rw-r--r--AppKit/GTMHotKeyTextFieldTest.xib42
4 files changed, 348 insertions, 617 deletions
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 <Cocoa/Cocoa.h>
#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 <NSCopying> {
+ @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 <Carbon/Carbon.h>
-#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 <Carbon/Carbon.h>
-@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 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.03">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">9G55</string>
- <string key="IBDocument.InterfaceBuilderVersion">677</string>
- <string key="IBDocument.AppKitVersion">949.43</string>
+ <string key="IBDocument.SystemVersion">9L31a</string>
+ <string key="IBDocument.InterfaceBuilderVersion">680</string>
+ <string key="IBDocument.AppKitVersion">949.54</string>
<string key="IBDocument.HIToolboxVersion">353.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
- <integer value="1"/>
+ <integer value="4"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -217,6 +217,7 @@
<string>2.IBPluginDependency</string>
<string>4.CustomClassName</string>
<string>4.IBPluginDependency</string>
+ <string>5.CustomClassName</string>
<string>5.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
@@ -234,6 +235,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>GTMHotKeyTextField</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>GTMHotKeyTextFieldCell</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
@@ -265,12 +267,17 @@
<object class="IBPartialClassDescription">
<string key="className">GTMHotKeyTextField</string>
<string key="superclassName">NSTextField</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="554256206">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">AppKit/GTMHotKeyTextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">GTMHotKeyTextFieldCell</string>
+ <string key="superclassName">NSTextFieldCell</string>
+ <reference key="sourceIdentifier" ref="554256206"/>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">GTMHotKeyTextFieldTestController</string>
<string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="outlets">
@@ -295,6 +302,13 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">NSApplication</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">UnitTesting/GTMAppKitUnitTestingUtilities.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSCell</string>
<reference key="sourceIdentifier" ref="837783357"/>
</object>
@@ -324,13 +338,6 @@
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
- <string key="minorKey">Foundation/GTMHTTPServer.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSObject</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBProjectSource</string>
<string key="minorKey">Foundation/GTMNSAppleEventDescriptor+Foundation.h</string>
</object>
</object>
@@ -368,10 +375,21 @@
</object>
<object class="IBPartialClassDescription">
<string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier" id="54851293">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">AppKit/GTMNSAnimatablePropertyContainer.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
<reference key="sourceIdentifier" ref="837783357"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSWindow</string>
+ <reference key="sourceIdentifier" ref="54851293"/>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSWindow</string>
<reference key="sourceIdentifier" ref="837783357"/>
</object>
</object>