Back to index

lightning-sunbird  0.9+nobinonly
nsMenuItemX.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsIDocumentViewer.h"
00040 #include "nsIContent.h"
00041 #include "nsPresContext.h"
00042 
00043 #include "nsMenuBarX.h"         // for MenuHelpers namespace
00044 #include "nsMenuX.h"
00045 #include "nsMenuItemX.h"
00046 #include "nsMenuItemIcon.h"
00047 
00048 #include "nsWidgetAtoms.h"
00049 
00050 #include "nsIMenu.h"
00051 #include "nsIMenuBar.h"
00052 #include "nsIWidget.h"
00053 #include "nsIMenuListener.h"
00054 #include "nsINameSpaceManager.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsIDocument.h"
00057 #include "nsIDOMDocument.h"
00058 #include "nsIPrivateDOMEvent.h"
00059 #include "nsIDOMEventReceiver.h"
00060 #include "nsIDOMDocumentEvent.h"
00061 
00062 #include "nsGUIEvent.h"
00063 
00064 
00065 #if DEBUG
00066 nsInstanceCounter   gMenuItemCounterX("nsMenuItemX");
00067 #endif
00068 
00069 
00070 NS_IMPL_ISUPPORTS5(nsMenuItemX, nsIMenuItem, nsIMenuItem_MOZILLA_1_8_BRANCH, nsIMenuListener, nsIChangeObserver, nsISupportsWeakReference)
00071 
00072 //
00073 // nsMenuItemX constructor
00074 //
00075 nsMenuItemX::nsMenuItemX()
00076 {
00077   mMenuParent         = nsnull;
00078   mManager            = nsnull;
00079   mIsSeparator        = PR_FALSE;
00080   mKeyEquivalent.AssignLiteral(" ");
00081   mEnabled            = PR_TRUE;
00082   mIsChecked          = PR_FALSE;
00083   mMenuType           = eRegular;
00084 
00085 #if DEBUG
00086   ++gMenuItemCounterX;
00087 #endif 
00088 }
00089 
00090 //
00091 // nsMenuItemX destructor
00092 //
00093 nsMenuItemX::~nsMenuItemX()
00094 {
00095   if (mManager) {
00096     if (mContent)
00097       mManager->Unregister(mContent);
00098     if (mCommandContent)
00099       mManager->Unregister(mCommandContent);
00100   }
00101 
00102 #if DEBUG
00103   --gMenuItemCounterX;
00104 #endif 
00105 }
00106 
00107 
00108 NS_METHOD nsMenuItemX::Create ( nsIMenu* aParent, const nsString & aLabel, PRBool aIsSeparator,
00109                                 EMenuItemType aItemType, PRBool aEnabled, 
00110                                 nsIChangeManager* aManager, nsIDocShell* aShell, nsIContent* aNode )
00111 {
00112   mContent = aNode;         // addref
00113   mMenuParent = aParent;    // weak
00114   mDocShellWeakRef = do_GetWeakReference(aShell);
00115   
00116   mEnabled = aEnabled;
00117   mMenuType = aItemType;
00118   
00119   // register for AttributeChanged messages
00120   mManager = aManager;
00121   nsCOMPtr<nsIChangeObserver> obs = do_QueryInterface(NS_STATIC_CAST(nsIChangeObserver*,this));
00122   mManager->Register(mContent, obs);   // does not addref this
00123   
00124   mIsSeparator = aIsSeparator;
00125   mLabel = aLabel;
00126   
00127   // We need to pick up a command content node, it is highly unlikely that one
00128   // won't exist. If we find one, register for changes on it.
00129   nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(aNode->GetDocument());
00130   if (domDocument) {
00131     nsAutoString ourCommand;
00132     aNode->GetAttr(kNameSpaceID_None, nsWidgetAtoms::command, ourCommand);
00133     if (!ourCommand.IsEmpty()) {
00134       nsCOMPtr<nsIDOMElement> commandElt;
00135       domDocument->GetElementById(ourCommand, getter_AddRefs(commandElt));
00136       if (commandElt) {
00137         mCommandContent = do_QueryInterface(commandElt);
00138         mManager->Register(mCommandContent, obs);
00139       }
00140     }
00141   }
00142 
00143   nsCOMPtr<nsIMenu_MOZILLA_1_8_BRANCH> menuParent_MOZILLA_1_8_BRANCH =
00144    do_QueryInterface(mMenuParent);
00145   mIcon = new nsMenuItemIcon(NS_STATIC_CAST(nsIMenuItem*, this),
00146                              menuParent_MOZILLA_1_8_BRANCH, mContent);
00147 
00148   return NS_OK;
00149 }
00150 
00151 NS_METHOD
00152 nsMenuItemX::GetLabel(nsString &aText)
00153 {
00154   aText = mLabel;
00155   return NS_OK;
00156 }
00157 
00158 
00159 NS_METHOD 
00160 nsMenuItemX::GetEnabled(PRBool *aIsEnabled)
00161 {
00162   *aIsEnabled = mEnabled;
00163   return NS_OK;
00164 }
00165 
00166 
00167 NS_METHOD nsMenuItemX::SetChecked(PRBool aIsEnabled)
00168 {
00169   mIsChecked = aIsEnabled;
00170   
00171   // update the content model. This will also handle unchecking our siblings
00172   // if we are a radiomenu
00173   if (mIsChecked)
00174       mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::checked,
00175                         NS_LITERAL_STRING("true"), PR_TRUE);
00176   else
00177       mContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::checked, PR_TRUE);
00178 
00179   return NS_OK;
00180 }
00181 
00182 //-------------------------------------------------------------------------
00183 NS_METHOD nsMenuItemX::GetChecked(PRBool *aIsEnabled)
00184 {
00185   *aIsEnabled = mIsChecked;
00186   return NS_OK;
00187 }
00188 
00189 
00190 //-------------------------------------------------------------------------
00191 NS_METHOD nsMenuItemX::GetMenuItemType(EMenuItemType *aType)
00192 {
00193   *aType = mMenuType;
00194   return NS_OK;
00195 }
00196 
00197 
00198 //-------------------------------------------------------------------------
00199 NS_METHOD nsMenuItemX::GetTarget(nsIWidget *& aTarget)
00200 {
00201   NS_IF_ADDREF(aTarget = mTarget);
00202   return NS_OK;
00203 }
00204 
00205 //-------------------------------------------------------------------------
00206 NS_METHOD nsMenuItemX::GetNativeData(void *& aData)
00207 {
00208   //aData = (void *)mMenu;
00209   return NS_OK;
00210 }
00211 
00212 //-------------------------------------------------------------------------
00213 NS_METHOD nsMenuItemX::AddMenuListener(nsIMenuListener * aMenuListener)
00214 {
00215   mXULCommandListener = aMenuListener;    // addref
00216   return NS_OK;
00217 }
00218 
00219 //-------------------------------------------------------------------------
00220 NS_METHOD nsMenuItemX::RemoveMenuListener(nsIMenuListener * aMenuListener)
00221 {
00222   if (mXULCommandListener.get() == aMenuListener)
00223     mXULCommandListener = nsnull;
00224   return NS_OK;
00225 }
00226 
00227 //-------------------------------------------------------------------------
00228 NS_METHOD nsMenuItemX::IsSeparator(PRBool & aIsSep)
00229 {
00230   aIsSep = mIsSeparator;
00231   return NS_OK;
00232 }
00233 
00234 
00235 //-------------------------------------------------------------------------
00236 // nsIMenuListener interface
00237 //-------------------------------------------------------------------------
00238 nsEventStatus nsMenuItemX::MenuItemSelected(const nsMenuEvent & aMenuEvent)
00239 {
00240   // this is all handled by Carbon Events
00241   return nsEventStatus_eConsumeNoDefault;
00242 }
00243 
00244 //-------------------------------------------------------------------------
00245 nsEventStatus nsMenuItemX::MenuSelected(const nsMenuEvent & aMenuEvent)
00246 {
00247   return nsEventStatus_eIgnore;
00248 }
00249 
00250 //-------------------------------------------------------------------------
00251 // nsIMenuListener interface
00252 //-------------------------------------------------------------------------
00253 nsEventStatus nsMenuItemX::MenuDeselected(const nsMenuEvent & aMenuEvent)
00254 {
00255     return nsEventStatus_eIgnore;
00256 }
00257 
00258 //-------------------------------------------------------------------------
00259 nsEventStatus nsMenuItemX::MenuConstruct(
00260     const nsMenuEvent & aMenuEvent,
00261     nsIWidget         * aParentWindow, 
00262     void              * menuNode,
00263     void              * aDocShell)
00264 {
00265     return nsEventStatus_eIgnore;
00266 }
00267 
00268 //-------------------------------------------------------------------------
00269 nsEventStatus nsMenuItemX::MenuDestruct(const nsMenuEvent & aMenuEvent)
00270 {
00271     return nsEventStatus_eIgnore;
00272 }
00273 
00274 //-------------------------------------------------------------------------
00275 nsEventStatus nsMenuItemX::CheckRebuild(PRBool & aNeedsRebuild)
00276 {
00277   aNeedsRebuild = PR_TRUE; 
00278   return nsEventStatus_eIgnore;
00279 }
00280 
00281 //-------------------------------------------------------------------------
00282 nsEventStatus nsMenuItemX::SetRebuild(PRBool aNeedsRebuild)
00283 {
00284   //mNeedsRebuild = aNeedsRebuild; 
00285   return nsEventStatus_eIgnore;
00286 }
00287 
00288 //-------------------------------------------------------------------------
00293 NS_METHOD nsMenuItemX::DoCommand()
00294 {
00295     // flip "checked" state if we're a checkbox menu, or an un-checked radio menu
00296     if (mMenuType == nsIMenuItem::eCheckbox || (mMenuType == nsIMenuItem::eRadio && !mIsChecked)) {
00297         nsAutoString value;
00298         mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::autocheck, value);
00299         if (!value.EqualsLiteral("false"))
00300             SetChecked(!mIsChecked);
00301             /* the AttributeChanged code will update all the internal state */
00302     }
00303 
00304     return MenuHelpersX::DispatchCommandTo(mDocShellWeakRef, mContent);
00305 }
00306     
00307 NS_IMETHODIMP nsMenuItemX::DispatchDOMEvent(const nsString &eventName, PRBool *preventDefaultCalled)
00308 {
00309   if (!mContent)
00310     return NS_ERROR_FAILURE;
00311 
00312   // get owner document for content
00313   nsCOMPtr<nsIDocument> parentDoc = mContent->GetOwnerDoc();
00314   if (!parentDoc) {
00315     NS_WARNING("Failed to get owner nsIDocument for menu item content");
00316     return NS_ERROR_FAILURE;
00317   }
00318   
00319   // get interface for creating DOM events from content owner document
00320   nsCOMPtr<nsIDOMDocumentEvent> DOMEventFactory = do_QueryInterface(parentDoc);
00321   if (!DOMEventFactory) {
00322     NS_WARNING("Failed to QI parent nsIDocument to nsIDOMDocumentEvent");
00323     return NS_ERROR_FAILURE;
00324   }
00325   
00326   // create DOM event
00327   nsCOMPtr<nsIDOMEvent> event;
00328   DOMEventFactory->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event));
00329   event->InitEvent(eventName, PR_TRUE, PR_TRUE);
00330   
00331   // mark DOM event as trusted
00332   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
00333   privateEvent->SetTrusted(PR_TRUE);
00334   
00335   // send DOM event
00336   nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(mContent);
00337   nsresult rv = eventTarget->DispatchEvent(event, preventDefaultCalled);
00338   if (NS_FAILED(rv)) {
00339     NS_WARNING("Failed to send DOM event via nsIDOMEventTarget");
00340     return NS_ERROR_FAILURE;
00341   }
00342   
00343   return NS_OK;
00344 }
00345    
00346 //-------------------------------------------------------------------------
00347 NS_METHOD nsMenuItemX::GetModifiers(PRUint8 * aModifiers) 
00348 {
00349     nsresult res = NS_OK;
00350     *aModifiers = mModifiers; 
00351     return res; 
00352 }
00353 
00354 //-------------------------------------------------------------------------
00355 NS_METHOD nsMenuItemX::SetModifiers(PRUint8 aModifiers)
00356 {
00357     nsresult res = NS_OK;
00358     
00359     mModifiers = aModifiers;
00360     return res;
00361 }
00362  
00363 //-------------------------------------------------------------------------
00364 NS_METHOD nsMenuItemX::SetShortcutChar(const nsString &aText)
00365 {
00366     nsresult res = NS_OK;
00367     mKeyEquivalent = aText;
00368     return res;
00369 } 
00370 
00371 //-------------------------------------------------------------------------
00372 NS_METHOD nsMenuItemX::GetShortcutChar(nsString &aText)
00373 {
00374     nsresult res = NS_OK;
00375     aText = mKeyEquivalent;
00376     return res;
00377 } 
00378 
00379 //
00380 // UncheckRadioSiblings
00381 //
00382 // walk the sibling list looking for nodes with the same name and
00383 // uncheck them all.
00384 //
00385 void
00386 nsMenuItemX :: UncheckRadioSiblings(nsIContent* inCheckedContent)
00387 {
00388   nsAutoString myGroupName;
00389   inCheckedContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::name, myGroupName);
00390   if ( ! myGroupName.Length() )        // no groupname, nothing to do
00391     return;
00392   
00393   nsCOMPtr<nsIContent> parent = inCheckedContent->GetParent();
00394   if ( !parent )
00395     return;
00396 
00397   // loop over siblings
00398   PRUint32 count = parent->GetChildCount();
00399   for ( PRUint32 i = 0; i < count; ++i ) {
00400     nsIContent *sibling = parent->GetChildAt(i);
00401     if ( sibling ) {      
00402       if ( sibling != inCheckedContent ) {                    // skip this node
00403         // if the current sibling is in the same group, clear it
00404         nsAutoString currGroupName;
00405         sibling->GetAttr(kNameSpaceID_None, nsWidgetAtoms::name, currGroupName);
00406         if ( currGroupName == myGroupName )
00407           sibling->SetAttr(kNameSpaceID_None, nsWidgetAtoms::checked, NS_LITERAL_STRING("false"), PR_TRUE);
00408       }
00409     }    
00410   } // for each sibling
00411 
00412 } // UncheckRadioSiblings
00413 
00414 #pragma mark -
00415 
00416 //
00417 // nsIChangeObserver
00418 //
00419 
00420 
00421 NS_IMETHODIMP
00422 nsMenuItemX::AttributeChanged(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIContent *aContent, nsIAtom *aAttribute)
00423 {
00424   if (aContent == mContent) {
00425     if (aAttribute == nsWidgetAtoms::checked) {
00426       // if we're a radio menu, uncheck our sibling radio items. No need to
00427       // do any of this if we're just a normal check menu.
00428       if (mMenuType == eRadio) {
00429         nsAutoString checked;
00430         mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::checked, checked);
00431         if (checked.EqualsLiteral("true")) 
00432           UncheckRadioSiblings(mContent);
00433       }
00434       nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
00435       listener->SetRebuild(PR_TRUE);
00436     } 
00437     else if (aAttribute == nsWidgetAtoms::disabled || aAttribute == nsWidgetAtoms::hidden ||
00438              aAttribute == nsWidgetAtoms::collapsed || aAttribute == nsWidgetAtoms::label )  {
00439       nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
00440       listener->SetRebuild(PR_TRUE);
00441     }
00442     else if (aAttribute == nsWidgetAtoms::image) {
00443       SetupIcon();
00444     }
00445   }
00446   else if (aContent == mCommandContent &&
00447            aAttribute == nsWidgetAtoms::disabled &&
00448            mMenuParent && mCommandContent) {
00449     nsAutoString menuItemDisabled;
00450     nsAutoString commandDisabled;
00451     mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, menuItemDisabled);
00452     mCommandContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, commandDisabled);
00453     if (!commandDisabled.Equals(menuItemDisabled)) {
00454       // The menu's disabled state needs to be updated to match the command.
00455       if (commandDisabled.IsEmpty())
00456         mContent->UnsetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, PR_TRUE);
00457       else
00458         mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::disabled, commandDisabled, PR_TRUE);
00459     }
00460     
00461     // we need to get our native menu item to update itself
00462     nsCOMPtr<nsIMenu_MOZILLA_1_8_BRANCH> parentMenu = do_QueryInterface(mMenuParent);
00463     if (parentMenu)
00464       parentMenu->ChangeNativeEnabledStatusForMenuItem(this, !commandDisabled.EqualsLiteral("true"));
00465   }
00466   
00467   return NS_OK;
00468 } // AttributeChanged
00469 
00470 
00471 NS_IMETHODIMP
00472 nsMenuItemX :: ContentRemoved(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
00473 {
00474   if (aChild == mCommandContent) {
00475     mManager->Unregister(mCommandContent);
00476     mCommandContent = nsnull;
00477   }
00478   
00479   nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
00480   listener->SetRebuild(PR_TRUE);
00481   return NS_OK;
00482 } // ContentRemoved
00483 
00484 NS_IMETHODIMP
00485 nsMenuItemX :: ContentInserted(nsIDocument *aDocument, nsIContent *aChild, PRInt32 aIndexInContainer)
00486 {
00487   
00488   nsCOMPtr<nsIMenuListener> listener = do_QueryInterface(mMenuParent);
00489   listener->SetRebuild(PR_TRUE);
00490   return NS_OK;
00491   
00492 } // ContentInserted
00493 
00494 
00495 NS_IMETHODIMP
00496 nsMenuItemX::SetupIcon()
00497 {
00498   if (!mIcon) return NS_ERROR_OUT_OF_MEMORY;
00499 
00500   return mIcon->SetupIcon();
00501 }