/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * 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 the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Original Author: Eric Vaughan (evaughan@netscape.com) * * Contributor(s): * Kyle Yuan (kyle.yuan@sun.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsGUIEvent.h" #include "nsHTMLSelectAccessible.h" #include "nsIAccessibilityService.h" #include "nsIFrame.h" #include "nsIComboboxControlFrame.h" #include "nsIDocument.h" #include "nsIDOMDocumentEvent.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLInputElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIDOMHTMLOptGroupElement.h" #include "nsIDOMHTMLSelectElement.h" #include "nsIListControlFrame.h" #include "nsIOptionElement.h" #include "nsISelectControlFrame.h" #include "nsIServiceManager.h" #include "nsIWidget.h" #include "nsLayoutAtoms.h" /** * Selects, Listboxes and Comboboxes, are made up of a number of different * widgets, some of which are shared between the two. This file contains * all of the widgets for both of the Selects, for HTML only. Some of them * extend classes from nsSelectAccessible.cpp, which contains base classes * that are also extended by the XUL Select Accessibility support. * * Listbox: * - nsHTMLListboxAccessible * - nsHTMLSelectListAccessible * - nsHTMLSelectOptionAccessible * * Comboboxes: * - nsHTMLComboboxAccessilbe * - nsHTMLComboboxTextFieldAccessible * - nsHTMLComboboxButtonAccessible * - nsHTMLComboboxWindowAccessilbe * - nsHTMLSelectListAccessible * - nsHTMLSelectOptionAccessible */ /** ------------------------------------------------------ */ /** Impl. of nsHTMLSelectableAccessible */ /** ------------------------------------------------------ */ // Helper class nsHTMLSelectableAccessible::iterator::iterator(nsHTMLSelectableAccessible *aParent) : mParent(aParent) { mLength = mIndex = 0; mSelCount = 0; nsCOMPtr htmlSelect(do_QueryInterface(mParent->mDOMNode)); if (htmlSelect) { htmlSelect->GetOptions(getter_AddRefs(mOptions)); if (mOptions) mOptions->GetLength(&mLength); } } PRBool nsHTMLSelectableAccessible::iterator::Advance() { if (mIndex < mLength) { nsCOMPtr tempNode; if (mOptions) { mOptions->Item(mIndex, getter_AddRefs(tempNode)); mOption = do_QueryInterface(tempNode); } mIndex++; return PR_TRUE; } return PR_FALSE; } void nsHTMLSelectableAccessible::iterator::CalcSelectionCount(PRInt32 *aSelectionCount) { PRBool isSelected = PR_FALSE; if (mOption) mOption->GetSelected(&isSelected); if (isSelected) (*aSelectionCount)++; } void nsHTMLSelectableAccessible::iterator::AddAccessibleIfSelected(nsIAccessibilityService *aAccService, nsISupportsArray *aSelectedAccessibles, nsIPresContext *aContext) { PRBool isSelected = PR_FALSE; nsCOMPtr tempAccess; if (mOption) { mOption->GetSelected(&isSelected); if (isSelected) aAccService->CreateHTMLSelectOptionAccessible(mOption, mParent, aContext, getter_AddRefs(tempAccess)); } if (tempAccess) aSelectedAccessibles->AppendElement(tempAccess); } PRBool nsHTMLSelectableAccessible::iterator::GetAccessibleIfSelected(PRInt32 aIndex, nsIAccessibilityService *aAccService, nsIPresContext *aContext, nsIAccessible **_retval) { PRBool isSelected = PR_FALSE; nsCOMPtr tempAccess; *_retval = nsnull; if (mOption) { mOption->GetSelected(&isSelected); if (isSelected) { if (mSelCount == aIndex) { aAccService->CreateHTMLSelectOptionAccessible(mOption, mParent, aContext, getter_AddRefs(tempAccess)); *_retval = tempAccess; NS_IF_ADDREF(*_retval); return PR_TRUE; } mSelCount++; } } return PR_FALSE; } void nsHTMLSelectableAccessible::iterator::Select(PRBool aSelect) { if (mOption) mOption->SetSelected(aSelect); } nsHTMLSelectableAccessible::nsHTMLSelectableAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): nsAccessible(aDOMNode, aShell) { } NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLSelectableAccessible, nsAccessible, nsIAccessibleSelectable) // Helper methods NS_IMETHODIMP nsHTMLSelectableAccessible::ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState) { *aSelState = PR_FALSE; nsCOMPtr htmlSelect(do_QueryInterface(mDOMNode)); if (!htmlSelect) return NS_ERROR_FAILURE; nsCOMPtr options; htmlSelect->GetOptions(getter_AddRefs(options)); if (!options) return NS_ERROR_FAILURE; nsCOMPtr tempNode; options->Item(aIndex, getter_AddRefs(tempNode)); nsCOMPtr tempOption(do_QueryInterface(tempNode)); if (!tempOption) return NS_ERROR_FAILURE; tempOption->GetSelected(aSelState); if (eSelection_Add == aMethod && !(*aSelState)) tempOption->SetSelected(PR_TRUE); else if (eSelection_Remove == aMethod && (*aSelState)) tempOption->SetSelected(PR_FALSE); return NS_OK; } // Interface methods NS_IMETHODIMP nsHTMLSelectableAccessible::GetSelectedChildren(nsISupportsArray **_retval) { *_retval = nsnull; nsCOMPtr accService(do_GetService("@mozilla.org/accessibilityService;1")); if (!accService) return NS_ERROR_FAILURE; nsCOMPtr selectedAccessibles; NS_NewISupportsArray(getter_AddRefs(selectedAccessibles)); if (!selectedAccessibles) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr context; GetPresContext(context); if (!context) return NS_ERROR_FAILURE; nsHTMLSelectableAccessible::iterator iter(this); while (iter.Advance()) iter.AddAccessibleIfSelected(accService, selectedAccessibles, context); PRUint32 uLength = 0; selectedAccessibles->Count(&uLength); if (uLength != 0) { // length of nsISupportsArray containing selected options *_retval = selectedAccessibles; NS_ADDREF(*_retval); } return NS_OK; } // return the nth selected child's nsIAccessible object NS_IMETHODIMP nsHTMLSelectableAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **_retval) { *_retval = nsnull; nsCOMPtr accService(do_GetService("@mozilla.org/accessibilityService;1")); if (!accService) return NS_ERROR_FAILURE; nsCOMPtr context; GetPresContext(context); if (!context) return NS_ERROR_FAILURE; nsHTMLSelectableAccessible::iterator iter(this); while (iter.Advance()) if (iter.GetAccessibleIfSelected(aIndex, accService, context, _retval)) return NS_OK; // No matched item found return NS_ERROR_FAILURE; } NS_IMETHODIMP nsHTMLSelectableAccessible::GetSelectionCount(PRInt32 *aSelectionCount) { *aSelectionCount = 0; nsHTMLSelectableAccessible::iterator iter(this); while (iter.Advance()) iter.CalcSelectionCount(aSelectionCount); return NS_OK; } NS_IMETHODIMP nsHTMLSelectableAccessible::AddSelection(PRInt32 aIndex) { PRBool isSelected; return ChangeSelection(aIndex, eSelection_Add, &isSelected); } NS_IMETHODIMP nsHTMLSelectableAccessible::RemoveSelection(PRInt32 aIndex) { PRBool isSelected; return ChangeSelection(aIndex, eSelection_Remove, &isSelected); } NS_IMETHODIMP nsHTMLSelectableAccessible::IsChildSelected(PRInt32 aIndex, PRBool *_retval) { *_retval = PR_FALSE; return ChangeSelection(aIndex, eSelection_GetState, _retval); } NS_IMETHODIMP nsHTMLSelectableAccessible::ClearSelection() { nsHTMLSelectableAccessible::iterator iter(this); while (iter.Advance()) iter.Select(PR_FALSE); return NS_OK; } NS_IMETHODIMP nsHTMLSelectableAccessible::SelectAllSelection(PRBool *_retval) { *_retval = PR_FALSE; nsCOMPtr htmlSelect(do_QueryInterface(mDOMNode)); if (!htmlSelect) return NS_ERROR_FAILURE; htmlSelect->GetMultiple(_retval); if (*_retval) { nsHTMLSelectableAccessible::iterator iter(this); while (iter.Advance()) iter.Select(PR_TRUE); } return NS_OK; } /** ------------------------------------------------------ */ /** First, the common widgets */ /** ------------------------------------------------------ */ /** ----- nsHTMLSelectListAccessible ----- */ /** Default Constructor */ nsHTMLSelectListAccessible::nsHTMLSelectListAccessible(nsIAccessible* aParent, nsIDOMNode* aDOMNode, nsIWeakReference* aShell) :nsSelectListAccessible(aParent, aDOMNode, aShell) { } /** * As a nsHTMLSelectListAccessible we can have the following states: * STATE_MULTISELECTABLE * STATE_EXTSELECTABLE */ NS_IMETHODIMP nsHTMLSelectListAccessible::GetAccState(PRUint32 *_retval) { nsCOMPtr select (do_QueryInterface(mDOMNode)); if ( select ) { PRBool multiple; select->GetMultiple(&multiple); if ( multiple ) *_retval |= STATE_MULTISELECTABLE | STATE_EXTSELECTABLE; } return NS_OK; } /** * Gets the last child of the DOM node and creates and returns * a nsHTMLSelectOptionAccessible. */ NS_IMETHODIMP nsHTMLSelectListAccessible::GetAccLastChild(nsIAccessible **_retval) { nsCOMPtr last; mDOMNode->GetLastChild(getter_AddRefs(last)); *_retval = new nsHTMLSelectOptionAccessible(this, last, mPresShell); if ( ! *_retval ) return NS_ERROR_FAILURE; NS_ADDREF(*_retval); return NS_OK; } /** * Gets the child count of a Select List Accessible. We want to count * all the s and