Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/Accessibility/AccessibleEditObject.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 <sal/config.h>
21
22
#include <memory>
23
#include <utility>
24
25
#include <AccessibleEditObject.hxx>
26
#include <AccessibleText.hxx>
27
#include <editsrc.hxx>
28
#include <scmod.hxx>
29
#include <inputhdl.hxx>
30
#include <inputwin.hxx>
31
32
#include <com/sun/star/accessibility/AccessibleRole.hpp>
33
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
34
#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
35
#include <com/sun/star/sheet/XSpreadsheet.hpp>
36
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
37
#include <svx/AccessibleTextHelper.hxx>
38
#include <editeng/editview.hxx>
39
#include <editeng/editeng.hxx>
40
#include <svx/svdmodel.hxx>
41
#include <vcl/svapp.hxx>
42
#include <vcl/window.hxx>
43
#include <sfx2/objsh.hxx>
44
#include <cppuhelper/queryinterface.hxx>
45
46
#include <unonames.hxx>
47
#include <document.hxx>
48
#include <AccessibleDocument.hxx>
49
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
50
#include <unotools/accessiblerelationsethelper.hxx>
51
#include <com/sun/star/accessibility/XAccessibleText.hpp>
52
53
using ::com::sun::star::lang::IndexOutOfBoundsException;
54
using namespace ::com::sun::star;
55
using namespace ::com::sun::star::accessibility;
56
57
ScAccessibleEditObject::ScAccessibleEditObject(const rtl::Reference<comphelper::OAccessible>& rpParent,
58
                                               EditView* pEditView, vcl::Window* pWin,
59
                                               const OUString& rName, const OUString& rDescription,
60
                                               EditObjectType eObjectType)
61
0
    : ImplInheritanceHelper(rpParent, AccessibleRole::TEXT_FRAME)
62
0
    , mpEditView(pEditView)
63
0
    , mpWindow(pWin)
64
0
    , mpTextWnd(nullptr)
65
0
    , meObjectType(eObjectType)
66
0
    , mbHasFocus(false)
67
0
    , m_pScDoc(nullptr)
68
0
{
69
0
    InitAcc(rpParent, pEditView, rName, rDescription);
70
0
}
71
72
ScAccessibleEditObject::ScAccessibleEditObject(EditObjectType eObjectType)
73
0
    : ImplInheritanceHelper(nullptr, AccessibleRole::TEXT_FRAME)
74
0
    , mpEditView(nullptr)
75
0
    , mpWindow(nullptr)
76
0
    , mpTextWnd(nullptr)
77
0
    , meObjectType(eObjectType)
78
0
    , mbHasFocus(false)
79
0
    , m_pScDoc(nullptr)
80
0
{
81
0
}
82
83
void ScAccessibleEditObject::InitAcc(
84
        const rtl::Reference<comphelper::OAccessible>& rpParent,
85
        EditView* pEditView,
86
        const OUString& rName,
87
        const OUString& rDescription)
88
0
{
89
0
    SetParent(rpParent);
90
0
    mpEditView = pEditView;
91
92
0
    CreateTextHelper();
93
0
    SetName(rName);
94
0
    SetDescription(rDescription);
95
0
    if( meObjectType == CellInEditMode)
96
0
    {
97
0
        const ScAccessibleDocument* pAccDoc = static_cast<ScAccessibleDocument*>(rpParent.get());
98
0
        if (pAccDoc)
99
0
        {
100
0
            m_pScDoc = pAccDoc->GetDocument();
101
0
            m_curCellAddress =pAccDoc->GetCurCellAddress();
102
0
        }
103
0
    }
104
0
}
105
106
ScAccessibleEditObject::~ScAccessibleEditObject()
107
0
{
108
0
    if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
109
0
    {
110
        // increment refcount to prevent double call of dtor
111
0
        osl_atomic_increment( &m_refCount );
112
        // call dispose to inform object which have a weak reference to this object
113
0
        dispose();
114
0
    }
115
0
}
116
117
void SAL_CALL ScAccessibleEditObject::disposing()
118
0
{
119
0
    SolarMutexGuard aGuard;
120
0
    mpTextHelper.reset();
121
122
0
    ScAccessibleContextBase::disposing();
123
0
}
124
125
void ScAccessibleEditObject::LostFocus()
126
0
{
127
0
    mbHasFocus = false;
128
0
    if (mpTextHelper)
129
0
        mpTextHelper->SetFocus(false);
130
0
    CommitFocusLost();
131
0
}
132
133
void ScAccessibleEditObject::GotFocus()
134
0
{
135
0
    mbHasFocus = true;
136
0
    CommitFocusGained();
137
0
    if (mpTextHelper)
138
0
        mpTextHelper->SetFocus();
139
0
}
140
141
    //=====  XAccessibleComponent  ============================================
142
143
uno::Reference< XAccessible > SAL_CALL ScAccessibleEditObject::getAccessibleAtPoint(
144
        const awt::Point& rPoint )
145
0
{
146
0
    uno::Reference<XAccessible> xRet;
147
0
    if (containsPoint(rPoint))
148
0
    {
149
0
        SolarMutexGuard aGuard;
150
0
        ensureAlive();
151
152
0
        CreateTextHelper();
153
154
0
        xRet = mpTextHelper->GetAt(rPoint);
155
0
    }
156
157
0
    return xRet;
158
0
}
159
160
AbsoluteScreenPixelRectangle ScAccessibleEditObject::GetBoundingBoxOnScreen()
161
0
{
162
0
    AbsoluteScreenPixelRectangle aScreenBounds;
163
164
0
    if ( mpWindow )
165
0
    {
166
0
        if ( meObjectType == CellInEditMode )
167
0
        {
168
0
            if (mpEditView)
169
0
            {
170
0
                MapMode aMapMode(mpEditView->getEditEngine().GetRefMapMode());
171
0
                tools::Rectangle aScreenBoundsLog = mpWindow->LogicToPixel( mpEditView->GetOutputArea(), aMapMode );
172
0
                Point aCellLoc = aScreenBoundsLog.TopLeft();
173
0
                AbsoluteScreenPixelRectangle aWindowRect = mpWindow->GetWindowExtentsAbsolute();
174
0
                AbsoluteScreenPixelPoint aWindowLoc = aWindowRect.TopLeft();
175
0
                AbsoluteScreenPixelPoint aPos( aCellLoc.getX() + aWindowLoc.getX(), aCellLoc.getY() + aWindowLoc.getY() );
176
0
                aScreenBounds = AbsoluteScreenPixelRectangle( aPos, aScreenBoundsLog.GetSize() );
177
0
            }
178
0
        }
179
0
        else
180
0
        {
181
0
            aScreenBounds = mpWindow->GetWindowExtentsAbsolute();
182
0
        }
183
0
    }
184
185
0
    return aScreenBounds;
186
0
}
187
188
tools::Rectangle ScAccessibleEditObject::GetBoundingBox()
189
0
{
190
0
    tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
191
192
0
    if ( mpWindow )
193
0
    {
194
0
        rtl::Reference<comphelper::OAccessible> pThis = mpWindow->GetAccessible();
195
0
        if (pThis.is())
196
0
        {
197
0
            uno::Reference<XAccessible> xParent = pThis->getAccessibleParent();
198
0
            if ( xParent.is() )
199
0
            {
200
0
                uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
201
0
                if ( xParentComponent.is() )
202
0
                {
203
0
                    Point aScreenLoc = aBounds.TopLeft();
204
0
                    awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
205
0
                    Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
206
0
                    aBounds.SetPos( aPos );
207
0
                }
208
0
            }
209
0
        }
210
0
    }
211
212
0
    return aBounds;
213
0
}
214
215
    //=====  XAccessibleContext  ==============================================
216
217
sal_Int64 SAL_CALL
218
    ScAccessibleEditObject::getAccessibleChildCount()
219
0
{
220
0
    SolarMutexGuard aGuard;
221
0
    ensureAlive();
222
0
    CreateTextHelper();
223
0
    return mpTextHelper->GetChildCount();
224
0
}
225
226
uno::Reference< XAccessible > SAL_CALL
227
    ScAccessibleEditObject::getAccessibleChild(sal_Int64 nIndex)
228
0
{
229
0
    SolarMutexGuard aGuard;
230
0
    ensureAlive();
231
0
    CreateTextHelper();
232
0
    return mpTextHelper->GetChild(nIndex);
233
0
}
234
235
sal_Int64 SAL_CALL ScAccessibleEditObject::getAccessibleStateSet()
236
0
{
237
0
    SolarMutexGuard aGuard;
238
0
    sal_Int64 nParentStates = 0;
239
0
    if (getAccessibleParent().is())
240
0
    {
241
0
        uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
242
0
        nParentStates = xParentContext->getAccessibleStateSet();
243
0
    }
244
0
    sal_Int64 nStateSet = 0;
245
0
    if (IsDefunc(nParentStates))
246
0
        nStateSet |= AccessibleStateType::DEFUNC;
247
0
    else
248
0
    {
249
        // all states are const, because this object exists only in one state
250
0
        nStateSet |= AccessibleStateType::EDITABLE;
251
0
        nStateSet |= AccessibleStateType::ENABLED;
252
0
        nStateSet |= AccessibleStateType::SENSITIVE;
253
0
        nStateSet |= AccessibleStateType::MULTI_LINE;
254
0
        nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
255
0
        nStateSet |= AccessibleStateType::SHOWING;
256
0
        nStateSet |= AccessibleStateType::VISIBLE;
257
0
    }
258
0
    return nStateSet;
259
0
}
260
261
OUString
262
    ScAccessibleEditObject::createAccessibleDescription()
263
0
{
264
//    OSL_FAIL("Should never be called, because is set in the constructor.")
265
0
    return OUString();
266
0
}
267
268
OUString
269
    ScAccessibleEditObject::createAccessibleName()
270
0
{
271
0
    OSL_FAIL("Should never be called, because is set in the constructor.");
272
0
    return OUString();
273
0
}
274
275
    ///=====  XAccessibleEventBroadcaster  =====================================
276
277
void SAL_CALL
278
    ScAccessibleEditObject::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
279
0
{
280
0
    CreateTextHelper();
281
282
0
    mpTextHelper->AddEventListener(xListener);
283
284
0
    ScAccessibleContextBase::addAccessibleEventListener(xListener);
285
0
}
286
287
void SAL_CALL
288
    ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
289
0
{
290
0
    CreateTextHelper();
291
292
0
    mpTextHelper->RemoveEventListener(xListener);
293
294
0
    ScAccessibleContextBase::removeAccessibleEventListener(xListener);
295
0
}
296
297
bool ScAccessibleEditObject::IsDefunc(sal_Int64 nParentStates)
298
0
{
299
0
    return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() ||
300
0
         (nParentStates & AccessibleStateType::DEFUNC);
301
0
}
302
303
OutputDevice* ScAccessibleEditObject::GetOutputDeviceForView()
304
0
{
305
0
    return mpWindow->GetOutDev();
306
0
}
307
308
void ScAccessibleEditObject::CreateTextHelper()
309
0
{
310
0
    if (mpTextHelper)
311
0
        return;
312
313
0
    ::std::unique_ptr < ScAccessibleTextData > pAccessibleTextData;
314
0
    if (meObjectType == CellInEditMode || meObjectType == EditControl)
315
0
    {
316
0
        pAccessibleTextData.reset
317
0
            (new ScAccessibleEditObjectTextData(mpEditView, GetOutputDeviceForView()));
318
0
    }
319
0
    else
320
0
    {
321
0
        pAccessibleTextData.reset
322
0
            (new ScAccessibleEditLineTextData(nullptr, GetOutputDeviceForView(), mpTextWnd));
323
0
    }
324
325
0
    std::unique_ptr<ScAccessibilityEditSource> pEditSrc =
326
0
        std::make_unique<ScAccessibilityEditSource>(std::move(pAccessibleTextData));
327
328
0
    mpTextHelper = std::make_unique<::accessibility::AccessibleTextHelper>(std::move(pEditSrc));
329
0
    mpTextHelper->SetEventSource(this);
330
331
0
    const ScInputHandler* pInputHdl = ScModule::get()->GetInputHdl();
332
0
    if ( pInputHdl && pInputHdl->IsEditMode() )
333
0
    {
334
0
        mpTextHelper->SetFocus();
335
0
    }
336
0
    else
337
0
    {
338
0
        mpTextHelper->SetFocus(mbHasFocus);
339
0
    }
340
341
    // #i54814# activate cell in edit mode
342
0
    if( meObjectType == CellInEditMode )
343
0
    {
344
        // do not activate cell object, if top edit line is active
345
0
        if( pInputHdl && !pInputHdl->IsTopMode() )
346
0
        {
347
0
            SdrHint aHint( SdrHintKind::BeginEdit );
348
0
            mpTextHelper->GetEditSource().GetBroadcaster().Broadcast( aHint );
349
0
        }
350
0
    }
351
0
}
352
353
sal_Int32 SAL_CALL ScAccessibleEditObject::getForeground(  )
354
0
{
355
0
    return GetFgBgColor(SC_UNONAME_CCOLOR);
356
0
}
357
358
sal_Int32 SAL_CALL ScAccessibleEditObject::getBackground(  )
359
0
{
360
0
    return GetFgBgColor(SC_UNONAME_CELLBACK);
361
0
}
362
363
sal_Int32 ScAccessibleEditObject::GetFgBgColor( const OUString &strPropColor)
364
0
{
365
0
    SolarMutexGuard aGuard;
366
0
    sal_Int32 nColor(0);
367
0
    if (m_pScDoc)
368
0
    {
369
0
        ScDocShell* pObjSh = m_pScDoc->GetDocumentShell();
370
0
        if ( pObjSh )
371
0
        {
372
0
            ScModelObj* pSpreadDoc = pObjSh->GetModel();
373
0
            if ( pSpreadDoc )
374
0
            {
375
0
                uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
376
0
                uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
377
0
                if ( xIndex.is() )
378
0
                {
379
0
                    uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab());
380
0
                    uno::Reference<sheet::XSpreadsheet> xTable;
381
0
                    if (aTable>>=xTable)
382
0
                    {
383
0
                        uno::Reference<table::XCell> xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row());
384
0
                        if (xCell.is())
385
0
                        {
386
0
                            uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
387
0
                            if (xCellProps.is())
388
0
                            {
389
0
                                uno::Any aAny = xCellProps->getPropertyValue(strPropColor);
390
0
                                aAny >>= nColor;
391
0
                            }
392
0
                        }
393
0
                    }
394
0
                }
395
0
            }
396
0
        }
397
0
    }
398
0
    return nColor;
399
0
}
400
//=====  XAccessibleSelection  ============================================
401
402
void SAL_CALL ScAccessibleEditObject::selectAccessibleChild( sal_Int64 )
403
0
{
404
0
}
405
406
sal_Bool SAL_CALL ScAccessibleEditObject::isAccessibleChildSelected( sal_Int64 nChildIndex )
407
0
{
408
0
    uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
409
0
    uno::Reference<XAccessibleContext> xContext;
410
0
    if( xAcc.is() )
411
0
        xContext = xAcc->getAccessibleContext();
412
0
    if( xContext.is() )
413
0
    {
414
0
        if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
415
0
        {
416
0
            uno::Reference< css::accessibility::XAccessibleText >
417
0
                xText(xAcc, uno::UNO_QUERY);
418
0
            if( xText.is() )
419
0
            {
420
0
                if( xText->getSelectionStart() >= 0 ) return true;
421
0
            }
422
0
        }
423
0
    }
424
0
    return false;
425
0
}
426
427
void SAL_CALL ScAccessibleEditObject::clearAccessibleSelection(  )
428
0
{
429
0
}
430
431
void SAL_CALL ScAccessibleEditObject::selectAllAccessibleChildren(  )
432
0
{
433
0
}
434
435
sal_Int64 SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChildCount()
436
0
{
437
0
    sal_Int64 nCount = 0;
438
0
    sal_Int64 TotalCount = getAccessibleChildCount();
439
0
    for( sal_Int64 i = 0; i < TotalCount; i++ )
440
0
        if( isAccessibleChildSelected(i) ) nCount++;
441
0
    return nCount;
442
0
}
443
444
uno::Reference<XAccessible> SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
445
0
{
446
0
    if ( nSelectedChildIndex < 0 || nSelectedChildIndex > getSelectedAccessibleChildCount() )
447
0
        throw IndexOutOfBoundsException();
448
449
0
    for (sal_Int64 i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
450
0
        if( isAccessibleChildSelected(i1) )
451
0
        {
452
0
            if( i2 == nSelectedChildIndex )
453
0
                return getAccessibleChild( i1 );
454
0
            i2++;
455
0
        }
456
0
    return uno::Reference<XAccessible>();
457
0
}
458
459
void SAL_CALL ScAccessibleEditObject::deselectAccessibleChild(sal_Int64)
460
0
{
461
0
}
462
463
uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet(  )
464
0
{
465
0
    SolarMutexGuard aGuard;
466
0
    vcl::Window* pWindow = mpWindow;
467
0
    rtl::Reference<utl::AccessibleRelationSetHelper> rRelationSet = new utl::AccessibleRelationSetHelper;
468
0
    if ( pWindow )
469
0
    {
470
0
        vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy();
471
0
        if ( pLabeledBy && pLabeledBy != pWindow )
472
0
        {
473
0
            uno::Sequence<uno::Reference<css::accessibility::XAccessible>> aSequence { pLabeledBy->GetAccessible() };
474
0
            rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType_LABELED_BY, aSequence ) );
475
0
        }
476
0
        vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf();
477
0
        if ( pMemberOf && pMemberOf != pWindow )
478
0
        {
479
0
            uno::Sequence< uno::Reference<css::accessibility::XAccessible> > aSequence { pMemberOf->GetAccessible() };
480
0
            rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType_MEMBER_OF, aSequence ) );
481
0
        }
482
0
        return rRelationSet;
483
0
    }
484
0
    return uno::Reference< XAccessibleRelationSet >();
485
0
}
486
487
AbsoluteScreenPixelRectangle ScAccessibleEditControlObject::GetBoundingBoxOnScreen()
488
0
{
489
0
    AbsoluteScreenPixelRectangle aScreenBounds;
490
491
0
    if (m_pController && m_pController->GetDrawingArea())
492
0
    {
493
0
        aScreenBounds = AbsoluteScreenPixelRectangle(m_pController->GetDrawingArea()->get_accessible_location_on_screen(),
494
0
                                         m_pController->GetOutputSizePixel());
495
0
    }
496
497
0
    return aScreenBounds;
498
0
}
499
500
tools::Rectangle ScAccessibleEditControlObject::GetBoundingBox()
501
0
{
502
0
    tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
503
504
0
    uno::Reference<XAccessibleContext> xContext = getAccessibleContext();
505
0
    if ( xContext.is() )
506
0
    {
507
0
        uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() );
508
0
        if ( xParent.is() )
509
0
        {
510
0
            uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
511
0
            if ( xParentComponent.is() )
512
0
            {
513
0
                Point aScreenLoc = aBounds.TopLeft();
514
0
                awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
515
0
                Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
516
0
                aBounds.SetPos( aPos );
517
0
            }
518
0
        }
519
0
    }
520
521
0
    return aBounds;
522
0
}
523
524
void SAL_CALL ScAccessibleEditControlObject::disposing()
525
0
{
526
0
    ScAccessibleEditObject::disposing();
527
0
    m_pController = nullptr;
528
0
}
529
530
uno::Reference< XAccessibleRelationSet > ScAccessibleEditControlObject::getAccessibleRelationSet()
531
0
{
532
0
    SolarMutexGuard aGuard;
533
0
    if (!m_pController || !m_pController->GetDrawingArea())
534
0
        return uno::Reference< XAccessibleRelationSet >();
535
0
    return m_pController->GetDrawingArea()->get_accessible_relation_set();
536
0
}
537
538
OutputDevice* ScAccessibleEditControlObject::GetOutputDeviceForView()
539
0
{
540
0
    if (!m_pController || !m_pController->GetDrawingArea())
541
0
        return nullptr;
542
0
    return &m_pController->GetDrawingArea()->get_ref_device();
543
0
}
544
545
ScAccessibleEditLineObject::ScAccessibleEditLineObject(ScTextWnd* pTextWnd)
546
0
    : ScAccessibleEditControlObject(pTextWnd, ScAccessibleEditObject::EditLine)
547
0
{
548
    // tdf#141769 set this early so it's always available, even before the on-demand
549
    // editview is created
550
0
    mpTextWnd = pTextWnd;
551
0
}
552
553
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */