tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / svx / source / svdraw / svdedxv.cxx
blob374c38baf00e4a3de8179cac5c60b5e9e60751e9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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/weld.hxx>
46 #include <vcl/window.hxx>
47 #include <comphelper/lok.hxx>
48 #include <basegfx/polygon/b2dpolygontools.hxx>
49 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
50 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
51 #include <drawinglayer/processor2d/processor2dtools.hxx>
52 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
53 #include <editeng/outliner.hxx>
54 #include <sal/log.hxx>
55 #include <sdr/overlay/overlaytools.hxx>
56 #include <sfx2/viewsh.hxx>
57 #include <svx/dialmgr.hxx>
58 #include <svx/sdr/overlay/overlaymanager.hxx>
59 #include <svx/sdr/overlay/overlayselection.hxx>
60 #include <svx/sdr/table/tablecontroller.hxx>
61 #include <svx/sdrpagewindow.hxx>
62 #include <svx/sdrpaintwindow.hxx>
63 #include <svx/sdrundomanager.hxx>
64 #include <svx/strings.hrc>
65 #include <svx/svdviter.hxx>
66 #include <svtools/optionsdrawinglayer.hxx>
67 #include <textchain.hxx>
68 #include <textchaincursor.hxx>
69 #include <tools/debug.hxx>
70 #include <vcl/svapp.hxx>
71 #include <svx/sdr/contact/viewcontact.hxx>
73 #include <memory>
75 SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
76 : SdrGlueEditView(rSdrModel, pOut)
77 , maTEOverlayGroup()
78 , maTextEditUpdateTimer("TextEditUpdateTimer")
79 , mxWeakTextEditObj()
80 , mpTextEditPV(nullptr)
81 , mpTextEditOutlinerView(nullptr)
82 , mpTextEditWin(nullptr)
83 , m_pTextEditCursorBuffer(nullptr)
84 , m_pMacroObj(nullptr)
85 , m_pMacroPV(nullptr)
86 , m_pMacroWin(nullptr)
87 , m_aTextEditArea()
88 , m_aMinTextEditArea()
89 , m_aOldCalcFieldValueLink()
90 , m_aMacroDownPos()
91 , m_nMacroTol(0)
92 , mbTextEditDontDelete(false)
93 , mbTextEditOnlyOneView(false)
94 , mbTextEditNewObj(false)
95 , mbQuickTextEditMode(true)
96 , mbMacroDown(false)
97 , mbInteractiveSlideShow(false)
98 , mxSelectionController()
99 , mxLastSelectionController()
100 , mpOldTextEditUndoManager(nullptr)
101 , mpLocalTextEditUndoManager()
103 // init some timer settings (not starting it of course)
104 maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
105 maTextEditUpdateTimer.SetInvokeHandler(LINK(this, SdrObjEditView, TextEditUpdate));
108 IMPL_LINK_NOARG(SdrObjEditView, ImpModifyHdl, LinkParamNone*, void)
110 // IASS: active TextEdit had a model change. Check and react.
111 if (nullptr == mpTextEditOutliner)
112 // no Outliner, no TextEdit
113 return;
115 if (!mxWeakTextEditObj.get().is())
116 // no TextObject, no TextEdit
117 return;
119 // reset & restart the timer
120 maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT);
121 maTextEditUpdateTimer.Start();
124 IMPL_LINK_NOARG(SdrObjEditView, TextEditUpdate, Timer*, void)
126 // IASS: text was changed and EDIT_UPDATEDATA_TIMEOUT has passed
127 // since last user input
128 maTextEditUpdateTimer.Stop();
130 // be safe: still in TextEdit?
131 if (nullptr == mpTextEditOutliner)
132 // no Outliner, no TextEdit
133 return;
135 if (!mxWeakTextEditObj.get().is())
136 // no TextObject, no TextEdit
137 return;
139 // launch an ObjectChange: This is the straightforward method
140 // to get this broadcasted. We do not risk to set the model
141 // unwantedly to changed, we had a text edit going on already.
142 // This is needed for SlideShow since it is not (yet) using the
143 // standard schema with VC/VOC/OC
144 if (isInteractiveSlideShow())
145 mxWeakTextEditObj.get()->BroadcastObjectChange();
147 // force repaint for objects with changed text in all views
148 // that are VC/VOC/OC based (SlideShow is not yet)
149 sdr::contact::ViewContact& rVC(mxWeakTextEditObj.get()->GetViewContact());
151 if (!rVC.hasMultipleViewObjectContacts())
152 // only one VOC -> this is us
153 return;
155 if (nullptr == mpTextEditPV)
156 // should not happen, just invalidate all visualizations
157 rVC.ActionChanged();
158 else
159 // invalidate only visualizations in different views:
160 // this is important to not cause evtl. high repaint costs
161 // in the EditView -> we avoid this by running the TextEdit
162 // on the overlay. NOTE: This is only for better performance,
163 // any repaint will just work fine and do the right thing
164 rVC.ActionChangedIfDifferentPageView(*mpTextEditPV);
167 SdrObjEditView::~SdrObjEditView()
169 maTextEditUpdateTimer.Stop();
170 mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
171 assert(!IsTextEdit());
172 if (IsTextEdit())
173 suppress_fun_call_w_exception(SdrEndTextEdit());
174 mpTextEditOutliner.reset();
175 assert(nullptr == mpOldTextEditUndoManager); // should have been reset
178 bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }
180 void SdrObjEditView::MovAction(const Point& rPnt)
182 if (IsMacroObj())
183 MovMacroObj(rPnt);
184 SdrGlueEditView::MovAction(rPnt);
187 void SdrObjEditView::EndAction()
189 if (IsMacroObj())
190 EndMacroObj();
191 SdrGlueEditView::EndAction();
194 void SdrObjEditView::BckAction()
196 BrkMacroObj();
197 SdrGlueEditView::BckAction();
200 void SdrObjEditView::BrkAction()
202 BrkMacroObj();
203 SdrGlueEditView::BrkAction();
206 SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
208 SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);
210 if (comphelper::LibreOfficeKit::isActive() && pPageView)
212 // Check if other views have an active text edit on the same page as
213 // this one.
214 SdrViewIter::ForAllViews(pPageView->GetPage(), [this](SdrView* pView) {
215 if (pView == this || !pView->IsTextEdit())
216 return;
218 OutputDevice* pOutDev = GetFirstOutputDevice();
219 if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
220 return;
222 // Found one, so create an outliner view, to get invalidations when
223 // the text edit changes.
224 // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
225 // registers the view shell of this draw view, and not the view
226 // shell of pView.
227 OutlinerView* pOutlinerView
228 = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
229 pOutlinerView->HideCursor();
230 pView->GetTextEditOutliner()->InsertView(pOutlinerView);
234 return pPageView;
237 namespace
239 /// Removes outliner views registered in other draw views that use pOutputDevice.
240 void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
241 OutputDevice const* pOutputDevice)
243 if (!comphelper::LibreOfficeKit::isActive())
244 return;
246 if (!pPageView)
247 return;
249 if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
250 return;
252 SdrViewIter::ForAllViews(pPageView->GetPage(), [&pThis, &pOutputDevice](SdrView* pView) {
253 if (pView == pThis || !pView->IsTextEdit())
254 return;
256 SdrOutliner* pOutliner = pView->GetTextEditOutliner();
257 for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
259 OutlinerView* pOutlinerView = pOutliner->GetView(nView);
260 if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
261 continue;
263 pOutliner->RemoveView(pOutlinerView);
264 delete pOutlinerView;
270 void SdrObjEditView::HideSdrPage()
272 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
274 if (mpTextEditPV == GetSdrPageView())
276 // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
277 mpTextEditPV = nullptr;
280 SdrGlueEditView::HideSdrPage();
283 void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
285 if (IsMacroObj())
287 rRect = m_pMacroObj->GetCurrentBoundRect();
289 else
291 SdrGlueEditView::TakeActionRect(rRect);
295 void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
297 SdrGlueEditView::Notify(rBC, rHint);
298 if (mpTextEditOutliner == nullptr)
299 return;
301 // change of printer while editing
302 if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
303 return;
305 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
306 SdrHintKind eKind = pSdrHint->GetKind();
307 if (eKind == SdrHintKind::RefDeviceChange)
309 mpTextEditOutliner->SetRefDevice(GetModel().GetRefDevice());
311 if (eKind == SdrHintKind::DefaultTabChange)
313 mpTextEditOutliner->SetDefTab(GetModel().GetDefaultTabulator());
317 void SdrObjEditView::ModelHasChanged()
319 SdrGlueEditView::ModelHasChanged();
320 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
321 if (pTextObj && !pTextObj->IsInserted())
322 SdrEndTextEdit(); // object deleted
323 // TextEditObj changed?
324 if (!IsTextEdit())
325 return;
327 if (pTextObj != nullptr)
329 size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
330 bool bAreaChg = false;
331 bool bAnchorChg = false;
332 bool bColorChg = false;
333 bool bContourFrame = pTextObj->IsContourTextFrame();
334 EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
335 tools::Rectangle aOldArea(m_aMinTextEditArea);
336 aOldArea.Union(m_aTextEditArea);
337 Color aNewColor;
338 { // check area
339 Size aPaperMin1;
340 Size aPaperMax1;
341 tools::Rectangle aEditArea1;
342 tools::Rectangle aMinArea1;
343 pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
344 Point aPvOfs(pTextObj->GetTextEditOffset());
346 // add possible GridOffset to up-to-now view-independent EditAreas
347 basegfx::B2DVector aGridOffset(0.0, 0.0);
348 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), GetSdrPageView()))
350 const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
351 basegfx::fround<tools::Long>(aGridOffset.getY()));
353 aEditArea1 += aOffset;
354 aMinArea1 += aOffset;
357 aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
358 aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
359 tools::Rectangle aNewArea(aMinArea1);
360 aNewArea.Union(aEditArea1);
362 if (aNewArea != aOldArea || aEditArea1 != m_aTextEditArea
363 || aMinArea1 != m_aMinTextEditArea
364 || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
365 || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
367 m_aTextEditArea = aEditArea1;
368 m_aMinTextEditArea = aMinArea1;
370 const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
371 mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
372 mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
373 mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner
375 if (!bContourFrame)
377 mpTextEditOutliner->ClearPolygon();
378 EEControlBits nStat = mpTextEditOutliner->GetControlWord();
379 nStat |= EEControlBits::AUTOPAGESIZE;
380 mpTextEditOutliner->SetControlWord(nStat);
382 else
384 EEControlBits nStat = mpTextEditOutliner->GetControlWord();
385 nStat &= ~EEControlBits::AUTOPAGESIZE;
386 mpTextEditOutliner->SetControlWord(nStat);
387 tools::Rectangle aAnchorRect;
388 pTextObj->TakeTextAnchorRect(aAnchorRect);
389 pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
391 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
393 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
394 EVControlBits nStat0 = pOLV->GetControlWord();
395 EVControlBits nStat = nStat0;
396 // AutoViewSize only if not ContourFrame.
397 if (!bContourFrame)
398 nStat |= EVControlBits::AUTOSIZE;
399 else
400 nStat &= ~EVControlBits::AUTOSIZE;
401 if (nStat != nStat0)
402 pOLV->SetControlWord(nStat);
405 mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
406 bAreaChg = true;
409 if (mpTextEditOutlinerView != nullptr)
410 { // check fill and anchor
411 EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
412 eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
413 bAnchorChg = eOldAnchor != eNewAnchor;
414 Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
415 aNewColor = GetTextEditBackgroundColor(*this);
416 bColorChg = aOldColor != aNewColor;
418 // refresh always when it's a contour frame. That
419 // refresh is necessary since it triggers the repaint
420 // which makes the Handles visible. Changes at TakeTextRect()
421 // seem to have resulted in a case where no refresh is executed.
422 // Before that, a refresh must have been always executed
423 // (else this error would have happened earlier), thus I
424 // even think here a refresh should be done always.
425 // Since follow-up problems cannot even be guessed I only
426 // add this one more case to the if below.
427 // BTW: It's VERY bad style that here, inside ModelHasChanged()
428 // the outliner is again massively changed for the text object
429 // in text edit mode. Normally, all necessary data should be
430 // set at SdrBeginTextEdit(). Some changes and value assigns in
431 // SdrBeginTextEdit() are completely useless since they are set here
432 // again on ModelHasChanged().
433 if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
435 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
437 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
438 vcl::Window* pWin = pOLV->GetWindow();
439 { // invalidate old OutlinerView area
440 tools::Rectangle aTmpRect(aOldArea);
441 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
442 Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
443 aTmpRect.AdjustLeft(-(aMore.Width()));
444 aTmpRect.AdjustRight(aMore.Width());
445 aTmpRect.AdjustTop(-(aMore.Height()));
446 aTmpRect.AdjustBottom(aMore.Height());
447 InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
449 if (bAnchorChg)
450 pOLV->SetAnchorMode(eNewAnchor);
451 if (bColorChg)
452 pOLV->SetBackgroundColor(aNewColor);
454 bool bWasCoursorVisible = pOLV->IsCursorVisible();
455 vcl::Cursor* pOldCursor = pWin->GetCursor();
456 pOLV->SetOutputArea(
457 m_aTextEditArea); // because otherwise, we're not re-anchoring correctly
458 ImpInvalidateOutlinerView(*pOLV);
459 // Undo SetOutputArea setting and showing the cursor
460 if (!bWasCoursorVisible)
461 pOLV->HideCursor();
462 pWin->SetCursor(pOldCursor);
464 mpTextEditOutlinerView->ShowCursor();
467 ImpMakeTextCursorAreaVisible();
470 namespace
472 class TextEditFrameOverlayObject;
473 class TextEditHighContrastOverlaySelection;
476 Helper class to visualize the content of an active EditView as an
477 OverlayObject. These objects work with Primitives and are handled
478 from the OverlayManager(s) in place as needed.
480 It allows complete visualization of the content of the active
481 EditView without the need of Invalidates triggered by the EditView
482 and thus avoiding potentially expensive repaints by using the
483 automatically buffered Overlay mechanism.
485 It buffers as much as possible locally and *only* triggers a real
486 change (see call to objectChange()) when really needed.
488 class TextEditOverlayObject : public sdr::overlay::OverlayObject
490 protected:
491 /// local access to associated sdr::overlay::OverlaySelection
492 std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection;
493 std::unique_ptr<TextEditHighContrastOverlaySelection> mxOverlayHighContrastSelection;
494 std::unique_ptr<TextEditFrameOverlayObject> mxOverlayFrame;
496 /// local definition depends on active OutlinerView
497 OutlinerView& mrOutlinerView;
499 /// geometry definitions with buffering
500 basegfx::B2DRange maLastRange;
501 basegfx::B2DRange maRange;
503 /// text content definitions with buffering
504 drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
505 drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
507 // geometry creation for OverlayObject, can use local *Last* values
508 virtual drawinglayer::primitive2d::Primitive2DContainer
509 createOverlayObjectPrimitive2DSequence() override;
511 public:
512 TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView);
513 virtual ~TextEditOverlayObject() override;
515 sdr::overlay::OverlayObject* getOverlaySelection();
516 sdr::overlay::OverlayObject* getOverlayFrame();
518 const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
520 /// override to check conditions for last createOverlayObjectPrimitive2DSequence
521 virtual drawinglayer::primitive2d::Primitive2DContainer
522 getOverlayObjectPrimitive2DSequence() const override;
524 // data write access. In this OverlayObject we only have the
525 // callback that triggers detecting if something *has* changed
526 void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
527 void checkSelectionChange();
529 const basegfx::B2DRange& getRange() const { return maRange; }
530 const drawinglayer::primitive2d::Primitive2DContainer& getTextPrimitives() const
532 return maTextPrimitives;
536 class TextEditFrameOverlayObject : public sdr::overlay::OverlayObject
538 private:
539 const TextEditOverlayObject& mrTextEditOverlayObject;
541 // geometry creation for OverlayObject, can use local *Last* values
542 virtual drawinglayer::primitive2d::Primitive2DContainer
543 createOverlayObjectPrimitive2DSequence() override;
545 public:
546 TextEditFrameOverlayObject(const TextEditOverlayObject& rTextEditOverlayObject);
547 using sdr::overlay::OverlayObject::objectChange;
548 virtual ~TextEditFrameOverlayObject() override;
551 class TextEditHighContrastOverlaySelection : public sdr::overlay::OverlayObject
553 private:
554 const TextEditOverlayObject& mrTextEditOverlayObject;
555 std::vector<basegfx::B2DRange> maRanges;
557 // geometry creation for OverlayObject, can use local *Last* values
558 virtual drawinglayer::primitive2d::Primitive2DContainer
559 createOverlayObjectPrimitive2DSequence() override;
561 public:
562 TextEditHighContrastOverlaySelection(const TextEditOverlayObject& rTextEditOverlayObject);
563 void setRanges(std::vector<basegfx::B2DRange>&& rNew);
564 virtual ~TextEditHighContrastOverlaySelection() override;
567 TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection(
568 const TextEditOverlayObject& rTextEditOverlayObject)
569 : OverlayObject(rTextEditOverlayObject.getBaseColor())
570 , mrTextEditOverlayObject(rTextEditOverlayObject)
572 allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
573 // use selection colors in HighContrast mode
574 mbHighContrastSelection = true;
577 void TextEditHighContrastOverlaySelection::setRanges(std::vector<basegfx::B2DRange>&& rNew)
579 if (rNew != maRanges)
581 maRanges = std::move(rNew);
582 objectChange();
586 drawinglayer::primitive2d::Primitive2DContainer
587 TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
589 drawinglayer::primitive2d::Primitive2DContainer aRetval;
591 size_t nCount = maRanges.size();
593 if (nCount)
595 basegfx::B2DPolyPolygon aClipPolyPolygon;
597 basegfx::BColor aRGBColor(getBaseColor().getBColor());
599 for (size_t a = 0; a < nCount; ++a)
600 aClipPolyPolygon.append(basegfx::utils::createPolygonFromRect(maRanges[a]));
602 // This is used in high contrast mode, we will render the selection
603 // with the bg forced to the selection Highlight color and the fg color
604 // forced to the HighlightText color
605 aRetval.append(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
606 basegfx::B2DPolyPolygon(
607 basegfx::utils::createPolygonFromRect(aClipPolyPolygon.getB2DRange())),
608 aRGBColor));
609 aRetval.append(mrTextEditOverlayObject.getTextPrimitives());
610 aRetval.append(new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aClipPolyPolygon),
611 std::move(aRetval)));
614 return aRetval;
617 TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection()
619 if (getOverlayManager())
621 getOverlayManager()->remove(*this);
625 sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlaySelection()
627 if (mxOverlayTransparentSelection)
628 return mxOverlayTransparentSelection.get();
629 return mxOverlayHighContrastSelection.get();
632 sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlayFrame()
634 if (!mxOverlayFrame)
635 mxOverlayFrame.reset(new TextEditFrameOverlayObject(*this));
636 return mxOverlayFrame.get();
639 drawinglayer::primitive2d::Primitive2DContainer
640 TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
642 drawinglayer::primitive2d::Primitive2DContainer aRetval;
644 // add buffered TextPrimitives
645 aRetval.append(maTextPrimitives);
647 return aRetval;
650 drawinglayer::primitive2d::Primitive2DContainer
651 TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence()
653 drawinglayer::primitive2d::Primitive2DContainer aRetval;
655 /// outer frame visualization
656 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
657 const sal_uInt16 nPixSiz(mrTextEditOverlayObject.getOutlinerView().GetInvalidateMore() - 1);
659 aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
660 mrTextEditOverlayObject.getRange(), getBaseColor().getBColor(), fTransparence,
661 std::max(6, nPixSiz - 2), // grow
662 0.0, // shrink
663 0.0));
665 return aRetval;
668 TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView)
669 : OverlayObject(rColor)
670 , mrOutlinerView(rOutlinerView)
672 // no AA for TextEdit overlay
673 allowAntiAliase(false);
675 // create local OverlaySelection - this is an integral part of EditText
676 // visualization
677 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
679 mxOverlayHighContrastSelection.reset(new TextEditHighContrastOverlaySelection(*this));
681 else
683 std::vector<basegfx::B2DRange> aEmptySelection{};
684 mxOverlayTransparentSelection.reset(new sdr::overlay::OverlaySelection(
685 sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
689 TextEditOverlayObject::~TextEditOverlayObject()
691 mxOverlayTransparentSelection.reset();
692 mxOverlayHighContrastSelection.reset();
694 if (getOverlayManager())
696 getOverlayManager()->remove(*this);
700 TextEditFrameOverlayObject::TextEditFrameOverlayObject(
701 const TextEditOverlayObject& rTextEditOverlayObject)
702 : OverlayObject(rTextEditOverlayObject.getBaseColor())
703 , mrTextEditOverlayObject(rTextEditOverlayObject)
705 allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
706 // use selection colors in HighContrast mode
707 mbHighContrastSelection = true;
710 TextEditFrameOverlayObject::~TextEditFrameOverlayObject()
712 if (getOverlayManager())
714 getOverlayManager()->remove(*this);
718 drawinglayer::primitive2d::Primitive2DContainer
719 TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
721 if (!getPrimitive2DSequence().empty())
723 if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
725 // conditions of last local decomposition have changed, delete to force new evaluation
726 const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
730 if (getPrimitive2DSequence().empty())
732 // remember new buffered values
733 const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
734 const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
737 // call base implementation
738 return OverlayObject::getOverlayObjectPrimitive2DSequence();
741 void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
743 bool bObjectChange(false);
745 // check current range
746 const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
747 basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
748 aNewRange.expand(rMinTextEditArea);
750 if (aNewRange != maRange)
752 maRange = aNewRange;
753 bObjectChange = true;
756 // check if text primitives did change
757 SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner());
759 if (pSdrOutliner)
761 // get TextPrimitives directly from active Outliner
762 basegfx::B2DHomMatrix aNewTransformA;
763 basegfx::B2DHomMatrix aNewTransformB;
764 basegfx::B2DRange aClipRange;
765 drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
767 // active Outliner is always in unified oriented coordinate system (currently)
768 // so just translate to TopLeft of visible Range. Keep in mind that top-left
769 // depends on vertical text and top-to-bottom text attributes
770 const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
771 const bool bVerticalWriting(pSdrOutliner->IsVertical());
772 const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
773 const double fStartInX(bVerticalWriting && bTopToBottom
774 ? aOutArea.Right() - aVisArea.Left()
775 : aOutArea.Left() - aVisArea.Left());
776 const double fStartInY(bVerticalWriting && !bTopToBottom
777 ? aOutArea.Bottom() - aVisArea.Top()
778 : aOutArea.Top() - aVisArea.Top());
780 aNewTransformB.translate(fStartInX, fStartInY);
782 // get the current TextPrimitives. This is the most expensive part
783 // of this mechanism, it *may* be possible to buffer layouted
784 // primitives per ParaPortion with/in/dependent on the EditEngine
785 // content if needed. For now, get and compare
786 SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
787 aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange);
789 if (aNewTextPrimitives != maTextPrimitives)
791 maTextPrimitives = std::move(aNewTextPrimitives);
792 bObjectChange = true;
796 if (bObjectChange)
798 // if there really *was* a change signal the OverlayManager to
799 // refresh this object's visualization
800 objectChange();
802 if (mxOverlayFrame)
803 mxOverlayFrame->objectChange();
805 // on data change, always do a SelectionChange, too
806 // since the selection is an integral part of text visualization
807 checkSelectionChange();
811 void TextEditOverlayObject::checkSelectionChange()
813 if (!(getOverlaySelection() && getOverlayManager()))
814 return;
816 std::vector<tools::Rectangle> aLogicRects;
817 std::vector<basegfx::B2DRange> aLogicRanges;
818 const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
820 // get logic selection
821 getOutlinerView().GetSelectionRectangles(aLogicRects);
823 aLogicRanges.reserve(aLogicRects.size());
824 for (const auto& aRect : aLogicRects)
826 // convert from logic Rectangles to logic Ranges, do not forget to add
827 // one Unit (in this case logical units for one pixel, pre-calculated)
828 aLogicRanges.emplace_back(
829 aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
830 aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
833 if (mxOverlayTransparentSelection)
834 mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges));
835 else
836 mxOverlayHighContrastSelection->setRanges(std::move(aLogicRanges));
838 } // end of anonymous namespace
840 // TextEdit
842 // callback from the active EditView, forward to evtl. existing instances of the
843 // TextEditOverlayObject(s). This will additionally update the selection which
844 // is an integral part of the text visualization
845 void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
847 if (!IsTextEdit())
848 return;
850 // MinTextRange may have changed. Forward it, too
851 const basegfx::B2DRange aMinTextRange
852 = vcl::unotools::b2DRectangleFromRectangle(m_aMinTextEditArea);
854 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
856 TextEditOverlayObject* pCandidate
857 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
859 if (pCandidate)
861 pCandidate->checkDataChange(aMinTextRange);
866 // callback from the active EditView, forward to evtl. existing instances of the
867 // TextEditOverlayObject(s). This cvall *only* updates the selection visualization
868 // which is e.g. used when only the selection is changed, but not the text
869 void SdrObjEditView::EditViewSelectionChange()
871 if (!IsTextEdit())
872 return;
874 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
876 TextEditOverlayObject* pCandidate
877 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
879 if (pCandidate)
881 pCandidate->checkSelectionChange();
886 OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }
888 Point SdrObjEditView::EditViewPointerPosPixel() const
890 return mpTextEditWin->GetPointerPosPixel();
893 css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
895 if (!mpTextEditWin)
896 return nullptr;
897 return mpTextEditWin->GetClipboard();
900 css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
902 if (!mpTextEditWin)
903 return nullptr;
904 return mpTextEditWin->GetDropTarget();
907 void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
909 if (!mpTextEditWin)
910 return;
911 mpTextEditWin->SetInputContext(rInputContext);
914 void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
916 if (!mpTextEditWin)
917 return;
918 mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
921 void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
923 if (!comphelper::LibreOfficeKit::isActive())
925 // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
926 // to update accordingly (will update selection, too). Suppress new
927 // stuff when LibreOfficeKit is active
928 EditViewInvalidate(tools::Rectangle());
930 else
932 // draw old text edit stuff
933 if (IsTextEdit())
935 const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
937 if (pActiveOutliner)
939 const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
941 if (nViewCount)
943 const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
944 const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
946 for (sal_uInt32 i(0); i < nViewCount; i++)
948 OutlinerView* pOLV = pActiveOutliner->GetView(i);
950 // If rPaintWindow knows that the output device is a render
951 // context and is aware of the underlying vcl::Window,
952 // compare against that; that's how double-buffering can
953 // still find the matching OutlinerView.
954 OutputDevice* pOutputDevice = rPaintWindow.GetWindow()
955 ? rPaintWindow.GetWindow()->GetOutDev()
956 : &rPaintWindow.GetOutputDevice();
957 if (pOLV->GetWindow()->GetOutDev() == pOutputDevice
958 || comphelper::LibreOfficeKit::isActive())
960 ImpPaintOutlinerView(*pOLV, aCheckRect,
961 rPaintWindow.GetTargetOutputDevice());
962 return;
971 void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
972 OutputDevice& rTargetDevice) const
974 const SdrTextObj* pText = GetTextEditObject();
975 bool bTextFrame(pText && pText->IsTextFrame());
976 bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
977 bool bModified(mpTextEditOutliner->IsModified());
978 tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
979 aBlankRect.Union(m_aMinTextEditArea);
980 tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));
982 // in the tiled rendering case, the setup is incomplete, and we very
983 // easily get an empty rRect on input - that will cause that everything is
984 // clipped; happens in case of editing text inside a shape in Calc.
985 // FIXME would be better to complete the setup so that we don't get an
986 // empty rRect here
987 if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
988 aBlankRect.Intersection(rRect);
990 rOutlView.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
991 rOutlView.Paint(aBlankRect, &rTargetDevice);
993 if (!bModified)
995 mpTextEditOutliner->ClearModifyFlag();
998 if (bTextFrame && !bFitToSize)
1000 // completely reworked to use primitives; this ensures same look and functionality
1001 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
1002 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
1003 drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
1004 aViewInformation2D));
1006 const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
1007 const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
1008 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
1009 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
1010 const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
1011 const drawinglayer::primitive2d::Primitive2DReference xReference(
1012 new drawinglayer::primitive2d::OverlayRectanglePrimitive(
1013 aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
1014 0.0, // shrink
1015 0.0));
1016 const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };
1018 rTargetDevice.EnableMapMode(false);
1019 xProcessor->process(aSequence);
1020 rTargetDevice.EnableMapMode(bMapModeEnabled);
1023 rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
1026 void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
1028 vcl::Window* pWin = rOutlView.GetWindow();
1030 if (!pWin)
1031 return;
1033 const SdrTextObj* pText = GetTextEditObject();
1034 bool bTextFrame(pText && pText->IsTextFrame());
1035 bool bFitToSize(pText && pText->IsFitToSize());
1037 if (!bTextFrame || bFitToSize)
1038 return;
1040 tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
1041 aBlankRect.Union(m_aMinTextEditArea);
1042 tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
1043 sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
1045 aPixRect.AdjustLeft(-1);
1046 aPixRect.AdjustTop(-1);
1047 aPixRect.AdjustRight(1);
1048 aPixRect.AdjustBottom(1);
1051 // limit xPixRect because of driver problems when pixel coordinates are too far out
1052 Size aMaxXY(pWin->GetOutputSizePixel());
1053 tools::Long a(2 * nPixSiz);
1054 tools::Long nMaxX(aMaxXY.Width() + a);
1055 tools::Long nMaxY(aMaxXY.Height() + a);
1057 if (aPixRect.Left() < -a)
1058 aPixRect.SetLeft(-a);
1059 if (aPixRect.Top() < -a)
1060 aPixRect.SetTop(-a);
1061 if (aPixRect.Right() > nMaxX)
1062 aPixRect.SetRight(nMaxX);
1063 if (aPixRect.Bottom() > nMaxY)
1064 aPixRect.SetBottom(nMaxY);
1067 tools::Rectangle aOuterPix(aPixRect);
1068 aOuterPix.AdjustLeft(-nPixSiz);
1069 aOuterPix.AdjustTop(-nPixSiz);
1070 aOuterPix.AdjustRight(nPixSiz);
1071 aOuterPix.AdjustBottom(nPixSiz);
1073 bool bMapModeEnabled(pWin->IsMapModeEnabled());
1074 pWin->EnableMapMode(false);
1075 pWin->Invalidate(aOuterPix);
1076 pWin->EnableMapMode(bMapModeEnabled);
1079 OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
1080 SfxViewShell* pViewShell) const
1082 // background
1083 Color aBackground(GetTextEditBackgroundColor(*this));
1084 rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get();
1085 bool bTextFrame = pText != nullptr && pText->IsTextFrame();
1086 bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
1087 // create OutlinerView
1088 OutlinerView* pOutlView = pGivenView;
1089 mpTextEditOutliner->SetUpdateLayout(false);
1091 if (pOutlView == nullptr)
1093 pOutlView = new OutlinerView(mpTextEditOutliner.get(), pWin);
1095 else
1097 pOutlView->SetWindow(pWin);
1100 if (mbNegativeX)
1101 pOutlView->GetEditView().SetNegativeX(mbNegativeX);
1103 // disallow scrolling
1104 EVControlBits nStat = pOutlView->GetControlWord();
1105 nStat &= ~EVControlBits::AUTOSCROLL;
1106 // AutoViewSize only if not ContourFrame.
1107 if (!bContourFrame)
1108 nStat |= EVControlBits::AUTOSIZE;
1109 if (bTextFrame)
1111 sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
1112 nStat |= EVControlBits::INVONEMORE;
1113 pOutlView->SetInvalidateMore(nPixSiz);
1115 pOutlView->SetControlWord(nStat);
1116 pOutlView->SetBackgroundColor(aBackground);
1118 // In case we're in the process of constructing a new view shell,
1119 // SfxViewShell::Current() may still point to the old one. So if possible,
1120 // depend on the application owning this draw view to provide the view
1121 // shell.
1122 SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
1123 pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());
1125 if (pText != nullptr)
1127 pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
1128 mpTextEditOutliner->SetFixedCellHeight(
1129 pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1131 // do update before setting output area so that aTextEditArea can be recalculated
1132 mpTextEditOutliner->SetUpdateLayout(true);
1133 pOutlView->SetOutputArea(m_aTextEditArea);
1134 ImpInvalidateOutlinerView(*pOutlView);
1135 return pOutlView;
1138 IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
1140 if (mpTextEditOutliner)
1142 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1143 if (pTextObj)
1145 pTextObj->onEditOutlinerStatusEvent(&rEditStat);
1150 void SdrObjEditView::ImpChainingEventHdl()
1152 if (!mpTextEditOutliner)
1153 return;
1155 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1156 OutlinerView* pOLV = GetTextEditOutlinerView();
1157 if (pTextObj && pOLV)
1159 TextChain* pTextChain = pTextObj->GetTextChain();
1161 // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
1162 if (!pTextObj->IsChainable())
1164 return;
1166 // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
1167 if (pTextChain->GetNilChainingEvent(pTextObj.get()))
1169 return;
1172 // We prevent to trigger further handling of overflow/underflow for pTextObj
1173 pTextChain->SetNilChainingEvent(pTextObj.get(), true); // XXX
1175 // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
1176 pTextChain->SetPreChainingSel(pTextObj.get(), pOLV->GetSelection());
1177 //maPreChainingSel = new ESelection(pOLV->GetSelection());
1179 // Handling Undo
1180 const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
1182 const bool bUndoEnabled = IsUndoEnabled();
1183 std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1184 if (bUndoEnabled)
1185 pTxtUndo.reset(
1186 dynamic_cast<SdrUndoObjSetText*>(GetModel()
1187 .GetSdrUndoFactory()
1188 .CreateUndoObjectSetText(*pTextObj, nText)
1189 .release()));
1191 // trigger actual chaining
1192 pTextObj->onChainingEvent();
1194 if (pTxtUndo)
1196 pTxtUndo->AfterSetText();
1197 if (!pTxtUndo->IsDifferent())
1199 pTxtUndo.reset();
1203 if (pTxtUndo)
1204 AddUndo(std::move(pTxtUndo));
1206 //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
1207 //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
1209 // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
1210 pTextChain->SetNilChainingEvent(pTextObj.get(), false);
1212 else
1214 // XXX
1215 SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
1219 IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void)
1221 SdrTextObj* pTextObj = GetTextEditObject();
1222 if (!pTextObj)
1223 return;
1224 ImpChainingEventHdl();
1225 TextChainCursorManager aCursorManager(this, pTextObj);
1226 ImpMoveCursorAfterChainingEvent(&aCursorManager);
1229 void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
1231 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1233 if (!pTextObj || !pCursorManager)
1234 return;
1236 // Check if it has links to move it to
1237 if (!pTextObj || !pTextObj->IsChainable())
1238 return;
1240 TextChain* pTextChain = pTextObj->GetTextChain();
1241 ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj.get());
1243 pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj.get()),
1244 aNewSel);
1246 // Reset event
1247 pTextChain->SetCursorEvent(pTextObj.get(), CursorChainingEvent::NULL_EVENT);
1250 IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
1252 bool bOk = false;
1253 OUString& rStr = pFI->GetRepresentation();
1254 rStr.clear();
1255 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1256 if (pTextObj != nullptr)
1258 std::optional<Color> pTxtCol;
1259 std::optional<Color> pFldCol;
1260 std::optional<FontLineStyle> pFldLineStyle;
1261 bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
1262 pTxtCol, pFldCol, pFldLineStyle, rStr);
1263 if (bOk)
1265 if (pTxtCol)
1267 pFI->SetTextColor(*pTxtCol);
1269 if (pFldLineStyle)
1271 pFI->SetFontLineStyle(*pFldLineStyle);
1273 if (pFldCol)
1275 pFI->SetFieldColor(*pFldCol);
1277 else
1279 pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
1283 Outliner& rDrawOutl = GetModel().GetDrawOutliner(pTextObj.get());
1284 Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
1285 if (!bOk && aDrawOutlLink.IsSet())
1287 aDrawOutlLink.Call(pFI);
1288 bOk = !rStr.isEmpty();
1290 if (!bOk)
1292 m_aOldCalcFieldValueLink.Call(pFI);
1296 IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }
1298 // Default implementation - null UndoManager
1299 std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
1301 SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
1302 return std::unique_ptr<SdrUndoManager>();
1305 bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
1306 bool bIsNewObj, SdrOutliner* pGivenOutliner,
1307 OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
1308 bool bOnlyOneView, bool bGrabFocus)
1310 // FIXME cannot be an assert() yet, the code is not ready for that;
1311 // eg. press F7 in Impress when you are inside a text object with spelling
1312 // mistakes => boom; and it is unclear how to avoid that
1313 SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
1314 // FIXME this encourages all sorts of bad habits and should be removed
1315 SdrEndTextEdit();
1317 SdrTextObj* pObj = DynCastSdrTextObj(pObj_);
1318 if (!pObj)
1319 return false; // currently only possible with text objects
1321 if (bGrabFocus && pWin)
1323 // attention, this call may cause an EndTextEdit() call to this view
1324 pWin->GrabFocus(); // to force the cursor into the edit view
1327 mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
1328 mbTextEditOnlyOneView = bOnlyOneView;
1329 mbTextEditNewObj = bIsNewObj;
1330 const sal_uInt32 nWinCount(PaintWindowCount());
1332 bool bBrk(false);
1334 if (!pWin)
1336 for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
1338 SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1340 if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
1342 pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
1346 // break, when no window exists
1347 if (!pWin)
1349 bBrk = true;
1353 if (!bBrk && !pPV)
1355 pPV = GetSdrPageView();
1357 // break, when no PageView for the object exists
1358 if (!pPV)
1360 bBrk = true;
1364 // no TextEdit on objects in locked Layer
1365 if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
1367 bBrk = true;
1370 if (mpTextEditOutliner)
1372 OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
1373 mpTextEditOutliner.reset();
1376 if (!bBrk)
1378 mpTextEditWin = pWin;
1379 mpTextEditPV = pPV;
1380 mxWeakTextEditObj = pObj;
1381 if (pGivenOutliner)
1383 mpTextEditOutliner.reset(pGivenOutliner);
1384 pGivenOutliner = nullptr; // so we don't delete it on the error path
1386 else
1387 mpTextEditOutliner
1388 = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject());
1391 mpTextEditOutliner->ForceAutoColor(
1392 officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get());
1395 m_aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
1396 // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
1397 mpTextEditOutliner->SetCalcFieldValueHdl(
1398 LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
1399 mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
1400 mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));
1402 // It is just necessary to make the visualized page known. Set it.
1403 mpTextEditOutliner->setVisualizedPage(pPV->GetPage());
1405 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1406 mpTextEditOutliner->SetTextObjNoInit(pTextObj.get());
1408 if (pTextObj->BegTextEdit(*mpTextEditOutliner))
1410 // switch off any running TextAnimations
1411 pTextObj->SetTextAnimationAllowed(false);
1413 // remember old cursor
1414 if (mpTextEditOutliner->GetViewCount() != 0)
1416 mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
1419 // Determine EditArea via TakeTextEditArea.
1420 // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
1421 // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
1422 pTextObj->TakeTextEditArea(nullptr, nullptr, &m_aTextEditArea, &m_aMinTextEditArea);
1424 tools::Rectangle aTextRect;
1425 tools::Rectangle aAnchorRect;
1426 pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
1427 &aAnchorRect /* Give true here, not false */);
1429 if (!pTextObj->IsContourTextFrame())
1431 // FitToSize not together with ContourFrame, for now
1432 if (pTextObj->IsFitToSize())
1433 aTextRect = aAnchorRect;
1436 m_aTextEditArea = aTextRect;
1438 // add possible GridOffset to up-to-now view-independent EditAreas
1439 basegfx::B2DVector aGridOffset(0.0, 0.0);
1440 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), pPV))
1442 const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()),
1443 basegfx::fround<tools::Long>(aGridOffset.getY()));
1445 m_aTextEditArea += aOffset;
1446 m_aMinTextEditArea += aOffset;
1449 Point aPvOfs(pTextObj->GetTextEditOffset());
1450 m_aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1451 m_aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1452 m_pTextEditCursorBuffer = pWin->GetCursor();
1454 maHdlList.SetMoveOutside(true);
1456 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1457 // to call AdjustMarkHdl() always.
1458 AdjustMarkHdl();
1460 mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);
1462 if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
1464 // activate visualization of EditView on Overlay, suppress when
1465 // LibreOfficeKit is active
1466 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
1468 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
1469 const SdrTextObj* pText = GetTextEditObject();
1470 // show for cases like tdf#94223 but not for table cells like tdf#151311
1471 const bool bVisualizeSurroundingFrame(
1472 pText && pText->GetObjIdentifier() != SdrObjKind::Table);
1473 SdrPageView* pPageView = GetSdrPageView();
1475 if (pPageView)
1477 for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
1479 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
1481 if (rPageWindow.GetPaintWindow().OutputToWindow())
1483 const rtl::Reference<sdr::overlay::OverlayManager>& xManager
1484 = rPageWindow.GetOverlayManager();
1485 if (xManager.is())
1487 std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
1488 new TextEditOverlayObject(aHilightColor,
1489 *mpTextEditOutlinerView));
1491 xManager->add(*pNewTextEditOverlayObject);
1492 if (bVisualizeSurroundingFrame)
1493 xManager->add(*pNewTextEditOverlayObject->getOverlayFrame());
1494 xManager->add(*pNewTextEditOverlayObject->getOverlaySelection());
1496 maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
1503 // check if this view is already inserted
1504 size_t i2, nCount = mpTextEditOutliner->GetViewCount();
1505 for (i2 = 0; i2 < nCount; i2++)
1507 if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
1508 break;
1511 if (i2 == nCount)
1512 mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);
1514 maHdlList.SetMoveOutside(false);
1515 maHdlList.SetMoveOutside(true);
1517 // register all windows as OutlinerViews with the Outliner
1518 if (!bOnlyOneView)
1520 for (sal_uInt32 i = 0; i < nWinCount; i++)
1522 SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1523 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1525 if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1527 OutlinerView* pOutlView
1528 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1529 mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
1533 if (comphelper::LibreOfficeKit::isActive())
1535 // Register an outliner view for all other sdr views that
1536 // show the same page, so that when the text edit changes,
1537 // all interested windows get an invalidation.
1538 SdrViewIter::ForAllViews(pObj->getSdrPageFromSdrObject(), [this, &pWin](
1539 SdrView* pView) {
1540 if (pView == this)
1541 return;
1543 for (sal_uInt32 nViewPaintWindow = 0;
1544 nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
1546 SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
1547 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1549 if (&rOutDev != pWin->GetOutDev()
1550 && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1552 OutlinerView* pOutlView
1553 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1554 pOutlView->HideCursor();
1555 rOutDev.GetOwnerWindow()->SetCursor(nullptr);
1556 mpTextEditOutliner->InsertView(pOutlView);
1563 mpTextEditOutlinerView->ShowCursor();
1564 mpTextEditOutliner->SetStatusEventHdl(
1565 LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));
1567 // IASS: start listening to ModelChanges of TextEdit
1568 if (isInteractiveSlideShow()
1569 || pTextObj->GetViewContact().hasMultipleViewObjectContacts())
1570 mpTextEditOutliner->SetModifyHdl(LINK(this, SdrObjEditView, ImpModifyHdl));
1572 if (pTextObj->IsChainable())
1574 mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
1575 LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
1578 mpTextEditOutliner->ClearModifyFlag();
1580 if (pTextObj->IsFitToSize())
1582 pWin->Invalidate(m_aTextEditArea);
1585 SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
1586 GetModel().Broadcast(aHint);
1587 if (auto pBroadcaster = pTextObj->GetBroadcaster())
1588 pBroadcaster->Broadcast(aHint);
1590 mpTextEditOutliner->setVisualizedPage(nullptr);
1592 if (mxSelectionController.is())
1593 mxSelectionController->onSelectionHasChanged();
1595 if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1597 SdrUndoManager* pSdrUndoManager = nullptr;
1598 mpLocalTextEditUndoManager = createLocalTextUndoManager();
1600 if (mpLocalTextEditUndoManager)
1601 pSdrUndoManager = mpLocalTextEditUndoManager.get();
1603 if (pSdrUndoManager)
1605 // we have an outliner, undo manager and it's an EditUndoManager, exchange
1606 // the document undo manager and the default one from the outliner and tell
1607 // it that text edit starts by setting a callback if it needs to end text edit mode.
1608 assert(nullptr == mpOldTextEditUndoManager);
1610 mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
1611 pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
1613 else
1615 OSL_ENSURE(false,
1616 "The document undo manager is not derived from SdrUndoManager (!)");
1620 return true; // ran fine, let TextEdit run now
1622 else
1624 mpTextEditOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink);
1625 mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1626 mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1629 if (mpTextEditOutliner != nullptr)
1631 mpTextEditOutliner->setVisualizedPage(nullptr);
1634 // something went wrong...
1635 if (!bDontDeleteOutliner)
1637 delete pGivenOutliner;
1638 if (pGivenOutlinerView != nullptr)
1640 delete pGivenOutlinerView;
1641 pGivenOutlinerView = nullptr;
1644 mpTextEditOutliner.reset();
1646 mpTextEditOutlinerView = nullptr;
1647 mxWeakTextEditObj.clear();
1648 mpTextEditPV = nullptr;
1649 mpTextEditWin = nullptr;
1650 maHdlList.SetMoveOutside(false);
1652 return false;
1655 SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
1657 // IASS: stop evtl. running timer immediately
1658 maTextEditUpdateTimer.Stop();
1660 SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
1661 rtl::Reference<SdrTextObj> pTEObj = mxWeakTextEditObj.get();
1662 vcl::Window* pTEWin = mpTextEditWin;
1663 OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
1664 vcl::Cursor* pTECursorBuffer = m_pTextEditCursorBuffer;
1665 SdrUndoManager* pUndoEditUndoManager = nullptr;
1666 bool bNeedToUndoSavedRedoTextEdit(false);
1668 if (IsUndoEnabled() && pTEObj && mpTextEditOutliner
1669 && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1671 // change back the UndoManager to the remembered original one
1672 SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
1673 mpOldTextEditUndoManager = nullptr;
1675 if (pOriginal)
1677 // check if we got back our document undo manager
1678 SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();
1680 if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
1682 if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
1684 // remember the UndoManager where missing Undos have to be triggered after end
1685 // text edit. When the undo had triggered the end text edit, the original action
1686 // which had to be undone originally is not yet undone.
1687 pUndoEditUndoManager = pSdrUndoManager;
1689 // We are ending text edit; if text edit was triggered from undo, execute all redos
1690 // to create a complete text change undo action for the redo buffer. Also mark this
1691 // state when at least one redo was executed; the created extra TextChange needs to
1692 // be undone in addition to the first real undo outside the text edit changes
1693 while (pSdrUndoManager->GetRedoActionCount()
1694 > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
1696 bNeedToUndoSavedRedoTextEdit = true;
1697 pSdrUndoManager->Redo();
1701 // reset the callback link and let the undo manager cleanup all text edit
1702 // undo actions to get the stack back to the form before the text edit
1703 pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
1705 else
1707 OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
1708 "expected document UndoManager (!)");
1709 delete pOriginal;
1712 // cid#1493241 - Wrapper object use after free
1713 if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
1714 pUndoEditUndoManager = nullptr;
1715 mpLocalTextEditUndoManager.reset();
1718 else
1720 assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
1723 if (auto pTextEditObj = mxWeakTextEditObj.get())
1725 SdrHint aHint(SdrHintKind::EndEdit, *pTextEditObj);
1726 GetModel().Broadcast(aHint);
1727 if (auto pBroadcaster = pTextEditObj->GetBroadcaster())
1728 pBroadcaster->Broadcast(aHint);
1731 // if new mechanism was used, clean it up. At cleanup no need to check
1732 // for LibreOfficeKit
1733 if (mpTextEditOutlinerView)
1735 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
1736 maTEOverlayGroup.clear();
1739 mxWeakTextEditObj.clear();
1740 mpTextEditPV = nullptr;
1741 mpTextEditWin = nullptr;
1742 mpTextEditOutlinerView = nullptr;
1743 m_pTextEditCursorBuffer = nullptr;
1744 m_aTextEditArea = tools::Rectangle();
1746 if (SdrOutliner* pTEOutliner = mpTextEditOutliner.release())
1748 bool bModified = pTEOutliner->IsModified();
1749 if (pTEOutlinerView != nullptr)
1751 pTEOutlinerView->HideCursor();
1753 if (pTEObj != nullptr)
1755 pTEOutliner->CompleteOnlineSpelling();
1757 std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1759 if (bModified)
1761 sal_Int32 nText;
1762 for (nText = 0; nText < pTEObj->getTextCount(); ++nText)
1763 if (pTEObj->getText(nText) == pTEObj->getActiveText())
1764 break;
1766 pTxtUndo.reset(
1767 dynamic_cast<SdrUndoObjSetText*>(GetModel()
1768 .GetSdrUndoFactory()
1769 .CreateUndoObjectSetText(*pTEObj, nText)
1770 .release()));
1772 DBG_ASSERT(!bModified || pTxtUndo,
1773 "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
1774 // Set old CalcFieldValue-Handler again, this
1775 // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
1776 pTEOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink);
1777 pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1778 pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1780 // IASS: stop listening to ModelChanges of TextEdit
1781 pTEOutliner->SetModifyHdl(Link<LinkParamNone*, void>());
1783 const bool bUndo = IsUndoEnabled();
1784 if (bUndo)
1786 OUString aObjName(pTEObj->TakeObjNameSingul());
1787 BegUndo(SvxResId(STR_UndoObjSetText), aObjName);
1790 pTEObj->EndTextEdit(*pTEOutliner);
1792 if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork()))
1794 pTEObj->ActionChanged();
1797 if (pTxtUndo != nullptr)
1799 pTxtUndo->AfterSetText();
1800 if (!pTxtUndo->IsDifferent())
1802 pTxtUndo.reset();
1805 // check deletion of entire TextObj
1806 std::unique_ptr<SdrUndoAction> pDelUndo;
1807 bool bDelObj = false;
1808 if (mbTextEditNewObj)
1810 bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj()
1811 && !pTEObj->HasFill() && !pTEObj->HasLine();
1813 if (pTEObj->IsInserted() && bDelObj
1814 && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally)
1816 SdrObjKind eIdent = pTEObj->GetObjIdentifier();
1817 if (eIdent == SdrObjKind::Text)
1819 pDelUndo = GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj);
1823 if (pTxtUndo)
1825 if (bUndo)
1826 AddUndo(std::move(pTxtUndo));
1827 eRet = SdrEndTextEditKind::Changed;
1829 if (pDelUndo != nullptr)
1831 if (bUndo)
1833 AddUndo(std::move(pDelUndo));
1835 eRet = SdrEndTextEditKind::Deleted;
1836 DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr,
1837 "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
1838 "ObjList!");
1839 if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr)
1841 pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum());
1842 CheckMarked(); // remove selection immediately...
1845 else if (bDelObj)
1846 { // for Writer: the app has to do the deletion itself.
1847 eRet = SdrEndTextEditKind::ShouldBeDeleted;
1850 if (bUndo)
1851 EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
1853 // Switch on any TextAnimation again after TextEdit
1854 if (pTEObj)
1856 pTEObj->SetTextAnimationAllowed(true);
1859 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1860 // to call AdjustMarkHdl() always.
1861 AdjustMarkHdl();
1863 if (pTEWin != nullptr)
1865 pTEWin->SetCursor(pTECursorBuffer);
1867 // delete all OutlinerViews
1868 for (size_t i = pTEOutliner->GetViewCount(); i > 0;)
1870 i--;
1871 OutlinerView* pOLV = pTEOutliner->GetView(i);
1872 sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10;
1873 VclPtr<vcl::Window> pWin = pOLV->GetWindow();
1874 tools::Rectangle aRect(pOLV->GetOutputArea());
1875 pTEOutliner->RemoveView(i);
1876 if (!mbTextEditDontDelete || i != 0)
1878 // may not own the zeroth one
1879 delete pOLV;
1881 aRect.Union(m_aTextEditArea);
1882 aRect.Union(m_aMinTextEditArea);
1883 aRect = pWin->LogicToPixel(aRect);
1884 aRect.AdjustLeft(-nMorePix);
1885 aRect.AdjustTop(-nMorePix);
1886 aRect.AdjustRight(nMorePix);
1887 aRect.AdjustBottom(nMorePix);
1888 aRect = pWin->PixelToLogic(aRect);
1889 InvalidateOneWin(*pWin->GetOutDev(), aRect);
1890 pWin->GetOutDev()->SetFillColor();
1891 pWin->GetOutDev()->SetLineColor(COL_BLACK);
1893 // and now the Outliner itself
1894 if (!mbTextEditDontDelete)
1895 delete pTEOutliner;
1896 else
1897 pTEOutliner->Clear();
1898 maHdlList.SetMoveOutside(false);
1899 if (eRet != SdrEndTextEditKind::Unchanged)
1901 GetMarkedObjectListWriteAccess().SetNameDirty();
1903 // coverity[leaked_storage] - if pTEOutliner wasn't deleted it didn't really belong to us
1906 if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster())
1908 SdrHint aHint(SdrHintKind::EndEdit, *pTEObj);
1909 pTEObj->GetBroadcaster()->Broadcast(aHint);
1912 if (pUndoEditUndoManager)
1914 if (bNeedToUndoSavedRedoTextEdit)
1916 // undo the text edit action since it was created as part of an EndTextEdit
1917 // callback from undo itself. This needs to be done after the call to
1918 // FmFormView::SdrEndTextEdit since it gets created there
1919 pUndoEditUndoManager->Undo();
1922 // trigger the Undo which was not executed, but lead to this
1923 // end text edit
1924 pUndoEditUndoManager->Undo();
1927 return eRet;
1930 // info about TextEdit. Default is false.
1931 bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.get().is(); }
1933 // info about TextEditPageView. Default is 0L.
1934 SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; }
1936 OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const
1938 if (pWin == nullptr)
1939 return nullptr;
1940 if (mpTextEditOutliner == nullptr)
1941 return nullptr;
1942 OutlinerView* pNewView = nullptr;
1943 size_t nWinCount = mpTextEditOutliner->GetViewCount();
1944 for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++)
1946 OutlinerView* pView = mpTextEditOutliner->GetView(i);
1947 if (pView->GetWindow() == pWin)
1948 pNewView = pView;
1950 return pNewView;
1953 void SdrObjEditView::SetTextEditWin(vcl::Window* pWin)
1955 if (!(mxWeakTextEditObj.get() && pWin != nullptr && pWin != mpTextEditWin))
1956 return;
1958 OutlinerView* pNewView = ImpFindOutlinerView(pWin);
1959 if (pNewView != nullptr && pNewView != mpTextEditOutlinerView)
1961 if (mpTextEditOutlinerView != nullptr)
1963 mpTextEditOutlinerView->HideCursor();
1965 mpTextEditOutlinerView = pNewView;
1966 mpTextEditWin = pWin;
1967 pWin->GrabFocus(); // Make the cursor blink here as well
1968 pNewView->ShowCursor();
1969 ImpMakeTextCursorAreaVisible();
1973 bool SdrObjEditView::IsTextEditHit(const Point& rHit) const
1975 bool bOk = false;
1976 if (mxWeakTextEditObj.get())
1978 tools::Rectangle aEditArea;
1979 if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0))
1980 aEditArea.Union(pOLV->GetOutputArea());
1982 if (aEditArea.Contains(rHit))
1983 { // check if any characters were actually hit
1984 const Point aPnt(rHit - aEditArea.TopLeft());
1985 tools::Long nHitTol = 2000;
1986 if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice())
1987 nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM,
1988 pRef->GetMapMode().GetMapUnit());
1990 bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol));
1993 return bOk;
1996 bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const
1998 bool bOk = false;
1999 if (rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get())
2001 OutlinerView* pOLV = mpTextEditOutliner->GetView(0);
2002 if (pOLV)
2004 vcl::Window* pWin = pOLV->GetWindow();
2005 if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr)
2007 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore();
2008 tools::Rectangle aEditArea(m_aMinTextEditArea);
2009 aEditArea.Union(pOLV->GetOutputArea());
2010 if (!aEditArea.Contains(rHit))
2012 Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
2013 aEditArea.AdjustLeft(-(aSiz.Width()));
2014 aEditArea.AdjustTop(-(aSiz.Height()));
2015 aEditArea.AdjustRight(aSiz.Width());
2016 aEditArea.AdjustBottom(aSiz.Height());
2017 bOk = aEditArea.Contains(rHit);
2022 return bOk;
2025 std::unique_ptr<TextChainCursorManager>
2026 SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled)
2028 *bOutHandled = false;
2030 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
2031 if (!pTextObj)
2032 return nullptr;
2034 if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain())
2035 return nullptr;
2037 std::unique_ptr<TextChainCursorManager> pCursorManager(
2038 new TextChainCursorManager(this, pTextObj.get()));
2039 if (pCursorManager->HandleKeyEvent(rKEvt))
2041 // Possibly do other stuff here if necessary...
2042 // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
2043 *bOutHandled = true;
2046 return pCursorManager;
2049 bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
2051 if (mpTextEditOutlinerView)
2053 /* Start special handling of keys within a chain */
2054 // We possibly move to another box before any handling
2055 bool bHandled = false;
2056 std::unique_ptr<TextChainCursorManager> xCursorManager(
2057 ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled));
2058 if (bHandled)
2059 return true;
2060 /* End special handling of keys within a chain */
2062 if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin))
2064 if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
2066 GetModel().SetChanged();
2067 SetInnerTextAreaForLOKit();
2070 /* Start chaining processing */
2071 ImpChainingEventHdl();
2072 ImpMoveCursorAfterChainingEvent(xCursorManager.get());
2073 /* End chaining processing */
2075 if (pWin != nullptr && pWin != mpTextEditWin)
2076 SetTextEditWin(pWin);
2077 ImpMakeTextCursorAreaVisible();
2078 return true;
2081 return SdrGlueEditView::KeyInput(rKEvt, pWin);
2084 bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
2086 if (mpTextEditOutlinerView != nullptr)
2088 bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
2089 if (!bPostIt)
2091 Point aPt(rMEvt.GetPosPixel());
2092 if (pWin != nullptr)
2093 aPt = pWin->PixelToLogic(aPt);
2094 else if (mpTextEditWin != nullptr)
2095 aPt = mpTextEditWin->PixelToLogic(aPt);
2096 bPostIt = IsTextEditHit(aPt);
2098 if (bPostIt)
2100 Point aPixPos(rMEvt.GetPosPixel());
2101 if (pWin)
2103 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2104 if (aPixPos.X() < aR.Left())
2105 aPixPos.setX(aR.Left());
2106 if (aPixPos.X() > aR.Right())
2107 aPixPos.setX(aR.Right());
2108 if (aPixPos.Y() < aR.Top())
2109 aPixPos.setY(aR.Top());
2110 if (aPixPos.Y() > aR.Bottom())
2111 aPixPos.setY(aR.Bottom());
2113 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2114 rMEvt.GetModifier());
2115 if (mpTextEditOutlinerView->MouseButtonDown(aMEvt))
2117 if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev()
2118 && pWin->GetOutDevType() == OUTDEV_WINDOW)
2119 SetTextEditWin(pWin->GetOwnerWindow());
2120 ImpMakeTextCursorAreaVisible();
2121 return true;
2125 return SdrGlueEditView::MouseButtonDown(rMEvt, pWin);
2128 bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
2130 if (mpTextEditOutlinerView != nullptr)
2132 bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
2133 if (!bPostIt)
2135 Point aPt(rMEvt.GetPosPixel());
2136 if (pWin != nullptr)
2137 aPt = pWin->PixelToLogic(aPt);
2138 else if (mpTextEditWin != nullptr)
2139 aPt = mpTextEditWin->PixelToLogic(aPt);
2140 bPostIt = IsTextEditHit(aPt);
2142 if (bPostIt && pWin)
2144 Point aPixPos(rMEvt.GetPosPixel());
2145 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2146 if (aPixPos.X() < aR.Left())
2147 aPixPos.setX(aR.Left());
2148 if (aPixPos.X() > aR.Right())
2149 aPixPos.setX(aR.Right());
2150 if (aPixPos.Y() < aR.Top())
2151 aPixPos.setY(aR.Top());
2152 if (aPixPos.Y() > aR.Bottom())
2153 aPixPos.setY(aR.Bottom());
2154 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2155 rMEvt.GetModifier());
2156 if (mpTextEditOutlinerView->MouseButtonUp(aMEvt))
2158 ImpMakeTextCursorAreaVisible();
2159 return true;
2163 return SdrGlueEditView::MouseButtonUp(rMEvt, pWin);
2166 bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
2168 if (mpTextEditOutlinerView != nullptr)
2170 bool bSelMode = mpTextEditOutliner->IsInSelectionMode();
2171 bool bPostIt = bSelMode;
2172 if (!bPostIt)
2174 Point aPt(rMEvt.GetPosPixel());
2175 if (pWin)
2176 aPt = pWin->PixelToLogic(aPt);
2177 else if (mpTextEditWin)
2178 aPt = mpTextEditWin->PixelToLogic(aPt);
2179 bPostIt = IsTextEditHit(aPt);
2181 if (bPostIt)
2183 Point aPixPos(rMEvt.GetPosPixel());
2184 tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea());
2185 if (pWin)
2186 aR = pWin->LogicToPixel(aR);
2187 else if (mpTextEditWin)
2188 aR = mpTextEditWin->LogicToPixel(aR);
2189 if (aPixPos.X() < aR.Left())
2190 aPixPos.setX(aR.Left());
2191 if (aPixPos.X() > aR.Right())
2192 aPixPos.setX(aR.Right());
2193 if (aPixPos.Y() < aR.Top())
2194 aPixPos.setY(aR.Top());
2195 if (aPixPos.Y() > aR.Bottom())
2196 aPixPos.setY(aR.Bottom());
2197 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2198 rMEvt.GetModifier());
2199 if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode)
2201 ImpMakeTextCursorAreaVisible();
2202 return true;
2206 return SdrGlueEditView::MouseMove(rMEvt, pWin);
2209 bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
2211 // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
2212 if (mpTextEditOutlinerView != nullptr)
2214 if (rCEvt.GetCommand() == CommandEventId::StartDrag)
2216 bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent();
2217 if (!bPostIt && rCEvt.IsMouseEvent())
2219 Point aPt(rCEvt.GetMousePosPixel());
2220 if (pWin != nullptr)
2221 aPt = pWin->PixelToLogic(aPt);
2222 else if (mpTextEditWin != nullptr)
2223 aPt = mpTextEditWin->PixelToLogic(aPt);
2224 bPostIt = IsTextEditHit(aPt);
2226 if (bPostIt)
2228 Point aPixPos(rCEvt.GetMousePosPixel());
2229 if (rCEvt.IsMouseEvent() && pWin)
2231 tools::Rectangle aR(
2232 pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2233 if (aPixPos.X() < aR.Left())
2234 aPixPos.setX(aR.Left());
2235 if (aPixPos.X() > aR.Right())
2236 aPixPos.setX(aR.Right());
2237 if (aPixPos.Y() < aR.Top())
2238 aPixPos.setY(aR.Top());
2239 if (aPixPos.Y() > aR.Bottom())
2240 aPixPos.setY(aR.Bottom());
2242 CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent());
2243 // Command is void at the OutlinerView, sadly
2244 mpTextEditOutlinerView->Command(aCEvt);
2245 if (pWin != nullptr && pWin != mpTextEditWin)
2246 SetTextEditWin(pWin);
2247 ImpMakeTextCursorAreaVisible();
2248 return true;
2251 else
2253 mpTextEditOutlinerView->Command(rCEvt);
2254 if (comphelper::LibreOfficeKit::isActive())
2256 // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
2257 // isn't called
2258 if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
2260 GetModel().SetChanged();
2261 SetInnerTextAreaForLOKit();
2264 return true;
2267 return SdrGlueEditView::Command(rCEvt, pWin);
2270 bool SdrObjEditView::ImpIsTextEditAllSelected() const
2272 bool bRet = false;
2273 if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr)
2275 if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get()))
2277 const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount();
2278 Paragraph* pLastPara
2279 = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0);
2281 ESelection aESel(mpTextEditOutlinerView->GetSelection());
2282 if (aESel.start.nPara == 0 && aESel.start.nIndex == 0
2283 && aESel.end.nPara == (nParaCnt - 1))
2285 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.end.nIndex)
2286 bRet = true;
2288 // in case the selection was done backwards
2289 if (!bRet && aESel.end.nPara == 0 && aESel.end.nIndex == 0
2290 && aESel.start.nPara == (nParaCnt - 1))
2292 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.start.nIndex)
2293 bRet = true;
2296 else
2298 bRet = true;
2301 return bRet;
2304 void SdrObjEditView::ImpMakeTextCursorAreaVisible()
2306 if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr)
2308 vcl::Cursor* pCsr = mpTextEditWin->GetCursor();
2309 if (pCsr != nullptr)
2311 Size aSiz(pCsr->GetSize());
2312 if (!aSiz.IsEmpty())
2314 MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin);
2320 SvtScriptType SdrObjEditView::GetScriptType() const
2322 SvtScriptType nScriptType = SvtScriptType::NONE;
2324 if (IsTextEdit())
2326 auto pText = mxWeakTextEditObj.get();
2327 if (pText->GetOutlinerParaObject())
2328 nScriptType = pText->GetOutlinerParaObject()->GetTextObject().GetScriptType();
2330 if (mpTextEditOutlinerView)
2331 nScriptType = mpTextEditOutlinerView->GetSelectedScriptType();
2333 else
2335 const SdrMarkList& rMarkList = GetMarkedObjectList();
2336 const size_t nMarkCount(rMarkList.GetMarkCount());
2338 for (size_t i = 0; i < nMarkCount; ++i)
2340 OutlinerParaObject* pParaObj
2341 = rMarkList.GetMark(i)->GetMarkedSdrObj()->GetOutlinerParaObject();
2343 if (pParaObj)
2345 nScriptType |= pParaObj->GetTextObject().GetScriptType();
2350 if (nScriptType == SvtScriptType::NONE)
2351 nScriptType = SvtScriptType::LATIN;
2353 return nScriptType;
2356 void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2358 if (mxSelectionController.is())
2359 if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr))
2360 return;
2362 if (IsTextEdit())
2364 DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2365 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2366 DBG_ASSERT(mpTextEditOutliner != nullptr,
2367 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2369 auto pText = mxWeakTextEditObj.get();
2370 // take care of bOnlyHardAttr(!)
2371 if (!bOnlyHardAttr && pText->GetStyleSheet())
2372 rTargetSet.Put(pText->GetStyleSheet()->GetItemSet());
2374 // add object attributes
2375 rTargetSet.Put(pText->GetMergedItemSet());
2377 if (mpTextEditOutlinerView)
2379 // FALSE= regard InvalidItems as "holes," not as Default
2380 rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false);
2383 const SdrMarkList& rMarkList = GetMarkedObjectList();
2384 if (rMarkList.GetMarkCount() == 1 && rMarkList.GetMark(0)->GetMarkedSdrObj() == pText.get())
2386 MergeNotPersistAttrFromMarked(rTargetSet);
2389 else
2391 SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr);
2395 bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2397 bool bRet = false;
2398 rtl::Reference<SdrTextObj> pTextEditObj = mxWeakTextEditObj.get();
2399 bool bTextEdit = mpTextEditOutlinerView != nullptr && pTextEditObj != nullptr;
2400 bool bAllTextSelected = ImpIsTextEditAllSelected();
2401 const SfxItemSet* pSet = &rSet;
2403 if (!bTextEdit)
2405 // no TextEdit active -> all Items to drawing object
2406 if (mxSelectionController.is())
2407 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2409 if (!bRet)
2411 SdrGlueEditView::SetAttributes(*pSet, bReplaceAll);
2412 bRet = true;
2415 else
2417 #ifdef DBG_UTIL
2419 bool bHasEEFeatureItems = false;
2420 SfxItemIter aIter(rSet);
2421 for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
2422 pItem = aIter.NextItem())
2424 if (!IsInvalidItem(pItem))
2426 sal_uInt16 nW = pItem->Which();
2427 if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END)
2428 bHasEEFeatureItems = true;
2432 if (bHasEEFeatureItems)
2434 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
2435 nullptr, VclMessageType::Info, VclButtonsType::Ok,
2436 u"SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
2437 "at the SdrView does not make sense! It only leads to "
2438 "overhead and unreadable documents."_ustr));
2439 xInfoBox->run();
2442 #endif
2444 bool bOnlyEEItems;
2445 bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems);
2446 // everything selected? -> attributes to the border, too
2447 // if no EEItems, attributes to the border only
2448 if (bAllTextSelected || bNoEEItems)
2450 if (mxSelectionController.is())
2451 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2453 if (!bRet)
2455 const bool bUndo = IsUndoEnabled();
2457 if (bUndo)
2459 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2460 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2462 // If this is a text object also rescue the OutlinerParaObject since
2463 // applying attributes to the object may change text layout when
2464 // multiple portions exist with multiple formats. If an OutlinerParaObject
2465 // really exists and needs to be rescued is evaluated in the undo
2466 // implementation itself.
2467 bool bRescueText(pTextEditObj);
2469 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(
2470 *pTextEditObj, false, !bNoEEItems || bRescueText));
2471 EndUndo();
2474 pTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll);
2476 FlushComeBackTimer(); // to set ModeHasChanged immediately
2479 else if (!bOnlyEEItems)
2481 // Otherwise split Set, if necessary.
2482 // Now we build an ItemSet aSet that doesn't contain EE_Items from
2483 // *pSet (otherwise it would be a copy).
2484 WhichRangesContainer pNewWhichTable
2485 = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END);
2486 SfxItemSet aSet(GetModel().GetItemPool(), std::move(pNewWhichTable));
2487 SfxWhichIter aIter(aSet);
2488 sal_uInt16 nWhich = aIter.FirstWhich();
2489 while (nWhich != 0)
2491 const SfxPoolItem* pItem;
2492 SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem);
2493 if (eState == SfxItemState::SET)
2494 aSet.Put(*pItem);
2495 nWhich = aIter.NextWhich();
2498 if (mxSelectionController.is())
2499 bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll);
2501 if (!bRet)
2503 if (IsUndoEnabled())
2505 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2506 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2507 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj));
2508 EndUndo();
2511 pTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll);
2513 const SdrMarkList& rMarkList = GetMarkedObjectList();
2514 if (rMarkList.GetMarkCount() == 1
2515 && rMarkList.GetMark(0)->GetMarkedSdrObj() == pTextEditObj.get())
2517 SetNotPersistAttrToMarked(aSet);
2520 FlushComeBackTimer();
2522 if (!bNoEEItems)
2524 // and now the attributes to the EditEngine
2525 if (bReplaceAll)
2527 mpTextEditOutlinerView->RemoveAttribs(true);
2529 mpTextEditOutlinerView->SetAttribs(rSet);
2531 Outliner* pTEOutliner = mpTextEditOutlinerView->GetOutliner();
2532 if (pTEOutliner && pTEOutliner->IsModified())
2534 GetModel().SetChanged();
2535 SetInnerTextAreaForLOKit();
2538 ImpMakeTextCursorAreaVisible();
2540 bRet = true;
2542 return bRet;
2545 SfxStyleSheet* SdrObjEditView::GetStyleSheet() const
2547 SfxStyleSheet* pSheet = nullptr;
2549 if (mxSelectionController.is())
2551 if (mxSelectionController->GetStyleSheet(pSheet))
2552 return pSheet;
2555 if (mpTextEditOutlinerView)
2557 pSheet = mpTextEditOutlinerView->GetStyleSheet();
2559 else
2561 pSheet = SdrGlueEditView::GetStyleSheet();
2563 return pSheet;
2566 void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
2568 if (mxSelectionController.is())
2570 if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr))
2571 return;
2574 // if we are currently in edit mode we must also set the stylesheet
2575 // on all paragraphs in the Outliner for the edit view
2576 if (nullptr != mpTextEditOutlinerView)
2578 Outliner* pOutliner = mpTextEditOutlinerView->GetOutliner();
2580 const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
2581 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
2583 pOutliner->SetStyleSheet(nPara, pStyleSheet);
2587 SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr);
2590 void SdrObjEditView::AddDeviceToPaintView(OutputDevice& rNewDev, vcl::Window* pWindow)
2592 SdrGlueEditView::AddDeviceToPaintView(rNewDev, pWindow);
2594 if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2595 && rNewDev.GetOutDevType() == OUTDEV_WINDOW)
2597 OutlinerView* pOutlView = ImpMakeOutlinerView(rNewDev.GetOwnerWindow(), nullptr);
2598 mpTextEditOutliner->InsertView(pOutlView);
2602 void SdrObjEditView::DeleteDeviceFromPaintView(OutputDevice& rOldDev)
2604 SdrGlueEditView::DeleteDeviceFromPaintView(rOldDev);
2606 if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2607 && rOldDev.GetOutDevType() == OUTDEV_WINDOW)
2609 for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;)
2611 i--;
2612 OutlinerView* pOLV = mpTextEditOutliner->GetView(i);
2613 if (pOLV && pOLV->GetWindow() == rOldDev.GetOwnerWindow())
2615 mpTextEditOutliner->RemoveView(i);
2620 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), &rOldDev);
2623 bool SdrObjEditView::IsTextEditInSelectionMode() const
2625 return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode();
2628 // MacroMode
2630 void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV,
2631 vcl::Window* pWin)
2633 BrkMacroObj();
2634 if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro())
2636 nTol = ImpGetHitTolLogic(nTol, nullptr);
2637 m_pMacroObj = pObj;
2638 m_pMacroPV = pPV;
2639 m_pMacroWin = pWin;
2640 mbMacroDown = false;
2641 m_nMacroTol = sal_uInt16(nTol);
2642 m_aMacroDownPos = rPnt;
2643 MovMacroObj(rPnt);
2647 void SdrObjEditView::ImpMacroUp(const Point& rUpPos)
2649 if (m_pMacroObj != nullptr && mbMacroDown)
2651 SdrObjMacroHitRec aHitRec;
2652 aHitRec.aPos = rUpPos;
2653 aHitRec.nTol = m_nMacroTol;
2654 aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2655 aHitRec.pPageView = m_pMacroPV;
2656 m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2657 mbMacroDown = false;
2661 void SdrObjEditView::ImpMacroDown(const Point& rDownPos)
2663 if (m_pMacroObj != nullptr && !mbMacroDown)
2665 SdrObjMacroHitRec aHitRec;
2666 aHitRec.aPos = rDownPos;
2667 aHitRec.nTol = m_nMacroTol;
2668 aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2669 aHitRec.pPageView = m_pMacroPV;
2670 m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2671 mbMacroDown = true;
2675 void SdrObjEditView::MovMacroObj(const Point& rPnt)
2677 if (m_pMacroObj == nullptr)
2678 return;
2680 SdrObjMacroHitRec aHitRec;
2681 aHitRec.aPos = rPnt;
2682 aHitRec.nTol = m_nMacroTol;
2683 aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2684 aHitRec.pPageView = m_pMacroPV;
2685 bool bDown = m_pMacroObj->IsMacroHit(aHitRec);
2686 if (bDown)
2687 ImpMacroDown(rPnt);
2688 else
2689 ImpMacroUp(rPnt);
2692 void SdrObjEditView::BrkMacroObj()
2694 if (m_pMacroObj != nullptr)
2696 ImpMacroUp(m_aMacroDownPos);
2697 m_pMacroObj = nullptr;
2698 m_pMacroPV = nullptr;
2699 m_pMacroWin = nullptr;
2703 bool SdrObjEditView::EndMacroObj()
2705 if (m_pMacroObj != nullptr && mbMacroDown)
2707 ImpMacroUp(m_aMacroDownPos);
2708 SdrObjMacroHitRec aHitRec;
2709 aHitRec.aPos = m_aMacroDownPos;
2710 aHitRec.nTol = m_nMacroTol;
2711 aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers();
2712 aHitRec.pPageView = m_pMacroPV;
2713 bool bRet = m_pMacroObj->DoMacro(aHitRec);
2714 m_pMacroObj = nullptr;
2715 m_pMacroPV = nullptr;
2716 m_pMacroWin = nullptr;
2717 return bRet;
2719 else
2721 BrkMacroObj();
2722 return false;
2726 /** fills the given any with a XTextCursor for the current text selection.
2727 Leaves the any untouched if there currently is no text selected */
2728 void SdrObjEditView::getTextSelection(css::uno::Any& rSelection)
2730 if (!IsTextEdit())
2731 return;
2733 OutlinerView* pOutlinerView = GetTextEditOutlinerView();
2734 if (!(pOutlinerView && pOutlinerView->HasSelection()))
2735 return;
2737 SdrObject* pObj = GetTextEditObject();
2739 if (!pObj)
2740 return;
2742 css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY);
2743 if (xText.is())
2745 SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText);
2746 if (pRange)
2748 rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection());
2753 /* check if we have a single selection and that single object likes
2754 to handle the mouse and keyboard events itself
2756 TODO: the selection controller should be queried from the
2757 object specific view contact. Currently this method only
2758 works for tables.
2760 void SdrObjEditView::MarkListHasChanged()
2762 SdrGlueEditView::MarkListHasChanged();
2764 if (mxSelectionController.is())
2766 mxLastSelectionController = mxSelectionController;
2767 mxSelectionController->onSelectionHasChanged();
2770 mxSelectionController.clear();
2772 const SdrMarkList& rMarkList = GetMarkedObjectList();
2773 if (rMarkList.GetMarkCount() != 1)
2774 return;
2776 const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
2777 SdrView* pView(dynamic_cast<SdrView*>(this));
2779 // check for table
2780 if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
2781 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2783 mxSelectionController = sdr::table::CreateTableController(
2784 *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController);
2786 if (mxSelectionController.is())
2788 mxLastSelectionController.clear();
2789 mxSelectionController->onSelectionHasChanged();
2794 IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2796 OnEndPasteOrDrop(pInfo);
2799 IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2801 OnBeginPasteOrDrop(pInfo);
2804 void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*)
2806 // applications can derive from these virtual methods to do something before a drop or paste operation
2809 void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*)
2811 // applications can derive from these virtual methods to do something before a drop or paste operation
2814 sal_uInt16 SdrObjEditView::GetSelectionLevel() const
2816 if (!IsTextEdit())
2817 return 0xFFFF;
2818 DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2819 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2820 DBG_ASSERT(mpTextEditOutliner != nullptr,
2821 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2822 if (!mpTextEditOutlinerView)
2823 return 0xFFFF;
2824 //start and end position
2825 ESelection aSelect = mpTextEditOutlinerView->GetSelection();
2826 sal_Int32 nStartPara = ::std::min(aSelect.start.nPara, aSelect.end.nPara);
2827 sal_Int32 nEndPara = ::std::max(aSelect.start.nPara, aSelect.end.nPara);
2828 //get level from each paragraph
2829 sal_uInt16 nLevel = 0;
2830 for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++)
2832 sal_Int16 nDepth = mpTextEditOutliner->GetDepth(nPara);
2833 assert(nDepth >= 0 && nDepth <= 15);
2834 sal_uInt16 nParaDepth = 1 << static_cast<sal_uInt16>(nDepth);
2835 if (!(nLevel & nParaDepth))
2836 nLevel += nParaDepth;
2838 //reduce one level for Outliner Object
2839 //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
2840 // nLevel = nLevel >> 1;
2841 //no bullet paragraph selected
2842 if (nLevel == 0)
2843 nLevel = 0xFFFF;
2844 return nLevel;
2847 bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor,
2848 SdrObjKind nObjectIdentifier)
2850 if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d)
2851 return false;
2852 switch (nObjectIdentifier)
2854 case SdrObjKind::NONE:
2855 case SdrObjKind::Group:
2856 return false;
2857 case SdrObjKind::Line:
2858 case SdrObjKind::Rectangle:
2859 case SdrObjKind::CircleOrEllipse:
2860 case SdrObjKind::CircleSection:
2861 case SdrObjKind::CircleArc:
2862 case SdrObjKind::CircleCut:
2863 case SdrObjKind::Polygon:
2864 case SdrObjKind::PolyLine:
2865 case SdrObjKind::PathLine:
2866 case SdrObjKind::PathFill:
2867 case SdrObjKind::FreehandLine:
2868 case SdrObjKind::FreehandFill:
2869 case SdrObjKind::Text:
2870 case SdrObjKind::TitleText:
2871 case SdrObjKind::OutlineText:
2872 case SdrObjKind::Graphic:
2873 case SdrObjKind::OLE2:
2874 case SdrObjKind::Table:
2875 return true;
2876 case SdrObjKind::Caption:
2877 return false;
2878 case SdrObjKind::Edge:
2879 case SdrObjKind::PathPoly:
2880 case SdrObjKind::PathPolyLine:
2881 return true;
2882 case SdrObjKind::Page:
2883 case SdrObjKind::Measure:
2884 case SdrObjKind::OLEPluginFrame:
2885 case SdrObjKind::UNO:
2886 return false;
2887 case SdrObjKind::CustomShape:
2888 return true;
2889 default:
2890 return false;
2894 static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly, bool withParagraphAttr = true)
2896 static const WhichRangesContainer gFull(
2897 svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL,
2898 SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST,
2899 SDRATTR_MISC_LAST, // table cell formats
2900 SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
2901 SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST,
2902 SDRATTR_SOFTEDGE_LAST, SDRATTR_GLOW_TEXT_FIRST, SDRATTR_GLOW_TEXT_LAST,
2903 EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>);
2905 static const WhichRangesContainer gTextOnly(
2906 svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_CHAR_START, EE_CHAR_END>);
2908 static const WhichRangesContainer gParaTextOnly(
2909 svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START,
2910 EE_CHAR_END>);
2912 return bTextOnly ? withParagraphAttr ? gParaTextOnly : gTextOnly : gFull;
2915 sal_Int32 SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet)
2917 sal_Int32 nDepth = -2;
2918 const SdrMarkList& rMarkList = GetMarkedObjectList();
2919 if (rMarkList.GetMarkCount() <= 0)
2920 return nDepth;
2922 OutlinerView* pOLV = GetTextEditOutlinerView();
2923 bool isParaSelection = pOLV ? pOLV->GetEditView().IsSelectionFullPara() : false;
2924 rFormatSet = std::make_shared<SfxItemSet>(GetModel().GetItemPool(),
2925 GetFormatRangeImpl(pOLV != nullptr, isParaSelection));
2926 if (pOLV)
2928 rFormatSet->Put(pOLV->GetAttribs());
2929 if (isParaSelection)
2930 nDepth = pOLV->GetDepth();
2932 else
2934 const bool bOnlyHardAttr = false;
2935 rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr));
2938 // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
2939 const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2940 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
2941 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2943 auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj);
2944 if (mxSelectionController.is() && pTable->getActiveCell().is())
2946 mxSelectionController->GetAttributes(*rFormatSet, false);
2949 return nDepth;
2952 static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool,
2953 const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet,
2954 bool bNoCharacterFormats, bool bNoParagraphFormats)
2956 SfxItemSet aPaintSet(rPool, pRanges);
2958 for (const auto& pRange : pRanges)
2960 sal_uInt16 nWhich = pRange.first;
2961 const sal_uInt16 nLastWhich = pRange.second;
2963 if (bNoCharacterFormats && (nWhich == EE_CHAR_START))
2964 continue;
2966 if (bNoParagraphFormats && (nWhich == EE_PARA_START))
2967 continue;
2969 for (; nWhich <= nLastWhich; nWhich++)
2971 const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich);
2972 const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich);
2974 if ((pSourceItem && !pTargetItem)
2975 || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem))
2977 aPaintSet.Put(*pSourceItem);
2981 return aPaintSet;
2984 void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj,
2985 SdrText* pText, sal_Int16 nDepth,
2986 bool bNoCharacterFormats, bool bNoParagraphFormats)
2988 OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
2989 if (!pParaObj)
2990 return;
2992 SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner();
2993 rOutliner.SetText(*pParaObj);
2995 sal_Int32 nParaCount(rOutliner.GetParagraphCount());
2997 if (!nParaCount)
2998 return;
3000 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
3002 if (!bNoCharacterFormats)
3003 rOutliner.RemoveCharAttribs(nPara);
3005 SfxItemSet aSet(rOutliner.GetParaAttribs(nPara));
3006 aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet,
3007 bNoCharacterFormats, bNoParagraphFormats));
3008 rOutliner.SetParaAttribs(nPara, aSet);
3009 Paragraph* pParagraph = rOutliner.GetParagraph(nPara);
3010 if (nDepth > -2)
3011 rOutliner.SetDepth(pParagraph, nDepth);
3014 std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
3015 rOutliner.Clear();
3017 rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
3020 void SdrObjEditView::DisposeUndoManager()
3022 if (mpTextEditOutliner)
3024 if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager))
3026 // Non-owning pointer, clear it.
3027 mpTextEditOutliner->SetUndoManager(nullptr);
3031 mpOldTextEditUndoManager = nullptr;
3034 void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, sal_Int16 nDepth,
3035 bool bNoCharacterFormats, bool bNoParagraphFormats)
3037 if (mxSelectionController.is()
3038 && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, nDepth, bNoCharacterFormats,
3039 bNoParagraphFormats))
3041 return;
3044 OutlinerView* pOLV = GetTextEditOutlinerView();
3045 const SdrMarkList& rMarkList = GetMarkedObjectList();
3046 if (!pOLV)
3048 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
3049 const SfxItemSet& rShapeSet = pObj->GetMergedItemSet();
3051 // if not in text edit mode (aka the user selected text or clicked on a word)
3052 // apply formatting attributes to selected shape
3053 // All formatting items (see ranges above) that are unequal in selected shape and
3054 // the format paintbrush are hard set on the selected shape.
3056 const WhichRangesContainer& pRanges = rFormatSet.GetRanges();
3057 bool bTextOnly = true;
3059 for (const auto& pRange : pRanges)
3061 if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START))
3063 bTextOnly = false;
3064 break;
3068 if (!bTextOnly)
3070 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(),
3071 rFormatSet, rShapeSet, bNoCharacterFormats,
3072 bNoParagraphFormats));
3073 SetAttrToMarked(aPaintSet, false /*bReplaceAll*/);
3076 // now apply character and paragraph formatting to text, if the shape has any
3077 SdrTextObj* pTextObj = DynCastSdrTextObj(pObj);
3078 if (pTextObj)
3080 sal_Int32 nText = pTextObj->getTextCount();
3082 while (--nText >= 0)
3084 SdrText* pText = pTextObj->getText(nText);
3085 ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, nDepth,
3086 bNoCharacterFormats, bNoParagraphFormats);
3090 else
3092 ::Outliner* pOutliner = pOLV->GetOutliner();
3093 if (pOutliner)
3095 const EditEngine& rEditEngine = pOutliner->GetEditEngine();
3097 ESelection aSel(pOLV->GetSelection());
3098 bool fullParaSelection
3099 = aSel.end.nPara != aSel.start.nPara || pOLV->GetEditView().IsSelectionFullPara();
3100 if (!aSel.HasRange())
3101 pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD));
3102 const bool bRemoveParaAttribs = !bNoParagraphFormats && !fullParaSelection;
3103 pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs);
3104 SfxItemSet aSet(pOLV->GetAttribs());
3105 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(),
3106 rFormatSet, aSet, bNoCharacterFormats,
3107 bNoParagraphFormats));
3108 pOLV->SetAttribs(aPaintSet);
3109 if (!bNoParagraphFormats && nDepth > -2)
3111 for (sal_Int32 nPara = aSel.start.nPara; nPara <= aSel.end.nPara; ++nPara)
3112 pOLV->SetDepth(nPara, nDepth);
3117 // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
3118 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
3119 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
3120 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
3122 auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj);
3123 if (pTable->getActiveCell().is() && mxSelectionController.is())
3125 mxSelectionController->SetAttributes(rFormatSet, false);
3130 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */