menu item enabled state needs to be actively updated based on DOM activity. b=341528 r=mento

This commit is contained in:
joshmoz%gmail.com 2006-06-15 15:54:58 +00:00
parent 358294fe69
commit bef4565f61
5 changed files with 83 additions and 23 deletions

View File

@ -54,11 +54,13 @@ interface nsIDocument;
// its corresponding content object.
// (NOTE: If we need more things, we can add them later)
//
[scriptable, uuid(d8cf3bd0-1dd1-11b2-a837-fec5f865b071)]
[uuid(f5734700-fb27-11da-974d-0800200c9a66)]
interface nsIChangeObserver : nsISupports
{
void AttributeChanged ( in nsIDocument aDocument,
in long aNameSpaceID, in nsIAtom aAttribute ) ;
void AttributeChanged(in nsIDocument aDocument,
in long aNameSpaceID,
in nsIContent aContent,
in nsIAtom aAttribute);
void ContentRemoved ( in nsIDocument aDocument, in nsIContent aChild,
in long aIndexInContainer ) ;

View File

@ -869,7 +869,7 @@ nsMenuBarX::AttributeChanged(nsIDocument * aDocument, nsIContent * aContent,
nsCOMPtr<nsIChangeObserver> obs;
Lookup(aContent, getter_AddRefs(obs));
if (obs)
obs->AttributeChanged(aDocument, aNameSpaceID, aAttribute);
obs->AttributeChanged(aDocument, aNameSpaceID, aContent, aAttribute);
}
void

View File

@ -113,7 +113,8 @@ protected:
nsCOMPtr<nsIMenuListener> mXULCommandListener;
nsWeakPtr mDocShellWeakRef; // weak ref to docshell
nsCOMPtr<nsIContent> mContent;
nsCOMPtr<nsIContent> mContent;
nsCOMPtr<nsIContent> mCommandContent;
PRUint8 mModifiers;
PRPackedBool mIsSeparator;

View File

@ -66,6 +66,7 @@ nsMenuItemX::nsMenuItemX()
{
mNativeMenuItem = nil;
mMenuParent = nsnull;
mManager = nsnull;
mIsSeparator = PR_FALSE;
mKeyEquivalent.AssignLiteral(" ");
mEnabled = PR_TRUE;
@ -77,7 +78,10 @@ nsMenuItemX::nsMenuItemX()
nsMenuItemX::~nsMenuItemX()
{
[mNativeMenuItem autorelease];
mManager->Unregister(mContent);
if (mContent)
mManager->Unregister(mContent);
if (mCommandContent)
mManager->Unregister(mCommandContent);
}
@ -97,6 +101,27 @@ NS_METHOD nsMenuItemX::Create(nsIMenu* aParent, const nsString & aLabel, PRBool
nsCOMPtr<nsIChangeObserver> obs = do_QueryInterface(NS_STATIC_CAST(nsIChangeObserver*,this));
mManager->Register(mContent, obs); // does not addref this
// if we have a command associated with this menu item, register for changes
// to the command DOM node
nsAutoString ourCommand;
mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::command, ourCommand);
if (!ourCommand.IsEmpty()) {
nsIDocument* currDoc = mContent->GetCurrentDoc();
if (currDoc) {
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(currDoc));
if (domDoc) {
// get the command DOM element
nsCOMPtr<nsIDOMElement> commandElt;
domDoc->GetElementById(ourCommand, getter_AddRefs(commandElt));
if (commandElt) {
mCommandContent = do_QueryInterface(commandElt);
// register to observe the command DOM element
mManager->Register(mCommandContent, obs); // does not addref this
}
}
}
}
mIsSeparator = aIsSeparator;
mLabel = aLabel;
@ -390,23 +415,51 @@ nsMenuItemX::UncheckRadioSiblings(nsIContent* inCheckedContent)
NS_IMETHODIMP
nsMenuItemX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom *aAttribute)
nsMenuItemX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIContent *aContent, nsIAtom *aAttribute)
{
if (aAttribute == nsWidgetAtoms::checked) {
// 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) {
if (mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::checked,
nsWidgetAtoms::_true, eCaseMatters))
UncheckRadioSiblings(mContent);
if (!aContent)
return NS_OK;
if (aContent == mContent) { // our own content node changed
if (aAttribute == nsWidgetAtoms::checked) {
// 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) {
if (mContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::checked,
nsWidgetAtoms::_true, eCaseMatters))
UncheckRadioSiblings(mContent);
}
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
}
else if (aAttribute == nsWidgetAtoms::hidden ||
aAttribute == nsWidgetAtoms::collapsed) {
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
}
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
}
else if (aAttribute == nsWidgetAtoms::disabled || aAttribute == nsWidgetAtoms::hidden ||
aAttribute == nsWidgetAtoms::collapsed) {
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
else if (aContent == mCommandContent) {
// the only thing that really matters when the menu isn't showing is the
// enabled state since it enables/disables keyboard commands
if (aAttribute == nsWidgetAtoms::disabled) {
// first we sync our menu item DOM node with the command DOM node
nsAutoString commandDisabled;
nsAutoString menuDisabled;
aContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, commandDisabled);
mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, menuDisabled);
if (!commandDisabled.Equals(menuDisabled)) {
// The menu's disabled state needs to be updated to match the command.
if (commandDisabled.IsEmpty())
mContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, PR_TRUE);
else
mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, commandDisabled, PR_TRUE);
}
// now we sync our native menu item with the command DOM node
if (aContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::disabled, nsWidgetAtoms::_true, eCaseMatters))
[mNativeMenuItem setEnabled:NO];
else
[mNativeMenuItem setEnabled:YES];
}
}
return NS_OK;
@ -416,15 +469,19 @@ nsMenuItemX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIA
NS_IMETHODIMP
nsMenuItemX::ContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
{
if (aChild == mCommandContent) {
mManager->Unregister(mCommandContent);
mCommandContent = nsnull;
}
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);
return NS_OK;
} // ContentRemoved
NS_IMETHODIMP
nsMenuItemX :: ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
nsMenuItemX::ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
{
nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
listener->SetRebuild(PR_TRUE);

View File

@ -992,7 +992,7 @@ nsMenuX::CountVisibleBefore(PRUint32* outVisibleBefore)
NS_IMETHODIMP
nsMenuX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom *aAttribute)
nsMenuX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIContent *aContent, nsIAtom *aAttribute)
{
// ignore the |open| attribute, which is by far the most common
if (gConstructingMenu || (aAttribute == nsWidgetAtoms::open))