Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/accessibility/vclxaccessiblemenuitem.cxx
Line
Count
Source
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/vclxaccessiblemenuitem.hxx>
21
#include <comphelper/accessiblecontexthelper.hxx>
22
23
#include <comphelper/accessiblekeybindinghelper.hxx>
24
#include <com/sun/star/awt/KeyModifier.hpp>
25
26
#include <com/sun/star/accessibility/AccessibleRole.hpp>
27
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
28
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
29
#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
30
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
31
#include <comphelper/sequence.hxx>
32
#include <i18nlangtag/languagetag.hxx>
33
#include <vcl/accessibility/characterattributeshelper.hxx>
34
#include <vcl/accessibility/strings.hxx>
35
#include <vcl/event.hxx>
36
#include <vcl/svapp.hxx>
37
#include <vcl/window.hxx>
38
#include <vcl/menu.hxx>
39
#include <vcl/unohelp.hxx>
40
#include <vcl/unohelp2.hxx>
41
#include <vcl/settings.hxx>
42
43
using namespace ::com::sun::star::accessibility;
44
using namespace ::com::sun::star::uno;
45
using namespace ::com::sun::star::beans;
46
using namespace ::com::sun::star::lang;
47
using namespace ::com::sun::star;
48
using namespace ::comphelper;
49
50
51
52
53
VCLXAccessibleMenuItem::VCLXAccessibleMenuItem( Menu* pParent, sal_uInt16 nItemPos, Menu* pMenu )
54
0
    :ImplInheritanceHelper( pParent, nItemPos, pMenu )
55
0
{
56
0
}
57
58
59
bool VCLXAccessibleMenuItem::IsFocused()
60
0
{
61
0
    return IsHighlighted();
62
0
}
63
64
65
bool VCLXAccessibleMenuItem::IsSelected()
66
0
{
67
0
    return IsHighlighted();
68
0
}
69
70
bool VCLXAccessibleMenuItem::IsCheckable()
71
0
{
72
0
    if (!m_pParent)
73
0
        return false;
74
75
0
    const sal_uInt16 nItemId = m_pParent->GetItemId(m_nItemPos);
76
0
    return m_pParent->IsItemCheckable(nItemId);
77
0
}
78
79
bool VCLXAccessibleMenuItem::IsChecked()
80
0
{
81
0
    bool bChecked = false;
82
83
0
    if ( m_pParent )
84
0
    {
85
0
        sal_uInt16 nItemId = m_pParent->GetItemId( m_nItemPos );
86
0
        if ( m_pParent->IsItemChecked( nItemId ) )
87
0
            bChecked = true;
88
0
    }
89
90
0
    return bChecked;
91
0
}
92
93
94
bool VCLXAccessibleMenuItem::IsHighlighted()
95
0
{
96
0
    bool bHighlighted = false;
97
98
0
    if ( m_pParent && m_pParent->IsHighlighted( m_nItemPos ) )
99
0
        bHighlighted = true;
100
101
0
    return bHighlighted;
102
0
}
103
104
105
void VCLXAccessibleMenuItem::FillAccessibleStateSet( sal_Int64& rStateSet )
106
0
{
107
0
    OAccessibleMenuItemComponent::FillAccessibleStateSet( rStateSet );
108
109
0
    rStateSet |= AccessibleStateType::FOCUSABLE;
110
111
0
    if ( IsFocused() )
112
0
        rStateSet |= AccessibleStateType::FOCUSED;
113
114
0
    rStateSet |= AccessibleStateType::SELECTABLE;
115
116
0
    if ( IsSelected() )
117
0
        rStateSet |= AccessibleStateType::SELECTED;
118
119
0
    if (IsCheckable())
120
0
        rStateSet |= AccessibleStateType::CHECKABLE;
121
0
    if ( IsChecked() )
122
0
        rStateSet |= AccessibleStateType::CHECKED;
123
0
}
124
125
126
// OCommonAccessibleText
127
128
129
OUString VCLXAccessibleMenuItem::implGetText()
130
0
{
131
0
    return m_sItemText;
132
0
}
133
134
135
Locale VCLXAccessibleMenuItem::implGetLocale()
136
0
{
137
0
    return Application::GetSettings().GetLanguageTag().getLocale();
138
0
}
139
140
141
void VCLXAccessibleMenuItem::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex )
142
0
{
143
0
    nStartIndex = 0;
144
0
    nEndIndex = 0;
145
0
}
146
147
// XAccessibleContext
148
149
150
sal_Int16 VCLXAccessibleMenuItem::getAccessibleRole(  )
151
0
{
152
0
    OExternalLockGuard aGuard( this );
153
    // IA2 CWS. MT: We had the additional roles in UAA for ever, but never used them anywhere.
154
    // Looks reasonable, but need to verify in Orca and VoiceOver.
155
0
    sal_Int16 nRole = AccessibleRole::MENU_ITEM;
156
0
    if ( m_pParent )
157
0
    {
158
0
        sal_uInt16 nItemId = m_pParent->GetItemId( m_nItemPos );
159
0
        MenuItemBits nItemBits = m_pParent->GetItemBits(nItemId);
160
0
        if(  nItemBits & MenuItemBits::RADIOCHECK)
161
0
            nRole = AccessibleRole::RADIO_MENU_ITEM;
162
0
        else if( nItemBits & MenuItemBits::CHECKABLE)
163
0
            nRole = AccessibleRole::CHECK_MENU_ITEM;
164
0
    }
165
0
    return nRole;
166
0
}
167
168
169
// XAccessibleText
170
171
172
sal_Int32 VCLXAccessibleMenuItem::getCaretPosition()
173
0
{
174
0
    return -1;
175
0
}
176
177
178
sal_Bool VCLXAccessibleMenuItem::setCaretPosition( sal_Int32 nIndex )
179
0
{
180
181
0
    OExternalLockGuard aGuard( this );
182
183
0
    if ( !implIsValidRange( nIndex, nIndex, m_sItemText.getLength() ) )
184
0
        throw IndexOutOfBoundsException();
185
186
0
    return false;
187
0
}
188
189
190
sal_Unicode VCLXAccessibleMenuItem::getCharacter( sal_Int32 nIndex )
191
0
{
192
0
    OExternalLockGuard aGuard( this );
193
194
0
    return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex );
195
0
}
196
197
198
Sequence< PropertyValue > VCLXAccessibleMenuItem::getCharacterAttributes( sal_Int32 nIndex, const Sequence< OUString >& aRequestedAttributes )
199
0
{
200
0
    OExternalLockGuard aGuard( this );
201
202
0
    if ( !implIsValidIndex( nIndex, m_sItemText.getLength() ) )
203
0
        throw IndexOutOfBoundsException();
204
205
0
    vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetMenuFont();
206
0
    sal_Int32 nBackColor = getBackground();
207
0
    sal_Int32 nColor = getForeground();
208
0
    return CharacterAttributesHelper( aFont, nBackColor, nColor )
209
0
        .GetCharacterAttributes( aRequestedAttributes );
210
0
}
211
212
213
awt::Rectangle VCLXAccessibleMenuItem::getCharacterBounds( sal_Int32 nIndex )
214
0
{
215
0
    OExternalLockGuard aGuard( this );
216
217
0
    if ( !implIsValidIndex( nIndex, m_sItemText.getLength() ) )
218
0
        throw IndexOutOfBoundsException();
219
220
0
    awt::Rectangle aBounds( 0, 0, 0, 0 );
221
0
    if ( m_pParent )
222
0
    {
223
0
        sal_uInt16 nItemId = m_pParent->GetItemId( m_nItemPos );
224
0
        tools::Rectangle aItemRect = m_pParent->GetBoundingRectangle( m_nItemPos );
225
0
        tools::Rectangle aCharRect = m_pParent->GetCharacterBounds( nItemId, nIndex );
226
0
        aCharRect.Move( -aItemRect.Left(), -aItemRect.Top() );
227
0
        aBounds = vcl::unohelper::ConvertToAWTRect(aCharRect);
228
0
    }
229
230
0
    return aBounds;
231
0
}
232
233
234
sal_Int32 VCLXAccessibleMenuItem::getCharacterCount()
235
0
{
236
0
    OExternalLockGuard aGuard( this );
237
238
0
    return m_sItemText.getLength();
239
0
}
240
241
242
sal_Int32 VCLXAccessibleMenuItem::getIndexAtPoint( const awt::Point& aPoint )
243
0
{
244
0
    OExternalLockGuard aGuard( this );
245
246
0
    sal_Int32 nIndex = -1;
247
0
    if ( m_pParent )
248
0
    {
249
0
        sal_uInt16 nItemId = 0;
250
0
        tools::Rectangle aItemRect = m_pParent->GetBoundingRectangle( m_nItemPos );
251
0
        Point aPnt(vcl::unohelper::ConvertToVCLPoint(aPoint));
252
0
        aPnt += aItemRect.TopLeft();
253
0
        sal_Int32 nI = m_pParent->GetIndexForPoint( aPnt, nItemId );
254
0
        if ( nI != -1 && m_pParent->GetItemId( m_nItemPos ) == nItemId )
255
0
            nIndex = nI;
256
0
    }
257
258
0
    return nIndex;
259
0
}
260
261
262
OUString VCLXAccessibleMenuItem::getSelectedText()
263
0
{
264
0
    OExternalLockGuard aGuard( this );
265
266
0
    return OUString();
267
0
}
268
269
270
sal_Int32 VCLXAccessibleMenuItem::getSelectionStart()
271
0
{
272
0
    OExternalLockGuard aGuard( this );
273
274
0
    return 0;
275
0
}
276
277
278
sal_Int32 VCLXAccessibleMenuItem::getSelectionEnd()
279
0
{
280
0
    OExternalLockGuard aGuard( this );
281
282
0
    return 0;
283
0
}
284
285
286
sal_Bool VCLXAccessibleMenuItem::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
287
0
{
288
0
    OExternalLockGuard aGuard( this );
289
290
0
    if ( !implIsValidRange( nStartIndex, nEndIndex, m_sItemText.getLength() ) )
291
0
        throw IndexOutOfBoundsException();
292
293
0
    return false;
294
0
}
295
296
297
OUString VCLXAccessibleMenuItem::getText()
298
0
{
299
0
    OExternalLockGuard aGuard( this );
300
301
0
    return m_sItemText;
302
0
}
303
304
305
OUString VCLXAccessibleMenuItem::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
306
0
{
307
0
    OExternalLockGuard aGuard( this );
308
309
0
    return OCommonAccessibleText::implGetTextRange( implGetText(), nStartIndex, nEndIndex );
310
0
}
311
312
313
css::accessibility::TextSegment VCLXAccessibleMenuItem::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
314
0
{
315
0
    OExternalLockGuard aGuard( this );
316
317
0
    return OCommonAccessibleText::getTextAtIndex( nIndex, aTextType );
318
0
}
319
320
321
css::accessibility::TextSegment VCLXAccessibleMenuItem::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
322
0
{
323
0
    OExternalLockGuard aGuard( this );
324
325
0
    return OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType );
326
0
}
327
328
329
css::accessibility::TextSegment VCLXAccessibleMenuItem::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
330
0
{
331
0
    OExternalLockGuard aGuard( this );
332
333
0
    return OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType );
334
0
}
335
336
337
sal_Bool VCLXAccessibleMenuItem::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
338
0
{
339
0
    OExternalLockGuard aGuard( this );
340
341
0
    bool bReturn = false;
342
343
0
    if ( m_pParent )
344
0
    {
345
0
        vcl::Window* pWindow = m_pParent->GetWindow();
346
0
        if ( pWindow )
347
0
        {
348
0
            Reference< datatransfer::clipboard::XClipboard > xClipboard = pWindow->GetClipboard();
349
0
            if ( xClipboard.is() )
350
0
            {
351
0
                OUString sText( getTextRange( nStartIndex, nEndIndex ) );
352
353
0
                rtl::Reference<vcl::unohelper::TextDataObject> pDataObj = new vcl::unohelper::TextDataObject( sText );
354
355
0
                SolarMutexReleaser aReleaser;
356
0
                xClipboard->setContents( pDataObj, nullptr );
357
0
                Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( xClipboard, uno::UNO_QUERY );
358
0
                if( xFlushableClipboard.is() )
359
0
                    xFlushableClipboard->flushClipboard();
360
361
0
                bReturn = true;
362
0
            }
363
0
        }
364
0
    }
365
366
0
    return bReturn;
367
0
}
368
369
sal_Bool VCLXAccessibleMenuItem::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
370
0
{
371
0
    return false;
372
0
}
373
374
375
// XAccessibleAction
376
377
378
sal_Int32 VCLXAccessibleMenuItem::getAccessibleActionCount( )
379
0
{
380
0
    return 1;
381
0
}
382
383
384
sal_Bool VCLXAccessibleMenuItem::doAccessibleAction ( sal_Int32 nIndex )
385
0
{
386
0
    OExternalLockGuard aGuard( this );
387
388
0
    if ( nIndex != 0 )
389
0
        throw IndexOutOfBoundsException();
390
391
0
    Click();
392
393
0
    return true;
394
0
}
395
396
397
OUString VCLXAccessibleMenuItem::getAccessibleActionDescription ( sal_Int32 nIndex )
398
0
{
399
0
    OExternalLockGuard aGuard( this );
400
401
0
    if ( nIndex != 0 )
402
0
        throw IndexOutOfBoundsException();
403
404
0
    return RID_STR_ACC_ACTION_SELECT;
405
0
}
406
407
408
Reference< XAccessibleKeyBinding > VCLXAccessibleMenuItem::getAccessibleActionKeyBinding( sal_Int32 nIndex )
409
0
{
410
0
    OExternalLockGuard aGuard( this );
411
412
0
    if ( nIndex != 0 )
413
0
        throw IndexOutOfBoundsException();
414
415
0
    rtl::Reference<OAccessibleKeyBindingHelper> pKeyBindingHelper = new OAccessibleKeyBindingHelper();
416
417
0
    if ( m_pParent )
418
0
    {
419
        // create auto mnemonics
420
0
        if (!(m_pParent->GetMenuFlags() & MenuFlags::NoAutoMnemonics))
421
0
            m_pParent->CreateAutoMnemonics();
422
423
        // activation key
424
0
        KeyEvent aKeyEvent = m_pParent->GetActivationKey( m_pParent->GetItemId( m_nItemPos ) );
425
0
        vcl::KeyCode aKeyCode = aKeyEvent.GetKeyCode();
426
0
        Sequence< awt::KeyStroke > aSeq1
427
0
        {
428
0
            {
429
0
                0, // Modifiers
430
0
                static_cast< sal_Int16 >(aKeyCode.GetCode()),
431
0
                aKeyEvent.GetCharCode(),
432
0
                static_cast< sal_Int16 >( aKeyCode.GetFunction())
433
0
            }
434
0
        };
435
0
        Reference< XAccessible > xParent( getAccessibleParent() );
436
0
        if ( xParent.is() )
437
0
        {
438
0
            Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
439
0
            if ( xParentContext.is() && xParentContext->getAccessibleRole() == AccessibleRole::MENU_BAR )
440
0
                aSeq1.getArray()[0].Modifiers |= awt::KeyModifier::MOD2;
441
0
        }
442
0
        pKeyBindingHelper->AddKeyBinding( aSeq1 );
443
444
        // complete menu activation key sequence
445
0
        Sequence< awt::KeyStroke > aSeq;
446
0
        if ( xParent.is() )
447
0
        {
448
0
            Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
449
0
            if ( xParentContext.is() && xParentContext->getAccessibleRole() == AccessibleRole::MENU )
450
0
            {
451
0
                Reference< XAccessibleAction > xAction( xParentContext, UNO_QUERY );
452
0
                if ( xAction.is() && xAction->getAccessibleActionCount() > 0 )
453
0
                {
454
0
                    Reference< XAccessibleKeyBinding > xKeyB( xAction->getAccessibleActionKeyBinding( 0 ) );
455
0
                    if ( xKeyB.is() && xKeyB->getAccessibleKeyBindingCount() > 1 )
456
0
                        aSeq = xKeyB->getAccessibleKeyBinding( 1 );
457
0
                }
458
0
            }
459
0
        }
460
0
        Sequence< awt::KeyStroke > aSeq2 = ::comphelper::concatSequences( aSeq, aSeq1 );
461
0
        pKeyBindingHelper->AddKeyBinding( aSeq2 );
462
463
        // accelerator key
464
0
        vcl::KeyCode aAccelKeyCode = m_pParent->GetAccelKey( m_pParent->GetItemId( m_nItemPos ) );
465
0
        if ( aAccelKeyCode.GetCode() != 0 )
466
0
        {
467
0
            Sequence< awt::KeyStroke > aSeq3
468
0
            {
469
0
                {
470
0
                    0, // Modifiers
471
0
                    static_cast< sal_Int16 >(aAccelKeyCode.GetCode()),
472
0
                    aKeyEvent.GetCharCode(),
473
0
                    static_cast< sal_Int16 >(aAccelKeyCode.GetFunction())
474
0
                }
475
0
            };
476
0
            if (aAccelKeyCode.GetModifier() != 0)
477
0
            {
478
0
                auto pSeq3 = aSeq3.getArray();
479
0
                if ( aAccelKeyCode.IsShift() )
480
0
                    pSeq3[0].Modifiers |= awt::KeyModifier::SHIFT;
481
0
                if ( aAccelKeyCode.IsMod1() )
482
0
                    pSeq3[0].Modifiers |= awt::KeyModifier::MOD1;
483
0
                if ( aAccelKeyCode.IsMod2() )
484
0
                    pSeq3[0].Modifiers |= awt::KeyModifier::MOD2;
485
0
                if ( aAccelKeyCode.IsMod3() )
486
0
                    pSeq3[0].Modifiers |= awt::KeyModifier::MOD3;
487
0
            }
488
0
            pKeyBindingHelper->AddKeyBinding( aSeq3 );
489
0
        }
490
0
    }
491
492
0
    return pKeyBindingHelper;
493
0
}
494
495
496
// XAccessibleValue
497
498
499
Any VCLXAccessibleMenuItem::getCurrentValue(  )
500
0
{
501
0
    OExternalLockGuard aGuard( this );
502
503
0
    Any aValue;
504
0
    if ( IsSelected() )
505
0
        aValue <<= sal_Int32(1);
506
0
    else
507
0
        aValue <<= sal_Int32(0);
508
509
0
    return aValue;
510
0
}
511
512
513
sal_Bool VCLXAccessibleMenuItem::setCurrentValue( const Any& aNumber )
514
0
{
515
0
    OExternalLockGuard aGuard( this );
516
517
0
    bool bReturn = false;
518
0
    sal_Int32 nValue = 0;
519
0
    OSL_VERIFY( aNumber >>= nValue );
520
521
0
    if ( nValue <= 0 )
522
0
    {
523
0
        DeSelect();
524
0
        bReturn = true;
525
0
    }
526
0
    else if ( nValue >= 1 )
527
0
    {
528
0
        Select();
529
0
        bReturn = true;
530
0
    }
531
532
0
    return bReturn;
533
0
}
534
535
536
Any VCLXAccessibleMenuItem::getMaximumValue(  )
537
0
{
538
0
    Any aValue;
539
0
    aValue <<= sal_Int32(1);
540
541
0
    return aValue;
542
0
}
543
544
545
Any VCLXAccessibleMenuItem::getMinimumValue(  )
546
0
{
547
0
    Any aValue;
548
0
    aValue <<= sal_Int32(0);
549
550
0
    return aValue;
551
0
}
552
553
Any VCLXAccessibleMenuItem::getMinimumIncrement( )
554
0
{
555
0
    Any aValue;
556
0
    aValue <<= sal_Int32(1);
557
558
0
    return aValue;
559
0
}
560
561
562
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */