Files
Mozilla/mozilla/widget/src/mac/nsMenuBar.cpp

990 lines
28 KiB
C++

/* -*- 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 "nsIDOMXULDocument.h"
#include <Menus.h>
#include <TextUtils.h>
#include <Balloons.h>
#ifndef XP_MACOSX
#include <Traps.h>
#endif
#include <Resources.h>
#include <Appearance.h>
#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(kMenuBarCID, NS_MENUBAR_CID);
static NS_DEFINE_CID(kMenuCID, NS_MENU_CID);
static NS_DEFINE_CID(kMenuItemCID, NS_MENUITEM_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);
NS_INIT_REFCNT();
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 = getter_AddRefs(NS_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<nsIDocumentObserver> observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) );
mDocument->RemoveObserver(observer);
}
--gMenuBarCounter;
if(gMenuBarCounter == 1) {
nsCOMPtr<nsIMenuBar> 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<nsISupports> menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
nsCOMPtr<nsIMenuListener> 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<nsIMenuListener> menuListener;
nsCOMPtr<nsIMenu> 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<nsISupports> menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
nsCOMPtr<nsIMenuListener> 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<nsIDocShell> docShell ( do_QueryInterface(inWebShell) );
nsCOMPtr<nsIContentViewer> cv;
if ( docShell ) {
docShell->GetContentViewer(getter_AddRefs(cv));
if (cv) {
// get the document
nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(cv));
if (!docv)
return;
docv->GetDocument(*outDocument); // addrefs
}
}
}
//
// RegisterAsDocumentObserver
//
// Name says it all.
//
void
nsMenuBar :: RegisterAsDocumentObserver ( nsIWebShell* inWebShell )
{
nsCOMPtr<nsIDocument> doc;
GetDocument(inWebShell, getter_AddRefs(doc));
if (!doc)
return;
// register ourselves
nsCOMPtr<nsIDocumentObserver> 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 = getter_AddRefs(NS_GetWeakReference(NS_STATIC_CAST(nsIWebShell*, aWebShell)));
nsIDOMNode* aDOMNode = NS_STATIC_CAST(nsIDOMNode*, menubarNode);
mMenuBarContent = do_QueryInterface(aDOMNode); // strong ref
if(gFirstMenuBar) {
gOriginalMenuBar = getter_AddRefs(NS_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<nsIWebShell> webShell = do_QueryReferent(mWebShellWeakRef);
if (webShell)
RegisterAsDocumentObserver(webShell);
// set this as a nsMenuListener on aParentWindow
aParentWindow->AddMenuListener((nsIMenuListener *)this);
PRInt32 count;
mMenuBarContent->ChildCount(count);
for ( int i = 0; i < count; ++i ) {
nsCOMPtr<nsIContent> menu;
mMenuBarContent->ChildAt ( i, *getter_AddRefs(menu) );
if ( menu ) {
nsCOMPtr<nsIAtom> tag;
menu->GetTag ( *getter_AddRefs(tag) );
if (tag == nsWidgetAtoms::menu) {
nsAutoString menuName;
nsAutoString menuAccessKey(NS_LITERAL_STRING(" "));
menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::accesskey, menuAccessKey);
// Don't create the whole menu yet, just add in the top level names
// Create nsMenu, the menubar will own it
nsCOMPtr<nsIMenu> 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<nsIMenuListener> 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<nsISupports> 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 <menuitem/> with
// id="aboutName" and puts its label in the Apple Menu
nsAutoString label;
nsCOMPtr<nsIContent> menu;
aMenu->GetMenuContent(getter_AddRefs(menu));
if (menu) {
nsCOMPtr<nsIDocument> doc;
menu->GetDocument(*getter_AddRefs(doc));
if (doc) {
nsCOMPtr<nsIDOMDocument> domdoc ( do_QueryInterface(doc) );
if ( domdoc ) {
nsCOMPtr<nsIDOMElement> 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<nsIContent> 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<nsISupports> 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 = getter_AddRefs(NS_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<nsISupports> thisItem = getter_AddRefs(mMenusArray.ElementAt(i));
nsCOMPtr<nsIMenu> 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<nsIMenuListener> listener = do_QueryInterface(menu);
listener->MenuSelected(event);
}
}
::DrawMenuBar();
return NS_OK;
}
#pragma mark -
//
// nsIDocumentObserver
// this is needed for menubar changes
//
NS_IMETHODIMP
nsMenuBar::BeginUpdate( nsIDocument * aDocument )
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::EndUpdate( nsIDocument * aDocument )
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::BeginLoad( nsIDocument * aDocument )
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::EndLoad( nsIDocument * aDocument )
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::BeginReflow( nsIDocument * aDocument, nsIPresShell * aShell)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::EndReflow( nsIDocument * aDocument, nsIPresShell * aShell)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentChanged( nsIDocument * aDocument, nsIContent * aContent, nsISupports * aSubContent)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentStatesChanged( nsIDocument * aDocument, nsIContent * aContent1,
nsIContent * aContent2, PRInt32 aStateMask)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentAppended( nsIDocument * aDocument, nsIContent * aContainer,
PRInt32 aNewIndexInContainer)
{
if ( aContainer == mMenuBarContent ) {
//Register(aContainer, );
//InsertMenu ( aNewIndexInContainer );
}
else {
nsCOMPtr<nsIChangeObserver> obs;
Lookup ( aContainer, getter_AddRefs(obs) );
if ( obs )
obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
else {
nsCOMPtr<nsIContent> parent;
aContainer->GetParent(*getter_AddRefs(parent));
if(parent) {
Lookup ( parent, getter_AddRefs(obs) );
if ( obs )
obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentReplaced( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aOldChild,
nsIContent * aNewChild, PRInt32 aIndexInContainer)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleSheetAdded( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleSheetRemoved(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleSheetDisabledStateChanged(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet,
PRBool aDisabled)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleRuleChanged( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet,
nsIStyleRule * aStyleRule, PRInt32 aHint)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleRuleAdded( nsIDocument * aDocument, nsIStyleSheet * aStyleSheet,
nsIStyleRule * aStyleRule)
{
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::StyleRuleRemoved(nsIDocument * aDocument, nsIStyleSheet * aStyleSheet,
nsIStyleRule * aStyleRule)
{
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, PRInt32 aHint)
{
// lookup and dispatch to registered thang.
nsCOMPtr<nsIChangeObserver> obs;
Lookup ( aContent, getter_AddRefs(obs) );
if ( obs )
obs->AttributeChanged ( aDocument, aNameSpaceID, aAttribute, aHint );
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentRemoved( nsIDocument * aDocument, nsIContent * aContainer,
nsIContent * aChild, PRInt32 aIndexInContainer )
{
if ( aContainer == mMenuBarContent ) {
Unregister(aChild);
RemoveMenu ( aIndexInContainer );
}
else {
nsCOMPtr<nsIChangeObserver> obs;
Lookup ( aContainer, getter_AddRefs(obs) );
if ( obs )
obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
else {
nsCOMPtr<nsIContent> parent;
aContainer->GetParent(*getter_AddRefs(parent));
if(parent) {
Lookup ( parent, getter_AddRefs(obs) );
if ( obs )
obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMenuBar::ContentInserted( nsIDocument * aDocument, nsIContent * aContainer,
nsIContent * aChild, PRInt32 aIndexInContainer )
{
if ( aContainer == mMenuBarContent ) {
//Register(aChild, );
//InsertMenu ( aIndexInContainer );
}
else {
nsCOMPtr<nsIChangeObserver> obs;
Lookup ( aContainer, getter_AddRefs(obs) );
if ( obs )
obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
else {
nsCOMPtr<nsIContent> parent;
aContainer->GetParent(*getter_AddRefs(parent));
if(parent) {
Lookup ( parent, getter_AddRefs(obs) );
if ( obs )
obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
}
}
}
return NS_OK;
}
#pragma mark -
//
// nsIChangeManager
//
// We don't use a |nsSupportsHashtable| because we know that the lifetime of all these items
// is bouded by the lifetime of the menubar. No need to add any more strong refs to the
// picture because the containment hierarchy already uses strong refs.
//
NS_IMETHODIMP
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<nsIDocShell> docShell(do_QueryInterface(inWebShell));
nsCOMPtr<nsIContentViewer> contentViewer;
docShell->GetContentViewer(getter_AddRefs(contentViewer));
if ( contentViewer ) {
nsCOMPtr<nsIDocumentViewer> 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