2011-03-20 15:16:13 +08:00
//
// SRValidator . h
// ShortcutRecorder
//
2011-06-10 00:30:07 +08:00
// Copyright 2006 -2011 Contributors . All rights reserved .
2011-03-20 15:16:13 +08:00
//
// License : BSD
//
// Contributors :
// David Dauer
// Jesper
// Jamie Kirkpatrick
2011-06-10 00:30:07 +08:00
// Andy Kim
2011-03-20 15:16:13 +08:00
# 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 :
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2011-06-10 00:30:07 +08:00
- ( BOOL ) isKeyCode : ( NSInteger ) keyCode andFlagsTaken : ( NSUInteger ) flags error : ( NSError * * ) error ;
2011-03-20 15:16:13 +08:00
{
// 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 :
2011-06-10 00:30:07 +08:00
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 ] ;
2011-03-20 15:16:13 +08:00
}
return YES ;
}
}
// then our implementation . . .
2011-06-10 00:30:07 +08:00
CFArrayRef tempArray = NULL ;
OSStatus err = noErr ;
2011-03-20 15:16:13 +08:00
// get global hot keys . . .
2011-06-10 00:30:07 +08:00
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 ) ;
2011-03-20 15:16:13 +08:00
NSEnumerator * globalHotKeysEnumerator = [ globalHotKeys objectEnumerator ] ;
NSDictionary * globalHotKeyInfoDictionary ;
2011-06-10 00:30:07 +08:00
int32_t globalHotKeyFlags ;
NSInteger globalHotKeyCharCode ;
2011-03-20 15:16:13 +08:00
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 ;
2011-06-10 00:30:07 +08:00
globalHotKeyCharCode = [ ( NSNumber * ) [ globalHotKeyInfoDictionary objectForKey : ( NSString * ) kHISymbolicHotKeyCode ] shortValue ] ;
2011-03-20 15:16:13 +08:00
2011-06-10 00:30:07 +08:00
CFNumberGetValue ( ( CFNumberRef ) [ globalHotKeyInfoDictionary objectForKey : ( NSString * ) kHISymbolicHotKeyModifiers ] , kCFNumberSInt32Type , & globalHotKeyFlags ) ;
2011-03-20 15:16:13 +08:00
2011-06-10 00:30:07 +08:00
if ( globalHotKeyFlags & cmdKey ) globalCommandMod = YES ;
if ( globalHotKeyFlags & optionKey ) globalOptionMod = YES ;
if ( globalHotKeyFlags & shiftKey ) globalShiftMod = YES ;
if ( globalHotKeyFlags & controlKey ) globalCtrlMod = YES ;
2011-03-20 15:16:13 +08:00
NSString * localKeyString = SRStringForKeyCode ( keyCode ) ;
if ( ! [ localKeyString length ] ) return YES ;
// compare unichar value and modifier flags
2011-06-10 00:30:07 +08:00
if ( ( globalHotKeyCharCode = = keyCode )
2011-03-20 15:16:13 +08:00
&& ( 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 ) ] ;
2011-06-10 00:30:07 +08:00
NSDictionary * userInfo = [ NSDictionary dictionaryWithObjectsAndKeys :
description , NSLocalizedDescriptionKey ,
recoverySuggestion , NSLocalizedRecoverySuggestionErrorKey ,
[ NSArray arrayWithObject : @ "OK" ] , NSLocalizedRecoveryOptionsErrorKey ,
nil ] ;
* error = [ NSError errorWithDomain : NSCocoaErrorDomain code : 0 userInfo : userInfo ] ;
2011-03-20 15:16:13 +08:00
}
return YES ;
}
}
// Check menus too
return [ self isKeyCode : keyCode andFlags : flags takenInMenu : [ NSApp mainMenu ] error : error ] ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// isKeyCode : andFlags : takenInMenu : error :
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2011-06-10 00:30:07 +08:00
- ( BOOL ) isKeyCode : ( NSInteger ) keyCode andFlags : ( NSUInteger ) flags takenInMenu : ( NSMenu * ) menu error : ( NSError * * ) error ;
2011-03-20 15:16:13 +08:00
{
NSArray * menuItemsArray = [ menu itemArray ] ;
NSEnumerator * menuItemsEnumerator = [ menuItemsArray objectEnumerator ] ;
NSMenuItem * menuItem ;
2011-06-10 00:30:07 +08:00
NSUInteger menuItemModifierFlags ;
2011-03-20 15:16:13 +08:00
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 :
2011-06-10 00:30:07 +08:00
description , NSLocalizedDescriptionKey ,
recoverySuggestion , NSLocalizedRecoverySuggestionErrorKey ,
[ NSArray arrayWithObject : @ "OK" ] , NSLocalizedRecoveryOptionsErrorKey ,
nil ] ;
* error = [ NSError errorWithDomain : NSCocoaErrorDomain code : 0 userInfo : userInfo ] ;
2011-03-20 15:16:13 +08:00
}
return YES ;
}
}
}
return NO ;
}
# pragma mark -
# pragma mark accessors
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// delegate
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ( id ) delegate
{
return delegate ;
}
- ( void ) setDelegate : ( id ) theDelegate
{
2011-06-10 00:30:07 +08:00
delegate = theDelegate ; // Standard delegate pattern does not retain the delegate
2011-03-20 15:16:13 +08:00
}
@ end
# pragma mark -
# pragma mark default delegate implementation
@ implementation NSObject ( SRValidation )
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// shortcutValidator : isKeyCode : andFlagsTaken : reason :
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2011-06-10 00:30:07 +08:00
- ( BOOL ) shortcutValidator : ( SRValidator * ) validator isKeyCode : ( NSInteger ) keyCode andFlagsTaken : ( NSUInteger ) flags reason : ( NSString * * ) aReason ;
2011-03-20 15:16:13 +08:00
{
return NO ;
}
@ end