Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/accessibility/AccessibleEmptyEditSource.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
21
// Global header
22
23
24
#include <memory>
25
#include <svl/itemset.hxx>
26
#include <editeng/editdata.hxx>
27
#include <editeng/outliner.hxx>
28
#include <svx/svdmodel.hxx>
29
#include <svx/svdobj.hxx>
30
#include <svx/svdpool.hxx>
31
32
33
// Project-local header
34
35
36
#include "AccessibleEmptyEditSource.hxx"
37
#include <svx/unoshtxt.hxx>
38
39
namespace accessibility
40
{
41
    namespace {
42
43
    /** This class simply wraps a SvxTextEditSource, forwarding all
44
        methods except the GetBroadcaster() call
45
     */
46
    class AccessibleProxyEditSource_Impl : public SvxEditSource
47
    {
48
    public:
49
        /** Construct AccessibleEmptyEditSource_Impl
50
51
            @param rBrdCast
52
53
            Proxy broadcaster to allow seamless flipping of edit source implementations. ProxyEditSource and EmptyEditSource
54
         */
55
        AccessibleProxyEditSource_Impl( SdrObject&      rObj,
56
                                        SdrView&        rView,
57
                                        const OutputDevice&   rViewWindow );
58
59
        // from the SvxEditSource interface
60
        SvxTextForwarder*       GetTextForwarder() override;
61
        SvxViewForwarder*       GetViewForwarder() override;
62
        SvxEditViewForwarder*   GetEditViewForwarder( bool bCreate = false ) override;
63
64
        std::unique_ptr<SvxEditSource> Clone() const override;
65
66
        void                    UpdateData() override;
67
68
        SfxBroadcaster&         GetBroadcaster() const override;
69
70
    private:
71
        SvxTextEditSource       maEditSource;
72
73
    };
74
75
    /** Dummy class, faking exactly one empty paragraph for EditEngine accessibility
76
     */
77
    class AccessibleEmptyEditSource_Impl : public SvxEditSource, public SvxViewForwarder, public SvxTextForwarder, public SfxBroadcaster
78
    {
79
    public:
80
81
0
        AccessibleEmptyEditSource_Impl() {}
82
83
        // SvxEditSource
84
0
        SvxTextForwarder*       GetTextForwarder() override { return this; }
85
0
        SvxViewForwarder*       GetViewForwarder() override { return this; }
86
0
        std::unique_ptr<SvxEditSource> Clone() const override { return nullptr; }
87
0
        void                    UpdateData() override {}
88
0
        SfxBroadcaster&         GetBroadcaster() const override { return *const_cast<AccessibleEmptyEditSource_Impl*>(this); }
89
90
        // SvxTextForwarder
91
0
        sal_Int32          GetParagraphCount() const override { return 1; }
92
0
        sal_Int32          GetTextLen( sal_Int32 /*nParagraph*/ ) const override { return 0; }
93
0
        OUString           GetText( const ESelection& /*rSel*/ ) const override { return OUString(); }
94
        SfxItemSet         GetAttribs( const ESelection& /*rSel*/, EditEngineAttribs /*nOnlyHardAttrib*/ = EditEngineAttribs::All ) const override
95
0
        {
96
            // AW: Very dangerous: The former implementation used a SfxItemPool created on the
97
            // fly which of course was deleted again ASAP. Thus, the returned SfxItemSet was using
98
            // a deleted Pool by design.
99
0
            return SfxItemSet(SdrObject::GetGlobalDrawObjectItemPool());
100
0
        }
101
0
        SfxItemSet      GetParaAttribs( sal_Int32 /*nPara*/ ) const override { return GetAttribs(ESelection()); }
102
0
        void            SetParaAttribs( sal_Int32 /*nPara*/, const SfxItemSet& /*rSet*/ ) override {}
103
0
        void            RemoveAttribs( const ESelection& /*rSelection*/ ) override {}
104
0
        void            GetPortions( sal_Int32 /*nPara*/, std::vector<sal_Int32>& /*rList*/ ) const override {}
105
106
0
        OUString    GetStyleSheet(sal_Int32 /*nPara*/) const override { return OUString(); }
107
0
        void        SetStyleSheet(sal_Int32 /*nPara*/, const OUString& /*rStyleName*/) override {}
108
109
0
        SfxItemState    GetItemState( const ESelection& /*rSel*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
110
0
        SfxItemState    GetItemState( sal_Int32 /*nPara*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
111
112
0
        SfxItemPool*    GetPool() const override { return nullptr; }
113
114
0
        void            QuickInsertText( const OUString& /*rText*/, const ESelection& /*rSel*/ ) override {}
115
0
        void            QuickInsertField( const SvxFieldItem& /*rFld*/, const ESelection& /*rSel*/ ) override {}
116
0
        void            QuickSetAttribs( const SfxItemSet& /*rSet*/, const ESelection& /*rSel*/ ) override {}
117
0
        void            QuickInsertLineBreak( const ESelection& /*rSel*/ ) override {}
118
119
0
        const SfxItemSet * GetEmptyItemSetPtr() override { return nullptr; }
120
121
0
        void        AppendParagraph() override {}
122
0
        sal_Int32  AppendTextPortion( sal_Int32 /*nPara*/, const OUString & /*rText*/, const SfxItemSet & /*rSet*/ ) override { return 0; }
123
124
        //XTextCopy
125
0
        void        CopyText(const SvxTextForwarder& ) override {}
126
127
        OUString    CalcFieldValue( const SvxFieldItem& /*rField*/, sal_Int32 /*nPara*/, sal_Int32 /*nPos*/, std::optional<Color>& /*rpTxtColor*/, std::optional<Color>& /*rpFldColor*/, std::optional<FontLineStyle>& /*rpFldLineStyle*/ ) override
128
0
        {
129
0
            return  OUString();
130
0
        }
131
0
        void            FieldClicked( const SvxFieldItem& ) override {}
132
133
0
        bool            IsValid() const override { return true; }
134
135
0
        LanguageType    GetLanguage( sal_Int32, sal_Int32 ) const override { return LANGUAGE_DONTKNOW; }
136
0
        std::vector<EFieldInfo> GetFieldInfo( sal_Int32 ) const override { return {}; }
137
0
        EBulletInfo     GetBulletInfo( sal_Int32 ) const override { return EBulletInfo(); }
138
0
        tools::Rectangle       GetCharBounds( sal_Int32, sal_Int32 ) const override { return tools::Rectangle(); }
139
0
        tools::Rectangle       GetParaBounds( sal_Int32 ) const override { return tools::Rectangle(); }
140
0
        MapMode         GetMapMode() const override { return MapMode(); }
141
0
        OutputDevice*   GetRefDevice() const override { return nullptr; }
142
0
        bool            GetIndexAtPoint( const Point&, sal_Int32&, sal_Int32& ) const override { return false; }
143
0
        bool            GetWordIndices( sal_Int32, sal_Int32, sal_Int32&, sal_Int32& ) const override { return false; }
144
0
        bool            GetAttributeRun( sal_Int32&, sal_Int32&, sal_Int32, sal_Int32, bool ) const override { return false; }
145
0
        sal_Int32       GetLineCount( sal_Int32 nPara ) const override { return nPara == 0 ? 1 : 0; }
146
0
        sal_Int32       GetLineLen( sal_Int32, sal_Int32 ) const override { return 0; }
147
0
        void            GetLineBoundaries( /*out*/sal_Int32 & rStart, /*out*/sal_Int32 & rEnd, sal_Int32 /*nParagraph*/, sal_Int32 /*nLine*/ ) const override  { rStart = rEnd = 0; }
148
0
        sal_Int32       GetLineNumberAtIndex( sal_Int32 /*nPara*/, sal_Int32 /*nIndex*/ ) const override   { return 0; }
149
150
        // the following two methods would, strictly speaking, require
151
        // a switch to a real EditSource, too. Fortunately, the
152
        // AccessibleEditableTextPara implementation currently always
153
        // calls GetEditViewForwarder(true) before doing
154
        // changes. Thus, we rely on this behaviour here (problem
155
        // when that changes: via accessibility API, it would no
156
        // longer be possible to enter text in previously empty
157
        // shapes).
158
0
        bool            Delete( const ESelection& ) override { return false; }
159
0
        bool            InsertText( const OUString&, const ESelection& ) override { return false; }
160
0
        bool            QuickFormatDoc( bool ) override { return true; }
161
0
        bool SupportsOutlineDepth() const override { return false; }
162
0
        sal_Int16       GetDepth( sal_Int32 ) const override { return -1; }
163
0
        bool            SetDepth( sal_Int32, sal_Int16 ) override { return true; }
164
165
0
        Point           LogicToPixel( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
166
0
        Point           PixelToLogic( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
167
168
    };
169
170
    }
171
172
    // Implementing AccessibleProxyEditSource_Impl
173
174
175
    AccessibleProxyEditSource_Impl::AccessibleProxyEditSource_Impl( SdrObject&      rObj,
176
                                                                    SdrView&        rView,
177
                                                                    const OutputDevice&   rViewWindow ) :
178
0
        maEditSource( rObj, nullptr, rView, rViewWindow )
179
0
    {
180
0
    }
181
182
    SvxTextForwarder* AccessibleProxyEditSource_Impl::GetTextForwarder()
183
0
    {
184
0
        return maEditSource.GetTextForwarder();
185
0
    }
186
187
    SvxViewForwarder* AccessibleProxyEditSource_Impl::GetViewForwarder()
188
0
    {
189
0
        return maEditSource.GetViewForwarder();
190
0
    }
191
192
    SvxEditViewForwarder* AccessibleProxyEditSource_Impl::GetEditViewForwarder( bool bCreate )
193
0
    {
194
0
        return maEditSource.GetEditViewForwarder( bCreate );
195
0
    }
196
197
    std::unique_ptr<SvxEditSource> AccessibleProxyEditSource_Impl::Clone() const
198
0
    {
199
0
        return maEditSource.Clone();
200
0
    }
201
202
    void AccessibleProxyEditSource_Impl::UpdateData()
203
0
    {
204
0
        maEditSource.UpdateData();
205
0
    }
206
207
    SfxBroadcaster& AccessibleProxyEditSource_Impl::GetBroadcaster() const
208
0
    {
209
0
        return maEditSource.GetBroadcaster();
210
0
    }
211
212
213
    // Implementing AccessibleEmptyEditSource
214
215
216
    AccessibleEmptyEditSource::AccessibleEmptyEditSource( SdrObject&    rObj,
217
                                                          SdrView&      rView,
218
                                                          const OutputDevice& rViewWindow ) :
219
0
        mpEditSource( new AccessibleEmptyEditSource_Impl() ),
220
0
        mrObj(rObj),
221
0
        mrView(rView),
222
0
        mrViewWindow(rViewWindow),
223
0
        mbEditSourceEmpty( true )
224
0
    {
225
0
        StartListening( mrObj.getSdrModelFromSdrObject() );
226
0
    }
227
228
    AccessibleEmptyEditSource::~AccessibleEmptyEditSource()
229
0
    {
230
0
        if( !mbEditSourceEmpty )
231
0
        {
232
            // deregister as listener
233
0
            if (mpEditSource)
234
0
                EndListening( mpEditSource->GetBroadcaster() );
235
0
        }
236
0
        else
237
0
        {
238
0
            EndListening( mrObj.getSdrModelFromSdrObject() );
239
0
        }
240
0
    }
241
242
    SvxTextForwarder* AccessibleEmptyEditSource::GetTextForwarder()
243
0
    {
244
0
        if (!mpEditSource)
245
0
            return nullptr;
246
247
0
        return mpEditSource->GetTextForwarder();
248
0
    }
249
250
    SvxViewForwarder* AccessibleEmptyEditSource::GetViewForwarder()
251
0
    {
252
0
        if (!mpEditSource)
253
0
            return nullptr;
254
255
0
        return mpEditSource->GetViewForwarder();
256
0
    }
257
258
    void AccessibleEmptyEditSource::Switch2ProxyEditSource()
259
0
    {
260
        // deregister EmptyEditSource model listener
261
0
        EndListening( mrObj.getSdrModelFromSdrObject() );
262
263
0
        ::std::unique_ptr< SvxEditSource > pProxySource( new AccessibleProxyEditSource_Impl(mrObj, mrView, mrViewWindow) );
264
0
        mpEditSource.swap(pProxySource);
265
266
        // register as listener
267
0
        StartListening( mpEditSource->GetBroadcaster() );
268
269
        // we've irrevocably a full EditSource now.
270
0
        mbEditSourceEmpty = false;
271
0
    }
272
273
    SvxEditViewForwarder* AccessibleEmptyEditSource::GetEditViewForwarder( bool bCreate )
274
0
    {
275
0
        if (!mpEditSource)
276
0
            return nullptr;
277
278
        // switch edit source, if not yet done
279
0
        if( mbEditSourceEmpty && bCreate )
280
0
            Switch2ProxyEditSource();
281
282
0
        return mpEditSource->GetEditViewForwarder( bCreate );
283
0
    }
284
285
    std::unique_ptr<SvxEditSource> AccessibleEmptyEditSource::Clone() const
286
0
    {
287
0
        if (!mpEditSource)
288
0
            return nullptr;
289
290
0
        return mpEditSource->Clone();
291
0
    }
292
293
    void AccessibleEmptyEditSource::UpdateData()
294
0
    {
295
0
        if (mpEditSource)
296
0
            mpEditSource->UpdateData();
297
0
    }
298
299
    SfxBroadcaster& AccessibleEmptyEditSource::GetBroadcaster() const
300
0
    {
301
0
        return *const_cast<AccessibleEmptyEditSource*>(this);
302
0
    }
303
304
    void AccessibleEmptyEditSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
305
0
    {
306
0
        const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
307
308
0
        if( pSdrHint && pSdrHint->GetKind() == SdrHintKind::BeginEdit &&
309
0
            &mrObj == pSdrHint->GetObject() && mpEditSource )
310
0
        {
311
            // switch edit source, if not yet done. This is necessary
312
            // to become a full-fledged EditSource the first time a
313
            // user start entering text in a previously empty object.
314
0
            if( mbEditSourceEmpty )
315
0
                Switch2ProxyEditSource();
316
0
        }
317
0
        else if (pSdrHint && pSdrHint->GetObject()!=nullptr)
318
0
        {
319
            // When the SdrObject just got a para outliner object then
320
            // switch the edit source.
321
0
            if (pSdrHint->GetObject()->GetOutlinerParaObject() != nullptr)
322
0
                Switch2ProxyEditSource();
323
0
        }
324
325
        // forward messages
326
0
        Broadcast( rHint );
327
0
    }
328
329
} // end of namespace accessibility
330
331
332
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */