vcl/Library_vclplug_gtk3.mk                       |    1 
 vcl/Library_vclplug_gtk3_kde5.mk                  |    1 
 vcl/unx/gtk3/a11y/atktablecell.cxx                |  269 ++++++++++++++++++++++
 vcl/unx/gtk3/a11y/atkwrapper.cxx                  |   56 ++++
 vcl/unx/gtk3/a11y/atkwrapper.hxx                  |    1 
 vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktablecell.cxx |   12 
 6 files changed, 339 insertions(+), 1 deletion(-)

New commits:
commit dd5f021e08b0f6da1b5330c37db4c759e35941c6
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Sat Sep 3 00:22:42 2022 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Sep 3 23:17:52 2022 +0200

    tdf#150683 gtk3 a11y: Expose AtkTableCell interface for cells
    
    While LO doesn't have any UNO a11y interface
    for table cells, the methods for the `AtkTableCell`
    interface can be implemented by using the methods
    from the `XAccessibleTable` interface of the table
    (which is the parent of the cell), similar to
    the way this was impemented for winaccessibility
    and the qt5/qt6/kf5 VCL plugins in
    
        commit 97a88e30e2e084ab860635ff4e0a03442d8a12af
        Author: Michael Weghorn <m.wegh...@posteo.de>
        Date:   Wed Sep 8 14:37:53 2021 +0100
    
            tdf#100086 tdf#124832 wina11y: Implement IAccessibleTableCell
    
    and
    
        commit 6735a37747a3443ebd1c29c870a5eb26990350f2
        Author: Michael Weghorn <m.wegh...@posteo.de>
        Date:   Tue Sep 21 14:47:51 2021 +0200
    
            qt5 a11y: Implement QAccessibleTableCellInterface
    
    With this and the Orca commit to make use of the AT-SPI
    TableCell interface to query the position for a cell [1],
    announcing selected cells that have a cell/child index
    that doesn't fit into 32 bit now also works for the
    gtk3 VCL plugin, see the full commit message of
    
        commit 206543c7bef58fc559852553a3b2faba0b604259
        Author: Michael Weghorn <m.wegh...@posteo.de>
        Date:   Fri Sep 2 13:06:08 2022 +0200
    
            [API CHANGE] tdf#150683 a11y: Switch a11y child index to 64 bit
    
    for more background information.
    
    In a quick test with Accerciser and the gtk3 VCL plugin
    as follows, the output looked as expected:
    
    1) start LO Writer, "Table" -> "Insert Table"
    2) select to create table with  2 rows, 2 columns
    3) make sure "Heading" is checked, "Heading rows": 1
    4) "Insert"
    5) in the first row, type "First heading" into first column,
       "Second heading" into second column
    5) start Accerciser
    7) select the table cell element "A2" in Accerciser's treeview of the a11y
       hierarchy (which refers to the table cell in the second row, first 
column)
    8) type these in Accerciser's IPython console:
    
        In [1]: acc.queryTableCell()
        Out[1]: <pyatspi.tablecell.TableCell at 0x7fa17cf2b550>
        In [2]: acc.queryTableCell().position
        Out[2]: (1, row=1, column=0)
        In [3]: acc.queryTableCell().rowSpan
        Out[3]: 1
        In [4]: acc.queryTableCell().columnSpan
        Out[4]: 1
        In [5]: acc.queryTableCell().get_table()
        Out[5]: <Atspi.Accessible object at 0x7fa17cf975c0 (AtspiAccessible at 
0x55bfe77b4e10)>
        In [6]: acc.queryTableCell().get_table().name
        Out[6]: 'Table1-1'
        In [7]: acc.queryTableCell().get_columnHeaderCells()[0]
        Out[7]: <Atspi.Accessible object at 0x7fa17d2e7b00 (AtspiAccessible at 
0x55bfe77ab240)>
        In [8]: acc.queryTableCell().get_columnHeaderCells()[0].name
        Out[8]: 'A1'
    
    [1] 
https://gitlab.gnome.org/GNOME/orca/-/commit/cb105d4d21c09d3e832273b493a407e8b48887fb
    
    Change-Id: Ia6ee90bea5c2f2faef6ed269981702f36496a3e8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139278
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/Library_vclplug_gtk3.mk b/vcl/Library_vclplug_gtk3.mk
index 702dedc3b43a..0809edd770a7 100644
--- a/vcl/Library_vclplug_gtk3.mk
+++ b/vcl/Library_vclplug_gtk3.mk
@@ -92,6 +92,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk3,\
     vcl/unx/gtk3/a11y/atkregistry \
     vcl/unx/gtk3/a11y/atkselection \
     vcl/unx/gtk3/a11y/atktable \
+    vcl/unx/gtk3/a11y/atktablecell \
     vcl/unx/gtk3/a11y/atktextattributes \
     vcl/unx/gtk3/a11y/atktext \
     vcl/unx/gtk3/a11y/atkutil \
diff --git a/vcl/Library_vclplug_gtk3_kde5.mk b/vcl/Library_vclplug_gtk3_kde5.mk
index 7ee7f11b088a..853ec7aafa4e 100644
--- a/vcl/Library_vclplug_gtk3_kde5.mk
+++ b/vcl/Library_vclplug_gtk3_kde5.mk
@@ -98,6 +98,7 @@ $(eval $(call 
gb_Library_add_exception_objects,vclplug_gtk3_kde5,\
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkregistry \
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkselection \
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktable \
+       vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktablecell \
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktextattributes \
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktext \
        vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atkutil \
diff --git a/vcl/unx/gtk3/a11y/atktablecell.cxx 
b/vcl/unx/gtk3/a11y/atktablecell.cxx
new file mode 100644
index 000000000000..35d681b0628c
--- /dev/null
+++ b/vcl/unx/gtk3/a11y/atktablecell.cxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "atkwrapper.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
+#include <sal/log.hxx>
+
+static css::uno::Reference<css::accessibility::XAccessibleContext>
+getContext(AtkTableCell* pTableCell)
+{
+    AtkObjectWrapper* pWrap = ATK_OBJECT_WRAPPER(pTableCell);
+    if (pWrap)
+    {
+        return pWrap->mpContext;
+    }
+
+    return css::uno::Reference<css::accessibility::XAccessibleContext>();
+}
+
+static css::uno::Reference<css::accessibility::XAccessibleTable>
+getTableParent(AtkTableCell* pTableCell)
+{
+    AtkObject* pParent = atk_object_get_parent(ATK_OBJECT(pTableCell));
+    if (!pParent)
+        return css::uno::Reference<css::accessibility::XAccessibleTable>();
+
+    AtkObjectWrapper* pWrap = ATK_OBJECT_WRAPPER(pParent);
+    if (pWrap)
+    {
+        if (!pWrap->mpTable.is())
+        {
+            pWrap->mpTable.set(pWrap->mpContext, css::uno::UNO_QUERY);
+        }
+
+        return pWrap->mpTable;
+    }
+
+    return css::uno::Reference<css::accessibility::XAccessibleTable>();
+}
+
+extern "C" {
+
+static int tablecell_wrapper_get_column_span(AtkTableCell* cell)
+{
+    int nColumnExtent = -1;
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return -1;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            const sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+            const sal_Int32 nColumn = xTable->getAccessibleColumn(nChildIndex);
+            nColumnExtent = xTable->getAccessibleColumnExtentAt(nRow, nColumn);
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_column_span");
+    }
+
+    return nColumnExtent;
+}
+
+static GPtrArray* tablecell_wrapper_get_column_header_cells(AtkTableCell* cell)
+{
+    GPtrArray* pHeaderCells = g_ptr_array_new();
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return pHeaderCells;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            const sal_Int32 nCol = xTable->getAccessibleColumn(nChildIndex);
+            css::uno::Reference<css::accessibility::XAccessibleTable> xHeaders
+                = xTable->getAccessibleColumnHeaders();
+            if (!xHeaders.is())
+                return pHeaderCells;
+
+            for (sal_Int32 nRow = 0; nRow < xHeaders->getAccessibleRowCount(); 
nRow++)
+            {
+                css::uno::Reference<css::accessibility::XAccessible> xCell
+                    = xHeaders->getAccessibleCellAt(nRow, nCol);
+                AtkObject* pCell = atk_object_wrapper_ref(xCell);
+                g_ptr_array_add(pHeaderCells, pCell);
+            }
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_column_header_cells");
+    }
+
+    return pHeaderCells;
+}
+
+static gboolean tablecell_wrapper_get_position(AtkTableCell* cell, gint* row, 
gint* column)
+{
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return false;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            *row = xTable->getAccessibleRow(nChildIndex);
+            *column = xTable->getAccessibleColumn(nChildIndex);
+            return true;
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_position()");
+    }
+
+    return false;
+}
+
+static gint tablecell_wrapper_get_row_span(AtkTableCell* cell)
+{
+    int nRowExtent = -1;
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return -1;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            const sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+            const sal_Int32 nColumn = xTable->getAccessibleColumn(nChildIndex);
+            nRowExtent = xTable->getAccessibleRowExtentAt(nRow, nColumn);
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_row_span");
+    }
+
+    return nRowExtent;
+}
+
+static GPtrArray* tablecell_wrapper_get_row_header_cells(AtkTableCell* cell)
+{
+    GPtrArray* pHeaderCells = g_ptr_array_new();
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return pHeaderCells;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            const sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+            css::uno::Reference<css::accessibility::XAccessibleTable> xHeaders
+                = xTable->getAccessibleRowHeaders();
+            if (!xHeaders.is())
+                return pHeaderCells;
+
+            for (sal_Int32 nCol = 0; nCol < 
xHeaders->getAccessibleColumnCount(); nCol++)
+            {
+                css::uno::Reference<css::accessibility::XAccessible> xCell
+                    = xHeaders->getAccessibleCellAt(nRow, nCol);
+                AtkObject* pCell = atk_object_wrapper_ref(xCell);
+                g_ptr_array_add(pHeaderCells, pCell);
+            }
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_row_header_cells");
+    }
+
+    return pHeaderCells;
+}
+
+static gboolean tablecell_wrapper_get_row_column_span(AtkTableCell* cell, 
gint* row, gint* column,
+                                                      gint* row_span, gint* 
column_span)
+{
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return -1;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> xTable = 
getTableParent(cell);
+        if (xTable.is())
+        {
+            const sal_Int64 nChildIndex = 
xContext->getAccessibleIndexInParent();
+            const sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+            const sal_Int32 nColumn = xTable->getAccessibleColumn(nChildIndex);
+            *row = nRow;
+            *column = nColumn;
+            *row_span = xTable->getAccessibleRowExtentAt(nRow, nColumn);
+            *column_span = xTable->getAccessibleColumnExtentAt(nRow, nColumn);
+            return true;
+        }
+    }
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_row_column_span");
+    }
+
+    return false;
+}
+
+static AtkObject* tablecell_wrapper_get_table(AtkTableCell* cell)
+{
+    try
+    {
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
getContext(cell);
+        if (!xContext.is())
+            return nullptr;
+
+        css::uno::Reference<css::accessibility::XAccessible> xParent
+            = getContext(cell)->getAccessibleParent();
+        if (!xParent.is())
+            return nullptr;
+
+        return atk_object_wrapper_ref(xParent);
+    }
+
+    catch (const css::uno::Exception&)
+    {
+        g_warning("Exception in tablecell_wrapper_get_table()");
+    }
+
+    return nullptr;
+}
+
+} // extern "C"
+
+void tablecellIfaceInit(AtkTableCellIface* iface)
+{
+    g_return_if_fail(iface != nullptr);
+
+    iface->get_column_span = tablecell_wrapper_get_column_span;
+    iface->get_column_header_cells = tablecell_wrapper_get_column_header_cells;
+    iface->get_position = tablecell_wrapper_get_position;
+    iface->get_row_span = tablecell_wrapper_get_row_span;
+    iface->get_row_header_cells = tablecell_wrapper_get_row_header_cells;
+    iface->get_row_column_span = tablecell_wrapper_get_row_column_span;
+    iface->get_table = tablecell_wrapper_get_table;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk3/a11y/atkwrapper.cxx b/vcl/unx/gtk3/a11y/atkwrapper.cxx
index e2aa2c210dd5..8159bb119e5f 100644
--- a/vcl/unx/gtk3/a11y/atkwrapper.cxx
+++ b/vcl/unx/gtk3/a11y/atkwrapper.cxx
@@ -750,6 +750,44 @@ isOfType( uno::XInterface *pInterface, const uno::Type & 
rType )
     return bIs;
 }
 
+// Whether AtkTableCell can be supported for the interface.
+// Returns true if the corresponding XAccessible has role TABLE_CELL
+// and an XAccessibleTable as parent.
+static bool isTableCell(uno::XInterface* pInterface)
+{
+    g_return_val_if_fail(pInterface != nullptr, false);
+
+    try {
+        auto aType = 
cppu::UnoType<accessibility::XAccessible>::get().getTypeLibType();
+        uno::Any aAcc = pInterface->queryInterface(aType);
+
+        css::uno::Reference<css::accessibility::XAccessible> xAcc;
+        aAcc >>= xAcc;
+        if (!xAcc.is())
+            return false;
+
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext = 
xAcc->getAccessibleContext();
+        if (!xContext.is() || !(xContext->getAccessibleRole() == 
accessibility::AccessibleRole::TABLE_CELL))
+            return false;
+
+        css::uno::Reference<css::accessibility::XAccessible> xParent = 
xContext->getAccessibleParent();
+        if (!xParent.is())
+            return false;
+        css::uno::Reference<css::accessibility::XAccessibleContext> 
xParentContext = xParent->getAccessibleContext();
+        if (!xParentContext.is())
+            return false;
+
+        css::uno::Reference<css::accessibility::XAccessibleTable> 
xTable(xParentContext, uno::UNO_QUERY);
+        return xTable.is();
+    }
+    catch(const uno::Exception &)
+    {
+        g_warning("Exception in isTableCell()");
+    }
+
+    return false;
+}
+
 extern "C" {
 typedef  GType (* GetGIfaceType ) ();
 }
@@ -785,6 +823,12 @@ const struct {
         atk_table_get_type,
         cppu::UnoType<accessibility::XAccessibleTable>::get
     },
+    {
+        "Cell",  reinterpret_cast<GInterfaceInitFunc>(tablecellIfaceInit),
+        atk_table_cell_get_type,
+        // there is no UNO a11y interface for table cells, so this case is 
handled separately below
+        nullptr
+    },
     {
         "Edt",  reinterpret_cast<GInterfaceInitFunc>(editableTextIfaceInit),
         atk_editable_text_get_type,
@@ -820,7 +864,17 @@ ensureTypeFor( uno::XInterface *pAccessible )
 
     for( i = 0; i < aTypeTableSize; i++ )
     {
-        if( isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) )
+        if(!g_strcmp0(aTypeTable[i].name, "Cell"))
+        {
+            // there is no UNO interface for table cells, but AtkTableCell can 
be supported
+            // for table cells via the methods of the parent that is a table
+            if (isTableCell(pAccessible))
+            {
+                aTypeNameBuf.append(aTypeTable[i].name);
+                bTypes[i] = true;
+            }
+        }
+        else if (isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) )
         {
             aTypeNameBuf.append(aTypeTable[i].name);
             bTypes[i] = true;
diff --git a/vcl/unx/gtk3/a11y/atkwrapper.hxx b/vcl/unx/gtk3/a11y/atkwrapper.hxx
index e43172f68ac1..911433fc8a6f 100644
--- a/vcl/unx/gtk3/a11y/atkwrapper.hxx
+++ b/vcl/unx/gtk3/a11y/atkwrapper.hxx
@@ -108,6 +108,7 @@ void                   hypertextIfaceInit(AtkHypertextIface 
*iface);
 void                   imageIfaceInit(AtkImageIface *iface);
 void                   selectionIfaceInit(AtkSelectionIface *iface);
 void                   tableIfaceInit(AtkTableIface *iface);
+void                   tablecellIfaceInit(AtkTableCellIface *iface);
 void                   textIfaceInit(AtkTextIface *iface);
 void                   valueIfaceInit(AtkValueIface *iface);
 
diff --git a/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktablecell.cxx 
b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktablecell.cxx
new file mode 100644
index 000000000000..fdbb6f62633d
--- /dev/null
+++ b/vcl/unx/gtk3_kde5/a11y/gtk3_kde5_atktablecell.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "../../gtk3/a11y/atktablecell.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to