431 lines
12 KiB
C++
431 lines
12 KiB
C++
/*
|
|
* 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 the Mozilla OS/2 libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is John Fairhurst,
|
|
* <john_fairhurst@iname.com>. Portions created by John Fairhurst are
|
|
* Copyright (C) 1999 John Fairhurst. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
*/
|
|
|
|
#include "nsMenuBase.h"
|
|
#include "nsMenuItem.h"
|
|
#include "nsMenu.h"
|
|
#include "nsToolkit.h"
|
|
#include "nsISupportsArray.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIDOMNode.h"
|
|
#include "nsIDOMElement.h"
|
|
|
|
// Common code used by menu classes
|
|
|
|
nsMenuBase::nsMenuBase() : mToolkit(0), mWnd(0), mElements(nsnull)
|
|
{}
|
|
|
|
void nsMenuBase::Create( HWND hwndParent, nsToolkit *aToolkit)
|
|
{
|
|
NS_ASSERTION( aToolkit, "Missing toolkit in menucreation");
|
|
|
|
mToolkit = aToolkit;
|
|
NS_ADDREF(mToolkit);
|
|
|
|
// thread-switch if necessary
|
|
if( !mToolkit->IsPMThread())
|
|
{
|
|
NS_NOTYETIMPLEMENTED( "Threaded menus");
|
|
}
|
|
else
|
|
{
|
|
mWnd = WinCreateWindow( hwndParent,
|
|
WC_MENU,
|
|
0, // text
|
|
WindowStyle(),
|
|
0, 0, 0, 0, // pos/size
|
|
hwndParent, // owner
|
|
HWND_TOP,
|
|
0, // window ID
|
|
0, 0); // ctldata, presparams
|
|
NS_ASSERTION( mWnd, "No menu window");
|
|
|
|
// store nsMenuBase * in QWL_USER
|
|
WinSetWindowPtr( mWnd, QWL_USER, this);
|
|
|
|
// nice font (hmm)
|
|
WinSetPresParam( mWnd, PP_FONTNAMESIZE,
|
|
strlen( gModuleData.pszFontNameSize) + 1,
|
|
gModuleData.pszFontNameSize);
|
|
|
|
NS_NewISupportsArray( &mElements);
|
|
}
|
|
}
|
|
|
|
ULONG nsMenuBase::WindowStyle()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void nsMenuBase::Destroy()
|
|
{
|
|
if( mToolkit)
|
|
if( !mToolkit->IsPMThread())
|
|
{
|
|
NS_NOTYETIMPLEMENTED( "Threaded menus");
|
|
}
|
|
else
|
|
{
|
|
// XXX Not sure about this -- need to think more.
|
|
// Shouldn't it be dealt with by the normal parent-death process?
|
|
WinDestroyWindow( mWnd);
|
|
mWnd = 0;
|
|
}
|
|
}
|
|
|
|
// dumb helper
|
|
MRESULT nsMenuBase::SendMsg( ULONG m, MPARAM mp1, MPARAM mp2)
|
|
{
|
|
MRESULT mrc = 0;
|
|
if( mToolkit)
|
|
mrc = mToolkit->SendMsg( mWnd, m, mp1, mp2);
|
|
return mrc;
|
|
}
|
|
|
|
nsMenuBase::~nsMenuBase()
|
|
{
|
|
if( mWnd) Destroy();
|
|
NS_IF_RELEASE(mToolkit);
|
|
NS_IF_RELEASE(mElements);
|
|
}
|
|
|
|
nsresult nsMenuBase::GetItemCount( PRUint32 &aCount)
|
|
{
|
|
MRESULT rc = SendMsg( MM_QUERYITEMCOUNT);
|
|
aCount = SHORT1FROMMP( rc);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMenuBase::GetNativeData( void **aNative)
|
|
{
|
|
*aNative = (void*) mWnd;
|
|
return NS_OK;
|
|
}
|
|
|
|
// New all-singing, all-dancing insert routine.
|
|
//
|
|
// The 'aThing' may be an nsIMenu or an nsIMenuItem, joy.
|
|
// If it is null, then we're talking about a separator, though this ought
|
|
// not to be used.
|
|
nsresult nsMenuBase::InsertItemAt( nsISupports *aThing, PRUint32 aPos)
|
|
{
|
|
nsIMenu *pMenu = nsnull;
|
|
nsIMenuItem *pItem = nsnull;
|
|
MENUITEM mI = { aPos, 0, 0, 0, 0, (ULONG) aThing };
|
|
char *pStr = nsnull;
|
|
|
|
// XXX needs to use unicode converter to get right text
|
|
// This is very much an issue now that menus are constructed
|
|
// from XUL and so can have an arbitrary encoding.
|
|
//
|
|
// Whether PM will know what to do with non-western characters
|
|
// is another issue! Probably okay if it's in the process'
|
|
// codepage (font set via presparams, which take that cp)
|
|
|
|
// set up menitem depending on what we have...
|
|
if( nsnull == aThing)
|
|
{
|
|
mI.afStyle |= MIS_SEPARATOR;
|
|
}
|
|
else if( NS_SUCCEEDED( aThing->QueryInterface( nsIMenu::GetIID(),
|
|
(void**) &pMenu)))
|
|
{
|
|
void *hwnd = nsnull;
|
|
|
|
nsString aString;
|
|
pMenu->GetLabel( aString);
|
|
pStr = gModuleData.ConvertFromUcs( aString);
|
|
|
|
mI.afStyle |= MIS_SUBMENU | MIS_TEXT;
|
|
|
|
pMenu->GetNativeData( &hwnd);
|
|
mI.hwndSubMenu = (HWND) hwnd;
|
|
|
|
NS_RELEASE(pMenu);
|
|
}
|
|
else if( NS_SUCCEEDED( aThing->QueryInterface( nsIMenuItem::GetIID(),
|
|
(void**) &pItem)))
|
|
{
|
|
nsMenuItem *pPMItem = (nsMenuItem*) pItem; // XXX
|
|
nsString aString;
|
|
PRBool bIsSep = PR_FALSE;
|
|
|
|
mI.id = pPMItem->GetPMID();
|
|
|
|
pPMItem->IsSeparator( bIsSep);
|
|
if( bIsSep)
|
|
{
|
|
mI.afStyle |= MIS_SEPARATOR;
|
|
}
|
|
else
|
|
{
|
|
mI.afStyle |= MIS_TEXT;
|
|
pPMItem->GetLabel( aString);
|
|
pStr = gModuleData.ConvertFromUcs( aString);
|
|
}
|
|
NS_RELEASE(pItem);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "Erk, strange menu thing being added\n");
|
|
#endif
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// add menu item to gui
|
|
SendMsg( MM_INSERTITEM, MPFROMP(&mI), MPFROMP(pStr));
|
|
// ..and take ownership of it (separators don't have it)
|
|
if( aThing)
|
|
mElements->InsertElementAt( aThing, 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// Pull items off of a menu
|
|
nsresult nsMenuBase::GetItemID( USHORT usID, PMENUITEM pItem)
|
|
{
|
|
SendMsg( MM_QUERYITEM, MPFROM2SHORT(usID, FALSE), MPFROMP(pItem));
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMenuBase::GetItemAt( const PRUint32 &pos, PMENUITEM pItem)
|
|
{
|
|
nsresult rc = NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
if( VerifyIndex( pos))
|
|
{
|
|
// find the ID
|
|
MRESULT mrc = SendMsg( MM_ITEMIDFROMPOSITION, MPFROMLONG(pos));
|
|
rc = GetItemID( SHORT1FROMMR(mrc), pItem);
|
|
rc = NS_OK;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
nsresult nsMenuBase::GetItemAt( const PRUint32 &aPos, nsISupports *&aThing)
|
|
{
|
|
MENUITEM mI = { 0 };
|
|
nsresult rc = GetItemAt( aPos, &mI);
|
|
|
|
if( NS_SUCCEEDED(rc))
|
|
{
|
|
NS_IF_RELEASE(aThing); // XP-COM correct, but probably bad, sigh.
|
|
|
|
// This is either an nsIMenu or an nsIMenuItem
|
|
aThing = (nsISupports*) mI.hItem;
|
|
NS_ADDREF(aThing);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
// Update an item (grey, tick)
|
|
nsresult nsMenuBase::UpdateItem( PMENUITEM aItem)
|
|
{
|
|
SendMsg( MM_SETITEM, 0, MPFROMP(aItem));
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMenuBase::RemoveItemAt( const PRUint32 index)
|
|
{
|
|
MENUITEM mI = { 0 };
|
|
nsresult rc = GetItemAt( index, &mI);
|
|
|
|
if( NS_SUCCEEDED(rc))
|
|
{
|
|
// remove item from gui
|
|
SendMsg( MM_REMOVEITEM, MPFROM2SHORT( mI.id, FALSE));
|
|
|
|
// remove item from our list if we have it (& hence delete the window)
|
|
nsISupports *pThing = (nsISupports*) mI.hItem;
|
|
PRInt32 lIndex = 0;
|
|
if( pThing && NS_SUCCEEDED( mElements->GetIndexOf( pThing, &lIndex)))
|
|
rc = mElements->DeleteElementAt( lIndex);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
nsresult nsMenuBase::RemoveAll()
|
|
{
|
|
PRUint32 count;
|
|
GetItemCount( count);
|
|
|
|
for( PRUint32 i = 0; i < count; i++)
|
|
RemoveItemAt( 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
BOOL nsMenuBase::VerifyIndex( PRUint32 index)
|
|
{
|
|
PRUint32 count;
|
|
GetItemCount( count);
|
|
return index == NS_MIT_END || index < count;
|
|
}
|
|
|
|
// Dummy nsIMenuListener implementation
|
|
|
|
nsEventStatus nsMenuBase::MenuItemSelected( const nsMenuEvent &aMenuEvent)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
nsEventStatus nsMenuBase::MenuSelected(const nsMenuEvent & aMenuEvent)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
nsEventStatus nsMenuBase::MenuDeselected(const nsMenuEvent & aMenuEvent)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
nsEventStatus nsMenuBase::MenuDestruct( const nsMenuEvent &aMenuEvent)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
nsEventStatus nsMenuBase::MenuConstruct( const nsMenuEvent &aMenuEvent,
|
|
nsIWidget *aParentWindow,
|
|
void *menubarNode,
|
|
void *aWebShell)
|
|
{
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
// nsDynamicMenu, common base class for context & 'normal' menus
|
|
//
|
|
nsDynamicMenu::nsDynamicMenu() : mListener(0), mWebShell(0),
|
|
mDOMNode(0), mDOMElement(0)
|
|
{}
|
|
|
|
nsDynamicMenu::~nsDynamicMenu()
|
|
{
|
|
NS_IF_RELEASE(mListener);
|
|
}
|
|
|
|
nsresult nsDynamicMenu::AddMenuListener( nsIMenuListener *aMenuListener)
|
|
{
|
|
if( !aMenuListener)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_IF_RELEASE(mListener);
|
|
mListener = aMenuListener;
|
|
NS_ADDREF(mListener);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDynamicMenu::RemoveMenuListener( nsIMenuListener *aMenuListener)
|
|
{
|
|
if( aMenuListener == mListener)
|
|
NS_IF_RELEASE(mListener);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDynamicMenu::SetDOMNode( nsIDOMNode *aMenuNode)
|
|
{
|
|
mDOMNode = aMenuNode;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDynamicMenu::SetDOMElement( nsIDOMElement *aMenuElement)
|
|
{
|
|
mDOMElement = aMenuElement;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsDynamicMenu::SetWebShell( nsIWebShell *aWebShell)
|
|
{
|
|
mWebShell = aWebShell;
|
|
return NS_OK;
|
|
}
|
|
|
|
// build the menu from the DOM content model.
|
|
//
|
|
// Note that the tear-down from the previous display is done in the init for
|
|
// the next menu so that the MENUITEMs are valid for WM_COMMAND dispatching
|
|
//
|
|
nsEventStatus nsDynamicMenu::MenuSelected( const nsMenuEvent &aMenuEvent)
|
|
{
|
|
RemoveAll();
|
|
|
|
// Go through children of menu node and populate menu
|
|
nsCOMPtr<nsIDOMNode> pItemNode;
|
|
mDOMNode->GetFirstChild( getter_AddRefs(pItemNode));
|
|
|
|
while( pItemNode)
|
|
{
|
|
nsCOMPtr<nsIDOMElement> pItemElement( do_QueryInterface(pItemNode));
|
|
if( pItemElement)
|
|
{
|
|
nsString nodeType;
|
|
pItemElement->GetNodeName( nodeType);
|
|
if( nodeType.Equals( "menuitem"))
|
|
{
|
|
// find attributes of menu item & create gui peer
|
|
nsString itemName;
|
|
nsIMenuItem *pItem = new nsMenuItem;
|
|
NS_ADDREF(pItem);
|
|
pItemElement->GetAttribute( nsAutoString("value"), itemName);
|
|
pItem->Create( (nsIMenu*)this, itemName, PR_FALSE);
|
|
InsertItemAt( pItem);
|
|
|
|
nsString itemCmd, disabled, checked;
|
|
pItemElement->GetAttribute( nsAutoString("oncommand"), itemCmd);
|
|
pItemElement->GetAttribute( nsAutoString("disabled"), disabled);
|
|
pItemElement->GetAttribute( nsAutoString("checked"), checked);
|
|
pItem->SetCommand( itemCmd);
|
|
pItem->SetWebShell( mWebShell);
|
|
pItem->SetDOMElement( pItemElement);
|
|
pItem->SetEnabled( !disabled.Equals( nsAutoString("true")));
|
|
pItem->SetChecked( checked.Equals( nsAutoString("true")));
|
|
NS_RELEASE(pItem); // ownership of the item has passed to nsMenuBase
|
|
}
|
|
else if( nodeType.Equals( "menuseparator"))
|
|
InsertItemAt( 0);
|
|
else if( nodeType.Equals( "menu"))
|
|
{
|
|
// new submenu
|
|
nsString menuName;
|
|
nsIMenu *pMenu = new nsMenu;
|
|
NS_ADDREF(pMenu);
|
|
pItemElement->GetAttribute( nsAutoString("value"), menuName);
|
|
pMenu->Create( (nsIMenu*)this, menuName);
|
|
pMenu->SetDOMNode( pItemNode);
|
|
pMenu->SetDOMElement( pItemElement);
|
|
pMenu->SetWebShell( mWebShell);
|
|
|
|
// insert into menubar
|
|
InsertItemAt( pMenu);
|
|
NS_RELEASE(pMenu); // owned in nsMenuBase
|
|
}
|
|
}
|
|
nsCOMPtr<nsIDOMNode> pOldNode( pItemNode);
|
|
pOldNode->GetNextSibling( getter_AddRefs(pItemNode));
|
|
}
|
|
|
|
return nsEventStatus_eIgnore;
|
|
}
|