mirror of
https://github.com/TermiT/Flycut.git
synced 2025-02-21 22:43:15 +08:00
258 lines
10 KiB
Objective-C
258 lines
10 KiB
Objective-C
//
|
|
// SRValidator.h
|
|
// ShortcutRecorder
|
|
//
|
|
// Copyright 2006-2011 Contributors. All rights reserved.
|
|
//
|
|
// License: BSD
|
|
//
|
|
// Contributors:
|
|
// David Dauer
|
|
// Jesper
|
|
// Jamie Kirkpatrick
|
|
// Andy Kim
|
|
|
|
#import "SRValidator.h"
|
|
#import "SRCommon.h"
|
|
|
|
@implementation SRValidator
|
|
|
|
//----------------------------------------------------------
|
|
// iinitWithDelegate:
|
|
//----------------------------------------------------------
|
|
- (id) initWithDelegate:(id)theDelegate;
|
|
{
|
|
self = [super init];
|
|
if ( !self )
|
|
return nil;
|
|
|
|
[self setDelegate:theDelegate];
|
|
|
|
return self;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// isKeyCode:andFlagsTaken:error:
|
|
//----------------------------------------------------------
|
|
- (BOOL) isKeyCode:(NSInteger)keyCode andFlagsTaken:(NSUInteger)flags error:(NSError **)error;
|
|
{
|
|
// if we have a delegate, it goes first...
|
|
if ( delegate )
|
|
{
|
|
NSString *delegateReason = nil;
|
|
if ( [delegate shortcutValidator:self
|
|
isKeyCode:keyCode
|
|
andFlagsTaken:SRCarbonToCocoaFlags( flags )
|
|
reason:&delegateReason])
|
|
{
|
|
if ( error )
|
|
{
|
|
NSString *description = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination %@ can't be used!"),
|
|
SRStringForCarbonModifierFlagsAndKeyCode( flags, keyCode )];
|
|
NSString *recoverySuggestion = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination \"%@\" can't be used because %@."),
|
|
SRReadableStringForCarbonModifierFlagsAndKeyCode( flags, keyCode ),
|
|
( delegateReason && [delegateReason length] ) ? delegateReason : @"it's already used"];
|
|
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
description, NSLocalizedDescriptionKey,
|
|
recoverySuggestion, NSLocalizedRecoverySuggestionErrorKey,
|
|
[NSArray arrayWithObject:@"OK"], NSLocalizedRecoveryOptionsErrorKey, // Is this needed? Shouldn't it show 'OK' by default? -AK
|
|
nil];
|
|
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
|
|
}
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
// then our implementation...
|
|
CFArrayRef tempArray = NULL;
|
|
OSStatus err = noErr;
|
|
|
|
// get global hot keys...
|
|
err = CopySymbolicHotKeys( &tempArray );
|
|
|
|
if ( err != noErr ) return YES;
|
|
|
|
// Not copying the array like this results in a leak on according to the Leaks Instrument
|
|
NSArray *globalHotKeys = [NSArray arrayWithArray:(NSArray *)tempArray];
|
|
|
|
if ( tempArray ) CFRelease(tempArray);
|
|
|
|
NSEnumerator *globalHotKeysEnumerator = [globalHotKeys objectEnumerator];
|
|
NSDictionary *globalHotKeyInfoDictionary;
|
|
int32_t globalHotKeyFlags;
|
|
NSInteger globalHotKeyCharCode;
|
|
BOOL globalCommandMod = NO, globalOptionMod = NO, globalShiftMod = NO, globalCtrlMod = NO;
|
|
BOOL localCommandMod = NO, localOptionMod = NO, localShiftMod = NO, localCtrlMod = NO;
|
|
|
|
// Prepare local carbon comparison flags
|
|
if ( flags & cmdKey ) localCommandMod = YES;
|
|
if ( flags & optionKey ) localOptionMod = YES;
|
|
if ( flags & shiftKey ) localShiftMod = YES;
|
|
if ( flags & controlKey ) localCtrlMod = YES;
|
|
|
|
while (( globalHotKeyInfoDictionary = [globalHotKeysEnumerator nextObject] ))
|
|
{
|
|
// Only check if global hotkey is enabled
|
|
if ( (CFBooleanRef)[globalHotKeyInfoDictionary objectForKey:(NSString *)kHISymbolicHotKeyEnabled] != kCFBooleanTrue )
|
|
continue;
|
|
|
|
globalCommandMod = NO;
|
|
globalOptionMod = NO;
|
|
globalShiftMod = NO;
|
|
globalCtrlMod = NO;
|
|
|
|
globalHotKeyCharCode = [(NSNumber *)[globalHotKeyInfoDictionary objectForKey:(NSString *)kHISymbolicHotKeyCode] shortValue];
|
|
|
|
CFNumberGetValue((CFNumberRef)[globalHotKeyInfoDictionary objectForKey: (NSString *)kHISymbolicHotKeyModifiers],kCFNumberSInt32Type,&globalHotKeyFlags);
|
|
|
|
if ( globalHotKeyFlags & cmdKey ) globalCommandMod = YES;
|
|
if ( globalHotKeyFlags & optionKey ) globalOptionMod = YES;
|
|
if ( globalHotKeyFlags & shiftKey) globalShiftMod = YES;
|
|
if ( globalHotKeyFlags & controlKey ) globalCtrlMod = YES;
|
|
|
|
NSString *localKeyString = SRStringForKeyCode( keyCode );
|
|
if (![localKeyString length]) return YES;
|
|
|
|
|
|
// compare unichar value and modifier flags
|
|
if ( ( globalHotKeyCharCode == keyCode )
|
|
&& ( globalCommandMod == localCommandMod )
|
|
&& ( globalOptionMod == localOptionMod )
|
|
&& ( globalShiftMod == localShiftMod )
|
|
&& ( globalCtrlMod == localCtrlMod ) )
|
|
{
|
|
if ( error )
|
|
{
|
|
NSString *description = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination %@ can't be used!"),
|
|
SRStringForCarbonModifierFlagsAndKeyCode( flags, keyCode )];
|
|
NSString *recoverySuggestion = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination \"%@\" can't be used because it's already used by a system-wide keyboard shortcut. (If you really want to use this key combination, most shortcuts can be changed in the Keyboard & Mouse panel in System Preferences.)"),
|
|
SRReadableStringForCarbonModifierFlagsAndKeyCode( flags, keyCode )];
|
|
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
description, NSLocalizedDescriptionKey,
|
|
recoverySuggestion, NSLocalizedRecoverySuggestionErrorKey,
|
|
[NSArray arrayWithObject:@"OK"], NSLocalizedRecoveryOptionsErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
|
|
}
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
// Check menus too
|
|
return [self isKeyCode:keyCode andFlags:flags takenInMenu:[NSApp mainMenu] error:error];
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// isKeyCode:andFlags:takenInMenu:error:
|
|
//----------------------------------------------------------
|
|
- (BOOL) isKeyCode:(NSInteger)keyCode andFlags:(NSUInteger)flags takenInMenu:(NSMenu *)menu error:(NSError **)error;
|
|
{
|
|
NSArray *menuItemsArray = [menu itemArray];
|
|
NSEnumerator *menuItemsEnumerator = [menuItemsArray objectEnumerator];
|
|
NSMenuItem *menuItem;
|
|
NSUInteger menuItemModifierFlags;
|
|
NSString *menuItemKeyEquivalent;
|
|
|
|
BOOL menuItemCommandMod = NO, menuItemOptionMod = NO, menuItemShiftMod = NO, menuItemCtrlMod = NO;
|
|
BOOL localCommandMod = NO, localOptionMod = NO, localShiftMod = NO, localCtrlMod = NO;
|
|
|
|
// Prepare local carbon comparison flags
|
|
if ( flags & cmdKey ) localCommandMod = YES;
|
|
if ( flags & optionKey ) localOptionMod = YES;
|
|
if ( flags & shiftKey ) localShiftMod = YES;
|
|
if ( flags & controlKey ) localCtrlMod = YES;
|
|
|
|
while (( menuItem = [menuItemsEnumerator nextObject] ))
|
|
{
|
|
// rescurse into all submenus...
|
|
if ( [menuItem hasSubmenu] )
|
|
{
|
|
if ( [self isKeyCode:keyCode andFlags:flags takenInMenu:[menuItem submenu] error:error] )
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
if ( ( menuItemKeyEquivalent = [menuItem keyEquivalent] )
|
|
&& ( ![menuItemKeyEquivalent isEqualToString: @""] ) )
|
|
{
|
|
menuItemCommandMod = NO;
|
|
menuItemOptionMod = NO;
|
|
menuItemShiftMod = NO;
|
|
menuItemCtrlMod = NO;
|
|
|
|
menuItemModifierFlags = [menuItem keyEquivalentModifierMask];
|
|
|
|
if ( menuItemModifierFlags & NSCommandKeyMask ) menuItemCommandMod = YES;
|
|
if ( menuItemModifierFlags & NSAlternateKeyMask ) menuItemOptionMod = YES;
|
|
if ( menuItemModifierFlags & NSShiftKeyMask ) menuItemShiftMod = YES;
|
|
if ( menuItemModifierFlags & NSControlKeyMask ) menuItemCtrlMod = YES;
|
|
|
|
NSString *localKeyString = SRStringForKeyCode( keyCode );
|
|
|
|
// Compare translated keyCode and modifier flags
|
|
if ( ( [[menuItemKeyEquivalent uppercaseString] isEqualToString: localKeyString] )
|
|
&& ( menuItemCommandMod == localCommandMod )
|
|
&& ( menuItemOptionMod == localOptionMod )
|
|
&& ( menuItemShiftMod == localShiftMod )
|
|
&& ( menuItemCtrlMod == localCtrlMod ) )
|
|
{
|
|
if ( error )
|
|
{
|
|
NSString *description = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination %@ can't be used!"),
|
|
SRStringForCarbonModifierFlagsAndKeyCode( flags, keyCode )];
|
|
NSString *recoverySuggestion = [NSString stringWithFormat:
|
|
SRLoc(@"The key combination \"%@\" can't be used because it's already used by the menu item \"%@\"."),
|
|
SRReadableStringForCocoaModifierFlagsAndKeyCode( menuItemModifierFlags, keyCode ),
|
|
[menuItem title]];
|
|
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
description, NSLocalizedDescriptionKey,
|
|
recoverySuggestion, NSLocalizedRecoverySuggestionErrorKey,
|
|
[NSArray arrayWithObject:@"OK"], NSLocalizedRecoveryOptionsErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:userInfo];
|
|
}
|
|
return YES;
|
|
}
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark accessors
|
|
|
|
//----------------------------------------------------------
|
|
// delegate
|
|
//----------------------------------------------------------
|
|
- (id) delegate
|
|
{
|
|
return delegate;
|
|
}
|
|
|
|
- (void) setDelegate: (id) theDelegate
|
|
{
|
|
delegate = theDelegate; // Standard delegate pattern does not retain the delegate
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
#pragma mark default delegate implementation
|
|
|
|
@implementation NSObject( SRValidation )
|
|
|
|
//----------------------------------------------------------
|
|
// shortcutValidator:isKeyCode:andFlagsTaken:reason:
|
|
//----------------------------------------------------------
|
|
- (BOOL) shortcutValidator:(SRValidator *)validator isKeyCode:(NSInteger)keyCode andFlagsTaken:(NSUInteger)flags reason:(NSString **)aReason;
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
@end
|