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
aIter(pPageView
->GetPage());
140 for (SdrView
* pView
= aIter
.FirstView(); pView
; pView
= aIter
.NextView())
142 if (pView
== this || !pView
->IsTextEdit())
145 OutputDevice
* pOutDev
= GetFirstOutputDevice();
146 if (!pOutDev
|| pOutDev
->GetOutDevType() != OUTDEV_WINDOW
)
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
154 OutlinerView
* pOutlinerView
155 = pView
->ImpMakeOutlinerView(pOutDev
->GetOwnerWindow(), nullptr, GetSfxViewShell());
156 pOutlinerView
->HideCursor();
157 pView
->GetTextEditOutliner()->InsertView(pOutlinerView
);
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())
176 if (!pOutputDevice
|| pOutputDevice
->GetOutDevType() != OUTDEV_WINDOW
)
179 SdrViewIter
aIter(pPageView
->GetPage());
180 for (SdrView
* pView
= aIter
.FirstView(); pView
; pView
= aIter
.NextView())
182 if (pView
== pThis
|| !pView
->IsTextEdit())
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
)
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
216 rRect
= pMacroObj
->GetCurrentBoundRect();
220 SdrGlueEditView::TakeActionRect(rRect
);
224 void SdrObjEditView::Notify(SfxBroadcaster
& rBC
, const SfxHint
& rHint
)
226 SdrGlueEditView::Notify(rBC
, rHint
);
227 if (mpTextEditOutliner
== nullptr)
230 // change of printer while editing
231 if (rHint
.GetId() != SfxHintId::ThisIsAnSdrHint
)
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?
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
);
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
305 mpTextEditOutliner
->ClearPolygon();
306 EEControlBits nStat
= mpTextEditOutliner
->GetControlWord();
307 nStat
|= EEControlBits::AUTOPAGESIZE
;
308 mpTextEditOutliner
->SetControlWord(nStat
);
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.
326 nStat
|= EVControlBits::AUTOSIZE
;
328 nStat
&= ~EVControlBits::AUTOSIZE
;
330 pOLV
->SetControlWord(nStat
);
333 mpTextEditOutliner
->SetUpdateLayout(bPrevUpdateLayout
);
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
);
378 pOLV
->SetAnchorMode(eNewAnchor
);
380 pOLV
->SetBackgroundColor(aNewColor
);
383 aTextEditArea
); // because otherwise, we're not re-anchoring correctly
384 ImpInvalidateOutlinerView(*pOLV
);
386 mpTextEditOutlinerView
->ShowCursor();
389 ImpMakeTextCursorAreaVisible();
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
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
;
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
461 const TextEditOverlayObject
& mrTextEditOverlayObject
;
463 // geometry creation for OverlayObject, can use local *Last* values
464 virtual drawinglayer::primitive2d::Primitive2DContainer
465 createOverlayObjectPrimitive2DSequence() override
;
468 TextEditFrameOverlayObject(const TextEditOverlayObject
& rTextEditOverlayObject
);
469 using sdr::overlay::OverlayObject::objectChange
;
470 virtual ~TextEditFrameOverlayObject() override
;
473 class TextEditHighContrastOverlaySelection
: public sdr::overlay::OverlayObject
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
;
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
);
508 drawinglayer::primitive2d::Primitive2DContainer
509 TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence()
511 drawinglayer::primitive2d::Primitive2DContainer aRetval
;
513 size_t nCount
= maRanges
.size();
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())),
532 aRetval
.append(mrTextEditOverlayObject
.getTextPrimitives());
533 aRetval
.append(drawinglayer::primitive2d::Primitive2DReference(
534 new drawinglayer::primitive2d::MaskPrimitive2D(aClipPolyPolygon
, std::move(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()
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
);
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
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
600 if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
602 mxOverlayHighContrastSelection
.reset(new TextEditHighContrastOverlaySelection(*this));
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
)
676 bObjectChange
= true;
679 // check if text primitives did change
680 SdrOutliner
* pSdrOutliner
= dynamic_cast<SdrOutliner
*>(getOutlinerView().GetOutliner());
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;
721 // if there really *was* a change signal the OverlayManager to
722 // refresh this object's visualization
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()))
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
));
759 mxOverlayHighContrastSelection
->setRanges(std::move(aLogicRanges
));
761 } // end of anonymous namespace
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
&)
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
));
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()
797 for (sal_uInt32
a(0); a
< maTEOverlayGroup
.count(); a
++)
799 TextEditOverlayObject
* pCandidate
800 = dynamic_cast<TextEditOverlayObject
*>(&maTEOverlayGroup
.getOverlayObject(a
));
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
820 return mpTextEditWin
->GetClipboard();
823 css::uno::Reference
<css::datatransfer::dnd::XDropTarget
> SdrObjEditView::GetDropTarget()
827 return mpTextEditWin
->GetDropTarget();
830 void SdrObjEditView::EditViewInputContext(const InputContext
& rInputContext
)
834 mpTextEditWin
->SetInputContext(rInputContext
);
837 void SdrObjEditView::EditViewCursorRect(const tools::Rectangle
& rRect
, int nExtTextInputWidth
)
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());
855 // draw old text edit stuff
858 const SdrOutliner
* pActiveOutliner
= GetTextEditOutliner();
862 const sal_uInt32
nViewCount(pActiveOutliner
->GetViewCount());
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());
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
910 if (!comphelper::LibreOfficeKit::isActive() || !rRect
.IsEmpty())
911 aBlankRect
.Intersection(rRect
);
913 rOutlView
.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
914 rOutlView
.Paint(aBlankRect
, &rTargetDevice
);
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
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();
956 const SdrTextObj
* pText
= GetTextEditObject();
957 bool bTextFrame(pText
&& pText
->IsTextFrame());
958 bool bFitToSize(pText
&& pText
->IsFitToSize());
960 if (!bTextFrame
|| bFitToSize
)
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
)
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
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
);
1020 pOutlView
->SetWindow(pWin
);
1024 pOutlView
->GetEditView().SetNegativeX(mbNegativeX
);
1026 // disallow scrolling
1027 EVControlBits nStat
= pOutlView
->GetControlWord();
1028 nStat
&= ~EVControlBits::AUTOSCROLL
;
1029 // AutoViewSize only if not ContourFrame.
1031 nStat
|= EVControlBits::AUTOSIZE
;
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
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
);
1061 IMPL_LINK(SdrObjEditView
, ImpOutlinerStatusEventHdl
, EditStatus
&, rEditStat
, void)
1063 if (mpTextEditOutliner
)
1065 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1068 pTextObj
->onEditOutlinerStatusEvent(&rEditStat
);
1073 void SdrObjEditView::ImpChainingEventHdl()
1075 if (!mpTextEditOutliner
)
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())
1089 // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
1090 if (pTextChain
->GetNilChainingEvent(pTextObj
.get()))
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());
1103 const int nText
= 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
1105 const bool bUndoEnabled
= IsUndoEnabled();
1106 std::unique_ptr
<SdrUndoObjSetText
> pTxtUndo
;
1109 dynamic_cast<SdrUndoObjSetText
*>(GetModel()
1110 .GetSdrUndoFactory()
1111 .CreateUndoObjectSetText(*pTextObj
, nText
)
1114 // trigger actual chaining
1115 pTextObj
->onChainingEvent();
1119 pTxtUndo
->AfterSetText();
1120 if (!pTxtUndo
->IsDifferent())
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);
1138 SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
1142 IMPL_LINK_NOARG(SdrObjEditView
, ImpAfterCutOrPasteChainingEventHdl
, LinkParamNone
*, void)
1144 SdrTextObj
* pTextObj
= GetTextEditObject();
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
)
1159 // Check if it has links to move it to
1160 if (!pTextObj
|| !pTextObj
->IsChainable())
1163 TextChain
* pTextChain
= pTextObj
->GetTextChain();
1164 ESelection aNewSel
= pTextChain
->GetPostChainingSel(pTextObj
.get());
1166 pCursorManager
->HandleCursorEventAfterChaining(pTextChain
->GetCursorEvent(pTextObj
.get()),
1170 pTextChain
->SetCursorEvent(pTextObj
.get(), CursorChainingEvent::NULL_EVENT
);
1173 IMPL_LINK(SdrObjEditView
, ImpOutlinerCalcFieldValueHdl
, EditFieldInfo
*, pFI
, void)
1176 OUString
& rStr
= pFI
->GetRepresentation();
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
);
1190 pFI
->SetTextColor(*pTxtCol
);
1194 pFI
->SetFontLineStyle(*pFldLineStyle
);
1198 pFI
->SetFieldColor(*pFldCol
);
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();
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
1240 SdrTextObj
* pObj
= DynCastSdrTextObj(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());
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
1278 pPV
= GetSdrPageView();
1280 // break, when no PageView for the object exists
1287 // no TextEdit on objects in locked Layer
1288 if (pPV
&& pPV
->GetLockedLayers().IsSet(pObj
->GetLayer()))
1293 if (mpTextEditOutliner
)
1295 OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
1296 mpTextEditOutliner
.reset();
1301 mpTextEditWin
= pWin
;
1303 mxWeakTextEditObj
= pObj
;
1306 mpTextEditOutliner
.reset(pGivenOutliner
);
1307 pGivenOutliner
= nullptr; // so we don't delete it on the error path
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.
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();
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();
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
)
1434 mpTextEditOutliner
->InsertView(mpTextEditOutlinerView
, 0);
1436 maHdlList
.SetMoveOutside(false);
1437 maHdlList
.SetMoveOutside(true);
1439 // register all windows as OutlinerViews with the Outliner
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())
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
));
1531 "The document undo manager is not derived from SdrUndoManager (!)");
1535 return true; // ran fine, let TextEdit run now
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);
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;
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>());
1619 OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
1620 "expected document UndoManager (!)");
1624 // cid#1493241 - Wrapper object use after free
1625 if (pUndoEditUndoManager
== mpLocalTextEditUndoManager
.get())
1626 pUndoEditUndoManager
= nullptr;
1627 mpLocalTextEditUndoManager
.reset();
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
;
1672 for (nText
= 0; nText
< pTEObj
->getTextCount(); ++nText
)
1673 if (pTEObj
->getText(nText
) == pTEObj
->getActiveText())
1677 dynamic_cast<SdrUndoObjSetText
*>(GetModel()
1678 .GetSdrUndoFactory()
1679 .CreateUndoObjectSetText(*pTEObj
, nText
)
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();
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())
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
);
1733 AddUndo(std::move(pTxtUndo
));
1734 eRet
= SdrEndTextEditKind::Changed
;
1736 if (pDelUndo
!= nullptr)
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 "
1746 if (pTEObj
->getParentSdrObjListFromSdrObject() != nullptr)
1748 pTEObj
->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj
->GetOrdNum());
1749 CheckMarked(); // remove selection immediately...
1753 { // for Writer: the app has to do the deletion itself.
1754 eRet
= SdrEndTextEditKind::ShouldBeDeleted
;
1758 EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
1760 // Switch on any TextAnimation again after TextEdit
1763 pTEObj
->SetTextAnimationAllowed(true);
1766 // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
1767 // to call AdjustMarkHdl() always.
1770 // delete all OutlinerViews
1771 for (size_t i
= pTEOutliner
->GetViewCount(); i
> 0;)
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
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
)
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
1831 pUndoEditUndoManager
->Undo();
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)
1847 if (mpTextEditOutliner
== 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
)
1860 void SdrObjEditView::SetTextEditWin(vcl::Window
* pWin
)
1862 if (!(mxWeakTextEditObj
.get() && pWin
!= nullptr && pWin
!= mpTextEditWin
))
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
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
));
1903 bool SdrObjEditView::IsTextEditFrameHit(const Point
& rHit
) const
1906 if (rtl::Reference
<SdrTextObj
> pText
= mxWeakTextEditObj
.get())
1908 OutlinerView
* pOLV
= mpTextEditOutliner
->GetView(0);
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
);
1932 std::unique_ptr
<TextChainCursorManager
>
1933 SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent
& rKEvt
, bool* bOutHandled
)
1935 *bOutHandled
= false;
1937 rtl::Reference
<SdrTextObj
> pTextObj
= mxWeakTextEditObj
.get();
1941 if (!pTextObj
->GetNextLinkInChain() && !pTextObj
->GetPrevLinkInChain())
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
));
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();
1985 return SdrGlueEditView::KeyInput(rKEvt
, pWin
);
1988 bool SdrObjEditView::MouseButtonDown(const MouseEvent
& rMEvt
, OutputDevice
* pWin
)
1990 if (mpTextEditOutlinerView
!= nullptr)
1992 bool bPostIt
= mpTextEditOutliner
->IsInSelectionMode();
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
);
2004 Point
aPixPos(rMEvt
.GetPosPixel());
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();
2029 return SdrGlueEditView::MouseButtonDown(rMEvt
, pWin
);
2032 bool SdrObjEditView::MouseButtonUp(const MouseEvent
& rMEvt
, OutputDevice
* pWin
)
2034 if (mpTextEditOutlinerView
!= nullptr)
2036 bool bPostIt
= mpTextEditOutliner
->IsInSelectionMode();
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();
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
;
2078 Point
aPt(rMEvt
.GetPosPixel());
2080 aPt
= pWin
->PixelToLogic(aPt
);
2081 else if (mpTextEditWin
)
2082 aPt
= mpTextEditWin
->PixelToLogic(aPt
);
2083 bPostIt
= IsTextEditHit(aPt
);
2087 Point
aPixPos(rMEvt
.GetPosPixel());
2088 tools::Rectangle
aR(mpTextEditOutlinerView
->GetOutputArea());
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();
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
);
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();
2157 mpTextEditOutlinerView
->Command(rCEvt
);
2158 if (comphelper::LibreOfficeKit::isActive())
2160 // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
2162 if (mpTextEditOutliner
&& mpTextEditOutliner
->IsModified())
2163 GetModel().SetChanged();
2168 return SdrGlueEditView::Command(rCEvt
, pWin
);
2171 bool SdrObjEditView::ImpIsTextEditAllSelected() const
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
)
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
)
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
;
2226 auto pText
= mxWeakTextEditObj
.get();
2227 if (pText
->GetOutlinerParaObject())
2228 nScriptType
= pText
->GetOutlinerParaObject()->GetTextObject().GetScriptType();
2230 if (mpTextEditOutlinerView
)
2231 nScriptType
= mpTextEditOutlinerView
->GetSelectedScriptType();
2235 const size_t nMarkCount(GetMarkedObjectCount());
2237 for (size_t i
= 0; i
< nMarkCount
; ++i
)
2239 OutlinerParaObject
* pParaObj
= GetMarkedObjectByIndex(i
)->GetOutlinerParaObject();
2243 nScriptType
|= pParaObj
->GetTextObject().GetScriptType();
2248 if (nScriptType
== SvtScriptType::NONE
)
2249 nScriptType
= SvtScriptType::LATIN
;
2254 void SdrObjEditView::GetAttributes(SfxItemSet
& rTargetSet
, bool bOnlyHardAttr
) const
2256 if (mxSelectionController
.is())
2257 if (mxSelectionController
->GetAttributes(rTargetSet
, bOnlyHardAttr
))
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
);
2288 SdrGlueEditView::GetAttributes(rTargetSet
, bOnlyHardAttr
);
2292 bool SdrObjEditView::SetAttributes(const SfxItemSet
& rSet
, bool bReplaceAll
)
2295 rtl::Reference
<SdrTextObj
> pTextEditObj
= mxWeakTextEditObj
.get();
2296 bool bTextEdit
= mpTextEditOutlinerView
!= nullptr && pTextEditObj
!= nullptr;
2297 bool bAllTextSelected
= ImpIsTextEditAllSelected();
2298 const SfxItemSet
* pSet
= &rSet
;
2302 // no TextEdit active -> all Items to drawing object
2303 if (mxSelectionController
.is())
2304 bRet
= mxSelectionController
->SetAttributes(*pSet
, bReplaceAll
);
2308 SdrGlueEditView::SetAttributes(*pSet
, bReplaceAll
);
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."));
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
);
2352 const bool bUndo
= IsUndoEnabled();
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
));
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();
2388 const SfxPoolItem
* pItem
;
2389 SfxItemState eState
= pSet
->GetItemState(nWhich
, false, &pItem
);
2390 if (eState
== SfxItemState::SET
)
2392 nWhich
= aIter
.NextWhich();
2395 if (mxSelectionController
.is())
2396 bRet
= mxSelectionController
->SetAttributes(aSet
, bReplaceAll
);
2400 if (IsUndoEnabled())
2402 BegUndo(ImpGetDescriptionString(STR_EditSetAttributes
));
2403 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj
));
2404 AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj
));
2408 pTextEditObj
->SetMergedItemSetAndBroadcast(aSet
, bReplaceAll
);
2410 if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == pTextEditObj
.get())
2412 SetNotPersistAttrToMarked(aSet
);
2415 FlushComeBackTimer();
2419 // and now the attributes to the EditEngine
2422 mpTextEditOutlinerView
->RemoveAttribs(true);
2424 mpTextEditOutlinerView
->SetAttribs(rSet
);
2426 Outliner
* pTEOutliner
= mpTextEditOutlinerView
->GetOutliner();
2427 if (pTEOutliner
&& pTEOutliner
->IsModified())
2428 GetModel().SetChanged();
2430 ImpMakeTextCursorAreaVisible();
2437 SfxStyleSheet
* SdrObjEditView::GetStyleSheet() const
2439 SfxStyleSheet
* pSheet
= nullptr;
2441 if (mxSelectionController
.is())
2443 if (mxSelectionController
->GetStyleSheet(pSheet
))
2447 if (mpTextEditOutlinerView
)
2449 pSheet
= mpTextEditOutlinerView
->GetStyleSheet();
2453 pSheet
= SdrGlueEditView::GetStyleSheet();
2458 void SdrObjEditView::SetStyleSheet(SfxStyleSheet
* pStyleSheet
, bool bDontRemoveHardAttr
)
2460 if (mxSelectionController
.is())
2462 if (mxSelectionController
->SetStyleSheet(pStyleSheet
, bDontRemoveHardAttr
))
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;)
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();
2522 void SdrObjEditView::BegMacroObj(const Point
& rPnt
, short nTol
, SdrObject
* pObj
, SdrPageView
* pPV
,
2526 if (pObj
!= nullptr && pPV
!= nullptr && pWin
!= nullptr && pObj
->HasMacro())
2528 nTol
= ImpGetHitTolLogic(nTol
, nullptr);
2532 mbMacroDown
= false;
2533 nMacroTol
= sal_uInt16(nTol
);
2534 aMacroDownPos
= 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
);
2567 void SdrObjEditView::MovMacroObj(const Point
& rPnt
)
2569 if (pMacroObj
== nullptr)
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
);
2584 void SdrObjEditView::BrkMacroObj()
2586 if (pMacroObj
!= nullptr)
2588 ImpMacroUp(aMacroDownPos
);
2589 pMacroObj
= 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;
2608 pMacroWin
= nullptr;
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
)
2625 OutlinerView
* pOutlinerView
= GetTextEditOutlinerView();
2626 if (!(pOutlinerView
&& pOutlinerView
->HasSelection()))
2629 SdrObject
* pObj
= GetTextEditObject();
2634 css::uno::Reference
<css::text::XText
> xText(pObj
->getUnoShape(), css::uno::UNO_QUERY
);
2637 SvxUnoTextBase
* pRange
= comphelper::getFromUnoTunnel
<SvxUnoTextBase
>(xText
);
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
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)
2668 const SdrObject
* pObj(rMarkList
.GetMark(0)->GetMarkedSdrObj());
2669 SdrView
* pView(dynamic_cast<SdrView
*>(this));
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;
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
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
2741 bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor
,
2742 SdrObjKind nObjectIdentifier
)
2744 if (nObjectInventor
!= SdrInventor::Default
&& nObjectInventor
!= SdrInventor::E3d
)
2746 switch (nObjectIdentifier
)
2748 case SdrObjKind::NONE
:
2749 case SdrObjKind::Group
:
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
:
2770 case SdrObjKind::Caption
:
2772 case SdrObjKind::Edge
:
2773 case SdrObjKind::PathPoly
:
2774 case SdrObjKind::PathPolyLine
:
2776 case SdrObjKind::Page
:
2777 case SdrObjKind::Measure
:
2778 case SdrObjKind::OLEPluginFrame
:
2779 case SdrObjKind::UNO
:
2781 case SdrObjKind::CustomShape
:
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
,
2802 return bTextOnly
? gTextOnly
: gFull
;
2805 void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr
<SfxItemSet
>& rFormatSet
)
2807 const SdrMarkList
& rMarkList
= GetMarkedObjectList();
2808 if (rMarkList
.GetMarkCount() <= 0)
2811 OutlinerView
* pOLV
= GetTextEditOutlinerView();
2813 rFormatSet
= std::make_shared
<SfxItemSet
>(GetModel().GetItemPool(),
2814 GetFormatRangeImpl(pOLV
!= nullptr));
2817 rFormatSet
->Put(pOLV
->GetAttribs());
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
))
2852 if (bNoParagraphFormats
&& (nWhich
== EE_PARA_START
))
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
);
2870 void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet
const& rFormatSet
, SdrTextObj
& rTextObj
,
2871 SdrText
* pText
, bool bNoCharacterFormats
,
2872 bool bNoParagraphFormats
)
2874 OutlinerParaObject
* pParaObj
= pText
? pText
->GetOutlinerParaObject() : nullptr;
2878 SdrOutliner
& rOutliner
= rTextObj
.ImpGetDrawOutliner();
2879 rOutliner
.SetText(*pParaObj
);
2881 sal_Int32
nParaCount(rOutliner
.GetParagraphCount());
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
);
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
))
2927 OutlinerView
* pOLV
= GetTextEditOutlinerView();
2928 const SdrMarkList
& rMarkList
= GetMarkedObjectList();
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
))
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
);
2963 sal_Int32 nText
= pTextObj
->getTextCount();
2965 while (--nText
>= 0)
2967 SdrText
* pText
= pTextObj
->getText(nText
);
2968 ApplyFormatPaintBrushToText(rFormatSet
, *pTextObj
, pText
, bNoCharacterFormats
,
2969 bNoParagraphFormats
);
2975 ::Outliner
* pOutliner
= pOLV
->GetOutliner();
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: */