fix performKeyEvent handling so we don't beep when commands are successful. b=376077 r=smichaud sr=roc

This commit is contained in:
joshmoz@gmail.com 2008-01-28 22:11:06 -08:00
parent 59ef880a05
commit 52533a5203
8 changed files with 157 additions and 70 deletions

View File

@ -283,7 +283,7 @@ public:
NS_IMETHOD SetMenuBar(nsIMenuBar * aMenuBar);
NS_IMETHOD ShowMenuBar(PRBool aShow);
virtual nsIMenuBar* GetMenuBar();
NS_IMETHOD GetPreferredSize(PRInt32& aWidth, PRInt32& aHeight);
NS_IMETHOD SetPreferredSize(PRInt32 aWidth, PRInt32 aHeight);

View File

@ -67,6 +67,7 @@
#include "nsCursorManager.h"
#include "nsWindowMap.h"
#include "nsCocoaUtils.h"
#include "nsMenuBarX.h"
#include "gfxContext.h"
#include "gfxQuartzSurface.h"
@ -823,12 +824,6 @@ NS_IMETHODIMP nsChildView::ShowMenuBar(PRBool aShow)
}
nsIMenuBar* nsChildView::GetMenuBar()
{
return nsnull; // subviews don't have menu bars
}
// Override to set the cursor on the mac
NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
{
@ -3221,6 +3216,7 @@ enum
kInsertKeyCode = 0x72, // also help key
kDeleteKeyCode = 0x75, // also forward delete key
kTabKeyCode = 0x30,
kTildeKeyCode = 0x32,
kBackspaceKeyCode = 0x33,
kHomeKeyCode = 0x73,
kEndKeyCode = 0x77,
@ -4059,18 +4055,46 @@ static BOOL keyUpAlreadySentKeyDown = NO;
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
{
// don't bother if we don't have a gecko widget or we're in composition
if (!mGeckoChild || nsTSMManager::IsComposing())
// First we need to ignore certain system commands. If we don't we'll have
// to duplicate their functionality in Gecko, which as of this time we haven't.
// The only thing we ignore now is command-tilde, because NSApp handles that for us
// and we need the event to propagate to there.
unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if (modifierFlags & NSCommandKeyMask && [theEvent keyCode] == kTildeKeyCode)
return NO;
// see if the menu system will handle the event
if ([[NSApp mainMenu] performKeyEquivalent:theEvent])
// don't bother if we don't have a gecko widget or we're in composition
if (!mGeckoChild || nsTSMManager::IsComposing())
return YES;
// see if the menu system will handle the event
if ([[NSApp mainMenu] performKeyEquivalent:theEvent]) {
return YES;
}
else {
// On Mac OS X 10.5 NSMenu's performKeyEquivalent: method returns NO for disabled menu
// items that have a matching key equiv. We need to know if that was the case so we can
// stop here like we would on 10.4 (it returns YES in that case). Since we want to eat
// the event if that happens the system won't give the disabled command beep, do it here
// manually.
if (nsToolkit::OnLeopardOrLater()) {
id delegate = [[self window] delegate];
if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
nsCocoaWindow* toplevelWindow = [delegate geckoWidget];
if (toplevelWindow) {
nsMenuBarX* menuBar = static_cast<nsMenuBarX*>(toplevelWindow->GetMenuBar());
if (menuBar && menuBar->ContainsKeyEquiv(modifierFlags, [theEvent characters])) {
NSBeep();
return YES;
}
}
}
}
}
// don't handle this if certain modifiers are down - those should
// be sent as normal key up/down events and cocoa will do so automatically
// if we reject here
unsigned int modifierFlags = [theEvent modifierFlags];
if ((modifierFlags & NSFunctionKeyMask) || (modifierFlags & NSNumericPadKeyMask))
return NO;
@ -4083,7 +4107,9 @@ static BOOL keyUpAlreadySentKeyDown = NO;
ConvertCocoaKeyEventToMacEvent(theEvent, macEvent);
geckoEvent.nativeMsg = &macEvent;
return (BOOL)mGeckoChild->DispatchWindowEvent(geckoEvent);
mGeckoChild->DispatchWindowEvent(geckoEvent);
return YES;
}

View File

@ -44,10 +44,10 @@
#include "nsIDOMElement.h"
// CC986E81-9F46-4AA2-B809-C544789E6F06
// 33FA04E3-EAFE-4DD1-AFB3-B3BC8C712716
#define NS_IMENUITEM_IID \
{ 0xCC986E81, 0x9F46, 0x4AA2, \
{ 0xB8, 0x09, 0xC5, 0x44, 0x78, 0x9E, 0x6F, 0x06 } }
{ 0x33FA04E3, 0xEAFE, 0x4DD1, \
{ 0xAF, 0xB3, 0xB3, 0xBC, 0x8C, 0x71, 0x27, 0x16 } }
class nsIMenu;
class nsIWidget;
@ -85,12 +85,6 @@ class nsIMenuItem : public nsISupports {
*/
NS_IMETHOD GetLabel(nsString &aText) = 0;
/**
* Set the Menu shortcut char
*
*/
NS_IMETHOD SetShortcutChar(const nsString &aText) = 0;
/**
* Get the Menu shortcut char
*
@ -145,12 +139,6 @@ class nsIMenuItem : public nsISupports {
*/
NS_IMETHOD DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled) = 0;
/**
*
*/
NS_IMETHOD SetModifiers(PRUint8 aModifiers) = 0;
NS_IMETHOD GetModifiers(PRUint8 * aModifiers) = 0;
/**
* Sets an appropriate icon for the menu item.
*/

View File

@ -43,6 +43,7 @@
#include "nsIMutationObserver.h"
#include "nsCOMArray.h"
#include "nsHashtable.h"
#include "nsTHashtable.h"
#include "nsWeakReference.h"
#include "nsIContent.h"
@ -75,8 +76,66 @@ namespace MenuHelpersX
@end
struct CocoaKeyEquivContainer {
CocoaKeyEquivContainer(const unsigned int modifiers, const NSString* string)
{
mModifiers = modifiers;
mString = [string retain];
}
~CocoaKeyEquivContainer()
{
[mString release];
}
CocoaKeyEquivContainer(const CocoaKeyEquivContainer& other)
{
mModifiers = other.mModifiers;
mString = [other.mString retain];
}
CocoaKeyEquivContainer& operator=(CocoaKeyEquivContainer& other)
{
mModifiers = other.mModifiers;
if (mString)
[mString release];
mString = [other.mString retain];
return *this;
}
unsigned int mModifiers;
NSString* mString;
};
struct CocoaKeyEquivKey : public PLDHashEntryHdr {
typedef const CocoaKeyEquivContainer& KeyType;
typedef const CocoaKeyEquivContainer* KeyTypePointer;
CocoaKeyEquivKey(KeyTypePointer aObj) : mObj(*aObj) { }
CocoaKeyEquivKey(const CocoaKeyEquivKey& other) : mObj(other.mObj) { }
~CocoaKeyEquivKey() { }
KeyType GetKey() const { return mObj; }
PRBool KeyEquals(KeyTypePointer aKey) const {
return aKey->mModifiers == mObj.mModifiers &&
[aKey->mString isEqualToString:mObj.mString];
}
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey) {
return [aKey->mString hash] ^ aKey->mModifiers;
}
enum { ALLOW_MEMMOVE = PR_FALSE };
private:
const CocoaKeyEquivContainer mObj;
};
//
// Native Mac menu bar wrapper
// Native menu bar wrapper
//
class nsMenuBarX : public nsIMenuBar,
@ -111,6 +170,7 @@ public:
NS_IMETHOD Paint();
NS_IMETHOD SetNativeData(void* aData);
NS_IMETHOD MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * aMenuNode);
PRBool ContainsKeyEquiv(unsigned int modifiers, NSString* string);
PRUint32 RegisterForCommand(nsIMenuItem* aItem);
void UnregisterCommand(PRUint32 aCommandID);
@ -119,6 +179,8 @@ public:
void UnregisterForContentChanges(nsIContent* aContent);
nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent);
void RegisterKeyEquivalent(unsigned int modifiers, NSString* string);
void UnregisterKeyEquivalent(unsigned int modifiers, NSString* string);
protected:
// Make our menubar conform to Aqua UI guidelines
void AquifyMenuBar();
@ -150,7 +212,9 @@ protected:
nsIDocument* mDocument; // pointer to document
NSMenu* mRootMenu; // root menu, representing entire menu bar
nsTHashtable<CocoaKeyEquivKey> mKeyEquivTable;
static EventHandlerUPP sCommandEventHandler; // carbon event handler for commands, shared
};

View File

@ -355,6 +355,8 @@ nsMenuBarX::MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget* aParentWind
NS_IMETHODIMP nsMenuBarX::Create(nsIWidget *aParent)
{
SetParent(aParent);
if (!mKeyEquivTable.Init())
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
@ -840,6 +842,27 @@ nsMenuBarX::LookupContentChangeObserver(nsIContent* aContent)
}
void nsMenuBarX::RegisterKeyEquivalent(unsigned int modifiers, NSString* string)
{
const CocoaKeyEquivContainer keyEquiv(modifiers, string);
mKeyEquivTable.PutEntry(keyEquiv);
}
void nsMenuBarX::UnregisterKeyEquivalent(unsigned int modifiers, NSString* string)
{
CocoaKeyEquivContainer keyEquiv(modifiers, string);
mKeyEquivTable.RemoveEntry(keyEquiv);
}
PRBool nsMenuBarX::ContainsKeyEquiv(unsigned int modifiers, NSString* string)
{
const CocoaKeyEquivContainer keyEquiv(modifiers, string);
return (mKeyEquivTable.GetEntry(keyEquiv) != nsnull);
}
// Given a menu item, creates a unique 4-character command ID and
// maps it to the item. Returns the id for use by the client.
PRUint32

View File

@ -68,7 +68,6 @@ public:
NS_IMETHOD Create(nsIMenu* aParent, const nsString & aLabel, EMenuItemType aItemType,
nsMenuBarX* aMenuBar, nsIContent* aNode);
NS_IMETHOD GetLabel(nsString &aText);
NS_IMETHOD SetShortcutChar(const nsString &aText);
NS_IMETHOD GetShortcutChar(nsString &aText);
NS_IMETHOD GetEnabled(PRBool *aIsEnabled);
NS_IMETHOD SetChecked(PRBool aIsEnabled);
@ -79,14 +78,13 @@ public:
NS_IMETHOD DoCommand();
NS_IMETHOD DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled);
NS_IMETHOD SetModifiers(PRUint8 aModifiers);
NS_IMETHOD GetModifiers(PRUint8 * aModifiers);
NS_IMETHOD SetupIcon();
NS_IMETHOD GetMenuItemContent(nsIContent ** aMenuItemContent);
protected:
void UncheckRadioSiblings(nsIContent* inCheckedElement);
void SetKeyEquiv(PRUint8 aModifiers, const nsString &aText);
NSMenuItem* mNativeMenuItem; // strong ref, we own

View File

@ -151,13 +151,12 @@ NS_METHOD nsMenuItemX::Create(nsIMenu* aParent, const nsString & aLabel, EMenuIt
nsCOMPtr<nsIContent> keyContent(do_QueryInterface(keyElement));
nsAutoString keyChar(NS_LITERAL_STRING(" "));
keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyChar);
if (!keyChar.EqualsLiteral(" "))
SetShortcutChar(keyChar);
nsAutoString modifiersStr;
keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::modifiers, modifiersStr);
PRUint8 modifiers = MenuHelpersX::GeckoModifiersForNodeAttribute(modifiersStr);
SetModifiers(modifiers);
SetKeyEquiv(modifiers, keyChar);
}
}
}
@ -296,39 +295,6 @@ NS_IMETHODIMP nsMenuItemX::DispatchDOMEvent(const nsString &eventName, PRBool *p
return NS_OK;
}
NS_METHOD nsMenuItemX::GetModifiers(PRUint8 * aModifiers)
{
*aModifiers = mModifiers;
return NS_OK;
}
NS_METHOD nsMenuItemX::SetModifiers(PRUint8 aModifiers)
{
mModifiers = aModifiers;
// set up shortcut key modifiers on native menu item
unsigned int macModifiers = MenuHelpersX::MacModifiersForGeckoModifiers(mModifiers);
[mNativeMenuItem setKeyEquivalentModifierMask:macModifiers];
return NS_OK;
}
NS_METHOD nsMenuItemX::SetShortcutChar(const nsString &aText)
{
mKeyEquivalent = aText;
// set up shortcut key on native menu item
NSString *keyEquivalent = [[NSString stringWithCharacters:(unichar*)mKeyEquivalent.get()
length:mKeyEquivalent.Length()] lowercaseString];
if (![keyEquivalent isEqualToString:@" "])
[mNativeMenuItem setKeyEquivalent:keyEquivalent];
return NS_OK;
}
NS_METHOD nsMenuItemX::GetShortcutChar(nsString &aText)
{
@ -367,6 +333,26 @@ nsMenuItemX::UncheckRadioSiblings(nsIContent* inCheckedContent)
}
void nsMenuItemX::SetKeyEquiv(PRUint8 aModifiers, const nsString &aText)
{
mMenuBar->UnregisterKeyEquivalent([mNativeMenuItem keyEquivalentModifierMask], [mNativeMenuItem keyEquivalent]);
mModifiers = aModifiers;
unsigned int macModifiers = MenuHelpersX::MacModifiersForGeckoModifiers(mModifiers);
[mNativeMenuItem setKeyEquivalentModifierMask:macModifiers];
mKeyEquivalent = aText;
NSString *keyEquivalent = [[NSString stringWithCharacters:(unichar*)mKeyEquivalent.get()
length:mKeyEquivalent.Length()] lowercaseString];
if ([keyEquivalent isEqualToString:@" "])
[mNativeMenuItem setKeyEquivalent:@""];
else
[mNativeMenuItem setKeyEquivalent:keyEquivalent];
mMenuBar->RegisterKeyEquivalent([mNativeMenuItem keyEquivalentModifierMask], [mNativeMenuItem keyEquivalent]);
}
//
// nsChangeObserver
//

View File

@ -531,6 +531,8 @@ static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow)
if (!pnsMenuBar)
return;
pnsMenuBar->Create(aParentWindow);
// fake event
nsMenuEvent fake(PR_TRUE, 0, nsnull);
pnsMenuBar->MenuConstruct(fake, aParentWindow, menubarNode);