Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/svx/source/unodraw/unoshtxt.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <memory>
23
24
#include <vcl/svapp.hxx>
25
26
#include <svx/unoshtxt.hxx>
27
#include <editeng/unoedhlp.hxx>
28
#include <svl/lstner.hxx>
29
#include <rtl/ref.hxx>
30
#include <tools/debug.hxx>
31
#include <svl/hint.hxx>
32
#include <svl/style.hxx>
33
#include <svx/svdmodel.hxx>
34
#include <svx/svdoutl.hxx>
35
#include <svx/svdobj.hxx>
36
#include <svx/svdview.hxx>
37
#include <editeng/outliner.hxx>
38
#include <editeng/unoforou.hxx>
39
#include <editeng/unoviwou.hxx>
40
#include <editeng/outlobj.hxx>
41
#include <svx/svdotext.hxx>
42
#include <svx/svdpage.hxx>
43
#include <editeng/editeng.hxx>
44
45
#include <editeng/unotext.hxx>
46
#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
47
#include <comphelper/processfactory.hxx>
48
#include <svx/svdotable.hxx>
49
#include <cell.hxx>
50
#include <comphelper/configuration.hxx>
51
52
53
// SvxTextEditSourceImpl
54
55
56
/** @descr
57
    <p>This class essentially provides the text and view forwarders. If
58
    no SdrView is given, this class handles the UNO objects, which are
59
    currently not concerned with view issues. In this case,
60
    GetViewForwarder() always returns NULL and the underlying
61
    EditEngine of the SvxTextForwarder is a background one (i.e. not
62
    the official DrawOutliner, but one created exclusively for this
63
    object, with no relation to a view).
64
    </p>
65
66
    <p>If a SdrView is given at construction time, the caller is
67
    responsible for destroying this object when the view becomes
68
    invalid (the views cannot notify). If GetViewForwarder(sal_True)
69
    is called, the underlying shape is put into edit mode, the view
70
    forwarder returned encapsulates the OutlinerView and the next call
71
    to GetTextForwarder() yields a forwarder encapsulating the actual
72
    DrawOutliner. Thus, changes on that Outliner are immediately
73
    reflected on the screen. If the object leaves edit mode, the old
74
    behaviour is restored.</p>
75
 */
76
class SvxTextEditSourceImpl : public SfxListener, public SfxBroadcaster, public sdr::ObjectUser
77
{
78
private:
79
    oslInterlockedCount maRefCount;
80
81
    SdrObject*                      mpObject;           // TTTT could be reference (?)
82
    SdrText*                        mpText;
83
    SdrView*                        mpView;
84
    VclPtr<const OutputDevice>      mpWindow;
85
    SdrModel*                       mpModel;            // TTTT probably not needed -> use SdrModel from SdrObject (?)
86
    std::unique_ptr<SdrOutliner>    mpOutliner;
87
    std::unique_ptr<SvxOutlinerForwarder> mpTextForwarder;
88
    std::unique_ptr<SvxDrawOutlinerViewForwarder> mpViewForwarder;    // if non-NULL, use GetViewModeTextForwarder text forwarder
89
    css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager;
90
    Point                           maTextOffset;
91
    bool                            mbDataValid;
92
    bool                            mbIsLocked;
93
    bool                            mbNeedsUpdate;
94
    bool                            mbOldUndoMode;
95
    bool                            mbForwarderIsEditMode;      // have to reflect that, since ENDEDIT can happen more often
96
    bool                            mbShapeIsEditMode;          // only true, if SdrHintKind::BeginEdit was received
97
    bool                            mbNotificationsDisabled;    // prevent EditEngine/Outliner notifications (e.g. when setting up forwarder)
98
    bool                            mbNotifyEditOutlinerSet;
99
100
    SvxUnoTextRangeBaseVec          mvTextRanges;
101
102
    SvxTextForwarder*               GetBackgroundTextForwarder();
103
    SvxTextForwarder*               GetEditModeTextForwarder();
104
    std::unique_ptr<SvxDrawOutlinerViewForwarder> CreateViewForwarder();
105
106
    void                            SetupOutliner();
107
108
12.8M
    bool                            HasView() const { return mpView != nullptr; }
109
    bool                            IsEditMode() const
110
11.0M
                                    {
111
11.0M
                                        if (!mbShapeIsEditMode)
112
11.0M
                                            return false;
113
0
                                        SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
114
0
                                        return pTextObj && pTextObj->IsTextEditActive();
115
11.0M
                                    }
116
117
    void                            dispose();
118
119
public:
120
    SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText );
121
    SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const OutputDevice& rWindow );
122
    virtual ~SvxTextEditSourceImpl() override;
123
124
    void acquire();
125
    void release();
126
127
    virtual void            Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
128
129
    SvxTextForwarder*       GetTextForwarder();
130
    SvxEditViewForwarder*   GetEditViewForwarder( bool );
131
    void                    UpdateData();
132
133
    void addRange( SvxUnoTextRangeBase* pNewRange );
134
    void removeRange( SvxUnoTextRangeBase* pOldRange );
135
195
    const SvxUnoTextRangeBaseVec& getRanges() const { return mvTextRanges;}
136
137
    void                    lock();
138
    void                    unlock();
139
140
    bool                    IsValid() const;
141
142
    Point                   LogicToPixel( const Point&, const MapMode& rMapMode );
143
    Point                   PixelToLogic( const Point&, const MapMode& rMapMode );
144
145
    DECL_LINK( NotifyHdl, EENotify&, void );
146
147
    virtual void ObjectInDestruction(const SdrObject& rObject) override;
148
149
    void                    UpdateOutliner();
150
};
151
152
153
SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText )
154
526k
  : maRefCount      ( 0 ),
155
526k
    mpObject        ( pObject ),
156
526k
    mpText          ( pText ),
157
526k
    mpView          ( nullptr ),
158
526k
    mpWindow        ( nullptr ),
159
526k
    mpModel         ( pObject ? &pObject->getSdrModelFromSdrObject() : nullptr ), // TTTT should be reference
160
526k
    mbDataValid     ( false ),
161
526k
    mbIsLocked      ( false ),
162
526k
    mbNeedsUpdate   ( false ),
163
526k
    mbOldUndoMode   ( false ),
164
526k
    mbForwarderIsEditMode ( false ),
165
526k
    mbShapeIsEditMode     ( false ),
166
526k
    mbNotificationsDisabled ( false ),
167
526k
    mbNotifyEditOutlinerSet ( false )
168
526k
{
169
526k
    DBG_ASSERT( mpObject, "invalid pObject!" );
170
171
526k
    if( !mpText )
172
403k
    {
173
403k
        SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject );
174
403k
        if( pTextObj )
175
403k
            mpText = pTextObj->getText( 0 );
176
403k
    }
177
178
526k
    if( mpObject )
179
526k
    {
180
526k
        mpObject->AddListener( *this );
181
526k
        mpObject->AddObjectUser( *this );
182
526k
    }
183
526k
}
184
185
186
SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const OutputDevice& rWindow )
187
0
  : maRefCount      ( 0 ),
188
0
    mpObject        ( &rObject ),
189
0
    mpText          ( pText ),
190
0
    mpView          ( &rView ),
191
0
    mpWindow        ( &rWindow ),
192
0
    mpModel         ( &rObject.getSdrModelFromSdrObject() ), // TTTT should be reference
193
0
    mbDataValid     ( false ),
194
0
    mbIsLocked      ( false ),
195
0
    mbNeedsUpdate   ( false ),
196
0
    mbOldUndoMode   ( false ),
197
0
    mbForwarderIsEditMode ( false ),
198
0
    mbShapeIsEditMode     ( true ),
199
0
    mbNotificationsDisabled ( false ),
200
0
    mbNotifyEditOutlinerSet ( false )
201
0
{
202
0
    if( !mpText )
203
0
    {
204
0
        SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject );
205
0
        if( pTextObj )
206
0
            mpText = pTextObj->getText( 0 );
207
0
    }
208
209
0
    rObject.AddListener( *this );
210
0
    StartListening( *mpView );
211
0
    mpObject->AddObjectUser( *this );
212
213
    // Init edit mode state from shape info (IsTextEditActive())
214
0
    mbShapeIsEditMode = IsEditMode();
215
0
}
216
217
218
SvxTextEditSourceImpl::~SvxTextEditSourceImpl()
219
526k
{
220
526k
    DBG_ASSERT( !mbIsLocked, "text edit source was not unlocked before dispose!" );
221
526k
    if( mpObject )
222
522k
        mpObject->RemoveObjectUser( *this );
223
224
526k
    dispose();
225
526k
}
226
227
228
void SvxTextEditSourceImpl::addRange( SvxUnoTextRangeBase* pNewRange )
229
961k
{
230
961k
    if( pNewRange )
231
961k
        if( std::find( mvTextRanges.begin(), mvTextRanges.end(), pNewRange ) == mvTextRanges.end() )
232
961k
            mvTextRanges.push_back( pNewRange );
233
961k
}
234
235
236
void SvxTextEditSourceImpl::removeRange( SvxUnoTextRangeBase* pOldRange )
237
961k
{
238
961k
    if( pOldRange )
239
961k
        std::erase(mvTextRanges, pOldRange);
240
961k
}
241
242
243
void SvxTextEditSourceImpl::acquire()
244
1.12M
{
245
1.12M
    osl_atomic_increment( &maRefCount );
246
1.12M
}
247
248
249
void SvxTextEditSourceImpl::release()
250
1.12M
{
251
1.12M
    if( ! osl_atomic_decrement( &maRefCount ) )
252
526k
        delete this;
253
1.12M
}
254
255
void SvxTextEditSourceImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
256
167k
{
257
    // #i105988 keep reference to this object
258
167k
    rtl::Reference< SvxTextEditSourceImpl > xThis( this );
259
260
167k
    if (SfxHintId::Dying == rHint.GetId())
261
3.78k
    {
262
3.78k
        if (&rBC == mpView)
263
0
        {
264
0
            mpView = nullptr;
265
0
            mpViewForwarder.reset();
266
0
        }
267
3.78k
    }
268
163k
    else if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
269
163k
    {
270
163k
        const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
271
163k
        switch( pSdrHint->GetKind() )
272
163k
        {
273
0
            case SdrHintKind::ObjectChange:
274
0
            {
275
0
                mbDataValid = false;                        // Text has to be get again
276
277
0
                if( HasView() )
278
0
                {
279
                    // Update maTextOffset, object has changed
280
                    // Cannot call that here, since TakeTextRect() (called from there)
281
                    // changes outliner content.
282
                    // UpdateOutliner();
283
284
                    // Broadcast object changes, as they might change visible attributes
285
0
                    SvxViewChangedHint aHint;
286
0
                    Broadcast( aHint );
287
0
                }
288
0
                break;
289
0
            }
290
291
0
            case SdrHintKind::BeginEdit:
292
0
                if( mpObject == pSdrHint->GetObject() )
293
0
                {
294
                    // Once SdrHintKind::BeginEdit is broadcast, each EditSource of
295
                    // AccessibleCell will handle it here and call below:
296
                    // mpView->GetTextEditOutliner()->SetNotifyHdl(), which
297
                    // will replace the Notifier for current editable cell. It
298
                    // is totally wrong. So add check here to avoid the
299
                    // incorrect replacement of notifier.
300
301
                    // Currently it only happens on the editsource of
302
                    // AccessibleCell
303
0
                    if (mpObject && mpText)
304
0
                    {
305
0
                        sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mpObject );
306
0
                        if(pTableObj)
307
0
                        {
308
0
                            const sdr::table::CellRef& xCell = pTableObj->getActiveCell();
309
0
                            if (xCell.is())
310
0
                            {
311
0
                                sdr::table::Cell* pCellObj = dynamic_cast< sdr::table::Cell* >( mpText );
312
0
                                if (pCellObj && xCell.get() != pCellObj)
313
0
                                    break;
314
0
                            }
315
0
                        }
316
0
                    }
317
                    // invalidate old forwarder
318
0
                    if( !mbForwarderIsEditMode )
319
0
                    {
320
0
                        mpTextForwarder.reset();
321
0
                    }
322
323
                    // register as listener - need to broadcast state change messages
324
0
                    if( mpView && mpView->GetTextEditOutliner() )
325
0
                    {
326
0
                        mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
327
0
                        mbNotifyEditOutlinerSet = true;
328
0
                    }
329
330
                    // Only now we're really in edit mode
331
0
                    mbShapeIsEditMode = true;
332
333
0
                    Broadcast( *pSdrHint );
334
0
                }
335
0
                break;
336
337
0
            case SdrHintKind::EndEdit:
338
0
                if( mpObject == pSdrHint->GetObject() )
339
0
                {
340
0
                    Broadcast( *pSdrHint );
341
342
                    // We're no longer in edit mode
343
0
                    mbShapeIsEditMode = false;
344
345
                    // remove as listener - outliner might outlive ourselves
346
0
                    if( mpView && mpView->GetTextEditOutliner() )
347
0
                    {
348
0
                        mpView->GetTextEditOutliner()->SetNotifyHdl( Link<EENotify&,void>() );
349
0
                        mbNotifyEditOutlinerSet = false;
350
0
                    }
351
352
                    // destroy view forwarder, OutlinerView no longer
353
                    // valid (no need for UpdateData(), it's been
354
                    // synched on SdrEndTextEdit)
355
0
                    mpViewForwarder.reset();
356
357
                    // Invalidate text forwarder, we might
358
                    // not be called again before entering edit mode a
359
                    // second time! Then, the old outliner might be
360
                    // invalid.
361
0
                    if( mbForwarderIsEditMode )
362
0
                    {
363
0
                        mbForwarderIsEditMode = false;
364
0
                        mpTextForwarder.reset();
365
0
                    }
366
0
                }
367
0
                break;
368
369
163k
            default:
370
163k
                break;
371
163k
        }
372
163k
    }
373
0
    else if (rHint.GetId() == SfxHintId::SvxViewChanged)
374
0
    {
375
0
        const SvxViewChangedHint* pViewHint = static_cast<const SvxViewChangedHint*>(&rHint);
376
0
        Broadcast( *pViewHint );
377
0
    }
378
167k
}
379
380
/* this is a callback from the attached SdrObject when it is actually deleted */
381
void SvxTextEditSourceImpl::ObjectInDestruction(const SdrObject&)
382
3.78k
{
383
3.78k
    mpObject = nullptr;
384
3.78k
    dispose();
385
3.78k
    Broadcast( SfxHint( SfxHintId::Dying ) );
386
3.78k
}
387
388
/* unregister at all objects and set all references to 0 */
389
void SvxTextEditSourceImpl::dispose()
390
530k
{
391
530k
    mpTextForwarder.reset();
392
530k
    mpViewForwarder.reset();
393
394
530k
    if( mpOutliner )
395
91.9k
    {
396
91.9k
        if( mpModel )
397
91.9k
        {
398
91.9k
            mpModel->disposeOutliner( std::move(mpOutliner) );
399
91.9k
        }
400
0
        else
401
0
        {
402
0
            mpOutliner.reset();
403
0
        }
404
91.9k
    }
405
406
530k
    if( mpModel )
407
526k
        mpModel = nullptr;
408
409
530k
    if( mpView )
410
0
    {
411
        // remove as listener - outliner might outlive ourselves
412
0
        if (mbNotifyEditOutlinerSet && mpView->GetTextEditOutliner())
413
0
        {
414
0
            mpView->GetTextEditOutliner()->SetNotifyHdl(Link<EENotify&,void>());
415
0
            mbNotifyEditOutlinerSet = false;
416
0
        }
417
0
        EndListening( *mpView );
418
0
        mpView = nullptr;
419
0
    }
420
421
530k
    if( mpObject )
422
522k
    {
423
522k
        mpObject->RemoveListener( *this );
424
522k
        mpObject->RemoveObjectUser( *this );
425
522k
        mpObject = nullptr;
426
522k
    }
427
530k
    mpWindow = nullptr;
428
530k
}
429
430
431
void SvxTextEditSourceImpl::SetupOutliner()
432
0
{
433
    // only for UAA edit source: setup outliner equivalently as in
434
    // SdrTextObj::Paint(), such that formatting equals screen
435
    // layout
436
0
    if( !(mpObject && mpOutliner) )
437
0
        return;
438
439
0
    SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
440
0
    if( pTextObj )
441
0
    {
442
0
        tools::Rectangle aPaintRect;
443
0
        tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
444
0
        pTextObj->SetupOutlinerFormatting( *mpOutliner, aPaintRect );
445
446
        // calc text offset from shape anchor
447
0
        maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
448
0
    }
449
0
}
450
451
452
void SvxTextEditSourceImpl::UpdateOutliner()
453
0
{
454
    // only for UAA edit source: update outliner equivalently as in
455
    // SdrTextObj::Paint(), such that formatting equals screen
456
    // layout
457
0
    if( !(mpObject && mpOutliner) )
458
0
        return;
459
460
0
    SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
461
0
    if( pTextObj )
462
0
    {
463
0
        tools::Rectangle aPaintRect;
464
0
        tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
465
0
        pTextObj->UpdateOutlinerFormatting( *mpOutliner, aPaintRect );
466
467
        // calc text offset from shape anchor
468
0
        maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
469
0
    }
470
0
}
471
472
473
SvxTextForwarder* SvxTextEditSourceImpl::GetBackgroundTextForwarder()
474
11.0M
{
475
11.0M
    bool bCreated = false;
476
477
    // prevent EE/Outliner notifications during setup
478
11.0M
    mbNotificationsDisabled = true;
479
480
11.0M
    if (!mpTextForwarder)
481
91.9k
    {
482
91.9k
        if( mpOutliner == nullptr )
483
91.9k
        {
484
91.9k
            SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
485
91.9k
            OutlinerMode nOutlMode = OutlinerMode::TextObject;
486
91.9k
            if( pTextObj && pTextObj->IsTextFrame() && pTextObj->GetTextKind() == SdrObjKind::OutlineText )
487
14.1k
                nOutlMode = OutlinerMode::OutlineObject;
488
489
91.9k
            mpOutliner = mpModel->createOutliner( nOutlMode );
490
491
            // Do the setup after outliner creation, would be useless otherwise
492
91.9k
            if( HasView() )
493
0
            {
494
                // Setup outliner _before_ filling it
495
0
                SetupOutliner();
496
0
            }
497
498
91.9k
            mpOutliner->SetTextObjNoInit( pTextObj );
499
91.9k
            if( mbIsLocked )
500
84.7k
            {
501
84.7k
                const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( false );
502
84.7k
                mbOldUndoMode = mpOutliner->GetEditEngine().IsUndoEnabled();
503
84.7k
                const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( false );
504
84.7k
            }
505
506
91.9k
            if (!comphelper::IsFuzzing())
507
0
            {
508
0
                if ( !m_xLinguServiceManager.is() )
509
0
                {
510
0
                    const css::uno::Reference< css::uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
511
0
                    m_xLinguServiceManager.set(css::linguistic2::LinguServiceManager::create(xContext));
512
0
                }
513
514
0
                css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator = m_xLinguServiceManager->getHyphenator();
515
0
                if( xHyphenator.is() )
516
0
                    mpOutliner->SetHyphenator( xHyphenator );
517
0
            }
518
91.9k
        }
519
520
521
91.9k
        mpTextForwarder.reset(new SvxOutlinerForwarder( *mpOutliner, (mpObject->GetObjInventor() == SdrInventor::Default) && (mpObject->GetObjIdentifier() == SdrObjKind::OutlineText) ));
522
        // delay listener subscription and UAA initialization until Outliner is fully setup
523
91.9k
        bCreated = true;
524
525
91.9k
        mbForwarderIsEditMode = false;
526
91.9k
        mbDataValid = false;
527
91.9k
    }
528
529
11.0M
    if( mpObject && mpText && !mbDataValid && mpObject->IsInserted() && mpObject->getSdrPageFromSdrObject() )
530
91.2k
    {
531
91.2k
        mpTextForwarder->flushCache();
532
533
91.2k
        std::optional<OutlinerParaObject> pOutlinerParaObject;
534
91.2k
        SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
535
91.2k
        if( pTextObj && pTextObj->getActiveText() == mpText )
536
87.5k
            pOutlinerParaObject = pTextObj->CreateEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active
537
91.2k
        bool bTextEditActive(false);
538
539
91.2k
        if( pOutlinerParaObject )
540
0
            bTextEditActive = true; // text edit active
541
91.2k
        else if (mpText->GetOutlinerParaObject())
542
46.5k
            pOutlinerParaObject = *mpText->GetOutlinerParaObject();
543
544
91.2k
        if( pOutlinerParaObject && ( bTextEditActive || !mpObject->IsEmptyPresObj() || mpObject->getSdrPageFromSdrObject()->IsMasterPage() ) )
545
45.9k
        {
546
45.9k
            mpOutliner->SetText( *pOutlinerParaObject );
547
548
            // put text to object and set EmptyPresObj to FALSE
549
45.9k
            if (mpText && bTextEditActive && mpObject->IsEmptyPresObj() && pTextObj && pTextObj->IsReallyEdited())
550
0
            {
551
0
                mpObject->SetEmptyPresObj( false );
552
0
                static_cast<SdrTextObj*>(mpObject)->NbcSetOutlinerParaObjectForText(std::move(pOutlinerParaObject), mpText);
553
0
            }
554
45.9k
        }
555
45.2k
        else
556
45.2k
        {
557
45.2k
            bool bVertical = pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical();
558
559
            // set objects style sheet on empty outliner
560
45.2k
            SfxStyleSheetPool* pPool = static_cast<SfxStyleSheetPool*>(mpObject->getSdrModelFromSdrObject().GetStyleSheetPool());
561
45.2k
            if( pPool )
562
38.0k
                mpOutliner->SetStyleSheetPool( pPool );
563
564
45.2k
            SfxStyleSheet* pStyleSheet = mpObject->getSdrPageFromSdrObject()->GetTextStyleSheetForObject( mpObject );
565
45.2k
            if( pStyleSheet )
566
38.0k
                mpOutliner->SetStyleSheet( 0, pStyleSheet );
567
568
45.2k
            if( bVertical )
569
0
            {
570
0
                mpOutliner->SetVertical( pOutlinerParaObject->GetVertical());
571
0
                mpOutliner->SetRotation( pOutlinerParaObject->GetRotation());
572
0
            }
573
45.2k
        }
574
575
        // maybe we have to set the border attributes
576
91.2k
        if (mpOutliner->GetParagraphCount()==1)
577
77.1k
        {
578
            // if we only have one paragraph we check if it is empty
579
77.1k
            OUString aStr(mpOutliner->GetText(mpOutliner->GetParagraph(0)));
580
581
77.1k
            if (aStr.isEmpty())
582
46.8k
            {
583
                // its empty, so we have to force the outliner to initialise itself
584
46.8k
                mpOutliner->SetText( u""_ustr, mpOutliner->GetParagraph( 0 ) );
585
586
46.8k
                auto pCell = dynamic_cast<sdr::table::Cell*>(mpText);
587
46.8k
                if (pCell && pCell->GetStyleSheet())
588
3
                    mpOutliner->SetStyleSheet( 0, pCell->GetStyleSheet());
589
46.8k
                else if (mpObject->GetStyleSheet())
590
38.3k
                    mpOutliner->SetStyleSheet( 0, mpObject->GetStyleSheet());
591
46.8k
            }
592
77.1k
        }
593
594
91.2k
        mbDataValid = true;
595
91.2k
    }
596
597
11.0M
    if( bCreated && mpOutliner && HasView() )
598
0
    {
599
        // register as listener - need to broadcast state change messages
600
        // registration delayed until outliner is completely set up
601
0
        mpOutliner->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
602
0
    }
603
604
    // prevent EE/Outliner notifications during setup
605
11.0M
    mbNotificationsDisabled = false;
606
607
11.0M
    return mpTextForwarder.get();
608
11.0M
}
609
610
611
SvxTextForwarder* SvxTextEditSourceImpl::GetEditModeTextForwarder()
612
0
{
613
0
    if( !mpTextForwarder && HasView() )
614
0
    {
615
0
        SdrOutliner* pEditOutliner = mpView->GetTextEditOutliner();
616
617
0
        if( pEditOutliner )
618
0
        {
619
0
            mpTextForwarder.reset(new SvxOutlinerForwarder( *pEditOutliner, (mpObject->GetObjInventor() == SdrInventor::Default) && (mpObject->GetObjIdentifier() == SdrObjKind::OutlineText) ));
620
0
            mbForwarderIsEditMode = true;
621
0
        }
622
0
    }
623
624
0
    return mpTextForwarder.get();
625
0
}
626
627
628
SvxTextForwarder* SvxTextEditSourceImpl::GetTextForwarder()
629
11.0M
{
630
11.0M
    if( mpObject == nullptr )
631
0
        return nullptr;
632
633
11.0M
    if( mpModel == nullptr )
634
0
        mpModel = &mpObject->getSdrModelFromSdrObject();
635
636
    // distinguish the cases
637
    // a) connected to view, maybe edit mode is active, can work directly on the EditOutliner
638
    // b) background Outliner, reflect changes into ParaOutlinerObject (this is exactly the old UNO code)
639
640
    // IASS: testing for HasView() is *not* sufficient - there may be more views of one document
641
    // open and TextEdit is only active in one of them, or - as with IASS - it may even be the view
642
    // of the running SlideShow itself which also will have no active TextEdit and thus no Outliner.
643
    // Thus, to identify the view which indeed does have an outliner (and is in TextEdit mode),
644
    // also check if it has an active Outliner by using GetTextEditOutliner()
645
11.0M
    if( HasView() && nullptr != mpView->GetTextEditOutliner() )
646
0
    {
647
0
        if( IsEditMode() != mbForwarderIsEditMode )
648
0
        {
649
            // forwarder mismatch - create new
650
0
            mpTextForwarder.reset();
651
0
        }
652
653
0
        if( IsEditMode() )
654
0
            return GetEditModeTextForwarder();
655
0
        else
656
0
            return GetBackgroundTextForwarder();
657
0
    }
658
11.0M
    else
659
11.0M
    {
660
        // tdf#123470 if the text edit mode of the shape is active, then we
661
        // cannot trust a previously cached TextForwarder state as the text may
662
        // be out of date, so force a refetch in that case, unless locked against
663
        // changes
664
11.0M
        if (IsEditMode() && mpTextForwarder && !mbIsLocked)
665
0
        {
666
0
            assert(!mbForwarderIsEditMode); // because without a view there is no other option except !mbForwarderIsEditMode
667
0
            bool bTextEditActive = false;
668
0
            SdrTextObj* pTextObj = DynCastSdrTextObj(mpObject);
669
            // similar to the GetBackgroundTextForwarder check, see if the text edit is active
670
0
            if (pTextObj && pTextObj->getActiveText() == mpText && pTextObj->CanCreateEditOutlinerParaObject())
671
0
                bTextEditActive = true; // text edit active
672
0
            if (bTextEditActive)
673
0
                mbDataValid = false;
674
0
        }
675
676
11.0M
        return GetBackgroundTextForwarder();
677
11.0M
    }
678
11.0M
}
679
680
std::unique_ptr<SvxDrawOutlinerViewForwarder> SvxTextEditSourceImpl::CreateViewForwarder()
681
0
{
682
0
    if( mpView->GetTextEditOutlinerView() && mpObject )
683
0
    {
684
        // register as listener - need to broadcast state change messages
685
0
        mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
686
0
        mbNotifyEditOutlinerSet = true;
687
688
0
        SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
689
0
        if( pTextObj )
690
0
        {
691
0
            tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
692
0
            OutlinerView& rOutlView = *mpView->GetTextEditOutlinerView();
693
694
0
            return std::unique_ptr<SvxDrawOutlinerViewForwarder>(new SvxDrawOutlinerViewForwarder( rOutlView, aBoundRect.TopLeft() ));
695
0
        }
696
0
    }
697
698
0
    return nullptr;
699
0
}
700
701
SvxEditViewForwarder* SvxTextEditSourceImpl::GetEditViewForwarder( bool bCreate )
702
0
{
703
0
    if( mpObject == nullptr )
704
0
        return nullptr;
705
706
0
    if( mpModel == nullptr )
707
0
        mpModel = &mpObject->getSdrModelFromSdrObject();
708
709
    // shall we delete?
710
0
    if( mpViewForwarder )
711
0
    {
712
0
        if( !IsEditMode() )
713
0
        {
714
            // destroy all forwarders (no need for UpdateData(),
715
            // it's been synched on SdrEndTextEdit)
716
0
            mpViewForwarder.reset();
717
0
        }
718
0
    }
719
    // which to create? Directly in edit mode, create new, or none?
720
0
    else if( mpView )
721
0
    {
722
0
        if( IsEditMode() )
723
0
        {
724
            // create new view forwarder
725
0
            mpViewForwarder = CreateViewForwarder();
726
0
        }
727
0
        else if( bCreate )
728
0
        {
729
            // dispose old text forwarder
730
0
            UpdateData();
731
732
0
            mpTextForwarder.reset();
733
734
            // enter edit mode
735
0
            mpView->SdrEndTextEdit();
736
737
0
            if(mpView->SdrBeginTextEdit(mpObject))
738
0
            {
739
0
                SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject  );
740
0
                if (pTextObj && pTextObj->IsTextEditActive())
741
0
                {
742
                    // create new view forwarder
743
0
                    mpViewForwarder = CreateViewForwarder();
744
0
                }
745
0
                else
746
0
                {
747
                    // failure. Somehow, SdrBeginTextEdit did not set
748
                    // our SdrTextObj into edit mode
749
0
                    mpView->SdrEndTextEdit();
750
0
                }
751
0
            }
752
0
        }
753
0
    }
754
755
0
    return mpViewForwarder.get();
756
0
}
757
758
759
void SvxTextEditSourceImpl::UpdateData()
760
1.56M
{
761
    // if we have a view and in edit mode, we're working with the
762
    // DrawOutliner. Thus, all changes made on the text forwarder are
763
    // reflected on the view and committed to the model on
764
    // SdrEndTextEdit(). Thus, no need for explicit updates here.
765
1.56M
    if( HasView() && IsEditMode() )
766
0
        return;
767
768
1.56M
    if( mbIsLocked  )
769
1.34M
    {
770
1.34M
        mbNeedsUpdate = true;
771
1.34M
    }
772
223k
    else
773
223k
    {
774
223k
        if( mpOutliner && mpObject && mpText )
775
223k
        {
776
223k
            SdrTextObj* pTextObj = DynCastSdrTextObj( mpObject );
777
223k
            if( pTextObj )
778
223k
            {
779
223k
                if( (mpOutliner->GetParagraphCount() == 1 && mpOutliner->GetEditEngine().GetTextLen( 0 ) == 0 )
780
223k
                        || (mpOutliner->GetParagraphCount() == 2 && mpOutliner->GetEditEngine().GetTextLen( 0 ) == 0
781
169k
                            && mpOutliner->GetEditEngine().GetTextLen( 1 ) == 0) )
782
59.6k
                {
783
59.6k
                    pTextObj->NbcSetOutlinerParaObjectForText( std::nullopt, mpText );
784
59.6k
                }
785
163k
                else
786
163k
                {
787
163k
                    pTextObj->NbcSetOutlinerParaObjectForText( mpOutliner->CreateParaObject(), mpText );
788
163k
                }
789
223k
            }
790
791
223k
            if( mpObject->IsEmptyPresObj() )
792
23.4k
                mpObject->SetEmptyPresObj(false);
793
223k
        }
794
223k
    }
795
1.56M
}
796
797
void SvxTextEditSourceImpl::lock()
798
125k
{
799
    // if this assert ever fires, we will need to make this a counter instead of a boolean
800
125k
    assert(!mbIsLocked && "cannot nest these loc() calls");
801
125k
    mbIsLocked = true;
802
125k
    if( mpOutliner )
803
11.4k
    {
804
11.4k
        const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( false );
805
11.4k
        mbOldUndoMode = mpOutliner->GetEditEngine().IsUndoEnabled();
806
11.4k
        const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( false );
807
11.4k
    }
808
125k
}
809
810
void SvxTextEditSourceImpl::unlock()
811
137k
{
812
137k
    mbIsLocked = false;
813
814
137k
    if( mbNeedsUpdate )
815
94.2k
    {
816
94.2k
        UpdateData();
817
94.2k
        mbNeedsUpdate = false;
818
94.2k
    }
819
820
137k
    if( mpOutliner )
821
94.2k
    {
822
94.2k
        const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( true );
823
94.2k
        const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( mbOldUndoMode );
824
94.2k
    }
825
137k
}
826
827
bool SvxTextEditSourceImpl::IsValid() const
828
0
{
829
0
    return mpView && mpWindow;
830
0
}
831
832
Point SvxTextEditSourceImpl::LogicToPixel( const Point& rPoint, const MapMode& rMapMode )
833
0
{
834
    // The responsibilities of ViewForwarder happen to be
835
    // somewhat mixed in this case. On the one hand, we need the
836
    // different interface queries on the SvxEditSource interface,
837
    // since we need both VisAreas. On the other hand, if an
838
    // EditViewForwarder exists, maTextOffset does not remain static,
839
    // but may change with every key press.
840
0
    if( IsEditMode() )
841
0
    {
842
0
        SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
843
844
0
        if( pForwarder )
845
0
            return pForwarder->LogicToPixel( rPoint, rMapMode );
846
0
    }
847
0
    else if( IsValid() && mpModel )
848
0
    {
849
0
        Point aPoint1( rPoint );
850
0
        aPoint1.AdjustX(maTextOffset.X() );
851
0
        aPoint1.AdjustY(maTextOffset.Y() );
852
853
0
        Point aPoint2( OutputDevice::LogicToLogic( aPoint1, rMapMode,
854
0
                                                   MapMode(mpModel->GetScaleUnit()) ) );
855
0
        MapMode aMapMode(mpWindow->GetMapMode());
856
0
        aMapMode.SetOrigin(Point());
857
0
        return mpWindow->LogicToPixel( aPoint2, aMapMode );
858
0
    }
859
860
0
    return Point();
861
0
}
862
863
Point SvxTextEditSourceImpl::PixelToLogic( const Point& rPoint, const MapMode& rMapMode )
864
0
{
865
    // The responsibilities of ViewForwarder happen to be
866
    // somewhat mixed in this case. On the one hand, we need the
867
    // different interface queries on the SvxEditSource interface,
868
    // since we need both VisAreas. On the other hand, if an
869
    // EditViewForwarder exists, maTextOffset does not remain static,
870
    // but may change with every key press.
871
0
    if( IsEditMode() )
872
0
    {
873
0
        SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
874
875
0
        if( pForwarder )
876
0
            return pForwarder->PixelToLogic( rPoint, rMapMode );
877
0
    }
878
0
    else if( IsValid() && mpModel )
879
0
    {
880
0
        MapMode aMapMode(mpWindow->GetMapMode());
881
0
        aMapMode.SetOrigin(Point());
882
0
        Point aPoint1( mpWindow->PixelToLogic( rPoint, aMapMode ) );
883
0
        Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
884
0
                                                   MapMode(mpModel->GetScaleUnit()),
885
0
                                                   rMapMode ) );
886
0
        aPoint2.AdjustX( -(maTextOffset.X()) );
887
0
        aPoint2.AdjustY( -(maTextOffset.Y()) );
888
889
0
        return aPoint2;
890
0
    }
891
892
0
    return Point();
893
0
}
894
895
IMPL_LINK(SvxTextEditSourceImpl, NotifyHdl, EENotify&, rNotify, void)
896
0
{
897
0
    if( !mbNotificationsDisabled )
898
0
    {
899
0
        std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify) );
900
901
0
        if (aHint)
902
0
            Broadcast(*aHint);
903
0
    }
904
0
}
905
906
SvxTextEditSource::SvxTextEditSource( SdrObject* pObject, SdrText* pText )
907
526k
{
908
526k
    mpImpl = new SvxTextEditSourceImpl( pObject, pText );
909
526k
}
910
911
912
SvxTextEditSource::SvxTextEditSource( SdrObject& rObj, SdrText* pText, SdrView& rView, const OutputDevice& rWindow )
913
0
{
914
0
    mpImpl = new SvxTextEditSourceImpl( rObj, pText, rView, rWindow );
915
0
}
916
917
918
SvxTextEditSource::SvxTextEditSource( SvxTextEditSourceImpl* pImpl )
919
434k
{
920
434k
    mpImpl = pImpl;
921
434k
}
922
923
924
SvxTextEditSource::~SvxTextEditSource()
925
961k
{
926
961k
    ::SolarMutexGuard aGuard;
927
961k
    mpImpl.clear();
928
961k
}
929
930
931
std::unique_ptr<SvxEditSource> SvxTextEditSource::Clone() const
932
434k
{
933
434k
    return std::unique_ptr<SvxEditSource>(new SvxTextEditSource( mpImpl.get() ));
934
434k
}
935
936
937
SvxTextForwarder* SvxTextEditSource::GetTextForwarder()
938
11.0M
{
939
11.0M
    return mpImpl->GetTextForwarder();
940
11.0M
}
941
942
943
SvxEditViewForwarder* SvxTextEditSource::GetEditViewForwarder( bool bCreate )
944
0
{
945
0
    return mpImpl->GetEditViewForwarder( bCreate );
946
0
}
947
948
949
SvxViewForwarder* SvxTextEditSource::GetViewForwarder()
950
0
{
951
0
    return this;
952
0
}
953
954
955
void SvxTextEditSource::UpdateData()
956
1.47M
{
957
1.47M
    mpImpl->UpdateData();
958
1.47M
}
959
960
SfxBroadcaster& SvxTextEditSource::GetBroadcaster() const
961
0
{
962
0
    return *mpImpl;
963
0
}
964
965
void SvxTextEditSource::lock()
966
125k
{
967
125k
    mpImpl->lock();
968
125k
}
969
970
void SvxTextEditSource::unlock()
971
137k
{
972
137k
    mpImpl->unlock();
973
137k
}
974
975
bool SvxTextEditSource::IsValid() const
976
0
{
977
0
    return mpImpl->IsValid();
978
0
}
979
980
Point SvxTextEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
981
0
{
982
0
    return mpImpl->LogicToPixel( rPoint, rMapMode );
983
0
}
984
985
Point SvxTextEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
986
0
{
987
0
    return mpImpl->PixelToLogic( rPoint, rMapMode );
988
0
}
989
990
void SvxTextEditSource::addRange( SvxUnoTextRangeBase* pNewRange )
991
961k
{
992
961k
    mpImpl->addRange( pNewRange );
993
961k
}
994
995
void SvxTextEditSource::removeRange( SvxUnoTextRangeBase* pOldRange )
996
961k
{
997
961k
    mpImpl->removeRange( pOldRange );
998
961k
}
999
1000
const SvxUnoTextRangeBaseVec& SvxTextEditSource::getRanges() const
1001
195
{
1002
195
    return mpImpl->getRanges();
1003
195
}
1004
1005
void SvxTextEditSource::UpdateOutliner()
1006
0
{
1007
0
    mpImpl->UpdateOutliner();
1008
0
}
1009
1010
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */