Back to index

lightning-sunbird  0.9+nobinonly
nsMenuBarX.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsIServiceManager.h"
00040 #include "nsIComponentManager.h"
00041 #include "nsINameSpaceManager.h"
00042 #include "nsIMenu.h"
00043 #include "nsIMenuItem.h"
00044 #include "nsIContent.h"
00045 
00046 #include "nsMenuBarX.h"
00047 #include "nsMenuX.h"
00048 
00049 #include "nsISupports.h"
00050 #include "nsIWidget.h"
00051 #include "nsString.h"
00052 #include "nsIStringBundle.h"
00053 #include "nsIDocument.h"
00054 #include "nsIDocShell.h"
00055 #include "nsIDocumentViewer.h"
00056 #include "nsIDocumentObserver.h"
00057 
00058 #include "nsIDOMDocument.h"
00059 #include "nsWidgetAtoms.h"
00060 
00061 #include <Menus.h>
00062 #include <TextUtils.h>
00063 #include <Balloons.h>
00064 #include <Resources.h>
00065 #include <Appearance.h>
00066 #include <Gestalt.h>
00067 
00068 #include "nsMacResources.h"
00069 
00070 #include "nsGUIEvent.h"
00071 
00072 #if !XP_MACOSX
00073 #include "MenuSharing.h"
00074 #endif
00075 
00076 // CIDs
00077 #include "nsWidgetsCID.h"
00078 static NS_DEFINE_CID(kMenuCID, NS_MENU_CID);
00079 
00080 NS_IMPL_ISUPPORTS6(nsMenuBarX, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, 
00081                     nsIChangeManager, nsIMenuCommandDispatcher, nsISupportsWeakReference)
00082 
00083 MenuRef nsMenuBarX::sAppleMenu = nsnull;
00084 EventHandlerUPP nsMenuBarX::sCommandEventHandler = nsnull;
00085 
00086 
00087 //
00088 // nsMenuBarX constructor
00089 //
00090 nsMenuBarX::nsMenuBarX()
00091   : mNumMenus(0), mParent(nsnull), mIsMenuBarAdded(PR_FALSE), mCurrentCommandID(1), mDocument(nsnull)
00092 {
00093   OSStatus status = ::CreateNewMenu(0, 0, &mRootMenu);
00094   NS_ASSERTION(status == noErr, "nsMenuBarX::nsMenuBarX:  creation of root menu failed.");
00095   
00096   // create our global carbon event command handler shared by all windows
00097   if ( !sCommandEventHandler )
00098     sCommandEventHandler = ::NewEventHandlerUPP(CommandEventHandler);
00099 }
00100 
00101 //
00102 // nsMenuBarX destructor
00103 //
00104 nsMenuBarX::~nsMenuBarX()
00105 {
00106   mMenusArray.Clear();    // release all menus
00107 
00108   // make sure we unregister ourselves as a document observer
00109   if ( mDocument ) {
00110     nsCOMPtr<nsIDocumentObserver> observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) );
00111     mDocument->RemoveObserver(observer);
00112   }
00113 
00114   if ( mRootMenu )
00115     ::ReleaseMenu(mRootMenu);
00116 }
00117 
00118 nsEventStatus 
00119 nsMenuBarX::MenuItemSelected(const nsMenuEvent & aMenuEvent)
00120 {
00121   // Dispatch menu event
00122   nsEventStatus eventStatus = nsEventStatus_eIgnore;
00123 
00124   PRUint32  numItems;
00125   mMenusArray.Count(&numItems);
00126   
00127   for (PRUint32 i = numItems; i > 0; --i)
00128   {
00129     nsCOMPtr<nsISupports>     menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
00130     nsCOMPtr<nsIMenuListener> menuListener = do_QueryInterface(menuSupports);
00131     if(menuListener)
00132     {
00133       eventStatus = menuListener->MenuItemSelected(aMenuEvent);
00134       if (nsEventStatus_eIgnore != eventStatus)
00135         return eventStatus;
00136     }
00137   }
00138   return eventStatus;
00139 }
00140 
00141 
00142 nsEventStatus 
00143 nsMenuBarX::MenuSelected(const nsMenuEvent & aMenuEvent)
00144 {
00145   // Dispatch event
00146   nsEventStatus eventStatus = nsEventStatus_eIgnore;
00147 
00148   nsCOMPtr<nsIMenuListener> menuListener;
00149   //((nsISupports*)mMenuVoidArray[i-1])->QueryInterface(NS_GET_IID(nsIMenuListener), (void**)&menuListener);
00150   //printf("gPreviousMenuStack.Count() = %d \n", gPreviousMenuStack.Count());
00151   if (menuListener) {
00152     //TODO: MenuSelected is the right thing to call...
00153     //eventStatus = menuListener->MenuSelected(aMenuEvent);
00154     eventStatus = menuListener->MenuItemSelected(aMenuEvent);
00155     if (nsEventStatus_eIgnore != eventStatus)
00156       return eventStatus;
00157   } else {
00158     // If it's the help menu, gPreviousMenuStack won't be accurate so we need to get the listener a different way 
00159     // We'll do it the old fashioned way of looping through and finding it
00160     PRUint32  numItems;
00161     mMenusArray.Count(&numItems);
00162     for (PRUint32 i = numItems; i > 0; --i)
00163     {
00164       nsCOMPtr<nsISupports>     menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
00165       nsCOMPtr<nsIMenuListener> thisListener = do_QueryInterface(menuSupports);
00166            if (thisListener)
00167            {
00168         //TODO: MenuSelected is the right thing to call...
00169            //eventStatus = menuListener->MenuSelected(aMenuEvent);
00170            eventStatus = thisListener->MenuItemSelected(aMenuEvent);
00171            if(nsEventStatus_eIgnore != eventStatus)
00172              return eventStatus;
00173       }
00174     }
00175   }
00176   return eventStatus;
00177 }
00178 
00179 
00180 nsEventStatus 
00181 nsMenuBarX::MenuDeselected(const nsMenuEvent & aMenuEvent)
00182 {
00183   return nsEventStatus_eIgnore;
00184 }
00185 
00186 nsEventStatus 
00187 nsMenuBarX::CheckRebuild(PRBool & aNeedsRebuild)
00188 {
00189   aNeedsRebuild = PR_TRUE;
00190   return nsEventStatus_eIgnore;
00191 }
00192 
00193 nsEventStatus
00194 nsMenuBarX::SetRebuild(PRBool aNeedsRebuild)
00195 {
00196   return nsEventStatus_eIgnore;
00197 }
00198 
00199 void
00200 nsMenuBarX :: GetDocument ( nsIDocShell* inDocShell, nsIDocument** outDocument )
00201 {
00202   *outDocument = nsnull;
00203   
00204   if ( inDocShell ) {
00205     nsCOMPtr<nsIContentViewer> cv;
00206     inDocShell->GetContentViewer(getter_AddRefs(cv));
00207     if (cv) {
00208       // get the document
00209       nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(cv));
00210       if (!docv)
00211         return;
00212       docv->GetDocument(outDocument);    // addrefs
00213     }
00214   }
00215 }
00216 
00217 
00218 //
00219 // RegisterAsDocumentObserver
00220 //
00221 // Name says it all.
00222 //
00223 void
00224 nsMenuBarX :: RegisterAsDocumentObserver ( nsIDocShell* inDocShell )
00225 {
00226   nsCOMPtr<nsIDocument> doc;
00227   GetDocument(inDocShell, getter_AddRefs(doc));
00228   if (!doc)
00229     return;
00230 
00231   // register ourselves
00232   nsCOMPtr<nsIDocumentObserver> observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) );
00233   doc->AddObserver(observer);
00234   // also get pointer to doc, just in case docshell goes away
00235   // we can still remove ourself as doc observer directly from doc
00236   mDocument = doc;
00237 } // RegisterAsDocumentObesrver
00238 
00239 
00240 //
00241 // AquifyMenuBar
00242 //
00243 // Do what's necessary to conform to the Aqua guidelines for menus. Initially, this
00244 // means removing 'Quit' from the file menu and 'Preferences' from the edit menu, along
00245 // with their various separators (if present).
00246 //
00247 void
00248 nsMenuBarX :: AquifyMenuBar ( )
00249 {
00250   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mMenuBarContent->GetDocument()));
00251   if ( domDoc ) {
00252     // remove quit item and its separator
00253     HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitSeparator"), nsnull );
00254     HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitItem"), getter_AddRefs(mQuitItemContent) );
00255   
00256     // remove prefs item and its separator, but save off the pref content node
00257     // so we can invoke its command later.
00258     HideItem ( domDoc, NS_LITERAL_STRING("menu_PrefsSeparator"), nsnull );
00259     HideItem ( domDoc, NS_LITERAL_STRING("menu_preferences"), getter_AddRefs(mPrefItemContent) );
00260     if (mPrefItemContent)
00261       ::EnableMenuCommand(NULL, kHICommandPreferences);
00262 
00263     // Cocoa menus compatibility - hide items that we use for the Application menu in Cocoa menus.
00264     // This way, a menu setup for the Cocoa application menu will also work for Carbon menus.
00265     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_services"), nsnull);
00266     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_app"), nsnull);
00267     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_hide_others"), nsnull);
00268     HideItem(domDoc, NS_LITERAL_STRING("menu_mac_show_all"), nsnull);
00269   }
00270       
00271 } // AquifyMenuBar
00272 
00273 
00274 //
00275 // InstallCommandEventHandler
00276 //
00277 // Grab our window and install an event handler to handle command events which are
00278 // used to drive the action when the user chooses an item from a menu. We have to install
00279 // it on the window because the menubar isn't in the event chain for a menu command event.
00280 //
00281 OSStatus
00282 nsMenuBarX :: InstallCommandEventHandler ( )
00283 {
00284   OSStatus err = noErr;
00285   
00286   WindowRef myWindow = NS_REINTERPRET_CAST(WindowRef, mParent->GetNativeData(NS_NATIVE_DISPLAY));
00287   NS_ASSERTION ( myWindow, "Can't get WindowRef to install command handler!" );
00288   if ( myWindow && sCommandEventHandler ) {
00289     const EventTypeSpec commandEventList[] = {
00290      {kEventClassCommand, kEventCommandProcess},
00291     };
00292     err = ::InstallWindowEventHandler(myWindow,
00293                                       sCommandEventHandler,
00294                                       GetEventTypeCount(commandEventList),
00295                                       commandEventList,
00296                                       this,
00297                                       NULL);
00298     NS_ASSERTION ( err == noErr, "Uh oh, command handler not installed" );
00299   }
00300 
00301   return err;
00302   
00303 } // InstallCommandEventHandler
00304 
00305 
00306 //
00307 // CommandEventHandler
00308 //
00309 // Processes Command carbon events from selecting of items in the menu.
00310 //
00311 pascal OSStatus
00312 nsMenuBarX :: CommandEventHandler ( EventHandlerCallRef inHandlerChain, EventRef inEvent, void* userData )
00313 {
00314   OSStatus handled = eventNotHandledErr;
00315 
00316   HICommand command;
00317   OSErr err1 = ::GetEventParameter ( inEvent, kEventParamDirectObject, typeHICommand,
00318                                         NULL, sizeof(HICommand), NULL, &command );  
00319   if ( err1 )
00320     return handled;
00321 
00322   NS_ASSERTION(::GetEventKind(inEvent) == kEventCommandProcess,
00323                "CommandEventHandler asked to handle unknown event kind");
00324 
00325   nsMenuBarX* self = NS_REINTERPRET_CAST(nsMenuBarX*, userData);
00326   switch (command.commandID) {
00327     case kHICommandPreferences: {
00328       nsEventStatus status = self->ExecuteCommand(self->mPrefItemContent);
00329       if (status == nsEventStatus_eConsumeNoDefault)
00330         // event handled, no other processing
00331         handled = noErr;
00332       break;
00333     }
00334 
00335     case kHICommandQuit: {
00336       nsEventStatus status = self->ExecuteCommand(self->mQuitItemContent);
00337       if (status == nsEventStatus_eConsumeNoDefault)
00338         // event handled, no other processing
00339         handled = noErr;
00340       break;
00341     }
00342 
00343     case kHICommandAbout: {
00344       // the 'about' command is special because we don't have a
00345       // nsIMenu or nsIMenuItem for the apple menu. Grovel for the
00346       // content node with an id of "aboutName" and call it
00347       // directly.
00348       nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(self->mDocument);
00349       if (domDoc) {
00350         nsCOMPtr<nsIDOMElement> domElement;
00351        domDoc->GetElementById(NS_LITERAL_STRING("aboutName"),
00352                                getter_AddRefs(domElement));
00353        nsCOMPtr<nsIContent> aboutContent (do_QueryInterface(domElement));
00354         self->ExecuteCommand(aboutContent);
00355       }
00356       handled = noErr;
00357       break;
00358     }
00359 
00360     default: {
00361       // given the commandID, look it up in our hashtable and dispatch to
00362       // that content node. Recall that we store weak pointers to the content
00363       // nodes in the hash table.
00364       nsPRUint32Key key(command.commandID);
00365       nsIMenuItem* content = NS_REINTERPRET_CAST(nsIMenuItem*,
00366                                                self->mObserverTable.Get(&key));
00367       if (content) {
00368         content->DoCommand();
00369         handled = noErr;
00370       }
00371       break;
00372     }
00373   }
00374   
00375   return handled;
00376   
00377 } // CommandEventHandler
00378 
00379 
00380 //
00381 // ExecuteCommand
00382 //
00383 // Execute the menu item by sending a command message to the 
00384 // DOM node specified in |inDispatchTo|.
00385 //
00386 nsEventStatus
00387 nsMenuBarX :: ExecuteCommand ( nsIContent* inDispatchTo )
00388 {
00389   if (!inDispatchTo)
00390     return nsEventStatus_eIgnore;
00391 
00392   return MenuHelpersX::DispatchCommandTo(mDocShellWeakRef, inDispatchTo);
00393 } // ExecuteCommand
00394 
00395 
00396 //
00397 // HideItem
00398 //
00399 // Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
00400 // the caller can hang onto it if they so choose. It is acceptable to pass nsull
00401 // for |outHiddenNode| if the caller doesn't care about the hidden node.
00402 //
00403 void
00404 nsMenuBarX :: HideItem ( nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode )
00405 {
00406   nsCOMPtr<nsIDOMElement> menuItem;
00407   inDoc->GetElementById(inID, getter_AddRefs(menuItem));  
00408   nsCOMPtr<nsIContent> menuContent ( do_QueryInterface(menuItem) );
00409   if ( menuContent ) {
00410     menuContent->SetAttr ( kNameSpaceID_None, nsWidgetAtoms::hidden, NS_LITERAL_STRING("true"), PR_FALSE );
00411     if ( outHiddenNode ) {
00412       *outHiddenNode = menuContent.get();
00413       NS_IF_ADDREF(*outHiddenNode);
00414     }
00415   }
00416 
00417 } // HideItem
00418 
00419 
00420 nsEventStatus
00421 nsMenuBarX::MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, 
00422                             void * menubarNode, void * aDocShell )
00423 {
00424   mDocShellWeakRef = do_GetWeakReference(NS_STATIC_CAST(nsIDocShell*, aDocShell));
00425   nsIDOMNode* aDOMNode  = NS_STATIC_CAST(nsIDOMNode*, menubarNode);
00426   mMenuBarContent = do_QueryInterface(aDOMNode);           // strong ref
00427   NS_ASSERTION ( mMenuBarContent, "No content specified for this menubar" );
00428   if ( !mMenuBarContent )
00429     return nsEventStatus_eIgnore;
00430     
00431   Create(aParentWindow);
00432   
00433   // if we're on X (using aqua UI guidelines for menus), remove quit and prefs
00434   // from our menubar.
00435   SInt32 result = 0L;
00436   OSStatus err = ::Gestalt ( gestaltMenuMgrAttr, &result );
00437   if ( !err && (result & gestaltMenuMgrAquaLayoutMask) )
00438     AquifyMenuBar();
00439   err = InstallCommandEventHandler();
00440   if ( err )
00441     return nsEventStatus_eIgnore;
00442 
00443   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShellWeakRef);
00444   if (docShell) RegisterAsDocumentObserver(docShell);
00445 
00446   // set this as a nsMenuListener on aParentWindow
00447   aParentWindow->AddMenuListener((nsIMenuListener *)this);
00448 
00449   PRUint32 count = mMenuBarContent->GetChildCount();
00450   for ( PRUint32 i = 0; i < count; ++i ) { 
00451     nsIContent *menu = mMenuBarContent->GetChildAt(i);
00452     if ( menu ) {
00453       if (menu->Tag() == nsWidgetAtoms::menu &&
00454           menu->IsContentOfType(nsIContent::eXUL)) {
00455         nsAutoString menuName;
00456         nsAutoString menuAccessKey(NS_LITERAL_STRING(" "));
00457         menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
00458         menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::accesskey, menuAccessKey);
00459                        
00460         // Don't create the whole menu yet, just add in the top level names
00461               
00462         // Create nsMenu, the menubar will own it
00463         nsCOMPtr<nsIMenu> pnsMenu ( do_CreateInstance(kMenuCID) );
00464         if ( pnsMenu ) {
00465           pnsMenu->Create(NS_STATIC_CAST(nsIMenuBar*, this), menuName, menuAccessKey, 
00466                           NS_STATIC_CAST(nsIChangeManager *, this), 
00467                           NS_REINTERPRET_CAST(nsIDocShell*, aDocShell), menu);
00468 
00469           // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership
00470           AddMenu(pnsMenu); 
00471                   
00472           nsAutoString menuIDstring;
00473           menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring);
00474           if ( menuIDstring.EqualsLiteral("menu_Help") ) {
00475             nsMenuEvent event(PR_TRUE, 0, nsnull);
00476             MenuHandle handle = nsnull;
00477             event.mCommand = (unsigned int) handle;
00478             nsCOMPtr<nsIMenuListener> listener(do_QueryInterface(pnsMenu));
00479             listener->MenuSelected(event);
00480           }          
00481         }
00482       } 
00483     }
00484   } // for each menu
00485 
00486   // Give the aParentWindow this nsMenuBarX to hold onto.
00487   // The parent takes ownership
00488   aParentWindow->SetMenuBar(this);
00489 
00490   return nsEventStatus_eIgnore;
00491 }
00492 
00493 
00494 nsEventStatus 
00495 nsMenuBarX::MenuDestruct(const nsMenuEvent & aMenuEvent)
00496 {
00497   return nsEventStatus_eIgnore;
00498 }
00499 
00500 
00501 //-------------------------------------------------------------------------
00502 //
00503 // Create the proper widget
00504 //
00505 //-------------------------------------------------------------------------
00506 NS_METHOD nsMenuBarX::Create(nsIWidget *aParent)
00507 {
00508   SetParent(aParent);
00509   return NS_OK;
00510 }
00511 
00512 //-------------------------------------------------------------------------
00513 NS_METHOD nsMenuBarX::GetParent(nsIWidget *&aParent)
00514 {
00515   NS_IF_ADDREF(aParent = mParent);
00516   return NS_OK;
00517 }
00518 
00519 
00520 //-------------------------------------------------------------------------
00521 NS_METHOD nsMenuBarX::SetParent(nsIWidget *aParent)
00522 {
00523   mParent = aParent;    // weak ref  
00524   return NS_OK;
00525 }
00526 
00527 //-------------------------------------------------------------------------
00528 NS_METHOD nsMenuBarX::AddMenu(nsIMenu * aMenu)
00529 {
00530   // keep track of all added menus.
00531   mMenusArray.AppendElement(aMenu);    // owner
00532 
00533   if (mNumMenus == 0) {
00534     // if apple menu hasn't been created, create it.
00535     if ( !sAppleMenu ) {
00536       nsresult rv = CreateAppleMenu(aMenu);
00537       NS_ASSERTION ( NS_SUCCEEDED(rv), "Can't create Apple menu" );
00538     }
00539     
00540     // add shared Apple menu to our menubar
00541     if ( sAppleMenu ) {
00542       // InsertMenuItem() is 1-based, so the apple/application menu needs to
00543       // be at index 1. |mNumMenus| will be incremented below, so the following menu (File)
00544       // won't overwrite the apple menu by reusing the ID.
00545       mNumMenus = 1;
00546       ::InsertMenuItem(mRootMenu, "\pA", mNumMenus);
00547       OSStatus status = ::SetMenuItemHierarchicalMenu(mRootMenu, 1, sAppleMenu);
00548                      NS_ASSERTION(status == noErr, "OS problem with SetMenuItemHierarchicalMenu");
00549     }
00550   }
00551 
00552   MenuRef menuRef = nsnull;
00553   aMenu->GetNativeData((void**)&menuRef);
00554 
00555   nsCOMPtr<nsIContent> menu;
00556   aMenu->GetMenuContent(getter_AddRefs(menu));
00557   nsAutoString menuHidden;
00558   menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, menuHidden);
00559   if(!menuHidden.EqualsLiteral("true") && menu->GetChildCount() > 0) {
00560     // make sure we only increment |mNumMenus| if the menu is visible, since
00561     // we use it as an index of where to insert the next menu.
00562     mNumMenus++;
00563     
00564     ::InsertMenuItem(mRootMenu, "\pPlaceholder", mNumMenus);
00565     OSStatus status = ::SetMenuItemHierarchicalMenu(mRootMenu, mNumMenus, menuRef);
00566     NS_ASSERTION(status == noErr, "nsMenuBarX::AddMenu: SetMenuItemHierarchicalMenu failed.");
00567   }
00568 
00569   return NS_OK;
00570 }
00571 
00572 
00573 //
00574 // CreateAppleMenu
00575 //
00576 // build the Apple menu shared by all menu bars.
00577 //
00578 nsresult
00579 nsMenuBarX :: CreateAppleMenu ( nsIMenu* inMenu )
00580 {
00581   Str32 menuStr = { 1, kMenuAppleLogoFilledGlyph };
00582   OSStatus s = ::CreateNewMenu(kAppleMenuID, 0, &sAppleMenu);
00583 
00584   if ( s == noErr && sAppleMenu )  {
00585     ::SetMenuTitle(sAppleMenu, menuStr);
00586     
00587     // this code reads the "label" attribute from the <menuitem/> with
00588     // id="aboutName" and puts its label in the Apple Menu
00589     nsAutoString label;
00590     nsCOMPtr<nsIContent> menu;
00591     inMenu->GetMenuContent(getter_AddRefs(menu));
00592     if (menu) {
00593       nsCOMPtr<nsIDocument> doc = menu->GetDocument();
00594       if (doc) {
00595         nsCOMPtr<nsIDOMDocument> domdoc ( do_QueryInterface(doc) );
00596         if ( domdoc ) {
00597           nsCOMPtr<nsIDOMElement> aboutMenuItem;
00598           domdoc->GetElementById(NS_LITERAL_STRING("aboutName"), getter_AddRefs(aboutMenuItem));
00599           if (aboutMenuItem)
00600             aboutMenuItem->GetAttribute(NS_LITERAL_STRING("label"), label);
00601         }
00602       }
00603     }
00604 
00605     CFStringRef labelRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)label.get(), label.Length());
00606     if ( labelRef ) {
00607       ::InsertMenuItemTextWithCFString(sAppleMenu, labelRef, 1, 0, 0);
00608       ::CFRelease(labelRef);
00609     }
00610     
00611     ::SetMenuItemCommandID(sAppleMenu, 1, kHICommandAbout);
00612 
00613     ::AppendMenu(sAppleMenu, "\p-");
00614   }
00615 
00616   return (s == noErr && sAppleMenu) ? NS_OK : NS_ERROR_FAILURE;
00617 }
00618 
00619         
00620 //-------------------------------------------------------------------------
00621 NS_METHOD nsMenuBarX::GetMenuCount(PRUint32 &aCount)
00622 {
00623   aCount = mNumMenus;
00624   return NS_OK;
00625 }
00626 
00627 //-------------------------------------------------------------------------
00628 NS_METHOD nsMenuBarX::GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
00629 { 
00630   aMenu = NULL;
00631   nsCOMPtr<nsISupports> supports = getter_AddRefs(mMenusArray.ElementAt(aCount));
00632   if (!supports) return NS_OK;
00633   
00634   return CallQueryInterface(supports, &aMenu); // addref
00635 }
00636 
00637 //-------------------------------------------------------------------------
00638 NS_METHOD nsMenuBarX::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
00639 {
00640   return NS_OK;
00641 }
00642 
00643 //-------------------------------------------------------------------------
00644 NS_METHOD nsMenuBarX::RemoveMenu(const PRUint32 aCount)
00645 {
00646   mMenusArray.RemoveElementAt(aCount);
00647   ::DeleteMenuItem(mRootMenu, aCount + 1);    // MenuManager is 1-based
00648   ::DrawMenuBar();
00649   return NS_OK;
00650 }
00651 
00652 //-------------------------------------------------------------------------
00653 NS_METHOD nsMenuBarX::RemoveAll()
00654 {
00655   NS_ASSERTION(0, "Not implemented!");
00656   // mMenusArray.Clear();    // maybe?
00657   return NS_OK;
00658 }
00659 
00660 //-------------------------------------------------------------------------
00661 NS_METHOD nsMenuBarX::GetNativeData(void *& aData)
00662 {
00663     aData = (void *) mRootMenu;
00664     return NS_OK;
00665 }
00666 
00667 //-------------------------------------------------------------------------
00668 NS_METHOD nsMenuBarX::SetNativeData(void* aData)
00669 {
00670 #if 0
00671     Handle menubarHandle = (Handle)aData;
00672     if (mMacMBarHandle && mMacMBarHandle != menubarHandle)
00673         ::DisposeHandle(mMacMBarHandle);
00674     mMacMBarHandle = menubarHandle;
00675 #endif
00676     return NS_OK;
00677 }
00678 
00679 //-------------------------------------------------------------------------
00680 NS_METHOD nsMenuBarX::Paint()
00681 {
00682     // hack to correctly swap menu bars.
00683     // hopefully this is fast enough.
00684     ::SetRootMenu(mRootMenu);
00685     ::DrawMenuBar();
00686     return NS_OK;
00687 }
00688 
00689 #pragma mark -
00690 
00691 //
00692 // nsIDocumentObserver
00693 // this is needed for menubar changes
00694 //
00695 
00696 NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsMenuBarX)
00697 NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsMenuBarX)
00698 NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsMenuBarX)
00699 NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsMenuBarX)
00700 
00701 void
00702 nsMenuBarX::BeginUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType )
00703 {
00704 }
00705 
00706 void
00707 nsMenuBarX::EndUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType )
00708 {
00709 }
00710 
00711 void
00712 nsMenuBarX::CharacterDataChanged( nsIDocument * aDocument,
00713                                   nsIContent * aContent, PRBool aAppend)
00714 {
00715 }
00716 
00717 void
00718 nsMenuBarX::ContentAppended( nsIDocument * aDocument, nsIContent  * aContainer,
00719                               PRInt32 aNewIndexInContainer)
00720 {
00721   if ( aContainer == mMenuBarContent ) {
00722     //Register(aContainer, );
00723     //InsertMenu ( aNewIndexInContainer );
00724   }
00725   else {
00726     nsCOMPtr<nsIChangeObserver> obs;
00727     Lookup ( aContainer, getter_AddRefs(obs) );
00728     if ( obs )
00729       obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
00730     else {
00731       nsCOMPtr<nsIContent> parent = aContainer->GetParent();
00732       if(parent) {
00733         Lookup ( parent, getter_AddRefs(obs) );
00734         if ( obs )
00735           obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
00736       }
00737     }
00738   }
00739 }
00740 
00741 void
00742 nsMenuBarX::DocumentWillBeDestroyed( nsIDocument * aDocument )
00743 {
00744   mDocument = nsnull;
00745 }
00746 
00747 
00748 void
00749 nsMenuBarX::AttributeChanged( nsIDocument * aDocument, nsIContent * aContent,
00750                               PRInt32 aNameSpaceID, nsIAtom * aAttribute,
00751                               PRInt32 aModType )
00752 {
00753   // lookup and dispatch to registered thang.
00754   nsCOMPtr<nsIChangeObserver> obs;
00755   Lookup ( aContent, getter_AddRefs(obs) );
00756   if ( obs )
00757     obs->AttributeChanged(aDocument, aNameSpaceID, aContent, aAttribute);
00758 }
00759 
00760 void
00761 nsMenuBarX::ContentRemoved( nsIDocument * aDocument, nsIContent * aContainer,
00762                             nsIContent * aChild, PRInt32 aIndexInContainer )
00763 {  
00764   if ( aContainer == mMenuBarContent ) {
00765     Unregister(aChild);
00766     RemoveMenu ( aIndexInContainer );
00767   }
00768   else {
00769     nsCOMPtr<nsIChangeObserver> obs;
00770     Lookup ( aContainer, getter_AddRefs(obs) );
00771     if ( obs )
00772       obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
00773     else {
00774       nsCOMPtr<nsIContent> parent = aContainer->GetParent();
00775       if(parent) {
00776         Lookup ( parent, getter_AddRefs(obs) );
00777         if ( obs )
00778           obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
00779       }
00780     }
00781   }
00782 }
00783 
00784 void
00785 nsMenuBarX::ContentInserted( nsIDocument * aDocument, nsIContent * aContainer,
00786                              nsIContent * aChild, PRInt32 aIndexInContainer )
00787 {  
00788   if ( aContainer == mMenuBarContent ) {
00789     //Register(aChild, );
00790     //InsertMenu ( aIndexInContainer );
00791   }
00792   else {
00793     nsCOMPtr<nsIChangeObserver> obs;
00794     Lookup ( aContainer, getter_AddRefs(obs) );
00795     if ( obs )
00796       obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
00797     else {
00798       nsCOMPtr<nsIContent> parent = aContainer->GetParent();
00799       if(parent) {
00800         Lookup ( parent, getter_AddRefs(obs) );
00801         if ( obs )
00802           obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
00803       }
00804     }
00805   }
00806 }
00807 
00808 #pragma mark - 
00809 
00810 //
00811 // nsIChangeManager
00812 //
00813 // We don't use a |nsSupportsHashtable| because we know that the lifetime of all these items
00814 // is bouded by the lifetime of the menubar. No need to add any more strong refs to the
00815 // picture because the containment hierarchy already uses strong refs.
00816 //
00817 
00818 NS_IMETHODIMP 
00819 nsMenuBarX :: Register ( nsIContent *aContent, nsIChangeObserver *aMenuObject )
00820 {
00821   nsVoidKey key ( aContent );
00822   mObserverTable.Put ( &key, aMenuObject );
00823   
00824   return NS_OK;
00825 }
00826 
00827 
00828 NS_IMETHODIMP 
00829 nsMenuBarX :: Unregister ( nsIContent *aContent )
00830 {
00831   nsVoidKey key ( aContent );
00832   mObserverTable.Remove ( &key );
00833   
00834   return NS_OK;
00835 }
00836 
00837 
00838 NS_IMETHODIMP 
00839 nsMenuBarX :: Lookup ( nsIContent *aContent, nsIChangeObserver **_retval )
00840 {
00841   *_retval = nsnull;
00842   
00843   nsVoidKey key ( aContent );
00844   *_retval = NS_REINTERPRET_CAST(nsIChangeObserver*, mObserverTable.Get(&key));
00845   NS_IF_ADDREF ( *_retval );
00846   
00847   return NS_OK;
00848 }
00849 
00850 
00851 #pragma mark -
00852 
00853 
00854 //
00855 // Implementation methods for nsIMenuCommandDispatcher
00856 //
00857 
00858 
00859 //
00860 // Register
00861 //
00862 // Given a menu item, creates a unique 4-character command ID and
00863 // maps it to the item. Returns the id for use by the client.
00864 //
00865 NS_IMETHODIMP 
00866 nsMenuBarX :: Register ( nsIMenuItem* inMenuItem, PRUint32* outCommandID )
00867 {
00868   // no real need to check for uniqueness. We always start afresh with each
00869   // window at 1. Even if we did get close to the reserved Apple command id's,
00870   // those don't start until at least '    ', which is integer 538976288. If
00871   // we have that many menu items in one window, I think we have other problems.
00872   
00873   // put it in the table, set out param for client
00874   nsPRUint32Key key ( mCurrentCommandID );
00875   mObserverTable.Put ( &key, inMenuItem );
00876   *outCommandID = mCurrentCommandID;
00877 
00878   // make id unique for next time
00879   ++mCurrentCommandID;
00880   
00881   return NS_OK;
00882 }
00883 
00884 
00885 // 
00886 // Unregister
00887 //
00888 // Removes the mapping between the given 4-character command ID
00889 // and its associated menu item.
00890 //
00891 NS_IMETHODIMP 
00892 nsMenuBarX :: Unregister ( PRUint32 inCommandID )
00893 {
00894   nsPRUint32Key key ( inCommandID );
00895   mObserverTable.Remove ( &key );
00896 
00897   return NS_OK;
00898 }
00899 
00900 
00901 #pragma mark -
00902 
00903 
00904 //
00905 // DocShellToPresContext
00906 //
00907 // Helper to dig out a pres context from a docshell. A common thing to do before
00908 // sending an event into the dom.
00909 //
00910 nsresult
00911 MenuHelpersX::DocShellToPresContext (nsIDocShell* inDocShell, nsPresContext** outContext )
00912 {
00913   NS_ENSURE_ARG_POINTER(outContext);
00914   *outContext = nsnull;
00915   if (!inDocShell)
00916     return NS_ERROR_INVALID_ARG;
00917   
00918   nsresult retval = NS_OK;
00919   
00920   nsCOMPtr<nsIContentViewer> contentViewer;
00921   inDocShell->GetContentViewer(getter_AddRefs(contentViewer));
00922   if ( contentViewer ) {
00923     nsCOMPtr<nsIDocumentViewer> docViewer ( do_QueryInterface(contentViewer) );
00924     if ( docViewer )
00925       docViewer->GetPresContext(outContext);     // AddRefs for us
00926     else
00927       retval = NS_ERROR_FAILURE;
00928   }
00929   else
00930     retval = NS_ERROR_FAILURE;
00931   
00932   return retval;
00933   
00934 } // DocShellToPresContext
00935 
00936 nsEventStatus
00937 MenuHelpersX::DispatchCommandTo(nsIWeakReference* aDocShellWeakRef,
00938                                 nsIContent* aTargetContent)
00939 {
00940   NS_PRECONDITION(aTargetContent, "null ptr");
00941 
00942   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(aDocShellWeakRef);
00943   if (!docShell)
00944     return nsEventStatus_eConsumeNoDefault;
00945   nsCOMPtr<nsPresContext> presContext;
00946   MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext));
00947 
00948   nsEventStatus status = nsEventStatus_eConsumeNoDefault;
00949   nsXULCommandEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull);
00950 
00951   // FIXME: Should probably figure out how to init this with the actual
00952   // pressed keys, but this is a big old edge case anyway. -dwh
00953 
00954   aTargetContent->HandleDOMEvent(presContext, &event, nsnull,
00955                                  NS_EVENT_FLAG_INIT, &status);
00956   return status;
00957 }