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