diff --git a/widget/src/cocoa/Makefile.in b/widget/src/cocoa/Makefile.in index bcefa59fbe43..64c04f88e01f 100644 --- a/widget/src/cocoa/Makefile.in +++ b/widget/src/cocoa/Makefile.in @@ -99,14 +99,14 @@ MAC_LCPPSRCS = \ CPPSRCS = \ - nsMenuX.cpp \ - nsMenuBarX.cpp \ - nsMenuItemX.cpp \ $(MAC_LCPPSRCS) \ $(GFX_LCPPSRCS) \ $(NULL) CMMSRCS = \ + nsMenuX.mm \ + nsMenuBarX.mm \ + nsMenuItemX.mm \ nsFilePicker.mm \ nsToolkit.mm \ nsAppShellCocoa.mm \ diff --git a/widget/src/cocoa/nsMenuBarX.cpp b/widget/src/cocoa/nsMenuBarX.cpp deleted file mode 100644 index f2854d8b03e3..000000000000 --- a/widget/src/cocoa/nsMenuBarX.cpp +++ /dev/null @@ -1,959 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla 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/MPL/ - * - * 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 MPL, 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 MPL, 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 "nsISupports.h" -#include "nsIWidget.h" -#include "nsString.h" -#include "nsIStringBundle.h" -#include "nsIDocument.h" -#include "nsIDocShell.h" -#include "nsIDocumentViewer.h" -#include "nsIDocumentObserver.h" - -#include "nsIDOMDocument.h" -#include "nsWidgetAtoms.h" - -#include -#include -#include -#include -#include -#include -#include "nsMacResources.h" - -#include "nsGUIEvent.h" - -// CIDs -#include "nsWidgetsCID.h" -static NS_DEFINE_CID(kMenuCID, NS_MENU_CID); - -NS_IMPL_ISUPPORTS6(nsMenuBarX, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, - nsIChangeManager, nsIMenuCommandDispatcher, nsISupportsWeakReference) - -MenuRef nsMenuBarX::sAppleMenu = nsnull; -EventHandlerUPP nsMenuBarX::sCommandEventHandler = nsnull; - - -// -// nsMenuBarX constructor -// -nsMenuBarX::nsMenuBarX() - : mCurrentCommandID(1), - mNumMenus(0), - mParent(nsnull), - mIsMenuBarAdded(PR_FALSE), - mDocument(nsnull) -{ - 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 ( nsIDocShell* inDocShell, nsIDocument** outDocument ) -{ - *outDocument = nsnull; - - if ( inDocShell ) { - nsCOMPtr cv; - inDocShell->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 ( nsIDocShell* inDocShell ) -{ - nsCOMPtr doc; - GetDocument(inDocShell, 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 docshell 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 domDoc ( do_QueryInterface(mMenuBarContent->GetDocument()) ); - 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 domDoc = do_QueryInterface(self->mDocument); - if ( domDoc ) { - nsCOMPtr domElement; - domDoc->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 ) -{ - nsEventStatus status = nsEventStatus_eIgnore; - if ( inDispatchTo ) { - nsCOMPtr docShell = do_QueryReferent(mDocShellWeakRef); - if (!docShell) - return nsEventStatus_eConsumeNoDefault; - nsCOMPtr presContext; - MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext)); - - nsMouseEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull, nsMouseEvent::eReal); - - inDispatchTo->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); - } - - return status; -} // 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 * aDocShell ) -{ - mDocShellWeakRef = do_GetWeakReference(NS_STATIC_CAST(nsIDocShell*, aDocShell)); - 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 docShell = do_QueryReferent(mDocShellWeakRef); - if (docShell) RegisterAsDocumentObserver(docShell); - - // set this as a nsMenuListener on aParentWindow - aParentWindow->AddMenuListener((nsIMenuListener *)this); - - PRUint32 count = mMenuBarContent->GetChildCount(); - for ( PRUint32 i = 0; i < count; ++i ) { - nsIContent *menu = mMenuBarContent->GetChildAt(i); - if ( menu ) { - if (menu->Tag() == nsWidgetAtoms::menu && - menu->IsContentOfType(nsIContent::eXUL)) { - 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(nsIDocShell*, aDocShell), menu); - - // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership - AddMenu(pnsMenu); - - nsAutoString menuIDstring; - menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring); - if ( menuIDstring.EqualsLiteral("menu_Help") ) { - nsMenuEvent event(PR_TRUE, 0, nsnull); - 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); - ::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.EqualsLiteral("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(); - 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_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsMenuBarX) -NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsMenuBarX) -NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsMenuBarX) -NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsMenuBarX) - -void -nsMenuBarX::BeginUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType ) -{ -} - -void -nsMenuBarX::EndUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType ) -{ -} - -void -nsMenuBarX::CharacterDataChanged( nsIDocument * aDocument, nsIContent * aContent, PRBool aAppend) -{ -} - -void -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(); - if(parent) { - Lookup ( parent, getter_AddRefs(obs) ); - if ( obs ) - obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer ); - } - } - } -} - -void -nsMenuBarX::DocumentWillBeDestroyed( nsIDocument * aDocument ) -{ - mDocument = nsnull; -} - - -void -nsMenuBarX::AttributeChanged( nsIDocument * aDocument, nsIContent * aContent, PRInt32 aNameSpaceID, - nsIAtom * aAttribute, PRInt32 aModType) -{ - // lookup and dispatch to registered thang. - nsCOMPtr obs; - Lookup ( aContent, getter_AddRefs(obs) ); - if ( obs ) - obs->AttributeChanged ( aDocument, aNameSpaceID, aAttribute ); -} - -void -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(); - if(parent) { - Lookup ( parent, getter_AddRefs(obs) ); - if ( obs ) - obs->ContentRemoved ( aDocument, aChild, aIndexInContainer ); - } - } - } -} - -void -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(); - if(parent) { - Lookup ( parent, getter_AddRefs(obs) ); - if ( obs ) - obs->ContentInserted ( aDocument, aChild, aIndexInContainer ); - } - } - } -} - -#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 - - - -// -// DocShellToPresContext -// -// Helper to dig out a pres context from a docshell. A common thing to do before -// sending an event into the dom. -// -// XXXbz this should be using DOM event apis! -nsresult -MenuHelpersX::DocShellToPresContext (nsIDocShell* inDocShell, nsPresContext** outContext ) -{ - NS_ENSURE_ARG_POINTER(outContext); - *outContext = nsnull; - if (!inDocShell) - return NS_ERROR_INVALID_ARG; - - nsresult retval = NS_OK; - - nsCOMPtr contentViewer; - inDocShell->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; - -} // DocShellToPresContext - - - diff --git a/widget/src/cocoa/nsMenuBarX.h b/widget/src/cocoa/nsMenuBarX.h index 24358c953b6c..b67abc2d7af7 100644 --- a/widget/src/cocoa/nsMenuBarX.h +++ b/widget/src/cocoa/nsMenuBarX.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Josh Aas * * 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 @@ -49,12 +50,8 @@ #include "nsWeakReference.h" #include "nsIContent.h" -#include -#include -#include -#include - -extern nsWeakPtr gMacMenubarX; +#import +#import class nsIWidget; class nsIDocument; @@ -62,15 +59,17 @@ class nsIDOMNode; namespace MenuHelpersX { - // utility routine for getting a PresContext out of a docShell + // utility routine for getting a PresContext out of a docShell nsresult DocShellToPresContext ( nsIDocShell* inDocShell, nsPresContext** outContext ) ; + nsEventStatus DispatchCommandTo(nsIWeakReference* aDocShellWeakRef, + nsIContent* aTargetContent); } -/** - * Native Mac MenuBar wrapper - */ +// +// Native Mac menu bar wrapper +// class nsMenuBarX : public nsIMenuBar, public nsIMenuListener, @@ -82,8 +81,8 @@ class nsMenuBarX : public nsIMenuBar, public: nsMenuBarX(); virtual ~nsMenuBarX(); - - enum { kAppleMenuID = 1 } ; + + enum {kApplicationMenuID = 1}; NS_DECL_ISUPPORTS NS_DECL_NSICHANGEMANAGER @@ -119,44 +118,44 @@ public: protected: - void GetDocument ( nsIDocShell* inDocShell, nsIDocument** outDocument ) ; - void RegisterAsDocumentObserver ( nsIDocShell* inDocShell ) ; + void GetDocument(nsIDocShell* inDocShell, nsIDocument** outDocument) ; + void RegisterAsDocumentObserver(nsIDocShell* inDocShell); - // Make our menubar conform to Aqua UI guidelines - void AquifyMenuBar ( ) ; - void HideItem ( nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode ) ; - OSStatus InstallCommandEventHandler ( ) ; + // Make our menubar conform to Aqua UI guidelines + void AquifyMenuBar(); + void HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode); + OSStatus InstallCommandEventHandler(); - // command handler for some special menu items (prefs/quit/etc) - pascal static OSStatus CommandEventHandler ( EventHandlerCallRef inHandlerChain, - EventRef inEvent, void* userData ) ; - nsEventStatus ExecuteCommand ( nsIContent* inDispatchTo ) ; + // command handler for some special menu items (prefs/quit/etc) + pascal static OSStatus CommandEventHandler(EventHandlerCallRef inHandlerChain, + EventRef inEvent, void* userData); + nsEventStatus ExecuteCommand(nsIContent* inDispatchTo); - // build the Apple menu shared by all menu bars. - nsresult CreateAppleMenu ( nsIMenu* inMenu ) ; + // build the Application menu shared by all menu bars. + nsresult CreateApplicationMenu(nsIMenu* inMenu); - nsHashtable mObserverTable; // stores observers for content change notification - nsHashtable mCommandMapTable; // maps CommandIDs to content nodes for CarbonEvent item selection - PRUint32 mCurrentCommandID; // unique command id (per menu-bar) to give to next item that asks + nsHashtable mObserverTable; // stores observers for content change notification + nsHashtable mCommandMapTable; // maps CommandIDs to content nodes for CarbonEvent item selection PRUint32 mNumMenus; - nsSupportsArray mMenusArray; // holds refs - nsCOMPtr mMenuBarContent; // menubar content node, strong ref - nsCOMPtr mPrefItemContent; // on X, holds the content node for the prefs item that has - // been removed from the menubar - nsCOMPtr mQuitItemContent; // as above, but for quit - nsIWidget* mParent; // weak ref - + nsSupportsArray mMenusArray; // holds refs + nsCOMPtr mMenuBarContent; // menubar content node, strong ref + nsCOMPtr mPrefItemContent; // on X, holds the content node for the prefs item that has + // been removed from the menubar + nsCOMPtr mQuitItemContent; // as above, but for quit + nsIWidget* mParent; // weak ref PRBool mIsMenuBarAdded; + PRUint32 mCurrentCommandID; // unique command id (per menu-bar) to give to next item that asks - nsWeakPtr mDocShellWeakRef; // weak ref to docshell - nsIDocument* mDocument; // pointer to document - MenuRef mRootMenu; // root menu, representing entire menu bar. + nsWeakPtr mDocShellWeakRef; // weak ref to docshell + nsIDocument* mDocument; // pointer to document + + NSMenu* mRootMenu; // root menu, representing entire menu bar. - static MenuRef sAppleMenu; // AppleMenu shared by all menubars + static NSMenu* sApplicationMenu; // Application menu shared by all menubars - static EventHandlerUPP sCommandEventHandler; // carbon event handler for commands, shared + static EventHandlerUPP sCommandEventHandler; // carbon event handler for commands, shared }; #endif // nsMenuBarX_h__ diff --git a/widget/src/cocoa/nsMenuBarX.mm b/widget/src/cocoa/nsMenuBarX.mm new file mode 100644 index 000000000000..cad2d4731151 --- /dev/null +++ b/widget/src/cocoa/nsMenuBarX.mm @@ -0,0 +1,909 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** +* Version: MPL 1.1/GPL 2.0/LGPL 2.1 +* +* The contents of this file are subject to the Mozilla 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/MPL/ +* +* 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): +* Josh Aas +* +* 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 MPL, 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 MPL, 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 "nsChildView.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 "nsIDOMDocument.h" +#include "nsWidgetAtoms.h" + +#include "nsMacResources.h" + +#include "nsGUIEvent.h" + +// CIDs +#include "nsWidgetsCID.h" +static NS_DEFINE_CID(kMenuCID, NS_MENU_CID); + +NS_IMPL_ISUPPORTS6(nsMenuBarX, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, + nsIChangeManager, nsIMenuCommandDispatcher, nsISupportsWeakReference) + +NSMenu* nsMenuBarX::sApplicationMenu = nsnull; +EventHandlerUPP nsMenuBarX::sCommandEventHandler = nsnull; + + +nsMenuBarX::nsMenuBarX() +: mNumMenus(0), mParent(nsnull), mIsMenuBarAdded(PR_FALSE), mCurrentCommandID(1), mDocument(nsnull) +{ + mRootMenu = [[NSMenu alloc] initWithTitle:@"MainMenuBar"]; + + // create our global carbon event command handler shared by all windows + if (!sCommandEventHandler) + sCommandEventHandler = ::NewEventHandlerUPP(CommandEventHandler); +} + + +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); + } + + [mRootMenu release]; +} + +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; + 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 { + 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(nsIDocShell* inDocShell, nsIDocument** outDocument) +{ + *outDocument = nsnull; + + if (inDocShell) { + nsCOMPtr cv; + inDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + // get the document + nsCOMPtr docv(do_QueryInterface(cv)); + if (!docv) + return; + docv->GetDocument(outDocument); // addrefs + } + } +} + + +void +nsMenuBarX::RegisterAsDocumentObserver(nsIDocShell* inDocShell) +{ + nsCOMPtr doc; + GetDocument(inDocShell, 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 docshell 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 domDoc(do_QueryInterface(mMenuBarContent->GetDocument())); + 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; + NSWindow* myWindow = NS_REINTERPRET_CAST(NSWindow*, mParent->GetNativeData(NS_NATIVE_WINDOW)); + WindowRef myWindowRef = (WindowRef)[myWindow windowRef]; + NS_ASSERTION(myWindowRef, "Can't get WindowRef to install command handler!"); + if (myWindowRef && sCommandEventHandler) { + const EventTypeSpec commandEventList[] = {{kEventClassCommand, kEventCommandProcess}, + {kEventClassCommand, kEventCommandUpdateStatus}}; + err = ::InstallWindowEventHandler(myWindowRef, 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 application menu. Grovel for the + // content node with an id of "aboutName" and call it + // directly. + nsCOMPtr domDoc = do_QueryInterface(self->mDocument); + if (domDoc) { + nsCOMPtr domElement; + domDoc->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: + { + //XXXJOSH implement this Cocoa-style + // 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(mDocShellWeakRef, 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 * aDocShell) +{ + mDocShellWeakRef = do_GetWeakReference(NS_STATIC_CAST(nsIDocShell*, aDocShell)); + 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); + + AquifyMenuBar(); + + OSStatus err = InstallCommandEventHandler(); + if (err) + return nsEventStatus_eIgnore; + + nsCOMPtr docShell = do_QueryReferent(mDocShellWeakRef); + if (docShell) + RegisterAsDocumentObserver(docShell); + + // set this as a nsMenuListener on aParentWindow + aParentWindow->AddMenuListener((nsIMenuListener *)this); + + PRUint32 count = mMenuBarContent->GetChildCount(); + for (PRUint32 i = 0; i < count; i++) { + nsIContent *menu = mMenuBarContent->GetChildAt(i); + if (menu) { + if (menu->Tag() == nsWidgetAtoms::menu && + menu->IsContentOfType(nsIContent::eXUL)) { + 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(nsIDocShell*, aDocShell), menu); + + // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership. + AddMenu(pnsMenu); + + nsAutoString menuIDstring; + menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring); + if (menuIDstring.EqualsLiteral("menu_Help")) { + nsMenuEvent event(PR_TRUE, 0, nsnull); + event.mCommand = (unsigned int)nsnull; + 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; +} + + +NS_IMETHODIMP nsMenuBarX::Create(nsIWidget *aParent) +{ + SetParent(aParent); + return NS_OK; +} + + +NS_IMETHODIMP nsMenuBarX::GetParent(nsIWidget *&aParent) +{ + NS_IF_ADDREF(aParent = mParent); + return NS_OK; +} + + +NS_IMETHODIMP nsMenuBarX::SetParent(nsIWidget *aParent) +{ + mParent = aParent; // weak ref + return NS_OK; +} + + +NS_IMETHODIMP nsMenuBarX::AddMenu(nsIMenu * aMenu) +{ + // keep track of all added menus. + mMenusArray.AppendElement(aMenu); // owner + + if (mNumMenus == 0) { + // if application menu hasn't been created, create it. + if (!sApplicationMenu) { + nsresult rv = CreateApplicationMenu(aMenu); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't create Application menu"); + } + + // add shared Application menu to our menubar + if (sApplicationMenu) { + [mRootMenu insertItem:[[[NSMenuItem alloc] initWithTitle:@"AppMenu" action:NULL keyEquivalent:@""] autorelease] atIndex:mNumMenus]; + // an NSMenu can't have multiple supermenus, so we clone the shared menu and insert the clone. + // this is actually a bad solution since we want any mods to the application menu to affect all + // application menus. Really we should unhook the application menu when we switch menu bars. + [[mRootMenu itemAtIndex:0] setSubmenu:[sApplicationMenu copy]]; //XXXJOSH memory leak? + // |mNumMenus| is incremented so the following menu won't overwrite the application menu by reusing the ID. + mNumMenus++; + } + } + + PRBool helpMenu; + aMenu->IsHelpMenu(&helpMenu); + if (!helpMenu) { + NSMenu* menuRef = NULL; + aMenu->GetNativeData((void**)&menuRef); + + nsCOMPtr menu; + aMenu->GetMenuContent(getter_AddRefs(menu)); + nsAutoString menuHidden; + menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, menuHidden); + if (!menuHidden.EqualsLiteral("true") && menu->GetChildCount() > 0) { + [mRootMenu insertItem:[[[NSMenuItem alloc] initWithTitle:@"SomeMenuItem" action:NULL keyEquivalent:@""] autorelease] atIndex:mNumMenus]; + [[mRootMenu itemAtIndex:mNumMenus] setSubmenu:menuRef]; + mNumMenus++; + } + } + + return NS_OK; +} + + +// +// CreateApplicationMenu +// +// build the Application menu shared by all menu bars. +// +nsresult +nsMenuBarX::CreateApplicationMenu(nsIMenu* inMenu) +{ + sApplicationMenu = [[NSMenu alloc] initWithTitle:@"ApplicationMenu"]; + + if (sApplicationMenu) { + // this code reads the "label" attribute from the with + // id="aboutName" and puts its label in the Application Menu + nsAutoString label; + nsCOMPtr menu; + inMenu->GetMenuContent(getter_AddRefs(menu)); + if (menu) { + nsCOMPtr doc = menu->GetDocument(); + 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) { + [sApplicationMenu insertItemWithTitle:(NSString*)labelRef action:nil keyEquivalent:@"" atIndex:0]; + ::CFRelease(labelRef); + } + + //XXXJOSH we need to get this event, maybe convert to a MenuRef, do later + //::SetMenuItemCommandID(sApplicationMenu, 1, kHICommandAbout); + + // add separator after About menu + [sApplicationMenu addItem:[NSMenuItem separatorItem]]; + + + } + + return (sApplicationMenu) ? NS_OK : NS_ERROR_FAILURE; +} + + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::GetMenuCount(PRUint32 &aCount) +{ + aCount = mNumMenus; + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP 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_IMETHODIMP nsMenuBarX::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) +{ + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::RemoveMenu(const PRUint32 aCount) +{ + mMenusArray.RemoveElementAt(aCount); + [mRootMenu removeItemAtIndex:aCount]; + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::RemoveAll() +{ + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::GetNativeData(void *& aData) +{ + aData = (void *) mRootMenu; + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::SetNativeData(void* aData) +{ + return NS_OK; +} + +//------------------------------------------------------------------------- +NS_IMETHODIMP nsMenuBarX::Paint() +{ + [NSApp setMainMenu:mRootMenu]; + return NS_OK; +} + + +// +// nsIDocumentObserver +// this is needed for menubar changes +// + +NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsMenuBarX) +NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsMenuBarX) +NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsMenuBarX) +NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsMenuBarX) + +void +nsMenuBarX::BeginUpdate(nsIDocument * aDocument, nsUpdateType aUpdateType) +{ +} + +void +nsMenuBarX::EndUpdate(nsIDocument * aDocument, nsUpdateType aUpdateType) +{ +} + +void +nsMenuBarX::CharacterDataChanged(nsIDocument * aDocument, + nsIContent * aContent, PRBool aAppend) +{ +} + +void +nsMenuBarX::ContentAppended(nsIDocument * aDocument, nsIContent * aContainer, + PRInt32 aNewIndexInContainer) +{ + if (aContainer != mMenuBarContent) { + nsCOMPtr obs; + Lookup(aContainer, getter_AddRefs(obs)); + if (obs) + obs->ContentInserted(aDocument, aContainer, aNewIndexInContainer); + else { + nsCOMPtr parent = aContainer->GetParent(); + if (parent) { + Lookup(parent, getter_AddRefs(obs)); + if (obs) + obs->ContentInserted(aDocument, aContainer, aNewIndexInContainer); + } + } + } +} + +void +nsMenuBarX::DocumentWillBeDestroyed(nsIDocument * aDocument) +{ + mDocument = nsnull; +} + + +void +nsMenuBarX::AttributeChanged(nsIDocument * aDocument, nsIContent * aContent, + PRInt32 aNameSpaceID, nsIAtom * aAttribute, + PRInt32 aModType) +{ + // lookup and dispatch to registered thang + nsCOMPtr obs; + Lookup(aContent, getter_AddRefs(obs)); + if (obs) + obs->AttributeChanged(aDocument, aNameSpaceID, aAttribute); +} + +void +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(); + if (parent) { + Lookup (parent, getter_AddRefs(obs)); + if (obs) + obs->ContentRemoved(aDocument, aChild, aIndexInContainer); + } + } + } +} + +void +nsMenuBarX::ContentInserted(nsIDocument * aDocument, nsIContent * aContainer, + nsIContent * aChild, PRInt32 aIndexInContainer) +{ + if (aContainer != mMenuBarContent) { + nsCOMPtr obs; + Lookup (aContainer, getter_AddRefs(obs)); + if (obs) + obs->ContentInserted (aDocument, aChild, aIndexInContainer); + else { + nsCOMPtr parent = aContainer->GetParent(); + if (parent) { + Lookup (parent, getter_AddRefs(obs)); + if (obs) + obs->ContentInserted(aDocument, aChild, aIndexInContainer); + } + } + } +} + +#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; +} + + +// +// 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; +} + + +// +// DocShellToPresContext +// +// Helper to dig out a pres context from a docshell. A common thing to do before +// sending an event into the dom. +// +nsresult +MenuHelpersX::DocShellToPresContext(nsIDocShell* inDocShell, nsPresContext** outContext) +{ + NS_ENSURE_ARG_POINTER(outContext); + *outContext = nsnull; + if (!inDocShell) + return NS_ERROR_INVALID_ARG; + + nsresult retval = NS_OK; + + nsCOMPtr contentViewer; + inDocShell->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; + +} // DocShellToPresContext + +nsEventStatus +MenuHelpersX::DispatchCommandTo(nsIWeakReference* aDocShellWeakRef, + nsIContent* aTargetContent) +{ + NS_PRECONDITION(aTargetContent, "null ptr"); + + nsCOMPtr docShell = do_QueryReferent(aDocShellWeakRef); + if (!docShell) + return nsEventStatus_eConsumeNoDefault; + nsCOMPtr presContext; + MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext)); + + nsEventStatus status = nsEventStatus_eConsumeNoDefault; + nsMouseEvent event(PR_TRUE, NS_XUL_COMMAND, nsnull, nsMouseEvent::eReal); + + // FIXME: Should probably figure out how to init this with the actual + // pressed keys, but this is a big old edge case anyway. -dwh + + // 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 domDoc(do_QueryInterface(aTargetContent->GetDocument())); + 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; +} diff --git a/widget/src/cocoa/nsMenuItemX.cpp b/widget/src/cocoa/nsMenuItemX.mm similarity index 100% rename from widget/src/cocoa/nsMenuItemX.cpp rename to widget/src/cocoa/nsMenuItemX.mm diff --git a/widget/src/cocoa/nsMenuX.h b/widget/src/cocoa/nsMenuX.h index 5ea7d83878a4..d7028df63334 100644 --- a/widget/src/cocoa/nsMenuX.h +++ b/widget/src/cocoa/nsMenuX.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Josh Aas * * 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 @@ -45,17 +46,23 @@ #include "nsIChangeManager.h" #include "nsWeakReference.h" -#include -#include -#include +#import +#import class nsIMenuBar; class nsIMenuListener; +class nsMenuX; -//static PRInt16 mMacMenuIDCount; // use GetUniqueMenuID() -extern PRInt16 mMacMenuIDCount;// = kMacMenuID; +// This class simply receives events from menus as a proxy for a gecko menu +@interface MenuDelegate : NSObject +{ + nsMenuX* mGeckoMenu; // weak ref + BOOL mHaveInstalledCarbonEvents; +} +- (id)initWithGeckoMenu:(nsMenuX*)geckoMenu; +@end class nsMenuX : public nsIMenu, @@ -75,15 +82,15 @@ public: nsEventStatus MenuItemSelected(const nsMenuEvent & aMenuEvent); nsEventStatus MenuSelected(const nsMenuEvent & aMenuEvent); nsEventStatus MenuDeselected(const nsMenuEvent & aMenuEvent); - nsEventStatus MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, + nsEventStatus MenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * menuNode, void * aDocShell); nsEventStatus MenuDestruct(const nsMenuEvent & aMenuEvent); nsEventStatus CheckRebuild(PRBool & aMenuEvent); nsEventStatus SetRebuild(PRBool aMenuEvent); // nsIMenu Methods - NS_IMETHOD Create ( nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, - nsIChangeManager* aManager, nsIDocShell* aShell, nsIContent* aNode ) ; + NS_IMETHOD Create (nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, + nsIChangeManager* aManager, nsIDocShell* aShell, nsIContent* aNode); NS_IMETHOD GetParent(nsISupports *&aParent); NS_IMETHOD GetLabel(nsString &aText); NS_IMETHOD SetLabel(const nsAString &aText); @@ -105,37 +112,36 @@ public: NS_IMETHOD GetEnabled(PRBool* aIsEnabled); NS_IMETHOD IsHelpMenu(PRBool* aIsEnabled); - // NS_IMETHOD AddMenuItem(nsIMenuItem * aMenuItem); NS_IMETHOD AddMenu(nsIMenu * aMenu); - + protected: - // Determines how many menus are visible among the siblings that are before me. - // It doesn't matter if I am visible. - nsresult CountVisibleBefore ( PRUint32* outVisibleBefore ) ; + // Determines how many menus are visible among the siblings that are before me. + // It doesn't matter if I am visible. + nsresult CountVisibleBefore(PRUint32* outVisibleBefore); // fetch the content node associated with the menupopup item - void GetMenuPopupContent ( nsIContent** aResult ) ; + void GetMenuPopupContent(nsIContent** aResult); - // Insert a new item in this menu with index |inItemIndex| with the text |inItemLabel|, - // middle-truncated to a certain pixel width with an elipsis. - void InsertMenuItemWithTruncation ( nsAutoString & inItemLabel, - PRUint32 inItemIndex ) ; + // Insert a new item in this menu with index |inItemIndex| with the text |inItemLabel|, + // middle-truncated to a certain pixel width with an elipsis. + void InsertMenuItemWithTruncation(nsAutoString & inItemLabel, + PRUint32 inItemIndex); // fire handlers for oncreate/ondestroy - PRBool OnDestroy() ; - PRBool OnCreate() ; - PRBool OnDestroyed() ; - PRBool OnCreated() ; + PRBool OnDestroy(); + PRBool OnCreate(); + PRBool OnDestroyed(); + PRBool OnCreated(); - void LoadMenuItem ( nsIMenu* pParentMenu, nsIContent* menuitemContent ); - void LoadSubMenu ( nsIMenu * pParentMenu, nsIContent* menuitemContent ); - void LoadSeparator ( nsIContent* menuitemContent ); + void LoadMenuItem(nsIMenu* pParentMenu, nsIContent* menuitemContent); + void LoadSubMenu(nsIMenu * pParentMenu, nsIContent* menuitemContent); + void LoadSeparator(nsIContent* menuitemContent); - nsEventStatus HelpMenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, - void* unused, void* aDocShell); + nsEventStatus HelpMenuConstruct(const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, + void* unused, void* aDocShell); - MenuHandle NSStringNewMenu(short menuID, nsString& menuTitle); + NSMenu* CreateMenuWithGeckoString(nsString& menuTitle); protected: nsString mLabel; @@ -148,9 +154,10 @@ protected: nsCOMPtr mMenuContent; // the |menu| tag, strong ref nsCOMPtr mListener; // strong ref - // MacSpecific + // Mac specific PRInt16 mMacMenuID; - MenuHandle mMacMenuHandle; + NSMenu* mMacMenu; // strong ref, we own it + MenuDelegate* mMenuDelegate; // strong ref, we keep this around to get events for us PRInt16 mHelpMenuOSItemsCount; PRPackedBool mIsHelpMenu; PRPackedBool mIsEnabled; diff --git a/widget/src/cocoa/nsMenuX.cpp b/widget/src/cocoa/nsMenuX.mm similarity index 57% rename from widget/src/cocoa/nsMenuX.cpp rename to widget/src/cocoa/nsMenuX.mm index 180deea6e071..7d3856e4129f 100644 --- a/widget/src/cocoa/nsMenuX.cpp +++ b/widget/src/cocoa/nsMenuX.mm @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Josh Aas * * 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 @@ -55,7 +56,6 @@ #include "nsIMenuCommandDispatcher.h" #include "nsString.h" -#include "nsCRT.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "plstr.h" @@ -65,27 +65,13 @@ #include "nsIXBLService.h" #include "nsIServiceManager.h" -#include -#include -#include - #include "nsGUIEvent.h" +#include "nsCRT.h" -static OSStatus InstallMyMenuEventHandler(MenuRef menuRef, void* userData, EventHandlerRef* outHandler) ; - -// keep track of the menuID of the menu the mouse is currently over. Yes, this is ugly, -// but necessary to work around bugs in Carbon with ::MenuSelect() sometimes returning -// the wrong menuID. -static MenuID gCurrentlyTrackedMenuID = 0; - -const PRInt16 kMacMenuIDX = nsMenuBarX::kAppleMenuID + 1; -static PRInt16 gMacMenuIDCountX = kMacMenuIDX; static PRBool gConstructingMenu = PR_FALSE; - -#if DEBUG -//nsInstanceCounter gMenuCounterX("nsMenuX"); -#endif + +static MenuRef GetCarbonMenuRef(NSMenu* aMenu); // CIDs #include "nsWidgetsCID.h" @@ -96,57 +82,49 @@ static NS_DEFINE_CID(kMenuItemCID, NS_MENUITEM_CID); class nsDummyMenuItemX : public nsISupports { public: NS_DECL_ISUPPORTS + + nsDummyMenuItemX() + { + } }; NS_IMETHODIMP_(nsrefcnt) nsDummyMenuItemX::AddRef() { return ++mRefCnt; } -NS_METHOD nsDummyMenuItemX::Release() { return --mRefCnt; } +NS_IMETHODIMP nsDummyMenuItemX::Release() { return --mRefCnt; } NS_IMPL_QUERY_INTERFACE0(nsDummyMenuItemX) static nsDummyMenuItemX gDummyMenuItemX; -//------------------------------------------------------------------------- + NS_IMPL_ISUPPORTS4(nsMenuX, nsIMenu, nsIMenuListener, nsIChangeObserver, nsISupportsWeakReference) -// -// nsMenuX constructor -// + nsMenuX::nsMenuX() - : mNumMenuItems(0), mParent(nsnull), mManager(nsnull), - mMacMenuID(0), mMacMenuHandle(nsnull), mHelpMenuOSItemsCount(0), - mIsHelpMenu(PR_FALSE), mIsEnabled(PR_TRUE), mDestroyHandlerCalled(PR_FALSE), - mNeedsRebuild(PR_TRUE), mConstructed(PR_FALSE), mVisible(PR_TRUE), mHandler(nsnull) +: mNumMenuItems(0), mParent(nsnull), mManager(nsnull), + mMacMenuID(0), mMacMenu(NULL), mHelpMenuOSItemsCount(0), + mIsHelpMenu(PR_FALSE), mIsEnabled(PR_TRUE), mDestroyHandlerCalled(PR_FALSE), + mNeedsRebuild(PR_TRUE), mConstructed(PR_FALSE), mVisible(PR_TRUE), mHandler(nsnull) { -#if DEBUG - //++gMenuCounterX; -#endif + mMenuDelegate = [[MenuDelegate alloc] initWithGeckoMenu:this]; } -// -// nsMenuX destructor -// nsMenuX::~nsMenuX() { RemoveAll(); - if ( mMacMenuHandle ) { - if ( mHandler ) + if (mMacMenu) { + if (mHandler) ::RemoveEventHandler(mHandler); - ::ReleaseMenu(mMacMenuHandle); + [mMacMenu release]; } + [mMenuDelegate release]; + // alert the change notifier we don't care no more mManager->Unregister(mMenuContent); - -#if DEBUG - //--gMenuCounterX; -#endif } -// -// Create -// -NS_METHOD +NS_IMETHODIMP nsMenuX::Create(nsISupports * aParent, const nsAString &aLabel, const nsAString &aAccessKey, nsIChangeManager* aManager, nsIDocShell* aShell, nsIContent* aNode ) { @@ -154,22 +132,18 @@ nsMenuX::Create(nsISupports * aParent, const nsAString &aLabel, const nsAString mMenuContent = aNode; // register this menu to be notified when changes are made to our content object - mManager = aManager; // weak ref - nsCOMPtr changeObs ( do_QueryInterface(NS_STATIC_CAST(nsIChangeObserver*, this)) ); + mManager = aManager; // weak ref + nsCOMPtr changeObs(do_QueryInterface(NS_STATIC_CAST(nsIChangeObserver*, this))); mManager->Register(mMenuContent, changeObs); - NS_ASSERTION ( mMenuContent, "Menu not given a dom node at creation time" ); - NS_ASSERTION ( mManager, "No change manager given, can't tell content model updates" ); + NS_ASSERTION(mMenuContent, "Menu not given a dom node at creation time"); + NS_ASSERTION(mManager, "No change manager given, can't tell content model updates"); mParent = aParent; // our parent could be either a menu bar (if we're toplevel) or a menu (if we're a submenu) - PRBool isValidParent = PR_FALSE; - if (aParent) { - nsCOMPtr menubar = do_QueryInterface(aParent); - nsCOMPtr menu = do_QueryInterface(aParent); - isValidParent = (menubar || menu); - } - NS_ASSERTION(isValidParent, "Menu parent not a menu bar or menu!" ); + nsCOMPtr menubar = do_QueryInterface(aParent); + nsCOMPtr menu = do_QueryInterface(aParent); + NS_ASSERTION(menubar || menu, "Menu parent not a menu bar or menu!"); SetLabel(aLabel); SetAccessKey(aAccessKey); @@ -177,99 +151,99 @@ nsMenuX::Create(nsISupports * aParent, const nsAString &aLabel, const nsAString nsAutoString hiddenValue, collapsedValue; mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hiddenValue); mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::collapsed, collapsedValue); - if ( hiddenValue.EqualsLiteral("true") || collapsedValue.EqualsLiteral("true") ) + if (hiddenValue.EqualsLiteral("true") || collapsedValue.EqualsLiteral("true")) + mVisible = PR_FALSE; + + if (menubar && mMenuContent->GetChildCount() == 0) mVisible = PR_FALSE; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetParent(nsISupports*& aParent) + +NS_IMETHODIMP nsMenuX::GetParent(nsISupports*& aParent) { aParent = mParent; NS_IF_ADDREF(aParent); return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetLabel(nsString &aText) + +NS_IMETHODIMP nsMenuX::GetLabel(nsString &aText) { aText = mLabel; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::SetLabel(const nsAString &aText) +NS_IMETHODIMP nsMenuX::SetLabel(const nsAString &aText) { mLabel = aText; - - // first time? create the menu handle, attach event handler to it. - if (mMacMenuHandle == nsnull) { - mMacMenuID = gMacMenuIDCountX++; - mMacMenuHandle = NSStringNewMenu(mMacMenuID, mLabel); - } - + // create an actual NSMenu if this is the first time + if (mMacMenu == NULL) + mMacMenu = CreateMenuWithGeckoString(mLabel); return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetAccessKey(nsString &aText) + +NS_IMETHODIMP nsMenuX::GetAccessKey(nsString &aText) { return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::SetAccessKey(const nsAString &aText) + +NS_IMETHODIMP nsMenuX::SetAccessKey(const nsAString &aText) { return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::AddItem(nsISupports* aItem) + +NS_IMETHODIMP nsMenuX::AddItem(nsISupports* aItem) { - nsresult rv = NS_ERROR_FAILURE; - if (aItem) { - // Figure out what we're adding - nsCOMPtr menuItem(do_QueryInterface(aItem)); - if (menuItem) { - rv = AddMenuItem(menuItem); - } else { - nsCOMPtr menu(do_QueryInterface(aItem)); - if (menu) - rv = AddMenu(menu); - } + nsresult rv = NS_ERROR_FAILURE; + if (aItem) { + // Figure out what we're adding + nsCOMPtr menuItem(do_QueryInterface(aItem)); + if (menuItem) { + rv = AddMenuItem(menuItem); + } else { + nsCOMPtr menu(do_QueryInterface(aItem)); + if (menu) + rv = AddMenu(menu); } - return rv; + } + return rv; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::AddMenuItem(nsIMenuItem * aMenuItem) -{ - if(!aMenuItem) return NS_ERROR_NULL_POINTER; - mMenuItemsArray.AppendElement(aMenuItem); // owning ref +NS_IMETHODIMP nsMenuX::AddMenuItem(nsIMenuItem * aMenuItem) +{ + if (!aMenuItem) + return NS_ERROR_NULL_POINTER; + PRUint32 currItemIndex; mMenuItemsArray.Count(&currItemIndex); + mMenuItemsArray.AppendElement(aMenuItem); // owning ref mNumMenuItems++; nsAutoString label; aMenuItem->GetLabel(label); - InsertMenuItemWithTruncation ( label, currItemIndex ); + InsertMenuItemWithTruncation(label, currItemIndex); + /* // I want to be internationalized too! nsAutoString keyEquivalent(NS_LITERAL_STRING(" ")); aMenuItem->GetShortcutChar(keyEquivalent); + if (!keyEquivalent.EqualsLiteral(" ")) { ToUpperCase(keyEquivalent); char keyStr[2]; keyEquivalent.ToCString(keyStr, sizeof(keyStr)); short inKey = keyStr[0]; - ::SetItemCmd(mMacMenuHandle, currItemIndex, inKey); - //::SetMenuItemKeyGlyph(mMacMenuHandle, mNumMenuItems, 0x61); + ::SetItemCmd(mMacMenu, currItemIndex, inKey); } - + PRUint8 modifiers; aMenuItem->GetModifiers(&modifiers); PRUint8 macModifiers = kMenuNoModifiers; @@ -285,65 +259,67 @@ NS_METHOD nsMenuX::AddMenuItem(nsIMenuItem * aMenuItem) if (!(knsMenuItemCommandModifier & modifiers)) macModifiers |= kMenuNoCommandModifier; - ::SetMenuItemModifiers(mMacMenuHandle, currItemIndex, macModifiers); + // ::SetMenuItemModifiers(mMacMenu, currItemIndex, macModifiers); // set its command. we get the unique command id from the menubar - nsCOMPtr dispatcher ( do_QueryInterface(mManager) ); - if ( dispatcher ) { + nsCOMPtr dispatcher(do_QueryInterface(mManager)); + if (dispatcher) { PRUint32 commandID = 0L; dispatcher->Register(aMenuItem, &commandID); - if ( commandID ) - ::SetMenuItemCommandID(mMacMenuHandle, currItemIndex, commandID); + if (commandID) + ::SetMenuItemCommandID(mMacMenu, currItemIndex, commandID); } - + */ PRBool isEnabled; aMenuItem->GetEnabled(&isEnabled); - if(isEnabled) - ::EnableMenuItem(mMacMenuHandle, currItemIndex); + if (isEnabled) + [[mMacMenu itemAtIndex:currItemIndex] setEnabled:YES]; else - ::DisableMenuItem(mMacMenuHandle, currItemIndex); + [[mMacMenu itemAtIndex:currItemIndex] setEnabled:NO]; PRBool isChecked; aMenuItem->GetChecked(&isChecked); - if(isChecked) - ::CheckMenuItem(mMacMenuHandle, currItemIndex, true); + if (isChecked) + [[mMacMenu itemAtIndex:currItemIndex] setState:NSOnState]; else - ::CheckMenuItem(mMacMenuHandle, currItemIndex, false); + [[mMacMenu itemAtIndex:currItemIndex] setState:NSOffState]; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::AddMenu(nsIMenu * aMenu) + +NS_IMETHODIMP nsMenuX::AddMenu(nsIMenu * aMenu) { // Add a submenu - if (!aMenu) return NS_ERROR_NULL_POINTER; + if (!aMenu) + return NS_ERROR_NULL_POINTER; - nsCOMPtr supports = do_QueryInterface(aMenu); - if (!supports) return NS_ERROR_NO_INTERFACE; + nsCOMPtr supports = do_QueryInterface(aMenu); + if (!supports) + return NS_ERROR_NO_INTERFACE; - mMenuItemsArray.AppendElement(supports); // owning ref PRUint32 currItemIndex; mMenuItemsArray.Count(&currItemIndex); + mMenuItemsArray.AppendElement(supports); // owning ref mNumMenuItems++; // We have to add it as a menu item and then associate it with the item nsAutoString label; aMenu->GetLabel(label); - InsertMenuItemWithTruncation ( label, currItemIndex ); + InsertMenuItemWithTruncation(label, currItemIndex); PRBool isEnabled; aMenu->GetEnabled(&isEnabled); if (isEnabled) - ::EnableMenuItem(mMacMenuHandle, currItemIndex); + [[mMacMenu itemAtIndex:currItemIndex] setEnabled:YES]; else - ::DisableMenuItem(mMacMenuHandle, currItemIndex); + [[mMacMenu itemAtIndex:currItemIndex] setEnabled:NO]; - MenuHandle childMenu; - if (aMenu->GetNativeData((void**)&childMenu) == NS_OK) - ::SetMenuItemHierarchicalMenu((MenuHandle) mMacMenuHandle, currItemIndex, childMenu); - + NSMenu* childMenu; + if (aMenu->GetNativeData((void**)&childMenu) == NS_OK) { + [[mMacMenu itemAtIndex:currItemIndex] setSubmenu:childMenu]; + } return NS_OK; } @@ -355,7 +331,7 @@ NS_METHOD nsMenuX::AddMenu(nsIMenu * aMenu) // middle-truncated to a certain pixel width with an elipsis. // void -nsMenuX :: InsertMenuItemWithTruncation ( nsAutoString & inItemLabel, PRUint32 inItemIndex ) +nsMenuX::InsertMenuItemWithTruncation(nsAutoString & inItemLabel, PRUint32 inItemIndex) { // ::TruncateThemeText() doesn't take the number of characters to truncate to, it takes a pixel with // to fit the string in. Ugh. I talked it over with sfraser and we couldn't come up with an @@ -363,99 +339,105 @@ nsMenuX :: InsertMenuItemWithTruncation ( nsAutoString & inItemLabel, PRUint32 i // to hard code it to something reasonable and bigger fonts will just have to deal. const short kMaxItemPixelWidth = 300; - CFMutableStringRef labelRef = ::CFStringCreateMutable ( kCFAllocatorDefault, inItemLabel.Length() ); - ::CFStringAppendCharacters ( labelRef, (UniChar*)inItemLabel.get(), inItemLabel.Length() ); + CFMutableStringRef labelRef = ::CFStringCreateMutable(kCFAllocatorDefault, inItemLabel.Length()); + ::CFStringAppendCharacters(labelRef, (UniChar*)inItemLabel.get(), inItemLabel.Length()); ::TruncateThemeText(labelRef, kThemeMenuItemFont, kThemeStateActive, kMaxItemPixelWidth, truncMiddle, NULL); - ::InsertMenuItemTextWithCFString(mMacMenuHandle, labelRef, inItemIndex, 0, 0); + [mMacMenu insertItem:[[[NSMenuItem alloc] initWithTitle:(NSString*)labelRef action:NULL keyEquivalent:@""] autorelease] atIndex:(inItemIndex)]; ::CFRelease(labelRef); - } // InsertMenuItemWithTruncation -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::AddSeparator() +NS_IMETHODIMP nsMenuX::AddSeparator() { - // HACK - We're not really appending an nsMenuItem but it - // needs to be here to make sure that event dispatching isn't off by one. - mMenuItemsArray.AppendElement(&gDummyMenuItemX); // owning ref + // We're not really appending an nsMenuItem but it needs to be here to make + // sure that event dispatching isn't off by one. + mMenuItemsArray.AppendElement(&gDummyMenuItemX); // owning ref PRUint32 numItems; mMenuItemsArray.Count(&numItems); - ::InsertMenuItem(mMacMenuHandle, "\p(-", numItems); + [mMacMenu addItem:[NSMenuItem separatorItem]]; mNumMenuItems++; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetItemCount(PRUint32 &aCount) + +NS_IMETHODIMP nsMenuX::GetItemCount(PRUint32 &aCount) { return mMenuItemsArray.Count(&aCount); } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetItemAt(const PRUint32 aPos, nsISupports *& aMenuItem) + +NS_IMETHODIMP nsMenuX::GetItemAt(const PRUint32 aPos, nsISupports *& aMenuItem) { mMenuItemsArray.GetElementAt(aPos, &aMenuItem); return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::InsertItemAt(const PRUint32 aPos, nsISupports * aMenuItem) + +NS_IMETHODIMP nsMenuX::InsertItemAt(const PRUint32 aPos, nsISupports * aMenuItem) { NS_ASSERTION(0, "Not implemented"); return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::RemoveItem(const PRUint32 aPos) + +NS_IMETHODIMP nsMenuX::RemoveItem(const PRUint32 aPos) { + //XXXJOSH Implement this NS_WARNING("Not implemented"); return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::RemoveAll() + +NS_IMETHODIMP nsMenuX::RemoveAll() { - if (mMacMenuHandle != NULL) { + if (mMacMenu != NULL) { + /* // clear command id's - nsCOMPtr dispatcher ( do_QueryInterface(mManager) ); - if ( dispatcher ) { - for ( int i = 1; i <= mNumMenuItems; ++i ) { + nsCOMPtr dispatcher(do_QueryInterface(mManager)); + if (dispatcher) { + for (unsigned int i = 1; i <= mNumMenuItems; ++i) { PRUint32 commandID = 0L; - OSErr err = ::GetMenuItemCommandID(mMacMenuHandle, i, (unsigned long*)&commandID); - if ( !err ) + OSErr err = ::GetMenuItemCommandID(mMacMenu, i, (unsigned long*)&commandID); + if (!err) dispatcher->Unregister(commandID); } } - ::DeleteMenuItems(mMacMenuHandle, 1, ::CountMenuItems(mMacMenuHandle)); + */ + //XXXJOSH we crash here sometimes, log message shows + // nsMenuX::RemoveAll: Total menu count is 16, attempting to access 31 + for (int i = mNumMenuItems - 1; i >= 0; i--) { + [mMacMenu removeItemAtIndex:i]; + } } - mMenuItemsArray.Clear(); // remove all items + mMenuItemsArray.Clear(); // remove all items return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::GetNativeData(void ** aData) + +NS_IMETHODIMP nsMenuX::GetNativeData(void ** aData) { - *aData = mMacMenuHandle; + *aData = mMacMenu; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::SetNativeData(void * aData) + +NS_IMETHODIMP nsMenuX::SetNativeData(void * aData) { - mMacMenuHandle = (MenuHandle) aData; + [mMacMenu release]; + mMacMenu = [(NSMenu*)aData retain]; return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::AddMenuListener(nsIMenuListener * aMenuListener) + +NS_IMETHODIMP nsMenuX::AddMenuListener(nsIMenuListener * aMenuListener) { - mListener = aMenuListener; // strong ref + mListener = aMenuListener; // strong ref return NS_OK; } -//------------------------------------------------------------------------- -NS_METHOD nsMenuX::RemoveMenuListener(nsIMenuListener * aMenuListener) + +NS_IMETHODIMP nsMenuX::RemoveMenuListener(nsIMenuListener * aMenuListener) { if (aMenuListener == mListener) mListener = nsnull; @@ -463,28 +445,30 @@ NS_METHOD nsMenuX::RemoveMenuListener(nsIMenuListener * aMenuListener) } -//------------------------------------------------------------------------- // // nsIMenuListener interface // -//------------------------------------------------------------------------- + + nsEventStatus nsMenuX::MenuItemSelected(const nsMenuEvent & aMenuEvent) { // all this is now handled by Carbon Events. return nsEventStatus_eConsumeNoDefault; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent) { - //printf("MenuSelected called for %s \n", NS_LossyConvertUCS2toASCII(mLabel).get()); + // printf("JOSH: MenuSelected called for %s \n", NS_LossyConvertUCS2toASCII(mLabel).get()); nsEventStatus eventStatus = nsEventStatus_eIgnore; // Determine if this is the correct menu to handle the event - MenuHandle selectedMenuHandle = (MenuHandle) aMenuEvent.mCommand; + MenuRef selectedMenuHandle = (MenuRef)aMenuEvent.mCommand; - if (mMacMenuHandle == selectedMenuHandle) { - if (mIsHelpMenu && mConstructed){ + // at this point, the carbon event handler was installed so there + // must be a carbon MenuRef to be had + if (GetCarbonMenuRef(mMacMenu) == selectedMenuHandle) { + if (mIsHelpMenu && mConstructed) { RemoveAll(); mConstructed = false; SetRebuild(PR_TRUE); @@ -492,7 +476,6 @@ nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent) // Open the node. mMenuContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE); - // Fire our oncreate handler. If we're told to stop, don't build the menu at all PRBool keepProcessing = OnCreate(); @@ -500,7 +483,7 @@ nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent) if (!mIsHelpMenu && !mNeedsRebuild || !keepProcessing) return nsEventStatus_eConsumeNoDefault; - if(!mConstructed || mNeedsRebuild) { + if (!mConstructed || mNeedsRebuild) { if (mNeedsRebuild) RemoveAll(); @@ -509,19 +492,20 @@ nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent) NS_ERROR("No doc shell"); return nsEventStatus_eConsumeNoDefault; } + if (mIsHelpMenu) { - HelpMenuConstruct(aMenuEvent, nsnull /* mParentWindow */, nsnull, docShell); + HelpMenuConstruct(aMenuEvent, nsnull, nsnull, docShell); mConstructed = true; } else { - MenuConstruct(aMenuEvent, nsnull /* mParentWindow */, nsnull, docShell); + MenuConstruct(aMenuEvent, nsnull, nsnull, docShell); mConstructed = true; - } - } + } + } OnCreated(); // Now that it's built, fire the popupShown event. eventStatus = nsEventStatus_eConsumeNoDefault; - } + } else { // Make sure none of our submenus are the ones that should be handling this PRUint32 numItems; @@ -541,7 +525,7 @@ nsEventStatus nsMenuX::MenuSelected(const nsMenuEvent & aMenuEvent) return eventStatus; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::MenuDeselected(const nsMenuEvent & aMenuEvent) { // Destroy the menu @@ -552,12 +536,12 @@ nsEventStatus nsMenuX::MenuDeselected(const nsMenuEvent & aMenuEvent) return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * /* menuNode */, - void * aDocShell) + void * aDocShell) { mConstructed = false; gConstructingMenu = PR_TRUE; @@ -565,7 +549,7 @@ nsEventStatus nsMenuX::MenuConstruct( // reset destroy handler flag so that we'll know to fire it next time this menu goes away. mDestroyHandlerCalled = PR_FALSE; - //printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUCS2toASCII(mLabel).get(), mMacMenuHandle); + //printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUCS2toASCII(mLabel).get(), mMacMenu); // Begin menuitem inner loop // Retrieve our menupopup. @@ -576,40 +560,37 @@ nsEventStatus nsMenuX::MenuConstruct( // Iterate over the kids PRUint32 count = menuPopup->GetChildCount(); - for ( PRUint32 i = 0; i < count; ++i ) { + for (PRUint32 i = 0; i < count; ++i) { nsIContent *child = menuPopup->GetChildAt(i); - if ( child ) { + if (child) { // depending on the type, create a menu item, separator, or submenu nsIAtom *tag = child->Tag(); - if ( tag == nsWidgetAtoms::menuitem ) + if (tag == nsWidgetAtoms::menuitem) LoadMenuItem(this, child); - else if ( tag == nsWidgetAtoms::menuseparator ) + else if (tag == nsWidgetAtoms::menuseparator) LoadSeparator(child); - else if ( tag == nsWidgetAtoms::menu ) + else if (tag == nsWidgetAtoms::menu) LoadSubMenu(this, child); } } // for each menu item gConstructingMenu = PR_FALSE; mNeedsRebuild = PR_FALSE; - //printf(" Done building, mMenuItemVoidArray.Count() = %d \n", mMenuItemVoidArray.Count()); + // printf("Done building, mMenuItemVoidArray.Count() = %d \n", mMenuItemVoidArray.Count()); return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::HelpMenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget * aParentWindow, void * /* menuNode */, void * aDocShell) -{ - //printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUCS2toASCII(mLabel).get(), mMacMenuHandle); - - int numHelpItems = ::CountMenuItems(mMacMenuHandle); - for (int i=0; i < numHelpItems; ++i) { +{ + int i, numHelpItems = [mMacMenu numberOfItems]; + for (i = 0; i < numHelpItems; i++) mMenuItemsArray.AppendElement(&gDummyMenuItemX); - } // Retrieve our menupopup. nsCOMPtr menuPopup; @@ -619,99 +600,75 @@ nsEventStatus nsMenuX::HelpMenuConstruct( // Iterate over the kids PRUint32 count = menuPopup->GetChildCount(); - for ( PRUint32 i = 0; i < count; ++i ) { + for (PRUint32 i = 0; i < count; ++i) { nsIContent *child = menuPopup->GetChildAt(i); - if ( child ) { + if (child) { // depending on the type, create a menu item, separator, or submenu nsIAtom *tag = child->Tag(); - if ( tag == nsWidgetAtoms::menuitem ) + if (tag == nsWidgetAtoms::menuitem) LoadMenuItem(this, child); - else if ( tag == nsWidgetAtoms::menuseparator ) + else if (tag == nsWidgetAtoms::menuseparator) LoadSeparator(child); - else if ( tag == nsWidgetAtoms::menu ) + else if (tag == nsWidgetAtoms::menu) LoadSubMenu(this, child); } } // for each menu item - //printf(" Done building, mMenuItemVoidArray.Count() = %d \n", mMenuItemVoidArray.Count()); + // printf("Done building, mMenuItemVoidArray.Count() = %d \n", mMenuItemVoidArray.Count()); return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::MenuDestruct(const nsMenuEvent & aMenuEvent) { - //printf("nsMenuX::MenuDestruct() called for %s \n", NS_LossyConvertUCS2toASCII(mLabel).get()); + // printf("nsMenuX::MenuDestruct() called for %s \n", NS_LossyConvertUCS2toASCII(mLabel).get()); // Fire our ondestroy handler. If we're told to stop, don't destroy the menu PRBool keepProcessing = OnDestroy(); - if ( keepProcessing ) { - if(mNeedsRebuild) { + if (keepProcessing) { + if (mNeedsRebuild) mConstructed = false; - //printf(" mMenuItemVoidArray.Count() = %d \n", mMenuItemVoidArray.Count()); - } // Close the node. mMenuContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::open, PR_TRUE); - OnDestroyed(); } - return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::CheckRebuild(PRBool & aNeedsRebuild) { - aNeedsRebuild = PR_TRUE; //mNeedsRebuild; + aNeedsRebuild = PR_TRUE; return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- + nsEventStatus nsMenuX::SetRebuild(PRBool aNeedsRebuild) { - if(!gConstructingMenu) { + if (!gConstructingMenu) { mNeedsRebuild = aNeedsRebuild; - //if(mNeedsRebuild) - // RemoveAll(); } return nsEventStatus_eIgnore; } -//------------------------------------------------------------------------- -/** -* Set enabled state -* -*/ -NS_METHOD nsMenuX::SetEnabled(PRBool aIsEnabled) + +NS_IMETHODIMP nsMenuX::SetEnabled(PRBool aIsEnabled) { mIsEnabled = aIsEnabled; - - if ( aIsEnabled ) - ::EnableMenuItem(mMacMenuHandle, 0); - else - ::DisableMenuItem(mMacMenuHandle, 0); - return NS_OK; } -//------------------------------------------------------------------------- -/** -* Get enabled state -* -*/ -NS_METHOD nsMenuX::GetEnabled(PRBool* aIsEnabled) + +NS_IMETHODIMP nsMenuX::GetEnabled(PRBool* aIsEnabled) { NS_ENSURE_ARG_POINTER(aIsEnabled); *aIsEnabled = mIsEnabled; return NS_OK; } -//------------------------------------------------------------------------- -/** -* Query if this is the help menu -* -*/ -NS_METHOD nsMenuX::IsHelpMenu(PRBool* aIsHelpMenu) + +NS_IMETHODIMP nsMenuX::IsHelpMenu(PRBool* aIsHelpMenu) { NS_ENSURE_ARG_POINTER(aIsHelpMenu); *aIsHelpMenu = mIsHelpMenu; @@ -719,109 +676,43 @@ NS_METHOD nsMenuX::IsHelpMenu(PRBool* aIsHelpMenu) } -//------------------------------------------------------------------------- -/** -* Get GetMenuContent -* -*/ -NS_METHOD nsMenuX::GetMenuContent(nsIContent ** aMenuContent) +NS_IMETHODIMP nsMenuX::GetMenuContent(nsIContent ** aMenuContent) { NS_ENSURE_ARG_POINTER(aMenuContent); NS_IF_ADDREF(*aMenuContent = mMenuContent); - return NS_OK; + return NS_OK; } -/* - Support for Carbon Menu Manager. - */ - -static pascal OSStatus MyMenuEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData) +NSMenu* nsMenuX::CreateMenuWithGeckoString(nsString& menuTitle) { - OSStatus result = eventNotHandledErr; - - UInt32 kind = ::GetEventKind(event); - if (kind == kEventMenuOpening || kind == kEventMenuClosed) { - nsISupports* supports = reinterpret_cast(userData); - nsCOMPtr listener(do_QueryInterface(supports)); - if (listener) { - MenuRef menuRef; - ::GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menuRef), NULL, &menuRef); - nsMenuEvent menuEvent(PR_TRUE, NS_MENU_SELECTED, nsnull); - menuEvent.time = PR_IntervalNow(); - menuEvent.mCommand = (PRUint32) menuRef; - if (kind == kEventMenuOpening) { - gCurrentlyTrackedMenuID = ::GetMenuID(menuRef); // remember which menu ID we're over for later - listener->MenuSelected(menuEvent); - } - else - listener->MenuDeselected(menuEvent); - } - } - else if ( kind == kEventMenuTargetItem ) { - // remember which menu ID we're over for later - MenuRef menuRef; - ::GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menuRef), NULL, &menuRef); - gCurrentlyTrackedMenuID = ::GetMenuID(menuRef); - } + NSString* title = [NSString stringWithCharacters:(UniChar*)menuTitle.get() length:menuTitle.Length()]; + NSMenu* myMenu = [[NSMenu alloc] initWithTitle:title]; + [myMenu setDelegate:mMenuDelegate]; - return result; -} - -static OSStatus InstallMyMenuEventHandler(MenuRef menuRef, void* userData, EventHandlerRef* outHandler) -{ - // install the event handler for the various carbon menu events. - static EventTypeSpec eventList[] = { - { kEventClassMenu, kEventMenuBeginTracking }, - { kEventClassMenu, kEventMenuEndTracking }, - { kEventClassMenu, kEventMenuChangeTrackingMode }, - { kEventClassMenu, kEventMenuOpening }, - { kEventClassMenu, kEventMenuClosed }, - { kEventClassMenu, kEventMenuTargetItem }, - { kEventClassMenu, kEventMenuMatchKey }, - { kEventClassMenu, kEventMenuEnableItems } - }; - static EventHandlerUPP gMyMenuEventHandlerUPP = NewEventHandlerUPP(&MyMenuEventHandler); - return ::InstallMenuEventHandler(menuRef, gMyMenuEventHandlerUPP, - sizeof(eventList) / sizeof(EventTypeSpec), eventList, - userData, outHandler); -} - -//------------------------------------------------------------------------- -MenuHandle nsMenuX::NSStringNewMenu(short menuID, nsString& menuTitle) -{ - MenuRef menuRef; - OSStatus status = ::CreateNewMenu(menuID, 0, &menuRef); - NS_ASSERTION(status == noErr,"nsMenuX::NSStringNewMenu: NewMenu failed."); - CFStringRef titleRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)menuTitle.get(), menuTitle.Length()); - NS_ASSERTION(titleRef,"nsMenuX::NSStringNewMenu: CFStringCreateWithCharacters failed."); - if (titleRef) { - ::SetMenuTitleWithCFString(menuRef, titleRef); - ::CFRelease(titleRef); - } + // we used to install Carbon event handlers here, but since NSMenu* doesn't + // create its underlying MenuRef until just before display, we delay until + // that happens. Now we install the event handlers when Cocoa notifies + // us that a menu is about to display - see the Cocoa MenuDelegate class. - status = InstallMyMenuEventHandler(menuRef, this, &mHandler); - NS_ASSERTION(status == noErr,"nsMenuX::NSStringNewMenu: InstallMyMenuEventHandler failed."); - - return menuRef; + return myMenu; } -//---------------------------------------- -void nsMenuX::LoadMenuItem( nsIMenu* inParentMenu, nsIContent* inMenuItemContent ) +void nsMenuX::LoadMenuItem(nsIMenu* inParentMenu, nsIContent* inMenuItemContent) { - if ( !inMenuItemContent ) + if (!inMenuItemContent) return; // if menu should be hidden, bail nsAutoString hidden; inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hidden); - if ( hidden.EqualsLiteral("true") ) + if (hidden.EqualsLiteral("true")) return; - // Create nsMenuItem - nsCOMPtr pnsMenuItem = do_CreateInstance ( kMenuItemCID ) ; - if ( pnsMenuItem ) { + // create nsMenuItem + nsCOMPtr pnsMenuItem = do_CreateInstance(kMenuItemCID); + if (pnsMenuItem) { nsAutoString disabled; nsAutoString checked; nsAutoString type; @@ -834,17 +725,17 @@ void nsMenuX::LoadMenuItem( nsIMenu* inParentMenu, nsIContent* inMenuItemContent inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuitemName); inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::command, menuitemCmd); - //printf("menuitem %s \n", NS_LossyConvertUCS2toASCII(menuitemName).get()); + // printf("menuitem %s \n", NS_LossyConvertUCS2toASCII(menuitemName).get()); PRBool enabled = ! (disabled.EqualsLiteral("true")); nsIMenuItem::EMenuItemType itemType = nsIMenuItem::eRegular; - if ( type.EqualsLiteral("checkbox") ) + if (type.EqualsLiteral("checkbox")) itemType = nsIMenuItem::eCheckbox; - else if ( type.EqualsLiteral("radio") ) + else if (type.EqualsLiteral("radio")) itemType = nsIMenuItem::eRadio; - nsCOMPtr docShell = do_QueryReferent(mDocShellWeakRef); + nsCOMPtr docShell = do_QueryReferent(mDocShellWeakRef); if (!docShell) return; @@ -852,9 +743,7 @@ void nsMenuX::LoadMenuItem( nsIMenu* inParentMenu, nsIContent* inMenuItemContent pnsMenuItem->Create(inParentMenu, menuitemName, PR_FALSE, itemType, enabled, mManager, docShell, inMenuItemContent); - // // Set key shortcut and modifiers - // nsAutoString keyValue; inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyValue); @@ -862,62 +751,62 @@ void nsMenuX::LoadMenuItem( nsIMenu* inParentMenu, nsIContent* inMenuItemContent // Try to find the key node. Get the document so we can do |GetElementByID| nsCOMPtr domDocument = do_QueryInterface(inMenuItemContent->GetDocument()); - if ( !domDocument ) + if (!domDocument) return; - + nsCOMPtr keyElement; if (!keyValue.IsEmpty()) domDocument->GetElementById(keyValue, getter_AddRefs(keyElement)); - if ( keyElement ) { - nsCOMPtr keyContent ( do_QueryInterface(keyElement) ); + if (keyElement) { + nsCOMPtr keyContent (do_QueryInterface(keyElement)); nsAutoString keyChar(NS_LITERAL_STRING(" ")); keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::key, keyChar); - if(!keyChar.EqualsLiteral(" ")) + if (!keyChar.EqualsLiteral(" ")) pnsMenuItem->SetShortcutChar(keyChar); PRUint8 modifiers = knsMenuItemNoModifier; - nsAutoString modifiersStr; + nsAutoString modifiersStr; keyContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::modifiers, modifiersStr); - char* str = ToNewCString(modifiersStr); - char* newStr; - char* token = nsCRT::strtok( str, ", \t", &newStr ); - while( token != NULL ) { - if (PL_strcmp(token, "shift") == 0) - modifiers |= knsMenuItemShiftModifier; - else if (PL_strcmp(token, "alt") == 0) - modifiers |= knsMenuItemAltModifier; - else if (PL_strcmp(token, "control") == 0) - modifiers |= knsMenuItemControlModifier; - else if ((PL_strcmp(token, "accel") == 0) || - (PL_strcmp(token, "meta") == 0)) { + char* str = ToNewCString(modifiersStr); + char* newStr; + char* token = nsCRT::strtok(str, ", \t", &newStr); + while (token != NULL) { + if (PL_strcmp(token, "shift") == 0) + modifiers |= knsMenuItemShiftModifier; + else if (PL_strcmp(token, "alt") == 0) + modifiers |= knsMenuItemAltModifier; + else if (PL_strcmp(token, "control") == 0) + modifiers |= knsMenuItemControlModifier; + else if ((PL_strcmp(token, "accel") == 0) || + (PL_strcmp(token, "meta") == 0)) { modifiers |= knsMenuItemCommandModifier; - } - - token = nsCRT::strtok( newStr, ", \t", &newStr ); - } - nsMemory::Free(str); + } - pnsMenuItem->SetModifiers ( modifiers ); + token = nsCRT::strtok(newStr, ", \t", &newStr); + } + nsMemory::Free(str); + + pnsMenuItem->SetModifiers(modifiers); } - if ( checked.EqualsLiteral("true") ) + if (checked.EqualsLiteral("true")) pnsMenuItem->SetChecked(PR_TRUE); else pnsMenuItem->SetChecked(PR_FALSE); - nsCOMPtr supports ( do_QueryInterface(pnsMenuItem) ); - inParentMenu->AddItem(supports); // Parent now owns menu item + nsCOMPtr supports(do_QueryInterface(pnsMenuItem)); + inParentMenu->AddItem(supports); // Parent now owns menu item } } void -nsMenuX::LoadSubMenu( nsIMenu * pParentMenu, nsIContent* inMenuItemContent ) +nsMenuX::LoadSubMenu(nsIMenu * pParentMenu, nsIContent* inMenuItemContent) { // if menu should be hidden, bail nsAutoString hidden; inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hidden); - if ( hidden.EqualsLiteral("true") ) + if (hidden.EqualsLiteral("true")) return; nsAutoString menuName; @@ -925,7 +814,7 @@ nsMenuX::LoadSubMenu( nsIMenu * pParentMenu, nsIContent* inMenuItemContent ) //printf("Creating Menu [%s] \n", NS_LossyConvertUCS2toASCII(menuName).get()); // Create nsMenu - nsCOMPtr pnsMenu ( do_CreateInstance(kMenuCID) ); + nsCOMPtr pnsMenu(do_CreateInstance(kMenuCID)); if (pnsMenu) { // Call Create nsCOMPtr docShell = do_QueryReferent(mDocShellWeakRef); @@ -937,25 +826,25 @@ nsMenuX::LoadSubMenu( nsIMenu * pParentMenu, nsIContent* inMenuItemContent ) // set if it's enabled or disabled nsAutoString disabled; inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, disabled); - if ( disabled.EqualsLiteral("true") ) - pnsMenu->SetEnabled ( PR_FALSE ); + if (disabled.EqualsLiteral("true")) + pnsMenu->SetEnabled (PR_FALSE); else - pnsMenu->SetEnabled ( PR_TRUE ); + pnsMenu->SetEnabled (PR_TRUE); // Make nsMenu a child of parent nsMenu. The parent takes ownership - nsCOMPtr supports2 ( do_QueryInterface(pnsMenu) ); - pParentMenu->AddItem(supports2); + nsCOMPtr supports2(do_QueryInterface(pnsMenu)); + pParentMenu->AddItem(supports2); } } void -nsMenuX::LoadSeparator ( nsIContent* inMenuItemContent ) +nsMenuX::LoadSeparator(nsIContent* inMenuItemContent) { // if item should be hidden, bail nsAutoString hidden; inMenuItemContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hidden); - if ( hidden.EqualsLiteral("true") ) + if (hidden.EqualsLiteral("true")) return; AddSeparator(); @@ -975,7 +864,7 @@ nsMenuX::OnCreate() nsEventStatus status = nsEventStatus_eIgnore; nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull, nsMouseEvent::eReal); - + nsCOMPtr popupContent; GetMenuPopupContent(getter_AddRefs(popupContent)); @@ -986,11 +875,11 @@ nsMenuX::OnCreate() } nsCOMPtr presContext; MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext) ); - if ( presContext ) { + if (presContext) { nsresult rv = NS_OK; nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent; rv = dispatchTo->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) + if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) return PR_FALSE; } @@ -1004,8 +893,7 @@ nsMenuX::OnCreate() PRUint32 count = popupContent->GetChildCount(); for (PRUint32 i = 0; i < count; i++) { nsIContent *grandChild = popupContent->GetChildAt(i); - nsIAtom *tag = grandChild->Tag(); - if (tag == nsWidgetAtoms::menuitem) { + if (grandChild->Tag() == nsWidgetAtoms::menuitem) { // See if we have a command attribute. nsAutoString command; grandChild->GetAttr(kNameSpaceID_None, nsWidgetAtoms::command, command); @@ -1015,7 +903,7 @@ nsMenuX::OnCreate() domDoc->GetElementById(command, getter_AddRefs(commandElt)); nsCOMPtr commandContent(do_QueryInterface(commandElt)); - if ( commandContent ) { + if (commandContent) { nsAutoString commandDisabled, menuDisabled; commandContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, commandDisabled); grandChild->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, menuDisabled); @@ -1068,12 +956,12 @@ nsMenuX::OnCreated() return PR_FALSE; } nsCOMPtr presContext; - MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext) ); - if ( presContext ) { + MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext)); + if (presContext) { nsresult rv = NS_OK; nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent; rv = dispatchTo->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) + if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) return PR_FALSE; } @@ -1089,7 +977,7 @@ nsMenuX::OnCreated() PRBool nsMenuX::OnDestroy() { - if ( mDestroyHandlerCalled ) + if (mDestroyHandlerCalled) return PR_TRUE; nsEventStatus status = nsEventStatus_eIgnore; @@ -1114,7 +1002,7 @@ nsMenuX::OnDestroy() mDestroyHandlerCalled = PR_TRUE; - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) + if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) return PR_FALSE; } return PR_TRUE; @@ -1137,15 +1025,15 @@ nsMenuX::OnDestroyed() GetMenuPopupContent(getter_AddRefs(popupContent)); nsCOMPtr presContext; - MenuHelpersX::DocShellToPresContext (docShell, getter_AddRefs(presContext) ); - if (presContext ) { + MenuHelpersX::DocShellToPresContext(docShell, getter_AddRefs(presContext)); + if (presContext) { nsresult rv = NS_OK; nsIContent* dispatchTo = popupContent ? popupContent : mMenuContent; rv = dispatchTo->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status); mDestroyHandlerCalled = PR_TRUE; - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) + if (NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault) return PR_FALSE; } return PR_TRUE; @@ -1160,17 +1048,17 @@ nsMenuX::OnDestroyed() void nsMenuX::GetMenuPopupContent(nsIContent** aResult) { - if (!aResult ) + if (!aResult) return; *aResult = nsnull; nsresult rv; - nsCOMPtr xblService = - do_GetService("@mozilla.org/xbl;1", &rv); - if ( !xblService ) + nsCOMPtr xblService = do_GetService("@mozilla.org/xbl;1", &rv); + if (!xblService) return; PRUint32 count = mMenuContent->GetChildCount(); + for (PRUint32 i = 0; i < count; i++) { PRInt32 dummy; nsIContent *child = mMenuContent->GetChildAt(i); @@ -1194,9 +1082,9 @@ nsMenuX::GetMenuPopupContent(nsIContent** aResult) // menu, since we always put it in there. // nsresult -nsMenuX :: CountVisibleBefore ( PRUint32* outVisibleBefore ) +nsMenuX::CountVisibleBefore (PRUint32* outVisibleBefore) { - NS_ASSERTION ( outVisibleBefore, "bad index param" ); + NS_ASSERTION(outVisibleBefore, "bad index param"); nsCOMPtr menubarParent = do_QueryInterface(mParent); if (!menubarParent) return NS_ERROR_FAILURE; @@ -1206,13 +1094,13 @@ nsMenuX :: CountVisibleBefore ( PRUint32* outVisibleBefore ) // Find this menu among the children of my parent menubar PRBool gotThisMenu = PR_FALSE; - *outVisibleBefore = 1; // start at 1, the apple menu will always be there - for ( PRUint32 i = 0; i < numMenus; ++i ) { + *outVisibleBefore = 1; // start at 1, the apple menu will always be there + for (PRUint32 i = 0; i < numMenus; i++) { nsCOMPtr currMenu; menubarParent->GetMenuAt(i, *getter_AddRefs(currMenu)); // we found ourselves, break out - if ( currMenu == NS_STATIC_CAST(nsIMenu*, this) ) { + if (currMenu == NS_STATIC_CAST(nsIMenu*, this)) { gotThisMenu = PR_TRUE; break; } @@ -1222,160 +1110,262 @@ nsMenuX :: CountVisibleBefore ( PRUint32* outVisibleBefore ) if (currMenu) { nsCOMPtr menuContent; currMenu->GetMenuContent(getter_AddRefs(menuContent)); - if ( menuContent ) { + if (menuContent) { nsAutoString hiddenValue, collapsedValue; menuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hiddenValue); menuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::collapsed, collapsedValue); - if ( !hiddenValue.EqualsLiteral("true") && !collapsedValue.EqualsLiteral("true")) + if (menuContent->GetChildCount() > 0 || + !hiddenValue.EqualsLiteral("true") && + !collapsedValue.EqualsLiteral("true")) { ++(*outVisibleBefore); + } } } } // for each menu return gotThisMenu ? NS_OK : NS_ERROR_FAILURE; - } // CountVisibleBefore -#pragma mark - - // // nsIChangeObserver // NS_IMETHODIMP -nsMenuX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom *aAttribute ) +nsMenuX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom *aAttribute) { - if (gConstructingMenu) - return NS_OK; - // ignore the |open| attribute, which is by far the most common - if ( aAttribute == nsWidgetAtoms::open ) + if (gConstructingMenu || (aAttribute == nsWidgetAtoms::open)) return NS_OK; nsCOMPtr menubarParent = do_QueryInterface(mParent); - if(aAttribute == nsWidgetAtoms::disabled) { + if (aAttribute == nsWidgetAtoms::disabled) { SetRebuild(PR_TRUE); nsAutoString valueString; mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, valueString); - if(valueString.EqualsLiteral("true")) + if (valueString.EqualsLiteral("true")) SetEnabled(PR_FALSE); else SetEnabled(PR_TRUE); - - ::DrawMenuBar(); } - else if(aAttribute == nsWidgetAtoms::label) { + else if (aAttribute == nsWidgetAtoms::label) { SetRebuild(PR_TRUE); mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, mLabel); - // reuse the existing menu, to avoid invalidating root menu bar. - NS_ASSERTION(mMacMenuHandle != NULL, "nsMenuX::AttributeChanged: invalid menu handle."); - RemoveAll(); - CFStringRef titleRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)mLabel.get(), mLabel.Length()); - NS_ASSERTION(titleRef, "nsMenuX::AttributeChanged: CFStringCreateWithCharacters failed."); - ::SetMenuTitleWithCFString(mMacMenuHandle, titleRef); - ::CFRelease(titleRef); - - if (menubarParent) - ::DrawMenuBar(); - + // invalidate my parent. If we're a submenu parent, we have to rebuild + // the parent menu in order for the changes to be picked up. If we're + // a regular menu, just change the title and redraw the menubar. + if (!menubarParent) { + nsCOMPtr parentListener(do_QueryInterface(mParent)); + parentListener->SetRebuild(PR_TRUE); + } + else { + // reuse the existing menu, to avoid rebuilding the root menu bar. + NS_ASSERTION(mMacMenu != NULL, "nsMenuX::AttributeChanged: invalid menu handle."); + RemoveAll(); + CFStringRef titleRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)mLabel.get(), mLabel.Length()); + NS_ASSERTION(titleRef, "nsMenuX::AttributeChanged: CFStringCreateWithCharacters failed."); + [mMacMenu setTitle:(NSString*)titleRef]; + ::CFRelease(titleRef); + } } - else if(aAttribute == nsWidgetAtoms::hidden || aAttribute == nsWidgetAtoms::collapsed) { + else if (aAttribute == nsWidgetAtoms::hidden || aAttribute == nsWidgetAtoms::collapsed) { SetRebuild(PR_TRUE); - - nsAutoString hiddenValue, collapsedValue; - mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hiddenValue); - mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::collapsed, collapsedValue); - - if (hiddenValue.EqualsLiteral("true") || collapsedValue.EqualsLiteral("true")) { - if ( mVisible ) { - if ( menubarParent ) { - PRUint32 indexToRemove = 0; - if ( NS_SUCCEEDED(CountVisibleBefore(&indexToRemove)) ) { - ++indexToRemove; // if there are N siblings before me, my index is N+1 - void *clientData = nsnull; - menubarParent->GetNativeData ( clientData ); - if ( clientData ) { - MenuRef menubar = reinterpret_cast(clientData); - ::SetMenuItemHierarchicalMenu(menubar, indexToRemove, nsnull); - ::DeleteMenuItem(menubar, indexToRemove); - mVisible = PR_FALSE; - } + + nsAutoString hiddenValue, collapsedValue; + mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, hiddenValue); + mMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::collapsed, collapsedValue); + + if (hiddenValue.EqualsLiteral("true") || collapsedValue.EqualsLiteral("true")) { + if (mVisible) { + if (menubarParent) { + PRUint32 indexToRemove = 0; + if (NS_SUCCEEDED(CountVisibleBefore(&indexToRemove))) { + indexToRemove++; // if there are N siblings before me, my index is N+1 + void *clientData = nsnull; + menubarParent->GetNativeData(clientData); + if (clientData) { + NSMenu* menubar = reinterpret_cast(clientData); + [menubar removeItemAtIndex:indexToRemove - 1]; + mVisible = PR_FALSE; } - } // if on the menubar - else { - // hide this submenu - NS_ASSERTION(PR_FALSE, "nsMenuX::AttributeChanged: WRITE HIDE CODE FOR SUBMENU."); } - } // if visible - else - NS_WARNING("You're hiding the menu twice, please stop"); - } // if told to hide menu - else { - if ( !mVisible ) { - if ( menubarParent ) { - PRUint32 insertAfter = 0; - if ( NS_SUCCEEDED(CountVisibleBefore(&insertAfter)) ) { - void *clientData = nsnull; - menubarParent->GetNativeData ( clientData ); - if ( clientData ) { - MenuRef menubar = reinterpret_cast(clientData); - // Shove this menu into its rightful place in the menubar. It doesn't matter - // what title we pass to InsertMenuItem() because when we stuff the actual menu - // handle in, the correct title goes with it. - ::InsertMenuItem(menubar, "\pPlaceholder", insertAfter); - ::SetMenuItemHierarchicalMenu(menubar, insertAfter + 1, mMacMenuHandle); // add 1 to get index of inserted item - mVisible = PR_TRUE; - } + } // if on the menubar + else { + // hide this submenu + NS_ASSERTION(PR_FALSE, "nsMenuX::AttributeChanged: WRITE HIDE CODE FOR SUBMENU."); + } + } // if visible + else + NS_WARNING("You're hiding the menu twice, please stop"); + } // if told to hide menu + else { + if (!mVisible) { + if (menubarParent) { + PRUint32 insertAfter = 0; + if (NS_SUCCEEDED(CountVisibleBefore(&insertAfter))) { + void *clientData = nsnull; + menubarParent->GetNativeData(clientData); + if (clientData) { + NSMenu* menubar = reinterpret_cast(clientData); + // Shove this menu into its rightful place in the menubar. It doesn't matter + // what title we pass to InsertMenuItem() because when we stuff the actual menu + // handle in, the correct title goes with it. + [menubar insertItemWithTitle:@"placeholder" action:NULL keyEquivalent:@"" atIndex:insertAfter - 1]; + [[menubar itemAtIndex:insertAfter] setSubmenu:mMacMenu]; + //::InsertMenuItem(menubar, "\pPlaceholder", insertAfter); + //::SetMenuItemHierarchicalMenu(menubar, insertAfter + 1, mMacMenu); // add 1 to get index of inserted item + mVisible = PR_TRUE; } - } // if on menubar - else { - // show this submenu - NS_ASSERTION(PR_FALSE, "nsMenuX::AttributeChanged: WRITE SHOW CODE FOR SUBMENU."); } - } // if not visible - } // if told to show menu - - if (menubarParent) { - ::DrawMenuBar(); - } + } // if on menubar + else { + // show this submenu + NS_ASSERTION(PR_FALSE, "nsMenuX::AttributeChanged: WRITE SHOW CODE FOR SUBMENU."); + } + } // if not visible + } // if told to show menu } return NS_OK; - } // AttributeChanged NS_IMETHODIMP -nsMenuX :: ContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer) +nsMenuX::ContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer) { if (gConstructingMenu) return NS_OK; - SetRebuild(PR_TRUE); + SetRebuild(PR_TRUE); RemoveItem(aIndexInContainer); mManager->Unregister(aChild); return NS_OK; - } // ContentRemoved NS_IMETHODIMP -nsMenuX :: ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer) +nsMenuX::ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer) { - if(gConstructingMenu) + if (gConstructingMenu) return NS_OK; - SetRebuild(PR_TRUE); + SetRebuild(PR_TRUE); return NS_OK; - } // ContentInserted + + +// +// Carbon event support +// + +static pascal OSStatus MyMenuEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData) +{ + OSStatus result = eventNotHandledErr; + UInt32 kind = ::GetEventKind(event); + if (kind == kEventMenuOpening || kind == kEventMenuClosed) { + //XXXJOSH userData is always null here!!! + nsISupports* supports = reinterpret_cast(userData); + nsCOMPtr listener(do_QueryInterface(supports)); + if (listener) { + MenuRef menuRef; + ::GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menuRef), NULL, &menuRef); + nsMenuEvent menuEvent(PR_TRUE, NS_MENU_SELECTED, nsnull); + menuEvent.time = PR_IntervalNow(); + menuEvent.mCommand = (PRUint32)menuRef; + if (kind == kEventMenuOpening) { + listener->MenuSelected(menuEvent); + } + else + listener->MenuDeselected(menuEvent); + } + } + return result; +} + +static OSStatus InstallMyMenuEventHandler(MenuRef menuRef, void* userData, EventHandlerRef* outHandler) +{ + //XXXJOSH do we really need all these events? + static EventTypeSpec eventList[] = { + {kEventClassMenu, kEventMenuBeginTracking}, + {kEventClassMenu, kEventMenuEndTracking}, + {kEventClassMenu, kEventMenuChangeTrackingMode}, + {kEventClassMenu, kEventMenuOpening}, + {kEventClassMenu, kEventMenuClosed}, + {kEventClassMenu, kEventMenuTargetItem}, + {kEventClassMenu, kEventMenuMatchKey}, + {kEventClassMenu, kEventMenuEnableItems}}; + + static EventHandlerUPP gMyMenuEventHandlerUPP = NewEventHandlerUPP(&MyMenuEventHandler); + OSStatus status = ::InstallMenuEventHandler(menuRef, gMyMenuEventHandlerUPP, + sizeof(eventList) / sizeof(EventTypeSpec), eventList, + userData, outHandler); + NS_ASSERTION(status == noErr,"Installing carbon menu events failed."); + return status; +} + +// #include +extern "C" MenuRef _NSGetCarbonMenu(NSMenu* aMenu); +static MenuRef GetCarbonMenuRef(NSMenu* aMenu) +{ + // we use a private API, so check for its existence and cache it (no static linking) + //XXXJOSH I don't feel like debugging this now, so I'll leave it for later + /* + typedef MenuRef (*NSGetCarbonMenuPtr)(NSMenu* menu); + static NSGetCarbonMenuPtr NSGetCarbonMenu = NULL; + + if (!NSGetCarbonMenu && NSIsSymbolNameDefined("_NSGetCarbonMenu")) + NSGetCarbonMenu = (NSGetCarbonMenuPtr)NSAddressOfSymbol(NSLookupAndBindSymbol("_NSGetCarbonMenu")); + + if (NSGetCarbonMenu) + return NSGetCarbonMenu(aMenu); + else + return NULL; + */ + return _NSGetCarbonMenu(aMenu); +} + + +// +// MenuDelegate Cocoa class, receives events for the gecko menus +// + + +@implementation MenuDelegate + +- (id)initWithGeckoMenu:(nsMenuX*)geckoMenu +{ + if ((self = [super init])) { + mGeckoMenu = geckoMenu; + mHaveInstalledCarbonEvents = FALSE; + } + return self; +} + + +// You can get a MenuRef from an NSMenu*, but not until it has been made visible +// or added to the main menu bar. Basically, Cocoa is attempting lazy loading, +// and that doesn't work for us. We don't need any carbon events until after the +// first time the menu is shown, so when that happens we install the carbon +// event handler. This works because at this point we can get a MenuRef without +// much trouble. This call is 10.3+, so this stuff won't work on 10.2.x or less. +- (void)menuNeedsUpdate:(NSMenu*)aMenu +{ + if (!mHaveInstalledCarbonEvents) { + MenuRef myMenuRef = GetCarbonMenuRef(aMenu); + if (myMenuRef) { + InstallMyMenuEventHandler(myMenuRef, mGeckoMenu, nil); // can't be nil because we need to clean up + mHaveInstalledCarbonEvents = TRUE; + } + } +} + +@end