Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/svdedxv.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <com/sun/star/i18n/WordType.hpp>
21
#include <editeng/editdata.hxx>
22
#include <editeng/editeng.hxx>
23
#include <editeng/editobj.hxx>
24
#include <editeng/editstat.hxx>
25
#include <editeng/outlobj.hxx>
26
#include <editeng/unotext.hxx>
27
#include <o3tl/deleter.hxx>
28
#include <officecfg/Office/Common.hxx>
29
#include <svl/itemiter.hxx>
30
#include <svl/style.hxx>
31
#include <svl/whiter.hxx>
32
#include <svx/sdtfchim.hxx>
33
#include <svx/selectioncontroller.hxx>
34
#include <svx/svdedxv.hxx>
35
#include <svx/svdetc.hxx>
36
#include <svx/svdotable.hxx>
37
#include <svx/svdotext.hxx>
38
#include <svx/svdoutl.hxx>
39
#include <svx/svdpage.hxx>
40
#include <svx/svdpagv.hxx>
41
#include <svx/svdundo.hxx>
42
#include <vcl/canvastools.hxx>
43
#include <vcl/commandevent.hxx>
44
#include <vcl/cursor.hxx>
45
#include <vcl/dndlistenercontainer.hxx>
46
#include <vcl/weld/MessageDialog.hxx>
47
#include <vcl/window.hxx>
48
#include <comphelper/lok.hxx>
49
#include <basegfx/polygon/b2dpolygontools.hxx>
50
#include <drawinglayer/processor2d/baseprocessor2d.hxx>
51
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
52
#include <drawinglayer/processor2d/processor2dtools.hxx>
53
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
54
#include <editeng/outliner.hxx>
55
#include <sal/log.hxx>
56
#include <sdr/overlay/overlaytools.hxx>
57
#include <sfx2/viewsh.hxx>
58
#include <svx/dialmgr.hxx>
59
#include <svx/sdr/overlay/overlaymanager.hxx>
60
#include <svx/sdr/overlay/overlayselection.hxx>
61
#include <svx/sdr/table/tablecontroller.hxx>
62
#include <svx/sdrpagewindow.hxx>
63
#include <svx/sdrpaintwindow.hxx>
64
#include <svx/sdrundomanager.hxx>
65
#include <svx/strings.hrc>
66
#include <svx/svdviter.hxx>
67
#include <svtools/optionsdrawinglayer.hxx>
68
#include <textchain.hxx>
69
#include <textchaincursor.hxx>
70
#include <tools/debug.hxx>
71
#include <vcl/svapp.hxx>
72
#include <svx/sdr/contact/viewcontact.hxx>
73
74
#include <memory>
75
76
SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
77
346k
    : SdrGlueEditView(rSdrModel, pOut)
78
346k
    , maTEOverlayGroup()
79
346k
    , maTextEditUpdateTimer("TextEditUpdateTimer")
80
346k
    , mxWeakTextEditObj()
81
346k
    , mpTextEditPV(nullptr)
82
346k
    , mpTextEditOutlinerView(nullptr)
83
346k
    , mpTextEditWin(nullptr)
84
346k
    , m_pTextEditCursorBuffer(nullptr)
85
346k
    , m_pMacroObj(nullptr)
86
346k
    , m_pMacroPV(nullptr)
87
346k
    , m_pMacroWin(nullptr)
88
346k
    , m_aTextEditArea()
89
346k
    , m_aMinTextEditArea()
90
346k
    , m_aOldCalcFieldValueLink()
91
346k
    , m_aMacroDownPos()
92
346k
    , m_nMacroTol(0)
93
346k
    , mbTextEditDontDelete(false)
94
346k
    , mbTextEditOnlyOneView(false)
95
346k
    , mbTextEditNewObj(false)
96
346k
    , mbQuickTextEditMode(true)
97
346k
    , mbMacroDown(false)
98
346k
    , mbInteractiveSlideShow(false)
99
346k
    , mxSelectionController()
100
346k
    , mxLastSelectionController()
101
346k
    , mpOldTextEditUndoManager(nullptr)
102
346k
    , mpLocalTextEditUndoManager()
103
346k
{
104
    // init some timer settings (not starting it of course)
105
346k
    maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
106
346k
    maTextEditUpdateTimer.SetInvokeHandler(LINK(this, SdrObjEditView, TextEditUpdate));
107
346k
}
108
109
IMPL_LINK_NOARG(SdrObjEditView, ImpModifyHdl, LinkParamNone*, void)
110
0
{
111
    // IASS: active TextEdit had a model change. Check and react.
112
0
    if (nullptr == mpTextEditOutliner)
113
        // no Outliner, no TextEdit
114
0
        return;
115
116
0
    if (!mxWeakTextEditObj.get().is())
117
        // no TextObject, no TextEdit
118
0
        return;
119
120
    // reset & restart the timer
121
0
    maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
122
0
    maTextEditUpdateTimer.Start();
123
0
}
124
125
IMPL_LINK_NOARG(SdrObjEditView, TextEditUpdate, Timer*, void)
126
0
{
127
    // IASS: text was changed and EDIT_UPDATEDATA_TIMEOUT has passed
128
    // since last user input
129
0
    maTextEditUpdateTimer.Stop();
130
131
    // be safe: still in TextEdit?
132
0
    if (nullptr == mpTextEditOutliner)
133
        // no Outliner, no TextEdit
134
0
        return;
135
136
0
    if (!mxWeakTextEditObj.get().is())
137
        // no TextObject, no TextEdit
138
0
        return;
139
140
    // launch an ObjectChange: This is the straightforward method
141
    // to get this broadcasted. We do not risk to set the model
142
    // unwantedly to changed, we had a text edit going on already.
143
    // This is needed for SlideShow since it is not (yet) using the
144
    // standard schema with VC/VOC/OC
145
0
    if (isInteractiveSlideShow())
146
0
        mxWeakTextEditObj.get()->BroadcastObjectChange();
147
148
    // force repaint for objects with changed text in all views
149
    // that are VC/VOC/OC based (SlideShow is not yet)
150
0
    sdr::contact::ViewContact& rVC(mxWeakTextEditObj.get()->GetViewContact());
151
152
0
    if (!rVC.hasMultipleViewObjectContacts())
153
        // only one VOC -> this is us
154
0
        return;
155
156
0
    if (nullptr == mpTextEditPV)
157
        // should not happen, just invalidate all visualizations
158
0
        rVC.ActionChanged();
159
0
    else
160
        // invalidate only visualizations in different views:
161
        // this is important to not cause eventual high repaint costs
162
        // in the EditView -> we avoid this by running the TextEdit
163
        // on the overlay. NOTE: This is only for better performance,
164
        // any repaint will just work fine and do the right thing
165
0
        rVC.ActionChangedIfDifferentPageView(*mpTextEditPV);
166
0
}
167
168
SdrObjEditView::~SdrObjEditView()
169
346k
{
170
346k
    maTextEditUpdateTimer.Stop();
171
346k
    mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
172
346k
    assert(!IsTextEdit());
173
346k
    if (IsTextEdit())
174
0
        suppress_fun_call_w_exception(SdrEndTextEdit());
175
346k
    mpTextEditOutliner.reset();
176
346k
    assert(nullptr == mpOldTextEditUndoManager); // should have been reset
177
346k
}
178
179
0
bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }
180
181
void SdrObjEditView::MovAction(const Point& rPnt)
182
0
{
183
0
    if (IsMacroObj())
184
0
        MovMacroObj(rPnt);
185
0
    SdrGlueEditView::MovAction(rPnt);
186
0
}
187
188
void SdrObjEditView::EndAction()
189
0
{
190
0
    if (IsMacroObj())
191
0
        EndMacroObj();
192
0
    SdrGlueEditView::EndAction();
193
0
}
194
195
void SdrObjEditView::BckAction()
196
0
{
197
0
    BrkMacroObj();
198
0
    SdrGlueEditView::BckAction();
199
0
}
200
201
void SdrObjEditView::BrkAction()
202
92.8k
{
203
92.8k
    BrkMacroObj();
204
92.8k
    SdrGlueEditView::BrkAction();
205
92.8k
}
206
207
SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
208
92.7k
{
209
92.7k
    SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);
210
211
92.7k
    if (comphelper::LibreOfficeKit::isActive() && pPageView)
212
0
    {
213
        // Check if other views have an active text edit on the same page as
214
        // this one.
215
0
        SdrViewIter::ForAllViews(pPageView->GetPage(), [this](SdrView* pView) {
216
0
            if (pView == this || !pView->IsTextEdit())
217
0
                return;
218
219
0
            OutputDevice* pOutDev = GetFirstOutputDevice();
220
0
            if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
221
0
                return;
222
223
            // Found one, so create an outliner view, to get invalidations when
224
            // the text edit changes.
225
            // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
226
            // registers the view shell of this draw view, and not the view
227
            // shell of pView.
228
0
            OutlinerView* pOutlinerView
229
0
                = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
230
0
            pOutlinerView->HideCursor();
231
0
            pView->GetTextEditOutliner()->InsertView(pOutlinerView);
232
0
        });
233
0
    }
234
235
92.7k
    return pPageView;
236
92.7k
}
237
238
namespace
239
{
240
/// Removes outliner views registered in other draw views that use pOutputDevice.
241
void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
242
                                     OutputDevice const* pOutputDevice)
243
92.7k
{
244
92.7k
    if (!comphelper::LibreOfficeKit::isActive())
245
92.7k
        return;
246
247
0
    if (!pPageView)
248
0
        return;
249
250
0
    if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
251
0
        return;
252
253
0
    SdrViewIter::ForAllViews(pPageView->GetPage(), [&pThis, &pOutputDevice](SdrView* pView) {
254
0
        if (pView == pThis || !pView->IsTextEdit())
255
0
            return;
256
257
0
        SdrOutliner* pOutliner = pView->GetTextEditOutliner();
258
0
        for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
259
0
        {
260
0
            OutlinerView* pOutlinerView = pOutliner->GetView(nView);
261
0
            if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
262
0
                continue;
263
264
0
            pOutliner->RemoveView(pOutlinerView);
265
0
            delete pOutlinerView;
266
0
        }
267
0
    });
268
0
}
269
}
270
271
void SdrObjEditView::HideSdrPage()
272
92.7k
{
273
92.7k
    lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
274
275
92.7k
    if (mpTextEditPV == GetSdrPageView())
276
0
    {
277
        // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
278
0
        mpTextEditPV = nullptr;
279
0
    }
280
281
92.7k
    SdrGlueEditView::HideSdrPage();
282
92.7k
}
283
284
void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
285
0
{
286
0
    if (IsMacroObj())
287
0
    {
288
0
        rRect = m_pMacroObj->GetCurrentBoundRect();
289
0
    }
290
0
    else
291
0
    {
292
0
        SdrGlueEditView::TakeActionRect(rRect);
293
0
    }
294
0
}
295
296
void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
297
613M
{
298
613M
    SdrGlueEditView::Notify(rBC, rHint);
299
613M
    if (mpTextEditOutliner == nullptr)
300
613M
        return;
301
302
    // change of printer while editing
303
0
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
304
0
        return;
305
306
0
    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
307
0
    SdrHintKind eKind = pSdrHint->GetKind();
308
0
    if (eKind == SdrHintKind::RefDeviceChange)
309
0
    {
310
0
        mpTextEditOutliner->SetRefDevice(GetModel().GetRefDevice());
311
0
    }
312
0
    if (eKind == SdrHintKind::DefaultTabChange)
313
0
    {
314
0
        mpTextEditOutliner->SetDefTab(GetModel().GetDefaultTabulator());
315
0
    }
316
0
}
317
318
void SdrObjEditView::ModelHasChanged()
319
0
{
320
0
    SdrGlueEditView::ModelHasChanged();
321
0
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
322
0
    if (pTextObj && !pTextObj->IsInserted())
323
0
        SdrEndTextEdit(); // object deleted
324
    // TextEditObj changed?
325
0
    if (!IsTextEdit())
326
0
        return;
327
328
0
    if (pTextObj != nullptr)
329
0
    {
330
0
        size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
331
0
        bool bAreaChg = false;
332
0
        bool bAnchorChg = false;
333
0
        bool bColorChg = false;
334
0
        bool bContourFrame = pTextObj->IsContourTextFrame();
335
0
        EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
336
0
        tools::Rectangle aOldArea(m_aMinTextEditArea);
337
0
        aOldArea.Union(m_aTextEditArea);
338
0
        Color aNewColor;
339
0
        { // check area
340
0
            Size aPaperMin1;
341
0
            Size aPaperMax1;
342
0
            tools::Rectangle aEditArea1;
343
0
            tools::Rectangle aMinArea1;
344
0
            pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
345
0
            Point aPvOfs(pTextObj->GetTextEditOffset());
346
347
            // add possible GridOffset to up-to-now view-independent EditAreas
348
0
            basegfx::B2DVector aGridOffset(0.0, 0.0);
349
0
            if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), GetSdrPageView()))
350
0
            {
351
0
                const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
352
0
                                    basegfx::fround<tools::Long>(aGridOffset.getY()));
353
354
0
                aEditArea1 += aOffset;
355
0
                aMinArea1 += aOffset;
356
0
            }
357
358
0
            aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
359
0
            aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
360
0
            tools::Rectangle aNewArea(aMinArea1);
361
0
            aNewArea.Union(aEditArea1);
362
363
0
            if (aNewArea != aOldArea || aEditArea1 != m_aTextEditArea
364
0
                || aMinArea1 != m_aMinTextEditArea
365
0
                || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
366
0
                || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
367
0
            {
368
0
                m_aTextEditArea = aEditArea1;
369
0
                m_aMinTextEditArea = aMinArea1;
370
371
0
                const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
372
0
                mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
373
0
                mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
374
0
                mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner
375
376
0
                if (!bContourFrame)
377
0
                {
378
0
                    mpTextEditOutliner->ClearPolygon();
379
0
                    EEControlBits nStat = mpTextEditOutliner->GetControlWord();
380
0
                    nStat |= EEControlBits::AUTOPAGESIZE;
381
0
                    mpTextEditOutliner->SetControlWord(nStat);
382
0
                }
383
0
                else
384
0
                {
385
0
                    EEControlBits nStat = mpTextEditOutliner->GetControlWord();
386
0
                    nStat &= ~EEControlBits::AUTOPAGESIZE;
387
0
                    mpTextEditOutliner->SetControlWord(nStat);
388
0
                    tools::Rectangle aAnchorRect;
389
0
                    pTextObj->TakeTextAnchorRect(aAnchorRect);
390
0
                    pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
391
0
                }
392
0
                for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
393
0
                {
394
0
                    OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
395
0
                    EVControlBits nStat0 = pOLV->GetControlWord();
396
0
                    EVControlBits nStat = nStat0;
397
                    // AutoViewSize only if not ContourFrame.
398
0
                    if (!bContourFrame)
399
0
                        nStat |= EVControlBits::AUTOSIZE;
400
0
                    else
401
0
                        nStat &= ~EVControlBits::AUTOSIZE;
402
0
                    if (nStat != nStat0)
403
0
                        pOLV->SetControlWord(nStat);
404
0
                }
405
406
0
                mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
407
0
                bAreaChg = true;
408
0
            }
409
0
        }
410
0
        if (mpTextEditOutlinerView != nullptr)
411
0
        { // check fill and anchor
412
0
            EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
413
0
            eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
414
0
            bAnchorChg = eOldAnchor != eNewAnchor;
415
0
            Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
416
0
            aNewColor = GetTextEditBackgroundColor(*this);
417
0
            bColorChg = aOldColor != aNewColor;
418
0
        }
419
        // refresh always when it's a contour frame. That
420
        // refresh is necessary since it triggers the repaint
421
        // which makes the Handles visible. Changes at TakeTextRect()
422
        // seem to have resulted in a case where no refresh is executed.
423
        // Before that, a refresh must have been always executed
424
        // (else this error would have happened earlier), thus I
425
        // even think here a refresh should be done always.
426
        // Since follow-up problems cannot even be guessed I only
427
        // add this one more case to the if below.
428
        // BTW: It's VERY bad style that here, inside ModelHasChanged()
429
        // the outliner is again massively changed for the text object
430
        // in text edit mode. Normally, all necessary data should be
431
        // set at SdrBeginTextEdit(). Some changes and value assigns in
432
        // SdrBeginTextEdit() are completely useless since they are set here
433
        // again on ModelHasChanged().
434
0
        if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
435
0
        {
436
0
            for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
437
0
            {
438
0
                OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
439
0
                vcl::Window* pWin = pOLV->GetWindow();
440
0
                if (!pWin || pWin->isDisposed())
441
0
                    continue;
442
0
                { // invalidate old OutlinerView area
443
0
                    tools::Rectangle aTmpRect(aOldArea);
444
0
                    sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
445
0
                    Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
446
0
                    aTmpRect.AdjustLeft(-(aMore.Width()));
447
0
                    aTmpRect.AdjustRight(aMore.Width());
448
0
                    aTmpRect.AdjustTop(-(aMore.Height()));
449
0
                    aTmpRect.AdjustBottom(aMore.Height());
450
0
                    InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
451
0
                }
452
0
                if (bAnchorChg)
453
0
                    pOLV->SetAnchorMode(eNewAnchor);
454
0
                if (bColorChg)
455
0
                    pOLV->SetBackgroundColor(aNewColor);
456
457
0
                bool bWasCoursorVisible = pOLV->IsCursorVisible();
458
0
                vcl::Cursor* pOldCursor = pWin->GetCursor();
459
0
                pOLV->SetOutputArea(
460
0
                    m_aTextEditArea); // because otherwise, we're not re-anchoring correctly
461
0
                ImpInvalidateOutlinerView(*pOLV);
462
                // Undo SetOutputArea setting and showing the cursor
463
0
                if (!bWasCoursorVisible)
464
0
                    pOLV->HideCursor();
465
0
                pWin->SetCursor(pOldCursor);
466
0
            }
467
0
            mpTextEditOutlinerView->ShowCursor();
468
0
        }
469
0
    }
470
0
    ImpMakeTextCursorAreaVisible();
471
0
}
472
473
namespace
474
{
475
class TextEditFrameOverlayObject;
476
class TextEditHighContrastOverlaySelection;
477
478
/**
479
        Helper class to visualize the content of an active EditView as an
480
        OverlayObject. These objects work with Primitives and are handled
481
        from the OverlayManager(s) in place as needed.
482
483
        It allows complete visualization of the content of the active
484
        EditView without the need of Invalidates triggered by the EditView
485
        and thus avoiding potentially expensive repaints by using the
486
        automatically buffered Overlay mechanism.
487
488
        It buffers as much as possible locally and *only* triggers a real
489
        change (see call to objectChange()) when really needed.
490
     */
491
class TextEditOverlayObject : public sdr::overlay::OverlayObject
492
{
493
protected:
494
    /// local access to associated sdr::overlay::OverlaySelection
495
    std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection;
496
    std::unique_ptr<TextEditHighContrastOverlaySelection> mxOverlayHighContrastSelection;
497
    std::unique_ptr<TextEditFrameOverlayObject> mxOverlayFrame;
498
499
    /// local definition depends on active OutlinerView
500
    OutlinerView& mrOutlinerView;
501
502
    /// geometry definitions with buffering
503
    basegfx::B2DRange maLastRange;
504
    basegfx::B2DRange maRange;
505
506
    /// text content definitions with buffering
507
    drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
508
    drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
509
510
    // geometry creation for OverlayObject, can use local *Last* values
511
    virtual drawinglayer::primitive2d::Primitive2DContainer
512
    createOverlayObjectPrimitive2DSequence() override;
513
514
public:
515
    TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView);
516
    virtual ~TextEditOverlayObject() override;
517
518
    sdr::overlay::OverlayObject* getOverlaySelection();
519
    sdr::overlay::OverlayObject* getOverlayFrame();
520
521
0
    const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
522
523
    /// override to check conditions for last createOverlayObjectPrimitive2DSequence
524
    virtual drawinglayer::primitive2d::Primitive2DContainer
525
    getOverlayObjectPrimitive2DSequence() const override;
526
527
    // data write access. In this OverlayObject we only have the
528
    // callback that triggers detecting if something *has* changed
529
    void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
530
    void checkSelectionChange();
531
532
0
    const basegfx::B2DRange& getRange() const { return maRange; }
533
    const drawinglayer::primitive2d::Primitive2DContainer& getTextPrimitives() const
534
0
    {
535
0
        return maTextPrimitives;
536
0
    }
537
};
538
539
class TextEditFrameOverlayObject : public sdr::overlay::OverlayObject
540
{
541
private:
542
    const TextEditOverlayObject& mrTextEditOverlayObject;
543
544
    // geometry creation for OverlayObject, can use local *Last* values
545
    virtual drawinglayer::primitive2d::Primitive2DContainer
546
    createOverlayObjectPrimitive2DSequence() override;
547
548
public:
549
    TextEditFrameOverlayObject(const TextEditOverlayObject& rTextEditOverlayObject);
550
    using sdr::overlay::OverlayObject::objectChange;
551
    virtual ~TextEditFrameOverlayObject() override;
552
};
553
554
class TextEditHighContrastOverlaySelection : public sdr::overlay::OverlayObject
555
{
556
private:
557
    const TextEditOverlayObject& mrTextEditOverlayObject;
558
    std::vector<basegfx::B2DRange> maRanges;
559
560
    // geometry creation for OverlayObject, can use local *Last* values
561
    virtual drawinglayer::primitive2d::Primitive2DContainer
562
    createOverlayObjectPrimitive2DSequence() override;
563
564
public:
565
    TextEditHighContrastOverlaySelection(const TextEditOverlayObject& rTextEditOverlayObject);
566
    void setRanges(std::vector<basegfx::B2DRange>&& rNew);
567
    virtual ~TextEditHighContrastOverlaySelection() override;
568
};
569
570
TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection(
571
    const TextEditOverlayObject& rTextEditOverlayObject)
572
0
    : OverlayObject(rTextEditOverlayObject.getBaseColor())
573
0
    , mrTextEditOverlayObject(rTextEditOverlayObject)
574
0
{
575
0
    allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
576
    // use selection colors in HighContrast mode
577
0
    mbHighContrastSelection = true;
578
0
}
579
580
void TextEditHighContrastOverlaySelection::setRanges(std::vector<basegfx::B2DRange>&& rNew)
581
0
{
582
0
    if (rNew != maRanges)
583
0
    {
584
0
        maRanges = std::move(rNew);
585
0
        objectChange();
586
0
    }
587
0
}
588
589
drawinglayer::primitive2d::Primitive2DContainer
590
TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
591
0
{
592
0
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
593
594
0
    size_t nCount = maRanges.size();
595
596
0
    if (nCount)
597
0
    {
598
0
        basegfx::B2DPolyPolygon aClipPolyPolygon;
599
600
0
        basegfx::BColor aRGBColor(getBaseColor().getBColor());
601
602
0
        for (size_t a = 0; a < nCount; ++a)
603
0
            aClipPolyPolygon.append(basegfx::utils::createPolygonFromRect(maRanges[a]));
604
605
        // This is used in high contrast mode, we will render the selection
606
        // with the bg forced to the selection Highlight color and the fg color
607
        // forced to the HighlightText color
608
0
        aRetval.append(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
609
0
            basegfx::B2DPolyPolygon(
610
0
                basegfx::utils::createPolygonFromRect(aClipPolyPolygon.getB2DRange())),
611
0
            aRGBColor));
612
0
        aRetval.append(mrTextEditOverlayObject.getTextPrimitives());
613
0
        aRetval.append(new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aClipPolyPolygon),
614
0
                                                                      std::move(aRetval)));
615
0
    }
616
617
0
    return aRetval;
618
0
}
619
620
TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection()
621
0
{
622
0
    if (getOverlayManager())
623
0
    {
624
0
        getOverlayManager()->remove(*this);
625
0
    }
626
0
}
627
628
sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlaySelection()
629
0
{
630
0
    if (mxOverlayTransparentSelection)
631
0
        return mxOverlayTransparentSelection.get();
632
0
    return mxOverlayHighContrastSelection.get();
633
0
}
634
635
sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlayFrame()
636
0
{
637
0
    if (!mxOverlayFrame)
638
0
        mxOverlayFrame.reset(new TextEditFrameOverlayObject(*this));
639
0
    return mxOverlayFrame.get();
640
0
}
641
642
drawinglayer::primitive2d::Primitive2DContainer
643
TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
644
0
{
645
0
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
646
647
    // add buffered TextPrimitives
648
0
    aRetval.append(maTextPrimitives);
649
650
0
    return aRetval;
651
0
}
652
653
drawinglayer::primitive2d::Primitive2DContainer
654
TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence()
655
0
{
656
0
    drawinglayer::primitive2d::Primitive2DContainer aRetval;
657
658
    /// outer frame visualization
659
0
    const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
660
0
    const sal_uInt16 nPixSiz(mrTextEditOverlayObject.getOutlinerView().GetInvalidateMore() - 1);
661
662
0
    aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
663
0
        mrTextEditOverlayObject.getRange(), getBaseColor().getBColor(), fTransparence,
664
0
        std::max(6, nPixSiz - 2), // grow
665
0
        0.0, // shrink
666
0
        0.0));
667
668
0
    return aRetval;
669
0
}
670
671
TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView)
672
0
    : OverlayObject(rColor)
673
0
    , mrOutlinerView(rOutlinerView)
674
0
{
675
    // no AA for TextEdit overlay
676
0
    allowAntiAliase(false);
677
678
    // create local OverlaySelection - this is an integral part of EditText
679
    // visualization
680
0
    if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
681
0
    {
682
0
        mxOverlayHighContrastSelection.reset(new TextEditHighContrastOverlaySelection(*this));
683
0
    }
684
0
    else
685
0
    {
686
0
        std::vector<basegfx::B2DRange> aEmptySelection{};
687
0
        mxOverlayTransparentSelection.reset(new sdr::overlay::OverlaySelection(
688
0
            sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
689
0
    }
690
0
}
691
692
TextEditOverlayObject::~TextEditOverlayObject()
693
0
{
694
0
    mxOverlayTransparentSelection.reset();
695
0
    mxOverlayHighContrastSelection.reset();
696
697
0
    if (getOverlayManager())
698
0
    {
699
0
        getOverlayManager()->remove(*this);
700
0
    }
701
0
}
702
703
TextEditFrameOverlayObject::TextEditFrameOverlayObject(
704
    const TextEditOverlayObject& rTextEditOverlayObject)
705
0
    : OverlayObject(rTextEditOverlayObject.getBaseColor())
706
0
    , mrTextEditOverlayObject(rTextEditOverlayObject)
707
0
{
708
0
    allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
709
    // use selection colors in HighContrast mode
710
0
    mbHighContrastSelection = true;
711
0
}
712
713
TextEditFrameOverlayObject::~TextEditFrameOverlayObject()
714
0
{
715
0
    if (getOverlayManager())
716
0
    {
717
0
        getOverlayManager()->remove(*this);
718
0
    }
719
0
}
720
721
drawinglayer::primitive2d::Primitive2DContainer
722
TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
723
0
{
724
0
    if (!getPrimitive2DSequence().empty())
725
0
    {
726
0
        if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
727
0
        {
728
            // conditions of last local decomposition have changed, delete to force new evaluation
729
0
            const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
730
0
        }
731
0
    }
732
733
0
    if (getPrimitive2DSequence().empty())
734
0
    {
735
        // remember new buffered values
736
0
        const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
737
0
        const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
738
0
    }
739
740
    // call base implementation
741
0
    return OverlayObject::getOverlayObjectPrimitive2DSequence();
742
0
}
743
744
void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
745
0
{
746
0
    bool bObjectChange(false);
747
748
    // check current range
749
0
    const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
750
0
    basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
751
0
    aNewRange.expand(rMinTextEditArea);
752
753
0
    if (aNewRange != maRange)
754
0
    {
755
0
        maRange = aNewRange;
756
0
        bObjectChange = true;
757
0
    }
758
759
    // check if text primitives did change
760
0
    SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(&getOutlinerView().GetOutliner());
761
762
0
    if (pSdrOutliner)
763
0
    {
764
        // get TextPrimitives directly from active Outliner
765
0
        basegfx::B2DHomMatrix aNewTransformA;
766
0
        basegfx::B2DHomMatrix aNewTransformB;
767
0
        basegfx::B2DRange aClipRange;
768
0
        drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
769
770
        // active Outliner is always in unified oriented coordinate system (currently)
771
        // so just translate to TopLeft of visible Range. Keep in mind that top-left
772
        // depends on vertical text and top-to-bottom text attributes
773
0
        const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
774
0
        const bool bVerticalWriting(pSdrOutliner->IsVertical());
775
0
        const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
776
0
        const double fStartInX(bVerticalWriting && bTopToBottom
777
0
                                   ? aOutArea.Right() - aVisArea.Left()
778
0
                                   : aOutArea.Left() - aVisArea.Left());
779
0
        const double fStartInY(bVerticalWriting && !bTopToBottom
780
0
                                   ? aOutArea.Bottom() - aVisArea.Top()
781
0
                                   : aOutArea.Top() - aVisArea.Top());
782
783
0
        aNewTransformB.translate(fStartInX, fStartInY);
784
785
        // get the current TextPrimitives. This is the most expensive part
786
        // of this mechanism, it *may* be possible to buffer layouted
787
        // primitives per ParaPortion with/in/dependent on the EditEngine
788
        // content if needed. For now, get and compare
789
0
        TextHierarchyBreakupBlockText aBreakup(*pSdrOutliner, aNewTransformA, aNewTransformB,
790
0
                                               aClipRange);
791
0
        pSdrOutliner->StripPortions(aBreakup);
792
0
        aNewTextPrimitives.append(aBreakup.getTextPortionPrimitives());
793
794
0
        if (aNewTextPrimitives != maTextPrimitives)
795
0
        {
796
0
            maTextPrimitives = std::move(aNewTextPrimitives);
797
0
            bObjectChange = true;
798
0
        }
799
0
    }
800
801
0
    if (bObjectChange)
802
0
    {
803
        // if there really *was* a change signal the OverlayManager to
804
        // refresh this object's visualization
805
0
        objectChange();
806
807
0
        if (mxOverlayFrame)
808
0
            mxOverlayFrame->objectChange();
809
810
        // on data change, always do a SelectionChange, too
811
        // since the selection is an integral part of text visualization
812
0
        checkSelectionChange();
813
0
    }
814
0
}
815
816
void TextEditOverlayObject::checkSelectionChange()
817
0
{
818
0
    if (!(getOverlaySelection() && getOverlayManager()))
819
0
        return;
820
821
0
    std::vector<tools::Rectangle> aLogicRects;
822
0
    std::vector<basegfx::B2DRange> aLogicRanges;
823
0
    const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
824
825
    // get logic selection
826
0
    getOutlinerView().GetSelectionRectangles(aLogicRects);
827
828
0
    aLogicRanges.reserve(aLogicRects.size());
829
0
    for (const auto& aRect : aLogicRects)
830
0
    {
831
        // convert from logic Rectangles to logic Ranges, do not forget to add
832
        // one Unit (in this case logical units for one pixel, pre-calculated)
833
0
        aLogicRanges.emplace_back(
834
0
            aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
835
0
            aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
836
0
    }
837
838
0
    if (mxOverlayTransparentSelection)
839
0
        mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges));
840
0
    else
841
0
        mxOverlayHighContrastSelection->setRanges(std::move(aLogicRanges));
842
0
}
843
} // end of anonymous namespace
844
845
// TextEdit
846
847
// callback from the active EditView, forward to eventual existing instances of the
848
// TextEditOverlayObject(s). This will additionally update the selection which
849
// is an integral part of the text visualization
850
void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
851
0
{
852
0
    if (!IsTextEdit())
853
0
        return;
854
855
    // MinTextRange may have changed. Forward it, too
856
0
    const basegfx::B2DRange aMinTextRange
857
0
        = vcl::unotools::b2DRectangleFromRectangle(m_aMinTextEditArea);
858
859
0
    for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
860
0
    {
861
0
        TextEditOverlayObject* pCandidate
862
0
            = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
863
864
0
        if (pCandidate)
865
0
        {
866
0
            pCandidate->checkDataChange(aMinTextRange);
867
0
        }
868
0
    }
869
0
}
870
871
// callback from the active EditView, forward to eventual existing instances of the
872
// TextEditOverlayObject(s). This call *only* updates the selection visualization
873
// which is e.g. used when only the selection is changed, but not the text
874
void SdrObjEditView::EditViewSelectionChange()
875
0
{
876
0
    if (!IsTextEdit())
877
0
        return;
878
879
0
    for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
880
0
    {
881
0
        TextEditOverlayObject* pCandidate
882
0
            = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
883
884
0
        if (pCandidate)
885
0
        {
886
0
            pCandidate->checkSelectionChange();
887
0
        }
888
0
    }
889
0
}
890
891
0
OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }
892
893
Point SdrObjEditView::EditViewPointerPosPixel() const
894
0
{
895
0
    return mpTextEditWin->GetPointerPosPixel();
896
0
}
897
898
css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
899
0
{
900
0
    if (!mpTextEditWin)
901
0
        return nullptr;
902
0
    return mpTextEditWin->GetClipboard();
903
0
}
904
905
css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
906
0
{
907
0
    if (!mpTextEditWin)
908
0
        return nullptr;
909
0
    return mpTextEditWin->GetDropTarget();
910
0
}
911
912
void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
913
0
{
914
0
    if (!mpTextEditWin)
915
0
        return;
916
0
    mpTextEditWin->SetInputContext(rInputContext);
917
0
}
918
919
void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
920
0
{
921
0
    if (!mpTextEditWin)
922
0
        return;
923
0
    mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
924
0
}
925
926
void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
927
0
{
928
0
    if (!comphelper::LibreOfficeKit::isActive())
929
0
    {
930
        // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
931
        // to update accordingly (will update selection, too). Suppress new
932
        // stuff when LibreOfficeKit is active
933
0
        EditViewInvalidate(tools::Rectangle());
934
0
        return;
935
0
    }
936
937
    // draw old text edit stuff
938
0
    if (!IsTextEdit())
939
0
        return;
940
941
0
    const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
942
0
    if (!pActiveOutliner)
943
0
        return;
944
945
0
    const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
946
0
    if (!nViewCount)
947
0
        return;
948
949
0
    const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
950
0
    const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
951
952
0
    OutlinerView* pOLV = pActiveOutliner->GetView(0);
953
0
    SdrPage* pPage = GetSdrPageView()->GetPage();
954
0
    pOLV->SetBackgroundColor(pPage->GetPageBackgroundColor(GetSdrPageView(), true));
955
0
    ImpPaintOutlinerView(*pOLV, aCheckRect, rPaintWindow.GetTargetOutputDevice());
956
0
}
957
958
void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
959
                                          OutputDevice& rTargetDevice) const
960
0
{
961
0
    const SdrTextObj* pText = GetTextEditObject();
962
0
    bool bTextFrame(pText && pText->IsTextFrame());
963
0
    bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
964
0
    bool bModified(mpTextEditOutliner->IsModified());
965
0
    tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
966
0
    aBlankRect.Union(m_aMinTextEditArea);
967
0
    tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));
968
969
    // in the tiled rendering case, the setup is incomplete, and we very
970
    // easily get an empty rRect on input - that will cause that everything is
971
    // clipped; happens in case of editing text inside a shape in Calc.
972
    // FIXME would be better to complete the setup so that we don't get an
973
    // empty rRect here
974
0
    if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
975
0
        aBlankRect.Intersection(rRect);
976
977
0
    rOutlView.GetOutliner().SetUpdateLayout(true); // Bugfix #22596#
978
0
    rOutlView.DrawText_ToEditView(aBlankRect, &rTargetDevice);
979
980
0
    if (!bModified)
981
0
    {
982
0
        mpTextEditOutliner->ClearModifyFlag();
983
0
    }
984
985
0
    if (bTextFrame && !bFitToSize)
986
0
    {
987
        // completely reworked to use primitives; this ensures same look and functionality
988
0
        const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
989
0
        std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
990
0
            drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
991
0
                                                                         aViewInformation2D));
992
993
0
        const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
994
0
        const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
995
0
        const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
996
0
        const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
997
0
        const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
998
0
        const drawinglayer::primitive2d::Primitive2DReference xReference(
999
0
            new drawinglayer::primitive2d::OverlayRectanglePrimitive(
1000
0
                aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
1001
0
                0.0, // shrink
1002
0
                0.0));
1003
0
        const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };
1004
1005
0
        rTargetDevice.EnableMapMode(false);
1006
0
        xProcessor->process(aSequence);
1007
0
        rTargetDevice.EnableMapMode(bMapModeEnabled);
1008
0
    }
1009
1010
0
    rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
1011
0
}
1012
1013
void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
1014
0
{
1015
0
    vcl::Window* pWin = rOutlView.GetWindow();
1016
1017
0
    if (!pWin || pWin->isDisposed())
1018
0
        return;
1019
1020
0
    const SdrTextObj* pText = GetTextEditObject();
1021
0
    bool bTextFrame(pText && pText->IsTextFrame());
1022
0
    bool bFitToSize(pText && pText->IsFitToSize());
1023
1024
0
    if (!bTextFrame || bFitToSize)
1025
0
        return;
1026
1027
0
    tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
1028
0
    aBlankRect.Union(m_aMinTextEditArea);
1029
0
    tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
1030
0
    sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
1031
1032
0
    aPixRect.AdjustLeft(-1);
1033
0
    aPixRect.AdjustTop(-1);
1034
0
    aPixRect.AdjustRight(1);
1035
0
    aPixRect.AdjustBottom(1);
1036
1037
0
    {
1038
        // limit xPixRect because of driver problems when pixel coordinates are too far out
1039
0
        Size aMaxXY(pWin->GetOutputSizePixel());
1040
0
        tools::Long a(2 * nPixSiz);
1041
0
        tools::Long nMaxX(aMaxXY.Width() + a);
1042
0
        tools::Long nMaxY(aMaxXY.Height() + a);
1043
1044
0
        if (aPixRect.Left() < -a)
1045
0
            aPixRect.SetLeft(-a);
1046
0
        if (aPixRect.Top() < -a)
1047
0
            aPixRect.SetTop(-a);
1048
0
        if (aPixRect.Right() > nMaxX)
1049
0
            aPixRect.SetRight(nMaxX);
1050
0
        if (aPixRect.Bottom() > nMaxY)
1051
0
            aPixRect.SetBottom(nMaxY);
1052
0
    }
1053
1054
0
    tools::Rectangle aOuterPix(aPixRect);
1055
0
    aOuterPix.AdjustLeft(-nPixSiz);
1056
0
    aOuterPix.AdjustTop(-nPixSiz);
1057
0
    aOuterPix.AdjustRight(nPixSiz);
1058
0
    aOuterPix.AdjustBottom(nPixSiz);
1059
1060
0
    bool bMapModeEnabled(pWin->IsMapModeEnabled());
1061
0
    pWin->EnableMapMode(false);
1062
0
    pWin->Invalidate(aOuterPix);
1063
0
    pWin->EnableMapMode(bMapModeEnabled);
1064
0
}
1065
1066
OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
1067
                                                  SfxViewShell* pViewShell) const
1068
0
{
1069
    // background
1070
0
    Color aBackground(GetTextEditBackgroundColor(*this));
1071
0
    rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get();
1072
0
    bool bTextFrame = pText != nullptr && pText->IsTextFrame();
1073
0
    bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
1074
    // create OutlinerView
1075
0
    OutlinerView* pOutlView = pGivenView;
1076
0
    mpTextEditOutliner->SetUpdateLayout(false);
1077
1078
0
    if (pOutlView == nullptr)
1079
0
    {
1080
0
        pOutlView = new OutlinerView(*mpTextEditOutliner, pWin);
1081
0
    }
1082
0
    else
1083
0
    {
1084
0
        pOutlView->SetWindow(pWin);
1085
0
    }
1086
1087
0
    if (mbNegativeX)
1088
0
        pOutlView->GetEditView().SetNegativeX(mbNegativeX);
1089
1090
    // disallow scrolling
1091
0
    EVControlBits nStat = pOutlView->GetControlWord();
1092
0
    nStat &= ~EVControlBits::AUTOSCROLL;
1093
    // AutoViewSize only if not ContourFrame.
1094
0
    if (!bContourFrame)
1095
0
        nStat |= EVControlBits::AUTOSIZE;
1096
0
    if (bTextFrame)
1097
0
    {
1098
0
        sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
1099
0
        nStat |= EVControlBits::INVONEMORE;
1100
0
        pOutlView->SetInvalidateMore(nPixSiz);
1101
0
    }
1102
0
    pOutlView->SetControlWord(nStat);
1103
0
    pOutlView->SetBackgroundColor(aBackground);
1104
1105
    // In case we're in the process of constructing a new view shell,
1106
    // SfxViewShell::Current() may still point to the old one. So if possible,
1107
    // depend on the application owning this draw view to provide the view
1108
    // shell.
1109
0
    SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
1110
0
    pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());
1111
1112
0
    if (pText != nullptr)
1113
0
    {
1114
0
        pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
1115
0
        mpTextEditOutliner->SetFixedCellHeight(
1116
0
            pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1117
0
    }
1118
    // do update before setting output area so that aTextEditArea can be recalculated
1119
0
    mpTextEditOutliner->SetUpdateLayout(true);
1120
0
    pOutlView->SetOutputArea(m_aTextEditArea);
1121
0
    ImpInvalidateOutlinerView(*pOutlView);
1122
0
    return pOutlView;
1123
0
}
1124
1125
IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
1126
0
{
1127
0
    if (mpTextEditOutliner)
1128
0
    {
1129
0
        rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1130
0
        if (pTextObj)
1131
0
        {
1132
0
            pTextObj->onEditOutlinerStatusEvent(&rEditStat);
1133
0
        }
1134
0
    }
1135
0
}
1136
1137
void SdrObjEditView::ImpChainingEventHdl()
1138
0
{
1139
0
    if (!mpTextEditOutliner)
1140
0
        return;
1141
1142
0
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1143
0
    OutlinerView* pOLV = GetTextEditOutlinerView();
1144
0
    if (pTextObj && pOLV)
1145
0
    {
1146
0
        TextChain* pTextChain = pTextObj->GetTextChain();
1147
1148
        // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
1149
0
        if (!pTextObj->IsChainable())
1150
0
        {
1151
0
            return;
1152
0
        }
1153
        // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
1154
0
        if (pTextChain->GetNilChainingEvent(pTextObj.get()))
1155
0
        {
1156
0
            return;
1157
0
        }
1158
1159
        // We prevent to trigger further handling of overflow/underflow for pTextObj
1160
0
        pTextChain->SetNilChainingEvent(pTextObj.get(), true); // XXX
1161
1162
        // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
1163
0
        pTextChain->SetPreChainingSel(pTextObj.get(), pOLV->GetSelection());
1164
        //maPreChainingSel = new ESelection(pOLV->GetSelection());
1165
1166
        // Handling Undo
1167
0
        const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
1168
1169
0
        const bool bUndoEnabled = IsUndoEnabled();
1170
0
        std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1171
0
        if (bUndoEnabled)
1172
0
            pTxtUndo.reset(
1173
0
                dynamic_cast<SdrUndoObjSetText*>(GetModel()
1174
0
                                                     .GetSdrUndoFactory()
1175
0
                                                     .CreateUndoObjectSetText(*pTextObj, nText)
1176
0
                                                     .release()));
1177
1178
        // trigger actual chaining
1179
0
        pTextObj->onChainingEvent();
1180
1181
0
        if (pTxtUndo)
1182
0
        {
1183
0
            pTxtUndo->AfterSetText();
1184
0
            if (!pTxtUndo->IsDifferent())
1185
0
            {
1186
0
                pTxtUndo.reset();
1187
0
            }
1188
0
        }
1189
1190
0
        if (pTxtUndo)
1191
0
            AddUndo(std::move(pTxtUndo));
1192
1193
        //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
1194
        //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
1195
1196
        // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
1197
0
        pTextChain->SetNilChainingEvent(pTextObj.get(), false);
1198
0
    }
1199
0
    else
1200
0
    {
1201
        // XXX
1202
0
        SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
1203
0
    }
1204
0
}
1205
1206
IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void)
1207
0
{
1208
0
    SdrTextObj* pTextObj = GetTextEditObject();
1209
0
    if (!pTextObj)
1210
0
        return;
1211
0
    ImpChainingEventHdl();
1212
0
    TextChainCursorManager aCursorManager(this, pTextObj);
1213
0
    ImpMoveCursorAfterChainingEvent(&aCursorManager);
1214
0
}
1215
1216
void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
1217
0
{
1218
0
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1219
1220
0
    if (!pTextObj || !pCursorManager)
1221
0
        return;
1222
1223
    // Check if it has links to move it to
1224
0
    if (!pTextObj || !pTextObj->IsChainable())
1225
0
        return;
1226
1227
0
    TextChain* pTextChain = pTextObj->GetTextChain();
1228
0
    ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj.get());
1229
1230
0
    pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj.get()),
1231
0
                                                   aNewSel);
1232
1233
    // Reset event
1234
0
    pTextChain->SetCursorEvent(pTextObj.get(), CursorChainingEvent::NULL_EVENT);
1235
0
}
1236
1237
IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
1238
0
{
1239
0
    bool bOk = false;
1240
0
    OUString& rStr = pFI->GetRepresentation();
1241
0
    rStr.clear();
1242
0
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1243
0
    if (pTextObj != nullptr)
1244
0
    {
1245
0
        std::optional<Color> pTxtCol;
1246
0
        std::optional<Color> pFldCol;
1247
0
        std::optional<FontLineStyle> pFldLineStyle;
1248
0
        bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
1249
0
                                       pTxtCol, pFldCol, pFldLineStyle, rStr);
1250
0
        if (bOk)
1251
0
        {
1252
0
            if (pTxtCol)
1253
0
            {
1254
0
                pFI->SetTextColor(*pTxtCol);
1255
0
            }
1256
0
            if (pFldLineStyle)
1257
0
            {
1258
0
                pFI->SetFontLineStyle(*pFldLineStyle);
1259
0
            }
1260
0
            if (pFldCol)
1261
0
            {
1262
0
                pFI->SetFieldColor(*pFldCol);
1263
0
            }
1264
0
            else
1265
0
            {
1266
0
                pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
1267
0
            }
1268
0
        }
1269
0
    }
1270
0
    Outliner& rDrawOutl = GetModel().GetDrawOutliner(pTextObj.get());
1271
0
    Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
1272
0
    if (!bOk && aDrawOutlLink.IsSet())
1273
0
    {
1274
0
        aDrawOutlLink.Call(pFI);
1275
0
        bOk = !rStr.isEmpty();
1276
0
    }
1277
0
    if (!bOk)
1278
0
    {
1279
0
        m_aOldCalcFieldValueLink.Call(pFI);
1280
0
    }
1281
0
}
1282
1283
0
IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }
1284
1285
// Default implementation - null UndoManager
1286
std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
1287
0
{
1288
0
    SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
1289
0
    return std::unique_ptr<SdrUndoManager>();
1290
0
}
1291
1292
bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
1293
                                      bool bIsNewObj, SdrOutliner* pGivenOutliner,
1294
                                      OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
1295
                                      bool bOnlyOneView, bool bGrabFocus)
1296
0
{
1297
    // FIXME cannot be an assert() yet, the code is not ready for that;
1298
    // eg. press F7 in Impress when you are inside a text object with spelling
1299
    // mistakes => boom; and it is unclear how to avoid that
1300
0
    SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
1301
    // FIXME this encourages all sorts of bad habits and should be removed
1302
0
    SdrEndTextEdit();
1303
1304
0
    SdrTextObj* pObj = DynCastSdrTextObj(pObj_);
1305
0
    if (!pObj)
1306
0
        return false; // currently only possible with text objects
1307
1308
0
    if (bGrabFocus && pWin)
1309
0
    {
1310
        // attention, this call may cause an EndTextEdit() call to this view
1311
0
        pWin->GrabFocus(); // to force the cursor into the edit view
1312
0
    }
1313
1314
0
    mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
1315
0
    mbTextEditOnlyOneView = bOnlyOneView;
1316
0
    mbTextEditNewObj = bIsNewObj;
1317
0
    const sal_uInt32 nWinCount(PaintWindowCount());
1318
1319
0
    bool bBrk(false);
1320
1321
0
    if (!pWin)
1322
0
    {
1323
0
        for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
1324
0
        {
1325
0
            SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1326
1327
0
            if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
1328
0
            {
1329
0
                pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
1330
0
            }
1331
0
        }
1332
1333
        // break, when no window exists
1334
0
        if (!pWin)
1335
0
        {
1336
0
            bBrk = true;
1337
0
        }
1338
0
    }
1339
1340
0
    if (!bBrk && !pPV)
1341
0
    {
1342
0
        pPV = GetSdrPageView();
1343
1344
        // break, when no PageView for the object exists
1345
0
        if (!pPV)
1346
0
        {
1347
0
            bBrk = true;
1348
0
        }
1349
0
    }
1350
1351
    // no TextEdit on objects in locked Layer
1352
0
    if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
1353
0
    {
1354
0
        bBrk = true;
1355
0
    }
1356
1357
0
    if (mpTextEditOutliner)
1358
0
    {
1359
0
        OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
1360
0
        mpTextEditOutliner.reset();
1361
0
    }
1362
1363
0
    if (!bBrk)
1364
0
    {
1365
0
        mpTextEditWin = pWin;
1366
0
        mpTextEditPV = pPV;
1367
0
        mxWeakTextEditObj = pObj;
1368
0
        if (pGivenOutliner)
1369
0
        {
1370
0
            mpTextEditOutliner.reset(pGivenOutliner);
1371
0
            pGivenOutliner = nullptr; // so we don't delete it on the error path
1372
0
        }
1373
0
        else
1374
0
            mpTextEditOutliner
1375
0
                = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject());
1376
1377
0
        {
1378
0
            mpTextEditOutliner->ForceAutoColor(
1379
0
                officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get());
1380
0
        }
1381
1382
0
        m_aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
1383
        // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
1384
0
        mpTextEditOutliner->SetCalcFieldValueHdl(
1385
0
            LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
1386
0
        mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
1387
0
        mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));
1388
1389
        // It is just necessary to make the visualized page known. Set it.
1390
0
        mpTextEditOutliner->setVisualizedPage(pPV->GetPage());
1391
1392
0
        rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1393
0
        mpTextEditOutliner->SetTextObjNoInit(pTextObj.get());
1394
1395
0
        if (pTextObj->BegTextEdit(*mpTextEditOutliner))
1396
0
        {
1397
            // switch off any running TextAnimations
1398
0
            pTextObj->SetTextAnimationAllowed(false);
1399
1400
            // remember old cursor
1401
0
            if (mpTextEditOutliner->GetViewCount() != 0)
1402
0
            {
1403
0
                mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
1404
0
            }
1405
1406
            // Determine EditArea via TakeTextEditArea.
1407
            // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
1408
            // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
1409
0
            pTextObj->TakeTextEditArea(nullptr, nullptr, &m_aTextEditArea, &m_aMinTextEditArea);
1410
1411
0
            tools::Rectangle aTextRect;
1412
0
            tools::Rectangle aAnchorRect;
1413
0
            pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
1414
0
                                   &aAnchorRect /* Give true here, not false */);
1415
1416
0
            if (!pTextObj->IsContourTextFrame())
1417
0
            {
1418
                // FitToSize not together with ContourFrame, for now
1419
0
                if (pTextObj->IsFitToSize())
1420
0
                    aTextRect = aAnchorRect;
1421
0
            }
1422
1423
0
            m_aTextEditArea = aTextRect;
1424
1425
            // add possible GridOffset to up-to-now view-independent EditAreas
1426
0
            basegfx::B2DVector aGridOffset(0.0, 0.0);
1427
0
            if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), pPV))
1428
0
            {
1429
0
                const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
1430
0
                                    basegfx::fround<tools::Long>(aGridOffset.getY()));
1431
1432
0
                m_aTextEditArea += aOffset;
1433
0
                m_aMinTextEditArea += aOffset;
1434
0
            }
1435
1436
0
            Point aPvOfs(pTextObj->GetTextEditOffset());
1437
0
            m_aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1438
0
            m_aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1439
0
            m_pTextEditCursorBuffer = pWin->GetCursor();
1440
1441
0
            maHdlList.SetMoveOutside(true);
1442
1443
            // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1444
            // to call AdjustMarkHdl() always.
1445
0
            AdjustMarkHdl();
1446
1447
0
            mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);
1448
1449
0
            if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
1450
0
            {
1451
                // activate visualization of EditView on Overlay, suppress when
1452
                // LibreOfficeKit is active
1453
0
                mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
1454
1455
0
                const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
1456
0
                const SdrTextObj* pText = GetTextEditObject();
1457
                // show for cases like tdf#94223 but not for table cells like tdf#151311
1458
0
                const bool bVisualizeSurroundingFrame(
1459
0
                    pText && pText->GetObjIdentifier() != SdrObjKind::Table);
1460
0
                SdrPageView* pPageView = GetSdrPageView();
1461
1462
0
                if (pPageView)
1463
0
                {
1464
0
                    for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
1465
0
                    {
1466
0
                        const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
1467
1468
0
                        if (rPageWindow.GetPaintWindow().OutputToWindow())
1469
0
                        {
1470
0
                            const rtl::Reference<sdr::overlay::OverlayManager>& xManager
1471
0
                                = rPageWindow.GetOverlayManager();
1472
0
                            if (xManager.is())
1473
0
                            {
1474
0
                                std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
1475
0
                                    new TextEditOverlayObject(aHilightColor,
1476
0
                                                              *mpTextEditOutlinerView));
1477
1478
0
                                xManager->add(*pNewTextEditOverlayObject);
1479
0
                                if (bVisualizeSurroundingFrame)
1480
0
                                    xManager->add(*pNewTextEditOverlayObject->getOverlayFrame());
1481
0
                                xManager->add(*pNewTextEditOverlayObject->getOverlaySelection());
1482
1483
0
                                maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
1484
0
                            }
1485
0
                        }
1486
0
                    }
1487
0
                }
1488
0
            }
1489
1490
            // check if this view is already inserted
1491
0
            size_t i2, nCount = mpTextEditOutliner->GetViewCount();
1492
0
            for (i2 = 0; i2 < nCount; i2++)
1493
0
            {
1494
0
                if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
1495
0
                    break;
1496
0
            }
1497
1498
0
            if (i2 == nCount)
1499
0
                mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);
1500
1501
0
            maHdlList.SetMoveOutside(false);
1502
0
            maHdlList.SetMoveOutside(true);
1503
1504
            // register all windows as OutlinerViews with the Outliner
1505
0
            if (!bOnlyOneView)
1506
0
            {
1507
0
                for (sal_uInt32 i = 0; i < nWinCount; i++)
1508
0
                {
1509
0
                    SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1510
0
                    OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1511
1512
0
                    if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1513
0
                    {
1514
0
                        OutlinerView* pOutlView
1515
0
                            = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1516
0
                        mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
1517
0
                    }
1518
0
                }
1519
1520
0
                if (comphelper::LibreOfficeKit::isActive())
1521
0
                {
1522
                    // Register an outliner view for all other sdr views that
1523
                    // show the same page, so that when the text edit changes,
1524
                    // all interested windows get an invalidation.
1525
0
                    SdrViewIter::ForAllViews(pObj->getSdrPageFromSdrObject(), [this, &pWin](
1526
0
                                                                                  SdrView* pView) {
1527
0
                        if (pView == this)
1528
0
                            return;
1529
1530
0
                        for (sal_uInt32 nViewPaintWindow = 0;
1531
0
                             nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
1532
0
                        {
1533
0
                            SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
1534
0
                            OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1535
1536
0
                            if (&rOutDev != pWin->GetOutDev()
1537
0
                                && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1538
0
                            {
1539
0
                                OutlinerView* pOutlView
1540
0
                                    = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1541
0
                                pOutlView->HideCursor();
1542
0
                                rOutDev.GetOwnerWindow()->SetCursor(nullptr);
1543
0
                                mpTextEditOutliner->InsertView(pOutlView);
1544
0
                            }
1545
0
                        }
1546
0
                    });
1547
0
                }
1548
0
            }
1549
1550
0
            mpTextEditOutlinerView->ShowCursor();
1551
0
            mpTextEditOutliner->SetStatusEventHdl(
1552
0
                LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));
1553
1554
            // IASS: start listening to ModelChanges of TextEdit
1555
0
            if (isInteractiveSlideShow()
1556
0
                || pTextObj->GetViewContact().hasMultipleViewObjectContacts())
1557
0
                mpTextEditOutliner->SetModifyHdl(LINK(this, SdrObjEditView, ImpModifyHdl));
1558
1559
0
            if (pTextObj->IsChainable())
1560
0
            {
1561
0
                mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
1562
0
                    LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
1563
0
            }
1564
1565
0
            mpTextEditOutliner->ClearModifyFlag();
1566
1567
0
            if (pTextObj->IsFitToSize())
1568
0
            {
1569
0
                pWin->Invalidate(m_aTextEditArea);
1570
0
            }
1571
1572
0
            SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
1573
0
            GetModel().Broadcast(aHint);
1574
0
            if (auto pBroadcaster = pTextObj->GetBroadcaster())
1575
0
                pBroadcaster->Broadcast(aHint);
1576
1577
0
            mpTextEditOutliner->setVisualizedPage(nullptr);
1578
1579
0
            if (mxSelectionController.is())
1580
0
                mxSelectionController->onSelectionHasChanged();
1581
1582
0
            if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1583
0
            {
1584
0
                SdrUndoManager* pSdrUndoManager = nullptr;
1585
0
                mpLocalTextEditUndoManager = createLocalTextUndoManager();
1586
1587
0
                if (mpLocalTextEditUndoManager)
1588
0
                    pSdrUndoManager = mpLocalTextEditUndoManager.get();
1589
1590
0
                if (pSdrUndoManager)
1591
0
                {
1592
                    // we have an outliner, undo manager and it's an EditUndoManager, exchange
1593
                    // the document undo manager and the default one from the outliner and tell
1594
                    // it that text edit starts by setting a callback if it needs to end text edit mode.
1595
0
                    assert(nullptr == mpOldTextEditUndoManager);
1596
1597
0
                    mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
1598
0
                    pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
1599
0
                }
1600
0
                else
1601
0
                {
1602
0
                    OSL_ENSURE(false,
1603
0
                               "The document undo manager is not derived from SdrUndoManager (!)");
1604
0
                }
1605
0
            }
1606
1607
0
            return true; // ran fine, let TextEdit run now
1608
0
        }
1609
0
        else
1610
0
        {
1611
0
            mpTextEditOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink);
1612
0
            mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1613
0
            mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1614
0
        }
1615
0
    }
1616
0
    if (mpTextEditOutliner != nullptr)
1617
0
    {
1618
0
        mpTextEditOutliner->setVisualizedPage(nullptr);
1619
0
    }
1620
1621
    // something went wrong...
1622
0
    if (!bDontDeleteOutliner)
1623
0
    {
1624
0
        delete pGivenOutliner;
1625
0
        if (pGivenOutlinerView != nullptr)
1626
0
        {
1627
0
            delete pGivenOutlinerView;
1628
0
            pGivenOutlinerView = nullptr;
1629
0
        }
1630
0
    }
1631
0
    mpTextEditOutliner.reset();
1632
1633
0
    mpTextEditOutlinerView = nullptr;
1634
0
    mxWeakTextEditObj.clear();
1635
0
    mpTextEditPV = nullptr;
1636
0
    mpTextEditWin = nullptr;
1637
0
    maHdlList.SetMoveOutside(false);
1638
1639
0
    return false;
1640
0
}
1641
1642
SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
1643
0
{
1644
    // IASS: stop eventual running timer immediately
1645
0
    maTextEditUpdateTimer.Stop();
1646
1647
0
    SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
1648
0
    rtl::Reference<SdrTextObj> pTEObj = mxWeakTextEditObj.get();
1649
0
    vcl::Window* pTEWin = mpTextEditWin;
1650
0
    OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
1651
0
    vcl::Cursor* pTECursorBuffer = m_pTextEditCursorBuffer;
1652
0
    SdrUndoManager* pUndoEditUndoManager = nullptr;
1653
0
    bool bNeedToUndoSavedRedoTextEdit(false);
1654
1655
0
    if (IsUndoEnabled() && pTEObj && mpTextEditOutliner
1656
0
        && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1657
0
    {
1658
        // change back the UndoManager to the remembered original one
1659
0
        SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
1660
0
        mpOldTextEditUndoManager = nullptr;
1661
1662
0
        if (pOriginal)
1663
0
        {
1664
            // check if we got back our document undo manager
1665
0
            SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();
1666
1667
0
            if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
1668
0
            {
1669
0
                if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
1670
0
                {
1671
                    // remember the UndoManager where missing Undos have to be triggered after end
1672
                    // text edit. When the undo had triggered the end text edit, the original action
1673
                    // which had to be undone originally is not yet undone.
1674
0
                    pUndoEditUndoManager = pSdrUndoManager;
1675
1676
                    // We are ending text edit; if text edit was triggered from undo, execute all redos
1677
                    // to create a complete text change undo action for the redo buffer. Also mark this
1678
                    // state when at least one redo was executed; the created extra TextChange needs to
1679
                    // be undone in addition to the first real undo outside the text edit changes
1680
0
                    while (pSdrUndoManager->GetRedoActionCount()
1681
0
                           > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
1682
0
                    {
1683
0
                        bNeedToUndoSavedRedoTextEdit = true;
1684
0
                        pSdrUndoManager->Redo();
1685
0
                    }
1686
0
                }
1687
1688
                // reset the callback link and let the undo manager cleanup all text edit
1689
                // undo actions to get the stack back to the form before the text edit
1690
0
                pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
1691
0
            }
1692
0
            else
1693
0
            {
1694
0
                OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
1695
0
                                  "expected document UndoManager (!)");
1696
0
                delete pOriginal;
1697
0
            }
1698
1699
            // cid#1493241 - Wrapper object use after free
1700
0
            if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
1701
0
                pUndoEditUndoManager = nullptr;
1702
0
            mpLocalTextEditUndoManager.reset();
1703
0
        }
1704
0
    }
1705
0
    else
1706
0
    {
1707
0
        assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
1708
0
    }
1709
1710
0
    if (auto pTextEditObj = mxWeakTextEditObj.get())
1711
0
    {
1712
0
        SdrHint aHint(SdrHintKind::EndEdit, *pTextEditObj);
1713
0
        GetModel().Broadcast(aHint);
1714
0
        if (auto pBroadcaster = pTextEditObj->GetBroadcaster())
1715
0
            pBroadcaster->Broadcast(aHint);
1716
0
    }
1717
1718
    // if new mechanism was used, clean it up. At cleanup no need to check
1719
    // for LibreOfficeKit
1720
0
    if (mpTextEditOutlinerView)
1721
0
    {
1722
0
        mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
1723
0
        maTEOverlayGroup.clear();
1724
0
    }
1725
1726
0
    mxWeakTextEditObj.clear();
1727
0
    mpTextEditPV = nullptr;
1728
0
    mpTextEditWin = nullptr;
1729
0
    mpTextEditOutlinerView = nullptr;
1730
0
    m_pTextEditCursorBuffer = nullptr;
1731
0
    m_aTextEditArea = tools::Rectangle();
1732
1733
0
    if (SdrOutliner* pTEOutliner = mpTextEditOutliner.release())
1734
0
    {
1735
0
        bool bModified = pTEOutliner->IsModified();
1736
0
        if (pTEOutlinerView != nullptr)
1737
0
        {
1738
0
            pTEOutlinerView->HideCursor();
1739
0
        }
1740
0
        if (pTEObj != nullptr)
1741
0
        {
1742
0
            pTEOutliner->CompleteOnlineSpelling();
1743
1744
0
            std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1745
1746
0
            if (bModified)
1747
0
            {
1748
0
                sal_Int32 nText;
1749
0
                for (nText = 0; nText < pTEObj->getTextCount(); ++nText)
1750
0
                    if (pTEObj->getText(nText) == pTEObj->getActiveText())
1751
0
                        break;
1752
1753
0
                pTxtUndo.reset(
1754
0
                    dynamic_cast<SdrUndoObjSetText*>(GetModel()
1755
0
                                                         .GetSdrUndoFactory()
1756
0
                                                         .CreateUndoObjectSetText(*pTEObj, nText)
1757
0
                                                         .release()));
1758
0
            }
1759
0
            DBG_ASSERT(!bModified || pTxtUndo,
1760
0
                       "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
1761
            // Set old CalcFieldValue-Handler again, this
1762
            // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
1763
0
            pTEOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink);
1764
0
            pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1765
0
            pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1766
1767
            // IASS: stop listening to ModelChanges of TextEdit
1768
0
            pTEOutliner->SetModifyHdl(Link<LinkParamNone*, void>());
1769
1770
0
            const bool bUndo = IsUndoEnabled();
1771
0
            if (bUndo)
1772
0
            {
1773
0
                OUString aObjName(pTEObj->TakeObjNameSingul());
1774
0
                BegUndo(SvxResId(STR_UndoObjSetText), aObjName);
1775
0
            }
1776
1777
0
            pTEObj->EndTextEdit(*pTEOutliner);
1778
1779
0
            if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork()))
1780
0
            {
1781
0
                pTEObj->ActionChanged();
1782
0
            }
1783
1784
0
            if (pTxtUndo != nullptr)
1785
0
            {
1786
0
                pTxtUndo->AfterSetText();
1787
0
                if (!pTxtUndo->IsDifferent())
1788
0
                {
1789
0
                    pTxtUndo.reset();
1790
0
                }
1791
0
            }
1792
            // check deletion of entire TextObj
1793
0
            std::unique_ptr<SdrUndoAction> pDelUndo;
1794
0
            bool bDelObj = false;
1795
0
            if (mbTextEditNewObj)
1796
0
            {
1797
0
                bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj()
1798
0
                          && !pTEObj->HasFill() && !pTEObj->HasLine();
1799
1800
0
                if (pTEObj->IsInserted() && bDelObj
1801
0
                    && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally)
1802
0
                {
1803
0
                    SdrObjKind eIdent = pTEObj->GetObjIdentifier();
1804
0
                    if (eIdent == SdrObjKind::Text)
1805
0
                    {
1806
0
                        pDelUndo = GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj);
1807
0
                    }
1808
0
                }
1809
0
            }
1810
0
            if (pTxtUndo)
1811
0
            {
1812
0
                if (bUndo)
1813
0
                    AddUndo(std::move(pTxtUndo));
1814
0
                eRet = SdrEndTextEditKind::Changed;
1815
0
            }
1816
0
            if (pDelUndo != nullptr)
1817
0
            {
1818
0
                if (bUndo)
1819
0
                {
1820
0
                    AddUndo(std::move(pDelUndo));
1821
0
                }
1822
0
                eRet = SdrEndTextEditKind::Deleted;
1823
0
                DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr,
1824
0
                           "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
1825
0
                           "ObjList!");
1826
0
                if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr)
1827
0
                {
1828
0
                    pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum());
1829
0
                    CheckMarked(); // remove selection immediately...
1830
0
                }
1831
0
            }
1832
0
            else if (bDelObj)
1833
0
            { // for Writer: the app has to do the deletion itself.
1834
0
                eRet = SdrEndTextEditKind::ShouldBeDeleted;
1835
0
            }
1836
1837
0
            if (bUndo)
1838
0
                EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
1839
1840
            // Switch on any TextAnimation again after TextEdit
1841
0
            if (pTEObj)
1842
0
            {
1843
0
                pTEObj->SetTextAnimationAllowed(true);
1844
0
            }
1845
1846
            // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1847
            // to call AdjustMarkHdl() always.
1848
0
            AdjustMarkHdl();
1849
0
        }
1850
0
        if (pTEWin != nullptr)
1851
0
        {
1852
0
            pTEWin->SetCursor(pTECursorBuffer);
1853
0
        }
1854
        // delete all OutlinerViews
1855
0
        for (size_t i = pTEOutliner->GetViewCount(); i > 0;)
1856
0
        {
1857
0
            i--;
1858
0
            OutlinerView* pOLV = pTEOutliner->GetView(i);
1859
0
            sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10;
1860
0
            VclPtr<vcl::Window> pWin = pOLV->GetWindow();
1861
0
            tools::Rectangle aRect(pOLV->GetOutputArea());
1862
0
            pTEOutliner->RemoveView(i);
1863
0
            if (!mbTextEditDontDelete || i != 0)
1864
0
            {
1865
                // may not own the zeroth one
1866
0
                delete pOLV;
1867
0
            }
1868
0
            aRect.Union(m_aTextEditArea);
1869
0
            aRect.Union(m_aMinTextEditArea);
1870
0
            aRect = pWin->LogicToPixel(aRect);
1871
0
            aRect.AdjustLeft(-nMorePix);
1872
0
            aRect.AdjustTop(-nMorePix);
1873
0
            aRect.AdjustRight(nMorePix);
1874
0
            aRect.AdjustBottom(nMorePix);
1875
0
            aRect = pWin->PixelToLogic(aRect);
1876
0
            InvalidateOneWin(*pWin->GetOutDev(), aRect);
1877
0
            pWin->GetOutDev()->SetFillColor();
1878
0
            pWin->GetOutDev()->SetLineColor(COL_BLACK);
1879
0
        }
1880
        // and now the Outliner itself
1881
0
        if (!mbTextEditDontDelete)
1882
0
            delete pTEOutliner;
1883
0
        else
1884
0
            pTEOutliner->Clear();
1885
0
        maHdlList.SetMoveOutside(false);
1886
0
        if (eRet != SdrEndTextEditKind::Unchanged)
1887
0
        {
1888
0
            GetMarkedObjectListWriteAccess().SetNameDirty();
1889
0
        }
1890
        // coverity[leaked_storage] - if pTEOutliner wasn't deleted it didn't really belong to us
1891
0
    }
1892
1893
0
    if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster())
1894
0
    {
1895
0
        SdrHint aHint(SdrHintKind::EndEdit, *pTEObj);
1896
0
        pTEObj->GetBroadcaster()->Broadcast(aHint);
1897
0
    }
1898
1899
0
    if (pUndoEditUndoManager)
1900
0
    {
1901
0
        if (bNeedToUndoSavedRedoTextEdit)
1902
0
        {
1903
            // undo the text edit action since it was created as part of an EndTextEdit
1904
            // callback from undo itself. This needs to be done after the call to
1905
            // FmFormView::SdrEndTextEdit since it gets created there
1906
0
            pUndoEditUndoManager->Undo();
1907
0
        }
1908
1909
        // trigger the Undo which was not executed, but lead to this
1910
        // end text edit
1911
0
        pUndoEditUndoManager->Undo();
1912
0
    }
1913
1914
0
    return eRet;
1915
0
}
1916
1917
// info about TextEdit. Default is false.
1918
691k
bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.get().is(); }
1919
1920
// info about TextEditPageView. Default is 0L.
1921
0
SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; }
1922
1923
OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const
1924
0
{
1925
0
    if (pWin == nullptr)
1926
0
        return nullptr;
1927
0
    if (mpTextEditOutliner == nullptr)
1928
0
        return nullptr;
1929
0
    OutlinerView* pNewView = nullptr;
1930
0
    size_t nWinCount = mpTextEditOutliner->GetViewCount();
1931
0
    for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++)
1932
0
    {
1933
0
        OutlinerView* pView = mpTextEditOutliner->GetView(i);
1934
0
        if (pView->GetWindow() == pWin)
1935
0
            pNewView = pView;
1936
0
    }
1937
0
    return pNewView;
1938
0
}
1939
1940
void SdrObjEditView::SetTextEditWin(vcl::Window* pWin)
1941
0
{
1942
0
    if (!(mxWeakTextEditObj.get() && pWin != nullptr && pWin != mpTextEditWin))
1943
0
        return;
1944
1945
0
    OutlinerView* pNewView = ImpFindOutlinerView(pWin);
1946
0
    if (pNewView != nullptr && pNewView != mpTextEditOutlinerView)
1947
0
    {
1948
0
        if (mpTextEditOutlinerView != nullptr)
1949
0
        {
1950
0
            mpTextEditOutlinerView->HideCursor();
1951
0
        }
1952
0
        mpTextEditOutlinerView = pNewView;
1953
0
        mpTextEditWin = pWin;
1954
0
        pWin->GrabFocus(); // Make the cursor blink here as well
1955
0
        pNewView->ShowCursor();
1956
0
        ImpMakeTextCursorAreaVisible();
1957
0
    }
1958
0
}
1959
1960
bool SdrObjEditView::IsTextEditHit(const Point& rHit) const
1961
0
{
1962
0
    bool bOk = false;
1963
0
    if (mxWeakTextEditObj.get())
1964
0
    {
1965
0
        tools::Rectangle aEditArea;
1966
0
        if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0))
1967
0
            aEditArea.Union(pOLV->GetOutputArea());
1968
1969
0
        if (aEditArea.Contains(rHit))
1970
0
        { // check if any characters were actually hit
1971
0
            const Point aPnt(rHit - aEditArea.TopLeft());
1972
0
            tools::Long nHitTol = 2000;
1973
0
            if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice())
1974
0
                nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM,
1975
0
                                                     pRef->GetMapMode().GetMapUnit());
1976
1977
0
            bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol));
1978
0
        }
1979
0
    }
1980
0
    return bOk;
1981
0
}
1982
1983
bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const
1984
0
{
1985
0
    bool bOk = false;
1986
0
    if (rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get())
1987
0
    {
1988
0
        OutlinerView* pOLV = mpTextEditOutliner->GetView(0);
1989
0
        if (pOLV)
1990
0
        {
1991
0
            vcl::Window* pWin = pOLV->GetWindow();
1992
0
            if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr)
1993
0
            {
1994
0
                sal_uInt16 nPixSiz = pOLV->GetInvalidateMore();
1995
0
                tools::Rectangle aEditArea(m_aMinTextEditArea);
1996
0
                aEditArea.Union(pOLV->GetOutputArea());
1997
0
                if (!aEditArea.Contains(rHit))
1998
0
                {
1999
0
                    Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
2000
0
                    aEditArea.AdjustLeft(-(aSiz.Width()));
2001
0
                    aEditArea.AdjustTop(-(aSiz.Height()));
2002
0
                    aEditArea.AdjustRight(aSiz.Width());
2003
0
                    aEditArea.AdjustBottom(aSiz.Height());
2004
0
                    bOk = aEditArea.Contains(rHit);
2005
0
                }
2006
0
            }
2007
0
        }
2008
0
    }
2009
0
    return bOk;
2010
0
}
2011
2012
std::unique_ptr<TextChainCursorManager>
2013
SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled)
2014
0
{
2015
0
    *bOutHandled = false;
2016
2017
0
    rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
2018
0
    if (!pTextObj)
2019
0
        return nullptr;
2020
2021
0
    if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain())
2022
0
        return nullptr;
2023
2024
0
    std::unique_ptr<TextChainCursorManager> pCursorManager(
2025
0
        new TextChainCursorManager(this, pTextObj.get()));
2026
0
    if (pCursorManager->HandleKeyEvent(rKEvt))
2027
0
    {
2028
        // Possibly do other stuff here if necessary...
2029
        // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
2030
0
        *bOutHandled = true;
2031
0
    }
2032
2033
0
    return pCursorManager;
2034
0
}
2035
2036
bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
2037
0
{
2038
0
    if (mpTextEditOutlinerView)
2039
0
    {
2040
        /* Start special handling of keys within a chain */
2041
        // We possibly move to another box before any handling
2042
0
        bool bHandled = false;
2043
0
        std::unique_ptr<TextChainCursorManager> xCursorManager(
2044
0
            ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled));
2045
0
        if (bHandled)
2046
0
            return true;
2047
        /* End special handling of keys within a chain */
2048
2049
0
        if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin))
2050
0
        {
2051
0
            if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
2052
0
            {
2053
0
                GetModel().SetChanged();
2054
0
                SetInnerTextAreaForLOKit();
2055
0
            }
2056
2057
            /* Start chaining processing */
2058
0
            ImpChainingEventHdl();
2059
0
            ImpMoveCursorAfterChainingEvent(xCursorManager.get());
2060
            /* End chaining processing */
2061
2062
0
            if (pWin != nullptr && pWin != mpTextEditWin)
2063
0
                SetTextEditWin(pWin);
2064
0
            ImpMakeTextCursorAreaVisible();
2065
0
            return true;
2066
0
        }
2067
0
    }
2068
0
    return SdrGlueEditView::KeyInput(rKEvt, pWin);
2069
0
}
2070
2071
bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
2072
0
{
2073
0
    if (mpTextEditOutlinerView != nullptr)
2074
0
    {
2075
0
        bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
2076
0
        if (!bPostIt)
2077
0
        {
2078
0
            Point aPt(rMEvt.GetPosPixel());
2079
0
            if (pWin != nullptr)
2080
0
                aPt = pWin->PixelToLogic(aPt);
2081
0
            else if (mpTextEditWin != nullptr)
2082
0
                aPt = mpTextEditWin->PixelToLogic(aPt);
2083
0
            bPostIt = IsTextEditHit(aPt);
2084
0
        }
2085
0
        if (bPostIt)
2086
0
        {
2087
0
            Point aPixPos(rMEvt.GetPosPixel());
2088
0
            if (pWin)
2089
0
            {
2090
0
                tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2091
0
                if (aPixPos.X() < aR.Left())
2092
0
                    aPixPos.setX(aR.Left());
2093
0
                if (aPixPos.X() > aR.Right())
2094
0
                    aPixPos.setX(aR.Right());
2095
0
                if (aPixPos.Y() < aR.Top())
2096
0
                    aPixPos.setY(aR.Top());
2097
0
                if (aPixPos.Y() > aR.Bottom())
2098
0
                    aPixPos.setY(aR.Bottom());
2099
0
            }
2100
0
            MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2101
0
                             rMEvt.GetModifier());
2102
0
            if (mpTextEditOutlinerView->MouseButtonDown(aMEvt))
2103
0
            {
2104
0
                if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev()
2105
0
                    && pWin->GetOutDevType() == OUTDEV_WINDOW)
2106
0
                    SetTextEditWin(pWin->GetOwnerWindow());
2107
0
                ImpMakeTextCursorAreaVisible();
2108
0
                return true;
2109
0
            }
2110
0
        }
2111
0
    }
2112
0
    return SdrGlueEditView::MouseButtonDown(rMEvt, pWin);
2113
0
}
2114
2115
bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
2116
0
{
2117
0
    if (mpTextEditOutlinerView != nullptr)
2118
0
    {
2119
0
        bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
2120
0
        if (!bPostIt)
2121
0
        {
2122
0
            Point aPt(rMEvt.GetPosPixel());
2123
0
            if (pWin != nullptr)
2124
0
                aPt = pWin->PixelToLogic(aPt);
2125
0
            else if (mpTextEditWin != nullptr)
2126
0
                aPt = mpTextEditWin->PixelToLogic(aPt);
2127
0
            bPostIt = IsTextEditHit(aPt);
2128
0
        }
2129
0
        if (bPostIt && pWin)
2130
0
        {
2131
0
            Point aPixPos(rMEvt.GetPosPixel());
2132
0
            tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2133
0
            if (aPixPos.X() < aR.Left())
2134
0
                aPixPos.setX(aR.Left());
2135
0
            if (aPixPos.X() > aR.Right())
2136
0
                aPixPos.setX(aR.Right());
2137
0
            if (aPixPos.Y() < aR.Top())
2138
0
                aPixPos.setY(aR.Top());
2139
0
            if (aPixPos.Y() > aR.Bottom())
2140
0
                aPixPos.setY(aR.Bottom());
2141
0
            MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2142
0
                             rMEvt.GetModifier());
2143
0
            if (mpTextEditOutlinerView->MouseButtonUp(aMEvt))
2144
0
            {
2145
0
                ImpMakeTextCursorAreaVisible();
2146
0
                return true;
2147
0
            }
2148
0
        }
2149
0
    }
2150
0
    return SdrGlueEditView::MouseButtonUp(rMEvt, pWin);
2151
0
}
2152
2153
bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
2154
0
{
2155
0
    if (mpTextEditOutlinerView != nullptr)
2156
0
    {
2157
0
        bool bSelMode = mpTextEditOutliner->IsInSelectionMode();
2158
0
        bool bPostIt = bSelMode;
2159
0
        if (!bPostIt)
2160
0
        {
2161
0
            Point aPt(rMEvt.GetPosPixel());
2162
0
            if (pWin)
2163
0
                aPt = pWin->PixelToLogic(aPt);
2164
0
            else if (mpTextEditWin)
2165
0
                aPt = mpTextEditWin->PixelToLogic(aPt);
2166
0
            bPostIt = IsTextEditHit(aPt);
2167
0
        }
2168
0
        if (bPostIt)
2169
0
        {
2170
0
            Point aPixPos(rMEvt.GetPosPixel());
2171
0
            tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea());
2172
0
            if (pWin)
2173
0
                aR = pWin->LogicToPixel(aR);
2174
0
            else if (mpTextEditWin)
2175
0
                aR = mpTextEditWin->LogicToPixel(aR);
2176
0
            if (aPixPos.X() < aR.Left())
2177
0
                aPixPos.setX(aR.Left());
2178
0
            if (aPixPos.X() > aR.Right())
2179
0
                aPixPos.setX(aR.Right());
2180
0
            if (aPixPos.Y() < aR.Top())
2181
0
                aPixPos.setY(aR.Top());
2182
0
            if (aPixPos.Y() > aR.Bottom())
2183
0
                aPixPos.setY(aR.Bottom());
2184
0
            MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2185
0
                             rMEvt.GetModifier());
2186
0
            if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode)
2187
0
            {
2188
0
                ImpMakeTextCursorAreaVisible();
2189
0
                return true;
2190
0
            }
2191
0
        }
2192
0
    }
2193
0
    return SdrGlueEditView::MouseMove(rMEvt, pWin);
2194
0
}
2195
2196
bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
2197
0
{
2198
    // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
2199
0
    if (mpTextEditOutlinerView != nullptr)
2200
0
    {
2201
0
        if (rCEvt.GetCommand() == CommandEventId::StartDrag)
2202
0
        {
2203
0
            bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent();
2204
0
            if (!bPostIt && rCEvt.IsMouseEvent())
2205
0
            {
2206
0
                Point aPt(rCEvt.GetMousePosPixel());
2207
0
                if (pWin != nullptr)
2208
0
                    aPt = pWin->PixelToLogic(aPt);
2209
0
                else if (mpTextEditWin != nullptr)
2210
0
                    aPt = mpTextEditWin->PixelToLogic(aPt);
2211
0
                bPostIt = IsTextEditHit(aPt);
2212
0
            }
2213
0
            if (bPostIt)
2214
0
            {
2215
0
                Point aPixPos(rCEvt.GetMousePosPixel());
2216
0
                if (rCEvt.IsMouseEvent() && pWin)
2217
0
                {
2218
0
                    tools::Rectangle aR(
2219
0
                        pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2220
0
                    if (aPixPos.X() < aR.Left())
2221
0
                        aPixPos.setX(aR.Left());
2222
0
                    if (aPixPos.X() > aR.Right())
2223
0
                        aPixPos.setX(aR.Right());
2224
0
                    if (aPixPos.Y() < aR.Top())
2225
0
                        aPixPos.setY(aR.Top());
2226
0
                    if (aPixPos.Y() > aR.Bottom())
2227
0
                        aPixPos.setY(aR.Bottom());
2228
0
                }
2229
0
                CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent());
2230
                // Command is void at the OutlinerView, sadly
2231
0
                mpTextEditOutlinerView->Command(aCEvt);
2232
0
                if (pWin != nullptr && pWin != mpTextEditWin)
2233
0
                    SetTextEditWin(pWin);
2234
0
                ImpMakeTextCursorAreaVisible();
2235
0
                return true;
2236
0
            }
2237
0
        }
2238
0
        else
2239
0
        {
2240
0
            mpTextEditOutlinerView->Command(rCEvt);
2241
0
            if (comphelper::LibreOfficeKit::isActive())
2242
0
            {
2243
                // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
2244
                // isn't called
2245
0
                if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
2246
0
                {
2247
0
                    GetModel().SetChanged();
2248
0
                    SetInnerTextAreaForLOKit();
2249
0
                }
2250
0
            }
2251
0
            return true;
2252
0
        }
2253
0
    }
2254
0
    return SdrGlueEditView::Command(rCEvt, pWin);
2255
0
}
2256
2257
bool SdrObjEditView::ImpIsTextEditAllSelected() const
2258
0
{
2259
0
    bool bRet = false;
2260
0
    if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr)
2261
0
    {
2262
0
        if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get()))
2263
0
        {
2264
0
            const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount();
2265
0
            sal_Int32 nLastPara = nParaCnt > 1 ? nParaCnt - 1 : 0;
2266
2267
0
            ESelection aESel(mpTextEditOutlinerView->GetSelection());
2268
0
            if (aESel.start.nPara == 0 && aESel.start.nIndex == 0
2269
0
                && aESel.end.nPara == (nParaCnt - 1))
2270
0
            {
2271
0
                if (mpTextEditOutliner->GetText(nLastPara).getLength() == aESel.end.nIndex)
2272
0
                    bRet = true;
2273
0
            }
2274
            // in case the selection was done backwards
2275
0
            if (!bRet && aESel.end.nPara == 0 && aESel.end.nIndex == 0
2276
0
                && aESel.start.nPara == (nParaCnt - 1))
2277
0
            {
2278
0
                if (mpTextEditOutliner->GetText(nLastPara).getLength() == aESel.start.nIndex)
2279
0
                    bRet = true;
2280
0
            }
2281
0
        }
2282
0
        else
2283
0
        {
2284
0
            bRet = true;
2285
0
        }
2286
0
    }
2287
0
    return bRet;
2288
0
}
2289
2290
void SdrObjEditView::ImpMakeTextCursorAreaVisible()
2291
0
{
2292
0
    if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr)
2293
0
    {
2294
0
        vcl::Cursor* pCsr = mpTextEditWin->GetCursor();
2295
0
        if (pCsr != nullptr)
2296
0
        {
2297
0
            Size aSiz(pCsr->GetSize());
2298
0
            if (!aSiz.IsEmpty())
2299
0
            {
2300
0
                MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin);
2301
0
            }
2302
0
        }
2303
0
    }
2304
0
}
2305
2306
SvtScriptType SdrObjEditView::GetScriptType() const
2307
0
{
2308
0
    SvtScriptType nScriptType = SvtScriptType::NONE;
2309
2310
0
    if (IsTextEdit())
2311
0
    {
2312
0
        auto pText = mxWeakTextEditObj.get();
2313
0
        if (pText->GetOutlinerParaObject())
2314
0
            nScriptType = pText->GetOutlinerParaObject()->GetTextObject().GetScriptType();
2315
2316
0
        if (mpTextEditOutlinerView)
2317
0
            nScriptType = mpTextEditOutlinerView->GetSelectedScriptType();
2318
0
    }
2319
0
    else
2320
0
    {
2321
0
        const SdrMarkList& rMarkList = GetMarkedObjectList();
2322
0
        const size_t nMarkCount(rMarkList.GetMarkCount());
2323
2324
0
        for (size_t i = 0; i < nMarkCount; ++i)
2325
0
        {
2326
0
            OutlinerParaObject* pParaObj
2327
0
                = rMarkList.GetMark(i)->GetMarkedSdrObj()->GetOutlinerParaObject();
2328
2329
0
            if (pParaObj)
2330
0
            {
2331
0
                nScriptType |= pParaObj->GetTextObject().GetScriptType();
2332
0
            }
2333
0
        }
2334
0
    }
2335
2336
0
    if (nScriptType == SvtScriptType::NONE)
2337
0
        nScriptType = SvtScriptType::LATIN;
2338
2339
0
    return nScriptType;
2340
0
}
2341
2342
void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2343
0
{
2344
0
    if (mxSelectionController.is())
2345
0
        if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr))
2346
0
            return;
2347
2348
0
    if (IsTextEdit())
2349
0
    {
2350
0
        DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2351
0
                   "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2352
0
        DBG_ASSERT(mpTextEditOutliner != nullptr,
2353
0
                   "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2354
2355
0
        auto pText = mxWeakTextEditObj.get();
2356
        // take care of bOnlyHardAttr(!)
2357
0
        if (!bOnlyHardAttr && pText->GetStyleSheet())
2358
0
            rTargetSet.Put(pText->GetStyleSheet()->GetItemSet());
2359
2360
        // add object attributes
2361
0
        rTargetSet.Put(pText->GetMergedItemSet());
2362
2363
0
        if (mpTextEditOutlinerView)
2364
0
        {
2365
            // FALSE= regard InvalidItems as "holes," not as Default
2366
0
            rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false);
2367
0
        }
2368
2369
0
        const SdrMarkList& rMarkList = GetMarkedObjectList();
2370
0
        if (rMarkList.GetMarkCount() == 1 && rMarkList.GetMark(0)->GetMarkedSdrObj() == pText.get())
2371
0
        {
2372
0
            MergeNotPersistAttrFromMarked(rTargetSet);
2373
0
        }
2374
0
    }
2375
0
    else
2376
0
    {
2377
0
        SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr);
2378
0
    }
2379
0
}
2380
2381
bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2382
0
{
2383
0
    bool bRet = false;
2384
0
    rtl::Reference<SdrTextObj> pTextEditObj = mxWeakTextEditObj.get();
2385
0
    bool bTextEdit = mpTextEditOutlinerView != nullptr && pTextEditObj != nullptr;
2386
0
    bool bAllTextSelected = ImpIsTextEditAllSelected();
2387
0
    const SfxItemSet* pSet = &rSet;
2388
2389
0
    if (!bTextEdit)
2390
0
    {
2391
        // no TextEdit active -> all Items to drawing object
2392
0
        if (mxSelectionController.is())
2393
0
            bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2394
2395
0
        if (!bRet)
2396
0
        {
2397
0
            SdrGlueEditView::SetAttributes(*pSet, bReplaceAll);
2398
0
            bRet = true;
2399
0
        }
2400
0
    }
2401
0
    else
2402
0
    {
2403
#ifdef DBG_UTIL
2404
        {
2405
            bool bHasEEFeatureItems = false;
2406
            for (SfxItemIter aIter(rSet); !bHasEEFeatureItems && !aIter.IsAtEnd(); aIter.Next())
2407
            {
2408
                const SfxPoolItem* pItem = aIter.GetCurItem();
2409
                if (!IsInvalidItem(pItem))
2410
                {
2411
                    sal_uInt16 nW = pItem->Which();
2412
                    if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END)
2413
                        bHasEEFeatureItems = true;
2414
                }
2415
            }
2416
2417
            if (bHasEEFeatureItems)
2418
            {
2419
                std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
2420
                    nullptr, VclMessageType::Info, VclButtonsType::Ok,
2421
                    u"SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
2422
                    "at the SdrView does not make sense! It only leads to "
2423
                    "overhead and unreadable documents."_ustr));
2424
                xInfoBox->run();
2425
            }
2426
        }
2427
#endif
2428
2429
0
        bool bOnlyEEItems;
2430
0
        bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems);
2431
        // everything selected? -> attributes to the border, too
2432
        // if no EEItems, attributes to the border only
2433
0
        if (bAllTextSelected || bNoEEItems)
2434
0
        {
2435
0
            if (mxSelectionController.is())
2436
0
                bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2437
2438
0
            if (!bRet)
2439
0
            {
2440
0
                const bool bUndo = IsUndoEnabled();
2441
2442
0
                if (bUndo)
2443
0
                {
2444
0
                    BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2445
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2446
2447
                    // If this is a text object also rescue the OutlinerParaObject since
2448
                    // applying attributes to the object may change text layout when
2449
                    // multiple portions exist with multiple formats. If an OutlinerParaObject
2450
                    // really exists and needs to be rescued is evaluated in the undo
2451
                    // implementation itself.
2452
0
                    bool bRescueText(pTextEditObj);
2453
2454
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(
2455
0
                        *pTextEditObj, false, !bNoEEItems || bRescueText));
2456
0
                    EndUndo();
2457
0
                }
2458
2459
0
                pTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll);
2460
2461
0
                FlushComeBackTimer(); // to set ModeHasChanged immediately
2462
0
            }
2463
0
        }
2464
0
        else if (!bOnlyEEItems)
2465
0
        {
2466
            // Otherwise split Set, if necessary.
2467
            // Now we build an ItemSet aSet that doesn't contain EE_Items from
2468
            // *pSet (otherwise it would be a copy).
2469
0
            WhichRangesContainer pNewWhichTable
2470
0
                = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END);
2471
0
            SfxItemSet aSet(GetModel().GetItemPool(), std::move(pNewWhichTable));
2472
0
            for (SfxItemIter aIter(aSet); !aIter.IsAtEnd(); aIter.Next())
2473
0
            {
2474
0
                const SfxPoolItem* pItem = aIter.GetCurItem();
2475
0
                if (IsDisabledItem(pItem))
2476
0
                    continue;
2477
0
                aSet.Put(*pItem);
2478
0
            }
2479
2480
0
            if (mxSelectionController.is())
2481
0
                bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll);
2482
2483
0
            if (!bRet)
2484
0
            {
2485
0
                if (IsUndoEnabled())
2486
0
                {
2487
0
                    BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2488
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2489
0
                    AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj));
2490
0
                    EndUndo();
2491
0
                }
2492
2493
0
                pTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll);
2494
2495
0
                const SdrMarkList& rMarkList = GetMarkedObjectList();
2496
0
                if (rMarkList.GetMarkCount() == 1
2497
0
                    && rMarkList.GetMark(0)->GetMarkedSdrObj() == pTextEditObj.get())
2498
0
                {
2499
0
                    SetNotPersistAttrToMarked(aSet);
2500
0
                }
2501
0
            }
2502
0
            FlushComeBackTimer();
2503
0
        }
2504
0
        if (!bNoEEItems)
2505
0
        {
2506
            // and now the attributes to the EditEngine
2507
0
            if (bReplaceAll)
2508
0
            {
2509
0
                mpTextEditOutlinerView->RemoveAttribs(true);
2510
0
            }
2511
0
            mpTextEditOutlinerView->SetAttribs(rSet);
2512
2513
0
            const Outliner& rTEOutliner = mpTextEditOutlinerView->GetOutliner();
2514
0
            if (rTEOutliner.IsModified())
2515
0
            {
2516
0
                GetModel().SetChanged();
2517
0
                SetInnerTextAreaForLOKit();
2518
0
            }
2519
2520
0
            ImpMakeTextCursorAreaVisible();
2521
0
        }
2522
0
        bRet = true;
2523
0
    }
2524
0
    return bRet;
2525
0
}
2526
2527
SfxStyleSheet* SdrObjEditView::GetStyleSheet() const
2528
0
{
2529
0
    SfxStyleSheet* pSheet = nullptr;
2530
2531
0
    if (mxSelectionController.is())
2532
0
    {
2533
0
        if (mxSelectionController->GetStyleSheet(pSheet))
2534
0
            return pSheet;
2535
0
    }
2536
2537
0
    if (mpTextEditOutlinerView)
2538
0
    {
2539
0
        pSheet = mpTextEditOutlinerView->GetStyleSheet();
2540
0
    }
2541
0
    else
2542
0
    {
2543
0
        pSheet = SdrGlueEditView::GetStyleSheet();
2544
0
    }
2545
0
    return pSheet;
2546
0
}
2547
2548
void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
2549
0
{
2550
0
    if (mxSelectionController.is())
2551
0
    {
2552
0
        if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr))
2553
0
            return;
2554
0
    }
2555
2556
    // if we are currently in edit mode we must also set the stylesheet
2557
    // on all paragraphs in the Outliner for the edit view
2558
0
    if (nullptr != mpTextEditOutlinerView)
2559
0
    {
2560
0
        Outliner& rOutliner = mpTextEditOutlinerView->GetOutliner();
2561
2562
0
        const sal_Int32 nParaCount = rOutliner.GetParagraphCount();
2563
0
        for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
2564
0
            rOutliner.SetStyleSheet(nPara, pStyleSheet);
2565
0
    }
2566
2567
0
    SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr);
2568
0
}
2569
2570
void SdrObjEditView::AddDeviceToPaintView(OutputDevice& rNewDev)
2571
0
{
2572
0
    SdrGlueEditView::AddDeviceToPaintView(rNewDev);
2573
2574
0
    if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2575
0
        && rNewDev.GetOutDevType() == OUTDEV_WINDOW)
2576
0
    {
2577
0
        OutlinerView* pOutlView = ImpMakeOutlinerView(rNewDev.GetOwnerWindow(), nullptr);
2578
0
        mpTextEditOutliner->InsertView(pOutlView);
2579
0
    }
2580
0
}
2581
2582
void SdrObjEditView::DeleteDeviceFromPaintView(OutputDevice& rOldDev)
2583
0
{
2584
0
    SdrGlueEditView::DeleteDeviceFromPaintView(rOldDev);
2585
2586
0
    if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2587
0
        && rOldDev.GetOutDevType() == OUTDEV_WINDOW)
2588
0
    {
2589
0
        for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;)
2590
0
        {
2591
0
            i--;
2592
0
            OutlinerView* pOLV = mpTextEditOutliner->GetView(i);
2593
0
            if (pOLV && pOLV->GetWindow() == rOldDev.GetOwnerWindow())
2594
0
            {
2595
0
                mpTextEditOutliner->RemoveView(i);
2596
0
            }
2597
0
        }
2598
0
    }
2599
2600
0
    lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), &rOldDev);
2601
0
}
2602
2603
bool SdrObjEditView::IsTextEditInSelectionMode() const
2604
0
{
2605
0
    return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode();
2606
0
}
2607
2608
// MacroMode
2609
2610
void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV,
2611
                                 vcl::Window* pWin)
2612
0
{
2613
0
    BrkMacroObj();
2614
0
    if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro())
2615
0
    {
2616
0
        nTol = ImpGetHitTolLogic(nTol, nullptr);
2617
0
        m_pMacroObj = pObj;
2618
0
        m_pMacroPV = pPV;
2619
0
        m_pMacroWin = pWin;
2620
0
        mbMacroDown = false;
2621
0
        m_nMacroTol = sal_uInt16(nTol);
2622
0
        m_aMacroDownPos = rPnt;
2623
0
        MovMacroObj(rPnt);
2624
0
    }
2625
0
}
2626
2627
void SdrObjEditView::ImpMacroUp(const Point& rUpPos)
2628
0
{
2629
0
    if (m_pMacroObj != nullptr && mbMacroDown)
2630
0
    {
2631
0
        SdrObjMacroHitRec aHitRec;
2632
0
        aHitRec.aPos = rUpPos;
2633
0
        aHitRec.nTol = m_nMacroTol;
2634
0
        aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2635
0
        aHitRec.pPageView = m_pMacroPV;
2636
0
        m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2637
0
        mbMacroDown = false;
2638
0
    }
2639
0
}
2640
2641
void SdrObjEditView::ImpMacroDown(const Point& rDownPos)
2642
0
{
2643
0
    if (m_pMacroObj != nullptr && !mbMacroDown)
2644
0
    {
2645
0
        SdrObjMacroHitRec aHitRec;
2646
0
        aHitRec.aPos = rDownPos;
2647
0
        aHitRec.nTol = m_nMacroTol;
2648
0
        aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2649
0
        aHitRec.pPageView = m_pMacroPV;
2650
0
        m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2651
0
        mbMacroDown = true;
2652
0
    }
2653
0
}
2654
2655
void SdrObjEditView::MovMacroObj(const Point& rPnt)
2656
0
{
2657
0
    if (m_pMacroObj == nullptr)
2658
0
        return;
2659
2660
0
    SdrObjMacroHitRec aHitRec;
2661
0
    aHitRec.aPos = rPnt;
2662
0
    aHitRec.nTol = m_nMacroTol;
2663
0
    aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2664
0
    aHitRec.pPageView = m_pMacroPV;
2665
0
    bool bDown = m_pMacroObj->IsMacroHit(aHitRec);
2666
0
    if (bDown)
2667
0
        ImpMacroDown(rPnt);
2668
0
    else
2669
0
        ImpMacroUp(rPnt);
2670
0
}
2671
2672
void SdrObjEditView::BrkMacroObj()
2673
92.8k
{
2674
92.8k
    if (m_pMacroObj != nullptr)
2675
0
    {
2676
0
        ImpMacroUp(m_aMacroDownPos);
2677
0
        m_pMacroObj = nullptr;
2678
0
        m_pMacroPV = nullptr;
2679
0
        m_pMacroWin = nullptr;
2680
0
    }
2681
92.8k
}
2682
2683
bool SdrObjEditView::EndMacroObj()
2684
0
{
2685
0
    if (m_pMacroObj != nullptr && mbMacroDown)
2686
0
    {
2687
0
        ImpMacroUp(m_aMacroDownPos);
2688
0
        SdrObjMacroHitRec aHitRec;
2689
0
        aHitRec.aPos = m_aMacroDownPos;
2690
0
        aHitRec.nTol = m_nMacroTol;
2691
0
        aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2692
0
        aHitRec.pPageView = m_pMacroPV;
2693
0
        bool bRet = m_pMacroObj->DoMacro(aHitRec);
2694
0
        m_pMacroObj = nullptr;
2695
0
        m_pMacroPV = nullptr;
2696
0
        m_pMacroWin = nullptr;
2697
0
        return bRet;
2698
0
    }
2699
0
    else
2700
0
    {
2701
0
        BrkMacroObj();
2702
0
        return false;
2703
0
    }
2704
0
}
2705
2706
/** fills the given any with a XTextCursor for the current text selection.
2707
    Leaves the any untouched if there currently is no text selected */
2708
void SdrObjEditView::getTextSelection(css::uno::Any& rSelection)
2709
0
{
2710
0
    if (!IsTextEdit())
2711
0
        return;
2712
2713
0
    OutlinerView* pOutlinerView = GetTextEditOutlinerView();
2714
0
    if (!(pOutlinerView && pOutlinerView->HasSelection()))
2715
0
        return;
2716
2717
0
    SdrObject* pObj = GetTextEditObject();
2718
2719
0
    if (!pObj)
2720
0
        return;
2721
2722
0
    css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY);
2723
0
    if (xText.is())
2724
0
    {
2725
0
        SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText);
2726
0
        if (pRange)
2727
0
        {
2728
0
            rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection());
2729
0
        }
2730
0
    }
2731
0
}
2732
2733
/* check if we have a single selection and that single object likes
2734
    to handle the mouse and keyboard events itself
2735
2736
    TODO: the selection controller should be queried from the
2737
    object specific view contact. Currently this method only
2738
    works for tables.
2739
*/
2740
void SdrObjEditView::MarkListHasChanged()
2741
100k
{
2742
100k
    SdrGlueEditView::MarkListHasChanged();
2743
2744
100k
    if (mxSelectionController.is())
2745
0
    {
2746
0
        mxLastSelectionController = mxSelectionController;
2747
0
        mxSelectionController->onSelectionHasChanged();
2748
0
    }
2749
2750
100k
    mxSelectionController.clear();
2751
2752
100k
    const SdrMarkList& rMarkList = GetMarkedObjectList();
2753
100k
    if (rMarkList.GetMarkCount() != 1)
2754
100k
        return;
2755
2756
0
    const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
2757
0
    SdrView* pView(dynamic_cast<SdrView*>(this));
2758
2759
    // check for table
2760
0
    if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
2761
0
        && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2762
0
    {
2763
0
        mxSelectionController = sdr::table::CreateTableController(
2764
0
            *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController);
2765
2766
0
        if (mxSelectionController.is())
2767
0
        {
2768
0
            mxLastSelectionController.clear();
2769
0
            mxSelectionController->onSelectionHasChanged();
2770
0
        }
2771
0
    }
2772
0
}
2773
2774
IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2775
0
{
2776
0
    OnEndPasteOrDrop(pInfo);
2777
0
}
2778
2779
IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2780
0
{
2781
0
    OnBeginPasteOrDrop(pInfo);
2782
0
}
2783
2784
void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*)
2785
0
{
2786
    // applications can derive from these virtual methods to do something before a drop or paste operation
2787
0
}
2788
2789
void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*)
2790
0
{
2791
    // applications can derive from these virtual methods to do something before a drop or paste operation
2792
0
}
2793
2794
sal_uInt16 SdrObjEditView::GetSelectionLevel() const
2795
0
{
2796
0
    if (!IsTextEdit())
2797
0
        return 0xFFFF;
2798
0
    DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2799
0
               "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2800
0
    DBG_ASSERT(mpTextEditOutliner != nullptr,
2801
0
               "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2802
0
    if (!mpTextEditOutlinerView)
2803
0
        return 0xFFFF;
2804
    //start and end position
2805
0
    ESelection aSelect = mpTextEditOutlinerView->GetSelection();
2806
0
    sal_Int32 nStartPara = ::std::min(aSelect.start.nPara, aSelect.end.nPara);
2807
0
    sal_Int32 nEndPara = ::std::max(aSelect.start.nPara, aSelect.end.nPara);
2808
    //get level from each paragraph
2809
0
    sal_uInt16 nLevel = 0;
2810
0
    for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++)
2811
0
    {
2812
0
        sal_Int16 nDepth = mpTextEditOutliner->GetDepth(nPara);
2813
0
        assert(nDepth <= 15);
2814
0
        if (nDepth >= 0)
2815
0
        {
2816
0
            sal_uInt16 nParaDepth = 1 << static_cast<sal_uInt16>(nDepth);
2817
0
            if (!(nLevel & nParaDepth))
2818
0
                nLevel += nParaDepth;
2819
0
        }
2820
0
    }
2821
    //reduce one level for Outliner Object
2822
    //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
2823
    //  nLevel = nLevel >> 1;
2824
    //no bullet paragraph selected
2825
0
    if (nLevel == 0)
2826
0
        nLevel = 0xFFFF;
2827
0
    return nLevel;
2828
0
}
2829
2830
bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor,
2831
                                              SdrObjKind nObjectIdentifier)
2832
0
{
2833
0
    if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d)
2834
0
        return false;
2835
0
    switch (nObjectIdentifier)
2836
0
    {
2837
0
        case SdrObjKind::NONE:
2838
0
        case SdrObjKind::Group:
2839
0
            return false;
2840
0
        case SdrObjKind::Line:
2841
0
        case SdrObjKind::Rectangle:
2842
0
        case SdrObjKind::CircleOrEllipse:
2843
0
        case SdrObjKind::CircleSection:
2844
0
        case SdrObjKind::CircleArc:
2845
0
        case SdrObjKind::CircleCut:
2846
0
        case SdrObjKind::Polygon:
2847
0
        case SdrObjKind::PolyLine:
2848
0
        case SdrObjKind::PathLine:
2849
0
        case SdrObjKind::PathFill:
2850
0
        case SdrObjKind::FreehandLine:
2851
0
        case SdrObjKind::FreehandFill:
2852
0
        case SdrObjKind::Text:
2853
0
        case SdrObjKind::TitleText:
2854
0
        case SdrObjKind::OutlineText:
2855
0
        case SdrObjKind::Graphic:
2856
0
        case SdrObjKind::OLE2:
2857
0
        case SdrObjKind::Table:
2858
0
            return true;
2859
0
        case SdrObjKind::Caption:
2860
0
            return false;
2861
0
        case SdrObjKind::Edge:
2862
0
        case SdrObjKind::PathPoly:
2863
0
        case SdrObjKind::PathPolyLine:
2864
0
            return true;
2865
0
        case SdrObjKind::Page:
2866
0
        case SdrObjKind::Measure:
2867
0
        case SdrObjKind::OLEPluginFrame:
2868
0
        case SdrObjKind::UNO:
2869
0
            return false;
2870
0
        case SdrObjKind::CustomShape:
2871
0
            return true;
2872
0
        default:
2873
0
            return false;
2874
0
    }
2875
0
}
2876
2877
static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly, bool withParagraphAttr = true)
2878
0
{
2879
0
    static const WhichRangesContainer gFull(
2880
0
        svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTR_FILL_LAST,
2881
0
                   SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST,
2882
0
                   SDRATTR_MISC_LAST, // table cell formats
2883
0
                   SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
2884
0
                   SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST,
2885
0
                   SDRATTR_SOFTEDGE_LAST, SDRATTR_GLOW_TEXT_FIRST, SDRATTR_GLOW_TEXT_LAST,
2886
0
                   SDRATTR_REFLECTION_FIRST, SDRATTR_REFLECTION_LAST, EE_PARA_START, EE_PARA_END,
2887
0
                   EE_CHAR_START, EE_CHAR_END>);
2888
2889
0
    static const WhichRangesContainer gTextOnly(
2890
0
        svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_CHAR_START, EE_CHAR_END>);
2891
2892
0
    static const WhichRangesContainer gParaTextOnly(
2893
0
        svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START,
2894
0
                   EE_CHAR_END>);
2895
2896
0
    return bTextOnly ? withParagraphAttr ? gParaTextOnly : gTextOnly : gFull;
2897
0
}
2898
2899
sal_Int32 SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet)
2900
0
{
2901
0
    sal_Int32 nDepth = -2;
2902
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
2903
0
    if (rMarkList.GetMarkCount() <= 0)
2904
0
        return nDepth;
2905
2906
0
    OutlinerView* pOLV = GetTextEditOutlinerView();
2907
0
    bool isParaSelection
2908
0
        = pOLV ? !pOLV->GetEditView().HasSelection() || pOLV->GetEditView().IsSelectionFullPara()
2909
0
               : false;
2910
0
    rFormatSet = std::make_shared<SfxItemSet>(GetModel().GetItemPool(),
2911
0
                                              GetFormatRangeImpl(pOLV != nullptr, isParaSelection));
2912
0
    if (pOLV)
2913
0
    {
2914
0
        rFormatSet->Put(pOLV->GetAttribs());
2915
0
        if (isParaSelection)
2916
0
            nDepth = pOLV->GetDepth();
2917
0
    }
2918
0
    else
2919
0
    {
2920
0
        const bool bOnlyHardAttr = false;
2921
0
        rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr));
2922
0
    }
2923
2924
    // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
2925
0
    const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2926
0
    if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
2927
0
        && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2928
0
    {
2929
0
        auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj);
2930
0
        if (mxSelectionController.is() && pTable->getActiveCell().is())
2931
0
        {
2932
0
            mxSelectionController->GetAttributes(*rFormatSet, false);
2933
0
        }
2934
0
    }
2935
0
    return nDepth;
2936
0
}
2937
2938
static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool,
2939
                                 const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet,
2940
                                 bool bNoCharacterFormats, bool bNoParagraphFormats)
2941
0
{
2942
0
    SfxItemSet aPaintSet(rPool, pRanges);
2943
2944
0
    for (const auto& pRange : pRanges)
2945
0
    {
2946
0
        sal_uInt16 nWhich = pRange.first;
2947
0
        const sal_uInt16 nLastWhich = pRange.second;
2948
2949
0
        if (bNoCharacterFormats && (nWhich == EE_CHAR_START))
2950
0
            continue;
2951
2952
0
        if (bNoParagraphFormats && (nWhich == EE_PARA_START))
2953
0
            continue;
2954
2955
0
        for (; nWhich <= nLastWhich; nWhich++)
2956
0
        {
2957
0
            const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich);
2958
0
            const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich);
2959
2960
0
            if ((pSourceItem && !pTargetItem)
2961
0
                || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem))
2962
0
            {
2963
0
                aPaintSet.Put(*pSourceItem);
2964
0
            }
2965
0
        }
2966
0
    }
2967
0
    return aPaintSet;
2968
0
}
2969
2970
void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj,
2971
                                                 SdrText* pText, sal_Int16 nDepth,
2972
                                                 bool bNoCharacterFormats, bool bNoParagraphFormats)
2973
0
{
2974
0
    OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
2975
0
    if (!pParaObj)
2976
0
        return;
2977
2978
0
    SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner();
2979
0
    rOutliner.SetText(*pParaObj);
2980
2981
0
    sal_Int32 nParaCount(rOutliner.GetParagraphCount());
2982
2983
0
    if (!nParaCount)
2984
0
        return;
2985
2986
0
    for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
2987
0
    {
2988
0
        if (!bNoCharacterFormats)
2989
0
            rOutliner.RemoveCharAttribs(nPara);
2990
2991
0
        SfxItemSet aSet(rOutliner.GetParaAttribs(nPara));
2992
0
        aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet,
2993
0
                                bNoCharacterFormats, bNoParagraphFormats));
2994
0
        rOutliner.SetParaAttribs(nPara, aSet);
2995
0
        if (nDepth > -2)
2996
0
            rOutliner.SetDepth(nPara, nDepth);
2997
0
    }
2998
2999
0
    std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
3000
0
    rOutliner.Clear();
3001
3002
0
    rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
3003
0
}
3004
3005
void SdrObjEditView::DisposeUndoManager()
3006
6.21k
{
3007
6.21k
    if (mpTextEditOutliner)
3008
0
    {
3009
0
        if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager))
3010
0
        {
3011
            // Non-owning pointer, clear it.
3012
0
            mpTextEditOutliner->SetUndoManager(nullptr);
3013
0
        }
3014
0
    }
3015
3016
6.21k
    mpOldTextEditUndoManager = nullptr;
3017
6.21k
}
3018
3019
void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, sal_Int16 nDepth,
3020
                                           bool bNoCharacterFormats, bool bNoParagraphFormats)
3021
0
{
3022
0
    if (mxSelectionController.is()
3023
0
        && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, nDepth, bNoCharacterFormats,
3024
0
                                                        bNoParagraphFormats))
3025
0
    {
3026
0
        return;
3027
0
    }
3028
3029
0
    OutlinerView* pOLV = GetTextEditOutlinerView();
3030
0
    const SdrMarkList& rMarkList = GetMarkedObjectList();
3031
0
    if (!pOLV)
3032
0
    {
3033
0
        SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
3034
0
        const SfxItemSet& rShapeSet = pObj->GetMergedItemSet();
3035
3036
        // if not in text edit mode (aka the user selected text or clicked on a word)
3037
        // apply formatting attributes to selected shape
3038
        // All formatting items (see ranges above) that are unequal in selected shape and
3039
        // the format paintbrush are hard set on the selected shape.
3040
3041
0
        const WhichRangesContainer& pRanges = rFormatSet.GetRanges();
3042
0
        bool bTextOnly = true;
3043
3044
0
        for (const auto& pRange : pRanges)
3045
0
        {
3046
0
            if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START))
3047
0
            {
3048
0
                bTextOnly = false;
3049
0
                break;
3050
0
            }
3051
0
        }
3052
3053
0
        if (!bTextOnly)
3054
0
        {
3055
0
            SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(),
3056
0
                                                rFormatSet, rShapeSet, bNoCharacterFormats,
3057
0
                                                bNoParagraphFormats));
3058
0
            SetAttrToMarked(aPaintSet, false /*bReplaceAll*/);
3059
0
        }
3060
3061
        // now apply character and paragraph formatting to text, if the shape has any
3062
0
        SdrTextObj* pTextObj = DynCastSdrTextObj(pObj);
3063
0
        if (pTextObj)
3064
0
        {
3065
0
            sal_Int32 nText = pTextObj->getTextCount();
3066
3067
0
            while (--nText >= 0)
3068
0
            {
3069
0
                SdrText* pText = pTextObj->getText(nText);
3070
0
                ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, nDepth,
3071
0
                                            bNoCharacterFormats, bNoParagraphFormats);
3072
0
            }
3073
0
        }
3074
0
    }
3075
0
    else
3076
0
    {
3077
0
        ::Outliner& rOutliner = pOLV->GetOutliner();
3078
0
        const EditEngine& rEditEngine = rOutliner.GetEditEngine();
3079
3080
0
        ESelection aSel(pOLV->GetSelection());
3081
0
        bool fullParaSelection
3082
0
            = aSel.end.nPara != aSel.start.nPara || pOLV->GetEditView().IsSelectionFullPara();
3083
0
        if (!aSel.HasRange())
3084
0
            pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD));
3085
0
        const bool bRemoveParaAttribs = !bNoParagraphFormats && !fullParaSelection;
3086
0
        pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs);
3087
0
        SfxItemSet aSet(pOLV->GetAttribs());
3088
0
        SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet,
3089
0
                                            aSet, bNoCharacterFormats, bNoParagraphFormats));
3090
0
        pOLV->SetAttribs(aPaintSet);
3091
0
        if (!bNoParagraphFormats && nDepth > -2)
3092
0
        {
3093
0
            for (sal_Int32 nPara = aSel.start.nPara; nPara <= aSel.end.nPara; ++nPara)
3094
0
                pOLV->SetDepth(nPara, nDepth);
3095
0
        }
3096
0
    }
3097
3098
    // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
3099
0
    SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
3100
0
    if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
3101
0
        && (pObj->GetObjIdentifier() == SdrObjKind::Table))
3102
0
    {
3103
0
        auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj);
3104
0
        if (pTable->getActiveCell().is() && mxSelectionController.is())
3105
0
        {
3106
0
            mxSelectionController->SetAttributes(rFormatSet, false);
3107
0
        }
3108
0
    }
3109
0
}
3110
3111
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */