From 229b94b63f393065749c53e3aaa98670e8e24e6a Mon Sep 17 00:00:00 2001 From: "hyatt%netscape.com" Date: Wed, 9 Feb 2000 09:34:35 +0000 Subject: [PATCH] Giving menus some seriously tough love. --- layout/xul/base/public/nsIMenuParent.h | 12 ++ layout/xul/base/src/Makefile.in | 1 + layout/xul/base/src/makefile.win | 2 + layout/xul/base/src/nsIMenuParent.h | 12 ++ layout/xul/base/src/nsMenuBarFrame.cpp | 108 +++++++++++++----- layout/xul/base/src/nsMenuBarFrame.h | 21 ++-- layout/xul/base/src/nsMenuBarListener.cpp | 110 ++++++------------- layout/xul/base/src/nsMenuBarListener.h | 1 - layout/xul/base/src/nsMenuFrame.cpp | 27 ++++- layout/xul/base/src/nsMenuPopupFrame.cpp | 71 +++++++++--- layout/xul/base/src/nsMenuPopupFrame.h | 19 +++- layout/xul/base/src/nsPopupSetFrame.cpp | 8 +- layout/xul/base/src/nsTreeTwistyListener.cpp | 3 +- 13 files changed, 248 insertions(+), 147 deletions(-) diff --git a/layout/xul/base/public/nsIMenuParent.h b/layout/xul/base/public/nsIMenuParent.h index fe581d34d7b6..6dabb633fbdf 100644 --- a/layout/xul/base/public/nsIMenuParent.h +++ b/layout/xul/base/public/nsIMenuParent.h @@ -49,6 +49,18 @@ public: NS_IMETHOD HideChain() = 0; NS_IMETHOD CreateDismissalListener() = 0; + + NS_IMETHOD InstallKeyboardNavigator() = 0; + NS_IMETHOD RemoveKeyboardNavigator() = 0; + + // Used to move up, down, left, and right in menus. + NS_IMETHOD KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) = 0; + NS_IMETHOD ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) = 0; + // Called when the ESC key is held down to close levels of menus. + NS_IMETHOD Escape(PRBool& aHandledFlag) = 0; + // Called to execute a menu item. + NS_IMETHOD Enter() = 0; + }; #endif diff --git a/layout/xul/base/src/Makefile.in b/layout/xul/base/src/Makefile.in index f6eb25abf6e7..a81954a38e82 100644 --- a/layout/xul/base/src/Makefile.in +++ b/layout/xul/base/src/Makefile.in @@ -61,6 +61,7 @@ CPPSRCS = \ nsMenuFrame.cpp \ nsMenuBarFrame.cpp \ nsMenuBarListener.cpp \ + nsMenuListener.cpp \ nsMenuDismissalListener.cpp \ nsPopupSetFrame.cpp \ nsRepeatService.cpp \ diff --git a/layout/xul/base/src/makefile.win b/layout/xul/base/src/makefile.win index e38d8c11ed32..2e2493771d0b 100644 --- a/layout/xul/base/src/makefile.win +++ b/layout/xul/base/src/makefile.win @@ -60,6 +60,7 @@ CPPSRCS= \ nsMenuFrame.cpp \ nsMenuBarFrame.cpp \ nsMenuBarListener.cpp \ + nsMenuListener.cpp \ nsMenuDismissalListener.cpp \ nsPopupSetFrame.cpp \ $(NULL) @@ -97,6 +98,7 @@ CPP_OBJS= \ .\$(OBJDIR)\nsMenuFrame.obj \ .\$(OBJDIR)\nsMenuBarFrame.obj \ .\$(OBJDIR)\nsMenuBarListener.obj \ + .\$(OBJDIR)\nsMenuListener.obj \ .\$(OBJDIR)\nsMenuDismissalListener.obj \ .\$(OBJDIR)\nsPopupSetFrame.obj \ $(NULL) diff --git a/layout/xul/base/src/nsIMenuParent.h b/layout/xul/base/src/nsIMenuParent.h index fe581d34d7b6..6dabb633fbdf 100644 --- a/layout/xul/base/src/nsIMenuParent.h +++ b/layout/xul/base/src/nsIMenuParent.h @@ -49,6 +49,18 @@ public: NS_IMETHOD HideChain() = 0; NS_IMETHOD CreateDismissalListener() = 0; + + NS_IMETHOD InstallKeyboardNavigator() = 0; + NS_IMETHOD RemoveKeyboardNavigator() = 0; + + // Used to move up, down, left, and right in menus. + NS_IMETHOD KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) = 0; + NS_IMETHOD ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) = 0; + // Called when the ESC key is held down to close levels of menus. + NS_IMETHOD Escape(PRBool& aHandledFlag) = 0; + // Called to execute a menu item. + NS_IMETHOD Enter() = 0; + }; #endif diff --git a/layout/xul/base/src/nsMenuBarFrame.cpp b/layout/xul/base/src/nsMenuBarFrame.cpp index cee825bc7ce3..f2d1e011f6c2 100644 --- a/layout/xul/base/src/nsMenuBarFrame.cpp +++ b/layout/xul/base/src/nsMenuBarFrame.cpp @@ -20,9 +20,8 @@ * Contributor(s): */ - +#include "nsMenuListener.h" #include "nsMenuBarFrame.h" - #include "nsIContent.h" #include "prtypes.h" #include "nsIAtom.h" @@ -85,7 +84,7 @@ NS_INTERFACE_MAP_END_INHERITING(nsToolbarFrame) // nsMenuBarFrame cntr // nsMenuBarFrame::nsMenuBarFrame() -:mIsActive(PR_FALSE), mTarget(nsnull) +:mIsActive(PR_FALSE), mTarget(nsnull), mKeyboardNavigator(nsnull), mMenuBarListener(nsnull) { } // cntr @@ -108,11 +107,12 @@ nsMenuBarFrame::Init(nsIPresContext* aPresContext, // Create the menu bar listener. mMenuBarListener = new nsMenuBarListener(this); + NS_IF_ADDREF(mMenuBarListener); if (! mMenuBarListener) return NS_ERROR_OUT_OF_MEMORY; - // Hook up the menu bar as a key listener (capturer) on the whole document. It will see every - // key press that occurs before anyone else does and will know when to take control. + // Hook up the menu bar as a key listener on the whole document. It will see every + // key press that occurs, but after everyone else does. nsCOMPtr doc; aContent->GetDocument(*getter_AddRefs(doc)); nsCOMPtr target = do_QueryInterface(doc); @@ -122,14 +122,13 @@ nsMenuBarFrame::Init(nsIPresContext* aPresContext, // Also hook up the listener to the window listening for focus events. This is so we can keep proper // state as the user alt-tabs through processes. - target->AddEventListener("blur", (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE); + target->AddEventListener("blur", (nsIDOMFocusListener*)mMenuBarListener, PR_FALSE); - target->AddEventListener("mousedown", (nsIDOMMouseListener*)mMenuBarListener, PR_TRUE); + target->AddEventListener("mousedown", (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE); - target->AddEventListener("keypress", (nsIDOMKeyListener*)mMenuBarListener, PR_TRUE); - target->AddEventListener("keydown", (nsIDOMKeyListener*)mMenuBarListener, PR_TRUE); - target->AddEventListener("keyup", (nsIDOMKeyListener*)mMenuBarListener, PR_TRUE); - NS_ADDREF(mMenuBarListener); + target->AddEventListener("keypress", (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); + target->AddEventListener("keydown", (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); + target->AddEventListener("keyup", (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); return rv; } @@ -152,6 +151,11 @@ NS_IMETHODIMP nsMenuBarFrame::SetActive(PRBool aActiveFlag) { mIsActive = aActiveFlag; + if (mIsActive) { + InstallKeyboardNavigator(); + } + else RemoveKeyboardNavigator(); + return NS_OK; } @@ -166,12 +170,15 @@ nsMenuBarFrame::ToggleMenuActiveState() mCurrentMenu->OpenMenu(PR_FALSE); mCurrentMenu->SelectMenu(PR_FALSE); mCurrentMenu = nsnull; + RemoveKeyboardNavigator(); } } else { // Activate the menu bar SetActive(PR_TRUE); + InstallKeyboardNavigator(); + // Set the active menu to be the top left item (e.g., the File menu). // We use an attribute called "active" to track the current active menu. nsCOMPtr firstMenuItem; @@ -218,7 +225,7 @@ nsMenuBarFrame::FindMenuWithShortcut(PRUint32 aLetter) return nsnull; } -void +NS_IMETHODIMP nsMenuBarFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) { if (mCurrentMenu) { @@ -227,7 +234,7 @@ nsMenuBarFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) if (isOpen) { // No way this applies to us. Give it to our child. mCurrentMenu->ShortcutNavigation(aLetter, aHandledFlag); - return; + return NS_OK; } } @@ -241,28 +248,30 @@ nsMenuBarFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) result->OpenMenu(PR_TRUE); result->SelectFirstItem(); } + + return NS_OK; } -void -nsMenuBarFrame::KeyboardNavigation(PRUint32 aDirection) +NS_IMETHODIMP +nsMenuBarFrame::KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) { if (!mCurrentMenu) - return; + return NS_OK; PRBool isContainer = PR_FALSE; PRBool isOpen = PR_FALSE; mCurrentMenu->MenuIsContainer(isContainer); mCurrentMenu->MenuIsOpen(isOpen); - PRBool handled = PR_FALSE; + aHandledFlag = PR_FALSE; if (isOpen) { // Let the child menu try to handle it. - mCurrentMenu->KeyboardNavigation(aDirection, handled); + mCurrentMenu->KeyboardNavigation(aDirection, aHandledFlag); } - if (handled) - return; + if (aHandledFlag) + return NS_OK; if (aDirection == NS_VK_RIGHT || aDirection == NS_VK_LEFT) { @@ -287,6 +296,8 @@ nsMenuBarFrame::KeyboardNavigation(PRUint32 aDirection) mCurrentMenu->OpenMenu(PR_TRUE); mCurrentMenu->SelectFirstItem(); } + + return NS_OK; } NS_IMETHODIMP @@ -421,27 +432,27 @@ NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem) } -void -nsMenuBarFrame::Escape() +NS_IMETHODIMP +nsMenuBarFrame::Escape(PRBool& aHandledFlag) { if (!mCurrentMenu) - return; + return NS_OK; // See if our menu is open. PRBool isOpen = PR_FALSE; mCurrentMenu->MenuIsOpen(isOpen); if (isOpen) { // Let the child menu handle this. - PRBool handled = PR_FALSE; - mCurrentMenu->Escape(handled); - if (!handled) { + aHandledFlag = PR_FALSE; + mCurrentMenu->Escape(aHandledFlag); + if (!aHandledFlag) { // Close up this menu but keep our current menu item // designation. mCurrentMenu->OpenMenu(PR_FALSE); } if (nsMenuFrame::mDismissalListener) nsMenuFrame::mDismissalListener->Unregister(); - return; + return NS_OK; } // It's us. Just set our active flag to false. @@ -453,13 +464,15 @@ nsMenuBarFrame::Escape() // Clear out our dismissal listener if (nsMenuFrame::mDismissalListener) nsMenuFrame::mDismissalListener->Unregister(); + + return NS_OK; } -void +NS_IMETHODIMP nsMenuBarFrame::Enter() { if (!mCurrentMenu) - return; + return NS_OK; // See if our menu is open. PRBool isOpen = PR_FALSE; @@ -467,12 +480,14 @@ nsMenuBarFrame::Enter() if (isOpen) { // Let the child menu handle this. mCurrentMenu->Enter(); - return; + return NS_OK; } // It's us. Open the current menu. mCurrentMenu->OpenMenu(PR_TRUE); mCurrentMenu->SelectFirstItem(); + + return NS_OK; } NS_IMETHODIMP @@ -523,6 +538,39 @@ nsMenuBarFrame::CreateDismissalListener() return NS_OK; } +NS_IMETHODIMP +nsMenuBarFrame::InstallKeyboardNavigator() +{ + if (mKeyboardNavigator) + return NS_OK; + + mKeyboardNavigator = new nsMenuListener(this); + NS_IF_ADDREF(mKeyboardNavigator); + + mTarget->AddEventListener("keypress", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->AddEventListener("keydown", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->AddEventListener("keyup", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + + return NS_OK; +} + +NS_IMETHODIMP +nsMenuBarFrame::RemoveKeyboardNavigator() +{ + if (!mKeyboardNavigator) + return NS_OK; + + mTarget->RemoveEventListener("keypress", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->RemoveEventListener("keydown", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->RemoveEventListener("keyup", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + + NS_IF_RELEASE(mKeyboardNavigator); + + return NS_OK; +} + +// helpers /////////////////////////////////////////////////////////// + PRBool nsMenuBarFrame::IsValidItem(nsIContent* aContent) { diff --git a/layout/xul/base/src/nsMenuBarFrame.h b/layout/xul/base/src/nsMenuBarFrame.h index 00f85ce0acae..bbd6beedabd7 100644 --- a/layout/xul/base/src/nsMenuBarFrame.h +++ b/layout/xul/base/src/nsMenuBarFrame.h @@ -66,6 +66,9 @@ public: // Hides the chain of cascaded menus without closing them up. NS_IMETHOD HideChain(); + NS_IMETHOD InstallKeyboardNavigator(); + NS_IMETHOD RemoveKeyboardNavigator(); + NS_IMETHOD GetWidget(nsIWidget **aWidget); // The dismissal listener gets created and attached to the window. NS_IMETHOD CreateDismissalListener(); @@ -84,23 +87,23 @@ public: void ToggleMenuActiveState(); // Used to move up, down, left, and right in menus. - void KeyboardNavigation(PRUint32 aDirection); - - // Used to handle ALT+key combos - void ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag); - nsIMenuFrame* FindMenuWithShortcut(PRUint32 aLetter); - + NS_IMETHOD KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag); + NS_IMETHOD ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag); // Called when the ESC key is held down to close levels of menus. - void Escape(); - + NS_IMETHOD Escape(PRBool& aHandledFlag); // Called to execute a menu item. - void Enter(); + NS_IMETHOD Enter(); + + // Used to handle ALT+key combos + nsIMenuFrame* FindMenuWithShortcut(PRUint32 aLetter); PRBool IsValidItem(nsIContent* aContent); PRBool IsDisabled(nsIContent* aContent); protected: nsMenuBarListener* mMenuBarListener; // The listener that tells us about key and mouse events. + nsMenuListener* mKeyboardNavigator; + PRBool mIsActive; // Whether or not the menu bar is active (a menu item is highlighted or shown). nsIMenuFrame* mCurrentMenu; // The current menu that is active. diff --git a/layout/xul/base/src/nsMenuBarListener.cpp b/layout/xul/base/src/nsMenuBarListener.cpp index 2933a5bfe469..3a1deb184836 100644 --- a/layout/xul/base/src/nsMenuBarListener.cpp +++ b/layout/xul/base/src/nsMenuBarListener.cpp @@ -55,7 +55,7 @@ NS_IMPL_QUERY_INTERFACE3(nsMenuBarListener, nsIDOMKeyListener, nsIDOMFocusListen //////////////////////////////////////////////////////////////////////// nsMenuBarListener::nsMenuBarListener(nsMenuBarFrame* aMenuBar) -:mAltKeyDown(PR_FALSE), mKeyboardNavigationActive(PR_FALSE) +:mAltKeyDown(PR_FALSE) { NS_INIT_REFCNT(); mMenuBarFrame = aMenuBar; @@ -89,13 +89,12 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) PRBool active = mMenuBarFrame->IsActive(); if (active) { - mKeyboardNavigationActive = PR_TRUE; - - aKeyEvent->PreventBubble(); + aKeyEvent->PreventBubble(); aKeyEvent->PreventCapture(); + aKeyEvent->PreventDefault(); return NS_ERROR_BASE; // I am consuming event - } else - mKeyboardNavigationActive = PR_FALSE; + } + return NS_OK; // means I am NOT consuming event } @@ -103,8 +102,7 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) nsresult nsMenuBarListener::KeyDown(nsIDOMEvent* aKeyEvent) { - PRBool active = mMenuBarFrame->IsActive(); - +#ifndef XP_UNIX nsCOMPtr keyEvent = do_QueryInterface(aKeyEvent); PRUint32 theChar; keyEvent->GetKeyCode(&theChar); @@ -114,59 +112,25 @@ nsMenuBarListener::KeyDown(nsIDOMEvent* aKeyEvent) return NS_OK; } - PRBool altKeyWasDown = mAltKeyDown; - mAltKeyDown = PR_FALSE; + if (mAltKeyDown) { + mAltKeyDown = PR_FALSE; - if (theChar == NS_VK_LEFT || - theChar == NS_VK_RIGHT || - theChar == NS_VK_UP || - theChar == NS_VK_DOWN) { - // The arrow keys were pressed. User is moving around within - // the menus. - if (active) { - mMenuBarFrame->KeyboardNavigation(theChar); - mKeyboardNavigationActive = PR_TRUE; - } else - mKeyboardNavigationActive = PR_FALSE; - } - else if (theChar == NS_VK_ESCAPE) { - // Close one level. - if (active) { - mMenuBarFrame->Escape(); - mKeyboardNavigationActive = PR_FALSE; - } - } - else if (theChar == NS_VK_ENTER || - theChar == NS_VK_RETURN) { - // Open one level. - if (active) - mMenuBarFrame->Enter(); - } -#ifndef XP_UNIX - else if (active || altKeyWasDown) { - // Get the character code. - nsCOMPtr keyEvent = do_QueryInterface(aKeyEvent); - if (keyEvent) { - // See if a letter was pressed. - PRUint32 charCode; - keyEvent->GetKeyCode(&charCode); + // Do shortcut navigation. + // A letter was pressed. We want to see if a shortcut gets matched. If + // so, we'll know the menu got activated. + PRBool active = PR_FALSE; + mMenuBarFrame->ShortcutNavigation(theChar, active); - // Do shortcut navigation. - // A letter was pressed. We want to see if a shortcut gets matched. If - // so, we'll know the menu got activated. - mMenuBarFrame->ShortcutNavigation(charCode, active); + if (active) { + aKeyEvent->PreventBubble(); + aKeyEvent->PreventCapture(); + aKeyEvent->PreventDefault(); } - } + + return NS_ERROR_BASE; // I am consuming event + } #endif - if (active) { - mKeyboardNavigationActive = PR_TRUE; - aKeyEvent->PreventBubble(); - aKeyEvent->PreventCapture(); - return NS_ERROR_BASE; // I am consuming event - } else - mKeyboardNavigationActive = PR_FALSE; - return NS_OK; // means I am NOT consuming event } @@ -174,18 +138,6 @@ nsMenuBarListener::KeyDown(nsIDOMEvent* aKeyEvent) nsresult nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) { - nsCOMPtr keyEvent = do_QueryInterface(aKeyEvent); - - PRBool active = mMenuBarFrame->IsActive(); - - if (active) { - mKeyboardNavigationActive = PR_TRUE; - aKeyEvent->PreventBubble(); - aKeyEvent->PreventCapture(); - return NS_ERROR_BASE; // I am consuming event - } else - mKeyboardNavigationActive = PR_FALSE; - return NS_OK; // means I am NOT consuming event } @@ -201,11 +153,11 @@ nsMenuBarListener::Focus(nsIDOMEvent* aEvent) nsresult nsMenuBarListener::Blur(nsIDOMEvent* aEvent) { - if (mKeyboardNavigationActive && mMenuBarFrame->IsActive()) { + if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { mMenuBarFrame->ToggleMenuActiveState(); - mMenuBarFrame->Escape(); + PRBool handled; + mMenuBarFrame->Escape(handled); mAltKeyDown = PR_FALSE; - mKeyboardNavigationActive = PR_FALSE; } return NS_OK; // means I am NOT consuming event @@ -217,9 +169,9 @@ nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent) { if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { mMenuBarFrame->ToggleMenuActiveState(); - mMenuBarFrame->Escape(); + PRBool handled; + mMenuBarFrame->Escape(handled); mAltKeyDown = PR_FALSE; - mKeyboardNavigationActive = PR_FALSE; } return NS_OK; // means I am NOT consuming event } @@ -230,9 +182,9 @@ nsMenuBarListener::MouseUp(nsIDOMEvent* aMouseEvent) { if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { mMenuBarFrame->ToggleMenuActiveState(); - mMenuBarFrame->Escape(); - mKeyboardNavigationActive = PR_FALSE; - mAltKeyDown = PR_FALSE; + PRBool handled; + mMenuBarFrame->Escape(handled); + mAltKeyDown = PR_FALSE; } return NS_OK; // means I am NOT consuming event } @@ -242,9 +194,9 @@ nsMenuBarListener::MouseClick(nsIDOMEvent* aMouseEvent) { if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { mMenuBarFrame->ToggleMenuActiveState(); - mMenuBarFrame->Escape(); - mKeyboardNavigationActive = PR_FALSE; - mAltKeyDown = PR_FALSE; + PRBool handled; + mMenuBarFrame->Escape(handled); + mAltKeyDown = PR_FALSE; } return NS_OK; // means I am NOT consuming event diff --git a/layout/xul/base/src/nsMenuBarListener.h b/layout/xul/base/src/nsMenuBarListener.h index b1004e877ac3..41e2148aff43 100644 --- a/layout/xul/base/src/nsMenuBarListener.h +++ b/layout/xul/base/src/nsMenuBarListener.h @@ -64,7 +64,6 @@ public: protected: nsMenuBarFrame* mMenuBarFrame; // The menu bar object. PRBool mAltKeyDown; // Whether or not the ALT key is currently down. - PRBool mKeyboardNavigationActive; // Are we currently navigating via the keyboard }; diff --git a/layout/xul/base/src/nsMenuFrame.cpp b/layout/xul/base/src/nsMenuFrame.cpp index f741dd63585a..6b0cd38fdc04 100644 --- a/layout/xul/base/src/nsMenuFrame.cpp +++ b/layout/xul/base/src/nsMenuFrame.cpp @@ -510,6 +510,16 @@ nsMenuFrame::OpenMenuInternal(PRBool aActivateFlag) nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; if (menuPopup) { + // Install a keyboard navigation listener if we're the root of the menu chain. + PRBool onMenuBar = PR_TRUE; + if (mMenuParent) + mMenuParent->IsMenuBar(onMenuBar); + + if (mMenuParent && onMenuBar) + mMenuParent->InstallKeyboardNavigator(); + else if (!mMenuParent) + menuPopup->InstallKeyboardNavigator(); + // Tell the menu bar we're active. if (mMenuParent) mMenuParent->SetActive(PR_TRUE); @@ -523,10 +533,7 @@ nsMenuFrame::OpenMenuInternal(PRBool aActivateFlag) menuPopupContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::popupanchor, popupAnchor); menuPopupContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::popupalign, popupAlign); - PRBool onMenuBar = PR_TRUE; - if (mMenuParent) - mMenuParent->IsMenuBar(onMenuBar); - + if (onMenuBar) { if (popupAnchor == "") popupAnchor = "bottomleft"; @@ -571,9 +578,19 @@ nsMenuFrame::OpenMenuInternal(PRBool aActivateFlag) nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; // Make sure we clear out our own items. - if (menuPopup) + if (menuPopup) { menuPopup->SetCurrentMenuItem(nsnull); + PRBool onMenuBar = PR_TRUE; + if (mMenuParent) + mMenuParent->IsMenuBar(onMenuBar); + + if (mMenuParent && onMenuBar) + mMenuParent->RemoveKeyboardNavigator(); + else if (!mMenuParent) + menuPopup->RemoveKeyboardNavigator(); + } + ActivateMenu(PR_FALSE); mMenuOpen = PR_FALSE; diff --git a/layout/xul/base/src/nsMenuPopupFrame.cpp b/layout/xul/base/src/nsMenuPopupFrame.cpp index e8d3645b3297..2ca74984f29b 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -402,8 +402,7 @@ nsMenuPopupFrame::SyncViewWithFrame(nsIPresContext* aPresContext, // // At this point, we should be positioned where we're told. Ensure that we fit // on the screen. - // - + // nsCOMPtr window(do_QueryInterface(scriptGlobalObject)); nsCOMPtr screen; window->GetScreen(getter_AddRefs(screen)); @@ -647,11 +646,11 @@ nsMenuPopupFrame::CaptureMouseEvents(nsIPresContext* aPresContext, PRBool aGrabM return NS_OK; } -void +NS_IMETHODIMP nsMenuPopupFrame::Escape(PRBool& aHandledFlag) { if (!mCurrentMenu) - return; + return NS_OK; // See if our menu is open. PRBool isOpen = PR_FALSE; @@ -664,16 +663,19 @@ nsMenuPopupFrame::Escape(PRBool& aHandledFlag) mCurrentMenu->OpenMenu(PR_FALSE); aHandledFlag = PR_TRUE; } - return; } + + return NS_OK; } -void +NS_IMETHODIMP nsMenuPopupFrame::Enter() { // Give it to the child. if (mCurrentMenu) mCurrentMenu->Enter(); + + return NS_OK; } nsIMenuFrame* @@ -688,13 +690,16 @@ nsMenuPopupFrame::FindMenuWithShortcut(PRUint32 aLetter) // See if it's a menu item. if (IsValidItem(current)) { // Get the shortcut attribute. - nsString shortcutKey = ""; + nsAutoString shortcutKey = ""; current->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey, shortcutKey); - shortcutKey.ToUpperCase(); if (shortcutKey.Length() > 0) { // We've got something. - PRUnichar shortcutChar = shortcutKey.CharAt(0); - if (shortcutChar == aLetter) { + char tempChar[2]; + tempChar[0] = aLetter; + tempChar[1] = 0; + nsAutoString tempChar2 = tempChar; + + if (shortcutKey.EqualsIgnoreCase(tempChar2)) { // We match! nsCOMPtr menuFrame = do_QueryInterface(currFrame); if (menuFrame) @@ -708,7 +713,7 @@ nsMenuPopupFrame::FindMenuWithShortcut(PRUint32 aLetter) return nsnull; } -void +NS_IMETHODIMP nsMenuPopupFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) { if (mCurrentMenu) { @@ -717,7 +722,7 @@ nsMenuPopupFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) if (isOpen) { // No way this applies to us. Give it to our child. mCurrentMenu->ShortcutNavigation(aLetter, aHandledFlag); - return; + return NS_OK; } } @@ -729,9 +734,11 @@ nsMenuPopupFrame::ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag) SetCurrentMenuItem(result); result->Enter(); } + + return NS_OK; } -void +NS_IMETHODIMP nsMenuPopupFrame::KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) { // This method only gets called if we're open. @@ -746,7 +753,7 @@ nsMenuPopupFrame::KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) SetCurrentMenuItem(nextItem); } } - return; + return NS_OK; } PRBool isContainer = PR_FALSE; @@ -768,7 +775,7 @@ nsMenuPopupFrame::KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) } if (aHandledFlag) - return; // The child menu took it for us. + return NS_OK; // The child menu took it for us. // For the vertical direction, we can move up or down. if (aDirection == NS_VK_UP || aDirection == NS_VK_DOWN) { @@ -790,6 +797,8 @@ nsMenuPopupFrame::KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag) aHandledFlag = PR_TRUE; } } + + return NS_OK; } NS_IMETHODIMP @@ -905,6 +914,38 @@ nsMenuPopupFrame::CreateDismissalListener() return NS_OK; } +NS_IMETHODIMP +nsMenuPopupFrame::InstallKeyboardNavigator() +{ + nsCOMPtr doc; + mContent->GetDocument(*getter_AddRefs(doc)); + nsCOMPtr target = do_QueryInterface(doc); + + mTarget = target; + mKeyboardNavigator = new nsMenuListener(this); + NS_IF_ADDREF(mKeyboardNavigator); + + target->AddEventListener("keypress", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + target->AddEventListener("keydown", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + target->AddEventListener("keyup", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + + return NS_OK; +} + +NS_IMETHODIMP +nsMenuPopupFrame::RemoveKeyboardNavigator() +{ + mTarget->RemoveEventListener("keypress", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->RemoveEventListener("keydown", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + mTarget->RemoveEventListener("keyup", (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); + + NS_IF_RELEASE(mKeyboardNavigator); + + return NS_OK; +} + +// helpers ///////////////////////////////////////////////////////////// + PRBool nsMenuPopupFrame::IsValidItem(nsIContent* aContent) { diff --git a/layout/xul/base/src/nsMenuPopupFrame.h b/layout/xul/base/src/nsMenuPopupFrame.h index 4f7d5cc65dde..4fc614c07882 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.h +++ b/layout/xul/base/src/nsMenuPopupFrame.h @@ -30,6 +30,8 @@ #include "prtypes.h" #include "nsIAtom.h" #include "nsCOMPtr.h" +#include "nsIDOMEventReceiver.h" +#include "nsMenuListener.h" #include "nsBoxFrame.h" #include "nsIMenuParent.h" @@ -67,6 +69,9 @@ public: // Hides the chain of cascaded menus without closing them up. NS_IMETHOD HideChain(); + NS_IMETHOD InstallKeyboardNavigator(); + NS_IMETHOD RemoveKeyboardNavigator(); + NS_IMETHOD GetWidget(nsIWidget **aWidget); // The dismissal listener gets created and attached to the window. @@ -99,13 +104,13 @@ public: NS_IMETHOD CaptureMouseEvents(nsIPresContext* aPresContext, PRBool aGrabMouseEvents); - void KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag); + NS_IMETHOD KeyboardNavigation(PRUint32 aDirection, PRBool& aHandledFlag); + NS_IMETHOD ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag); - void ShortcutNavigation(PRUint32 aLetter, PRBool& aHandledFlag); - nsIMenuFrame* FindMenuWithShortcut(PRUint32 aLetter); + NS_IMETHOD Escape(PRBool& aHandledFlag); + NS_IMETHOD Enter(); - void Escape(PRBool& aHandledFlag); - void Enter(); + nsIMenuFrame* FindMenuWithShortcut(PRUint32 aLetter); PRBool IsValidItem(nsIContent* aContent); PRBool IsDisabled(nsIContent* aContent); @@ -123,6 +128,10 @@ protected: PRBool mIsCapturingMouseEvents; // Whether or not we're grabbing the mouse events. // XXX Hack nsIPresContext* mPresContext; // weak reference + + nsMenuListener* mKeyboardNavigator; // The listener that tells us about key events. + nsIDOMEventReceiver* mTarget; + }; // class nsMenuPopupFrame #endif diff --git a/layout/xul/base/src/nsPopupSetFrame.cpp b/layout/xul/base/src/nsPopupSetFrame.cpp index d4f6ca30face..db711fd48fd5 100644 --- a/layout/xul/base/src/nsPopupSetFrame.cpp +++ b/layout/xul/base/src/nsPopupSetFrame.cpp @@ -455,6 +455,7 @@ nsPopupSetFrame::OpenPopup(PRBool aActivateFlag) nsCOMPtr childPopup = do_QueryInterface(activeChild); UpdateDismissalListener(childPopup); + childPopup->InstallKeyboardNavigator(); } else { if (!OnDestroy()) @@ -465,6 +466,11 @@ nsPopupSetFrame::OpenPopup(PRBool aActivateFlag) nsMenuFrame::mDismissalListener->Unregister(); } + // Remove any keyboard navigators + nsIFrame* activeChild = GetActiveChild(); + nsCOMPtr childPopup = do_QueryInterface(activeChild); + childPopup->RemoveKeyboardNavigator(); + ActivatePopup(PR_FALSE); } } @@ -553,5 +559,3 @@ nsPopupSetFrame::UpdateDismissalListener(nsIMenuParent* aMenuParent) // innermost menu popup frame is. nsMenuFrame::mDismissalListener->SetCurrentMenuParent(aMenuParent); } - - diff --git a/layout/xul/base/src/nsTreeTwistyListener.cpp b/layout/xul/base/src/nsTreeTwistyListener.cpp index 26632fe70638..d4f52cfc1cb9 100644 --- a/layout/xul/base/src/nsTreeTwistyListener.cpp +++ b/layout/xul/base/src/nsTreeTwistyListener.cpp @@ -103,7 +103,8 @@ nsTreeTwistyListener::MouseDown(nsIDOMEvent* aEvent) // Eat the event. aEvent->PreventCapture(); aEvent->PreventBubble(); - + aEvent->PreventDefault(); + nsAutoString open; treeItem->GetAttribute("open", open); if (open == "true")