Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/accessibility/GraphCtlAccessibleContext.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 <com/sun/star/accessibility/AccessibleRole.hpp>
21
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
22
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
23
#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
24
#include <com/sun/star/lang/DisposedException.hpp>
25
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
26
#include <cppuhelper/supportsservice.hxx>
27
#include <vcl/svapp.hxx>
28
#include <vcl/settings.hxx>
29
#include <o3tl/safeint.hxx>
30
#include <osl/mutex.hxx>
31
#include <tools/gen.hxx>
32
#include <svtools/colorcfg.hxx>
33
#include <comphelper/accessibleeventnotifier.hxx>
34
#include <svx/sdrpaintwindow.hxx>
35
36
#include <svx/ShapeTypeHandler.hxx>
37
#include <svx/AccessibleShapeInfo.hxx>
38
#include <GraphCtlAccessibleContext.hxx>
39
#include <svx/graphctl.hxx>
40
#include <svx/strings.hrc>
41
#include <svx/svdpage.hxx>
42
#include <svx/dialmgr.hxx>
43
#include <svx/sdrhittesthelper.hxx>
44
45
// namespaces
46
using namespace ::cppu;
47
using namespace ::osl;
48
using namespace ::accessibility;
49
using namespace ::com::sun::star;
50
using namespace ::com::sun::star::uno;
51
using namespace ::com::sun::star::drawing;
52
using namespace ::com::sun::star::lang;
53
using namespace ::com::sun::star::accessibility;
54
55
// internal
56
/** initialize this component and set default values */
57
SvxGraphCtrlAccessibleContext::SvxGraphCtrlAccessibleContext(GraphCtrl& rRepr)
58
0
    : mpControl(&rRepr)
59
0
    , mpPage(nullptr)
60
0
    , mpView(nullptr)
61
0
{
62
0
    if (mpControl != nullptr)
63
0
    {
64
0
        SdrModel* pModel = mpControl->GetSdrModel();
65
0
        if (pModel)
66
0
            mpPage = pModel->GetPage(0);
67
0
        mpView = mpControl->GetSdrView();
68
69
0
        if (pModel == nullptr || mpPage == nullptr || mpView == nullptr)
70
0
        {
71
            // Set all the pointers to NULL
72
0
            mpPage = nullptr;
73
0
            mpView = nullptr;
74
0
        }
75
0
    }
76
77
0
    {
78
0
        ::SolarMutexGuard aSolarGuard;
79
0
        msName = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_NAME );
80
0
        msDescription = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION );
81
0
    }
82
0
}
83
84
/** returns the XAccessible interface for a given SdrObject.
85
    Multiple calls for the same SdrObject return the same XAccessible.
86
*/
87
Reference< XAccessible > SvxGraphCtrlAccessibleContext::getAccessible( const SdrObject* pObj )
88
0
{
89
0
    Reference<XAccessible> xAccessibleShape;
90
91
0
    if( pObj )
92
0
    {
93
        // see if we already created an XAccessible for the given SdrObject
94
0
        ShapesMapType::const_iterator iter = mxShapes.find( pObj );
95
96
0
        if( iter != mxShapes.end() )
97
0
        {
98
            // if we already have one, return it
99
0
            xAccessibleShape = (*iter).second.get();
100
0
        }
101
0
        else
102
0
        {
103
            // create a new one and remember in our internal map
104
0
            Reference< XShape > xShape( Reference< XShape >::query( const_cast<SdrObject*>(pObj)->getUnoShape() ) );
105
106
0
            css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
107
0
            AccessibleShapeInfo aShapeInfo (xShape,xParent);
108
0
            ::accessibility::AccessibleShapeTreeInfo aTreeInfo;
109
0
            aTreeInfo.SetSdrView(mpView);
110
0
            aTreeInfo.SetWindow(mpControl->GetDrawingArea()->get_ref_device().GetOwnerWindow());
111
0
            aTreeInfo.SetViewForwarder(this);
112
            // Create accessible object that corresponds to the descriptor's shape.
113
0
            rtl::Reference<AccessibleShape> pAcc(ShapeTypeHandler::Instance().CreateAccessibleObject(
114
0
                aShapeInfo, aTreeInfo));
115
0
            xAccessibleShape = pAcc.get();
116
0
            if (pAcc.is())
117
0
            {
118
0
                pAcc->Init();
119
0
            }
120
0
            mxShapes[pObj] = std::move(pAcc);
121
122
            // Create event and inform listeners of the object creation.
123
0
            NotifyAccessibleEvent(AccessibleEventId::CHILD, Any(), Any(xAccessibleShape));
124
0
        }
125
0
    }
126
127
0
    return xAccessibleShape;
128
0
}
129
130
Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint )
131
0
{
132
0
    ::osl::MutexGuard   aGuard( m_aMutex );
133
134
0
    Reference< XAccessible > xAccessible;
135
136
0
    if( !mpControl )
137
0
    {
138
0
        throw DisposedException();
139
0
    }
140
141
0
    Point aPnt( rPoint.X, rPoint.Y );
142
0
    aPnt = mpControl->GetDrawingArea()->get_ref_device().PixelToLogic(aPnt);
143
144
0
    SdrObject* pObj = nullptr;
145
146
0
    if(mpView && mpView->GetSdrPageView())
147
0
    {
148
0
        pObj = SdrObjListPrimitiveHit(*mpPage, aPnt, {1, 1}, *mpView->GetSdrPageView(), nullptr, false);
149
0
    }
150
151
0
    if( pObj )
152
0
        xAccessible = getAccessible( pObj );
153
154
0
    return xAccessible;
155
0
}
156
157
awt::Rectangle SvxGraphCtrlAccessibleContext::implGetBounds()
158
0
{
159
0
    const Point         aOutPos;
160
0
    const Size          aOutSize( mpControl->GetOutputSizePixel() );
161
0
    awt::Rectangle      aRet;
162
163
0
    aRet.X = aOutPos.X();
164
0
    aRet.Y = aOutPos.Y();
165
0
    aRet.Width = aOutSize.Width();
166
0
    aRet.Height = aOutSize.Height();
167
168
0
    return aRet;
169
0
}
170
171
// XAccessibleContext
172
sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChildCount()
173
0
{
174
0
    ::SolarMutexGuard aGuard;
175
176
0
    if( nullptr == mpPage )
177
0
        throw DisposedException();
178
179
0
    return mpPage->GetObjCount();
180
0
}
181
182
183
/** returns the SdrObject at index nIndex from the model of this graph */
184
SdrObject* SvxGraphCtrlAccessibleContext::getSdrObject( sal_Int64 nIndex )
185
0
{
186
0
    ::SolarMutexGuard aGuard;
187
188
0
    if( nullptr == mpPage )
189
0
        throw DisposedException();
190
191
0
    if( (nIndex < 0) || ( o3tl::make_unsigned(nIndex) >= mpPage->GetObjCount() ) )
192
0
        throw lang::IndexOutOfBoundsException();
193
194
0
    return mpPage->GetObj( nIndex );
195
0
}
196
197
Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChild( sal_Int64 nIndex )
198
0
{
199
0
    ::SolarMutexGuard aGuard;
200
201
0
    return getAccessible( getSdrObject( nIndex ) );
202
0
}
203
204
Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleParent()
205
0
{
206
0
    ::SolarMutexGuard aGuard;
207
208
0
    if( nullptr == mpControl )
209
0
        throw DisposedException();
210
211
0
    return mpControl->GetDrawingArea()->get_accessible_parent();
212
0
}
213
214
sal_Int16 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRole()
215
0
{
216
0
    return AccessibleRole::PANEL;
217
0
}
218
219
220
OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleDescription()
221
0
{
222
0
    ::SolarMutexGuard aGuard;
223
0
    return msDescription;
224
0
}
225
226
227
OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleName()
228
0
{
229
0
    ::SolarMutexGuard aGuard;
230
0
    return msName;
231
0
}
232
233
234
/** Return empty reference to indicate that the relation set is not
235
    supported.
236
*/
237
Reference< XAccessibleRelationSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRelationSet()
238
0
{
239
0
    return Reference< XAccessibleRelationSet >();
240
0
}
241
242
243
sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleStateSet()
244
0
{
245
0
    ::SolarMutexGuard aGuard;
246
247
0
    sal_Int64 nStateSet = 0;
248
249
0
    if (!isAlive())
250
0
    {
251
0
        nStateSet |= AccessibleStateType::DEFUNC;
252
0
    }
253
0
    else
254
0
    {
255
0
        nStateSet |= AccessibleStateType::FOCUSABLE;
256
0
        if( mpControl->HasFocus() )
257
0
            nStateSet |= AccessibleStateType::FOCUSED;
258
0
        nStateSet |= AccessibleStateType::OPAQUE;
259
0
        nStateSet |= AccessibleStateType::SHOWING;
260
0
        nStateSet |= AccessibleStateType::VISIBLE;
261
0
    }
262
263
0
    return nStateSet;
264
0
}
265
266
267
lang::Locale SAL_CALL SvxGraphCtrlAccessibleContext::getLocale()
268
0
{
269
0
    ::SolarMutexGuard aGuard;
270
271
0
    css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
272
0
    if (xParent.is())
273
0
    {
274
0
        Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
275
0
        if( xParentContext.is() )
276
0
            return xParentContext->getLocale();
277
0
    }
278
279
    //  No parent.  Therefore throw exception to indicate this cluelessness.
280
0
    throw IllegalAccessibleComponentStateException();
281
0
}
282
283
void SAL_CALL SvxGraphCtrlAccessibleContext::grabFocus()
284
0
{
285
0
    ::SolarMutexGuard aGuard;
286
287
0
    if( nullptr == mpControl )
288
0
        throw DisposedException();
289
290
0
    mpControl->GrabFocus();
291
0
}
292
293
sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getForeground()
294
0
{
295
0
    svtools::ColorConfig aColorConfig;
296
0
    Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
297
0
    return static_cast<sal_Int32>(nColor);
298
0
}
299
300
sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getBackground()
301
0
{
302
0
    Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor();
303
0
    return static_cast<sal_Int32>(nColor);
304
0
}
305
306
// XServiceInfo
307
OUString SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationName()
308
0
{
309
0
    return u"com.sun.star.comp.ui.SvxGraphCtrlAccessibleContext"_ustr;
310
0
}
311
312
sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::supportsService( const OUString& sServiceName )
313
0
{
314
0
    return cppu::supportsService(this, sServiceName);
315
0
}
316
317
Sequence< OUString > SAL_CALL SvxGraphCtrlAccessibleContext::getSupportedServiceNames()
318
0
{
319
0
    return { u"com.sun.star.accessibility.AccessibleContext"_ustr };
320
0
}
321
322
// XAccessibleSelection
323
void SAL_CALL SvxGraphCtrlAccessibleContext::selectAccessibleChild( sal_Int64 nIndex )
324
0
{
325
0
    ::SolarMutexGuard aGuard;
326
327
0
    if( nullptr == mpView )
328
0
        throw DisposedException();
329
330
0
    if (nIndex < 0 || nIndex >= getAccessibleChildCount())
331
0
        throw lang::IndexOutOfBoundsException();
332
333
0
    SdrObject* pObj = getSdrObject( nIndex );
334
335
0
    if( pObj )
336
0
        mpView->MarkObj( pObj, mpView->GetSdrPageView());
337
0
}
338
339
340
sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::isAccessibleChildSelected( sal_Int64 nIndex )
341
0
{
342
0
    ::SolarMutexGuard aGuard;
343
344
0
    if( nullptr == mpView )
345
0
        throw DisposedException();
346
347
0
    if (nIndex < 0 || nIndex >= getAccessibleChildCount())
348
0
        throw lang::IndexOutOfBoundsException();
349
350
0
    return mpView->IsObjMarked( getSdrObject( nIndex ) );
351
0
}
352
353
354
void SAL_CALL SvxGraphCtrlAccessibleContext::clearAccessibleSelection()
355
0
{
356
0
    ::SolarMutexGuard aGuard;
357
358
0
    if( nullptr == mpView )
359
0
        throw DisposedException();
360
361
0
    mpView->UnmarkAllObj();
362
0
}
363
364
365
void SAL_CALL SvxGraphCtrlAccessibleContext::selectAllAccessibleChildren()
366
0
{
367
0
    ::SolarMutexGuard aGuard;
368
369
0
    if( nullptr == mpView )
370
0
        throw DisposedException();
371
372
0
    mpView->MarkAllObj();
373
0
}
374
375
376
sal_Int64 SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChildCount()
377
0
{
378
0
    ::SolarMutexGuard aGuard;
379
380
0
    if( nullptr == mpView )
381
0
        throw DisposedException();
382
383
0
    const SdrMarkList& rList = mpView->GetMarkedObjectList();
384
0
    return static_cast<sal_Int64>(rList.GetMarkCount());
385
0
}
386
387
388
Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChild( sal_Int64 nIndex )
389
0
{
390
0
    ::SolarMutexGuard aGuard;
391
392
0
    checkChildIndexOnSelection( nIndex );
393
394
0
    Reference< XAccessible > xAccessible;
395
396
0
    const SdrMarkList& rList = mpView->GetMarkedObjectList();
397
0
    SdrObject* pObj = rList.GetMark(static_cast<size_t>(nIndex))->GetMarkedSdrObj();
398
0
    if( pObj )
399
0
        xAccessible = getAccessible( pObj );
400
401
0
    return xAccessible;
402
0
}
403
404
405
void SAL_CALL SvxGraphCtrlAccessibleContext::deselectAccessibleChild( sal_Int64 nIndex )
406
0
{
407
0
    ::SolarMutexGuard aGuard;
408
409
0
    checkChildIndexOnSelection( nIndex );
410
411
0
    if( !mpView )
412
0
        return;
413
414
0
    const SdrMarkList& rList = mpView->GetMarkedObjectList();
415
416
0
    SdrObject* pObj = getSdrObject( nIndex );
417
0
    if( !pObj )
418
0
        return;
419
420
0
    SdrMarkList aRefList( rList );
421
422
0
    SdrPageView* pPV = mpView->GetSdrPageView();
423
0
    mpView->UnmarkAllObj( pPV );
424
425
0
    const size_t nCount = aRefList.GetMarkCount();
426
0
    for( size_t nMark = 0; nMark < nCount; ++nMark )
427
0
    {
428
0
        if( aRefList.GetMark(nMark)->GetMarkedSdrObj() != pObj )
429
0
            mpView->MarkObj( aRefList.GetMark(nMark)->GetMarkedSdrObj(), pPV );
430
0
    }
431
0
}
432
433
// internals
434
void SvxGraphCtrlAccessibleContext::checkChildIndexOnSelection(sal_Int64 nIndex )
435
0
{
436
0
    if( nIndex < 0 || nIndex >= getSelectedAccessibleChildCount() )
437
0
        throw lang::IndexOutOfBoundsException();
438
0
}
439
440
441
/** Replace the model, page, and view pointers by the ones provided
442
    (explicitly and implicitly).
443
*/
444
void SvxGraphCtrlAccessibleContext::setModelAndView (
445
    SdrModel* pModel,
446
    SdrView* pView)
447
0
{
448
0
    ::SolarMutexGuard aGuard;
449
450
0
    mpPage = pModel ? pModel->GetPage(0) : nullptr;
451
0
    mpView = pView;
452
453
0
    if (mpPage == nullptr || mpView == nullptr)
454
0
    {
455
        // Set all the pointers to NULL
456
0
        mpPage = nullptr;
457
0
        mpView = nullptr;
458
0
    }
459
0
}
460
461
462
void SAL_CALL SvxGraphCtrlAccessibleContext::disposing()
463
0
{
464
0
    ::SolarMutexGuard aGuard;
465
466
0
    if (!isAlive())
467
0
        return;
468
469
0
    mpControl = nullptr;       // object dies with representation
470
0
    mpView = nullptr;
471
0
    mpPage = nullptr;
472
473
0
    {
474
0
        for (const auto& rEntry : mxShapes)
475
0
        {
476
0
            rtl::Reference<XAccessible> pAcc(rEntry.second);
477
0
            Reference< XComponent > xComp( pAcc.get(), UNO_QUERY );
478
0
            if( xComp.is() )
479
0
                xComp->dispose();
480
0
        }
481
482
0
        mxShapes.clear();
483
0
    }
484
485
0
    comphelper::OAccessible::disposing();
486
0
}
487
488
void SvxGraphCtrlAccessibleContext::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
489
0
{
490
0
    if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
491
0
    {
492
0
        const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
493
0
        switch( pSdrHint->GetKind() )
494
0
        {
495
0
            case SdrHintKind::ObjectChange:
496
0
                {
497
0
                    ShapesMapType::iterator iter = mxShapes.find( pSdrHint->GetObject() );
498
499
0
                    if( iter != mxShapes.end() )
500
0
                    {
501
                        // if we already have one, return it
502
0
                        rtl::Reference<AccessibleShape> pShape((*iter).second);
503
504
0
                        if( pShape.is() )
505
0
                            pShape->CommitChange( AccessibleEventId::VISIBLE_DATA_CHANGED, uno::Any(), uno::Any(), -1 );
506
0
                    }
507
0
                }
508
0
                break;
509
510
0
            case SdrHintKind::ObjectInserted:
511
0
                NotifyAccessibleEvent(AccessibleEventId::CHILD, uno::Any(),
512
0
                                      Any(getAccessible(pSdrHint->GetObject())));
513
0
                break;
514
0
            case SdrHintKind::ObjectRemoved:
515
0
                NotifyAccessibleEvent(AccessibleEventId::CHILD,
516
0
                                      Any(getAccessible(pSdrHint->GetObject())), uno::Any());
517
0
                break;
518
0
            case SdrHintKind::ModelCleared:
519
0
                dispose();
520
0
                break;
521
0
            default:
522
0
                break;
523
0
        }
524
0
    }
525
0
    else
526
0
    {
527
        // Has our SdDrawDocument just died?
528
0
        if(rHint.GetId() == SfxHintId::Dying)
529
0
        {
530
0
            dispose();
531
0
        }
532
0
    }
533
0
}
534
535
// IAccessibleViewforwarder
536
tools::Rectangle SvxGraphCtrlAccessibleContext::GetVisibleArea() const
537
0
{
538
0
    tools::Rectangle aVisArea;
539
540
0
    if( mpView && mpView->PaintWindowCount())
541
0
    {
542
0
        SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(0);
543
0
        aVisArea = pPaintWindow->GetVisibleArea();
544
0
    }
545
546
0
    return aVisArea;
547
0
}
548
549
Point SvxGraphCtrlAccessibleContext::LogicToPixel (const Point& rPoint) const
550
0
{
551
0
    if( mpControl )
552
0
    {
553
0
        return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel (rPoint) + mpControl->GetPositionInDialog();
554
0
    }
555
0
    else
556
0
    {
557
0
        return rPoint;
558
0
    }
559
0
}
560
561
Size SvxGraphCtrlAccessibleContext::LogicToPixel (const Size& rSize) const
562
0
{
563
0
    if( mpControl )
564
0
        return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel(rSize);
565
0
    else
566
0
        return rSize;
567
0
}
568
569
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */