Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/accessibility/accessiblelistbox.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <accessibility/accessiblelistbox.hxx>
21
#include <accessibility/accessiblelistboxentry.hxx>
22
#include <vcl/toolkit/treelistbox.hxx>
23
#include <vcl/toolkit/treelistentry.hxx>
24
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
25
#include <com/sun/star/accessibility/AccessibleRole.hpp>
26
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
27
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
28
#include <comphelper/accessiblecontexthelper.hxx>
29
30
31
// class AccessibleListBox -----------------------------------------------------
32
33
using namespace ::com::sun::star::accessibility;
34
using namespace ::com::sun::star::uno;
35
using namespace ::com::sun::star::lang;
36
using namespace ::com::sun::star;
37
38
39
// Ctor() and Dtor()
40
41
AccessibleListBox::AccessibleListBox(SvTreeListBox& _rListBox, const Reference< XAccessible >& _xParent)
42
0
    : ImplInheritanceHelper(&_rListBox),
43
0
    m_xParent( _xParent )
44
0
{
45
0
}
46
47
AccessibleListBox::~AccessibleListBox()
48
0
{
49
0
    if ( isAlive() )
50
0
    {
51
        // increment ref count to prevent double call of Dtor
52
0
        osl_atomic_increment( &m_refCount );
53
0
        dispose();
54
0
    }
55
0
}
56
57
void AccessibleListBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent )
58
0
{
59
0
    if ( !isAlive() )
60
0
        return;
61
62
0
    switch ( rVclWindowEvent.GetId() )
63
0
    {
64
0
    case  VclEventId::CheckboxToggle :
65
0
        {
66
0
            if ( !getListBox() || !getListBox()->HasFocus() )
67
0
            {
68
0
                return;
69
0
            }
70
0
            AccessibleListBoxEntry* pCurOpEntry = GetCurEventEntry(rVclWindowEvent);
71
0
            if(!pCurOpEntry)
72
0
            {
73
0
                return ;
74
0
            }
75
0
            uno::Any aValue;
76
0
            aValue <<= AccessibleStateType::CHECKED;
77
78
0
            if ( getListBox()->GetCheckButtonState( pCurOpEntry->GetSvLBoxEntry() ) == SvButtonState::Checked )
79
0
            {
80
0
                pCurOpEntry->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), aValue );
81
0
            }
82
0
            else
83
0
            {
84
0
                pCurOpEntry->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aValue,uno::Any() );
85
0
            }
86
0
            break;
87
0
        }
88
89
0
    case VclEventId::ListboxSelect :
90
0
        {
91
0
            OSL_FAIL("Debug: Treelist shouldn't use VclEventId::ListboxSelect");
92
0
            break;
93
0
        }
94
95
0
    case VclEventId::ListboxTreeSelect:
96
0
        {
97
0
            if ( getListBox() && getListBox()->HasFocus() )
98
0
            {
99
0
                if (m_xFocusedEntry.is())
100
0
                {
101
0
                    m_xFocusedEntry->NotifyAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, Any(), Any());
102
0
                }
103
0
            }
104
0
        }
105
0
        break;
106
0
    case VclEventId::ListboxTreeFocus:
107
0
        {
108
0
            VclPtr<SvTreeListBox> pBox = getListBox();
109
0
            if( pBox && pBox->HasFocus() )
110
0
            {
111
0
                uno::Any aNewValue;
112
0
                SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() );
113
0
                if ( pEntry )
114
0
                {
115
0
                    if (m_xFocusedEntry.is() && m_xFocusedEntry->GetSvLBoxEntry() == pEntry)
116
0
                    {
117
0
                        aNewValue <<= uno::Reference<XAccessible>(m_xFocusedEntry);;
118
0
                        NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, uno::Any(), aNewValue );
119
0
                        return ;
120
0
                    }
121
0
                    uno::Any aOldValue;
122
0
                    aOldValue <<= uno::Reference<XAccessible>(m_xFocusedEntry);;
123
124
0
                    m_xFocusedEntry = implGetAccessible(*pEntry);
125
126
0
                    aNewValue <<= uno::Reference<XAccessible>(m_xFocusedEntry);
127
0
                    NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue );
128
0
                }
129
0
                else
130
0
                {
131
0
                    aNewValue <<= AccessibleStateType::FOCUSED;
132
0
                    NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), aNewValue );
133
0
                }
134
0
            }
135
0
        }
136
0
        break;
137
0
    case VclEventId::ListboxItemRemoved:
138
0
        {
139
0
            SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() );
140
0
            if ( pEntry )
141
0
            {
142
0
                RemoveChildEntries(pEntry);
143
0
            }
144
0
            else
145
0
            {
146
                // NULL means Clear()
147
0
                for (auto const& entry : m_mapEntry)
148
0
                {
149
0
                    uno::Any aNewValue;
150
0
                    uno::Any aOldValue;
151
0
                    aOldValue <<= uno::Reference<XAccessible>(entry.second);
152
0
                    NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
153
0
                }
154
0
                for (auto const& entry : m_mapEntry)
155
0
                {   // release references ...
156
0
                    entry.second->dispose();
157
0
                }
158
0
                m_mapEntry.clear();
159
0
            }
160
0
        }
161
0
        break;
162
163
        // #i92103#
164
0
    case VclEventId::ItemExpanded :
165
0
    case VclEventId::ItemCollapsed :
166
0
        {
167
0
            SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() );
168
0
            if ( pEntry )
169
0
            {
170
0
                rtl::Reference<AccessibleListBoxEntry> const xChild(implGetAccessible(*pEntry));
171
0
                const short nAccEvent =
172
0
                        ( rVclWindowEvent.GetId() == VclEventId::ItemExpanded )
173
0
                        ? AccessibleEventId::LISTBOX_ENTRY_EXPANDED
174
0
                        : AccessibleEventId::LISTBOX_ENTRY_COLLAPSED;
175
0
                uno::Any aListBoxEntry;
176
0
                aListBoxEntry <<= Reference<XAccessible>(xChild);
177
0
                NotifyAccessibleEvent( nAccEvent, Any(), aListBoxEntry );
178
0
                if ( getListBox() && getListBox()->HasFocus() )
179
0
                {
180
0
                    NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), aListBoxEntry );
181
0
                }
182
0
            }
183
0
        }
184
0
        break;
185
0
    default:
186
0
        VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent);
187
0
    }
188
0
}
189
190
AccessibleListBoxEntry* AccessibleListBox::GetCurEventEntry( const VclWindowEvent& rVclWindowEvent )
191
0
{
192
0
    SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() );
193
0
    if ( !pEntry )
194
0
        pEntry = getListBox()->GetCurEntry();
195
196
0
    if (m_xFocusedEntry.is() && pEntry && pEntry != m_xFocusedEntry->GetSvLBoxEntry())
197
0
    {
198
0
        AccessibleListBoxEntry *const pAccCurOptionEntry = implGetAccessible(*pEntry).get();
199
0
        uno::Any aNewValue;
200
0
        aNewValue <<= uno::Reference<XAccessible>(pAccCurOptionEntry);
201
0
        NotifyAccessibleEvent( AccessibleEventId::CHILD, uno::Any(), aNewValue );//Add
202
203
0
        return pAccCurOptionEntry;
204
0
    }
205
0
    else
206
0
    {
207
0
        return m_xFocusedEntry.get();
208
0
    }
209
0
}
210
211
void AccessibleListBox::RemoveChildEntries(SvTreeListEntry* pEntry)
212
0
{
213
0
    MAP_ENTRY::iterator mi = m_mapEntry.find(pEntry);
214
0
    if ( mi != m_mapEntry.end() )
215
0
    {
216
0
        uno::Any aNewValue;
217
0
        uno::Any aOldValue;
218
0
        aOldValue <<= uno::Reference<XAccessible>(mi->second);
219
0
        NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
220
221
0
        m_mapEntry.erase(mi);
222
0
    }
223
224
0
    VclPtr<SvTreeListBox> pBox = getListBox();
225
0
    SvTreeListEntry* pEntryChild = pBox->FirstChild(pEntry);
226
0
    while (pEntryChild)
227
0
    {
228
0
        RemoveChildEntries(pEntryChild);
229
0
        pEntryChild = pEntryChild->NextSibling();
230
0
    }
231
0
}
232
233
// XComponent
234
235
void SAL_CALL AccessibleListBox::disposing()
236
0
{
237
0
    ::osl::MutexGuard aGuard( m_aMutex );
238
239
0
    m_mapEntry.clear();
240
0
    VCLXAccessibleComponent::disposing();
241
0
    m_xParent = nullptr;
242
0
}
243
244
// XServiceInfo
245
246
OUString SAL_CALL AccessibleListBox::getImplementationName()
247
0
{
248
0
    return u"com.sun.star.comp.svtools.AccessibleTreeListBox"_ustr;
249
0
}
250
251
Sequence< OUString > SAL_CALL AccessibleListBox::getSupportedServiceNames()
252
0
{
253
0
    return {u"com.sun.star.accessibility.AccessibleContext"_ustr,
254
0
            u"com.sun.star.accessibility.AccessibleComponent"_ustr,
255
0
            u"com.sun.star.awt.AccessibleTreeListBox"_ustr};
256
0
}
257
258
// XAccessibleContext
259
260
sal_Int64 SAL_CALL AccessibleListBox::getAccessibleChildCount(  )
261
0
{
262
0
    ::comphelper::OExternalLockGuard aGuard( this );
263
264
0
    sal_Int32 nCount = 0;
265
0
    VclPtr<SvTreeListBox> pSvTreeListBox = getListBox();
266
0
    if ( pSvTreeListBox )
267
0
        nCount = pSvTreeListBox->GetLevelChildCount( nullptr );
268
269
0
    return nCount;
270
0
}
271
272
Reference< XAccessible > SAL_CALL AccessibleListBox::getAccessibleChild( sal_Int64 i )
273
0
{
274
0
    ::comphelper::OExternalLockGuard aGuard( this );
275
276
0
    SvTreeListEntry* pEntry = getListBox()->GetEntry(i);
277
0
    if ( !pEntry )
278
0
        throw IndexOutOfBoundsException();
279
280
    // Solution: Set the parameter of the parent to null to let entry determine the parent by itself
281
    //return new AccessibleListBoxEntry( *getListBox(), pEntry, this );
282
    //return new AccessibleListBoxEntry( *getListBox(), pEntry, nullptr );
283
0
    return implGetAccessible(*pEntry);
284
0
}
285
286
Reference< XAccessible > SAL_CALL AccessibleListBox::getAccessibleParent(  )
287
0
{
288
0
    ::osl::MutexGuard aGuard( m_aMutex );
289
290
0
    ensureAlive();
291
0
    return m_xParent;
292
0
}
293
294
sal_Int32 AccessibleListBox::GetRoleType() const
295
0
{
296
0
    sal_Int32 nCase = 0;
297
0
    SvTreeListEntry* pEntry = getListBox()->GetEntry(0);
298
0
    if ( pEntry )
299
0
    {
300
0
        if( pEntry->HasChildrenOnDemand() || getListBox()->GetChildCount(pEntry) > 0  )
301
0
        {
302
0
            nCase = 1;
303
0
            return nCase;
304
0
        }
305
0
    }
306
307
0
    bool bHasButtons = (getListBox()->GetStyle() & WB_HASBUTTONS)!=0;
308
0
    if( !(getListBox()->GetTreeFlags() & SvTreeFlags::CHKBTN) )
309
0
    {
310
0
        if( bHasButtons )
311
0
            nCase = 1;
312
0
    }
313
0
    else
314
0
    {
315
0
        if( bHasButtons )
316
0
            nCase = 2;
317
0
        else
318
0
            nCase = 3;
319
0
    }
320
0
    return nCase;
321
0
}
322
323
sal_Int16 SAL_CALL AccessibleListBox::getAccessibleRole()
324
0
{
325
0
    ::comphelper::OExternalLockGuard aGuard( this );
326
327
0
    VclPtr<SvTreeListBox> pListBox = getListBox();
328
0
    if (!pListBox)
329
0
        return AccessibleRole::LIST;
330
331
    //o is: return AccessibleRole::TREE;
332
0
    bool bHasButtons = (pListBox->GetStyle() & WB_HASBUTTONS) != 0;
333
0
    if (!bHasButtons && (pListBox->GetTreeFlags() & SvTreeFlags::CHKBTN))
334
0
        return AccessibleRole::LIST;
335
0
    else
336
0
        if (GetRoleType() == 0)
337
0
            return AccessibleRole::LIST;
338
0
        else
339
0
        return AccessibleRole::TREE;
340
0
}
341
342
OUString SAL_CALL AccessibleListBox::getAccessibleDescription(  )
343
0
{
344
0
    ::comphelper::OExternalLockGuard aGuard( this );
345
346
0
    return getListBox()->GetAccessibleDescription();
347
0
}
348
349
OUString SAL_CALL AccessibleListBox::getAccessibleName(  )
350
0
{
351
0
    ::comphelper::OExternalLockGuard aGuard( this );
352
353
0
    return getListBox()->GetAccessibleName();
354
0
}
355
356
// XAccessibleSelection
357
358
void SAL_CALL AccessibleListBox::selectAccessibleChild( sal_Int64 nChildIndex )
359
0
{
360
0
    ::comphelper::OExternalLockGuard aGuard( this );
361
362
0
    SvTreeListEntry* pEntry = getListBox()->GetEntry( nChildIndex );
363
0
    if ( !pEntry )
364
0
        throw IndexOutOfBoundsException();
365
366
0
    getListBox()->Select( pEntry );
367
0
}
368
369
sal_Bool SAL_CALL AccessibleListBox::isAccessibleChildSelected( sal_Int64 nChildIndex )
370
0
{
371
0
    ::comphelper::OExternalLockGuard aGuard( this );
372
373
0
    if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
374
0
        throw IndexOutOfBoundsException();
375
376
0
    SvTreeListEntry* pEntry = getListBox()->GetEntry( nChildIndex );
377
0
    if ( !pEntry )
378
0
        throw IndexOutOfBoundsException();
379
380
0
    return getListBox()->IsSelected( pEntry );
381
0
}
382
383
void SAL_CALL AccessibleListBox::clearAccessibleSelection(  )
384
0
{
385
0
    ::comphelper::OExternalLockGuard aGuard( this );
386
387
0
    sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr );
388
0
    for ( sal_Int32 i = 0; i < nCount; ++i )
389
0
    {
390
0
        SvTreeListEntry* pEntry = getListBox()->GetEntry( i );
391
0
        if ( getListBox()->IsSelected( pEntry ) )
392
0
            getListBox()->Select( pEntry, false );
393
0
    }
394
0
}
395
396
void SAL_CALL AccessibleListBox::selectAllAccessibleChildren(  )
397
0
{
398
0
    ::comphelper::OExternalLockGuard aGuard( this );
399
400
0
    sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr );
401
0
    for ( sal_Int32 i = 0; i < nCount; ++i )
402
0
    {
403
0
        SvTreeListEntry* pEntry = getListBox()->GetEntry( i );
404
0
        if ( !getListBox()->IsSelected( pEntry ) )
405
0
            getListBox()->Select( pEntry );
406
0
    }
407
0
}
408
409
sal_Int64 SAL_CALL AccessibleListBox::getSelectedAccessibleChildCount(  )
410
0
{
411
0
    ::comphelper::OExternalLockGuard aGuard( this );
412
413
0
    return getListBox()->GetSelectionCount();
414
0
}
415
416
Reference< XAccessible > SAL_CALL AccessibleListBox::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
417
0
{
418
0
    ::comphelper::OExternalLockGuard aGuard( this );
419
420
0
    if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() )
421
0
        throw IndexOutOfBoundsException();
422
423
0
    Reference< XAccessible > xChild;
424
0
    sal_Int64 nSelCount= 0;
425
0
    sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr );
426
0
    for ( sal_Int32 i = 0; i < nCount; ++i )
427
0
    {
428
0
        SvTreeListEntry* pEntry = getListBox()->GetEntry( i );
429
0
        if ( getListBox()->IsSelected( pEntry ) )
430
0
            ++nSelCount;
431
432
0
        if ( nSelCount == ( nSelectedChildIndex + 1 ) )
433
0
        {
434
            // Solution: Set the parameter of the parent to null to let entry determine the parent by itself
435
            //xChild = new AccessibleListBoxEntry( *getListBox(), pEntry, this );
436
            //xChild = new AccessibleListBoxEntry( *getListBox(), pEntry, nullptr );
437
0
            xChild = implGetAccessible(*pEntry).get();
438
0
            break;
439
0
        }
440
0
    }
441
442
0
    return xChild;
443
0
}
444
445
void SAL_CALL AccessibleListBox::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
446
0
{
447
0
    ::comphelper::OExternalLockGuard aGuard( this );
448
449
0
    SvTreeListEntry* pEntry = getListBox()->GetEntry( nSelectedChildIndex );
450
0
    if ( !pEntry )
451
0
        throw IndexOutOfBoundsException();
452
453
0
    getListBox()->Select( pEntry, false );
454
0
}
455
456
void AccessibleListBox::FillAccessibleStateSet( sal_Int64& rStateSet )
457
0
{
458
0
    VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
459
0
    if ( getListBox() && isAlive() )
460
0
    {
461
0
        rStateSet |= AccessibleStateType::FOCUSABLE;
462
0
        rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
463
0
        if ( getListBox()->GetSelectionMode() == SelectionMode::Multiple )
464
0
            rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
465
0
    }
466
0
}
467
468
rtl::Reference<AccessibleListBoxEntry> AccessibleListBox::implGetAccessible(SvTreeListEntry & rEntry)
469
0
{
470
0
    rtl::Reference<AccessibleListBoxEntry> pAccessible;
471
0
    auto const it = m_mapEntry.find(&rEntry);
472
0
    if (it != m_mapEntry.end())
473
0
    {
474
0
        pAccessible = it->second;
475
0
    }
476
0
    else
477
0
    {
478
0
        pAccessible = new AccessibleListBoxEntry(*getListBox(), rEntry, *this);
479
0
        m_mapEntry.emplace(&rEntry, pAccessible);
480
0
    }
481
0
    assert(pAccessible.is());
482
0
    return pAccessible;
483
0
}
484
485
VclPtr< SvTreeListBox > AccessibleListBox::getListBox() const
486
0
{
487
0
    return GetAs< SvTreeListBox >();
488
0
}
489
490
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */