Back to index

lightning-sunbird  0.9+nobinonly
nsMenuBarFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Dan Rosen <dr@netscape.com>
00024  *   Dean Tessman <dean_tessman@hotmail.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsMenuListener.h"
00041 #include "nsMenuBarFrame.h"
00042 #include "nsIServiceManager.h"
00043 #include "nsIContent.h"
00044 #include "prtypes.h"
00045 #include "nsIAtom.h"
00046 #include "nsPresContext.h"
00047 #include "nsStyleContext.h"
00048 #include "nsCSSRendering.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsIDocument.h"
00051 #include "nsIDOMEventReceiver.h"
00052 #include "nsXULAtoms.h"
00053 #include "nsHTMLAtoms.h"
00054 #include "nsMenuFrame.h"
00055 #include "nsIView.h"
00056 #include "nsIViewManager.h"
00057 #include "nsMenuPopupFrame.h"
00058 #include "nsGUIEvent.h"
00059 #include "nsUnicharUtils.h"
00060 #include "nsICaret.h"
00061 #include "nsIFocusController.h"
00062 #include "nsIDOMWindowInternal.h"
00063 #include "nsIDOMDocument.h"
00064 #include "nsPIDOMWindow.h"
00065 #include "nsIInterfaceRequestorUtils.h"
00066 #include "nsCSSFrameConstructor.h"
00067 #ifdef XP_WIN
00068 #include "nsISound.h"
00069 #include "nsWidgetsCID.h"
00070 #endif
00071 
00072 
00073 //
00074 // NS_NewMenuBarFrame
00075 //
00076 // Wrapper for creating a new menu Bar container
00077 //
00078 nsresult
00079 NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00080 {
00081   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00082   if (nsnull == aNewFrame) {
00083     return NS_ERROR_NULL_POINTER;
00084   }
00085   nsMenuBarFrame* it = new (aPresShell) nsMenuBarFrame (aPresShell);
00086   if ( !it )
00087     return NS_ERROR_OUT_OF_MEMORY;
00088   *aNewFrame = it;
00089   return NS_OK;
00090 }
00091 
00092 NS_IMETHODIMP_(nsrefcnt) 
00093 nsMenuBarFrame::AddRef(void)
00094 {
00095   return NS_OK;
00096 }
00097 
00098 NS_IMETHODIMP_(nsrefcnt) 
00099 nsMenuBarFrame::Release(void)
00100 {
00101     return NS_OK;
00102 }
00103 
00104 
00105 //
00106 // QueryInterface
00107 //
00108 NS_INTERFACE_MAP_BEGIN(nsMenuBarFrame)
00109   NS_INTERFACE_MAP_ENTRY(nsIMenuParent)
00110 NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
00111 
00112 
00113 //
00114 // nsMenuBarFrame cntr
00115 //
00116 nsMenuBarFrame::nsMenuBarFrame(nsIPresShell* aShell):nsBoxFrame(aShell),
00117 mMenuBarListener(nsnull), mKeyboardNavigator(nsnull),
00118 mIsActive(PR_FALSE), mTarget(nsnull), mCaretWasVisible(PR_FALSE)
00119 {
00120 
00121 } // cntr
00122 
00123 nsMenuBarFrame::~nsMenuBarFrame()
00124 {
00125   /* The menubar can still be active at this point under unusual circumstances.
00126      (say, while switching skins (which tears down all frames including
00127      this one) after having made a menu selection (say, Edit->Preferences,
00128      to get to the skin switching UI)). SetActive(PR_FALSE) releases
00129      mKeyboardNavigator, which is by now pointing to a deleted frame.
00130   */
00131   SetActive(PR_FALSE);
00132 }
00133 
00134 NS_IMETHODIMP
00135 nsMenuBarFrame::Init(nsPresContext*  aPresContext,
00136                      nsIContent*      aContent,
00137                      nsIFrame*        aParent,
00138                      nsStyleContext*  aContext,
00139                      nsIFrame*        aPrevInFlow)
00140 {
00141   nsresult  rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00142 
00143   // XXX hack
00144   mPresContext = aPresContext;
00145 
00146   // Create the menu bar listener.
00147   mMenuBarListener = new nsMenuBarListener(this);
00148   NS_IF_ADDREF(mMenuBarListener);
00149   if (! mMenuBarListener)
00150     return NS_ERROR_OUT_OF_MEMORY;
00151 
00152   // Hook up the menu bar as a key listener on the whole document.  It will see every
00153   // key press that occurs, but after everyone else does.
00154   nsCOMPtr<nsIDOMEventReceiver> target = do_QueryInterface(aContent->GetDocument());
00155   
00156   mTarget = target;
00157 
00158   // Also hook up the listener to the window listening for focus events. This is so we can keep proper
00159   // state as the user alt-tabs through processes.
00160   
00161   target->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); 
00162   target->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);  
00163   target->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);   
00164 
00165   target->AddEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);   
00166   target->AddEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);   
00167 
00168   return rv;
00169 }
00170 
00171 NS_IMETHODIMP
00172 nsMenuBarFrame::IsOpen()
00173 {
00174   PRBool isOpen = PR_FALSE;
00175   if(mCurrentMenu) {
00176     mCurrentMenu->MenuIsOpen(isOpen);
00177     if (isOpen) {
00178       return PR_TRUE;
00179     }
00180   }
00181   return PR_FALSE;
00182 }
00183 
00184 
00185 NS_IMETHODIMP
00186 nsMenuBarFrame::SetActive(PRBool aActiveFlag)
00187 {
00188   // If the activity is not changed, there is nothing to do.
00189   if (mIsActive == aActiveFlag)
00190     return NS_OK;
00191 
00192   mIsActive = aActiveFlag;
00193   if (mIsActive) {
00194     InstallKeyboardNavigator();
00195   }
00196   else {
00197     RemoveKeyboardNavigator();
00198   }
00199   
00200   // We don't want the caret to blink while the menus are active
00201   // The caret distracts screen readers and other assistive technologies from the menu selection
00202   // There is 1 caret per document, we need to find the focused document and toggle its caret 
00203   do {
00204     nsIPresShell *presShell = mPresContext->GetPresShell();
00205     if (!presShell)
00206       break;
00207 
00208     nsIDocument *document = presShell->GetDocument();
00209     if (!document)
00210       break;
00211 
00212     nsCOMPtr<nsISupports> container = document->GetContainer();
00213     nsCOMPtr<nsPIDOMWindow> windowPrivate = do_GetInterface(container);
00214     if (!windowPrivate)
00215       break;
00216 
00217     nsIFocusController *focusController =
00218       windowPrivate->GetRootFocusController();
00219     if (!focusController)
00220       break;
00221 
00222     nsCOMPtr<nsIDOMWindowInternal> windowInternal;
00223     focusController->GetFocusedWindow(getter_AddRefs(windowInternal));
00224     if (!windowInternal)
00225       break;
00226 
00227     nsCOMPtr<nsIDOMDocument> domDoc;
00228     nsCOMPtr<nsIDocument> focusedDoc;
00229     windowInternal->GetDocument(getter_AddRefs(domDoc));
00230     focusedDoc = do_QueryInterface(domDoc);
00231     if (!focusedDoc)
00232       break;
00233 
00234     presShell = focusedDoc->GetShellAt(0);
00235     nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
00236     // there is no selection controller for full page plugins
00237     if (!selCon)
00238       break;
00239 
00240     if (mIsActive) {// store whether caret was visible so that we can restore that state when menu is closed
00241       PRBool isCaretVisible;
00242       selCon->GetCaretEnabled(&isCaretVisible);
00243       mCaretWasVisible |= isCaretVisible;
00244     }
00245     selCon->SetCaretEnabled(!mIsActive && mCaretWasVisible);
00246     if (!mIsActive) {
00247       mCaretWasVisible = PR_FALSE;
00248     }
00249   } while (0);
00250 
00251   NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive");
00252   NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive");
00253   
00254   FireDOMEventSynch(mIsActive ? active : inactive);
00255 
00256   return NS_OK;
00257 }
00258 
00259 void
00260 nsMenuBarFrame::ToggleMenuActiveState()
00261 {
00262   if (mIsActive) {
00263     // Deactivate the menu bar
00264     SetActive(PR_FALSE);
00265     if (mCurrentMenu) {
00266       // Deactivate the menu.
00267       mCurrentMenu->OpenMenu(PR_FALSE);
00268       mCurrentMenu->SelectMenu(PR_FALSE);
00269       mCurrentMenu = nsnull;
00270     }
00271   }
00272   else {
00273     // if the menu bar is already selected (eg. mouseover), deselect it
00274     if (mCurrentMenu)
00275       mCurrentMenu->SelectMenu(PR_FALSE);
00276     
00277     // Activate the menu bar
00278     SetActive(PR_TRUE);
00279 
00280     // Set the active menu to be the top left item (e.g., the File menu).
00281     // We use an attribute called "menuactive" to track the current 
00282     // active menu.
00283     nsIMenuFrame* firstFrame = GetNextMenuItem(nsnull);
00284     if (firstFrame) {
00285       firstFrame->SelectMenu(PR_TRUE);
00286       
00287       // Track this item for keyboard navigation.
00288       mCurrentMenu = firstFrame;
00289     }
00290   }
00291 }
00292 
00293 static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
00294                               nsIFrame** aResult)
00295 {
00296   nsIContent* child = nsnull;
00297   if (aChild)
00298     child = aChild->GetContent();
00299   aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
00300 }
00301 
00302 nsIMenuFrame*
00303 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
00304 {
00305   PRUint32 charCode;
00306   aKeyEvent->GetCharCode(&charCode);
00307 
00308   // Enumerate over our list of frames.
00309   nsIFrame* immediateParent = nsnull;
00310   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
00311   if (!immediateParent)
00312     immediateParent = this;
00313 
00314   nsIFrame* currFrame = immediateParent->GetFirstChild(nsnull);
00315 
00316   while (currFrame) {
00317     nsIContent* current = currFrame->GetContent();
00318     
00319     // See if it's a menu item.
00320     if (IsValidItem(current)) {
00321       // Get the shortcut attribute.
00322       nsAutoString shortcutKey;
00323       current->GetAttr(kNameSpaceID_None, nsXULAtoms::accesskey, shortcutKey);
00324       if (!shortcutKey.IsEmpty()) {
00325         // We've got something.
00326         PRUnichar letter = PRUnichar(charCode); // throw away the high-zero-fill
00327         if ( shortcutKey.Equals(Substring(&letter, &letter+1),
00328                                 nsCaseInsensitiveStringComparator()) )  {
00329           // We match!
00330           nsIMenuFrame *menuFrame;
00331           if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) {
00332             menuFrame = nsnull;
00333           }
00334           return menuFrame;
00335         }
00336       }
00337     }
00338     currFrame = currFrame->GetNextSibling();
00339   }
00340 
00341   // didn't find a matching menu item
00342 #ifdef XP_WIN
00343   // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
00344   if (mIsActive) {
00345     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
00346     if (soundInterface)
00347       soundInterface->Beep();
00348   }
00349 
00350   DismissChain();
00351 #endif  // #ifdef XP_WIN
00352 
00353   return nsnull;
00354 }
00355 
00356 NS_IMETHODIMP 
00357 nsMenuBarFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag)
00358 {
00359   if (mCurrentMenu) {
00360     PRBool isOpen = PR_FALSE;
00361     mCurrentMenu->MenuIsOpen(isOpen);
00362     if (isOpen) {
00363       // No way this applies to us. Give it to our child.
00364       mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag);
00365       return NS_OK;
00366     }
00367   }
00368 
00369   // This applies to us. Let's see if one of the shortcuts applies
00370   nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent);
00371   if (result) {
00372     // We got one!
00373     nsWeakFrame weakFrame(this);
00374     nsIFrame* frame = nsnull;
00375     CallQueryInterface(result, &frame);
00376     nsWeakFrame weakResult(frame);
00377     aHandledFlag = PR_TRUE;
00378     SetActive(PR_TRUE);
00379     if (weakFrame.IsAlive()) {
00380       SetCurrentMenuItem(result);
00381     }
00382     if (weakResult.IsAlive()) {
00383       result->OpenMenu(PR_TRUE);
00384       if (weakResult.IsAlive()) {
00385         result->SelectFirstItem();
00386       }
00387     }
00388   }
00389 
00390   return NS_OK;
00391 }
00392 
00393 NS_IMETHODIMP
00394 nsMenuBarFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag)
00395 {
00396   nsNavigationDirection theDirection;
00397   NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode);
00398   if (!mCurrentMenu)
00399     return NS_OK;
00400   
00401   nsWeakFrame weakFrame(this);
00402   PRBool isContainer = PR_FALSE;
00403   PRBool isOpen = PR_FALSE;
00404   mCurrentMenu->MenuIsContainer(isContainer);
00405   mCurrentMenu->MenuIsOpen(isOpen);
00406 
00407   aHandledFlag = PR_FALSE;
00408   
00409   if (isOpen) {
00410     // Let the child menu try to handle it.
00411     mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag);
00412   }
00413 
00414   if (aHandledFlag)
00415     return NS_OK;
00416 
00417   if NS_DIRECTION_IS_INLINE(theDirection) {
00418     
00419     nsIMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ?
00420                              GetNextMenuItem(mCurrentMenu) : 
00421                              GetPreviousMenuItem(mCurrentMenu);
00422 
00423     nsIFrame* nextFrame = nsnull;
00424     if (nextItem) {
00425       CallQueryInterface(nextItem, &nextFrame);
00426     }
00427     nsWeakFrame weakNext(nextFrame);
00428     SetCurrentMenuItem(nextItem);
00429     if (weakNext.IsAlive()) {
00430       PRBool nextIsOpen;
00431       nextItem->MenuIsOpen(nextIsOpen);
00432       if (nextIsOpen) {
00433         // Select the first item.
00434         nextItem->SelectFirstItem();
00435       }
00436     }
00437   }
00438   else if NS_DIRECTION_IS_BLOCK(theDirection) {
00439     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00440     nsIFrame* frame = nsnull;
00441     CallQueryInterface(mCurrentMenu, &frame);
00442     nsWeakFrame weakCurrentMenu(frame);
00443     nsIMenuFrame* currentMenu = mCurrentMenu;
00444      // Open the menu and select its first item.
00445     currentMenu->OpenMenu(PR_TRUE);
00446     if (weakCurrentMenu.IsAlive()) {
00447       currentMenu->SelectFirstItem();
00448     }
00449   }
00450 
00451   return NS_OK;
00452 }
00453 
00454 /* virtual */ nsIMenuFrame*
00455 nsMenuBarFrame::GetNextMenuItem(nsIMenuFrame* aStart)
00456 {
00457   nsIFrame* immediateParent = nsnull;
00458   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
00459   if (!immediateParent)
00460     immediateParent = this;
00461 
00462   nsIFrame* currFrame = nsnull;
00463   nsIFrame* startFrame = nsnull;
00464   if (aStart) {
00465     aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); 
00466     if (currFrame) {
00467       startFrame = currFrame;
00468       currFrame = currFrame->GetNextSibling();
00469     }
00470   }
00471   else 
00472     currFrame = immediateParent->GetFirstChild(nsnull);
00473 
00474   while (currFrame) {
00475     // See if it's a menu item.
00476     if (IsValidItem(currFrame->GetContent())) {
00477       nsIMenuFrame *menuFrame;
00478       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
00479         menuFrame = nsnull;
00480       return menuFrame;
00481     }
00482     currFrame = currFrame->GetNextSibling();
00483   }
00484 
00485   currFrame = immediateParent->GetFirstChild(nsnull);
00486 
00487   // Still don't have anything. Try cycling from the beginning.
00488   while (currFrame && currFrame != startFrame) {
00489     // See if it's a menu item.
00490     if (IsValidItem(currFrame->GetContent())) {
00491       nsIMenuFrame *menuFrame;
00492       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
00493         menuFrame = nsnull;
00494       return menuFrame;
00495     }
00496 
00497     currFrame = currFrame->GetNextSibling();
00498   }
00499 
00500   // No luck. Just return our start value.
00501   return aStart;
00502 }
00503 
00504 /* virtual */ nsIMenuFrame*
00505 nsMenuBarFrame::GetPreviousMenuItem(nsIMenuFrame* aStart)
00506 {
00507   nsIFrame* immediateParent = nsnull;
00508   GetInsertionPoint(mPresContext->PresShell(), this, nsnull, &immediateParent);
00509   if (!immediateParent)
00510     immediateParent = this;
00511 
00512   nsFrameList frames(immediateParent->GetFirstChild(nsnull));
00513                               
00514   nsIFrame* currFrame = nsnull;
00515   nsIFrame* startFrame = nsnull;
00516   if (aStart) {
00517     aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame);
00518     if (currFrame) {
00519       startFrame = currFrame;
00520       currFrame = frames.GetPrevSiblingFor(currFrame);
00521     }
00522   }
00523   else currFrame = frames.LastChild();
00524 
00525   while (currFrame) {
00526     // See if it's a menu item.
00527     if (IsValidItem(currFrame->GetContent())) {
00528       nsIMenuFrame *menuFrame;
00529       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
00530         menuFrame = nsnull;
00531       return menuFrame;
00532     }
00533     currFrame = frames.GetPrevSiblingFor(currFrame);
00534   }
00535 
00536   currFrame = frames.LastChild();
00537 
00538   // Still don't have anything. Try cycling from the end.
00539   while (currFrame && currFrame != startFrame) {
00540     // See if it's a menu item.
00541     if (IsValidItem(currFrame->GetContent())) {
00542       nsIMenuFrame *menuFrame;
00543       if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame)))
00544         menuFrame = nsnull;
00545       return menuFrame;
00546     }
00547 
00548     currFrame = frames.GetPrevSiblingFor(currFrame);
00549   }
00550 
00551   // No luck. Just return our start value.
00552   return aStart;
00553 }
00554 
00555 /* virtual */ nsIMenuFrame*
00556 nsMenuBarFrame::GetCurrentMenuItem()
00557 {
00558   return mCurrentMenu;
00559 }
00560 
00561 
00562 NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
00563 {
00564   if (mCurrentMenu == aMenuItem)
00565     return NS_OK;
00566 
00567   PRBool wasOpen = PR_FALSE;
00568   
00569   // check if there's an open context menu, we ignore this
00570   if (nsMenuFrame::GetContextMenu())
00571     return NS_OK;
00572 
00573   nsWeakFrame weakFrame(this);
00574 
00575   // Unset the current child.
00576   if (mCurrentMenu) {
00577     nsIFrame* frame = nsnull;
00578     CallQueryInterface(mCurrentMenu, &frame);
00579     nsWeakFrame weakCurrentMenu(frame);
00580     nsIMenuFrame* currentMenu = mCurrentMenu;
00581     currentMenu->MenuIsOpen(wasOpen);
00582     currentMenu->SelectMenu(PR_FALSE);
00583     if (wasOpen && weakCurrentMenu.IsAlive()) {
00584       currentMenu->OpenMenu(PR_FALSE);
00585     }
00586   }
00587   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00588 
00589   // Set the new child.
00590   if (aMenuItem) {
00591     nsIFrame* newMenu = nsnull;
00592     CallQueryInterface(aMenuItem, &newMenu);
00593     nsWeakFrame weakNewMenu(newMenu);
00594     aMenuItem->SelectMenu(PR_TRUE);
00595     NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK);
00596     aMenuItem->MarkAsGenerated(); // Have the menu building. Get it ready to be shown.
00597     NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK);
00598 
00599     PRBool isDisabled = PR_FALSE;
00600     aMenuItem->MenuIsDisabled(isDisabled);
00601     if (wasOpen&&!isDisabled)
00602       aMenuItem->OpenMenu(PR_TRUE);
00603     ClearRecentlyRolledUp();
00604   }
00605 
00606   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00607   mCurrentMenu = aMenuItem;
00608 
00609   return NS_OK;
00610 }
00611 
00612 
00613 NS_IMETHODIMP 
00614 nsMenuBarFrame::Escape(PRBool& aHandledFlag)
00615 {
00616   if (!mCurrentMenu)
00617     return NS_OK;
00618 
00619   nsWeakFrame weakFrame(this);
00620   // See if our menu is open.
00621   PRBool isOpen = PR_FALSE;
00622   mCurrentMenu->MenuIsOpen(isOpen);
00623   if (isOpen) {
00624     // Let the child menu handle this.
00625     aHandledFlag = PR_FALSE;
00626     mCurrentMenu->Escape(aHandledFlag);
00627     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00628     if (!aHandledFlag) {
00629       // Close up this menu but keep our current menu item
00630       // designation.
00631       mCurrentMenu->OpenMenu(PR_FALSE);
00632       NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00633     }
00634        if (nsMenuFrame::sDismissalListener)
00635       nsMenuFrame::sDismissalListener->Unregister();
00636     return NS_OK;
00637   }
00638 
00639   // Clear our current menu item if we've got one.
00640   SetCurrentMenuItem(nsnull);
00641   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00642 
00643   SetActive(PR_FALSE);
00644 
00645   // Clear out our dismissal listener
00646   if (nsMenuFrame::sDismissalListener)
00647     nsMenuFrame::sDismissalListener->Unregister();
00648 
00649   return NS_OK;
00650 }
00651 
00652 NS_IMETHODIMP 
00653 nsMenuBarFrame::Enter()
00654 {
00655   if (!mCurrentMenu)
00656     return NS_OK;
00657 
00658   ClearRecentlyRolledUp();
00659 
00660   // See if our menu is open.
00661   PRBool isOpen = PR_FALSE;
00662   mCurrentMenu->MenuIsOpen(isOpen);
00663   if (isOpen) {
00664     // Let the child menu handle this.
00665     mCurrentMenu->Enter();
00666     return NS_OK;
00667   }
00668 
00669   // It's us. Open the current menu.
00670   mCurrentMenu->OpenMenu(PR_TRUE);
00671   mCurrentMenu->SelectFirstItem();
00672 
00673   return NS_OK;
00674 }
00675 
00676 NS_IMETHODIMP
00677 nsMenuBarFrame::ClearRecentlyRolledUp()
00678 {
00679   // We're no longer in danger of popping down a menu from the same 
00680   // click on the menubar, which was supposed to toggle the menu closed
00681   mRecentRollupMenu = nsnull;
00682 
00683   return NS_OK;
00684 }
00685 
00686 NS_IMETHODIMP
00687 nsMenuBarFrame::RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp)
00688 {
00689   // Don't let a click reopen a menu that was just rolled up
00690   // from the same click. Otherwise, the user can't click on
00691   // a menubar item to toggle its submenu closed.
00692   *aJustRolledUp = (mRecentRollupMenu == aMenuFrame);
00693 
00694   return NS_OK;
00695 }
00696 
00697 NS_IMETHODIMP
00698 nsMenuBarFrame::HideChain()
00699 {
00700   // XXX hack if a context menu is active, do an Escape, which is
00701   // currently bugged and destroys everything.  We need to close
00702   // the context menu first, otherwise SetCurrentMenuItem above
00703   // would get blocked.
00704   if (nsMenuFrame::GetContextMenu()) {
00705     PRBool dummy;
00706     mCurrentMenu->Escape(dummy);
00707   }
00708 
00709   // Stop capturing rollups
00710   // (must do this during Hide, which happens before the menu item is executed,
00711   // since this reinstates normal event handling.)
00712   if (nsMenuFrame::sDismissalListener)
00713     nsMenuFrame::sDismissalListener->Unregister();
00714 
00715   ClearRecentlyRolledUp();
00716   if (mCurrentMenu) {
00717     mCurrentMenu->ActivateMenu(PR_FALSE);
00718     mCurrentMenu->SelectMenu(PR_FALSE);
00719     mRecentRollupMenu = mCurrentMenu;
00720   }
00721 
00722   if (mIsActive) {
00723     ToggleMenuActiveState();
00724   }
00725 
00726   return NS_OK;
00727 }
00728 
00729 NS_IMETHODIMP
00730 nsMenuBarFrame::DismissChain()
00731 {
00732   // Stop capturing rollups
00733   if (nsMenuFrame::sDismissalListener)
00734     nsMenuFrame::sDismissalListener->Unregister();
00735   
00736   nsWeakFrame weakFrame(this);
00737   SetCurrentMenuItem(nsnull);
00738   if (weakFrame.IsAlive()) {
00739     SetActive(PR_FALSE);
00740   }
00741   return NS_OK;
00742 }
00743 
00744 
00745 NS_IMETHODIMP
00746 nsMenuBarFrame :: KillPendingTimers ( )
00747 {
00748   return NS_OK;
00749 
00750 } // KillPendingTimers
00751 
00752 
00753 NS_IMETHODIMP
00754 nsMenuBarFrame::GetWidget(nsIWidget **aWidget)
00755 {
00756   // (pinkerton/hyatt)
00757   // since the menubar is a menuparent but not a menuItem, the win32 rollup code
00758   // would erroneously add the entire top-level window to the widget list built up for
00759   // determining if a click is in a submenu's menu chain. To get around this, we just 
00760   // don't let the menubar have a widget. Things seem to work because the dismissal
00761   // listener is registered when a new menu is popped up, which is the only real reason
00762   // why we need a widget at all.
00763   *aWidget = nsnull;
00764   return NS_OK;
00765 
00766 #if DONT_WANT_TO_DO_THIS
00767   // Get parent view
00768   nsIView * view = nsnull;
00769   nsMenuPopupFrame::GetNearestEnclosingView(mPresContext, this, &view);
00770   if (!view)
00771     return NS_OK;
00772 
00773   *aWidget = view->GetWidget();
00774   NS_IF_ADDREF(*aWidget);
00775 #endif
00776 }
00777 
00778 NS_IMETHODIMP
00779 nsMenuBarFrame::CreateDismissalListener()
00780 {
00781   NS_ADDREF(nsMenuFrame::sDismissalListener = new nsMenuDismissalListener());
00782   return NS_OK;
00783 }
00784 
00785 NS_IMETHODIMP
00786 nsMenuBarFrame::InstallKeyboardNavigator()
00787 {
00788   if (mKeyboardNavigator)
00789     return NS_OK;
00790 
00791   mKeyboardNavigator = new nsMenuListener(this);
00792   NS_IF_ADDREF(mKeyboardNavigator);
00793 
00794   mTarget->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); 
00795   mTarget->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);  
00796   mTarget->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);   
00797   
00798   return NS_OK;
00799 }
00800 
00801 NS_IMETHODIMP
00802 nsMenuBarFrame::RemoveKeyboardNavigator()
00803 {
00804   if (!mKeyboardNavigator || mIsActive)
00805     return NS_OK;
00806 
00807   mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
00808   mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
00809   mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE);
00810   
00811   NS_IF_RELEASE(mKeyboardNavigator);
00812 
00813   return NS_OK;
00814 }
00815 
00816 // helpers ///////////////////////////////////////////////////////////
00817 
00818 PRBool 
00819 nsMenuBarFrame::IsValidItem(nsIContent* aContent)
00820 {
00821   nsIAtom *tag = aContent->Tag();
00822 
00823   return ((tag == nsXULAtoms::menu ||
00824            tag == nsXULAtoms::menuitem) &&
00825           !IsDisabled(aContent));
00826 }
00827 
00828 PRBool 
00829 nsMenuBarFrame::IsDisabled(nsIContent* aContent)
00830 {
00831   nsString disabled;
00832   aContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, disabled);
00833   if (disabled.EqualsLiteral("true"))
00834     return PR_TRUE;
00835   return PR_FALSE;
00836 }
00837 
00838 NS_IMETHODIMP
00839 nsMenuBarFrame::Destroy(nsPresContext* aPresContext)
00840 {
00841   mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); 
00842   mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);  
00843   mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE);
00844 
00845   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);
00846   mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);
00847 
00848   NS_IF_RELEASE(mMenuBarListener);
00849 
00850   return nsBoxFrame::Destroy(aPresContext);
00851 }
00852