/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" #include "nsINameSpaceManager.h" #include "nsIMenu.h" #include "nsIMenuItem.h" #include "nsIContent.h" #include "nsMenuBarX.h" #include "nsMenuX.h" #include "nsDynamicMDEF.h" #include "nsISupports.h" #include "nsIWidget.h" #include "nsString.h" #include "nsIStringBundle.h" #include "nsIDocument.h" #include "nsIDocShell.h" #include "nsIDocumentViewer.h" #include "nsIDocumentObserver.h" #include "nsIDOMXULDocument.h" #include "nsWidgetAtoms.h" #include #include #include #include #include #include #include #include "nsMacResources.h" #include "nsGUIEvent.h" // CIDs #include "nsWidgetsCID.h" static NS_DEFINE_CID(kMenuBarCID, NS_MENUBAR_CID); static NS_DEFINE_CID(kMenuCID, NS_MENU_CID); static NS_DEFINE_CID(kMenuItemCID, NS_MENUITEM_CID); NS_IMPL_ISUPPORTS6(nsMenuBarX, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, nsIChangeManager, nsIMenuCommandDispatcher, nsISupportsWeakReference) MenuRef nsMenuBarX::sAppleMenu = nsnull; EventHandlerUPP nsMenuBarX::sCommandEventHandler = nsnull; // // nsMenuBarX constructor // nsMenuBarX::nsMenuBarX() : mNumMenus(0), mParent(nsnull), mIsMenuBarAdded(PR_FALSE), mDocument(nsnull), mCurrentCommandID(1) { NS_INIT_REFCNT(); OSStatus status = ::CreateNewMenu(0, 0, &mRootMenu); NS_ASSERTION(status == noErr, "nsMenuBarX::nsMenuBarX: creation of root menu failed."); // create our global carbon event command handler shared by all windows if ( !sCommandEventHandler ) sCommandEventHandler = ::NewEventHandlerUPP(CommandEventHandler); } // // nsMenuBarX destructor // nsMenuBarX::~nsMenuBarX() { mMenusArray.Clear(); // release all menus // make sure we unregister ourselves as a document observer if ( mDocument ) { nsCOMPtr observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) ); mDocument->RemoveObserver(observer); } if ( mRootMenu ) ::ReleaseMenu(mRootMenu); } nsEventStatus nsMenuBarX::MenuItemSelected(const nsMenuEvent & aMenuEvent) { // Dispatch menu event nsEventStatus eventStatus = nsEventStatus_eIgnore; PRUint32 numItems; mMenusArray.Count(&numItems); for (PRUint32 i = numItems; i > 0; --i) { nsCOMPtr menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1)); nsCOMPtr menuListener = do_QueryInterface(menuSupports); if(menuListener) { eventStatus = menuListener->MenuItemSelected(aMenuEvent); if (nsEventStatus_eIgnore != eventStatus) return eventStatus; } } return eventStatus; } nsEventStatus nsMenuBarX::MenuSelected(const nsMenuEvent & aMenuEvent) { // Dispatch event nsEventStatus eventStatus = nsEventStatus_eIgnore; nsCOMPtr menuListener; //((nsISupports*)mMenuVoidArray[i-1])->QueryInterface(NS_GET_IID(nsIMenuListener), (void**)&menuListener); //printf("gPreviousMenuStack.Count() = %d \n", gPreviousMenuStack.Count()); #if !TARGET_CARBON nsCOMPtr theMenu; gPreviousMenuStack.GetMenuAt(gPreviousMenuStack.Count() - 1, getter_AddRefs(theMenu)); menuListener = do_QueryInterface(theMenu); #endif if (menuListener) { //TODO: MenuSelected is the right thing to call... //eventStatus = menuListener->MenuSelected(aMenuEvent); eventStatus = menuListener->MenuItemSelected(aMenuEvent); if (nsEventStatus_eIgnore != eventStatus) return eventStatus; } else { // If it's the help menu, gPreviousMenuStack won't be accurate so we need to get the listener a different way // We'll do it the old fashioned way of looping through and finding it PRUint32 numItems; mMenusArray.Count(&numItems); for (PRUint32 i = numItems; i > 0; --i) { nsCOMPtr menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1)); nsCOMPtr thisListener = do_QueryInterface(menuSupports); if (thisListener) { //TODO: MenuSelected is the right thing to call... //eventStatus = menuListener->MenuSelected(aMenuEvent); eventStatus = thisListener->MenuItemSelected(aMenuEvent); if(nsEventStatus_eIgnore != eventStatus) return eventStatus; } } } return eventStatus; } nsEventStatus nsMenuBarX::MenuDeselected(const nsMenuEvent & aMenuEvent) { return nsEventStatus_eIgnore; } nsEventStatus nsMenuBarX::CheckRebuild(PRBool & aNeedsRebuild) { aNeedsRebuild = PR_TRUE; return nsEventStatus_eIgnore; } nsEventStatus nsMenuBarX::SetRebuild(PRBool aNeedsRebuild) { return nsEventStatus_eIgnore; } void nsMenuBarX :: GetDocument ( nsIWebShell* inWebShell, nsIDocument** outDocument ) { *outDocument = nsnull; nsCOMPtr docShell ( do_QueryInterface(inWebShell) ); nsCOMPtr cv; if ( docShell ) { docShell->GetContentViewer(getter_AddRefs(cv)); if (cv) { // get the document nsCOMPtr docv(do_QueryInterface(cv)); if (!docv) return; docv->GetDocument(*outDocument); // addrefs } } } // // RegisterAsDocumentObserver // // Name says it all. // void nsMenuBarX :: RegisterAsDocumentObserver ( nsIWebShell* inWebShell ) { nsCOMPtr doc; GetDocument(inWebShell, getter_AddRefs(doc)); if (!doc) return; // register ourselves nsCOMPtr observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) ); doc->AddObserver(observer); // also get pointer to doc, just in case webshell goes away // we can still remove ourself as doc observer directly from doc mDocument = doc; } // RegisterAsDocumentObesrver // // AquifyMenuBar // // Do what's necessary to conform to the Aqua guidelines for menus. Initially, this // means removing 'Quit' from the file menu and 'Preferences' from the edit menu, along // with their various separators (if present). // void nsMenuBarX :: AquifyMenuBar ( ) { nsCOMPtr containingDoc; mMenuBarContent->GetDocument ( *getter_AddRefs(containingDoc) ); nsCOMPtr domDoc ( do_QueryInterface(containingDoc) ); if ( domDoc ) { // remove quit item and its separator HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitSeparator"), nsnull ); HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitItem"), getter_AddRefs(mQuitItemContent) ); // remove prefs item and its separator, but save off the pref content node // so we can invoke its command later. HideItem ( domDoc, NS_LITERAL_STRING("menu_PrefsSeparator"), nsnull ); HideItem ( domDoc, NS_LITERAL_STRING("menu_preferences"), getter_AddRefs(mPrefItemContent) ); } } // AquifyMenuBar // // InstallCommandEventHandler // // Grab our window and install an event handler to handle command events which are // used to drive the action when the user chooses an item from a menu. We have to install // it on the window because the menubar isn't in the event chain for a menu command event. // OSStatus nsMenuBarX :: InstallCommandEventHandler ( ) { OSStatus err = noErr; WindowRef myWindow = NS_REINTERPRET_CAST(WindowRef, mParent->GetNativeData(NS_NATIVE_DISPLAY)); NS_ASSERTION ( myWindow, "Can't get WindowRef to install command handler!" ); if ( myWindow && sCommandEventHandler ) { const EventTypeSpec commandEventList[] = { {kEventClassCommand, kEventCommandProcess}, {kEventClassCommand, kEventCommandUpdateStatus} }; err = ::InstallWindowEventHandler ( myWindow, sCommandEventHandler, 2, commandEventList, this, NULL ); NS_ASSERTION ( err == noErr, "Uh oh, command handler not installed" ); } return err; } // InstallCommandEventHandler // // CommandEventHandler // // Processes Command carbon events from enabling/selecting of items in the menu. // pascal OSStatus nsMenuBarX :: CommandEventHandler ( EventHandlerCallRef inHandlerChain, EventRef inEvent, void* userData ) { OSStatus handled = eventNotHandledErr; HICommand command; OSErr err1 = ::GetEventParameter ( inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command ); if ( err1 ) return handled; nsMenuBarX* self = NS_REINTERPRET_CAST(nsMenuBarX*, userData); switch ( ::GetEventKind(inEvent) ) { // user selected a menu item. See if it's one we handle. case kEventCommandProcess: { switch ( command.commandID ) { case kHICommandPreferences: { nsEventStatus status = self->ExecuteCommand(self->mPrefItemContent); if ( status == nsEventStatus_eConsumeNoDefault ) // event handled, no other processing handled = noErr; break; } case kHICommandQuit: { nsEventStatus status = self->ExecuteCommand(self->mQuitItemContent); if ( status == nsEventStatus_eConsumeNoDefault ) // event handled, no other processing handled = noErr; break; } case kHICommandAbout: { // the 'about' command is special because we don't have a nsIMenu or nsIMenuItem // for the apple menu. Grovel for the content node with an id of "aboutName" // and call it directly. nsCOMPtr xulDoc = do_QueryInterface(self->mDocument); if ( xulDoc ) { nsCOMPtr domElement; xulDoc->GetElementById(NS_LITERAL_STRING("aboutName"), getter_AddRefs(domElement)); nsCOMPtr aboutContent ( do_QueryInterface(domElement) ); self->ExecuteCommand(aboutContent); } handled = noErr; break; } default: { // given the commandID, look it up in our hashtable and dispatch to // that content node. Recall that we store weak pointers to the content // nodes in the hash table. nsPRUint32Key key ( command.commandID ); nsIMenuItem* content = NS_REINTERPRET_CAST(nsIMenuItem*, self->mObserverTable.Get(&key)); if ( content ) { content->DoCommand(); handled = noErr; } break; } } // switch on commandID break; } // enable/disable menu id's case kEventCommandUpdateStatus: { // only enable the preferences item in the app menu if we found a pref // item DOM node in this menubar. if ( command.commandID == kHICommandPreferences ) { if ( self->mPrefItemContent ) ::EnableMenuCommand ( nsnull, kHICommandPreferences ); else ::DisableMenuCommand ( nsnull, kHICommandPreferences ); handled = noErr; } break; } } // switch on event type return handled; } // CommandEventHandler // // ExecuteCommand // // Execute the menu item by sending a command message to the // DOM node specified in |inDispatchTo|. // nsEventStatus nsMenuBarX :: ExecuteCommand ( nsIContent* inDispatchTo ) { if (!inDispatchTo) return nsEventStatus_eIgnore; return MenuHelpersX::DispatchCommandTo(mWebShellWeakRef, inDispatchTo); } // ExecuteCommand // // HideItem // // Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so // the caller can hang onto it if they so choose. It is acceptable to pass nsull // for |outHiddenNode| if the caller doesn't care about the hidden node. // void nsMenuBarX :: HideItem ( nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode ) { nsCOMPtr menuItem; inDoc->GetElementById(inID, getter_AddRefs(menuItem)); nsCOMPtr menuContent ( do_QueryInterface(menuItem) ); if ( menuContent ) { menuContent->SetAttr ( kNameSpaceID_None, nsWidgetAtoms::hidden, NS_LITERAL_STRING("true"), PR_FALSE ); if ( outHiddenNode ) { *outHiddenNode = menuContent.get(); NS_IF_ADDREF(*outHiddenNode); } } } // HideItem nsEventStatus nsMenuBarX::MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, void * menubarNode, void * aWebShell ) { mWebShellWeakRef = getter_AddRefs(NS_GetWeakReference(NS_STATIC_CAST(nsIWebShell*, aWebShell))); nsIDOMNode* aDOMNode = NS_STATIC_CAST(nsIDOMNode*, menubarNode); mMenuBarContent = do_QueryInterface(aDOMNode); // strong ref NS_ASSERTION ( mMenuBarContent, "No content specified for this menubar" ); if ( !mMenuBarContent ) return nsEventStatus_eIgnore; Create(aParentWindow); // if we're on X (using aqua UI guidelines for menus), remove quit and prefs // from our menubar. SInt32 result = 0L; OSStatus err = ::Gestalt ( gestaltMenuMgrAttr, &result ); if ( !err && (result & gestaltMenuMgrAquaLayoutMask) ) AquifyMenuBar(); err = InstallCommandEventHandler(); if ( err ) return nsEventStatus_eIgnore; nsCOMPtr webShell = do_QueryReferent(mWebShellWeakRef); if (webShell) RegisterAsDocumentObserver(webShell); // set this as a nsMenuListener on aParentWindow aParentWindow->AddMenuListener((nsIMenuListener *)this); PRInt32 count; mMenuBarContent->ChildCount(count); for ( int i = 0; i < count; ++i ) { nsCOMPtr menu; mMenuBarContent->ChildAt ( i, *getter_AddRefs(menu) ); if ( menu ) { nsCOMPtr tag; menu->GetTag ( *getter_AddRefs(tag) ); if (tag == nsWidgetAtoms::menu) { nsAutoString menuName; nsAutoString menuAccessKey(NS_LITERAL_STRING(" ")); menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName); menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::accesskey, menuAccessKey); // Don't create the whole menu yet, just add in the top level names // Create nsMenu, the menubar will own it nsCOMPtr pnsMenu ( do_CreateInstance(kMenuCID) ); if ( pnsMenu ) { pnsMenu->Create(NS_STATIC_CAST(nsIMenuBar*, this), menuName, menuAccessKey, NS_STATIC_CAST(nsIChangeManager *, this), NS_REINTERPRET_CAST(nsIWebShell*, aWebShell), menu); // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership AddMenu(pnsMenu); nsAutoString menuIDstring; menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring); if ( menuIDstring == NS_LITERAL_STRING("menu_Help") ) { nsMenuEvent event; MenuHandle handle = nsnull; #if !TARGET_CARBON ::HMGetHelpMenuHandle(&handle); #endif event.mCommand = (unsigned int) handle; nsCOMPtr listener(do_QueryInterface(pnsMenu)); listener->MenuSelected(event); } } } } } // for each menu // Give the aParentWindow this nsMenuBarX to hold onto. // The parent takes ownership aParentWindow->SetMenuBar(this); return nsEventStatus_eIgnore; } nsEventStatus nsMenuBarX::MenuDestruct(const nsMenuEvent & aMenuEvent) { return nsEventStatus_eIgnore; } //------------------------------------------------------------------------- // // Create the proper widget // //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::Create(nsIWidget *aParent) { SetParent(aParent); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::GetParent(nsIWidget *&aParent) { NS_IF_ADDREF(aParent = mParent); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::SetParent(nsIWidget *aParent) { mParent = aParent; // weak ref return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::AddMenu(nsIMenu * aMenu) { // keep track of all added menus. mMenusArray.AppendElement(aMenu); // owner if (mNumMenus == 0) { // if apple menu hasn't been created, create it. if ( !sAppleMenu ) { nsresult rv = CreateAppleMenu(aMenu); NS_ASSERTION ( NS_SUCCEEDED(rv), "Can't create Apple menu" ); } // add shared Apple menu to our menubar if ( sAppleMenu ) { // InsertMenuItem() is 1-based, so the apple/application menu needs to // be at index 1. |mNumMenus| will be incremented below, so the following menu (File) // won't overwrite the apple menu by reusing the ID. mNumMenus = 1; ::InsertMenuItem(mRootMenu, "\pA", mNumMenus); OSStatus status = ::SetMenuItemHierarchicalMenu(mRootMenu, 1, sAppleMenu); } } MenuRef menuRef = nsnull; aMenu->GetNativeData((void**)&menuRef); PRBool helpMenu; aMenu->IsHelpMenu(&helpMenu); if(!helpMenu) { nsCOMPtr menu; aMenu->GetMenuContent(getter_AddRefs(menu)); nsAutoString menuHidden; menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, menuHidden); if( menuHidden != NS_LITERAL_STRING("true")) { // make sure we only increment |mNumMenus| if the menu is visible, since // we use it as an index of where to insert the next menu. mNumMenus++; ::InsertMenuItem(mRootMenu, "\pPlaceholder", mNumMenus); OSStatus status = ::SetMenuItemHierarchicalMenu(mRootMenu, mNumMenus, menuRef); NS_ASSERTION(status == noErr, "nsMenuBarX::AddMenu: SetMenuItemHierarchicalMenu failed."); } } return NS_OK; } // // CreateAppleMenu // // build the Apple menu shared by all menu bars. // nsresult nsMenuBarX :: CreateAppleMenu ( nsIMenu* inMenu ) { Str32 menuStr = { 1, kMenuAppleLogoFilledGlyph }; OSStatus s = ::CreateNewMenu(kAppleMenuID, 0, &sAppleMenu); if ( s == noErr && sAppleMenu ) { ::SetMenuTitle(sAppleMenu, menuStr); // this code reads the "label" attribute from the with // id="aboutName" and puts its label in the Apple Menu nsAutoString label; nsCOMPtr menu; inMenu->GetMenuContent(getter_AddRefs(menu)); if (menu) { nsCOMPtr doc; menu->GetDocument(*getter_AddRefs(doc)); if (doc) { nsCOMPtr domdoc ( do_QueryInterface(doc) ); if ( domdoc ) { nsCOMPtr aboutMenuItem; domdoc->GetElementById(NS_LITERAL_STRING("aboutName"), getter_AddRefs(aboutMenuItem)); if (aboutMenuItem) aboutMenuItem->GetAttribute(NS_LITERAL_STRING("label"), label); } } } CFStringRef labelRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)label.get(), label.Length()); if ( labelRef ) { ::InsertMenuItemTextWithCFString(sAppleMenu, labelRef, 1, 0, 0); ::CFRelease(labelRef); } ::SetMenuItemCommandID(sAppleMenu, 1, kHICommandAbout); ::AppendMenu(sAppleMenu, "\p-"); } return (s == noErr && sAppleMenu) ? NS_OK : NS_ERROR_FAILURE; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::GetMenuCount(PRUint32 &aCount) { aCount = mNumMenus; return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) { aMenu = NULL; nsCOMPtr supports = getter_AddRefs(mMenusArray.ElementAt(aCount)); if (!supports) return NS_OK; return CallQueryInterface(supports, &aMenu); // addref } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) { return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::RemoveMenu(const PRUint32 aCount) { mMenusArray.RemoveElementAt(aCount); ::DeleteMenuItem(mRootMenu, aCount + 1); // MenuManager is 1-based ::DrawMenuBar(); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::RemoveAll() { NS_ASSERTION(0, "Not implemented!"); // mMenusArray.Clear(); // maybe? return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::GetNativeData(void *& aData) { aData = (void *) mRootMenu; return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::SetNativeData(void* aData) { #if 0 Handle menubarHandle = (Handle)aData; if (mMacMBarHandle && mMacMBarHandle != menubarHandle) ::DisposeHandle(mMacMBarHandle); mMacMBarHandle = menubarHandle; #endif return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBarX::Paint() { // hack to correctly swap menu bars. // hopefully this is fast enough. ::SetRootMenu(mRootMenu); ::DrawMenuBar(); return NS_OK; } #pragma mark - // // nsIDocumentObserver // this is needed for menubar changes // NS_IMETHODIMP nsMenuBarX::BeginUpdate( nsIDocument * aDocument ) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::EndUpdate( nsIDocument * aDocument ) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::BeginLoad( nsIDocument * aDocument ) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::EndLoad( nsIDocument * aDocument ) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::BeginReflow( nsIDocument * aDocument, nsIPresShell * aShell) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::EndReflow( nsIDocument * aDocument, nsIPresShell * aShell) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentChanged( nsIDocument * aDocument, nsIContent * aContent, nsISupports * aSubContent) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentStatesChanged( nsIDocument * aDocument, nsIContent * aContent1, nsIContent * aContent2, PRInt32 aStateMask) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentAppended( nsIDocument * aDocument, nsIContent * aContainer, PRInt32 aNewIndexInContainer) { if ( aContainer == mMenuBarContent ) { //Register(aContainer, ); //InsertMenu ( aNewIndexInContainer ); } else { nsCOMPtr obs; Lookup ( aContainer, getter_AddRefs(obs) ); if ( obs ) obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer ); else { nsCOMPtr parent; aContainer->GetParent(*getter_AddRefs(parent)); if(parent) { Lookup ( parent, getter_AddRefs(obs) ); if ( obs ) obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer ); } } } return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentReplaced( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aOldChild, nsIContent * aNewChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleSheetAdded( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleSheetRemoved(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleSheetDisabledStateChanged(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet, PRBool aDisabled) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleRuleChanged( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet, nsIStyleRule * aStyleRule, PRInt32 aHint) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleRuleAdded( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet, nsIStyleRule * aStyleRule) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::StyleRuleRemoved(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet, nsIStyleRule * aStyleRule) { return NS_OK; } NS_IMETHODIMP nsMenuBarX::DocumentWillBeDestroyed( nsIDocument * aDocument ) { mDocument = nsnull; return NS_OK; } NS_IMETHODIMP nsMenuBarX::AttributeChanged( nsIDocument * aDocument, nsIContent * aContent, PRInt32 aNameSpaceID, nsIAtom * aAttribute, PRInt32 aModType, PRInt32 aHint) { // lookup and dispatch to registered thang. nsCOMPtr obs; Lookup ( aContent, getter_AddRefs(obs) ); if ( obs ) obs->AttributeChanged ( aDocument, aNameSpaceID, aAttribute, aHint ); return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentRemoved( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aChild, PRInt32 aIndexInContainer ) { if ( aContainer == mMenuBarContent ) { Unregister(aChild); RemoveMenu ( aIndexInContainer ); } else { nsCOMPtr obs; Lookup ( aContainer, getter_AddRefs(obs) ); if ( obs ) obs->ContentRemoved ( aDocument, aChild, aIndexInContainer ); else { nsCOMPtr parent; aContainer->GetParent(*getter_AddRefs(parent)); if(parent) { Lookup ( parent, getter_AddRefs(obs) ); if ( obs ) obs->ContentRemoved ( aDocument, aChild, aIndexInContainer ); } } } return NS_OK; } NS_IMETHODIMP nsMenuBarX::ContentInserted( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aChild, PRInt32 aIndexInContainer ) { if ( aContainer == mMenuBarContent ) { //Register(aChild, ); //InsertMenu ( aIndexInContainer ); } else { nsCOMPtr obs; Lookup ( aContainer, getter_AddRefs(obs) ); if ( obs ) obs->ContentInserted ( aDocument, aChild, aIndexInContainer ); else { nsCOMPtr parent; aContainer->GetParent(*getter_AddRefs(parent)); if(parent) { Lookup ( parent, getter_AddRefs(obs) ); if ( obs ) obs->ContentInserted ( aDocument, aChild, aIndexInContainer ); } } } return NS_OK; } #pragma mark - // // nsIChangeManager // // We don't use a |nsSupportsHashtable| because we know that the lifetime of all these items // is bouded by the lifetime of the menubar. No need to add any more strong refs to the // picture because the containment hierarchy already uses strong refs. // NS_IMETHODIMP nsMenuBarX :: Register ( nsIContent *aContent, nsIChangeObserver *aMenuObject ) { nsVoidKey key ( aContent ); mObserverTable.Put ( &key, aMenuObject ); return NS_OK; } NS_IMETHODIMP nsMenuBarX :: Unregister ( nsIContent *aContent ) { nsVoidKey key ( aContent ); mObserverTable.Remove ( &key ); return NS_OK; } NS_IMETHODIMP nsMenuBarX :: Lookup ( nsIContent *aContent, nsIChangeObserver **_retval ) { *_retval = nsnull; nsVoidKey key ( aContent ); *_retval = NS_REINTERPRET_CAST(nsIChangeObserver*, mObserverTable.Get(&key)); NS_IF_ADDREF ( *_retval ); return NS_OK; } #pragma mark - // // Implementation methods for nsIMenuCommandDispatcher // // // Register // // Given a menu item, creates a unique 4-character command ID and // maps it to the item. Returns the id for use by the client. // NS_IMETHODIMP nsMenuBarX :: Register ( nsIMenuItem* inMenuItem, PRUint32* outCommandID ) { // no real need to check for uniqueness. We always start afresh with each // window at 1. Even if we did get close to the reserved Apple command id's, // those don't start until at least ' ', which is integer 538976288. If // we have that many menu items in one window, I think we have other problems. // put it in the table, set out param for client nsPRUint32Key key ( mCurrentCommandID ); mObserverTable.Put ( &key, inMenuItem ); *outCommandID = mCurrentCommandID; // make id unique for next time ++mCurrentCommandID; return NS_OK; } // // Unregister // // Removes the mapping between the given 4-character command ID // and its associated menu item. // NS_IMETHODIMP nsMenuBarX :: Unregister ( PRUint32 inCommandID ) { nsPRUint32Key key ( inCommandID ); mObserverTable.Remove ( &key ); return NS_OK; } #pragma mark - // // WebShellToPresContext // // Helper to dig out a pres context from a webshell. A common thing to do before // sending an event into the dom. // nsresult MenuHelpersX::WebShellToPresContext (nsIWebShell* inWebShell, nsIPresContext** outContext ) { NS_ENSURE_ARG_POINTER(outContext); *outContext = nsnull; if (!inWebShell) return NS_ERROR_INVALID_ARG; nsresult retval = NS_OK; nsCOMPtr docShell(do_QueryInterface(inWebShell)); nsCOMPtr contentViewer; docShell->GetContentViewer(getter_AddRefs(contentViewer)); if ( contentViewer ) { nsCOMPtr docViewer ( do_QueryInterface(contentViewer) ); if ( docViewer ) docViewer->GetPresContext(*outContext); // AddRefs for us else retval = NS_ERROR_FAILURE; } else retval = NS_ERROR_FAILURE; return retval; } // WebShellToPresContext nsEventStatus MenuHelpersX::DispatchCommandTo(nsIWeakReference* aWebShellWeakRef, nsIContent* aTargetContent) { NS_PRECONDITION(aTargetContent, "null ptr"); nsCOMPtr webShell = do_QueryReferent(aWebShellWeakRef); if (!webShell) return nsEventStatus_eConsumeNoDefault; nsCOMPtr presContext; MenuHelpersX::WebShellToPresContext(webShell, getter_AddRefs(presContext)); nsEventStatus status = nsEventStatus_eConsumeNoDefault; nsMouseEvent event; event.eventStructType = NS_MOUSE_EVENT; event.message = NS_XUL_COMMAND; // See if we have a command element. If so, we execute on the // command instead of on our content element. nsAutoString command; aTargetContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::command, command); if (!command.IsEmpty()) { nsCOMPtr doc; aTargetContent->GetDocument(*getter_AddRefs(doc)); nsCOMPtr domDoc(do_QueryInterface(doc)); nsCOMPtr commandElt; domDoc->GetElementById(command, getter_AddRefs(commandElt)); nsCOMPtr commandContent(do_QueryInterface(commandElt)); if (commandContent) commandContent->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); } else aTargetContent->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); return status; }