diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index c5033ed53..cc6e44e4d 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -446,7 +446,7 @@ CPRunContinuesResponse = -1002; if ([_mainMenu performKeyEquivalent:anEvent]) return YES; - return NO; + return [[self keyWindow] performKeyEquivalent:anEvent]; } /*! @@ -458,10 +458,7 @@ CPRunContinuesResponse = -1002; _currentEvent = anEvent; // Check if this is a candidate for key equivalent... - if ([anEvent type] == CPKeyDown && - [anEvent modifierFlags] & (CPCommandKeyMask | CPControlKeyMask) && - [[anEvent characters] length] > 0 && - [self _handleKeyEquivalent:anEvent]) + if ([anEvent _couldBeKeyEquivalent] && [self _handleKeyEquivalent:anEvent]) return; if (_eventListeners.length) diff --git a/AppKit/CPCompatibility.j b/AppKit/CPCompatibility.j index 2539b2ab1..b2636811d 100644 --- a/AppKit/CPCompatibility.j +++ b/AppKit/CPCompatibility.j @@ -179,7 +179,7 @@ function CPBrowserIsEngine(anEngine) if (USER_AGENT.indexOf("Mac") != -1) { CPPlatformActionKeyMask = CPCommandKeyMask; - + CPUndoKeyEquivalent = @"Z"; CPRedoKeyEquivalent = @"Z"; @@ -189,10 +189,10 @@ if (USER_AGENT.indexOf("Mac") != -1) else { CPPlatformActionKeyMask = CPControlKeyMask; - + CPUndoKeyEquivalent = @"Z"; CPRedoKeyEquivalent = @"Y"; - + CPUndoKeyEquivalentModifierMask = CPControlKeyMask; CPRedoKeyEquivalentModifierMask = CPControlKeyMask; } diff --git a/AppKit/CPEvent.j b/AppKit/CPEvent.j index eb942a79f..95993fb8a 100644 --- a/AppKit/CPEvent.j +++ b/AppKit/CPEvent.j @@ -515,6 +515,14 @@ var _CPEventPeriodicEventPeriod = 0, return _deltaZ; } +- (BOOL)_couldBeKeyEquivalent +{ + // FIXME: More cases? Space? + return _type === CPKeyDown && + _modifierFlags & (CPCommandKeyMask | CPControlKeyMask) && + [_characters length] > 0; +} + /*! Generates periodic events every \c aPeriod seconds. @param aDelay the number of seconds before the first event diff --git a/AppKit/CPMenuItem.j b/AppKit/CPMenuItem.j index 2a35aceb2..7306b8f49 100644 --- a/AppKit/CPMenuItem.j +++ b/AppKit/CPMenuItem.j @@ -744,26 +744,29 @@ CPControlKeyMask @end -var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", +var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", - CPMenuItemTitleKey = @"CPMenuItemTitleKey", - CPMenuItemTargetKey = @"CPMenuItemTargetKey", - CPMenuItemActionKey = @"CPMenuItemActionKey", + CPMenuItemTitleKey = @"CPMenuItemTitleKey", + CPMenuItemTargetKey = @"CPMenuItemTargetKey", + CPMenuItemActionKey = @"CPMenuItemActionKey", - CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey", - CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey", + CPMenuItemIsEnabledKey = @"CPMenuItemIsEnabledKey", + CPMenuItemIsHiddenKey = @"CPMenuItemIsHiddenKey", - CPMenuItemTagKey = @"CPMenuItemTagKey", - CPMenuItemStateKey = @"CPMenuItemStateKey", + CPMenuItemTagKey = @"CPMenuItemTagKey", + CPMenuItemStateKey = @"CPMenuItemStateKey", - CPMenuItemImageKey = @"CPMenuItemImageKey", - CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey", + CPMenuItemImageKey = @"CPMenuItemImageKey", + CPMenuItemAlternateImageKey = @"CPMenuItemAlternateImageKey", - CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey", - CPMenuItemMenuKey = @"CPMenuItemMenuKey", + CPMenuItemSubmenuKey = @"CPMenuItemSubmenuKey", + CPMenuItemMenuKey = @"CPMenuItemMenuKey", - CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey", - CPMenuItemViewKey = @"CPMenuItemViewKey"; + CPMenuItemKeyEquivalentKey = @"CPMenuItemKeyEquivalentKey", + CPMenuItemKeyEquivalentModifierMaskKey = @"CPMenuItemKeyEquivalentModifierMaskKey", + + CPMenuItemRepresentedObjectKey = @"CPMenuItemRepresentedObjectKey", + CPMenuItemViewKey = @"CPMenuItemViewKey"; #define DEFAULT_VALUE(aKey, aDefaultValue) [aCoder containsValueForKey:(aKey)] ? [aCoder decodeObjectForKey:(aKey)] : (aDefaultValue) #define ENCODE_IFNOT(aKey, aValue, aDefaultValue) if ((aValue) !== (aDefaultValue)) [aCoder encodeObject:(aValue) forKey:(aKey)]; @@ -804,8 +807,8 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", _submenu = DEFAULT_VALUE(CPMenuItemSubmenuKey, nil); _menu = DEFAULT_VALUE(CPMenuItemMenuKey, nil); -// CPString _keyEquivalent; -// unsigned _keyEquivalentModifierMask; + _keyEquivalent = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentKey] || @""; + _keyEquivalentModifierMask = [aCoder decodeObjectForKey:CPMenuItemKeyEquivalentModifierMaskKey] || 0; // int _mnemonicLocation; @@ -847,6 +850,12 @@ var CPMenuItemIsSeparatorKey = @"CPMenuItemIsSeparatorKey", ENCODE_IFNOT(CPMenuItemSubmenuKey, _submenu, nil); ENCODE_IFNOT(CPMenuItemMenuKey, _menu, nil); + if (_keyEquivalent && _keyEquivalent.length) + [aCoder encodeObject:_keyEquivalent forKey:CPMenuItemKeyEquivalentKey]; + + if (_keyEquivalentModifierMask) + [aCoder encodeObject:_keyEquivalentModifierMask forKey:CPMenuItemKeyEquivalentModifierMaskKey]; + ENCODE_IFNOT(CPMenuItemRepresentedObjectKey, _representedObject, nil); ENCODE_IFNOT(CPMenuItemViewKey, _view, nil); } diff --git a/AppKit/CPView.j b/AppKit/CPView.j index c0e7a5157..c53892f76 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -1861,6 +1861,19 @@ setBoundsOrigin: @implementation CPView (KeyView) +- (BOOL)performKeyEquivalent:(CPEvent)anEvent +{ + var subviews = [self subviews], + count = [subviews count]; + + // Is reverse iteration correct here? It matches the other (correct) code like hit testing. + while (count--) + if ([subviews[count] performKeyEquivalent:anEvent]) + return YES; + + return NO; +} + - (BOOL)canBecomeKeyView { return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor]; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 32509b2d3..fc82ea3a8 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -1996,10 +1996,19 @@ CPTexturedBackgroundWindowMask return NO; } -- (void)keyDown:(CPEvent)event +- (void)performKeyEquivalent:(CPEvent)anEvent { - if (![self performKeyEquivalent:event]) - [self interpretKeyEvents:[event]]; + // FIXME: should we be starting at the root, in other words _windowView? + // The evidence seems to point to no... + return [[self contentView] performKeyEquivalent:anEvent]; +} + +- (void)keyDown:(CPEvent)anEvent +{ + // It's not clear why we do performKeyEquivalent again here... + // Perhaps to allow something to happen between sendEvent: and keyDown:? + if (![anEvent _couldBeKeyEquivalent] || ![self performKeyEquivalent:anEvent]) + [self interpretKeyEvents:[anEvent]]; } - (void)insertNewline:(id)sender diff --git a/Tools/nib2cib/NSEvent.j b/Tools/nib2cib/NSEvent.j new file mode 100644 index 000000000..d0d7e53d4 --- /dev/null +++ b/Tools/nib2cib/NSEvent.j @@ -0,0 +1,62 @@ +/* + * NSEvent.j + * nib2cib + * + * Created by Francisco Tolmasky. + * Copyright 2009, 280 North, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +NSAlphaShiftKeyMask = 1 << 16; +NSShiftKeyMask = 1 << 17; +NSControlKeyMask = 1 << 18; +NSAlternateKeyMask = 1 << 19; +NSCommandKeyMask = 1 << 20; +NSNumericPadKeyMask = 1 << 21; +NSHelpKeyMask = 1 << 22; +NSFunctionKeyMask = 1 << 23; +NSDeviceIndependentModifierFlagsMask = 0xffff0000; + +function CP_NSMapKeyMask(anNSKeyMask) +{ + var keyMask = 0; + + if (anNSKeyMask & NSAlphaShiftKeyMask) + keyMask |= CPAlphaShiftKeyMask; + + if (anNSKeyMask & NSShiftKeyMask) + keyMask |= CPShiftKeyMask; + + if (anNSKeyMask & NSControlKeyMask) + keyMask |= CPControlKeyMask; + + if (anNSKeyMask & NSAlternateKeyMask) + keyMask |= CPAlternateKeyMask; + + if (anNSKeyMask & NSCommandKeyMask) + keyMask |= CPCommandKeyMask; + + if (anNSKeyMask & NSNumericPadKeyMask) + keyMask |= CPNumericPadKeyMask; + + if (anNSKeyMask & NSHelpKeyMask) + keyMask |= CPHelpKeyMask; + + if (aKeyMask & NSFunctionKeyMask) + keyMask |= CPFunctionKeyMask; + + return keyMask; +} diff --git a/Tools/nib2cib/NSMenuItem.j b/Tools/nib2cib/NSMenuItem.j index 56ed373a2..245e867bf 100644 --- a/Tools/nib2cib/NSMenuItem.j +++ b/Tools/nib2cib/NSMenuItem.j @@ -57,9 +57,9 @@ _submenu = [aCoder decodeObjectForKey:"NSSubmenu"]; _menu = [aCoder decodeObjectForKey:"NSMenu"]; -// _keyEquivalent = [aCoder decodeObjectForKey:"NSKeyEquiv"]; -// _keyEquivalentModifierMask = [aCoder decodeObjectForKey:"NSKeyEquivModMask"]; - + _keyEquivalent = [aCoder decodeObjectForKey:"NSKeyEquiv"]; + _keyEquivalentModifierMask = CP_NSMapKeyMask([aCoder decodeObjectForKey:"NSKeyEquivModMask"]); + // _mnemonicLocation = [aCoder decodeObjectForKey:"NSMnemonicLoc"]; // _isAlternate = [aCoder decodeBoolForKey:"NSIsAlternate"];