mirror of
https://github.com/TermiT/Flycut.git
synced 2025-02-21 22:43:15 +08:00
386 lines
9 KiB
Objective-C
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
|