Flycut/ShortcutRecorder/SRRecorderControl.m
2011-06-10 00:30:07 +08:00

386 lines
9 KiB
Objective-C

//
// SRRecorderControl.m
// ShortcutRecorder
//
// Copyright 2006-2007 Contributors. All rights reserved.
//
// License: BSD
//
// Contributors:
// David Dauer
// Jesper
// Jamie Kirkpatrick
#import "SRRecorderControl.h"
#import "SRCommon.h"
#define SRCell (SRRecorderCell *)[self cell]
@interface SRRecorderControl (Private)
- (void)resetTrackingRects;
@end
@implementation SRRecorderControl
+ (void)initialize
{
if (self == [SRRecorderControl class])
{
[self setCellClass: [SRRecorderCell class]];
}
}
+ (Class)cellClass
{
return [SRRecorderCell class];
}
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame: frameRect];
[SRCell setDelegate: self];
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder: aDecoder];
[SRCell setDelegate: self];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[super encodeWithCoder: aCoder];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#pragma mark *** Cell Behavior ***
// We need keyboard access
- (BOOL)acceptsFirstResponder
{
return YES;
}
// Allow the control to be activated with the first click on it even if it's window isn't the key window
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
return YES;
}
- (BOOL) becomeFirstResponder
{
BOOL okToChange = [SRCell becomeFirstResponder];
if (okToChange) [super setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
return okToChange;
}
- (BOOL) resignFirstResponder
{
BOOL okToChange = [SRCell resignFirstResponder];
if (okToChange) [super setKeyboardFocusRingNeedsDisplayInRect:[self bounds]];
return okToChange;
}
#pragma mark *** Aesthetics ***
- (BOOL)animates {
return [SRCell animates];
}
- (void)setAnimates:(BOOL)an {
[SRCell setAnimates:an];
}
- (SRRecorderStyle)style {
return [SRCell style];
}
- (void)setStyle:(SRRecorderStyle)nStyle {
[SRCell setStyle:nStyle];
}
#pragma mark *** Interface Stuff ***
// If the control is set to be resizeable in width, this will make sure that the tracking rects are always updated
- (void)viewDidMoveToWindow
{
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver: self];
[center addObserver:self selector:@selector(viewFrameDidChange:) name:NSViewFrameDidChangeNotification object:self];
[self resetTrackingRects];
}
- (void)viewFrameDidChange:(NSNotification *)aNotification
{
[self resetTrackingRects];
}
// Prevent from being too small
- (void)setFrameSize:(NSSize)newSize
{
NSSize correctedSize = newSize;
correctedSize.height = SRMaxHeight;
if (correctedSize.width < SRMinWidth) correctedSize.width = SRMinWidth;
[super setFrameSize: correctedSize];
}
- (void)setFrame:(NSRect)frameRect
{
NSRect correctedFrarme = frameRect;
correctedFrarme.size.height = SRMaxHeight;
if (correctedFrarme.size.width < SRMinWidth) correctedFrarme.size.width = SRMinWidth;
[super setFrame: correctedFrarme];
}
- (NSString *)keyChars {
return [SRCell keyChars];
}
- (NSString *)keyCharsIgnoringModifiers {
return [SRCell keyCharsIgnoringModifiers];
}
#pragma mark *** Key Interception ***
// Like most NSControls, pass things on to the cell
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
// Only if we're key, please. Otherwise hitting Space after having
// tabbed past SRRecorderControl will put you into recording mode.
if (([[[self window] firstResponder] isEqualTo:self])) {
if ([SRCell performKeyEquivalent:theEvent]) return YES;
}
return [super performKeyEquivalent: theEvent];
}
- (void)flagsChanged:(NSEvent *)theEvent
{
[SRCell flagsChanged:theEvent];
}
- (void)keyDown:(NSEvent *)theEvent
{
if ( [SRCell performKeyEquivalent: theEvent] )
return;
[super keyDown:theEvent];
}
#pragma mark *** Key Combination Control ***
- (NSUInteger)allowedFlags
{
return [SRCell allowedFlags];
}
- (void)setAllowedFlags:(NSUInteger)flags
{
[SRCell setAllowedFlags: flags];
}
- (BOOL)allowsKeyOnly {
return [SRCell allowsKeyOnly];
}
- (void)setAllowsKeyOnly:(BOOL)nAllowsKeyOnly escapeKeysRecord:(BOOL)nEscapeKeysRecord {
[SRCell setAllowsKeyOnly:nAllowsKeyOnly escapeKeysRecord:nEscapeKeysRecord];
}
- (BOOL)escapeKeysRecord {
return [SRCell escapeKeysRecord];
}
- (BOOL)canCaptureGlobalHotKeys
{
return [[self cell] canCaptureGlobalHotKeys];
}
- (void)setCanCaptureGlobalHotKeys:(BOOL)inState
{
[[self cell] setCanCaptureGlobalHotKeys:inState];
}
- (NSUInteger)requiredFlags
{
return [SRCell requiredFlags];
}
- (void)setRequiredFlags:(NSUInteger)flags
{
[SRCell setRequiredFlags: flags];
}
- (KeyCombo)keyCombo
{
return [SRCell keyCombo];
}
- (void)setKeyCombo:(KeyCombo)aKeyCombo
{
[SRCell setKeyCombo: aKeyCombo];
}
#pragma mark *** Binding Methods ***
- (NSDictionary *)objectValue
{
KeyCombo keyCombo = [self keyCombo];
if (keyCombo.code == ShortcutRecorderEmptyCode || keyCombo.flags == ShortcutRecorderEmptyFlags)
return nil;
return [NSDictionary dictionaryWithObjectsAndKeys:
[self keyCharsIgnoringModifiers], @"characters",
[NSNumber numberWithInteger:keyCombo.code], @"keyCode",
[NSNumber numberWithUnsignedInteger:keyCombo.flags], @"modifierFlags",
nil];
}
- (void)setObjectValue:(NSDictionary *)shortcut
{
KeyCombo keyCombo = SRMakeKeyCombo(ShortcutRecorderEmptyCode, ShortcutRecorderEmptyFlags);
if (shortcut != nil && [shortcut isKindOfClass:[NSDictionary class]]) {
NSNumber *keyCode = [shortcut objectForKey:@"keyCode"];
NSNumber *modifierFlags = [shortcut objectForKey:@"modifierFlags"];
if ([keyCode isKindOfClass:[NSNumber class]] && [modifierFlags isKindOfClass:[NSNumber class]]) {
keyCombo.code = [keyCode integerValue];
keyCombo.flags = [modifierFlags unsignedIntegerValue];
}
}
[self setKeyCombo: keyCombo];
}
- (Class)valueClassForBinding:(NSString *)binding
{
if ([binding isEqualToString:@"value"])
return [NSDictionary class];
return [super valueClassForBinding:binding];
}
#pragma mark *** Autosave Control ***
- (NSString *)autosaveName
{
return [SRCell autosaveName];
}
- (void)setAutosaveName:(NSString *)aName
{
[SRCell setAutosaveName: aName];
}
#pragma mark -
- (NSString *)keyComboString
{
return [SRCell keyComboString];
}
#pragma mark *** Conversion Methods ***
- (NSUInteger)cocoaToCarbonFlags:(NSUInteger)cocoaFlags
{
return SRCocoaToCarbonFlags( cocoaFlags );
}
- (NSUInteger)carbonToCocoaFlags:(NSUInteger)carbonFlags;
{
return SRCarbonToCocoaFlags( carbonFlags );
}
#pragma mark *** Delegate ***
// Only the delegate will be handled by the control
- (id)delegate
{
return delegate;
}
- (void)setDelegate:(id)aDelegate
{
delegate = aDelegate;
}
#pragma mark *** Delegate pass-through ***
- (BOOL)shortcutRecorderCell:(SRRecorderCell *)aRecorderCell isKeyCode:(NSInteger)keyCode andFlagsTaken:(NSUInteger)flags reason:(NSString **)aReason
{
if (delegate != nil && [delegate respondsToSelector: @selector(shortcutRecorder:isKeyCode:andFlagsTaken:reason:)])
return [delegate shortcutRecorder:self isKeyCode:keyCode andFlagsTaken:flags reason:aReason];
else
return NO;
}
#define NilOrNull(o) ((o) == nil || (id)(o) == [NSNull null])
- (void)shortcutRecorderCell:(SRRecorderCell *)aRecorderCell keyComboDidChange:(KeyCombo)newKeyCombo
{
if (delegate != nil && [delegate respondsToSelector: @selector(shortcutRecorder:keyComboDidChange:)])
[delegate shortcutRecorder:self keyComboDidChange:newKeyCombo];
// propagate view changes to binding (see http://www.tomdalling.com/cocoa/implementing-your-own-cocoa-bindings)
NSDictionary *bindingInfo = [self infoForBinding:@"value"];
if (!bindingInfo)
return;
// apply the value transformer, if one has been set
NSDictionary *value = [self objectValue];
NSDictionary *bindingOptions = [bindingInfo objectForKey:NSOptionsKey];
if (bindingOptions != nil) {
NSValueTransformer *transformer = [bindingOptions valueForKey:NSValueTransformerBindingOption];
if (NilOrNull(transformer)) {
NSString *transformerName = [bindingOptions valueForKey:NSValueTransformerNameBindingOption];
if (!NilOrNull(transformerName))
transformer = [NSValueTransformer valueTransformerForName:transformerName];
}
if (!NilOrNull(transformer)) {
if ([[transformer class] allowsReverseTransformation])
value = [transformer reverseTransformedValue:value];
else
NSLog(@"WARNING: value has value transformer, but it doesn't allow reverse transformations in %s", __PRETTY_FUNCTION__);
}
}
id boundObject = [bindingInfo objectForKey:NSObservedObjectKey];
if (NilOrNull(boundObject)) {
NSLog(@"ERROR: NSObservedObjectKey was nil for value binding in %s", __PRETTY_FUNCTION__);
return;
}
NSString *boundKeyPath = [bindingInfo objectForKey:NSObservedKeyPathKey];
if (NilOrNull(boundKeyPath)) {
NSLog(@"ERROR: NSObservedKeyPathKey was nil for value binding in %s", __PRETTY_FUNCTION__);
return;
}
[boundObject setValue:value forKeyPath:boundKeyPath];
}
@end
@implementation SRRecorderControl (Private)
- (void)resetTrackingRects
{
[SRCell resetTrackingRects];
}
@end