Coverage Report

Created: 2026-09-28 10:59

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