summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editeng/source/editeng/editview.cxx33
-rw-r--r--editeng/source/editeng/impedit.cxx82
-rw-r--r--editeng/source/editeng/impedit.hxx21
-rw-r--r--editeng/source/editeng/impedit3.cxx5
-rw-r--r--include/editeng/editview.hxx21
-rw-r--r--include/svx/svdedxv.hxx12
-rw-r--r--include/svx/svdotext.hxx10
-rw-r--r--include/svx/svdoutl.hxx2
-rw-r--r--sd/source/ui/view/Outliner.cxx11
-rw-r--r--svx/source/svdraw/svdedxv.cxx373
-rw-r--r--svx/source/svdraw/svdotextdecomposition.cxx13
-rw-r--r--svx/source/svdraw/svdotxat.cxx20
-rw-r--r--svx/source/svdraw/svdoutl.cxx16
13 files changed, 575 insertions, 44 deletions
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
index 4bcc257fc0e4..db7ff1951767 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -153,8 +153,12 @@ LanguageType EditView::CheckLanguage(
return nLang;
}
-// class EditView
+// class EditViewCallbacks
+EditViewCallbacks::~EditViewCallbacks()
+{
+}
+// class EditView
EditView::EditView( EditEngine* pEng, vcl::Window* pWindow )
{
pImpEditView.reset( new ImpEditView( this, pEng, pWindow ) );
@@ -164,6 +168,16 @@ EditView::~EditView()
{
}
+void EditView::setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks)
+{
+ pImpEditView->setEditViewCallbacks(pEditViewCallbacks);
+}
+
+bool EditView::hasEditViewCallbacks() const
+{
+ return pImpEditView->hasEditViewCallbacks();
+}
+
ImpEditEngine* EditView::GetImpEditEngine() const
{
return pImpEditView->pEditEngine->pImpEditEngine.get();
@@ -190,6 +204,23 @@ tools::Rectangle EditView::GetInvalidateRect() const
}
}
+void EditView::InvalidateWindow(const tools::Rectangle& rClipRect)
+{
+ if (pImpEditView->hasEditViewCallbacks())
+ {
+ // do not invalidate and trigger a global repaint, but forward
+ // the need for change to the applied EditViewCallback, can e.g.
+ // be used to visualize the active edit text in an OverlayObject
+ pImpEditView->mpEditViewCallbacks->EditViewInvalidate();
+ }
+ else
+ {
+ // classic mode: invalidate and trigger full repaint
+ // of the changed area
+ GetWindow()->Invalidate(rClipRect);
+ }
+}
+
void EditView::InvalidateOtherViewWindows( const tools::Rectangle& rInvRect )
{
if (comphelper::LibreOfficeKit::isActive())
diff --git a/editeng/source/editeng/impedit.cxx b/editeng/source/editeng/impedit.cxx
index 22efd056dbee..fc8ee6745a3a 100644
--- a/editeng/source/editeng/impedit.cxx
+++ b/editeng/source/editeng/impedit.cxx
@@ -83,6 +83,7 @@ ImpEditView::ImpEditView( EditView* pView, EditEngine* pEng, vcl::Window* pWindo
bClickedInSelection = false;
eSelectionMode = EESelectionMode::TxtOnly;
eAnchorMode = EEAnchorMode::TopLeft;
+ mpEditViewCallbacks = nullptr;
nInvMore = 1;
nTravelXPos = TRAVEL_X_DONTKNOW;
nControl = EVControlBits::AUTOSCROLL | EVControlBits::ENABLEPASTE;
@@ -185,6 +186,35 @@ void lcl_translateTwips(vcl::Window const & rParent, vcl::Window& rChild)
void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, OutputDevice* pTargetDevice )
{
+ if (hasEditViewCallbacks() && !pRegion)
+ {
+ // when we have an own mechanism for painting EditViews, collect the Selection
+ // in a basegfx::B2DRange vector and hand over. To do so, call GetSelectionRectangles
+ // which recursively calls ImpEditView::DrawSelection, but with nullptr != pRegion
+ std::vector<tools::Rectangle> aLogicRects;
+ std::vector<basegfx::B2DRange> aLogicRanges;
+ OutputDevice* pTarget = pTargetDevice ? pTargetDevice : pOutWin;
+ const Point aPixel(pTarget ? pTarget->LogicToPixel(Point(1, 1)) : Point(1, 1));
+
+ GetSelectionRectangles(aLogicRects);
+
+ for (const auto& aRect : aLogicRects)
+ {
+ // convert from logic Rectangles to logic Ranges, do not forget to add
+ // one Unit (in this case logical unit, thus calculate first)
+ aLogicRanges.push_back(
+ basegfx::B2DRange(
+ aRect.Left(), aRect.Top(),
+ aRect.Right() + aPixel.X(), aRect.Bottom() + aPixel.Y()));
+ }
+
+ // use callback to tell about change in selection visualisation
+ mpEditViewCallbacks->EditViewSelectionChange(aLogicRanges);
+
+ // we are done, do *not* visualize self
+ return;
+ }
+
if ( eSelectionMode == EESelectionMode::Hidden )
return;
@@ -449,16 +479,9 @@ void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, Ou
void ImpEditView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects)
{
- bool bMm100ToTwip = pOutWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM;
vcl::Region aRegion;
DrawSelection(aEditSelection, &aRegion);
aRegion.GetRegionRectangles(rLogicRects);
-
- for (tools::Rectangle& rRectangle : rLogicRects)
- {
- if (bMm100ToTwip)
- rRectangle = OutputDevice::LogicToLogic(rRectangle, MapUnit::Map100thMM, MapUnit::MapTwip);
- }
}
void ImpEditView::ImplDrawHighlightRect( OutputDevice* _pTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly )
@@ -627,6 +650,23 @@ void ImpEditView::SetOutputArea( const tools::Rectangle& rRect )
SetScrollDiffX( (sal_uInt16)aOutArea.GetWidth() * 2 / 10 );
}
+void ImpEditView::InvalidateAtWindow(const tools::Rectangle& rRect)
+{
+ if (hasEditViewCallbacks())
+ {
+ // do not invalidate and trigger a global repaint, but forward
+ // the need for change to the applied EditViewCallback, can e.g.
+ // be used to visualize the active edit text in an OverlayObject
+ mpEditViewCallbacks->EditViewInvalidate();
+ }
+ else
+ {
+ // classic mode: invalidate and trigger full repaint
+ // of the changed area
+ GetWindow()->Invalidate(rRect);
+ }
+}
+
void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect )
{
// remember old out area
@@ -643,38 +683,46 @@ void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect )
if(aOldArea.Left() > aOutArea.Left())
{
- GetWindow()->Invalidate(tools::Rectangle(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Left() < aOutArea.Left())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Right() > aOutArea.Right())
{
- GetWindow()->Invalidate(tools::Rectangle(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Right() < aOutArea.Right())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Top() > aOutArea.Top())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top()));
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top());
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Top() < aOutArea.Top())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top()));
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top());
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Bottom() > aOutArea.Bottom())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Bottom() < aOutArea.Bottom())
{
- GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore));
+ const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
}
}
@@ -1629,6 +1677,10 @@ void ImpEditView::DeselectAll()
pEditEngine->SetInSelectionMode(false);
DrawSelection();
GetEditSelection().Min() = GetEditSelection().Max();
+
+ // Selection is empty, still need to draw it due to new forward selection
+ // functionality. When without that, nothing will be drawn (since it's empty)
+ DrawSelection();
}
bool ImpEditView::IsSelectionAtPoint( const Point& rPosPixel )
diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx
index 4d7365d49221..89cf8947d7c8 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -257,6 +257,27 @@ private:
EditSelection aEditSelection;
EEAnchorMode eAnchorMode;
+ /// mechanism to change from the classic refresh mode that simply
+ // invalidates the area where text was changed. When set, the invalidate
+ // and the direct repaint of the Window-plugged EditView will be suppressed.
+ // Instead, a consumer that has registered using a EditViewCallbacks
+ // incarnation has to handle that. Used e.g. to represent the edited text
+ // in Draw/Impres in an OverlayObject which avoids evtl. expensive full
+ // repaints of the EditView(s)
+ const EditViewCallbacks* mpEditViewCallbacks;
+
+ bool hasEditViewCallbacks() const
+ {
+ return nullptr != mpEditViewCallbacks;
+ }
+
+ void setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks)
+ {
+ mpEditViewCallbacks = pEditViewCallbacks;
+ }
+
+ void InvalidateAtWindow(const tools::Rectangle& rRect);
+
protected:
// DragAndDropClient
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
index 372e0993723f..e07312e4aa05 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -287,7 +287,10 @@ void ImpEditEngine::UpdateViews( EditView* pCurView )
{
// convert to window coordinates ....
aClipRect = pView->pImpEditView->GetWindowPos( aClipRect );
- pView->GetWindow()->Invalidate( aClipRect );
+
+ // moved to one executing method to allow finer control
+ pView->InvalidateWindow(aClipRect);
+
pView->InvalidateOtherViewWindows( aClipRect );
}
}
diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index 07333c75fe93..ac6ffc5433d1 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -72,6 +72,7 @@ namespace linguistic2 {
class XLanguageGuessing;
}
}}}
+namespace basegfx { class B2DRange; }
enum class ScrollRangeCheck
{
@@ -79,6 +80,21 @@ enum class ScrollRangeCheck
PaperWidthTextSize = 2, // VisArea must be within paper width, Text Size
};
+// Helper class that allows to set a callback at the EditView. When
+// set, Invalidates and repains are suppressed at the EditView, but
+// EditViewInvalidate() will be triggered to allow the consumer to
+// react itself as needed.
+// Also Selection visualization is suppressed and EditViewSelectionChange
+// is triggered when Selection changes and needs reaction.
+class EDITENG_DLLPUBLIC EditViewCallbacks
+{
+public:
+ EditViewCallbacks() {}
+ virtual ~EditViewCallbacks();
+
+ virtual void EditViewInvalidate() const = 0;
+ virtual void EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const = 0;
+};
class EDITENG_DLLPUBLIC EditView final
{
@@ -105,6 +121,10 @@ public:
EditView( EditEngine* pEng, vcl::Window* pWindow );
~EditView();
+ // set EditViewCallbacks for external handling of Repaints/Visualization
+ void setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks);
+ bool hasEditViewCallbacks() const;
+
void SetEditEngine( EditEngine* pEditEngine );
EditEngine* GetEditEngine() const;
@@ -117,6 +137,7 @@ public:
void Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice = nullptr );
tools::Rectangle GetInvalidateRect() const;
+ void InvalidateWindow(const tools::Rectangle& rClipRect);
void InvalidateOtherViewWindows( const tools::Rectangle& rInvRect );
void Invalidate();
Pair Scroll( long nHorzScroll, long nVertScroll, ScrollRangeCheck nRangeCheck = ScrollRangeCheck::NoNegative );
diff --git a/include/svx/svdedxv.hxx b/include/svx/svdedxv.hxx
index 8dd62ff0b3ec..8bdae7fb0ee4 100644
--- a/include/svx/svdedxv.hxx
+++ b/include/svx/svdedxv.hxx
@@ -25,6 +25,7 @@
#include <svx/svxdllapi.h>
#include <svx/svdglev.hxx>
#include <svx/selectioncontroller.hxx>
+#include <editeng/editview.hxx>
#include <memory>
class SdrOutliner;
@@ -57,11 +58,20 @@ enum class SdrEndTextEditKind
// - macromod
-class SVX_DLLPUBLIC SdrObjEditView: public SdrGlueEditView
+class SVX_DLLPUBLIC SdrObjEditView: public SdrGlueEditView, public EditViewCallbacks
{
friend class SdrPageView;
friend class ImpSdrEditPara;
+ // Now derived from EditViewCallbacks and overriding these callbacks to
+ // allow own EditText visualization
+ virtual void EditViewInvalidate() const override;
+ virtual void EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const override;
+
+ // The OverlayObjects used for visualizing active TextEdit (currently
+ // using TextEditOverlayObject, but not limitied to it
+ sdr::overlay::OverlayObjectList maTEOverlayGroup;
+
protected:
// TextEdit
SdrObjectWeakRef mxTextEditObj; // current object in TextEdit
diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx
index 0269c88e5d39..33fa54a321fa 100644
--- a/include/svx/svdotext.hxx
+++ b/include/svx/svdotext.hxx
@@ -598,11 +598,19 @@ public:
const drawinglayer::geometry::ViewInformation2D& aViewInformation) const;
void impHandleChainingEventsDuringDecomposition(SdrOutliner &rOutliner) const;
-
// timing generators
void impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const;
void impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const;
+ // Direct decomposer for text visualization when you already have a prepared
+ // Outliner containing all the needed information
+ static void impDecomposeBlockTextPrimitiveDirect(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ SdrOutliner& rOutliner,
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange);
+
/** returns false if the given pointer is NULL
or if the given SdrOutliner contains no text.
Also checks for one empty paragraph.
diff --git a/include/svx/svdoutl.hxx b/include/svx/svdoutl.hxx
index a9a885f68fac..5d1f9e842593 100644
--- a/include/svx/svdoutl.hxx
+++ b/include/svx/svdoutl.hxx
@@ -45,6 +45,8 @@ public:
const SdrPage* getVisualizedPage() const { return mpVisualizedPage; }
virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, Color*& rpTxtColor, Color*& rpFldColor) override;
+
+ bool hasEditViewCallbacks() const;
};
#endif // INCLUDED_SVX_SVDOUTL_HXX
diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 9b2619ae4e14..c70bb15cd1f8 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -797,6 +797,17 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
std::vector<::tools::Rectangle> aLogicRects;
pOutlinerView->GetSelectionRectangles(aLogicRects);
+ // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
+ // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
+ // which makes that method unusable for others
+ if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
+ {
+ for (tools::Rectangle& rRectangle : aLogicRects)
+ {
+ rRectangle = OutputDevice::LogicToLogic(rRectangle, MapUnit::Map100thMM, MapUnit::MapTwip);
+ }
+ }
+
std::vector<OString> aLogicRectStrings;
std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings), [](const ::tools::Rectangle& rRectangle) { return rRectangle.toString(); });
OString sRectangles = comphelper::string::join("; ", aLogicRectStrings);
diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx
index 65dede8abcb1..8348988b2ca1 100644
--- a/svx/source/svdraw/svdedxv.cxx
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -65,6 +65,9 @@
#include <comphelper/lok.hxx>
#include <sfx2/viewsh.hxx>
#include <svx/svdviter.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpagewindow.hxx>
#include <memory>
@@ -103,7 +106,6 @@ SdrObjEditView::~SdrObjEditView()
assert(nullptr == mpOldTextEditUndoManager); // should have been reset
}
-
bool SdrObjEditView::IsAction() const
{
return IsMacroObj() || SdrGlueEditView::IsAction();
@@ -353,39 +355,298 @@ void SdrObjEditView::ModelHasChanged()
}
}
+namespace
+{
+ /**
+ Helper class to visualize the content of an active EditView as an
+ OverlayObject. These objects work with Primitives and are handled
+ from the OverlayManager(s) in place as needed.
+
+ It allows complete visualization of the content of the active
+ EditView without the need of Invalidates triggered by the EditView
+ and thus avoiding potentially expensive repaints by using the
+ automatically buffered Overlay mechanism.
+
+ It buffers as amuch as possible locally and *only* triggers a real
+ change (see call to objectChange()) when really needed.
+ */
+ class TextEditOverlayObject : public sdr::overlay::OverlayObject
+ {
+ protected:
+ /// local access to associated sdr::overlay::OverlaySelection
+ sdr::overlay::OverlaySelection* mpOverlaySelection;
+
+ /// local definition depends on active OutlinerView
+ OutlinerView& mrOutlinerView;
+
+ /// geometry definitions with buffering
+ basegfx::B2DRange maLastRange;
+ basegfx::B2DRange maRange;
+
+ /// text content definitions with buffering
+ drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
+
+ /// bitfield
+ bool mbVisualizeSurroundingFrame : 1;
+
+ // geometry creation for OverlayObject, can use local *Last* values
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+ public:
+ TextEditOverlayObject(
+ sdr::overlay::OverlaySelection* pOverlaySelection,
+ const Color& rColor,
+ OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame);
+ virtual ~TextEditOverlayObject() override;
+
+ // data read access
+ const sdr::overlay::OverlaySelection* getOverlaySelection() const { return mpOverlaySelection; }
+ const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
+ bool getVisualizeSurroundingFrame() const { return mbVisualizeSurroundingFrame; }
+
+ /// override to check conditions for last createOverlayObjectPrimitive2DSequence
+ virtual drawinglayer::primitive2d::Primitive2DContainer getOverlayObjectPrimitive2DSequence() const override;
+
+ // data write access. In this OverlayObject we only have the
+ // callback that triggers detecting if something *has* changed
+ void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
+ void checkSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges);
+ };
+
+ drawinglayer::primitive2d::Primitive2DContainer TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ /// outer frame visualization
+ if (getVisualizeSurroundingFrame())
+ {
+ const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
+ const double fTransparence(aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01);
+ const sal_uInt16 nPixSiz(getOutlinerView().GetInvalidateMore() - 1);
+
+ aRetval.push_back(
+ new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ maRange,
+ getBaseColor().getBColor(),
+ fTransparence,
+ std::max(6, nPixSiz - 2), // grow
+ 0.0, // shrink
+ 0.0));
+ }
+
+ // add buffered TextPrimitives
+ aRetval.append(maTextPrimitives);
+
+ return aRetval;
+ }
+
+ TextEditOverlayObject::TextEditOverlayObject(
+ sdr::overlay::OverlaySelection* pOverlaySelection,
+ const Color& rColor,
+ OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame)
+ : OverlayObject(rColor),
+ mpOverlaySelection(pOverlaySelection),
+ mrOutlinerView(rOutlinerView),
+ maLastRange(),
+ maRange(),
+ maTextPrimitives(),
+ maLastTextPrimitives(),
+ mbVisualizeSurroundingFrame(bVisualizeSurroundingFrame)
+ {
+ // no AA for TextEdit overlay
+ allowAntiAliase(false);
+ }
+
+ TextEditOverlayObject::~TextEditOverlayObject()
+ {
+ if (getOverlaySelection())
+ {
+ delete mpOverlaySelection;
+ mpOverlaySelection = nullptr;
+ }
+
+ if (getOverlayManager())
+ {
+ getOverlayManager()->remove(*this);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
+ {
+ if (!getPrimitive2DSequence().empty())
+ {
+ if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
+ {
+ // conditions of last local decomposition have changed, delete to force new evaluation
+ const_cast<TextEditOverlayObject*>(this)->setPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DContainer());
+ }
+ }
+
+ if (getPrimitive2DSequence().empty())
+ {
+ // remember new buffered values
+ const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
+ const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
+ }
+
+ // call base implementation
+ return OverlayObject::getOverlayObjectPrimitive2DSequence();
+ }
+
+ void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
+ {
+ bool bObjectChange(false);
+
+ // check current range
+ const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
+ basegfx::B2DRange aNewRange(aOutArea.Left(), aOutArea.Top(), aOutArea.Right(), aOutArea.Bottom());
+ aNewRange.expand(rMinTextEditArea);
+
+ if (aNewRange != maRange)
+ {
+ maRange = aNewRange;
+ bObjectChange = true;
+ }
+
+ // check if text primitives did change
+ SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner());
+
+ if (pSdrOutliner)
+ {
+ // get TextPrimitives directly from active Outliner
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+ basegfx::B2DRange aClipRange;
+ drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
+
+ // active Outliner is always in unified oriented coordinate system (currently)
+ // so just translate to TopLeft of visible Range
+ tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
+
+ aNewTransformB.translate(
+ aOutArea.Left() - aVisArea.Left(),
+ aOutArea.Top() - aVisArea.Top());
+
+ // get the current TextPrimitives. This is the most expensive part
+ // of this mechanism, it *may* be possible to buffer layouted
+ // primitives per ParaPortion with/in/dependent on the EditEngine
+ // content if needed. For now, get and compare
+ SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ aNewTextPrimitives,
+ *pSdrOutliner,
+ aNewTransformA,
+ aNewTransformB,
+ aClipRange);
+
+ if (aNewTextPrimitives != maTextPrimitives)
+ {
+ maTextPrimitives = aNewTextPrimitives;
+ bObjectChange = true;
+ }
+ }
+
+ if (bObjectChange)
+ {
+ // if there really *was* a change signal the OverlayManager to
+ // refresh this object's visualization
+ objectChange();
+ }
+ }
+
+ void TextEditOverlayObject::checkSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges)
+ {
+ if (getOverlaySelection())
+ {
+ mpOverlaySelection->setRanges(rLogicRanges);
+ }
+ }
+} // end of anonymous namespace
// TextEdit
+// file-local static bool to control old/new behaviour of active TextEdit visualization
+static bool bAllowTextEditVisualizatkionOnOverlay(true);
-void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) const
+// callback from the active EditView, forward to evtl. existing instances of the
+// TextEditOverlayObject(s)
+void SdrObjEditView::EditViewInvalidate() const
{
- // draw old text edit stuff
- if(IsTextEdit())
+ if (IsTextEdit())
{
- const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
+ // MinTextRange may have changed. Forward it, too
+ const basegfx::B2DRange aMinTextRange(
+ aMinTextEditArea.Left(), aMinTextEditArea.Top(),
+ aMinTextEditArea.Right(), aMinTextEditArea.Bottom());
+
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate = dynamic_cast< TextEditOverlayObject* >(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkDataChange(aMinTextRange);
+ }
+ }
+ }
+}
- if(pActiveOutliner)
+void SdrObjEditView::EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const
+{
+ if (IsTextEdit())
+ {
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
{
- const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
+ TextEditOverlayObject* pCandidate = dynamic_cast< TextEditOverlayObject* >(&maTEOverlayGroup.getOverlayObject(a));
- if(nViewCount)
+ if (pCandidate)
{
- const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
- const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
+ pCandidate->checkSelectionChange(rLogicRanges);
+ }
+ }
+ }
+}
- for(sal_uInt32 i(0); i < nViewCount; i++)
+void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) const
+{
+ if (bAllowTextEditVisualizatkionOnOverlay)
+ {
+ // adapt TextEditOverlayObject(s). Need also to do this here to
+ // update the current values accordingly
+ EditViewInvalidate();
+ }
+ else
+ {
+ // draw old text edit stuff
+ if (IsTextEdit())
+ {
+ const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
+
+ if (pActiveOutliner)
+ {
+ const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
+
+ if (nViewCount)
{
- OutlinerView* pOLV = pActiveOutliner->GetView(i);
-
- // If rPaintWindow knows that the output device is a render
- // context and is aware of the underlying vcl::Window,
- // compare against that; that's how double-buffering can
- // still find the matching OutlinerView.
- OutputDevice* pOutputDevice = rPaintWindow.GetWindow() ? rPaintWindow.GetWindow() : &rPaintWindow.GetOutputDevice();
- if(pOLV->GetWindow() == pOutputDevice || comphelper::LibreOfficeKit::isActive())
+ const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
+ const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
+
+ for (sal_uInt32 i(0); i < nViewCount; i++)
{
- ImpPaintOutlinerView(*pOLV, aCheckRect, rPaintWindow.GetTargetOutputDevice());
- return;
+ OutlinerView* pOLV = pActiveOutliner->GetView(i);
+
+ // If rPaintWindow knows that the output device is a render
+ // context and is aware of the underlying vcl::Window,
+ // compare against that; that's how double-buffering can
+ // still find the matching OutlinerView.
+ OutputDevice* pOutputDevice = rPaintWindow.GetWindow() ? rPaintWindow.GetWindow() : &rPaintWindow.GetOutputDevice();
+ if (pOLV->GetWindow() == pOutputDevice || comphelper::LibreOfficeKit::isActive())
+ {
+ ImpPaintOutlinerView(*pOLV, aCheckRect, rPaintWindow.GetTargetOutputDevice());
+ return;
+ }
}
}
}
@@ -513,8 +774,16 @@ OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerVie
// create OutlinerView
OutlinerView* pOutlView=pGivenView;
pTextEditOutliner->SetUpdateMode(false);
- if (pOutlView==nullptr) pOutlView = new OutlinerView(pTextEditOutliner,pWin);
- else pOutlView->SetWindow(pWin);
+
+ if (pOutlView == nullptr)
+ {
+ pOutlView = new OutlinerView(pTextEditOutliner, pWin);
+ }
+ else
+ {
+ pOutlView->SetWindow(pWin);
+ }
+
// disallow scrolling
EVControlBits nStat=pOutlView->GetControlWord();
nStat&=~EVControlBits::AUTOSCROLL;
@@ -859,6 +1128,57 @@ bool SdrObjEditView::SdrBeginTextEdit(
pTextEditOutlinerView=ImpMakeOutlinerView(pWin,pGivenOutlinerView);
+ if (bAllowTextEditVisualizatkionOnOverlay && pTextEditOutlinerView)
+ {
+ // activate visualization of EditView on Overlay
+ pTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
+
+ const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
+ const Color aHilightColor(aSvtOptionsDrawinglayer.getHilightColor());
+ const SdrTextObj* pText = dynamic_cast<SdrTextObj*>(GetTextEditObject());
+ const bool bTextFrame(pText && pText->IsTextFrame());
+ const bool bFitToSize(pTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
+ const bool bVisualizeSurroundingFrame(bTextFrame && !bFitToSize);
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if (pPageView)
+ {
+ for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if (rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ const basegfx::B2DRange aMinTEArea(
+ aMinTextEditArea.Left(), aMinTextEditArea.Top(),
+ aMinTextEditArea.Right(), aMinTextEditArea.Bottom());
+ const std::vector< basegfx::B2DRange > aEmptySelection;
+
+ sdr::overlay::OverlaySelection* pNewOverlaySelection = new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHilightColor,
+ aEmptySelection,
+ true);
+
+ sdr::overlay::OverlayObject* pNewTextEditOverlayObject = new TextEditOverlayObject(
+ pNewOverlaySelection,
+ aHilightColor,
+ *pTextEditOutlinerView,
+ bVisualizeSurroundingFrame);
+
+ xManager->add(*pNewTextEditOverlayObject);
+ xManager->add(*pNewOverlaySelection);
+
+ maTEOverlayGroup.append(pNewTextEditOverlayObject);
+ }
+ }
+ }
+ }
+ }
+
// check if this view is already inserted
sal_uIntPtr i2,nCount = pTextEditOutliner->GetViewCount();
for( i2 = 0; i2 < nCount; i2++ )
@@ -1067,6 +1387,13 @@ SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
GetModel()->Broadcast(aHint);
}
+ // if new mechanism was used, clean it up
+ if (bAllowTextEditVisualizatkionOnOverlay && pTextEditOutlinerView)
+ {
+ pTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
+ maTEOverlayGroup.clear();
+ }
+
mxTextEditObj.reset(nullptr);
pTextEditPV=nullptr;
pTextEditWin=nullptr;
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx
index 83078bb72216..76ff4f398612 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -1639,5 +1639,18 @@ void SdrTextObj::impDecomposeChainedTextPrimitive(
rTarget = aConverter.getPrimitive2DSequence();
}
+// Direct decomposer for text visualization when you already have a prepared
+// Outliner containing all the needed information
+void SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ SdrOutliner& rOutliner,
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange)
+{
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange);
+ rTarget.append(aConverter.getPrimitive2DSequence());
+}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxat.cxx b/svx/source/svdraw/svdotxat.cxx
index 20325165a172..b25ead77cc87 100644
--- a/svx/source/svdraw/svdotxat.cxx
+++ b/svx/source/svdraw/svdotxat.cxx
@@ -287,8 +287,24 @@ bool SdrTextObj::AdjustTextFrameWidthAndHeight()
if (dynamic_cast<const SdrCaptionObj *>(this) != nullptr) { // this is a hack
static_cast<SdrCaptionObj*>(this)->ImpRecalcTail();
}
- SetChanged();
- BroadcastObjectChange();
+
+ // to not slow down EditView visualization on Overlay (see
+ // TextEditOverlayObject) it is necessary to suppress the
+ // Invalidates for the deep repaint when the size of the
+ // TextFrame changed (AdjustTextFrameWidthAndHeight returned
+ // true). The ObjectChanges are valid, invalidate will be
+ // done on EndTextEdit anyways
+ const bool bSuppressChangeWhenEditOnOverlay(
+ IsInEditMode() &&
+ GetTextEditOutliner() &&
+ GetTextEditOutliner()->hasEditViewCallbacks());
+
+ if (!bSuppressChangeWhenEditOnOverlay)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+
SendUserCall(SdrUserCallType::Resize,aBoundRect0);
}
return bRet;
diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx
index 1e3ea5e2e150..580dee39b174 100644
--- a/svx/source/svdraw/svdoutl.cxx
+++ b/svx/source/svdraw/svdoutl.cxx
@@ -24,6 +24,7 @@
#include <svx/svdmodel.hxx>
#include <editeng/eeitem.hxx>
#include <svl/itempool.hxx>
+#include <editeng/editview.hxx>
SdrOutliner::SdrOutliner( SfxItemPool* pItemPool, OutlinerMode nMode )
@@ -94,4 +95,19 @@ const SdrTextObj* SdrOutliner::GetTextObj() const
return nullptr;
}
+bool SdrOutliner::hasEditViewCallbacks() const
+{
+ for (size_t a(0); a < GetViewCount(); a++)
+ {
+ OutlinerView* pOutlinerView = GetView(a);
+
+ if (pOutlinerView && pOutlinerView->GetEditView().hasEditViewCallbacks())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */