1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
74 SdrObjEditView::SdrObjEditView(SdrModel
& rSdrModel
, OutputDevice
* pOut
)
75 : SdrGlueEditView(rSdrModel
, pOut
)
76 , mpTextEditPV(nullptr)
77 , mpTextEditOutlinerView(nullptr)
78 , mpTextEditWin(nullptr)
79 , pTextEditCursorBuffer(nullptr)
84 , mbTextEditDontDelete(false)
85 , mbTextEditOnlyOneView(false)
86 , mbTextEditNewObj(false)
87 , mbQuickTextEditMode(true)
89 , mpOldTextEditUndoManager(nullptr)
93 SdrObjEditView::~SdrObjEditView()
95 mpTextEditWin
= nullptr; // so there's no ShowCursor in SdrEndTextEdit
96 assert(!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
)
109 SdrGlueEditView::MovAction(rPnt
);
112 void SdrObjEditView::EndAction()
116 SdrGlueEditView::EndAction();
119 void SdrObjEditView::BckAction()
122 SdrGlueEditView::BckAction();
125 void SdrObjEditView::BrkAction()
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
139 SdrViewIter::ForAllViews(pPageView
->GetPage(), [this](SdrView
* pView
) {
140 if (pView
== this || !pView
->IsTextEdit())
143 OutputDevice
* pOutDev
= GetFirstOutputDevice();
144 if (!pOutDev
|| pOutDev
->GetOutDevType() != OUTDEV_WINDOW
)
147 // Found one, so create an outliner view, to get invalidations when
148 // the text edit changes.
149 // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
150 // registers the view shell of this draw view, and not the view
152 OutlinerView
* pOutlinerView
153 = pView
->ImpMakeOutlinerView(pOutDev
->GetOwnerWindow(), nullptr, GetSfxViewShell());
154 pOutlinerView
->HideCursor();
155 pView
->GetTextEditOutliner()->InsertView(pOutlinerView
);
164 /// Removes outliner views registered in other draw views that use pOutputDevice.
165 void lcl_RemoveTextEditOutlinerViews(SdrObjEditView
const* pThis
, SdrPageView
const* pPageView
,
166 OutputDevice
const* pOutputDevice
)
168 if (!comphelper::LibreOfficeKit::isActive())
174 if (!pOutputDevice
|| pOutputDevice
->GetOutDevType() != OUTDEV_WINDOW
)
177 SdrViewIter::ForAllViews(pPageView
->GetPage(), [&pThis
, &pOutputDevice
](SdrView
* pView
) {
178 if (pView
== pThis
|| !pView
->IsTextEdit())
181 SdrOutliner
* pOutliner
= pView
->GetTextEditOutliner();
182 for (size_t nView
= 0; nView
< pOutliner
->GetViewCount(); ++nView
)
184 OutlinerView
* pOutlinerView
= pOutliner
->GetView(nView
);
185 if (pOutlinerView
->GetWindow()->GetOutDev() != pOutputDevice
)
188 pOutliner
->RemoveView(pOutlinerView
);
189 delete pOutlinerView
;
195 void SdrObjEditView::HideSdrPage()
197 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
199 if (mpTextEditPV
== GetSdrPageView())
201 // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
202 mpTextEditPV
= nullptr;
205 SdrGlueEditView::HideSdrPage();
208 void SdrObjEditView::TakeActionRect(tools::Rectangle
& rRect
) const
212 rRect
= pMacroObj
->GetCurrentBoundRect();
216 SdrGlueEditView::TakeActionRect(rRect
);
220 void SdrObjEditView::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
222 SdrGlueEditView::Notify(rBC
, rHint
);
223 if (mpTextEditOutliner
== nullptr)
226 // change of printer while editing
227 if (rHint
.GetId() != SfxHintId::ThisIsAnSdrHint
)
230 const SdrHint
* pSdrHint
= static_cast<const SdrHint
*>(&rHint
);
231 SdrHintKind eKind
= pSdrHint
->GetKind();
232 if (eKind
== SdrHintKind::RefDeviceChange
)
234 mpTextEditOutliner
->SetRefDevice(GetModel().GetRefDevice());
236 if (eKind
== SdrHintKind::DefaultTabChange
)
238 mpTextEditOutliner
->SetDefTab(GetModel().GetDefaultTabulator());
242 void SdrObjEditView::ModelHasChanged()
244 SdrGlueEditView::ModelHasChanged();
245 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
246 if (pTextObj
&& !pTextObj
->IsInserted())
247 SdrEndTextEdit(); // object deleted
248 // TextEditObj changed?
252 if (pTextObj
!= nullptr)
254 size_t nOutlViewCnt
= mpTextEditOutliner
->GetViewCount();
255 bool bAreaChg
= false;
256 bool bAnchorChg
= false;
257 bool bColorChg
= false;
258 bool bContourFrame
= pTextObj
->IsContourTextFrame();
259 EEAnchorMode
eNewAnchor(EEAnchorMode::VCenterHCenter
);
260 tools::Rectangle
aOldArea(aMinTextEditArea
);
261 aOldArea
.Union(aTextEditArea
);
266 tools::Rectangle aEditArea1
;
267 tools::Rectangle aMinArea1
;
268 pTextObj
->TakeTextEditArea(&aPaperMin1
, &aPaperMax1
, &aEditArea1
, &aMinArea1
);
269 Point
aPvOfs(pTextObj
->GetTextEditOffset());
271 // add possible GridOffset to up-to-now view-independent EditAreas
272 basegfx::B2DVector
aGridOffset(0.0, 0.0);
273 if (getPossibleGridOffsetForSdrObject(aGridOffset
, pTextObj
.get(), GetSdrPageView()))
275 const Point
aOffset(basegfx::fround(aGridOffset
.getX()),
276 basegfx::fround(aGridOffset
.getY()));
278 aEditArea1
+= aOffset
;
279 aMinArea1
+= aOffset
;
282 aEditArea1
.Move(aPvOfs
.X(), aPvOfs
.Y());
283 aMinArea1
.Move(aPvOfs
.X(), aPvOfs
.Y());
284 tools::Rectangle
aNewArea(aMinArea1
);
285 aNewArea
.Union(aEditArea1
);
287 if (aNewArea
!= aOldArea
|| aEditArea1
!= aTextEditArea
|| aMinArea1
!= aMinTextEditArea
288 || mpTextEditOutliner
->GetMinAutoPaperSize() != aPaperMin1
289 || mpTextEditOutliner
->GetMaxAutoPaperSize() != aPaperMax1
)
291 aTextEditArea
= aEditArea1
;
292 aMinTextEditArea
= aMinArea1
;
294 const bool bPrevUpdateLayout
= mpTextEditOutliner
->SetUpdateLayout(false);
295 mpTextEditOutliner
->SetMinAutoPaperSize(aPaperMin1
);
296 mpTextEditOutliner
->SetMaxAutoPaperSize(aPaperMax1
);
297 mpTextEditOutliner
->SetPaperSize(Size(0, 0)); // re-format Outliner
301 mpTextEditOutliner
->ClearPolygon();
302 EEControlBits nStat
= mpTextEditOutliner
->GetControlWord();
303 nStat
|= EEControlBits::AUTOPAGESIZE
;
304 mpTextEditOutliner
->SetControlWord(nStat
);
308 EEControlBits nStat
= mpTextEditOutliner
->GetControlWord();
309 nStat
&= ~EEControlBits::AUTOPAGESIZE
;
310 mpTextEditOutliner
->SetControlWord(nStat
);
311 tools::Rectangle aAnchorRect
;
312 pTextObj
->TakeTextAnchorRect(aAnchorRect
);
313 pTextObj
->ImpSetContourPolygon(*mpTextEditOutliner
, aAnchorRect
, true);
315 for (size_t nOV
= 0; nOV
< nOutlViewCnt
; nOV
++)
317 OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(nOV
);
318 EVControlBits nStat0
= pOLV
->GetControlWord();
319 EVControlBits nStat
= nStat0
;
320 // AutoViewSize only if not ContourFrame.
322 nStat
|= EVControlBits::AUTOSIZE
;
324 nStat
&= ~EVControlBits::AUTOSIZE
;
326 pOLV
->SetControlWord(nStat
);
329 mpTextEditOutliner
->SetUpdateLayout(bPrevUpdateLayout
);
333 if (mpTextEditOutlinerView
!= nullptr)
334 { // check fill and anchor
335 EEAnchorMode eOldAnchor
= mpTextEditOutlinerView
->GetAnchorMode();
336 eNewAnchor
= pTextObj
->GetOutlinerViewAnchorMode();
337 bAnchorChg
= eOldAnchor
!= eNewAnchor
;
338 Color
aOldColor(mpTextEditOutlinerView
->GetBackgroundColor());
339 aNewColor
= GetTextEditBackgroundColor(*this);
340 bColorChg
= aOldColor
!= aNewColor
;
342 // refresh always when it's a contour frame. That
343 // refresh is necessary since it triggers the repaint
344 // which makes the Handles visible. Changes at TakeTextRect()
345 // seem to have resulted in a case where no refresh is executed.
346 // Before that, a refresh must have been always executed
347 // (else this error would have happened earlier), thus I
348 // even think here a refresh should be done always.
349 // Since follow-up problems cannot even be guessed I only
350 // add this one more case to the if below.
351 // BTW: It's VERY bad style that here, inside ModelHasChanged()
352 // the outliner is again massively changed for the text object
353 // in text edit mode. Normally, all necessary data should be
354 // set at SdrBeginTextEdit(). Some changes and value assigns in
355 // SdrBeginTextEdit() are completely useless since they are set here
356 // again on ModelHasChanged().
357 if (bContourFrame
|| bAreaChg
|| bAnchorChg
|| bColorChg
)
359 for (size_t nOV
= 0; nOV
< nOutlViewCnt
; nOV
++)
361 OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(nOV
);
362 { // invalidate old OutlinerView area
363 vcl::Window
* pWin
= pOLV
->GetWindow();
364 tools::Rectangle
aTmpRect(aOldArea
);
365 sal_uInt16 nPixSiz
= pOLV
->GetInvalidateMore() + 1;
366 Size
aMore(pWin
->PixelToLogic(Size(nPixSiz
, nPixSiz
)));
367 aTmpRect
.AdjustLeft(-(aMore
.Width()));
368 aTmpRect
.AdjustRight(aMore
.Width());
369 aTmpRect
.AdjustTop(-(aMore
.Height()));
370 aTmpRect
.AdjustBottom(aMore
.Height());
371 InvalidateOneWin(*pWin
->GetOutDev(), aTmpRect
);
374 pOLV
->SetAnchorMode(eNewAnchor
);
376 pOLV
->SetBackgroundColor(aNewColor
);
379 aTextEditArea
); // because otherwise, we're not re-anchoring correctly
380 ImpInvalidateOutlinerView(*pOLV
);
382 mpTextEditOutlinerView
->ShowCursor();
385 ImpMakeTextCursorAreaVisible();
390 class TextEditFrameOverlayObject
;
391 class TextEditHighContrastOverlaySelection
;
394 Helper class to visualize the content of an active EditView as an
395 OverlayObject. These objects work with Primitives and are handled
396 from the OverlayManager(s) in place as needed.
398 It allows complete visualization of the content of the active
399 EditView without the need of Invalidates triggered by the EditView
400 and thus avoiding potentially expensive repaints by using the
401 automatically buffered Overlay mechanism.
403 It buffers as much as possible locally and *only* triggers a real
404 change (see call to objectChange()) when really needed.
406 class TextEditOverlayObject
: public sdr::overlay::OverlayObject
409 /// local access to associated sdr::overlay::OverlaySelection
410 std::unique_ptr
<sdr::overlay::OverlaySelection
> mxOverlayTransparentSelection
;
411 std::unique_ptr
<TextEditHighContrastOverlaySelection
> mxOverlayHighContrastSelection
;
412 std::unique_ptr
<TextEditFrameOverlayObject
> mxOverlayFrame
;
414 /// local definition depends on active OutlinerView
415 OutlinerView
& mrOutlinerView
;
417 /// geometry definitions with buffering
418 basegfx::B2DRange maLastRange
;
419 basegfx::B2DRange maRange
;
421 /// text content definitions with buffering
422 drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives
;
423 drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives
;
425 // geometry creation for OverlayObject, can use local *Last* values
426 virtual drawinglayer::primitive2d::Primitive2DContainer
427 createOverlayObjectPrimitive2DSequence() override
;
430 TextEditOverlayObject(const Color
& rColor
, OutlinerView
& rOutlinerView
);
431 virtual ~TextEditOverlayObject() override
;
433 sdr::overlay::OverlayObject
* getOverlaySelection();
434 sdr::overlay::OverlayObject
* getOverlayFrame();
436 const OutlinerView
& getOutlinerView() const { return mrOutlinerView
; }
438 /// override to check conditions for last createOverlayObjectPrimitive2DSequence
439 virtual drawinglayer::primitive2d::Primitive2DContainer
440 getOverlayObjectPrimitive2DSequence() const override
;
442 // data write access. In this OverlayObject we only have the
443 // callback that triggers detecting if something *has* changed
444 void checkDataChange(const basegfx::B2DRange
& rMinTextEditArea
);
445 void checkSelectionChange();
447 const basegfx::B2DRange
& getRange() const { return maRange
; }
448 const drawinglayer::primitive2d::Primitive2DContainer
& getTextPrimitives() const
450 return maTextPrimitives
;
454 class TextEditFrameOverlayObject
: public sdr::overlay::OverlayObject
457 const TextEditOverlayObject
& mrTextEditOverlayObject
;
459 // geometry creation for OverlayObject, can use local *Last* values
460 virtual drawinglayer::primitive2d::Primitive2DContainer
461 createOverlayObjectPrimitive2DSequence() override
;
464 TextEditFrameOverlayObject(const TextEditOverlayObject
& rTextEditOverlayObject
);
465 using sdr::overlay::OverlayObject::objectChange
;
466 virtual ~TextEditFrameOverlayObject() override
;
469 class TextEditHighContrastOverlaySelection
: public sdr::overlay::OverlayObject
472 const TextEditOverlayObject
& mrTextEditOverlayObject
;
473 std::vector
<basegfx::B2DRange
> maRanges
;
475 // geometry creation for OverlayObject, can use local *Last* values
476 virtual drawinglayer::primitive2d::Primitive2DContainer
477 createOverlayObjectPrimitive2DSequence() override
;
480 TextEditHighContrastOverlaySelection(const TextEditOverlayObject
& rTextEditOverlayObject
);
481 void setRanges(std::vector
<basegfx::B2DRange
>&& rNew
);
482 virtual ~TextEditHighContrastOverlaySelection() override
;
485 TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection(
486 const TextEditOverlayObject
& rTextEditOverlayObject
)
487 : OverlayObject(rTextEditOverlayObject
.getBaseColor())
488 , mrTextEditOverlayObject(rTextEditOverlayObject
)
490 allowAntiAliase(rTextEditOverlayObject
.allowsAntiAliase());
491 // use selection colors in HighContrast mode
492 mbHighContrastSelection
= true;
495 void TextEditHighContrastOverlaySelection::setRanges(std::vector
<basegfx::B2DRange
>&& rNew
)
497 if (rNew
!= maRanges
)
499 maRanges
= std::move(rNew
);
504 drawinglayer::primitive2d::Primitive2DContainer
505 TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
507 drawinglayer::primitive2d::Primitive2DContainer aRetval
;
509 size_t nCount
= maRanges
.size();
513 basegfx::B2DPolyPolygon aClipPolyPolygon
;
515 basegfx::BColor
aRGBColor(getBaseColor().getBColor());
517 for (size_t a
= 0; a
< nCount
; ++a
)
518 aClipPolyPolygon
.append(basegfx::utils::createPolygonFromRect(maRanges
[a
]));
520 // This is used in high contrast mode, we will render the selection
521 // with the bg forced to the selection Highlight color and the fg color
522 // forced to the HighlightText color
523 aRetval
.append(drawinglayer::primitive2d::Primitive2DReference(
524 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
525 basegfx::B2DPolyPolygon(
526 basegfx::utils::createPolygonFromRect(aClipPolyPolygon
.getB2DRange())),
528 aRetval
.append(mrTextEditOverlayObject
.getTextPrimitives());
529 aRetval
.append(drawinglayer::primitive2d::Primitive2DReference(
530 new drawinglayer::primitive2d::MaskPrimitive2D(aClipPolyPolygon
, std::move(aRetval
))));
536 TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection()
538 if (getOverlayManager())
540 getOverlayManager()->remove(*this);
544 sdr::overlay::OverlayObject
* TextEditOverlayObject::getOverlaySelection()
546 if (mxOverlayTransparentSelection
)
547 return mxOverlayTransparentSelection
.get();
548 return mxOverlayHighContrastSelection
.get();
551 sdr::overlay::OverlayObject
* TextEditOverlayObject::getOverlayFrame()
554 mxOverlayFrame
.reset(new TextEditFrameOverlayObject(*this));
555 return mxOverlayFrame
.get();
558 drawinglayer::primitive2d::Primitive2DContainer
559 TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
561 drawinglayer::primitive2d::Primitive2DContainer aRetval
;
563 // add buffered TextPrimitives
564 aRetval
.append(maTextPrimitives
);
569 drawinglayer::primitive2d::Primitive2DContainer
570 TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence()
572 drawinglayer::primitive2d::Primitive2DContainer aRetval
;
574 /// outer frame visualization
575 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
576 const sal_uInt16
nPixSiz(mrTextEditOverlayObject
.getOutlinerView().GetInvalidateMore() - 1);
578 aRetval
.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
579 mrTextEditOverlayObject
.getRange(), getBaseColor().getBColor(), fTransparence
,
580 std::max(6, nPixSiz
- 2), // grow
587 TextEditOverlayObject::TextEditOverlayObject(const Color
& rColor
, OutlinerView
& rOutlinerView
)
588 : OverlayObject(rColor
)
589 , mrOutlinerView(rOutlinerView
)
591 // no AA for TextEdit overlay
592 allowAntiAliase(false);
594 // create local OverlaySelection - this is an integral part of EditText
596 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
598 mxOverlayHighContrastSelection
.reset(new TextEditHighContrastOverlaySelection(*this));
602 std::vector
<basegfx::B2DRange
> aEmptySelection
{};
603 mxOverlayTransparentSelection
.reset(new sdr::overlay::OverlaySelection(
604 sdr::overlay::OverlayType::Transparent
, rColor
, std::move(aEmptySelection
), true));
608 TextEditOverlayObject::~TextEditOverlayObject()
610 mxOverlayTransparentSelection
.reset();
611 mxOverlayHighContrastSelection
.reset();
613 if (getOverlayManager())
615 getOverlayManager()->remove(*this);
619 TextEditFrameOverlayObject::TextEditFrameOverlayObject(
620 const TextEditOverlayObject
& rTextEditOverlayObject
)
621 : OverlayObject(rTextEditOverlayObject
.getBaseColor())
622 , mrTextEditOverlayObject(rTextEditOverlayObject
)
624 allowAntiAliase(rTextEditOverlayObject
.allowsAntiAliase());
625 // use selection colors in HighContrast mode
626 mbHighContrastSelection
= true;
629 TextEditFrameOverlayObject::~TextEditFrameOverlayObject()
631 if (getOverlayManager())
633 getOverlayManager()->remove(*this);
637 drawinglayer::primitive2d::Primitive2DContainer
638 TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
640 if (!getPrimitive2DSequence().empty())
642 if (!maRange
.equal(maLastRange
) || maLastTextPrimitives
!= maTextPrimitives
)
644 // conditions of last local decomposition have changed, delete to force new evaluation
645 const_cast<TextEditOverlayObject
*>(this)->resetPrimitive2DSequence();
649 if (getPrimitive2DSequence().empty())
651 // remember new buffered values
652 const_cast<TextEditOverlayObject
*>(this)->maLastRange
= maRange
;
653 const_cast<TextEditOverlayObject
*>(this)->maLastTextPrimitives
= maTextPrimitives
;
656 // call base implementation
657 return OverlayObject::getOverlayObjectPrimitive2DSequence();
660 void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange
& rMinTextEditArea
)
662 bool bObjectChange(false);
664 // check current range
665 const tools::Rectangle
aOutArea(mrOutlinerView
.GetOutputArea());
666 basegfx::B2DRange aNewRange
= vcl::unotools::b2DRectangleFromRectangle(aOutArea
);
667 aNewRange
.expand(rMinTextEditArea
);
669 if (aNewRange
!= maRange
)
672 bObjectChange
= true;
675 // check if text primitives did change
676 SdrOutliner
* pSdrOutliner
= dynamic_cast<SdrOutliner
*>(getOutlinerView().GetOutliner());
680 // get TextPrimitives directly from active Outliner
681 basegfx::B2DHomMatrix aNewTransformA
;
682 basegfx::B2DHomMatrix aNewTransformB
;
683 basegfx::B2DRange aClipRange
;
684 drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives
;
686 // active Outliner is always in unified oriented coordinate system (currently)
687 // so just translate to TopLeft of visible Range. Keep in mind that top-left
688 // depends on vertical text and top-to-bottom text attributes
689 const tools::Rectangle
aVisArea(mrOutlinerView
.GetVisArea());
690 const bool bVerticalWriting(pSdrOutliner
->IsVertical());
691 const bool bTopToBottom(pSdrOutliner
->IsTopToBottom());
692 const double fStartInX(bVerticalWriting
&& bTopToBottom
693 ? aOutArea
.Right() - aVisArea
.Left()
694 : aOutArea
.Left() - aVisArea
.Left());
695 const double fStartInY(bVerticalWriting
&& !bTopToBottom
696 ? aOutArea
.Bottom() - aVisArea
.Top()
697 : aOutArea
.Top() - aVisArea
.Top());
699 aNewTransformB
.translate(fStartInX
, fStartInY
);
701 // get the current TextPrimitives. This is the most expensive part
702 // of this mechanism, it *may* be possible to buffer layouted
703 // primitives per ParaPortion with/in/dependent on the EditEngine
704 // content if needed. For now, get and compare
705 SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
706 aNewTextPrimitives
, *pSdrOutliner
, aNewTransformA
, aNewTransformB
, aClipRange
);
708 if (aNewTextPrimitives
!= maTextPrimitives
)
710 maTextPrimitives
= std::move(aNewTextPrimitives
);
711 bObjectChange
= true;
717 // if there really *was* a change signal the OverlayManager to
718 // refresh this object's visualization
722 mxOverlayFrame
->objectChange();
724 // on data change, always do a SelectionChange, too
725 // since the selection is an integral part of text visualization
726 checkSelectionChange();
730 void TextEditOverlayObject::checkSelectionChange()
732 if (!(getOverlaySelection() && getOverlayManager()))
735 std::vector
<tools::Rectangle
> aLogicRects
;
736 std::vector
<basegfx::B2DRange
> aLogicRanges
;
737 const Size
aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
739 // get logic selection
740 getOutlinerView().GetSelectionRectangles(aLogicRects
);
742 aLogicRanges
.reserve(aLogicRects
.size());
743 for (const auto& aRect
: aLogicRects
)
745 // convert from logic Rectangles to logic Ranges, do not forget to add
746 // one Unit (in this case logical units for one pixel, pre-calculated)
747 aLogicRanges
.emplace_back(
748 aRect
.Left() - aLogicPixel
.Width(), aRect
.Top() - aLogicPixel
.Height(),
749 aRect
.Right() + aLogicPixel
.Width(), aRect
.Bottom() + aLogicPixel
.Height());
752 if (mxOverlayTransparentSelection
)
753 mxOverlayTransparentSelection
->setRanges(std::move(aLogicRanges
));
755 mxOverlayHighContrastSelection
->setRanges(std::move(aLogicRanges
));
757 } // end of anonymous namespace
761 // callback from the active EditView, forward to evtl. existing instances of the
762 // TextEditOverlayObject(s). This will additionally update the selection which
763 // is an integral part of the text visualization
764 void SdrObjEditView::EditViewInvalidate(const tools::Rectangle
&)
769 // MinTextRange may have changed. Forward it, too
770 const basegfx::B2DRange aMinTextRange
771 = vcl::unotools::b2DRectangleFromRectangle(aMinTextEditArea
);
773 for (sal_uInt32
a(0); a
< maTEOverlayGroup
.count(); a
++)
775 TextEditOverlayObject
* pCandidate
776 = dynamic_cast<TextEditOverlayObject
*>(&maTEOverlayGroup
.getOverlayObject(a
));
780 pCandidate
->checkDataChange(aMinTextRange
);
785 // callback from the active EditView, forward to evtl. existing instances of the
786 // TextEditOverlayObject(s). This cvall *only* updates the selection visualization
787 // which is e.g. used when only the selection is changed, but not the text
788 void SdrObjEditView::EditViewSelectionChange()
793 for (sal_uInt32
a(0); a
< maTEOverlayGroup
.count(); a
++)
795 TextEditOverlayObject
* pCandidate
796 = dynamic_cast<TextEditOverlayObject
*>(&maTEOverlayGroup
.getOverlayObject(a
));
800 pCandidate
->checkSelectionChange();
805 OutputDevice
& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin
->GetOutDev(); }
807 Point
SdrObjEditView::EditViewPointerPosPixel() const
809 return mpTextEditWin
->GetPointerPosPixel();
812 css::uno::Reference
<css::datatransfer::clipboard::XClipboard
> SdrObjEditView::GetClipboard() const
816 return mpTextEditWin
->GetClipboard();
819 css::uno::Reference
<css::datatransfer::dnd::XDropTarget
> SdrObjEditView::GetDropTarget()
823 return mpTextEditWin
->GetDropTarget();
826 void SdrObjEditView::EditViewInputContext(const InputContext
& rInputContext
)
830 mpTextEditWin
->SetInputContext(rInputContext
);
833 void SdrObjEditView::EditViewCursorRect(const tools::Rectangle
& rRect
, int nExtTextInputWidth
)
837 mpTextEditWin
->SetCursorRect(&rRect
, nExtTextInputWidth
);
840 void SdrObjEditView::TextEditDrawing(SdrPaintWindow
& rPaintWindow
)
842 if (!comphelper::LibreOfficeKit::isActive())
844 // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
845 // to update accordingly (will update selection, too). Suppress new
846 // stuff when LibreOfficeKit is active
847 EditViewInvalidate(tools::Rectangle());
851 // draw old text edit stuff
854 const SdrOutliner
* pActiveOutliner
= GetTextEditOutliner();
858 const sal_uInt32
nViewCount(pActiveOutliner
->GetViewCount());
862 const vcl::Region
& rRedrawRegion
= rPaintWindow
.GetRedrawRegion();
863 const tools::Rectangle
aCheckRect(rRedrawRegion
.GetBoundRect());
865 for (sal_uInt32
i(0); i
< nViewCount
; i
++)
867 OutlinerView
* pOLV
= pActiveOutliner
->GetView(i
);
869 // If rPaintWindow knows that the output device is a render
870 // context and is aware of the underlying vcl::Window,
871 // compare against that; that's how double-buffering can
872 // still find the matching OutlinerView.
873 OutputDevice
* pOutputDevice
= rPaintWindow
.GetWindow()
874 ? rPaintWindow
.GetWindow()->GetOutDev()
875 : &rPaintWindow
.GetOutputDevice();
876 if (pOLV
->GetWindow()->GetOutDev() == pOutputDevice
877 || comphelper::LibreOfficeKit::isActive())
879 ImpPaintOutlinerView(*pOLV
, aCheckRect
,
880 rPaintWindow
.GetTargetOutputDevice());
890 void SdrObjEditView::ImpPaintOutlinerView(OutlinerView
& rOutlView
, const tools::Rectangle
& rRect
,
891 OutputDevice
& rTargetDevice
) const
893 const SdrTextObj
* pText
= GetTextEditObject();
894 bool bTextFrame(pText
&& pText
->IsTextFrame());
895 bool bFitToSize(mpTextEditOutliner
->GetControlWord() & EEControlBits::STRETCHING
);
896 bool bModified(mpTextEditOutliner
->IsModified());
897 tools::Rectangle
aBlankRect(rOutlView
.GetOutputArea());
898 aBlankRect
.Union(aMinTextEditArea
);
899 tools::Rectangle
aPixRect(rTargetDevice
.LogicToPixel(aBlankRect
));
901 // in the tiled rendering case, the setup is incomplete, and we very
902 // easily get an empty rRect on input - that will cause that everything is
903 // clipped; happens in case of editing text inside a shape in Calc.
904 // FIXME would be better to complete the setup so that we don't get an
906 if (!comphelper::LibreOfficeKit::isActive() || !rRect
.IsEmpty())
907 aBlankRect
.Intersection(rRect
);
909 rOutlView
.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
910 rOutlView
.Paint(aBlankRect
, &rTargetDevice
);
914 mpTextEditOutliner
->ClearModifyFlag();
917 if (bTextFrame
&& !bFitToSize
)
919 // completely reworked to use primitives; this ensures same look and functionality
920 const drawinglayer::geometry::ViewInformation2D aViewInformation2D
;
921 std::unique_ptr
<drawinglayer::processor2d::BaseProcessor2D
> xProcessor(
922 drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice
,
923 aViewInformation2D
));
925 const bool bMapModeEnabled(rTargetDevice
.IsMapModeEnabled());
926 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(aPixRect
);
927 const Color
aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
928 const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
929 const sal_uInt16
nPixSiz(rOutlView
.GetInvalidateMore() - 1);
930 const drawinglayer::primitive2d::Primitive2DReference
xReference(
931 new drawinglayer::primitive2d::OverlayRectanglePrimitive(
932 aRange
, aHilightColor
.getBColor(), fTransparence
, std::max(6, nPixSiz
- 2), // grow
935 const drawinglayer::primitive2d::Primitive2DContainer aSequence
{ xReference
};
937 rTargetDevice
.EnableMapMode(false);
938 xProcessor
->process(aSequence
);
939 rTargetDevice
.EnableMapMode(bMapModeEnabled
);
942 rOutlView
.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
945 void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView
const& rOutlView
) const
947 vcl::Window
* pWin
= rOutlView
.GetWindow();
952 const SdrTextObj
* pText
= GetTextEditObject();
953 bool bTextFrame(pText
&& pText
->IsTextFrame());
954 bool bFitToSize(pText
&& pText
->IsFitToSize());
956 if (!bTextFrame
|| bFitToSize
)
959 tools::Rectangle
aBlankRect(rOutlView
.GetOutputArea());
960 aBlankRect
.Union(aMinTextEditArea
);
961 tools::Rectangle
aPixRect(pWin
->LogicToPixel(aBlankRect
));
962 sal_uInt16
nPixSiz(rOutlView
.GetInvalidateMore() - 1);
964 aPixRect
.AdjustLeft(-1);
965 aPixRect
.AdjustTop(-1);
966 aPixRect
.AdjustRight(1);
967 aPixRect
.AdjustBottom(1);
970 // limit xPixRect because of driver problems when pixel coordinates are too far out
971 Size
aMaxXY(pWin
->GetOutputSizePixel());
972 tools::Long
a(2 * nPixSiz
);
973 tools::Long
nMaxX(aMaxXY
.Width() + a
);
974 tools::Long
nMaxY(aMaxXY
.Height() + a
);
976 if (aPixRect
.Left() < -a
)
977 aPixRect
.SetLeft(-a
);
978 if (aPixRect
.Top() < -a
)
980 if (aPixRect
.Right() > nMaxX
)
981 aPixRect
.SetRight(nMaxX
);
982 if (aPixRect
.Bottom() > nMaxY
)
983 aPixRect
.SetBottom(nMaxY
);
986 tools::Rectangle
aOuterPix(aPixRect
);
987 aOuterPix
.AdjustLeft(-nPixSiz
);
988 aOuterPix
.AdjustTop(-nPixSiz
);
989 aOuterPix
.AdjustRight(nPixSiz
);
990 aOuterPix
.AdjustBottom(nPixSiz
);
992 bool bMapModeEnabled(pWin
->IsMapModeEnabled());
993 pWin
->EnableMapMode(false);
994 pWin
->Invalidate(aOuterPix
);
995 pWin
->EnableMapMode(bMapModeEnabled
);
998 OutlinerView
* SdrObjEditView::ImpMakeOutlinerView(vcl::Window
* pWin
, OutlinerView
* pGivenView
,
999 SfxViewShell
* pViewShell
) const
1002 Color
aBackground(GetTextEditBackgroundColor(*this));
1003 rtl::Reference
<SdrTextObj
> pText
= mxWeakTextEditObj
.get();
1004 bool bTextFrame
= pText
!= nullptr && pText
->IsTextFrame();
1005 bool bContourFrame
= pText
!= nullptr && pText
->IsContourTextFrame();
1006 // create OutlinerView
1007 OutlinerView
* pOutlView
= pGivenView
;
1008 mpTextEditOutliner
->SetUpdateLayout(false);
1010 if (pOutlView
== nullptr)
1012 pOutlView
= new OutlinerView(mpTextEditOutliner
.get(), pWin
);
1016 pOutlView
->SetWindow(pWin
);
1020 pOutlView
->GetEditView().SetNegativeX(mbNegativeX
);
1022 // disallow scrolling
1023 EVControlBits nStat
= pOutlView
->GetControlWord();
1024 nStat
&= ~EVControlBits::AUTOSCROLL
;
1025 // AutoViewSize only if not ContourFrame.
1027 nStat
|= EVControlBits::AUTOSIZE
;
1030 sal_uInt16 nPixSiz
= maHdlList
.GetHdlSize() * 2 + 1;
1031 nStat
|= EVControlBits::INVONEMORE
;
1032 pOutlView
->SetInvalidateMore(nPixSiz
);
1034 pOutlView
->SetControlWord(nStat
);
1035 pOutlView
->SetBackgroundColor(aBackground
);
1037 // In case we're in the process of constructing a new view shell,
1038 // SfxViewShell::Current() may still point to the old one. So if possible,
1039 // depend on the application owning this draw view to provide the view
1041 SfxViewShell
* pSfxViewShell
= pViewShell
? pViewShell
: GetSfxViewShell();
1042 pOutlView
->RegisterViewShell(pSfxViewShell
? pSfxViewShell
: SfxViewShell::Current());
1044 if (pText
!= nullptr)
1046 pOutlView
->SetAnchorMode(pText
->GetOutlinerViewAnchorMode());
1047 mpTextEditOutliner
->SetFixedCellHeight(
1048 pText
->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT
).GetValue());
1050 // do update before setting output area so that aTextEditArea can be recalculated
1051 mpTextEditOutliner
->SetUpdateLayout(true);
1052 pOutlView
->SetOutputArea(aTextEditArea
);
1053 ImpInvalidateOutlinerView(*pOutlView
);
1057 IMPL_LINK(SdrObjEditView
, ImpOutlinerStatusEventHdl
, EditStatus
&, rEditStat
, void)
1059 if (mpTextEditOutliner
)
1061 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1064 pTextObj
->onEditOutlinerStatusEvent(&rEditStat
);
1069 void SdrObjEditView::ImpChainingEventHdl()
1071 if (!mpTextEditOutliner
)
1074 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1075 OutlinerView
* pOLV
= GetTextEditOutlinerView();
1076 if (pTextObj
&& pOLV
)
1078 TextChain
* pTextChain
= pTextObj
->GetTextChain();
1080 // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
1081 if (!pTextObj
->IsChainable())
1085 // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
1086 if (pTextChain
->GetNilChainingEvent(pTextObj
.get()))
1091 // We prevent to trigger further handling of overflow/underflow for pTextObj
1092 pTextChain
->SetNilChainingEvent(pTextObj
.get(), true); // XXX
1094 // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
1095 pTextChain
->SetPreChainingSel(pTextObj
.get(), pOLV
->GetSelection());
1096 //maPreChainingSel = new ESelection(pOLV->GetSelection());
1099 const int nText
= 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
1101 const bool bUndoEnabled
= IsUndoEnabled();
1102 std::unique_ptr
<SdrUndoObjSetText
> pTxtUndo
;
1105 dynamic_cast<SdrUndoObjSetText
*>(GetModel()
1106 .GetSdrUndoFactory()
1107 .CreateUndoObjectSetText(*pTextObj
, nText
)
1110 // trigger actual chaining
1111 pTextObj
->onChainingEvent();
1115 pTxtUndo
->AfterSetText();
1116 if (!pTxtUndo
->IsDifferent())
1123 AddUndo(std::move(pTxtUndo
));
1125 //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
1126 //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
1128 // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
1129 pTextChain
->SetNilChainingEvent(pTextObj
.get(), false);
1134 SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
1138 IMPL_LINK_NOARG(SdrObjEditView
, ImpAfterCutOrPasteChainingEventHdl
, LinkParamNone
*, void)
1140 SdrTextObj
* pTextObj
= GetTextEditObject();
1143 ImpChainingEventHdl();
1144 TextChainCursorManager
aCursorManager(this, pTextObj
);
1145 ImpMoveCursorAfterChainingEvent(&aCursorManager
);
1148 void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager
* pCursorManager
)
1150 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1152 if (!pTextObj
|| !pCursorManager
)
1155 // Check if it has links to move it to
1156 if (!pTextObj
|| !pTextObj
->IsChainable())
1159 TextChain
* pTextChain
= pTextObj
->GetTextChain();
1160 ESelection aNewSel
= pTextChain
->GetPostChainingSel(pTextObj
.get());
1162 pCursorManager
->HandleCursorEventAfterChaining(pTextChain
->GetCursorEvent(pTextObj
.get()),
1166 pTextChain
->SetCursorEvent(pTextObj
.get(), CursorChainingEvent::NULL_EVENT
);
1169 IMPL_LINK(SdrObjEditView
, ImpOutlinerCalcFieldValueHdl
, EditFieldInfo
*, pFI
, void)
1172 OUString
& rStr
= pFI
->GetRepresentation();
1174 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1175 if (pTextObj
!= nullptr)
1177 std::optional
<Color
> pTxtCol
;
1178 std::optional
<Color
> pFldCol
;
1179 std::optional
<FontLineStyle
> pFldLineStyle
;
1180 bOk
= pTextObj
->CalcFieldValue(pFI
->GetField(), pFI
->GetPara(), pFI
->GetPos(), true,
1181 pTxtCol
, pFldCol
, pFldLineStyle
, rStr
);
1186 pFI
->SetTextColor(*pTxtCol
);
1190 pFI
->SetFontLineStyle(*pFldLineStyle
);
1194 pFI
->SetFieldColor(*pFldCol
);
1198 pFI
->SetFieldColor(COL_LIGHTGRAY
); // TODO: remove this later on (357)
1202 Outliner
& rDrawOutl
= GetModel().GetDrawOutliner(pTextObj
.get());
1203 Link
<EditFieldInfo
*, void> aDrawOutlLink
= rDrawOutl
.GetCalcFieldValueHdl();
1204 if (!bOk
&& aDrawOutlLink
.IsSet())
1206 aDrawOutlLink
.Call(pFI
);
1207 bOk
= !rStr
.isEmpty();
1211 aOldCalcFieldValueLink
.Call(pFI
);
1215 IMPL_LINK_NOARG(SdrObjEditView
, EndTextEditHdl
, SdrUndoManager
*, void) { SdrEndTextEdit(); }
1217 // Default implementation - null UndoManager
1218 std::unique_ptr
<SdrUndoManager
> SdrObjEditView::createLocalTextUndoManager()
1220 SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
1221 return std::unique_ptr
<SdrUndoManager
>();
1224 bool SdrObjEditView::SdrBeginTextEdit(SdrObject
* pObj_
, SdrPageView
* pPV
, vcl::Window
* pWin
,
1225 bool bIsNewObj
, SdrOutliner
* pGivenOutliner
,
1226 OutlinerView
* pGivenOutlinerView
, bool bDontDeleteOutliner
,
1227 bool bOnlyOneView
, bool bGrabFocus
)
1229 // FIXME cannot be an assert() yet, the code is not ready for that;
1230 // eg. press F7 in Impress when you are inside a text object with spelling
1231 // mistakes => boom; and it is unclear how to avoid that
1232 SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
1233 // FIXME this encourages all sorts of bad habits and should be removed
1236 SdrTextObj
* pObj
= DynCastSdrTextObj(pObj_
);
1238 return false; // currently only possible with text objects
1240 if (bGrabFocus
&& pWin
)
1242 // attention, this call may cause an EndTextEdit() call to this view
1243 pWin
->GrabFocus(); // to force the cursor into the edit view
1246 mbTextEditDontDelete
= bDontDeleteOutliner
&& pGivenOutliner
!= nullptr;
1247 mbTextEditOnlyOneView
= bOnlyOneView
;
1248 mbTextEditNewObj
= bIsNewObj
;
1249 const sal_uInt32
nWinCount(PaintWindowCount());
1255 for (sal_uInt32 i
= 0; i
< nWinCount
&& !pWin
; i
++)
1257 SdrPaintWindow
* pPaintWindow
= GetPaintWindow(i
);
1259 if (OUTDEV_WINDOW
== pPaintWindow
->GetOutputDevice().GetOutDevType())
1261 pWin
= pPaintWindow
->GetOutputDevice().GetOwnerWindow();
1265 // break, when no window exists
1274 pPV
= GetSdrPageView();
1276 // break, when no PageView for the object exists
1283 // no TextEdit on objects in locked Layer
1284 if (pPV
&& pPV
->GetLockedLayers().IsSet(pObj
->GetLayer()))
1289 if (mpTextEditOutliner
)
1291 OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
1292 mpTextEditOutliner
.reset();
1297 mpTextEditWin
= pWin
;
1299 mxWeakTextEditObj
= pObj
;
1302 mpTextEditOutliner
.reset(pGivenOutliner
);
1303 pGivenOutliner
= nullptr; // so we don't delete it on the error path
1307 = SdrMakeOutliner(OutlinerMode::TextObject
, pObj
->getSdrModelFromSdrObject());
1310 mpTextEditOutliner
->ForceAutoColor(SvtAccessibilityOptions::GetIsAutomaticFontColor());
1313 aOldCalcFieldValueLink
= mpTextEditOutliner
->GetCalcFieldValueHdl();
1314 // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
1315 mpTextEditOutliner
->SetCalcFieldValueHdl(
1316 LINK(this, SdrObjEditView
, ImpOutlinerCalcFieldValueHdl
));
1317 mpTextEditOutliner
->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView
, BeginPasteOrDropHdl
));
1318 mpTextEditOutliner
->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView
, EndPasteOrDropHdl
));
1320 // It is just necessary to make the visualized page known. Set it.
1321 mpTextEditOutliner
->setVisualizedPage(pPV
->GetPage());
1323 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1324 mpTextEditOutliner
->SetTextObjNoInit(pTextObj
.get());
1326 if (pTextObj
->BegTextEdit(*mpTextEditOutliner
))
1328 // switch off any running TextAnimations
1329 pTextObj
->SetTextAnimationAllowed(false);
1331 // remember old cursor
1332 if (mpTextEditOutliner
->GetViewCount() != 0)
1334 mpTextEditOutliner
->RemoveView(static_cast<size_t>(0));
1337 // Determine EditArea via TakeTextEditArea.
1338 // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
1339 // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
1340 pTextObj
->TakeTextEditArea(nullptr, nullptr, &aTextEditArea
, &aMinTextEditArea
);
1342 tools::Rectangle aTextRect
;
1343 tools::Rectangle aAnchorRect
;
1344 pTextObj
->TakeTextRect(*mpTextEditOutliner
, aTextRect
, true,
1345 &aAnchorRect
/* Give true here, not false */);
1347 if (!pTextObj
->IsContourTextFrame())
1349 // FitToSize not together with ContourFrame, for now
1350 if (pTextObj
->IsFitToSize())
1351 aTextRect
= aAnchorRect
;
1354 aTextEditArea
= aTextRect
;
1356 // add possible GridOffset to up-to-now view-independent EditAreas
1357 basegfx::B2DVector
aGridOffset(0.0, 0.0);
1358 if (getPossibleGridOffsetForSdrObject(aGridOffset
, pTextObj
.get(), pPV
))
1360 const Point
aOffset(basegfx::fround(aGridOffset
.getX()),
1361 basegfx::fround(aGridOffset
.getY()));
1363 aTextEditArea
+= aOffset
;
1364 aMinTextEditArea
+= aOffset
;
1367 Point
aPvOfs(pTextObj
->GetTextEditOffset());
1368 aTextEditArea
.Move(aPvOfs
.X(), aPvOfs
.Y());
1369 aMinTextEditArea
.Move(aPvOfs
.X(), aPvOfs
.Y());
1370 pTextEditCursorBuffer
= pWin
->GetCursor();
1372 maHdlList
.SetMoveOutside(true);
1374 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1375 // to call AdjustMarkHdl() always.
1378 mpTextEditOutlinerView
= ImpMakeOutlinerView(pWin
, pGivenOutlinerView
);
1380 if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView
)
1382 // activate visualization of EditView on Overlay, suppress when
1383 // LibreOfficeKit is active
1384 mpTextEditOutlinerView
->GetEditView().setEditViewCallbacks(this);
1386 const Color
aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
1387 const SdrTextObj
* pText
= GetTextEditObject();
1388 // show for cases like tdf#94223 but not for table cells like tdf#151311
1389 const bool bVisualizeSurroundingFrame(
1390 pText
&& pText
->GetObjIdentifier() != SdrObjKind::Table
);
1391 SdrPageView
* pPageView
= GetSdrPageView();
1395 for (sal_uInt32
b(0); b
< pPageView
->PageWindowCount(); b
++)
1397 const SdrPageWindow
& rPageWindow
= *pPageView
->GetPageWindow(b
);
1399 if (rPageWindow
.GetPaintWindow().OutputToWindow())
1401 const rtl::Reference
<sdr::overlay::OverlayManager
>& xManager
1402 = rPageWindow
.GetOverlayManager();
1405 std::unique_ptr
<TextEditOverlayObject
> pNewTextEditOverlayObject(
1406 new TextEditOverlayObject(aHilightColor
,
1407 *mpTextEditOutlinerView
));
1409 xManager
->add(*pNewTextEditOverlayObject
);
1410 if (bVisualizeSurroundingFrame
)
1411 xManager
->add(*pNewTextEditOverlayObject
->getOverlayFrame());
1412 xManager
->add(*pNewTextEditOverlayObject
->getOverlaySelection());
1414 maTEOverlayGroup
.append(std::move(pNewTextEditOverlayObject
));
1421 // check if this view is already inserted
1422 size_t i2
, nCount
= mpTextEditOutliner
->GetViewCount();
1423 for (i2
= 0; i2
< nCount
; i2
++)
1425 if (mpTextEditOutliner
->GetView(i2
) == mpTextEditOutlinerView
)
1430 mpTextEditOutliner
->InsertView(mpTextEditOutlinerView
, 0);
1432 maHdlList
.SetMoveOutside(false);
1433 maHdlList
.SetMoveOutside(true);
1435 // register all windows as OutlinerViews with the Outliner
1438 for (sal_uInt32 i
= 0; i
< nWinCount
; i
++)
1440 SdrPaintWindow
* pPaintWindow
= GetPaintWindow(i
);
1441 OutputDevice
& rOutDev
= pPaintWindow
->GetOutputDevice();
1443 if (&rOutDev
!= pWin
->GetOutDev() && OUTDEV_WINDOW
== rOutDev
.GetOutDevType())
1445 OutlinerView
* pOutlView
1446 = ImpMakeOutlinerView(rOutDev
.GetOwnerWindow(), nullptr);
1447 mpTextEditOutliner
->InsertView(pOutlView
, static_cast<sal_uInt16
>(i
));
1451 if (comphelper::LibreOfficeKit::isActive())
1453 // Register an outliner view for all other sdr views that
1454 // show the same page, so that when the text edit changes,
1455 // all interested windows get an invalidation.
1456 SdrViewIter::ForAllViews(pObj
->getSdrPageFromSdrObject(), [this, &pWin
](
1461 for (sal_uInt32 nViewPaintWindow
= 0;
1462 nViewPaintWindow
< pView
->PaintWindowCount(); ++nViewPaintWindow
)
1464 SdrPaintWindow
* pPaintWindow
= pView
->GetPaintWindow(nViewPaintWindow
);
1465 OutputDevice
& rOutDev
= pPaintWindow
->GetOutputDevice();
1467 if (&rOutDev
!= pWin
->GetOutDev()
1468 && OUTDEV_WINDOW
== rOutDev
.GetOutDevType())
1470 OutlinerView
* pOutlView
1471 = ImpMakeOutlinerView(rOutDev
.GetOwnerWindow(), nullptr);
1472 pOutlView
->HideCursor();
1473 rOutDev
.GetOwnerWindow()->SetCursor(nullptr);
1474 mpTextEditOutliner
->InsertView(pOutlView
);
1481 mpTextEditOutlinerView
->ShowCursor();
1482 mpTextEditOutliner
->SetStatusEventHdl(
1483 LINK(this, SdrObjEditView
, ImpOutlinerStatusEventHdl
));
1484 if (pTextObj
->IsChainable())
1486 mpTextEditOutlinerView
->SetEndCutPasteLinkHdl(
1487 LINK(this, SdrObjEditView
, ImpAfterCutOrPasteChainingEventHdl
));
1490 mpTextEditOutliner
->ClearModifyFlag();
1492 if (pTextObj
->IsFitToSize())
1494 pWin
->Invalidate(aTextEditArea
);
1497 SdrHint
aHint(SdrHintKind::BeginEdit
, *pTextObj
);
1498 GetModel().Broadcast(aHint
);
1500 mpTextEditOutliner
->setVisualizedPage(nullptr);
1502 if (mxSelectionController
.is())
1503 mxSelectionController
->onSelectionHasChanged();
1505 if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1507 SdrUndoManager
* pSdrUndoManager
= nullptr;
1508 mpLocalTextEditUndoManager
= createLocalTextUndoManager();
1510 if (mpLocalTextEditUndoManager
)
1511 pSdrUndoManager
= mpLocalTextEditUndoManager
.get();
1513 if (pSdrUndoManager
)
1515 // we have an outliner, undo manager and it's an EditUndoManager, exchange
1516 // the document undo manager and the default one from the outliner and tell
1517 // it that text edit starts by setting a callback if it needs to end text edit mode.
1518 assert(nullptr == mpOldTextEditUndoManager
);
1520 mpOldTextEditUndoManager
= mpTextEditOutliner
->SetUndoManager(pSdrUndoManager
);
1521 pSdrUndoManager
->SetEndTextEditHdl(LINK(this, SdrObjEditView
, EndTextEditHdl
));
1526 "The document undo manager is not derived from SdrUndoManager (!)");
1530 return true; // ran fine, let TextEdit run now
1534 mpTextEditOutliner
->SetCalcFieldValueHdl(aOldCalcFieldValueLink
);
1535 mpTextEditOutliner
->SetBeginPasteOrDropHdl(Link
<PasteOrDropInfos
*, void>());
1536 mpTextEditOutliner
->SetEndPasteOrDropHdl(Link
<PasteOrDropInfos
*, void>());
1539 if (mpTextEditOutliner
!= nullptr)
1541 mpTextEditOutliner
->setVisualizedPage(nullptr);
1544 // something went wrong...
1545 if (!bDontDeleteOutliner
)
1547 delete pGivenOutliner
;
1548 if (pGivenOutlinerView
!= nullptr)
1550 delete pGivenOutlinerView
;
1551 pGivenOutlinerView
= nullptr;
1554 mpTextEditOutliner
.reset();
1556 mpTextEditOutlinerView
= nullptr;
1557 mxWeakTextEditObj
.clear();
1558 mpTextEditPV
= nullptr;
1559 mpTextEditWin
= nullptr;
1560 maHdlList
.SetMoveOutside(false);
1565 SdrEndTextEditKind
SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally
)
1567 SdrEndTextEditKind eRet
= SdrEndTextEditKind::Unchanged
;
1568 rtl::Reference
<SdrTextObj
> pTEObj
= mxWeakTextEditObj
.get();
1569 vcl::Window
* pTEWin
= mpTextEditWin
;
1570 OutlinerView
* pTEOutlinerView
= mpTextEditOutlinerView
;
1571 vcl::Cursor
* pTECursorBuffer
= pTextEditCursorBuffer
;
1572 SdrUndoManager
* pUndoEditUndoManager
= nullptr;
1573 bool bNeedToUndoSavedRedoTextEdit(false);
1575 if (IsUndoEnabled() && pTEObj
&& mpTextEditOutliner
1576 && !GetModel().GetDisableTextEditUsesCommonUndoManager())
1578 // change back the UndoManager to the remembered original one
1579 SfxUndoManager
* pOriginal
= mpTextEditOutliner
->SetUndoManager(mpOldTextEditUndoManager
);
1580 mpOldTextEditUndoManager
= nullptr;
1584 // check if we got back our document undo manager
1585 SdrUndoManager
* pSdrUndoManager
= mpLocalTextEditUndoManager
.get();
1587 if (pSdrUndoManager
&& dynamic_cast<SdrUndoManager
*>(pOriginal
) == pSdrUndoManager
)
1589 if (pSdrUndoManager
->isEndTextEditTriggeredFromUndo())
1591 // remember the UndoManager where missing Undos have to be triggered after end
1592 // text edit. When the undo had triggered the end text edit, the original action
1593 // which had to be undone originally is not yet undone.
1594 pUndoEditUndoManager
= pSdrUndoManager
;
1596 // We are ending text edit; if text edit was triggered from undo, execute all redos
1597 // to create a complete text change undo action for the redo buffer. Also mark this
1598 // state when at least one redo was executed; the created extra TextChange needs to
1599 // be undone in addition to the first real undo outside the text edit changes
1600 while (pSdrUndoManager
->GetRedoActionCount()
1601 > pSdrUndoManager
->GetRedoActionCountBeforeTextEdit())
1603 bNeedToUndoSavedRedoTextEdit
= true;
1604 pSdrUndoManager
->Redo();
1608 // reset the callback link and let the undo manager cleanup all text edit
1609 // undo actions to get the stack back to the form before the text edit
1610 pSdrUndoManager
->SetEndTextEditHdl(Link
<SdrUndoManager
*, void>());
1614 OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
1615 "expected document UndoManager (!)");
1619 // cid#1493241 - Wrapper object use after free
1620 if (pUndoEditUndoManager
== mpLocalTextEditUndoManager
.get())
1621 pUndoEditUndoManager
= nullptr;
1622 mpLocalTextEditUndoManager
.reset();
1627 assert(nullptr == mpOldTextEditUndoManager
); // cannot be restored!
1630 if (auto pTextEditObj
= mxWeakTextEditObj
.get())
1632 SdrHint
aHint(SdrHintKind::EndEdit
, *pTextEditObj
);
1633 GetModel().Broadcast(aHint
);
1636 // if new mechanism was used, clean it up. At cleanup no need to check
1637 // for LibreOfficeKit
1638 if (mpTextEditOutlinerView
)
1640 mpTextEditOutlinerView
->GetEditView().setEditViewCallbacks(nullptr);
1641 maTEOverlayGroup
.clear();
1644 mxWeakTextEditObj
.clear();
1645 mpTextEditPV
= nullptr;
1646 mpTextEditWin
= nullptr;
1647 mpTextEditOutlinerView
= nullptr;
1648 pTextEditCursorBuffer
= nullptr;
1649 aTextEditArea
= tools::Rectangle();
1651 if (SdrOutliner
* pTEOutliner
= mpTextEditOutliner
.release())
1653 bool bModified
= pTEOutliner
->IsModified();
1654 if (pTEOutlinerView
!= nullptr)
1656 pTEOutlinerView
->HideCursor();
1658 if (pTEObj
!= nullptr)
1660 pTEOutliner
->CompleteOnlineSpelling();
1662 std::unique_ptr
<SdrUndoObjSetText
> pTxtUndo
;
1667 for (nText
= 0; nText
< pTEObj
->getTextCount(); ++nText
)
1668 if (pTEObj
->getText(nText
) == pTEObj
->getActiveText())
1672 dynamic_cast<SdrUndoObjSetText
*>(GetModel()
1673 .GetSdrUndoFactory()
1674 .CreateUndoObjectSetText(*pTEObj
, nText
)
1677 DBG_ASSERT(!bModified
|| pTxtUndo
,
1678 "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
1679 // Set old CalcFieldValue-Handler again, this
1680 // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
1681 pTEOutliner
->SetCalcFieldValueHdl(aOldCalcFieldValueLink
);
1682 pTEOutliner
->SetBeginPasteOrDropHdl(Link
<PasteOrDropInfos
*, void>());
1683 pTEOutliner
->SetEndPasteOrDropHdl(Link
<PasteOrDropInfos
*, void>());
1685 const bool bUndo
= IsUndoEnabled();
1688 OUString
aObjName(pTEObj
->TakeObjNameSingul());
1689 BegUndo(SvxResId(STR_UndoObjSetText
), aObjName
);
1692 pTEObj
->EndTextEdit(*pTEOutliner
);
1694 if ((pTEObj
->GetRotateAngle() != 0_deg100
) || (pTEObj
&& pTEObj
->IsFontwork()))
1696 pTEObj
->ActionChanged();
1699 if (pTxtUndo
!= nullptr)
1701 pTxtUndo
->AfterSetText();
1702 if (!pTxtUndo
->IsDifferent())
1707 // check deletion of entire TextObj
1708 std::unique_ptr
<SdrUndoAction
> pDelUndo
;
1709 bool bDelObj
= false;
1710 if (mbTextEditNewObj
)
1712 bDelObj
= pTEObj
->IsTextFrame() && !pTEObj
->HasText() && !pTEObj
->IsEmptyPresObj()
1713 && !pTEObj
->HasFill() && !pTEObj
->HasLine();
1715 if (pTEObj
->IsInserted() && bDelObj
1716 && pTEObj
->GetObjInventor() == SdrInventor::Default
&& !bDontDeleteReally
)
1718 SdrObjKind eIdent
= pTEObj
->GetObjIdentifier();
1719 if (eIdent
== SdrObjKind::Text
)
1721 pDelUndo
= GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj
);
1728 AddUndo(std::move(pTxtUndo
));
1729 eRet
= SdrEndTextEditKind::Changed
;
1731 if (pDelUndo
!= nullptr)
1735 AddUndo(std::move(pDelUndo
));
1737 eRet
= SdrEndTextEditKind::Deleted
;
1738 DBG_ASSERT(pTEObj
->getParentSdrObjListFromSdrObject() != nullptr,
1739 "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
1741 if (pTEObj
->getParentSdrObjListFromSdrObject() != nullptr)
1743 pTEObj
->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj
->GetOrdNum());
1744 CheckMarked(); // remove selection immediately...
1748 { // for Writer: the app has to do the deletion itself.
1749 eRet
= SdrEndTextEditKind::ShouldBeDeleted
;
1753 EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
1755 // Switch on any TextAnimation again after TextEdit
1758 pTEObj
->SetTextAnimationAllowed(true);
1761 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1762 // to call AdjustMarkHdl() always.
1765 // delete all OutlinerViews
1766 for (size_t i
= pTEOutliner
->GetViewCount(); i
> 0;)
1769 OutlinerView
* pOLV
= pTEOutliner
->GetView(i
);
1770 sal_uInt16 nMorePix
= pOLV
->GetInvalidateMore() + 10;
1771 vcl::Window
* pWin
= pOLV
->GetWindow();
1772 tools::Rectangle
aRect(pOLV
->GetOutputArea());
1773 pTEOutliner
->RemoveView(i
);
1774 if (!mbTextEditDontDelete
|| i
!= 0)
1776 // may not own the zeroth one
1779 aRect
.Union(aTextEditArea
);
1780 aRect
.Union(aMinTextEditArea
);
1781 aRect
= pWin
->LogicToPixel(aRect
);
1782 aRect
.AdjustLeft(-nMorePix
);
1783 aRect
.AdjustTop(-nMorePix
);
1784 aRect
.AdjustRight(nMorePix
);
1785 aRect
.AdjustBottom(nMorePix
);
1786 aRect
= pWin
->PixelToLogic(aRect
);
1787 InvalidateOneWin(*pWin
->GetOutDev(), aRect
);
1788 pWin
->GetOutDev()->SetFillColor();
1789 pWin
->GetOutDev()->SetLineColor(COL_BLACK
);
1791 // and now the Outliner itself
1792 if (!mbTextEditDontDelete
)
1795 pTEOutliner
->Clear();
1796 if (pTEWin
!= nullptr)
1798 pTEWin
->SetCursor(pTECursorBuffer
);
1800 maHdlList
.SetMoveOutside(false);
1801 if (eRet
!= SdrEndTextEditKind::Unchanged
)
1803 GetMarkedObjectListWriteAccess().SetNameDirty();
1805 // coverity[leaked_storage] - if pTEOutliner wasn't deleted it didn't really belong to us
1808 if (pTEObj
&& !pTEObj
->getSdrModelFromSdrObject().isLocked() && pTEObj
->GetBroadcaster())
1810 SdrHint
aHint(SdrHintKind::EndEdit
, *pTEObj
);
1811 const_cast<SfxBroadcaster
*>(pTEObj
->GetBroadcaster())->Broadcast(aHint
);
1814 if (pUndoEditUndoManager
)
1816 if (bNeedToUndoSavedRedoTextEdit
)
1818 // undo the text edit action since it was created as part of an EndTextEdit
1819 // callback from undo itself. This needs to be done after the call to
1820 // FmFormView::SdrEndTextEdit since it gets created there
1821 pUndoEditUndoManager
->Undo();
1824 // trigger the Undo which was not executed, but lead to this
1826 pUndoEditUndoManager
->Undo();
1832 // info about TextEdit. Default is false.
1833 bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj
.get().is(); }
1835 // info about TextEditPageView. Default is 0L.
1836 SdrPageView
* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV
; }
1838 OutlinerView
* SdrObjEditView::ImpFindOutlinerView(vcl::Window
const* pWin
) const
1840 if (pWin
== nullptr)
1842 if (mpTextEditOutliner
== nullptr)
1844 OutlinerView
* pNewView
= nullptr;
1845 size_t nWinCount
= mpTextEditOutliner
->GetViewCount();
1846 for (size_t i
= 0; i
< nWinCount
&& pNewView
== nullptr; i
++)
1848 OutlinerView
* pView
= mpTextEditOutliner
->GetView(i
);
1849 if (pView
->GetWindow() == pWin
)
1855 void SdrObjEditView::SetTextEditWin(vcl::Window
* pWin
)
1857 if (!(mxWeakTextEditObj
.get() && pWin
!= nullptr && pWin
!= mpTextEditWin
))
1860 OutlinerView
* pNewView
= ImpFindOutlinerView(pWin
);
1861 if (pNewView
!= nullptr && pNewView
!= mpTextEditOutlinerView
)
1863 if (mpTextEditOutlinerView
!= nullptr)
1865 mpTextEditOutlinerView
->HideCursor();
1867 mpTextEditOutlinerView
= pNewView
;
1868 mpTextEditWin
= pWin
;
1869 pWin
->GrabFocus(); // Make the cursor blink here as well
1870 pNewView
->ShowCursor();
1871 ImpMakeTextCursorAreaVisible();
1875 bool SdrObjEditView::IsTextEditHit(const Point
& rHit
) const
1878 if (mxWeakTextEditObj
.get())
1880 tools::Rectangle aEditArea
;
1881 if (OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(0))
1882 aEditArea
.Union(pOLV
->GetOutputArea());
1884 if (aEditArea
.Contains(rHit
))
1885 { // check if any characters were actually hit
1886 const Point
aPnt(rHit
- aEditArea
.TopLeft());
1887 tools::Long nHitTol
= 2000;
1888 if (OutputDevice
* pRef
= mpTextEditOutliner
->GetRefDevice())
1889 nHitTol
= OutputDevice::LogicToLogic(nHitTol
, MapUnit::Map100thMM
,
1890 pRef
->GetMapMode().GetMapUnit());
1892 bOk
= mpTextEditOutliner
->IsTextPos(aPnt
, static_cast<sal_uInt16
>(nHitTol
));
1898 bool SdrObjEditView::IsTextEditFrameHit(const Point
& rHit
) const
1901 if (rtl::Reference
<SdrTextObj
> pText
= mxWeakTextEditObj
.get())
1903 OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(0);
1906 vcl::Window
* pWin
= pOLV
->GetWindow();
1907 if (pText
!= nullptr && pText
->IsTextFrame() && pWin
!= nullptr)
1909 sal_uInt16 nPixSiz
= pOLV
->GetInvalidateMore();
1910 tools::Rectangle
aEditArea(aMinTextEditArea
);
1911 aEditArea
.Union(pOLV
->GetOutputArea());
1912 if (!aEditArea
.Contains(rHit
))
1914 Size
aSiz(pWin
->PixelToLogic(Size(nPixSiz
, nPixSiz
)));
1915 aEditArea
.AdjustLeft(-(aSiz
.Width()));
1916 aEditArea
.AdjustTop(-(aSiz
.Height()));
1917 aEditArea
.AdjustRight(aSiz
.Width());
1918 aEditArea
.AdjustBottom(aSiz
.Height());
1919 bOk
= aEditArea
.Contains(rHit
);
1927 std::unique_ptr
<TextChainCursorManager
>
1928 SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent
& rKEvt
, bool* bOutHandled
)
1930 *bOutHandled
= false;
1932 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1936 if (!pTextObj
->GetNextLinkInChain() && !pTextObj
->GetPrevLinkInChain())
1939 std::unique_ptr
<TextChainCursorManager
> pCursorManager(
1940 new TextChainCursorManager(this, pTextObj
.get()));
1941 if (pCursorManager
->HandleKeyEvent(rKEvt
))
1943 // Possibly do other stuff here if necessary...
1944 // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
1945 *bOutHandled
= true;
1948 return pCursorManager
;
1951 bool SdrObjEditView::KeyInput(const KeyEvent
& rKEvt
, vcl::Window
* pWin
)
1953 if (mpTextEditOutlinerView
)
1955 /* Start special handling of keys within a chain */
1956 // We possibly move to another box before any handling
1957 bool bHandled
= false;
1958 std::unique_ptr
<TextChainCursorManager
> xCursorManager(
1959 ImpHandleMotionThroughBoxesKeyInput(rKEvt
, &bHandled
));
1962 /* End special handling of keys within a chain */
1964 if (mpTextEditOutlinerView
->PostKeyEvent(rKEvt
, pWin
))
1966 if (mpTextEditOutliner
&& mpTextEditOutliner
->IsModified())
1968 GetModel().SetChanged();
1969 SetInnerTextAreaForLOKit();
1972 /* Start chaining processing */
1973 ImpChainingEventHdl();
1974 ImpMoveCursorAfterChainingEvent(xCursorManager
.get());
1975 /* End chaining processing */
1977 if (pWin
!= nullptr && pWin
!= mpTextEditWin
)
1978 SetTextEditWin(pWin
);
1979 ImpMakeTextCursorAreaVisible();
1983 return SdrGlueEditView::KeyInput(rKEvt
, pWin
);
1986 bool SdrObjEditView::MouseButtonDown(const MouseEvent
& rMEvt
, OutputDevice
* pWin
)
1988 if (mpTextEditOutlinerView
!= nullptr)
1990 bool bPostIt
= mpTextEditOutliner
->IsInSelectionMode();
1993 Point
aPt(rMEvt
.GetPosPixel());
1994 if (pWin
!= nullptr)
1995 aPt
= pWin
->PixelToLogic(aPt
);
1996 else if (mpTextEditWin
!= nullptr)
1997 aPt
= mpTextEditWin
->PixelToLogic(aPt
);
1998 bPostIt
= IsTextEditHit(aPt
);
2002 Point
aPixPos(rMEvt
.GetPosPixel());
2005 tools::Rectangle
aR(pWin
->LogicToPixel(mpTextEditOutlinerView
->GetOutputArea()));
2006 if (aPixPos
.X() < aR
.Left())
2007 aPixPos
.setX(aR
.Left());
2008 if (aPixPos
.X() > aR
.Right())
2009 aPixPos
.setX(aR
.Right());
2010 if (aPixPos
.Y() < aR
.Top())
2011 aPixPos
.setY(aR
.Top());
2012 if (aPixPos
.Y() > aR
.Bottom())
2013 aPixPos
.setY(aR
.Bottom());
2015 MouseEvent
aMEvt(aPixPos
, rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(),
2016 rMEvt
.GetModifier());
2017 if (mpTextEditOutlinerView
->MouseButtonDown(aMEvt
))
2019 if (pWin
!= nullptr && pWin
!= mpTextEditWin
->GetOutDev()
2020 && pWin
->GetOutDevType() == OUTDEV_WINDOW
)
2021 SetTextEditWin(pWin
->GetOwnerWindow());
2022 ImpMakeTextCursorAreaVisible();
2027 return SdrGlueEditView::MouseButtonDown(rMEvt
, pWin
);
2030 bool SdrObjEditView::MouseButtonUp(const MouseEvent
& rMEvt
, OutputDevice
* pWin
)
2032 if (mpTextEditOutlinerView
!= nullptr)
2034 bool bPostIt
= mpTextEditOutliner
->IsInSelectionMode();
2037 Point
aPt(rMEvt
.GetPosPixel());
2038 if (pWin
!= nullptr)
2039 aPt
= pWin
->PixelToLogic(aPt
);
2040 else if (mpTextEditWin
!= nullptr)
2041 aPt
= mpTextEditWin
->PixelToLogic(aPt
);
2042 bPostIt
= IsTextEditHit(aPt
);
2044 if (bPostIt
&& pWin
)
2046 Point
aPixPos(rMEvt
.GetPosPixel());
2047 tools::Rectangle
aR(pWin
->LogicToPixel(mpTextEditOutlinerView
->GetOutputArea()));
2048 if (aPixPos
.X() < aR
.Left())
2049 aPixPos
.setX(aR
.Left());
2050 if (aPixPos
.X() > aR
.Right())
2051 aPixPos
.setX(aR
.Right());
2052 if (aPixPos
.Y() < aR
.Top())
2053 aPixPos
.setY(aR
.Top());
2054 if (aPixPos
.Y() > aR
.Bottom())
2055 aPixPos
.setY(aR
.Bottom());
2056 MouseEvent
aMEvt(aPixPos
, rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(),
2057 rMEvt
.GetModifier());
2058 if (mpTextEditOutlinerView
->MouseButtonUp(aMEvt
))
2060 ImpMakeTextCursorAreaVisible();
2065 return SdrGlueEditView::MouseButtonUp(rMEvt
, pWin
);
2068 bool SdrObjEditView::MouseMove(const MouseEvent
& rMEvt
, OutputDevice
* pWin
)
2070 if (mpTextEditOutlinerView
!= nullptr)
2072 bool bSelMode
= mpTextEditOutliner
->IsInSelectionMode();
2073 bool bPostIt
= bSelMode
;
2076 Point
aPt(rMEvt
.GetPosPixel());
2078 aPt
= pWin
->PixelToLogic(aPt
);
2079 else if (mpTextEditWin
)
2080 aPt
= mpTextEditWin
->PixelToLogic(aPt
);
2081 bPostIt
= IsTextEditHit(aPt
);
2085 Point
aPixPos(rMEvt
.GetPosPixel());
2086 tools::Rectangle
aR(mpTextEditOutlinerView
->GetOutputArea());
2088 aR
= pWin
->LogicToPixel(aR
);
2089 else if (mpTextEditWin
)
2090 aR
= mpTextEditWin
->LogicToPixel(aR
);
2091 if (aPixPos
.X() < aR
.Left())
2092 aPixPos
.setX(aR
.Left());
2093 if (aPixPos
.X() > aR
.Right())
2094 aPixPos
.setX(aR
.Right());
2095 if (aPixPos
.Y() < aR
.Top())
2096 aPixPos
.setY(aR
.Top());
2097 if (aPixPos
.Y() > aR
.Bottom())
2098 aPixPos
.setY(aR
.Bottom());
2099 MouseEvent
aMEvt(aPixPos
, rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(),
2100 rMEvt
.GetModifier());
2101 if (mpTextEditOutlinerView
->MouseMove(aMEvt
) && bSelMode
)
2103 ImpMakeTextCursorAreaVisible();
2108 return SdrGlueEditView::MouseMove(rMEvt
, pWin
);
2111 bool SdrObjEditView::Command(const CommandEvent
& rCEvt
, vcl::Window
* pWin
)
2113 // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
2114 if (mpTextEditOutlinerView
!= nullptr)
2116 if (rCEvt
.GetCommand() == CommandEventId::StartDrag
)
2118 bool bPostIt
= mpTextEditOutliner
->IsInSelectionMode() || !rCEvt
.IsMouseEvent();
2119 if (!bPostIt
&& rCEvt
.IsMouseEvent())
2121 Point
aPt(rCEvt
.GetMousePosPixel());
2122 if (pWin
!= nullptr)
2123 aPt
= pWin
->PixelToLogic(aPt
);
2124 else if (mpTextEditWin
!= nullptr)
2125 aPt
= mpTextEditWin
->PixelToLogic(aPt
);
2126 bPostIt
= IsTextEditHit(aPt
);
2130 Point
aPixPos(rCEvt
.GetMousePosPixel());
2131 if (rCEvt
.IsMouseEvent() && pWin
)
2133 tools::Rectangle
aR(
2134 pWin
->LogicToPixel(mpTextEditOutlinerView
->GetOutputArea()));
2135 if (aPixPos
.X() < aR
.Left())
2136 aPixPos
.setX(aR
.Left());
2137 if (aPixPos
.X() > aR
.Right())
2138 aPixPos
.setX(aR
.Right());
2139 if (aPixPos
.Y() < aR
.Top())
2140 aPixPos
.setY(aR
.Top());
2141 if (aPixPos
.Y() > aR
.Bottom())
2142 aPixPos
.setY(aR
.Bottom());
2144 CommandEvent
aCEvt(aPixPos
, rCEvt
.GetCommand(), rCEvt
.IsMouseEvent());
2145 // Command is void at the OutlinerView, sadly
2146 mpTextEditOutlinerView
->Command(aCEvt
);
2147 if (pWin
!= nullptr && pWin
!= mpTextEditWin
)
2148 SetTextEditWin(pWin
);
2149 ImpMakeTextCursorAreaVisible();
2155 mpTextEditOutlinerView
->Command(rCEvt
);
2156 if (comphelper::LibreOfficeKit::isActive())
2158 // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
2160 if (mpTextEditOutliner
&& mpTextEditOutliner
->IsModified())
2162 GetModel().SetChanged();
2163 SetInnerTextAreaForLOKit();
2169 return SdrGlueEditView::Command(rCEvt
, pWin
);
2172 bool SdrObjEditView::ImpIsTextEditAllSelected() const
2175 if (mpTextEditOutliner
!= nullptr && mpTextEditOutlinerView
!= nullptr)
2177 if (SdrTextObj::HasTextImpl(mpTextEditOutliner
.get()))
2179 const sal_Int32 nParaCnt
= mpTextEditOutliner
->GetParagraphCount();
2180 Paragraph
* pLastPara
2181 = mpTextEditOutliner
->GetParagraph(nParaCnt
> 1 ? nParaCnt
- 1 : 0);
2183 ESelection
aESel(mpTextEditOutlinerView
->GetSelection());
2184 if (aESel
.nStartPara
== 0 && aESel
.nStartPos
== 0 && aESel
.nEndPara
== (nParaCnt
- 1))
2186 if (mpTextEditOutliner
->GetText(pLastPara
).getLength() == aESel
.nEndPos
)
2189 // in case the selection was done backwards
2190 if (!bRet
&& aESel
.nEndPara
== 0 && aESel
.nEndPos
== 0
2191 && aESel
.nStartPara
== (nParaCnt
- 1))
2193 if (mpTextEditOutliner
->GetText(pLastPara
).getLength() == aESel
.nStartPos
)
2205 void SdrObjEditView::ImpMakeTextCursorAreaVisible()
2207 if (mpTextEditOutlinerView
!= nullptr && mpTextEditWin
!= nullptr)
2209 vcl::Cursor
* pCsr
= mpTextEditWin
->GetCursor();
2210 if (pCsr
!= nullptr)
2212 Size
aSiz(pCsr
->GetSize());
2213 if (!aSiz
.IsEmpty())
2215 MakeVisible(tools::Rectangle(pCsr
->GetPos(), aSiz
), *mpTextEditWin
);
2221 SvtScriptType
SdrObjEditView::GetScriptType() const
2223 SvtScriptType nScriptType
= SvtScriptType::NONE
;
2227 auto pText
= mxWeakTextEditObj
.get();
2228 if (pText
->GetOutlinerParaObject())
2229 nScriptType
= pText
->GetOutlinerParaObject()->GetTextObject().GetScriptType();
2231 if (mpTextEditOutlinerView
)
2232 nScriptType
= mpTextEditOutlinerView
->GetSelectedScriptType();
2236 const size_t nMarkCount(GetMarkedObjectCount());
2238 for (size_t i
= 0; i
< nMarkCount
; ++i
)
2240 OutlinerParaObject
* pParaObj
= GetMarkedObjectByIndex(i
)->GetOutlinerParaObject();
2244 nScriptType
|= pParaObj
->GetTextObject().GetScriptType();
2249 if (nScriptType
== SvtScriptType::NONE
)
2250 nScriptType
= SvtScriptType::LATIN
;
2255 void SdrObjEditView::GetAttributes(SfxItemSet
& rTargetSet
, bool bOnlyHardAttr
) const
2257 if (mxSelectionController
.is())
2258 if (mxSelectionController
->GetAttributes(rTargetSet
, bOnlyHardAttr
))
2263 DBG_ASSERT(mpTextEditOutlinerView
!= nullptr,
2264 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2265 DBG_ASSERT(mpTextEditOutliner
!= nullptr,
2266 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2268 auto pText
= mxWeakTextEditObj
.get();
2269 // take care of bOnlyHardAttr(!)
2270 if (!bOnlyHardAttr
&& pText
->GetStyleSheet())
2271 rTargetSet
.Put(pText
->GetStyleSheet()->GetItemSet());
2273 // add object attributes
2274 rTargetSet
.Put(pText
->GetMergedItemSet());
2276 if (mpTextEditOutlinerView
)
2278 // FALSE= regard InvalidItems as "holes," not as Default
2279 rTargetSet
.Put(mpTextEditOutlinerView
->GetAttribs(), false);
2282 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == pText
.get())
2284 MergeNotPersistAttrFromMarked(rTargetSet
);
2289 SdrGlueEditView::GetAttributes(rTargetSet
, bOnlyHardAttr
);
2293 bool SdrObjEditView::SetAttributes(const SfxItemSet
& rSet
, bool bReplaceAll
)
2296 rtl::Reference
<SdrTextObj
> pTextEditObj
= mxWeakTextEditObj
.get();
2297 bool bTextEdit
= mpTextEditOutlinerView
!= nullptr && pTextEditObj
!= nullptr;
2298 bool bAllTextSelected
= ImpIsTextEditAllSelected();
2299 const SfxItemSet
* pSet
= &rSet
;
2303 // no TextEdit active -> all Items to drawing object
2304 if (mxSelectionController
.is())
2305 bRet
= mxSelectionController
->SetAttributes(*pSet
, bReplaceAll
);
2309 SdrGlueEditView::SetAttributes(*pSet
, bReplaceAll
);
2317 bool bHasEEFeatureItems
= false;
2318 SfxItemIter
aIter(rSet
);
2319 for (const SfxPoolItem
* pItem
= aIter
.GetCurItem(); !bHasEEFeatureItems
&& pItem
;
2320 pItem
= aIter
.NextItem())
2322 if (!IsInvalidItem(pItem
))
2324 sal_uInt16 nW
= pItem
->Which();
2325 if (nW
>= EE_FEATURE_START
&& nW
<= EE_FEATURE_END
)
2326 bHasEEFeatureItems
= true;
2330 if (bHasEEFeatureItems
)
2332 std::unique_ptr
<weld::MessageDialog
> xInfoBox(Application::CreateMessageDialog(
2333 nullptr, VclMessageType::Info
, VclButtonsType::Ok
,
2334 "SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
2335 "at the SdrView does not make sense! It only leads to "
2336 "overhead and unreadable documents."));
2343 bool bNoEEItems
= !SearchOutlinerItems(*pSet
, bReplaceAll
, &bOnlyEEItems
);
2344 // everything selected? -> attributes to the border, too
2345 // if no EEItems, attributes to the border only
2346 if (bAllTextSelected
|| bNoEEItems
)
2348 if (mxSelectionController
.is())
2349 bRet
= mxSelectionController
->SetAttributes(*pSet
, bReplaceAll
);
2353 const bool bUndo
= IsUndoEnabled();
2357 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes
));
2358 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj
));
2360 // If this is a text object also rescue the OutlinerParaObject since
2361 // applying attributes to the object may change text layout when
2362 // multiple portions exist with multiple formats. If an OutlinerParaObject
2363 // really exists and needs to be rescued is evaluated in the undo
2364 // implementation itself.
2365 bool bRescueText(pTextEditObj
);
2367 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(
2368 *pTextEditObj
, false, !bNoEEItems
|| bRescueText
));
2372 pTextEditObj
->SetMergedItemSetAndBroadcast(*pSet
, bReplaceAll
);
2374 FlushComeBackTimer(); // to set ModeHasChanged immediately
2377 else if (!bOnlyEEItems
)
2379 // Otherwise split Set, if necessary.
2380 // Now we build an ItemSet aSet that doesn't contain EE_Items from
2381 // *pSet (otherwise it would be a copy).
2382 WhichRangesContainer pNewWhichTable
2383 = RemoveWhichRange(pSet
->GetRanges(), EE_ITEMS_START
, EE_ITEMS_END
);
2384 SfxItemSet
aSet(GetModel().GetItemPool(), std::move(pNewWhichTable
));
2385 SfxWhichIter
aIter(aSet
);
2386 sal_uInt16 nWhich
= aIter
.FirstWhich();
2389 const SfxPoolItem
* pItem
;
2390 SfxItemState eState
= pSet
->GetItemState(nWhich
, false, &pItem
);
2391 if (eState
== SfxItemState::SET
)
2393 nWhich
= aIter
.NextWhich();
2396 if (mxSelectionController
.is())
2397 bRet
= mxSelectionController
->SetAttributes(aSet
, bReplaceAll
);
2401 if (IsUndoEnabled())
2403 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes
));
2404 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj
));
2405 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj
));
2409 pTextEditObj
->SetMergedItemSetAndBroadcast(aSet
, bReplaceAll
);
2411 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == pTextEditObj
.get())
2413 SetNotPersistAttrToMarked(aSet
);
2416 FlushComeBackTimer();
2420 // and now the attributes to the EditEngine
2423 mpTextEditOutlinerView
->RemoveAttribs(true);
2425 mpTextEditOutlinerView
->SetAttribs(rSet
);
2427 Outliner
* pTEOutliner
= mpTextEditOutlinerView
->GetOutliner();
2428 if (pTEOutliner
&& pTEOutliner
->IsModified())
2430 GetModel().SetChanged();
2431 SetInnerTextAreaForLOKit();
2434 ImpMakeTextCursorAreaVisible();
2441 SfxStyleSheet
* SdrObjEditView::GetStyleSheet() const
2443 SfxStyleSheet
* pSheet
= nullptr;
2445 if (mxSelectionController
.is())
2447 if (mxSelectionController
->GetStyleSheet(pSheet
))
2451 if (mpTextEditOutlinerView
)
2453 pSheet
= mpTextEditOutlinerView
->GetStyleSheet();
2457 pSheet
= SdrGlueEditView::GetStyleSheet();
2462 void SdrObjEditView::SetStyleSheet(SfxStyleSheet
* pStyleSheet
, bool bDontRemoveHardAttr
)
2464 if (mxSelectionController
.is())
2466 if (mxSelectionController
->SetStyleSheet(pStyleSheet
, bDontRemoveHardAttr
))
2470 // if we are currently in edit mode we must also set the stylesheet
2471 // on all paragraphs in the Outliner for the edit view
2472 if (nullptr != mpTextEditOutlinerView
)
2474 Outliner
* pOutliner
= mpTextEditOutlinerView
->GetOutliner();
2476 const sal_Int32 nParaCount
= pOutliner
->GetParagraphCount();
2477 for (sal_Int32 nPara
= 0; nPara
< nParaCount
; nPara
++)
2479 pOutliner
->SetStyleSheet(nPara
, pStyleSheet
);
2483 SdrGlueEditView::SetStyleSheet(pStyleSheet
, bDontRemoveHardAttr
);
2486 void SdrObjEditView::AddDeviceToPaintView(OutputDevice
& rNewDev
, vcl::Window
* pWindow
)
2488 SdrGlueEditView::AddDeviceToPaintView(rNewDev
, pWindow
);
2490 if (mxWeakTextEditObj
.get() && !mbTextEditOnlyOneView
2491 && rNewDev
.GetOutDevType() == OUTDEV_WINDOW
)
2493 OutlinerView
* pOutlView
= ImpMakeOutlinerView(rNewDev
.GetOwnerWindow(), nullptr);
2494 mpTextEditOutliner
->InsertView(pOutlView
);
2498 void SdrObjEditView::DeleteDeviceFromPaintView(OutputDevice
& rOldDev
)
2500 SdrGlueEditView::DeleteDeviceFromPaintView(rOldDev
);
2502 if (mxWeakTextEditObj
.get() && !mbTextEditOnlyOneView
2503 && rOldDev
.GetOutDevType() == OUTDEV_WINDOW
)
2505 for (size_t i
= mpTextEditOutliner
->GetViewCount(); i
> 0;)
2508 OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(i
);
2509 if (pOLV
&& pOLV
->GetWindow() == rOldDev
.GetOwnerWindow())
2511 mpTextEditOutliner
->RemoveView(i
);
2516 lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), &rOldDev
);
2519 bool SdrObjEditView::IsTextEditInSelectionMode() const
2521 return mpTextEditOutliner
!= nullptr && mpTextEditOutliner
->IsInSelectionMode();
2526 void SdrObjEditView::BegMacroObj(const Point
& rPnt
, short nTol
, SdrObject
* pObj
, SdrPageView
* pPV
,
2530 if (pObj
!= nullptr && pPV
!= nullptr && pWin
!= nullptr && pObj
->HasMacro())
2532 nTol
= ImpGetHitTolLogic(nTol
, nullptr);
2536 mbMacroDown
= false;
2537 nMacroTol
= sal_uInt16(nTol
);
2538 aMacroDownPos
= rPnt
;
2543 void SdrObjEditView::ImpMacroUp(const Point
& rUpPos
)
2545 if (pMacroObj
!= nullptr && mbMacroDown
)
2547 SdrObjMacroHitRec aHitRec
;
2548 aHitRec
.aPos
= rUpPos
;
2549 aHitRec
.nTol
= nMacroTol
;
2550 aHitRec
.pVisiLayer
= &pMacroPV
->GetVisibleLayers();
2551 aHitRec
.pPageView
= pMacroPV
;
2552 pMacroObj
->PaintMacro(*pMacroWin
->GetOutDev(), tools::Rectangle(), aHitRec
);
2553 mbMacroDown
= false;
2557 void SdrObjEditView::ImpMacroDown(const Point
& rDownPos
)
2559 if (pMacroObj
!= nullptr && !mbMacroDown
)
2561 SdrObjMacroHitRec aHitRec
;
2562 aHitRec
.aPos
= rDownPos
;
2563 aHitRec
.nTol
= nMacroTol
;
2564 aHitRec
.pVisiLayer
= &pMacroPV
->GetVisibleLayers();
2565 aHitRec
.pPageView
= pMacroPV
;
2566 pMacroObj
->PaintMacro(*pMacroWin
->GetOutDev(), tools::Rectangle(), aHitRec
);
2571 void SdrObjEditView::MovMacroObj(const Point
& rPnt
)
2573 if (pMacroObj
== nullptr)
2576 SdrObjMacroHitRec aHitRec
;
2577 aHitRec
.aPos
= rPnt
;
2578 aHitRec
.nTol
= nMacroTol
;
2579 aHitRec
.pVisiLayer
= &pMacroPV
->GetVisibleLayers();
2580 aHitRec
.pPageView
= pMacroPV
;
2581 bool bDown
= pMacroObj
->IsMacroHit(aHitRec
);
2588 void SdrObjEditView::BrkMacroObj()
2590 if (pMacroObj
!= nullptr)
2592 ImpMacroUp(aMacroDownPos
);
2593 pMacroObj
= nullptr;
2595 pMacroWin
= nullptr;
2599 bool SdrObjEditView::EndMacroObj()
2601 if (pMacroObj
!= nullptr && mbMacroDown
)
2603 ImpMacroUp(aMacroDownPos
);
2604 SdrObjMacroHitRec aHitRec
;
2605 aHitRec
.aPos
= aMacroDownPos
;
2606 aHitRec
.nTol
= nMacroTol
;
2607 aHitRec
.pVisiLayer
= &pMacroPV
->GetVisibleLayers();
2608 aHitRec
.pPageView
= pMacroPV
;
2609 bool bRet
= pMacroObj
->DoMacro(aHitRec
);
2610 pMacroObj
= nullptr;
2612 pMacroWin
= nullptr;
2622 /** fills the given any with a XTextCursor for the current text selection.
2623 Leaves the any untouched if there currently is no text selected */
2624 void SdrObjEditView::getTextSelection(css::uno::Any
& rSelection
)
2629 OutlinerView
* pOutlinerView
= GetTextEditOutlinerView();
2630 if (!(pOutlinerView
&& pOutlinerView
->HasSelection()))
2633 SdrObject
* pObj
= GetTextEditObject();
2638 css::uno::Reference
<css::text::XText
> xText(pObj
->getUnoShape(), css::uno::UNO_QUERY
);
2641 SvxUnoTextBase
* pRange
= comphelper::getFromUnoTunnel
<SvxUnoTextBase
>(xText
);
2644 rSelection
<<= pRange
->createTextCursorBySelection(pOutlinerView
->GetSelection());
2649 /* check if we have a single selection and that single object likes
2650 to handle the mouse and keyboard events itself
2652 TODO: the selection controller should be queried from the
2653 object specific view contact. Currently this method only
2656 void SdrObjEditView::MarkListHasChanged()
2658 SdrGlueEditView::MarkListHasChanged();
2660 if (mxSelectionController
.is())
2662 mxLastSelectionController
= mxSelectionController
;
2663 mxSelectionController
->onSelectionHasChanged();
2666 mxSelectionController
.clear();
2668 const SdrMarkList
& rMarkList
= GetMarkedObjectList();
2669 if (rMarkList
.GetMarkCount() != 1)
2672 const SdrObject
* pObj(rMarkList
.GetMark(0)->GetMarkedSdrObj());
2673 SdrView
* pView(dynamic_cast<SdrView
*>(this));
2676 if (pObj
&& pView
&& (pObj
->GetObjInventor() == SdrInventor::Default
)
2677 && (pObj
->GetObjIdentifier() == SdrObjKind::Table
))
2679 mxSelectionController
= sdr::table::CreateTableController(
2680 *pView
, static_cast<const sdr::table::SdrTableObj
&>(*pObj
), mxLastSelectionController
);
2682 if (mxSelectionController
.is())
2684 mxLastSelectionController
.clear();
2685 mxSelectionController
->onSelectionHasChanged();
2690 IMPL_LINK(SdrObjEditView
, EndPasteOrDropHdl
, PasteOrDropInfos
*, pInfo
, void)
2692 OnEndPasteOrDrop(pInfo
);
2695 IMPL_LINK(SdrObjEditView
, BeginPasteOrDropHdl
, PasteOrDropInfos
*, pInfo
, void)
2697 OnBeginPasteOrDrop(pInfo
);
2700 void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos
*)
2702 // applications can derive from these virtual methods to do something before a drop or paste operation
2705 void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos
*)
2707 // applications can derive from these virtual methods to do something before a drop or paste operation
2710 sal_uInt16
SdrObjEditView::GetSelectionLevel() const
2712 sal_uInt16 nLevel
= 0xFFFF;
2715 DBG_ASSERT(mpTextEditOutlinerView
!= nullptr,
2716 "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
2717 DBG_ASSERT(mpTextEditOutliner
!= nullptr,
2718 "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
2719 if (mpTextEditOutlinerView
)
2721 //start and end position
2722 ESelection aSelect
= mpTextEditOutlinerView
->GetSelection();
2723 sal_uInt16 nStartPara
= ::std::min(aSelect
.nStartPara
, aSelect
.nEndPara
);
2724 sal_uInt16 nEndPara
= ::std::max(aSelect
.nStartPara
, aSelect
.nEndPara
);
2725 //get level from each paragraph
2727 for (sal_uInt16 nPara
= nStartPara
; nPara
<= nEndPara
; nPara
++)
2729 sal_uInt16 nParaDepth
2730 = 1 << static_cast<sal_uInt16
>(mpTextEditOutliner
->GetDepth(nPara
));
2731 if (!(nLevel
& nParaDepth
))
2732 nLevel
+= nParaDepth
;
2734 //reduce one level for Outliner Object
2735 //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
2736 // nLevel = nLevel >> 1;
2737 //no bullet paragraph selected
2745 bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor
,
2746 SdrObjKind nObjectIdentifier
)
2748 if (nObjectInventor
!= SdrInventor::Default
&& nObjectInventor
!= SdrInventor::E3d
)
2750 switch (nObjectIdentifier
)
2752 case SdrObjKind::NONE
:
2753 case SdrObjKind::Group
:
2755 case SdrObjKind::Line
:
2756 case SdrObjKind::Rectangle
:
2757 case SdrObjKind::CircleOrEllipse
:
2758 case SdrObjKind::CircleSection
:
2759 case SdrObjKind::CircleArc
:
2760 case SdrObjKind::CircleCut
:
2761 case SdrObjKind::Polygon
:
2762 case SdrObjKind::PolyLine
:
2763 case SdrObjKind::PathLine
:
2764 case SdrObjKind::PathFill
:
2765 case SdrObjKind::FreehandLine
:
2766 case SdrObjKind::FreehandFill
:
2767 case SdrObjKind::Text
:
2768 case SdrObjKind::TitleText
:
2769 case SdrObjKind::OutlineText
:
2770 case SdrObjKind::Graphic
:
2771 case SdrObjKind::OLE2
:
2772 case SdrObjKind::Table
:
2774 case SdrObjKind::Caption
:
2776 case SdrObjKind::Edge
:
2777 case SdrObjKind::PathPoly
:
2778 case SdrObjKind::PathPolyLine
:
2780 case SdrObjKind::Page
:
2781 case SdrObjKind::Measure
:
2782 case SdrObjKind::OLEPluginFrame
:
2783 case SdrObjKind::UNO
:
2785 case SdrObjKind::CustomShape
:
2792 static const WhichRangesContainer
& GetFormatRangeImpl(bool bTextOnly
)
2794 static const WhichRangesContainer
gFull(
2795 svl::Items
<XATTR_LINE_FIRST
, XATTR_LINE_LAST
, XATTR_FILL_FIRST
, XATTRSET_FILL
,
2796 SDRATTR_SHADOW_FIRST
, SDRATTR_SHADOW_LAST
, SDRATTR_MISC_FIRST
,
2797 SDRATTR_MISC_LAST
, // table cell formats
2798 SDRATTR_GRAF_FIRST
, SDRATTR_GRAF_LAST
, SDRATTR_TABLE_FIRST
, SDRATTR_TABLE_LAST
,
2799 SDRATTR_GLOW_FIRST
, SDRATTR_GLOW_LAST
, SDRATTR_SOFTEDGE_FIRST
,
2800 SDRATTR_SOFTEDGE_LAST
, EE_PARA_START
, EE_PARA_END
, EE_CHAR_START
, EE_CHAR_END
>);
2802 static const WhichRangesContainer
gTextOnly(
2803 svl::Items
<SDRATTR_MISC_FIRST
, SDRATTR_MISC_LAST
, EE_PARA_START
, EE_PARA_END
, EE_CHAR_START
,
2806 return bTextOnly
? gTextOnly
: gFull
;
2809 void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr
<SfxItemSet
>& rFormatSet
)
2811 const SdrMarkList
& rMarkList
= GetMarkedObjectList();
2812 if (rMarkList
.GetMarkCount() <= 0)
2815 OutlinerView
* pOLV
= GetTextEditOutlinerView();
2817 rFormatSet
= std::make_shared
<SfxItemSet
>(GetModel().GetItemPool(),
2818 GetFormatRangeImpl(pOLV
!= nullptr));
2821 rFormatSet
->Put(pOLV
->GetAttribs());
2825 const bool bOnlyHardAttr
= false;
2826 rFormatSet
->Put(GetAttrFromMarked(bOnlyHardAttr
));
2829 // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
2830 const SdrObject
* pObj
= rMarkList
.GetMark(0)->GetMarkedSdrObj();
2831 if (pObj
&& (pObj
->GetObjInventor() == SdrInventor::Default
)
2832 && (pObj
->GetObjIdentifier() == SdrObjKind::Table
))
2834 auto pTable
= static_cast<const sdr::table::SdrTableObj
*>(pObj
);
2835 if (mxSelectionController
.is() && pTable
->getActiveCell().is())
2837 mxSelectionController
->GetAttributes(*rFormatSet
, false);
2842 static SfxItemSet
CreatePaintSet(const WhichRangesContainer
& pRanges
, SfxItemPool
& rPool
,
2843 const SfxItemSet
& rSourceSet
, const SfxItemSet
& rTargetSet
,
2844 bool bNoCharacterFormats
, bool bNoParagraphFormats
)
2846 SfxItemSet
aPaintSet(rPool
, pRanges
);
2848 for (const auto& pRange
: pRanges
)
2850 sal_uInt16 nWhich
= pRange
.first
;
2851 const sal_uInt16 nLastWhich
= pRange
.second
;
2853 if (bNoCharacterFormats
&& (nWhich
== EE_CHAR_START
))
2856 if (bNoParagraphFormats
&& (nWhich
== EE_PARA_START
))
2859 for (; nWhich
<= nLastWhich
; nWhich
++)
2861 const SfxPoolItem
* pSourceItem
= rSourceSet
.GetItem(nWhich
);
2862 const SfxPoolItem
* pTargetItem
= rTargetSet
.GetItem(nWhich
);
2864 if ((pSourceItem
&& !pTargetItem
)
2865 || (pSourceItem
&& pTargetItem
&& *pSourceItem
!= *pTargetItem
))
2867 aPaintSet
.Put(*pSourceItem
);
2874 void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet
const& rFormatSet
, SdrTextObj
& rTextObj
,
2875 SdrText
* pText
, bool bNoCharacterFormats
,
2876 bool bNoParagraphFormats
)
2878 OutlinerParaObject
* pParaObj
= pText
? pText
->GetOutlinerParaObject() : nullptr;
2882 SdrOutliner
& rOutliner
= rTextObj
.ImpGetDrawOutliner();
2883 rOutliner
.SetText(*pParaObj
);
2885 sal_Int32
nParaCount(rOutliner
.GetParagraphCount());
2890 for (sal_Int32 nPara
= 0; nPara
< nParaCount
; nPara
++)
2892 if (!bNoCharacterFormats
)
2893 rOutliner
.RemoveCharAttribs(nPara
);
2895 SfxItemSet
aSet(rOutliner
.GetParaAttribs(nPara
));
2896 aSet
.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet
.GetPool(), rFormatSet
, aSet
,
2897 bNoCharacterFormats
, bNoParagraphFormats
));
2898 rOutliner
.SetParaAttribs(nPara
, aSet
);
2901 std::optional
<OutlinerParaObject
> pTemp
= rOutliner
.CreateParaObject(0, nParaCount
);
2904 rTextObj
.NbcSetOutlinerParaObjectForText(std::move(pTemp
), pText
);
2907 void SdrObjEditView::DisposeUndoManager()
2909 if (mpTextEditOutliner
)
2911 if (typeid(mpTextEditOutliner
->GetUndoManager()) != typeid(EditUndoManager
))
2913 // Non-owning pointer, clear it.
2914 mpTextEditOutliner
->SetUndoManager(nullptr);
2918 mpOldTextEditUndoManager
= nullptr;
2921 void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet
& rFormatSet
, bool bNoCharacterFormats
,
2922 bool bNoParagraphFormats
)
2924 if (mxSelectionController
.is()
2925 && mxSelectionController
->ApplyFormatPaintBrush(rFormatSet
, bNoCharacterFormats
,
2926 bNoParagraphFormats
))
2931 OutlinerView
* pOLV
= GetTextEditOutlinerView();
2932 const SdrMarkList
& rMarkList
= GetMarkedObjectList();
2935 SdrObject
* pObj
= rMarkList
.GetMark(0)->GetMarkedSdrObj();
2936 const SfxItemSet
& rShapeSet
= pObj
->GetMergedItemSet();
2938 // if not in text edit mode (aka the user selected text or clicked on a word)
2939 // apply formatting attributes to selected shape
2940 // All formatting items (see ranges above) that are unequal in selected shape and
2941 // the format paintbrush are hard set on the selected shape.
2943 const WhichRangesContainer
& pRanges
= rFormatSet
.GetRanges();
2944 bool bTextOnly
= true;
2946 for (const auto& pRange
: pRanges
)
2948 if ((pRange
.first
!= EE_PARA_START
) && (pRange
.first
!= EE_CHAR_START
))
2957 SfxItemSet
aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet
.GetPool(),
2958 rFormatSet
, rShapeSet
, bNoCharacterFormats
,
2959 bNoParagraphFormats
));
2960 SetAttrToMarked(aPaintSet
, false /*bReplaceAll*/);
2963 // now apply character and paragraph formatting to text, if the shape has any
2964 SdrTextObj
* pTextObj
= DynCastSdrTextObj(pObj
);
2967 sal_Int32 nText
= pTextObj
->getTextCount();
2969 while (--nText
>= 0)
2971 SdrText
* pText
= pTextObj
->getText(nText
);
2972 ApplyFormatPaintBrushToText(rFormatSet
, *pTextObj
, pText
, bNoCharacterFormats
,
2973 bNoParagraphFormats
);
2979 ::Outliner
* pOutliner
= pOLV
->GetOutliner();
2982 const EditEngine
& rEditEngine
= pOutliner
->GetEditEngine();
2984 ESelection
aSel(pOLV
->GetSelection());
2985 if (!aSel
.HasRange())
2986 pOLV
->SetSelection(rEditEngine
.GetWord(aSel
, css::i18n::WordType::DICTIONARY_WORD
));
2988 const bool bRemoveParaAttribs
= !bNoParagraphFormats
;
2989 pOLV
->RemoveAttribsKeepLanguages(bRemoveParaAttribs
);
2990 SfxItemSet
aSet(pOLV
->GetAttribs());
2991 SfxItemSet
aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet
.GetPool(),
2992 rFormatSet
, aSet
, bNoCharacterFormats
,
2993 bNoParagraphFormats
));
2994 pOLV
->SetAttribs(aPaintSet
);
2998 // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
2999 SdrObject
* pObj
= rMarkList
.GetMark(0)->GetMarkedSdrObj();
3000 if (pObj
&& (pObj
->GetObjInventor() == SdrInventor::Default
)
3001 && (pObj
->GetObjIdentifier() == SdrObjKind::Table
))
3003 auto pTable
= static_cast<sdr::table::SdrTableObj
*>(pObj
);
3004 if (pTable
->getActiveCell().is() && mxSelectionController
.is())
3006 mxSelectionController
->SetAttributes(rFormatSet
, false);
3011 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */