Mozilla/mozilla/widget/src/mac/nsMenuItemX.cpp
beard%netscape.com fadfb65e26 [not part of build] first cut at Carbon/Mac OS X compatible menu code.
git-svn-id: svn://10.0.0.236/trunk@83265 18797224-902f-48f8-a5cc-f745e15eee43
2000-12-05 07:19:25 +00:00

440 lines
12 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsCOMPtr.h"
#include "nsIDocumentViewer.h"
#include "nsIContent.h"
#include "nsIPresContext.h"
#include "nsMenuBarX.h" // for MenuHelpers namespace
#include "nsMenuItemX.h"
#include "nsIMenu.h"
#include "nsIMenuBar.h"
#include "nsIWidget.h"
#include "nsIMenuListener.h"
#include "nsDynamicMDEF.h"
#include "nsStringUtil.h"
#if DEBUG
nsInstanceCounter gMenuItemCounterX("nsMenuItemX");
#endif
NS_IMPL_ISUPPORTS4(nsMenuItemX, nsIMenuItem, nsIMenuListener, nsIChangeObserver, nsISupportsWeakReference)
//
// nsMenuItemX constructor
//
nsMenuItemX::nsMenuItemX()
{
NS_INIT_REFCNT();
mMenuParent = nsnull;
mIsSeparator = PR_FALSE;
mKeyEquivalent.AssignWithConversion(" ");
mEnabled = PR_TRUE;
mIsChecked = PR_FALSE;
mMenuType = eRegular;
#if DEBUG
++gMenuItemCounterX;
#endif
}
//
// nsMenuItemX destructor
//
nsMenuItemX::~nsMenuItemX()
{
//printf("nsMenuItemX::~nsMenuItemX() called \n");
// if we're a radio menu, we've been registered to get AttributeChanged, so
// make sure we unregister when we go away.
//if (mMenuType == eRadio) {
nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
mManager->Unregister(content);
//}
#if DEBUG
--gMenuItemCounterX;
#endif
}
NS_METHOD nsMenuItemX::Create ( nsIMenu* aParent, const nsString & aLabel, PRBool aIsSeparator,
EMenuItemType aItemType, PRBool aEnabled,
nsIChangeManager* aManager, nsIWebShell* aShell, nsIDOMNode* aNode )
{
mDOMNode = aNode; // addref
mMenuParent = aParent; // weak
mWebShellWeakRef = getter_AddRefs(NS_GetWeakReference(aShell));
mEnabled = aEnabled;
mMenuType = aItemType;
// if we're a radio menu, register for AttributeChanged messages
mManager = aManager;
//if ( aItemType == eRadio ) {
nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
nsCOMPtr<nsIChangeObserver> obs = do_QueryInterface(NS_STATIC_CAST(nsIChangeObserver*,this));
mManager->Register(content, obs); // does not addref this
//}
mIsSeparator = aIsSeparator;
mLabel = aLabel;
return NS_OK;
}
NS_METHOD
nsMenuItemX::GetLabel(nsString &aText)
{
aText = mLabel;
return NS_OK;
}
NS_METHOD
nsMenuItemX::GetEnabled(PRBool *aIsEnabled)
{
*aIsEnabled = mEnabled;
return NS_OK;
}
NS_METHOD nsMenuItemX::SetChecked(PRBool aIsEnabled)
{
mIsChecked = aIsEnabled;
// update the content model. This will also handle unchecking our siblings
// if we are a radiomenu
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(mDOMNode);
domElement->SetAttribute(NS_LITERAL_STRING("checked"), mIsChecked ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"));
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetChecked(PRBool *aIsEnabled)
{
*aIsEnabled = mIsChecked;
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetMenuItemType(EMenuItemType *aType)
{
*aType = mMenuType;
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetTarget(nsIWidget *& aTarget)
{
NS_IF_ADDREF(aTarget = mTarget);
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetNativeData(void *& aData)
{
//aData = (void *)mMenu;
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::AddMenuListener(nsIMenuListener * aMenuListener)
{
mXULCommandListener = aMenuListener; // addref
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::RemoveMenuListener(nsIMenuListener * aMenuListener)
{
if (mXULCommandListener.get() == aMenuListener)
mXULCommandListener = nsnull;
return NS_OK;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::IsSeparator(PRBool & aIsSep)
{
aIsSep = mIsSeparator;
return NS_OK;
}
//-------------------------------------------------------------------------
// nsIMenuListener interface
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::MenuItemSelected(const nsMenuEvent & aMenuEvent)
{
switch ( mMenuType ) {
case eCheckbox:
SetChecked(!mIsChecked);
break;
case eRadio:
{
// we only want to muck with things if we were selected and we're not
// already checked.
if ( mIsChecked )
break;
SetChecked(PR_TRUE);
break;
}
case eRegular:
break; // do nothing special
} // which menu type
DoCommand();
return nsEventStatus_eConsumeNoDefault;
}
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::MenuSelected(const nsMenuEvent & aMenuEvent)
{
//if(mXULCommandListener)
// return mXULCommandListener->MenuSelected(aMenuEvent);
DoCommand();
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
// nsIMenuListener interface
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::MenuDeselected(const nsMenuEvent & aMenuEvent)
{
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::MenuConstruct(
const nsMenuEvent & aMenuEvent,
nsIWidget * aParentWindow,
void * menuNode,
void * aWebShell)
{
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::MenuDestruct(const nsMenuEvent & aMenuEvent)
{
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::CheckRebuild(PRBool & aNeedsRebuild)
{
aNeedsRebuild = PR_TRUE;
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
nsEventStatus nsMenuItemX::SetRebuild(PRBool aNeedsRebuild)
{
//mNeedsRebuild = aNeedsRebuild;
return nsEventStatus_eIgnore;
}
//-------------------------------------------------------------------------
/**
* Executes the "cached" JavaScript Command
* @return NS_OK if the command was executed properly, otherwise an error code
*/
NS_METHOD nsMenuItemX::DoCommand()
{
nsresult rv = NS_ERROR_FAILURE;
nsCOMPtr<nsIPresContext> presContext;
nsCOMPtr<nsIWebShell> webShell = do_QueryReferent(mWebShellWeakRef);
if (!webShell)
{
NS_ERROR("No web shell");
return nsEventStatus_eConsumeNoDefault;
}
MenuHelpersX::WebShellToPresContext(webShell, getter_AddRefs(presContext));
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event;
event.eventStructType = NS_MOUSE_EVENT;
event.message = NS_MENU_ACTION;
nsCOMPtr<nsIContent> contentNode = do_QueryInterface(mDOMNode);
if (!contentNode) {
NS_ERROR("DOM Node doesn't support the nsIContent interface required to handle DOM events.");
return rv;
}
rv = contentNode->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
return nsEventStatus_eConsumeNoDefault;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetModifiers(PRUint8 * aModifiers)
{
nsresult res = NS_OK;
*aModifiers = mModifiers;
return res;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::SetModifiers(PRUint8 aModifiers)
{
nsresult res = NS_OK;
mModifiers = aModifiers;
return res;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::SetShortcutChar(const nsString &aText)
{
nsresult res = NS_OK;
mKeyEquivalent = aText;
return res;
}
//-------------------------------------------------------------------------
NS_METHOD nsMenuItemX::GetShortcutChar(nsString &aText)
{
nsresult res = NS_OK;
aText = mKeyEquivalent;
return res;
}
//
// UncheckRadioSiblings
//
// walk the sibling list looking for nodes with the same name and
// uncheck them all.
//
void
nsMenuItemX :: UncheckRadioSiblings(nsIDOMElement* inCheckedElement)
{
nsCOMPtr<nsIDOMNode> checkedNode = do_QueryInterface(inCheckedElement);
nsAutoString myGroupName;
inCheckedElement->GetAttribute(NS_LITERAL_STRING("name"), myGroupName);
if ( ! myGroupName.Length() ) // no groupname, nothing to do
return;
nsCOMPtr<nsIDOMNode> parent;
checkedNode->GetParentNode(getter_AddRefs(parent));
if (!parent )
return;
nsCOMPtr<nsIDOMNode> currSibling;
parent->GetFirstChild(getter_AddRefs(currSibling));
while ( currSibling ) {
// skip this node
if ( currSibling.get() != checkedNode ) {
nsCOMPtr<nsIDOMElement> currElement = do_QueryInterface(currSibling);
if ( !currElement )
break;
// if the current sibling is in the same group, clear it
nsAutoString currGroupName;
currElement->GetAttribute(NS_LITERAL_STRING("name"), currGroupName);
if ( currGroupName == myGroupName )
currElement->SetAttribute(NS_LITERAL_STRING("checked"), NS_LITERAL_STRING("false"));
}
// advance to the next node
nsIDOMNode* next;
currSibling->GetNextSibling(&next);
currSibling = dont_AddRef(next);
} // for each sibling
} // UncheckRadioSiblings
#pragma mark -
//
// nsIChangeObserver
//
NS_IMETHODIMP
nsMenuItemX :: AttributeChanged ( nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom *aAttribute,
PRInt32 aHint)
{
nsCOMPtr<nsIAtom> checkedAtom = NS_NewAtom("checked");
nsCOMPtr<nsIAtom> disabledAtom = NS_NewAtom("disabled");
nsCOMPtr<nsIAtom> valueAtom = NS_NewAtom("value");
nsCOMPtr<nsIAtom> hiddenAtom = NS_NewAtom("hidden");
nsCOMPtr<nsIAtom> collapsedAtom = NS_NewAtom("collapsed");
if (aAttribute == checkedAtom.get())
{
// if we're a radio menu, uncheck our sibling radio items. No need to
// do any of this if we're just a normal check menu.
if ( mMenuType == eRadio ) {
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(mDOMNode);
nsAutoString checked;
domElement->GetAttribute(NS_LITERAL_STRING("checked"), checked);
if (checked == NS_LITERAL_STRING("true") )
UncheckRadioSiblings(domElement);
}
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
} else if (aAttribute == disabledAtom.get() ||
aAttribute == hiddenAtom.get() ||
aAttribute == collapsedAtom.get() ) {
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
}
return NS_OK;
} // AttributeChanged
NS_IMETHODIMP
nsMenuItemX :: ContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
{
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
return NS_OK;
} // ContentRemoved
NS_IMETHODIMP
nsMenuItemX :: ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
{
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
return NS_OK;
} // ContentInserted