Coverage Report

Created: 2025-11-16 09:57

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