Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / svdraw / svdedxv.cxx
blobae19c26496f357f97d8dbfccfe8670c3b605c3d7
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 <svl/itemiter.hxx>
29 #include <svl/style.hxx>
30 #include <svl/whiter.hxx>
31 #include <svtools/accessibilityoptions.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>
72 #include <memory>
74 SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
75 : SdrGlueEditView(rSdrModel, pOut)
76 , mpTextEditPV(nullptr)
77 , mpTextEditOutlinerView(nullptr)
78 , mpTextEditWin(nullptr)
79 , pTextEditCursorBuffer(nullptr)
80 , pMacroObj(nullptr)
81 , pMacroPV(nullptr)
82 , pMacroWin(nullptr)
83 , nMacroTol(0)
84 , mbTextEditDontDelete(false)
85 , mbTextEditOnlyOneView(false)
86 , mbTextEditNewObj(false)
87 , mbQuickTextEditMode(true)
88 , mbMacroDown(false)
89 , mpOldTextEditUndoManager(nullptr)
93 SdrObjEditView::~SdrObjEditView()
95 mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
96 assert(!IsTextEdit());
97 if (IsTextEdit())
98 suppress_fun_call_w_exception(SdrEndTextEdit());
99 mpTextEditOutliner.reset();
100 assert(nullptr == mpOldTextEditUndoManager); // should have been reset
103 bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }
105 void SdrObjEditView::MovAction(const Point& rPnt)
107 if (IsMacroObj())
108 MovMacroObj(rPnt);
109 SdrGlueEditView::MovAction(rPnt);
112 void SdrObjEditView::EndAction()
114 if (IsMacroObj())
115 EndMacroObj();
116 SdrGlueEditView::EndAction();
119 void SdrObjEditView::BckAction()
121 BrkMacroObj();
122 SdrGlueEditView::BckAction();
125 void SdrObjEditView::BrkAction()
127 BrkMacroObj();
128 SdrGlueEditView::BrkAction();
131 SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
133 SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);
135 if (comphelper::LibreOfficeKit::isActive() && pPageView)
137 // Check if other views have an active text edit on the same page as
138 // this one.
139 SdrViewIter aIter(pPageView->GetPage());
140 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
142 if (pView == this || !pView->IsTextEdit())
143 continue;
145 OutputDevice* pOutDev = GetFirstOutputDevice();
146 if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
147 continue;
149 // Found one, so create an outliner view, to get invalidations when
150 // the text edit changes.
151 // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
152 // registers the view shell of this draw view, and not the view
153 // shell of pView.
154 OutlinerView* pOutlinerView
155 = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
156 pOutlinerView->HideCursor();
157 pView->GetTextEditOutliner()->InsertView(pOutlinerView);
161 return pPageView;
164 namespace
166 /// Removes outliner views registered in other draw views that use pOutputDevice.
167 void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
168 OutputDevice const* pOutputDevice)
170 if (!comphelper::LibreOfficeKit::isActive())
171 return;
173 if (!pPageView)
174 return;
176 if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
177 return;
179 SdrViewIter aIter(pPageView->GetPage());
180 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
182 if (pView == pThis || !pView->IsTextEdit())
183 continue;
185 SdrOutliner* pOutliner = pView->GetTextEditOutliner();
186 for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
188 OutlinerView* pOutlinerView = pOutliner->GetView(nView);
189 if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
190 continue;
192 pOutliner->RemoveView(pOutlinerView);
193 delete pOutlinerView;
199 void SdrObjEditView::HideSdrPage()
201 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
203 if (mpTextEditPV == GetSdrPageView())
205 // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
206 mpTextEditPV = nullptr;
209 SdrGlueEditView::HideSdrPage();
212 void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
214 if (IsMacroObj())
216 rRect = pMacroObj->GetCurrentBoundRect();
218 else
220 SdrGlueEditView::TakeActionRect(rRect);
224 void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
226 SdrGlueEditView::Notify(rBC, rHint);
227 if (mpTextEditOutliner == nullptr)
228 return;
230 // change of printer while editing
231 if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
232 return;
234 const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
235 SdrHintKind eKind = pSdrHint->GetKind();
236 if (eKind == SdrHintKind::RefDeviceChange)
238 mpTextEditOutliner->SetRefDevice(GetModel().GetRefDevice());
240 if (eKind == SdrHintKind::DefaultTabChange)
242 mpTextEditOutliner->SetDefTab(GetModel().GetDefaultTabulator());
246 void SdrObjEditView::ModelHasChanged()
248 SdrGlueEditView::ModelHasChanged();
249 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
250 if (pTextObj && !pTextObj->IsInserted())
251 SdrEndTextEdit(); // object deleted
252 // TextEditObj changed?
253 if (!IsTextEdit())
254 return;
256 if (pTextObj != nullptr)
258 size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
259 bool bAreaChg = false;
260 bool bAnchorChg = false;
261 bool bColorChg = false;
262 bool bContourFrame = pTextObj->IsContourTextFrame();
263 EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
264 tools::Rectangle aOldArea(aMinTextEditArea);
265 aOldArea.Union(aTextEditArea);
266 Color aNewColor;
267 { // check area
268 Size aPaperMin1;
269 Size aPaperMax1;
270 tools::Rectangle aEditArea1;
271 tools::Rectangle aMinArea1;
272 pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
273 Point aPvOfs(pTextObj->GetTextEditOffset());
275 // add possible GridOffset to up-to-now view-independent EditAreas
276 basegfx::B2DVector aGridOffset(0.0, 0.0);
277 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), GetSdrPageView()))
279 const Point aOffset(basegfx::fround(aGridOffset.getX()),
280 basegfx::fround(aGridOffset.getY()));
282 aEditArea1 += aOffset;
283 aMinArea1 += aOffset;
286 aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
287 aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
288 tools::Rectangle aNewArea(aMinArea1);
289 aNewArea.Union(aEditArea1);
291 if (aNewArea != aOldArea || aEditArea1 != aTextEditArea || aMinArea1 != aMinTextEditArea
292 || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
293 || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
295 aTextEditArea = aEditArea1;
296 aMinTextEditArea = aMinArea1;
298 const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
299 mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
300 mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
301 mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner
303 if (!bContourFrame)
305 mpTextEditOutliner->ClearPolygon();
306 EEControlBits nStat = mpTextEditOutliner->GetControlWord();
307 nStat |= EEControlBits::AUTOPAGESIZE;
308 mpTextEditOutliner->SetControlWord(nStat);
310 else
312 EEControlBits nStat = mpTextEditOutliner->GetControlWord();
313 nStat &= ~EEControlBits::AUTOPAGESIZE;
314 mpTextEditOutliner->SetControlWord(nStat);
315 tools::Rectangle aAnchorRect;
316 pTextObj->TakeTextAnchorRect(aAnchorRect);
317 pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
319 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
321 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
322 EVControlBits nStat0 = pOLV->GetControlWord();
323 EVControlBits nStat = nStat0;
324 // AutoViewSize only if not ContourFrame.
325 if (!bContourFrame)
326 nStat |= EVControlBits::AUTOSIZE;
327 else
328 nStat &= ~EVControlBits::AUTOSIZE;
329 if (nStat != nStat0)
330 pOLV->SetControlWord(nStat);
333 mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
334 bAreaChg = true;
337 if (mpTextEditOutlinerView != nullptr)
338 { // check fill and anchor
339 EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
340 eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
341 bAnchorChg = eOldAnchor != eNewAnchor;
342 Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
343 aNewColor = GetTextEditBackgroundColor(*this);
344 bColorChg = aOldColor != aNewColor;
346 // refresh always when it's a contour frame. That
347 // refresh is necessary since it triggers the repaint
348 // which makes the Handles visible. Changes at TakeTextRect()
349 // seem to have resulted in a case where no refresh is executed.
350 // Before that, a refresh must have been always executed
351 // (else this error would have happened earlier), thus I
352 // even think here a refresh should be done always.
353 // Since follow-up problems cannot even be guessed I only
354 // add this one more case to the if below.
355 // BTW: It's VERY bad style that here, inside ModelHasChanged()
356 // the outliner is again massively changed for the text object
357 // in text edit mode. Normally, all necessary data should be
358 // set at SdrBeginTextEdit(). Some changes and value assigns in
359 // SdrBeginTextEdit() are completely useless since they are set here
360 // again on ModelHasChanged().
361 if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
363 for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
365 OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
366 { // invalidate old OutlinerView area
367 vcl::Window* pWin = pOLV->GetWindow();
368 tools::Rectangle aTmpRect(aOldArea);
369 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
370 Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
371 aTmpRect.AdjustLeft(-(aMore.Width()));
372 aTmpRect.AdjustRight(aMore.Width());
373 aTmpRect.AdjustTop(-(aMore.Height()));
374 aTmpRect.AdjustBottom(aMore.Height());
375 InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
377 if (bAnchorChg)
378 pOLV->SetAnchorMode(eNewAnchor);
379 if (bColorChg)
380 pOLV->SetBackgroundColor(aNewColor);
382 pOLV->SetOutputArea(
383 aTextEditArea); // because otherwise, we're not re-anchoring correctly
384 ImpInvalidateOutlinerView(*pOLV);
386 mpTextEditOutlinerView->ShowCursor();
389 ImpMakeTextCursorAreaVisible();
392 namespace
394 class TextEditFrameOverlayObject;
395 class TextEditHighContrastOverlaySelection;
398 Helper class to visualize the content of an active EditView as an
399 OverlayObject. These objects work with Primitives and are handled
400 from the OverlayManager(s) in place as needed.
402 It allows complete visualization of the content of the active
403 EditView without the need of Invalidates triggered by the EditView
404 and thus avoiding potentially expensive repaints by using the
405 automatically buffered Overlay mechanism.
407 It buffers as much as possible locally and *only* triggers a real
408 change (see call to objectChange()) when really needed.
410 class TextEditOverlayObject : public sdr::overlay::OverlayObject
412 protected:
413 /// local access to associated sdr::overlay::OverlaySelection
414 std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection;
415 std::unique_ptr<TextEditHighContrastOverlaySelection> mxOverlayHighContrastSelection;
416 std::unique_ptr<TextEditFrameOverlayObject> mxOverlayFrame;
418 /// local definition depends on active OutlinerView
419 OutlinerView& mrOutlinerView;
421 /// geometry definitions with buffering
422 basegfx::B2DRange maLastRange;
423 basegfx::B2DRange maRange;
425 /// text content definitions with buffering
426 drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
427 drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
429 // geometry creation for OverlayObject, can use local *Last* values
430 virtual drawinglayer::primitive2d::Primitive2DContainer
431 createOverlayObjectPrimitive2DSequence() override;
433 public:
434 TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView);
435 virtual ~TextEditOverlayObject() override;
437 sdr::overlay::OverlayObject* getOverlaySelection();
438 sdr::overlay::OverlayObject* getOverlayFrame();
440 const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
442 /// override to check conditions for last createOverlayObjectPrimitive2DSequence
443 virtual drawinglayer::primitive2d::Primitive2DContainer
444 getOverlayObjectPrimitive2DSequence() const override;
446 // data write access. In this OverlayObject we only have the
447 // callback that triggers detecting if something *has* changed
448 void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
449 void checkSelectionChange();
451 const basegfx::B2DRange& getRange() const { return maRange; }
452 const drawinglayer::primitive2d::Primitive2DContainer& getTextPrimitives() const
454 return maTextPrimitives;
458 class TextEditFrameOverlayObject : public sdr::overlay::OverlayObject
460 private:
461 const TextEditOverlayObject& mrTextEditOverlayObject;
463 // geometry creation for OverlayObject, can use local *Last* values
464 virtual drawinglayer::primitive2d::Primitive2DContainer
465 createOverlayObjectPrimitive2DSequence() override;
467 public:
468 TextEditFrameOverlayObject(const TextEditOverlayObject& rTextEditOverlayObject);
469 using sdr::overlay::OverlayObject::objectChange;
470 virtual ~TextEditFrameOverlayObject() override;
473 class TextEditHighContrastOverlaySelection : public sdr::overlay::OverlayObject
475 private:
476 const TextEditOverlayObject& mrTextEditOverlayObject;
477 std::vector<basegfx::B2DRange> maRanges;
479 // geometry creation for OverlayObject, can use local *Last* values
480 virtual drawinglayer::primitive2d::Primitive2DContainer
481 createOverlayObjectPrimitive2DSequence() override;
483 public:
484 TextEditHighContrastOverlaySelection(const TextEditOverlayObject& rTextEditOverlayObject);
485 void setRanges(std::vector<basegfx::B2DRange>&& rNew);
486 virtual ~TextEditHighContrastOverlaySelection() override;
489 TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection(
490 const TextEditOverlayObject& rTextEditOverlayObject)
491 : OverlayObject(rTextEditOverlayObject.getBaseColor())
492 , mrTextEditOverlayObject(rTextEditOverlayObject)
494 allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
495 // use selection colors in HighContrast mode
496 mbHighContrastSelection = true;
499 void TextEditHighContrastOverlaySelection::setRanges(std::vector<basegfx::B2DRange>&& rNew)
501 if (rNew != maRanges)
503 maRanges = std::move(rNew);
504 objectChange();
508 drawinglayer::primitive2d::Primitive2DContainer
509 TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
511 drawinglayer::primitive2d::Primitive2DContainer aRetval;
513 size_t nCount = maRanges.size();
515 if (nCount)
517 basegfx::B2DPolyPolygon aClipPolyPolygon;
519 basegfx::BColor aRGBColor(getBaseColor().getBColor());
521 for (size_t a = 0; a < nCount; ++a)
522 aClipPolyPolygon.append(basegfx::utils::createPolygonFromRect(maRanges[a]));
524 // This is used in high contrast mode, we will render the selection
525 // with the bg forced to the selection Highlight color and the fg color
526 // forced to the HighlightText color
527 aRetval.append(drawinglayer::primitive2d::Primitive2DReference(
528 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
529 basegfx::B2DPolyPolygon(
530 basegfx::utils::createPolygonFromRect(aClipPolyPolygon.getB2DRange())),
531 aRGBColor)));
532 aRetval.append(mrTextEditOverlayObject.getTextPrimitives());
533 aRetval.append(drawinglayer::primitive2d::Primitive2DReference(
534 new drawinglayer::primitive2d::MaskPrimitive2D(aClipPolyPolygon, std::move(aRetval))));
537 return aRetval;
540 TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection()
542 if (getOverlayManager())
544 getOverlayManager()->remove(*this);
548 sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlaySelection()
550 if (mxOverlayTransparentSelection)
551 return mxOverlayTransparentSelection.get();
552 return mxOverlayHighContrastSelection.get();
555 sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlayFrame()
557 if (!mxOverlayFrame)
558 mxOverlayFrame.reset(new TextEditFrameOverlayObject(*this));
559 return mxOverlayFrame.get();
562 drawinglayer::primitive2d::Primitive2DContainer
563 TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
565 drawinglayer::primitive2d::Primitive2DContainer aRetval;
567 // add buffered TextPrimitives
568 aRetval.append(maTextPrimitives);
570 return aRetval;
573 drawinglayer::primitive2d::Primitive2DContainer
574 TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence()
576 drawinglayer::primitive2d::Primitive2DContainer aRetval;
578 /// outer frame visualization
579 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
580 const sal_uInt16 nPixSiz(mrTextEditOverlayObject.getOutlinerView().GetInvalidateMore() - 1);
582 aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
583 mrTextEditOverlayObject.getRange(), getBaseColor().getBColor(), fTransparence,
584 std::max(6, nPixSiz - 2), // grow
585 0.0, // shrink
586 0.0));
588 return aRetval;
591 TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView)
592 : OverlayObject(rColor)
593 , mrOutlinerView(rOutlinerView)
595 // no AA for TextEdit overlay
596 allowAntiAliase(false);
598 // create local OverlaySelection - this is an integral part of EditText
599 // visualization
600 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
602 mxOverlayHighContrastSelection.reset(new TextEditHighContrastOverlaySelection(*this));
604 else
606 std::vector<basegfx::B2DRange> aEmptySelection{};
607 mxOverlayTransparentSelection.reset(new sdr::overlay::OverlaySelection(
608 sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
612 TextEditOverlayObject::~TextEditOverlayObject()
614 mxOverlayTransparentSelection.reset();
615 mxOverlayHighContrastSelection.reset();
617 if (getOverlayManager())
619 getOverlayManager()->remove(*this);
623 TextEditFrameOverlayObject::TextEditFrameOverlayObject(
624 const TextEditOverlayObject& rTextEditOverlayObject)
625 : OverlayObject(rTextEditOverlayObject.getBaseColor())
626 , mrTextEditOverlayObject(rTextEditOverlayObject)
628 allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase());
629 // use selection colors in HighContrast mode
630 mbHighContrastSelection = true;
633 TextEditFrameOverlayObject::~TextEditFrameOverlayObject()
635 if (getOverlayManager())
637 getOverlayManager()->remove(*this);
641 drawinglayer::primitive2d::Primitive2DContainer
642 TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
644 if (!getPrimitive2DSequence().empty())
646 if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
648 // conditions of last local decomposition have changed, delete to force new evaluation
649 const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
653 if (getPrimitive2DSequence().empty())
655 // remember new buffered values
656 const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
657 const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
660 // call base implementation
661 return OverlayObject::getOverlayObjectPrimitive2DSequence();
664 void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
666 bool bObjectChange(false);
668 // check current range
669 const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
670 basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
671 aNewRange.expand(rMinTextEditArea);
673 if (aNewRange != maRange)
675 maRange = aNewRange;
676 bObjectChange = true;
679 // check if text primitives did change
680 SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner());
682 if (pSdrOutliner)
684 // get TextPrimitives directly from active Outliner
685 basegfx::B2DHomMatrix aNewTransformA;
686 basegfx::B2DHomMatrix aNewTransformB;
687 basegfx::B2DRange aClipRange;
688 drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
690 // active Outliner is always in unified oriented coordinate system (currently)
691 // so just translate to TopLeft of visible Range. Keep in mind that top-left
692 // depends on vertical text and top-to-bottom text attributes
693 const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
694 const bool bVerticalWriting(pSdrOutliner->IsVertical());
695 const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
696 const double fStartInX(bVerticalWriting && bTopToBottom
697 ? aOutArea.Right() - aVisArea.Left()
698 : aOutArea.Left() - aVisArea.Left());
699 const double fStartInY(bVerticalWriting && !bTopToBottom
700 ? aOutArea.Bottom() - aVisArea.Top()
701 : aOutArea.Top() - aVisArea.Top());
703 aNewTransformB.translate(fStartInX, fStartInY);
705 // get the current TextPrimitives. This is the most expensive part
706 // of this mechanism, it *may* be possible to buffer layouted
707 // primitives per ParaPortion with/in/dependent on the EditEngine
708 // content if needed. For now, get and compare
709 SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
710 aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange);
712 if (aNewTextPrimitives != maTextPrimitives)
714 maTextPrimitives = std::move(aNewTextPrimitives);
715 bObjectChange = true;
719 if (bObjectChange)
721 // if there really *was* a change signal the OverlayManager to
722 // refresh this object's visualization
723 objectChange();
725 if (mxOverlayFrame)
726 mxOverlayFrame->objectChange();
728 // on data change, always do a SelectionChange, too
729 // since the selection is an integral part of text visualization
730 checkSelectionChange();
734 void TextEditOverlayObject::checkSelectionChange()
736 if (!(getOverlaySelection() && getOverlayManager()))
737 return;
739 std::vector<tools::Rectangle> aLogicRects;
740 std::vector<basegfx::B2DRange> aLogicRanges;
741 const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
743 // get logic selection
744 getOutlinerView().GetSelectionRectangles(aLogicRects);
746 aLogicRanges.reserve(aLogicRects.size());
747 for (const auto& aRect : aLogicRects)
749 // convert from logic Rectangles to logic Ranges, do not forget to add
750 // one Unit (in this case logical units for one pixel, pre-calculated)
751 aLogicRanges.emplace_back(
752 aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
753 aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
756 if (mxOverlayTransparentSelection)
757 mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges));
758 else
759 mxOverlayHighContrastSelection->setRanges(std::move(aLogicRanges));
761 } // end of anonymous namespace
763 // TextEdit
765 // callback from the active EditView, forward to evtl. existing instances of the
766 // TextEditOverlayObject(s). This will additionally update the selection which
767 // is an integral part of the text visualization
768 void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
770 if (!IsTextEdit())
771 return;
773 // MinTextRange may have changed. Forward it, too
774 const basegfx::B2DRange aMinTextRange
775 = vcl::unotools::b2DRectangleFromRectangle(aMinTextEditArea);
777 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
779 TextEditOverlayObject* pCandidate
780 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
782 if (pCandidate)
784 pCandidate->checkDataChange(aMinTextRange);
789 // callback from the active EditView, forward to evtl. existing instances of the
790 // TextEditOverlayObject(s). This cvall *only* updates the selection visualization
791 // which is e.g. used when only the selection is changed, but not the text
792 void SdrObjEditView::EditViewSelectionChange()
794 if (!IsTextEdit())
795 return;
797 for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
799 TextEditOverlayObject* pCandidate
800 = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
802 if (pCandidate)
804 pCandidate->checkSelectionChange();
809 OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }
811 Point SdrObjEditView::EditViewPointerPosPixel() const
813 return mpTextEditWin->GetPointerPosPixel();
816 css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
818 if (!mpTextEditWin)
819 return nullptr;
820 return mpTextEditWin->GetClipboard();
823 css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
825 if (!mpTextEditWin)
826 return nullptr;
827 return mpTextEditWin->GetDropTarget();
830 void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
832 if (!mpTextEditWin)
833 return;
834 mpTextEditWin->SetInputContext(rInputContext);
837 void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
839 if (!mpTextEditWin)
840 return;
841 mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
844 void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
846 if (!comphelper::LibreOfficeKit::isActive())
848 // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
849 // to update accordingly (will update selection, too). Suppress new
850 // stuff when LibreOfficeKit is active
851 EditViewInvalidate(tools::Rectangle());
853 else
855 // draw old text edit stuff
856 if (IsTextEdit())
858 const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
860 if (pActiveOutliner)
862 const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
864 if (nViewCount)
866 const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
867 const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
869 for (sal_uInt32 i(0); i < nViewCount; i++)
871 OutlinerView* pOLV = pActiveOutliner->GetView(i);
873 // If rPaintWindow knows that the output device is a render
874 // context and is aware of the underlying vcl::Window,
875 // compare against that; that's how double-buffering can
876 // still find the matching OutlinerView.
877 OutputDevice* pOutputDevice = rPaintWindow.GetWindow()
878 ? rPaintWindow.GetWindow()->GetOutDev()
879 : &rPaintWindow.GetOutputDevice();
880 if (pOLV->GetWindow()->GetOutDev() == pOutputDevice
881 || comphelper::LibreOfficeKit::isActive())
883 ImpPaintOutlinerView(*pOLV, aCheckRect,
884 rPaintWindow.GetTargetOutputDevice());
885 return;
894 void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
895 OutputDevice& rTargetDevice) const
897 const SdrTextObj* pText = GetTextEditObject();
898 bool bTextFrame(pText && pText->IsTextFrame());
899 bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
900 bool bModified(mpTextEditOutliner->IsModified());
901 tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
902 aBlankRect.Union(aMinTextEditArea);
903 tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));
905 // in the tiled rendering case, the setup is incomplete, and we very
906 // easily get an empty rRect on input - that will cause that everything is
907 // clipped; happens in case of editing text inside a shape in Calc.
908 // FIXME would be better to complete the setup so that we don't get an
909 // empty rRect here
910 if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
911 aBlankRect.Intersection(rRect);
913 rOutlView.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
914 rOutlView.Paint(aBlankRect, &rTargetDevice);
916 if (!bModified)
918 mpTextEditOutliner->ClearModifyFlag();
921 if (bTextFrame && !bFitToSize)
923 // completely reworked to use primitives; this ensures same look and functionality
924 const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
925 std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
926 drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
927 aViewInformation2D));
929 const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
930 const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
931 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
932 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
933 const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
934 const drawinglayer::primitive2d::Primitive2DReference xReference(
935 new drawinglayer::primitive2d::OverlayRectanglePrimitive(
936 aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
937 0.0, // shrink
938 0.0));
939 const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };
941 rTargetDevice.EnableMapMode(false);
942 xProcessor->process(aSequence);
943 rTargetDevice.EnableMapMode(bMapModeEnabled);
946 rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
949 void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
951 vcl::Window* pWin = rOutlView.GetWindow();
953 if (!pWin)
954 return;
956 const SdrTextObj* pText = GetTextEditObject();
957 bool bTextFrame(pText && pText->IsTextFrame());
958 bool bFitToSize(pText && pText->IsFitToSize());
960 if (!bTextFrame || bFitToSize)
961 return;
963 tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
964 aBlankRect.Union(aMinTextEditArea);
965 tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
966 sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
968 aPixRect.AdjustLeft(-1);
969 aPixRect.AdjustTop(-1);
970 aPixRect.AdjustRight(1);
971 aPixRect.AdjustBottom(1);
974 // limit xPixRect because of driver problems when pixel coordinates are too far out
975 Size aMaxXY(pWin->GetOutputSizePixel());
976 tools::Long a(2 * nPixSiz);
977 tools::Long nMaxX(aMaxXY.Width() + a);
978 tools::Long nMaxY(aMaxXY.Height() + a);
980 if (aPixRect.Left() < -a)
981 aPixRect.SetLeft(-a);
982 if (aPixRect.Top() < -a)
983 aPixRect.SetTop(-a);
984 if (aPixRect.Right() > nMaxX)
985 aPixRect.SetRight(nMaxX);
986 if (aPixRect.Bottom() > nMaxY)
987 aPixRect.SetBottom(nMaxY);
990 tools::Rectangle aOuterPix(aPixRect);
991 aOuterPix.AdjustLeft(-nPixSiz);
992 aOuterPix.AdjustTop(-nPixSiz);
993 aOuterPix.AdjustRight(nPixSiz);
994 aOuterPix.AdjustBottom(nPixSiz);
996 bool bMapModeEnabled(pWin->IsMapModeEnabled());
997 pWin->EnableMapMode(false);
998 pWin->Invalidate(aOuterPix);
999 pWin->EnableMapMode(bMapModeEnabled);
1002 OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
1003 SfxViewShell* pViewShell) const
1005 // background
1006 Color aBackground(GetTextEditBackgroundColor(*this));
1007 rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get();
1008 bool bTextFrame = pText != nullptr && pText->IsTextFrame();
1009 bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
1010 // create OutlinerView
1011 OutlinerView* pOutlView = pGivenView;
1012 mpTextEditOutliner->SetUpdateLayout(false);
1014 if (pOutlView == nullptr)
1016 pOutlView = new OutlinerView(mpTextEditOutliner.get(), pWin);
1018 else
1020 pOutlView->SetWindow(pWin);
1023 if (mbNegativeX)
1024 pOutlView->GetEditView().SetNegativeX(mbNegativeX);
1026 // disallow scrolling
1027 EVControlBits nStat = pOutlView->GetControlWord();
1028 nStat &= ~EVControlBits::AUTOSCROLL;
1029 // AutoViewSize only if not ContourFrame.
1030 if (!bContourFrame)
1031 nStat |= EVControlBits::AUTOSIZE;
1032 if (bTextFrame)
1034 sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
1035 nStat |= EVControlBits::INVONEMORE;
1036 pOutlView->SetInvalidateMore(nPixSiz);
1038 pOutlView->SetControlWord(nStat);
1039 pOutlView->SetBackgroundColor(aBackground);
1041 // In case we're in the process of constructing a new view shell,
1042 // SfxViewShell::Current() may still point to the old one. So if possible,
1043 // depend on the application owning this draw view to provide the view
1044 // shell.
1045 SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
1046 pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());
1048 if (pText != nullptr)
1050 pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
1051 mpTextEditOutliner->SetFixedCellHeight(
1052 pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1054 // do update before setting output area so that aTextEditArea can be recalculated
1055 mpTextEditOutliner->SetUpdateLayout(true);
1056 pOutlView->SetOutputArea(aTextEditArea);
1057 ImpInvalidateOutlinerView(*pOutlView);
1058 return pOutlView;
1061 IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
1063 if (mpTextEditOutliner)
1065 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1066 if (pTextObj)
1068 pTextObj->onEditOutlinerStatusEvent(&rEditStat);
1073 void SdrObjEditView::ImpChainingEventHdl()
1075 if (!mpTextEditOutliner)
1076 return;
1078 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1079 OutlinerView* pOLV = GetTextEditOutlinerView();
1080 if (pTextObj && pOLV)
1082 TextChain* pTextChain = pTextObj->GetTextChain();
1084 // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
1085 if (!pTextObj->IsChainable())
1087 return;
1089 // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
1090 if (pTextChain->GetNilChainingEvent(pTextObj.get()))
1092 return;
1095 // We prevent to trigger further handling of overflow/underflow for pTextObj
1096 pTextChain->SetNilChainingEvent(pTextObj.get(), true); // XXX
1098 // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
1099 pTextChain->SetPreChainingSel(pTextObj.get(), pOLV->GetSelection());
1100 //maPreChainingSel = new ESelection(pOLV->GetSelection());
1102 // Handling Undo
1103 const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
1105 const bool bUndoEnabled = IsUndoEnabled();
1106 std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1107 if (bUndoEnabled)
1108 pTxtUndo.reset(
1109 dynamic_cast<SdrUndoObjSetText*>(GetModel()
1110 .GetSdrUndoFactory()
1111 .CreateUndoObjectSetText(*pTextObj, nText)
1112 .release()));
1114 // trigger actual chaining
1115 pTextObj->onChainingEvent();
1117 if (pTxtUndo)
1119 pTxtUndo->AfterSetText();
1120 if (!pTxtUndo->IsDifferent())
1122 pTxtUndo.reset();
1126 if (pTxtUndo)
1127 AddUndo(std::move(pTxtUndo));
1129 //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
1130 //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
1132 // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
1133 pTextChain->SetNilChainingEvent(pTextObj.get(), false);
1135 else
1137 // XXX
1138 SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
1142 IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void)
1144 SdrTextObj* pTextObj = GetTextEditObject();
1145 if (!pTextObj)
1146 return;
1147 ImpChainingEventHdl();
1148 TextChainCursorManager aCursorManager(this, pTextObj);
1149 ImpMoveCursorAfterChainingEvent(&aCursorManager);
1152 void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
1154 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1156 if (!pTextObj || !pCursorManager)
1157 return;
1159 // Check if it has links to move it to
1160 if (!pTextObj || !pTextObj->IsChainable())
1161 return;
1163 TextChain* pTextChain = pTextObj->GetTextChain();
1164 ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj.get());
1166 pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj.get()),
1167 aNewSel);
1169 // Reset event
1170 pTextChain->SetCursorEvent(pTextObj.get(), CursorChainingEvent::NULL_EVENT);
1173 IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
1175 bool bOk = false;
1176 OUString& rStr = pFI->GetRepresentation();
1177 rStr.clear();
1178 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1179 if (pTextObj != nullptr)
1181 std::optional<Color> pTxtCol;
1182 std::optional<Color> pFldCol;
1183 std::optional<FontLineStyle> pFldLineStyle;
1184 bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
1185 pTxtCol, pFldCol, pFldLineStyle, rStr);
1186 if (bOk)
1188 if (pTxtCol)
1190 pFI->SetTextColor(*pTxtCol);
1192 if (pFldLineStyle)
1194 pFI->SetFontLineStyle(*pFldLineStyle);
1196 if (pFldCol)
1198 pFI->SetFieldColor(*pFldCol);
1200 else
1202 pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
1206 Outliner& rDrawOutl = GetModel().GetDrawOutliner(pTextObj.get());
1207 Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
1208 if (!bOk && aDrawOutlLink.IsSet())
1210 aDrawOutlLink.Call(pFI);
1211 bOk = !rStr.isEmpty();
1213 if (!bOk)
1215 aOldCalcFieldValueLink.Call(pFI);
1219 IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }
1221 // Default implementation - null UndoManager
1222 std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
1224 SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
1225 return std::unique_ptr<SdrUndoManager>();
1228 bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
1229 bool bIsNewObj, SdrOutliner* pGivenOutliner,
1230 OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
1231 bool bOnlyOneView, bool bGrabFocus)
1233 // FIXME cannot be an assert() yet, the code is not ready for that;
1234 // eg. press F7 in Impress when you are inside a text object with spelling
1235 // mistakes => boom; and it is unclear how to avoid that
1236 SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
1237 // FIXME this encourages all sorts of bad habits and should be removed
1238 SdrEndTextEdit();
1240 SdrTextObj* pObj = DynCastSdrTextObj(pObj_);
1241 if (!pObj)
1242 return false; // currently only possible with text objects
1244 if (bGrabFocus && pWin)
1246 // attention, this call may cause an EndTextEdit() call to this view
1247 pWin->GrabFocus(); // to force the cursor into the edit view
1250 mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
1251 mbTextEditOnlyOneView = bOnlyOneView;
1252 mbTextEditNewObj = bIsNewObj;
1253 const sal_uInt32 nWinCount(PaintWindowCount());
1255 bool bBrk(false);
1257 if (!pWin)
1259 for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
1261 SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1263 if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
1265 pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
1269 // break, when no window exists
1270 if (!pWin)
1272 bBrk = true;
1276 if (!bBrk && !pPV)
1278 pPV = GetSdrPageView();
1280 // break, when no PageView for the object exists
1281 if (!pPV)
1283 bBrk = true;
1287 // no TextEdit on objects in locked Layer
1288 if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
1290 bBrk = true;
1293 if (mpTextEditOutliner)
1295 OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
1296 mpTextEditOutliner.reset();
1299 if (!bBrk)
1301 mpTextEditWin = pWin;
1302 mpTextEditPV = pPV;
1303 mxWeakTextEditObj = pObj;
1304 if (pGivenOutliner)
1306 mpTextEditOutliner.reset(pGivenOutliner);
1307 pGivenOutliner = nullptr; // so we don't delete it on the error path
1309 else
1310 mpTextEditOutliner
1311 = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject());
1314 mpTextEditOutliner->ForceAutoColor(SvtAccessibilityOptions::GetIsAutomaticFontColor());
1317 aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
1318 // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
1319 mpTextEditOutliner->SetCalcFieldValueHdl(
1320 LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
1321 mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
1322 mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));
1324 // It is just necessary to make the visualized page known. Set it.
1325 mpTextEditOutliner->setVisualizedPage(pPV->GetPage());
1327 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1328 mpTextEditOutliner->SetTextObjNoInit(pTextObj.get());
1330 if (pTextObj->BegTextEdit(*mpTextEditOutliner))
1332 // switch off any running TextAnimations
1333 pTextObj->SetTextAnimationAllowed(false);
1335 // remember old cursor
1336 if (mpTextEditOutliner->GetViewCount() != 0)
1338 mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
1341 // Determine EditArea via TakeTextEditArea.
1342 // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
1343 // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
1344 pTextObj->TakeTextEditArea(nullptr, nullptr, &aTextEditArea, &aMinTextEditArea);
1346 tools::Rectangle aTextRect;
1347 tools::Rectangle aAnchorRect;
1348 pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
1349 &aAnchorRect /* Give true here, not false */);
1351 if (!pTextObj->IsContourTextFrame())
1353 // FitToSize not together with ContourFrame, for now
1354 if (pTextObj->IsFitToSize())
1355 aTextRect = aAnchorRect;
1358 aTextEditArea = aTextRect;
1360 // add possible GridOffset to up-to-now view-independent EditAreas
1361 basegfx::B2DVector aGridOffset(0.0, 0.0);
1362 if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), pPV))
1364 const Point aOffset(basegfx::fround(aGridOffset.getX()),
1365 basegfx::fround(aGridOffset.getY()));
1367 aTextEditArea += aOffset;
1368 aMinTextEditArea += aOffset;
1371 Point aPvOfs(pTextObj->GetTextEditOffset());
1372 aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1373 aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
1374 pTextEditCursorBuffer = pWin->GetCursor();
1376 maHdlList.SetMoveOutside(true);
1378 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1379 // to call AdjustMarkHdl() always.
1380 AdjustMarkHdl();
1382 mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);
1384 if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
1386 // activate visualization of EditView on Overlay, suppress when
1387 // LibreOfficeKit is active
1388 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
1390 const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
1391 const SdrTextObj* pText = GetTextEditObject();
1392 // show for cases like tdf#94223 but not for table cells like tdf#151311
1393 const bool bVisualizeSurroundingFrame(
1394 pText && pText->GetObjIdentifier() != SdrObjKind::Table);
1395 SdrPageView* pPageView = GetSdrPageView();
1397 if (pPageView)
1399 for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
1401 const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
1403 if (rPageWindow.GetPaintWindow().OutputToWindow())
1405 const rtl::Reference<sdr::overlay::OverlayManager>& xManager
1406 = rPageWindow.GetOverlayManager();
1407 if (xManager.is())
1409 std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
1410 new TextEditOverlayObject(aHilightColor,
1411 *mpTextEditOutlinerView));
1413 xManager->add(*pNewTextEditOverlayObject);
1414 if (bVisualizeSurroundingFrame)
1415 xManager->add(*pNewTextEditOverlayObject->getOverlayFrame());
1416 xManager->add(*pNewTextEditOverlayObject->getOverlaySelection());
1418 maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
1425 // check if this view is already inserted
1426 size_t i2, nCount = mpTextEditOutliner->GetViewCount();
1427 for (i2 = 0; i2 < nCount; i2++)
1429 if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
1430 break;
1433 if (i2 == nCount)
1434 mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);
1436 maHdlList.SetMoveOutside(false);
1437 maHdlList.SetMoveOutside(true);
1439 // register all windows as OutlinerViews with the Outliner
1440 if (!bOnlyOneView)
1442 for (sal_uInt32 i = 0; i < nWinCount; i++)
1444 SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
1445 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1447 if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1449 OutlinerView* pOutlView
1450 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1451 mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
1455 if (comphelper::LibreOfficeKit::isActive())
1457 // Register an outliner view for all other sdr views that
1458 // show the same page, so that when the text edit changes,
1459 // all interested windows get an invalidation.
1460 SdrViewIter aIter(pObj->getSdrPageFromSdrObject());
1461 for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
1463 if (pView == this)
1464 continue;
1466 for (sal_uInt32 nViewPaintWindow = 0;
1467 nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
1469 SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
1470 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1472 if (&rOutDev != pWin->GetOutDev()
1473 && OUTDEV_WINDOW == rOutDev.GetOutDevType())
1475 OutlinerView* pOutlView
1476 = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
1477 pOutlView->HideCursor();
1478 rOutDev.GetOwnerWindow()->SetCursor(nullptr);
1479 mpTextEditOutliner->InsertView(pOutlView);
1486 mpTextEditOutlinerView->ShowCursor();
1487 mpTextEditOutliner->SetStatusEventHdl(
1488 LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));
1489 if (pTextObj->IsChainable())
1491 mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
1492 LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
1495 mpTextEditOutliner->ClearModifyFlag();
1497 if (pTextObj->IsFitToSize())
1499 pWin->Invalidate(aTextEditArea);
1502 SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
1503 GetModel().Broadcast(aHint);
1505 mpTextEditOutliner->setVisualizedPage(nullptr);
1507 if (mxSelectionController.is())
1508 mxSelectionController->onSelectionHasChanged();
1510 if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1512 SdrUndoManager* pSdrUndoManager = nullptr;
1513 mpLocalTextEditUndoManager = createLocalTextUndoManager();
1515 if (mpLocalTextEditUndoManager)
1516 pSdrUndoManager = mpLocalTextEditUndoManager.get();
1518 if (pSdrUndoManager)
1520 // we have an outliner, undo manager and it's an EditUndoManager, exchange
1521 // the document undo manager and the default one from the outliner and tell
1522 // it that text edit starts by setting a callback if it needs to end text edit mode.
1523 assert(nullptr == mpOldTextEditUndoManager);
1525 mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
1526 pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
1528 else
1530 OSL_ENSURE(false,
1531 "The document undo manager is not derived from SdrUndoManager (!)");
1535 return true; // ran fine, let TextEdit run now
1537 else
1539 mpTextEditOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
1540 mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1541 mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1544 if (mpTextEditOutliner != nullptr)
1546 mpTextEditOutliner->setVisualizedPage(nullptr);
1549 // something went wrong...
1550 if (!bDontDeleteOutliner)
1552 delete pGivenOutliner;
1553 if (pGivenOutlinerView != nullptr)
1555 delete pGivenOutlinerView;
1556 pGivenOutlinerView = nullptr;
1559 mpTextEditOutliner.reset();
1561 mpTextEditOutlinerView = nullptr;
1562 mxWeakTextEditObj.clear();
1563 mpTextEditPV = nullptr;
1564 mpTextEditWin = nullptr;
1565 maHdlList.SetMoveOutside(false);
1567 return false;
1570 SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
1572 SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
1573 rtl::Reference<SdrTextObj> pTEObj = mxWeakTextEditObj.get();
1574 vcl::Window* pTEWin = mpTextEditWin;
1575 OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
1576 vcl::Cursor* pTECursorBuffer = pTextEditCursorBuffer;
1577 SdrUndoManager* pUndoEditUndoManager = nullptr;
1578 bool bNeedToUndoSavedRedoTextEdit(false);
1580 if (IsUndoEnabled() && pTEObj && mpTextEditOutliner
1581 && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1583 // change back the UndoManager to the remembered original one
1584 SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
1585 mpOldTextEditUndoManager = nullptr;
1587 if (pOriginal)
1589 // check if we got back our document undo manager
1590 SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();
1592 if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
1594 if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
1596 // remember the UndoManager where missing Undos have to be triggered after end
1597 // text edit. When the undo had triggered the end text edit, the original action
1598 // which had to be undone originally is not yet undone.
1599 pUndoEditUndoManager = pSdrUndoManager;
1601 // We are ending text edit; if text edit was triggered from undo, execute all redos
1602 // to create a complete text change undo action for the redo buffer. Also mark this
1603 // state when at least one redo was executed; the created extra TextChange needs to
1604 // be undone in addition to the first real undo outside the text edit changes
1605 while (pSdrUndoManager->GetRedoActionCount()
1606 > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
1608 bNeedToUndoSavedRedoTextEdit = true;
1609 pSdrUndoManager->Redo();
1613 // reset the callback link and let the undo manager cleanup all text edit
1614 // undo actions to get the stack back to the form before the text edit
1615 pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
1617 else
1619 OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
1620 "expected document UndoManager (!)");
1621 delete pOriginal;
1624 // cid#1493241 - Wrapper object use after free
1625 if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
1626 pUndoEditUndoManager = nullptr;
1627 mpLocalTextEditUndoManager.reset();
1630 else
1632 assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
1635 if (auto pTextEditObj = mxWeakTextEditObj.get())
1637 SdrHint aHint(SdrHintKind::EndEdit, *pTextEditObj);
1638 GetModel().Broadcast(aHint);
1641 // if new mechanism was used, clean it up. At cleanup no need to check
1642 // for LibreOfficeKit
1643 if (mpTextEditOutlinerView)
1645 mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
1646 maTEOverlayGroup.clear();
1649 mxWeakTextEditObj.clear();
1650 mpTextEditPV = nullptr;
1651 mpTextEditWin = nullptr;
1652 mpTextEditOutlinerView = nullptr;
1653 pTextEditCursorBuffer = nullptr;
1654 aTextEditArea = tools::Rectangle();
1656 if (SdrOutliner* pTEOutliner = mpTextEditOutliner.release())
1658 bool bModified = pTEOutliner->IsModified();
1659 if (pTEOutlinerView != nullptr)
1661 pTEOutlinerView->HideCursor();
1663 if (pTEObj != nullptr)
1665 pTEOutliner->CompleteOnlineSpelling();
1667 std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
1669 if (bModified)
1671 sal_Int32 nText;
1672 for (nText = 0; nText < pTEObj->getTextCount(); ++nText)
1673 if (pTEObj->getText(nText) == pTEObj->getActiveText())
1674 break;
1676 pTxtUndo.reset(
1677 dynamic_cast<SdrUndoObjSetText*>(GetModel()
1678 .GetSdrUndoFactory()
1679 .CreateUndoObjectSetText(*pTEObj, nText)
1680 .release()));
1682 DBG_ASSERT(!bModified || pTxtUndo,
1683 "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
1684 // Set old CalcFieldValue-Handler again, this
1685 // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
1686 pTEOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
1687 pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1688 pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
1690 const bool bUndo = IsUndoEnabled();
1691 if (bUndo)
1693 OUString aObjName(pTEObj->TakeObjNameSingul());
1694 BegUndo(SvxResId(STR_UndoObjSetText), aObjName);
1697 pTEObj->EndTextEdit(*pTEOutliner);
1699 if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork()))
1701 pTEObj->ActionChanged();
1704 if (pTxtUndo != nullptr)
1706 pTxtUndo->AfterSetText();
1707 if (!pTxtUndo->IsDifferent())
1709 pTxtUndo.reset();
1712 // check deletion of entire TextObj
1713 std::unique_ptr<SdrUndoAction> pDelUndo;
1714 bool bDelObj = false;
1715 if (mbTextEditNewObj)
1717 bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj()
1718 && !pTEObj->HasFill() && !pTEObj->HasLine();
1720 if (pTEObj->IsInserted() && bDelObj
1721 && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally)
1723 SdrObjKind eIdent = pTEObj->GetObjIdentifier();
1724 if (eIdent == SdrObjKind::Text)
1726 pDelUndo = GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj);
1730 if (pTxtUndo)
1732 if (bUndo)
1733 AddUndo(std::move(pTxtUndo));
1734 eRet = SdrEndTextEditKind::Changed;
1736 if (pDelUndo != nullptr)
1738 if (bUndo)
1740 AddUndo(std::move(pDelUndo));
1742 eRet = SdrEndTextEditKind::Deleted;
1743 DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr,
1744 "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
1745 "ObjList!");
1746 if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr)
1748 pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum());
1749 CheckMarked(); // remove selection immediately...
1752 else if (bDelObj)
1753 { // for Writer: the app has to do the deletion itself.
1754 eRet = SdrEndTextEditKind::ShouldBeDeleted;
1757 if (bUndo)
1758 EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
1760 // Switch on any TextAnimation again after TextEdit
1761 if (pTEObj)
1763 pTEObj->SetTextAnimationAllowed(true);
1766 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1767 // to call AdjustMarkHdl() always.
1768 AdjustMarkHdl();
1770 // delete all OutlinerViews
1771 for (size_t i = pTEOutliner->GetViewCount(); i > 0;)
1773 i--;
1774 OutlinerView* pOLV = pTEOutliner->GetView(i);
1775 sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10;
1776 vcl::Window* pWin = pOLV->GetWindow();
1777 tools::Rectangle aRect(pOLV->GetOutputArea());
1778 pTEOutliner->RemoveView(i);
1779 if (!mbTextEditDontDelete || i != 0)
1781 // may not own the zeroth one
1782 delete pOLV;
1784 aRect.Union(aTextEditArea);
1785 aRect.Union(aMinTextEditArea);
1786 aRect = pWin->LogicToPixel(aRect);
1787 aRect.AdjustLeft(-nMorePix);
1788 aRect.AdjustTop(-nMorePix);
1789 aRect.AdjustRight(nMorePix);
1790 aRect.AdjustBottom(nMorePix);
1791 aRect = pWin->PixelToLogic(aRect);
1792 InvalidateOneWin(*pWin->GetOutDev(), aRect);
1793 pWin->GetOutDev()->SetFillColor();
1794 pWin->GetOutDev()->SetLineColor(COL_BLACK);
1796 // and now the Outliner itself
1797 if (!mbTextEditDontDelete)
1798 delete pTEOutliner;
1799 else
1800 pTEOutliner->Clear();
1801 if (pTEWin != nullptr)
1803 pTEWin->SetCursor(pTECursorBuffer);
1805 maHdlList.SetMoveOutside(false);
1806 if (eRet != SdrEndTextEditKind::Unchanged)
1808 GetMarkedObjectListWriteAccess().SetNameDirty();
1810 // coverity[leaked_storage] - if pTEOutliner wasn't deleted it didn't really belong to us
1813 if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster())
1815 SdrHint aHint(SdrHintKind::EndEdit, *pTEObj);
1816 const_cast<SfxBroadcaster*>(pTEObj->GetBroadcaster())->Broadcast(aHint);
1819 if (pUndoEditUndoManager)
1821 if (bNeedToUndoSavedRedoTextEdit)
1823 // undo the text edit action since it was created as part of an EndTextEdit
1824 // callback from undo itself. This needs to be done after the call to
1825 // FmFormView::SdrEndTextEdit since it gets created there
1826 pUndoEditUndoManager->Undo();
1829 // trigger the Undo which was not executed, but lead to this
1830 // end text edit
1831 pUndoEditUndoManager->Undo();
1834 return eRet;
1837 // info about TextEdit. Default is false.
1838 bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.get().is(); }
1840 // info about TextEditPageView. Default is 0L.
1841 SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; }
1843 OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const
1845 if (pWin == nullptr)
1846 return nullptr;
1847 if (mpTextEditOutliner == nullptr)
1848 return nullptr;
1849 OutlinerView* pNewView = nullptr;
1850 size_t nWinCount = mpTextEditOutliner->GetViewCount();
1851 for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++)
1853 OutlinerView* pView = mpTextEditOutliner->GetView(i);
1854 if (pView->GetWindow() == pWin)
1855 pNewView = pView;
1857 return pNewView;
1860 void SdrObjEditView::SetTextEditWin(vcl::Window* pWin)
1862 if (!(mxWeakTextEditObj.get() && pWin != nullptr && pWin != mpTextEditWin))
1863 return;
1865 OutlinerView* pNewView = ImpFindOutlinerView(pWin);
1866 if (pNewView != nullptr && pNewView != mpTextEditOutlinerView)
1868 if (mpTextEditOutlinerView != nullptr)
1870 mpTextEditOutlinerView->HideCursor();
1872 mpTextEditOutlinerView = pNewView;
1873 mpTextEditWin = pWin;
1874 pWin->GrabFocus(); // Make the cursor blink here as well
1875 pNewView->ShowCursor();
1876 ImpMakeTextCursorAreaVisible();
1880 bool SdrObjEditView::IsTextEditHit(const Point& rHit) const
1882 bool bOk = false;
1883 if (mxWeakTextEditObj.get())
1885 tools::Rectangle aEditArea;
1886 if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0))
1887 aEditArea.Union(pOLV->GetOutputArea());
1889 if (aEditArea.Contains(rHit))
1890 { // check if any characters were actually hit
1891 const Point aPnt(rHit - aEditArea.TopLeft());
1892 tools::Long nHitTol = 2000;
1893 if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice())
1894 nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM,
1895 pRef->GetMapMode().GetMapUnit());
1897 bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol));
1900 return bOk;
1903 bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const
1905 bool bOk = false;
1906 if (rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get())
1908 OutlinerView* pOLV = mpTextEditOutliner->GetView(0);
1909 if (pOLV)
1911 vcl::Window* pWin = pOLV->GetWindow();
1912 if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr)
1914 sal_uInt16 nPixSiz = pOLV->GetInvalidateMore();
1915 tools::Rectangle aEditArea(aMinTextEditArea);
1916 aEditArea.Union(pOLV->GetOutputArea());
1917 if (!aEditArea.Contains(rHit))
1919 Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
1920 aEditArea.AdjustLeft(-(aSiz.Width()));
1921 aEditArea.AdjustTop(-(aSiz.Height()));
1922 aEditArea.AdjustRight(aSiz.Width());
1923 aEditArea.AdjustBottom(aSiz.Height());
1924 bOk = aEditArea.Contains(rHit);
1929 return bOk;
1932 std::unique_ptr<TextChainCursorManager>
1933 SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled)
1935 *bOutHandled = false;
1937 rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get();
1938 if (!pTextObj)
1939 return nullptr;
1941 if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain())
1942 return nullptr;
1944 std::unique_ptr<TextChainCursorManager> pCursorManager(
1945 new TextChainCursorManager(this, pTextObj.get()));
1946 if (pCursorManager->HandleKeyEvent(rKEvt))
1948 // Possibly do other stuff here if necessary...
1949 // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
1950 *bOutHandled = true;
1953 return pCursorManager;
1956 bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
1958 if (mpTextEditOutlinerView)
1960 /* Start special handling of keys within a chain */
1961 // We possibly move to another box before any handling
1962 bool bHandled = false;
1963 std::unique_ptr<TextChainCursorManager> xCursorManager(
1964 ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled));
1965 if (bHandled)
1966 return true;
1967 /* End special handling of keys within a chain */
1969 if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin))
1971 if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
1972 GetModel().SetChanged();
1974 /* Start chaining processing */
1975 ImpChainingEventHdl();
1976 ImpMoveCursorAfterChainingEvent(xCursorManager.get());
1977 /* End chaining processing */
1979 if (pWin != nullptr && pWin != mpTextEditWin)
1980 SetTextEditWin(pWin);
1981 ImpMakeTextCursorAreaVisible();
1982 return true;
1985 return SdrGlueEditView::KeyInput(rKEvt, pWin);
1988 bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
1990 if (mpTextEditOutlinerView != nullptr)
1992 bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
1993 if (!bPostIt)
1995 Point aPt(rMEvt.GetPosPixel());
1996 if (pWin != nullptr)
1997 aPt = pWin->PixelToLogic(aPt);
1998 else if (mpTextEditWin != nullptr)
1999 aPt = mpTextEditWin->PixelToLogic(aPt);
2000 bPostIt = IsTextEditHit(aPt);
2002 if (bPostIt)
2004 Point aPixPos(rMEvt.GetPosPixel());
2005 if (pWin)
2007 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2008 if (aPixPos.X() < aR.Left())
2009 aPixPos.setX(aR.Left());
2010 if (aPixPos.X() > aR.Right())
2011 aPixPos.setX(aR.Right());
2012 if (aPixPos.Y() < aR.Top())
2013 aPixPos.setY(aR.Top());
2014 if (aPixPos.Y() > aR.Bottom())
2015 aPixPos.setY(aR.Bottom());
2017 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2018 rMEvt.GetModifier());
2019 if (mpTextEditOutlinerView->MouseButtonDown(aMEvt))
2021 if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev()
2022 && pWin->GetOutDevType() == OUTDEV_WINDOW)
2023 SetTextEditWin(pWin->GetOwnerWindow());
2024 ImpMakeTextCursorAreaVisible();
2025 return true;
2029 return SdrGlueEditView::MouseButtonDown(rMEvt, pWin);
2032 bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
2034 if (mpTextEditOutlinerView != nullptr)
2036 bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
2037 if (!bPostIt)
2039 Point aPt(rMEvt.GetPosPixel());
2040 if (pWin != nullptr)
2041 aPt = pWin->PixelToLogic(aPt);
2042 else if (mpTextEditWin != nullptr)
2043 aPt = mpTextEditWin->PixelToLogic(aPt);
2044 bPostIt = IsTextEditHit(aPt);
2046 if (bPostIt && pWin)
2048 Point aPixPos(rMEvt.GetPosPixel());
2049 tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2050 if (aPixPos.X() < aR.Left())
2051 aPixPos.setX(aR.Left());
2052 if (aPixPos.X() > aR.Right())
2053 aPixPos.setX(aR.Right());
2054 if (aPixPos.Y() < aR.Top())
2055 aPixPos.setY(aR.Top());
2056 if (aPixPos.Y() > aR.Bottom())
2057 aPixPos.setY(aR.Bottom());
2058 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2059 rMEvt.GetModifier());
2060 if (mpTextEditOutlinerView->MouseButtonUp(aMEvt))
2062 ImpMakeTextCursorAreaVisible();
2063 return true;
2067 return SdrGlueEditView::MouseButtonUp(rMEvt, pWin);
2070 bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
2072 if (mpTextEditOutlinerView != nullptr)
2074 bool bSelMode = mpTextEditOutliner->IsInSelectionMode();
2075 bool bPostIt = bSelMode;
2076 if (!bPostIt)
2078 Point aPt(rMEvt.GetPosPixel());
2079 if (pWin)
2080 aPt = pWin->PixelToLogic(aPt);
2081 else if (mpTextEditWin)
2082 aPt = mpTextEditWin->PixelToLogic(aPt);
2083 bPostIt = IsTextEditHit(aPt);
2085 if (bPostIt)
2087 Point aPixPos(rMEvt.GetPosPixel());
2088 tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea());
2089 if (pWin)
2090 aR = pWin->LogicToPixel(aR);
2091 else if (mpTextEditWin)
2092 aR = mpTextEditWin->LogicToPixel(aR);
2093 if (aPixPos.X() < aR.Left())
2094 aPixPos.setX(aR.Left());
2095 if (aPixPos.X() > aR.Right())
2096 aPixPos.setX(aR.Right());
2097 if (aPixPos.Y() < aR.Top())
2098 aPixPos.setY(aR.Top());
2099 if (aPixPos.Y() > aR.Bottom())
2100 aPixPos.setY(aR.Bottom());
2101 MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
2102 rMEvt.GetModifier());
2103 if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode)
2105 ImpMakeTextCursorAreaVisible();
2106 return true;
2110 return SdrGlueEditView::MouseMove(rMEvt, pWin);
2113 bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
2115 // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
2116 if (mpTextEditOutlinerView != nullptr)
2118 if (rCEvt.GetCommand() == CommandEventId::StartDrag)
2120 bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent();
2121 if (!bPostIt && rCEvt.IsMouseEvent())
2123 Point aPt(rCEvt.GetMousePosPixel());
2124 if (pWin != nullptr)
2125 aPt = pWin->PixelToLogic(aPt);
2126 else if (mpTextEditWin != nullptr)
2127 aPt = mpTextEditWin->PixelToLogic(aPt);
2128 bPostIt = IsTextEditHit(aPt);
2130 if (bPostIt)
2132 Point aPixPos(rCEvt.GetMousePosPixel());
2133 if (rCEvt.IsMouseEvent() && pWin)
2135 tools::Rectangle aR(
2136 pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
2137 if (aPixPos.X() < aR.Left())
2138 aPixPos.setX(aR.Left());
2139 if (aPixPos.X() > aR.Right())
2140 aPixPos.setX(aR.Right());
2141 if (aPixPos.Y() < aR.Top())
2142 aPixPos.setY(aR.Top());
2143 if (aPixPos.Y() > aR.Bottom())
2144 aPixPos.setY(aR.Bottom());
2146 CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent());
2147 // Command is void at the OutlinerView, sadly
2148 mpTextEditOutlinerView->Command(aCEvt);
2149 if (pWin != nullptr && pWin != mpTextEditWin)
2150 SetTextEditWin(pWin);
2151 ImpMakeTextCursorAreaVisible();
2152 return true;
2155 else
2157 mpTextEditOutlinerView->Command(rCEvt);
2158 if (comphelper::LibreOfficeKit::isActive())
2160 // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
2161 // isn't called
2162 if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
2163 GetModel().SetChanged();
2165 return true;
2168 return SdrGlueEditView::Command(rCEvt, pWin);
2171 bool SdrObjEditView::ImpIsTextEditAllSelected() const
2173 bool bRet = false;
2174 if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr)
2176 if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get()))
2178 const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount();
2179 Paragraph* pLastPara
2180 = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0);
2182 ESelection aESel(mpTextEditOutlinerView->GetSelection());
2183 if (aESel.nStartPara == 0 && aESel.nStartPos == 0 && aESel.nEndPara == (nParaCnt - 1))
2185 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nEndPos)
2186 bRet = true;
2188 // in case the selection was done backwards
2189 if (!bRet && aESel.nEndPara == 0 && aESel.nEndPos == 0
2190 && aESel.nStartPara == (nParaCnt - 1))
2192 if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nStartPos)
2193 bRet = true;
2196 else
2198 bRet = true;
2201 return bRet;
2204 void SdrObjEditView::ImpMakeTextCursorAreaVisible()
2206 if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr)
2208 vcl::Cursor* pCsr = mpTextEditWin->GetCursor();
2209 if (pCsr != nullptr)
2211 Size aSiz(pCsr->GetSize());
2212 if (!aSiz.IsEmpty())
2214 MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin);
2220 SvtScriptType SdrObjEditView::GetScriptType() const
2222 SvtScriptType nScriptType = SvtScriptType::NONE;
2224 if (IsTextEdit())
2226 auto pText = mxWeakTextEditObj.get();
2227 if (pText->GetOutlinerParaObject())
2228 nScriptType = pText->GetOutlinerParaObject()->GetTextObject().GetScriptType();
2230 if (mpTextEditOutlinerView)
2231 nScriptType = mpTextEditOutlinerView->GetSelectedScriptType();
2233 else
2235 const size_t nMarkCount(GetMarkedObjectCount());
2237 for (size_t i = 0; i < nMarkCount; ++i)
2239 OutlinerParaObject* pParaObj = GetMarkedObjectByIndex(i)->GetOutlinerParaObject();
2241 if (pParaObj)
2243 nScriptType |= pParaObj->GetTextObject().GetScriptType();
2248 if (nScriptType == SvtScriptType::NONE)
2249 nScriptType = SvtScriptType::LATIN;
2251 return nScriptType;
2254 void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2256 if (mxSelectionController.is())
2257 if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr))
2258 return;
2260 if (IsTextEdit())
2262 DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2263 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2264 DBG_ASSERT(mpTextEditOutliner != nullptr,
2265 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2267 auto pText = mxWeakTextEditObj.get();
2268 // take care of bOnlyHardAttr(!)
2269 if (!bOnlyHardAttr && pText->GetStyleSheet())
2270 rTargetSet.Put(pText->GetStyleSheet()->GetItemSet());
2272 // add object attributes
2273 rTargetSet.Put(pText->GetMergedItemSet());
2275 if (mpTextEditOutlinerView)
2277 // FALSE= regard InvalidItems as "holes," not as Default
2278 rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false);
2281 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == pText.get())
2283 MergeNotPersistAttrFromMarked(rTargetSet);
2286 else
2288 SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr);
2292 bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2294 bool bRet = false;
2295 rtl::Reference<SdrTextObj> pTextEditObj = mxWeakTextEditObj.get();
2296 bool bTextEdit = mpTextEditOutlinerView != nullptr && pTextEditObj != nullptr;
2297 bool bAllTextSelected = ImpIsTextEditAllSelected();
2298 const SfxItemSet* pSet = &rSet;
2300 if (!bTextEdit)
2302 // no TextEdit active -> all Items to drawing object
2303 if (mxSelectionController.is())
2304 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2306 if (!bRet)
2308 SdrGlueEditView::SetAttributes(*pSet, bReplaceAll);
2309 bRet = true;
2312 else
2314 #ifdef DBG_UTIL
2316 bool bHasEEFeatureItems = false;
2317 SfxItemIter aIter(rSet);
2318 for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
2319 pItem = aIter.NextItem())
2321 if (!IsInvalidItem(pItem))
2323 sal_uInt16 nW = pItem->Which();
2324 if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END)
2325 bHasEEFeatureItems = true;
2329 if (bHasEEFeatureItems)
2331 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
2332 nullptr, VclMessageType::Info, VclButtonsType::Ok,
2333 "SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
2334 "at the SdrView does not make sense! It only leads to "
2335 "overhead and unreadable documents."));
2336 xInfoBox->run();
2339 #endif
2341 bool bOnlyEEItems;
2342 bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems);
2343 // everything selected? -> attributes to the border, too
2344 // if no EEItems, attributes to the border only
2345 if (bAllTextSelected || bNoEEItems)
2347 if (mxSelectionController.is())
2348 bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
2350 if (!bRet)
2352 const bool bUndo = IsUndoEnabled();
2354 if (bUndo)
2356 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2357 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2359 // If this is a text object also rescue the OutlinerParaObject since
2360 // applying attributes to the object may change text layout when
2361 // multiple portions exist with multiple formats. If an OutlinerParaObject
2362 // really exists and needs to be rescued is evaluated in the undo
2363 // implementation itself.
2364 bool bRescueText(pTextEditObj);
2366 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(
2367 *pTextEditObj, false, !bNoEEItems || bRescueText));
2368 EndUndo();
2371 pTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll);
2373 FlushComeBackTimer(); // to set ModeHasChanged immediately
2376 else if (!bOnlyEEItems)
2378 // Otherwise split Set, if necessary.
2379 // Now we build an ItemSet aSet that doesn't contain EE_Items from
2380 // *pSet (otherwise it would be a copy).
2381 WhichRangesContainer pNewWhichTable
2382 = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END);
2383 SfxItemSet aSet(GetModel().GetItemPool(), std::move(pNewWhichTable));
2384 SfxWhichIter aIter(aSet);
2385 sal_uInt16 nWhich = aIter.FirstWhich();
2386 while (nWhich != 0)
2388 const SfxPoolItem* pItem;
2389 SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem);
2390 if (eState == SfxItemState::SET)
2391 aSet.Put(*pItem);
2392 nWhich = aIter.NextWhich();
2395 if (mxSelectionController.is())
2396 bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll);
2398 if (!bRet)
2400 if (IsUndoEnabled())
2402 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
2403 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj));
2404 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj));
2405 EndUndo();
2408 pTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll);
2410 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == pTextEditObj.get())
2412 SetNotPersistAttrToMarked(aSet);
2415 FlushComeBackTimer();
2417 if (!bNoEEItems)
2419 // and now the attributes to the EditEngine
2420 if (bReplaceAll)
2422 mpTextEditOutlinerView->RemoveAttribs(true);
2424 mpTextEditOutlinerView->SetAttribs(rSet);
2426 Outliner* pTEOutliner = mpTextEditOutlinerView->GetOutliner();
2427 if (pTEOutliner && pTEOutliner->IsModified())
2428 GetModel().SetChanged();
2430 ImpMakeTextCursorAreaVisible();
2432 bRet = true;
2434 return bRet;
2437 SfxStyleSheet* SdrObjEditView::GetStyleSheet() const
2439 SfxStyleSheet* pSheet = nullptr;
2441 if (mxSelectionController.is())
2443 if (mxSelectionController->GetStyleSheet(pSheet))
2444 return pSheet;
2447 if (mpTextEditOutlinerView)
2449 pSheet = mpTextEditOutlinerView->GetStyleSheet();
2451 else
2453 pSheet = SdrGlueEditView::GetStyleSheet();
2455 return pSheet;
2458 void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
2460 if (mxSelectionController.is())
2462 if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr))
2463 return;
2466 // if we are currently in edit mode we must also set the stylesheet
2467 // on all paragraphs in the Outliner for the edit view
2468 if (nullptr != mpTextEditOutlinerView)
2470 Outliner* pOutliner = mpTextEditOutlinerView->GetOutliner();
2472 const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
2473 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
2475 pOutliner->SetStyleSheet(nPara, pStyleSheet);
2479 SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr);
2482 void SdrObjEditView::AddDeviceToPaintView(OutputDevice& rNewDev, vcl::Window* pWindow)
2484 SdrGlueEditView::AddDeviceToPaintView(rNewDev, pWindow);
2486 if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2487 && rNewDev.GetOutDevType() == OUTDEV_WINDOW)
2489 OutlinerView* pOutlView = ImpMakeOutlinerView(rNewDev.GetOwnerWindow(), nullptr);
2490 mpTextEditOutliner->InsertView(pOutlView);
2494 void SdrObjEditView::DeleteDeviceFromPaintView(OutputDevice& rOldDev)
2496 SdrGlueEditView::DeleteDeviceFromPaintView(rOldDev);
2498 if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView
2499 && rOldDev.GetOutDevType() == OUTDEV_WINDOW)
2501 for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;)
2503 i--;
2504 OutlinerView* pOLV = mpTextEditOutliner->GetView(i);
2505 if (pOLV && pOLV->GetWindow() == rOldDev.GetOwnerWindow())
2507 mpTextEditOutliner->RemoveView(i);
2512 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), &rOldDev);
2515 bool SdrObjEditView::IsTextEditInSelectionMode() const
2517 return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode();
2520 // MacroMode
2522 void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV,
2523 vcl::Window* pWin)
2525 BrkMacroObj();
2526 if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro())
2528 nTol = ImpGetHitTolLogic(nTol, nullptr);
2529 pMacroObj = pObj;
2530 pMacroPV = pPV;
2531 pMacroWin = pWin;
2532 mbMacroDown = false;
2533 nMacroTol = sal_uInt16(nTol);
2534 aMacroDownPos = rPnt;
2535 MovMacroObj(rPnt);
2539 void SdrObjEditView::ImpMacroUp(const Point& rUpPos)
2541 if (pMacroObj != nullptr && mbMacroDown)
2543 SdrObjMacroHitRec aHitRec;
2544 aHitRec.aPos = rUpPos;
2545 aHitRec.nTol = nMacroTol;
2546 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
2547 aHitRec.pPageView = pMacroPV;
2548 pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2549 mbMacroDown = false;
2553 void SdrObjEditView::ImpMacroDown(const Point& rDownPos)
2555 if (pMacroObj != nullptr && !mbMacroDown)
2557 SdrObjMacroHitRec aHitRec;
2558 aHitRec.aPos = rDownPos;
2559 aHitRec.nTol = nMacroTol;
2560 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
2561 aHitRec.pPageView = pMacroPV;
2562 pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
2563 mbMacroDown = true;
2567 void SdrObjEditView::MovMacroObj(const Point& rPnt)
2569 if (pMacroObj == nullptr)
2570 return;
2572 SdrObjMacroHitRec aHitRec;
2573 aHitRec.aPos = rPnt;
2574 aHitRec.nTol = nMacroTol;
2575 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
2576 aHitRec.pPageView = pMacroPV;
2577 bool bDown = pMacroObj->IsMacroHit(aHitRec);
2578 if (bDown)
2579 ImpMacroDown(rPnt);
2580 else
2581 ImpMacroUp(rPnt);
2584 void SdrObjEditView::BrkMacroObj()
2586 if (pMacroObj != nullptr)
2588 ImpMacroUp(aMacroDownPos);
2589 pMacroObj = nullptr;
2590 pMacroPV = nullptr;
2591 pMacroWin = nullptr;
2595 bool SdrObjEditView::EndMacroObj()
2597 if (pMacroObj != nullptr && mbMacroDown)
2599 ImpMacroUp(aMacroDownPos);
2600 SdrObjMacroHitRec aHitRec;
2601 aHitRec.aPos = aMacroDownPos;
2602 aHitRec.nTol = nMacroTol;
2603 aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
2604 aHitRec.pPageView = pMacroPV;
2605 bool bRet = pMacroObj->DoMacro(aHitRec);
2606 pMacroObj = nullptr;
2607 pMacroPV = nullptr;
2608 pMacroWin = nullptr;
2609 return bRet;
2611 else
2613 BrkMacroObj();
2614 return false;
2618 /** fills the given any with a XTextCursor for the current text selection.
2619 Leaves the any untouched if there currently is no text selected */
2620 void SdrObjEditView::getTextSelection(css::uno::Any& rSelection)
2622 if (!IsTextEdit())
2623 return;
2625 OutlinerView* pOutlinerView = GetTextEditOutlinerView();
2626 if (!(pOutlinerView && pOutlinerView->HasSelection()))
2627 return;
2629 SdrObject* pObj = GetTextEditObject();
2631 if (!pObj)
2632 return;
2634 css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY);
2635 if (xText.is())
2637 SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText);
2638 if (pRange)
2640 rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection());
2645 /* check if we have a single selection and that single object likes
2646 to handle the mouse and keyboard events itself
2648 TODO: the selection controller should be queried from the
2649 object specific view contact. Currently this method only
2650 works for tables.
2652 void SdrObjEditView::MarkListHasChanged()
2654 SdrGlueEditView::MarkListHasChanged();
2656 if (mxSelectionController.is())
2658 mxLastSelectionController = mxSelectionController;
2659 mxSelectionController->onSelectionHasChanged();
2662 mxSelectionController.clear();
2664 const SdrMarkList& rMarkList = GetMarkedObjectList();
2665 if (rMarkList.GetMarkCount() != 1)
2666 return;
2668 const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
2669 SdrView* pView(dynamic_cast<SdrView*>(this));
2671 // check for table
2672 if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
2673 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2675 mxSelectionController = sdr::table::CreateTableController(
2676 *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController);
2678 if (mxSelectionController.is())
2680 mxLastSelectionController.clear();
2681 mxSelectionController->onSelectionHasChanged();
2686 IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2688 OnEndPasteOrDrop(pInfo);
2691 IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
2693 OnBeginPasteOrDrop(pInfo);
2696 void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*)
2698 // applications can derive from these virtual methods to do something before a drop or paste operation
2701 void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*)
2703 // applications can derive from these virtual methods to do something before a drop or paste operation
2706 sal_uInt16 SdrObjEditView::GetSelectionLevel() const
2708 sal_uInt16 nLevel = 0xFFFF;
2709 if (IsTextEdit())
2711 DBG_ASSERT(mpTextEditOutlinerView != nullptr,
2712 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2713 DBG_ASSERT(mpTextEditOutliner != nullptr,
2714 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2715 if (mpTextEditOutlinerView)
2717 //start and end position
2718 ESelection aSelect = mpTextEditOutlinerView->GetSelection();
2719 sal_uInt16 nStartPara = ::std::min(aSelect.nStartPara, aSelect.nEndPara);
2720 sal_uInt16 nEndPara = ::std::max(aSelect.nStartPara, aSelect.nEndPara);
2721 //get level from each paragraph
2722 nLevel = 0;
2723 for (sal_uInt16 nPara = nStartPara; nPara <= nEndPara; nPara++)
2725 sal_uInt16 nParaDepth
2726 = 1 << static_cast<sal_uInt16>(mpTextEditOutliner->GetDepth(nPara));
2727 if (!(nLevel & nParaDepth))
2728 nLevel += nParaDepth;
2730 //reduce one level for Outliner Object
2731 //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
2732 // nLevel = nLevel >> 1;
2733 //no bullet paragraph selected
2734 if (nLevel == 0)
2735 nLevel = 0xFFFF;
2738 return nLevel;
2741 bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor,
2742 SdrObjKind nObjectIdentifier)
2744 if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d)
2745 return false;
2746 switch (nObjectIdentifier)
2748 case SdrObjKind::NONE:
2749 case SdrObjKind::Group:
2750 return false;
2751 case SdrObjKind::Line:
2752 case SdrObjKind::Rectangle:
2753 case SdrObjKind::CircleOrEllipse:
2754 case SdrObjKind::CircleSection:
2755 case SdrObjKind::CircleArc:
2756 case SdrObjKind::CircleCut:
2757 case SdrObjKind::Polygon:
2758 case SdrObjKind::PolyLine:
2759 case SdrObjKind::PathLine:
2760 case SdrObjKind::PathFill:
2761 case SdrObjKind::FreehandLine:
2762 case SdrObjKind::FreehandFill:
2763 case SdrObjKind::Text:
2764 case SdrObjKind::TitleText:
2765 case SdrObjKind::OutlineText:
2766 case SdrObjKind::Graphic:
2767 case SdrObjKind::OLE2:
2768 case SdrObjKind::Table:
2769 return true;
2770 case SdrObjKind::Caption:
2771 return false;
2772 case SdrObjKind::Edge:
2773 case SdrObjKind::PathPoly:
2774 case SdrObjKind::PathPolyLine:
2775 return true;
2776 case SdrObjKind::Page:
2777 case SdrObjKind::Measure:
2778 case SdrObjKind::OLEPluginFrame:
2779 case SdrObjKind::UNO:
2780 return false;
2781 case SdrObjKind::CustomShape:
2782 return true;
2783 default:
2784 return false;
2788 static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly)
2790 static const WhichRangesContainer gFull(
2791 svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL,
2792 SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST,
2793 SDRATTR_MISC_LAST, // table cell formats
2794 SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
2795 SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST,
2796 SDRATTR_SOFTEDGE_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>);
2798 static const WhichRangesContainer gTextOnly(
2799 svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START,
2800 EE_CHAR_END>);
2802 return bTextOnly ? gTextOnly : gFull;
2805 void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet)
2807 const SdrMarkList& rMarkList = GetMarkedObjectList();
2808 if (rMarkList.GetMarkCount() <= 0)
2809 return;
2811 OutlinerView* pOLV = GetTextEditOutlinerView();
2813 rFormatSet = std::make_shared<SfxItemSet>(GetModel().GetItemPool(),
2814 GetFormatRangeImpl(pOLV != nullptr));
2815 if (pOLV)
2817 rFormatSet->Put(pOLV->GetAttribs());
2819 else
2821 const bool bOnlyHardAttr = false;
2822 rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr));
2825 // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
2826 const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2827 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
2828 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2830 auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj);
2831 if (mxSelectionController.is() && pTable->getActiveCell().is())
2833 mxSelectionController->GetAttributes(*rFormatSet, false);
2838 static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool,
2839 const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet,
2840 bool bNoCharacterFormats, bool bNoParagraphFormats)
2842 SfxItemSet aPaintSet(rPool, pRanges);
2844 for (const auto& pRange : pRanges)
2846 sal_uInt16 nWhich = pRange.first;
2847 const sal_uInt16 nLastWhich = pRange.second;
2849 if (bNoCharacterFormats && (nWhich == EE_CHAR_START))
2850 continue;
2852 if (bNoParagraphFormats && (nWhich == EE_PARA_START))
2853 continue;
2855 for (; nWhich <= nLastWhich; nWhich++)
2857 const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich);
2858 const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich);
2860 if ((pSourceItem && !pTargetItem)
2861 || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem))
2863 aPaintSet.Put(*pSourceItem);
2867 return aPaintSet;
2870 void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj,
2871 SdrText* pText, bool bNoCharacterFormats,
2872 bool bNoParagraphFormats)
2874 OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
2875 if (!pParaObj)
2876 return;
2878 SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner();
2879 rOutliner.SetText(*pParaObj);
2881 sal_Int32 nParaCount(rOutliner.GetParagraphCount());
2883 if (!nParaCount)
2884 return;
2886 for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
2888 if (!bNoCharacterFormats)
2889 rOutliner.RemoveCharAttribs(nPara);
2891 SfxItemSet aSet(rOutliner.GetParaAttribs(nPara));
2892 aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet,
2893 bNoCharacterFormats, bNoParagraphFormats));
2894 rOutliner.SetParaAttribs(nPara, aSet);
2897 std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
2898 rOutliner.Clear();
2900 rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
2903 void SdrObjEditView::DisposeUndoManager()
2905 if (mpTextEditOutliner)
2907 if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager))
2909 // Non-owning pointer, clear it.
2910 mpTextEditOutliner->SetUndoManager(nullptr);
2914 mpOldTextEditUndoManager = nullptr;
2917 void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, bool bNoCharacterFormats,
2918 bool bNoParagraphFormats)
2920 if (mxSelectionController.is()
2921 && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, bNoCharacterFormats,
2922 bNoParagraphFormats))
2924 return;
2927 OutlinerView* pOLV = GetTextEditOutlinerView();
2928 const SdrMarkList& rMarkList = GetMarkedObjectList();
2929 if (!pOLV)
2931 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2932 const SfxItemSet& rShapeSet = pObj->GetMergedItemSet();
2934 // if not in text edit mode (aka the user selected text or clicked on a word)
2935 // apply formatting attributes to selected shape
2936 // All formatting items (see ranges above) that are unequal in selected shape and
2937 // the format paintbrush are hard set on the selected shape.
2939 const WhichRangesContainer& pRanges = rFormatSet.GetRanges();
2940 bool bTextOnly = true;
2942 for (const auto& pRange : pRanges)
2944 if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START))
2946 bTextOnly = false;
2947 break;
2951 if (!bTextOnly)
2953 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(),
2954 rFormatSet, rShapeSet, bNoCharacterFormats,
2955 bNoParagraphFormats));
2956 SetAttrToMarked(aPaintSet, false /*bReplaceAll*/);
2959 // now apply character and paragraph formatting to text, if the shape has any
2960 SdrTextObj* pTextObj = DynCastSdrTextObj(pObj);
2961 if (pTextObj)
2963 sal_Int32 nText = pTextObj->getTextCount();
2965 while (--nText >= 0)
2967 SdrText* pText = pTextObj->getText(nText);
2968 ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, bNoCharacterFormats,
2969 bNoParagraphFormats);
2973 else
2975 ::Outliner* pOutliner = pOLV->GetOutliner();
2976 if (pOutliner)
2978 const EditEngine& rEditEngine = pOutliner->GetEditEngine();
2980 ESelection aSel(pOLV->GetSelection());
2981 if (!aSel.HasRange())
2982 pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD));
2984 const bool bRemoveParaAttribs = !bNoParagraphFormats;
2985 pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs);
2986 SfxItemSet aSet(pOLV->GetAttribs());
2987 SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(),
2988 rFormatSet, aSet, bNoCharacterFormats,
2989 bNoParagraphFormats));
2990 pOLV->SetAttribs(aPaintSet);
2994 // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
2995 SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
2996 if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
2997 && (pObj->GetObjIdentifier() == SdrObjKind::Table))
2999 auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj);
3000 if (pTable->getActiveCell().is() && mxSelectionController.is())
3002 mxSelectionController->SetAttributes(rFormatSet, false);
3007 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */