gecko/accessible/src/xul/nsXULTreeAccessible.cpp

1374 lines
38 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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.
*
* Contributor(s):
* Author: Kyle Yuan (kyle.yuan@sun.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 MPL, 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 MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIBoxObject.h"
#include "nsIDOMXULElement.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
#include "nsIDOMXULTreeElement.h"
#include "nsITreeSelection.h"
#include "nsXULTreeAccessibleWrap.h"
#include "nsIMutableArray.h"
#include "nsComponentManagerUtils.h"
#ifdef MOZ_ACCESSIBILITY_ATK
#include "nsIAccessibleTable.h"
#endif
/* static */
PRBool nsXULTreeAccessible::IsColumnHidden(nsITreeColumn *aColumn)
{
nsCOMPtr<nsIDOMElement> element;
aColumn->GetElement(getter_AddRefs(element));
nsCOMPtr<nsIContent> content = do_QueryInterface(element);
return content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::hidden,
nsAccessibilityAtoms::_true, eCaseMatters);
}
/* static */
already_AddRefed<nsITreeColumn> nsXULTreeAccessible::GetNextVisibleColumn(nsITreeColumn *aColumn)
{
// Skip hidden columns.
nsCOMPtr<nsITreeColumn> nextColumn;
aColumn->GetNext(getter_AddRefs(nextColumn));
while (nextColumn && IsColumnHidden(nextColumn)) {
nsCOMPtr<nsITreeColumn> tempColumn;
nextColumn->GetNext(getter_AddRefs(tempColumn));
nextColumn.swap(tempColumn);
}
nsITreeColumn *retCol = nsnull;
nextColumn.swap(retCol);
return retCol;
}
/* static */
already_AddRefed<nsITreeColumn> nsXULTreeAccessible::GetFirstVisibleColumn(nsITreeBoxObject *aTree)
{
nsCOMPtr<nsITreeColumns> cols;
nsCOMPtr<nsITreeColumn> column;
aTree->GetColumns(getter_AddRefs(cols));
if (cols) {
cols->GetFirstColumn(getter_AddRefs(column));
}
if (column && IsColumnHidden(column)) {
column = GetNextVisibleColumn(column);
}
NS_ENSURE_TRUE(column, nsnull);
nsITreeColumn *retCol = nsnull;
column.swap(retCol);
return retCol;
}
/* static */
already_AddRefed<nsITreeColumn> nsXULTreeAccessible::GetLastVisibleColumn(nsITreeBoxObject *aTree)
{
nsCOMPtr<nsITreeColumns> cols;
nsCOMPtr<nsITreeColumn> column;
aTree->GetColumns(getter_AddRefs(cols));
if (cols) {
cols->GetLastColumn(getter_AddRefs(column));
}
// Skip hidden columns.
while (column && IsColumnHidden(column)) {
nsCOMPtr<nsITreeColumn> tempColumn;
column->GetPrevious(getter_AddRefs(tempColumn));
column.swap(tempColumn);
}
NS_ENSURE_TRUE(column, nsnull);
nsITreeColumn *retCol = nsnull;
column.swap(retCol);
return retCol;
}
// ---------- nsXULTreeAccessible ----------
nsXULTreeAccessible::nsXULTreeAccessible(nsIDOMNode *aDOMNode, nsIWeakReference *aShell):
nsXULSelectableAccessible(aDOMNode, aShell),
mAccessNodeCache(nsnull)
{
GetTreeBoxObject(aDOMNode, getter_AddRefs(mTree));
if (mTree)
mTree->GetView(getter_AddRefs(mTreeView));
NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n");
mAccessNodeCache = new nsAccessNodeHashtable;
mAccessNodeCache->Init(kDefaultTreeCacheSize);
}
NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeAccessible, nsXULSelectableAccessible, nsIAccessibleTreeCache)
// Get the nsITreeBoxObject interface from any levels DOMNode under the <tree>
void nsXULTreeAccessible::GetTreeBoxObject(nsIDOMNode *aDOMNode, nsITreeBoxObject **aBoxObject)
{
nsAutoString name;
nsCOMPtr<nsIDOMNode> parentNode, currentNode;
// Find DOMNode's parents recursively until reach the <tree> tag
currentNode = aDOMNode;
while (currentNode) {
currentNode->GetLocalName(name);
if (name.EqualsLiteral("tree")) {
// We will get the nsITreeBoxObject from the tree node
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(currentNode));
if (xulElement) {
nsCOMPtr<nsIBoxObject> box;
xulElement->GetBoxObject(getter_AddRefs(box));
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
if (treeBox) {
*aBoxObject = treeBox;
NS_ADDREF(*aBoxObject);
return;
}
}
}
currentNode->GetParentNode(getter_AddRefs(parentNode));
currentNode = parentNode;
}
*aBoxObject = nsnull;
}
nsresult
nsXULTreeAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
{
// Get focus status from base class
nsresult rv = nsAccessible::GetStateInternal(aState, aExtraState);
NS_ENSURE_A11Y_SUCCESS(rv, rv);
// see if we are multiple select if so set ourselves as such
nsCOMPtr<nsIDOMElement> element (do_QueryInterface(mDOMNode));
if (element) {
// the default selection type is multiple
nsAutoString selType;
element->GetAttribute(NS_LITERAL_STRING("seltype"), selType);
if (selType.IsEmpty() || !selType.EqualsLiteral("single"))
*aState |= nsIAccessibleStates::STATE_MULTISELECTABLE;
}
*aState |= nsIAccessibleStates::STATE_READONLY |
nsIAccessibleStates::STATE_FOCUSABLE;
return NS_OK;
}
// The value is the first selected child
NS_IMETHODIMP nsXULTreeAccessible::GetValue(nsAString& _retval)
{
_retval.Truncate(0);
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (! selection)
return NS_ERROR_FAILURE;
PRInt32 currentIndex;
nsCOMPtr<nsIDOMElement> selectItem;
selection->GetCurrentIndex(&currentIndex);
if (currentIndex >= 0) {
nsCOMPtr<nsITreeColumn> keyCol;
nsCOMPtr<nsITreeColumns> cols;
mTree->GetColumns(getter_AddRefs(cols));
if (cols)
cols->GetKeyColumn(getter_AddRefs(keyCol));
return mTreeView->GetCellText(currentIndex, keyCol, _retval);
}
return NS_OK;
}
nsresult
nsXULTreeAccessible::Shutdown()
{
mTree = nsnull;
mTreeView = nsnull;
nsXULSelectableAccessible::Shutdown();
if (mAccessNodeCache) {
ClearCache(*mAccessNodeCache);
delete mAccessNodeCache;
mAccessNodeCache = nsnull;
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::GetRole(PRUint32 *aRole)
{
NS_ASSERTION(mTree, "No tree view");
PRInt32 colCount = 0;
if (NS_SUCCEEDED(GetColumnCount(mTree, &colCount)) && (colCount > 1))
*aRole = nsIAccessibleRole::ROLE_TREE_TABLE;
else
*aRole = nsIAccessibleRole::ROLE_OUTLINE;
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::GetFirstChild(nsIAccessible **aFirstChild)
{
nsAccessible::GetFirstChild(aFirstChild);
// in normal case, tree's first child should be treecols, if it is not here,
// use the first row as tree's first child
if (*aFirstChild == nsnull) {
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
PRInt32 rowCount;
mTreeView->GetRowCount(&rowCount);
if (rowCount > 0) {
nsCOMPtr<nsITreeColumn> column = GetFirstVisibleColumn(mTree);
return GetCachedTreeitemAccessible(0, column, aFirstChild);
}
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::GetLastChild(nsIAccessible **aLastChild)
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
PRInt32 rowCount;
mTreeView->GetRowCount(&rowCount);
if (rowCount > 0) {
nsCOMPtr<nsITreeColumn> column = GetLastVisibleColumn(mTree);
return GetCachedTreeitemAccessible(rowCount - 1, column, aLastChild);
}
else // if there is not any rows, use treecols as tree's last child
nsAccessible::GetLastChild(aLastChild);
return NS_OK;
}
// tree's children count is row count + treecols count
NS_IMETHODIMP nsXULTreeAccessible::GetChildCount(PRInt32 *aAccChildCount)
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsAccessible::GetChildCount(aAccChildCount);
if (*aAccChildCount != eChildCountUninitialized) {
PRInt32 rowCount;
mTreeView->GetRowCount(&rowCount);
*aAccChildCount += rowCount;
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
{
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
do_QueryInterface(mDOMNode);
if (multiSelect) {
PRInt32 row;
multiSelect->GetCurrentIndex(&row);
if (row >= 0) {
GetCachedTreeitemAccessible(row, nsnull, aFocusedChild);
if (*aFocusedChild) {
return NS_OK; // Already addref'd by getter
}
}
}
NS_ADDREF(*aFocusedChild = this);
return NS_OK;
}
// nsIAccessible::getChildAtPoint(in long x, in long y)
NS_IMETHODIMP
nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY,
nsIAccessible **aAccessible)
{
nsIFrame *frame = GetFrame();
if (!frame)
return NS_ERROR_FAILURE;
nsPresContext *presContext = frame->PresContext();
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
nsIFrame *rootFrame = presShell->GetRootFrame();
NS_ENSURE_STATE(rootFrame);
nsIntRect rootRect = rootFrame->GetScreenRectExternal();
PRInt32 clientX = presContext->AppUnitsToIntCSSPixels(
presContext->DevPixelsToAppUnits(aX - rootRect.x));
PRInt32 clientY = presContext->AppUnitsToIntCSSPixels(
presContext->DevPixelsToAppUnits(aY - rootRect.y));
PRInt32 row = -1;
nsCOMPtr<nsITreeColumn> column;
nsCAutoString childEltUnused;
mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
childEltUnused);
// If we failed to find tree cell for the given point then it might be
// tree columns.
if (row == -1 || !column)
return nsXULSelectableAccessible::GetChildAtPoint(aX, aY, aAccessible);
return GetCachedTreeitemAccessible(row, column, aAccessible);
}
// nsIAccessible::getDeepestChildAtPoint(in long x, in long y)
NS_IMETHODIMP
nsXULTreeAccessible::GetDeepestChildAtPoint(PRInt32 aX, PRInt32 aY,
nsIAccessible **aAccessible)
{
// Call getChildAtPoint until tree doesn't support complex content.
return GetChildAtPoint(aX, aY, aAccessible);
}
// Ask treeselection to get all selected children
NS_IMETHODIMP nsXULTreeAccessible::GetSelectedChildren(nsIArray **_retval)
{
*_retval = nsnull;
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIMutableArray> selectedAccessibles =
do_CreateInstance(NS_ARRAY_CONTRACTID);
NS_ENSURE_STATE(selectedAccessibles);
PRInt32 rowIndex, rowCount;
PRBool isSelected;
mTreeView->GetRowCount(&rowCount);
for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
selection->IsSelected(rowIndex, &isSelected);
if (isSelected) {
nsCOMPtr<nsIAccessible> tempAccess;
if (NS_FAILED(GetCachedTreeitemAccessible(rowIndex, nsnull, getter_AddRefs(tempAccess))) || !tempAccess)
return NS_ERROR_OUT_OF_MEMORY;
selectedAccessibles->AppendElement(tempAccess, PR_FALSE);
}
}
PRUint32 length;
selectedAccessibles->GetLength(&length);
if (length != 0) {
*_retval = selectedAccessibles;
NS_IF_ADDREF(*_retval);
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
{
*aSelectionCount = 0;
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection)
selection->GetCount(aSelectionCount);
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState)
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
selection->IsSelected(aIndex, aSelState);
if ((!(*aSelState) && eSelection_Add == aMethod) ||
((*aSelState) && eSelection_Remove == aMethod))
return selection->ToggleSelect(aIndex);
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::AddChildToSelection(PRInt32 aIndex)
{
PRBool isSelected;
return ChangeSelection(aIndex, eSelection_Add, &isSelected);
}
NS_IMETHODIMP nsXULTreeAccessible::RemoveChildFromSelection(PRInt32 aIndex)
{
PRBool isSelected;
return ChangeSelection(aIndex, eSelection_Remove, &isSelected);
}
NS_IMETHODIMP nsXULTreeAccessible::IsChildSelected(PRInt32 aIndex, PRBool *_retval)
{
return ChangeSelection(aIndex, eSelection_GetState, _retval);
}
NS_IMETHODIMP nsXULTreeAccessible::ClearSelection()
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection)
selection->ClearSelection();
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **_retval)
{
*_retval = nsnull;
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection)
return NS_ERROR_FAILURE;
PRInt32 rowIndex, rowCount;
PRInt32 selCount = 0;
PRBool isSelected;
mTreeView->GetRowCount(&rowCount);
for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
selection->IsSelected(rowIndex, &isSelected);
if (isSelected) {
if (selCount == aIndex) {
return GetCachedTreeitemAccessible(rowIndex, nsnull, _retval);
}
selCount++;
}
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeAccessible::SelectAllSelection(PRBool *_retval)
{
*_retval = PR_FALSE;
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
// see if we are multiple select if so set ourselves as such
nsCOMPtr<nsIDOMElement> element (do_QueryInterface(mDOMNode));
if (element) {
nsAutoString selType;
element->GetAttribute(NS_LITERAL_STRING("seltype"), selType);
if (selType.IsEmpty() || !selType.EqualsLiteral("single")) {
*_retval = PR_TRUE;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection)
selection->SelectAll();
}
}
return NS_OK;
}
// nsIAccessible nsIAccessibleTreeCache::
// GetCachedTreeitemAccessible(in long aRow, nsITreeColumn* aColumn)
NS_IMETHODIMP
nsXULTreeAccessible::GetCachedTreeitemAccessible(PRInt32 aRow,
nsITreeColumn* aColumn,
nsIAccessible** aAccessible)
{
NS_ENSURE_ARG_POINTER(aAccessible);
*aAccessible = nsnull;
NS_ASSERTION(mAccessNodeCache, "No accessibility cache for tree");
NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n");
nsCOMPtr<nsITreeColumn> col;
#ifdef MOZ_ACCESSIBILITY_ATK
col = aColumn;
#endif
PRInt32 columnIndex = -1;
if (!col && mTree) {
nsCOMPtr<nsITreeColumns> cols;
mTree->GetColumns(getter_AddRefs(cols));
if (cols)
cols->GetKeyColumn(getter_AddRefs(col));
}
// Do not create accessible for treeitem if there is no column in the tree
// because it doesn't render treeitems properly.
if (!col)
return NS_OK;
col->GetIndex(&columnIndex);
nsCOMPtr<nsIAccessNode> accessNode;
GetCacheEntry(*mAccessNodeCache, (void*)(aRow * kMaxTreeColumns + columnIndex), getter_AddRefs(accessNode));
if (!accessNode)
{
nsXULTreeitemAccessibleWrap* treeItemAcc =
new nsXULTreeitemAccessibleWrap(this, mDOMNode, mWeakShell, aRow, col);
NS_ENSURE_TRUE(treeItemAcc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = treeItemAcc->Init();
NS_ENSURE_SUCCESS(rv, rv);
accessNode = treeItemAcc;
PutCacheEntry(*mAccessNodeCache, (void*)(aRow * kMaxTreeColumns + columnIndex), accessNode);
}
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
NS_IF_ADDREF(*aAccessible = accessible);
return NS_OK;
}
// void nsIAccessibleTreeCache::
// invalidateCache(in PRInt32 aRow, in PRInt32 aCount)
NS_IMETHODIMP
nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
{
// Do not invalidate the cache if rows have been inserted.
if (aCount > 0)
return NS_OK;
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeColumns> cols;
nsresult rv = mTree->GetColumns(getter_AddRefs(cols));
NS_ENSURE_STATE(cols);
#ifdef MOZ_ACCESSIBILITY_ATK
PRInt32 colsCount = 0;
rv = cols->GetCount(&colsCount);
NS_ENSURE_SUCCESS(rv, rv);
#else
nsCOMPtr<nsITreeColumn> col;
rv = cols->GetKeyColumn(getter_AddRefs(col));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 colIdx = 0;
rv = col->GetIndex(&colIdx);
NS_ENSURE_SUCCESS(rv, rv);
#endif
for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
#ifdef MOZ_ACCESSIBILITY_ATK
for (PRInt32 colIdx = 0; colIdx < colsCount; ++colIdx) {
#else
{
#endif
void *key = reinterpret_cast<void*>(rowIdx * kMaxTreeColumns + colIdx);
nsCOMPtr<nsIAccessNode> accessNode;
GetCacheEntry(*mAccessNodeCache, key, getter_AddRefs(accessNode));
if (accessNode) {
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
nsCOMPtr<nsIAccessibleEvent> event =
new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_DESTROY,
accessible, PR_FALSE);
FireAccessibleEvent(event);
mAccessNodeCache->Remove(key);
}
}
}
PRInt32 newRowCount = 0;
rv = mTreeView->GetRowCount(&newRowCount);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 oldRowCount = newRowCount - aCount;
for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
#ifdef MOZ_ACCESSIBILITY_ATK
for (PRInt32 colIdx = 0; colIdx < colsCount; ++colIdx) {
#else
{
#endif
void *key = reinterpret_cast<void*>(rowIdx * kMaxTreeColumns + colIdx);
mAccessNodeCache->Remove(key);
}
}
return NS_OK;
}
// void nsIAccessibleTreeCache::
// treeViewInvalidated(in long aStartRow, in long aEndRow,
// in long aStartCol, in long aEndCol);
NS_IMETHODIMP
nsXULTreeAccessible::TreeViewInvalidated(PRInt32 aStartRow, PRInt32 aEndRow,
PRInt32 aStartCol, PRInt32 aEndCol)
{
NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE);
PRInt32 endRow = aEndRow;
nsresult rv;
if (endRow == -1) {
PRInt32 rowCount = 0;
rv = mTreeView->GetRowCount(&rowCount);
NS_ENSURE_SUCCESS(rv, rv);
endRow = rowCount - 1;
}
nsCOMPtr<nsITreeColumns> treeColumns;
mTree->GetColumns(getter_AddRefs(treeColumns));
NS_ENSURE_STATE(treeColumns);
#ifdef MOZ_ACCESSIBILITY_ATK
PRInt32 endCol = aEndCol;
if (endCol == -1) {
PRInt32 colCount = 0;
rv = treeColumns->GetCount(&colCount);
NS_ENSURE_SUCCESS(rv, rv);
endCol = colCount - 1;
}
#else
nsCOMPtr<nsITreeColumn> col;
rv = treeColumns->GetKeyColumn(getter_AddRefs(col));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 colIdx = 0;
rv = col->GetIndex(&colIdx);
NS_ENSURE_SUCCESS(rv, rv);
#endif
for (PRInt32 rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
#ifdef MOZ_ACCESSIBILITY_ATK
2008-02-08 05:25:09 -08:00
for (PRInt32 colIdx = aStartCol; colIdx <= endCol; ++colIdx)
#endif
{
void *key = reinterpret_cast<void*>(rowIdx * kMaxTreeColumns + colIdx);
nsCOMPtr<nsIAccessNode> accessNode;
GetCacheEntry(*mAccessNodeCache, key, getter_AddRefs(accessNode));
if (accessNode) {
nsCOMPtr<nsIAccessible> acc(do_QueryInterface(accessNode));
NS_ENSURE_STATE(acc);
nsCOMPtr<nsPIAccessibleTreeItem> treeItemAcc(
do_QueryInterface(accessNode));
NS_ENSURE_STATE(treeItemAcc);
nsAutoString name, cachedName;
rv = acc->GetName(name);
NS_ENSURE_SUCCESS(rv, rv);
rv = treeItemAcc->GetCachedName(cachedName);
NS_ENSURE_SUCCESS(rv, rv);
if (name != cachedName) {
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, acc);
treeItemAcc->SetCachedName(name);
}
}
}
}
return NS_OK;
}
// void nsIAccessibleTreeCache::treeViewChanged();
NS_IMETHODIMP
nsXULTreeAccessible::TreeViewChanged()
{
if (!mTree)
return NS_ERROR_FAILURE;
// Fire only notification destroy/create events on accessible tree to lie to
// AT because it should be expensive to fire destroy events for each tree item
// in cache.
nsCOMPtr<nsIAccessibleEvent> eventDestroy =
new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_DESTROY,
this, PR_FALSE);
NS_ENSURE_TRUE(eventDestroy, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = FirePlatformEvent(eventDestroy);
ClearCache(*mAccessNodeCache);
mTree->GetView(getter_AddRefs(mTreeView));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAccessibleEvent> eventCreate =
new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_CREATE,
this, PR_FALSE);
NS_ENSURE_TRUE(eventCreate, NS_ERROR_OUT_OF_MEMORY);
return FirePlatformEvent(eventCreate);
}
nsresult nsXULTreeAccessible::GetColumnCount(nsITreeBoxObject* aBoxObject, PRInt32* aCount)
{
NS_ENSURE_TRUE(aBoxObject, NS_ERROR_FAILURE);
nsCOMPtr<nsITreeColumns> treeColumns;
aBoxObject->GetColumns(getter_AddRefs(treeColumns));
NS_ENSURE_TRUE(treeColumns, NS_ERROR_FAILURE);
return treeColumns->GetCount(aCount);
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeitemAccessible
nsXULTreeitemAccessible::nsXULTreeitemAccessible(nsIAccessible *aParent, nsIDOMNode *aDOMNode, nsIWeakReference *aShell, PRInt32 aRow, nsITreeColumn* aColumn)
: nsLeafAccessible(aDOMNode, aShell)
{
mParent = aParent; // xxx todo: do we need this? We already have mParent on nsAccessible
nsXULTreeAccessible::GetTreeBoxObject(aDOMNode, getter_AddRefs(mTree));
if (mTree)
mTree->GetView(getter_AddRefs(mTreeView));
NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n");
// Since the real tree item does not correspond to any DOMNode, use the row index to distinguish each item
mRow = aRow;
mColumn = aColumn;
if (!mColumn && mTree) {
nsCOMPtr<nsITreeColumns> cols;
mTree->GetColumns(getter_AddRefs(cols));
if (cols)
cols->GetKeyColumn(getter_AddRefs(mColumn));
}
}
NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeitemAccessible, nsLeafAccessible,
2008-02-08 05:27:54 -08:00
nsPIAccessibleTreeItem)
nsresult
nsXULTreeitemAccessible::Shutdown()
{
mTree = nsnull;
mTreeView = nsnull;
mColumn = nsnull;
return nsLeafAccessible::Shutdown();
}
NS_IMETHODIMP
nsXULTreeitemAccessible::GetName(nsAString& aName)
{
// XXX: we should take into account ARIA usage for content tree.
aName.Truncate();
if (IsDefunct())
return NS_ERROR_FAILURE;
mTreeView->GetCellText(mRow, mColumn, aName);
// If there is still no name try the cell value:
// This is for graphical cells. We need tree/table view implementors to implement
// FooView::GetCellValue to return a meaningful string for cases where there is
// something shown in the cell (non-text) such as a star icon; in which case
// GetCellValue for that cell would return "starred" or "flagged" for example.
if (aName.IsEmpty()) {
mTreeView->GetCellValue(mRow, mColumn, aName);
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeitemAccessible::GetUniqueID(void **aUniqueID)
{
// Since mDOMNode is same for all tree item, use |this| pointer as the unique Id
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
// nsAccessNode::Init()
nsresult
nsXULTreeitemAccessible::Init()
{
nsresult rv = nsLeafAccessible::Init();
NS_ENSURE_SUCCESS(rv, rv);
return GetName(mCachedName);
}
NS_IMETHODIMP nsXULTreeitemAccessible::GetRole(PRUint32 *aRole)
{
PRInt32 colCount = 0;
if (NS_SUCCEEDED(nsXULTreeAccessible::GetColumnCount(mTree, &colCount)) && colCount > 1)
*aRole = nsIAccessibleRole::ROLE_CELL;
else
*aRole = nsIAccessibleRole::ROLE_OUTLINEITEM;
return NS_OK;
}
// Possible states: focused, focusable, selected, checkable, checked,
// expanded/collapsed, invisible
nsresult
nsXULTreeitemAccessible::GetStateInternal(PRUint32 *aState,
PRUint32 *aExtraState)
{
NS_ENSURE_ARG_POINTER(aState);
*aState = 0;
if (aExtraState)
*aExtraState = 0;
if (IsDefunct()) {
if (aExtraState)
*aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT;
return NS_OK_DEFUNCT_OBJECT;
}
*aState = nsIAccessibleStates::STATE_FOCUSABLE |
nsIAccessibleStates::STATE_SELECTABLE;
// get expanded/collapsed state
if (IsExpandable()) {
PRBool isContainerOpen;
mTreeView->IsContainerOpen(mRow, &isContainerOpen);
*aState |= isContainerOpen? PRUint32(nsIAccessibleStates::STATE_EXPANDED):
PRUint32(nsIAccessibleStates::STATE_COLLAPSED);
}
// get selected state
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
PRBool isSelected;
selection->IsSelected(mRow, &isSelected);
if (isSelected)
*aState |= nsIAccessibleStates::STATE_SELECTED;
}
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
do_QueryInterface(mDOMNode);
if (multiSelect) {
PRInt32 currentIndex;
multiSelect->GetCurrentIndex(&currentIndex);
if (currentIndex == mRow) {
*aState |= nsIAccessibleStates::STATE_FOCUSED;
}
}
PRInt32 firstVisibleRow, lastVisibleRow;
mTree->GetFirstVisibleRow(&firstVisibleRow);
mTree->GetLastVisibleRow(&lastVisibleRow);
if (mRow < firstVisibleRow || mRow > lastVisibleRow)
*aState |= nsIAccessibleStates::STATE_INVISIBLE;
PRInt16 type;
mColumn->GetType(&type);
if (type == nsITreeColumn::TYPE_CHECKBOX) {
*aState |= nsIAccessibleStates::STATE_CHECKABLE;
nsAutoString checked;
mTreeView->GetCellValue(mRow, mColumn, checked);
if (checked.EqualsIgnoreCase("true")) {
*aState |= nsIAccessibleStates::STATE_CHECKED;
}
}
return NS_OK;
}
PRBool
nsXULTreeitemAccessible::IsDefunct()
{
if (!mTree || !mTreeView || !mColumn || mRow < 0)
return PR_TRUE;
PRInt32 rowCount = 0;
nsresult rv = mTreeView->GetRowCount(&rowCount);
return NS_FAILED(rv) || mRow >= rowCount;
}
PRBool nsXULTreeitemAccessible::IsExpandable()
{
if (IsDefunct())
return PR_FALSE;
PRBool isContainer;
mTreeView->IsContainer(mRow, &isContainer);
if (isContainer) {
PRBool isEmpty;
mTreeView->IsContainerEmpty(mRow, &isEmpty);
if (!isEmpty) {
PRBool isPrimary;
mColumn->GetPrimary(&isPrimary);
if (isPrimary) {
return PR_TRUE;
}
}
}
return PR_FALSE;
}
// "activate" (xor "cycle") action is available for all treeitems
// "expand/collapse" action is avaible for treeitem which is container
NS_IMETHODIMP nsXULTreeitemAccessible::GetNumActions(PRUint8 *aNumActions)
{
NS_ENSURE_ARG_POINTER(aNumActions);
*aNumActions = 0;
if (IsDefunct())
return NS_ERROR_FAILURE;
*aNumActions = IsExpandable() ? 2 : 1;
return NS_OK;
}
// Return the name of our actions
NS_IMETHODIMP nsXULTreeitemAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
if (aIndex == eAction_Click) {
PRBool isCycler;
mColumn->GetCycler(&isCycler);
if (isCycler) {
aName.AssignLiteral("cycle");
}
else {
aName.AssignLiteral("activate");
}
return NS_OK;
}
else if (aIndex == eAction_Expand && IsExpandable()) {
PRBool isContainerOpen;
mTreeView->IsContainerOpen(mRow, &isContainerOpen);
if (isContainerOpen)
aName.AssignLiteral("collapse");
else
aName.AssignLiteral("expand");
return NS_OK;
}
return NS_ERROR_INVALID_ARG;
}
nsresult
nsXULTreeitemAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
{
NS_ENSURE_ARG_POINTER(aAttributes);
if (IsDefunct())
return NS_ERROR_FAILURE;
nsresult rv = nsLeafAccessible::GetAttributesInternal(aAttributes);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMXULTreeElement> tree(do_QueryInterface(mDOMNode));
NS_ENSURE_TRUE(tree, NS_OK);
nsCOMPtr<nsITreeView> view;
tree->GetView(getter_AddRefs(view));
NS_ENSURE_TRUE(view, NS_OK);
PRInt32 level;
rv = view->GetLevel(mRow, &level);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 topCount = 1;
for (PRInt32 index = mRow - 1; index >= 0; index--) {
PRInt32 lvl = -1;
if (NS_SUCCEEDED(view->GetLevel(index, &lvl))) {
if (lvl < level)
break;
if (lvl == level)
topCount++;
}
}
PRInt32 rowCount = 0;
rv = view->GetRowCount(&rowCount);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 bottomCount = 0;
for (PRInt32 index = mRow + 1; index < rowCount; index++) {
PRInt32 lvl = -1;
if (NS_SUCCEEDED(view->GetLevel(index, &lvl))) {
if (lvl < level)
break;
if (lvl == level)
bottomCount++;
}
}
PRInt32 setSize = topCount + bottomCount;
PRInt32 posInSet = topCount;
// set the group attributes
nsAccUtils::SetAccGroupAttrs(aAttributes, level + 1, posInSet, setSize);
// set the "cycles" attribute
PRBool isCycler;
mColumn->GetCycler(&isCycler);
if (isCycler) {
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::cycles,
NS_LITERAL_STRING("true"));
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeitemAccessible::GetParent(nsIAccessible **aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
*aParent = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
if (mParent) {
*aParent = mParent;
NS_ADDREF(*aParent);
}
return NS_OK;
}
// Return the next row of tree if mColumn (if any),
// otherwise return the next cell.
NS_IMETHODIMP nsXULTreeitemAccessible::GetNextSibling(nsIAccessible **aNextSibling)
{
NS_ENSURE_ARG_POINTER(aNextSibling);
*aNextSibling = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessibleTreeCache> treeCache(do_QueryInterface(mParent));
NS_ENSURE_TRUE(treeCache, NS_ERROR_FAILURE);
PRInt32 rowCount;
mTreeView->GetRowCount(&rowCount);
if (!mColumn) {
if (mRow < rowCount - 1)
return treeCache->GetCachedTreeitemAccessible(mRow + 1, nsnull, aNextSibling);
else
return NS_OK;
}
nsresult rv = NS_OK;
PRInt32 row = mRow;
nsCOMPtr<nsITreeColumn> column;
#ifdef MOZ_ACCESSIBILITY_ATK
column = nsXULTreeAccessible::GetNextVisibleColumn(mColumn);
if (!column) {
if (mRow < rowCount -1) {
row++;
column = nsXULTreeAccessible::GetFirstVisibleColumn(mTree);
} else {
// the next sibling of the last treeitem is null
return NS_OK;
}
}
#else
if (++row >= rowCount) {
return NS_ERROR_FAILURE;
}
#endif //MOZ_ACCESSIBILITY_ATK
rv = treeCache->GetCachedTreeitemAccessible(row, column, aNextSibling);
return rv;
}
// Return the previous row of tree if mColumn (if any),
// otherwise return the previous cell.
NS_IMETHODIMP nsXULTreeitemAccessible::GetPreviousSibling(nsIAccessible **aPreviousSibling)
{
NS_ENSURE_ARG_POINTER(aPreviousSibling);
*aPreviousSibling = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessibleTreeCache> treeCache(do_QueryInterface(mParent));
NS_ENSURE_TRUE(treeCache, NS_ERROR_FAILURE);
if (!mColumn && mRow > 0)
return treeCache->GetCachedTreeitemAccessible(mRow - 1, nsnull, aPreviousSibling);
nsresult rv = NS_OK;
PRInt32 row = mRow;
nsCOMPtr<nsITreeColumn> column;
#ifdef MOZ_ACCESSIBILITY_ATK
rv = mColumn->GetPrevious(getter_AddRefs(column));
NS_ENSURE_SUCCESS(rv, rv);
if (!column && mRow > 0) {
row--;
column = nsXULTreeAccessible::GetLastVisibleColumn(mTree);
}
#else
if (--row < 0) {
return NS_ERROR_FAILURE;
}
#endif
rv = treeCache->GetCachedTreeitemAccessible(row, column, aPreviousSibling);
return rv;
}
NS_IMETHODIMP nsXULTreeitemAccessible::DoAction(PRUint8 index)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
if (index == eAction_Click) {
nsresult rv = NS_OK;
PRBool isCycler;
mColumn->GetCycler(&isCycler);
if (isCycler) {
rv = mTreeView->CycleCell(mRow, mColumn);
}
else {
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
rv = selection->Select(mRow);
mTree->EnsureRowIsVisible(mRow);
}
}
return rv;
}
else if (index == eAction_Expand && IsExpandable()) {
return mTreeView->ToggleOpenState(mRow);
}
return NS_ERROR_INVALID_ARG;
}
NS_IMETHODIMP nsXULTreeitemAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height)
{
NS_ENSURE_ARG_POINTER(x);
*x = 0;
NS_ENSURE_ARG_POINTER(y);
*y = 0;
NS_ENSURE_ARG_POINTER(width);
*width = 0;
NS_ENSURE_ARG_POINTER(height);
*height = 0;
if (IsDefunct())
return NS_ERROR_FAILURE;
// This Bounds are based on Tree's coord
mTree->GetCoordsForCellItem(mRow, mColumn, EmptyCString(), x, y, width, height);
// Get treechildren's BoxObject to adjust the Bounds' upper left corner
// XXXvarga consider using mTree->GetTreeBody()
nsCOMPtr<nsIBoxObject> boxObject(do_QueryInterface(mTree));
if (boxObject) {
nsCOMPtr<nsIDOMElement> boxElement;
boxObject->GetElement(getter_AddRefs(boxElement));
nsCOMPtr<nsIDOMNode> boxNode(do_QueryInterface(boxElement));
if (boxNode) {
nsCOMPtr<nsIDOMNodeList> childNodes;
boxNode->GetChildNodes(getter_AddRefs(childNodes));
if (childNodes) {
nsAutoString name;
nsCOMPtr<nsIDOMNode> childNode;
PRUint32 childCount, childIndex;
childNodes->GetLength(&childCount);
for (childIndex = 0; childIndex < childCount; childIndex++) {
childNodes->Item(childIndex, getter_AddRefs(childNode));
childNode->GetLocalName(name);
if (name.EqualsLiteral("treechildren")) {
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(childNode));
if (xulElement) {
nsCOMPtr<nsIBoxObject> box;
xulElement->GetBoxObject(getter_AddRefs(box));
if (box) {
PRInt32 myX, myY;
box->GetScreenX(&myX);
box->GetScreenY(&myY);
*x += myX;
*y += myY;
}
}
break;
}
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeitemAccessible::SetSelected(PRBool aSelect)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
PRBool isSelected;
selection->IsSelected(mRow, &isSelected);
if (isSelected != aSelect)
selection->ToggleSelect(mRow);
}
return NS_OK;
}
NS_IMETHODIMP nsXULTreeitemAccessible::TakeFocus()
{
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection)
selection->SetCurrentIndex(mRow);
// focus event will be fired here
return nsAccessible::TakeFocus();
}
NS_IMETHODIMP
nsXULTreeitemAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelation);
*aRelation = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
if (aRelationType == nsIAccessibleRelation::RELATION_NODE_CHILD_OF) {
PRInt32 columnIndex;
if (NS_SUCCEEDED(mColumn->GetIndex(&columnIndex)) && columnIndex == 0) {
PRInt32 parentIndex;
if (NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) {
if (parentIndex == -1)
return nsRelUtils::AddTarget(aRelationType, aRelation, mParent);
nsCOMPtr<nsIAccessibleTreeCache> cache =
do_QueryInterface(mParent);
nsCOMPtr<nsIAccessible> accParent;
nsresult rv = cache->
GetCachedTreeitemAccessible(parentIndex, mColumn,
getter_AddRefs(accParent));
NS_ENSURE_SUCCESS(rv, rv);
return nsRelUtils::AddTarget(aRelationType, aRelation, accParent);
}
}
return NS_OK;
}
return nsAccessible::GetRelationByType(aRelationType, aRelation);
}
// attribute AString nsIAccessibleTreeItem::cachedName
NS_IMETHODIMP
nsXULTreeitemAccessible::GetCachedName(nsAString &aName)
{
aName = mCachedName;
return NS_OK;
}
// attribute AString nsIAccessibleTreeItem::cachedName
NS_IMETHODIMP
nsXULTreeitemAccessible::SetCachedName(const nsAString &aName)
{
mCachedName = aName;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeColumnsAccessible
nsXULTreeColumnsAccessible::
nsXULTreeColumnsAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
nsXULColumnsAccessible(aDOMNode, aShell)
{
}
NS_IMETHODIMP
nsXULTreeColumnsAccessible::GetNextSibling(nsIAccessible **aNextSibling)
{
NS_ENSURE_ARG_POINTER(aNextSibling);
*aNextSibling = nsnull;
nsCOMPtr<nsITreeBoxObject> tree;
nsCOMPtr<nsITreeView> treeView;
nsXULTreeAccessible::GetTreeBoxObject(mDOMNode, getter_AddRefs(tree));
if (tree) {
tree->GetView(getter_AddRefs(treeView));
if (treeView) {
PRInt32 rowCount;
treeView->GetRowCount(&rowCount);
if (rowCount > 0) {
nsCOMPtr<nsITreeColumn> column =
nsXULTreeAccessible::GetFirstVisibleColumn(tree);
nsCOMPtr<nsIAccessibleTreeCache> treeCache(do_QueryInterface(mParent));
NS_ENSURE_TRUE(treeCache, NS_ERROR_FAILURE);
return treeCache->GetCachedTreeitemAccessible(0, column, aNextSibling);
}
}
}
return NS_OK;
}