/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsIServiceManager.h" #include "nsIComponentManager.h" #include "nsINameSpaceManager.h" #include "nsIMenu.h" #include "nsIMenuItem.h" #include "nsIContent.h" #include "nsMenuBar.h" #include "nsDynamicMDEF.h" #include "nsISupports.h" #include "nsIWidget.h" #include "nsGUIEvent.h" #include "nsString.h" #include "nsIDocument.h" #include "nsIDocShell.h" #include "nsIDocumentViewer.h" #include "nsIDocumentObserver.h" #include "nsWidgetAtoms.h" #include "nsIDOMDocument.h" #include #include #include #ifndef XP_MACOSX #include #endif #include #include #include "nsMacResources.h" #include "DefProcFakery.h" Handle gMDEF = nsnull; nsWeakPtr gMacMenubar; nsWeakPtr gOriginalMenuBar; bool gFirstMenuBar = true; // The four Golden Hierarchical Child Menus MenuHandle gLevel2HierMenu = nsnull; MenuHandle gLevel3HierMenu = nsnull; MenuHandle gLevel4HierMenu = nsnull; MenuHandle gLevel5HierMenu = nsnull; extern nsMenuStack gPreviousMenuStack; extern PRInt16 gCurrentMenuDepth; // #if APPLE_MENU_HACK #include "nsMenu.h" // need to get APPLE_MENU_HACK macro // #endif // CIDs #include "nsWidgetsCID.h" static NS_DEFINE_CID(kMenuCID, NS_MENU_CID); void InstallDefProc( short dpPath, ResType dpType, short dpID, Ptr dpAddr); PRInt32 gMenuBarCounter = 0; NS_IMPL_ISUPPORTS5(nsMenuBar, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, nsIChangeManager, nsISupportsWeakReference) // // nsMenuBar constructor // nsMenuBar::nsMenuBar() { gCurrentMenuDepth = 1; nsPreviousMenuStackUnwind(nsnull, nsnull); mNumMenus = 0; mParent = nsnull; mIsMenuBarAdded = PR_FALSE; mUnicodeTextRunConverter = nsnull; mOriginalMacMBarHandle = nsnull; mMacMBarHandle = nsnull; mDocument = nsnull; mOriginalMacMBarHandle = ::GetMenuBar(); Handle tmp = ::GetMenuBar(); ::SetMenuBar(tmp); this->SetNativeData((void*)tmp); ::ClearMenuBar(); mRefCnt = 1; // NS_GetWeakReference does an addref then a release, so this +1 is needed gMacMenubar = do_GetWeakReference((nsIMenuBar *)this); mRefCnt = 0; // copy from nsMenu.cpp ScriptCode ps[1]; ps[0] = ::GetScriptManagerVariable(smSysScript); OSErr err = ::CreateUnicodeToTextRunInfoByScriptCode(0x80000000, ps, &mUnicodeTextRunConverter); NS_ASSERTION(err==noErr,"nsMenu::nsMenu: CreateUnicodeToTextRunInfoByScriptCode failed."); ++gMenuBarCounter; } // // nsMenuBar destructor // nsMenuBar::~nsMenuBar() { mMenusArray.Clear(); // release all menus OSErr err = ::DisposeUnicodeToTextRunInfo(&mUnicodeTextRunConverter); NS_ASSERTION(err==noErr,"nsMenu::~nsMenu: DisposeUnicodeToTextRunInfo failed."); // make sure we unregister ourselves as a document observer if ( mDocument ) { nsCOMPtr observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) ); mDocument->RemoveObserver(observer); } --gMenuBarCounter; if(gMenuBarCounter == 1) { nsCOMPtr menubar = do_QueryReferent(gOriginalMenuBar); if(menubar) menubar->Paint(); } ::DisposeHandle(mOriginalMacMBarHandle); ::DisposeHandle(mMacMBarHandle); } nsEventStatus nsMenuBar::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 nsMenuBar::MenuSelected(const nsMenuEvent & aMenuEvent) { // Dispatch event nsEventStatus eventStatus = nsEventStatus_eIgnore; nsCOMPtr menuListener; nsCOMPtr theMenu; gPreviousMenuStack.GetMenuAt(gPreviousMenuStack.Count() - 1, getter_AddRefs(theMenu)); menuListener = do_QueryInterface(theMenu); 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 nsMenuBar::MenuDeselected(const nsMenuEvent & aMenuEvent) { return nsEventStatus_eIgnore; } nsEventStatus nsMenuBar::CheckRebuild(PRBool & aNeedsRebuild) { aNeedsRebuild = PR_TRUE; return nsEventStatus_eIgnore; } nsEventStatus nsMenuBar::SetRebuild(PRBool aNeedsRebuild) { return nsEventStatus_eIgnore; } void nsMenuBar :: GetDocument ( nsIWebShell* inWebShell, nsIDocument** outDocument ) { *outDocument = nsnull; nsCOMPtr docShell ( do_QueryInterface(inWebShell) ); nsCOMPtr cv; if ( docShell ) { docShell->GetContentViewer(getter_AddRefs(cv)); if (cv) { // get the document nsCOMPtr docv(do_QueryInterface(cv)); if (!docv) return; docv->GetDocument(outDocument); // addrefs } } } // // RegisterAsDocumentObserver // // Name says it all. // void nsMenuBar :: RegisterAsDocumentObserver ( nsIWebShell* inWebShell ) { nsCOMPtr doc; GetDocument(inWebShell, getter_AddRefs(doc)); if (!doc) return; // register ourselves nsCOMPtr observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) ); doc->AddObserver(observer); // also get pointer to doc, just in case webshell goes away // we can still remove ourself as doc observer directly from doc mDocument = doc; } // RegisterAsDocumentObesrver nsEventStatus nsMenuBar::MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, void * menubarNode, void * aWebShell ) { mWebShellWeakRef = do_GetWeakReference(NS_STATIC_CAST(nsIWebShell*, aWebShell)); nsIDOMNode* aDOMNode = NS_STATIC_CAST(nsIDOMNode*, menubarNode); mMenuBarContent = do_QueryInterface(aDOMNode); // strong ref if(gFirstMenuBar) { gOriginalMenuBar = do_GetWeakReference((nsIMenuBar *)this); gFirstMenuBar = false; // Add the 4 Golden Hierarchical Menus to the MenuList MenuHandle macMenuHandle = ::NewMenu(2, "\psubmenu"); if(macMenuHandle) { // get our fake MDEF ready to be stashed into every menu that comes our way. // if we fail creating it, we're probably so doomed there's no point in going // on. if ( !gMDEF ) { MenuDefUPP mdef = NewMenuDefProc( nsDynamicMDEFMain ); Boolean success = DefProcFakery::CreateDefProc( mdef, (**macMenuHandle).menuProc, &gMDEF ); if ( !success ) ::ExitToShell(); } gLevel2HierMenu = macMenuHandle; (**macMenuHandle).menuProc = gMDEF; (**macMenuHandle).menuWidth = -1; (**macMenuHandle).menuHeight = -1; ::InsertMenu(macMenuHandle, hierMenu); } macMenuHandle = ::NewMenu(3, "\psubmenu"); if(macMenuHandle) { gLevel3HierMenu = macMenuHandle; (**macMenuHandle).menuProc = gMDEF; (**macMenuHandle).menuWidth = -1; (**macMenuHandle).menuHeight = -1; ::InsertMenu(macMenuHandle, hierMenu); } macMenuHandle = ::NewMenu(4, "\psubmenu"); if(macMenuHandle) { gLevel4HierMenu = macMenuHandle; (**macMenuHandle).menuProc = gMDEF; (**macMenuHandle).menuWidth = -1; (**macMenuHandle).menuHeight = -1; ::InsertMenu(macMenuHandle, hierMenu); } macMenuHandle = ::NewMenu(5, "\psubmenu"); if(macMenuHandle) { gLevel5HierMenu = macMenuHandle; (**macMenuHandle).menuProc = gMDEF; (**macMenuHandle).menuWidth = -1; (**macMenuHandle).menuHeight = -1; ::InsertMenu(macMenuHandle, hierMenu); } } else { ::InsertMenu(gLevel2HierMenu, hierMenu); ::InsertMenu(gLevel3HierMenu, hierMenu); ::InsertMenu(gLevel4HierMenu, hierMenu); ::InsertMenu(gLevel5HierMenu, hierMenu); } Create(aParentWindow); nsCOMPtr webShell = do_QueryReferent(mWebShellWeakRef); if (webShell) RegisterAsDocumentObserver(webShell); // 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(nsIWebShell*, aWebShell), menu); // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership AddMenu(pnsMenu); nsAutoString menuIDstring; menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring); if ( menuIDstring == NS_LITERAL_STRING("menu_Help") ) { nsMenuEvent event; MenuHandle handle = nsnull; ::HMGetHelpMenuHandle(&handle); event.mCommand = (unsigned int) handle; nsCOMPtr listener(do_QueryInterface(pnsMenu)); listener->MenuSelected(event); } } } } } // for each menu // Give the aParentWindow this nsMenuBar to hold onto. // The parent takes ownership aParentWindow->SetMenuBar(this); Handle tempMenuBar = ::GetMenuBar(); // Get a copy of the menu list SetNativeData((void*)tempMenuBar); return nsEventStatus_eIgnore; } nsEventStatus nsMenuBar::MenuDestruct(const nsMenuEvent & aMenuEvent) { return nsEventStatus_eIgnore; } //------------------------------------------------------------------------- // // Create the proper widget // //------------------------------------------------------------------------- NS_METHOD nsMenuBar::Create(nsIWidget *aParent) { SetParent(aParent); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::GetParent(nsIWidget *&aParent) { NS_IF_ADDREF(aParent = mParent); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::SetParent(nsIWidget *aParent) { mParent = aParent; // weak ref return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::AddMenu(nsIMenu * aMenu) { // XXX add to internal data structure nsCOMPtr supports = do_QueryInterface(aMenu); if(supports) mMenusArray.AppendElement(supports); // owner #ifdef APPLE_MENU_HACK if (mNumMenus == 0) { Str32 menuStr = { 1, 0x14 }; MenuHandle appleMenu = ::NewMenu(kAppleMenuID, menuStr); if (appleMenu) { // this code reads the "label" attribute from the with // id="aboutName" and puts its label in the Apple Menu nsAutoString label; nsCOMPtr menu; aMenu->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); } } } ::AppendMenu(appleMenu, "\pa"); MenuHelpers::SetMenuItemText(appleMenu, 1, label, mUnicodeTextRunConverter); ::AppendMenu(appleMenu, "\p-"); ::AppendResMenu(appleMenu, 'DRVR'); ::InsertMenu(appleMenu, 0); } } #endif MenuHandle menuHandle = nsnull; aMenu->GetNativeData((void**)&menuHandle); mNumMenus++; PRBool helpMenu; aMenu->IsHelpMenu(&helpMenu); if(!helpMenu) { nsCOMPtr menu; aMenu->GetMenuContent(getter_AddRefs(menu)); nsAutoString menuHidden; menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, menuHidden); if( menuHidden != NS_LITERAL_STRING("true")) ::InsertMenu(menuHandle, 0); } return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::GetMenuCount(PRUint32 &aCount) { aCount = mNumMenus; return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::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 nsMenuBar::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu) { return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::RemoveMenu(const PRUint32 aCount) { mMenusArray.RemoveElementAt(aCount); ::DrawMenuBar(); return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::RemoveAll() { NS_ASSERTION(0, "Not implemented!"); // mMenusArray.Clear(); // maybe? return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::GetNativeData(void *& aData) { aData = (void *) mMacMBarHandle; return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::SetNativeData(void* aData) { Handle menubarHandle = (Handle)aData; if(mMacMBarHandle && mMacMBarHandle != menubarHandle) ::DisposeHandle(mMacMBarHandle); mMacMBarHandle = menubarHandle; return NS_OK; } //------------------------------------------------------------------------- NS_METHOD nsMenuBar::Paint() { gMacMenubar = do_GetWeakReference((nsIMenuBar *)this); ::SetMenuBar(mMacMBarHandle); // Now we have blown away the merged Help menu, so we have to rebuild it PRUint32 numItems; mMenusArray.Count(&numItems); for (PRInt32 i = numItems - 1; i >= 0; --i) { nsCOMPtr thisItem = getter_AddRefs(mMenusArray.ElementAt(i)); nsCOMPtr menu = do_QueryInterface(thisItem); PRBool isHelpMenu = PR_FALSE; if (menu) menu->IsHelpMenu(&isHelpMenu); if (isHelpMenu) { MenuHandle helpMenuHandle = nil; ::HMGetHelpMenuHandle(&helpMenuHandle); menu->SetNativeData((void*)helpMenuHandle); nsMenuEvent event; event.mCommand = (unsigned int) helpMenuHandle; nsCOMPtr listener = do_QueryInterface(menu); listener->MenuSelected(event); } } ::DrawMenuBar(); return NS_OK; } #pragma mark - // // nsIDocumentObserver // this is needed for menubar changes // NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsMenuBar) NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsMenuBar) NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsMenuBar) NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsMenuBar) NS_IMETHODIMP nsMenuBar::BeginUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType ) { return NS_OK; } NS_IMETHODIMP nsMenuBar::EndUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType ) { return NS_OK; } NS_IMETHODIMP nsMenuBar::CharacterDataChanged( nsIDocument * aDocument, nsIContent * aContent, PRBool aAppend) { return NS_OK; } NS_IMETHODIMP nsMenuBar::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 ); } } } return NS_OK; } NS_IMETHODIMP nsMenuBar::ContentReplaced( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aOldChild, nsIContent * aNewChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHODIMP nsMenuBar::DocumentWillBeDestroyed( nsIDocument * aDocument ) { mDocument = nsnull; // just for yucks return NS_OK; } NS_IMETHODIMP nsMenuBar::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 ); return NS_OK; } NS_IMETHODIMP nsMenuBar::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 ); } } } return NS_OK; } NS_IMETHODIMP nsMenuBar::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 ); } } } return NS_OK; } #pragma mark - // // nsIChangeManager // // We don't use a |nsSupportsHashtable| because we know that the lifetime of all these items // is bouded by the lifetime of the menubar. No need to add any more strong refs to the // picture because the containment hierarchy already uses strong refs. // NS_IMETHODIMP nsMenuBar :: Register ( nsIContent *aContent, nsIChangeObserver *aMenuObject ) { nsVoidKey key ( aContent ); mObserverTable.Put ( &key, aMenuObject ); return NS_OK; } NS_IMETHODIMP nsMenuBar :: Unregister ( nsIContent *aContent ) { nsVoidKey key ( aContent ); mObserverTable.Remove ( &key ); return NS_OK; } NS_IMETHODIMP nsMenuBar :: 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 - // // SetMenuItemText // // A utility routine for handling unicode->OS text conversions for setting the item // text in a menu. // void MenuHelpers :: SetMenuItemText ( MenuHandle inMacMenuHandle, short inMenuItem, const nsString& inMenuString, const UnicodeToTextRunInfo inConverter ) { // ::TruncString() 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 // easy way to compute what this should be given the system font, etc, so we're just going // to hard code it to something reasonable and bigger fonts will just have to deal. const short kMaxItemPixelWidth = 300; short themeFontID; SInt16 themeFontSize; Style themeFontStyle; char* scriptRunText = ConvertToScriptRun ( inMenuString, inConverter, &themeFontID, &themeFontSize, &themeFontStyle ); if ( scriptRunText ) { // convert it to a pascal string Str255 menuTitle; short scriptRunTextLength = strlen(scriptRunText); if (scriptRunTextLength > 255) scriptRunTextLength = 255; BlockMoveData(scriptRunText, &menuTitle[1], scriptRunTextLength); menuTitle[0] = scriptRunTextLength; // if the item text is too long, truncate it. ::TruncString ( kMaxItemPixelWidth, menuTitle, truncMiddle ); ::SetMenuItemText(inMacMenuHandle, inMenuItem, menuTitle); OSErr err = ::SetMenuItemFontID(inMacMenuHandle, inMenuItem, themeFontID); nsMemory::Free(scriptRunText); } } // SetMenuItemText // // ConvertToScriptRun // // Converts unicode to a single script run and extract the relevant font information. The // caller is responsible for deleting the memory allocated by this call with |nsMemory::Free()|. // Returns |nsnull| if an error occurred. // char* MenuHelpers :: ConvertToScriptRun ( const nsString & inStr, const UnicodeToTextRunInfo inConverter, short* outFontID, SInt16* outFontSize, Style* outFontStyle ) { // // extract the Unicode text from the nsString and convert it into a single script run // const PRUnichar* unicodeText = inStr.get(); size_t unicodeTextLengthInBytes = inStr.Length() * sizeof(PRUnichar); size_t scriptRunTextSizeInBytes = unicodeTextLengthInBytes * 2; char* scriptRunText = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(scriptRunTextSizeInBytes + sizeof(char))); if ( !scriptRunText ) return nsnull; ScriptCodeRun convertedTextScript; size_t unicdeTextReadInBytes, scriptRunTextLengthInBytes, scriptCodeRunListLength; OSErr err = ::ConvertFromUnicodeToScriptCodeRun(inConverter, unicodeTextLengthInBytes, NS_REINTERPRET_CAST(const PRUint16*, unicodeText), 0, /* no flags*/ 0,NULL,NULL,NULL, /* no offset arrays */ scriptRunTextSizeInBytes,&unicdeTextReadInBytes,&scriptRunTextLengthInBytes, scriptRunText, 1 /* count of script runs*/,&scriptCodeRunListLength,&convertedTextScript); NS_ASSERTION(err==noErr,"nsMenu::NSStringSetMenuItemText: ConvertFromUnicodeToScriptCodeRun failed."); if ( err ) { nsMemory::Free(scriptRunText); return nsnull; } scriptRunText[scriptRunTextLengthInBytes] = '\0'; // null terminate // // get a font from the script code // Str255 themeFontName; err = ::GetThemeFont(kThemeSystemFont, convertedTextScript.script, themeFontName, outFontSize, outFontStyle); NS_ASSERTION(err==noErr,"nsMenu::NSStringSetMenuItemText: GetThemeFont failed."); if ( err ) { nsMemory::Free(scriptRunText); return nsnull; } ::GetFNum(themeFontName, outFontID); return scriptRunText; } // ConvertToScriptRun // // WebShellToPresContext // // Helper to dig out a pres context from a webshell. A common thing to do before // sending an event into the dom. // nsresult MenuHelpers :: WebShellToPresContext (nsIWebShell* inWebShell, nsIPresContext** outContext ) { NS_ENSURE_ARG_POINTER(outContext); *outContext = nsnull; if (!inWebShell) return NS_ERROR_INVALID_ARG; nsresult retval = NS_OK; nsCOMPtr docShell(do_QueryInterface(inWebShell)); nsCOMPtr contentViewer; docShell->GetContentViewer(getter_AddRefs(contentViewer)); if ( contentViewer ) { nsCOMPtr docViewer ( do_QueryInterface(contentViewer) ); if ( docViewer ) docViewer->GetPresContext(*outContext); // AddRefs for us else retval = NS_ERROR_FAILURE; } else retval = NS_ERROR_FAILURE; return retval; } // WebShellToPresContext