Back to index

lightning-sunbird  0.9+nobinonly
nsXULMenuAccessible.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  *   Author: Aaron Leventhal (aaronl@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsXULMenuAccessible.h"
00040 #include "nsIDOMElement.h"
00041 #include "nsIDOMXULElement.h"
00042 #include "nsIDOMXULSelectCntrlItemEl.h"
00043 #include "nsIDOMKeyEvent.h"
00044 #include "nsIPrefService.h"
00045 #include "nsIPrefBranch.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsIPresShell.h"
00048 #include "nsIContent.h"
00049 #include "nsGUIEvent.h"
00050 
00051 
00052 // ------------------------ Menu Item -----------------------------
00053 
00054 nsXULMenuitemAccessible::nsXULMenuitemAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): 
00055 nsAccessibleWrap(aDOMNode, aShell)
00056 { 
00057 }
00058 
00059 NS_IMETHODIMP nsXULMenuitemAccessible::GetState(PRUint32 *_retval)
00060 {
00061   nsAccessible::GetState(_retval);
00062 
00063   // Focused?
00064   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
00065   if (!element)
00066     return NS_ERROR_FAILURE;
00067   PRBool isFocused = PR_FALSE;
00068   element->HasAttribute(NS_LITERAL_STRING("_moz-menuactive"), &isFocused); 
00069   if (isFocused)
00070     *_retval |= STATE_FOCUSED;
00071 
00072   // Has Popup?
00073   nsAutoString tagName;
00074   element->GetLocalName(tagName);
00075   if (tagName.EqualsLiteral("menu"))
00076     *_retval |= STATE_HASPOPUP;
00077 
00078   nsAutoString menuItemType;
00079   element->GetAttribute(NS_LITERAL_STRING("type"), menuItemType); 
00080 
00081   if (!menuItemType.IsEmpty()) {
00082     // Checkable?
00083     if (menuItemType.EqualsIgnoreCase("radio") ||
00084         menuItemType.EqualsIgnoreCase("checkbox"))
00085       *_retval |= STATE_CHECKABLE;
00086 
00087     // Checked?
00088     nsAutoString checkValue;
00089     element->GetAttribute(NS_LITERAL_STRING("checked"), checkValue);
00090     if (checkValue.EqualsLiteral("true")) {
00091       *_retval |= STATE_CHECKED;
00092     }
00093   }
00094 
00095   // Offscreen?
00096   // If parent or grandparent menuitem is offscreen, then we're offscreen too
00097   // We get it by replacing the current offscreen bit with the parent's
00098   PRUint32 parentState = 0;
00099   nsCOMPtr<nsIAccessible> parentAccessible;
00100   if (parentAccessible) {
00101     GetParent(getter_AddRefs(parentAccessible));
00102     if (parentAccessible) {
00103       parentAccessible->GetFinalState(&parentState);
00104       *_retval &= ~STATE_OFFSCREEN;  // clear the old OFFSCREEN bit
00105       *_retval |= (parentState & STATE_OFFSCREEN);  // or it with the parent's offscreen bit
00106     }
00107   }
00108 
00109   return NS_OK;
00110 }
00111 
00112 NS_IMETHODIMP nsXULMenuitemAccessible::GetName(nsAString& _retval)
00113 {
00114   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
00115   if (!element) {
00116     return NS_ERROR_FAILURE;
00117   }
00118   element->GetAttribute(NS_LITERAL_STRING("label"), _retval); 
00119 
00120   return NS_OK;
00121 }
00122 
00123 //return menu accesskey: N or Alt+F
00124 NS_IMETHODIMP nsXULMenuitemAccessible::GetKeyboardShortcut(nsAString& _retval)
00125 {
00126   static PRInt32 gMenuAccesskeyModifier = -1;  // magic value of -1 indicates unitialized state
00127 
00128   nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(mDOMNode));
00129   if (elt) {
00130     nsAutoString accesskey;
00131     elt->GetAttribute(NS_LITERAL_STRING("accesskey"), accesskey);
00132     if (accesskey.IsEmpty())
00133       return NS_OK;
00134 
00135     nsCOMPtr<nsIAccessible> parentAccessible;
00136     GetParent(getter_AddRefs(parentAccessible));
00137     if (parentAccessible) {
00138       PRUint32 role;
00139       parentAccessible->GetRole(&role);
00140       if (role == ROLE_MENUBAR) {
00141         // If top level menu item, add Alt+ or whatever modifier text to string
00142         // No need to cache pref service, this happens rarely
00143         if (gMenuAccesskeyModifier == -1) {
00144           // Need to initialize cached global accesskey pref
00145           gMenuAccesskeyModifier = 0;
00146           nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00147           if (prefBranch)
00148             prefBranch->GetIntPref("ui.key.menuAccessKey", &gMenuAccesskeyModifier);
00149         }
00150         nsAutoString propertyKey;
00151         switch (gMenuAccesskeyModifier) {
00152           case nsIDOMKeyEvent::DOM_VK_CONTROL: propertyKey.AssignLiteral("VK_CONTROL"); break;
00153           case nsIDOMKeyEvent::DOM_VK_ALT: propertyKey.AssignLiteral("VK_ALT"); break;
00154           case nsIDOMKeyEvent::DOM_VK_META: propertyKey.AssignLiteral("VK_META"); break;
00155         }
00156         if (!propertyKey.IsEmpty())
00157           nsAccessible::GetFullKeyName(propertyKey, accesskey, _retval);
00158       }
00159     }
00160     if (_retval.IsEmpty())
00161       _retval = accesskey;
00162     return NS_OK;
00163   }
00164   return NS_ERROR_FAILURE;
00165 }
00166 
00167 //return menu shortcut: Ctrl+F or Ctrl+Shift+L
00168 NS_IMETHODIMP nsXULMenuitemAccessible::GetKeyBinding(nsAString& _retval)
00169 {
00170   nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(mDOMNode));
00171   if (elt) {
00172     nsAutoString accelText;
00173     elt->GetAttribute(NS_LITERAL_STRING("acceltext"), accelText);
00174     if (accelText.IsEmpty())
00175       return NS_OK;
00176 
00177     _retval = accelText;
00178     return NS_OK;
00179   }
00180   return NS_ERROR_FAILURE;
00181 }
00182 
00183 NS_IMETHODIMP nsXULMenuitemAccessible::GetRole(PRUint32 *_retval)
00184 {
00185   *_retval = ROLE_MENUITEM;
00186   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
00187   if (!element)
00188     return NS_ERROR_FAILURE;
00189   nsAutoString menuItemType;
00190   element->GetAttribute(NS_LITERAL_STRING("type"), menuItemType);
00191   if (menuItemType.EqualsIgnoreCase("radio"))
00192     *_retval = ROLE_RADIO_MENU_ITEM;
00193   else if (menuItemType.EqualsIgnoreCase("checkbox"))
00194     *_retval = ROLE_CHECK_MENU_ITEM;
00195   return NS_OK;
00196 }
00197 
00198 NS_IMETHODIMP nsXULMenuitemAccessible::GetChildCount(PRInt32 *aAccChildCount)
00199 {
00200   // Set menugenerated="true" on the menupopup node to generate the
00201   // sub-menu items if they have not been generated
00202   PRUint32 childIndex, numChildren = 0;
00203   nsCOMPtr<nsIDOMNode> childNode;
00204   nsCOMPtr<nsIDOMNodeList> nodeList;
00205   mDOMNode->GetChildNodes(getter_AddRefs(nodeList));
00206   if (nodeList && NS_OK == nodeList->GetLength(&numChildren)) {
00207     for (childIndex = 0; childIndex < numChildren; childIndex++) {
00208       nodeList->Item(childIndex, getter_AddRefs(childNode));
00209       nsAutoString nodeName;
00210       childNode->GetNodeName(nodeName);
00211       if (nodeName.EqualsLiteral("menupopup")) {
00212         break;
00213       }
00214     }
00215 
00216     if (childIndex < numChildren) {
00217       nsCOMPtr<nsIDOMElement> element(do_QueryInterface(childNode));
00218       if (element) {
00219         nsAutoString attr;
00220         element->GetAttribute(NS_LITERAL_STRING("menugenerated"), attr);
00221         if (!attr.EqualsLiteral("true")) {
00222           element->SetAttribute(NS_LITERAL_STRING("menugenerated"), NS_LITERAL_STRING("true"));
00223         }
00224       }
00225       // fire a popup dom event
00226       nsEventStatus status = nsEventStatus_eIgnore;
00227       nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull,
00228                          nsMouseEvent::eReal);
00229 
00230       nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
00231       nsCOMPtr<nsIContent> content(do_QueryInterface(childNode));
00232       if (presShell && content)
00233         presShell->HandleDOMEventWithTarget(content, &event, &status);
00234     }
00235   }
00236 
00237   // Argument of PR_FALSE indicates we don't walk anonymous children for menuitems
00238   CacheChildren(PR_FALSE);
00239   *aAccChildCount = mAccChildCount;
00240   return NS_OK;  
00241 }
00242 
00243 NS_IMETHODIMP nsXULMenuitemAccessible::DoAction(PRUint8 index)
00244 {
00245   if (index == eAction_Select) {   // default action
00246     DoCommand();
00247     nsCOMPtr<nsIAccessible> parentAccessible;
00248     GetParent(getter_AddRefs(parentAccessible));
00249     if (parentAccessible) {
00250       PRUint32 role;
00251       parentAccessible->GetRole(&role);
00252       if (role == ROLE_LIST) {
00253         nsCOMPtr<nsIAccessible> buttonAccessible;
00254         parentAccessible->GetPreviousSibling(getter_AddRefs(buttonAccessible));
00255         PRUint32 state;
00256         buttonAccessible->GetFinalState(&state);
00257         if (state & STATE_PRESSED)
00258           buttonAccessible->DoAction(eAction_Click);
00259       }
00260     }
00261     return NS_OK;
00262   }
00263 
00264   return NS_ERROR_INVALID_ARG;
00265 }
00266 
00268 NS_IMETHODIMP nsXULMenuitemAccessible::GetActionName(PRUint8 index, nsAString& _retval)
00269 {
00270   if (index == eAction_Select) {
00271     nsAccessible::GetTranslatedString(NS_LITERAL_STRING("select"), _retval); 
00272     return NS_OK;
00273   }
00274   return NS_ERROR_INVALID_ARG;
00275 }
00276 
00277 NS_IMETHODIMP nsXULMenuitemAccessible::GetNumActions(PRUint8 *_retval)
00278 {
00279   *_retval = eSingle_Action;
00280   return NS_OK;
00281 }
00282 
00283 
00284 // ------------------------ Menu Separator ----------------------------
00285 
00286 nsXULMenuSeparatorAccessible::nsXULMenuSeparatorAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): 
00287 nsXULMenuitemAccessible(aDOMNode, aShell)
00288 { 
00289 }
00290 
00291 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetState(PRUint32 *_retval)
00292 {
00293   // Isn't focusable, but can be offscreen
00294   nsXULMenuitemAccessible::GetState(_retval);
00295   *_retval &= STATE_OFFSCREEN;
00296 
00297   return NS_OK;
00298 }
00299 
00300 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetName(nsAString& _retval)
00301 {
00302   _retval.Truncate();
00303   return NS_OK;
00304 }
00305 
00306 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetRole(PRUint32 *_retval)
00307 {
00308   *_retval = ROLE_SEPARATOR;
00309   return NS_OK;
00310 }
00311 
00312 NS_IMETHODIMP nsXULMenuSeparatorAccessible::DoAction(PRUint8 index)
00313 {
00314   return NS_ERROR_NOT_IMPLEMENTED;
00315 }
00316 
00317 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetActionName(PRUint8 index, nsAString& _retval)
00318 {
00319   return NS_ERROR_NOT_IMPLEMENTED;
00320 }
00321 
00322 NS_IMETHODIMP nsXULMenuSeparatorAccessible::GetNumActions(PRUint8 *_retval)
00323 {
00324   return NS_ERROR_NOT_IMPLEMENTED;
00325 }
00326 // ------------------------ Menu Popup -----------------------------
00327 
00328 nsXULMenupopupAccessible::nsXULMenupopupAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): 
00329   nsAccessibleWrap(aDOMNode, aShell)
00330 { 
00331 }
00332 
00333 NS_IMETHODIMP nsXULMenupopupAccessible::GetState(PRUint32 *_retval)
00334 {
00335   // We are onscreen if our parent is active
00336   *_retval = 0;
00337   PRBool isActive = PR_FALSE;
00338 
00339   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
00340   element->HasAttribute(NS_LITERAL_STRING("menuactive"), &isActive);
00341   if (!isActive) {
00342     nsCOMPtr<nsIAccessible> parentAccessible;
00343     nsCOMPtr<nsIDOMNode> parentNode;
00344     GetParent(getter_AddRefs(parentAccessible));
00345     nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(parentAccessible));
00346     if (accessNode) 
00347       accessNode->GetDOMNode(getter_AddRefs(parentNode));
00348     element = do_QueryInterface(parentNode);
00349     if (element)
00350       element->HasAttribute(NS_LITERAL_STRING("open"), &isActive);
00351   }
00352 
00353   if (!isActive)
00354     *_retval |= STATE_OFFSCREEN;
00355 
00356   return NS_OK;
00357 }
00358 
00359 NS_IMETHODIMP nsXULMenupopupAccessible::GetName(nsAString& _retval)
00360 {
00361   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
00362   NS_ASSERTION(element, "No element for popup node!");
00363 
00364   while (element) {
00365     element->GetAttribute(NS_LITERAL_STRING("label"), _retval);
00366     if (!_retval.IsEmpty())
00367       return NS_OK;
00368     nsCOMPtr<nsIDOMNode> parentNode, node(do_QueryInterface(element));
00369     if (!node)
00370       return NS_ERROR_FAILURE;
00371     node->GetParentNode(getter_AddRefs(parentNode));
00372     element = do_QueryInterface(parentNode);
00373   }
00374 
00375   return NS_ERROR_FAILURE;
00376 }
00377 
00378 NS_IMETHODIMP nsXULMenupopupAccessible::GetRole(PRUint32 *_retval)
00379 {
00380   *_retval = ROLE_MENUPOPUP;
00381   return NS_OK;
00382 }
00383 
00384 // ------------------------ Menu Bar -----------------------------
00385 
00386 nsXULMenubarAccessible::nsXULMenubarAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): 
00387   nsAccessibleWrap(aDOMNode, aShell)
00388 { 
00389 }
00390 
00391 NS_IMETHODIMP nsXULMenubarAccessible::GetState(PRUint32 *_retval)
00392 {
00393   nsresult rv = nsAccessible::GetState(_retval);
00394   *_retval &= ~STATE_FOCUSABLE; // Menu bar iteself is not actually focusable
00395   return rv;
00396 }
00397 
00398 
00399 NS_IMETHODIMP nsXULMenubarAccessible::GetName(nsAString& _retval)
00400 {
00401   _retval.AssignLiteral("Application");
00402 
00403   return NS_OK;
00404 }
00405 
00406 NS_IMETHODIMP nsXULMenubarAccessible::GetRole(PRUint32 *_retval)
00407 {
00408   *_retval = ROLE_MENUBAR;
00409   return NS_OK;
00410 }
00411