1/*
2 * Copyright (C) 2008 Nuanti Ltd.
3 * Copyright (C) 2009 Jan Alonzo
4 * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
5 *
6 * Portions from Mozilla a11y, copyright as follows:
7 *
8 * The Original Code is mozilla.org code.
9 *
10 * The Initial Developer of the Original Code is
11 * Sun Microsystems, Inc.
12 * Portions created by the Initial Developer are Copyright (C) 2002
13 * the Initial Developer. All Rights Reserved.
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public
17 * License as published by the Free Software Foundation; either
18 * version 2 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public License
26 * along with this library; see the file COPYING.LIB.  If not, write to
27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 * Boston, MA 02110-1301, USA.
29 */
30
31#include "config.h"
32#include "WebKitAccessibleInterfaceTable.h"
33
34#if HAVE(ACCESSIBILITY)
35
36#include "AccessibilityListBox.h"
37#include "AccessibilityObject.h"
38#include "AccessibilityTable.h"
39#include "AccessibilityTableCell.h"
40#include "HTMLSelectElement.h"
41#include "HTMLTableCaptionElement.h"
42#include "HTMLTableElement.h"
43#include "RenderObject.h"
44#include "WebKitAccessibleInterfaceText.h"
45#include "WebKitAccessibleWrapperAtk.h"
46
47using namespace WebCore;
48
49static AccessibilityObject* core(AtkTable* table)
50{
51    if (!WEBKIT_IS_ACCESSIBLE(table))
52        return 0;
53
54    return webkitAccessibleGetAccessibilityObject(WEBKIT_ACCESSIBLE(table));
55}
56
57static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
58{
59    AccessibilityObject* accTable = core(table);
60    if (accTable->isAccessibilityRenderObject())
61        return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
62    return 0;
63}
64
65static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
66{
67    // Calculate the cell's index as if we had a traditional Gtk+ table in
68    // which cells are all direct children of the table, arranged row-first.
69    AccessibilityObject::AccessibilityChildrenVector allCells;
70    axTable->cells(allCells);
71    AccessibilityObject::AccessibilityChildrenVector::iterator position;
72    position = std::find(allCells.begin(), allCells.end(), axCell);
73    if (position == allCells.end())
74        return -1;
75    return position - allCells.begin();
76}
77
78static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
79{
80    AccessibilityObject* accTable = core(table);
81    if (accTable->isAccessibilityRenderObject()) {
82        AccessibilityObject::AccessibilityChildrenVector allCells;
83        static_cast<AccessibilityTable*>(accTable)->cells(allCells);
84        if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
85            AccessibilityObject* accCell = allCells.at(index).get();
86            return static_cast<AccessibilityTableCell*>(accCell);
87        }
88    }
89    return 0;
90}
91
92static AtkObject* webkitAccessibleTableRefAt(AtkTable* table, gint row, gint column)
93{
94    AccessibilityTableCell* axCell = cell(table, row, column);
95    if (!axCell)
96        return 0;
97
98    AtkObject* cell = axCell->wrapper();
99    if (!cell)
100        return 0;
101
102    // This method transfers full ownership over the returned
103    // AtkObject, so an extra reference is needed here.
104    return ATK_OBJECT(g_object_ref(cell));
105}
106
107static gint webkitAccessibleTableGetIndexAt(AtkTable* table, gint row, gint column)
108{
109    AccessibilityTableCell* axCell = cell(table, row, column);
110    AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
111    return cellIndex(axCell, axTable);
112}
113
114static gint webkitAccessibleTableGetColumnAtIndex(AtkTable* table, gint index)
115{
116    AccessibilityTableCell* axCell = cellAtIndex(table, index);
117    if (axCell) {
118        pair<unsigned, unsigned> columnRange;
119        axCell->columnIndexRange(columnRange);
120        return columnRange.first;
121    }
122    return -1;
123}
124
125static gint webkitAccessibleTableGetRowAtIndex(AtkTable* table, gint index)
126{
127    AccessibilityTableCell* axCell = cellAtIndex(table, index);
128    if (axCell) {
129        pair<unsigned, unsigned> rowRange;
130        axCell->rowIndexRange(rowRange);
131        return rowRange.first;
132    }
133    return -1;
134}
135
136static gint webkitAccessibleTableGetNColumns(AtkTable* table)
137{
138    AccessibilityObject* accTable = core(table);
139    if (accTable->isAccessibilityRenderObject())
140        return static_cast<AccessibilityTable*>(accTable)->columnCount();
141    return 0;
142}
143
144static gint webkitAccessibleTableGetNRows(AtkTable* table)
145{
146    AccessibilityObject* accTable = core(table);
147    if (accTable->isAccessibilityRenderObject())
148        return static_cast<AccessibilityTable*>(accTable)->rowCount();
149    return 0;
150}
151
152static gint webkitAccessibleTableGetColumnExtentAt(AtkTable* table, gint row, gint column)
153{
154    AccessibilityTableCell* axCell = cell(table, row, column);
155    if (axCell) {
156        pair<unsigned, unsigned> columnRange;
157        axCell->columnIndexRange(columnRange);
158        return columnRange.second;
159    }
160    return 0;
161}
162
163static gint webkitAccessibleTableGetRowExtentAt(AtkTable* table, gint row, gint column)
164{
165    AccessibilityTableCell* axCell = cell(table, row, column);
166    if (axCell) {
167        pair<unsigned, unsigned> rowRange;
168        axCell->rowIndexRange(rowRange);
169        return rowRange.second;
170    }
171    return 0;
172}
173
174static AtkObject* webkitAccessibleTableGetColumnHeader(AtkTable* table, gint column)
175{
176    AccessibilityObject* accTable = core(table);
177    if (accTable->isAccessibilityRenderObject()) {
178        AccessibilityObject::AccessibilityChildrenVector allColumnHeaders;
179        static_cast<AccessibilityTable*>(accTable)->columnHeaders(allColumnHeaders);
180        unsigned columnCount = allColumnHeaders.size();
181        for (unsigned k = 0; k < columnCount; ++k) {
182            pair<unsigned, unsigned> columnRange;
183            AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allColumnHeaders.at(k).get());
184            cell->columnIndexRange(columnRange);
185            if (columnRange.first <= static_cast<unsigned>(column) && static_cast<unsigned>(column) < columnRange.first + columnRange.second)
186                return allColumnHeaders[k]->wrapper();
187        }
188    }
189    return 0;
190}
191
192static AtkObject* webkitAccessibleTableGetRowHeader(AtkTable* table, gint row)
193{
194    AccessibilityObject* accTable = core(table);
195    if (accTable->isAccessibilityRenderObject()) {
196        AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
197        static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
198        unsigned rowCount = allRowHeaders.size();
199        for (unsigned k = 0; k < rowCount; ++k) {
200            pair<unsigned, unsigned> rowRange;
201            AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allRowHeaders.at(k).get());
202            cell->rowIndexRange(rowRange);
203            if (rowRange.first <= static_cast<unsigned>(row) && static_cast<unsigned>(row) < rowRange.first + rowRange.second)
204                return allRowHeaders[k]->wrapper();
205        }
206    }
207    return 0;
208}
209
210static AtkObject* webkitAccessibleTableGetCaption(AtkTable* table)
211{
212    AccessibilityObject* accTable = core(table);
213    if (accTable->isAccessibilityRenderObject()) {
214        Node* node = accTable->node();
215        if (node && node->hasTagName(HTMLNames::tableTag)) {
216            HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
217            if (caption)
218                return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
219        }
220    }
221    return 0;
222}
223
224static const gchar* webkitAccessibleTableGetColumnDescription(AtkTable* table, gint column)
225{
226    AtkObject* columnHeader = atk_table_get_column_header(table, column);
227    if (columnHeader && ATK_IS_TEXT(columnHeader))
228        return atk_text_get_text(ATK_TEXT(columnHeader), 0, -1);
229
230    return 0;
231}
232
233static const gchar* webkitAccessibleTableGetRowDescription(AtkTable* table, gint row)
234{
235    AtkObject* rowHeader = atk_table_get_row_header(table, row);
236    if (rowHeader && ATK_IS_TEXT(rowHeader))
237        return atk_text_get_text(ATK_TEXT(rowHeader), 0, -1);
238
239    return 0;
240}
241
242void webkitAccessibleTableInterfaceInit(AtkTableIface* iface)
243{
244    iface->ref_at = webkitAccessibleTableRefAt;
245    iface->get_index_at = webkitAccessibleTableGetIndexAt;
246    iface->get_column_at_index = webkitAccessibleTableGetColumnAtIndex;
247    iface->get_row_at_index = webkitAccessibleTableGetRowAtIndex;
248    iface->get_n_columns = webkitAccessibleTableGetNColumns;
249    iface->get_n_rows = webkitAccessibleTableGetNRows;
250    iface->get_column_extent_at = webkitAccessibleTableGetColumnExtentAt;
251    iface->get_row_extent_at = webkitAccessibleTableGetRowExtentAt;
252    iface->get_column_header = webkitAccessibleTableGetColumnHeader;
253    iface->get_row_header = webkitAccessibleTableGetRowHeader;
254    iface->get_caption = webkitAccessibleTableGetCaption;
255    iface->get_column_description = webkitAccessibleTableGetColumnDescription;
256    iface->get_row_description = webkitAccessibleTableGetRowDescription;
257}
258
259#endif
260