Coverage Report

Created: 2026-10-14 09:33

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