sw/source/core/access/accselectionhelper.cxx |   51 +++++++++++++++++++--------
 1 file changed, 36 insertions(+), 15 deletions(-)

New commits:
commit 65a5a4772af919e71a1fe7a752e622ec4cdba7ad
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Tue Jan 14 12:11:46 2025 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Wed Jan 15 10:10:12 2025 +0100

    tdf#163335 sw a11y: Handle cursor selection consistently
    
    Fix the issue described in more detail in
    
        Change-Id: I874ad4a7a9faf79492967c943517d83df5f47e75
        Author: Michael Weghorn <m.wegh...@posteo.de>
        Date:   Tue Jan 14 11:32:25 2025 +0100
    
            tdf#163335 qt a11y: Work around broken sw selection handling
    
    :
    
    > SwAccessibleSelectionHelper::getSelectedAccessibleChildCount
    > returns the count of paragraphs that are (partially) selected
    > by the text cursor if no other objects are selected.
    > However, SwAccessibleSelectionHelper::getSelectedAccessibleChild
    > doesn't take these into account and therefore throws the
    > IndexOutOfBoundsException when called with any index.
    
    Sample steps to reproduce with the gtk3 VCL plugin:
    
    1) have a Writer doc with 3 paragraphs
    2) in Accerciser, select the document in the a11y tree
    3) interactively query information in Accerciser's IPython
       console:
    
        In [5]: sel = acc.get_selection_iface()
        In [6]: sel.get_n_selected_children()
        Out[6]: 2
        In [7]: sel.get_selected_child(0)
        In [8]: sel.get_selected_child(1)
    
    The last 2 lines trigger
    
        ** (soffice:238967): WARNING **: 12:00:10.723: Exception in 
getSelectedAccessibleChild()
    
    in `selection_ref_selection`.
    
    Fix this by aligning the logic in
    SwAccessibleSelectionHelper::getSelectedAccessibleChild
    with the one in
    SwAccessibleSelectionHelper::getSelectedAccessibleChildCount
    and take the paragraphs that have cursor selection into account there
    as well.
    (This is also consistent with the fact that they have the SELECTED
    a11y state set.)
    
    With that in place, the selected paragraphs can now be queried
    in Accerciser:
    
        In [10]: sel = acc.get_selection_iface()
        In [11]: sel.get_n_selected_children()
        Out[11]: 2
        In [12]: sel.get_selected_child(0)
        Out[12]: <Atspi.Accessible object at 0x7f0405596a40 (AtspiAccessible at 
0x402b7380)>
        In [13]: sel.get_selected_child(1)
        Out[13]: <Atspi.Accessible object at 0x7f040557a100 (AtspiAccessible at 
0x402b86d0)>
        In [14]: sel.get_selected_child(1).role
        Out[14]: <enum ATSPI_ROLE_PARAGRAPH of type Atspi.Role>
    
    But querying an invalid index still gives a `None` result
    as expected:
    
        In [16]: sel.get_selected_child(2)
        In [17]: sel.get_selected_child(2) == None
        Out[17]: True
    
    (Behavior with qt6 VCL plugin is basically the same.)
    
    Change-Id: Ic8cf462ae19e494b899643db1709f5075460d287
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180231
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/sw/source/core/access/accselectionhelper.cxx 
b/sw/source/core/access/accselectionhelper.cxx
index 956ec75ccacd..0a77c606970d 100644
--- a/sw/source/core/access/accselectionhelper.cxx
+++ b/sw/source/core/access/accselectionhelper.cxx
@@ -274,27 +274,48 @@ Reference<XAccessible> 
SwAccessibleSelectionHelper::getSelectedAccessibleChild(
     }
     else
     {
+        std::list<SwAccessibleChild> aChildren;
+        m_rContext.GetChildren(*(m_rContext.GetMap()), aChildren);
+
         const size_t nSelObjs = pFEShell->GetSelectedObjCount();
-        if( 0 == nSelObjs || o3tl::make_unsigned(nSelectedChildIndex) >= 
nSelObjs )
-            throwIndexOutOfBoundsException();
+        if (nSelObjs > 0)
+        {
+            if (o3tl::make_unsigned(nSelectedChildIndex) >= nSelObjs)
+                throwIndexOutOfBoundsException();
 
-        std::list< SwAccessibleChild > aChildren;
-        m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+            for( const SwAccessibleChild& rChild : aChildren )
+            {
+                if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
+                    SwAccessibleFrame::GetParent(rChild, 
m_rContext.IsInPagePreview()) ==
+                        m_rContext.GetFrame() &&
+                    pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+                {
+                    if( 0 == nSelectedChildIndex )
+                        aChild = rChild;
+                    else
+                        --nSelectedChildIndex;
+                }
+                if (aChild.IsValid())
+                    break;
+            }
+        }
 
-        for( const SwAccessibleChild& rChild : aChildren )
+        // check children selected by the selection cursor
+        if (!aChild.IsValid())
         {
-            if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
-                SwAccessibleFrame::GetParent(rChild, 
m_rContext.IsInPagePreview()) ==
-                    m_rContext.GetFrame() &&
-                pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+            sal_Int64 nCount = 0;
+            for (const SwAccessibleChild& rChild : aChildren)
             {
-                if( 0 == nSelectedChildIndex )
-                    aChild = rChild;
-                else
-                    --nSelectedChildIndex;
+                if (lcl_getSelectedState(rChild, &m_rContext, 
m_rContext.GetMap()))
+                {
+                    if (nCount == nSelectedChildIndex)
+                    {
+                        aChild = rChild;
+                        break;
+                    }
+                    nCount++;
+                }
             }
-            if (aChild.IsValid())
-                break;
         }
     }
 

Reply via email to