Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/Accessibility/AccessibleDocument.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 <AccessibleDocument.hxx>
21
#include <AccessibleSpreadsheet.hxx>
22
#include <tabvwsh.hxx>
23
#include <AccessibilityHints.hxx>
24
#include <document.hxx>
25
#include <drwlayer.hxx>
26
#include <DrawModelBroadcaster.hxx>
27
#include <drawview.hxx>
28
#include <gridwin.hxx>
29
#include <AccessibleEditObject.hxx>
30
#include <userdat.hxx>
31
#include <scresid.hxx>
32
#include <strings.hrc>
33
#include <strings.hxx>
34
#include <markdata.hxx>
35
36
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
37
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
38
#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
39
#include <com/sun/star/accessibility/AccessibleRole.hpp>
40
#include <com/sun/star/view/XSelectionSupplier.hpp>
41
#include <com/sun/star/drawing/ShapeCollection.hpp>
42
#include <com/sun/star/drawing/XShape.hpp>
43
#include <com/sun/star/drawing/XShapes.hpp>
44
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
45
#include <o3tl/safeint.hxx>
46
#include <tools/gen.hxx>
47
#include <svx/svdpage.hxx>
48
#include <svx/svdobj.hxx>
49
#include <svx/ShapeTypeHandler.hxx>
50
#include <svx/AccessibleShape.hxx>
51
#include <svx/AccessibleShapeTreeInfo.hxx>
52
#include <svx/AccessibleShapeInfo.hxx>
53
#include <svx/IAccessibleParent.hxx>
54
#include <comphelper/sequence.hxx>
55
#include <sfx2/viewfrm.hxx>
56
#include <sfx2/docfile.hxx>
57
#include <toolkit/helper/vclunohelper.hxx>
58
#include <unotools/accessiblerelationsethelper.hxx>
59
#include <utility>
60
#include <vcl/svapp.hxx>
61
#include <vcl/unohelp.hxx>
62
#include <vcl/vclevent.hxx>
63
64
#include <svx/AccessibleControlShape.hxx>
65
#include <svx/SvxShapeTypes.hxx>
66
#include <sfx2/objsh.hxx>
67
#include <editeng/editview.hxx>
68
#include <editeng/editeng.hxx>
69
#include <comphelper/processfactory.hxx>
70
71
#include <algorithm>
72
73
#include <scmod.hxx>
74
75
#ifdef indices
76
#undef indices
77
#endif
78
79
#ifdef extents
80
#undef extents
81
#endif
82
83
using namespace ::com::sun::star;
84
using namespace ::com::sun::star::accessibility;
85
86
namespace {
87
88
struct ScAccessibleShapeData
89
{
90
    ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_);
91
    ~ScAccessibleShapeData();
92
    mutable rtl::Reference< ::accessibility::AccessibleShape > pAccShape;
93
    std::optional<ScAddress> xRelationCell; // if it is NULL this shape is anchored on the table
94
    css::uno::Reference< css::drawing::XShape > xShape;
95
    bool                    bSelected;
96
    bool                    bSelectable;
97
    // cache these to make the sorting cheaper
98
    std::optional<sal_Int16> mxLayerID;
99
    std::optional<sal_Int32> mxZOrder;
100
};
101
102
}
103
104
ScAccessibleShapeData::ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_)
105
0
    : xShape(std::move(xShape_)),
106
0
    bSelected(false), bSelectable(true)
107
0
{
108
0
    static constexpr OUStringLiteral gsLayerId = u"LayerID";
109
0
    static constexpr OUStringLiteral gsZOrder = u"ZOrder";
110
0
    uno::Reference< beans::XPropertySet> xProps(xShape, uno::UNO_QUERY);
111
0
    if (xProps.is())
112
0
    {
113
0
        uno::Any aAny = xProps->getPropertyValue(gsLayerId);
114
0
        sal_Int16 nLayerID;
115
0
        if (aAny >>= nLayerID)
116
0
            mxLayerID = nLayerID;
117
0
        sal_Int32 nZOrder;
118
0
        aAny = xProps->getPropertyValue(gsZOrder);
119
0
        if (aAny >>= nZOrder)
120
0
            mxZOrder = nZOrder;
121
0
    }
122
0
}
123
124
ScAccessibleShapeData::~ScAccessibleShapeData()
125
0
{
126
0
    if (pAccShape.is())
127
0
    {
128
0
        pAccShape->dispose();
129
0
    }
130
0
}
131
132
namespace {
133
134
struct ScShapeDataLess
135
{
136
    static void ConvertLayerId(sal_Int16& rLayerID) // changes the number of the LayerId so it the accessibility order
137
0
    {
138
        // note: MSVC 2017 ICE's if this is written as "switch" so use "if"
139
0
        if (SC_LAYER_FRONT.get() == rLayerID)
140
0
        {
141
0
            rLayerID = 1;
142
0
        }
143
0
        else if (SC_LAYER_BACK.get() == rLayerID)
144
0
        {
145
0
            rLayerID = 0;
146
0
        }
147
0
        else if (SC_LAYER_INTERN.get() == rLayerID)
148
0
        {
149
0
            rLayerID = 2;
150
0
        }
151
0
        else if (SC_LAYER_CONTROLS.get() == rLayerID)
152
0
        {
153
0
            rLayerID = 3;
154
0
        }
155
0
    }
156
    static bool LessThanSheet(const ScAccessibleShapeData* pData)
157
0
    {
158
0
        bool bResult(false);
159
0
        if (pData->mxLayerID)
160
0
        {
161
0
            if (SdrLayerID(*pData->mxLayerID) == SC_LAYER_BACK)
162
0
                bResult = true;
163
0
        }
164
0
        return bResult;
165
0
    }
166
    bool operator()(const ScAccessibleShapeData* pData1, const ScAccessibleShapeData* pData2) const
167
0
    {
168
0
        bool bResult(false);
169
0
        if (pData1 && pData2)
170
0
        {
171
0
            if( pData1->mxLayerID && pData2->mxLayerID )
172
0
            {
173
0
                sal_Int16 nLayerID1 = *pData1->mxLayerID;
174
0
                sal_Int16 nLayerID2 = *pData2->mxLayerID;
175
0
                if (nLayerID1 == nLayerID2)
176
0
                {
177
0
                    if ( pData1->mxZOrder && pData2->mxZOrder )
178
0
                        bResult = (*pData1->mxZOrder < *pData2->mxZOrder);
179
0
                }
180
0
                else
181
0
                {
182
0
                    ConvertLayerId(nLayerID1);
183
0
                    ConvertLayerId(nLayerID2);
184
0
                    bResult = (nLayerID1 < nLayerID2);
185
0
                }
186
0
            }
187
0
        }
188
0
        else if (pData1 && !pData2)
189
0
            bResult = LessThanSheet(pData1);
190
0
        else if (!pData1 && pData2)
191
0
            bResult = !LessThanSheet(pData2);
192
0
        else
193
0
            bResult = false;
194
0
        return bResult;
195
0
    }
196
};
197
198
}
199
200
class ScChildrenShapes : public SfxListener,
201
    public ::accessibility::IAccessibleParent
202
{
203
public:
204
    ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
205
    virtual ~ScChildrenShapes() override;
206
207
    ///=====  SfxListener  =====================================================
208
209
    virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
210
211
    ///=====  IAccessibleParent  ===============================================
212
213
    virtual bool ReplaceChild (
214
        ::accessibility::AccessibleShape* pCurrentChild,
215
        const css::uno::Reference< css::drawing::XShape >& _rxShape,
216
        const tools::Long _nIndex,
217
        const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
218
    ) override;
219
220
    virtual ::accessibility::AccessibleControlShape* GetAccControlShapeFromModel
221
        (css::beans::XPropertySet* pSet) override;
222
    virtual ::accessibility::AccessibleShape*
223
        GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
224
    ///=====  Internal  ========================================================
225
    void SetDrawBroadcaster();
226
227
    sal_Int32 GetCount() const;
228
    rtl::Reference<::accessibility::AccessibleShape> Get(const ScAccessibleShapeData* pData) const;
229
    rtl::Reference<::accessibility::AccessibleShape> Get(sal_Int32 nIndex) const;
230
    uno::Reference< XAccessible > GetAt(const awt::Point& rPoint) const;
231
232
    // gets the index of the shape starting on 0 (without the index of the table)
233
    // returns the selected shape
234
    bool IsSelected(sal_Int32 nIndex,
235
        css::uno::Reference<css::drawing::XShape>& rShape) const;
236
237
    bool SelectionChanged();
238
239
    void Select(sal_Int32 nIndex);
240
    void DeselectAll(); // deselect also the table
241
    void SelectAll();
242
    sal_Int32 GetSelectedCount() const;
243
    rtl::Reference<::accessibility::AccessibleShape> GetSelected(sal_Int32 nSelectedChildIndex,
244
                                                                 bool bTabSelected) const;
245
    void Deselect(sal_Int32 nChildIndex);
246
247
    SdrPage* GetDrawPage() const;
248
249
    rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAddress* pAddress) const;
250
251
    void VisAreaChanged() const;
252
private:
253
    typedef std::vector<ScAccessibleShapeData*> SortedShapes;
254
    typedef std::unordered_map<css::uno::Reference< css::drawing::XShape >, ScAccessibleShapeData*> ShapesMap;
255
256
    mutable SortedShapes maZOrderedShapes; // a null pointer represents the sheet in the correct order
257
    mutable ShapesMap maShapesMap;
258
    mutable bool mbShapesNeedSorting; // set if maZOrderedShapes needs sorting
259
260
    mutable ::accessibility::AccessibleShapeTreeInfo maShapeTreeInfo;
261
    mutable css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier;
262
    mutable sal_uInt32 mnShapesSelected;
263
    ScTabViewShell* mpViewShell;
264
    ScAccessibleDocument* mpAccessibleDocument;
265
    ScSplitPos meSplitPos;
266
267
    void FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const;
268
    bool FindSelectedShapesChanges(const css::uno::Reference<css::drawing::XShapes>& xShapes) const;
269
270
    std::optional<ScAddress> GetAnchor(const uno::Reference<drawing::XShape>& xShape) const;
271
    rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAccessibleShapeData* pData) const;
272
    void SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const;
273
    void AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const;
274
    void RemoveShape(const uno::Reference<drawing::XShape>& xShape) const;
275
276
    bool FindShape(const uno::Reference<drawing::XShape>& xShape, SortedShapes::iterator& rItr) const;
277
278
    static sal_Int8 Compare(const ScAccessibleShapeData* pData1,
279
        const ScAccessibleShapeData* pData2);
280
};
281
282
ScChildrenShapes::ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
283
    :
284
0
    mbShapesNeedSorting(false),
285
0
    mnShapesSelected(0),
286
0
    mpViewShell(pViewShell),
287
0
    mpAccessibleDocument(pAccessibleDocument),
288
0
    meSplitPos(eSplitPos)
289
0
{
290
0
    if (mpViewShell)
291
0
    {
292
0
        SfxViewFrame& rViewFrame = mpViewShell->GetViewFrame();
293
0
        xSelectionSupplier = uno::Reference<view::XSelectionSupplier>(rViewFrame.GetFrame().GetController(), uno::UNO_QUERY);
294
0
        if (xSelectionSupplier.is())
295
0
        {
296
0
            xSelectionSupplier->addSelectionChangeListener(mpAccessibleDocument);
297
0
            uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
298
0
            if (xShapes.is())
299
0
                mnShapesSelected = xShapes->getCount();
300
0
        }
301
0
    }
302
303
0
    maZOrderedShapes.push_back(nullptr); // add an element which represents the table
304
305
0
    GetCount(); // fill list with filtered shapes (no internal shapes)
306
307
0
    if (mnShapesSelected)
308
0
    {
309
        //set flag on every selected shape
310
0
        if (!xSelectionSupplier.is())
311
0
            throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::ScChildrenShapes."_ustr);
312
313
0
        uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
314
0
        if (xShapes.is())
315
0
            FindSelectedShapesChanges(xShapes);
316
0
    }
317
0
    if (!pViewShell)
318
0
        return;
319
320
0
    ScViewData& rViewData = pViewShell->GetViewData();
321
0
    SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
322
0
    if (pDrawBC)
323
0
    {
324
0
        StartListening(*pDrawBC);
325
326
0
        maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
327
0
        maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
328
0
        maShapeTreeInfo.SetController(nullptr);
329
0
        maShapeTreeInfo.SetWindow(pViewShell->GetWindowByPos(meSplitPos));
330
0
        maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
331
0
    }
332
0
}
333
334
ScChildrenShapes::~ScChildrenShapes()
335
0
{
336
0
    for (ScAccessibleShapeData* pShapeData : maZOrderedShapes)
337
0
        delete pShapeData;
338
0
    if (mpViewShell)
339
0
    {
340
0
        SfxBroadcaster* pDrawBC = mpViewShell->GetViewData().GetDocument().GetDrawBroadcaster();
341
0
        if (pDrawBC)
342
0
            EndListening(*pDrawBC);
343
0
    }
344
0
    if (mpAccessibleDocument && xSelectionSupplier.is())
345
0
        xSelectionSupplier->removeSelectionChangeListener(mpAccessibleDocument);
346
0
}
347
348
void ScChildrenShapes::SetDrawBroadcaster()
349
0
{
350
0
    if (!mpViewShell)
351
0
        return;
352
353
0
    ScViewData& rViewData = mpViewShell->GetViewData();
354
0
    SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
355
0
    if (pDrawBC)
356
0
    {
357
0
        StartListening(*pDrawBC, DuplicateHandling::Prevent);
358
359
0
        maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
360
0
        maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
361
0
        maShapeTreeInfo.SetController(nullptr);
362
0
        maShapeTreeInfo.SetWindow(mpViewShell->GetWindowByPos(meSplitPos));
363
0
        maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
364
0
    }
365
0
}
366
367
void ScChildrenShapes::Notify(SfxBroadcaster&, const SfxHint& rHint)
368
0
{
369
0
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
370
0
        return;
371
0
    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
372
373
0
    SdrObject* pObj = const_cast<SdrObject*>(pSdrHint->GetObject());
374
0
    if (!(pObj && /*(pObj->GetLayer() != SC_LAYER_INTERN) && */(pObj->getSdrPageFromSdrObject() == GetDrawPage()) &&
375
0
        (pObj->getSdrPageFromSdrObject() == pObj->getParentSdrObjListFromSdrObject())) ) //only do something if the object lies direct on the page
376
0
        return;
377
378
0
    switch (pSdrHint->GetKind())
379
0
    {
380
0
        case SdrHintKind::ObjectChange :         // object changed
381
0
        {
382
0
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
383
0
            if (xShape.is())
384
0
            {
385
0
                mbShapesNeedSorting = true; // sort, because the z index or layer could be changed
386
0
                auto it = maShapesMap.find(xShape);
387
0
                if (it != maShapesMap.end())
388
0
                    SetAnchor(xShape, it->second);
389
0
            }
390
0
        }
391
0
        break;
392
0
        case SdrHintKind::ObjectInserted :    // new drawing object inserted
393
0
        {
394
0
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
395
0
            if (xShape.is())
396
0
                AddShape(xShape, true);
397
0
        }
398
0
        break;
399
0
        case SdrHintKind::ObjectRemoved :     // Removed drawing object from list
400
0
        {
401
0
            uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
402
0
            if (xShape.is())
403
0
                RemoveShape(xShape);
404
0
        }
405
0
        break;
406
0
        default :
407
0
        {
408
            // other events are not interesting
409
0
        }
410
0
        break;
411
0
    }
412
0
}
413
414
bool ScChildrenShapes::ReplaceChild (::accessibility::AccessibleShape* pCurrentChild,
415
        const css::uno::Reference< css::drawing::XShape >& _rxShape,
416
        const tools::Long /*_nIndex*/, const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo)
417
0
{
418
    // create the new child
419
0
    rtl::Reference< ::accessibility::AccessibleShape > pReplacement(::accessibility::ShapeTypeHandler::Instance().CreateAccessibleObject (
420
0
        ::accessibility::AccessibleShapeInfo ( _rxShape, pCurrentChild->getAccessibleParent(), this ),
421
0
        _rShapeTreeInfo
422
0
    ));
423
424
0
    bool bResult(false);
425
0
    if (pReplacement.is())
426
0
    {
427
0
        OSL_ENSURE(pCurrentChild->GetXShape().get() == pReplacement->GetXShape().get(), "XShape changes and should be inserted sorted");
428
0
        auto it = maShapesMap.find(pCurrentChild->GetXShape());
429
0
        if (it != maShapesMap.end() && it->second->pAccShape.is())
430
0
        {
431
0
            OSL_ENSURE(it->second->pAccShape == pCurrentChild, "wrong child found");
432
            // child is gone - event
433
0
            mpAccessibleDocument->CommitChange(AccessibleEventId::CHILD,
434
0
                                               uno::Any(uno::Reference<XAccessible>(pCurrentChild)),
435
0
                                               uno::Any());
436
437
0
            pCurrentChild->dispose();
438
0
        }
439
440
        // Init after above possible pCurrentChild->dispose so we don't trigger the assert
441
        // ScDrawModelBroadcaster::addShapeEventListener of duplicate listeners
442
0
        pReplacement->Init();
443
444
0
        if (it != maShapesMap.end())
445
0
        {
446
0
            it->second->pAccShape = pReplacement;
447
            // child is new - event
448
0
            mpAccessibleDocument->CommitChange(AccessibleEventId::CHILD, uno::Any(),
449
0
                                               uno::Any(uno::Reference<XAccessible>(pReplacement)));
450
0
            bResult = true;
451
0
        }
452
0
    }
453
0
    return bResult;
454
0
}
455
456
::accessibility::AccessibleControlShape * ScChildrenShapes::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
457
0
{
458
0
    GetCount(); // populate
459
0
    for (ScAccessibleShapeData* pShape : maZOrderedShapes)
460
0
    {
461
0
        if (pShape)
462
0
        {
463
0
            rtl::Reference< ::accessibility::AccessibleShape > pAccShape(pShape->pAccShape);
464
0
            if (pAccShape.is() && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
465
0
            {
466
0
                ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape.get());
467
0
                if (pCtlAccShape && pCtlAccShape->GetControlModel() == pSet)
468
0
                    return pCtlAccShape;
469
0
            }
470
0
        }
471
0
    }
472
0
    return nullptr;
473
0
}
474
475
::accessibility::AccessibleShape*
476
ScChildrenShapes::GetAccessibleCaption (const css::uno::Reference < css::drawing::XShape>& xShape)
477
0
{
478
0
    GetCount(); // populate
479
0
    auto it = maShapesMap.find(xShape);
480
0
    if (it == maShapesMap.end())
481
0
        return nullptr;
482
0
    ScAccessibleShapeData* pShape = it->second;
483
0
    return pShape->pAccShape.get();
484
0
}
485
486
sal_Int32 ScChildrenShapes::GetCount() const
487
0
{
488
0
    SdrPage* pDrawPage = GetDrawPage();
489
0
    if (pDrawPage && (maZOrderedShapes.size() == 1)) // the table is always in
490
0
    {
491
0
        size_t nSdrObjCount = pDrawPage->GetObjCount();
492
0
        maZOrderedShapes.reserve(nSdrObjCount + 1); // the table is always in
493
0
        for (const rtl::Reference<SdrObject>& pObj : *pDrawPage)
494
0
        {
495
0
            uno::Reference< drawing::XShape > xShape (pObj->getUnoShape(), uno::UNO_QUERY);
496
0
            AddShape(xShape, false); //inserts in the correct order
497
0
        }
498
0
    }
499
0
    return maZOrderedShapes.size();
500
0
}
501
502
rtl::Reference<::accessibility::AccessibleShape>
503
ScChildrenShapes::Get(const ScAccessibleShapeData* pData) const
504
0
{
505
0
    if (!pData)
506
0
        return nullptr;
507
508
0
    if (!pData->pAccShape.is())
509
0
    {
510
0
        ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
511
0
        ::accessibility::AccessibleShapeInfo aShapeInfo(pData->xShape, mpAccessibleDocument, const_cast<ScChildrenShapes*>(this));
512
0
        pData->pAccShape = rShapeHandler.CreateAccessibleObject(
513
0
            aShapeInfo, maShapeTreeInfo);
514
0
        if (pData->pAccShape.is())
515
0
        {
516
0
            pData->pAccShape->Init();
517
0
            if (pData->bSelected)
518
0
                pData->pAccShape->SetState(AccessibleStateType::SELECTED);
519
0
            if (!pData->bSelectable)
520
0
                pData->pAccShape->ResetState(AccessibleStateType::SELECTABLE);
521
0
            pData->pAccShape->SetRelationSet(GetRelationSet(pData));
522
0
        }
523
0
    }
524
0
    return pData->pAccShape;
525
0
 }
526
527
rtl::Reference<::accessibility::AccessibleShape> ScChildrenShapes::Get(sal_Int32 nIndex) const
528
0
{
529
0
    if (maZOrderedShapes.size() <= 1)
530
0
        GetCount(); // fill list with filtered shapes (no internal shapes)
531
532
0
    if (mbShapesNeedSorting)
533
0
    {
534
0
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
535
0
        mbShapesNeedSorting = false;
536
0
    }
537
538
0
    if (o3tl::make_unsigned(nIndex) >= maZOrderedShapes.size())
539
0
        return nullptr;
540
541
0
    return Get(maZOrderedShapes[nIndex]);
542
0
}
543
544
uno::Reference< XAccessible > ScChildrenShapes::GetAt(const awt::Point& rPoint) const
545
0
{
546
0
    uno::Reference<XAccessible> xAccessible;
547
0
    if(mpViewShell)
548
0
    {
549
0
        if (mbShapesNeedSorting)
550
0
        {
551
0
            std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
552
0
            mbShapesNeedSorting = false;
553
0
        }
554
555
0
        sal_Int32 i(maZOrderedShapes.size() - 1);
556
0
        bool bFound(false);
557
0
        while (!bFound && i >= 0)
558
0
        {
559
0
            ScAccessibleShapeData* pShape = maZOrderedShapes[i];
560
0
            if (pShape)
561
0
            {
562
0
                if (!pShape->pAccShape.is())
563
0
                    Get(pShape);
564
565
0
                if (pShape->pAccShape.is())
566
0
                {
567
0
                    Point aPoint(vcl::unohelper::ConvertToVCLPoint(rPoint));
568
0
                    aPoint
569
0
                        -= vcl::unohelper::ConvertToVCLRect(pShape->pAccShape->getBounds()).TopLeft();
570
0
                    if (pShape->pAccShape->containsPoint(vcl::unohelper::ConvertToAWTPoint(aPoint)))
571
0
                    {
572
0
                        xAccessible = pShape->pAccShape.get();
573
0
                        bFound = true;
574
0
                    }
575
0
                }
576
0
                else
577
0
                {
578
0
                    OSL_FAIL("I should have an accessible shape now!");
579
0
                }
580
0
            }
581
0
            else
582
0
                bFound = true; // this is the sheet and it lies before the rest of the shapes which are background shapes
583
584
0
            --i;
585
0
        }
586
0
    }
587
0
    return xAccessible;
588
0
}
589
590
bool ScChildrenShapes::IsSelected(sal_Int32 nIndex,
591
                        uno::Reference<drawing::XShape>& rShape) const
592
0
{
593
0
    bool bResult (false);
594
0
    if (maZOrderedShapes.size() <= 1)
595
0
        GetCount(); // fill list with filtered shapes (no internal shapes)
596
597
0
    if (!xSelectionSupplier.is())
598
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::IsSelected."_ustr);
599
600
0
    if (mbShapesNeedSorting)
601
0
    {
602
0
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
603
0
        mbShapesNeedSorting = false;
604
0
    }
605
606
0
    if (!maZOrderedShapes[nIndex])
607
0
        return false;
608
609
0
    bResult = maZOrderedShapes[nIndex]->bSelected;
610
0
    rShape = maZOrderedShapes[nIndex]->xShape;
611
612
#if OSL_DEBUG_LEVEL > 0 // test whether it is truly selected by a slower method
613
    uno::Reference< drawing::XShape > xReturnShape;
614
    bool bDebugResult(false);
615
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
616
617
    if (xShapes.is())
618
    {
619
        sal_Int32 nCount(xShapes->getCount());
620
        if (nCount)
621
        {
622
            uno::Reference< drawing::XShape > xShape;
623
            uno::Reference< drawing::XShape > xIndexShape = maZOrderedShapes[nIndex]->xShape;
624
            sal_Int32 i(0);
625
            while (!bDebugResult && (i < nCount))
626
            {
627
                xShapes->getByIndex(i) >>= xShape;
628
                if (xShape.is() && (xIndexShape.get() == xShape.get()))
629
                {
630
                    bDebugResult = true;
631
                    xReturnShape = xShape;
632
                }
633
                else
634
                    ++i;
635
            }
636
        }
637
    }
638
    OSL_ENSURE((bResult == bDebugResult) && ((bResult && (rShape.get() == xReturnShape.get())) || !bResult), "found the wrong shape or result");
639
#endif
640
641
0
    return bResult;
642
0
}
643
644
bool ScChildrenShapes::SelectionChanged()
645
0
{
646
0
    bool bResult(false);
647
0
    if (!xSelectionSupplier.is())
648
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectionChanged."_ustr);
649
650
0
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
651
652
0
    bResult = FindSelectedShapesChanges(xShapes);
653
654
0
    return bResult;
655
0
}
656
657
void ScChildrenShapes::Select(sal_Int32 nIndex)
658
0
{
659
0
    if (maZOrderedShapes.size() <= 1)
660
0
        GetCount(); // fill list with filtered shapes (no internal shapes)
661
662
0
    if (!xSelectionSupplier.is())
663
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::Select."_ustr);
664
665
0
    if (mbShapesNeedSorting)
666
0
    {
667
0
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
668
0
        mbShapesNeedSorting = false;
669
0
    }
670
671
0
    if (!maZOrderedShapes[nIndex])
672
0
        return;
673
674
0
    uno::Reference<drawing::XShape> xShape;
675
0
    if (IsSelected(nIndex, xShape) || !maZOrderedShapes[nIndex]->bSelectable)
676
0
        return;
677
678
0
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
679
680
0
    if (!xShapes.is())
681
0
        xShapes = drawing::ShapeCollection::create(
682
0
                comphelper::getProcessComponentContext());
683
684
0
    xShapes->add(maZOrderedShapes[nIndex]->xShape);
685
686
0
    try
687
0
    {
688
0
        xSelectionSupplier->select(uno::Any(xShapes));
689
0
        maZOrderedShapes[nIndex]->bSelected = true;
690
0
        if (maZOrderedShapes[nIndex]->pAccShape.is())
691
0
            maZOrderedShapes[nIndex]->pAccShape->SetState(AccessibleStateType::SELECTED);
692
0
    }
693
0
    catch (lang::IllegalArgumentException&)
694
0
    {
695
0
    }
696
0
}
697
698
void ScChildrenShapes::DeselectAll()
699
0
{
700
0
    if (!xSelectionSupplier.is())
701
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::DeselectAll."_ustr);
702
703
0
    bool bSomethingSelected(true);
704
0
    try
705
0
    {
706
0
        xSelectionSupplier->select(uno::Any()); //deselects all
707
0
    }
708
0
    catch (lang::IllegalArgumentException&)
709
0
    {
710
0
        OSL_FAIL("nothing selected before");
711
0
        bSomethingSelected = false;
712
0
    }
713
714
0
    if (bSomethingSelected)
715
0
        for (ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
716
0
            if (pAccShapeData)
717
0
            {
718
0
                pAccShapeData->bSelected = false;
719
0
                if (pAccShapeData->pAccShape.is())
720
0
                    pAccShapeData->pAccShape->ResetState(AccessibleStateType::SELECTED);
721
0
            }
722
0
};
723
724
725
void ScChildrenShapes::SelectAll()
726
0
{
727
0
    if (!xSelectionSupplier.is())
728
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectAll."_ustr);
729
730
0
    if (maZOrderedShapes.size() <= 1)
731
0
        GetCount(); // fill list with filtered shapes (no internal shapes)
732
733
0
    if (maZOrderedShapes.size() <= 1)
734
0
        return;
735
736
0
    uno::Reference<drawing::XShapes> xShapes = drawing::ShapeCollection::create(
737
0
            comphelper::getProcessComponentContext());
738
739
0
    try
740
0
    {
741
0
        for (ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
742
0
        {
743
0
            if (pAccShapeData && pAccShapeData->bSelectable)
744
0
            {
745
0
                pAccShapeData->bSelected = true;
746
0
                if (pAccShapeData->pAccShape.is())
747
0
                    pAccShapeData->pAccShape->SetState(AccessibleStateType::SELECTED);
748
0
                if (xShapes.is())
749
0
                    xShapes->add(pAccShapeData->xShape);
750
0
            }
751
0
        }
752
0
        xSelectionSupplier->select(uno::Any(xShapes));
753
0
    }
754
0
    catch (lang::IllegalArgumentException&)
755
0
    {
756
0
        SelectionChanged(); // find all selected shapes and set the flags
757
0
    }
758
0
}
759
760
void ScChildrenShapes::FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const
761
0
{
762
0
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
763
0
    if (xShapes.is())
764
0
    {
765
0
        sal_uInt32 nCount(xShapes->getCount());
766
0
        for (sal_uInt32 i = 0; i < nCount; ++i)
767
0
        {
768
0
            uno::Reference<drawing::XShape> xShape;
769
0
            xShapes->getByIndex(i) >>= xShape;
770
0
            if (xShape.is())
771
0
                rShapes.push_back(xShape);
772
0
        }
773
0
    }
774
0
}
775
776
sal_Int32 ScChildrenShapes::GetSelectedCount() const
777
0
{
778
0
    if (!xSelectionSupplier.is())
779
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::GetSelectedCount."_ustr);
780
781
0
    std::vector < uno::Reference < drawing::XShape > > aShapes;
782
0
    FillShapes(aShapes);
783
784
0
    return aShapes.size();
785
0
}
786
787
rtl::Reference<::accessibility::AccessibleShape>
788
ScChildrenShapes::GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const
789
0
{
790
0
    rtl::Reference<::accessibility::AccessibleShape> xAccessible;
791
792
0
    if (maZOrderedShapes.size() <= 1)
793
0
        GetCount(); // fill list with shapes
794
795
0
    if (!bTabSelected)
796
0
    {
797
0
        std::vector < uno::Reference < drawing::XShape > > aShapes;
798
0
        FillShapes(aShapes);
799
800
0
        if (nSelectedChildIndex < 0 || o3tl::make_unsigned(nSelectedChildIndex) >= aShapes.size())
801
0
            return xAccessible;
802
803
0
        SortedShapes::iterator aItr;
804
0
        if (FindShape(aShapes[nSelectedChildIndex], aItr))
805
0
            xAccessible = Get(*aItr);
806
0
    }
807
0
    else
808
0
    {
809
0
        if (mbShapesNeedSorting)
810
0
        {
811
0
            std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
812
0
            mbShapesNeedSorting = false;
813
0
        }
814
0
        for(const auto& rpShape : maZOrderedShapes)
815
0
        {
816
0
            if (!rpShape || rpShape->bSelected)
817
0
            {
818
0
                if (nSelectedChildIndex == 0)
819
0
                {
820
0
                    if (rpShape)
821
0
                        xAccessible = rpShape->pAccShape.get();
822
0
                    break;
823
0
                }
824
0
                else
825
0
                    --nSelectedChildIndex;
826
0
            }
827
0
        }
828
0
    }
829
830
0
    return xAccessible;
831
0
}
832
833
void ScChildrenShapes::Deselect(sal_Int32 nChildIndex)
834
0
{
835
0
    uno::Reference<drawing::XShape> xShape;
836
0
    if (!IsSelected(nChildIndex, xShape)) // returns false if it is the sheet
837
0
        return;
838
839
0
    if (!xShape.is())
840
0
        return;
841
842
0
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
843
0
    if (xShapes.is())
844
0
        xShapes->remove(xShape);
845
846
0
    try
847
0
    {
848
0
        xSelectionSupplier->select(uno::Any(xShapes));
849
0
    }
850
0
    catch (lang::IllegalArgumentException&)
851
0
    {
852
0
        OSL_FAIL("something not selectable");
853
0
    }
854
855
0
    maZOrderedShapes[nChildIndex]->bSelected = false;
856
0
    if (maZOrderedShapes[nChildIndex]->pAccShape.is())
857
0
        maZOrderedShapes[nChildIndex]->pAccShape->ResetState(AccessibleStateType::SELECTED);
858
0
}
859
860
SdrPage* ScChildrenShapes::GetDrawPage() const
861
0
{
862
0
    SCTAB nTab(mpAccessibleDocument->getVisibleTable());
863
0
    SdrPage* pDrawPage = nullptr;
864
0
    if (mpViewShell)
865
0
    {
866
0
        ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
867
0
        if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer())
868
0
        {
869
0
            if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
870
0
                pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
871
0
        }
872
0
    }
873
0
    return pDrawPage;
874
0
}
875
876
rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const
877
0
{
878
0
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
879
0
    for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
880
0
    {
881
0
        if (pAccShapeData &&
882
0
            ((!pAccShapeData->xRelationCell && !pAddress) ||
883
0
            (pAccShapeData->xRelationCell && pAddress && (*(pAccShapeData->xRelationCell) == *pAddress))))
884
0
        {
885
0
            if (!pRelationSet)
886
0
                pRelationSet = new utl::AccessibleRelationSetHelper();
887
888
0
            AccessibleRelation aRelation;
889
0
            aRelation.TargetSet = { Get(pAccShapeData) };
890
0
            aRelation.RelationType = AccessibleRelationType_CONTROLLER_FOR;
891
892
0
            pRelationSet->AddRelation(aRelation);
893
0
        }
894
0
    }
895
0
    return pRelationSet;
896
0
}
897
898
bool ScChildrenShapes::FindSelectedShapesChanges(const uno::Reference<drawing::XShapes>& xShapes) const
899
0
{
900
0
    bool bResult(false);
901
0
    SortedShapes aShapesList;
902
0
    if (xShapes.is())
903
0
    {
904
0
        mnShapesSelected = xShapes->getCount();
905
0
        for (sal_uInt32 i = 0; i < mnShapesSelected; ++i)
906
0
        {
907
0
            uno::Reference< drawing::XShape > xShape;
908
0
            xShapes->getByIndex(i) >>= xShape;
909
0
            if (xShape.is())
910
0
            {
911
0
                ScAccessibleShapeData* pShapeData = new ScAccessibleShapeData(xShape);
912
0
                aShapesList.push_back(pShapeData);
913
0
            }
914
0
        }
915
0
    }
916
0
    else
917
0
        mnShapesSelected = 0;
918
0
    SdrObject *pFocusedObj = nullptr;
919
0
    if( mnShapesSelected == 1 && aShapesList.size() == 1)
920
0
    {
921
0
        pFocusedObj = SdrObject::getSdrObjectFromXShape(aShapesList[0]->xShape);
922
0
    }
923
0
    std::sort(aShapesList.begin(), aShapesList.end(), ScShapeDataLess());
924
0
    SortedShapes vecSelectedShapeAdd;
925
0
    SortedShapes vecSelectedShapeRemove;
926
0
    bool bHasSelect=false;
927
0
    SortedShapes::iterator aXShapesItr(aShapesList.begin());
928
0
    SortedShapes::const_iterator aXShapesEndItr(aShapesList.end());
929
0
    SortedShapes::iterator aDataItr(maZOrderedShapes.begin());
930
0
    SortedShapes::const_iterator aDataEndItr(maZOrderedShapes.end());
931
0
    SortedShapes::const_iterator aFocusedItr = aDataEndItr;
932
0
    while(aDataItr != aDataEndItr)
933
0
    {
934
0
        if (*aDataItr) // is it really a shape or only the sheet
935
0
        {
936
0
            sal_Int8 nComp(0);
937
0
            if (aXShapesItr == aXShapesEndItr)
938
0
                nComp = -1; // simulate that the Shape is lower, so the selection state will be removed
939
0
            else
940
0
                nComp = Compare(*aDataItr, *aXShapesItr);
941
0
            if (nComp == 0)
942
0
            {
943
0
                if (!(*aDataItr)->bSelected)
944
0
                {
945
0
                    (*aDataItr)->bSelected = true;
946
0
                    if ((*aDataItr)->pAccShape.is())
947
0
                    {
948
0
                        (*aDataItr)->pAccShape->SetState(AccessibleStateType::SELECTED);
949
0
                        (*aDataItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
950
0
                        bResult = true;
951
0
                        vecSelectedShapeAdd.push_back(*aDataItr);
952
0
                    }
953
0
                    aFocusedItr = aDataItr;
954
0
                }
955
0
                else
956
0
                {
957
0
                     bHasSelect = true;
958
0
                }
959
0
                ++aDataItr;
960
0
                ++aXShapesItr;
961
0
            }
962
0
            else if (nComp < 0)
963
0
            {
964
0
                if ((*aDataItr)->bSelected)
965
0
                {
966
0
                    (*aDataItr)->bSelected = false;
967
0
                    if ((*aDataItr)->pAccShape.is())
968
0
                    {
969
0
                        (*aDataItr)->pAccShape->ResetState(AccessibleStateType::SELECTED);
970
0
                        (*aDataItr)->pAccShape->ResetState(AccessibleStateType::FOCUSED);
971
0
                        bResult = true;
972
0
                        vecSelectedShapeRemove.push_back(*aDataItr);
973
0
                    }
974
0
                }
975
0
                ++aDataItr;
976
0
            }
977
0
            else
978
0
            {
979
0
                OSL_FAIL("here is a selected shape which is not in the childlist");
980
0
                ++aXShapesItr;
981
0
                --mnShapesSelected;
982
0
            }
983
0
        }
984
0
        else
985
0
            ++aDataItr;
986
0
    }
987
0
    bool bWinFocus=false;
988
0
    if (mpViewShell)
989
0
    {
990
0
        ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
991
0
        if (pWin)
992
0
        {
993
0
            bWinFocus = pWin->HasFocus();
994
0
        }
995
0
    }
996
0
    const SdrMarkList* pMarkList = nullptr;
997
0
    SdrObject* pMarkedObj = nullptr;
998
0
    bool bIsFocuseMarked = true;
999
0
    if( mpViewShell && mnShapesSelected == 1 && bWinFocus)
1000
0
    {
1001
0
        ScDrawView* pScDrawView = mpViewShell->GetViewData().GetScDrawView();
1002
0
        if( pScDrawView )
1003
0
        {
1004
0
            pMarkList = &(pScDrawView->GetMarkedObjectList());
1005
0
            if( pMarkList->GetMarkCount() == 1 )
1006
0
            {
1007
0
                pMarkedObj = pMarkList->GetMark(0)->GetMarkedSdrObj();
1008
0
                uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
1009
0
                if( aFocusedItr != aDataEndItr &&
1010
0
                    (*aFocusedItr)->xShape.is() &&
1011
0
                    xMarkedXShape.is() &&
1012
0
                    (*aFocusedItr)->xShape != xMarkedXShape )
1013
0
                    bIsFocuseMarked = false;
1014
0
            }
1015
0
        }
1016
0
    }
1017
    //if ((aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1))
1018
0
    if ( bIsFocuseMarked && (aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1) && bWinFocus)
1019
0
    {
1020
0
        (*aFocusedItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
1021
0
    }
1022
0
    else if( pFocusedObj && bWinFocus && pMarkList && pMarkList->GetMarkCount() == 1 && mnShapesSelected == 1 )
1023
0
    {
1024
0
        if( pMarkedObj )
1025
0
        {
1026
0
            uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
1027
0
            SdrObject* pUpObj = pMarkedObj->getParentSdrObjectFromSdrObject();
1028
1029
0
            if( pMarkedObj == pFocusedObj && pUpObj )
1030
0
            {
1031
0
                uno::Reference< drawing::XShape > xUpGroupXShape (pUpObj->getUnoShape(), uno::UNO_QUERY);
1032
0
                ::accessibility::AccessibleShape* pAccGroupShape =
1033
0
                    const_cast<ScChildrenShapes*>(this)->GetAccessibleCaption( xUpGroupXShape );
1034
0
                if( pAccGroupShape )
1035
0
                {
1036
0
                    sal_Int64 nCount =  pAccGroupShape->getAccessibleChildCount();
1037
0
                    for( sal_Int64 i = 0; i < nCount; i++ )
1038
0
                    {
1039
0
                        uno::Reference<XAccessible> xAccShape = pAccGroupShape->getAccessibleChild(i);
1040
0
                        if (xAccShape.is())
1041
0
                        {
1042
0
                            ::accessibility::AccessibleShape* pChildAccShape =  static_cast< ::accessibility::AccessibleShape* >(xAccShape.get());
1043
0
                            uno::Reference< drawing::XShape > xChildShape = pChildAccShape->GetXShape();
1044
0
                            if (xChildShape == xMarkedXShape)
1045
0
                            {
1046
0
                                pChildAccShape->SetState(AccessibleStateType::FOCUSED);
1047
0
                            }
1048
0
                            else
1049
0
                            {
1050
0
                                pChildAccShape->ResetState(AccessibleStateType::FOCUSED);
1051
0
                            }
1052
0
                        }
1053
0
                    }
1054
0
                }
1055
0
            }
1056
0
        }
1057
0
    }
1058
0
    if (vecSelectedShapeAdd.size() >= 10 )
1059
0
    {
1060
0
        mpAccessibleDocument->CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN, uno::Any(),
1061
0
                                           uno::Any());
1062
0
    }
1063
0
    else
1064
0
    {
1065
0
        for (const auto& rpShape : vecSelectedShapeAdd)
1066
0
        {
1067
0
            sal_Int16 nEventId;
1068
0
            if (bHasSelect)
1069
0
                nEventId = AccessibleEventId::SELECTION_CHANGED_ADD;
1070
0
            else
1071
0
                nEventId = AccessibleEventId::SELECTION_CHANGED;
1072
0
            mpAccessibleDocument->CommitChange(
1073
0
                nEventId, uno::Any(), uno::Any(uno::Reference<XAccessible>(rpShape->pAccShape)));
1074
0
        }
1075
0
    }
1076
0
    for (const auto& rpShape : vecSelectedShapeRemove)
1077
0
    {
1078
0
        mpAccessibleDocument->CommitChange(
1079
0
            AccessibleEventId::SELECTION_CHANGED_REMOVE, uno::Any(),
1080
0
            uno::Any(uno::Reference<XAccessible>(rpShape->pAccShape)));
1081
0
    }
1082
0
    for(ScAccessibleShapeData*& pShapeData : aShapesList)
1083
0
    {
1084
0
        delete pShapeData;
1085
0
        pShapeData = nullptr;
1086
0
    }
1087
0
    return bResult;
1088
0
}
1089
1090
std::optional<ScAddress> ScChildrenShapes::GetAnchor(const uno::Reference<drawing::XShape>& xShape) const
1091
0
{
1092
0
    if (mpViewShell)
1093
0
    {
1094
0
        SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
1095
0
        uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
1096
0
        if (pSdrObj && xShapeProp.is())
1097
0
        {
1098
0
            if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj))
1099
0
                return std::optional<ScAddress>(pAnchor->maStart);
1100
0
        }
1101
0
    }
1102
1103
0
    return std::optional<ScAddress>();
1104
0
}
1105
1106
rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const
1107
0
{
1108
0
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper();
1109
1110
0
    if (pData && mpAccessibleDocument)
1111
0
    {
1112
0
        uno::Reference<XAccessible> xAccessible = mpAccessibleDocument->GetAccessibleSpreadsheet(); // should be the current table
1113
0
        if (pData->xRelationCell && xAccessible.is())
1114
0
        {
1115
0
            sal_Int32 nRow = pData->xRelationCell->Row();
1116
0
            sal_Int32 nColumn = pData->xRelationCell->Col();
1117
0
            bool bPositionUnset = nRow == -1 && nColumn == -1;
1118
0
            if (!bPositionUnset)
1119
0
            {
1120
0
                uno::Reference<XAccessibleTable> xAccTable(xAccessible->getAccessibleContext(), uno::UNO_QUERY);
1121
0
                if (xAccTable.is())
1122
0
                    xAccessible = xAccTable->getAccessibleCellAt(nRow, nColumn);
1123
0
            }
1124
0
        }
1125
0
        AccessibleRelation aRelation;
1126
0
        aRelation.TargetSet = { xAccessible };
1127
0
        aRelation.RelationType = AccessibleRelationType_CONTROLLED_BY;
1128
0
        pRelationSet->AddRelation(aRelation);
1129
0
    }
1130
1131
0
    return pRelationSet;
1132
0
}
1133
1134
void ScChildrenShapes::SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const
1135
0
{
1136
0
    if (pData)
1137
0
    {
1138
0
        std::optional<ScAddress> xAddress = GetAnchor(xShape);
1139
0
        if ((xAddress && pData->xRelationCell && (*xAddress != *(pData->xRelationCell))) ||
1140
0
            (!xAddress && pData->xRelationCell) || (xAddress && !pData->xRelationCell))
1141
0
        {
1142
0
            pData->xRelationCell = std::move(xAddress);
1143
0
            if (pData->pAccShape.is())
1144
0
                pData->pAccShape->SetRelationSet(GetRelationSet(pData));
1145
0
        }
1146
0
    }
1147
0
}
1148
1149
void ScChildrenShapes::AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const
1150
0
{
1151
0
    assert( maShapesMap.find(xShape) == maShapesMap.end());
1152
1153
0
    ScAccessibleShapeData* pShape = new ScAccessibleShapeData(xShape);
1154
0
    maZOrderedShapes.push_back(pShape);
1155
0
    mbShapesNeedSorting = true;
1156
0
    maShapesMap[xShape] = pShape;
1157
0
    SetAnchor(xShape, pShape);
1158
1159
0
    uno::Reference< beans::XPropertySet > xShapeProp(xShape, uno::UNO_QUERY);
1160
0
    if (xShapeProp.is())
1161
0
    {
1162
0
        uno::Any aPropAny = xShapeProp->getPropertyValue(u"LayerID"_ustr);
1163
0
        sal_Int16 nLayerID = 0;
1164
0
        if( aPropAny >>= nLayerID )
1165
0
        {
1166
0
            if( (SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN) )
1167
0
                pShape->bSelectable = false;
1168
0
            else
1169
0
                pShape->bSelectable = true;
1170
0
        }
1171
0
    }
1172
1173
0
    if (!xSelectionSupplier.is())
1174
0
        throw uno::RuntimeException(u"Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::AddShape."_ustr);
1175
1176
0
    uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
1177
0
    uno::Reference<container::XEnumerationAccess> xEnumAcc(xShapes, uno::UNO_QUERY);
1178
0
    if (xEnumAcc.is())
1179
0
    {
1180
0
        uno::Reference<container::XEnumeration> xEnum = xEnumAcc->createEnumeration();
1181
0
        if (xEnum.is())
1182
0
        {
1183
0
            uno::Reference<drawing::XShape> xSelectedShape;
1184
0
            bool bFound(false);
1185
0
            while (!bFound && xEnum->hasMoreElements())
1186
0
            {
1187
0
                xEnum->nextElement() >>= xSelectedShape;
1188
0
                if (xShape.is() && (xShape.get() == xSelectedShape.get()))
1189
0
                {
1190
0
                    pShape->bSelected = true;
1191
0
                    bFound = true;
1192
0
                }
1193
0
            }
1194
0
        }
1195
0
    }
1196
0
    if (mpAccessibleDocument && bCommitChange)
1197
0
    {
1198
        // new child - event
1199
0
        mpAccessibleDocument->CommitChange(AccessibleEventId::CHILD, uno::Any(),
1200
0
                                           uno::Any(uno::Reference<XAccessible>(Get(pShape))));
1201
0
    }
1202
0
}
1203
1204
void ScChildrenShapes::RemoveShape(const uno::Reference<drawing::XShape>& xShape) const
1205
0
{
1206
0
    if (mbShapesNeedSorting)
1207
0
    {
1208
0
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
1209
0
        mbShapesNeedSorting = false;
1210
0
    }
1211
0
    SortedShapes::iterator aItr;
1212
0
    if (FindShape(xShape, aItr))
1213
0
    {
1214
0
        if (mpAccessibleDocument)
1215
0
        {
1216
0
            rtl::Reference<::accessibility::AccessibleShape> xOldAccessible(Get(*aItr));
1217
1218
0
            delete *aItr;
1219
0
            maShapesMap.erase((*aItr)->xShape);
1220
0
            maZOrderedShapes.erase(aItr);
1221
1222
            // child is gone - event
1223
0
            mpAccessibleDocument->CommitChange(
1224
0
                AccessibleEventId::CHILD, uno::Any(uno::Reference<XAccessible>(xOldAccessible)),
1225
0
                uno::Any());
1226
0
        }
1227
0
        else
1228
0
        {
1229
0
            delete *aItr;
1230
0
            maShapesMap.erase((*aItr)->xShape);
1231
0
            maZOrderedShapes.erase(aItr);
1232
0
        }
1233
0
    }
1234
0
    else
1235
0
    {
1236
0
        OSL_FAIL("shape was not in internal list");
1237
0
    }
1238
0
}
1239
1240
bool ScChildrenShapes::FindShape(const uno::Reference<drawing::XShape>& xShape, ScChildrenShapes::SortedShapes::iterator& rItr) const
1241
0
{
1242
0
    if (mbShapesNeedSorting)
1243
0
    {
1244
0
        std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
1245
0
        mbShapesNeedSorting = false;
1246
0
    }
1247
0
    bool bResult(false);
1248
0
    ScAccessibleShapeData aShape(xShape);
1249
0
    rItr = std::lower_bound(maZOrderedShapes.begin(), maZOrderedShapes.end(), &aShape, ScShapeDataLess());
1250
0
    if ((rItr != maZOrderedShapes.end()) && (*rItr != nullptr) && ((*rItr)->xShape.get() == xShape.get()))
1251
0
        bResult = true; // if the shape is found
1252
1253
#if OSL_DEBUG_LEVEL > 0 // test whether it finds truly the correct shape (perhaps it is not really sorted)
1254
    SortedShapes::iterator aDebugItr = std::find_if(maZOrderedShapes.begin(), maZOrderedShapes.end(),
1255
        [&xShape](const ScAccessibleShapeData* pShape) { return pShape && (pShape->xShape.get() == xShape.get()); });
1256
    bool bResult2 = (aDebugItr != maZOrderedShapes.end());
1257
    OSL_ENSURE((bResult == bResult2) && ((bResult && (rItr == aDebugItr)) || !bResult), "wrong Shape found");
1258
#endif
1259
0
    return bResult;
1260
0
}
1261
1262
sal_Int8 ScChildrenShapes::Compare(const ScAccessibleShapeData* pData1,
1263
        const ScAccessibleShapeData* pData2)
1264
0
{
1265
0
    ScShapeDataLess aLess;
1266
1267
0
    bool bResult1(aLess(pData1, pData2));
1268
0
    bool bResult2(aLess(pData2, pData1));
1269
1270
0
    sal_Int8 nResult(0);
1271
0
    if (!bResult1 && bResult2)
1272
0
        nResult = 1;
1273
0
    else if (bResult1 && !bResult2)
1274
0
        nResult = -1;
1275
1276
0
    return nResult;
1277
0
}
1278
1279
void ScChildrenShapes::VisAreaChanged() const
1280
0
{
1281
0
    for (const ScAccessibleShapeData* pAccShapeData: maZOrderedShapes)
1282
0
        if (pAccShapeData && pAccShapeData->pAccShape.is())
1283
0
            pAccShapeData->pAccShape->ViewForwarderChanged();
1284
0
}
1285
1286
ScAccessibleDocument::ScAccessibleDocument(
1287
        const uno::Reference<XAccessible>& rxParent,
1288
        ScTabViewShell* pViewShell,
1289
        ScSplitPos eSplitPos)
1290
0
    : ImplInheritanceHelper(rxParent),
1291
0
    mpViewShell(pViewShell),
1292
0
    meSplitPos(eSplitPos),
1293
0
    mbCompleteSheetSelected(false)
1294
0
{
1295
0
    maVisArea = GetVisibleArea_Impl();
1296
0
}
1297
1298
void ScAccessibleDocument::PreInit()
1299
0
{
1300
0
    if (!mpViewShell)
1301
0
        return;
1302
1303
0
    mpViewShell->AddAccessibilityObject(*this);
1304
0
    vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
1305
0
    if( pWin )
1306
0
    {
1307
0
        pWin->AddChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
1308
0
        sal_uInt16 nCount =   pWin->GetChildCount();
1309
0
        for( sal_uInt16 i=0; i < nCount; ++i )
1310
0
        {
1311
0
            vcl::Window *pChildWin = pWin->GetChild( i );
1312
0
            if( pChildWin &&
1313
0
                AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
1314
0
                AddChild( pChildWin->GetAccessible(), false );
1315
0
        }
1316
0
    }
1317
0
    ScViewData& rViewData = mpViewShell->GetViewData();
1318
0
    if (rViewData.HasEditView(meSplitPos))
1319
0
    {
1320
0
        uno::Reference<XAccessible> xAcc = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
1321
0
            mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(), GetCurrentCellDescription(),
1322
0
            ScAccessibleEditObject::CellInEditMode);
1323
0
        AddChild(xAcc, false);
1324
0
    }
1325
0
}
1326
1327
void ScAccessibleDocument::Init()
1328
0
{
1329
0
    if(!mpChildrenShapes)
1330
0
        mpChildrenShapes.reset( new ScChildrenShapes(this, mpViewShell, meSplitPos) );
1331
0
}
1332
1333
ScAccessibleDocument::~ScAccessibleDocument()
1334
0
{
1335
0
    if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
1336
0
    {
1337
        // increment refcount to prevent double call of dtor
1338
0
        osl_atomic_increment( &m_refCount );
1339
0
        dispose();
1340
0
    }
1341
0
}
1342
1343
void SAL_CALL ScAccessibleDocument::disposing()
1344
0
{
1345
0
    SolarMutexGuard aGuard;
1346
0
    FreeAccessibleSpreadsheet();
1347
0
    if (mpViewShell)
1348
0
    {
1349
0
        vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
1350
0
        if( pWin )
1351
0
            pWin->RemoveChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
1352
1353
0
        mpViewShell->RemoveAccessibilityObject(*this);
1354
0
        mpViewShell = nullptr;
1355
0
    }
1356
0
    mpChildrenShapes.reset();
1357
1358
0
    ScAccessibleDocumentBase::disposing();
1359
0
}
1360
1361
void SAL_CALL ScAccessibleDocument::disposing( const lang::EventObject& /* Source */ )
1362
0
{
1363
0
    disposing();
1364
0
}
1365
1366
    //=====  SfxListener  =====================================================
1367
1368
IMPL_LINK( ScAccessibleDocument, WindowChildEventListener, VclWindowEvent&, rEvent, void )
1369
0
{
1370
0
    OSL_ENSURE( rEvent.GetWindow(), "Window???" );
1371
0
    switch ( rEvent.GetId() )
1372
0
    {
1373
0
    case VclEventId::WindowShow:  // send create on show for direct accessible children
1374
0
        {
1375
0
            vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
1376
0
            if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
1377
0
            {
1378
0
                AddChild( pChildWin->GetAccessible(), true );
1379
0
            }
1380
0
        }
1381
0
        break;
1382
0
    case VclEventId::WindowHide:  // send destroy on hide for direct accessible children
1383
0
        {
1384
0
            vcl::Window* pChildWin = static_cast < vcl::Window * >( rEvent.GetData() );
1385
0
            if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
1386
0
            {
1387
0
                RemoveChild( pChildWin->GetAccessible(), true );
1388
0
            }
1389
0
        }
1390
0
        break;
1391
0
    default: break;
1392
0
    }
1393
0
}
1394
1395
void ScAccessibleDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
1396
0
{
1397
0
    if (rHint.GetId() == SfxHintId::ScAccGridWinFocusLost )
1398
0
    {
1399
0
        auto pFocusLostHint = static_cast<const ScAccGridWinFocusLostHint*>(&rHint);
1400
0
        if (pFocusLostHint->GetOldGridWin() == meSplitPos)
1401
0
        {
1402
0
            if (mxTempAcc.is() && mpTempAccEdit)
1403
0
                mpTempAccEdit->LostFocus();
1404
0
            else if (mpAccessibleSpreadsheet.is())
1405
0
                mpAccessibleSpreadsheet->LostFocus();
1406
0
            else
1407
0
                CommitFocusLost();
1408
0
        }
1409
0
    }
1410
0
    else if (rHint.GetId() == SfxHintId::ScAccGridWinFocusGot)
1411
0
    {
1412
0
        auto pFocusGotHint = static_cast<const ScAccGridWinFocusGotHint*>(&rHint);
1413
0
        if (pFocusGotHint->GetNewGridWin() == meSplitPos)
1414
0
        {
1415
0
            rtl::Reference<::accessibility::AccessibleShape> xAccShape;
1416
0
            if (mpChildrenShapes)
1417
0
            {
1418
0
                bool bTabMarked(IsTableSelected());
1419
0
                xAccShape = mpChildrenShapes->GetSelected(0, bTabMarked);
1420
0
            }
1421
0
            if (xAccShape.is())
1422
0
            {
1423
0
                uno::Any aNewValue;
1424
0
                aNewValue<<=AccessibleStateType::FOCUSED;
1425
0
                xAccShape->CommitChange(AccessibleEventId::STATE_CHANGED, aNewValue, uno::Any(),
1426
0
                                        -1);
1427
0
            }
1428
0
            else
1429
0
            {
1430
0
            if (mxTempAcc.is() && mpTempAccEdit)
1431
0
                mpTempAccEdit->GotFocus();
1432
0
            else if (mpAccessibleSpreadsheet.is())
1433
0
                mpAccessibleSpreadsheet->GotFocus();
1434
0
            else
1435
0
                CommitFocusGained();
1436
0
            }
1437
0
        }
1438
0
    }
1439
0
    else if (rHint.GetId() == SfxHintId::ScAccTableChanged)
1440
0
    {
1441
        // only notify if child exist, otherwise it is not necessary
1442
0
        if (mpAccessibleSpreadsheet.is())
1443
0
        {
1444
0
            FreeAccessibleSpreadsheet();
1445
1446
            // Shapes / form controls after reload not accessible, rebuild the
1447
            // mpChildrenShapes variable.
1448
0
            mpChildrenShapes.reset( new ScChildrenShapes( this, mpViewShell, meSplitPos ) );
1449
1450
            // all children changed
1451
0
            CommitChange(AccessibleEventId::INVALIDATE_ALL_CHILDREN, uno::Any(), uno::Any());
1452
1453
0
            if (mpAccessibleSpreadsheet.is())
1454
0
                mpAccessibleSpreadsheet->GotFocus();
1455
0
        }
1456
0
    }
1457
0
    else if (rHint.GetId() == SfxHintId::ScAccMakeDrawLayer)
1458
0
    {
1459
0
        if (mpChildrenShapes)
1460
0
            mpChildrenShapes->SetDrawBroadcaster();
1461
0
    }
1462
0
    else if (rHint.GetId() == SfxHintId::ScAccEnterEditMode) // this event comes only on creating edit field of a cell
1463
0
    {
1464
0
        if (mpViewShell->GetViewData().GetEditActivePart() == meSplitPos)
1465
0
        {
1466
0
            ScViewData& rViewData = mpViewShell->GetViewData();
1467
0
            EditEngine const& rEditEng = rViewData.GetEditView(meSplitPos)->getEditEngine();
1468
0
            if (rEditEng.IsUpdateLayout())
1469
0
            {
1470
0
                mpTempAccEdit = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
1471
0
                    mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(),
1472
0
                    ScResId(STR_ACC_EDITLINE_DESCR), ScAccessibleEditObject::CellInEditMode);
1473
1474
0
                AddChild(uno::Reference<XAccessible>(mpTempAccEdit), true);
1475
1476
0
                if (mpAccessibleSpreadsheet.is())
1477
0
                    mpAccessibleSpreadsheet->LostFocus();
1478
0
                else
1479
0
                    CommitFocusLost();
1480
1481
0
                mpTempAccEdit->GotFocus();
1482
0
            }
1483
0
        }
1484
0
    }
1485
0
    else if (rHint.GetId() == SfxHintId::ScAccLeaveEditMode)
1486
0
    {
1487
0
        if (mxTempAcc.is())
1488
0
        {
1489
0
            if (mpTempAccEdit)
1490
0
            {
1491
0
                mpTempAccEdit->LostFocus();
1492
0
            }
1493
0
            RemoveChild(mxTempAcc, true);
1494
0
            if (mpTempAccEdit)
1495
0
            {
1496
                // tdf#125982 a11y use-after-free of editengine by
1497
                // ScAccessibleEditObjectTextData living past the
1498
                // the editengine of the editview passed in above
1499
                // in ScAccEnterEditMode
1500
0
                mpTempAccEdit->dispose();
1501
0
                mpTempAccEdit = nullptr;
1502
0
            }
1503
0
            if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
1504
0
                mpAccessibleSpreadsheet->GotFocus();
1505
0
            else if( mpViewShell && mpViewShell->IsActive())
1506
0
                CommitFocusGained();
1507
0
        }
1508
0
    }
1509
0
    else if ((rHint.GetId() == SfxHintId::ScAccVisAreaChanged) || (rHint.GetId() == SfxHintId::ScAccWindowResized))
1510
0
    {
1511
0
        tools::Rectangle aOldVisArea(maVisArea);
1512
0
        maVisArea = GetVisibleArea_Impl();
1513
1514
0
        if (maVisArea != aOldVisArea)
1515
0
        {
1516
0
            if (maVisArea.GetSize() != aOldVisArea.GetSize())
1517
0
            {
1518
0
                CommitChange(AccessibleEventId::BOUNDRECT_CHANGED, uno::Any(), uno::Any());
1519
1520
0
                if (mpAccessibleSpreadsheet.is())
1521
0
                    mpAccessibleSpreadsheet->BoundingBoxChanged();
1522
0
                if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
1523
0
                    mpAccessibleSpreadsheet->FireFirstCellFocus();
1524
0
            }
1525
0
            else if (mpAccessibleSpreadsheet.is())
1526
0
            {
1527
0
                mpAccessibleSpreadsheet->VisAreaChanged();
1528
0
            }
1529
0
            if (mpChildrenShapes)
1530
0
                mpChildrenShapes->VisAreaChanged();
1531
0
        }
1532
0
    }
1533
1534
0
    ScAccessibleDocumentBase::Notify(rBC, rHint);
1535
0
}
1536
1537
void SAL_CALL ScAccessibleDocument::selectionChanged( const lang::EventObject& /* aEvent */ )
1538
0
{
1539
0
    bool bSelectionChanged(false);
1540
0
    if (mpAccessibleSpreadsheet.is())
1541
0
    {
1542
0
        bool bOldSelected(mbCompleteSheetSelected);
1543
0
        mbCompleteSheetSelected = IsTableSelected();
1544
0
        if (bOldSelected != mbCompleteSheetSelected)
1545
0
        {
1546
0
            mpAccessibleSpreadsheet->CompleteSelectionChanged(mbCompleteSheetSelected);
1547
0
            bSelectionChanged = true;
1548
0
        }
1549
0
    }
1550
1551
0
    if (mpChildrenShapes && mpChildrenShapes->SelectionChanged())
1552
0
        bSelectionChanged = true;
1553
1554
0
    if (bSelectionChanged)
1555
0
        CommitChange(AccessibleEventId::SELECTION_CHANGED, uno::Any(), uno::Any());
1556
0
}
1557
1558
    //=====  XAccessibleComponent  ============================================
1559
1560
uno::Reference< XAccessible > SAL_CALL ScAccessibleDocument::getAccessibleAtPoint(
1561
        const awt::Point& rPoint )
1562
0
{
1563
0
    uno::Reference<XAccessible> xAccessible;
1564
0
    if (containsPoint(rPoint))
1565
0
    {
1566
0
        SolarMutexGuard aGuard;
1567
0
        ensureAlive();
1568
0
        if (mpChildrenShapes)
1569
0
            xAccessible = mpChildrenShapes->GetAt(rPoint);
1570
0
        if(!xAccessible.is())
1571
0
        {
1572
0
            if (mxTempAcc.is())
1573
0
            {
1574
0
                uno::Reference< XAccessibleContext > xCont(mxTempAcc->getAccessibleContext());
1575
0
                uno::Reference< XAccessibleComponent > xComp(xCont, uno::UNO_QUERY);
1576
0
                if (xComp.is())
1577
0
                {
1578
0
                    tools::Rectangle aBound(vcl::unohelper::ConvertToVCLRect(xComp->getBounds()));
1579
0
                    if (aBound.Contains(vcl::unohelper::ConvertToVCLPoint(rPoint)))
1580
0
                        xAccessible = mxTempAcc;
1581
0
                }
1582
0
            }
1583
0
            if (!xAccessible.is())
1584
0
                xAccessible = GetAccessibleSpreadsheet();
1585
0
        }
1586
0
    }
1587
0
    return xAccessible;
1588
0
}
1589
1590
void SAL_CALL ScAccessibleDocument::grabFocus(  )
1591
0
{
1592
0
    SolarMutexGuard aGuard;
1593
0
    ensureAlive();
1594
0
    if (!getAccessibleParent().is())
1595
0
        return;
1596
1597
0
    uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
1598
0
    if (xAccessibleComponent.is())
1599
0
    {
1600
0
        xAccessibleComponent->grabFocus();
1601
        // grab only focus if it does not have the focus and it is not hidden
1602
0
        if (mpViewShell &&
1603
0
            (mpViewShell->GetViewData().GetActivePart() != meSplitPos) &&
1604
0
            mpViewShell->GetWindowByPos(meSplitPos)->IsVisible())
1605
0
        {
1606
0
            mpViewShell->ActivatePart(meSplitPos);
1607
0
        }
1608
0
    }
1609
0
}
1610
1611
    //=====  XAccessibleContext  ==============================================
1612
1613
    /// Return the number of currently visible children.
1614
sal_Int64 SAL_CALL
1615
    ScAccessibleDocument::getAccessibleChildCount()
1616
0
{
1617
0
    SolarMutexGuard aGuard;
1618
0
    ensureAlive();
1619
0
    sal_Int64 nCount(1);
1620
0
    if (mpChildrenShapes)
1621
0
        nCount = mpChildrenShapes->GetCount(); // returns the count of the shapes inclusive the table
1622
1623
0
    if (mxTempAcc.is())
1624
0
        ++nCount;
1625
1626
0
    return nCount;
1627
0
}
1628
1629
    /// Return the specified child or NULL if index is invalid.
1630
uno::Reference<XAccessible> SAL_CALL
1631
    ScAccessibleDocument::getAccessibleChild(sal_Int64 nIndex)
1632
0
{
1633
0
    SolarMutexGuard aGuard;
1634
0
    ensureAlive();
1635
0
    uno::Reference<XAccessible> xAccessible;
1636
0
    if (nIndex >= 0)
1637
0
    {
1638
0
        sal_Int64 nCount(1);
1639
0
        if (mpChildrenShapes)
1640
0
        {
1641
0
            xAccessible = mpChildrenShapes->Get(nIndex); // returns NULL if it is the table or out of range
1642
0
            nCount = mpChildrenShapes->GetCount(); //there is always a table
1643
0
        }
1644
0
        if (!xAccessible.is())
1645
0
        {
1646
0
            if (nIndex < nCount)
1647
0
                xAccessible = GetAccessibleSpreadsheet();
1648
0
            else if (nIndex == nCount && mxTempAcc.is())
1649
0
                xAccessible = mxTempAcc;
1650
0
        }
1651
0
    }
1652
1653
0
    if (!xAccessible.is())
1654
0
        throw lang::IndexOutOfBoundsException();
1655
1656
0
    return xAccessible;
1657
0
}
1658
1659
    /// Return the set of current states.
1660
sal_Int64 SAL_CALL
1661
    ScAccessibleDocument::getAccessibleStateSet()
1662
0
{
1663
0
    SolarMutexGuard aGuard;
1664
0
    sal_Int64 nParentStates = 0;
1665
0
    if (getAccessibleParent().is())
1666
0
    {
1667
0
        uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
1668
0
        nParentStates = xParentContext->getAccessibleStateSet();
1669
0
    }
1670
0
    sal_Int64 nStateSet = 0;
1671
0
    if (IsDefunc(nParentStates))
1672
0
        nStateSet |= AccessibleStateType::DEFUNC;
1673
0
    else
1674
0
    {
1675
0
        nStateSet |= AccessibleStateType::EDITABLE;
1676
0
        nStateSet |= AccessibleStateType::ENABLED;
1677
0
        nStateSet |= AccessibleStateType::OPAQUE;
1678
0
        if (isShowing())
1679
0
            nStateSet |= AccessibleStateType::SHOWING;
1680
0
        if (isVisible())
1681
0
            nStateSet |= AccessibleStateType::VISIBLE;
1682
0
    }
1683
0
    return nStateSet;
1684
0
}
1685
1686
OUString SAL_CALL
1687
    ScAccessibleDocument::getAccessibleName()
1688
0
{
1689
0
    SolarMutexGuard g;
1690
1691
0
    OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
1692
0
    ScDocument* pScDoc = GetDocument();
1693
0
    if (!pScDoc)
1694
0
        return aName;
1695
1696
0
    ScDocShell* pObjSh = pScDoc->GetDocumentShell();
1697
0
    if (!pObjSh)
1698
0
        return aName;
1699
1700
0
    OUString aFileName;
1701
0
    SfxMedium* pMed = pObjSh->GetMedium();
1702
0
    if (pMed)
1703
0
        aFileName = pMed->GetName();
1704
1705
0
    if (aFileName.isEmpty())
1706
0
        aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
1707
1708
0
    if (!aFileName.isEmpty())
1709
0
    {
1710
0
        OUString aReadOnly;
1711
0
        if (pObjSh->IsReadOnly())
1712
0
            aReadOnly = ScResId(STR_ACC_DOC_SPREADSHEET_READONLY);
1713
1714
0
        aName = aFileName + aReadOnly + " - " + aName;
1715
0
    }
1716
0
    return aName;
1717
0
}
1718
1719
///=====  XAccessibleSelection  ===========================================
1720
1721
void SAL_CALL
1722
    ScAccessibleDocument::selectAccessibleChild( sal_Int64 nChildIndex )
1723
0
{
1724
0
    SolarMutexGuard aGuard;
1725
0
    ensureAlive();
1726
1727
0
    if (!(mpChildrenShapes && mpViewShell))
1728
0
        return;
1729
1730
0
    sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
1731
0
    if (mxTempAcc.is())
1732
0
        ++nCount;
1733
0
    if (nChildIndex < 0 || nChildIndex >= nCount)
1734
0
        throw lang::IndexOutOfBoundsException();
1735
1736
0
    uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
1737
0
    if (xAccessible.is())
1738
0
    {
1739
0
        bool bWasTableSelected(IsTableSelected());
1740
0
        mpChildrenShapes->Select(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
1741
0
        if (bWasTableSelected)
1742
0
            mpViewShell->SelectAll();
1743
0
    }
1744
0
    else
1745
0
    {
1746
0
        mpViewShell->SelectAll();
1747
0
    }
1748
0
}
1749
1750
sal_Bool SAL_CALL
1751
    ScAccessibleDocument::isAccessibleChildSelected( sal_Int64 nChildIndex )
1752
0
{
1753
0
    SolarMutexGuard aGuard;
1754
0
    ensureAlive();
1755
0
    bool bResult(false);
1756
1757
0
    if (mpChildrenShapes)
1758
0
    {
1759
0
        sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
1760
0
        if (mxTempAcc.is())
1761
0
            ++nCount;
1762
0
        if (nChildIndex < 0 || nChildIndex >= nCount)
1763
0
            throw lang::IndexOutOfBoundsException();
1764
1765
0
        uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
1766
0
        if (xAccessible.is())
1767
0
        {
1768
0
            uno::Reference<drawing::XShape> xShape;
1769
0
            bResult = mpChildrenShapes->IsSelected(nChildIndex, xShape); // throws no lang::IndexOutOfBoundsException if Index is too high
1770
0
        }
1771
0
        else
1772
0
        {
1773
0
            if (mxTempAcc.is() && nChildIndex == nCount)
1774
0
                bResult = true;
1775
0
            else
1776
0
                bResult = IsTableSelected();
1777
0
        }
1778
0
    }
1779
0
    return bResult;
1780
0
}
1781
1782
void SAL_CALL
1783
    ScAccessibleDocument::clearAccessibleSelection(  )
1784
0
{
1785
0
    SolarMutexGuard aGuard;
1786
0
    ensureAlive();
1787
1788
0
    if (mpChildrenShapes)
1789
0
        mpChildrenShapes->DeselectAll(); //deselects all (also the table)
1790
0
}
1791
1792
void SAL_CALL
1793
    ScAccessibleDocument::selectAllAccessibleChildren(  )
1794
0
{
1795
0
    SolarMutexGuard aGuard;
1796
0
    ensureAlive();
1797
1798
0
    if (mpChildrenShapes)
1799
0
        mpChildrenShapes->SelectAll();
1800
1801
    // select table after shapes, because while selecting shapes the table will be deselected
1802
0
    if (mpViewShell)
1803
0
    {
1804
0
        mpViewShell->SelectAll();
1805
0
    }
1806
0
}
1807
1808
sal_Int64 SAL_CALL
1809
    ScAccessibleDocument::getSelectedAccessibleChildCount(  )
1810
0
{
1811
0
    SolarMutexGuard aGuard;
1812
0
    ensureAlive();
1813
0
    sal_Int64 nCount(0);
1814
1815
0
    if (mpChildrenShapes)
1816
0
        nCount = mpChildrenShapes->GetSelectedCount();
1817
1818
0
    if (IsTableSelected())
1819
0
        ++nCount;
1820
1821
0
    if (mxTempAcc.is())
1822
0
        ++nCount;
1823
1824
0
    return nCount;
1825
0
}
1826
1827
uno::Reference<XAccessible > SAL_CALL
1828
    ScAccessibleDocument::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
1829
0
{
1830
0
    SolarMutexGuard aGuard;
1831
0
    ensureAlive();
1832
0
    uno::Reference<XAccessible> xAccessible;
1833
0
    if (mpChildrenShapes)
1834
0
    {
1835
0
        sal_Int64 nCount(getSelectedAccessibleChildCount()); //all shapes and the table
1836
0
        if (nSelectedChildIndex < 0 || nSelectedChildIndex >= nCount)
1837
0
            throw lang::IndexOutOfBoundsException();
1838
1839
0
        bool bTabMarked(IsTableSelected());
1840
1841
0
        if (mpChildrenShapes)
1842
0
            xAccessible = mpChildrenShapes->GetSelected(nSelectedChildIndex, bTabMarked); // throws no lang::IndexOutOfBoundsException if Index is too high
1843
0
        if (mxTempAcc.is() && nSelectedChildIndex == nCount - 1)
1844
0
            xAccessible = mxTempAcc;
1845
0
        else if (bTabMarked)
1846
0
            xAccessible = GetAccessibleSpreadsheet();
1847
0
    }
1848
1849
0
    OSL_ENSURE(xAccessible.is(), "here should always be an accessible object or an exception thrown");
1850
1851
0
    return xAccessible;
1852
0
}
1853
1854
void SAL_CALL
1855
    ScAccessibleDocument::deselectAccessibleChild( sal_Int64 nChildIndex )
1856
0
{
1857
0
    SolarMutexGuard aGuard;
1858
0
    ensureAlive();
1859
1860
0
    if (!(mpChildrenShapes && mpViewShell))
1861
0
        return;
1862
1863
0
    sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
1864
0
    if (mxTempAcc.is())
1865
0
        ++nCount;
1866
0
    if (nChildIndex < 0 || nChildIndex >= nCount)
1867
0
        throw lang::IndexOutOfBoundsException();
1868
1869
0
    bool bTabMarked(IsTableSelected());
1870
1871
0
    uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
1872
0
    if (xAccessible.is())
1873
0
    {
1874
0
        mpChildrenShapes->Deselect(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
1875
0
        if (bTabMarked)
1876
0
            mpViewShell->SelectAll(); // select the table again
1877
0
    }
1878
0
    else if (bTabMarked)
1879
0
        mpViewShell->Unmark();
1880
0
}
1881
1882
///=====  IAccessibleViewForwarder  ========================================
1883
1884
tools::Rectangle ScAccessibleDocument::GetVisibleArea_Impl()
1885
0
{
1886
0
    tools::Rectangle aVisRect(GetBoundingBox());
1887
1888
0
    if (mpViewShell)
1889
0
    {
1890
0
        Point aPoint(mpViewShell->GetViewData().GetPixPos(meSplitPos)); // returns a negative Point
1891
0
        aPoint.setX(-aPoint.getX());
1892
0
        aPoint.setY(-aPoint.getY());
1893
0
        aVisRect.SetPos(aPoint);
1894
1895
0
        ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
1896
0
        if (pWin)
1897
0
            aVisRect = pWin->PixelToLogic(aVisRect, pWin->GetDrawMapMode());
1898
0
    }
1899
1900
0
    return aVisRect;
1901
0
}
1902
1903
tools::Rectangle ScAccessibleDocument::GetVisibleArea() const
1904
0
{
1905
0
    SolarMutexGuard aGuard;
1906
0
    ensureAlive();
1907
0
    return maVisArea;
1908
0
}
1909
1910
Point ScAccessibleDocument::LogicToPixel (const Point& rPoint) const
1911
0
{
1912
0
    SolarMutexGuard aGuard;
1913
0
    ensureAlive();
1914
0
    Point aPoint;
1915
0
    ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
1916
0
    if (pWin)
1917
0
    {
1918
0
        aPoint = pWin->LogicToPixel(rPoint, pWin->GetDrawMapMode());
1919
0
        aPoint += Point(pWin->GetWindowExtentsAbsolute().TopLeft());
1920
0
    }
1921
0
    return aPoint;
1922
0
}
1923
1924
Size ScAccessibleDocument::LogicToPixel (const Size& rSize) const
1925
0
{
1926
0
    SolarMutexGuard aGuard;
1927
0
    ensureAlive();
1928
0
    Size aSize;
1929
0
    ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
1930
0
    if (pWin)
1931
0
        aSize = pWin->LogicToPixel(rSize, pWin->GetDrawMapMode());
1932
0
    return aSize;
1933
0
}
1934
1935
rtl::Reference<utl::AccessibleRelationSetHelper> ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const
1936
0
{
1937
0
    rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
1938
0
    if (mpChildrenShapes)
1939
0
        pRelationSet = mpChildrenShapes->GetRelationSet(pAddress);
1940
0
    return pRelationSet;
1941
0
}
1942
1943
OUString
1944
    ScAccessibleDocument::createAccessibleDescription()
1945
0
{
1946
0
    return STR_ACC_DOC_DESCR;
1947
0
}
1948
1949
OUString
1950
    ScAccessibleDocument::createAccessibleName()
1951
0
{
1952
0
    SolarMutexGuard aGuard;
1953
0
    ensureAlive();
1954
0
    OUString sName = ScResId(STR_ACC_DOC_NAME);
1955
0
    sal_Int32 nNumber(sal_Int32(meSplitPos) + 1);
1956
0
    sName += OUString::number(nNumber);
1957
0
    return sName;
1958
0
}
1959
1960
AbsoluteScreenPixelRectangle ScAccessibleDocument::GetBoundingBoxOnScreen()
1961
0
{
1962
0
    AbsoluteScreenPixelRectangle aRect;
1963
0
    if (mpViewShell)
1964
0
    {
1965
0
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
1966
0
        if (pWindow)
1967
0
            aRect = pWindow->GetWindowExtentsAbsolute();
1968
0
    }
1969
0
    return aRect;
1970
0
}
1971
1972
tools::Rectangle ScAccessibleDocument::GetBoundingBox()
1973
0
{
1974
0
    tools::Rectangle aRect;
1975
0
    if (mpViewShell)
1976
0
    {
1977
0
        vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
1978
0
        if (pWindow)
1979
0
            aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
1980
0
    }
1981
0
    return aRect;
1982
0
}
1983
1984
SCTAB ScAccessibleDocument::getVisibleTable() const
1985
0
{
1986
0
    SCTAB nVisibleTable(0);
1987
0
    if (mpViewShell)
1988
0
        nVisibleTable = mpViewShell->GetViewData().CurrentTabForData();
1989
0
    return nVisibleTable;
1990
0
}
1991
1992
uno::Reference < XAccessible >
1993
    ScAccessibleDocument::GetAccessibleSpreadsheet()
1994
0
{
1995
0
    if (!mpAccessibleSpreadsheet.is() && mpViewShell)
1996
0
    {
1997
0
        mpAccessibleSpreadsheet = new ScAccessibleSpreadsheet(this, mpViewShell, getVisibleTable(), meSplitPos);
1998
0
        mpAccessibleSpreadsheet->Init();
1999
0
        mbCompleteSheetSelected = IsTableSelected();
2000
0
    }
2001
0
    return mpAccessibleSpreadsheet;
2002
0
}
2003
2004
void ScAccessibleDocument::FreeAccessibleSpreadsheet()
2005
0
{
2006
0
    if (mpAccessibleSpreadsheet.is())
2007
0
    {
2008
0
        mpAccessibleSpreadsheet->dispose();
2009
0
        mpAccessibleSpreadsheet.clear();
2010
0
    }
2011
0
}
2012
2013
bool ScAccessibleDocument::IsTableSelected() const
2014
0
{
2015
0
    bool bResult (false);
2016
0
    if(mpViewShell)
2017
0
    {
2018
0
        SCTAB nTab(getVisibleTable());
2019
        //#103800#; use a copy of MarkData
2020
0
        ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
2021
0
        ScDocument* pDoc = GetDocument();
2022
0
        if (aMarkData.IsAllMarked( ScRange( 0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab)))
2023
0
            bResult = true;
2024
0
    }
2025
0
    return bResult;
2026
0
}
2027
2028
bool ScAccessibleDocument::IsDefunc(sal_Int64 nParentStates)
2029
0
{
2030
0
    return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
2031
0
        (nParentStates & AccessibleStateType::DEFUNC);
2032
0
}
2033
2034
void ScAccessibleDocument::AddChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
2035
0
{
2036
0
    OSL_ENSURE(!mxTempAcc.is(), "this object should be removed before");
2037
0
    if (xAcc.is())
2038
0
    {
2039
0
        mxTempAcc = xAcc;
2040
0
        if( bFireEvent )
2041
0
        {
2042
0
            CommitChange(AccessibleEventId::CHILD, uno::Any(), uno::Any(mxTempAcc),
2043
0
                         getAccessibleChildCount() - 1);
2044
0
        }
2045
0
    }
2046
0
}
2047
2048
void ScAccessibleDocument::RemoveChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
2049
0
{
2050
0
    OSL_ENSURE(mxTempAcc.is(), "this object should be added before");
2051
0
    if (!xAcc.is())
2052
0
        return;
2053
2054
0
    OSL_ENSURE(xAcc.get() == mxTempAcc.get(), "only the same object should be removed");
2055
0
    if( bFireEvent )
2056
0
        CommitChange(AccessibleEventId::CHILD, uno::Any(mxTempAcc), uno::Any());
2057
0
    mxTempAcc = nullptr;
2058
0
}
2059
2060
OUString ScAccessibleDocument::GetCurrentCellName() const
2061
0
{
2062
0
    OUString sName(ScResId(STR_ACC_CELL_NAME));
2063
0
    if (mpViewShell)
2064
0
    {
2065
        // Document not needed, because only the cell address, but not the tablename is needed
2066
0
        OUString sAddress(mpViewShell->GetViewData().GetCurPos().Format(ScRefFlags::VALID));
2067
0
        sName = sName.replaceFirst("%1", sAddress);
2068
0
    }
2069
0
    return sName;
2070
0
}
2071
2072
const OUString & ScAccessibleDocument::GetCurrentCellDescription()
2073
0
{
2074
0
    return EMPTY_OUSTRING;
2075
0
}
2076
2077
ScDocument *ScAccessibleDocument::GetDocument() const
2078
0
{
2079
0
    return mpViewShell ? &mpViewShell->GetViewData().GetDocument() : nullptr;
2080
0
}
2081
2082
ScAddress   ScAccessibleDocument::GetCurCellAddress() const
2083
0
{
2084
0
    return mpViewShell ? mpViewShell->GetViewData().GetCurPos() : ScAddress();
2085
0
}
2086
2087
std::unordered_map<OUString, OUString> ScAccessibleDocument::implGetExtendedAttributes()
2088
0
{
2089
0
    sal_uInt16 sheetIndex;
2090
0
    OUString sSheetName;
2091
0
    sheetIndex = getVisibleTable();
2092
0
    if(GetDocument()==nullptr)
2093
0
        return {};
2094
2095
0
    GetDocument()->GetName(sheetIndex,sSheetName);
2096
0
    return { { u"page-name"_ustr, sSheetName },
2097
0
             { u"page-number"_ustr, OUString::number(sheetIndex + 1) },
2098
0
             { u"total-pages"_ustr, OUString::number(GetDocument()->GetTableCount()) } };
2099
0
}
2100
2101
sal_Int32 SAL_CALL ScAccessibleDocument::getForeground(  )
2102
0
{
2103
0
    return sal_Int32(COL_BLACK);
2104
0
}
2105
2106
sal_Int32 SAL_CALL ScAccessibleDocument::getBackground(  )
2107
0
{
2108
0
    SolarMutexGuard aGuard;
2109
0
    ensureAlive();
2110
0
    return sal_Int32(ScModule::get()->GetColorConfig().GetColorValue(::svtools::DOCCOLOR).nColor);
2111
0
}
2112
2113
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */