summaryrefslogtreecommitdiffstats
path: root/sw/source/uibase/docvw
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/uibase/docvw')
-rw-r--r--sw/source/uibase/docvw/AnchorOverlayObject.cxx384
-rw-r--r--sw/source/uibase/docvw/AnchorOverlayObject.hxx120
-rw-r--r--sw/source/uibase/docvw/AnnotationMenuButton.cxx133
-rw-r--r--sw/source/uibase/docvw/AnnotationWin.cxx514
-rw-r--r--sw/source/uibase/docvw/AnnotationWin2.cxx1450
-rw-r--r--sw/source/uibase/docvw/DashedLine.cxx91
-rw-r--r--sw/source/uibase/docvw/FrameControlsManager.cxx269
-rw-r--r--sw/source/uibase/docvw/HeaderFooterWin.cxx591
-rw-r--r--sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx246
-rw-r--r--sw/source/uibase/docvw/OverlayRanges.cxx176
-rw-r--r--sw/source/uibase/docvw/OverlayRanges.hxx70
-rw-r--r--sw/source/uibase/docvw/PageBreakWin.cxx506
-rw-r--r--sw/source/uibase/docvw/PostItMgr.cxx2518
-rw-r--r--sw/source/uibase/docvw/ShadowOverlayObject.cxx236
-rw-r--r--sw/source/uibase/docvw/ShadowOverlayObject.hxx67
-rw-r--r--sw/source/uibase/docvw/SidebarTxtControl.cxx470
-rw-r--r--sw/source/uibase/docvw/SidebarTxtControl.hxx83
-rw-r--r--sw/source/uibase/docvw/SidebarWinAcc.cxx142
-rw-r--r--sw/source/uibase/docvw/SidebarWinAcc.hxx53
-rw-r--r--sw/source/uibase/docvw/UnfloatTableButton.cxx254
-rw-r--r--sw/source/uibase/docvw/edtdd.cxx497
-rw-r--r--sw/source/uibase/docvw/edtwin.cxx6845
-rw-r--r--sw/source/uibase/docvw/edtwin2.cxx500
-rw-r--r--sw/source/uibase/docvw/edtwin3.cxx175
-rw-r--r--sw/source/uibase/docvw/frmsidebarwincontainer.cxx172
-rw-r--r--sw/source/uibase/docvw/frmsidebarwincontainer.hxx62
-rw-r--r--sw/source/uibase/docvw/romenu.cxx344
-rw-r--r--sw/source/uibase/docvw/romenu.hxx80
-rw-r--r--sw/source/uibase/docvw/srcedtw.cxx984
29 files changed, 18032 insertions, 0 deletions
diff --git a/sw/source/uibase/docvw/AnchorOverlayObject.cxx b/sw/source/uibase/docvw/AnchorOverlayObject.cxx
new file mode 100644
index 000000000..482bc6b83
--- /dev/null
+++ b/sw/source/uibase/docvw/AnchorOverlayObject.cxx
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "AnchorOverlayObject.hxx"
+#include <SidebarWindowsConsts.hxx>
+
+#include <swrect.hxx>
+#include <view.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <tools/long.hxx>
+
+#include <sw_primitivetypes2d.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/primitivetools2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+
+namespace sw::sidebarwindows {
+
+namespace {
+
+// helper class: Primitive for discrete visualisation
+class AnchorPrimitive : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
+{
+private:
+ basegfx::B2DPolygon maTriangle;
+ basegfx::B2DPolygon maLine;
+ basegfx::B2DPolygon maLineTop;
+ const AnchorState maAnchorState;
+ basegfx::BColor maColor;
+
+ // discrete line width
+ double mfDiscreteLineWidth;
+
+ bool mbLineSolid : 1;
+
+protected:
+ virtual void create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override;
+
+public:
+ AnchorPrimitive( const basegfx::B2DPolygon& rTriangle,
+ const basegfx::B2DPolygon& rLine,
+ const basegfx::B2DPolygon& rLineTop,
+ AnchorState aAnchorState,
+ const basegfx::BColor& rColor,
+ double fDiscreteLineWidth,
+ bool bLineSolid )
+ : maTriangle(rTriangle),
+ maLine(rLine),
+ maLineTop(rLineTop),
+ maAnchorState(aAnchorState),
+ maColor(rColor),
+ mfDiscreteLineWidth(fDiscreteLineWidth),
+ mbLineSolid(bLineSolid)
+ {}
+
+ // data access
+ const basegfx::B2DPolygon& getLine() const { return maLine; }
+ const basegfx::BColor& getColor() const { return maColor; }
+ bool getLineSolid() const { return mbLineSolid; }
+
+ virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const override;
+
+ virtual sal_uInt32 getPrimitive2DID() const override;
+};
+
+}
+
+void AnchorPrimitive::create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ if ( AnchorState::Tri == maAnchorState ||
+ AnchorState::All == maAnchorState )
+ {
+ // create triangle
+ const drawinglayer::primitive2d::Primitive2DReference aTriangle(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(maTriangle),
+ getColor()));
+
+ rContainer.push_back(aTriangle);
+ }
+
+ // prepare view-independent LineWidth and color
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ getColor(),
+ mfDiscreteLineWidth * getDiscreteUnit());
+
+ if ( AnchorState::All == maAnchorState )
+ {
+ // create line start
+ if(getLineSolid())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aSolidLine(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ getLine(),
+ aLineAttribute));
+
+ rContainer.push_back(aSolidLine);
+ }
+ else
+ {
+ std::vector< double > aDotDashArray;
+ const double fDistance(3.0 * 15.0);
+ const double fDashLen(5.0 * 15.0);
+
+ aDotDashArray.push_back(fDashLen);
+ aDotDashArray.push_back(fDistance);
+
+ const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
+ std::move(aDotDashArray),
+ fDistance + fDashLen);
+
+ const drawinglayer::primitive2d::Primitive2DReference aStrokedLine(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ getLine(),
+ aLineAttribute,
+ aStrokeAttribute));
+
+ rContainer.push_back(aStrokedLine);
+ }
+ }
+
+ if ( AnchorState::All == maAnchorState ||
+ AnchorState::End == maAnchorState )
+ {
+ // LineTop has to be created, too, but uses no shadow, so add after
+ // the other parts are created
+ const drawinglayer::primitive2d::Primitive2DReference aLineTop(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ maLineTop,
+ aLineAttribute));
+
+ rContainer.push_back(aLineTop);
+ }
+}
+
+bool AnchorPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
+{
+ if(drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const AnchorPrimitive& rCompare = static_cast< const AnchorPrimitive& >(rPrimitive);
+
+ return (maTriangle == rCompare.maTriangle
+ && getLine() == rCompare.getLine()
+ && maLineTop == rCompare.maLineTop
+ && maAnchorState == rCompare.maAnchorState
+ && getColor() == rCompare.getColor()
+ && mfDiscreteLineWidth == rCompare.mfDiscreteLineWidth
+ && getLineSolid() == rCompare.getLineSolid());
+ }
+
+ return false;
+}
+
+sal_uInt32 AnchorPrimitive::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SWSIDEBARANCHORPRIMITIVE;
+}
+
+/*static*/ std::unique_ptr<AnchorOverlayObject> AnchorOverlayObject::CreateAnchorOverlayObject(
+ SwView const & rDocView,
+ const SwRect& aAnchorRect,
+ tools::Long aPageBorder,
+ const Point& aLineStart,
+ const Point& aLineEnd,
+ const Color& aColorAnchor )
+{
+ std::unique_ptr<AnchorOverlayObject> pAnchorOverlayObject;
+ if ( rDocView.GetDrawView() )
+ {
+ SdrPaintWindow* pPaintWindow = rDocView.GetDrawView()->GetPaintWindow(0);
+ if( pPaintWindow )
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
+
+ if ( xOverlayManager.is() )
+ {
+ pAnchorOverlayObject.reset(new AnchorOverlayObject(
+ basegfx::B2DPoint( aAnchorRect.Left() , aAnchorRect.Bottom()-5*15),
+ basegfx::B2DPoint( aAnchorRect.Left()-5*15 , aAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( aAnchorRect.Left()+5*15 , aAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( aAnchorRect.Left(), aAnchorRect.Bottom()+2*15),
+ basegfx::B2DPoint( aPageBorder ,aAnchorRect.Bottom()+2*15),
+ basegfx::B2DPoint( aLineStart.X(),aLineStart.Y()),
+ basegfx::B2DPoint( aLineEnd.X(),aLineEnd.Y()) ,
+ aColorAnchor));
+ xOverlayManager->add(*pAnchorOverlayObject);
+ }
+ }
+ }
+
+ return pAnchorOverlayObject;
+}
+
+AnchorOverlayObject::AnchorOverlayObject( const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPos,
+ const basegfx::B2DPoint& rThirdPos,
+ const basegfx::B2DPoint& rFourthPos,
+ const basegfx::B2DPoint& rFifthPos,
+ const basegfx::B2DPoint& rSixthPos,
+ const basegfx::B2DPoint& rSeventhPos,
+ const Color& rBaseColor)
+ : OverlayObjectWithBasePosition(rBasePos, rBaseColor)
+ , maSecondPosition(rSecondPos)
+ , maThirdPosition(rThirdPos)
+ , maFourthPosition(rFourthPos)
+ , maFifthPosition(rFifthPos)
+ , maSixthPosition(rSixthPos)
+ , maSeventhPosition(rSeventhPos)
+ , mAnchorState(AnchorState::All)
+ , mbLineSolid(false)
+{
+}
+
+AnchorOverlayObject::~AnchorOverlayObject()
+{
+ if ( getOverlayManager() )
+ {
+ // remove this object from the chain
+ getOverlayManager()->remove(*this);
+ }
+}
+
+void AnchorOverlayObject::implEnsureGeometry()
+{
+ if(!maTriangle.count())
+ {
+ maTriangle.append(getBasePosition());
+ maTriangle.append(GetSecondPosition());
+ maTriangle.append(GetThirdPosition());
+ maTriangle.setClosed(true);
+ }
+
+ if(!maLine.count())
+ {
+ maLine.append(GetFourthPosition());
+ maLine.append(GetFifthPosition());
+ maLine.append(GetSixthPosition());
+ }
+
+ if(!maLineTop.count())
+ {
+ maLineTop.append(GetSixthPosition());
+ maLineTop.append(GetSeventhPosition());
+ }
+}
+
+void AnchorOverlayObject::implResetGeometry()
+{
+ maTriangle.clear();
+ maLine.clear();
+ maLineTop.clear();
+}
+
+drawinglayer::primitive2d::Primitive2DContainer AnchorOverlayObject::createOverlayObjectPrimitive2DSequence()
+{
+ implEnsureGeometry();
+
+ static const double aDiscreteLineWidth(1.6);
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new AnchorPrimitive( maTriangle,
+ maLine,
+ maLineTop,
+ GetAnchorState(),
+ getBaseColor().getBColor(),
+ ANCHORLINE_WIDTH * aDiscreteLineWidth,
+ getLineSolid()) );
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+}
+
+void AnchorOverlayObject::SetAllPosition( const basegfx::B2DPoint& rPoint1,
+ const basegfx::B2DPoint& rPoint2,
+ const basegfx::B2DPoint& rPoint3,
+ const basegfx::B2DPoint& rPoint4,
+ const basegfx::B2DPoint& rPoint5,
+ const basegfx::B2DPoint& rPoint6,
+ const basegfx::B2DPoint& rPoint7)
+{
+ if ( !(rPoint1 != getBasePosition() ||
+ rPoint2 != GetSecondPosition() ||
+ rPoint3 != GetThirdPosition() ||
+ rPoint4 != GetFourthPosition() ||
+ rPoint5 != GetFifthPosition() ||
+ rPoint6 != GetSixthPosition() ||
+ rPoint7 != GetSeventhPosition()) )
+ return;
+
+ maBasePosition = rPoint1;
+ maSecondPosition = rPoint2;
+ maThirdPosition = rPoint3;
+ maFourthPosition = rPoint4;
+ maFifthPosition = rPoint5;
+ maSixthPosition = rPoint6;
+ maSeventhPosition = rPoint7;
+
+ implResetGeometry();
+ objectChange();
+}
+
+void AnchorOverlayObject::SetSixthPosition(const basegfx::B2DPoint& rNew)
+{
+ if(rNew != maSixthPosition)
+ {
+ maSixthPosition = rNew;
+ implResetGeometry();
+ objectChange();
+ }
+}
+
+void AnchorOverlayObject::SetSeventhPosition(const basegfx::B2DPoint& rNew)
+{
+ if(rNew != maSeventhPosition)
+ {
+ maSeventhPosition = rNew;
+ implResetGeometry();
+ objectChange();
+ }
+}
+
+void AnchorOverlayObject::SetTriPosition(const basegfx::B2DPoint& rPoint1,const basegfx::B2DPoint& rPoint2,const basegfx::B2DPoint& rPoint3,
+ const basegfx::B2DPoint& rPoint4,const basegfx::B2DPoint& rPoint5)
+{
+ if(rPoint1 != getBasePosition()
+ || rPoint2 != GetSecondPosition()
+ || rPoint3 != GetThirdPosition()
+ || rPoint4 != GetFourthPosition()
+ || rPoint5 != GetFifthPosition())
+ {
+ maBasePosition = rPoint1;
+ maSecondPosition = rPoint2;
+ maThirdPosition = rPoint3;
+ maFourthPosition = rPoint4;
+ maFifthPosition = rPoint5;
+
+ implResetGeometry();
+ objectChange();
+ }
+}
+
+void AnchorOverlayObject::setLineSolid( const bool bNew )
+{
+ if ( bNew != getLineSolid() )
+ {
+ mbLineSolid = bNew;
+ objectChange();
+ }
+}
+
+void AnchorOverlayObject::SetAnchorState( const AnchorState aState)
+{
+ if ( mAnchorState != aState)
+ {
+ mAnchorState = aState;
+ objectChange();
+ }
+}
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/AnchorOverlayObject.hxx b/sw/source/uibase/docvw/AnchorOverlayObject.hxx
new file mode 100644
index 000000000..744b2f062
--- /dev/null
+++ b/sw/source/uibase/docvw/AnchorOverlayObject.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <tools/long.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+class SwView;
+class SwRect;
+class Point;
+
+namespace sw::sidebarwindows {
+
+enum class AnchorState
+{
+ All,
+ End,
+ Tri
+};
+
+class AnchorOverlayObject final : public sdr::overlay::OverlayObjectWithBasePosition
+{
+ public:
+ static std::unique_ptr<AnchorOverlayObject> CreateAnchorOverlayObject( SwView const & rDocView,
+ const SwRect& aAnchorRect,
+ tools::Long aPageBorder,
+ const Point& aLineStart,
+ const Point& aLineEnd,
+ const Color& aColorAnchor );
+
+ const basegfx::B2DPoint& GetSecondPosition() const { return maSecondPosition; }
+ const basegfx::B2DPoint& GetThirdPosition() const { return maThirdPosition; }
+ const basegfx::B2DPoint& GetFourthPosition() const { return maFourthPosition; }
+ const basegfx::B2DPoint& GetFifthPosition() const { return maFifthPosition; }
+ const basegfx::B2DPoint& GetSixthPosition() const { return maSixthPosition; }
+ const basegfx::B2DPoint& GetSeventhPosition() const { return maSeventhPosition; }
+
+ void SetAllPosition( const basegfx::B2DPoint& rPoint1,
+ const basegfx::B2DPoint& rPoint2,
+ const basegfx::B2DPoint& rPoint3,
+ const basegfx::B2DPoint& rPoint4,
+ const basegfx::B2DPoint& rPoint5,
+ const basegfx::B2DPoint& rPoint6,
+ const basegfx::B2DPoint& rPoint7 );
+ void SetTriPosition( const basegfx::B2DPoint& rPoint1,
+ const basegfx::B2DPoint& rPoint2,
+ const basegfx::B2DPoint& rPoint3,
+ const basegfx::B2DPoint& rPoint4,
+ const basegfx::B2DPoint& rPoint5 );
+ void SetSixthPosition( const basegfx::B2DPoint& rNew );
+ void SetSeventhPosition( const basegfx::B2DPoint& rNew );
+
+ void setLineSolid( const bool bNew );
+ bool getLineSolid() const { return mbLineSolid; }
+
+ void SetAnchorState( const AnchorState aState );
+ AnchorState GetAnchorState() const { return mAnchorState; }
+
+ private:
+ /* 6------------7
+ 1 /
+ /4\ ---------------5
+ 2 - 3
+ */
+
+ basegfx::B2DPoint maSecondPosition;
+ basegfx::B2DPoint maThirdPosition;
+ basegfx::B2DPoint maFourthPosition;
+ basegfx::B2DPoint maFifthPosition;
+ basegfx::B2DPoint maSixthPosition;
+ basegfx::B2DPoint maSeventhPosition;
+
+ // helpers to fill and reset geometry
+ void implEnsureGeometry();
+ void implResetGeometry();
+
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+ // object's geometry
+ basegfx::B2DPolygon maTriangle;
+ basegfx::B2DPolygon maLine;
+ basegfx::B2DPolygon maLineTop;
+ AnchorState mAnchorState;
+
+ bool mbLineSolid : 1;
+
+ AnchorOverlayObject( const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPos,
+ const basegfx::B2DPoint& rThirdPos,
+ const basegfx::B2DPoint& rFourthPos,
+ const basegfx::B2DPoint& rFifthPos,
+ const basegfx::B2DPoint& rSixthPos,
+ const basegfx::B2DPoint& rSeventhPos,
+ const Color& rBaseColor );
+ public:
+ virtual ~AnchorOverlayObject() override;
+};
+
+} // end of namespace sw::annotation
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/AnnotationMenuButton.cxx b/sw/source/uibase/docvw/AnnotationMenuButton.cxx
new file mode 100644
index 000000000..1696f7e6d
--- /dev/null
+++ b/sw/source/uibase/docvw/AnnotationMenuButton.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AnnotationWin.hxx>
+#include <strings.hrc>
+
+#include <unotools/useroptions.hxx>
+
+#include <vcl/event.hxx>
+
+#include <cmdid.h>
+
+#include <swtypes.hxx>
+
+namespace sw::annotation {
+
+IMPL_LINK(SwAnnotationWin, SelectHdl, const OString&, rIdent, void)
+{
+ if (rIdent.isEmpty())
+ return;
+
+ // tdf#136682 ensure this is the currently active sidebar win so the command
+ // operates in an active sidebar context
+ bool bSwitchedFocus = SetActiveSidebarWin();
+
+ if (rIdent == "reply")
+ ExecuteCommand(FN_REPLY);
+ if (rIdent == "resolve" || rIdent == "unresolve")
+ ExecuteCommand(FN_RESOLVE_NOTE);
+ else if (rIdent == "resolvethread" || rIdent == "unresolvethread")
+ ExecuteCommand(FN_RESOLVE_NOTE_THREAD);
+ else if (rIdent == "delete")
+ ExecuteCommand(FN_DELETE_COMMENT);
+ else if (rIdent == "deletethread")
+ ExecuteCommand(FN_DELETE_COMMENT_THREAD);
+ else if (rIdent == "deleteby")
+ ExecuteCommand(FN_DELETE_NOTE_AUTHOR);
+ else if (rIdent == "deleteall")
+ ExecuteCommand(FN_DELETE_ALL_NOTES);
+ else if (rIdent == "formatall")
+ ExecuteCommand(FN_FORMAT_ALL_NOTES);
+
+ if (bSwitchedFocus)
+ UnsetActiveSidebarWin();
+ GrabFocusToDocument();
+}
+
+IMPL_LINK_NOARG(SwAnnotationWin, ToggleHdl, weld::Toggleable&, void)
+{
+ if (!mxMenuButton->get_active())
+ return;
+
+ bool bReplyVis = true;
+
+ bool bReadOnly = IsReadOnly();
+ if (bReadOnly)
+ {
+ mxMenuButton->set_item_visible("reply", false);
+ bReplyVis = false;
+ mxMenuButton->set_item_visible("resolve", false);
+ mxMenuButton->set_item_visible("unresolve", false);
+ mxMenuButton->set_item_visible("resolvethread", false);
+ mxMenuButton->set_item_visible("unresolvethread", false);
+ mxMenuButton->set_item_visible("delete", false );
+ }
+ else
+ {
+ mxMenuButton->set_item_visible("resolve", !IsResolved());
+ mxMenuButton->set_item_visible("unresolve", IsResolved());
+ mxMenuButton->set_item_visible("resolvethread", !IsThreadResolved());
+ mxMenuButton->set_item_visible("unresolvethread", IsThreadResolved());
+ mxMenuButton->set_item_visible("delete", !IsProtected());
+ }
+
+ mxMenuButton->set_item_visible("deletethread", !bReadOnly);
+ mxMenuButton->set_item_visible("deleteby", !bReadOnly);
+ mxMenuButton->set_item_visible("deleteall", !bReadOnly);
+ mxMenuButton->set_item_visible("formatall", !bReadOnly);
+
+ if (IsProtected())
+ {
+ mxMenuButton->set_item_visible("reply", false);
+ bReplyVis = false;
+ }
+ else
+ {
+ SvtUserOptions aUserOpt;
+ OUString sAuthor;
+ if ((sAuthor = aUserOpt.GetFullName()).isEmpty())
+ {
+ if ((sAuthor = aUserOpt.GetID()).isEmpty())
+ {
+ sAuthor = SwResId(STR_REDLINE_UNKNOWN_AUTHOR);
+ }
+ }
+ // do not allow to reply to ourself and no answer possible if this note is in a protected section
+ bReplyVis = sAuthor != GetAuthor();
+ mxMenuButton->set_item_visible("reply", bReplyVis);
+ }
+ mxMenuButton->set_item_visible("sep1", bReplyVis);
+}
+
+IMPL_LINK(SwAnnotationWin, KeyInputHdl, const KeyEvent&, rKeyEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode();
+ if (rKeyCode.GetCode() == KEY_TAB)
+ {
+ ActivatePostIt();
+ GrabFocus();
+ return true;
+ }
+ return false;
+}
+
+} // end of namespace sw::annotation
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/AnnotationWin.cxx b/sw/source/uibase/docvw/AnnotationWin.cxx
new file mode 100644
index 000000000..b7858a973
--- /dev/null
+++ b/sw/source/uibase/docvw/AnnotationWin.cxx
@@ -0,0 +1,514 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <AnnotationWin.hxx>
+
+#include <PostItMgr.hxx>
+
+#include <strings.hrc>
+
+#include <uiobject.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <svl/undo.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <svl/languageoptions.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/eeitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/langitem.hxx>
+
+#include <editeng/editview.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <comphelper/lok.hxx>
+#include <docufld.hxx>
+#include <txtfld.hxx>
+#include <ndtxt.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <wrtsh.hxx>
+#include <docsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <SwUndoField.hxx>
+#include <edtwin.hxx>
+#include "ShadowOverlayObject.hxx"
+#include "AnchorOverlayObject.hxx"
+#include "OverlayRanges.hxx"
+#include "SidebarTxtControl.hxx"
+#include "SidebarWinAcc.hxx"
+
+#include <memory>
+
+namespace{
+
+void collectUIInformation( const OUString& aevent , const OUString& aID )
+{
+ EventDescription aDescription;
+ aDescription.aID = aID;
+ aDescription.aParameters = {{"" , ""}};
+ aDescription.aAction = aevent;
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "SwEditWinUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+namespace sw::annotation {
+
+// see AnnotationContents in sd for something similar
+SwAnnotationWin::SwAnnotationWin( SwEditWin& rEditWin,
+ SwPostItMgr& aMgr,
+ SwSidebarItem& rSidebarItem,
+ SwFormatField* aField )
+ : InterimItemWindow(&rEditWin, "modules/swriter/ui/annotation.ui", "Annotation")
+ , mrMgr(aMgr)
+ , mrView(rEditWin.GetView())
+ , mnDeleteEventId(nullptr)
+ , meSidebarPosition(sw::sidebarwindows::SidebarPosition::NONE)
+ , mPageBorder(0)
+ , mbAnchorRectChanged(false)
+ , mbResolvedStateUpdated(false)
+ , mbMouseOver(false)
+ , mLayoutStatus(SwPostItHelper::INVISIBLE)
+ , mbReadonly(false)
+ , mbIsFollow(false)
+ , mrSidebarItem(rSidebarItem)
+ , mpAnchorFrame(rSidebarItem.maLayoutInfo.mpAnchorFrame)
+ , mpFormatField(aField)
+ , mpField( static_cast<SwPostItField*>(aField->GetField()))
+{
+ set_id("Comment"+OUString::number(mpField->GetPostItId()));
+
+ m_xContainer->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl));
+
+ mpShadow = sidebarwindows::ShadowOverlayObject::CreateShadowOverlayObject( mrView );
+ if ( mpShadow )
+ {
+ mpShadow->setVisible(false);
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ mrMgr.ConnectSidebarWinToFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame),
+ mrSidebarItem.GetFormatField(),
+ *this );
+#endif
+
+ if (SupportsDoubleBuffering())
+ // When double-buffering, allow parents to paint on our area. That's
+ // necessary when parents paint the complete buffer.
+ SetParentClipMode(ParentClipMode::NoClip);
+}
+
+SwAnnotationWin::~SwAnnotationWin()
+{
+ disposeOnce();
+}
+
+void SwAnnotationWin::dispose()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ mrMgr.DisconnectSidebarWinFromFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame),
+ *this );
+#endif
+ Disable();
+
+ mxSidebarTextControlWin.reset();
+ mxSidebarTextControl.reset();
+
+ mxMetadataAuthor.reset();
+ mxMetadataResolved.reset();
+ mxMetadataDate.reset();
+ mxVScrollbar.reset();
+
+ mpAnchor.reset();
+ mpShadow.reset();
+
+ mpTextRangeOverlay.reset();
+
+ mxMenuButton.reset();
+
+ if (mnDeleteEventId)
+ Application::RemoveUserEvent(mnDeleteEventId);
+
+ mpOutliner.reset();
+ mpOutlinerView.reset();
+
+ InterimItemWindow::dispose();
+}
+
+void SwAnnotationWin::SetPostItText()
+{
+ //If the cursor was visible, then make it visible again after
+ //changing text, e.g. fdo#33599
+ vcl::Cursor *pCursor = GetOutlinerView()->GetEditView().GetCursor();
+ bool bCursorVisible = pCursor && pCursor->IsVisible();
+
+ //If the new text is the same as the old text, keep the same insertion
+ //point .e.g. fdo#33599
+ mpField = static_cast<SwPostItField*>(mpFormatField->GetField());
+ OUString sNewText = mpField->GetPar2();
+ bool bTextUnchanged = sNewText == mpOutliner->GetEditEngine().GetText();
+ ESelection aOrigSelection(GetOutlinerView()->GetEditView().GetSelection());
+
+ // get text from SwPostItField and insert into our textview
+ mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() );
+ mpOutliner->EnableUndo( false );
+ if( mpField->GetTextObject() )
+ mpOutliner->SetText( *mpField->GetTextObject() );
+ else
+ {
+ mpOutliner->Clear();
+ GetOutlinerView()->SetAttribs(DefaultItem());
+ GetOutlinerView()->InsertText(sNewText);
+ }
+
+ mpOutliner->ClearModifyFlag();
+ mpOutliner->GetUndoManager().Clear();
+ mpOutliner->EnableUndo( true );
+ mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) );
+ if (bTextUnchanged)
+ GetOutlinerView()->GetEditView().SetSelection(aOrigSelection);
+ if (bCursorVisible)
+ GetOutlinerView()->ShowCursor();
+ Invalidate();
+}
+
+void SwAnnotationWin::SetResolved(bool resolved)
+{
+ bool oldState = IsResolved();
+ static_cast<SwPostItField*>(mpFormatField->GetField())->SetResolved(resolved);
+ const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions();
+ mrSidebarItem.mbShow = !IsResolved() || (pVOpt->IsResolvedPostIts());
+
+ mpTextRangeOverlay.reset();
+
+ if(IsResolved())
+ mxMetadataResolved->show();
+ else
+ mxMetadataResolved->hide();
+
+ if(IsResolved() != oldState)
+ mbResolvedStateUpdated = true;
+ UpdateData();
+ Invalidate();
+ collectUIInformation("SETRESOLVED",get_id());
+}
+
+void SwAnnotationWin::ToggleResolved()
+{
+ SetResolved(!IsResolved());
+}
+
+void SwAnnotationWin::ToggleResolvedForThread()
+{
+ GetTopReplyNote()->ToggleResolved();
+ mrMgr.UpdateResolvedStatus(GetTopReplyNote());
+ mrMgr.LayoutPostIts();
+}
+
+void SwAnnotationWin::DeleteThread()
+{
+ // Go to the top and delete each comment one by one
+ SwAnnotationWin *current, *topNote;
+ current = topNote = GetTopReplyNote();
+ SwAnnotationWin* next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
+
+ while(next && next->GetTopReplyNote() == topNote)
+ {
+ current->mnDeleteEventId = Application::PostUserEvent( LINK( current, SwAnnotationWin, DeleteHdl), nullptr, true );
+ current = next;
+ next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current);
+ }
+ current->mnDeleteEventId = Application::PostUserEvent( LINK( current, SwAnnotationWin, DeleteHdl), nullptr, true );
+}
+
+bool SwAnnotationWin::IsResolved() const
+{
+ return static_cast<SwPostItField*>(mpFormatField->GetField())->GetResolved();
+}
+
+bool SwAnnotationWin::IsThreadResolved()
+{
+ /// First Get the top note
+ // then iterate downwards checking resolved status
+ SwAnnotationWin *pTopNote, *TopNote;
+ pTopNote = TopNote = GetTopReplyNote();
+ if (!pTopNote->IsResolved())
+ return false;
+
+ SwAnnotationWin* pSidebarWin = mrMgr.GetNextPostIt(KEY_PAGEDOWN, pTopNote);
+
+ while (pSidebarWin && pSidebarWin->GetTopReplyNote() == TopNote)
+ {
+ pTopNote = pSidebarWin;
+ if (!pTopNote->IsResolved())
+ return false;
+ pSidebarWin = mrMgr.GetNextPostIt(KEY_PAGEDOWN, pSidebarWin);
+ }
+ return true;
+}
+
+void SwAnnotationWin::UpdateData()
+{
+ if ( mpOutliner->IsModified() || mbResolvedStateUpdated )
+ {
+ IDocumentUndoRedo & rUndoRedo(
+ mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo());
+ std::unique_ptr<SwField> pOldField;
+ if (rUndoRedo.DoesUndo())
+ {
+ pOldField = mpField->Copy();
+ }
+ mpField->SetPar2(mpOutliner->GetEditEngine().GetText());
+ mpField->SetTextObject(mpOutliner->CreateParaObject());
+ if (rUndoRedo.DoesUndo())
+ {
+ SwTextField *const pTextField = mpFormatField->GetTextField();
+ SwPosition aPosition( pTextField->GetTextNode() );
+ aPosition.nContent = pTextField->GetStart();
+ rUndoRedo.AppendUndo(
+ std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, nullptr, true));
+ }
+ // so we get a new layout of notes (anchor position is still the same and we would otherwise not get one)
+ mrMgr.SetLayout();
+ // #i98686# if we have several views, all notes should update their text
+ if(mbResolvedStateUpdated)
+ mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::RESOLVED));
+ else
+ mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::CHANGED));
+ mrView.GetDocShell()->SetModified();
+ }
+ mpOutliner->ClearModifyFlag();
+ mpOutliner->GetUndoManager().Clear();
+ mbResolvedStateUpdated = false;
+}
+
+void SwAnnotationWin::Delete()
+{
+ collectUIInformation("DELETE",get_id());
+ if (!mrView.GetWrtShellPtr()->GotoField(*mpFormatField))
+ return;
+
+ if ( mrMgr.GetActiveSidebarWin() == this)
+ {
+ mrMgr.SetActiveSidebarWin(nullptr);
+ // if the note is empty, the previous line will send a delete event, but we are already there
+ if (mnDeleteEventId)
+ {
+ Application::RemoveUserEvent(mnDeleteEventId);
+ mnDeleteEventId = nullptr;
+ }
+ }
+ // we delete the field directly, the Mgr cleans up the PostIt by listening
+ GrabFocusToDocument();
+ mrView.GetWrtShellPtr()->ClearMark();
+ mrView.GetWrtShellPtr()->DelRight();
+}
+
+void SwAnnotationWin::GotoPos()
+{
+ mrView.GetDocShell()->GetWrtShell()->GotoField(*mpFormatField);
+}
+
+sal_uInt32 SwAnnotationWin::MoveCaret()
+{
+ // if this is an answer, do not skip over all following ones, but insert directly behind the current one
+ // but when just leaving a note, skip all following ones as well to continue typing
+ return mrMgr.IsAnswer()
+ ? 1
+ : 1 + CountFollowing();
+}
+
+// returns a non-zero postit parent id, if exists, otherwise 0 for root comments
+sal_uInt32 SwAnnotationWin::CalcParent()
+{
+ SwTextField* pTextField = mpFormatField->GetTextField();
+ SwPosition aPosition( pTextField->GetTextNode() );
+ aPosition.nContent = pTextField->GetStart();
+ SwTextAttr * const pTextAttr =
+ pTextField->GetTextNode().GetTextAttrForCharAt(
+ aPosition.nContent.GetIndex() - 1,
+ RES_TXTATR_ANNOTATION );
+ const SwField* pField = pTextAttr ? pTextAttr->GetFormatField().GetField() : nullptr;
+ sal_uInt32 nParentId = 0;
+ if (pField && pField->Which() == SwFieldIds::Postit)
+ {
+ const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField);
+ nParentId = pPostItField->GetPostItId();
+ }
+ return nParentId;
+}
+
+// counts how many SwPostItField we have right after the current one
+sal_uInt32 SwAnnotationWin::CountFollowing()
+{
+ sal_uInt32 aCount = 1; // we start with 1, so we have to subtract one at the end again
+ SwTextField* pTextField = mpFormatField->GetTextField();
+ SwPosition aPosition( pTextField->GetTextNode() );
+ aPosition.nContent = pTextField->GetStart();
+
+ SwTextAttr * pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt(
+ aPosition.nContent.GetIndex() + 1,
+ RES_TXTATR_ANNOTATION );
+ SwField* pField = pTextAttr
+ ? const_cast<SwField*>(pTextAttr->GetFormatField().GetField())
+ : nullptr;
+ while ( pField && ( pField->Which()== SwFieldIds::Postit ) )
+ {
+ aCount++;
+ pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt(
+ aPosition.nContent.GetIndex() + aCount,
+ RES_TXTATR_ANNOTATION );
+ pField = pTextAttr
+ ? const_cast<SwField*>(pTextAttr->GetFormatField().GetField())
+ : nullptr;
+ }
+ return aCount - 1;
+}
+
+void SwAnnotationWin::InitAnswer(OutlinerParaObject const & rText)
+{
+ // If tiled annotations is off in lok case, skip adding additional reply text.
+ if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
+ return;
+
+ //collect our old meta data
+ SwAnnotationWin* pWin = mrMgr.GetNextPostIt(KEY_PAGEUP, this);
+ if (!pWin)
+ return;
+
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData();
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, pWin->GetAuthor());
+ const OUString aText = aRewriter.Apply(SwResId(STR_REPLY))
+ + " (" + rLocalData.getDate( pWin->GetDate())
+ + ", " + rLocalData.getTime( pWin->GetTime(), false)
+ + "): \"";
+ GetOutlinerView()->InsertText(aText);
+
+ // insert old, selected text or "..."
+ // TODO: iterate over all paragraphs, not only first one to find out if it is empty
+ if (!rText.GetTextObject().GetText(0).isEmpty())
+ GetOutlinerView()->GetEditView().InsertText(rText.GetTextObject());
+ else
+ GetOutlinerView()->InsertText("...");
+ GetOutlinerView()->InsertText("\"\n");
+
+ GetOutlinerView()->SetSelection(ESelection(0,0,EE_PARA_ALL,EE_TEXTPOS_ALL));
+ SfxItemSet aAnswerSet( mrView.GetDocShell()->GetPool() );
+ aAnswerSet.Put(SvxFontHeightItem(200,80,EE_CHAR_FONTHEIGHT));
+ aAnswerSet.Put(SvxPostureItem(ITALIC_NORMAL,EE_CHAR_ITALIC));
+ GetOutlinerView()->SetAttribs(aAnswerSet);
+ GetOutlinerView()->SetSelection(ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT));
+
+ //remove all attributes and reset our standard ones
+ GetOutlinerView()->GetEditView().RemoveAttribsKeepLanguages(true);
+ GetOutlinerView()->SetAttribs(DefaultItem());
+ // lets insert an undo step so the initial text can be easily deleted
+ // but do not use UpdateData() directly, would set modified state again and reentrance into Mgr
+ mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() );
+ IDocumentUndoRedo & rUndoRedo(
+ mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo());
+ std::unique_ptr<SwField> pOldField;
+ if (rUndoRedo.DoesUndo())
+ {
+ pOldField = mpField->Copy();
+ }
+ mpField->SetPar2(mpOutliner->GetEditEngine().GetText());
+ mpField->SetTextObject(mpOutliner->CreateParaObject());
+ if (rUndoRedo.DoesUndo())
+ {
+ SwTextField *const pTextField = mpFormatField->GetTextField();
+ SwPosition aPosition( pTextField->GetTextNode() );
+ aPosition.nContent = pTextField->GetStart();
+ rUndoRedo.AppendUndo(
+ std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, nullptr, true));
+ }
+ mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) );
+ mpOutliner->ClearModifyFlag();
+ mpOutliner->GetUndoManager().Clear();
+}
+
+void SwAnnotationWin::UpdateText(const OUString& aText)
+{
+ mpOutliner->Clear();
+ GetOutlinerView()->InsertText(aText);
+ UpdateData();
+}
+
+SvxLanguageItem SwAnnotationWin::GetLanguage() const
+{
+ // set initial language for outliner
+ SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( mpField->GetLanguage() );
+ sal_uInt16 nLangWhichId = 0;
+ switch (nScriptType)
+ {
+ case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE ; break;
+ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
+ default: OSL_FAIL("GetLanguage: wrong script type");
+ }
+ return SvxLanguageItem(mpField->GetLanguage(),nLangWhichId);
+}
+
+bool SwAnnotationWin::IsProtected() const
+{
+ return mbReadonly ||
+ GetLayoutStatus() == SwPostItHelper::DELETED ||
+ ( mpFormatField && mpFormatField->IsProtect() );
+}
+
+OUString SwAnnotationWin::GetAuthor() const
+{
+ return mpField->GetPar1();
+}
+
+Date SwAnnotationWin::GetDate() const
+{
+ return mpField->GetDate();
+}
+
+tools::Time SwAnnotationWin::GetTime() const
+{
+ return mpField->GetTime();
+}
+
+FactoryFunction SwAnnotationWin::GetUITestFactory() const
+{
+ return CommentUIObject::create;
+}
+
+} // end of namespace sw::annotation
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/AnnotationWin2.cxx b/sw/source/uibase/docvw/AnnotationWin2.cxx
new file mode 100644
index 000000000..fb414a4f1
--- /dev/null
+++ b/sw/source/uibase/docvw/AnnotationWin2.cxx
@@ -0,0 +1,1450 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <sal/config.h>
+
+#include <cstddef>
+
+#include "SidebarWinAcc.hxx"
+#include <PostItMgr.hxx>
+#include <AnnotationWin.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include "SidebarTxtControl.hxx"
+#include "AnchorOverlayObject.hxx"
+#include "ShadowOverlayObject.hxx"
+#include "OverlayRanges.hxx"
+
+#include <strings.hrc>
+
+#include <viewopt.hxx>
+#include <cmdid.h>
+
+#include <editeng/fhgtitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <svl/undo.hxx>
+#include <svl/stritem.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+
+#include <vcl/decoview.hxx>
+#include <vcl/event.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <swmodule.hxx>
+
+#include <SwRewriter.hxx>
+#include <txtannotationfld.hxx>
+#include <ndtxt.hxx>
+
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <osl/diagnose.h>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <memory>
+#include <comphelper/lok.hxx>
+
+using namespace sw::sidebarwindows;
+
+namespace
+{
+
+void collectUIInformation( const OUString& aevent , const OUString& aID )
+{
+ EventDescription aDescription;
+ aDescription.aID = aID;
+ aDescription.aParameters = {{"" , ""}};
+ aDescription.aAction = aevent;
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "SwEditWinUIObject";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+namespace sw::annotation {
+
+#define METABUTTON_WIDTH 16
+#define METABUTTON_HEIGHT 18
+#define POSTIT_MINIMUMSIZE_WITHOUT_META 50
+
+void SwAnnotationWin::PaintTile(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ bool bMenuButtonVisible = mxMenuButton->get_visible();
+ // No point in showing this button till click on it are not handled.
+ if (bMenuButtonVisible)
+ mxMenuButton->hide();
+
+ // draw left over space
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ rRenderContext.SetFillColor(COL_BLACK);
+ else
+ rRenderContext.SetFillColor(mColorDark);
+ rRenderContext.SetLineColor();
+ rRenderContext.DrawRect(rRect);
+
+ m_xContainer->draw(rRenderContext, rRect.TopLeft(), GetSizePixel());
+
+ const drawinglayer::geometry::ViewInformation2D aViewInformation;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aViewInformation));
+
+ // drawinglayer sets the map mode to pixels, not needed here.
+ rRenderContext.Pop();
+ // Work in document-global twips.
+ rRenderContext.Pop();
+ if (mpAnchor)
+ pProcessor->process(mpAnchor->getOverlayObjectPrimitive2DSequence());
+ if (mpTextRangeOverlay)
+ pProcessor->process(mpTextRangeOverlay->getOverlayObjectPrimitive2DSequence());
+
+ rRenderContext.Push(vcl::PushFlags::NONE);
+ pProcessor.reset();
+ rRenderContext.Push(vcl::PushFlags::NONE);
+
+ if (bMenuButtonVisible)
+ mxMenuButton->show();
+}
+
+bool SwAnnotationWin::IsHitWindow(const Point& rPointLogic)
+{
+ tools::Rectangle aRectangleLogic(EditWin().PixelToLogic(GetPosPixel()), EditWin().PixelToLogic(GetSizePixel()));
+ return aRectangleLogic.Contains(rPointLogic);
+}
+
+void SwAnnotationWin::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark)
+{
+ mxSidebarTextControl->SetCursorLogicPosition(rPosition, bPoint, bClearMark);
+}
+
+void SwAnnotationWin::DrawForPage(OutputDevice* pDev, const Point& rPt)
+{
+ // tdf#143511 unclip SysObj so get_extents_relative_to of children
+ // of the SysObj can provide meaningful results
+ UnclipVisibleSysObj();
+
+ pDev->Push();
+
+ pDev->SetFillColor(mColorDark);
+ pDev->SetLineColor();
+
+ pDev->SetTextColor(mColorAnchor);
+ vcl::Font aFont = maLabelFont;
+ aFont.SetFontHeight(aFont.GetFontHeight() * 20);
+ pDev->SetFont(aFont);
+
+ Size aSz = PixelToLogic(GetSizePixel());
+
+ pDev->DrawRect(tools::Rectangle(rPt, aSz));
+
+ if (mxMetadataAuthor->get_visible())
+ {
+ int x, y, width, height;
+ mxMetadataAuthor->get_extents_relative_to(*m_xContainer, x, y, width, height);
+ Point aPos(rPt + PixelToLogic(Point(x, y)));
+ Size aSize(PixelToLogic(Size(width, height)));
+
+ pDev->Push(vcl::PushFlags::CLIPREGION);
+ pDev->IntersectClipRegion(tools::Rectangle(aPos, aSize));
+ pDev->DrawText(aPos, mxMetadataAuthor->get_label());
+ pDev->Pop();
+ }
+
+ if (mxMetadataDate->get_visible())
+ {
+ int x, y, width, height;
+ mxMetadataDate->get_extents_relative_to(*m_xContainer, x, y, width, height);
+ Point aPos(rPt + PixelToLogic(Point(x, y)));
+ Size aSize(PixelToLogic(Size(width, height)));
+
+ pDev->Push(vcl::PushFlags::CLIPREGION);
+ pDev->IntersectClipRegion(tools::Rectangle(aPos, aSize));
+ pDev->DrawText(aPos, mxMetadataDate->get_label());
+ pDev->Pop();
+ }
+
+ if (mxMetadataResolved->get_visible())
+ {
+ int x, y, width, height;
+ mxMetadataResolved->get_extents_relative_to(*m_xContainer, x, y, width, height);
+ Point aPos(rPt + PixelToLogic(Point(x, y)));
+ Size aSize(PixelToLogic(Size(width, height)));
+
+ pDev->Push(vcl::PushFlags::CLIPREGION);
+ pDev->IntersectClipRegion(tools::Rectangle(aPos, aSize));
+ pDev->DrawText(aPos, mxMetadataResolved->get_label());
+ pDev->Pop();
+ }
+
+ mxSidebarTextControl->DrawForPage(pDev, rPt);
+
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(
+ *pDev, aNewViewInfos ));
+
+ if (mpAnchor)
+ pProcessor->process(mpAnchor->getOverlayObjectPrimitive2DSequence());
+ if (mpTextRangeOverlay)
+ pProcessor->process(mpTextRangeOverlay->getOverlayObjectPrimitive2DSequence());
+ pProcessor.reset();
+
+ if (mxVScrollbar->get_vpolicy() != VclPolicyType::NEVER)
+ {
+ // if there is a scrollbar shown, draw "..." to indicate the comment isn't
+ // completely shown
+ int x, y, width, height;
+ mxMenuButton->get_extents_relative_to(*m_xContainer, x, y, width, height);
+ Point aPos(rPt + PixelToLogic(Point(x, y)));
+ pDev->DrawText(aPos, "...");
+ }
+
+ pDev->Pop();
+}
+
+void SwAnnotationWin::SetPosSizePixelRect(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
+ const SwRect& aAnchorRect, const tools::Long aPageBorder)
+{
+ mPosSize = tools::Rectangle(Point(nX,nY),Size(nWidth,nHeight));
+ if (!mAnchorRect.IsEmpty() && mAnchorRect != aAnchorRect)
+ mbAnchorRectChanged = true;
+ mAnchorRect = aAnchorRect;
+ mPageBorder = aPageBorder;
+}
+
+void SwAnnotationWin::SetSize( const Size& rNewSize )
+{
+ mPosSize.SetSize(rNewSize);
+}
+
+void SwAnnotationWin::SetVirtualPosSize( const Point& aPoint, const Size& aSize)
+{
+ mPosSize = tools::Rectangle(aPoint,aSize);
+}
+
+void SwAnnotationWin::TranslateTopPosition(const tools::Long aAmount)
+{
+ mPosSize.Move(0,aAmount);
+}
+
+void SwAnnotationWin::ShowAnchorOnly(const Point &aPoint)
+{
+ HideNote();
+ SetPosAndSize();
+ if (mpAnchor)
+ {
+ mpAnchor->SetSixthPosition(basegfx::B2DPoint(aPoint.X(),aPoint.Y()));
+ mpAnchor->SetSeventhPosition(basegfx::B2DPoint(aPoint.X(),aPoint.Y()));
+ mpAnchor->SetAnchorState(AnchorState::All);
+ mpAnchor->setVisible(true);
+ }
+ if (mpShadow)
+ mpShadow->setVisible(false);
+}
+
+SfxItemSet SwAnnotationWin::DefaultItem()
+{
+ SfxItemSet aItem( mrView.GetDocShell()->GetPool() );
+ aItem.Put(SvxFontHeightItem(200,100,EE_CHAR_FONTHEIGHT));
+ return aItem;
+}
+
+void SwAnnotationWin::InitControls()
+{
+ // window controls for author and date
+ mxMetadataAuthor = m_xBuilder->weld_label("author");
+ mxMetadataAuthor->set_accessible_name( SwResId( STR_ACCESS_ANNOTATION_AUTHOR_NAME ) );
+ mxMetadataAuthor->set_direction(AllSettings::GetLayoutRTL());
+
+ maLabelFont = Application::GetSettings().GetStyleSettings().GetLabelFont();
+ maLabelFont.SetFontHeight(8);
+
+ // we should leave this setting alone, but for this we need a better layout algo
+ // with variable meta size height
+ mxMetadataAuthor->set_font(maLabelFont);
+
+ mxMetadataDate = m_xBuilder->weld_label("date");
+ mxMetadataDate->set_accessible_name( SwResId( STR_ACCESS_ANNOTATION_DATE_NAME ) );
+ mxMetadataDate->set_direction(AllSettings::GetLayoutRTL());
+ mxMetadataDate->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl));
+
+ // we should leave this setting alone, but for this we need a better layout algo
+ // with variable meta size height
+ mxMetadataDate->set_font(maLabelFont);
+
+ mxMetadataResolved = m_xBuilder->weld_label("resolved");
+ mxMetadataResolved->set_accessible_name( SwResId( STR_ACCESS_ANNOTATION_RESOLVED_NAME ) );
+ mxMetadataResolved->set_direction(AllSettings::GetLayoutRTL());
+ mxMetadataResolved->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl));
+
+ // we should leave this setting alone, but for this we need a better layout algo
+ // with variable meta size height
+ mxMetadataResolved->set_font(maLabelFont);
+ mxMetadataResolved->set_label(SwResId(STR_ACCESS_ANNOTATION_RESOLVED_NAME));
+
+ SwDocShell* aShell = mrView.GetDocShell();
+ mpOutliner.reset(new Outliner(&aShell->GetPool(),OutlinerMode::TextObject));
+ aShell->GetDoc()->SetCalcFieldValueHdl( mpOutliner.get() );
+ mpOutliner->SetUpdateLayout( true );
+
+ mpOutlinerView.reset(new OutlinerView(mpOutliner.get(), nullptr));
+ mpOutliner->InsertView(mpOutlinerView.get());
+
+ //create Scrollbars
+ mxVScrollbar = m_xBuilder->weld_scrolled_window("scrolledwindow", true);
+
+ mxMenuButton = m_xBuilder->weld_menu_button("menubutton");
+ mxMenuButton->set_size_request(METABUTTON_WIDTH, METABUTTON_HEIGHT);
+
+ // actual window which holds the user text
+ mxSidebarTextControl.reset(new SidebarTextControl(*this, mrView, mrMgr));
+ mxSidebarTextControlWin.reset(new weld::CustomWeld(*m_xBuilder, "editview", *mxSidebarTextControl));
+ mxSidebarTextControl->SetPointer(PointerStyle::Text);
+
+ Rescale();
+
+ mpOutlinerView->SetBackgroundColor(COL_TRANSPARENT);
+ mpOutlinerView->SetOutputArea( PixelToLogic( tools::Rectangle(0,0,1,1) ) );
+
+ mpOutlinerView->SetAttribs(DefaultItem());
+
+ mxVScrollbar->set_direction(false);
+ mxVScrollbar->connect_vadjustment_changed(LINK(this, SwAnnotationWin, ScrollHdl));
+ mxVScrollbar->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl));
+
+ const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions();
+ EEControlBits nCntrl = mpOutliner->GetControlWord();
+ // TODO: crash when AUTOCOMPLETE enabled
+ nCntrl |= EEControlBits::MARKFIELDS | EEControlBits::PASTESPECIAL | EEControlBits::AUTOCORRECT | EEControlBits::USECHARATTRIBS; // | EEControlBits::AUTOCOMPLETE;
+ if (SwViewOption::IsFieldShadings())
+ nCntrl |= EEControlBits::MARKFIELDS;
+ else
+ nCntrl &= ~EEControlBits::MARKFIELDS;
+ if (pVOpt->IsOnlineSpell())
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ mpOutliner->SetControlWord(nCntrl);
+
+ std::size_t aIndex = SW_MOD()->InsertRedlineAuthor(GetAuthor());
+ SetColor( SwPostItMgr::GetColorDark(aIndex),
+ SwPostItMgr::GetColorLight(aIndex),
+ SwPostItMgr::GetColorAnchor(aIndex));
+
+ CheckMetaText();
+
+ // expand %1 "Author"
+ OUString aText = mxMenuButton->get_item_label("deleteby");
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, GetAuthor());
+ aText = aRewriter.Apply(aText);
+ mxMenuButton->set_item_label("deleteby", aText);
+
+ mxMenuButton->set_accessible_name(SwResId(STR_ACCESS_ANNOTATION_BUTTON_NAME));
+ mxMenuButton->set_accessible_description(SwResId(STR_ACCESS_ANNOTATION_BUTTON_DESC));
+ mxMenuButton->set_tooltip_text(SwResId(STR_ACCESS_ANNOTATION_BUTTON_DESC));
+
+ mxMenuButton->connect_toggled(LINK(this, SwAnnotationWin, ToggleHdl));
+ mxMenuButton->connect_selected(LINK(this, SwAnnotationWin, SelectHdl));
+ mxMenuButton->connect_key_press(LINK(this, SwAnnotationWin, KeyInputHdl));
+ mxMenuButton->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl));
+
+ SetLanguage(GetLanguage());
+ GetOutlinerView()->StartSpeller(mxSidebarTextControl->GetDrawingArea());
+ SetPostItText();
+ mpOutliner->CompleteOnlineSpelling();
+
+ mxSidebarTextControl->Show();
+ mxMetadataAuthor->show();
+ mxMetadataDate->show();
+ mxMetadataResolved->set_visible(IsResolved());
+ mxVScrollbar->set_vpolicy(VclPolicyType::ALWAYS);
+}
+
+void SwAnnotationWin::CheckMetaText()
+{
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData();
+ OUString sMeta = GetAuthor();
+ if (sMeta.isEmpty())
+ {
+ sMeta = SwResId(STR_NOAUTHOR);
+ }
+ else if (sMeta.getLength() > 23)
+ {
+ sMeta = OUString::Concat(sMeta.subView(0, 20)) + "...";
+ }
+ if ( mxMetadataAuthor->get_label() != sMeta )
+ {
+ mxMetadataAuthor->set_label(sMeta);
+ }
+
+ Date aDate = GetDate();
+ if (aDate.IsValidAndGregorian() )
+ {
+ sMeta = rLocalData.getDate(aDate);
+ }
+ else
+ {
+ sMeta = SwResId(STR_NODATE);
+ }
+ if (GetTime().GetTime()!=0)
+ {
+ sMeta += " " + rLocalData.getTime( GetTime(),false );
+ }
+ if ( mxMetadataDate->get_label() != sMeta )
+ {
+ mxMetadataDate->set_label(sMeta);
+ }
+
+ std::size_t aIndex = SW_MOD()->InsertRedlineAuthor(GetAuthor());
+ SetColor( SwPostItMgr::GetColorDark(aIndex),
+ SwPostItMgr::GetColorLight(aIndex),
+ SwPostItMgr::GetColorAnchor(aIndex));
+}
+
+static Color ColorFromAlphaColor(const sal_uInt8 aTransparency, const Color& aFront, const Color& aBack)
+{
+ return Color(sal_uInt8(aFront.GetRed() * aTransparency / 255.0 + aBack.GetRed() * (1 - aTransparency / 255.0)),
+ sal_uInt8(aFront.GetGreen() * aTransparency / 255.0 + aBack.GetGreen() * (1 - aTransparency / 255.0)),
+ sal_uInt8(aFront.GetBlue() * aTransparency / 255.0 + aBack.GetBlue() * (1 - aTransparency / 255.0)));
+}
+
+void SwAnnotationWin::SetMenuButtonColors()
+{
+ if (!mxMenuButton)
+ return;
+
+ mxMenuButton->set_background(mColorDark);
+
+ const Fraction& rFraction = mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY();
+
+ ScopedVclPtrInstance<VirtualDevice> xVirDev;
+ Size aSize(tools::Long(METABUTTON_WIDTH * rFraction),
+ tools::Long(METABUTTON_HEIGHT * rFraction));
+ tools::Rectangle aRect(Point(0, 0), aSize);
+ xVirDev->SetOutputSizePixel(aSize);
+
+ Gradient aGradient(GradientStyle::Linear,
+ ColorFromAlphaColor(15, mColorAnchor, mColorDark),
+ ColorFromAlphaColor(80, mColorAnchor, mColorDark));
+ xVirDev->DrawGradient(aRect, aGradient);
+
+ //draw rect around button
+ xVirDev->SetFillColor();
+ xVirDev->SetLineColor(ColorFromAlphaColor(90, mColorAnchor, mColorDark));
+ xVirDev->DrawRect(aRect);
+
+ tools::Rectangle aSymbolRect(aRect);
+ // 25% distance to the left and right button border
+ const tools::Long nBorderDistanceLeftAndRight = ((aSymbolRect.GetWidth() * 250) + 500) / 1000;
+ aSymbolRect.AdjustLeft(nBorderDistanceLeftAndRight );
+ aSymbolRect.AdjustRight( -nBorderDistanceLeftAndRight );
+ // 40% distance to the top button border
+ const tools::Long nBorderDistanceTop = ((aSymbolRect.GetHeight() * 400) + 500) / 1000;
+ aSymbolRect.AdjustTop(nBorderDistanceTop );
+ // 15% distance to the bottom button border
+ const tools::Long nBorderDistanceBottom = ((aSymbolRect.GetHeight() * 150) + 500) / 1000;
+ aSymbolRect.AdjustBottom( -nBorderDistanceBottom );
+ DecorationView aDecoView(xVirDev.get());
+ aDecoView.DrawSymbol(aSymbolRect, SymbolType::SPIN_DOWN, GetTextColor(),
+ DrawSymbolFlags::NONE);
+ mxMenuButton->set_image(xVirDev);
+ mxMenuButton->set_size_request(aSize.Width() + 4, aSize.Height() + 4);
+}
+
+void SwAnnotationWin::Rescale()
+{
+ // On Android, this method leads to invoke ImpEditEngine::UpdateViews
+ // which hides the text cursor. Moreover it causes sudden document scroll
+ // when modifying a commented text. Not clear the root cause,
+ // anyway skipping this method fixes the problem, and there should be
+ // no side effect, since the client has disabled annotations rendering.
+ if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
+ return;
+
+ MapMode aMode = GetParent()->GetMapMode();
+ aMode.SetOrigin( Point() );
+ SetMapMode( aMode );
+ mxSidebarTextControl->SetMapMode( aMode );
+ const Fraction& rFraction = mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY();
+
+ vcl::Font aFont = maLabelFont;
+ sal_Int32 nHeight = tools::Long(aFont.GetFontHeight() * rFraction);
+ aFont.SetFontHeight( nHeight );
+
+ if (mxMetadataAuthor)
+ mxMetadataAuthor->set_font(aFont);
+ if (mxMetadataDate)
+ mxMetadataDate->set_font(aFont);
+ if (mxMetadataResolved)
+ mxMetadataResolved->set_font(aFont);
+ SetMenuButtonColors();
+ if (mxVScrollbar)
+ mxVScrollbar->set_scroll_thickness(GetPrefScrollbarWidth());
+}
+
+void SwAnnotationWin::SetPosAndSize()
+{
+ bool bChange = false;
+
+ if (GetSizePixel() != mPosSize.GetSize())
+ {
+ bChange = true;
+ SetSizePixel(mPosSize.GetSize());
+
+ DoResize();
+ }
+
+ if (GetPosPixel().X() != mPosSize.Left() || (std::abs(GetPosPixel().Y() - mPosSize.Top()) > 5) )
+ {
+ bChange = true;
+ SetPosPixel(mPosSize.TopLeft());
+
+ Point aLineStart;
+ Point aLineEnd ;
+ switch ( meSidebarPosition )
+ {
+ case sw::sidebarwindows::SidebarPosition::LEFT:
+ {
+ aLineStart = EditWin().PixelToLogic( Point(GetPosPixel().X()+GetSizePixel().Width(),GetPosPixel().Y()-1) );
+ aLineEnd = EditWin().PixelToLogic( Point(GetPosPixel().X(),GetPosPixel().Y()-1) );
+ }
+ break;
+ case sw::sidebarwindows::SidebarPosition::RIGHT:
+ {
+ aLineStart = EditWin().PixelToLogic( Point(GetPosPixel().X(),GetPosPixel().Y()-1) );
+ aLineEnd = EditWin().PixelToLogic( Point(GetPosPixel().X()+GetSizePixel().Width(),GetPosPixel().Y()-1) );
+ }
+ break;
+ default:
+ OSL_FAIL( "<SwAnnotationWin::SetPosAndSize()> - unexpected position of sidebar" );
+ break;
+ }
+
+ // LOK has map mode disabled, and we still want to perform pixel ->
+ // twips conversion for the size of the line above the note.
+ if (comphelper::LibreOfficeKit::isActive() && !EditWin().IsMapModeEnabled())
+ {
+ EditWin().EnableMapMode();
+ Size aSize(aLineEnd.getX() - aLineStart.getX(), aLineEnd.getY() - aLineStart.getY());
+ aSize = EditWin().PixelToLogic(aSize);
+ aLineEnd = aLineStart;
+ aLineEnd.Move(aSize.getWidth(), aSize.getHeight());
+ EditWin().EnableMapMode(false);
+ }
+
+ if (mpAnchor)
+ {
+ mpAnchor->SetAllPosition( basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom() - 5* 15),
+ basegfx::B2DPoint( mAnchorRect.Left()-5*15 , mAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( mAnchorRect.Left()+5*15 , mAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( mAnchorRect.Left(), mAnchorRect.Bottom()+2*15),
+ basegfx::B2DPoint( mPageBorder ,mAnchorRect.Bottom()+2*15),
+ basegfx::B2DPoint( aLineStart.X(),aLineStart.Y()),
+ basegfx::B2DPoint( aLineEnd.X(),aLineEnd.Y()));
+ }
+ else
+ {
+ mpAnchor = AnchorOverlayObject::CreateAnchorOverlayObject( mrView,
+ mAnchorRect,
+ mPageBorder,
+ aLineStart,
+ aLineEnd,
+ mColorAnchor );
+ if ( mpAnchor )
+ {
+ mpAnchor->setVisible(true);
+ mpAnchor->SetAnchorState(AnchorState::Tri);
+ if (HasChildPathFocus())
+ {
+ mpAnchor->setLineSolid(true);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( mpAnchor &&
+ ( mpAnchor->getBasePosition() != basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom()-5*15) ) )
+ {
+ mpAnchor->SetTriPosition( basegfx::B2DPoint( mAnchorRect.Left() , mAnchorRect.Bottom() - 5* 15),
+ basegfx::B2DPoint( mAnchorRect.Left()-5*15 , mAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( mAnchorRect.Left()+5*15 , mAnchorRect.Bottom()+5*15),
+ basegfx::B2DPoint( mAnchorRect.Left(), mAnchorRect.Bottom()+2*15),
+ basegfx::B2DPoint( mPageBorder , mAnchorRect.Bottom()+2*15));
+ }
+ }
+
+ if (mpShadow && bChange)
+ {
+ Point aStart = EditWin().PixelToLogic(GetPosPixel()+Point(0,GetSizePixel().Height()));
+ Point aEnd = EditWin().PixelToLogic(GetPosPixel()+Point(GetSizePixel().Width()-1,GetSizePixel().Height()));
+ mpShadow->SetPosition(basegfx::B2DPoint(aStart.X(),aStart.Y()), basegfx::B2DPoint(aEnd.X(),aEnd.Y()));
+ }
+
+ if (mrMgr.ShowNotes())
+ {
+ if (IsFollow() && !HasChildPathFocus())
+ {
+ // #i111964#
+ if ( mpAnchor )
+ {
+ mpAnchor->SetAnchorState(AnchorState::End);
+ }
+ }
+ else
+ {
+ // #i111964#
+ if ( mpAnchor )
+ {
+ mpAnchor->SetAnchorState(AnchorState::All);
+ }
+ SwAnnotationWin* pWin = GetTopReplyNote();
+ // #i111964#
+ if ( pWin != this && pWin->Anchor() )
+ {
+ pWin->Anchor()->SetAnchorState(AnchorState::End);
+ }
+ }
+ }
+
+
+ // text range overlay
+ maAnnotationTextRanges.clear();
+ if ( mrSidebarItem.maLayoutInfo.mnStartNodeIdx != SwNodeOffset(0)
+ && mrSidebarItem.maLayoutInfo.mnStartContent != -1 )
+ {
+ const SwTextAnnotationField* pTextAnnotationField =
+ dynamic_cast< const SwTextAnnotationField* >( mrSidebarItem.GetFormatField().GetTextField() );
+ SwTextNode* pTextNode = pTextAnnotationField ? pTextAnnotationField->GetpTextNode() : nullptr;
+ SwContentNode* pContentNd = nullptr;
+ if (pTextNode)
+ {
+ SwNodes& rNds = pTextNode->GetDoc().GetNodes();
+ pContentNd = rNds[mrSidebarItem.maLayoutInfo.mnStartNodeIdx]->GetContentNode();
+ }
+ if (pContentNd)
+ {
+ SwPosition aStartPos( *pContentNd, mrSidebarItem.maLayoutInfo.mnStartContent );
+ SwShellCursor* pTmpCursor = nullptr;
+ const bool bTableCursorNeeded = pTextNode->FindTableBoxStartNode() != pContentNd->FindTableBoxStartNode();
+ if ( bTableCursorNeeded )
+ {
+ SwShellTableCursor* pTableCursor = new SwShellTableCursor( mrView.GetWrtShell(), aStartPos );
+ pTableCursor->SetMark();
+ pTableCursor->GetMark()->nNode = *pTextNode;
+ pTableCursor->GetMark()->nContent.Assign( pTextNode, pTextAnnotationField->GetStart()+1 );
+ pTableCursor->NewTableSelection();
+ pTmpCursor = pTableCursor;
+ }
+ else
+ {
+ SwShellCursor* pCursor = new SwShellCursor( mrView.GetWrtShell(), aStartPos );
+ pCursor->SetMark();
+ pCursor->GetMark()->nNode = *pTextNode;
+ pCursor->GetMark()->nContent.Assign( pTextNode, pTextAnnotationField->GetStart()+1 );
+ pTmpCursor = pCursor;
+ }
+ std::unique_ptr<SwShellCursor> pTmpCursorForAnnotationTextRange( pTmpCursor );
+
+ // For annotation text range rectangles to be calculated correctly,
+ // we need the map mode disabled
+ bool bDisableMapMode = comphelper::LibreOfficeKit::isActive() && EditWin().IsMapModeEnabled();
+ if (bDisableMapMode)
+ EditWin().EnableMapMode(false);
+
+ if (mrSidebarItem.maLayoutInfo.mPositionFromCommentAnchor)
+ pTmpCursorForAnnotationTextRange->FillRects();
+
+ if (bDisableMapMode)
+ EditWin().EnableMapMode();
+
+ SwRects* pRects(pTmpCursorForAnnotationTextRange.get());
+ for(const SwRect & rNextRect : *pRects)
+ {
+ const tools::Rectangle aPntRect(rNextRect.SVRect());
+ maAnnotationTextRanges.emplace_back(
+ aPntRect.Left(), aPntRect.Top(),
+ aPntRect.Right() + 1, aPntRect.Bottom() + 1);
+ }
+ }
+ }
+
+ if (mrMgr.ShowNotes() && !maAnnotationTextRanges.empty())
+ {
+ if ( mpTextRangeOverlay != nullptr )
+ {
+ mpTextRangeOverlay->setRanges( std::vector(maAnnotationTextRanges) );
+ if ( mpAnchor != nullptr && mpAnchor->getLineSolid() )
+ {
+ mpTextRangeOverlay->ShowSolidBorder();
+ }
+ else
+ {
+ mpTextRangeOverlay->HideSolidBorder();
+ }
+ }
+ else if (!IsFollow())
+ {
+ // This window is not a reply, then draw its range overlay.
+ mpTextRangeOverlay =
+ sw::overlay::OverlayRanges::CreateOverlayRange(
+ mrView,
+ mColorAnchor,
+ std::vector(maAnnotationTextRanges),
+ mpAnchor && mpAnchor->getLineSolid() );
+ }
+ }
+ else
+ {
+ mpTextRangeOverlay.reset();
+ }
+}
+
+void SwAnnotationWin::DoResize()
+{
+ tools::Long aHeight = GetSizePixel().Height();
+ tools::ULong aWidth = GetSizePixel().Width();
+
+ aHeight -= GetMetaHeight();
+
+ mpOutliner->SetPaperSize( PixelToLogic( Size(aWidth, aHeight) ) ) ;
+ tools::Long aTextHeight = LogicToPixel( mpOutliner->CalcTextSize()).Height();
+
+ mxMetadataAuthor->show();
+ if(IsResolved()) { mxMetadataResolved->show(); }
+ mxMetadataDate->show();
+
+ if (aTextHeight > aHeight)
+ {
+ const int nThickness = mxVScrollbar->get_scroll_thickness();
+ if (nThickness)
+ {
+ // we need vertical scrollbars and have to reduce the width
+ aWidth -= nThickness;
+ mpOutliner->SetPaperSize(PixelToLogic(Size(aWidth, aHeight)));
+ }
+ mxVScrollbar->set_vpolicy(VclPolicyType::ALWAYS);
+ }
+ else
+ {
+ mxVScrollbar->set_vpolicy(VclPolicyType::NEVER);
+ }
+
+ tools::Rectangle aOutputArea = PixelToLogic(tools::Rectangle(0, 0, aWidth, aHeight));
+ if (mxVScrollbar->get_vpolicy() == VclPolicyType::NEVER)
+ {
+ // if we do not have a scrollbar anymore, we want to see the complete text
+ mpOutlinerView->SetVisArea(aOutputArea);
+ }
+ mpOutlinerView->SetOutputArea(aOutputArea);
+ mpOutlinerView->ShowCursor(true, true);
+
+ // Don't leave an empty area at the bottom if we can move the text down.
+ tools::Long nMaxVisAreaTop = mpOutliner->GetTextHeight() - aOutputArea.GetHeight();
+ if (mpOutlinerView->GetVisArea().Top() > nMaxVisAreaTop)
+ {
+ GetOutlinerView()->Scroll(0, mpOutlinerView->GetVisArea().Top() - nMaxVisAreaTop);
+ }
+
+ int nUpper = mpOutliner->GetTextHeight();
+ int nCurrentDocPos = mpOutlinerView->GetVisArea().Top();
+ int nStepIncrement = mpOutliner->GetTextHeight() / 10;
+ int nPageIncrement = PixelToLogic(Size(0,aHeight)).Height() * 8 / 10;
+ int nPageSize = PixelToLogic(Size(0,aHeight)).Height();
+
+ /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
+ effectively...
+
+ lower = gtk_adjustment_get_lower
+ upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
+
+ and requires that upper > lower or the deceleration animation never ends
+ */
+ nPageSize = std::min(nPageSize, nUpper);
+
+ mxVScrollbar->vadjustment_configure(nCurrentDocPos, 0, nUpper,
+ nStepIncrement, nPageIncrement, nPageSize);
+}
+
+void SwAnnotationWin::SetSizePixel( const Size& rNewSize )
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ InterimItemWindow::SetSizePixel(rNewSize);
+
+ if (mpShadow)
+ {
+ Point aStart = EditWin().PixelToLogic(GetPosPixel()+Point(0,GetSizePixel().Height()));
+ Point aEnd = EditWin().PixelToLogic(GetPosPixel()+Point(GetSizePixel().Width()-1,GetSizePixel().Height()));
+ mpShadow->SetPosition(basegfx::B2DPoint(aStart.X(),aStart.Y()), basegfx::B2DPoint(aEnd.X(),aEnd.Y()));
+ }
+}
+
+void SwAnnotationWin::SetScrollbar()
+{
+ mxVScrollbar->vadjustment_set_value(mpOutlinerView->GetVisArea().Top());
+}
+
+void SwAnnotationWin::ResizeIfNecessary(tools::Long aOldHeight, tools::Long aNewHeight)
+{
+ if (aOldHeight != aNewHeight)
+ {
+ //check for lower border or next note
+ tools::Long aBorder = mrMgr.GetNextBorder();
+ if (aBorder != -1)
+ {
+ if (aNewHeight > GetMinimumSizeWithoutMeta())
+ {
+ tools::Long aNewLowerValue = GetPosPixel().Y() + aNewHeight + GetMetaHeight();
+ if (aNewLowerValue < aBorder)
+ SetSizePixel(Size(GetSizePixel().Width(),aNewHeight+GetMetaHeight()));
+ else
+ SetSizePixel(Size(GetSizePixel().Width(),aBorder - GetPosPixel().Y()));
+ DoResize();
+ Invalidate();
+ }
+ else
+ {
+ if (GetSizePixel().Height() != GetMinimumSizeWithoutMeta() + GetMetaHeight())
+ SetSizePixel(Size(GetSizePixel().Width(),GetMinimumSizeWithoutMeta() + GetMetaHeight()));
+ DoResize();
+ Invalidate();
+ }
+ }
+ else
+ {
+ DoResize();
+ Invalidate();
+ }
+ }
+ else
+ {
+ SetScrollbar();
+ }
+}
+
+void SwAnnotationWin::SetColor(Color aColorDark,Color aColorLight, Color aColorAnchor)
+{
+ mColorDark = aColorDark;
+ mColorLight = aColorLight;
+ mColorAnchor = aColorAnchor;
+
+ if ( Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ return;
+
+ m_xContainer->set_background(mColorDark);
+ SetMenuButtonColors();
+
+ mxMetadataAuthor->set_font_color(aColorAnchor);
+
+ mxMetadataDate->set_font_color(aColorAnchor);
+
+ mxMetadataResolved->set_font_color(aColorAnchor);
+
+ mxVScrollbar->customize_scrollbars(mColorLight,
+ mColorAnchor,
+ mColorDark);
+}
+
+void SwAnnotationWin::SetSidebarPosition(sw::sidebarwindows::SidebarPosition eSidebarPosition)
+{
+ meSidebarPosition = eSidebarPosition;
+}
+
+void SwAnnotationWin::SetReadonly(bool bSet)
+{
+ mbReadonly = bSet;
+ GetOutlinerView()->SetReadOnly(bSet);
+}
+
+void SwAnnotationWin::SetLanguage(const SvxLanguageItem& rNewItem)
+{
+ IDocumentUndoRedo& rUndoRedo(
+ mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo());
+ const bool bDocUndoEnabled = rUndoRedo.DoesUndo();
+ const bool bOutlinerUndoEnabled = mpOutliner->IsUndoEnabled();
+ const bool bOutlinerModified = mpOutliner->IsModified();
+ const bool bDisableAndRestoreUndoMode = !bDocUndoEnabled && bOutlinerUndoEnabled;
+
+ if (bDisableAndRestoreUndoMode)
+ {
+ // doc undo is disabled, but outliner was enabled, turn outliner undo off
+ // for the duration of this function
+ mpOutliner->EnableUndo(false);
+ }
+
+ Link<LinkParamNone*,void> aLink = mpOutliner->GetModifyHdl();
+ mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() );
+ ESelection aOld = GetOutlinerView()->GetSelection();
+
+ ESelection aNewSelection( 0, 0, mpOutliner->GetParagraphCount()-1, EE_TEXTPOS_ALL );
+ GetOutlinerView()->SetSelection( aNewSelection );
+ SfxItemSet aEditAttr(GetOutlinerView()->GetAttribs());
+ aEditAttr.Put(rNewItem);
+ GetOutlinerView()->SetAttribs( aEditAttr );
+
+ if (!mpOutliner->IsUndoEnabled() && !bOutlinerModified)
+ {
+ // if undo was disabled (e.g. this is a redo action) and we were
+ // originally 'unmodified' keep it that way
+ mpOutliner->ClearModifyFlag();
+ }
+
+ GetOutlinerView()->SetSelection(aOld);
+ mpOutliner->SetModifyHdl( aLink );
+
+ const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions();
+ EEControlBits nCntrl = mpOutliner->GetControlWord();
+ // turn off
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ mpOutliner->SetControlWord(nCntrl);
+
+ //turn back on
+ if (pVOpt->IsOnlineSpell())
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ mpOutliner->SetControlWord(nCntrl);
+
+ mpOutliner->CompleteOnlineSpelling();
+
+ // restore original mode
+ if (bDisableAndRestoreUndoMode)
+ mpOutliner->EnableUndo(true);
+
+ Invalidate();
+}
+
+void SwAnnotationWin::GetFocus()
+{
+ if (mxSidebarTextControl)
+ mxSidebarTextControl->GrabFocus();
+}
+
+void SwAnnotationWin::LoseFocus()
+{
+}
+
+void SwAnnotationWin::ShowNote()
+{
+ SetPosAndSize();
+ if (!IsVisible())
+ Window::Show();
+ if (mpShadow && !mpShadow->isVisible())
+ mpShadow->setVisible(true);
+ if (mpAnchor && !mpAnchor->isVisible())
+ mpAnchor->setVisible(true);
+ if (mpTextRangeOverlay && !mpTextRangeOverlay->isVisible())
+ mpTextRangeOverlay->setVisible(true);
+
+ collectUIInformation("SHOW",get_id());
+}
+
+void SwAnnotationWin::HideNote()
+{
+ if (IsVisible())
+ Window::Hide();
+ if (mpAnchor)
+ {
+ if (mrMgr.IsShowAnchor())
+ mpAnchor->SetAnchorState(AnchorState::Tri);
+ else
+ mpAnchor->setVisible(false);
+ }
+ if (mpShadow && mpShadow->isVisible())
+ mpShadow->setVisible(false);
+ if (mpTextRangeOverlay && mpTextRangeOverlay->isVisible())
+ mpTextRangeOverlay->setVisible(false);
+ collectUIInformation("HIDE",get_id());
+}
+
+void SwAnnotationWin::ActivatePostIt()
+{
+ mrMgr.AssureStdModeAtShell();
+
+ mpOutliner->ClearModifyFlag();
+ mpOutliner->GetUndoManager().Clear();
+
+ CheckMetaText();
+ SetViewState(ViewState::EDIT);
+
+ // prevent autoscroll to the old cursor location
+ // when cursor out of visible area
+ GetOutlinerView()->ShowCursor(false);
+
+ mpOutlinerView->GetEditView().SetInsertMode(mrView.GetWrtShellPtr()->IsInsMode());
+
+ if ( !Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ GetOutlinerView()->SetBackgroundColor(mColorDark);
+
+ //tdf#119130 only have the active postit as a dialog control in which pressing
+ //ctrl+tab cycles between text and button so we don't waste time searching
+ //thousands of SwAnnotationWins
+ SetStyle(GetStyle() | WB_DIALOGCONTROL);
+}
+
+void SwAnnotationWin::DeactivatePostIt()
+{
+ //tdf#119130 only have the active postit as a dialog control in which pressing
+ //ctrl+tab cycles between text and button so we don't waste time searching
+ //thousands of SwAnnotationWins
+ SetStyle(GetStyle() & ~WB_DIALOGCONTROL);
+
+ // remove selection, #i87073#
+ if (GetOutlinerView()->GetEditView().HasSelection())
+ {
+ ESelection aSelection = GetOutlinerView()->GetEditView().GetSelection();
+ aSelection.nEndPara = aSelection.nStartPara;
+ aSelection.nEndPos = aSelection.nStartPos;
+ GetOutlinerView()->GetEditView().SetSelection(aSelection);
+ }
+
+ mpOutliner->CompleteOnlineSpelling();
+
+ SetViewState(ViewState::NORMAL);
+ // Make sure this view doesn't emit LOK callbacks during the update, as the
+ // sidebar window's SidebarTextControl doesn't have a valid twip offset
+ // (map mode origin) during that operation.
+ bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting();
+ comphelper::LibreOfficeKit::setTiledPainting(true);
+ // write the visible text back into the SwField
+ UpdateData();
+ comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
+
+ if ( !Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ GetOutlinerView()->SetBackgroundColor(COL_TRANSPARENT);
+
+ if (!mnDeleteEventId && !IsProtected() && mpOutliner->GetEditEngine().GetText().isEmpty())
+ {
+ mnDeleteEventId = Application::PostUserEvent( LINK( this, SwAnnotationWin, DeleteHdl), nullptr, true );
+ }
+}
+
+void SwAnnotationWin::ToggleInsMode()
+{
+ if (!mrView.GetWrtShell().IsRedlineOn())
+ {
+ //change outliner
+ mpOutlinerView->GetEditView().SetInsertMode(!mpOutlinerView->GetEditView().IsInsertMode());
+ //change document
+ mrView.GetWrtShell().ToggleInsMode();
+ //update statusbar
+ SfxBindings &rBnd = mrView.GetViewFrame()->GetBindings();
+ rBnd.Invalidate(SID_ATTR_INSERT);
+ rBnd.Update(SID_ATTR_INSERT);
+ }
+}
+
+void SwAnnotationWin::ExecuteCommand(sal_uInt16 nSlot)
+{
+ mrMgr.AssureStdModeAtShell();
+
+ switch (nSlot)
+ {
+ case FN_POSTIT:
+ case FN_REPLY:
+ {
+ // if this note is empty, it will be deleted once losing the focus, so no reply, but only a new note
+ // will be created
+ if (!mpOutliner->GetEditEngine().GetText().isEmpty())
+ {
+ OutlinerParaObject aPara(GetOutlinerView()->GetEditView().CreateTextObject());
+ mrMgr.RegisterAnswer(&aPara);
+ }
+ if (mrMgr.HasActiveSidebarWin())
+ mrMgr.SetActiveSidebarWin(nullptr);
+ SwitchToFieldPos();
+ mrView.GetViewFrame()->GetDispatcher()->Execute(FN_POSTIT);
+ break;
+ }
+ case FN_DELETE_COMMENT:
+ //Delete(); // do not kill the parent of our open popup menu
+ mnDeleteEventId = Application::PostUserEvent( LINK( this, SwAnnotationWin, DeleteHdl), nullptr, true );
+ break;
+ case FN_DELETE_COMMENT_THREAD:
+ DeleteThread();
+ break;
+ case FN_RESOLVE_NOTE:
+ ToggleResolved();
+ DoResize();
+ Invalidate();
+ mrMgr.LayoutPostIts();
+ break;
+ case FN_RESOLVE_NOTE_THREAD:
+ GetTopReplyNote()->SetResolved(!IsThreadResolved());
+ mrMgr.UpdateResolvedStatus(GetTopReplyNote());
+ DoResize();
+ Invalidate();
+ mrMgr.LayoutPostIts();
+ break;
+ case FN_FORMAT_ALL_NOTES:
+ case FN_DELETE_ALL_NOTES:
+ case FN_HIDE_ALL_NOTES:
+ // not possible as slot as this would require that "this" is the active postit
+ mrView.GetViewFrame()->GetBindings().Execute( nSlot, nullptr, SfxCallMode::ASYNCHRON );
+ break;
+ case FN_DELETE_NOTE_AUTHOR:
+ case FN_HIDE_NOTE_AUTHOR:
+ {
+ // not possible as slot as this would require that "this" is the active postit
+ SfxStringItem aItem( nSlot, GetAuthor() );
+ const SfxPoolItem* aItems[2];
+ aItems[0] = &aItem;
+ aItems[1] = nullptr;
+ mrView.GetViewFrame()->GetBindings().Execute( nSlot, aItems, SfxCallMode::ASYNCHRON );
+ }
+ break;
+ default:
+ mrView.GetViewFrame()->GetBindings().Execute( nSlot );
+ break;
+ }
+}
+
+SwEditWin& SwAnnotationWin::EditWin()
+{
+ return mrView.GetEditWin();
+}
+
+tools::Long SwAnnotationWin::GetPostItTextHeight()
+{
+ return mpOutliner ? LogicToPixel(mpOutliner->CalcTextSize()).Height() : 0;
+}
+
+void SwAnnotationWin::SwitchToPostIt(sal_uInt16 aDirection)
+{
+ SwAnnotationWin* pPostIt = mrMgr.GetNextPostIt(aDirection, this);
+ if (pPostIt)
+ pPostIt->GrabFocus();
+}
+
+IMPL_LINK(SwAnnotationWin, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (rMEvt.IsEnterWindow())
+ {
+ mbMouseOver = true;
+ if ( !HasFocus() )
+ {
+ SetViewState(ViewState::VIEW);
+ Invalidate();
+ }
+ }
+ else if (rMEvt.IsLeaveWindow())
+ {
+ mbMouseOver = false;
+ if ( !HasFocus() )
+ {
+ SetViewState(ViewState::NORMAL);
+ Invalidate();
+ }
+ }
+ return false;
+}
+
+bool SwAnnotationWin::SetActiveSidebarWin()
+{
+ if (mrMgr.GetActiveSidebarWin() == this)
+ return false;
+ mrView.GetWrtShell().LockView( true );
+ mrMgr.SetActiveSidebarWin(this);
+ mrView.GetWrtShell().LockView( true );
+
+ return true;
+}
+
+void SwAnnotationWin::UnsetActiveSidebarWin()
+{
+ if (mrMgr.GetActiveSidebarWin() != this)
+ return;
+ mrView.GetWrtShell().LockView( true );
+ mrMgr.SetActiveSidebarWin(nullptr);
+ mrView.GetWrtShell().LockView( false );
+}
+
+void SwAnnotationWin::LockView(bool bLock)
+{
+ mrView.GetWrtShell().LockView( bLock );
+}
+
+IMPL_LINK(SwAnnotationWin, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void)
+{
+ tools::Long nDiff = GetOutlinerView()->GetEditView().GetVisArea().Top() - rScrolledWindow.vadjustment_get_value();
+ GetOutlinerView()->Scroll( 0, nDiff );
+}
+
+IMPL_LINK_NOARG(SwAnnotationWin, ModifyHdl, LinkParamNone*, void)
+{
+ mrView.GetDocShell()->SetModified();
+}
+
+IMPL_LINK_NOARG(SwAnnotationWin, DeleteHdl, void*, void)
+{
+ mnDeleteEventId = nullptr;
+ Delete();
+}
+
+void SwAnnotationWin::ResetAttributes()
+{
+ mpOutlinerView->RemoveAttribsKeepLanguages(true);
+ mpOutliner->RemoveFields();
+ mpOutlinerView->SetAttribs(DefaultItem());
+}
+
+int SwAnnotationWin::GetPrefScrollbarWidth() const
+{
+ const Fraction& f(mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY());
+ return tools::Long(Application::GetSettings().GetStyleSettings().GetScrollBarSize() * f);
+}
+
+sal_Int32 SwAnnotationWin::GetMetaHeight() const
+{
+ const int fields = GetNumFields();
+
+ sal_Int32 nRequiredHeight = 0;
+ weld::Label* aLabels[3] = { mxMetadataAuthor.get(), mxMetadataDate.get(), mxMetadataResolved.get() };
+ for (int i = 0; i < fields; ++i)
+ nRequiredHeight += aLabels[i]->get_preferred_size().Height();
+
+ return nRequiredHeight;
+}
+
+sal_Int32 SwAnnotationWin::GetNumFields() const
+{
+ return IsResolved() ? 3 : 2;
+}
+
+sal_Int32 SwAnnotationWin::GetMinimumSizeWithMeta() const
+{
+ return mrMgr.GetMinimumSizeWithMeta();
+}
+
+sal_Int32 SwAnnotationWin::GetMinimumSizeWithoutMeta() const
+{
+ const Fraction& f(mrView.GetWrtShellPtr()->GetOut()->GetMapMode().GetScaleY());
+ return tools::Long(POSTIT_MINIMUMSIZE_WITHOUT_META * f);
+}
+
+void SwAnnotationWin::SetSpellChecking()
+{
+ const SwViewOption* pVOpt = mrView.GetWrtShellPtr()->GetViewOptions();
+ EEControlBits nCntrl = mpOutliner->GetControlWord();
+ if (pVOpt->IsOnlineSpell())
+ nCntrl |= EEControlBits::ONLINESPELLING;
+ else
+ nCntrl &= ~EEControlBits::ONLINESPELLING;
+ mpOutliner->SetControlWord(nCntrl);
+
+ mpOutliner->CompleteOnlineSpelling();
+ Invalidate();
+}
+
+void SwAnnotationWin::SetViewState(ViewState bViewState)
+{
+ switch (bViewState)
+ {
+ case ViewState::EDIT:
+ {
+ if (mpAnchor)
+ {
+ mpAnchor->SetAnchorState(AnchorState::All);
+ SwAnnotationWin* pWin = GetTopReplyNote();
+ // #i111964#
+ if ( pWin != this && pWin->Anchor() )
+ {
+ pWin->Anchor()->SetAnchorState(AnchorState::End);
+ }
+ mpAnchor->setLineSolid(true);
+ if ( mpTextRangeOverlay != nullptr )
+ {
+ mpTextRangeOverlay->ShowSolidBorder();
+ }
+ }
+ if (mpShadow)
+ mpShadow->SetShadowState(SS_EDIT);
+ break;
+ }
+ case ViewState::VIEW:
+ {
+ if (mpAnchor)
+ {
+ mpAnchor->setLineSolid(true);
+ if ( mpTextRangeOverlay != nullptr )
+ {
+ mpTextRangeOverlay->ShowSolidBorder();
+ }
+ }
+ if (mpShadow)
+ mpShadow->SetShadowState(SS_VIEW);
+ break;
+ }
+ case ViewState::NORMAL:
+ {
+ if (mpAnchor)
+ {
+ if (IsFollow())
+ {
+ // if there is no visible parent note, we want to see the complete anchor ??
+ //if (IsAnyStackParentVisible())
+ mpAnchor->SetAnchorState(AnchorState::End);
+ SwAnnotationWin* pTopWinSelf = GetTopReplyNote();
+ SwAnnotationWin* pTopWinActive = mrMgr.HasActiveSidebarWin()
+ ? mrMgr.GetActiveSidebarWin()->GetTopReplyNote()
+ : nullptr;
+ // #i111964#
+ if ( ( pTopWinSelf != this ) &&
+ ( pTopWinSelf != pTopWinActive ) &&
+ pTopWinSelf->Anchor() )
+ {
+ if ( pTopWinSelf != mrMgr.GetActiveSidebarWin() )
+ {
+ pTopWinSelf->Anchor()->setLineSolid(false);
+ if ( pTopWinSelf->TextRange() != nullptr )
+ {
+ pTopWinSelf->TextRange()->HideSolidBorder();
+ }
+ }
+ pTopWinSelf->Anchor()->SetAnchorState(AnchorState::All);
+ }
+ }
+ mpAnchor->setLineSolid(false);
+ if ( mpTextRangeOverlay != nullptr )
+ {
+ mpTextRangeOverlay->HideSolidBorder();
+ }
+ }
+ if ( mpShadow )
+ {
+ mpShadow->SetShadowState(SS_NORMAL);
+ }
+ break;
+ }
+ }
+}
+
+SwAnnotationWin* SwAnnotationWin::GetTopReplyNote()
+{
+ SwAnnotationWin* pTopNote = this;
+ SwAnnotationWin* pSidebarWin = IsFollow() ? mrMgr.GetNextPostIt(KEY_PAGEUP, this) : nullptr;
+ while (pSidebarWin)
+ {
+ pTopNote = pSidebarWin;
+ pSidebarWin = pSidebarWin->IsFollow() ? mrMgr.GetNextPostIt(KEY_PAGEUP, pSidebarWin) : nullptr;
+ }
+ return pTopNote;
+}
+
+void SwAnnotationWin::SwitchToFieldPos()
+{
+ if ( mrMgr.GetActiveSidebarWin() == this )
+ mrMgr.SetActiveSidebarWin(nullptr);
+ GotoPos();
+ sal_uInt32 aCount = MoveCaret();
+ if (aCount)
+ mrView.GetDocShell()->GetWrtShell()->SwCursorShell::Right(aCount, 0);
+ GrabFocusToDocument();
+ collectUIInformation("LEAVE",get_id());
+}
+
+void SwAnnotationWin::SetChangeTracking( const SwPostItHelper::SwLayoutStatus aLayoutStatus,
+ const Color& aChangeColor )
+{
+ if ( (mLayoutStatus != aLayoutStatus) ||
+ (mChangeColor != aChangeColor) )
+ {
+ mLayoutStatus = aLayoutStatus;
+ mChangeColor = aChangeColor;
+ Invalidate();
+ }
+}
+
+bool SwAnnotationWin::HasScrollbar() const
+{
+ return static_cast<bool>(mxVScrollbar);
+}
+
+bool SwAnnotationWin::IsScrollbarVisible() const
+{
+ return HasScrollbar() && mxVScrollbar->get_vpolicy() == VclPolicyType::ALWAYS;
+}
+
+void SwAnnotationWin::ChangeSidebarItem( SwSidebarItem const & rSidebarItem )
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ const bool bAnchorChanged = mpAnchorFrame != rSidebarItem.maLayoutInfo.mpAnchorFrame;
+ if ( bAnchorChanged )
+ {
+ mrMgr.DisconnectSidebarWinFromFrame( *mpAnchorFrame, *this );
+ }
+#endif
+
+ mrSidebarItem = rSidebarItem;
+ mpAnchorFrame = mrSidebarItem.maLayoutInfo.mpAnchorFrame;
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (mxSidebarWinAccessible)
+ mxSidebarWinAccessible->ChangeSidebarItem( mrSidebarItem );
+
+ if ( bAnchorChanged )
+ {
+ mrMgr.ConnectSidebarWinToFrame( *(mrSidebarItem.maLayoutInfo.mpAnchorFrame),
+ mrSidebarItem.GetFormatField(),
+ *this );
+ }
+#endif
+}
+
+css::uno::Reference< css::accessibility::XAccessible > SwAnnotationWin::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ // This is rather dodgy code. Normally in CreateAccessible, if we want a custom
+ // object, we return a custom object, but we do no override the default toolkit
+ // window peer.
+ if (!mxSidebarWinAccessible)
+ mxSidebarWinAccessible = new SidebarWinAccessible( *this,
+ mrView.GetWrtShell(),
+ mrSidebarItem );
+#endif
+ return mxSidebarWinAccessible;
+}
+
+} // eof of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/DashedLine.cxx b/sw/source/uibase/docvw/DashedLine.cxx
new file mode 100644
index 000000000..5727cf280
--- /dev/null
+++ b/sw/source/uibase/docvw/DashedLine.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <DashedLine.hxx>
+
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <memory>
+
+SwDashedLine::SwDashedLine( vcl::Window* pParent, Color& ( *pColorFn )() )
+ : Control( pParent, WB_DIALOGCONTROL | WB_HORZ )
+ , m_pColorFn( pColorFn )
+{
+}
+
+SwDashedLine::~SwDashedLine( )
+{
+}
+
+void SwDashedLine::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(rRenderContext, aNewViewInfos));
+
+ // Compute the start and end points
+ const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), rRenderContext.PixelToLogic(GetSizePixel())));
+ double nHalfWidth = double(aRect.Top() + aRect.Bottom()) / 2.0;
+
+ basegfx::B2DPoint aStart(double(aRect.Left()), nHalfWidth);
+ basegfx::B2DPoint aEnd(double(aRect.Right()), nHalfWidth);
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(aStart);
+ aPolygon.append(aEnd);
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(1);
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+
+ std::vector<double> aStrokePattern;
+ basegfx::BColor aColor = m_pColorFn().getBColor();
+ if (rSettings.GetHighContrastMode())
+ {
+ // Only a solid line in high contrast mode
+ aColor = rSettings.GetDialogTextColor().getBColor();
+ }
+ else
+ {
+ // Get a color for the contrast
+ basegfx::BColor aHslLine = basegfx::utils::rgb2hsl(aColor);
+ double nLuminance = aHslLine.getZ();
+ nLuminance += (1.0 - nLuminance) * 0.75;
+ if (aHslLine.getZ() > 0.7)
+ nLuminance = aHslLine.getZ() * 0.7;
+ aHslLine.setZ(nLuminance);
+ const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb(aHslLine);
+
+ // Compute the plain line
+ aSeq[0] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon, aOtherColor);
+
+ // Dashed line in twips
+ aStrokePattern.push_back(3);
+ aStrokePattern.push_back(3);
+
+ aSeq.resize(2);
+ }
+
+ // Compute the dashed line primitive
+ aSeq[aSeq.size() - 1] =
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ drawinglayer::attribute::LineAttribute(m_pColorFn().getBColor()),
+ drawinglayer::attribute::StrokeAttribute(std::move(aStrokePattern)));
+
+ pProcessor->process(aSeq);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/FrameControlsManager.cxx b/sw/source/uibase/docvw/FrameControlsManager.cxx
new file mode 100644
index 000000000..03252faa1
--- /dev/null
+++ b/sw/source/uibase/docvw/FrameControlsManager.cxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <edtwin.hxx>
+#include <cntfrm.hxx>
+#include <FrameControlsManager.hxx>
+#include <HeaderFooterWin.hxx>
+#include <PageBreakWin.hxx>
+#include <UnfloatTableButton.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <viewopt.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <OutlineContentVisibilityWin.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+
+SwFrameControlsManager::SwFrameControlsManager( SwEditWin* pEditWin ) :
+ m_pEditWin( pEditWin )
+{
+}
+
+SwFrameControlsManager::~SwFrameControlsManager()
+{
+}
+
+void SwFrameControlsManager::dispose()
+{
+ m_aControls.clear();
+}
+
+SwFrameControlPtr SwFrameControlsManager::GetControl( FrameControlType eType, const SwFrame* pFrame )
+{
+ SwFrameControlPtrMap& rControls = m_aControls[eType];
+
+ SwFrameControlPtrMap::iterator aIt = rControls.find(pFrame);
+
+ if (aIt != rControls.end())
+ return aIt->second;
+
+ return SwFrameControlPtr();
+}
+
+void SwFrameControlsManager::RemoveControls( const SwFrame* pFrame )
+{
+ for ( auto& rEntry : m_aControls )
+ {
+ SwFrameControlPtrMap& rMap = rEntry.second;
+ rMap.erase(pFrame);
+ }
+}
+
+void SwFrameControlsManager::RemoveControlsByType( FrameControlType eType, const SwFrame* pFrame )
+{
+ SwFrameControlPtrMap& rMap = m_aControls[eType];
+ rMap.erase(pFrame);
+}
+
+void SwFrameControlsManager::HideControls( FrameControlType eType )
+{
+ for ( const auto& rCtrl : m_aControls[eType] )
+ rCtrl.second->ShowAll( false );
+}
+
+void SwFrameControlsManager::SetReadonlyControls( bool bReadonly )
+{
+ for ( auto& rEntry : m_aControls )
+ for ( auto& rCtrl : rEntry.second )
+ rCtrl.second->SetReadonly( bReadonly );
+}
+
+void SwFrameControlsManager::SetHeaderFooterControl( const SwPageFrame* pPageFrame, FrameControlType eType, Point aOffset )
+{
+ assert( eType == FrameControlType::Header || eType == FrameControlType::Footer );
+
+ // Check if we already have the control
+ SwFrameControlPtr pControl;
+ const bool bHeader = ( eType == FrameControlType::Header );
+
+ SwFrameControlPtrMap& rControls = m_aControls[eType];
+
+ SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pPageFrame);
+ if (lb != rControls.end() && !(rControls.key_comp()(pPageFrame, lb->first)))
+ pControl = lb->second;
+ else
+ {
+ SwFrameControlPtr pNewControl =
+ std::make_shared<SwFrameControl>( VclPtr<SwHeaderFooterDashedLine>::Create(
+ m_pEditWin, pPageFrame, bHeader ).get() );
+ const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions();
+ pNewControl->SetReadonly( pViewOpt->IsReadonly() );
+ rControls.insert(lb, make_pair(pPageFrame, pNewControl));
+ pControl.swap( pNewControl );
+ }
+
+ tools::Rectangle aPageRect = m_pEditWin->LogicToPixel( pPageFrame->getFrameArea().SVRect() );
+
+ SwHeaderFooterDashedLine* pWin = dynamic_cast<SwHeaderFooterDashedLine*>(pControl->GetWindow());
+ assert( pWin != nullptr) ;
+ assert( pWin->IsHeader() == bHeader );
+ pWin->SetOffset( aOffset, aPageRect.Left(), aPageRect.Right() );
+
+ if (!pWin->IsVisible())
+ pControl->ShowAll( true );
+}
+
+void SwFrameControlsManager::SetPageBreakControl( const SwPageFrame* pPageFrame )
+{
+ // Check if we already have the control
+ SwFrameControlPtr pControl;
+
+ SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::PageBreak];
+
+ SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pPageFrame);
+ if (lb != rControls.end() && !(rControls.key_comp()(pPageFrame, lb->first)))
+ pControl = lb->second;
+ else
+ {
+ SwFrameControlPtr pNewControl = std::make_shared<SwFrameControl>(
+ VclPtr<SwBreakDashedLine>::Create( m_pEditWin, pPageFrame ).get() );
+ const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions();
+ pNewControl->SetReadonly( pViewOpt->IsReadonly() );
+
+ rControls.insert(lb, make_pair(pPageFrame, pNewControl));
+
+ pControl.swap( pNewControl );
+ }
+
+ SwBreakDashedLine* pWin = static_cast<SwBreakDashedLine*>(pControl->GetWindow());
+ assert (pWin != nullptr);
+ pWin->UpdatePosition();
+ if (!pWin->IsVisible())
+ pControl->ShowAll( true );
+}
+
+void SwFrameControlsManager::SetUnfloatTableButton( const SwFlyFrame* pFlyFrame, bool bShow, Point aTopRightPixel )
+{
+ if(pFlyFrame == nullptr)
+ return;
+
+ // Check if we already have the control
+ SwFrameControlPtr pControl;
+
+ SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::FloatingTable];
+
+ SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pFlyFrame);
+ if (lb != rControls.end() && !(rControls.key_comp()(pFlyFrame, lb->first)))
+ pControl = lb->second;
+ else if (!bShow) // Do not create the control when it's not shown
+ return;
+ else
+ {
+ SwFrameControlPtr pNewControl = std::make_shared<SwFrameControl>(
+ VclPtr<UnfloatTableButton>::Create( m_pEditWin, pFlyFrame ).get() );
+ const SwViewOption* pViewOpt = m_pEditWin->GetView().GetWrtShell().GetViewOptions();
+ pNewControl->SetReadonly( pViewOpt->IsReadonly() );
+
+ rControls.insert(lb, make_pair(pFlyFrame, pNewControl));
+
+ pControl.swap( pNewControl );
+ }
+
+ UnfloatTableButton* pButton = dynamic_cast<UnfloatTableButton*>(pControl->GetWindow());
+ assert(pButton != nullptr);
+ pButton->SetOffset(aTopRightPixel);
+ pControl->ShowAll( bShow );
+}
+
+SwFrameMenuButtonBase::SwFrameMenuButtonBase(SwEditWin* pEditWin, const SwFrame* pFrame,
+ const OUString& rUIXMLDescription, const OString& rID)
+ : InterimItemWindow(pEditWin, rUIXMLDescription, rID)
+ , m_pEditWin(pEditWin)
+ , m_pFrame(pFrame)
+{
+}
+
+void SwFrameControlsManager::SetOutlineContentVisibilityButton(const SwContentFrame* pContentFrame)
+{
+ // Check if we already have the control
+ SwFrameControlPtr pControl;
+
+ SwFrameControlPtrMap& rControls = m_aControls[FrameControlType::Outline];
+
+ SwFrameControlPtrMap::iterator lb = rControls.lower_bound(pContentFrame);
+ if (lb != rControls.end() && !(rControls.key_comp()(pContentFrame, lb->first)))
+ {
+ pControl = lb->second;
+ }
+ else
+ {
+ SwFrameControlPtr pNewControl =
+ std::make_shared<SwFrameControl>(VclPtr<SwOutlineContentVisibilityWin>::Create(
+ m_pEditWin, pContentFrame).get());
+ rControls.insert(lb, make_pair(pContentFrame, pNewControl));
+ pControl.swap(pNewControl);
+ }
+
+ SwOutlineContentVisibilityWin* pWin = dynamic_cast<SwOutlineContentVisibilityWin *>(pControl->GetWindow());
+ assert(pWin != nullptr);
+ pWin->Set();
+
+ if (pWin->GetSymbol() == ButtonSymbol::SHOW)
+ pWin->Show(); // show the SHOW button immediately
+ else if (!pWin->IsVisible() && pWin->GetSymbol() == ButtonSymbol::HIDE)
+ pWin->ShowAll(true);
+}
+
+const SwPageFrame* SwFrameMenuButtonBase::GetPageFrame(const SwFrame* pFrame)
+{
+ if (pFrame->IsPageFrame())
+ return static_cast<const SwPageFrame*>(pFrame);
+
+ if (pFrame->IsFlyFrame())
+ return static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame()->FindPageFrame();
+
+ return pFrame->FindPageFrame();
+}
+
+const SwPageFrame* SwFrameMenuButtonBase::GetPageFrame() const
+{
+ return SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+}
+
+void SwFrameMenuButtonBase::dispose()
+{
+ m_pEditWin.clear();
+ m_pFrame = nullptr;
+ m_xVirDev.disposeAndClear();
+ InterimItemWindow::dispose();
+}
+
+void SwFrameMenuButtonBase::SetVirDevFont(OutputDevice& rVirDev)
+{
+ // Get the font and configure it
+ vcl::Font aFont = Application::GetSettings().GetStyleSettings().GetToolFont();
+ weld::SetPointFont(rVirDev, aFont);
+}
+
+void SwFrameMenuButtonBase::SetVirDevFont()
+{
+ SetVirDevFont(*m_xVirDev);
+}
+
+SwFrameControl::SwFrameControl( const VclPtr<vcl::Window> &pWindow )
+{
+ assert(static_cast<bool>(pWindow));
+ mxWindow.reset( pWindow );
+ mpIFace = dynamic_cast<ISwFrameControl *>( pWindow.get() );
+}
+
+SwFrameControl::~SwFrameControl()
+{
+ mpIFace = nullptr;
+ mxWindow.disposeAndClear();
+}
+
+ISwFrameControl::~ISwFrameControl()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx b/sw/source/uibase/docvw/HeaderFooterWin.cxx
new file mode 100644
index 000000000..6c184c6fe
--- /dev/null
+++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx
@@ -0,0 +1,591 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <strings.hrc>
+
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <cmdid.h>
+#include <DashedLine.hxx>
+#include <docsh.hxx>
+#include <edtwin.hxx>
+#include <fmthdft.hxx>
+#include <HeaderFooterWin.hxx>
+#include <pagedesc.hxx>
+#include <pagefrm.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <wrtsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <editeng/boxitem.hxx>
+#include <svx/hdft.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <memory>
+
+#define TEXT_PADDING 5
+#define BOX_DISTANCE 10
+#define BUTTON_WIDTH 18
+
+using namespace basegfx;
+using namespace basegfx::utils;
+using namespace drawinglayer::attribute;
+
+namespace
+{
+ basegfx::BColor lcl_GetFillColor(const basegfx::BColor& rLineColor)
+ {
+ basegfx::BColor aHslLine = basegfx::utils::rgb2hsl(rLineColor);
+ double nLuminance = aHslLine.getZ() * 2.5;
+ if ( nLuminance == 0 )
+ nLuminance = 0.5;
+ else if ( nLuminance >= 1.0 )
+ nLuminance = aHslLine.getZ() * 0.4;
+ aHslLine.setZ( nLuminance );
+ return basegfx::utils::hsl2rgb( aHslLine );
+ }
+
+ basegfx::BColor lcl_GetLighterGradientColor(const basegfx::BColor& rDarkColor)
+ {
+ basegfx::BColor aHslDark = basegfx::utils::rgb2hsl(rDarkColor);
+ double nLuminance = aHslDark.getZ() * 255 + 20;
+ aHslDark.setZ( nLuminance / 255.0 );
+ return basegfx::utils::hsl2rgb( aHslDark );
+ }
+
+ B2DPolygon lcl_GetPolygon( const ::tools::Rectangle& rRect, bool bOnTop )
+ {
+ const double nRadius = 3;
+ const double nKappa((M_SQRT2 - 1.0) * 4.0 / 3.0);
+
+ B2DPolygon aPolygon;
+ aPolygon.append( B2DPoint( rRect.Left(), rRect.Top() ) );
+
+ {
+ B2DPoint aCorner( rRect.Left(), rRect.Bottom() );
+ B2DPoint aStart( rRect.Left(), rRect.Bottom() - nRadius );
+ B2DPoint aEnd( rRect.Left() + nRadius, rRect.Bottom() );
+ aPolygon.append( aStart );
+ aPolygon.appendBezierSegment(
+ interpolate( aStart, aCorner, nKappa ),
+ interpolate( aEnd, aCorner, nKappa ),
+ aEnd );
+ }
+
+ {
+ B2DPoint aCorner( rRect.Right(), rRect.Bottom() );
+ B2DPoint aStart( rRect.Right() - nRadius, rRect.Bottom() );
+ B2DPoint aEnd( rRect.Right(), rRect.Bottom() - nRadius );
+ aPolygon.append( aStart );
+ aPolygon.appendBezierSegment(
+ interpolate( aStart, aCorner, nKappa ),
+ interpolate( aEnd, aCorner, nKappa ),
+ aEnd );
+ }
+
+ aPolygon.append( B2DPoint( rRect.Right(), rRect.Top() ) );
+
+ if ( !bOnTop )
+ {
+ B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(rRect);
+ B2DHomMatrix aRotation = createRotateAroundPoint(
+ aBRect.getCenterX(), aBRect.getCenterY(), M_PI );
+ aPolygon.transform( aRotation );
+ }
+
+ return aPolygon;
+ }
+}
+
+void SwFrameButtonPainter::PaintButton(drawinglayer::primitive2d::Primitive2DContainer& rSeq,
+ const tools::Rectangle& rRect, bool bOnTop)
+{
+ rSeq.clear();
+ B2DPolygon aPolygon = lcl_GetPolygon(rRect, bOnTop);
+
+ // Colors
+ basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor();
+ basegfx::BColor aFillColor = lcl_GetFillColor(aLineColor);
+ basegfx::BColor aLighterColor = lcl_GetLighterGradientColor(aFillColor);
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ if (rSettings.GetHighContrastMode())
+ {
+ aFillColor = rSettings.GetDialogColor().getBColor();
+ aLineColor = rSettings.GetDialogTextColor().getBColor();
+
+ rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), aFillColor)));
+ }
+ else
+ {
+ B2DRectangle aGradientRect = vcl::unotools::b2DRectangleFromRectangle(rRect);
+ double nAngle = M_PI;
+ if (bOnTop)
+ nAngle = 0;
+ FillGradientAttribute aFillAttrs(drawinglayer::attribute::GradientStyle::Linear, 0.0, 0.0, 0.0, nAngle, aLighterColor, aFillColor);
+ rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(aGradientRect, aFillAttrs)));
+ }
+
+ // Create the border lines primitive
+ rSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPolygon, aLineColor)));
+}
+
+SwHeaderFooterDashedLine::SwHeaderFooterDashedLine(SwEditWin* pEditWin, const SwFrame *pFrame, bool bHeader)
+ : SwDashedLine(pEditWin, &SwViewOption::GetHeaderFooterMarkColor)
+ , m_pEditWin(pEditWin)
+ , m_pFrame(pFrame)
+ , m_bIsHeader(bHeader)
+{
+}
+
+bool SwHeaderFooterDashedLine::IsOnScreen()
+{
+ tools::Rectangle aBounds(GetPosPixel(), GetSizePixel());
+ tools::Rectangle aVisArea = GetEditWin()->LogicToPixel(GetEditWin()->GetView().GetVisArea());
+ return aBounds.Overlaps(aVisArea);
+}
+
+void SwHeaderFooterDashedLine::EnsureWin()
+{
+ if (!m_pWin)
+ {
+ m_pWin = VclPtr<SwHeaderFooterWin>::Create(m_pEditWin, m_pFrame, m_bIsHeader);
+ m_pWin->SetZOrder(this, ZOrderFlags::Before);
+ }
+}
+
+void SwHeaderFooterDashedLine::ShowAll(bool bShow)
+{
+ Show(bShow);
+ if (!m_pWin && IsOnScreen())
+ EnsureWin();
+ if (m_pWin)
+ m_pWin->ShowAll(bShow);
+}
+
+void SwHeaderFooterDashedLine::SetReadonly(bool bReadonly)
+{
+ ShowAll(!bReadonly);
+}
+
+bool SwHeaderFooterDashedLine::Contains(const Point &rDocPt) const
+{
+ if (m_pWin && m_pWin->Contains(rDocPt))
+ return true;
+
+ ::tools::Rectangle aLineRect(GetPosPixel(), GetSizePixel());
+ return aLineRect.Contains(rDocPt);
+}
+
+void SwHeaderFooterDashedLine::SetOffset(Point aOffset, tools::Long nXLineStart, tools::Long nXLineEnd)
+{
+ Point aLinePos(nXLineStart, aOffset.Y());
+ Size aLineSize(nXLineEnd - nXLineStart, 1);
+ SetPosSizePixel(aLinePos, aLineSize);
+
+ bool bOnScreen = IsOnScreen();
+ if (!m_pWin && bOnScreen)
+ {
+ EnsureWin();
+ m_pWin->ShowAll(true);
+ }
+ else if (m_pWin && !bOnScreen)
+ m_pWin.disposeAndClear();
+
+ if (m_pWin)
+ m_pWin->SetOffset(aOffset);
+}
+
+SwHeaderFooterWin::SwHeaderFooterWin(SwEditWin* pEditWin, const SwFrame *pFrame, bool bHeader ) :
+ InterimItemWindow(pEditWin, "modules/swriter/ui/hfmenubutton.ui", "HFMenuButton"),
+ m_xMenuButton(m_xBuilder->weld_menu_button("menubutton")),
+ m_xPushButton(m_xBuilder->weld_button("button")),
+ m_pEditWin(pEditWin),
+ m_pFrame(pFrame),
+ m_bIsHeader( bHeader ),
+ m_bIsAppearing( false ),
+ m_nFadeRate( 100 ),
+ m_aFadeTimer("SwHeaderFooterWin m_aFadeTimer")
+{
+ m_xVirDev = m_xMenuButton->create_virtual_device();
+ SwFrameMenuButtonBase::SetVirDevFont(*m_xVirDev);
+
+ m_xPushButton->connect_clicked(LINK(this, SwHeaderFooterWin, ClickHdl));
+ m_xMenuButton->connect_selected(LINK(this, SwHeaderFooterWin, SelectHdl));
+
+ // set the PopupMenu
+ // Rewrite the menu entries' text
+ if (m_bIsHeader)
+ {
+ m_xMenuButton->set_item_label("edit", SwResId(STR_FORMAT_HEADER));
+ m_xMenuButton->set_item_label("delete", SwResId(STR_DELETE_HEADER));
+ }
+ else
+ {
+ m_xMenuButton->set_item_label("edit", SwResId(STR_FORMAT_FOOTER));
+ m_xMenuButton->set_item_label("delete", SwResId(STR_DELETE_FOOTER));
+ }
+
+ m_aFadeTimer.SetTimeout(50);
+ m_aFadeTimer.SetInvokeHandler(LINK(this, SwHeaderFooterWin, FadeHandler));
+}
+
+SwHeaderFooterWin::~SwHeaderFooterWin( )
+{
+ disposeOnce();
+}
+
+void SwHeaderFooterWin::dispose()
+{
+ m_xPushButton.reset();
+ m_xMenuButton.reset();
+ m_pEditWin.clear();
+ m_xVirDev.disposeAndClear();
+ InterimItemWindow::dispose();
+}
+
+void SwHeaderFooterWin::SetOffset(Point aOffset)
+{
+ // Compute the text to show
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ const SwPageDesc* pDesc = pPageFrame->GetPageDesc();
+ bool bIsFirst = !pDesc->IsFirstShared() && pPageFrame->OnFirstPage();
+ bool bIsLeft = !pDesc->IsHeaderShared() && !pPageFrame->OnRightPage();
+ bool bIsRight = !pDesc->IsHeaderShared() && pPageFrame->OnRightPage();
+ m_sLabel = SwResId(STR_HEADER_TITLE);
+ if (!m_bIsHeader)
+ m_sLabel = bIsFirst ? SwResId(STR_FIRST_FOOTER_TITLE)
+ : bIsLeft ? SwResId(STR_LEFT_FOOTER_TITLE)
+ : bIsRight ? SwResId(STR_RIGHT_FOOTER_TITLE)
+ : SwResId(STR_FOOTER_TITLE );
+ else
+ m_sLabel = bIsFirst ? SwResId(STR_FIRST_HEADER_TITLE)
+ : bIsLeft ? SwResId(STR_LEFT_HEADER_TITLE)
+ : bIsRight ? SwResId(STR_RIGHT_HEADER_TITLE)
+ : SwResId(STR_HEADER_TITLE);
+
+ sal_Int32 nPos = m_sLabel.lastIndexOf("%1");
+ m_sLabel = m_sLabel.replaceAt(nPos, 2, pDesc->GetName());
+ m_xMenuButton->set_accessible_name(m_sLabel);
+
+ // Compute the text size and get the box position & size from it
+ ::tools::Rectangle aTextRect;
+ m_xVirDev->GetTextBoundRect(aTextRect, m_sLabel);
+ ::tools::Rectangle aTextPxRect = m_xVirDev->LogicToPixel(aTextRect);
+ FontMetric aFontMetric = m_xVirDev->GetFontMetric(m_xVirDev->GetFont());
+ Size aBoxSize (aTextPxRect.GetWidth() + BUTTON_WIDTH + TEXT_PADDING * 2,
+ aFontMetric.GetLineHeight() + TEXT_PADDING * 2 );
+
+ tools::Long nYFooterOff = 0;
+ if (!m_bIsHeader)
+ nYFooterOff = aBoxSize.Height();
+
+ Point aBoxPos(aOffset.X() - aBoxSize.Width() - BOX_DISTANCE,
+ aOffset.Y() - nYFooterOff);
+
+ if (AllSettings::GetLayoutRTL())
+ {
+ aBoxPos.setX( aOffset.X() + BOX_DISTANCE );
+ }
+
+ // Set the position & Size of the window
+ SetPosSizePixel(aBoxPos, aBoxSize);
+
+ m_xVirDev->SetOutputSizePixel(aBoxSize);
+ PaintButton();
+}
+
+void SwHeaderFooterWin::ShowAll(bool bShow)
+{
+ bool bIsEmptyHeaderFooter = IsEmptyHeaderFooter();
+ m_xMenuButton->set_visible(!bIsEmptyHeaderFooter);
+ m_xPushButton->set_visible(bIsEmptyHeaderFooter);
+
+ m_bIsAppearing = bShow;
+
+ if (m_aFadeTimer.IsActive())
+ m_aFadeTimer.Stop();
+ m_aFadeTimer.Start();
+}
+
+bool SwHeaderFooterWin::Contains( const Point &rDocPt ) const
+{
+ ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel());
+ return aRect.Contains(rDocPt);
+}
+
+void SwHeaderFooterWin::PaintButton()
+{
+ if (!m_xVirDev)
+ return;
+
+ // Use pixels for the rest of the drawing
+ SetMapMode(MapMode(MapUnit::MapPixel));
+ drawinglayer::primitive2d::Primitive2DContainer aSeq;
+ const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), m_xVirDev->PixelToLogic(GetSizePixel())));
+
+ SwFrameButtonPainter::PaintButton(aSeq, aRect, m_bIsHeader);
+
+ // Create the text primitive
+ basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor();
+ B2DVector aFontSize;
+ FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, m_xVirDev->GetFont(), false, false);
+
+ FontMetric aFontMetric = m_xVirDev->GetFontMetric(m_xVirDev->GetFont());
+ double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING;
+ Point aTextPos(TEXT_PADDING, nTextOffsetY);
+
+ basegfx::B2DHomMatrix aTextMatrix(createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ double(aTextPos.X()), double(aTextPos.Y())));
+
+ aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, m_sLabel, 0, m_sLabel.getLength(),
+ std::vector<double>(), aFontAttr, css::lang::Locale(), aLineColor)));
+
+ // Create the 'plus' or 'arrow' primitive
+ B2DRectangle aSignArea(B2DPoint(aRect.Right() - BUTTON_WIDTH, 0.0),
+ B2DSize(aRect.Right(), aRect.getHeight()));
+
+ B2DPolygon aSign;
+ bool bIsEmptyHeaderFooter = IsEmptyHeaderFooter();
+ if (bIsEmptyHeaderFooter)
+ {
+ // Create the + polygon
+ double nLeft = aSignArea.getMinX() + TEXT_PADDING;
+ double nRight = aSignArea.getMaxX() - TEXT_PADDING;
+ double nHalfW = ( nRight - nLeft ) / 2.0;
+
+ double nTop = aSignArea.getCenterY() - nHalfW;
+ double nBottom = aSignArea.getCenterY() + nHalfW;
+
+ aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() - 1.0));
+ aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() - 1.0));
+ aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nTop));
+ aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nTop));
+ aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() - 1.0));
+ aSign.append(B2DPoint(nRight, aSignArea.getCenterY() - 1.0));
+ aSign.append(B2DPoint(nRight, aSignArea.getCenterY() + 1.0));
+ aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, aSignArea.getCenterY() + 1.0));
+ aSign.append(B2DPoint(aSignArea.getCenterX() + 1.0, nBottom));
+ aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, nBottom));
+ aSign.append(B2DPoint(aSignArea.getCenterX() - 1.0, aSignArea.getCenterY() + 1.0));
+ aSign.append(B2DPoint(nLeft, aSignArea.getCenterY() + 1.0));
+ aSign.setClosed(true);
+ }
+ else
+ {
+ // Create the v polygon
+ B2DPoint aLeft(aSignArea.getMinX() + TEXT_PADDING, aSignArea.getCenterY());
+ B2DPoint aRight(aSignArea.getMaxX() - TEXT_PADDING, aSignArea.getCenterY());
+ B2DPoint aBottom((aLeft.getX() + aRight.getX()) / 2.0, aLeft.getY() + 4.0);
+ aSign.append(aLeft);
+ aSign.append(aRight);
+ aSign.append(aBottom);
+ aSign.setClosed(true);
+ }
+
+ BColor aSignColor = COL_BLACK.getBColor();
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ aSignColor = COL_WHITE.getBColor();
+
+ aSeq.push_back( drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ B2DPolyPolygon(aSign), aSignColor)) );
+
+ // Create the processor and process the primitives
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(*m_xVirDev, aNewViewInfos));
+
+ // TODO Ghost it all if needed
+ drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq(1);
+ double nFadeRate = double(m_nFadeRate) / 100.0;
+
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(),
+ 1.0 - nFadeRate);
+
+ aGhostedSeq[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(std::move(aSeq), aBColorModifier));
+
+ pProcessor->process(aGhostedSeq);
+
+ if (bIsEmptyHeaderFooter)
+ m_xPushButton->set_custom_button(m_xVirDev.get());
+ else
+ m_xMenuButton->set_custom_button(m_xVirDev.get());
+}
+
+bool SwHeaderFooterWin::IsEmptyHeaderFooter( ) const
+{
+ bool bResult = true;
+
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ if (!pPageFrame)
+ {
+ return bResult;
+ }
+
+ // Actually check it
+ const SwPageDesc* pDesc = pPageFrame->GetPageDesc();
+
+ bool const bFirst(pPageFrame->OnFirstPage());
+ const SwFrameFormat *const pFormat = (pPageFrame->OnRightPage())
+ ? pDesc->GetRightFormat(bFirst)
+ : pDesc->GetLeftFormat(bFirst);
+
+ if ( pFormat )
+ {
+ if ( m_bIsHeader )
+ bResult = !pFormat->GetHeader().IsActive();
+ else
+ bResult = !pFormat->GetFooter().IsActive();
+ }
+
+ return bResult;
+}
+
+void SwHeaderFooterWin::ExecuteCommand(std::string_view rIdent)
+{
+ SwView& rView = m_pEditWin->GetView();
+ SwWrtShell& rSh = rView.GetWrtShell();
+
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ const OUString& rStyleName = pPageFrame->GetPageDesc()->GetName();
+ if (rIdent == "edit")
+ {
+ OString sPageId = m_bIsHeader ? OString("header") : OString("footer");
+ rView.GetDocShell()->FormatPage(rView.GetFrameWeld(), rStyleName, sPageId, rSh);
+ }
+ else if (rIdent == "borderback")
+ {
+ const SwPageDesc* pDesc = pPageFrame->GetPageDesc();
+ const SwFrameFormat& rMaster = pDesc->GetMaster();
+ SwFrameFormat* pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetFooter().GetFooterFormat() );
+ if ( m_bIsHeader )
+ pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetHeader().GetHeaderFormat() );
+ SfxItemSet aSet( pHFFormat->GetAttrSet() );
+
+ // Items to hand over XPropertyList things like XColorList,
+ // XHatchList, XGradientList, and XBitmapList to the Area TabPage:
+ aSet.MergeRange( SID_COLOR_TABLE, SID_PATTERN_LIST );
+ // create needed items for XPropertyList entries from the DrawModel so that
+ // the Area TabPage can access them
+ rSh.GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->PutAreaListItems( aSet );
+
+ aSet.MergeRange(SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER);
+ // Create a box info item... needed by the dialog
+ std::shared_ptr<SvxBoxInfoItem> aBoxInfo(std::make_shared<SvxBoxInfoItem>(SID_ATTR_BORDER_INNER));
+ if (const SvxBoxInfoItem *pBoxInfo = pHFFormat->GetAttrSet().GetItemIfSet(SID_ATTR_BORDER_INNER))
+ aBoxInfo.reset(pBoxInfo->Clone());
+
+ aBoxInfo->SetTable(false);
+ aBoxInfo->SetDist(true);
+ aBoxInfo->SetMinDist(false);
+ aBoxInfo->SetDefDist(MIN_BORDER_DIST);
+ aBoxInfo->SetValid(SvxBoxInfoItemValidFlags::DISABLE);
+ aSet.Put(*aBoxInfo);
+
+ if (svx::ShowBorderBackgroundDlg( GetFrameWeld(), &aSet ) )
+ {
+ pHFFormat->SetFormatAttr( aSet );
+ rView.GetDocShell()->SetModified();
+ }
+ }
+ else if (rIdent == "delete")
+ {
+ rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, false, true );
+ // warning: "this" may be disposed now
+ rSh.GetWin()->GrabFocusToDocument();
+ }
+ else if (rIdent == "insert_pagenumber")
+ {
+ SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame();
+ pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGNUMBER);
+ }
+ else if (rIdent == "insert_pagecount")
+ {
+ SfxViewFrame* pVFrame = rSh.GetView().GetViewFrame();
+ pVFrame->GetBindings().Execute(FN_INSERT_FLD_PGCOUNT);
+ }
+}
+
+IMPL_LINK_NOARG(SwHeaderFooterWin, ClickHdl, weld::Button&, void)
+{
+ SwView& rView = m_pEditWin->GetView();
+ SwWrtShell& rSh = rView.GetWrtShell();
+
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ const OUString& rStyleName = pPageFrame->GetPageDesc()->GetName();
+ {
+ VclPtr<SwHeaderFooterWin> xThis(this);
+ rSh.ChangeHeaderOrFooter( rStyleName, m_bIsHeader, true, false );
+ //tdf#153059 after ChangeHeaderOrFooter is it possible that "this" is disposed
+ if (xThis->isDisposed())
+ return;
+ }
+ m_xPushButton->hide();
+ m_xMenuButton->show();
+ PaintButton();
+}
+
+IMPL_LINK(SwHeaderFooterWin, SelectHdl, const OString&, rIdent, void)
+{
+ ExecuteCommand(rIdent);
+}
+
+IMPL_LINK_NOARG(SwHeaderFooterWin, FadeHandler, Timer *, void)
+{
+ if (m_bIsAppearing && m_nFadeRate > 0)
+ m_nFadeRate -= 25;
+ else if (!m_bIsAppearing && m_nFadeRate < 100)
+ m_nFadeRate += 25;
+
+ if (m_nFadeRate != 100 && !IsVisible())
+ {
+ Show();
+ }
+ else if (m_nFadeRate == 100 && IsVisible())
+ {
+ Show(false);
+ }
+ else
+ PaintButton();
+
+ if (IsVisible() && m_nFadeRate > 0 && m_nFadeRate < 100)
+ m_aFadeTimer.Start();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx b/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx
new file mode 100644
index 000000000..33d0b72ff
--- /dev/null
+++ b/sw/source/uibase/docvw/OutlineContentVisibilityWin.cxx
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <edtwin.hxx>
+#include <OutlineContentVisibilityWin.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+
+#include <IDocumentOutlineNodes.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <strings.hrc>
+
+#include <viewopt.hxx>
+
+#include <FrameControlsManager.hxx>
+
+SwOutlineContentVisibilityWin::SwOutlineContentVisibilityWin(SwEditWin* pEditWin,
+ const SwFrame* pFrame)
+ : InterimItemWindow(pEditWin, "modules/swriter/ui/outlinebutton.ui", "OutlineButton")
+ , m_xShowBtn(m_xBuilder->weld_button("show"))
+ , m_xHideBtn(m_xBuilder->weld_button("hide"))
+ , m_pEditWin(pEditWin)
+ , m_pFrame(pFrame)
+ , m_nDelayAppearing(0)
+ , m_aDelayTimer("SwOutlineContentVisibilityWin m_aDelayTimer")
+ , m_bDestroyed(false)
+ , m_nOutlinePos(SwOutlineNodes::npos)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ SetPaintTransparent(false);
+ SetBackground(rStyleSettings.GetFaceColor());
+
+ Size aBtnsSize(m_xShowBtn->get_preferred_size());
+ auto nDim = std::max(aBtnsSize.Width(), aBtnsSize.Height());
+ m_xShowBtn->set_size_request(nDim, nDim);
+ m_xHideBtn->set_size_request(nDim, nDim);
+
+ SetSizePixel(get_preferred_size());
+ SetSymbol(ButtonSymbol::NONE);
+
+ m_xShowBtn->connect_mouse_press(LINK(this, SwOutlineContentVisibilityWin, MousePressHdl));
+ m_xHideBtn->connect_mouse_press(LINK(this, SwOutlineContentVisibilityWin, MousePressHdl));
+
+ m_aDelayTimer.SetTimeout(25);
+ m_aDelayTimer.SetInvokeHandler(LINK(this, SwOutlineContentVisibilityWin, DelayAppearHandler));
+}
+
+void SwOutlineContentVisibilityWin::dispose()
+{
+ m_bDestroyed = true;
+ m_aDelayTimer.Stop();
+
+ m_pEditWin.clear();
+ m_pFrame = nullptr;
+
+ m_xHideBtn.reset();
+ m_xShowBtn.reset();
+
+ InterimItemWindow::dispose();
+}
+
+ButtonSymbol SwOutlineContentVisibilityWin::GetSymbol() const
+{
+ if (m_xShowBtn->get_visible())
+ return ButtonSymbol::SHOW;
+ if (m_xHideBtn->get_visible())
+ return ButtonSymbol::HIDE;
+ return ButtonSymbol::NONE;
+}
+
+void SwOutlineContentVisibilityWin::SetSymbol(ButtonSymbol eStyle)
+{
+ if (GetSymbol() == eStyle)
+ return;
+
+ bool bShow = eStyle == ButtonSymbol::SHOW;
+ bool bHide = eStyle == ButtonSymbol::HIDE;
+
+ // disable mouse move for the hidden button so we don't get mouse
+ // leave events we don't care about when we swap buttons
+ m_xShowBtn->connect_mouse_move(Link<const MouseEvent&, bool>());
+ m_xHideBtn->connect_mouse_move(Link<const MouseEvent&, bool>());
+
+ m_xShowBtn->set_visible(bShow);
+ m_xHideBtn->set_visible(bHide);
+
+ weld::Button* pButton = nullptr;
+ if (bShow)
+ pButton = m_xShowBtn.get();
+ else if (bHide)
+ pButton = m_xHideBtn.get();
+ InitControlBase(pButton);
+ if (pButton)
+ pButton->connect_mouse_move(LINK(this, SwOutlineContentVisibilityWin, MouseMoveHdl));
+}
+
+void SwOutlineContentVisibilityWin::Set()
+{
+ const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(GetFrame());
+ const SwTextNode* pTextNode = pTextFrame->GetTextNodeFirst();
+ SwWrtShell& rSh = GetEditWin()->GetView().GetWrtShell();
+ const SwOutlineNodes& rOutlineNodes = rSh.GetNodes().GetOutLineNds();
+
+ (void)rOutlineNodes.Seek_Entry(static_cast<SwNode*>(const_cast<SwTextNode*>(pTextNode)),
+ &m_nOutlinePos);
+
+ // set symbol displayed on button
+ bool bVisible = true;
+ const_cast<SwTextNode*>(pTextNode)->GetAttrOutlineContentVisible(bVisible);
+ SetSymbol(bVisible ? ButtonSymbol::HIDE : ButtonSymbol::SHOW);
+
+ // set quick help
+ SwOutlineNodes::size_type nOutlineNodesCount
+ = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+ int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(m_nOutlinePos);
+ OUString sQuickHelp(SwResId(STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY));
+ if (!rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent()
+ && m_nOutlinePos + 1 < nOutlineNodesCount
+ && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(m_nOutlinePos + 1) > nLevel)
+ sQuickHelp += " (" + SwResId(STR_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT) + ")";
+ SetQuickHelpText(sQuickHelp);
+
+ // Set the position of the window
+ SwRect aFrameAreaRect = GetFrame()->getFrameArea();
+ aFrameAreaRect.AddTop(GetFrame()->GetTopMargin());
+ SwRect aCharRect;
+ GetFrame()->GetCharRect(aCharRect, SwPosition(*pTextNode));
+ Point aPxPt(GetEditWin()->GetOutDev()->LogicToPixel(
+ Point(aCharRect.Left(), aFrameAreaRect.Center().getY())));
+ if (GetFrame()->IsRightToLeft())
+ aPxPt.AdjustX(2);
+ else
+ aPxPt.AdjustX(-(GetSizePixel().getWidth() + 2));
+ aPxPt.AdjustY(-GetSizePixel().getHeight() / 2);
+ SetPosPixel(aPxPt);
+}
+
+void SwOutlineContentVisibilityWin::ShowAll(bool bShow)
+{
+ if (bShow)
+ {
+ m_nDelayAppearing = 0;
+ if (!m_bDestroyed && m_aDelayTimer.IsActive())
+ m_aDelayTimer.Stop();
+ if (!m_bDestroyed)
+ m_aDelayTimer.Start();
+ }
+ else
+ Hide();
+}
+
+bool SwOutlineContentVisibilityWin::Contains(const Point& rDocPt) const
+{
+ ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel());
+ if (aRect.Contains(rDocPt))
+ return true;
+ return false;
+}
+
+IMPL_LINK(SwOutlineContentVisibilityWin, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (rMEvt.IsLeaveWindow())
+ {
+ if (GetSymbol() == ButtonSymbol::HIDE)
+ {
+ // MouseMove event may not be seen by the edit window for example when move is to
+ // a show button or when move is outside of the edit window.
+ // Only hide when mouse leave results in leaving the frame.
+ tools::Rectangle aFrameAreaPxRect
+ = GetEditWin()->LogicToPixel(GetFrame()->getFrameArea().SVRect());
+ auto nY = GetPosPixel().getY() + rMEvt.GetPosPixel().getY();
+ if (nY <= 0 || nY <= aFrameAreaPxRect.Top() || nY >= aFrameAreaPxRect.Bottom()
+ || nY >= GetEditWin()->GetSizePixel().Height())
+ {
+ GetEditWin()->SetSavedOutlineFrame(nullptr);
+ GetEditWin()->GetFrameControlsManager().RemoveControlsByType(
+ FrameControlType::Outline, GetFrame());
+ // warning: "this" is disposed now
+ }
+ }
+ }
+ else if (rMEvt.IsEnterWindow())
+ {
+ // Leave window event might not have resulted in removing hide button from saved frame
+ // and the edit win might not receive mouse event between leaving saved frame button and
+ // entering this button.
+ if (GetFrame() != GetEditWin()->GetSavedOutlineFrame())
+ {
+ SwFrameControlPtr pFrameControl = GetEditWin()->GetFrameControlsManager().GetControl(
+ FrameControlType::Outline, GetEditWin()->GetSavedOutlineFrame());
+ if (pFrameControl)
+ {
+ SwOutlineContentVisibilityWin* pControl
+ = dynamic_cast<SwOutlineContentVisibilityWin*>(pFrameControl->GetIFacePtr());
+ if (pControl && pControl->GetSymbol() == ButtonSymbol::HIDE)
+ {
+ GetEditWin()->GetFrameControlsManager().RemoveControlsByType(
+ FrameControlType::Outline, GetEditWin()->GetSavedOutlineFrame());
+ // The outline content visibility window frame control (hide button)
+ // for saved outline frame is now disposed.
+ }
+ }
+ GetEditWin()->SetSavedOutlineFrame(
+ static_cast<SwTextFrame*>(const_cast<SwFrame*>(GetFrame())));
+ }
+ if (!m_bDestroyed && m_aDelayTimer.IsActive())
+ m_aDelayTimer.Stop();
+ // bring button to top
+ SetZOrder(this, ZOrderFlags::First);
+ }
+ return false;
+}
+
+// Toggle the outline content visibility on mouse press
+IMPL_LINK(SwOutlineContentVisibilityWin, MousePressHdl, const MouseEvent&, rMEvt, bool)
+{
+ Hide();
+ GetEditWin()->ToggleOutlineContentVisibility(m_nOutlinePos, rMEvt.IsRight());
+ return false;
+}
+
+IMPL_LINK_NOARG(SwOutlineContentVisibilityWin, DelayAppearHandler, Timer*, void)
+{
+ const int TICKS_BEFORE_WE_APPEAR = 3;
+ if (m_nDelayAppearing < TICKS_BEFORE_WE_APPEAR)
+ {
+ ++m_nDelayAppearing;
+ m_aDelayTimer.Start();
+ return;
+ }
+ if (GetEditWin()->GetSavedOutlineFrame() == GetFrame())
+ Show();
+ m_aDelayTimer.Stop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/OverlayRanges.cxx b/sw/source/uibase/docvw/OverlayRanges.cxx
new file mode 100644
index 000000000..ccbcc0041
--- /dev/null
+++ b/sw/source/uibase/docvw/OverlayRanges.cxx
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "OverlayRanges.hxx"
+#include <view.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+namespace
+{
+ // combine ranges geometrically to a single, ORed polygon
+ basegfx::B2DPolyPolygon impCombineRangesToPolyPolygon(const std::vector< basegfx::B2DRange >& rRanges)
+ {
+ const sal_uInt32 nCount(rRanges.size());
+ basegfx::B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const basegfx::B2DPolygon aDiscretePolygon(basegfx::utils::createPolygonFromRect(rRanges[a]));
+
+ if(0 == a)
+ {
+ aRetval.append(aDiscretePolygon);
+ }
+ else
+ {
+ aRetval = basegfx::utils::solvePolygonOperationOr(aRetval, basegfx::B2DPolyPolygon(aDiscretePolygon));
+ }
+ }
+
+ return aRetval;
+ }
+}
+
+namespace sw::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayRanges::createOverlayObjectPrimitive2DSequence()
+ {
+ const sal_uInt32 nCount(getRanges().size());
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ aRetval.resize(nCount);
+ for ( sal_uInt32 a = 0; a < nCount; ++a )
+ {
+ const basegfx::BColor aRGBColor(getBaseColor().getBColor());
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(maRanges[a]));
+ aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ aRGBColor));
+ }
+ // embed all rectangles in transparent paint
+ const sal_uInt16 nTransparence( SvtOptionsDrawinglayer::GetTransparentSelectionPercent() );
+ const double fTransparence( nTransparence / 100.0 );
+ const drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aRetval),
+ fTransparence));
+
+ if ( mbShowSolidBorder )
+ {
+ const basegfx::BColor aRGBColor(getBaseColor().getBColor());
+ const basegfx::B2DPolyPolygon aPolyPolygon(impCombineRangesToPolyPolygon(getRanges()));
+ const drawinglayer::primitive2d::Primitive2DReference aOutline(
+ new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
+ aPolyPolygon,
+ aRGBColor));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence, aOutline };
+ }
+ else
+ {
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence };
+ }
+
+ return aRetval;
+ }
+
+ /*static*/ std::unique_ptr<OverlayRanges> OverlayRanges::CreateOverlayRange(
+ SwView const & rDocView,
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges,
+ const bool bShowSolidBorder )
+ {
+ std::unique_ptr<OverlayRanges> pOverlayRanges;
+
+ SdrView* pView = rDocView.GetDrawView();
+ if ( pView != nullptr )
+ {
+ SdrPaintWindow* pCandidate = pView->GetPaintWindow(0);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if ( xTargetOverlay.is() )
+ {
+ pOverlayRanges.reset(new sw::overlay::OverlayRanges( rColor, std::move(rRanges), bShowSolidBorder ));
+ xTargetOverlay->add( *pOverlayRanges );
+ }
+ }
+
+ return pOverlayRanges;
+ }
+
+ OverlayRanges::OverlayRanges(
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges,
+ const bool bShowSolidBorder )
+ : sdr::overlay::OverlayObject( rColor )
+ , maRanges( std::move(rRanges) )
+ , mbShowSolidBorder( bShowSolidBorder )
+ {
+ // no AA for highlight overlays
+ allowAntiAliase(false);
+ }
+
+ OverlayRanges::~OverlayRanges()
+ {
+ if( getOverlayManager() )
+ {
+ getOverlayManager()->remove(*this);
+ }
+ }
+
+ void OverlayRanges::setRanges(std::vector< basegfx::B2DRange >&& rNew)
+ {
+ if(rNew != maRanges)
+ {
+ maRanges = std::move(rNew);
+ objectChange();
+ }
+ }
+
+ void OverlayRanges::ShowSolidBorder()
+ {
+ if ( !mbShowSolidBorder )
+ {
+ mbShowSolidBorder = true;
+ objectChange();
+ }
+ }
+
+ void OverlayRanges::HideSolidBorder()
+ {
+ if ( mbShowSolidBorder )
+ {
+ mbShowSolidBorder = false;
+ objectChange();
+ }
+ }
+
+} // end of namespace sw::overlay
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/OverlayRanges.hxx b/sw/source/uibase/docvw/OverlayRanges.hxx
new file mode 100644
index 000000000..d698e2cde
--- /dev/null
+++ b/sw/source/uibase/docvw/OverlayRanges.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+#include <memory>
+#include <vector>
+
+class SwView;
+
+namespace sw::overlay
+ {
+ class OverlayRanges final : public sdr::overlay::OverlayObject
+ {
+ public:
+ static std::unique_ptr<OverlayRanges> CreateOverlayRange(
+ SwView const & rDocView,
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges,
+ const bool bShowSolidBorder );
+
+ virtual ~OverlayRanges() override;
+
+ // data read access
+ const std::vector< basegfx::B2DRange >& getRanges() const
+ {
+ return maRanges;
+ }
+
+ // data write access
+ void setRanges(std::vector< basegfx::B2DRange >&& rNew);
+
+ void ShowSolidBorder();
+ void HideSolidBorder();
+
+ private:
+ OverlayRanges(
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges,
+ const bool bShowSolidBorder );
+
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+ // geometry of overlay
+ std::vector< basegfx::B2DRange > maRanges;
+ bool mbShowSolidBorder;
+ };
+} // end of namespace sw::overlay
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/PageBreakWin.cxx b/sw/source/uibase/docvw/PageBreakWin.cxx
new file mode 100644
index 000000000..600d23a44
--- /dev/null
+++ b/sw/source/uibase/docvw/PageBreakWin.cxx
@@ -0,0 +1,506 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <bitmaps.hlst>
+
+#include <cmdid.h>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <ndtxt.hxx>
+#include <DashedLine.hxx>
+#include <doc.hxx>
+#include <edtwin.hxx>
+#include <fmtpdsc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <PageBreakWin.hxx>
+#include <pagefrm.hxx>
+#include <PostItMgr.hxx>
+#include <FrameControlsManager.hxx>
+#include <strings.hrc>
+#include <tabfrm.hxx>
+#include <uiitems.hxx>
+#include <uiobject.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <wrtsh.hxx>
+
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+#include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <memory>
+
+#define BUTTON_WIDTH 30
+#define BUTTON_HEIGHT 19
+#define ARROW_WIDTH 9
+
+using namespace basegfx;
+using namespace basegfx::utils;
+
+SwBreakDashedLine::SwBreakDashedLine(SwEditWin* pEditWin, const SwFrame *pFrame)
+ : SwDashedLine(pEditWin, &SwViewOption::GetPageBreakColor)
+ , m_pEditWin(pEditWin)
+ , m_pFrame(pFrame)
+{
+ set_id("PageBreak"); // for uitest
+}
+
+SwPageBreakWin& SwBreakDashedLine::GetOrCreateWin()
+{
+ if (!m_pWin)
+ {
+ m_pWin = VclPtr<SwPageBreakWin>::Create(this, m_pEditWin, m_pFrame);
+ m_pWin->SetPosSizePixel(m_aBtnRect.TopLeft(), m_aBtnRect.GetSize());
+ m_pWin->SetZOrder(this, ZOrderFlags::Before);
+ }
+ return *m_pWin;
+}
+
+void SwBreakDashedLine::DestroyWin()
+{
+ m_pWin.disposeAndClear();
+}
+
+void SwBreakDashedLine::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( rMEvt.IsLeaveWindow() )
+ {
+ // don't fade if we just move to the 'button'
+ Point aEventPos( GetPosPixel() + rMEvt.GetPosPixel() );
+ if (m_pWin && (!Contains(aEventPos) || !m_pWin->IsVisible()))
+ m_pWin->Fade(false);
+ }
+ else if (!m_pWin || !m_pWin->IsVisible())
+ {
+ GetOrCreateWin().Fade(true);
+ }
+
+ if (!rMEvt.IsSynthetic() && (!m_pWin || !m_pWin->IsVisible()))
+ {
+ UpdatePosition(rMEvt.GetPosPixel());
+ }
+}
+
+void SwBreakDashedLine::ShowAll(bool bShow)
+{
+ Show(bShow);
+}
+
+void SwBreakDashedLine::SetReadonly(bool bReadonly)
+{
+ ShowAll(!bReadonly);
+}
+
+bool SwBreakDashedLine::Contains(const Point &rDocPt) const
+{
+ if (m_aBtnRect.Contains(rDocPt))
+ return true;
+
+ ::tools::Rectangle aLineRect(GetPosPixel(), GetSizePixel());
+ return aLineRect.Contains(rDocPt);
+}
+
+SwPageBreakWin::SwPageBreakWin(SwBreakDashedLine* pLine, SwEditWin* pEditWin, const SwFrame *pFrame) :
+ InterimItemWindow(pEditWin, "modules/swriter/ui/pbmenubutton.ui", "PBMenuButton"),
+ m_xMenuButton(m_xBuilder->weld_menu_button("menubutton")),
+ m_pLine(pLine),
+ m_pEditWin(pEditWin),
+ m_pFrame(pFrame),
+ m_bIsAppearing( false ),
+ m_nFadeRate( 100 ),
+ m_nDelayAppearing( 0 ),
+ m_aFadeTimer("SwPageBreakWin m_aFadeTimer"),
+ m_bDestroyed( false )
+{
+ m_xMenuButton->connect_toggled(LINK(this, SwPageBreakWin, ToggleHdl));
+ m_xMenuButton->connect_selected(LINK(this, SwPageBreakWin, SelectHdl));
+ m_xMenuButton->set_accessible_name(SwResId(STR_PAGE_BREAK_BUTTON));
+
+ m_xVirDev = m_xMenuButton->create_virtual_device();
+ SwFrameMenuButtonBase::SetVirDevFont(*m_xVirDev);
+
+ // Use pixels for the rest of the drawing
+ m_xVirDev->SetMapMode( MapMode ( MapUnit::MapPixel ) );
+
+ m_aFadeTimer.SetTimeout( 50 );
+ m_aFadeTimer.SetInvokeHandler( LINK( this, SwPageBreakWin, FadeHandler ) );
+}
+
+SwPageBreakWin::~SwPageBreakWin( )
+{
+ disposeOnce();
+}
+
+void SwPageBreakWin::dispose()
+{
+ m_bDestroyed = true;
+ m_aFadeTimer.Stop();
+ m_xVirDev.disposeAndClear();
+
+ m_pLine.clear();
+ m_pEditWin.clear();
+
+ m_xMenuButton.reset();
+ InterimItemWindow::dispose();
+}
+
+void SwPageBreakWin::PaintButton()
+{
+ if (!m_xVirDev)
+ return;
+
+ const ::tools::Rectangle aRect(::tools::Rectangle(Point(0, 0), m_xVirDev->PixelToLogic(GetSizePixel())));
+
+ // Properly paint the control
+ BColor aColor = SwViewOption::GetPageBreakColor().getBColor();
+
+ BColor aHslLine = rgb2hsl(aColor);
+ double nLuminance = aHslLine.getZ();
+ nLuminance += (1.0 - nLuminance) * 0.75;
+ if ( aHslLine.getZ() > 0.7 )
+ nLuminance = aHslLine.getZ() * 0.7;
+ aHslLine.setZ(nLuminance);
+ BColor aOtherColor = hsl2rgb(aHslLine);
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ if (rSettings.GetHighContrastMode())
+ {
+ aColor = rSettings.GetDialogTextColor().getBColor();
+ aOtherColor = rSettings.GetDialogColor().getBColor();
+ }
+
+ bool bRtl = AllSettings::GetLayoutRTL();
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq(3);
+ B2DRectangle aBRect = vcl::unotools::b2DRectangleFromRectangle(aRect);
+ B2DPolygon aPolygon = createPolygonFromRect(aBRect, 3.0 / BUTTON_WIDTH, 3.0 / BUTTON_HEIGHT);
+
+ // Create the polygon primitives
+ aSeq[0].set(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ B2DPolyPolygon(aPolygon), aOtherColor));
+ aSeq[1].set(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aColor));
+
+ // Create the primitive for the image
+ BitmapEx aBmpEx(RID_BMP_PAGE_BREAK);
+ double nImgOfstX = 3.0;
+ if (bRtl)
+ nImgOfstX = aRect.Right() - aBmpEx.GetSizePixel().Width() - 3.0;
+ aSeq[2].set(new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
+ aBmpEx, B2DPoint(nImgOfstX, 1.0)));
+
+ double nTop = double(aRect.getHeight()) / 2.0;
+ double nBottom = nTop + 4.0;
+ double nLeft = aRect.getWidth() - ARROW_WIDTH - 6.0;
+ if (bRtl)
+ nLeft = ARROW_WIDTH - 2.0;
+ double nRight = nLeft + 8.0;
+
+ B2DPolygon aTriangle;
+ aTriangle.append(B2DPoint(nLeft, nTop));
+ aTriangle.append(B2DPoint(nRight, nTop));
+ aTriangle.append(B2DPoint((nLeft + nRight) / 2.0, nBottom));
+ aTriangle.setClosed(true);
+
+ BColor aTriangleColor = COL_BLACK.getBColor();
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ aTriangleColor = COL_WHITE.getBColor();
+
+ aSeq.emplace_back();
+ aSeq.back().set( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ B2DPolyPolygon(aTriangle), aTriangleColor));
+
+ drawinglayer::primitive2d::Primitive2DContainer aGhostedSeq(1);
+ double nFadeRate = double(m_nFadeRate) / 100.0;
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_interpolate>(COL_WHITE.getBColor(),
+ 1.0 - nFadeRate);
+ aGhostedSeq[0].set( new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ std::move(aSeq), aBColorModifier));
+
+ // Create the processor and process the primitives
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(*m_xVirDev, aNewViewInfos));
+
+ pProcessor->process(aGhostedSeq);
+
+ m_xMenuButton->set_custom_button(m_xVirDev.get());
+}
+
+static SvxBreak lcl_GetBreakItem(const SwContentFrame* pCnt)
+{
+ SvxBreak eBreak = SvxBreak::NONE;
+ if ( pCnt )
+ {
+ if ( pCnt->IsInTab() )
+ eBreak = pCnt->FindTabFrame()->GetBreakItem().GetBreak();
+ else
+ eBreak = pCnt->GetBreakItem().GetBreak();
+ }
+ return eBreak;
+}
+
+IMPL_LINK(SwPageBreakWin, SelectHdl, const OString&, rIdent, void)
+{
+ SwFrameControlPtr pFrameControl = m_pEditWin->GetFrameControlsManager().GetControl(FrameControlType::PageBreak, m_pFrame);
+
+ m_pLine->execute(rIdent);
+
+ // Only fade if there is more than this temporary shared pointer:
+ // The main reference has been deleted due to a page break removal
+ if (pFrameControl.use_count() > 1)
+ Fade( false );
+}
+
+void SwBreakDashedLine::execute(std::string_view rIdent)
+{
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ // Is there a PageBefore break on this page?
+ SwContentFrame *pCnt = const_cast<SwContentFrame*>(pPageFrame->FindFirstBodyContent());
+ SvxBreak eBreak = lcl_GetBreakItem( pCnt );
+
+ // Also check the previous page - to see if there is a PageAfter break
+ SwContentFrame *pPrevCnt = nullptr;
+ SvxBreak ePrevBreak = SvxBreak::NONE;
+ const SwPageFrame* pPrevPage = static_cast<const SwPageFrame*>(pPageFrame->GetPrev());
+ if ( pPrevPage )
+ {
+ pPrevCnt = const_cast<SwContentFrame*>(pPrevPage->FindLastBodyContent());
+ ePrevBreak = lcl_GetBreakItem( pPrevCnt );
+ }
+
+ if (pCnt && rIdent == "edit")
+ {
+ SwWrtShell& rSh = m_pEditWin->GetView().GetWrtShell();
+ bool bOldLock = rSh.IsViewLocked();
+ rSh.LockView( true );
+
+ // Order of edit detection: first RES_BREAK PageAfter, then RES_BREAK PageBefore/RES_PAGEDESC
+ if ( ePrevBreak == SvxBreak::PageAfter )
+ pCnt = pPrevCnt;
+
+ SwContentNode& rNd = pCnt->IsTextFrame()
+ ? *static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
+
+ if ( pCnt->IsInTab() )
+ {
+ rSh.Push( );
+ rSh.ClearMark();
+
+ rSh.SetSelection( rNd );
+
+ SfxStringItem aItem(m_pEditWin->GetView().GetPool().GetWhich(FN_FORMAT_TABLE_DLG), "textflow");
+ m_pEditWin->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(
+ FN_FORMAT_TABLE_DLG,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aItem });
+
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ else
+ {
+ SwPaM aPaM( rNd );
+ SwPaMItem aPaMItem( m_pEditWin->GetView().GetPool( ).GetWhich( FN_PARAM_PAM ), &aPaM );
+ SfxStringItem aItem( SID_PARA_DLG, "textflow" );
+ m_pEditWin->GetView().GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_PARA_DLG,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aItem, &aPaMItem });
+ }
+ rSh.LockView( bOldLock );
+ m_pEditWin->GrabFocus( );
+ }
+ else if (pCnt && rIdent == "delete")
+ {
+ SwContentNode& rNd = pCnt->IsTextFrame()
+ ? *static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
+
+ rNd.GetDoc().GetIDocumentUndoRedo( ).StartUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr );
+
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> aSet(
+ m_pEditWin->GetView().GetWrtShell().GetAttrPool());
+
+ aSet.Put( SwFormatPageDesc( nullptr ) );
+ // This break could be from the current paragraph, if it has a PageBefore break.
+ if ( eBreak == SvxBreak::PageBefore )
+ aSet.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) );
+
+ rNd.GetDoc().getIDocumentContentOperations().InsertItemSet(
+ SwPaM(rNd), aSet, SetAttrMode::DEFAULT, pPageFrame->getRootFrame());
+
+ // This break could be from the previous paragraph, if it has a PageAfter break.
+ if ( ePrevBreak == SvxBreak::PageAfter )
+ {
+ SwContentNode& rPrevNd = pPrevCnt->IsTextFrame()
+ ? *static_cast<SwTextFrame*>(pPrevCnt)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame*>(pPrevCnt)->GetNode();
+ aSet.ClearItem();
+ aSet.Put( SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK ) );
+ rPrevNd.GetDoc().getIDocumentContentOperations().InsertItemSet(
+ SwPaM(rPrevNd), aSet, SetAttrMode::DEFAULT, pPrevCnt->getRootFrame());
+ }
+
+ rNd.GetDoc().GetIDocumentUndoRedo( ).EndUndo( SwUndoId::UI_DELETE_PAGE_BREAK, nullptr );
+ }
+}
+
+void SwBreakDashedLine::UpdatePosition(const std::optional<Point>& xEvtPt)
+{
+ if ( xEvtPt )
+ {
+ if ( xEvtPt == m_xMousePt )
+ return;
+ m_xMousePt = xEvtPt;
+ }
+
+ const SwPageFrame* pPageFrame = SwFrameMenuButtonBase::GetPageFrame(m_pFrame);
+ const SwFrame* pPrevPage = pPageFrame;
+ do
+ {
+ pPrevPage = pPrevPage->GetPrev();
+ }
+ while ( pPrevPage && ( ( pPrevPage->getFrameArea().Top( ) == pPageFrame->getFrameArea().Top( ) )
+ || static_cast< const SwPageFrame* >( pPrevPage )->IsEmptyPage( ) ) );
+
+ ::tools::Rectangle aBoundRect = GetEditWin()->LogicToPixel( pPageFrame->GetBoundRect(GetEditWin()->GetOutDev()).SVRect() );
+ ::tools::Rectangle aFrameRect = GetEditWin()->LogicToPixel( pPageFrame->getFrameArea().SVRect() );
+
+ tools::Long nYLineOffset = ( aBoundRect.Top() + aFrameRect.Top() ) / 2;
+ if ( pPrevPage )
+ {
+ ::tools::Rectangle aPrevFrameRect = GetEditWin()->LogicToPixel( pPrevPage->getFrameArea().SVRect() );
+ nYLineOffset = ( aPrevFrameRect.Bottom() + aFrameRect.Top() ) / 2;
+ }
+
+ // Get the page + sidebar coords
+ tools::Long nPgLeft = aFrameRect.Left();
+ tools::Long nPgRight = aFrameRect.Right();
+
+ tools::ULong nSidebarWidth = 0;
+ const SwPostItMgr* pPostItMngr = GetEditWin()->GetView().GetWrtShell().GetPostItMgr();
+ if ( pPostItMngr && pPostItMngr->HasNotes() && pPostItMngr->ShowNotes() )
+ nSidebarWidth = pPostItMngr->GetSidebarBorderWidth( true ) + pPostItMngr->GetSidebarWidth( true );
+
+ if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::LEFT )
+ nPgLeft -= nSidebarWidth;
+ else if ( pPageFrame->SidebarPosition( ) == sw::sidebarwindows::SidebarPosition::RIGHT )
+ nPgRight += nSidebarWidth;
+
+ Size aBtnSize( BUTTON_WIDTH + ARROW_WIDTH, BUTTON_HEIGHT );
+
+ // Place the button on the left or right?
+ ::tools::Rectangle aVisArea = GetEditWin()->LogicToPixel( GetEditWin()->GetView().GetVisArea() );
+
+ tools::Long nLineLeft = std::max( nPgLeft, aVisArea.Left() );
+ tools::Long nLineRight = std::min( nPgRight, aVisArea.Right() );
+ tools::Long nBtnLeft = nLineLeft;
+
+ if ( m_xMousePt )
+ {
+ nBtnLeft = nLineLeft + m_xMousePt->X() - aBtnSize.getWidth() / 2;
+
+ if ( nBtnLeft < nLineLeft )
+ nBtnLeft = nLineLeft;
+ else if ( ( nBtnLeft + aBtnSize.getWidth() ) > nLineRight )
+ nBtnLeft = nLineRight - aBtnSize.getWidth();
+ }
+
+ // Set the button position
+ m_aBtnRect = ::tools::Rectangle(Point(nBtnLeft, nYLineOffset - BUTTON_HEIGHT / 2), aBtnSize);
+ if (m_pWin)
+ m_pWin->SetRectanglePixel(m_aBtnRect);
+
+ // Set the line position
+ Point aLinePos( nLineLeft, nYLineOffset - 5 );
+ Size aLineSize( nLineRight - nLineLeft, 10 );
+ SetPosSizePixel(aLinePos, aLineSize);
+}
+
+void SwPageBreakWin::SetRectanglePixel(const ::tools::Rectangle& rRect)
+{
+ SetPosSizePixel(rRect.TopLeft(), rRect.GetSize());
+ m_xVirDev->SetOutputSizePixel(rRect.GetSize());
+}
+
+void SwPageBreakWin::Fade( bool bFadeIn )
+{
+ m_bIsAppearing = bFadeIn;
+ if ( bFadeIn )
+ m_nDelayAppearing = 0;
+
+ if ( !m_bDestroyed && m_aFadeTimer.IsActive( ) )
+ m_aFadeTimer.Stop();
+ if ( !m_bDestroyed )
+ m_aFadeTimer.Start( );
+}
+
+IMPL_LINK(SwPageBreakWin, ToggleHdl, weld::Toggleable&, rMenuButton, void)
+{
+ // hide on dropdown, draw fully unfaded if dropdown before fully faded in
+ Fade(rMenuButton.get_active());
+}
+
+IMPL_LINK_NOARG(SwPageBreakWin, FadeHandler, Timer *, void)
+{
+ const int TICKS_BEFORE_WE_APPEAR = 10;
+ if ( m_bIsAppearing && m_nDelayAppearing < TICKS_BEFORE_WE_APPEAR )
+ {
+ ++m_nDelayAppearing;
+ m_aFadeTimer.Start();
+ return;
+ }
+
+ if ( m_bIsAppearing && m_nFadeRate > 0 )
+ m_nFadeRate -= 25;
+ else if ( !m_bIsAppearing && m_nFadeRate < 100 )
+ m_nFadeRate += 25;
+
+ if ( m_nFadeRate != 100 && !IsVisible() )
+ Show();
+ else if ( m_nFadeRate == 100 && IsVisible( ) )
+ {
+ Hide();
+ m_pLine->DestroyWin();
+ return;
+ }
+ else
+ {
+ m_pLine->UpdatePosition();
+ PaintButton();
+ }
+
+ if (IsVisible( ) && m_nFadeRate > 0 && m_nFadeRate < 100)
+ m_aFadeTimer.Start();
+}
+
+FactoryFunction SwBreakDashedLine::GetUITestFactory() const
+{
+ return PageBreakUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/PostItMgr.cxx b/sw/source/uibase/docvw/PostItMgr.cxx
new file mode 100644
index 000000000..56daa35f7
--- /dev/null
+++ b/sw/source/uibase/docvw/PostItMgr.cxx
@@ -0,0 +1,2518 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <PostItMgr.hxx>
+#include <postithelper.hxx>
+
+#include <AnnotationWin.hxx>
+#include "frmsidebarwincontainer.hxx"
+#include <accmap.hxx>
+
+#include <SidebarWindowsConsts.hxx>
+#include "AnchorOverlayObject.hxx"
+#include "ShadowOverlayObject.hxx"
+
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+
+#include <chrdlgmodes.hxx>
+#include <viewopt.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <docufld.hxx>
+#include <edtwin.hxx>
+#include <txtfld.hxx>
+#include <txtannotationfld.hxx>
+#include <rootfrm.hxx>
+#include <SwRewriter.hxx>
+#include <tools/color.hxx>
+#include <unotools/datetime.hxx>
+
+#include <swmodule.hxx>
+#include <strings.hrc>
+#include <cmdid.h>
+
+#include <sfx2/request.hxx>
+#include <sfx2/event.hxx>
+#include <svl/srchitem.hxx>
+
+#include <svl/languageoptions.hxx>
+#include <svl/hint.hxx>
+
+#include <svx/svdview.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <annotsh.hxx>
+#include <swabstdlg.hxx>
+#include <memory>
+
+// distance between Anchor Y and initial note position
+#define POSTIT_INITIAL_ANCHOR_DISTANCE 20
+//distance between two postits
+#define POSTIT_SPACE_BETWEEN 8
+#define POSTIT_MINIMUMSIZE_WITH_META 60
+#define POSTIT_SCROLL_SIDEBAR_HEIGHT 20
+
+// if we layout more often we stop, this should never happen
+#define MAX_LOOP_COUNT 50
+
+using namespace sw::sidebarwindows;
+using namespace sw::annotation;
+
+namespace {
+
+ enum class CommentNotificationType { Add, Remove, Modify, Resolve };
+
+ bool comp_pos(const std::unique_ptr<SwSidebarItem>& a, const std::unique_ptr<SwSidebarItem>& b)
+ {
+ // sort by anchor position
+ SwPosition aPosAnchorA = a->GetAnchorPosition();
+ SwPosition aPosAnchorB = b->GetAnchorPosition();
+
+ bool aAnchorAInFooter = false;
+ bool aAnchorBInFooter = false;
+
+ // is the anchor placed in Footnote or the Footer?
+ if( aPosAnchorA.nNode.GetNode().FindFootnoteStartNode() || aPosAnchorA.nNode.GetNode().FindFooterStartNode() )
+ aAnchorAInFooter = true;
+ if( aPosAnchorB.nNode.GetNode().FindFootnoteStartNode() || aPosAnchorB.nNode.GetNode().FindFooterStartNode() )
+ aAnchorBInFooter = true;
+
+ // fdo#34800
+ // if AnchorA is in footnote, and AnchorB isn't
+ // we do not want to change over the position
+ if( aAnchorAInFooter && !aAnchorBInFooter )
+ return false;
+ // if aAnchorA is not placed in a footnote, and aAnchorB is
+ // force a change over
+ else if( !aAnchorAInFooter && aAnchorBInFooter )
+ return true;
+ // If neither or both are in the footer, compare the positions.
+ // Since footnotes are in Inserts section of nodes array and footers
+ // in Autotext section, all footnotes precede any footers so no need
+ // to check that.
+ else
+ return aPosAnchorA < aPosAnchorB;
+ }
+
+ /// Emits LOK notification about one addition/removal/change of a comment
+ void lcl_CommentNotification(const SwView* pView, const CommentNotificationType nType, const SwSidebarItem* pItem, const sal_uInt32 nPostItId)
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ boost::property_tree::ptree aAnnotation;
+ aAnnotation.put("action", (nType == CommentNotificationType::Add ? "Add" :
+ (nType == CommentNotificationType::Remove ? "Remove" :
+ (nType == CommentNotificationType::Modify ? "Modify" :
+ (nType == CommentNotificationType::Resolve ? "Resolve" : "???")))));
+ aAnnotation.put("id", nPostItId);
+ if (nType != CommentNotificationType::Remove && pItem != nullptr)
+ {
+ sw::annotation::SwAnnotationWin* pWin = pItem->mpPostIt.get();
+
+ const SwPostItField* pField = pWin->GetPostItField();
+ const SwRect& aRect = pWin->GetAnchorRect();
+ tools::Rectangle aSVRect(aRect.Pos().getX(),
+ aRect.Pos().getY(),
+ aRect.Pos().getX() + aRect.SSize().Width(),
+ aRect.Pos().getY() + aRect.SSize().Height());
+
+ if (!pItem->maLayoutInfo.mPositionFromCommentAnchor)
+ {
+ // Comments on frames: anchor position is the corner position, not the whole frame.
+ aSVRect.SetSize(Size(0, 0));
+ }
+
+ std::vector<OString> aRects;
+ for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges())
+ {
+ const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight());
+ aRects.push_back(rect.SVRect().toString());
+ }
+ const OString sRects = comphelper::string::join("; ", aRects);
+
+ aAnnotation.put("id", pField->GetPostItId());
+ aAnnotation.put("parent", pWin->CalcParent());
+ aAnnotation.put("author", pField->GetPar1().toUtf8().getStr());
+ aAnnotation.put("text", pField->GetPar2().toUtf8().getStr());
+ aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false");
+ aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
+ aAnnotation.put("anchorPos", aSVRect.toString());
+ aAnnotation.put("textRange", sRects.getStr());
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.add_child("comment", aAnnotation);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ std::string aPayload = aStream.str();
+
+ if (pView)
+ {
+ pView->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str());
+ }
+ }
+
+} // anonymous namespace
+
+SwPostItMgr::SwPostItMgr(SwView* pView)
+ : mpView(pView)
+ , mpWrtShell(mpView->GetDocShell()->GetWrtShell())
+ , mpEditWin(&mpView->GetEditWin())
+ , mnEventId(nullptr)
+ , mbWaitingForCalcRects(false)
+ , mpActivePostIt(nullptr)
+ , mbLayout(false)
+ , mbLayoutHeight(0)
+ , mbLayouting(false)
+ , mbReadOnly(mpView->GetDocShell()->IsReadOnly())
+ , mbDeleteNote(true)
+ , mbIsShowAnchor( false )
+{
+ if(!mpView->GetDrawView() )
+ mpView->GetWrtShell().MakeDrawView();
+
+ SwNoteProps aProps;
+ mbIsShowAnchor = aProps.IsShowAnchor();
+
+ //make sure we get the colour yellow always, even if not the first one of comments or redlining
+ SW_MOD()->GetRedlineAuthor();
+
+ // collect all PostIts and redline comments that exist after loading the document
+ // don't check for existence for any of them, don't focus them
+ AddPostIts(false,false);
+ /* this code can be used once we want redline comments in the Sidebar
+ AddRedlineComments(false,false);
+ */
+ // we want to receive stuff like SfxHintId::DocChanged
+ StartListening(*mpView->GetDocShell());
+ if (!mvPostItFields.empty())
+ {
+ mbWaitingForCalcRects = true;
+ mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
+ }
+}
+
+SwPostItMgr::~SwPostItMgr()
+{
+ if ( mnEventId )
+ Application::RemoveUserEvent( mnEventId );
+ // forget about all our Sidebar windows
+ RemoveSidebarWin();
+ EndListening( *mpView->GetDocShell() );
+
+ mPages.clear();
+}
+
+bool SwPostItMgr::CheckForRemovedPostIts()
+{
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ bool bRemoved = false;
+ auto it = mvPostItFields.begin();
+ while(it != mvPostItFields.end())
+ {
+ if (!(*it)->UseElement(*mpWrtShell->GetLayout(), rIDRA))
+ {
+ EndListening(const_cast<SfxBroadcaster&>(*(*it)->GetBroadcaster()));
+ std::unique_ptr<SwSidebarItem> p = std::move(*it);
+ it = mvPostItFields.erase(it);
+ if (GetActiveSidebarWin() == p->mpPostIt)
+ SetActiveSidebarWin(nullptr);
+ p->mpPostIt.disposeAndClear();
+ bRemoved = true;
+ }
+ else
+ ++it;
+ }
+
+ if ( !bRemoved )
+ return false;
+
+ // make sure that no deleted items remain in page lists
+ // todo: only remove deleted ones?!
+ if ( mvPostItFields.empty() )
+ {
+ PreparePageContainer();
+ PrepareView();
+ }
+ else
+ {
+ // if postits are there make sure that page lists are not empty
+ // otherwise sudden paints can cause pain (in BorderOverPageBorder)
+ CalcRects();
+ }
+
+ return true;
+}
+
+SwSidebarItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus)
+{
+ if (bCheckExistence)
+ {
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( postItField->GetBroadcaster() == pItem )
+ return nullptr;
+ }
+ }
+ mbLayout = bFocus;
+
+ SwSidebarItem* pAnnotationItem = nullptr;
+ if (auto pSwFormatField = dynamic_cast< SwFormatField *>( pItem ))
+ {
+ mvPostItFields.push_back(std::make_unique<SwAnnotationItem>(*pSwFormatField, bFocus));
+ pAnnotationItem = mvPostItFields.back().get();
+ }
+ assert(dynamic_cast< const SwFormatField *>( pItem ) && "Mgr::InsertItem: seems like new stuff was added");
+ StartListening(*pItem);
+ return pAnnotationItem;
+}
+
+void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast )
+{
+ EndListening(*pBroadcast);
+ auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
+ [&pBroadcast](const std::unique_ptr<SwSidebarItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
+ if (i != mvPostItFields.end())
+ {
+ std::unique_ptr<SwSidebarItem> p = std::move(*i);
+ // tdf#120487 remove from list before dispose, so comment window
+ // won't be recreated due to the entry still in the list if focus
+ // transferring from the pPostIt triggers relayout of postits
+ // tdf#133348 remove from list before calling SetActiveSidebarWin
+ // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr
+ mvPostItFields.erase(i);
+ if (GetActiveSidebarWin() == p->mpPostIt)
+ SetActiveSidebarWin(nullptr);
+ p->mpPostIt.disposeAndClear();
+ }
+ mbLayout = true;
+ PrepareView();
+}
+
+void SwPostItMgr::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( const SfxEventHint* pSfxEventHint = dynamic_cast<const SfxEventHint*>(&rHint) )
+ {
+ if ( pSfxEventHint->GetEventId() == SfxEventHintId::SwEventLayoutFinished )
+ {
+ if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
+ {
+ mbWaitingForCalcRects = true;
+ mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
+ }
+ }
+ }
+ else if ( const SwFormatFieldHint * pFormatHint = dynamic_cast<const SwFormatFieldHint*>(&rHint) )
+ {
+ SwFormatField* pField = const_cast <SwFormatField*>( pFormatHint->GetField() );
+ switch ( pFormatHint->Which() )
+ {
+ case SwFormatFieldHintWhich::INSERTED :
+ {
+ if (!pField)
+ {
+ AddPostIts();
+ break;
+ }
+ // get field to be inserted from hint
+ if ( pField->IsFieldInDoc() )
+ {
+ bool bEmpty = !HasNotes();
+ SwSidebarItem* pItem = InsertItem( pField, true, false );
+
+ if (bEmpty && !mvPostItFields.empty())
+ PrepareView(true);
+
+ // True until the layout of this post it finishes
+ if (pItem)
+ pItem->mbPendingLayout = true;
+ }
+ else
+ {
+ OSL_FAIL("Inserted field not in document!" );
+ }
+ break;
+ }
+ case SwFormatFieldHintWhich::REMOVED:
+ {
+ if (mbDeleteNote)
+ {
+ if (!pField)
+ {
+ const bool bWasRemoved = CheckForRemovedPostIts();
+ // tdf#143643 ensure relayout on undo of insert comment
+ if (bWasRemoved)
+ mbLayout = true;
+ break;
+ }
+ RemoveItem(pField);
+
+ // If LOK has disabled tiled annotations, emit annotation callbacks
+ if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
+ {
+ SwPostItField* pPostItField = static_cast<SwPostItField*>(pField->GetField());
+ lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, pPostItField->GetPostItId());
+ }
+ }
+ break;
+ }
+ case SwFormatFieldHintWhich::FOCUS:
+ {
+ if (pFormatHint->GetView()== mpView)
+ Focus(rBC);
+ break;
+ }
+ case SwFormatFieldHintWhich::CHANGED:
+ case SwFormatFieldHintWhich::RESOLVED:
+ {
+ SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC);
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( pFormatField == postItField->GetBroadcaster() )
+ {
+ if (postItField->mpPostIt)
+ {
+ postItField->mpPostIt->SetPostItText();
+ mbLayout = true;
+ }
+
+ // If LOK has disabled tiled annotations, emit annotation callbacks
+ if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
+ {
+ if(SwFormatFieldHintWhich::CHANGED == pFormatHint->Which())
+ lcl_CommentNotification(mpView, CommentNotificationType::Modify, postItField.get(), 0);
+ else
+ lcl_CommentNotification(mpView, CommentNotificationType::Resolve, postItField.get(), 0);
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case SwFormatFieldHintWhich::LANGUAGE:
+ {
+ SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC);
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( pFormatField == postItField->GetBroadcaster() )
+ {
+ if (postItField->mpPostIt)
+ {
+ const SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( postItField->GetFormatField().GetField()->GetLanguage() );
+ sal_uInt16 nLangWhichId = 0;
+ switch (nScriptType)
+ {
+ case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE ; break;
+ case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
+ default: break;
+ }
+ postItField->mpPostIt->SetLanguage(
+ SvxLanguageItem(
+ postItField->GetFormatField().GetField()->GetLanguage(),
+ nLangWhichId) );
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ SfxHintId nId = rHint.GetId();
+ switch ( nId )
+ {
+ case SfxHintId::ModeChanged:
+ {
+ if ( mbReadOnly != mpView->GetDocShell()->IsReadOnly() )
+ {
+ mbReadOnly = !mbReadOnly;
+ SetReadOnlyState();
+ mbLayout = true;
+ }
+ break;
+ }
+ case SfxHintId::DocChanged:
+ {
+ if ( mpView->GetDocShell() == &rBC )
+ {
+ if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
+ {
+ mbWaitingForCalcRects = true;
+ mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
+ }
+ }
+ break;
+ }
+ case SfxHintId::SwSplitNodeOperation:
+ {
+ // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker
+ mbDeleteNote = !mbDeleteNote;
+ break;
+ }
+ case SfxHintId::Dying:
+ {
+ if ( mpView->GetDocShell() != &rBC )
+ {
+ // field to be removed is the broadcaster
+ OSL_FAIL("Notification for removed SwFormatField was not sent!");
+ RemoveItem(&rBC);
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+}
+
+void SwPostItMgr::Focus(const SfxBroadcaster& rBC)
+{
+ if (!mpWrtShell->GetViewOptions()->IsPostIts())
+ {
+ SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_NOTES);
+ mpView->ExecViewOptions(aRequest);
+ }
+
+ for (auto const& postItField : mvPostItFields)
+ {
+ // field to get the focus is the broadcaster
+ if ( &rBC == postItField->GetBroadcaster() )
+ {
+ if (postItField->mpPostIt)
+ {
+ if (postItField->mpPostIt->IsResolved() &&
+ !mpWrtShell->GetViewOptions()->IsResolvedPostIts())
+ {
+ SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_RESOLVED_NOTES);
+ mpView->ExecViewOptions(aRequest);
+ }
+ postItField->mpPostIt->GrabFocus();
+ MakeVisible(postItField->mpPostIt);
+ }
+ else
+ {
+ // when the layout algorithm starts, this postit is created and receives focus
+ postItField->mbFocus = true;
+ }
+ }
+ }
+}
+
+bool SwPostItMgr::CalcRects()
+{
+ if ( mnEventId )
+ {
+ // if CalcRects() was forced and an event is still pending: remove it
+ // it is superfluous and also may cause reentrance problems if triggered while layouting
+ Application::RemoveUserEvent( mnEventId );
+ mnEventId = nullptr;
+ }
+
+ bool bChange = false;
+ bool bRepair = false;
+ PreparePageContainer();
+ if ( !mvPostItFields.empty() )
+ {
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ for (auto const& pItem : mvPostItFields)
+ {
+ if (!pItem->UseElement(*mpWrtShell->GetLayout(), rIDRA))
+ {
+ OSL_FAIL("PostIt is not in doc or other wrong use");
+ bRepair = true;
+ continue;
+ }
+ const SwRect aOldAnchorRect( pItem->maLayoutInfo.mPosition );
+ const SwPostItHelper::SwLayoutStatus eOldLayoutStatus = pItem->mLayoutStatus;
+ const SwNodeOffset nOldStartNodeIdx( pItem->maLayoutInfo.mnStartNodeIdx );
+ const sal_Int32 nOldStartContent( pItem->maLayoutInfo.mnStartContent );
+ {
+ // update layout information
+ const SwTextAnnotationField* pTextAnnotationField =
+ dynamic_cast< const SwTextAnnotationField* >( pItem->GetFormatField().GetTextField() );
+ const ::sw::mark::IMark* pAnnotationMark =
+ pTextAnnotationField != nullptr ? pTextAnnotationField->GetAnnotationMark() : nullptr;
+ if ( pAnnotationMark != nullptr )
+ {
+ pItem->mLayoutStatus =
+ SwPostItHelper::getLayoutInfos(
+ pItem->maLayoutInfo,
+ pItem->GetAnchorPosition(),
+ pAnnotationMark );
+ }
+ else
+ {
+ pItem->mLayoutStatus =
+ SwPostItHelper::getLayoutInfos( pItem->maLayoutInfo, pItem->GetAnchorPosition() );
+ }
+ }
+ bChange = bChange
+ || pItem->maLayoutInfo.mPosition != aOldAnchorRect
+ || pItem->mLayoutStatus != eOldLayoutStatus
+ || pItem->maLayoutInfo.mnStartNodeIdx != nOldStartNodeIdx
+ || pItem->maLayoutInfo.mnStartContent != nOldStartContent;
+ }
+
+ // show notes in right order in navigator
+ //prevent Anchors during layout to overlap, e.g. when moving a frame
+ if (mvPostItFields.size()>1 )
+ std::stable_sort(mvPostItFields.begin(), mvPostItFields.end(), comp_pos);
+
+ // sort the items into the right page vector, so layout can be done by page
+ for (auto const& pItem : mvPostItFields)
+ {
+ if( SwPostItHelper::INVISIBLE == pItem->mLayoutStatus )
+ {
+ if (pItem->mpPostIt)
+ pItem->mpPostIt->HideNote();
+ continue;
+ }
+
+ if( SwPostItHelper::HIDDEN == pItem->mLayoutStatus )
+ {
+ if (!mpWrtShell->GetViewOptions()->IsShowHiddenChar())
+ {
+ if (pItem->mpPostIt)
+ pItem->mpPostIt->HideNote();
+ continue;
+ }
+ }
+
+ const tools::ULong aPageNum = pItem->maLayoutInfo.mnPageNumber;
+ if (aPageNum > mPages.size())
+ {
+ const tools::ULong nNumberOfPages = mPages.size();
+ mPages.reserve(aPageNum);
+ for (tools::ULong j=0; j<aPageNum - nNumberOfPages; ++j)
+ mPages.emplace_back( new SwPostItPageItem());
+ }
+ mPages[aPageNum-1]->mvSidebarItems.push_back(pItem.get());
+ mPages[aPageNum-1]->mPageRect = pItem->maLayoutInfo.mPageFrame;
+ mPages[aPageNum-1]->eSidebarPosition = pItem->maLayoutInfo.meSidebarPosition;
+ }
+
+ if (!bChange && mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
+ {
+ tools::Long nLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
+ if( nLayoutHeight > mbLayoutHeight )
+ {
+ if (mPages[0]->bScrollbar || HasScrollbars())
+ bChange = true;
+ }
+ else if( nLayoutHeight < mbLayoutHeight )
+ {
+ if (mPages[0]->bScrollbar || !BorderOverPageBorder(1))
+ bChange = true;
+ }
+ }
+ }
+
+ if ( bRepair )
+ CheckForRemovedPostIts();
+
+ mbLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
+ mbWaitingForCalcRects = false;
+ return bChange;
+}
+
+bool SwPostItMgr::HasScrollbars() const
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ if (postItField->mbShow && postItField->mpPostIt && postItField->mpPostIt->HasScrollbar())
+ return true;
+ }
+ return false;
+}
+
+void SwPostItMgr::PreparePageContainer()
+{
+ // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost
+ tools::Long lPageSize = mpWrtShell->GetNumPages();
+ tools::Long lContainerSize = mPages.size();
+
+ if (lContainerSize < lPageSize)
+ {
+ mPages.reserve(lPageSize);
+ for (tools::Long i=0; i<lPageSize - lContainerSize;i++)
+ mPages.emplace_back( new SwPostItPageItem());
+ }
+ else if (lContainerSize > lPageSize)
+ {
+ for (int i=mPages.size()-1; i >= lPageSize;--i)
+ {
+ mPages.pop_back();
+ }
+ }
+ // only clear the list, DO NOT delete the objects itself
+ for (auto const& page : mPages)
+ {
+ page->mvSidebarItems.clear();
+ if (mvPostItFields.empty())
+ page->bScrollbar = false;
+ }
+}
+
+void SwPostItMgr::LayoutPostIts()
+{
+ bool bEnableMapMode = comphelper::LibreOfficeKit::isActive() && !mpEditWin->IsMapModeEnabled();
+ if (bEnableMapMode)
+ mpEditWin->EnableMapMode();
+
+ if ( !mvPostItFields.empty() && !mbWaitingForCalcRects )
+ {
+ mbLayouting = true;
+
+ //loop over all pages and do the layout
+ // - create SwPostIt if necessary
+ // - place SwPostIts on their initial position
+ // - calculate necessary height for all PostIts together
+ bool bUpdate = false;
+ for (std::unique_ptr<SwPostItPageItem>& pPage : mPages)
+ {
+ // only layout if there are notes on this page
+ if (!pPage->mvSidebarItems.empty())
+ {
+ std::vector<SwAnnotationWin*> aVisiblePostItList;
+ tools::ULong lNeededHeight = 0;
+ tools::Long mlPageBorder = 0;
+ tools::Long mlPageEnd = 0;
+
+ for (auto const& pItem : pPage->mvSidebarItems)
+ {
+ VclPtr<SwAnnotationWin> pPostIt = pItem->mpPostIt;
+
+ if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
+ {
+ // x value for notes positioning
+ mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true);
+ //bending point
+ mlPageEnd =
+ mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
+ ? pItem->maLayoutInfo.mPagePrtArea.Left()
+ : pPage->mPageRect.Left() + 350;
+ }
+ else if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
+ {
+ // x value for notes positioning
+ mlPageBorder = mpEditWin->LogicToPixel( Point(pPage->mPageRect.Right(), 0)).X() + GetSidebarBorderWidth(true);
+ //bending point
+ mlPageEnd =
+ mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
+ ? pItem->maLayoutInfo.mPagePrtArea.Right() :
+ pPage->mPageRect.Right() - 350;
+ }
+
+ if (pItem->mbShow)
+ {
+ tools::Long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y();
+ tools::Long aPostItHeight = 0;
+ if (!pPostIt)
+ {
+ pPostIt = pItem->GetSidebarWindow( mpView->GetEditWin(),
+ *this );
+ pPostIt->InitControls();
+ pPostIt->SetReadonly(mbReadOnly);
+ pItem->mpPostIt = pPostIt;
+ if (mpAnswer)
+ {
+ if (static_cast<bool>(pPostIt->CalcParent())) //do we really have another note in front of this one
+ pPostIt->InitAnswer(*mpAnswer);
+ mpAnswer.reset();
+ }
+ }
+
+ pPostIt->SetChangeTracking(
+ pItem->mLayoutStatus,
+ GetColorAnchor(pItem->maLayoutInfo.mRedlineAuthor));
+ pPostIt->SetSidebarPosition(pPage->eSidebarPosition);
+ pPostIt->SetFollow(static_cast<bool>(pPostIt->CalcParent()));
+ aPostItHeight = ( pPostIt->GetPostItTextHeight() < pPostIt->GetMinimumSizeWithoutMeta()
+ ? pPostIt->GetMinimumSizeWithoutMeta()
+ : pPostIt->GetPostItTextHeight() )
+ + pPostIt->GetMetaHeight();
+ pPostIt->SetPosSizePixelRect( mlPageBorder ,
+ Y - GetInitialAnchorDistance(),
+ GetSidebarWidth(true),
+ aPostItHeight,
+ pItem->maLayoutInfo.mPosition,
+ mlPageEnd );
+ pPostIt->ChangeSidebarItem( *pItem );
+
+ if (pItem->mbFocus)
+ {
+ mbLayout = true;
+ pPostIt->GrabFocus();
+ pItem->mbFocus = false;
+ }
+ // only the visible postits are used for the final layout
+ aVisiblePostItList.push_back(pPostIt);
+ lNeededHeight += pPostIt->IsFollow() ? aPostItHeight : aPostItHeight+GetSpaceBetween();
+ }
+ else // we don't want to see it
+ {
+ if (pPostIt)
+ pPostIt->HideNote();
+ }
+ }
+
+ if ((!aVisiblePostItList.empty()) && ShowNotes())
+ {
+ bool bOldScrollbar = pPage->bScrollbar;
+ if (ShowNotes())
+ pPage->bScrollbar = LayoutByPage(aVisiblePostItList, pPage->mPageRect.SVRect(), lNeededHeight);
+ else
+ pPage->bScrollbar = false;
+ if (!pPage->bScrollbar)
+ {
+ pPage->lOffset = 0;
+ }
+ else if (sal_Int32 nScrollSize = GetScrollSize())
+ {
+ //when we changed our zoom level, the offset value can be too big, so lets check for the largest possible zoom value
+ tools::Long aAvailableHeight = mpEditWin->LogicToPixel(Size(0,pPage->mPageRect.Height())).Height() - 2 * GetSidebarScrollerHeight();
+ tools::Long lOffset = -1 * nScrollSize * (aVisiblePostItList.size() - aAvailableHeight / nScrollSize);
+ if (pPage->lOffset < lOffset)
+ pPage->lOffset = lOffset;
+ }
+ bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
+ const tools::Long aSidebarheight = pPage->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
+ /*
+ TODO
+ - enlarge all notes till GetNextBorder(), as we resized to average value before
+ */
+ //lets hide the ones which overlap the page
+ for (auto const& visiblePostIt : aVisiblePostItList)
+ {
+ if (pPage->lOffset != 0)
+ visiblePostIt->TranslateTopPosition(pPage->lOffset);
+
+ bool bBottom = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y()+visiblePostIt->VirtualSize().Height())).Y() <= (pPage->mPageRect.Bottom()-aSidebarheight);
+ bool bTop = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() >= (pPage->mPageRect.Top()+aSidebarheight);
+ if ( bBottom && bTop )
+ {
+ // When tiled rendering, make sure that only the
+ // view that has the comment focus emits callbacks,
+ // so the editing view jumps to the comment, but
+ // not the others.
+ bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting();
+ if (!bTiledPainting)
+ // No focus -> disable callbacks.
+ comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt->HasChildPathFocus());
+ visiblePostIt->ShowNote();
+ if (!bTiledPainting)
+ comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
+ }
+ else
+ {
+ if (mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() < (pPage->mPageRect.Top()+aSidebarheight))
+ {
+ if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
+ visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Left(),
+ pPage->mPageRect.Top()));
+ else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
+ visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Right(),
+ pPage->mPageRect.Top()));
+ }
+ else
+ {
+ if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
+ visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Left(),
+ pPage->mPageRect.Bottom()));
+ else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
+ visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Right(),
+ pPage->mPageRect.Bottom()));
+ }
+ OSL_ENSURE(pPage->bScrollbar,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true");
+ }
+ }
+ }
+ else
+ {
+ for (auto const& visiblePostIt : aVisiblePostItList)
+ {
+ visiblePostIt->SetPosAndSize();
+ }
+
+ bool bOldScrollbar = pPage->bScrollbar;
+ pPage->bScrollbar = false;
+ bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
+ }
+
+ for (auto const& visiblePostIt : aVisiblePostItList)
+ {
+ if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
+ {
+ if (visiblePostIt->GetSidebarItem().mbPendingLayout)
+ lcl_CommentNotification(mpView, CommentNotificationType::Add, &visiblePostIt->GetSidebarItem(), 0);
+ else if (visiblePostIt->IsAnchorRectChanged())
+ {
+ lcl_CommentNotification(mpView, CommentNotificationType::Modify, &visiblePostIt->GetSidebarItem(), 0);
+ visiblePostIt->ResetAnchorRectChanged();
+ }
+ }
+
+ // Layout for this post it finished now
+ visiblePostIt->GetSidebarItem().mbPendingLayout = false;
+ }
+ }
+ else
+ {
+ if (pPage->bScrollbar)
+ bUpdate = true;
+ pPage->bScrollbar = false;
+ }
+ }
+
+ if (!ShowNotes())
+ { // we do not want to see the notes anymore -> Options-Writer-View-Notes
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ bool bRepair = false;
+ for (auto const& postItField : mvPostItFields)
+ {
+ if (!postItField->UseElement(*mpWrtShell->GetLayout(), rIDRA))
+ {
+ OSL_FAIL("PostIt is not in doc!");
+ bRepair = true;
+ continue;
+ }
+
+ if (postItField->mpPostIt)
+ {
+ postItField->mpPostIt->HideNote();
+ if (postItField->mpPostIt->HasChildPathFocus())
+ {
+ SetActiveSidebarWin(nullptr);
+ postItField->mpPostIt->GrabFocusToDocument();
+ }
+ }
+ }
+
+ if ( bRepair )
+ CheckForRemovedPostIts();
+ }
+
+ // notes scrollbar is otherwise not drawn correctly for some cases
+ // scrollbar area is enough
+ if (bUpdate)
+ mpEditWin->Invalidate(); /*This is a super expensive relayout and render of the entire page*/
+
+ mbLayouting = false;
+ }
+
+ if (bEnableMapMode)
+ mpEditWin->EnableMapMode(false);
+}
+
+bool SwPostItMgr::BorderOverPageBorder(tools::ULong aPage) const
+{
+ if ( mPages[aPage-1]->mvSidebarItems.empty() )
+ {
+ OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!");
+ return false;
+ }
+
+ auto aItem = mPages[aPage-1]->mvSidebarItems.end();
+ --aItem;
+ OSL_ENSURE ((*aItem)->mpPostIt,"BorderOverPageBorder: NULL postIt, should never happen");
+ if ((*aItem)->mpPostIt)
+ {
+ const tools::Long aSidebarheight = mPages[aPage-1]->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
+ const tools::Long aEndValue = mpEditWin->PixelToLogic(Point(0,(*aItem)->mpPostIt->GetPosPixel().Y()+(*aItem)->mpPostIt->GetSizePixel().Height())).Y();
+ return aEndValue <= mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight;
+ }
+ else
+ return false;
+}
+
+void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage)
+{
+ assert(nPage < mPages.size());
+ if (nPage >= mPages.size())
+ return;
+ for (auto const& pItem : mPages[nPage]->mvSidebarItems)
+ {
+ SwAnnotationWin* pPostIt = pItem->mpPostIt;
+ if (!pPostIt)
+ continue;
+ Point aPoint(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
+ pPostIt->DrawForPage(pOutDev, aPoint);
+ }
+}
+
+void SwPostItMgr::PaintTile(OutputDevice& rRenderContext)
+{
+ for (const std::unique_ptr<SwSidebarItem>& pItem : mvPostItFields)
+ {
+ SwAnnotationWin* pPostIt = pItem->mpPostIt;
+ if (!pPostIt)
+ continue;
+
+ bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
+ mpEditWin->EnableMapMode();
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ Point aOffset(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
+ MapMode aMapMode(rRenderContext.GetMapMode());
+ aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset);
+ rRenderContext.SetMapMode(aMapMode);
+ Size aSize(rRenderContext.PixelToLogic(pPostIt->GetSizePixel()));
+ tools::Rectangle aRectangle(Point(0, 0), aSize);
+
+ pPostIt->PaintTile(rRenderContext, aRectangle);
+
+ rRenderContext.Pop();
+ if (bEnableMapMode)
+ mpEditWin->EnableMapMode(false);
+ }
+}
+
+void SwPostItMgr::Scroll(const tools::Long lScroll,const tools::ULong aPage)
+{
+ OSL_ENSURE((lScroll % GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value");
+ // do not scroll more than necessary up or down
+ if ( ((mPages[aPage-1]->lOffset == 0) && (lScroll>0)) || ( BorderOverPageBorder(aPage) && (lScroll<0)) )
+ return;
+
+ const bool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage);
+ const bool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage);
+ const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
+ for (auto const& item : mPages[aPage-1]->mvSidebarItems)
+ {
+ SwAnnotationWin* pPostIt = item->mpPostIt;
+ // if this is an answer, we should take the normal position and not the real, slightly moved position
+ pPostIt->SetVirtualPosSize(pPostIt->GetPosPixel(),pPostIt->GetSizePixel());
+ pPostIt->TranslateTopPosition(lScroll);
+
+ if (item->mbShow)
+ {
+ bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y()+pPostIt->VirtualSize().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
+ bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
+ if ( bBottom && bTop)
+ {
+ pPostIt->ShowNote();
+ }
+ else
+ {
+ if ( mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() < (mPages[aPage-1]->mPageRect.Top()+aSidebarheight))
+ {
+ if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
+ pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Top()));
+ else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
+ pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Top()));
+ }
+ else
+ {
+ if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
+ pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Bottom()));
+ else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
+ pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Bottom()));
+ }
+ }
+ }
+ }
+ mPages[aPage-1]->lOffset += lScroll;
+ if ( (bOldUp != ArrowEnabled(KEY_PAGEUP,aPage)) ||(bOldDown != ArrowEnabled(KEY_PAGEDOWN,aPage)) )
+ {
+ mpEditWin->Invalidate(GetBottomScrollRect(aPage));
+ mpEditWin->Invalidate(GetTopScrollRect(aPage));
+ }
+}
+
+void SwPostItMgr::AutoScroll(const SwAnnotationWin* pPostIt,const tools::ULong aPage )
+{
+ // otherwise all notes are visible
+ if (!mPages[aPage-1]->bScrollbar)
+ return;
+
+ const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
+ const bool bBottom = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
+ const bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
+ if ( !(bBottom && bTop))
+ {
+ const tools::Long aDiff = bBottom ? mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Top() + aSidebarheight)).Y() - pPostIt->GetPosPixel().Y() :
+ mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Bottom() - aSidebarheight)).Y() - (pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height());
+ // this just adds the missing value to get the next a* GetScrollSize() after aDiff
+ // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100
+ const auto nScrollSize = GetScrollSize();
+ assert(nScrollSize);
+ const tools::Long lScroll = bBottom ? (aDiff + ( nScrollSize - (aDiff % nScrollSize))) : (aDiff - (nScrollSize + (aDiff % nScrollSize)));
+ Scroll(lScroll, aPage);
+ }
+}
+
+void SwPostItMgr::MakeVisible(const SwAnnotationWin* pPostIt )
+{
+ tools::Long aPage = -1;
+ // we don't know the page yet, lets find it ourselves
+ std::vector<SwPostItPageItem*>::size_type n=0;
+ for (auto const& page : mPages)
+ {
+ for (auto const& item : page->mvSidebarItems)
+ {
+ if (item->mpPostIt==pPostIt)
+ {
+ aPage = n+1;
+ break;
+ }
+ }
+ ++n;
+ }
+ if (aPage!=-1)
+ AutoScroll(pPostIt,aPage);
+ tools::Rectangle aNoteRect (Point(pPostIt->GetPosPixel().X(),pPostIt->GetPosPixel().Y()-5),pPostIt->GetSizePixel());
+ if (!aNoteRect.IsEmpty())
+ mpWrtShell->MakeVisible(SwRect(mpEditWin->PixelToLogic(aNoteRect)));
+}
+
+bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection,tools::ULong aPage) const
+{
+ switch (aDirection)
+ {
+ case KEY_PAGEUP:
+ {
+ return (mPages[aPage-1]->lOffset != 0);
+ }
+ case KEY_PAGEDOWN:
+ {
+ return (!BorderOverPageBorder(aPage));
+ }
+ default: return false;
+ }
+}
+
+Color SwPostItMgr::GetArrowColor(sal_uInt16 aDirection,tools::ULong aPage) const
+{
+ if (ArrowEnabled(aDirection,aPage))
+ {
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ return COL_WHITE;
+ else
+ return COL_NOTES_SIDEPANE_ARROW_ENABLED;
+ }
+ else
+ {
+ return COL_NOTES_SIDEPANE_ARROW_DISABLED;
+ }
+}
+
+bool SwPostItMgr::LayoutByPage(std::vector<SwAnnotationWin*> &aVisiblePostItList, const tools::Rectangle& rBorder, tools::Long lNeededHeight)
+{
+ /*** General layout idea:***/
+ // - if we have space left, we always move the current one up,
+ // otherwise the next one down
+ // - first all notes are resized
+ // - then the real layout starts
+
+ //rBorder is the page rect
+ const tools::Rectangle aBorder = mpEditWin->LogicToPixel(rBorder);
+ tools::Long lTopBorder = aBorder.Top() + 5;
+ tools::Long lBottomBorder = aBorder.Bottom() - 5;
+ const tools::Long lVisibleHeight = lBottomBorder - lTopBorder; //aBorder.GetHeight() ;
+ const size_t nPostItListSize = aVisiblePostItList.size();
+ tools::Long lTranslatePos = 0;
+ bool bScrollbars = false;
+
+ // do all necessary resizings
+ if (nPostItListSize > 0 && lVisibleHeight < lNeededHeight)
+ {
+ // ok, now we have to really resize and adding scrollbars
+ const tools::Long lAverageHeight = (lVisibleHeight - nPostItListSize*GetSpaceBetween()) / nPostItListSize;
+ if (lAverageHeight<GetMinimumSizeWithMeta())
+ {
+ bScrollbars = true;
+ lTopBorder += GetSidebarScrollerHeight() + 10;
+ lBottomBorder -= (GetSidebarScrollerHeight() + 10);
+ for (auto const& visiblePostIt : aVisiblePostItList)
+ visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta()));
+ }
+ else
+ {
+ for (auto const& visiblePostIt : aVisiblePostItList)
+ {
+ if ( visiblePostIt->VirtualSize().getHeight() > lAverageHeight)
+ visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),lAverageHeight));
+ }
+ }
+ }
+
+ //start the real layout so nothing overlaps anymore
+ if (aVisiblePostItList.size()>1)
+ {
+ int loop = 0;
+ bool bDone = false;
+ // if no window is moved anymore we are finished
+ while (!bDone)
+ {
+ loop++;
+ bDone = true;
+ tools::Long lSpaceUsed = lTopBorder + GetSpaceBetween();
+ for(auto i = aVisiblePostItList.begin(); i != aVisiblePostItList.end() ; ++i)
+ {
+ auto aNextPostIt = i;
+ ++aNextPostIt;
+
+ if (aNextPostIt != aVisiblePostItList.end())
+ {
+ lTranslatePos = ( (*i)->VirtualPos().Y() + (*i)->VirtualSize().Height()) - (*aNextPostIt)->VirtualPos().Y();
+ if (lTranslatePos > 0) // note windows overlaps the next one
+ {
+ // we are not done yet, loop at least once more
+ bDone = false;
+ // if there is space left, move the current note up
+ // it could also happen that there is no space left for the first note due to a scrollbar
+ // then we also jump into, so we move the current one up and the next one down
+ if ( (lSpaceUsed <= (*i)->VirtualPos().Y()) || (i==aVisiblePostItList.begin()))
+ {
+ // we have space left, so let's move the current one up
+ if ( ((*i)->VirtualPos().Y()- lTranslatePos - GetSpaceBetween()) > lTopBorder)
+ {
+ if ((*aNextPostIt)->IsFollow())
+ (*i)->TranslateTopPosition(-1*(lTranslatePos+ANCHORLINE_WIDTH));
+ else
+ (*i)->TranslateTopPosition(-1*(lTranslatePos+GetSpaceBetween()));
+ }
+ else
+ {
+ tools::Long lMoveUp = (*i)->VirtualPos().Y() - lTopBorder;
+ (*i)->TranslateTopPosition(-1* lMoveUp);
+ if ((*aNextPostIt)->IsFollow())
+ (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+ANCHORLINE_WIDTH) - lMoveUp);
+ else
+ (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+GetSpaceBetween()) - lMoveUp);
+ }
+ }
+ else
+ {
+ // no space left, left move the next one down
+ if ((*aNextPostIt)->IsFollow())
+ (*aNextPostIt)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
+ else
+ (*aNextPostIt)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
+ }
+ }
+ else
+ {
+ // the first one could overlap the topborder instead of a second note
+ if (i==aVisiblePostItList.begin())
+ {
+ tools::Long lMoveDown = lTopBorder - (*i)->VirtualPos().Y();
+ if (lMoveDown>0)
+ {
+ bDone = false;
+ (*i)->TranslateTopPosition( lMoveDown);
+ }
+ }
+ }
+ if ( (*aNextPostIt)->IsFollow() )
+ lSpaceUsed += (*i)->VirtualSize().Height() + ANCHORLINE_WIDTH;
+ else
+ lSpaceUsed += (*i)->VirtualSize().Height() + GetSpaceBetween();
+ }
+ else
+ {
+ //(*i) is the last visible item
+ auto aPrevPostIt = i;
+ --aPrevPostIt;
+ lTranslatePos = ( (*aPrevPostIt)->VirtualPos().Y() + (*aPrevPostIt)->VirtualSize().Height() ) - (*i)->VirtualPos().Y();
+ if (lTranslatePos > 0)
+ {
+ bDone = false;
+ if ( ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()+lTranslatePos) < lBottomBorder)
+ {
+ if ( (*i)->IsFollow() )
+ (*i)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
+ else
+ (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
+ }
+ else
+ {
+ (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()) );
+ }
+ }
+ else
+ {
+ // note does not overlap, but we might be over the lower border
+ // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border
+ if (!bScrollbars && ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height() > lBottomBorder) )
+ {
+ bDone = false;
+ (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()));
+ }
+ }
+ }
+ }
+ // security check so we don't loop forever
+ if (loop>MAX_LOOP_COUNT)
+ {
+ OSL_FAIL("PostItMgr::Layout(): We are looping forever");
+ break;
+ }
+ }
+ }
+ else
+ {
+ // only one left, make sure it is not hidden at the top or bottom
+ auto i = aVisiblePostItList.begin();
+ lTranslatePos = lTopBorder - (*i)->VirtualPos().Y();
+ if (lTranslatePos>0)
+ {
+ (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
+ }
+ lTranslatePos = lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height());
+ if (lTranslatePos<0)
+ {
+ (*i)->TranslateTopPosition(lTranslatePos);
+ }
+ }
+ return bScrollbars;
+ }
+
+void SwPostItMgr::AddPostIts(const bool bCheckExistence, const bool bFocus)
+{
+ const bool bEmpty = mvPostItFields.empty();
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ SwFieldType* pType = mpView->GetDocShell()->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(),false);
+ std::vector<SwFormatField*> vFormatFields;
+ pType->CollectPostIts(vFormatFields, rIDRA, mpWrtShell->GetLayout()->IsHideRedlines());
+ for(auto pFormatField : vFormatFields)
+ InsertItem(pFormatField, bCheckExistence, bFocus);
+ // if we just added the first one we have to update the view for centering
+ if (bEmpty && !mvPostItFields.empty())
+ PrepareView(true);
+}
+
+void SwPostItMgr::RemoveSidebarWin()
+{
+ for (auto& postItField : mvPostItFields)
+ {
+ EndListening( *const_cast<SfxBroadcaster*>(postItField->GetBroadcaster()) );
+ postItField->mpPostIt.disposeAndClear();
+ postItField.reset();
+ }
+ mvPostItFields.clear();
+
+ // all postits removed, no items should be left in pages
+ PreparePageContainer();
+}
+
+namespace {
+
+class FilterFunctor
+{
+public:
+ virtual bool operator()(const SwFormatField* pField) const = 0;
+ virtual ~FilterFunctor() {}
+};
+
+class IsPostitField : public FilterFunctor
+{
+public:
+ bool operator()(const SwFormatField* pField) const override
+ {
+ return pField->GetField()->GetTyp()->Which() == SwFieldIds::Postit;
+ }
+};
+
+class IsPostitFieldWithAuthorOf : public FilterFunctor
+{
+ OUString m_sAuthor;
+public:
+ explicit IsPostitFieldWithAuthorOf(const OUString &rAuthor)
+ : m_sAuthor(rAuthor)
+ {
+ }
+ bool operator()(const SwFormatField* pField) const override
+ {
+ if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
+ return false;
+ return static_cast<const SwPostItField*>(pField->GetField())->GetPar1() == m_sAuthor;
+ }
+};
+
+class IsPostitFieldWithPostitId : public FilterFunctor
+{
+ sal_uInt32 m_nPostItId;
+public:
+ explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId)
+ : m_nPostItId(nPostItId)
+ {}
+
+ bool operator()(const SwFormatField* pField) const override
+ {
+ if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
+ return false;
+ return static_cast<const SwPostItField*>(pField->GetField())->GetPostItId() == m_nPostItId;
+ }
+};
+
+class IsFieldNotDeleted : public FilterFunctor
+{
+private:
+ IDocumentRedlineAccess const& m_rIDRA;
+ FilterFunctor const& m_rNext;
+
+public:
+ IsFieldNotDeleted(IDocumentRedlineAccess const& rIDRA,
+ const FilterFunctor & rNext)
+ : m_rIDRA(rIDRA)
+ , m_rNext(rNext)
+ {
+ }
+ bool operator()(const SwFormatField* pField) const override
+ {
+ if (!m_rNext(pField))
+ return false;
+ if (!pField->GetTextField())
+ return false;
+ return !sw::IsFieldDeletedInModel(m_rIDRA, *pField->GetTextField());
+ }
+};
+
+//Manages the passed in vector by automatically removing entries if they are deleted
+//and automatically adding entries if they appear in the document and match the
+//functor.
+//
+//This will completely refill in the case of a "anonymous" NULL pField stating
+//rather unhelpfully that "something changed" so you may process the same
+//Fields more than once.
+class FieldDocWatchingStack : public SfxListener
+{
+ std::vector<std::unique_ptr<SwSidebarItem>>& m_aSidebarItems;
+ std::vector<const SwFormatField*> m_aFormatFields;
+ SwDocShell& m_rDocShell;
+ FilterFunctor& m_rFilter;
+
+ virtual void Notify(SfxBroadcaster&, const SfxHint& rHint) override
+ {
+ const SwFormatFieldHint* pHint = dynamic_cast<const SwFormatFieldHint*>(&rHint);
+ if (!pHint)
+ return;
+
+ bool bAllInvalidated = false;
+ if (pHint->Which() == SwFormatFieldHintWhich::REMOVED)
+ {
+ const SwFormatField* pField = pHint->GetField();
+ bAllInvalidated = pField == nullptr;
+ if (!bAllInvalidated && m_rFilter(pField))
+ {
+ EndListening(const_cast<SwFormatField&>(*pField));
+ m_aFormatFields.erase(std::remove(m_aFormatFields.begin(), m_aFormatFields.end(), pField), m_aFormatFields.end());
+ }
+ }
+ else if (pHint->Which() == SwFormatFieldHintWhich::INSERTED)
+ {
+ const SwFormatField* pField = pHint->GetField();
+ bAllInvalidated = pField == nullptr;
+ if (!bAllInvalidated && m_rFilter(pField))
+ {
+ StartListening(const_cast<SwFormatField&>(*pField));
+ m_aFormatFields.push_back(pField);
+ }
+ }
+
+ if (bAllInvalidated)
+ FillVector();
+
+ return;
+ }
+
+public:
+ FieldDocWatchingStack(std::vector<std::unique_ptr<SwSidebarItem>>& in, SwDocShell &rDocShell, FilterFunctor& rFilter)
+ : m_aSidebarItems(in)
+ , m_rDocShell(rDocShell)
+ , m_rFilter(rFilter)
+ {
+ FillVector();
+ StartListening(m_rDocShell);
+ }
+ void FillVector()
+ {
+ EndListeningToAllFields();
+ m_aFormatFields.clear();
+ m_aFormatFields.reserve(m_aSidebarItems.size());
+ for (auto const& p : m_aSidebarItems)
+ {
+ const SwFormatField& rField = p->GetFormatField();
+ if (!m_rFilter(&rField))
+ continue;
+ StartListening(const_cast<SwFormatField&>(rField));
+ m_aFormatFields.push_back(&rField);
+ }
+ }
+ void EndListeningToAllFields()
+ {
+ for (auto const& pField : m_aFormatFields)
+ {
+ EndListening(const_cast<SwFormatField&>(*pField));
+ }
+ }
+ virtual ~FieldDocWatchingStack() override
+ {
+ EndListeningToAllFields();
+ EndListening(m_rDocShell);
+ }
+ const SwFormatField* pop()
+ {
+ if (m_aFormatFields.empty())
+ return nullptr;
+ const SwFormatField* p = m_aFormatFields.back();
+ EndListening(const_cast<SwFormatField&>(*p));
+ m_aFormatFields.pop_back();
+ return p;
+ }
+};
+
+}
+
+// copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well
+// RemoveItem will clean up the core field and visible postit if necessary
+// we cannot just delete everything as before, as postits could move into change tracking
+void SwPostItMgr::Delete(const OUString& rAuthor)
+{
+ mpWrtShell->StartAllAction();
+ if (HasActiveSidebarWin() && (GetActiveSidebarWin()->GetAuthor() == rAuthor))
+ {
+ SetActiveSidebarWin(nullptr);
+ }
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_AUTHOR_NOTES) + rAuthor);
+ mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
+
+ IsPostitFieldWithAuthorOf aFilter(rAuthor);
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ IsFieldNotDeleted aFilter2(rIDRA, aFilter);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
+ while (const SwFormatField* pField = aStack.pop())
+ {
+ if (mpWrtShell->GotoField(*pField))
+ mpWrtShell->DelRight();
+ }
+ mpWrtShell->EndUndo();
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::Delete(sal_uInt32 nPostItId)
+{
+ mpWrtShell->StartAllAction();
+ if (HasActiveSidebarWin() &&
+ mpActivePostIt->GetPostItField()->GetPostItId() == nPostItId)
+ {
+ SetActiveSidebarWin(nullptr);
+ }
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
+ mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
+
+ IsPostitFieldWithPostitId aFilter(nPostItId);
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ IsFieldNotDeleted aFilter2(rIDRA, aFilter);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
+ const SwFormatField* pField = aStack.pop();
+ if (pField && mpWrtShell->GotoField(*pField))
+ mpWrtShell->DelRight();
+ mpWrtShell->EndUndo();
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::DeleteCommentThread(sal_uInt32 nPostItId)
+{
+ mpWrtShell->StartAllAction();
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
+
+ // We have no undo ID at the moment.
+
+ IsPostitFieldWithPostitId aFilter(nPostItId);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
+ const SwFormatField* pField = aStack.pop();
+ // pField now contains our AnnotationWin object
+ if (pField) {
+ SwAnnotationWin* pWin = GetSidebarWin(pField);
+ pWin->DeleteThread();
+ }
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::ToggleResolved(sal_uInt32 nPostItId)
+{
+ mpWrtShell->StartAllAction();
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
+
+ // We have no undo ID at the moment.
+
+ IsPostitFieldWithPostitId aFilter(nPostItId);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
+ const SwFormatField* pField = aStack.pop();
+ // pField now contains our AnnotationWin object
+ if (pField) {
+ SwAnnotationWin* pWin = GetSidebarWin(pField);
+ pWin->ToggleResolved();
+ }
+
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::ToggleResolvedForThread(sal_uInt32 nPostItId)
+{
+ mpWrtShell->StartAllAction();
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
+
+ // We have no undo ID at the moment.
+
+ IsPostitFieldWithPostitId aFilter(nPostItId);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
+ const SwFormatField* pField = aStack.pop();
+ // pField now contains our AnnotationWin object
+ if (pField) {
+ SwAnnotationWin* pWin = GetSidebarWin(pField);
+ pWin->ToggleResolvedForThread();
+ }
+
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+
+void SwPostItMgr::Delete()
+{
+ mpWrtShell->StartAllAction();
+ SetActiveSidebarWin(nullptr);
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_ALL_NOTES) );
+ mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
+
+ IsPostitField aFilter;
+ IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
+ IsFieldNotDeleted aFilter2(rIDRA, aFilter);
+ FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(),
+ aFilter2);
+ while (const SwFormatField* pField = aStack.pop())
+ {
+ if (mpWrtShell->GotoField(*pField))
+ mpWrtShell->DelRight();
+ }
+
+ mpWrtShell->EndUndo();
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::ExecuteFormatAllDialog(SwView& rView)
+{
+ if (mvPostItFields.empty())
+ return;
+ sw::annotation::SwAnnotationWin *pOrigActiveWin = GetActiveSidebarWin();
+ sw::annotation::SwAnnotationWin *pWin = pOrigActiveWin;
+ if (!pWin)
+ {
+ for (auto const& postItField : mvPostItFields)
+ {
+ pWin = postItField->mpPostIt;
+ if (pWin)
+ break;
+ }
+ }
+ if (!pWin)
+ return;
+ SetActiveSidebarWin(pWin);
+ OutlinerView* pOLV = pWin->GetOutlinerView();
+ SfxItemSet aEditAttr(pOLV->GetAttribs());
+ SfxItemPool* pPool(SwAnnotationShell::GetAnnotationPool(rView));
+ SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END> aDlgAttr(*pPool);
+ aDlgAttr.Put(aEditAttr);
+ SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(rView.GetFrameWeld(), rView, aDlgAttr, SwCharDlgMode::Ann));
+ sal_uInt16 nRet = pDlg->Execute();
+ if (RET_OK == nRet)
+ {
+ aDlgAttr.Put(*pDlg->GetOutputItemSet());
+ FormatAll(aDlgAttr);
+ }
+ pDlg.disposeAndClear();
+ SetActiveSidebarWin(pOrigActiveWin);
+}
+
+void SwPostItMgr::FormatAll(const SfxItemSet &rNewAttr)
+{
+ mpWrtShell->StartAllAction();
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_FORMAT_ALL_NOTES) );
+ mpWrtShell->StartUndo( SwUndoId::INSATTR, &aRewriter );
+
+ for (auto const& postItField : mvPostItFields)
+ {
+ if (!postItField->mpPostIt)
+ continue;
+ OutlinerView* pOLV = postItField->mpPostIt->GetOutlinerView();
+ //save old selection
+ ESelection aOrigSel(pOLV->GetSelection());
+ //select all
+ Outliner *pOutliner = pOLV->GetOutliner();
+ if (pOutliner)
+ {
+ sal_Int32 nParaCount = pOutliner->GetParagraphCount();
+ if (nParaCount > 0)
+ pOLV->SelectRange(0, nParaCount);
+ }
+ //set new char properties
+ pOLV->SetAttribs(rNewAttr);
+ //restore old selection
+ pOLV->SetSelection(aOrigSel);
+ // tdf#91596 store updated formatting in SwField
+ postItField->mpPostIt->UpdateData();
+ }
+
+ mpWrtShell->EndUndo();
+ PrepareView();
+ mpWrtShell->EndAllAction();
+ mbLayout = true;
+ CalcRects();
+ LayoutPostIts();
+}
+
+void SwPostItMgr::Hide( std::u16string_view rAuthor )
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( postItField->mpPostIt && (postItField->mpPostIt->GetAuthor() == rAuthor) )
+ {
+ postItField->mbShow = false;
+ postItField->mpPostIt->HideNote();
+ }
+ }
+
+ LayoutPostIts();
+}
+
+void SwPostItMgr::Hide()
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ postItField->mbShow = false;
+ if (postItField->mpPostIt)
+ postItField->mpPostIt->HideNote();
+ }
+}
+
+void SwPostItMgr::Show()
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ postItField->mbShow = true;
+ }
+ LayoutPostIts();
+}
+
+SwAnnotationWin* SwPostItMgr::GetSidebarWin( const SfxBroadcaster* pBroadcaster) const
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( postItField->GetBroadcaster() == pBroadcaster)
+ return postItField->mpPostIt;
+ }
+ return nullptr;
+}
+
+sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const SwPostItField* pField) const
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( postItField->GetFormatField().GetField() == pField )
+ return postItField->mpPostIt.get();
+ }
+ return nullptr;
+}
+
+sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const sal_uInt32 nPostItId) const
+{
+ for (auto const& postItField : mvPostItFields)
+ {
+ if ( static_cast<const SwPostItField*>(postItField->GetFormatField().GetField())->GetPostItId() == nPostItId )
+ return postItField->mpPostIt.get();
+ }
+ return nullptr;
+}
+
+SwAnnotationWin* SwPostItMgr::GetNextPostIt( sal_uInt16 aDirection,
+ SwAnnotationWin* aPostIt )
+{
+ if (mvPostItFields.size()>1)
+ {
+ auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
+ [&aPostIt](const std::unique_ptr<SwSidebarItem>& pField) { return pField->mpPostIt == aPostIt; });
+ if (i == mvPostItFields.end())
+ return nullptr;
+
+ auto iNextPostIt = i;
+ if (aDirection == KEY_PAGEUP)
+ {
+ if ( iNextPostIt == mvPostItFields.begin() )
+ {
+ return nullptr;
+ }
+ --iNextPostIt;
+ }
+ else
+ {
+ ++iNextPostIt;
+ if ( iNextPostIt == mvPostItFields.end() )
+ {
+ return nullptr;
+ }
+ }
+ // lets quit, we are back at the beginning
+ if ( (*iNextPostIt)->mpPostIt == aPostIt)
+ return nullptr;
+ return (*iNextPostIt)->mpPostIt;
+ }
+ else
+ return nullptr;
+}
+
+tools::Long SwPostItMgr::GetNextBorder()
+{
+ for (auto const& pPage : mPages)
+ {
+ for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
+ {
+ if ((*b)->mpPostIt == mpActivePostIt)
+ {
+ auto aNext = b;
+ ++aNext;
+ bool bFollow = (aNext != pPage->mvSidebarItems.end()) && (*aNext)->mpPostIt->IsFollow();
+ if ( pPage->bScrollbar || bFollow )
+ {
+ return -1;
+ }
+ else
+ {
+ //if this is the last item, return the bottom border otherwise the next item
+ if (aNext == pPage->mvSidebarItems.end())
+ return mpEditWin->LogicToPixel(Point(0,pPage->mPageRect.Bottom())).Y() - GetSpaceBetween();
+ else
+ return (*aNext)->mpPostIt->GetPosPixel().Y() - GetSpaceBetween();
+ }
+ }
+ }
+ }
+
+ OSL_FAIL("SwPostItMgr::GetNextBorder(): We have to find a next border here");
+ return -1;
+}
+
+void SwPostItMgr::SetShadowState(const SwPostItField* pField,bool bCursor)
+{
+ if (pField)
+ {
+ if (pField !=mShadowState.mpShadowField)
+ {
+ if (mShadowState.mpShadowField)
+ {
+ // reset old one if still alive
+ // TODO: does not work properly if mouse and cursor was set
+ sw::annotation::SwAnnotationWin* pOldPostIt =
+ GetAnnotationWin(mShadowState.mpShadowField);
+ if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
+ pOldPostIt->SetViewState(ViewState::NORMAL);
+ }
+ //set new one, if it is not currently edited
+ sw::annotation::SwAnnotationWin* pNewPostIt = GetAnnotationWin(pField);
+ if (pNewPostIt && pNewPostIt->Shadow() && (pNewPostIt->Shadow()->GetShadowState() != SS_EDIT))
+ {
+ pNewPostIt->SetViewState(ViewState::VIEW);
+ //remember our new field
+ mShadowState.mpShadowField = pField;
+ mShadowState.bCursor = false;
+ mShadowState.bMouse = false;
+ }
+ }
+ if (bCursor)
+ mShadowState.bCursor = true;
+ else
+ mShadowState.bMouse = true;
+ }
+ else
+ {
+ if (mShadowState.mpShadowField)
+ {
+ if (bCursor)
+ mShadowState.bCursor = false;
+ else
+ mShadowState.bMouse = false;
+ if (!mShadowState.bCursor && !mShadowState.bMouse)
+ {
+ // reset old one if still alive
+ sw::annotation::SwAnnotationWin* pOldPostIt = GetAnnotationWin(mShadowState.mpShadowField);
+ if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
+ {
+ pOldPostIt->SetViewState(ViewState::NORMAL);
+ mShadowState.mpShadowField = nullptr;
+ }
+ }
+ }
+ }
+}
+
+void SwPostItMgr::PrepareView(bool bIgnoreCount)
+{
+ if (!HasNotes() || bIgnoreCount)
+ {
+ mpWrtShell->StartAllAction();
+ SwRootFrame* pLayout = mpWrtShell->GetLayout();
+ if ( pLayout )
+ SwPostItHelper::setSidebarChanged( pLayout,
+ mpWrtShell->getIDocumentSettingAccess().get( DocumentSettingId::BROWSE_MODE ) );
+ mpWrtShell->EndAllAction();
+ }
+}
+
+bool SwPostItMgr::ShowScrollbar(const tools::ULong aPage) const
+{
+ if (mPages.size() > aPage-1)
+ return (mPages[aPage-1]->bScrollbar && !mbWaitingForCalcRects);
+ else
+ return false;
+}
+
+bool SwPostItMgr::IsHit(const Point &aPointPixel)
+{
+ if (HasNotes() && ShowNotes())
+ {
+ const Point aPoint = mpEditWin->PixelToLogic(aPointPixel);
+ const SwRootFrame* pLayout = mpWrtShell->GetLayout();
+ SwRect aPageFrame;
+ const tools::ULong nPageNum = SwPostItHelper::getPageInfo( aPageFrame, pLayout, aPoint );
+ if( nPageNum )
+ {
+ tools::Rectangle aRect;
+ OSL_ENSURE(mPages.size()>nPageNum-1,"SwPostitMgr:: page container size wrong");
+ aRect = mPages[nPageNum-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? tools::Rectangle(Point(aPageFrame.Left()-GetSidebarWidth()-GetSidebarBorderWidth(),aPageFrame.Top()),Size(GetSidebarWidth(),aPageFrame.Height()))
+ : tools::Rectangle( Point(aPageFrame.Right()+GetSidebarBorderWidth(),aPageFrame.Top()) , Size(GetSidebarWidth(),aPageFrame.Height()));
+ if (aRect.Contains(aPoint))
+ {
+ // we hit the note's sidebar
+ // lets now test for the arrow area
+ if (mPages[nPageNum-1]->bScrollbar)
+ return ScrollbarHit(nPageNum,aPoint);
+ else
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+vcl::Window* SwPostItMgr::IsHitSidebarWindow(const Point& rPointLogic)
+{
+ vcl::Window* pRet = nullptr;
+
+ if (HasNotes() && ShowNotes())
+ {
+ bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
+ if (bEnableMapMode)
+ mpEditWin->EnableMapMode();
+
+ for (const std::unique_ptr<SwSidebarItem>& pItem : mvPostItFields)
+ {
+ SwAnnotationWin* pPostIt = pItem->mpPostIt;
+ if (!pPostIt)
+ continue;
+
+ if (pPostIt->IsHitWindow(rPointLogic))
+ {
+ pRet = pPostIt;
+ break;
+ }
+ }
+
+ if (bEnableMapMode)
+ mpEditWin->EnableMapMode(false);
+ }
+
+ return pRet;
+}
+
+tools::Rectangle SwPostItMgr::GetBottomScrollRect(const tools::ULong aPage) const
+{
+ SwRect aPageRect = mPages[aPage-1]->mPageRect;
+ Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? Point(aPageRect.Left() - GetSidebarWidth() - GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
+ : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
+ Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
+ return tools::Rectangle(aPointBottom,aSize);
+}
+
+tools::Rectangle SwPostItMgr::GetTopScrollRect(const tools::ULong aPage) const
+{
+ SwRect aPageRect = mPages[aPage-1]->mPageRect;
+ Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? Point(aPageRect.Left() - GetSidebarWidth() -GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
+ : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
+ Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
+ return tools::Rectangle(aPointTop,aSize);
+}
+
+//IMPORTANT: if you change the rects here, also change SwPageFrame::PaintNotesSidebar()
+bool SwPostItMgr::ScrollbarHit(const tools::ULong aPage,const Point &aPoint)
+{
+ SwRect aPageRect = mPages[aPage-1]->mPageRect;
+ Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
+ : Point(aPageRect.Right() + GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
+
+ Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
+ : Point(aPageRect.Right()+GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
+
+ tools::Rectangle aRectBottom(GetBottomScrollRect(aPage));
+ tools::Rectangle aRectTop(GetTopScrollRect(aPage));
+
+ if (aRectBottom.Contains(aPoint))
+ {
+ if (aPoint.X() < tools::Long((aPointBottom.X() + GetSidebarWidth()/3)))
+ Scroll( GetScrollSize(),aPage);
+ else
+ Scroll( -1*GetScrollSize(), aPage);
+ return true;
+ }
+ else if (aRectTop.Contains(aPoint))
+ {
+ if (aPoint.X() < tools::Long((aPointTop.X() + GetSidebarWidth()/3*2)))
+ Scroll(GetScrollSize(), aPage);
+ else
+ Scroll(-1*GetScrollSize(), aPage);
+ return true;
+ }
+ return false;
+}
+
+void SwPostItMgr::CorrectPositions()
+{
+ if ( mbWaitingForCalcRects || mbLayouting || mvPostItFields.empty() )
+ return;
+
+ // find first valid note
+ SwAnnotationWin *pFirstPostIt = nullptr;
+ for (auto const& postItField : mvPostItFields)
+ {
+ pFirstPostIt = postItField->mpPostIt;
+ if (pFirstPostIt)
+ break;
+ }
+
+ //if we have not found a valid note, forget about it and leave
+ if (!pFirstPostIt)
+ return;
+
+ // yeah, I know, if this is a left page it could be wrong, but finding the page and the note is probably not even faster than just doing it
+ // check, if anchor overlay object exists.
+ const tools::Long aAnchorX = pFirstPostIt->Anchor()
+ ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getX()),0)).X()
+ : 0;
+ const tools::Long aAnchorY = pFirstPostIt->Anchor()
+ ? mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1
+ : 0;
+ if (Point(aAnchorX,aAnchorY) == pFirstPostIt->GetPosPixel())
+ return;
+
+ tools::Long aAnchorPosX = 0;
+ tools::Long aAnchorPosY = 0;
+ for (const std::unique_ptr<SwPostItPageItem>& pPage : mPages)
+ {
+ for (auto const& item : pPage->mvSidebarItems)
+ {
+ // check, if anchor overlay object exists.
+ if ( item->mbShow && item->mpPostIt && item->mpPostIt->Anchor() )
+ {
+ aAnchorPosX = pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
+ ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSeventhPosition().getX()),0)).X()
+ : mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getX()),0)).X();
+ aAnchorPosY = mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1;
+ item->mpPostIt->SetPosPixel(Point(aAnchorPosX,aAnchorPosY));
+ }
+ }
+ }
+}
+
+bool SwPostItMgr::ShowNotes() const
+{
+ // we only want to see notes if Options - Writer - View - Notes is ticked
+ return mpWrtShell->GetViewOptions()->IsPostIts();
+}
+
+bool SwPostItMgr::HasNotes() const
+{
+ return !mvPostItFields.empty();
+}
+
+tools::ULong SwPostItMgr::GetSidebarWidth(bool bPx) const
+{
+ bool bEnableMapMode = !mpWrtShell->GetOut()->IsMapModeEnabled();
+ sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom();
+ if (comphelper::LibreOfficeKit::isActive() && !bEnableMapMode)
+ {
+ // The output device is the tile and contains the real wanted scale factor.
+ double fScaleX = double(mpWrtShell->GetOut()->GetMapMode().GetScaleX());
+ nZoom = fScaleX * 100;
+ }
+ tools::ULong aWidth = static_cast<tools::ULong>(nZoom * 1.8);
+
+ if (bPx)
+ return aWidth;
+ else
+ {
+ if (bEnableMapMode)
+ // The output device is the window.
+ mpWrtShell->GetOut()->EnableMapMode();
+ tools::Long nRet = mpWrtShell->GetOut()->PixelToLogic(Size(aWidth, 0)).Width();
+ if (bEnableMapMode)
+ mpWrtShell->GetOut()->EnableMapMode(false);
+ return nRet;
+ }
+}
+
+tools::ULong SwPostItMgr::GetSidebarBorderWidth(bool bPx) const
+{
+ if (bPx)
+ return 2;
+ else
+ return mpWrtShell->GetOut()->PixelToLogic(Size(2,0)).Width();
+}
+
+Color SwPostItMgr::GetColorDark(std::size_t aAuthorIndex)
+{
+ if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ static const Color aArrayNormal[] = {
+ COL_AUTHOR1_NORMAL, COL_AUTHOR2_NORMAL, COL_AUTHOR3_NORMAL,
+ COL_AUTHOR4_NORMAL, COL_AUTHOR5_NORMAL, COL_AUTHOR6_NORMAL,
+ COL_AUTHOR7_NORMAL, COL_AUTHOR8_NORMAL, COL_AUTHOR9_NORMAL };
+
+ return aArrayNormal[ aAuthorIndex % SAL_N_ELEMENTS( aArrayNormal )];
+ }
+ else
+ return COL_WHITE;
+}
+
+Color SwPostItMgr::GetColorLight(std::size_t aAuthorIndex)
+{
+ if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ static const Color aArrayLight[] = {
+ COL_AUTHOR1_LIGHT, COL_AUTHOR2_LIGHT, COL_AUTHOR3_LIGHT,
+ COL_AUTHOR4_LIGHT, COL_AUTHOR5_LIGHT, COL_AUTHOR6_LIGHT,
+ COL_AUTHOR7_LIGHT, COL_AUTHOR8_LIGHT, COL_AUTHOR9_LIGHT };
+
+ return aArrayLight[ aAuthorIndex % SAL_N_ELEMENTS( aArrayLight )];
+ }
+ else
+ return COL_WHITE;
+}
+
+Color SwPostItMgr::GetColorAnchor(std::size_t aAuthorIndex)
+{
+ if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ static const Color aArrayAnchor[] = {
+ COL_AUTHOR1_DARK, COL_AUTHOR2_DARK, COL_AUTHOR3_DARK,
+ COL_AUTHOR4_DARK, COL_AUTHOR5_DARK, COL_AUTHOR6_DARK,
+ COL_AUTHOR7_DARK, COL_AUTHOR8_DARK, COL_AUTHOR9_DARK };
+
+ return aArrayAnchor[ aAuthorIndex % SAL_N_ELEMENTS( aArrayAnchor )];
+ }
+ else
+ return COL_WHITE;
+}
+
+void SwPostItMgr::SetActiveSidebarWin( SwAnnotationWin* p)
+{
+ if ( p == mpActivePostIt )
+ return;
+
+ // we need the temp variable so we can set mpActivePostIt before we call DeactivatePostIt
+ // therefore we get a new layout in DOCCHANGED when switching from postit to document,
+ // otherwise, GetActivePostIt() would still hold our old postit
+ SwAnnotationWin* pActive = mpActivePostIt;
+ mpActivePostIt = p;
+ if (pActive)
+ {
+ pActive->DeactivatePostIt();
+ mShadowState.mpShadowField = nullptr;
+ }
+ if (mpActivePostIt)
+ {
+ mpActivePostIt->GotoPos();
+ mpView->AttrChangedNotify(nullptr);
+ mpActivePostIt->ActivatePostIt();
+ }
+}
+
+IMPL_LINK_NOARG( SwPostItMgr, CalcHdl, void*, void )
+{
+ mnEventId = nullptr;
+ if ( mbLayouting )
+ {
+ OSL_FAIL("Reentrance problem in Layout Manager!");
+ mbWaitingForCalcRects = false;
+ return;
+ }
+
+ // do not change order, even if it would seem so in the first place, we need the calcrects always
+ if (CalcRects() || mbLayout)
+ {
+ mbLayout = false;
+ LayoutPostIts();
+ }
+}
+
+void SwPostItMgr::Rescale()
+{
+ for (auto const& postItField : mvPostItFields)
+ if ( postItField->mpPostIt )
+ postItField->mpPostIt->Rescale();
+}
+
+sal_Int32 SwPostItMgr::GetInitialAnchorDistance() const
+{
+ const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
+ return sal_Int32(POSTIT_INITIAL_ANCHOR_DISTANCE * f);
+}
+
+sal_Int32 SwPostItMgr::GetSpaceBetween() const
+{
+ const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
+ return sal_Int32(POSTIT_SPACE_BETWEEN * f);
+}
+
+sal_Int32 SwPostItMgr::GetScrollSize() const
+{
+ const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
+ return sal_Int32((POSTIT_SPACE_BETWEEN + POSTIT_MINIMUMSIZE_WITH_META) * f);
+}
+
+sal_Int32 SwPostItMgr::GetMinimumSizeWithMeta() const
+{
+ const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
+ return sal_Int32(POSTIT_MINIMUMSIZE_WITH_META * f);
+}
+
+sal_Int32 SwPostItMgr::GetSidebarScrollerHeight() const
+{
+ const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
+ return sal_Int32(POSTIT_SCROLL_SIDEBAR_HEIGHT * f);
+}
+
+void SwPostItMgr::SetSpellChecking()
+{
+ for (auto const& postItField : mvPostItFields)
+ if ( postItField->mpPostIt )
+ postItField->mpPostIt->SetSpellChecking();
+}
+
+void SwPostItMgr::SetReadOnlyState()
+{
+ for (auto const& postItField : mvPostItFields)
+ if ( postItField->mpPostIt )
+ postItField->mpPostIt->SetReadonly( mbReadOnly );
+}
+
+void SwPostItMgr::CheckMetaText()
+{
+ for (auto const& postItField : mvPostItFields)
+ if ( postItField->mpPostIt )
+ postItField->mpPostIt->CheckMetaText();
+
+}
+
+sal_uInt16 SwPostItMgr::Replace(SvxSearchItem const * pItem)
+{
+ SwAnnotationWin* pWin = GetActiveSidebarWin();
+ sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( *pItem );
+ if (!aResult)
+ SetActiveSidebarWin(nullptr);
+ return aResult;
+}
+
+sal_uInt16 SwPostItMgr::FinishSearchReplace(const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
+{
+ SwAnnotationWin* pWin = GetActiveSidebarWin();
+ SvxSearchItem aItem(SID_SEARCH_ITEM );
+ aItem.SetSearchOptions(rSearchOptions);
+ aItem.SetBackward(!bSrchForward);
+ sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
+ if (!aResult)
+ SetActiveSidebarWin(nullptr);
+ return aResult;
+}
+
+sal_uInt16 SwPostItMgr::SearchReplace(const SwFormatField &pField, const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
+{
+ sal_uInt16 aResult = 0;
+ SwAnnotationWin* pWin = GetSidebarWin(&pField);
+ if (pWin)
+ {
+ ESelection aOldSelection = pWin->GetOutlinerView()->GetSelection();
+ if (bSrchForward)
+ pWin->GetOutlinerView()->SetSelection(ESelection(0,0,0,0));
+ else
+ pWin->GetOutlinerView()->SetSelection(
+ ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT));
+ SvxSearchItem aItem(SID_SEARCH_ITEM );
+ aItem.SetSearchOptions(rSearchOptions);
+ aItem.SetBackward(!bSrchForward);
+ aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
+ if (!aResult)
+ pWin->GetOutlinerView()->SetSelection(aOldSelection);
+ else
+ {
+ SetActiveSidebarWin(pWin);
+ MakeVisible(pWin);
+ }
+ }
+ return aResult;
+}
+
+void SwPostItMgr::AssureStdModeAtShell()
+{
+ // deselect any drawing or frame and leave editing mode
+ SdrView* pSdrView = mpWrtShell->GetDrawView();
+ if ( pSdrView && pSdrView->IsTextEdit() )
+ {
+ bool bLockView = mpWrtShell->IsViewLocked();
+ mpWrtShell->LockView( true );
+ mpWrtShell->EndTextEdit();
+ mpWrtShell->LockView( bLockView );
+ }
+
+ if( mpWrtShell->IsSelFrameMode() || mpWrtShell->IsObjSelected())
+ {
+ mpWrtShell->UnSelectFrame();
+ mpWrtShell->LeaveSelFrameMode();
+ mpWrtShell->GetView().LeaveDrawCreate();
+ mpWrtShell->EnterStdMode();
+
+ mpWrtShell->DrawSelChanged();
+ mpView->StopShellTimer();
+ }
+}
+
+bool SwPostItMgr::HasActiveSidebarWin() const
+{
+ return mpActivePostIt != nullptr;
+}
+
+bool SwPostItMgr::HasActiveAnnotationWin() const
+{
+ return HasActiveSidebarWin() &&
+ mpActivePostIt != nullptr;
+}
+
+void SwPostItMgr::GrabFocusOnActiveSidebarWin()
+{
+ if ( HasActiveSidebarWin() )
+ {
+ mpActivePostIt->GrabFocus();
+ }
+}
+
+void SwPostItMgr::UpdateDataOnActiveSidebarWin()
+{
+ if ( HasActiveSidebarWin() )
+ {
+ mpActivePostIt->UpdateData();
+ }
+}
+
+void SwPostItMgr::DeleteActiveSidebarWin()
+{
+ if ( HasActiveSidebarWin() )
+ {
+ mpActivePostIt->Delete();
+ }
+}
+
+void SwPostItMgr::HideActiveSidebarWin()
+{
+ if ( HasActiveSidebarWin() )
+ {
+ mpActivePostIt->Hide();
+ }
+}
+
+void SwPostItMgr::ToggleInsModeOnActiveSidebarWin()
+{
+ if ( HasActiveSidebarWin() )
+ {
+ mpActivePostIt->ToggleInsMode();
+ }
+}
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+void SwPostItMgr::ConnectSidebarWinToFrame( const SwFrame& rFrame,
+ const SwFormatField& rFormatField,
+ SwAnnotationWin& rSidebarWin )
+{
+ if ( mpFrameSidebarWinContainer == nullptr )
+ {
+ mpFrameSidebarWinContainer.reset(new SwFrameSidebarWinContainer());
+ }
+
+ const bool bInserted = mpFrameSidebarWinContainer->insert( rFrame, rFormatField, rSidebarWin );
+ if ( bInserted &&
+ mpWrtShell->GetAccessibleMap() )
+ {
+ mpWrtShell->GetAccessibleMap()->InvalidatePosOrSize( nullptr, nullptr, &rSidebarWin, SwRect() );
+ }
+}
+
+void SwPostItMgr::DisconnectSidebarWinFromFrame( const SwFrame& rFrame,
+ SwAnnotationWin& rSidebarWin )
+{
+ if ( mpFrameSidebarWinContainer != nullptr )
+ {
+ const bool bRemoved = mpFrameSidebarWinContainer->remove( rFrame, rSidebarWin );
+ if ( bRemoved &&
+ mpWrtShell->GetAccessibleMap() )
+ {
+ mpWrtShell->GetAccessibleMap()->A11yDispose( nullptr, nullptr, &rSidebarWin );
+ }
+ }
+}
+#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
+
+bool SwPostItMgr::HasFrameConnectedSidebarWins( const SwFrame& rFrame )
+{
+ bool bRet( false );
+
+ if ( mpFrameSidebarWinContainer != nullptr )
+ {
+ bRet = !mpFrameSidebarWinContainer->empty( rFrame );
+ }
+
+ return bRet;
+}
+
+vcl::Window* SwPostItMgr::GetSidebarWinForFrameByIndex( const SwFrame& rFrame,
+ const sal_Int32 nIndex )
+{
+ vcl::Window* pSidebarWin( nullptr );
+
+ if ( mpFrameSidebarWinContainer != nullptr )
+ {
+ pSidebarWin = mpFrameSidebarWinContainer->get( rFrame, nIndex );
+ }
+
+ return pSidebarWin;
+}
+
+void SwPostItMgr::GetAllSidebarWinForFrame( const SwFrame& rFrame,
+ std::vector< vcl::Window* >* pChildren )
+{
+ if ( mpFrameSidebarWinContainer != nullptr )
+ {
+ mpFrameSidebarWinContainer->getAll( rFrame, pChildren );
+ }
+}
+
+void SwPostItMgr::ShowHideResolvedNotes(bool visible) {
+ for (auto const& pPage : mPages)
+ {
+ for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
+ {
+ if ((*b)->mpPostIt->IsResolved())
+ {
+ (*b)->mpPostIt->SetResolved(true);
+ (*b)->mpPostIt->GetSidebarItem().mbShow = visible;
+ }
+ }
+ }
+ LayoutPostIts();
+}
+
+void SwPostItMgr::UpdateResolvedStatus(const sw::annotation::SwAnnotationWin* topNote) {
+ // Given the topmost note as an argument, scans over all notes and sets the
+ // 'resolved' state of each descendant of the top notes to the resolved state
+ // of the top note.
+ bool resolved = topNote->IsResolved();
+ for (auto const& pPage : mPages)
+ {
+ for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
+ {
+ if((*b)->mpPostIt->GetTopReplyNote() == topNote) {
+ (*b)->mpPostIt->SetResolved(resolved);
+ }
+ }
+ }
+}
+
+void SwNoteProps::ImplCommit() {}
+void SwNoteProps::Notify( const css::uno::Sequence< OUString >& ) {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.cxx b/sw/source/uibase/docvw/ShadowOverlayObject.cxx
new file mode 100644
index 000000000..7607cb335
--- /dev/null
+++ b/sw/source/uibase/docvw/ShadowOverlayObject.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ShadowOverlayObject.hxx"
+
+#include <view.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+
+#include <sw_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/primitivetools2d.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+
+namespace sw::sidebarwindows {
+
+// helper SwPostItShadowPrimitive
+
+namespace {
+
+// Used to allow view-dependent primitive definition. For that purpose, the
+// initially created primitive (this one) always has to be view-independent,
+// but the decomposition is made view-dependent. Very simple primitive which
+// just remembers the discrete data and applies it at decomposition time.
+class ShadowPrimitive : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
+{
+private:
+ basegfx::B2DPoint maBasePosition;
+ basegfx::B2DPoint maSecondPosition;
+ ShadowState maShadowState;
+
+protected:
+ virtual void create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override;
+
+public:
+ ShadowPrimitive(
+ const basegfx::B2DPoint& rBasePosition,
+ const basegfx::B2DPoint& rSecondPosition,
+ ShadowState aShadowState)
+ : maBasePosition(rBasePosition),
+ maSecondPosition(rSecondPosition),
+ maShadowState(aShadowState)
+ {}
+
+ // data access
+ const basegfx::B2DPoint& getSecondPosition() const { return maSecondPosition; }
+
+ virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const override;
+
+ virtual sal_uInt32 getPrimitive2DID() const override;
+};
+
+}
+
+void ShadowPrimitive::create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // get logic sizes in object coordinate system
+ basegfx::B2DRange aRange(maBasePosition);
+
+ switch(maShadowState)
+ {
+ case SS_NORMAL:
+ {
+ aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (2.0 * getDiscreteUnit())));
+ const ::drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute(
+ drawinglayer::attribute::GradientStyle::Linear,
+ 0.0,
+ 0.5,
+ 0.5,
+ M_PI,
+ basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
+ basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0));
+
+ rContainer.push_back(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aFillGradientAttribute));
+ break;
+ }
+ case SS_VIEW:
+ {
+ aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (4.0 * getDiscreteUnit())));
+ const drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute(
+ drawinglayer::attribute::GradientStyle::Linear,
+ 0.0,
+ 0.5,
+ 0.5,
+ M_PI,
+ basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
+ basegfx::BColor(180.0/255.0,180.0/255.0,180.0/255.0));
+
+ rContainer.push_back(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aFillGradientAttribute));
+ break;
+ }
+ case SS_EDIT:
+ {
+ aRange.expand(basegfx::B2DTuple(getSecondPosition().getX(), getSecondPosition().getY() + (4.0 * getDiscreteUnit())));
+ const drawinglayer::attribute::FillGradientAttribute aFillGradientAttribute(
+ drawinglayer::attribute::GradientStyle::Linear,
+ 0.0,
+ 0.5,
+ 0.5,
+ M_PI,
+ basegfx::BColor(230.0/255.0,230.0/255.0,230.0/255.0),
+ basegfx::BColor(83.0/255.0,83.0/255.0,83.0/255.0));
+
+ rContainer.push_back(
+ new drawinglayer::primitive2d::FillGradientPrimitive2D(
+ aRange,
+ aFillGradientAttribute));
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool ShadowPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
+{
+ if(drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const ShadowPrimitive& rCompare = static_cast< const ShadowPrimitive& >(rPrimitive);
+
+ return (maBasePosition == rCompare.maBasePosition
+ && getSecondPosition() == rCompare.getSecondPosition()
+ && maShadowState == rCompare.maShadowState);
+ }
+
+ return false;
+}
+
+sal_uInt32 ShadowPrimitive::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SWSIDEBARSHADOWPRIMITIVE;
+}
+
+/* static */ std::unique_ptr<ShadowOverlayObject> ShadowOverlayObject::CreateShadowOverlayObject( SwView const & rDocView )
+{
+ std::unique_ptr<ShadowOverlayObject> pShadowOverlayObject;
+
+ if ( rDocView.GetDrawView() )
+ {
+ SdrPaintWindow* pPaintWindow = rDocView.GetDrawView()->GetPaintWindow(0);
+ if( pPaintWindow )
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
+
+ if ( xOverlayManager.is() )
+ {
+ pShadowOverlayObject.reset( new ShadowOverlayObject( basegfx::B2DPoint(0,0),
+ basegfx::B2DPoint(0,0),
+ Color(0,0,0) ) );
+ xOverlayManager->add(*pShadowOverlayObject);
+ }
+ }
+ }
+
+ return pShadowOverlayObject;
+}
+
+ShadowOverlayObject::ShadowOverlayObject( const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPosition,
+ Color aBaseColor )
+ : OverlayObjectWithBasePosition(rBasePos, aBaseColor)
+ , maSecondPosition(rSecondPosition)
+ , mShadowState(SS_NORMAL)
+{
+}
+
+ShadowOverlayObject::~ShadowOverlayObject()
+{
+ if ( getOverlayManager() )
+ {
+ getOverlayManager()->remove(*this);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ShadowOverlayObject::createOverlayObjectPrimitive2DSequence()
+{
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new ShadowPrimitive( getBasePosition(),
+ maSecondPosition,
+ GetShadowState() ) );
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+}
+
+void ShadowOverlayObject::SetShadowState(ShadowState aState)
+{
+ if (mShadowState != aState)
+ {
+ mShadowState = aState;
+
+ objectChange();
+ }
+}
+
+void ShadowOverlayObject::SetPosition( const basegfx::B2DPoint& rPoint1,
+ const basegfx::B2DPoint& rPoint2)
+{
+ if(!rPoint1.equal(getBasePosition()) || !rPoint2.equal(maSecondPosition))
+ {
+ maBasePosition = rPoint1;
+ maSecondPosition = rPoint2;
+
+ objectChange();
+ }
+}
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/ShadowOverlayObject.hxx b/sw/source/uibase/docvw/ShadowOverlayObject.hxx
new file mode 100644
index 000000000..cd612f7ce
--- /dev/null
+++ b/sw/source/uibase/docvw/ShadowOverlayObject.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <svx/sdr/overlay/overlayobject.hxx>
+
+class SwView;
+
+namespace sw::sidebarwindows {
+
+enum ShadowState
+{
+ SS_NORMAL,
+ SS_VIEW,
+ SS_EDIT
+};
+
+class ShadowOverlayObject: public sdr::overlay::OverlayObjectWithBasePosition
+{
+protected:
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+private:
+ basegfx::B2DPoint maSecondPosition;
+ ShadowState mShadowState;
+
+ ShadowOverlayObject( const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPosition,
+ Color aBaseColor );
+
+public:
+ virtual ~ShadowOverlayObject() override;
+
+ void SetShadowState(ShadowState aState);
+ ShadowState GetShadowState() const {return mShadowState;}
+
+ void SetPosition( const basegfx::B2DPoint& rPoint1,
+ const basegfx::B2DPoint& rPoint2 );
+
+ static std::unique_ptr<ShadowOverlayObject> CreateShadowOverlayObject( SwView const & rDocView );
+};
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/SidebarTxtControl.cxx b/sw/source/uibase/docvw/SidebarTxtControl.cxx
new file mode 100644
index 000000000..7b7002b7f
--- /dev/null
+++ b/sw/source/uibase/docvw/SidebarTxtControl.cxx
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include "SidebarTxtControl.hxx"
+
+#include <docsh.hxx>
+#include <doc.hxx>
+
+#include <PostItMgr.hxx>
+
+#include <cmdid.h>
+#include <strings.hrc>
+
+#include <unotools/securityoptions.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxhelp.hxx>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/settings.hxx>
+
+#include <editeng/outliner.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/flditem.hxx>
+
+#include <uitool.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <AnnotationWin.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <redline.hxx>
+#include <memory>
+
+namespace sw::sidebarwindows {
+
+SidebarTextControl::SidebarTextControl(sw::annotation::SwAnnotationWin& rSidebarWin,
+ SwView& rDocView,
+ SwPostItMgr& rPostItMgr)
+ : mrSidebarWin(rSidebarWin)
+ , mrDocView(rDocView)
+ , mrPostItMgr(rPostItMgr)
+ , mbMouseDownGainingFocus(false)
+{
+}
+
+EditView* SidebarTextControl::GetEditView() const
+{
+ OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView();
+ if (!pOutlinerView)
+ return nullptr;
+ return &pOutlinerView->GetEditView();
+}
+
+EditEngine* SidebarTextControl::GetEditEngine() const
+{
+ OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView();
+ if (!pOutlinerView)
+ return nullptr;
+ return pOutlinerView->GetEditView().GetEditEngine();
+}
+
+void SidebarTextControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(0, 0);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+ SetOutputSizePixel(aSize);
+
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ EnableRTL(false);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
+ rDevice.SetBackground(aBgColor);
+
+ Size aOutputSize(rDevice.PixelToLogic(aSize));
+
+ EditView* pEditView = GetEditView();
+ pEditView->setEditViewCallbacks(this);
+
+ EditEngine* pEditEngine = GetEditEngine();
+ // For tdf#143443 note we want an 'infinite' height initially (which is the
+ // editengines default). For tdf#144686 it is helpful if the initial width
+ // is the "SidebarWidth" so the calculated text height is always meaningful
+ // for layout in the sidebar.
+ Size aPaperSize(mrPostItMgr.GetSidebarWidth(), pEditEngine->GetPaperSize().Height());
+ pEditEngine->SetPaperSize(aPaperSize);
+ pEditEngine->SetRefDevice(mrDocView.GetWrtShell().getIDocumentDeviceAccess().getReferenceDevice(false));
+
+ pEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize));
+ pEditView->SetBackgroundColor(aBgColor);
+
+ pDrawingArea->set_cursor(PointerStyle::Text);
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ InitAccessible();
+#endif
+}
+
+void SidebarTextControl::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark)
+{
+ Point aMousePos = EditViewOutputDevice().PixelToLogic(rPosition);
+ m_xEditView->SetCursorLogicPosition(aMousePos, bPoint, bClearMark);
+}
+
+void SidebarTextControl::GetFocus()
+{
+ WeldEditView::GetFocus();
+ if ( !mrSidebarWin.IsMouseOver() )
+ Invalidate();
+ mrSidebarWin.SetActiveSidebarWin();
+}
+
+void SidebarTextControl::LoseFocus()
+{
+ // write the visible text back into the SwField
+ mrSidebarWin.UpdateData();
+
+ WeldEditView::LoseFocus();
+ if ( !mrSidebarWin.IsMouseOver() )
+ {
+ Invalidate();
+ }
+ // set false for autoscroll to typing location
+ mrSidebarWin.LockView(false);
+}
+
+OUString SidebarTextControl::RequestHelp(tools::Rectangle& rHelpRect)
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ Point aPos = rHelpRect.TopLeft();
+
+ const OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ Point aLogicClick = rOutDev.PixelToLogic(aPos);
+ const SvxFieldItem* pItem = pEditView->GetField(aLogicClick);
+ if (pItem)
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField );
+ if (pURL)
+ {
+ rHelpRect = tools::Rectangle(aPos, Size(50, 10));
+ return SfxHelp::GetURLHelpText(pURL->GetURL());
+ }
+ }
+ }
+
+ TranslateId pResId;
+ switch( mrSidebarWin.GetLayoutStatus() )
+ {
+ case SwPostItHelper::INSERTED: pResId = STR_REDLINE_INSERT; break;
+ case SwPostItHelper::DELETED: pResId = STR_REDLINE_DELETE; break;
+ default: break;
+ }
+
+ SwContentAtPos aContentAtPos( IsAttrAtPos::Redline );
+ if ( pResId &&
+ mrDocView.GetWrtShell().GetContentAtPos( mrSidebarWin.GetAnchorPos(), aContentAtPos ) )
+ {
+ OUString sText = SwResId(pResId) + ": " +
+ aContentAtPos.aFnd.pRedl->GetAuthorString() + " - " +
+ GetAppLangDateTimeString( aContentAtPos.aFnd.pRedl->GetTimeStamp() );
+ return sText;
+ }
+
+ return OUString();
+}
+
+void SidebarTextControl::EditViewScrollStateChange()
+{
+ mrSidebarWin.SetScrollbar();
+}
+
+void SidebarTextControl::DrawForPage(OutputDevice* pDev, const Point& rPt)
+{
+ //Take the control's height, but overwrite the scrollbar area if there was one
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ Size aSize(rDevice.PixelToLogic(GetOutputSizePixel()));
+
+ if (OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView())
+ {
+ pOutlinerView->GetOutliner()->Draw(*pDev, tools::Rectangle(rPt, aSize));
+ }
+
+ if ( mrSidebarWin.GetLayoutStatus()!=SwPostItHelper::DELETED )
+ return;
+
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+
+ pDev->SetLineColor(mrSidebarWin.GetChangeColor());
+ Point aBottomRight(rPt);
+ aBottomRight.Move(aSize);
+ pDev->DrawLine(rPt, aBottomRight);
+
+ Point aTopRight(rPt);
+ aTopRight.Move(Size(aSize.Width(), 0));
+
+ Point aBottomLeft(rPt);
+ aBottomLeft.Move(Size(0, aSize.Height()));
+
+ pDev->DrawLine(aTopRight, aBottomLeft);
+
+ pDev->Pop();
+}
+
+void SidebarTextControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ Size aSize = GetOutputSizePixel();
+ Point aPos;
+
+ if (!rRenderContext.GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ if (mrSidebarWin.IsMouseOverSidebarWin() || HasFocus())
+ {
+ rRenderContext.DrawGradient(tools::Rectangle(aPos, rRenderContext.PixelToLogic(aSize)),
+ Gradient(GradientStyle::Linear, mrSidebarWin.ColorDark(), mrSidebarWin.ColorDark()));
+ }
+ else
+ {
+ rRenderContext.DrawGradient(tools::Rectangle(aPos, rRenderContext.PixelToLogic(aSize)),
+ Gradient(GradientStyle::Linear, mrSidebarWin.ColorLight(), mrSidebarWin.ColorDark()));
+ }
+ }
+
+ DoPaint(rRenderContext, rRect);
+
+ if (mrSidebarWin.GetLayoutStatus() != SwPostItHelper::DELETED)
+ return;
+
+ const AntialiasingFlags nFormerAntialiasing( rRenderContext.GetAntialiasing() );
+ const bool bIsAntiAliasing = officecfg::Office::Common::Drawinglayer::AntiAliasing::get();
+ if ( bIsAntiAliasing )
+ rRenderContext.SetAntialiasing(AntialiasingFlags::Enable);
+ rRenderContext.SetLineColor(mrSidebarWin.GetChangeColor());
+ rRenderContext.DrawLine(rRenderContext.PixelToLogic(aPos),
+ rRenderContext.PixelToLogic(aPos + Point(aSize.Width(),
+ aSize.Height() * 0.95)));
+ rRenderContext.DrawLine(rRenderContext.PixelToLogic(aPos + Point(aSize.Width(),
+ 0)),
+ rRenderContext.PixelToLogic(aPos + Point(0,
+ aSize.Height() * 0.95)));
+ if ( bIsAntiAliasing )
+ rRenderContext.SetAntialiasing(nFormerAntialiasing);
+}
+
+void SidebarTextControl::MakeVisible()
+{
+ //let's make sure we see our note
+ mrPostItMgr.MakeVisible(&mrSidebarWin);
+}
+
+bool SidebarTextControl::KeyInput( const KeyEvent& rKeyEvt )
+{
+ if (getenv("SW_DEBUG") && rKeyEvt.GetKeyCode().GetCode() == KEY_F12)
+ {
+ if (rKeyEvt.GetKeyCode().IsShift())
+ {
+ mrDocView.GetDocShell()->GetDoc()->dumpAsXml();
+ return true;
+ }
+ }
+
+ bool bDone = false;
+
+ const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode();
+ sal_uInt16 nKey = rKeyCode.GetCode();
+ if ( ( rKeyCode.IsMod1() && rKeyCode.IsMod2() ) &&
+ ( (nKey == KEY_PAGEUP) || (nKey == KEY_PAGEDOWN) ) )
+ {
+ mrSidebarWin.SwitchToPostIt(nKey);
+ bDone = true;
+ }
+ else if ( nKey == KEY_ESCAPE ||
+ ( rKeyCode.IsMod1() &&
+ ( nKey == KEY_PAGEUP ||
+ nKey == KEY_PAGEDOWN ) ) )
+ {
+ mrSidebarWin.SwitchToFieldPos();
+ bDone = true;
+ }
+ else if ( rKeyCode.GetFullCode() == KEY_INSERT )
+ {
+ mrSidebarWin.ToggleInsMode();
+ bDone = true;
+ }
+ else
+ {
+ MakeVisible();
+
+ tools::Long aOldHeight = mrSidebarWin.GetPostItTextHeight();
+
+ /// HACK: need to switch off processing of Undo/Redo in Outliner
+ if ( !( (nKey == KEY_Z || nKey == KEY_Y) && rKeyCode.IsMod1()) )
+ {
+ bool bIsProtected = mrSidebarWin.IsProtected();
+ if ( !bIsProtected || !EditEngine::DoesKeyChangeText(rKeyEvt) )
+ {
+ EditView* pEditView = GetEditView();
+ bDone = pEditView && pEditView->PostKeyEvent(rKeyEvt);
+ }
+ else
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetDrawingArea(), "modules/swriter/ui/inforeadonlydialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("InfoReadonlyDialog"));
+ xQuery->run();
+ }
+ }
+ if (bDone)
+ mrSidebarWin.ResizeIfNecessary( aOldHeight, mrSidebarWin.GetPostItTextHeight() );
+ else
+ {
+ // write back data first when showing navigator
+ if ( nKey==KEY_F5 )
+ mrSidebarWin.UpdateData();
+ bDone = mrDocView.KeyInput(rKeyEvt);
+ }
+ }
+
+ mrDocView.GetViewFrame()->GetBindings().InvalidateAll(false);
+
+ return bDone;
+}
+
+bool SidebarTextControl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ bool bExecuteMod = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink);
+
+ if ( !bExecuteMod || (rMEvt.GetModifier() == KEY_MOD1))
+ {
+ const OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ Point aLogicClick = rOutDev.PixelToLogic(rMEvt.GetPosPixel());
+ if (const SvxFieldItem* pItem = pEditView->GetField(aLogicClick))
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField );
+ if ( pURL )
+ {
+ pEditView->MouseButtonDown( rMEvt );
+ SwWrtShell &rSh = mrDocView.GetWrtShell();
+ const OUString& sURL( pURL->GetURL() );
+ const OUString& sTarget( pURL->GetTargetFrame() );
+ ::LoadURL(rSh, sURL, LoadUrlFlags::NONE, sTarget);
+ return true;
+ }
+ }
+ }
+ }
+
+ mbMouseDownGainingFocus = !HasFocus();
+ GrabFocus();
+
+ bool bRet = WeldEditView::MouseButtonDown(rMEvt);
+
+ mrDocView.GetViewFrame()->GetBindings().InvalidateAll(false);
+
+ return bRet;
+}
+
+bool SidebarTextControl::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ bool bRet = WeldEditView::MouseButtonUp(rMEvt);
+
+ if (mbMouseDownGainingFocus)
+ {
+ MakeVisible();
+ mbMouseDownGainingFocus = false;
+ }
+
+ return bRet;
+}
+
+IMPL_LINK( SidebarTextControl, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void )
+{
+ if ( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
+ {
+ mrDocView.GetViewFrame()->GetDispatcher()->Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON);
+ }
+}
+
+bool SidebarTextControl::Command( const CommandEvent& rCEvt )
+{
+ EditView* pEditView = GetEditView();
+
+ if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
+ {
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ if ( !mrSidebarWin.IsProtected() &&
+ pEditView &&
+ pEditView->IsWrongSpelledWordAtPos( rCEvt.GetMousePosPixel(), true ))
+ {
+ Link<SpellCallbackInfo&,void> aLink = LINK(this, SidebarTextControl, OnlineSpellCallback);
+ pEditView->ExecuteSpellPopup(rCEvt.GetMousePosPixel(), aLink);
+ }
+ else
+ {
+ Point aPos;
+ if (rCEvt.IsMouseEvent())
+ aPos = rCEvt.GetMousePosPixel();
+ else
+ {
+ const Size aSize = GetOutputSizePixel();
+ aPos = Point( aSize.getWidth()/2, aSize.getHeight()/2 );
+ }
+ SfxDispatcher::ExecutePopup(&mrSidebarWin, &aPos);
+ }
+ return true;
+ }
+ else if (rCEvt.GetCommand() == CommandEventId::Wheel)
+ {
+ // if no scrollbar, or extra keys held scroll the document and consume
+ // this event, otherwise don't consume and let the event get to the
+ // surrounding scrolled window
+ if (!mrSidebarWin.IsScrollbarVisible())
+ {
+ mrDocView.HandleWheelCommands(rCEvt);
+ return true;
+ }
+ else
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if (pData->IsShift() || pData->IsMod1() || pData->IsMod2())
+ {
+ mrDocView.HandleWheelCommands(rCEvt);
+ return true;
+ }
+ }
+ }
+
+ return WeldEditView::Command(rCEvt);
+}
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/SidebarTxtControl.hxx b/sw/source/uibase/docvw/SidebarTxtControl.hxx
new file mode 100644
index 000000000..e0652aa5b
--- /dev/null
+++ b/sw/source/uibase/docvw/SidebarTxtControl.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/weldeditview.hxx>
+
+class OutlinerView;
+class SwView;
+class SwPostItMgr;
+struct SpellCallbackInfo;
+namespace sw::annotation { class SwAnnotationWin; }
+
+namespace sw::sidebarwindows {
+
+class SidebarTextControl : public WeldEditView
+{
+ private:
+ sw::annotation::SwAnnotationWin& mrSidebarWin;
+ SwView& mrDocView;
+ SwPostItMgr& mrPostItMgr;
+ bool mbMouseDownGainingFocus;
+
+ void MakeVisible();
+
+ protected:
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+
+ virtual bool Command(const CommandEvent& rCEvt) override;
+ virtual void GetFocus() override;
+ virtual void LoseFocus() override;
+
+ virtual OUString RequestHelp(tools::Rectangle& rRect) override;
+
+ public:
+ SidebarTextControl(sw::annotation::SwAnnotationWin& rSidebarWin,
+ SwView& rDocView,
+ SwPostItMgr& rPostItMgr);
+
+ virtual EditView* GetEditView() const override;
+
+ virtual EditEngine* GetEditEngine() const override;
+
+ virtual void EditViewScrollStateChange() override;
+
+ void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ void SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark);
+
+ virtual bool KeyInput(const KeyEvent& rKeyEvt) override;
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+
+ void SetMapMode(const MapMode& rNewMapMode)
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rDevice.SetMapMode(rNewMapMode);
+ }
+
+ DECL_LINK( OnlineSpellCallback, SpellCallbackInfo&, void );
+
+ void DrawForPage(OutputDevice* pDev, const Point& rPos);
+};
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/SidebarWinAcc.cxx b/sw/source/uibase/docvw/SidebarWinAcc.cxx
new file mode 100644
index 000000000..54c30f518
--- /dev/null
+++ b/sw/source/uibase/docvw/SidebarWinAcc.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "SidebarWinAcc.hxx"
+#include <AnnotationWin.hxx>
+
+#include <viewsh.hxx>
+#include <accmap.hxx>
+#include <toolkit/awt/vclxaccessiblecomponent.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <mutex>
+
+namespace sw::sidebarwindows {
+
+namespace {
+
+// declaration and implementation of accessible context for <SidebarWinAccessible> instance
+class SidebarWinAccessibleContext : public VCLXAccessibleComponent
+{
+ public:
+ explicit SidebarWinAccessibleContext( sw::annotation::SwAnnotationWin& rSidebarWin,
+ SwViewShell& rViewShell,
+ const SwFrame* pAnchorFrame )
+ : VCLXAccessibleComponent( dynamic_cast<VCLXWindow*>(rSidebarWin.CreateAccessible().get()) )
+ , mrViewShell( rViewShell )
+ , mpAnchorFrame( pAnchorFrame )
+ {
+ rSidebarWin.SetAccessibleRole( css::accessibility::AccessibleRole::COMMENT );
+ }
+
+ void ChangeAnchor( const SwFrame* pAnchorFrame )
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ mpAnchorFrame = pAnchorFrame;
+ }
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL
+ getAccessibleParent() override
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ css::uno::Reference< css::accessibility::XAccessible > xAccParent;
+
+ if ( mpAnchorFrame &&
+ mrViewShell.GetAccessibleMap() )
+ {
+ xAccParent = mrViewShell.GetAccessibleMap()->GetContext( mpAnchorFrame, false );
+ }
+
+ return xAccParent;
+ }
+
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ sal_Int32 nIndex( -1 );
+
+ if ( mpAnchorFrame && GetWindow() &&
+ mrViewShell.GetAccessibleMap() )
+ {
+ nIndex = mrViewShell.GetAccessibleMap()->GetChildIndex( *mpAnchorFrame,
+ *GetWindow() );
+ }
+
+ return nIndex;
+ }
+
+ private:
+ SwViewShell& mrViewShell;
+ const SwFrame* mpAnchorFrame;
+
+ std::mutex maMutex;
+};
+
+}
+
+// implementation of accessible for <SwAnnotationWin> instance
+SidebarWinAccessible::SidebarWinAccessible( sw::annotation::SwAnnotationWin& rSidebarWin,
+ SwViewShell& rViewShell,
+ const SwSidebarItem& rSidebarItem )
+ : mrSidebarWin( rSidebarWin )
+ , mrViewShell( rViewShell )
+ , mpAnchorFrame( rSidebarItem.maLayoutInfo.mpAnchorFrame )
+ , m_bAccContextCreated( false )
+{
+ SetWindow( &mrSidebarWin );
+}
+
+SidebarWinAccessible::~SidebarWinAccessible()
+{
+}
+
+void SidebarWinAccessible::ChangeSidebarItem( const SwSidebarItem& rSidebarItem )
+{
+ if ( !m_bAccContextCreated )
+ return;
+
+ css::uno::Reference< css::accessibility::XAccessibleContext > xAcc
+ = getAccessibleContext();
+ if ( xAcc.is() )
+ {
+ SidebarWinAccessibleContext* pAccContext =
+ dynamic_cast<SidebarWinAccessibleContext*>(xAcc.get());
+ if ( pAccContext )
+ {
+ pAccContext->ChangeAnchor( rSidebarItem.maLayoutInfo.mpAnchorFrame );
+ }
+ }
+}
+
+css::uno::Reference< css::accessibility::XAccessibleContext > SidebarWinAccessible::CreateAccessibleContext()
+{
+ rtl::Reference<SidebarWinAccessibleContext> pAccContext =
+ new SidebarWinAccessibleContext( mrSidebarWin,
+ mrViewShell,
+ mpAnchorFrame );
+ m_bAccContextCreated = true;
+ return pAccContext;
+}
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/SidebarWinAcc.hxx b/sw/source/uibase/docvw/SidebarWinAcc.hxx
new file mode 100644
index 000000000..5453a42c9
--- /dev/null
+++ b/sw/source/uibase/docvw/SidebarWinAcc.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <toolkit/awt/vclxwindow.hxx>
+
+class SwViewShell;
+class SwSidebarItem;
+class SwFrame;
+namespace sw::annotation { class SwAnnotationWin; }
+
+namespace sw::sidebarwindows {
+
+class SidebarWinAccessible : public VCLXWindow
+{
+ public:
+ explicit SidebarWinAccessible( sw::annotation::SwAnnotationWin& rSidebarWin,
+ SwViewShell& rViewShell,
+ const SwSidebarItem& rSidebarItem );
+ virtual ~SidebarWinAccessible() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext >
+ CreateAccessibleContext() override;
+
+ void ChangeSidebarItem( const SwSidebarItem& rSidebarItem );
+
+ private:
+ sw::annotation::SwAnnotationWin& mrSidebarWin;
+ SwViewShell& mrViewShell;
+ const SwFrame* mpAnchorFrame;
+ bool m_bAccContextCreated;
+};
+
+} // end of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/UnfloatTableButton.cxx b/sw/source/uibase/docvw/UnfloatTableButton.cxx
new file mode 100644
index 000000000..962140567
--- /dev/null
+++ b/sw/source/uibase/docvw/UnfloatTableButton.cxx
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <UnfloatTableButton.hxx>
+#include <HeaderFooterWin.hxx>
+
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <strings.hrc>
+#include <fmtpdsc.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/settings.hxx>
+#include <viewopt.hxx>
+#include <frame.hxx>
+#include <flyfrm.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <ndindex.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <unoprnms.hxx>
+#include <unotbl.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <svl/grabbagitem.hxx>
+#include <doc.hxx>
+
+#define TEXT_PADDING 3
+#define BOX_DISTANCE 3
+#define BUTTON_WIDTH 12
+
+UnfloatTableButton::UnfloatTableButton(SwEditWin* pEditWin, const SwFrame* pFrame)
+ : SwFrameMenuButtonBase(pEditWin, pFrame, "modules/swriter/ui/unfloatbutton.ui",
+ "UnfloatButton")
+ , m_xPushButton(m_xBuilder->weld_button("button"))
+ , m_sLabel(SwResId(STR_UNFLOAT_TABLE))
+{
+ m_xPushButton->set_accessible_name(m_sLabel);
+ m_xVirDev = m_xPushButton->create_virtual_device();
+ SetVirDevFont();
+}
+
+UnfloatTableButton::~UnfloatTableButton() { disposeOnce(); }
+
+void UnfloatTableButton::dispose()
+{
+ m_xPushButton.reset();
+ m_xVirDev.disposeAndClear();
+ SwFrameMenuButtonBase::dispose();
+}
+
+void UnfloatTableButton::SetOffset(Point aTopRightPixel)
+{
+ // Compute the text size and get the box position & size from it
+ tools::Rectangle aTextRect;
+ m_xVirDev->GetTextBoundRect(aTextRect, m_sLabel);
+ tools::Rectangle aTextPxRect = m_xVirDev->LogicToPixel(aTextRect);
+ FontMetric aFontMetric = m_xVirDev->GetFontMetric(m_xVirDev->GetFont());
+ Size aBoxSize(aTextPxRect.GetWidth() + BUTTON_WIDTH + TEXT_PADDING * 2,
+ aFontMetric.GetLineHeight() + TEXT_PADDING * 2);
+
+ Point aBoxPos(aTopRightPixel.X() - aBoxSize.Width() - BOX_DISTANCE, aTopRightPixel.Y());
+
+ if (AllSettings::GetLayoutRTL())
+ {
+ aBoxPos.setX(aTopRightPixel.X() + BOX_DISTANCE);
+ }
+
+ // Set the position & Size of the window
+ SetPosSizePixel(aBoxPos, aBoxSize);
+ m_xVirDev->SetOutputSizePixel(aBoxSize);
+
+ PaintButton();
+}
+
+void UnfloatTableButton::MouseButtonDown(const MouseEvent& /*rMEvt*/)
+{
+ assert(GetFrame()->IsFlyFrame());
+ // const_cast is needed because of bad design of ISwFrameControl and derived classes
+ SwFlyFrame* pFlyFrame = const_cast<SwFlyFrame*>(static_cast<const SwFlyFrame*>(GetFrame()));
+
+ // Find the table inside the text frame
+ SwTabFrame* pTableFrame = nullptr;
+ SwFrame* pLower = pFlyFrame->GetLower();
+ while (pLower)
+ {
+ if (pLower->IsTabFrame())
+ {
+ pTableFrame = static_cast<SwTabFrame*>(pLower);
+ break;
+ }
+ pLower = pLower->GetNext();
+ }
+
+ if (pTableFrame == nullptr)
+ return;
+
+ // Insert the table at the position of the text node which has the frame anchored to
+ SwFrame* pAnchoreFrame = pFlyFrame->AnchorFrame();
+ if (pAnchoreFrame == nullptr || !pAnchoreFrame->IsTextFrame())
+ return;
+
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pAnchoreFrame);
+ if (pTextFrame->GetTextNodeFirst() == nullptr)
+ return;
+
+ SwNodeIndex aInsertPos((*pTextFrame->GetTextNodeFirst()));
+
+ SwTableNode* pTableNode = pTableFrame->GetTable()->GetTableNode();
+ if (pTableNode == nullptr)
+ return;
+
+ SwDoc& rDoc = pTextFrame->GetDoc();
+
+ // tdf#129176: clear "TablePosition" grab bag, since we explicitly change the position here
+ // See DomainMapperTableHandler::endTableGetTableStyle, where the grab bag is filled, and
+ // DocxAttributeOutput::TableDefinition that uses it on export
+ SwFrameFormat* pTableFormat = pTableFrame->GetTable()->GetFrameFormat();
+ assert(pTableFormat);
+ if (const SfxGrabBagItem* pGrabBagItem = pTableFormat->GetAttrSet().GetItem(RES_FRMATR_GRABBAG))
+ {
+ SfxGrabBagItem aGrabBagItem(*pGrabBagItem); // Editable copy
+ if (aGrabBagItem.GetGrabBag().erase("TablePosition"))
+ {
+ css::uno::Any aVal;
+ aGrabBagItem.QueryValue(aVal);
+ const auto xTable = SwXTextTable::CreateXTextTable(pTableFormat);
+ const css::uno::Reference<css::beans::XPropertySet> xSet(xTable, css::uno::UNO_QUERY);
+ assert(xSet);
+ xSet->setPropertyValue(UNO_NAME_TABLE_INTEROP_GRAB_BAG, aVal);
+ }
+ }
+
+ // When we move the table before the first text node, we need to clear RES_PAGEDESC attribute
+ // of the text node otherwise LO will create a page break after the table
+ if (pTextFrame->GetTextNodeFirst())
+ {
+ const SwPageDesc* pPageDesc
+ = pTextFrame->GetPageDescItem().GetPageDesc(); // First text node of the page has this
+ if (pPageDesc)
+ {
+ // First set the existing page desc for the table node
+ SfxItemSetFixed<RES_PAGEDESC, RES_PAGEDESC> aSet(
+ GetEditWin()->GetView().GetWrtShell().GetAttrPool());
+ aSet.Put(SwFormatPageDesc(pPageDesc));
+ SwPaM aPaMTable(*pTableNode);
+ rDoc.getIDocumentContentOperations().InsertItemSet(
+ aPaMTable, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame());
+
+ // Then remove pagedesc from the attributes of the text node
+ aSet.Put(SwFormatPageDesc(nullptr));
+ SwPaM aPaMTextNode(*pTextFrame->GetTextNodeFirst());
+ rDoc.getIDocumentContentOperations().InsertItemSet(
+ aPaMTextNode, aSet, SetAttrMode::DEFAULT, GetPageFrame()->getRootFrame());
+ }
+ }
+
+ // Move the table outside of the text frame
+ SwNodeRange aRange(*pTableNode, SwNodeOffset(0), *pTableNode->EndOfSectionNode(),
+ SwNodeOffset(1));
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRange, aInsertPos, SwMoveFlags::DEFAULT);
+
+ // Remove the floating table's frame
+ SwFlyFrameFormat* pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ rDoc.getIDocumentLayoutAccess().DelLayoutFormat(pFrameFormat);
+ }
+
+ rDoc.getIDocumentState().SetModified();
+
+ // Undoing MoveNodeRange() is not working correctly in case of tables, it crashes sometimes
+ // So don't allow to undo after unfloating (similar to MakeFlyAndMove() method)
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ rDoc.GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+}
+
+void UnfloatTableButton::PaintButton()
+{
+ if (!m_xVirDev)
+ return;
+
+ m_xVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ drawinglayer::primitive2d::Primitive2DContainer aSeq;
+ const ::tools::Rectangle aRect(
+ ::tools::Rectangle(Point(0, 0), m_xVirDev->PixelToLogic(GetSizePixel())));
+
+ // Create button
+ SwFrameButtonPainter::PaintButton(aSeq, aRect, true);
+
+ // Create the text primitive
+ basegfx::BColor aLineColor = SwViewOption::GetHeaderFooterMarkColor().getBColor();
+ basegfx::B2DVector aFontSize;
+ drawinglayer::attribute::FontAttribute aFontAttr
+ = drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, m_xVirDev->GetFont(),
+ false, false);
+
+ FontMetric aFontMetric = m_xVirDev->GetFontMetric(m_xVirDev->GetFont());
+ double nTextOffsetY = aFontMetric.GetAscent() + TEXT_PADDING;
+ double nTextOffsetX = std::abs(aRect.GetWidth() - m_xVirDev->GetTextWidth(m_sLabel)) / 2.0;
+ Point aTextPos(nTextOffsetX, nTextOffsetY);
+
+ basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(), static_cast<double>(aTextPos.X()),
+ static_cast<double>(aTextPos.Y())));
+
+ aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), std::vector<double>(), aFontAttr,
+ css::lang::Locale(), aLineColor)));
+
+ // Create the processor and process the primitives
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(*m_xVirDev,
+ aNewViewInfos));
+
+ pProcessor->process(aSeq);
+
+ m_xPushButton->set_custom_button(m_xVirDev.get());
+}
+
+void UnfloatTableButton::ShowAll(bool bShow) { Show(bShow); }
+
+bool UnfloatTableButton::Contains(const Point& rDocPt) const
+{
+ ::tools::Rectangle aRect(GetPosPixel(), GetSizePixel());
+ if (aRect.Contains(rDocPt))
+ return true;
+
+ return false;
+}
+
+void UnfloatTableButton::SetReadonly(bool bReadonly) { ShowAll(!bReadonly); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/uibase/docvw/edtdd.cxx b/sw/source/uibase/docvw/edtdd.cxx
new file mode 100644
index 000000000..8f86aa1d3
--- /dev/null
+++ b/sw/source/uibase/docvw/edtdd.cxx
@@ -0,0 +1,497 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svdview.hxx>
+#include <editeng/outliner.hxx>
+#include <svx/svdobj.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <sfx2/bindings.hxx>
+#include <vcl/commandevent.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/viewfrm.hxx>
+#include <fmturl.hxx>
+#include <frmfmt.hxx>
+#include <wrtsh.hxx>
+#include <edtdd.hxx>
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <swdtflvr.hxx>
+#include <swmodule.hxx>
+#include <docsh.hxx>
+#include <wdocsh.hxx>
+
+using namespace ::com::sun::star;
+
+// no include "dbgoutsw.hxx" here!!!!!!
+
+bool g_bExecuteDrag = false;
+
+void SwEditWin::StartDDTimer()
+{
+ m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, DDHandler));
+ m_aTimer.SetTimeout(480);
+ m_aTimer.Start();
+ g_bDDTimerStarted = true;
+}
+
+void SwEditWin::StopDDTimer(SwWrtShell *pSh, const Point &rPt)
+{
+ m_aTimer.Stop();
+ g_bDDTimerStarted = false;
+ if(!pSh->IsSelFrameMode())
+ pSh->CallSetCursor(&rPt, false);
+ m_aTimer.SetInvokeHandler(LINK(this,SwEditWin, TimerHandler));
+}
+
+void SwEditWin::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
+{
+ if (m_rView.GetObjectShell()->isContentExtractionLocked())
+ return;
+
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ if( rSh.GetDrawView() )
+ {
+ CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true );
+ if( rSh.GetDrawView()->Command( aDragEvent, this ) )
+ {
+ m_rView.GetViewFrame()->GetBindings().InvalidateAll(false);
+ return; // Event evaluated by SdrView
+ }
+ }
+
+ if ( m_pApplyTempl || rSh.IsDrawCreate() || IsDrawAction())
+ return;
+
+ bool bStart = false, bDelSelect = false;
+ SdrObject *pObj = nullptr;
+ Point aDocPos( PixelToLogic( rPosPixel ) );
+ const bool bInSelect = rSh.IsInSelect();
+ if (!bInSelect && rSh.TestCurrPam(aDocPos, true))
+ //We are not selecting and aren't at a selection
+ bStart = true;
+ else if ( !g_bFrameDrag && rSh.IsSelFrameMode() &&
+ rSh.IsInsideSelectedObj( aDocPos ) &&
+ nullptr == m_pAnchorMarker)
+ {
+ //We are not dragging internally and are not at an
+ //object (frame, draw object)
+
+ // #i106131# *and* AnchorDrag is *not* active: When active,
+ // entering global drag mode will destroy the AnchorHdl but
+ // keep the now invalid ptr in place, next access will crash.
+ // It is indeed wrong to enter drag mode when AnchorDrag is
+ // already active
+ bStart = true;
+ }
+ else if( !g_bFrameDrag && m_rView.GetDocShell()->IsReadOnly() &&
+ OBJCNT_NONE != rSh.GetObjCntType( aDocPos, pObj ))
+ {
+ rSh.LockPaint();
+ if( rSh.SelectObj( aDocPos, 0, pObj ))
+ bStart = bDelSelect = true;
+ else
+ rSh.UnlockPaint();
+ }
+ else if (!bInSelect)// tdf#116384 only drag hyperlink if user's not currently setting the selection
+ {
+ SwContentAtPos aSwContentAtPos( IsAttrAtPos::InetAttr );
+ bStart = rSh.GetContentAtPos( aDocPos,
+ aSwContentAtPos );
+ }
+
+ if ( !bStart || m_bIsInDrag )
+ return;
+
+ m_bMBPressed = false;
+ ReleaseMouse();
+ g_bFrameDrag = false;
+ g_bExecuteDrag = true;
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ m_aMovePos = aDocPos;
+ StartExecuteDrag();
+ if( bDelSelect )
+ {
+ rSh.UnSelectFrame();
+ rSh.UnlockPaint();
+ }
+}
+
+void SwEditWin::StartExecuteDrag()
+{
+ if( !g_bExecuteDrag || m_bIsInDrag )
+ return;
+
+ m_bIsInDrag = true;
+
+ rtl::Reference<SwTransferable> pTransfer = new SwTransferable( m_rView.GetWrtShell() );
+
+ pTransfer->StartDrag( this, m_aMovePos );
+}
+
+void SwEditWin::DragFinished()
+{
+ DropCleanup();
+ m_aTimer.SetInvokeHandler( LINK(this,SwEditWin, TimerHandler) );
+ m_bIsInDrag = false;
+}
+
+void SwEditWin::DropCleanup()
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+
+ // reset statuses
+ g_bNoInterrupt = false;
+ if ( m_bOldIdleSet )
+ {
+ rSh.GetViewOptions()->SetIdle( m_bOldIdle );
+ m_bOldIdleSet = false;
+ }
+ if ( m_pUserMarker )
+ CleanupDropUserMarker();
+ else
+ rSh.UnSetVisibleCursor();
+
+}
+
+void SwEditWin::CleanupDropUserMarker()
+{
+ if ( m_pUserMarker )
+ {
+ m_pUserMarker.reset();
+ m_pUserMarkerObj = nullptr;
+ }
+}
+
+//exhibition hack (MA,MBA)
+void SwView::SelectShellForDrop()
+{
+ if ( !GetCurShell() )
+ SelectShell();
+}
+
+sal_Int8 SwEditWin::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ GetView().SelectShellForDrop();
+ DropCleanup();
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ //A Drop to an open OutlinerView doesn't concern us (also see QueryDrop)
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ const Point aDocPt( PixelToLogic( rEvt.maPosPixel ));
+ SdrObject *pObj = nullptr;
+ OutlinerView* pOLV;
+ rSh.GetObjCntType( aDocPt, pObj );
+
+ if( pObj && nullptr != ( pOLV = rSh.GetDrawView()->GetTextEditOutlinerView() ))
+ {
+ tools::Rectangle aRect( pOLV->GetOutputArea() );
+ aRect.Union( pObj->GetLogicRect() );
+ const Point aPos = pOLV->GetWindow()->PixelToLogic(rEvt.maPosPixel);
+ if ( aRect.Contains(aPos) )
+ {
+ rSh.StartAllAction();
+ rSh.EndAllAction();
+ return nRet;
+ }
+ }
+
+ // There's a special treatment for file lists with a single
+ // element, that depends on the actual content of the
+ // Transferable to be accessible. Since the transferable
+ // may only be accessed after the drop has been accepted
+ // (according to KA due to Java D&D), we'll have to
+ // reevaluate the drop action once more _with_ the
+ // Transferable.
+ sal_uInt8 nEventAction;
+ sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
+ : rEvt.mnAction;
+ SotExchangeActionFlags nActionFlags;
+ m_nDropAction = SotExchange::GetExchangeAction(
+ GetDataFlavorExVector(),
+ m_nDropDestination,
+ rEvt.mnAction,
+ nUserOpt, m_nDropFormat, nEventAction, SotClipboardFormatId::NONE,
+ &rEvt.maDropEvent.Transferable,
+ &nActionFlags );
+
+ TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
+ nRet = rEvt.mnAction;
+ if( !SwTransferable::PasteData( aData, rSh, m_nDropAction, nActionFlags, m_nDropFormat,
+ m_nDropDestination, false, rEvt.mbDefault, &aDocPt, nRet))
+ nRet = DND_ACTION_NONE;
+ else if ( SW_MOD()->m_pDragDrop )
+ //Don't clean up anymore at internal D&D!
+ SW_MOD()->m_pDragDrop->SetCleanUp( false );
+
+ return nRet;
+}
+
+SotExchangeDest SwEditWin::GetDropDestination( const Point& rPixPnt, SdrObject ** ppObj )
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ const Point aDocPt( PixelToLogic( rPixPnt ) );
+ if( rSh.TestCurrPam( aDocPt )
+ || rSh.IsOverReadOnlyPos( aDocPt )
+ || rSh.DocPtInsideInputField( aDocPt ) )
+ return SotExchangeDest::NONE;
+
+ SdrObject *pObj = nullptr;
+ const ObjCntType eType = rSh.GetObjCntType( aDocPt, pObj );
+
+ //Drop to OutlinerView (TextEdit in Drawing) should decide it on its own!
+ if( pObj )
+ {
+ OutlinerView* pOLV = rSh.GetDrawView()->GetTextEditOutlinerView();
+ if ( pOLV )
+ {
+ tools::Rectangle aRect( pOLV->GetOutputArea() );
+ aRect.Union( pObj->GetLogicRect() );
+ const Point aPos = pOLV->GetWindow()->PixelToLogic( rPixPnt );
+ if( aRect.Contains( aPos ) )
+ return SotExchangeDest::NONE;
+ }
+ }
+
+ //What do we want to drop on now?
+ SotExchangeDest nDropDestination = SotExchangeDest::NONE;
+
+ //Did anything else arrive from the DrawingEngine?
+ if( OBJCNT_NONE != eType )
+ {
+ switch ( eType )
+ {
+ case OBJCNT_GRF:
+ {
+ bool bLink,
+ bIMap = nullptr != rSh.GetFormatFromObj( aDocPt )->GetURL().GetMap();
+ OUString aDummy;
+ rSh.GetGrfAtPos( aDocPt, aDummy, bLink );
+ if ( bLink && bIMap )
+ nDropDestination = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP;
+ else if ( bLink )
+ nDropDestination = SotExchangeDest::DOC_LNKD_GRAPHOBJ;
+ else if ( bIMap )
+ nDropDestination = SotExchangeDest::DOC_GRAPH_W_IMAP;
+ else
+ nDropDestination = SotExchangeDest::DOC_GRAPHOBJ;
+ }
+ break;
+ case OBJCNT_FLY:
+ if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr )
+ nDropDestination = SotExchangeDest::DOC_TEXTFRAME_WEB;
+ else
+ nDropDestination = SotExchangeDest::DOC_TEXTFRAME;
+ break;
+ case OBJCNT_OLE: nDropDestination = SotExchangeDest::DOC_OLEOBJ; break;
+ case OBJCNT_CONTROL: /* no Action avail */
+ case OBJCNT_SIMPLE: nDropDestination = SotExchangeDest::DOC_DRAWOBJ; break;
+ case OBJCNT_URLBUTTON: nDropDestination = SotExchangeDest::DOC_URLBUTTON; break;
+ case OBJCNT_GROUPOBJ: nDropDestination = SotExchangeDest::DOC_GROUPOBJ; break;
+
+ default: OSL_ENSURE( false, "new ObjectType?" );
+ }
+ }
+ if ( nDropDestination == SotExchangeDest::NONE )
+ {
+ if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr )
+ nDropDestination = SotExchangeDest::SWDOC_FREE_AREA_WEB;
+ else
+ nDropDestination = SotExchangeDest::SWDOC_FREE_AREA;
+ }
+ if( ppObj )
+ *ppObj = pObj;
+ return nDropDestination;
+}
+
+sal_Int8 SwEditWin::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ if( rEvt.mbLeaving )
+ {
+ DropCleanup();
+ return rEvt.mnAction;
+ }
+
+ if( m_rView.GetDocShell()->IsReadOnly() )
+ return DND_ACTION_NONE;
+
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+
+ Point aPixPt( rEvt.maPosPixel );
+
+ // If the cursor is near the inner boundary
+ // we attempt to scroll towards the desired direction.
+ tools::Rectangle aWin(Point(), GetOutputSizePixel());
+ const int nMargin = 10;
+ aWin.AdjustLeft(nMargin );
+ aWin.AdjustTop(nMargin );
+ aWin.AdjustRight( -nMargin );
+ aWin.AdjustBottom( -nMargin );
+ if(!aWin.Contains(aPixPt)) {
+ static sal_uInt64 last_tick = 0;
+ sal_uInt64 current_tick = tools::Time::GetSystemTicks();
+ if((current_tick-last_tick) > 500) {
+ last_tick = current_tick;
+ if(!m_bOldIdleSet) {
+ m_bOldIdle = rSh.GetViewOptions()->IsIdle();
+ rSh.GetViewOptions()->SetIdle(false);
+ m_bOldIdleSet = true;
+ }
+ CleanupDropUserMarker();
+ if(aPixPt.X() > aWin.Right()) aPixPt.AdjustX(nMargin );
+ if(aPixPt.X() < aWin.Left()) aPixPt.AdjustX( -nMargin );
+ if(aPixPt.Y() > aWin.Bottom()) aPixPt.AdjustY(nMargin );
+ if(aPixPt.Y() < aWin.Top()) aPixPt.AdjustY( -nMargin );
+ Point aDocPt(PixelToLogic(aPixPt));
+ SwRect rect(aDocPt,Size(1,1));
+ rSh.MakeVisible(rect);
+ }
+ }
+
+ if(m_bOldIdleSet) {
+ rSh.GetViewOptions()->SetIdle( m_bOldIdle );
+ m_bOldIdleSet = false;
+ }
+
+ SdrObject *pObj = nullptr;
+ m_nDropDestination = GetDropDestination( aPixPt, &pObj );
+ if( m_nDropDestination == SotExchangeDest::NONE )
+ return DND_ACTION_NONE;
+
+ sal_uInt8 nEventAction;
+ sal_Int8 nUserOpt = rEvt.mbDefault ? EXCHG_IN_ACTION_DEFAULT
+ : rEvt.mnAction;
+
+ m_nDropAction = SotExchange::GetExchangeAction(
+ GetDataFlavorExVector(),
+ m_nDropDestination,
+ rEvt.mnAction,
+ nUserOpt, m_nDropFormat, nEventAction );
+
+ if( EXCHG_INOUT_ACTION_NONE != m_nDropAction )
+ {
+ const Point aDocPt( PixelToLogic( aPixPt ) );
+
+ //With the default action we still want to have a say.
+ SwModule *pMod = SW_MOD();
+ if( pMod->m_pDragDrop )
+ {
+ bool bCleanup = false;
+ //Drawing objects in Headers/Footers are not allowed
+
+ SwWrtShell *pSrcSh = pMod->m_pDragDrop->GetShell();
+ if( (pSrcSh->GetSelFrameType() == FrameTypeFlags::DRAWOBJ) &&
+ pSrcSh->IsSelContainsControl() &&
+ (rSh.GetFrameType( &aDocPt, false ) & (FrameTypeFlags::HEADER|FrameTypeFlags::FOOTER)) )
+ {
+ bCleanup = true;
+ }
+ // don't more position protected objects!
+ else if( DND_ACTION_MOVE == rEvt.mnAction &&
+ pSrcSh->IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE )
+ {
+ bCleanup = true;
+ }
+ else if( rEvt.mbDefault )
+ {
+ // internal Drag&Drop: within same Doc a Move
+ // otherwise a Copy - Task 54974
+ nEventAction = pSrcSh->GetDoc() == rSh.GetDoc()
+ ? DND_ACTION_MOVE
+ : DND_ACTION_COPY;
+ }
+ if ( bCleanup )
+ {
+ CleanupDropUserMarker();
+ rSh.UnSetVisibleCursor();
+ return DND_ACTION_NONE;
+ }
+ }
+ else
+ {
+ //D&D from outside of SW should be a Copy per default.
+ if( EXCHG_IN_ACTION_DEFAULT == nEventAction &&
+ DND_ACTION_MOVE == rEvt.mnAction )
+ nEventAction = DND_ACTION_COPY;
+
+ if( (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == m_nDropFormat &&
+ EXCHG_IN_ACTION_LINK == m_nDropAction) ||
+ SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == m_nDropFormat )
+ {
+ SdrMarkView* pMView = rSh.GetDrawView();
+ if( pMView && !pMView->IsDesignMode() )
+ return DND_ACTION_NONE;
+ }
+ }
+
+ if ( EXCHG_IN_ACTION_DEFAULT != nEventAction )
+ nUserOpt = static_cast<sal_Int8>(nEventAction);
+
+ // show DropCursor or UserMarker ?
+ if( SotExchangeDest::SWDOC_FREE_AREA_WEB == m_nDropDestination ||
+ SotExchangeDest::SWDOC_FREE_AREA == m_nDropDestination )
+ {
+ CleanupDropUserMarker();
+ SwContentAtPos aCont( IsAttrAtPos::ContentCheck );
+ if(rSh.GetContentAtPos(aDocPt, aCont))
+ rSh.SwCursorShell::SetVisibleCursor( aDocPt );
+ }
+ else
+ {
+ rSh.UnSetVisibleCursor();
+
+ if ( m_pUserMarkerObj != pObj )
+ {
+ CleanupDropUserMarker();
+ m_pUserMarkerObj = pObj;
+
+ if(m_pUserMarkerObj)
+ {
+ m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), *m_pUserMarkerObj ));
+ }
+ }
+ }
+ return nUserOpt;
+ }
+
+ CleanupDropUserMarker();
+ rSh.UnSetVisibleCursor();
+ return DND_ACTION_NONE;
+}
+
+IMPL_LINK_NOARG(SwEditWin, DDHandler, Timer *, void)
+{
+ g_bDDTimerStarted = false;
+ m_aTimer.Stop();
+ m_aTimer.SetTimeout(240);
+ m_bMBPressed = false;
+ ReleaseMouse();
+ g_bFrameDrag = false;
+
+ if ( m_rView.GetViewFrame() )
+ {
+ g_bExecuteDrag = true;
+ StartExecuteDrag();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx
new file mode 100644
index 000000000..ef7070fd0
--- /dev/null
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -0,0 +1,6845 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <swtypes.hxx>
+#include <hintids.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
+#include <com/sun/star/i18n/UnicodeScript.hpp>
+#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
+#include <com/sun/star/ui/ContextMenuExecuteEvent.hpp>
+
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/string.hxx>
+
+#include <vcl/dialoghelper.hxx>
+#include <vcl/inputctx.hxx>
+#include <vcl/help.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <svl/macitem.hxx>
+#include <unotools/securityoptions.hxx>
+#include <basic/sbxvar.hxx>
+#include <svl/ctloptions.hxx>
+#include <basic/sbx.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svl/ptitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/svxacorr.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/colritem.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/datetime.hxx>
+
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+
+#include <editeng/acorrcfg.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <edtdd.hxx>
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <textboxhelper.hxx>
+#include <dcontact.hxx>
+#include <fldbas.hxx>
+#include <swmodule.hxx>
+#include <docsh.hxx>
+#include <viewopt.hxx>
+#include <drawbase.hxx>
+#include <dselect.hxx>
+#include <textsh.hxx>
+#include <shdwcrsr.hxx>
+#include <txatbase.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmthdft.hxx>
+#include <frmfmt.hxx>
+#include <modcfg.hxx>
+#include <fmtcol.hxx>
+#include <wview.hxx>
+#include <gloslst.hxx>
+#include <inputwin.hxx>
+#include <gloshdl.hxx>
+#include <swundo.hxx>
+#include <drwtxtsh.hxx>
+#include <fchrfmt.hxx>
+#include "romenu.hxx"
+#include <initui.hxx>
+#include <frmatr.hxx>
+#include <extinput.hxx>
+#include <acmplwrd.hxx>
+#include <swcalwrp.hxx>
+#include <swdtflvr.hxx>
+#include <breakit.hxx>
+#include <checkit.hxx>
+#include <pagefrm.hxx>
+
+#include <helpids.h>
+#include <cmdid.h>
+#include <uitool.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <charfmt.hxx>
+#include <numrule.hxx>
+#include <pagedesc.hxx>
+#include <svtools/ruler.hxx>
+#include <formatclipboard.hxx>
+#include <vcl/svapp.hxx>
+#include <wordcountdialog.hxx>
+#include <fmtfld.hxx>
+
+#include <IMark.hxx>
+#include <doc.hxx>
+#include <xmloff/odffields.hxx>
+
+#include <PostItMgr.hxx>
+#include <FrameControlsManager.hxx>
+#include <AnnotationWin.hxx>
+
+#include <algorithm>
+#include <vector>
+
+#include <rootfrm.hxx>
+
+#include <unotools/syslocaleoptions.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <salhelper/singletonref.hxx>
+#include <sfx2/event.hxx>
+#include <memory>
+
+#include "../../core/crsr/callnk.hxx"
+#include <IDocumentOutlineNodes.hxx>
+#include <ndtxt.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <strings.hrc>
+#include <textcontentcontrol.hxx>
+#include <contentcontrolbutton.hxx>
+
+using namespace sw::mark;
+using namespace ::com::sun::star;
+
+/**
+ * Globals
+ */
+static bool g_bInputLanguageSwitched = false;
+
+// Usually in MouseButtonUp a selection is revoked when the selection is
+// not currently being pulled open. Unfortunately in MouseButtonDown there
+// is being selected at double/triple click. That selection is completely
+// finished in the Handler and thus can't be distinguished in the Up.
+// To resolve this g_bHoldSelection is set in Down and evaluated in Up.
+static bool g_bHoldSelection = false;
+
+bool g_bFrameDrag = false;
+static bool g_bValidCursorPos = false;
+static bool g_bModePushed = false;
+bool g_bDDTimerStarted = false;
+bool g_bDDINetAttr = false;
+static SdrHdlKind g_eSdrMoveHdl = SdrHdlKind::User;
+
+QuickHelpData* SwEditWin::s_pQuickHlpData = nullptr;
+
+tools::Long SwEditWin::s_nDDStartPosY = 0;
+tools::Long SwEditWin::s_nDDStartPosX = 0;
+
+static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView );
+
+/// Check if the selected shape has a TextBox: if so, go into that instead.
+static bool lcl_goIntoTextBox(SwEditWin& rEditWin, SwWrtShell& rSh)
+{
+ SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0);
+ if (!pMark)
+ return false;
+
+ SdrObject* pSdrObject = pMark->GetMarkedSdrObj();
+ SwFrameFormat* pObjectFormat = ::FindFrameFormat(pSdrObject);
+ if (SwFrameFormat* pTextBoxFormat = SwTextBoxHelper::getOtherTextBoxFormat(pObjectFormat, RES_DRAWFRMFMT))
+ {
+ SdrObject* pTextBox = pTextBoxFormat->FindRealSdrObject();
+ SdrView* pSdrView = rSh.GetDrawView();
+ // Unmark the shape.
+ pSdrView->UnmarkAllObj();
+ // Mark the textbox.
+ rSh.SelectObj(Point(), SW_ALLOW_TEXTBOX, pTextBox);
+ // Clear the DrawFuncPtr.
+ rEditWin.StopInsFrame();
+ return true;
+ }
+ return false;
+}
+
+class SwAnchorMarker
+{
+ SdrHdl* m_pHdl;
+ Point m_aHdlPos;
+ Point m_aLastPos;
+ bool m_bTopRightHandle;
+public:
+ explicit SwAnchorMarker( SdrHdl* pH )
+ : m_pHdl( pH )
+ , m_aHdlPos( pH->GetPos() )
+ , m_aLastPos( pH->GetPos() )
+ , m_bTopRightHandle( pH->GetKind() == SdrHdlKind::Anchor_TR )
+ {}
+ const Point& GetLastPos() const { return m_aLastPos; }
+ void SetLastPos( const Point& rNew ) { m_aLastPos = rNew; }
+ void SetPos( const Point& rNew ) { m_pHdl->SetPos( rNew ); }
+ const Point& GetHdlPos() const { return m_aHdlPos; }
+ SdrHdl* GetHdl() const { return m_pHdl; }
+ void ChgHdl( SdrHdl* pNew )
+ {
+ m_pHdl = pNew;
+ if ( m_pHdl )
+ {
+ m_bTopRightHandle = (m_pHdl->GetKind() == SdrHdlKind::Anchor_TR);
+ }
+ }
+ Point GetPosForHitTest( const OutputDevice& rOut )
+ {
+ Point aHitTestPos( m_pHdl->GetPos() );
+ aHitTestPos = rOut.LogicToPixel( aHitTestPos );
+ if ( m_bTopRightHandle )
+ {
+ aHitTestPos += Point( -1, 1 );
+ }
+ else
+ {
+ aHitTestPos += Point( 1, 1 );
+ }
+ aHitTestPos = rOut.PixelToLogic( aHitTestPos );
+
+ return aHitTestPos;
+ }
+};
+
+/// Assists with auto-completion of AutoComplete words and AutoText names.
+struct QuickHelpData
+{
+ /// Strings that at least partially match an input word, and match length.
+ std::vector<std::pair<OUString, sal_uInt16>> m_aHelpStrings;
+ /// Index of the current help string.
+ sal_uInt16 nCurArrPos;
+ static constexpr sal_uInt16 nNoPos = std::numeric_limits<sal_uInt16>::max();
+
+ /// Help data stores AutoText names rather than AutoComplete words.
+ bool m_bIsAutoText;
+ /// Display help string as a tip rather than inline.
+ bool m_bIsTip;
+ /// Tip ID when a help string is displayed as a tip.
+ void* nTipId;
+ /// Append a space character to the displayed help string (if appropriate).
+ bool m_bAppendSpace;
+
+ /// Help string is currently displayed.
+ bool m_bIsDisplayed;
+
+ QuickHelpData() { ClearContent(); }
+
+ void Move( QuickHelpData& rCpy );
+ void ClearContent();
+ void Start(SwWrtShell& rSh, bool bRestart);
+ void Stop( SwWrtShell& rSh );
+
+ bool HasContent() const { return !m_aHelpStrings.empty() && nCurArrPos != nNoPos; }
+ const OUString& CurStr() const { return m_aHelpStrings[nCurArrPos].first; }
+ sal_uInt16 CurLen() const { return m_aHelpStrings[nCurArrPos].second; }
+
+ /// Next help string.
+ void Next( bool bEndLess )
+ {
+ if( ++nCurArrPos >= m_aHelpStrings.size() )
+ nCurArrPos = (bEndLess && !m_bIsAutoText ) ? 0 : nCurArrPos-1;
+ }
+ /// Previous help string.
+ void Previous( bool bEndLess )
+ {
+ if( 0 == nCurArrPos-- )
+ nCurArrPos = (bEndLess && !m_bIsAutoText ) ? m_aHelpStrings.size()-1 : 0;
+ }
+
+ // Fills internal structures with hopefully helpful information.
+ void FillStrArr( SwWrtShell const & rSh, const OUString& rWord );
+ void SortAndFilter(const OUString &rOrigWord);
+};
+
+/**
+ * Avoid minimal movement shiver
+ */
+#define HIT_PIX 2 /* hit tolerance in pixel */
+#define MIN_MOVE 4
+
+static bool IsMinMove(const Point &rStartPos, const Point &rLPt)
+{
+ return std::abs(rStartPos.X() - rLPt.X()) > MIN_MOVE ||
+ std::abs(rStartPos.Y() - rLPt.Y()) > MIN_MOVE;
+}
+
+/**
+ * For MouseButtonDown - determine whether a DrawObject
+ * a NO SwgFrame was hit! Shift/Ctrl should only result
+ * in selecting, with DrawObjects; at SwgFlys to trigger
+ * hyperlinks if applicable (Download/NewWindow!)
+ */
+static bool IsDrawObjSelectable( const SwWrtShell& rSh, const Point& rPt )
+{
+ bool bRet = true;
+ SdrObject* pObj;
+ switch( rSh.GetObjCntType( rPt, pObj ))
+ {
+ case OBJCNT_NONE:
+ case OBJCNT_FLY:
+ case OBJCNT_GRF:
+ case OBJCNT_OLE:
+ bRet = false;
+ break;
+ default:; //prevent warning
+ }
+ return bRet;
+}
+
+/*
+ * Switch pointer
+ */
+void SwEditWin::UpdatePointer(const Point &rLPt, sal_uInt16 nModifier )
+{
+ SetQuickHelpText(OUString());
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ if( m_pApplyTempl )
+ {
+ PointerStyle eStyle = PointerStyle::Fill;
+ if ( rSh.IsOverReadOnlyPos( rLPt ) )
+ {
+ m_pUserMarker.reset();
+
+ eStyle = PointerStyle::NotAllowed;
+ }
+ else
+ {
+ SwRect aRect;
+ SwRect* pRect = &aRect;
+ const SwFrameFormat* pFormat = nullptr;
+
+ bool bFrameIsValidTarget = false;
+ if( m_pApplyTempl->m_pFormatClipboard )
+ bFrameIsValidTarget = m_pApplyTempl->m_pFormatClipboard->HasContentForThisType( SelectionType::Frame );
+ else if( !m_pApplyTempl->nColor )
+ bFrameIsValidTarget = ( m_pApplyTempl->eType == SfxStyleFamily::Frame );
+
+ if( bFrameIsValidTarget &&
+ nullptr !=(pFormat = rSh.GetFormatFromObj( rLPt, &pRect )) &&
+ dynamic_cast<const SwFlyFrameFormat*>( pFormat) )
+ {
+ //turn on highlight for frame
+ tools::Rectangle aTmp( pRect->SVRect() );
+
+ if ( !m_pUserMarker )
+ {
+ m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp ));
+ }
+ }
+ else
+ {
+ m_pUserMarker.reset();
+ }
+
+ rSh.SwCursorShell::SetVisibleCursor( rLPt );
+ }
+ SetPointer( eStyle );
+ return;
+ }
+
+ if( !rSh.VisArea().Width() )
+ return;
+
+ CurrShell aCurr(&rSh);
+
+ if ( IsChainMode() )
+ {
+ SwRect aRect;
+ SwChainRet nChainable = rSh.Chainable( aRect, *rSh.GetFlyFrameFormat(), rLPt );
+ PointerStyle eStyle = nChainable != SwChainRet::OK
+ ? PointerStyle::ChainNotAllowed : PointerStyle::Chain;
+ if ( nChainable == SwChainRet::OK )
+ {
+ tools::Rectangle aTmp( aRect.SVRect() );
+
+ if ( !m_pUserMarker )
+ {
+ m_pUserMarker.reset(new SdrDropMarkerOverlay( *rSh.GetDrawView(), aTmp ));
+ }
+ }
+ else
+ {
+ m_pUserMarker.reset();
+ }
+
+ SetPointer( eStyle );
+ return;
+ }
+
+ bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly();
+ if ( !bExecHyperlinks )
+ {
+ const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink );
+ if ( ( bSecureOption && nModifier == KEY_MOD1 ) ||
+ ( !bSecureOption && nModifier != KEY_MOD1 ) )
+ bExecHyperlinks = true;
+ }
+
+ const bool bExecSmarttags = nModifier == KEY_MOD1;
+
+ SdrView *pSdrView = rSh.GetDrawView();
+ bool bPrefSdrPointer = false;
+ bool bHitHandle = false;
+ bool bCntAtPos = false;
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() &&
+ rSh.IsCursorReadonly();
+ m_aActHitType = SdrHitKind::NONE;
+ PointerStyle eStyle = PointerStyle::Text;
+ if ( !pSdrView )
+ bCntAtPos = true;
+ else if ( (bHitHandle = (pSdrView->PickHandle(rLPt) != nullptr)) )
+ {
+ m_aActHitType = SdrHitKind::Object;
+ bPrefSdrPointer = true;
+ }
+ else
+ {
+ const bool bNotInSelObj = !rSh.IsInsideSelectedObj( rLPt );
+ if ( m_rView.GetDrawFuncPtr() && !m_bInsDraw && bNotInSelObj )
+ {
+ m_aActHitType = SdrHitKind::Object;
+ if (IsObjectSelect())
+ eStyle = PointerStyle::Arrow;
+ else
+ bPrefSdrPointer = true;
+ }
+ else
+ {
+ SdrPageView* pPV = nullptr;
+ pSdrView->SetHitTolerancePixel( HIT_PIX );
+ SdrObject* pObj = (bNotInSelObj && bExecHyperlinks) ?
+ pSdrView->PickObj(rLPt, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) :
+ nullptr;
+ if (pObj)
+ {
+ SdrObjMacroHitRec aTmp;
+ aTmp.aPos = rLPt;
+ aTmp.pPageView = pPV;
+ SetPointer( pObj->GetMacroPointer( aTmp ) );
+ return;
+ }
+ else
+ {
+ // dvo: IsObjSelectable() eventually calls SdrView::PickObj, so
+ // apparently this is used to determine whether this is a
+ // drawling layer object or not.
+ if ( rSh.IsObjSelectable( rLPt ) )
+ {
+ if (pSdrView->IsTextEdit())
+ {
+ m_aActHitType = SdrHitKind::NONE;
+ bPrefSdrPointer = true;
+ }
+ else
+ {
+ SdrViewEvent aVEvt;
+ SdrHitKind eHit = pSdrView->PickAnything(rLPt, aVEvt);
+
+ if (eHit == SdrHitKind::UrlField && bExecHyperlinks)
+ {
+ m_aActHitType = SdrHitKind::Object;
+ bPrefSdrPointer = true;
+ }
+ else
+ {
+ // if we're over a selected object, we show an
+ // ARROW by default. We only show a MOVE if 1) the
+ // object is selected, and 2) it may be moved
+ // (i.e., position is not protected).
+ bool bMovable =
+ (!bNotInSelObj) &&
+ (rSh.IsObjSelected() || rSh.IsFrameSelected()) &&
+ (rSh.IsSelObjProtected(FlyProtectFlags::Pos) == FlyProtectFlags::NONE);
+
+ SdrObject* pSelectableObj = rSh.GetObjAt(rLPt);
+ // Don't update pointer if this is a background image only.
+ if (pSelectableObj->GetLayer() != rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId())
+ eStyle = bMovable ? PointerStyle::Move : PointerStyle::Arrow;
+ m_aActHitType = SdrHitKind::Object;
+ }
+ }
+ }
+ else
+ {
+ if ( rSh.IsFrameSelected() && !bNotInSelObj )
+ {
+ // dvo: this branch appears to be dead and should be
+ // removed in a future version. Reason: The condition
+ // !bNotInSelObj means that this branch will only be
+ // executed in the cursor points inside a selected
+ // object. However, if this is the case, the previous
+ // if( rSh.IsObjSelectable(rLPt) ) must always be true:
+ // rLPt is inside a selected object, then obviously
+ // rLPt is over a selectable object.
+ if (rSh.IsSelObjProtected(FlyProtectFlags::Size) != FlyProtectFlags::NONE)
+ eStyle = PointerStyle::NotAllowed;
+ else
+ eStyle = PointerStyle::Move;
+ m_aActHitType = SdrHitKind::Object;
+ }
+ else
+ {
+ if ( m_rView.GetDrawFuncPtr() )
+ bPrefSdrPointer = true;
+ else
+ bCntAtPos = true;
+ }
+ }
+ }
+ }
+ }
+ if ( bPrefSdrPointer )
+ {
+ if (bIsDocReadOnly || (rSh.IsObjSelected() && rSh.IsSelObjProtected(FlyProtectFlags::Content) != FlyProtectFlags::NONE))
+ SetPointer( PointerStyle::NotAllowed );
+ else
+ {
+ if (m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->IsInsertForm() && !bHitHandle)
+ SetPointer( PointerStyle::DrawRect );
+ else
+ SetPointer( pSdrView->GetPreferredPointer( rLPt, rSh.GetOut() ) );
+ }
+ }
+ else
+ {
+ if( !rSh.IsPageAtPos( rLPt ) || m_pAnchorMarker )
+ eStyle = PointerStyle::Arrow;
+ else
+ {
+ // Even if we already have something, prefer URLs if possible.
+ SwContentAtPos aUrlPos(IsAttrAtPos::InetAttr);
+ if (bCntAtPos || rSh.GetContentAtPos(rLPt, aUrlPos))
+ {
+ SwContentAtPos aSwContentAtPos(
+ IsAttrAtPos::Field |
+ IsAttrAtPos::ClickField |
+ IsAttrAtPos::InetAttr |
+ IsAttrAtPos::Ftn |
+ IsAttrAtPos::SmartTag);
+ if( rSh.GetContentAtPos( rLPt, aSwContentAtPos) )
+ {
+ // Is edit inline input field
+ if (IsAttrAtPos::Field == aSwContentAtPos.eContentAtPos
+ && aSwContentAtPos.pFndTextAttr != nullptr
+ && aSwContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD)
+ {
+ const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr;
+ if (!(pCursorField && pCursorField == aSwContentAtPos.pFndTextAttr->GetFormatField().GetField()))
+ eStyle = PointerStyle::RefHand;
+ }
+ else
+ {
+ const bool bClickToFollow = IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos ||
+ IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos;
+ if( !bClickToFollow ||
+ (IsAttrAtPos::InetAttr == aSwContentAtPos.eContentAtPos && bExecHyperlinks) ||
+ (IsAttrAtPos::SmartTag == aSwContentAtPos.eContentAtPos && bExecSmarttags) )
+ eStyle = PointerStyle::RefHand;
+ }
+ }
+ else if (GetView().GetWrtShell().GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ aSwContentAtPos.eContentAtPos = IsAttrAtPos::Outline;
+ if (rSh.GetContentAtPos(rLPt, aSwContentAtPos))
+ {
+ if (IsAttrAtPos::Outline == aSwContentAtPos.eContentAtPos)
+ {
+ if (nModifier == KEY_MOD1)
+ {
+ eStyle = PointerStyle::RefHand;
+ // set quick help
+ if(aSwContentAtPos.aFnd.pNode && aSwContentAtPos.aFnd.pNode->IsTextNode())
+ {
+ const SwNodes& rNds = GetView().GetWrtShell().GetDoc()->GetNodes();
+ SwOutlineNodes::size_type nPos;
+ rNds.GetOutLineNds().Seek_Entry(aSwContentAtPos.aFnd.pNode->GetTextNode(), &nPos);
+ SwOutlineNodes::size_type nOutlineNodesCount
+ = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+ int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos);
+ OUString sQuickHelp(SwResId(STR_CLICK_OUTLINE_CONTENT_TOGGLE_VISIBILITY));
+ if (!rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent()
+ && nPos + 1 < nOutlineNodesCount
+ && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos + 1) > nLevel)
+ sQuickHelp += " (" + SwResId(STR_CLICK_OUTLINE_CONTENT_TOGGLE_VISIBILITY_EXT) + ")";
+ SetQuickHelpText(sQuickHelp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // which kind of text pointer have we to show - horz / vert - ?
+ if( PointerStyle::Text == eStyle && rSh.IsInVerticalText( &rLPt ))
+ eStyle = PointerStyle::TextVertical;
+ else if (rSh.GetViewOptions()->CanHideWhitespace() &&
+ rSh.GetLayout()->IsBetweenPages(rLPt))
+ {
+ if (rSh.GetViewOptions()->IsHideWhitespaceMode())
+ eStyle = PointerStyle::ShowWhitespace;
+ else
+ eStyle = PointerStyle::HideWhitespace;
+ }
+
+ SetPointer( eStyle );
+ }
+}
+
+/**
+ * Increase timer for selection
+ */
+IMPL_LINK_NOARG(SwEditWin, TimerHandler, Timer *, void)
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ Point aModPt( m_aMovePos );
+ const SwRect aOldVis( rSh.VisArea() );
+ bool bDone = false;
+
+ if ( !rSh.VisArea().Contains( aModPt ) )
+ {
+ if ( m_bInsDraw )
+ {
+ const int nMaxScroll = 40;
+ m_rView.Scroll( tools::Rectangle(aModPt,Size(1,1)), nMaxScroll, nMaxScroll);
+ bDone = true;
+ }
+ else if ( g_bFrameDrag )
+ {
+ rSh.Drag(&aModPt, false);
+ bDone = true;
+ }
+ if ( !bDone )
+ aModPt = rSh.GetContentPos( aModPt,aModPt.Y() > rSh.VisArea().Bottom() );
+ }
+ if ( !bDone && !(g_bFrameDrag || m_bInsDraw) )
+ {
+ if ( m_xRowColumnSelectionStart )
+ {
+ Point aPos( aModPt );
+ rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag );
+ }
+ else
+ rSh.CallSetCursor( &aModPt, false );
+
+ // It can be that a "jump" over a table cannot be accomplished like
+ // that. So we jump over the table by Up/Down here.
+ const SwRect& rVisArea = rSh.VisArea();
+ if( aOldVis == rVisArea && !rSh.IsStartOfDoc() && !rSh.IsEndOfDoc() )
+ {
+ // take the center point of VisArea to
+ // decide in which direction the user want.
+ if( aModPt.Y() < ( rVisArea.Top() + rVisArea.Height() / 2 ) )
+ rSh.Up( true );
+ else
+ rSh.Down( true );
+ }
+ }
+
+ m_aMovePos += rSh.VisArea().Pos() - aOldVis.Pos();
+ JustifyAreaTimer();
+}
+
+void SwEditWin::JustifyAreaTimer()
+{
+ const tools::Rectangle &rVisArea = GetView().GetVisArea();
+#ifdef UNX
+ const tools::Long coMinLen = 100;
+#else
+ const tools::Long coMinLen = 50;
+#endif
+ tools::Long const nTimeout = 800,
+ nDiff = std::max(
+ std::max( m_aMovePos.Y() - rVisArea.Bottom(), rVisArea.Top() - m_aMovePos.Y() ),
+ std::max( m_aMovePos.X() - rVisArea.Right(), rVisArea.Left() - m_aMovePos.X()));
+ m_aTimer.SetTimeout( std::max( coMinLen, nTimeout - nDiff*2L) );
+}
+
+void SwEditWin::LeaveArea(const Point &rPos)
+{
+ m_aMovePos = rPos;
+ JustifyAreaTimer();
+ if( !m_aTimer.IsActive() )
+ m_aTimer.Start();
+ m_pShadCursor.reset();
+}
+
+inline void SwEditWin::EnterArea()
+{
+ m_aTimer.Stop();
+}
+
+/**
+ * Insert mode for frames
+ */
+void SwEditWin::InsFrame(sal_uInt16 nCols)
+{
+ StdDrawMode( SdrObjKind::NONE, false );
+ m_bInsFrame = true;
+ m_nInsFrameColCount = nCols;
+}
+
+void SwEditWin::StdDrawMode( SdrObjKind eSdrObjectKind, bool bObjSelect )
+{
+ SetSdrDrawMode( eSdrObjectKind );
+
+ if (bObjSelect)
+ m_rView.SetDrawFuncPtr(std::make_unique<DrawSelection>( &m_rView.GetWrtShell(), this, &m_rView ));
+ else
+ m_rView.SetDrawFuncPtr(std::make_unique<SwDrawBase>( &m_rView.GetWrtShell(), this, &m_rView ));
+
+ m_rView.SetSelDrawSlot();
+ SetSdrDrawMode( eSdrObjectKind );
+ if (bObjSelect)
+ m_rView.GetDrawFuncPtr()->Activate( SID_OBJECT_SELECT );
+ else
+ m_rView.GetDrawFuncPtr()->Activate( sal::static_int_cast< sal_uInt16 >(eSdrObjectKind) );
+ m_bInsFrame = false;
+ m_nInsFrameColCount = 1;
+}
+
+void SwEditWin::StopInsFrame()
+{
+ if (m_rView.GetDrawFuncPtr())
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ }
+ m_rView.LeaveDrawCreate(); // leave construction mode
+ m_bInsFrame = false;
+ m_nInsFrameColCount = 1;
+}
+
+bool SwEditWin::IsInputSequenceCheckingRequired( const OUString &rText, const SwPaM& rCursor )
+{
+ const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions();
+ if ( !rCTLOptions.IsCTLFontEnabled() ||
+ !rCTLOptions.IsCTLSequenceChecking() )
+ return false;
+
+ if ( 0 == rCursor.Start()->nContent.GetIndex() ) /* first char needs not to be checked */
+ return false;
+
+ SwBreakIt *pBreakIter = SwBreakIt::Get();
+ uno::Reference < i18n::XBreakIterator > xBI = pBreakIter->GetBreakIter();
+ assert(xBI.is());
+ tools::Long nCTLScriptPos = -1;
+
+ if (xBI->getScriptType( rText, 0 ) == i18n::ScriptType::COMPLEX)
+ nCTLScriptPos = 0;
+ else
+ nCTLScriptPos = xBI->nextScript( rText, 0, i18n::ScriptType::COMPLEX );
+
+ return (0 <= nCTLScriptPos && nCTLScriptPos <= rText.getLength());
+}
+
+//return INVALID_HINT if language should not be explicitly overridden, the correct
+//HintId to use for the eBufferLanguage otherwise
+static sal_uInt16 lcl_isNonDefaultLanguage(LanguageType eBufferLanguage, SwView const & rView,
+ const OUString &rInBuffer)
+{
+ sal_uInt16 nWhich = INVALID_HINT;
+
+ //If the option to IgnoreLanguageChange is set, short-circuit this method
+ //which results in the document/paragraph language remaining the same
+ //despite a change to the keyboard/input language
+ SvtSysLocaleOptions aSysLocaleOptions;
+ if(aSysLocaleOptions.IsIgnoreLanguageChange())
+ {
+ return INVALID_HINT;
+ }
+
+ bool bLang = true;
+ if(eBufferLanguage != LANGUAGE_DONTKNOW)
+ {
+ switch( SvtLanguageOptions::GetI18NScriptTypeOfLanguage( eBufferLanguage ))
+ {
+ case i18n::ScriptType::ASIAN: nWhich = RES_CHRATR_CJK_LANGUAGE; break;
+ case i18n::ScriptType::COMPLEX: nWhich = RES_CHRATR_CTL_LANGUAGE; break;
+ case i18n::ScriptType::LATIN: nWhich = RES_CHRATR_LANGUAGE; break;
+ default: bLang = false;
+ }
+ if(bLang)
+ {
+ SfxItemSet aLangSet(rView.GetPool(), nWhich, nWhich);
+ SwWrtShell& rSh = rView.GetWrtShell();
+ rSh.GetCurAttr(aLangSet);
+ if(SfxItemState::DEFAULT <= aLangSet.GetItemState(nWhich))
+ {
+ LanguageType eLang = static_cast<const SvxLanguageItem&>(aLangSet.Get(nWhich)).GetLanguage();
+ if ( eLang == eBufferLanguage )
+ {
+ // current language attribute equal to language reported from system
+ bLang = false;
+ }
+ else if ( !g_bInputLanguageSwitched && RES_CHRATR_LANGUAGE == nWhich )
+ {
+ // special case: switching between two "LATIN" languages
+ // In case the current keyboard setting might be suitable
+ // for both languages we can't safely assume that the user
+ // wants to use the language reported from the system,
+ // except if we knew that it was explicitly switched (thus
+ // the check for "bInputLangeSwitched").
+
+ // The language reported by the system could be just the
+ // system default language that the user is not even aware
+ // of, because no language selection tool is installed at
+ // all. In this case the OOo language should get preference
+ // as it might have been selected by the user explicitly.
+
+ // Usually this case happens if the OOo language is
+ // different to the system language but the system keyboard
+ // is still suitable for the OOo language (e.g. writing
+ // English texts with a German keyboard).
+
+ // For non-latin keyboards overwriting the attribute is
+ // still valid. We do this for cyrillic and greek ATM. In
+ // future versions of OOo this should be replaced by a
+ // configuration switch that allows to give the preference
+ // to the OOo setting or the system setting explicitly
+ // and/or a better handling of the script type.
+ i18n::UnicodeScript eType = !rInBuffer.isEmpty() ?
+ GetAppCharClass().getScript( rInBuffer, 0 ) :
+ i18n::UnicodeScript_kScriptCount;
+
+ bool bSystemIsNonLatin = false;
+ switch ( eType )
+ {
+ case i18n::UnicodeScript_kGreek:
+ case i18n::UnicodeScript_kCyrillic:
+ // in case other UnicodeScripts require special
+ // keyboards they can be added here
+ bSystemIsNonLatin = true;
+ break;
+ default:
+ break;
+ }
+
+ bool bOOoLangIsNonLatin = MsLangId::isNonLatinWestern( eLang);
+
+ bLang = (bSystemIsNonLatin != bOOoLangIsNonLatin);
+ }
+ }
+ }
+ }
+ return bLang ? nWhich : INVALID_HINT;
+}
+
+/**
+ * Character buffer is inserted into the document
+ */
+void SwEditWin::FlushInBuffer()
+{
+ if ( m_aKeyInputFlushTimer.IsActive())
+ m_aKeyInputFlushTimer.Stop();
+
+ if ( m_aInBuffer.isEmpty() )
+ return;
+
+ SwWrtShell& rSh = m_rView.GetWrtShell();
+
+ // generate new sequence input checker if not already done
+ if ( !pCheckIt )
+ pCheckIt = new SwCheckIt;
+
+ uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = pCheckIt->xCheck;
+ if ( xISC.is() && IsInputSequenceCheckingRequired( m_aInBuffer, *rSh.GetCursor() ) )
+ {
+
+ // apply (Thai) input sequence checking/correction
+
+ rSh.Push(); // push current cursor to stack
+
+ // get text from the beginning (i.e left side) of current selection
+ // to the start of the paragraph
+ rSh.NormalizePam(); // make point be the first (left) one
+ if (!rSh.GetCursor()->HasMark())
+ rSh.GetCursor()->SetMark();
+ rSh.GetCursor()->GetMark()->nContent = 0;
+
+ const OUString aOldText( rSh.GetCursor()->GetText() );
+ const sal_Int32 nOldLen = aOldText.getLength();
+
+ SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions();
+
+ sal_Int32 nExpandSelection = 0;
+ if (nOldLen > 0)
+ {
+ sal_Int32 nTmpPos = nOldLen;
+ sal_Int16 nCheckMode = rCTLOptions.IsCTLSequenceCheckingRestricted() ?
+ i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
+
+ OUString aNewText( aOldText );
+ if (rCTLOptions.IsCTLSequenceCheckingTypeAndReplace())
+ {
+ for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k)
+ {
+ const sal_Unicode cChar = m_aInBuffer[k];
+ const sal_Int32 nPrevPos =xISC->correctInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode );
+
+ // valid sequence or sequence could be corrected:
+ if (nPrevPos != aNewText.getLength())
+ nTmpPos = nPrevPos + 1;
+ }
+
+ // find position of first character that has changed
+ sal_Int32 nNewLen = aNewText.getLength();
+ const sal_Unicode *pOldText = aOldText.getStr();
+ const sal_Unicode *pNewText = aNewText.getStr();
+ sal_Int32 nChgPos = 0;
+ while ( nChgPos < nOldLen && nChgPos < nNewLen &&
+ pOldText[nChgPos] == pNewText[nChgPos] )
+ ++nChgPos;
+
+ const sal_Int32 nChgLen = nNewLen - nChgPos;
+ if (nChgLen)
+ {
+ m_aInBuffer = aNewText.copy( nChgPos, nChgLen );
+ nExpandSelection = nOldLen - nChgPos;
+ }
+ else
+ m_aInBuffer.clear();
+ }
+ else
+ {
+ for( sal_Int32 k = 0; k < m_aInBuffer.getLength(); ++k )
+ {
+ const sal_Unicode cChar = m_aInBuffer[k];
+ if (xISC->checkInputSequence( aNewText, nTmpPos - 1, cChar, nCheckMode ))
+ {
+ // character can be inserted:
+ aNewText += OUStringChar( cChar );
+ ++nTmpPos;
+ }
+ }
+ m_aInBuffer = aNewText.copy( aOldText.getLength() ); // copy new text to be inserted to buffer
+ }
+ }
+
+ // at this point now we will insert the buffer text 'normally' some lines below...
+
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
+
+ if (m_aInBuffer.isEmpty())
+ return;
+
+ // if text prior to the original selection needs to be changed
+ // as well, we now expand the selection accordingly.
+ SwPaM &rCursor = *rSh.GetCursor();
+ const sal_Int32 nCursorStartPos = rCursor.Start()->nContent.GetIndex();
+ OSL_ENSURE( nCursorStartPos >= nExpandSelection, "cannot expand selection as specified!!" );
+ if (nExpandSelection && nCursorStartPos >= nExpandSelection)
+ {
+ if (!rCursor.HasMark())
+ rCursor.SetMark();
+ rCursor.Start()->nContent -= nExpandSelection;
+ }
+ }
+
+ uno::Reference< frame::XDispatchRecorder > xRecorder =
+ m_rView.GetViewFrame()->GetBindings().GetRecorder();
+ if ( xRecorder.is() )
+ {
+ // determine shell
+ SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView );
+ // generate request and record
+ if (pSfxShell)
+ {
+ SfxRequest aReq( m_rView.GetViewFrame(), FN_INSERT_STRING );
+ aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, m_aInBuffer ) );
+ aReq.Done();
+ }
+ }
+
+ sal_uInt16 nWhich = lcl_isNonDefaultLanguage(m_eBufferLanguage, m_rView, m_aInBuffer);
+ if (nWhich != INVALID_HINT )
+ {
+ SvxLanguageItem aLangItem( m_eBufferLanguage, nWhich );
+ rSh.SetAttrItem( aLangItem );
+ }
+
+ rSh.Insert( m_aInBuffer );
+ m_eBufferLanguage = LANGUAGE_DONTKNOW;
+ m_aInBuffer.clear();
+}
+
+#define MOVE_LEFT_SMALL 0
+#define MOVE_UP_SMALL 1
+#define MOVE_RIGHT_BIG 2
+#define MOVE_DOWN_BIG 3
+#define MOVE_LEFT_BIG 4
+#define MOVE_UP_BIG 5
+#define MOVE_RIGHT_SMALL 6
+#define MOVE_DOWN_SMALL 7
+
+// #i121236# Support for shift key in writer
+#define MOVE_LEFT_HUGE 8
+#define MOVE_UP_HUGE 9
+#define MOVE_RIGHT_HUGE 10
+#define MOVE_DOWN_HUGE 11
+
+void SwEditWin::ChangeFly( sal_uInt8 nDir, bool bWeb )
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ SwRect aTmp = rSh.GetFlyRect();
+ if( !aTmp.HasArea() ||
+ rSh.IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE )
+ return;
+
+ SfxItemSetFixed<
+ RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_ANCHOR,
+ RES_COL, RES_COL,
+ RES_FOLLOW_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW>
+ aSet( rSh.GetAttrPool() );
+ rSh.GetFlyFrameAttr( aSet );
+ RndStdIds eAnchorId = aSet.Get(RES_ANCHOR).GetAnchorId();
+ Size aSnap;
+ bool bHuge(MOVE_LEFT_HUGE == nDir ||
+ MOVE_UP_HUGE == nDir ||
+ MOVE_RIGHT_HUGE == nDir ||
+ MOVE_DOWN_HUGE == nDir);
+
+ if(MOVE_LEFT_SMALL == nDir ||
+ MOVE_UP_SMALL == nDir ||
+ MOVE_RIGHT_SMALL == nDir ||
+ MOVE_DOWN_SMALL == nDir )
+ {
+ aSnap = PixelToLogic(Size(1,1));
+ }
+ else
+ {
+ aSnap = rSh.GetViewOptions()->GetSnapSize();
+ short nDiv = rSh.GetViewOptions()->GetDivisionX();
+ if ( nDiv > 0 )
+ aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) );
+ nDiv = rSh.GetViewOptions()->GetDivisionY();
+ if ( nDiv > 0 )
+ aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) );
+ }
+
+ if(bHuge)
+ {
+ // #i121236# 567twips == 1cm, but just take three times the normal snap
+ aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3);
+ }
+
+ SwRect aBoundRect;
+ Point aRefPoint;
+ // adjustment for allowing vertical position
+ // aligned to page for fly frame anchored to paragraph or to character.
+ {
+ const SwFormatVertOrient& aVert( aSet.Get(RES_VERT_ORIENT) );
+ const bool bFollowTextFlow =
+ aSet.Get(RES_FOLLOW_TEXT_FLOW).GetValue();
+ const SwPosition* pToCharContentPos = aSet.Get(RES_ANCHOR).GetContentAnchor();
+ rSh.CalcBoundRect( aBoundRect, eAnchorId,
+ text::RelOrientation::FRAME, aVert.GetRelationOrient(),
+ pToCharContentPos, bFollowTextFlow,
+ false, &aRefPoint );
+ }
+ tools::Long nLeft = std::min( aTmp.Left() - aBoundRect.Left(), aSnap.Width() );
+ tools::Long nRight = std::min( aBoundRect.Right() - aTmp.Right(), aSnap.Width() );
+ tools::Long nUp = std::min( aTmp.Top() - aBoundRect.Top(), aSnap.Height() );
+ tools::Long nDown = std::min( aBoundRect.Bottom() - aTmp.Bottom(), aSnap.Height() );
+
+ switch ( nDir )
+ {
+ case MOVE_LEFT_BIG:
+ case MOVE_LEFT_HUGE:
+ case MOVE_LEFT_SMALL: aTmp.Left( aTmp.Left() - nLeft );
+ break;
+
+ case MOVE_UP_BIG:
+ case MOVE_UP_HUGE:
+ case MOVE_UP_SMALL: aTmp.Top( aTmp.Top() - nUp );
+ break;
+
+ case MOVE_RIGHT_SMALL:
+ if( aTmp.Width() < aSnap.Width() + MINFLY )
+ break;
+ nRight = aSnap.Width();
+ [[fallthrough]];
+ case MOVE_RIGHT_HUGE:
+ case MOVE_RIGHT_BIG: aTmp.Left( aTmp.Left() + nRight );
+ break;
+
+ case MOVE_DOWN_SMALL:
+ if( aTmp.Height() < aSnap.Height() + MINFLY )
+ break;
+ nDown = aSnap.Height();
+ [[fallthrough]];
+ case MOVE_DOWN_HUGE:
+ case MOVE_DOWN_BIG: aTmp.Top( aTmp.Top() + nDown );
+ break;
+
+ default: OSL_ENSURE(true, "ChangeFly: Unknown direction." );
+ }
+ bool bSet = false;
+ if ((RndStdIds::FLY_AS_CHAR == eAnchorId) && ( nDir % 2 ))
+ {
+ tools::Long aDiff = aTmp.Top() - aRefPoint.Y();
+ if( aDiff > 0 )
+ aDiff = 0;
+ else if ( aDiff < -aTmp.Height() )
+ aDiff = -aTmp.Height();
+ SwFormatVertOrient aVert( aSet.Get(RES_VERT_ORIENT) );
+ sal_Int16 eNew;
+ if( bWeb )
+ {
+ eNew = aVert.GetVertOrient();
+ bool bDown = 0 != ( nDir & 0x02 );
+ switch( eNew )
+ {
+ case text::VertOrientation::CHAR_TOP:
+ if( bDown ) eNew = text::VertOrientation::CENTER;
+ break;
+ case text::VertOrientation::CENTER:
+ eNew = bDown ? text::VertOrientation::TOP : text::VertOrientation::CHAR_TOP;
+ break;
+ case text::VertOrientation::TOP:
+ if( !bDown ) eNew = text::VertOrientation::CENTER;
+ break;
+ case text::VertOrientation::LINE_TOP:
+ if( bDown ) eNew = text::VertOrientation::LINE_CENTER;
+ break;
+ case text::VertOrientation::LINE_CENTER:
+ eNew = bDown ? text::VertOrientation::LINE_BOTTOM : text::VertOrientation::LINE_TOP;
+ break;
+ case text::VertOrientation::LINE_BOTTOM:
+ if( !bDown ) eNew = text::VertOrientation::LINE_CENTER;
+ break;
+ default:; //prevent warning
+ }
+ }
+ else
+ {
+ aVert.SetPos( aDiff );
+ eNew = text::VertOrientation::NONE;
+ }
+ aVert.SetVertOrient( eNew );
+ aSet.Put( aVert );
+ bSet = true;
+ }
+ if (bWeb && (RndStdIds::FLY_AT_PARA == eAnchorId)
+ && ( nDir==MOVE_LEFT_SMALL || nDir==MOVE_RIGHT_BIG ))
+ {
+ SwFormatHoriOrient aHori( aSet.Get(RES_HORI_ORIENT) );
+ sal_Int16 eNew;
+ eNew = aHori.GetHoriOrient();
+ switch( eNew )
+ {
+ case text::HoriOrientation::RIGHT:
+ if( nDir==MOVE_LEFT_SMALL )
+ eNew = text::HoriOrientation::LEFT;
+ break;
+ case text::HoriOrientation::LEFT:
+ if( nDir==MOVE_RIGHT_BIG )
+ eNew = text::HoriOrientation::RIGHT;
+ break;
+ default:; //prevent warning
+ }
+ if( eNew != aHori.GetHoriOrient() )
+ {
+ aHori.SetHoriOrient( eNew );
+ aSet.Put( aHori );
+ bSet = true;
+ }
+ }
+ rSh.StartAllAction();
+ if( bSet )
+ rSh.SetFlyFrameAttr( aSet );
+ bool bSetPos = (RndStdIds::FLY_AS_CHAR != eAnchorId);
+ if(bSetPos && bWeb)
+ {
+ bSetPos = RndStdIds::FLY_AT_PAGE == eAnchorId;
+ }
+ if( bSetPos )
+ rSh.SetFlyPos( aTmp.Pos() );
+ rSh.EndAllAction();
+
+}
+
+void SwEditWin::ChangeDrawing( sal_uInt8 nDir )
+{
+ // start undo action in order to get only one
+ // undo action for this change.
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ rSh.StartUndo();
+
+ tools::Long nX = 0;
+ tools::Long nY = 0;
+ const bool bOnePixel(
+ MOVE_LEFT_SMALL == nDir ||
+ MOVE_UP_SMALL == nDir ||
+ MOVE_RIGHT_SMALL == nDir ||
+ MOVE_DOWN_SMALL == nDir);
+ const bool bHuge(
+ MOVE_LEFT_HUGE == nDir ||
+ MOVE_UP_HUGE == nDir ||
+ MOVE_RIGHT_HUGE == nDir ||
+ MOVE_DOWN_HUGE == nDir);
+ SwMove nAnchorDir = SwMove::UP;
+ switch(nDir)
+ {
+ case MOVE_LEFT_SMALL:
+ case MOVE_LEFT_HUGE:
+ case MOVE_LEFT_BIG:
+ nX = -1;
+ nAnchorDir = SwMove::LEFT;
+ break;
+ case MOVE_UP_SMALL:
+ case MOVE_UP_HUGE:
+ case MOVE_UP_BIG:
+ nY = -1;
+ break;
+ case MOVE_RIGHT_SMALL:
+ case MOVE_RIGHT_HUGE:
+ case MOVE_RIGHT_BIG:
+ nX = +1;
+ nAnchorDir = SwMove::RIGHT;
+ break;
+ case MOVE_DOWN_SMALL:
+ case MOVE_DOWN_HUGE:
+ case MOVE_DOWN_BIG:
+ nY = +1;
+ nAnchorDir = SwMove::DOWN;
+ break;
+ }
+
+ if(0 != nX || 0 != nY)
+ {
+ FlyProtectFlags nProtect = rSh.IsSelObjProtected( FlyProtectFlags::Pos|FlyProtectFlags::Size );
+ Size aSnap( rSh.GetViewOptions()->GetSnapSize() );
+ short nDiv = rSh.GetViewOptions()->GetDivisionX();
+ if ( nDiv > 0 )
+ aSnap.setWidth( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Width()) / nDiv ) );
+ nDiv = rSh.GetViewOptions()->GetDivisionY();
+ if ( nDiv > 0 )
+ aSnap.setHeight( std::max( sal_uLong(1), static_cast<sal_uLong>(aSnap.Height()) / nDiv ) );
+
+ if(bOnePixel)
+ {
+ aSnap = PixelToLogic(Size(1,1));
+ }
+ else if(bHuge)
+ {
+ // #i121236# 567twips == 1cm, but just take three times the normal snap
+ aSnap = Size(aSnap.Width() * 3, aSnap.Height() * 3);
+ }
+
+ nX *= aSnap.Width();
+ nY *= aSnap.Height();
+
+ SdrView *pSdrView = rSh.GetDrawView();
+ const SdrHdlList& rHdlList = pSdrView->GetHdlList();
+ SdrHdl* pHdl = rHdlList.GetFocusHdl();
+ rSh.StartAllAction();
+ if(nullptr == pHdl)
+ {
+ // now move the selected draw objects
+ // if the object's position is not protected
+ if(!(nProtect&FlyProtectFlags::Pos))
+ {
+ // Check if object is anchored as character and move direction
+ bool bDummy1, bDummy2;
+ const bool bVertAnchor = rSh.IsFrameVertical( true, bDummy1, bDummy2 );
+ bool bHoriMove = !bVertAnchor == !( nDir % 2 );
+ bool bMoveAllowed =
+ !bHoriMove || (rSh.GetAnchorId() != RndStdIds::FLY_AS_CHAR);
+ if ( bMoveAllowed )
+ {
+ pSdrView->MoveAllMarked(Size(nX, nY));
+ rSh.SetModified();
+ }
+ }
+ }
+ else
+ {
+ // move handle with index nHandleIndex
+ if (nX || nY)
+ {
+ if( SdrHdlKind::Anchor == pHdl->GetKind() ||
+ SdrHdlKind::Anchor_TR == pHdl->GetKind() )
+ {
+ // anchor move cannot be allowed when position is protected
+ if(!(nProtect&FlyProtectFlags::Pos))
+ rSh.MoveAnchor( nAnchorDir );
+ }
+ //now resize if size is protected
+ else if(!(nProtect&FlyProtectFlags::Size))
+ {
+ // now move the Handle (nX, nY)
+ Point aStartPoint(pHdl->GetPos());
+ Point aEndPoint(pHdl->GetPos() + Point(nX, nY));
+ const SdrDragStat& rDragStat = pSdrView->GetDragStat();
+
+ // start dragging
+ pSdrView->BegDragObj(aStartPoint, nullptr, pHdl, 0);
+
+ if(pSdrView->IsDragObj())
+ {
+ bool bWasNoSnap = rDragStat.IsNoSnap();
+ bool bWasSnapEnabled = pSdrView->IsSnapEnabled();
+
+ // switch snapping off
+ if(!bWasNoSnap)
+ const_cast<SdrDragStat&>(rDragStat).SetNoSnap();
+ if(bWasSnapEnabled)
+ pSdrView->SetSnapEnabled(false);
+
+ pSdrView->MovAction(aEndPoint);
+ pSdrView->EndDragObj();
+ rSh.SetModified();
+
+ // restore snap
+ if(!bWasNoSnap)
+ const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap);
+ if(bWasSnapEnabled)
+ pSdrView->SetSnapEnabled(bWasSnapEnabled);
+ }
+ }
+ }
+ }
+ rSh.EndAllAction();
+ }
+
+ rSh.EndUndo();
+}
+
+/**
+ * KeyEvents
+ */
+void SwEditWin::KeyInput(const KeyEvent &rKEvt)
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+
+ if (comphelper::LibreOfficeKit::isActive() && m_rView.GetPostItMgr())
+ {
+ if (vcl::Window* pWindow = m_rView.GetPostItMgr()->GetActiveSidebarWin())
+ {
+ pWindow->KeyInput(rKEvt);
+ return;
+ }
+ }
+
+ if( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE &&
+ m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard )
+ {
+ m_pApplyTempl->m_pFormatClipboard->Erase();
+ SetApplyTemplate(SwApplyTemplate());
+ m_rView.GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH);
+ }
+ else if ( rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE &&
+ rSh.IsHeaderFooterEdit( ) )
+ {
+ bool bHeader = bool(FrameTypeFlags::HEADER & rSh.GetFrameType(nullptr,false));
+ if ( bHeader )
+ rSh.SttPg();
+ else
+ rSh.EndPg();
+ rSh.ToggleHeaderFooterEdit();
+ }
+
+ SfxObjectShell *pObjSh = m_rView.GetViewFrame()->GetObjectShell();
+ if ( m_bLockInput || (pObjSh && pObjSh->GetProgress()) )
+ // When the progress bar is active or a progress is
+ // running on a document, no order is being taken
+ return;
+
+ m_pShadCursor.reset();
+ // Do not reset the timer here, otherwise when flooded with events it would never time out
+ // if every key event stopped and started it again.
+ comphelper::ScopeGuard keyInputFlushTimerStop([this]() { m_aKeyInputFlushTimer.Stop(); });
+
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() &&
+ rSh.IsCursorReadonly();
+
+ //if the language changes the buffer must be flushed
+ LanguageType eNewLanguage = GetInputLanguage();
+ if(!bIsDocReadOnly && m_eBufferLanguage != eNewLanguage && !m_aInBuffer.isEmpty())
+ {
+ FlushInBuffer();
+ }
+ m_eBufferLanguage = eNewLanguage;
+
+ QuickHelpData aTmpQHD;
+ if( s_pQuickHlpData->m_bIsDisplayed )
+ {
+ aTmpQHD.Move( *s_pQuickHlpData );
+ s_pQuickHlpData->Stop( rSh );
+ }
+
+ // OS:the DrawView also needs a readonly-Flag as well
+ if ( !bIsDocReadOnly && rSh.GetDrawView() && rSh.GetDrawView()->KeyInput( rKEvt, this ) )
+ {
+ rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll( false );
+ rSh.SetModified();
+ return; // Event evaluated by SdrView
+ }
+
+ if ( m_rView.GetDrawFuncPtr() && m_bInsFrame )
+ {
+ StopInsFrame();
+ rSh.Edit();
+ }
+
+ bool bFlushBuffer = false;
+ bool bNormalChar = false;
+ bool bAppendSpace = s_pQuickHlpData->m_bAppendSpace;
+ s_pQuickHlpData->m_bAppendSpace = false;
+
+ if ( getenv("SW_DEBUG") && rKEvt.GetKeyCode().GetCode() == KEY_F12 )
+ {
+ if( rKEvt.GetKeyCode().IsShift())
+ {
+ GetView().GetDocShell()->GetDoc()->dumpAsXml();
+ return;
+ }
+ else
+ {
+ SwRootFrame* pLayout = GetView().GetDocShell()->GetWrtShell()->GetLayout();
+ pLayout->dumpAsXml( );
+ return;
+ }
+ }
+
+ KeyEvent aKeyEvent( rKEvt );
+ // look for vertical mappings
+ if( !bIsDocReadOnly && !rSh.IsSelFrameMode() && !rSh.IsObjSelected() )
+ {
+ sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+
+ if( KEY_UP == nKey || KEY_DOWN == nKey ||
+ KEY_LEFT == nKey || KEY_RIGHT == nKey )
+ {
+ // In general, we want to map the direction keys if we are inside
+ // some vertical formatted text.
+ // 1. Exception: For a table cursor in a horizontal table, the
+ // directions should never be mapped.
+ // 2. Exception: For a table cursor in a vertical table, the
+ // directions should always be mapped.
+ const bool bVertText = rSh.IsInVerticalText();
+ const bool bTableCursor = rSh.GetTableCursor();
+ const bool bVertTable = rSh.IsTableVertical();
+ if( ( bVertText && ( !bTableCursor || bVertTable ) ) ||
+ ( bTableCursor && bVertTable ) )
+ {
+ SvxFrameDirection eDirection = rSh.GetTextDirection();
+ if (eDirection == SvxFrameDirection::Vertical_LR_BT)
+ {
+ // Map from physical to logical, so rotate clockwise.
+ if (KEY_UP == nKey)
+ nKey = KEY_RIGHT;
+ else if (KEY_DOWN == nKey)
+ nKey = KEY_LEFT;
+ else if (KEY_LEFT == nKey)
+ nKey = KEY_UP;
+ else /* KEY_RIGHT == nKey */
+ nKey = KEY_DOWN;
+ }
+ else
+ {
+ // Attempt to integrate cursor travelling for mongolian layout does not work.
+ // Thus, back to previous mapping of cursor keys to direction keys.
+ if( KEY_UP == nKey ) nKey = KEY_LEFT;
+ else if( KEY_DOWN == nKey ) nKey = KEY_RIGHT;
+ else if( KEY_LEFT == nKey ) nKey = KEY_DOWN;
+ else /* KEY_RIGHT == nKey */ nKey = KEY_UP;
+ }
+ }
+
+ if ( rSh.IsInRightToLeftText() )
+ {
+ if( KEY_LEFT == nKey ) nKey = KEY_RIGHT;
+ else if( KEY_RIGHT == nKey ) nKey = KEY_LEFT;
+ }
+
+ aKeyEvent = KeyEvent( rKEvt.GetCharCode(),
+ vcl::KeyCode( nKey, rKEvt.GetKeyCode().GetModifier() ),
+ rKEvt.GetRepeat() );
+ }
+ }
+
+ const vcl::KeyCode& rKeyCode = aKeyEvent.GetKeyCode();
+ sal_Unicode aCh = aKeyEvent.GetCharCode();
+
+ // enable switching to notes anchor with Ctrl - Alt - Page Up/Down
+ // pressing this inside a note will switch to next/previous note
+ if ((rKeyCode.IsMod1() && rKeyCode.IsMod2()) && ((rKeyCode.GetCode() == KEY_PAGEUP) || (rKeyCode.GetCode() == KEY_PAGEDOWN)))
+ {
+ const bool bNext = rKeyCode.GetCode()==KEY_PAGEDOWN;
+ const SwFieldType* pFieldType = rSh.GetFieldType( 0, SwFieldIds::Postit );
+ rSh.MoveFieldType( pFieldType, bNext );
+ return;
+ }
+
+ if (SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl())
+ {
+ // Check if this combination of rKeyCode and pTextContentControl should open a popup.
+ const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl();
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ if (pContentControl->ShouldOpenPopup(rKeyCode))
+ {
+ SwShellCursor* pCursor = rSh.GetCursor_();
+ if (pCursor)
+ {
+ VclPtr<SwContentControlButton> pContentControlButton = pCursor->GetContentControlButton();
+ if (pContentControlButton)
+ {
+ pContentControlButton->StartPopup();
+ return;
+ }
+ }
+ }
+ }
+
+ const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat();
+
+ if (pFlyFormat)
+ {
+ // See if the fly frame's anchor is in a content control. If so,
+ // try to interact with it.
+ const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
+ const SwPosition* pAnchorPos = rFormatAnchor.GetContentAnchor();
+ if (pAnchorPos)
+ {
+ SwTextNode* pTextNode = pAnchorPos->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ SwTextAttr* pAttr = pTextNode->GetTextAttrAt(
+ pAnchorPos->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+ if (pAttr)
+ {
+ SwTextContentControl* pTextContentControl
+ = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ const SwFormatContentControl& rFormatContentControl
+ = pTextContentControl->GetContentControl();
+ std::shared_ptr<SwContentControl> pContentControl
+ = rFormatContentControl.GetContentControl();
+ if (pContentControl->IsInteractingCharacter(aCh))
+ {
+ rSh.GotoContentControl(rFormatContentControl);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if( pFlyFormat )
+ {
+ SvMacroItemId nEvent;
+
+ if( 32 <= aCh &&
+ 0 == (( KEY_MOD1 | KEY_MOD2 ) & rKeyCode.GetModifier() ))
+ nEvent = SvMacroItemId::SwFrmKeyInputAlpha;
+ else
+ nEvent = SvMacroItemId::SwFrmKeyInputNoAlpha;
+
+ const SvxMacro* pMacro = pFlyFormat->GetMacro().GetMacroTable().Get( nEvent );
+ if( pMacro )
+ {
+ SbxArrayRef xArgs = new SbxArray;
+ SbxVariableRef xVar = new SbxVariable;
+ xVar->PutString( pFlyFormat->GetName() );
+ xArgs->Put(xVar.get(), 1);
+
+ xVar = new SbxVariable;
+ if( SvMacroItemId::SwFrmKeyInputAlpha == nEvent )
+ xVar->PutChar( aCh );
+ else
+ xVar->PutUShort( rKeyCode.GetModifier() | rKeyCode.GetCode() );
+ xArgs->Put(xVar.get(), 2);
+
+ OUString sRet;
+ rSh.ExecMacro( *pMacro, &sRet, xArgs.get() );
+ if( !sRet.isEmpty() && sRet.toInt32()!=0 )
+ return ;
+ }
+ }
+ SelectionType nLclSelectionType;
+ //A is converted to 1
+ if( rKeyCode.GetFullCode() == (KEY_A | KEY_MOD1 |KEY_SHIFT)
+ && rSh.HasDrawView() &&
+ (bool(nLclSelectionType = rSh.GetSelectionType()) &&
+ ((nLclSelectionType & (SelectionType::Frame|SelectionType::Graphic)) ||
+ ((nLclSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) &&
+ rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1))))
+ {
+ SdrHdlList& rHdlList = const_cast<SdrHdlList&>(rSh.GetDrawView()->GetHdlList());
+ SdrHdl* pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor);
+ if ( ! pAnchor )
+ pAnchor = rHdlList.GetHdl(SdrHdlKind::Anchor_TR);
+ if(pAnchor)
+ rHdlList.SetFocusHdl(pAnchor);
+ return;
+ }
+
+ SvxAutoCorrCfg* pACfg = nullptr;
+ SvxAutoCorrect* pACorr = nullptr;
+
+ uno::Reference< frame::XDispatchRecorder > xRecorder =
+ m_rView.GetViewFrame()->GetBindings().GetRecorder();
+ if ( !xRecorder.is() )
+ {
+ pACfg = &SvxAutoCorrCfg::Get();
+ pACorr = pACfg->GetAutoCorrect();
+ }
+
+ SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig();
+
+ OUString sFormulaEntry;
+
+ enum class SwKeyState { CheckKey, InsChar, InsTab,
+ NoNum, NumOff, NumOrNoNum, NumDown, NumUp,
+ NumIndentInc, NumIndentDec,
+
+ OutlineLvOff,
+ NextCell, PrevCell, OutlineUp, OutlineDown,
+ GlossaryExpand, NextPrevGlossary,
+ AutoFormatByInput,
+ NextObject, PrevObject,
+ KeyToView,
+ LaunchOLEObject, GoIntoFly, GoIntoDrawing,
+ EnterDrawHandleMode,
+ CheckDocReadOnlyKeys,
+ CheckAutoCorrect, EditFormula,
+ ColLeftBig, ColRightBig,
+ ColLeftSmall, ColRightSmall,
+ ColBottomBig,
+ ColBottomSmall,
+ CellLeftBig, CellRightBig,
+ CellLeftSmall, CellRightSmall,
+ CellTopBig, CellBottomBig,
+ CellTopSmall, CellBottomSmall,
+
+ Fly_Change, Draw_Change,
+ SpecialInsert,
+ EnterCharCell,
+ GotoNextFieldMark,
+ GotoPrevFieldMark,
+ End };
+
+ SwKeyState eKeyState = bIsDocReadOnly ? SwKeyState::CheckDocReadOnlyKeys : SwKeyState::CheckKey;
+ SwKeyState eNextKeyState = SwKeyState::End;
+ sal_uInt8 nDir = 0;
+
+ if (m_nKS_NUMDOWN_Count > 0)
+ m_nKS_NUMDOWN_Count--;
+
+ if (m_nKS_NUMINDENTINC_Count > 0)
+ m_nKS_NUMINDENTINC_Count--;
+
+ while( SwKeyState::End != eKeyState )
+ {
+ SwKeyState eFlyState = SwKeyState::KeyToView;
+
+ switch( eKeyState )
+ {
+ case SwKeyState::CheckKey:
+ eKeyState = SwKeyState::KeyToView; // default forward to View
+
+ if (!comphelper::LibreOfficeKit::isActive() &&
+ !rKeyCode.IsMod2() && '=' == aCh &&
+ !rSh.IsTableMode() && rSh.GetTableFormat() &&
+ rSh.IsSttPara() &&
+ !rSh.HasReadonlySel())
+ {
+ // at the beginning of the table's cell a '=' ->
+ // call EditRow (F2-functionality)
+ // [Avoid this for LibreOfficeKit, as the separate input window
+ // steals the focus & things go wrong - the user never gets
+ // the focus back.]
+ rSh.Push();
+ if( !rSh.MoveSection( GoCurrSection, fnSectionStart) &&
+ !rSh.IsTableBoxTextFormat() )
+ {
+ // is at the beginning of the box
+ eKeyState = SwKeyState::EditFormula;
+ if( rSh.HasMark() )
+ rSh.SwapPam();
+ else
+ rSh.SttSelect();
+ rSh.MoveSection( GoCurrSection, fnSectionEnd );
+ rSh.Pop();
+ rSh.EndSelect();
+ sFormulaEntry = "=";
+ }
+ else
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ else
+ {
+ if( pACorr && aTmpQHD.HasContent() && !rSh.HasSelection() &&
+ !rSh.HasReadonlySel() && !aTmpQHD.m_bIsAutoText &&
+ pACorr->GetSwFlags().nAutoCmpltExpandKey ==
+ (rKeyCode.GetModifier() | rKeyCode.GetCode()) )
+ {
+ eKeyState = SwKeyState::GlossaryExpand;
+ break;
+ }
+
+ switch( rKeyCode.GetModifier() | rKeyCode.GetCode() )
+ {
+ case KEY_RIGHT | KEY_MOD2:
+ eKeyState = SwKeyState::ColRightBig;
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_RIGHT_SMALL;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_LEFT | KEY_MOD2:
+ eKeyState = SwKeyState::ColRightSmall;
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_LEFT_SMALL;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT:
+ eKeyState = SwKeyState::ColLeftSmall;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_LEFT | KEY_MOD2 | KEY_SHIFT:
+ eKeyState = SwKeyState::ColLeftBig;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_RIGHT | KEY_MOD2 | KEY_MOD1:
+ eKeyState = SwKeyState::CellRightBig;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_LEFT | KEY_MOD2 | KEY_MOD1:
+ eKeyState = SwKeyState::CellRightSmall;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_RIGHT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1:
+ eKeyState = SwKeyState::CellLeftSmall;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_LEFT | KEY_MOD2 | KEY_SHIFT | KEY_MOD1:
+ eKeyState = SwKeyState::CellLeftBig;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_UP | KEY_MOD2:
+ eKeyState = SwKeyState::ColBottomSmall;
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_UP_SMALL;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_DOWN | KEY_MOD2:
+ eKeyState = SwKeyState::ColBottomBig;
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_DOWN_SMALL;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_UP | KEY_MOD2 | KEY_MOD1:
+ eKeyState = SwKeyState::CellBottomSmall;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_DOWN | KEY_MOD2 | KEY_MOD1:
+ eKeyState = SwKeyState::CellBottomBig;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_UP | KEY_MOD2 | KEY_SHIFT | KEY_MOD1:
+ eKeyState = SwKeyState::CellTopBig;
+ goto KEYINPUT_CHECKTABLE;
+
+ case KEY_DOWN | KEY_MOD2 | KEY_SHIFT | KEY_MOD1:
+ eKeyState = SwKeyState::CellTopSmall;
+ goto KEYINPUT_CHECKTABLE;
+
+KEYINPUT_CHECKTABLE:
+ // Resolve bugs 49091, 53190, 93402 and
+ // https://bz.apache.org/ooo/show_bug.cgi?id=113502
+ // but provide an option for restoring interactive
+ // table sizing functionality when needed.
+ if (
+ ! (Window::GetIndicatorState() & KeyIndicatorState::CAPSLOCK)
+ && m_rView.KeyInput( aKeyEvent ) // Keystroke is customized
+ )
+ {
+ bFlushBuffer = true;
+ bNormalChar = false;
+ eKeyState = SwKeyState::End;
+ break ;
+ }
+
+ if( rSh.IsTableMode() || !rSh.GetTableFormat() )
+ {
+ if(!pFlyFormat && SwKeyState::KeyToView != eFlyState &&
+ (rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm)) &&
+ rSh.GetDrawView()->AreObjectsMarked())
+ eKeyState = SwKeyState::Draw_Change;
+
+ if( pFlyFormat )
+ eKeyState = eFlyState;
+ else if( SwKeyState::Draw_Change != eKeyState)
+ eKeyState = SwKeyState::EnterCharCell;
+ }
+ break;
+
+ // huge object move
+ case KEY_RIGHT | KEY_SHIFT:
+ case KEY_LEFT | KEY_SHIFT:
+ case KEY_UP | KEY_SHIFT:
+ case KEY_DOWN | KEY_SHIFT:
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+ if ( ( pFlyFormat
+ && ( nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic) ) )
+ || ( ( nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm) )
+ && rSh.GetDrawView()->AreObjectsMarked() ) )
+ {
+ eKeyState = pFlyFormat ? SwKeyState::Fly_Change : SwKeyState::Draw_Change;
+ if (nSelectionType & SelectionType::DrawObject)
+ {
+ // tdf#137964: always move the DrawObject if one is selected
+ eKeyState = SwKeyState::Draw_Change;
+ }
+ switch ( rKeyCode.GetCode() )
+ {
+ case KEY_RIGHT: nDir = MOVE_RIGHT_HUGE; break;
+ case KEY_LEFT: nDir = MOVE_LEFT_HUGE; break;
+ case KEY_UP: nDir = MOVE_UP_HUGE; break;
+ case KEY_DOWN: nDir = MOVE_DOWN_HUGE; break;
+ }
+ }
+ break;
+ }
+
+ case KEY_LEFT:
+ case KEY_LEFT | KEY_MOD1:
+ {
+ bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1);
+ if(!bMod1)
+ {
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_LEFT_BIG;
+ }
+ goto KEYINPUT_CHECKTABLE_INSDEL;
+ }
+ case KEY_RIGHT | KEY_MOD1:
+ {
+ goto KEYINPUT_CHECKTABLE_INSDEL;
+ }
+ case KEY_UP:
+ case KEY_UP | KEY_MOD1:
+ {
+ bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1);
+ if(!bMod1)
+ {
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_UP_BIG;
+ }
+ goto KEYINPUT_CHECKTABLE_INSDEL;
+ }
+ case KEY_DOWN:
+ case KEY_DOWN | KEY_MOD1:
+ {
+ bool bMod1 = 0 != (rKeyCode.GetModifier() & KEY_MOD1);
+ if(!bMod1)
+ {
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_DOWN_BIG;
+ }
+ goto KEYINPUT_CHECKTABLE_INSDEL;
+ }
+
+KEYINPUT_CHECKTABLE_INSDEL:
+ if( rSh.IsTableMode() || !rSh.GetTableFormat() )
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+
+ eKeyState = SwKeyState::KeyToView;
+ if(SwKeyState::KeyToView != eFlyState)
+ {
+ if((nSelectionType & (SelectionType::DrawObject|SelectionType::DbForm)) &&
+ rSh.GetDrawView()->AreObjectsMarked())
+ eKeyState = SwKeyState::Draw_Change;
+ else if(nSelectionType & (SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic))
+ eKeyState = SwKeyState::Fly_Change;
+ }
+ }
+ break;
+
+
+ case KEY_DELETE:
+ if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField())
+ {
+ if (rSh.IsInFrontOfLabel() && rSh.NumOrNoNum())
+ eKeyState = SwKeyState::NumOrNoNum;
+ }
+ else if (!rSh.IsCursorInParagraphMetadataField())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog"));
+ xInfo->run();
+ eKeyState = SwKeyState::End;
+ }
+ break;
+
+ case KEY_RETURN:
+ {
+ if ( !rSh.HasReadonlySel()
+ && !rSh.CursorInsideInputField()
+ && !rSh.CursorInsideContentControl() )
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+ if(nSelectionType & SelectionType::Ole)
+ eKeyState = SwKeyState::LaunchOLEObject;
+ else if(nSelectionType & SelectionType::Frame)
+ eKeyState = SwKeyState::GoIntoFly;
+ else if((nSelectionType & SelectionType::DrawObject) &&
+ !(nSelectionType & SelectionType::DrawObjectEditMode) &&
+ rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1)
+ {
+ eKeyState = SwKeyState::GoIntoDrawing;
+ if (lcl_goIntoTextBox(*this, rSh))
+ eKeyState = SwKeyState::GoIntoFly;
+ }
+ else if( aTmpQHD.HasContent() && !rSh.HasSelection() &&
+ aTmpQHD.m_bIsAutoText )
+ eKeyState = SwKeyState::GlossaryExpand;
+
+ //RETURN and empty paragraph in numbering -> end numbering
+ else if( m_aInBuffer.isEmpty() &&
+ rSh.GetNumRuleAtCurrCursorPos() &&
+ !rSh.GetNumRuleAtCurrCursorPos()->IsOutlineRule() &&
+ !rSh.HasSelection() &&
+ rSh.IsSttPara() && rSh.IsEndPara() )
+ {
+ eKeyState = SwKeyState::NumOff;
+ eNextKeyState = SwKeyState::OutlineLvOff;
+ }
+ //RETURN for new paragraph with AutoFormatting
+ else if( pACfg && pACfg->IsAutoFormatByInput() &&
+ !(nSelectionType & (SelectionType::Graphic |
+ SelectionType::Ole | SelectionType::Frame |
+ SelectionType::TableCell | SelectionType::DrawObject |
+ SelectionType::DrawObjectEditMode)) )
+ {
+ eKeyState = SwKeyState::AutoFormatByInput;
+ }
+ else
+ {
+ eNextKeyState = eKeyState;
+ eKeyState = SwKeyState::CheckAutoCorrect;
+ }
+ }
+ }
+ break;
+ case KEY_RETURN | KEY_MOD2:
+ {
+ if ( !rSh.HasReadonlySel()
+ && !rSh.IsSttPara()
+ && rSh.GetNumRuleAtCurrCursorPos()
+ && !rSh.CursorInsideInputField() )
+ {
+ eKeyState = SwKeyState::NoNum;
+ }
+ else if( rSh.CanSpecialInsert() )
+ eKeyState = SwKeyState::SpecialInsert;
+ }
+ break;
+ case KEY_BACKSPACE:
+ case KEY_BACKSPACE | KEY_SHIFT:
+ if ( !rSh.HasReadonlySel() || rSh.CursorInsideInputField())
+ {
+ bool bDone = false;
+ // try to add comment for code snip:
+ // Remove the paragraph indent, if the cursor is at the
+ // beginning of a paragraph, there is no selection
+ // and no numbering rule found at the current paragraph
+ // Also try to remove indent, if current paragraph
+ // has numbering rule, but isn't counted and only
+ // key <backspace> is hit.
+ const bool bOnlyBackspaceKey( KEY_BACKSPACE == rKeyCode.GetFullCode() );
+ if ( rSh.IsSttPara()
+ && !rSh.HasSelection()
+ && ( rSh.GetNumRuleAtCurrCursorPos() == nullptr
+ || ( rSh.IsNoNum() && bOnlyBackspaceKey ) ) )
+ {
+ bDone = rSh.TryRemoveIndent();
+ }
+
+ if (bDone)
+ eKeyState = SwKeyState::End;
+ else
+ {
+ if ( rSh.IsSttPara() && !rSh.IsNoNum() )
+ {
+ if (m_nKS_NUMDOWN_Count > 0 &&
+ 0 < rSh.GetNumLevel())
+ {
+ eKeyState = SwKeyState::NumUp;
+ m_nKS_NUMDOWN_Count = 2;
+ bDone = true;
+ }
+ else if (m_nKS_NUMINDENTINC_Count > 0)
+ {
+ eKeyState = SwKeyState::NumIndentDec;
+ m_nKS_NUMINDENTINC_Count = 2;
+ bDone = true;
+ }
+ }
+
+ // If the cursor is in an empty paragraph, which has
+ // a numbering, but not the outline numbering, and
+ // there is no selection, the numbering has to be
+ // deleted on key <Backspace>.
+ // Otherwise method <SwEditShell::NumOrNoNum(..)>
+ // should only change the <IsCounted()> state of
+ // the current paragraph depending of the key.
+ // On <backspace> it is set to <false>,
+ // on <shift-backspace> it is set to <true>.
+ // Thus, assure that method <SwEditShell::NumOrNum(..)>
+ // is only called for the intended purpose.
+ if ( !bDone && rSh.IsSttPara() )
+ {
+ bool bCallNumOrNoNum( false );
+ if ( bOnlyBackspaceKey && !rSh.IsNoNum() )
+ {
+ bCallNumOrNoNum = true;
+ }
+ else if ( !bOnlyBackspaceKey && rSh.IsNoNum() )
+ {
+ bCallNumOrNoNum = true;
+ }
+ else if ( bOnlyBackspaceKey
+ && rSh.IsSttPara()
+ && rSh.IsEndPara()
+ && !rSh.HasSelection() )
+ {
+ const SwNumRule* pCurrNumRule( rSh.GetNumRuleAtCurrCursorPos() );
+ if ( pCurrNumRule != nullptr
+ && pCurrNumRule != rSh.GetOutlineNumRule() )
+ {
+ bCallNumOrNoNum = true;
+ }
+ }
+ if ( bCallNumOrNoNum
+ && rSh.NumOrNoNum( !bOnlyBackspaceKey ) )
+ {
+ eKeyState = SwKeyState::NumOrNoNum;
+ }
+ }
+ }
+ }
+ else if (!rSh.IsCursorInParagraphMetadataField())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xInfo(xBuilder->weld_message_dialog("InfoReadonlyDialog"));
+ xInfo->run();
+ eKeyState = SwKeyState::End;
+ }
+ break;
+
+ case KEY_RIGHT:
+ {
+ eFlyState = SwKeyState::Fly_Change;
+ nDir = MOVE_RIGHT_BIG;
+ goto KEYINPUT_CHECKTABLE_INSDEL;
+ }
+ case KEY_TAB:
+ {
+
+ if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark() || rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT)
+ {
+ eKeyState = SwKeyState::GotoNextFieldMark;
+ }
+ else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() )
+ {
+ GetView().GetViewFrame()->GetDispatcher()->Execute( FN_GOTO_NEXT_INPUTFLD );
+ eKeyState = SwKeyState::End;
+ }
+ else if( rSh.GetNumRuleAtCurrCursorPos()
+ && rSh.IsSttOfPara()
+ && !rSh.HasReadonlySel() )
+ {
+ if (numfunc::NumDownChangesIndent(rSh))
+ {
+ eKeyState = SwKeyState::NumDown;
+ }
+ else
+ {
+ eKeyState = SwKeyState::InsTab;
+ }
+ }
+ else if (rSh.GetSelectionType() &
+ (SelectionType::Graphic |
+ SelectionType::Frame |
+ SelectionType::Ole |
+ SelectionType::DrawObject |
+ SelectionType::DbForm))
+ {
+ eKeyState = SwKeyState::NextObject;
+ }
+ else if ( rSh.GetTableFormat() )
+ {
+ if( rSh.HasSelection() || rSh.HasReadonlySel() )
+ eKeyState = SwKeyState::NextCell;
+ else
+ {
+ eKeyState = SwKeyState::CheckAutoCorrect;
+ eNextKeyState = SwKeyState::NextCell;
+ }
+ }
+ else
+ {
+ eKeyState = SwKeyState::InsTab;
+ if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() )
+ {
+ SwTextFormatColl* pColl = rSh.GetCurTextFormatColl();
+ if( pColl &&
+
+ pColl->IsAssignedToListLevelOfOutlineStyle()
+ && MAXLEVEL-1 > pColl->GetAssignedOutlineStyleLevel() )
+ eKeyState = SwKeyState::OutlineDown;
+ }
+ }
+ }
+ break;
+ case KEY_TAB | KEY_SHIFT:
+ {
+ if (rSh.IsFormProtected() || rSh.GetCurrentFieldmark()|| rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT)
+ {
+ eKeyState = SwKeyState::GotoPrevFieldMark;
+ }
+ else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() )
+ {
+ GetView().GetViewFrame()->GetDispatcher()->Execute( FN_GOTO_PREV_INPUTFLD );
+ eKeyState = SwKeyState::End;
+ }
+ else if( rSh.GetNumRuleAtCurrCursorPos()
+ && rSh.IsSttOfPara()
+ && !rSh.HasReadonlySel() )
+ {
+ eKeyState = SwKeyState::NumUp;
+ }
+ else if (rSh.GetSelectionType() &
+ (SelectionType::Graphic |
+ SelectionType::Frame |
+ SelectionType::Ole |
+ SelectionType::DrawObject |
+ SelectionType::DbForm))
+ {
+ eKeyState = SwKeyState::PrevObject;
+ }
+ else if ( rSh.GetTableFormat() )
+ {
+ if( rSh.HasSelection() || rSh.HasReadonlySel() )
+ eKeyState = SwKeyState::PrevCell;
+ else
+ {
+ eKeyState = SwKeyState::CheckAutoCorrect;
+ eNextKeyState = SwKeyState::PrevCell;
+ }
+ }
+ else
+ {
+ eKeyState = SwKeyState::End;
+ if( rSh.IsSttOfPara() && !rSh.HasReadonlySel() )
+ {
+ SwTextFormatColl* pColl = rSh.GetCurTextFormatColl();
+ if( pColl &&
+ pColl->IsAssignedToListLevelOfOutlineStyle() &&
+ 0 < pColl->GetAssignedOutlineStyleLevel())
+ eKeyState = SwKeyState::OutlineUp;
+ }
+ }
+ }
+ break;
+ case KEY_TAB | KEY_MOD1:
+ case KEY_TAB | KEY_MOD2:
+ if( !rSh.HasReadonlySel() )
+ {
+ if( aTmpQHD.HasContent() && !rSh.HasSelection() )
+ {
+ // Next auto-complete suggestion
+ aTmpQHD.Next( pACorr &&
+ pACorr->GetSwFlags().bAutoCmpltEndless );
+ eKeyState = SwKeyState::NextPrevGlossary;
+ }
+ else if( rSh.GetTableFormat() )
+ eKeyState = SwKeyState::InsTab;
+ else if((rSh.GetSelectionType() &
+ (SelectionType::DrawObject|SelectionType::DbForm|
+ SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) &&
+ rSh.GetDrawView()->AreObjectsMarked())
+ eKeyState = SwKeyState::EnterDrawHandleMode;
+ else
+ {
+ if ( !rSh.IsMultiSelection()
+ && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() )
+ eKeyState = SwKeyState::NumIndentInc;
+ }
+ }
+ break;
+
+ case KEY_TAB | KEY_MOD1 | KEY_SHIFT:
+ {
+ if( aTmpQHD.HasContent() && !rSh.HasSelection() &&
+ !rSh.HasReadonlySel() )
+ {
+ // Previous auto-complete suggestion.
+ aTmpQHD.Previous( pACorr &&
+ pACorr->GetSwFlags().bAutoCmpltEndless );
+ eKeyState = SwKeyState::NextPrevGlossary;
+ }
+ else if((rSh.GetSelectionType() & (SelectionType::DrawObject|SelectionType::DbForm|
+ SelectionType::Frame|SelectionType::Ole|SelectionType::Graphic)) &&
+ rSh.GetDrawView()->AreObjectsMarked())
+ {
+ eKeyState = SwKeyState::EnterDrawHandleMode;
+ }
+ else
+ {
+ if ( !rSh.IsMultiSelection()
+ && numfunc::ChangeIndentOnTabAtFirstPosOfFirstListItem() )
+ eKeyState = SwKeyState::NumIndentDec;
+ }
+ }
+ break;
+ case KEY_F2 :
+ if( !rSh.HasReadonlySel() )
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+ if(nSelectionType & SelectionType::Frame)
+ eKeyState = SwKeyState::GoIntoFly;
+ else if(nSelectionType & SelectionType::DrawObject)
+ {
+ eKeyState = SwKeyState::GoIntoDrawing;
+ if (lcl_goIntoTextBox(*this, rSh))
+ eKeyState = SwKeyState::GoIntoFly;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case SwKeyState::CheckDocReadOnlyKeys:
+ {
+ eKeyState = SwKeyState::KeyToView;
+ switch( rKeyCode.GetModifier() | rKeyCode.GetCode() )
+ {
+ case KEY_TAB:
+ case KEY_TAB | KEY_SHIFT:
+ bNormalChar = false;
+ eKeyState = SwKeyState::End;
+ if ( rSh.GetSelectionType() &
+ (SelectionType::Graphic |
+ SelectionType::Frame |
+ SelectionType::Ole |
+ SelectionType::DrawObject |
+ SelectionType::DbForm))
+
+ {
+ eKeyState = (rKeyCode.GetModifier() & KEY_SHIFT) ?
+ SwKeyState::PrevObject : SwKeyState::NextObject;
+ }
+ else if ( !rSh.IsMultiSelection() && rSh.CursorInsideInputField() )
+ {
+ GetView().GetViewFrame()->GetDispatcher()->Execute(
+ KEY_SHIFT != rKeyCode.GetModifier() ? FN_GOTO_NEXT_INPUTFLD : FN_GOTO_PREV_INPUTFLD );
+ }
+ else
+ {
+ rSh.SelectNextPrevHyperlink( KEY_SHIFT != rKeyCode.GetModifier() );
+ }
+ break;
+ case KEY_RETURN:
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+ if(nSelectionType & SelectionType::Frame)
+ eKeyState = SwKeyState::GoIntoFly;
+ else
+ {
+ SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT> aSet(rSh.GetAttrPool());
+ rSh.GetCurAttr(aSet);
+ if(SfxItemState::SET == aSet.GetItemState(RES_TXTATR_INETFMT, false))
+ {
+ const SfxPoolItem& rItem = aSet.Get(RES_TXTATR_INETFMT);
+ bNormalChar = false;
+ eKeyState = SwKeyState::End;
+ rSh.ClickToINetAttr(static_cast<const SwFormatINetFormat&>(rItem));
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case SwKeyState::EnterCharCell:
+ {
+ eKeyState = SwKeyState::KeyToView;
+ switch ( rKeyCode.GetModifier() | rKeyCode.GetCode() )
+ {
+ case KEY_RIGHT | KEY_MOD2:
+ rSh.Right( CRSR_SKIP_CHARS, false, 1, false );
+ eKeyState = SwKeyState::End;
+ FlushInBuffer();
+ break;
+ case KEY_LEFT | KEY_MOD2:
+ rSh.Left( CRSR_SKIP_CHARS, false, 1, false );
+ eKeyState = SwKeyState::End;
+ FlushInBuffer();
+ break;
+ }
+ }
+ break;
+
+ case SwKeyState::KeyToView:
+ {
+ eKeyState = SwKeyState::End;
+ bNormalChar =
+ !rKeyCode.IsMod2() &&
+ rKeyCode.GetModifier() != KEY_MOD1 &&
+ rKeyCode.GetModifier() != (KEY_MOD1|KEY_SHIFT) &&
+ SW_ISPRINTABLE( aCh );
+
+ if( bNormalChar && rSh.IsInFrontOfLabel() )
+ {
+ rSh.NumOrNoNum();
+ }
+
+ if( !m_aInBuffer.isEmpty() && ( !bNormalChar || bIsDocReadOnly ))
+ FlushInBuffer();
+
+ if (rSh.HasReadonlySel()
+ && ( rKeyCode.GetFunction() == KeyFuncType::PASTE
+ || rKeyCode.GetFunction() == KeyFuncType::CUT))
+ {
+ auto xInfo(std::make_shared<weld::GenericDialogController>(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog"));
+ weld::DialogController::runAsync(xInfo, [](int) {});
+ eKeyState = SwKeyState::End;
+ }
+ else if( m_rView.KeyInput( aKeyEvent ) )
+ {
+ bFlushBuffer = true;
+ bNormalChar = false;
+ }
+ else
+ {
+ // Because Sfx accelerators are only called when they were
+ // enabled at the last status update, copy has to called
+ // 'forcefully' by us if necessary.
+ if( rKeyCode.GetFunction() == KeyFuncType::COPY )
+ GetView().GetViewFrame()->GetBindings().Execute(SID_COPY);
+
+ if( !bIsDocReadOnly && bNormalChar )
+ {
+ const SelectionType nSelectionType = rSh.GetSelectionType();
+ const bool bDrawObject = (nSelectionType & SelectionType::DrawObject) &&
+ !(nSelectionType & SelectionType::DrawObjectEditMode) &&
+ rSh.GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1;
+
+ bool bTextBox = false;
+ if (bDrawObject && lcl_goIntoTextBox(*this, rSh))
+ // A draw shape was selected, but it has a TextBox,
+ // start editing that instead when the normal
+ // character is pressed.
+ bTextBox = true;
+
+ if (bDrawObject && !bTextBox)
+ {
+ SdrObject* pObj = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
+ if(pObj)
+ {
+ EnterDrawTextMode(pObj->GetLogicRect().Center());
+ if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) )
+ pSwDrawTextShell->Init();
+ rSh.GetDrawView()->KeyInput( rKEvt, this );
+ }
+ }
+ else if (nSelectionType & SelectionType::Frame || bTextBox)
+ {
+ rSh.UnSelectFrame();
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ rSh.MoveSection( GoCurrSection, fnSectionEnd );
+ }
+ eKeyState = SwKeyState::InsChar;
+ }
+ else
+ {
+ bNormalChar = false;
+ Window::KeyInput( aKeyEvent );
+ }
+ }
+ }
+ break;
+ case SwKeyState::LaunchOLEObject:
+ {
+ rSh.LaunchOLEObj();
+ eKeyState = SwKeyState::End;
+ }
+ break;
+ case SwKeyState::GoIntoFly:
+ {
+ rSh.UnSelectFrame();
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ rSh.MoveSection( GoCurrSection, fnSectionEnd );
+ eKeyState = SwKeyState::End;
+ }
+ break;
+ case SwKeyState::GoIntoDrawing:
+ {
+ if (SdrMark* pMark = rSh.GetDrawView()->GetMarkedObjectList().GetMark(0))
+ {
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if(pObj)
+ {
+ EnterDrawTextMode(pObj->GetLogicRect().Center());
+ if (auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) )
+ pSwDrawTextShell->Init();
+ }
+ }
+ eKeyState = SwKeyState::End;
+ }
+ break;
+ case SwKeyState::EnterDrawHandleMode:
+ {
+ const SdrHdlList& rHdlList = rSh.GetDrawView()->GetHdlList();
+ bool bForward(!aKeyEvent.GetKeyCode().IsShift());
+
+ const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward);
+ eKeyState = SwKeyState::End;
+ }
+ break;
+ case SwKeyState::InsTab:
+ if( dynamic_cast<const SwWebView*>( &m_rView) != nullptr) // no Tab for WebView
+ {
+ // then it should be passed along
+ Window::KeyInput( aKeyEvent );
+ eKeyState = SwKeyState::End;
+ break;
+ }
+ aCh = '\t';
+ [[fallthrough]];
+ case SwKeyState::InsChar:
+ if (rSh.CursorInsideContentControl())
+ {
+ const SwPosition* pStart = rSh.GetCursor()->Start();
+ SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ sal_Int32 nIndex = pStart->nContent.GetIndex();
+ SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+ if (pAttr)
+ {
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl();
+ std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl();
+ if (pContentControl->IsInteractingCharacter(aCh))
+ {
+ rSh.GotoContentControl(rFormatContentControl);
+ eKeyState = SwKeyState::End;
+ break;
+ }
+ }
+ }
+ }
+
+ if (rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT)
+ {
+ ::sw::mark::ICheckboxFieldmark* pFieldmark =
+ dynamic_cast< ::sw::mark::ICheckboxFieldmark* >
+ (rSh.GetCurrentFieldmark());
+ OSL_ENSURE(pFieldmark,
+ "Where is my FieldMark??");
+ if(pFieldmark)
+ {
+ pFieldmark->SetChecked(!pFieldmark->IsChecked());
+ OSL_ENSURE(pFieldmark->IsExpanded(),
+ "where is the otherpos?");
+ if (pFieldmark->IsExpanded())
+ {
+ rSh.CalcLayout();
+ }
+ }
+ eKeyState = SwKeyState::End;
+ }
+ else if ( !rSh.HasReadonlySel()
+ || rSh.CursorInsideInputField() )
+ {
+ const bool bIsNormalChar =
+ GetAppCharClass().isLetterNumeric( OUString( aCh ), 0 );
+ if( bAppendSpace && bIsNormalChar &&
+ (!m_aInBuffer.isEmpty() || !rSh.IsSttPara() || !rSh.IsEndPara() ))
+ {
+ // insert a blank ahead of the character. this ends up
+ // between the expanded text and the new "non-word-separator".
+ m_aInBuffer += " ";
+ }
+
+ const bool bIsAutoCorrectChar = SvxAutoCorrect::IsAutoCorrectChar( aCh );
+ if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) &&
+ pACfg->IsAutoFormatByInput() &&
+ (( pACorr->IsAutoCorrFlag( ACFlags::ChgWeightUnderl ) &&
+ ( '*' == aCh || '_' == aCh ) ) ||
+ ( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))||
+ ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh))))
+ {
+ FlushInBuffer();
+ rSh.AutoCorrect( *pACorr, aCh );
+ if( '\"' != aCh && '\'' != aCh ) // only call when "*_"!
+ rSh.UpdateAttr();
+ }
+ else if( !aKeyEvent.GetRepeat() && pACorr && ( bIsAutoCorrectChar || rSh.IsNbspRunNext() ) &&
+ pACfg->IsAutoFormatByInput() &&
+ pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord |
+ ACFlags::ChgOrdinalNumber | ACFlags::AddNonBrkSpace |
+ ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr |
+ ACFlags::Autocorrect | ACFlags::TransliterateRTL ) &&
+ '\"' != aCh && '\'' != aCh && '*' != aCh && '_' != aCh
+ )
+ {
+ FlushInBuffer();
+ rSh.AutoCorrect( *pACorr, aCh );
+ }
+ else
+ {
+ OUStringBuffer aBuf(m_aInBuffer);
+ comphelper::string::padToLength(aBuf,
+ m_aInBuffer.getLength() + aKeyEvent.GetRepeat() + 1, aCh);
+ m_aInBuffer = aBuf.makeStringAndClear();
+ bool delayFlush = Application::AnyInput( VclInputFlags::KEYBOARD );
+ bFlushBuffer = !delayFlush;
+ if( delayFlush )
+ {
+ // Start the timer, make sure to not restart it.
+ keyInputFlushTimerStop.dismiss();
+ if( !m_aKeyInputFlushTimer.IsActive())
+ m_aKeyInputFlushTimer.Start();
+ }
+ }
+ eKeyState = SwKeyState::End;
+ }
+ else
+ {
+ auto xInfo(std::make_shared<weld::GenericDialogController>(GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog"));
+ weld::DialogController::runAsync(xInfo, [](int) {});
+ eKeyState = SwKeyState::End;
+ }
+ break;
+
+ case SwKeyState::CheckAutoCorrect:
+ {
+ if( pACorr && pACfg->IsAutoFormatByInput() &&
+ pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord |
+ ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL |
+ ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr |
+ ACFlags::Autocorrect ) &&
+ !rSh.HasReadonlySel() )
+ {
+ FlushInBuffer();
+ rSh.AutoCorrect( *pACorr, u'\0' );
+ }
+ eKeyState = eNextKeyState;
+ }
+ break;
+
+ default:
+ {
+ sal_uInt16 nSlotId = 0;
+ FlushInBuffer();
+ switch( eKeyState )
+ {
+ case SwKeyState::SpecialInsert:
+ rSh.DoSpecialInsert();
+ break;
+
+ case SwKeyState::NoNum:
+ rSh.NoNum();
+ break;
+
+ case SwKeyState::NumOff:
+ // shell change - so record in advance
+ rSh.DelNumRules();
+ break;
+ case SwKeyState::OutlineLvOff: // delete autofmt outlinelevel later
+ break;
+
+ case SwKeyState::NumDown:
+ rSh.NumUpDown();
+ m_nKS_NUMDOWN_Count = 2;
+ break;
+ case SwKeyState::NumUp:
+ rSh.NumUpDown( false );
+ break;
+
+ case SwKeyState::NumIndentInc:
+ rSh.ChangeIndentOfAllListLevels(360);
+ m_nKS_NUMINDENTINC_Count = 2;
+ break;
+
+ case SwKeyState::GotoNextFieldMark:
+ {
+ ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkAfter();
+ if(pFieldmark) rSh.GotoFieldmark(pFieldmark);
+ }
+ break;
+
+ case SwKeyState::GotoPrevFieldMark:
+ {
+ ::sw::mark::IFieldmark const * const pFieldmark = rSh.GetFieldmarkBefore();
+ if( pFieldmark )
+ rSh.GotoFieldmark(pFieldmark);
+ }
+ break;
+
+ case SwKeyState::NumIndentDec:
+ rSh.ChangeIndentOfAllListLevels(-360);
+ break;
+
+ case SwKeyState::OutlineDown:
+ rSh.OutlineUpDown();
+ break;
+ case SwKeyState::OutlineUp:
+ rSh.OutlineUpDown( -1 );
+ break;
+
+ case SwKeyState::NextCell:
+ // always 'flush' in tables
+ rSh.GoNextCell(!rSh.HasReadonlySel());
+ nSlotId = FN_GOTO_NEXT_CELL;
+ break;
+ case SwKeyState::PrevCell:
+ rSh.GoPrevCell();
+ nSlotId = FN_GOTO_PREV_CELL;
+ break;
+ case SwKeyState::AutoFormatByInput:
+ rSh.SplitNode( true );
+ break;
+
+ case SwKeyState::NextObject:
+ case SwKeyState::PrevObject:
+ if(rSh.GotoObj( SwKeyState::NextObject == eKeyState, GotoObjFlags::Any))
+ {
+ if( rSh.IsFrameSelected() &&
+ m_rView.GetDrawFuncPtr() )
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+ rSh.HideCursor();
+ rSh.EnterSelFrameMode();
+ }
+ break;
+ case SwKeyState::GlossaryExpand:
+ {
+ // replace the word or abbreviation with the auto text
+ rSh.StartUndo( SwUndoId::START );
+
+ OUString sFnd(aTmpQHD.CurStr());
+ if( aTmpQHD.m_bIsAutoText )
+ {
+ SwGlossaryList* pList = ::GetGlossaryList();
+ OUString sShrtNm;
+ OUString sGroup;
+ if(pList->GetShortName( sFnd, sShrtNm, sGroup))
+ {
+ rSh.SttSelect();
+ rSh.ExtendSelection(false, aTmpQHD.CurLen());
+ SwGlossaryHdl* pGlosHdl = GetView().GetGlosHdl();
+ pGlosHdl->SetCurGroup(sGroup, true);
+ pGlosHdl->InsertGlossary( sShrtNm);
+ s_pQuickHlpData->m_bAppendSpace = true;
+ }
+ }
+ else
+ {
+ sFnd = sFnd.copy(aTmpQHD.CurLen());
+ rSh.Insert( sFnd );
+ s_pQuickHlpData->m_bAppendSpace = !pACorr ||
+ pACorr->GetSwFlags().bAutoCmpltAppendBlank;
+ }
+ rSh.EndUndo( SwUndoId::END );
+ }
+ break;
+
+ case SwKeyState::NextPrevGlossary:
+ s_pQuickHlpData->Move( aTmpQHD );
+ s_pQuickHlpData->Start(rSh, false);
+ break;
+
+ case SwKeyState::EditFormula:
+ {
+ const sal_uInt16 nId = SwInputChild::GetChildWindowId();
+
+ SfxViewFrame* pVFrame = GetView().GetViewFrame();
+ pVFrame->ToggleChildWindow( nId );
+ SwInputChild* pChildWin = static_cast<SwInputChild*>(pVFrame->
+ GetChildWindow( nId ));
+ if( pChildWin )
+ pChildWin->SetFormula( sFormulaEntry );
+ }
+ break;
+
+ case SwKeyState::ColLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::ColRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::ColLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColLeft, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::ColRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::ColRight, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::ColBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break;
+ case SwKeyState::ColBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::RowBottom, pModOpt->GetTableVMove() ); break;
+ case SwKeyState::CellLeftBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::CellRightBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::CellLeftSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellLeft, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::CellRightSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellRight, pModOpt->GetTableHMove() ); break;
+ case SwKeyState::CellTopBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break;
+ case SwKeyState::CellBottomBig: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom|TableChgWidthHeightType::BiggerMode, pModOpt->GetTableVMove() ); break;
+ case SwKeyState::CellTopSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellTop, pModOpt->GetTableVMove() ); break;
+ case SwKeyState::CellBottomSmall: rSh.SetColRowWidthHeight( TableChgWidthHeightType::CellBottom, pModOpt->GetTableVMove() ); break;
+
+ case SwKeyState::Fly_Change:
+ {
+ SdrView *pSdrView = rSh.GetDrawView();
+ const SdrHdlList& rHdlList = pSdrView->GetHdlList();
+ if(rHdlList.GetFocusHdl())
+ ChangeDrawing( nDir );
+ else
+ ChangeFly( nDir, dynamic_cast<const SwWebView*>( &m_rView) != nullptr );
+ }
+ break;
+ case SwKeyState::Draw_Change :
+ ChangeDrawing( nDir );
+ break;
+ default:
+ break;
+ }
+ if( nSlotId && m_rView.GetViewFrame()->GetBindings().GetRecorder().is() )
+ {
+ SfxRequest aReq(m_rView.GetViewFrame(), nSlotId );
+ aReq.Done();
+ }
+ eKeyState = SwKeyState::End;
+ }
+ }
+ }
+
+ // update the page number in the statusbar
+ sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+ if( KEY_UP == nKey || KEY_DOWN == nKey || KEY_PAGEUP == nKey || KEY_PAGEDOWN == nKey )
+ GetView().GetViewFrame()->GetBindings().Update( FN_STAT_PAGE );
+
+ // in case the buffered characters are inserted
+ if( bFlushBuffer && !m_aInBuffer.isEmpty() )
+ {
+ FlushInBuffer();
+
+ // maybe show Tip-Help
+ if (bNormalChar)
+ {
+ const bool bAutoTextShown
+ = pACfg && pACfg->IsAutoTextTip() && ShowAutoText(rSh.GetChunkForAutoText());
+ if (!bAutoTextShown && pACorr && pACorr->GetSwFlags().bAutoCompleteWords)
+ ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr);
+ }
+ }
+
+ // get the word count dialog to update itself
+ SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId()));
+ if( pWrdCnt )
+ pWrdCnt->UpdateCounts();
+
+}
+
+/**
+ * MouseEvents
+ */
+void SwEditWin::RstMBDownFlags()
+{
+ // Not on all systems a MouseButtonUp is used ahead
+ // of the modal dialog (like on WINDOWS).
+ // So reset the statuses here and release the mouse
+ // for the dialog.
+ m_bMBPressed = false;
+ g_bNoInterrupt = false;
+ EnterArea();
+ ReleaseMouse();
+}
+
+/**
+ * Determines if the current position has a clickable url over a background
+ * frame. In that case, ctrl-click should select the url, not the frame.
+ */
+static bool lcl_urlOverBackground(SwWrtShell& rSh, const Point& rDocPos)
+{
+ SwContentAtPos aSwContentAtPos(IsAttrAtPos::InetAttr);
+ SdrObject* pSelectableObj = rSh.GetObjAt(rDocPos);
+
+ return rSh.GetContentAtPos(rDocPos, aSwContentAtPos) && pSelectableObj->GetLayer() == rSh.GetDoc()->getIDocumentDrawModelAccess().GetHellId();
+}
+
+void SwEditWin::MoveCursor( SwWrtShell &rSh, const Point& rDocPos,
+ const bool bOnlyText, bool bLockView )
+{
+ const bool bTmpNoInterrupt = g_bNoInterrupt;
+ g_bNoInterrupt = false;
+
+ int nTmpSetCursor = 0;
+
+ if( !rSh.IsViewLocked() && bLockView )
+ rSh.LockView( true );
+ else
+ bLockView = false;
+
+ {
+ // only temporary generate move context because otherwise
+ // the query to the content form doesn't work!!!
+ SwMvContext aMvContext( &rSh );
+ nTmpSetCursor = rSh.CallSetCursor(&rDocPos, bOnlyText);
+ g_bValidCursorPos = !(CRSR_POSCHG & nTmpSetCursor);
+ }
+
+ // notify the edit window that from now on we do not use the input language
+ if ( !(CRSR_POSOLD & nTmpSetCursor) )
+ SetUseInputLanguage( false );
+
+ if( bLockView )
+ rSh.LockView( false );
+
+ g_bNoInterrupt = bTmpNoInterrupt;
+}
+
+void SwEditWin::MouseButtonDown(const MouseEvent& _rMEvt)
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ const SwField *pCursorField = rSh.CursorInsideInputField() ? rSh.GetCurField( true ) : nullptr;
+
+ // We have to check if a context menu is shown and we have an UI
+ // active inplace client. In that case we have to ignore the mouse
+ // button down event. Otherwise we would crash (context menu has been
+ // opened by inplace client and we would deactivate the inplace client,
+ // the context menu is closed by VCL asynchronously which in the end
+ // would work on deleted objects or the context menu has no parent anymore)
+ SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient();
+ bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() );
+
+ if (bIsOleActive && vcl::IsInPopupMenuExecute())
+ return;
+
+ MouseEvent aMEvt(_rMEvt);
+
+ if (m_rView.GetPostItMgr()->IsHit(aMEvt.GetPosPixel()))
+ return;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(aMEvt.GetPosPixel()))
+ {
+ pWindow->MouseButtonDown(aMEvt);
+ return;
+ }
+ }
+
+ m_rView.GetPostItMgr()->SetActiveSidebarWin(nullptr);
+
+ GrabFocus();
+ rSh.addCurrentPosition();
+
+ //ignore key modifiers for format paintbrush
+ {
+ bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard
+ && m_pApplyTempl->m_pFormatClipboard->HasContent();
+ if( bExecFormatPaintbrush )
+ aMEvt = MouseEvent(_rMEvt.GetPosPixel(), _rMEvt.GetClicks(), _rMEvt.GetMode(),
+ _rMEvt.GetButtons());
+ }
+
+ m_bWasShdwCursor = nullptr != m_pShadCursor;
+ m_pShadCursor.reset();
+
+ const Point aDocPos(PixelToLogic(aMEvt.GetPosPixel()));
+
+ FrameControlType eControl;
+ bool bOverFly = false;
+ bool bPageAnchored = false;
+ bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored );
+
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly();
+ if (bOverHeaderFooterFly && (!bIsDocReadOnly && rSh.GetCurField()))
+ // We have a field here, that should have priority over header/footer fly.
+ bOverHeaderFooterFly = false;
+
+ // Are we clicking on a blank header/footer area?
+ if ( IsInHeaderFooter( aDocPos, eControl ) || bOverHeaderFooterFly )
+ {
+ const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( aDocPos );
+
+ if ( pPageFrame )
+ {
+ // Is it active?
+ bool bActive = true;
+ const SwPageDesc* pDesc = pPageFrame->GetPageDesc();
+
+ const SwFrameFormat* pFormat = pDesc->GetLeftFormat();
+ if ( pPageFrame->OnRightPage() )
+ pFormat = pDesc->GetRightFormat();
+
+ if ( pFormat )
+ {
+ if ( eControl == FrameControlType::Header )
+ bActive = pFormat->GetHeader().IsActive();
+ else
+ bActive = pFormat->GetFooter().IsActive();
+ }
+
+ if ( !bActive )
+ {
+ // When in Hide-Whitespace mode, we don't want header
+ // and footer controls.
+ if (!rSh.GetViewOptions()->IsHideWhitespaceMode())
+ {
+ SwPaM aPam(*rSh.GetCurrentShellCursor().GetPoint());
+ const bool bWasInHeader = aPam.GetPoint()->nNode.GetNode().FindHeaderStartNode() != nullptr;
+ const bool bWasInFooter = aPam.GetPoint()->nNode.GetNode().FindFooterStartNode() != nullptr;
+
+ // Is the cursor in a part like similar to the one we clicked on? For example,
+ // if the cursor is in a header and we click on an empty header... don't change anything to
+ // keep consistent behaviour due to header edit mode (and the same for the footer as well).
+
+ // Otherwise, we hide the header/footer control if a separator is shown, and vice versa.
+ if (!(bWasInHeader && eControl == FrameControlType::Header) &&
+ !(bWasInFooter && eControl == FrameControlType::Footer))
+ {
+ const bool bSeparatorWasVisible = rSh.IsShowHeaderFooterSeparator(eControl);
+ rSh.SetShowHeaderFooterSeparator(eControl, !bSeparatorWasVisible);
+
+ // Repaint everything
+ Invalidate();
+
+ // tdf#84929. If the footer control had not been showing, do not change the cursor position,
+ // because the user may have scrolled to turn on the separator control and
+ // if the cursor cannot be positioned on-screen, then the user would need to scroll back again to use the control.
+ // This should only be done for the footer. The cursor can always be re-positioned near the header. tdf#134023.
+ if ( eControl == FrameControlType::Footer && !bSeparatorWasVisible
+ && rSh.GetViewOptions()->IsUseHeaderFooterMenu() && !Application::IsHeadlessModeEnabled() )
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Make sure we have the proper Header/Footer separators shown
+ // as these may be changed if clicking on an empty Header/Footer
+ rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, eControl == FrameControlType::Header );
+ rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, eControl == FrameControlType::Footer );
+
+ if ( !rSh.IsHeaderFooterEdit() )
+ {
+ rSh.ToggleHeaderFooterEdit();
+
+ // Repaint everything
+ rSh.GetWin()->Invalidate();
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( rSh.IsHeaderFooterEdit( ) )
+ rSh.ToggleHeaderFooterEdit( );
+ else
+ {
+ // Make sure that the separators are hidden
+ rSh.SetShowHeaderFooterSeparator( FrameControlType::Header, false );
+ rSh.SetShowHeaderFooterSeparator( FrameControlType::Footer, false );
+
+ // Repaint everything
+ // FIXME fdo#67358 for unknown reasons this causes painting
+ // problems when resizing table columns, so disable it
+// rSh.GetWin()->Invalidate();
+ }
+
+ // Toggle Hide-Whitespace if between pages.
+ if (rSh.GetViewOptions()->CanHideWhitespace() &&
+ rSh.GetLayout()->IsBetweenPages(aDocPos))
+ {
+ if (_rMEvt.GetClicks() >= 2)
+ {
+ SwViewOption aOpt(*rSh.GetViewOptions());
+ aOpt.SetHideWhitespaceMode(!aOpt.IsHideWhitespaceMode());
+ rSh.ApplyViewOptions(aOpt);
+ }
+
+ return;
+ }
+ }
+
+ if ( IsChainMode() )
+ {
+ SetChainMode( false );
+ SwRect aDummy;
+ SwFlyFrameFormat *pFormat = static_cast<SwFlyFrameFormat*>(rSh.GetFlyFrameFormat());
+ if ( rSh.Chainable( aDummy, *pFormat, aDocPos ) == SwChainRet::OK )
+ rSh.Chain( *pFormat, aDocPos );
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ return;
+ }
+
+ // After GrabFocus a shell should be pushed. That should actually
+ // work but in practice ...
+ m_rView.SelectShellForDrop();
+
+ bool bCallBase = true;
+
+ if( s_pQuickHlpData->m_bIsDisplayed )
+ s_pQuickHlpData->Stop( rSh );
+ s_pQuickHlpData->m_bAppendSpace = false;
+
+ if( rSh.FinishOLEObj() )
+ return; // end InPlace and the click doesn't count anymore
+
+ CurrShell aCurr( &rSh );
+
+ SdrView *pSdrView = rSh.GetDrawView();
+ if ( pSdrView )
+ {
+ if (pSdrView->MouseButtonDown(aMEvt, GetOutDev()))
+ {
+ rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false);
+ return; // SdrView's event evaluated
+ }
+ }
+
+ m_bIsInMove = false;
+ m_aStartPos = aMEvt.GetPosPixel();
+ m_aRszMvHdlPt.setX( 0 );
+ m_aRszMvHdlPt.setY( 0 );
+
+ SwTab nMouseTabCol = SwTab::COL_NONE;
+ const bool bTmp = !rSh.IsDrawCreate() && !m_pApplyTempl && !rSh.IsInSelect()
+ && aMEvt.GetClicks() == 1 && MOUSE_LEFT == aMEvt.GetButtons();
+ if ( bTmp &&
+ SwTab::COL_NONE != (nMouseTabCol = rSh.WhichMouseTabCol( aDocPos ) ) &&
+ !rSh.IsObjSelectable( aDocPos ) )
+ {
+ // Enhanced table selection
+ if ( SwTab::SEL_HORI <= nMouseTabCol && SwTab::COLSEL_VERT >= nMouseTabCol )
+ {
+ rSh.EnterStdMode();
+ rSh.SelectTableRowCol( aDocPos );
+ if( SwTab::SEL_HORI != nMouseTabCol && SwTab::SEL_HORI_RTL != nMouseTabCol)
+ {
+ m_xRowColumnSelectionStart = aDocPos;
+ m_bIsRowDrag = SwTab::ROWSEL_HORI == nMouseTabCol||
+ SwTab::ROWSEL_HORI_RTL == nMouseTabCol ||
+ SwTab::COLSEL_VERT == nMouseTabCol;
+ m_bMBPressed = true;
+ CaptureMouse();
+ }
+ return;
+ }
+
+ if ( !rSh.IsTableMode() )
+ {
+ // comes from table columns out of the document.
+ if(SwTab::COL_VERT == nMouseTabCol || SwTab::COL_HORI == nMouseTabCol)
+ m_rView.SetTabColFromDoc( true );
+ else
+ m_rView.SetTabRowFromDoc( true );
+
+ m_rView.SetTabColFromDocPos( aDocPos );
+ m_rView.InvalidateRulerPos();
+ SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings();
+ rBind.Update();
+ if (RulerColumnDrag(
+ aMEvt, (SwTab::COL_VERT == nMouseTabCol || SwTab::ROW_HORI == nMouseTabCol)))
+ {
+ m_rView.SetTabColFromDoc( false );
+ m_rView.SetTabRowFromDoc( false );
+ m_rView.InvalidateRulerPos();
+ rBind.Update();
+ bCallBase = false;
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+ else if (bTmp &&
+ rSh.IsNumLabel(aDocPos))
+ {
+ SwTextNode* pNodeAtPos = rSh.GetNumRuleNodeAtPos( aDocPos );
+ m_rView.SetNumRuleNodeFromDoc( pNodeAtPos );
+ m_rView.InvalidateRulerPos();
+ SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings();
+ rBind.Update();
+
+ if (RulerMarginDrag(aMEvt, SwFEShell::IsVerticalModeAtNdAndPos(*pNodeAtPos, aDocPos)))
+ {
+ m_rView.SetNumRuleNodeFromDoc( nullptr );
+ m_rView.InvalidateRulerPos();
+ rBind.Update();
+ bCallBase = false;
+ }
+ else
+ {
+ // Make sure the pointer is set to 0, otherwise it may point to
+ // nowhere after deleting the corresponding text node.
+ m_rView.SetNumRuleNodeFromDoc( nullptr );
+ return;
+ }
+ }
+
+ if ( rSh.IsInSelect() )
+ rSh.EndSelect();
+
+ // query against LEFT because otherwise for example also a right
+ // click releases the selection.
+ if (MOUSE_LEFT == aMEvt.GetButtons())
+ {
+ bool bOnlyText = false;
+ m_bMBPressed = true;
+ g_bNoInterrupt = true;
+ m_nKS_NUMDOWN_Count = 0;
+
+ CaptureMouse();
+
+ // reset cursor position if applicable
+ rSh.ResetCursorStack();
+
+ switch (aMEvt.GetModifier() + aMEvt.GetButtons())
+ {
+ case MOUSE_LEFT:
+ case MOUSE_LEFT + KEY_SHIFT:
+ case MOUSE_LEFT + KEY_MOD2:
+ if( rSh.IsObjSelected() )
+ {
+ SdrHdl* pHdl;
+ if( !bIsDocReadOnly &&
+ !m_pAnchorMarker &&
+ pSdrView &&
+ nullptr != ( pHdl = pSdrView->PickHandle(aDocPos) ) &&
+ ( pHdl->GetKind() == SdrHdlKind::Anchor ||
+ pHdl->GetKind() == SdrHdlKind::Anchor_TR ) )
+ {
+ // #i121463# Set selected during drag
+ pHdl->SetSelected();
+ m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) );
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ return;
+ }
+ }
+ if (EnterDrawMode(aMEvt, aDocPos))
+ {
+ g_bNoInterrupt = false;
+ return;
+ }
+ else if ( m_rView.GetDrawFuncPtr() && m_bInsFrame )
+ {
+ StopInsFrame();
+ rSh.Edit();
+ }
+
+ // Without SHIFT because otherwise Toggle doesn't work at selection
+ if (aMEvt.GetClicks() == 1)
+ {
+ if ( rSh.IsSelFrameMode())
+ {
+ SdrHdl* pHdl = rSh.GetDrawView()->PickHandle(aDocPos);
+ bool bHitHandle = pHdl && pHdl->GetKind() != SdrHdlKind::Anchor &&
+ pHdl->GetKind() != SdrHdlKind::Anchor_TR;
+
+ if ((rSh.IsInsideSelectedObj(aDocPos) || bHitHandle)
+ && (aMEvt.GetModifier() != KEY_SHIFT || bHitHandle))
+ {
+ rSh.EnterSelFrameMode( &aDocPos );
+ if ( !m_pApplyTempl )
+ {
+ // only if no position to size was hit.
+ if (!bHitHandle)
+ {
+ StartDDTimer();
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ }
+ g_bFrameDrag = true;
+ }
+ g_bNoInterrupt = false;
+ return;
+ }
+ }
+ }
+ }
+
+ bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly();
+ if ( !bExecHyperlinks )
+ {
+ const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink );
+ if ((bSecureOption && aMEvt.GetModifier() == KEY_MOD1)
+ || (!bSecureOption && aMEvt.GetModifier() != KEY_MOD1))
+ bExecHyperlinks = true;
+ }
+
+ // Enhanced selection
+ sal_uInt8 nNumberOfClicks = static_cast<sal_uInt8>(aMEvt.GetClicks() % 4);
+ if (0 == nNumberOfClicks && 0 < aMEvt.GetClicks())
+ nNumberOfClicks = 4;
+
+ bool bExecDrawTextLink = false;
+
+ switch (aMEvt.GetModifier() + aMEvt.GetButtons())
+ {
+ case MOUSE_LEFT:
+ case MOUSE_LEFT + KEY_MOD1:
+ case MOUSE_LEFT + KEY_MOD2:
+ {
+
+ // fdo#79604: first, check if a link has been clicked - do not
+ // select fly in this case!
+ if (1 == nNumberOfClicks)
+ {
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+
+ // hit a URL in DrawText object?
+ if (bExecHyperlinks && pSdrView)
+ {
+ SdrViewEvent aVEvt;
+ pSdrView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
+
+ if (aVEvt.meEvent == SdrEventKind::ExecuteUrl)
+ bExecDrawTextLink = true;
+ }
+ }
+
+ if (1 == nNumberOfClicks && !bExecDrawTextLink)
+ {
+ // only try to select frame, if pointer already was
+ // switched accordingly
+ if ( m_aActHitType != SdrHitKind::NONE && !rSh.IsSelFrameMode() &&
+ !GetView().GetViewFrame()->GetDispatcher()->IsLocked())
+ {
+ // Test if there is a draw object at that position and if it should be selected.
+ bool bShould = rSh.ShouldObjectBeSelected(aDocPos);
+
+ if(bShould)
+ {
+ m_rView.NoRotate();
+ rSh.HideCursor();
+
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+ bool bSelObj
+ = rSh.SelectObj(aDocPos, aMEvt.IsMod1() ? SW_ENTER_GROUP : 0);
+ if( bUnLockView )
+ rSh.LockView( false );
+
+ if( bSelObj )
+ {
+ // if the frame was deselected in the macro
+ // the cursor just has to be displayed again
+ if( FrameTypeFlags::NONE == rSh.GetSelFrameType() )
+ rSh.ShowCursor();
+ else
+ {
+ if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr())
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+
+ rSh.EnterSelFrameMode( &aDocPos );
+ g_bFrameDrag = true;
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ }
+ return;
+ }
+ else
+ bOnlyText = rSh.IsObjSelectable( aDocPos );
+
+ if (!m_rView.GetDrawFuncPtr())
+ rSh.ShowCursor();
+ }
+ else
+ bOnlyText = KEY_MOD1 != aMEvt.GetModifier();
+ }
+ else if ( rSh.IsSelFrameMode() &&
+ (m_aActHitType == SdrHitKind::NONE ||
+ !rSh.IsInsideSelectedObj( aDocPos )))
+ {
+ m_rView.NoRotate();
+ SdrHdl *pHdl;
+ if( !bIsDocReadOnly && !m_pAnchorMarker && nullptr !=
+ ( pHdl = pSdrView->PickHandle(aDocPos) ) &&
+ ( pHdl->GetKind() == SdrHdlKind::Anchor ||
+ pHdl->GetKind() == SdrHdlKind::Anchor_TR ) )
+ {
+ m_pAnchorMarker.reset( new SwAnchorMarker( pHdl ) );
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ return;
+ }
+ else
+ {
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+ sal_uInt8 nFlag = aMEvt.IsShift() ? SW_ADD_SELECT : 0;
+ if (aMEvt.IsMod1())
+ nFlag = nFlag | SW_ENTER_GROUP;
+
+ if ( rSh.IsSelFrameMode() )
+ {
+ rSh.UnSelectFrame();
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+
+ bool bSelObj = rSh.SelectObj( aDocPos, nFlag );
+ if( bUnLockView )
+ rSh.LockView( false );
+
+ if( !bSelObj )
+ {
+ // move cursor here so that it is not drawn in the
+ // frame first; ShowCursor() happens in LeaveSelFrameMode()
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false));
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ bCallBase = false;
+ }
+ else
+ {
+ rSh.HideCursor();
+ rSh.EnterSelFrameMode( &aDocPos );
+ rSh.SelFlyGrabCursor();
+ rSh.MakeSelVisible();
+ g_bFrameDrag = true;
+ if( rSh.IsFrameSelected() &&
+ m_rView.GetDrawFuncPtr() )
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+ UpdatePointer(aDocPos, aMEvt.GetModifier());
+ return;
+ }
+ }
+ }
+ }
+
+ switch ( nNumberOfClicks )
+ {
+ case 1:
+ break;
+ case 2:
+ {
+ g_bFrameDrag = false;
+ if (!bIsDocReadOnly && rSh.IsInsideSelectedObj(aDocPos)
+ && (FlyProtectFlags::NONE
+ == rSh.IsSelObjProtected(FlyProtectFlags::Content
+ | FlyProtectFlags::Parent)
+ || rSh.GetSelectionType() == SelectionType::Ole))
+ {
+ /* This is no good: on the one hand GetSelectionType is used as flag field
+ * (take a look into the GetSelectionType method) and on the other hand the
+ * return value is used in a switch without proper masking (very nice), this must lead to trouble
+ */
+ switch ( rSh.GetSelectionType() & ~SelectionType( SelectionType::FontWork | SelectionType::ExtrudedCustomShape ) )
+ {
+ case SelectionType::Graphic:
+ RstMBDownFlags();
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ GetView().GetViewFrame()->GetBindings().Execute(
+ FN_FORMAT_GRAFIC_DLG, nullptr,
+ SfxCallMode::RECORD|SfxCallMode::SLOT);
+ }
+ return;
+
+ // double click on OLE object --> OLE-InPlace
+ case SelectionType::Ole:
+ RstMBDownFlags();
+ rSh.LaunchOLEObj();
+ return;
+
+ case SelectionType::Frame:
+ RstMBDownFlags();
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ GetView().GetViewFrame()->GetBindings().Execute(
+ FN_FORMAT_FRAME_DLG, nullptr,
+ SfxCallMode::RECORD|SfxCallMode::SLOT);
+ }
+ return;
+
+ case SelectionType::DrawObject:
+ RstMBDownFlags();
+ EnterDrawTextMode(aDocPos);
+ if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) )
+ pSwDrawTextShell->Init();
+ return;
+
+ default: break;
+ }
+ }
+
+ // if the cursor position was corrected or if a Fly
+ // was selected in ReadOnlyMode, no word selection, except when tiled rendering.
+ if ((!g_bValidCursorPos || rSh.IsFrameSelected()) && !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SwField *pField;
+ bool bFootnote = false;
+
+ if( !bIsDocReadOnly &&
+ (nullptr != (pField = rSh.GetCurField(true)) ||
+ ( bFootnote = rSh.GetCurFootnote() ) ) )
+ {
+ RstMBDownFlags();
+ if( bFootnote )
+ GetView().GetViewFrame()->GetBindings().Execute( FN_EDIT_FOOTNOTE );
+ else
+ {
+ SwFieldTypesEnum nTypeId = pField->GetTypeId();
+ SfxViewFrame* pVFrame = GetView().GetViewFrame();
+ switch( nTypeId )
+ {
+ case SwFieldTypesEnum::Postit:
+ case SwFieldTypesEnum::Script:
+ {
+ // if it's a Readonly region, status has to be enabled
+ sal_uInt16 nSlot = SwFieldTypesEnum::Postit == nTypeId ? FN_POSTIT : FN_JAVAEDIT;
+ SfxBoolItem aItem(nSlot, true);
+ pVFrame->GetBindings().SetState(aItem);
+ pVFrame->GetBindings().Execute(nSlot);
+ break;
+ }
+ case SwFieldTypesEnum::Authority :
+ pVFrame->GetBindings().Execute(FN_EDIT_AUTH_ENTRY_DLG);
+ break;
+ case SwFieldTypesEnum::Input:
+ case SwFieldTypesEnum::Dropdown:
+ case SwFieldTypesEnum::SetInput:
+ pVFrame->GetBindings().Execute(FN_UPDATE_INPUTFIELDS);
+ break;
+ default:
+ pVFrame->GetBindings().Execute(FN_EDIT_FIELD);
+ }
+ }
+ return;
+ }
+ // in extended mode double and triple
+ // click has no effect.
+ if ( rSh.IsExtMode() || rSh.IsBlockMode() )
+ return;
+
+ // select word, AdditionalMode if applicable
+ if (KEY_MOD1 == aMEvt.GetModifier() && !rSh.IsAddMode())
+ {
+ rSh.EnterAddMode();
+ rSh.SelWrd( &aDocPos );
+ rSh.LeaveAddMode();
+ }
+ else
+ {
+ if (!rSh.SelWrd(&aDocPos) && comphelper::LibreOfficeKit::isActive())
+ // Double click did not select any word: try to
+ // select the current cell in case we are in a
+ // table.
+ rSh.SelTableBox();
+ }
+
+ SwContentAtPos aContentAtPos(IsAttrAtPos::FormControl);
+ if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) &&
+ aContentAtPos.aFnd.pFieldmark != nullptr)
+ {
+ IFieldmark *pFieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark );
+ if ( pFieldBM->GetFieldname( ) == ODF_FORMDROPDOWN || pFieldBM->GetFieldname( ) == ODF_FORMDATE )
+ {
+ RstMBDownFlags();
+ rSh.getIDocumentMarkAccess()->ClearFieldActivation();
+ GetView().GetViewFrame()->GetBindings().Execute(SID_FM_CTL_PROPERTIES);
+ return;
+ }
+ }
+
+ g_bHoldSelection = true;
+ return;
+ }
+ case 3:
+ case 4:
+ {
+ g_bFrameDrag = false;
+ // in extended mode double and triple
+ // click has no effect.
+ if ( rSh.IsExtMode() )
+ return;
+
+ // if the cursor position was corrected or if a Fly
+ // was selected in ReadOnlyMode, no word selection.
+ if ( !g_bValidCursorPos || rSh.IsFrameSelected() )
+ return;
+
+ // select line, AdditionalMode if applicable
+ const bool bMod = KEY_MOD1 == aMEvt.GetModifier() && !rSh.IsAddMode();
+
+ if ( bMod )
+ rSh.EnterAddMode();
+
+ // Enhanced selection
+ if ( 3 == nNumberOfClicks )
+ rSh.SelSentence( &aDocPos );
+ else
+ rSh.SelPara( &aDocPos );
+
+ if ( bMod )
+ rSh.LeaveAddMode();
+
+ g_bHoldSelection = true;
+ return;
+ }
+
+ default:
+ return;
+ }
+
+ [[fallthrough]];
+ }
+ case MOUSE_LEFT + KEY_SHIFT:
+ case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1:
+ {
+ bool bLockView = m_bWasShdwCursor;
+
+ switch (aMEvt.GetModifier())
+ {
+ case KEY_MOD1 + KEY_SHIFT:
+ {
+ if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) )
+ {
+ m_rView.NoRotate();
+ rSh.HideCursor();
+ if ( rSh.IsSelFrameMode() )
+ rSh.SelectObj(aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP);
+ else
+ { if ( rSh.SelectObj( aDocPos, SW_ADD_SELECT | SW_ENTER_GROUP ) )
+ {
+ rSh.EnterSelFrameMode( &aDocPos );
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ g_bFrameDrag = true;
+ return;
+ }
+ }
+ }
+ else if( rSh.IsSelFrameMode() &&
+ rSh.GetDrawView()->PickHandle( aDocPos ))
+ {
+ g_bFrameDrag = true;
+ g_bNoInterrupt = false;
+ return;
+ }
+ }
+ break;
+ case KEY_MOD1:
+ if ( !bExecDrawTextLink )
+ {
+ if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ // ctrl+left-click on outline node frame
+ SwContentAtPos aContentAtPos(IsAttrAtPos::Outline);
+ if(rSh.GetContentAtPos(aDocPos, aContentAtPos))
+ {
+ SwOutlineNodes::size_type nPos;
+ if (rSh.GetNodes().GetOutLineNds().Seek_Entry(aContentAtPos.aFnd.pNode, &nPos))
+ {
+ ToggleOutlineContentVisibility(nPos, false);
+ return;
+ }
+ }
+ }
+ if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) && !lcl_urlOverBackground( rSh, aDocPos ) )
+ {
+ m_rView.NoRotate();
+ rSh.HideCursor();
+ if ( rSh.IsSelFrameMode() )
+ rSh.SelectObj(aDocPos, SW_ENTER_GROUP);
+ else
+ { if ( rSh.SelectObj( aDocPos, SW_ENTER_GROUP ) )
+ {
+ rSh.EnterSelFrameMode( &aDocPos );
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ g_bFrameDrag = true;
+ return;
+ }
+ }
+ }
+ else if( rSh.IsSelFrameMode() &&
+ rSh.GetDrawView()->PickHandle( aDocPos ))
+ {
+ g_bFrameDrag = true;
+ g_bNoInterrupt = false;
+ return;
+ }
+ else
+ {
+ if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() )
+ {
+ rSh.PushMode();
+ g_bModePushed = true;
+
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+ rSh.EnterAddMode();
+ if( bUnLockView )
+ rSh.LockView( false );
+ }
+ bCallBase = false;
+ }
+ }
+ break;
+ case KEY_MOD2:
+ {
+ if ( !rSh.IsAddMode() && !rSh.IsExtMode() && !rSh.IsBlockMode() )
+ {
+ rSh.PushMode();
+ g_bModePushed = true;
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+ rSh.EnterBlockMode();
+ if( bUnLockView )
+ rSh.LockView( false );
+ }
+ bCallBase = false;
+ }
+ break;
+ case KEY_SHIFT:
+ {
+ if ( !m_bInsDraw && IsDrawObjSelectable( rSh, aDocPos ) )
+ {
+ m_rView.NoRotate();
+ rSh.HideCursor();
+ if ( rSh.IsSelFrameMode() )
+ {
+ rSh.SelectObj(aDocPos, SW_ADD_SELECT);
+
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ if (rMarkList.GetMark(0) == nullptr)
+ {
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ g_bFrameDrag = false;
+ }
+ }
+ else
+ { if ( rSh.SelectObj( aDocPos ) )
+ {
+ rSh.EnterSelFrameMode( &aDocPos );
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ g_bFrameDrag = true;
+ return;
+ }
+ }
+ }
+ else
+ {
+ if ( rSh.IsSelFrameMode() &&
+ rSh.IsInsideSelectedObj( aDocPos ) )
+ {
+ rSh.EnterSelFrameMode( &aDocPos );
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ g_bFrameDrag = true;
+ return;
+ }
+ if ( rSh.IsSelFrameMode() )
+ {
+ rSh.UnSelectFrame();
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ g_bFrameDrag = false;
+ }
+ if ( !rSh.IsExtMode() )
+ {
+ // don't start a selection when an
+ // URL field or a graphic is clicked
+ bool bSttSelect = rSh.HasSelection() ||
+ PointerStyle::RefHand != GetPointer();
+
+ if( !bSttSelect )
+ {
+ bSttSelect = true;
+ if( bExecHyperlinks )
+ {
+ SwContentAtPos aContentAtPos(
+ IsAttrAtPos::Ftn |
+ IsAttrAtPos::InetAttr );
+
+ if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) )
+ {
+ if( !rSh.IsViewLocked() &&
+ !rSh.IsReadOnlyAvailable() &&
+ aContentAtPos.IsInProtectSect() )
+ bLockView = true;
+
+ bSttSelect = false;
+ }
+ else if( rSh.IsURLGrfAtPos( aDocPos ))
+ bSttSelect = false;
+ }
+ }
+
+ if( bSttSelect )
+ rSh.SttSelect();
+ }
+ }
+ bCallBase = false;
+ break;
+ }
+ default:
+ if( !rSh.IsViewLocked() )
+ {
+ SwContentAtPos aContentAtPos( IsAttrAtPos::ClickField |
+ IsAttrAtPos::InetAttr );
+ if( rSh.GetContentAtPos( aDocPos, aContentAtPos ) &&
+ !rSh.IsReadOnlyAvailable() &&
+ aContentAtPos.IsInProtectSect() )
+ bLockView = true;
+ }
+ }
+
+ if ( rSh.IsGCAttr() )
+ {
+ rSh.GCAttr();
+ rSh.ClearGCAttr();
+ }
+
+ SwContentAtPos aFieldAtPos(IsAttrAtPos::Field);
+ bool bEditableFieldClicked = false;
+
+ // Are we clicking on a field?
+ if (rSh.GetContentAtPos(aDocPos, aFieldAtPos))
+ {
+ bool bEditableField = (aFieldAtPos.pFndTextAttr != nullptr
+ && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD);
+
+ if (!bEditableField)
+ {
+ rSh.CallSetCursor(&aDocPos, bOnlyText);
+ // Unfortunately the cursor may be on field
+ // position or on position after field depending on which
+ // half of the field was clicked on.
+ SwTextAttr const*const pTextField(aFieldAtPos.pFndTextAttr);
+ if (pTextField && rSh.GetCurrentShellCursor().GetPoint()->nContent
+ .GetIndex() != pTextField->GetStart())
+ {
+ assert(rSh.GetCurrentShellCursor().GetPoint()->nContent
+ .GetIndex() == (pTextField->GetStart() + 1));
+ rSh.Left( CRSR_SKIP_CHARS, false, 1, false );
+ }
+ // don't go into the !bOverSelect block below - it moves
+ // the cursor
+ break;
+ }
+ else
+ {
+ bEditableFieldClicked = true;
+ }
+ }
+
+ bool bOverSelect = rSh.TestCurrPam( aDocPos );
+ bool bOverURLGrf = false;
+ if( !bOverSelect )
+ bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos );
+
+ if ( !bOverSelect || rSh.IsInSelect() )
+ {
+ MoveCursor( rSh, aDocPos, bOnlyText, bLockView );
+ bCallBase = false;
+ }
+ if (!bOverURLGrf && !bExecDrawTextLink && !bOnlyText)
+ {
+ const SelectionType nSelType = rSh.GetSelectionType();
+ // Check in general, if an object is selectable at given position.
+ // Thus, also text fly frames in background become selectable via Ctrl-Click.
+ if ( ( nSelType & SelectionType::Ole ||
+ nSelType & SelectionType::Graphic ||
+ rSh.IsObjSelectable( aDocPos ) ) && !lcl_urlOverBackground( rSh, aDocPos ) )
+ {
+ SwMvContext aMvContext( &rSh );
+ rSh.EnterSelFrameMode();
+ bCallBase = false;
+ }
+ }
+ if ( !bOverSelect && bEditableFieldClicked && (!pCursorField ||
+ pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField()))
+ {
+ // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART
+ // and CH_TXT_ATR_INPUTFIELDEND
+ rSh.SttSelect();
+ rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1,
+ *(aFieldAtPos.pFndTextAttr->End()) - 1 );
+ }
+ // don't reset here any longer so that, in case through MouseMove
+ // with pressed Ctrl key a multiple-selection should happen,
+ // the previous selection is not released in Drag.
+ break;
+ }
+ }
+ }
+ else if (MOUSE_RIGHT == aMEvt.GetButtons())
+ {
+ if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton()
+ && aMEvt.GetModifier() == KEY_MOD1)
+ {
+ // ctrl+right-click on outline node frame
+ SwContentAtPos aContentAtPos(IsAttrAtPos::Outline);
+ if(rSh.GetContentAtPos(aDocPos, aContentAtPos))
+ {
+ SwOutlineNodes::size_type nPos;
+ if (rSh.GetNodes().GetOutLineNds().Seek_Entry(aContentAtPos.aFnd.pNode, &nPos))
+ {
+ ToggleOutlineContentVisibility(nPos, !rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent());
+ return;
+ }
+ }
+ }
+ else if (!aMEvt.GetModifier() && static_cast<sal_uInt8>(aMEvt.GetClicks() % 4) == 1
+ && !rSh.TestCurrPam(aDocPos))
+ {
+ SwContentAtPos aFieldAtPos(IsAttrAtPos::Field);
+
+ // Are we clicking on a field?
+ if (g_bValidCursorPos
+ && rSh.GetContentAtPos(aDocPos, aFieldAtPos)
+ && aFieldAtPos.pFndTextAttr != nullptr
+ && aFieldAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD
+ && (!pCursorField || pCursorField != aFieldAtPos.pFndTextAttr->GetFormatField().GetField()))
+ {
+ // Move the cursor
+ MoveCursor( rSh, aDocPos, rSh.IsObjSelectable( aDocPos ), m_bWasShdwCursor );
+ bCallBase = false;
+
+ // select content of Input Field, but exclude CH_TXT_ATR_INPUTFIELDSTART
+ // and CH_TXT_ATR_INPUTFIELDEND
+ rSh.SttSelect();
+ rSh.SelectTextModel( aFieldAtPos.pFndTextAttr->GetStart() + 1,
+ *(aFieldAtPos.pFndTextAttr->End()) - 1 );
+ }
+ }
+ }
+
+ if (bCallBase)
+ Window::MouseButtonDown(aMEvt);
+}
+
+bool SwEditWin::changeMousePointer(Point const & rDocPoint)
+{
+ SwWrtShell & rShell = m_rView.GetWrtShell();
+
+ SwTab nMouseTabCol;
+ if ( SwTab::COL_NONE != (nMouseTabCol = rShell.WhichMouseTabCol( rDocPoint ) ) &&
+ !rShell.IsObjSelectable( rDocPoint ) )
+ {
+ PointerStyle nPointer = PointerStyle::Null;
+ bool bChkTableSel = false;
+
+ switch ( nMouseTabCol )
+ {
+ case SwTab::COL_VERT :
+ case SwTab::ROW_HORI :
+ nPointer = PointerStyle::VSizeBar;
+ bChkTableSel = true;
+ break;
+ case SwTab::ROW_VERT :
+ case SwTab::COL_HORI :
+ nPointer = PointerStyle::HSizeBar;
+ bChkTableSel = true;
+ break;
+ // Enhanced table selection
+ case SwTab::SEL_HORI :
+ nPointer = PointerStyle::TabSelectSE;
+ break;
+ case SwTab::SEL_HORI_RTL :
+ case SwTab::SEL_VERT :
+ nPointer = PointerStyle::TabSelectSW;
+ break;
+ case SwTab::COLSEL_HORI :
+ case SwTab::ROWSEL_VERT :
+ nPointer = PointerStyle::TabSelectS;
+ break;
+ case SwTab::ROWSEL_HORI :
+ nPointer = PointerStyle::TabSelectE;
+ break;
+ case SwTab::ROWSEL_HORI_RTL :
+ case SwTab::COLSEL_VERT :
+ nPointer = PointerStyle::TabSelectW;
+ break;
+ default: break; // prevent compiler warning
+ }
+
+ if ( PointerStyle::Null != nPointer &&
+ // i#35543 - Enhanced table selection is explicitly allowed in table mode
+ ( !bChkTableSel || !rShell.IsTableMode() ) &&
+ !comphelper::LibreOfficeKit::isActive() )
+ {
+ SetPointer( nPointer );
+ }
+
+ return true;
+ }
+ else if (rShell.IsNumLabel(rDocPoint, RULER_MOUSE_MARGINWIDTH))
+ {
+ // i#42921 - consider vertical mode
+ SwTextNode* pNodeAtPos = rShell.GetNumRuleNodeAtPos( rDocPoint );
+ const PointerStyle nPointer =
+ SwFEShell::IsVerticalModeAtNdAndPos( *pNodeAtPos, rDocPoint )
+ ? PointerStyle::VSizeBar
+ : PointerStyle::HSizeBar;
+ SetPointer( nPointer );
+
+ return true;
+ }
+ return false;
+}
+
+void SwEditWin::MouseMove(const MouseEvent& _rMEvt)
+{
+ MouseEvent rMEvt(_rMEvt);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel()))
+ {
+ pWindow->MouseMove(rMEvt);
+ return;
+ }
+ }
+
+ //ignore key modifiers for format paintbrush
+ {
+ bool bExecFormatPaintbrush = m_pApplyTempl && m_pApplyTempl->m_pFormatClipboard
+ && m_pApplyTempl->m_pFormatClipboard->HasContent();
+ if( bExecFormatPaintbrush )
+ rMEvt = MouseEvent( _rMEvt.GetPosPixel(), _rMEvt.GetClicks(),
+ _rMEvt.GetMode(), _rMEvt.GetButtons() );
+ }
+
+ // as long as an action is running the MouseMove should be disconnected
+ // otherwise bug 40102 occurs
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ if( rSh.ActionPend() )
+ return ;
+
+ if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ // add/remove outline content hide button
+ const SwNodes& rNds = rSh.GetDoc()->GetNodes();
+ SwOutlineNodes::size_type nPos;
+ SwContentAtPos aSwContentAtPos(IsAttrAtPos::Outline);
+ if (rSh.GetContentAtPos(PixelToLogic(rMEvt.GetPosPixel()), aSwContentAtPos))
+ {
+ // mouse pointer is on an outline paragraph node
+ if(aSwContentAtPos.aFnd.pNode && aSwContentAtPos.aFnd.pNode->IsTextNode())
+ {
+ // Get the outline paragraph frame and compare it to the saved outline frame. If they
+ // are not the same, remove the fold button from the saved outline frame, if not
+ // already removed, and then add a fold button to the mouse over outline frame if
+ // the content is not folded.
+ SwContentFrame* pContentFrame =
+ aSwContentAtPos.aFnd.pNode->GetTextNode()->getLayoutFrame(rSh.GetLayout());
+ if (pContentFrame != m_pSavedOutlineFrame)
+ {
+ if (m_pSavedOutlineFrame)
+ {
+ if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid())
+ {
+ SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst();
+ if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) &&
+ rSh.GetAttrOutlineContentVisible(nPos))
+ {
+ GetFrameControlsManager().RemoveControlsByType(
+ FrameControlType::Outline, m_pSavedOutlineFrame);
+ }
+ }
+ }
+ m_pSavedOutlineFrame = static_cast<SwTextFrame*>(pContentFrame);
+ }
+ // show fold button if outline content is visible
+ if (rNds.GetOutLineNds().Seek_Entry(aSwContentAtPos.aFnd.pNode->GetTextNode(), &nPos) &&
+ rSh.GetAttrOutlineContentVisible(nPos))
+ GetFrameControlsManager().SetOutlineContentVisibilityButton(pContentFrame);
+ }
+ }
+ else if (m_pSavedOutlineFrame)
+ {
+ // The saved frame may not still be in the document, e.g., when an outline paragraph
+ // is deleted. This causes the call to GetTextNodeFirst to behave badly. Use
+ // isFrameAreaDefinitionValid to check if the frame is still in the document.
+ if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid())
+ {
+ // current pointer pos is not over an outline frame
+ // previous pointer pos was over an outline frame
+ // remove outline content visibility button if showing
+ SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst();
+ if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) &&
+ rSh.GetAttrOutlineContentVisible(nPos))
+ {
+ GetFrameControlsManager().RemoveControlsByType(
+ FrameControlType::Outline, m_pSavedOutlineFrame);
+ }
+ }
+ m_pSavedOutlineFrame = nullptr;
+ }
+ }
+
+ if( m_pShadCursor && 0 != (rMEvt.GetModifier() + rMEvt.GetButtons() ) )
+ {
+ m_pShadCursor.reset();
+ }
+
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly();
+
+ CurrShell aCurr( &rSh );
+
+ //aPixPt == Point in Pixel, relative to ChildWin
+ //aDocPt == Point in Twips, document coordinates
+ const Point aPixPt( rMEvt.GetPosPixel() );
+ const Point aDocPt( PixelToLogic( aPixPt ) );
+
+ if ( IsChainMode() )
+ {
+ UpdatePointer( aDocPt, rMEvt.GetModifier() );
+ return;
+ }
+
+ SdrView *pSdrView = rSh.GetDrawView();
+
+ const SwCallMouseEvent aLastCallEvent( m_aSaveCallEvent );
+ m_aSaveCallEvent.Clear();
+
+ if ( !bIsDocReadOnly && pSdrView && pSdrView->MouseMove(rMEvt,GetOutDev()) )
+ {
+ SetPointer( PointerStyle::Text );
+ return; // evaluate SdrView's event
+ }
+
+ const Point aOldPt( rSh.VisArea().Pos() );
+ const bool bInsWin = rSh.VisArea().Contains( aDocPt ) || comphelper::LibreOfficeKit::isActive();
+
+ if (rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ if (m_pSavedOutlineFrame && !bInsWin)
+ {
+ // the mouse pointer has left the building (edit window)
+ // remove the outline content visibility button if showing
+ if (m_pSavedOutlineFrame->isFrameAreaDefinitionValid())
+ {
+ const SwNodes& rNds = rSh.GetDoc()->GetNodes();
+ SwOutlineNodes::size_type nPos;
+ SwTextNode* pTextNode = m_pSavedOutlineFrame->GetTextNodeFirst();
+ if (pTextNode && rNds.GetOutLineNds().Seek_Entry(pTextNode, &nPos) &&
+ rSh.GetAttrOutlineContentVisible(nPos))
+ {
+ GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline,
+ m_pSavedOutlineFrame);
+ }
+ }
+ m_pSavedOutlineFrame = nullptr;
+ }
+ }
+
+ if( m_pShadCursor && !bInsWin )
+ {
+ m_pShadCursor.reset();
+ }
+
+ if( bInsWin && m_xRowColumnSelectionStart )
+ {
+ EnterArea();
+ Point aPos( aDocPt );
+ if( rSh.SelectTableRowCol( *m_xRowColumnSelectionStart, &aPos, m_bIsRowDrag ))
+ return;
+ }
+
+ // position is necessary for OS/2 because obviously after a MB-Down
+ // a MB-Move is called immediately.
+ if( g_bDDTimerStarted )
+ {
+ Point aDD( SwEditWin::s_nDDStartPosX, SwEditWin::s_nDDStartPosY );
+ aDD = LogicToPixel( aDD );
+ tools::Rectangle aRect( aDD.X()-3, aDD.Y()-3, aDD.X()+3, aDD.Y()+3 );
+ if ( !aRect.Contains( aPixPt ) )
+ StopDDTimer( &rSh, aDocPt );
+ }
+
+ if(m_rView.GetDrawFuncPtr())
+ {
+ if( m_bInsDraw )
+ {
+ m_rView.GetDrawFuncPtr()->MouseMove( rMEvt );
+ if ( !bInsWin )
+ {
+ Point aTmp( aDocPt );
+ aTmp += rSh.VisArea().Pos() - aOldPt;
+ LeaveArea( aTmp );
+ }
+ else
+ EnterArea();
+ return;
+ }
+ else if(!rSh.IsFrameSelected() && !rSh.IsObjSelected())
+ {
+ SfxBindings &rBnd = rSh.GetView().GetViewFrame()->GetBindings();
+ Point aRelPos = rSh.GetRelativePagePosition(aDocPt);
+ if(aRelPos.X() >= 0)
+ {
+ FieldUnit eMetric = ::GetDfltMetric(dynamic_cast<SwWebView*>( &GetView()) != nullptr );
+ SW_MOD()->PutItem(SfxUInt16Item(SID_ATTR_METRIC, static_cast< sal_uInt16 >(eMetric)));
+ const SfxPointItem aTmp1( SID_ATTR_POSITION, aRelPos );
+ rBnd.SetState( aTmp1 );
+ }
+ else
+ {
+ rBnd.Invalidate(SID_ATTR_POSITION);
+ }
+ rBnd.Invalidate(SID_ATTR_SIZE);
+ const SfxStringItem aCell( SID_TABLE_CELL, OUString() );
+ rBnd.SetState( aCell );
+ }
+ }
+
+ // determine if we only change the mouse pointer and return
+ if (!bIsDocReadOnly && bInsWin && !m_pApplyTempl && !rSh.IsInSelect() && changeMousePointer(aDocPt))
+ {
+ return;
+ }
+
+ bool bDelShadCursor = true;
+
+ switch ( rMEvt.GetModifier() + rMEvt.GetButtons() )
+ {
+ case MOUSE_LEFT:
+ if( m_pAnchorMarker )
+ {
+ // Now we need to refresh the SdrHdl pointer of m_pAnchorMarker.
+ // This looks a little bit tricky, but it solves the following
+ // problem: the m_pAnchorMarker contains a pointer to an SdrHdl,
+ // if the FindAnchorPos-call cause a scrolling of the visible
+ // area, it's possible that the SdrHdl will be destroyed and a
+ // new one will initialized at the original position(GetHdlPos).
+ // So the m_pAnchorMarker has to find the right SdrHdl, if it's
+ // the old one, it will find it with position aOld, if this one
+ // is destroyed, it will find a new one at position GetHdlPos().
+
+ const Point aOld = m_pAnchorMarker->GetPosForHitTest( *(rSh.GetOut()) );
+ Point aNew = rSh.FindAnchorPos( aDocPt );
+ SdrHdl* pHdl;
+ if( pSdrView && (nullptr!=( pHdl = pSdrView->PickHandle( aOld ) )||
+ nullptr !=(pHdl = pSdrView->PickHandle( m_pAnchorMarker->GetHdlPos()) ) ) &&
+ ( pHdl->GetKind() == SdrHdlKind::Anchor ||
+ pHdl->GetKind() == SdrHdlKind::Anchor_TR ) )
+ {
+ m_pAnchorMarker->ChgHdl( pHdl );
+ if( aNew.X() || aNew.Y() )
+ {
+ m_pAnchorMarker->SetPos( aNew );
+ m_pAnchorMarker->SetLastPos( aDocPt );
+ }
+ }
+ else
+ {
+ m_pAnchorMarker.reset();
+ }
+ }
+ if ( m_bInsDraw )
+ {
+ if ( !m_bMBPressed )
+ break;
+ if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) )
+ {
+ if ( !bInsWin )
+ LeaveArea( aDocPt );
+ else
+ EnterArea();
+ if ( m_rView.GetDrawFuncPtr() )
+ {
+ pSdrView->SetOrtho(false);
+ m_rView.GetDrawFuncPtr()->MouseMove( rMEvt );
+ }
+ m_bIsInMove = true;
+ }
+ return;
+ }
+
+ {
+ SwWordCountWrapper *pWrdCnt = static_cast<SwWordCountWrapper*>(GetView().GetViewFrame()->GetChildWindow(SwWordCountWrapper::GetChildWindowId()));
+ if (pWrdCnt)
+ pWrdCnt->UpdateCounts();
+ }
+ [[fallthrough]];
+
+ case MOUSE_LEFT + KEY_SHIFT:
+ case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1:
+ if ( !m_bMBPressed )
+ break;
+ [[fallthrough]];
+ case MOUSE_LEFT + KEY_MOD1:
+ if ( g_bFrameDrag && rSh.IsSelFrameMode() )
+ {
+ if( !m_bMBPressed )
+ break;
+
+ if ( m_bIsInMove || IsMinMove( m_aStartPos, aPixPt ) )
+ {
+ // event processing for resizing
+ if (pSdrView && pSdrView->AreObjectsMarked())
+ {
+ const Point aSttPt( PixelToLogic( m_aStartPos ) );
+
+ // can we start?
+ if( SdrHdlKind::User == g_eSdrMoveHdl )
+ {
+ SdrHdl* pHdl = pSdrView->PickHandle( aSttPt );
+ g_eSdrMoveHdl = pHdl ? pHdl->GetKind() : SdrHdlKind::Move;
+ }
+
+ const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat());
+ const SvxMacro* pMacro = nullptr;
+
+ SvMacroItemId nEvent = SdrHdlKind::Move == g_eSdrMoveHdl
+ ? SvMacroItemId::SwFrmMove
+ : SvMacroItemId::SwFrmResize;
+
+ if (nullptr != pFlyFormat)
+ pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent);
+ if (nullptr != pMacro &&
+ // or notify only e.g. every 20 Twip?
+ m_aRszMvHdlPt != aDocPt )
+ {
+ m_aRszMvHdlPt = aDocPt;
+ sal_uInt32 nPos = 0;
+ SbxArrayRef xArgs = new SbxArray;
+ SbxVariableRef xVar = new SbxVariable;
+ xVar->PutString( pFlyFormat->GetName() );
+ xArgs->Put(xVar.get(), ++nPos);
+
+ if( SvMacroItemId::SwFrmResize == nEvent )
+ {
+ xVar = new SbxVariable;
+ xVar->PutUShort( static_cast< sal_uInt16 >(g_eSdrMoveHdl) );
+ xArgs->Put(xVar.get(), ++nPos);
+ }
+
+ xVar = new SbxVariable;
+ xVar->PutLong( aDocPt.X() - aSttPt.X() );
+ xArgs->Put(xVar.get(), ++nPos);
+ xVar = new SbxVariable;
+ xVar->PutLong( aDocPt.Y() - aSttPt.Y() );
+ xArgs->Put(xVar.get(), ++nPos);
+
+ OUString sRet;
+
+ ReleaseMouse();
+
+ rSh.ExecMacro( *pMacro, &sRet, xArgs.get() );
+
+ CaptureMouse();
+
+ if( !sRet.isEmpty() && sRet.toInt32()!=0 )
+ return ;
+ }
+ }
+ // event processing for resizing
+
+ if( bIsDocReadOnly )
+ break;
+
+ bool bResizeKeepRatio = rSh.GetSelectionType() & SelectionType::Graphic ||
+ rSh.GetSelectionType() & SelectionType::Media ||
+ rSh.GetSelectionType() & SelectionType::Ole;
+ bool bisResize = g_eSdrMoveHdl != SdrHdlKind::Move;
+
+ if (pSdrView)
+ {
+ // Resize proportionally when media is selected and the user drags on a corner
+ const Point aSttPt(PixelToLogic(m_aStartPos));
+ SdrHdl* pHdl = pSdrView->PickHandle(aSttPt);
+ if (pHdl)
+ bResizeKeepRatio = bResizeKeepRatio && pHdl->IsCornerHdl();
+
+ if (pSdrView->GetDragMode() == SdrDragMode::Crop)
+ bisResize = false;
+ if (rMEvt.IsShift())
+ {
+ pSdrView->SetAngleSnapEnabled(!bResizeKeepRatio);
+ if (bisResize)
+ pSdrView->SetOrtho(!bResizeKeepRatio);
+ else
+ pSdrView->SetOrtho(true);
+ }
+ else
+ {
+ pSdrView->SetAngleSnapEnabled(bResizeKeepRatio);
+ if (bisResize)
+ pSdrView->SetOrtho(bResizeKeepRatio);
+ else
+ pSdrView->SetOrtho(false);
+ }
+ }
+
+ rSh.Drag( &aDocPt, rMEvt.IsShift() );
+ m_bIsInMove = true;
+ }
+ else if( bIsDocReadOnly )
+ break;
+
+ if ( !bInsWin )
+ {
+ Point aTmp( aDocPt );
+ aTmp += rSh.VisArea().Pos() - aOldPt;
+ LeaveArea( aTmp );
+ }
+ else if(m_bIsInMove)
+ EnterArea();
+ return;
+ }
+ if ( !rSh.IsSelFrameMode() && !g_bDDINetAttr &&
+ (IsMinMove( m_aStartPos,aPixPt ) || m_bIsInMove) &&
+ (rSh.IsInSelect() || !rSh.TestCurrPam( aDocPt )) )
+ {
+ if ( pSdrView )
+ {
+ if ( rMEvt.IsShift() )
+ pSdrView->SetOrtho(true);
+ else
+ pSdrView->SetOrtho(false);
+ }
+ if ( !bInsWin )
+ {
+ Point aTmp( aDocPt );
+ aTmp += rSh.VisArea().Pos() - aOldPt;
+ LeaveArea( aTmp );
+ }
+ else
+ {
+ if( !rMEvt.IsSynthetic() &&
+ ( MOUSE_LEFT != rMEvt.GetButtons() ||
+ KEY_MOD1 != rMEvt.GetModifier() ||
+ !rSh.Is_FnDragEQBeginDrag() ||
+ rSh.IsAddMode() ) )
+ {
+ rSh.Drag( &aDocPt, false );
+
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false));
+ EnterArea();
+ }
+ }
+ }
+ g_bDDINetAttr = false;
+ break;
+ case 0:
+ {
+ if ( m_pApplyTempl )
+ {
+ UpdatePointer(aDocPt); // maybe a frame has to be marked here
+ break;
+ }
+ // change ui if mouse is over SwPostItField
+ // TODO: do the same thing for redlines IsAttrAtPos::Redline
+ SwContentAtPos aContentAtPos( IsAttrAtPos::Field);
+ if (rSh.GetContentAtPos(aDocPt, aContentAtPos, false))
+ {
+ const SwField* pField = aContentAtPos.aFnd.pField;
+ if (pField->Which()== SwFieldIds::Postit)
+ {
+ m_rView.GetPostItMgr()->SetShadowState(reinterpret_cast<const SwPostItField*>(pField),false);
+ }
+ else
+ m_rView.GetPostItMgr()->SetShadowState(nullptr,false);
+ }
+ else
+ m_rView.GetPostItMgr()->SetShadowState(nullptr,false);
+ [[fallthrough]];
+ }
+ case KEY_SHIFT:
+ case KEY_MOD2:
+ case KEY_MOD1:
+ if ( !m_bInsDraw )
+ {
+ bool bTstShdwCursor = true;
+
+ UpdatePointer( aDocPt, rMEvt.GetModifier() );
+
+ const SwFrameFormat* pFormat = nullptr;
+ const SwFormatINetFormat* pINet = nullptr;
+ SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr );
+ if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) )
+ pINet = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr);
+
+ const void* pTmp = pINet;
+
+ if( pINet ||
+ nullptr != ( pTmp = pFormat = rSh.GetFormatFromAnyObj( aDocPt )))
+ {
+ bTstShdwCursor = false;
+ if( pTmp == pINet )
+ m_aSaveCallEvent.Set( pINet );
+ else
+ {
+ IMapObject* pIMapObj = pFormat->GetIMapObject( aDocPt );
+ if( pIMapObj )
+ m_aSaveCallEvent.Set( pFormat, pIMapObj );
+ else
+ m_aSaveCallEvent.Set( EVENT_OBJECT_URLITEM, pFormat );
+ }
+
+ // should be over an InternetField with an
+ // embedded macro?
+ if( m_aSaveCallEvent != aLastCallEvent )
+ {
+ if( aLastCallEvent.HasEvent() )
+ rSh.CallEvent( SvMacroItemId::OnMouseOut,
+ aLastCallEvent, true );
+ // 0 says that the object doesn't have any table
+ if( !rSh.CallEvent( SvMacroItemId::OnMouseOver,
+ m_aSaveCallEvent ))
+ m_aSaveCallEvent.Clear();
+ }
+ }
+ else if( aLastCallEvent.HasEvent() )
+ {
+ // cursor was on an object
+ rSh.CallEvent( SvMacroItemId::OnMouseOut,
+ aLastCallEvent, true );
+ }
+
+ if( bTstShdwCursor && bInsWin && !bIsDocReadOnly &&
+ !m_bInsFrame &&
+ !rSh.GetViewOptions()->getBrowseMode() &&
+ rSh.GetViewOptions()->IsShadowCursor() &&
+ !(rMEvt.GetModifier() + rMEvt.GetButtons()) &&
+ !rSh.HasSelection() && !GetOutDev()->GetConnectMetaFile() )
+ {
+ SwRect aRect;
+ sal_Int16 eOrient;
+ SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode();
+ if( rSh.GetShadowCursorPos( aDocPt, eMode, aRect, eOrient ))
+ {
+ if( !m_pShadCursor )
+ m_pShadCursor.reset( new SwShadowCursor( *this,
+ SwViewOption::GetDirectCursorColor() ) );
+ if( text::HoriOrientation::RIGHT != eOrient && text::HoriOrientation::CENTER != eOrient )
+ eOrient = text::HoriOrientation::LEFT;
+ m_pShadCursor->SetPos( aRect.Pos(), aRect.Height(), static_cast< sal_uInt16 >(eOrient) );
+ bDelShadCursor = false;
+ }
+ }
+ }
+ break;
+ case MOUSE_LEFT + KEY_MOD2:
+ if( rSh.IsBlockMode() && !rMEvt.IsSynthetic() )
+ {
+ rSh.Drag( &aDocPt, false );
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPt, false));
+ EnterArea();
+ }
+ break;
+ }
+
+ if( bDelShadCursor && m_pShadCursor )
+ {
+ m_pShadCursor.reset();
+ }
+ m_bWasShdwCursor = false;
+}
+
+/**
+ * Button Up
+ */
+void SwEditWin::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (vcl::Window* pWindow = m_rView.GetPostItMgr()->IsHitSidebarWindow(rMEvt.GetPosPixel()))
+ {
+ pWindow->MouseButtonUp(rMEvt);
+ return;
+ }
+ }
+
+ bool bCallBase = true;
+
+ bool bCallShadowCursor = m_bWasShdwCursor;
+ m_bWasShdwCursor = false;
+ if( m_pShadCursor )
+ {
+ m_pShadCursor.reset();
+ }
+
+ m_xRowColumnSelectionStart.reset();
+
+ SdrHdlKind eOldSdrMoveHdl = g_eSdrMoveHdl;
+ g_eSdrMoveHdl = SdrHdlKind::User; // for MoveEvents - reset again
+
+ // preventively reset
+ m_rView.SetTabColFromDoc( false );
+ m_rView.SetNumRuleNodeFromDoc(nullptr);
+
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ CurrShell aCurr( &rSh );
+ SdrView *pSdrView = rSh.GetDrawView();
+ if ( pSdrView )
+ {
+ // tdf34555: ortho was always reset before being used in EndSdrDrag
+ // Now, it is reset only if not in Crop mode.
+ if (pSdrView->GetDragMode() != SdrDragMode::Crop && !rMEvt.IsShift())
+ pSdrView->SetOrtho(false);
+
+ if ( pSdrView->MouseButtonUp( rMEvt,GetOutDev() ) )
+ {
+ rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false);
+ return; // SdrView's event evaluated
+ }
+ }
+ // only process MouseButtonUp when the Down went to that windows as well.
+ if ( !m_bMBPressed )
+ {
+ // Undo for the watering can is already in CommandHdl
+ // that's the way it should be!
+
+ return;
+ }
+
+ Point aDocPt( PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ if ( g_bDDTimerStarted )
+ {
+ StopDDTimer( &rSh, aDocPt );
+ m_bMBPressed = false;
+ if ( rSh.IsSelFrameMode() )
+ {
+ rSh.EndDrag( &aDocPt, false );
+ g_bFrameDrag = false;
+ }
+ g_bNoInterrupt = false;
+ const Point aDocPos( PixelToLogic( rMEvt.GetPosPixel() ) );
+ if ((PixelToLogic(m_aStartPos).Y() == (aDocPos.Y())) && (PixelToLogic(m_aStartPos).X() == (aDocPos.X())))//To make sure it was not moved
+ {
+ SdrPageView* pPV = nullptr;
+ SdrObject* pObj = pSdrView ? pSdrView->PickObj(aDocPos, pSdrView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER) : nullptr;
+ if (pObj)
+ {
+ SwFrameFormat* pFormat = GetUserCall(pObj)->GetFormat();
+ SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT);
+ if (!pShapeFormat)
+ {
+ pSdrView->UnmarkAllObj();
+ pSdrView->MarkObj(pObj,pPV);
+ }
+ else
+ {
+ // If the fly frame is a textbox of a shape, then select the shape instead.
+ SdrObject* pShape = pShapeFormat->FindSdrObject();
+ pSdrView->UnmarkAllObj();
+ pSdrView->MarkObj(pShape, pPV);
+ }
+ }
+ }
+ ReleaseMouse();
+ return;
+ }
+
+ if( m_pAnchorMarker )
+ {
+ if(m_pAnchorMarker->GetHdl())
+ {
+ // #i121463# delete selected after drag
+ m_pAnchorMarker->GetHdl()->SetSelected(false);
+ }
+
+ Point aPnt( m_pAnchorMarker->GetLastPos() );
+ m_pAnchorMarker.reset();
+ if( aPnt.X() || aPnt.Y() )
+ rSh.FindAnchorPos( aPnt, true );
+ }
+ if ( m_bInsDraw && m_rView.GetDrawFuncPtr() )
+ {
+ if ( m_rView.GetDrawFuncPtr()->MouseButtonUp( rMEvt ) )
+ {
+ if (m_rView.GetDrawFuncPtr()) // could have been destroyed in MouseButtonUp
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+
+ if (!m_rView.IsDrawMode())
+ {
+ m_rView.SetDrawFuncPtr(nullptr);
+ SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings();
+ rBind.Invalidate( SID_ATTR_SIZE );
+ rBind.Invalidate( SID_TABLE_CELL );
+ }
+ }
+
+ if ( rSh.IsObjSelected() )
+ {
+ rSh.EnterSelFrameMode();
+ if (!m_rView.GetDrawFuncPtr())
+ StdDrawMode( SdrObjKind::NONE, true );
+ }
+ else if ( rSh.IsFrameSelected() )
+ {
+ rSh.EnterSelFrameMode();
+ StopInsFrame();
+ }
+ else
+ {
+ const Point aDocPos( PixelToLogic( m_aStartPos ) );
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false));
+ rSh.Edit();
+ }
+
+ m_rView.AttrChangedNotify(nullptr);
+ }
+ else if (rMEvt.GetButtons() == MOUSE_RIGHT && rSh.IsDrawCreate())
+ m_rView.GetDrawFuncPtr()->BreakCreate(); // abort drawing
+
+ g_bNoInterrupt = false;
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ return;
+ }
+ bool bPopMode = false;
+ switch ( rMEvt.GetModifier() + rMEvt.GetButtons() )
+ {
+ case MOUSE_LEFT:
+ if ( m_bInsDraw && rSh.IsDrawCreate() )
+ {
+ if ( m_rView.GetDrawFuncPtr() && m_rView.GetDrawFuncPtr()->MouseButtonUp(rMEvt) )
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.AttrChangedNotify(nullptr);
+ if ( rSh.IsObjSelected() )
+ rSh.EnterSelFrameMode();
+ if ( m_rView.GetDrawFuncPtr() && m_bInsFrame )
+ StopInsFrame();
+ }
+ bCallBase = false;
+ break;
+ }
+ [[fallthrough]];
+ case MOUSE_LEFT + KEY_MOD1:
+ case MOUSE_LEFT + KEY_MOD2:
+ case MOUSE_LEFT + KEY_SHIFT + KEY_MOD1:
+ if ( g_bFrameDrag && rSh.IsSelFrameMode() )
+ {
+ if ( rMEvt.IsMod1() ) // copy and don't move.
+ {
+ // abort drag, use internal Copy instead
+ tools::Rectangle aRect;
+ rSh.GetDrawView()->TakeActionRect( aRect );
+ if (!aRect.IsEmpty())
+ {
+ rSh.BreakDrag();
+ Point aEndPt, aSttPt;
+ if ( rSh.GetSelFrameType() & FrameTypeFlags::FLY_ATCNT )
+ {
+ aEndPt = aRect.TopLeft();
+ aSttPt = rSh.GetDrawView()->GetAllMarkedRect().TopLeft();
+ }
+ else
+ {
+ aEndPt = aRect.Center();
+ aSttPt = rSh.GetDrawView()->GetAllMarkedRect().Center();
+ }
+ if ( aSttPt != aEndPt )
+ {
+ rSh.StartUndo( SwUndoId::UI_DRAG_AND_COPY );
+ rSh.Copy(rSh, aSttPt, aEndPt);
+ rSh.EndUndo( SwUndoId::UI_DRAG_AND_COPY );
+ }
+ }
+ else {
+ rSh.EndDrag( &aDocPt, false );
+ }
+ }
+ else
+ {
+ {
+ const SwFrameFormat *const pFlyFormat(rSh.GetFlyFrameFormat());
+ const SvxMacro* pMacro = nullptr;
+
+ SvMacroItemId nEvent = SdrHdlKind::Move == eOldSdrMoveHdl
+ ? SvMacroItemId::SwFrmMove
+ : SvMacroItemId::SwFrmResize;
+
+ if (nullptr != pFlyFormat)
+ pMacro = pFlyFormat->GetMacro().GetMacroTable().Get(nEvent);
+ if (nullptr != pMacro)
+ {
+ const Point aSttPt( PixelToLogic( m_aStartPos ) );
+ m_aRszMvHdlPt = aDocPt;
+ sal_uInt32 nPos = 0;
+ SbxArrayRef xArgs = new SbxArray;
+ SbxVariableRef xVar = new SbxVariable;
+ xVar->PutString( pFlyFormat->GetName() );
+ xArgs->Put(xVar.get(), ++nPos);
+
+ if( SvMacroItemId::SwFrmResize == nEvent )
+ {
+ xVar = new SbxVariable;
+ xVar->PutUShort( static_cast< sal_uInt16 >(eOldSdrMoveHdl) );
+ xArgs->Put(xVar.get(), ++nPos);
+ }
+
+ xVar = new SbxVariable;
+ xVar->PutLong( aDocPt.X() - aSttPt.X() );
+ xArgs->Put(xVar.get(), ++nPos);
+ xVar = new SbxVariable;
+ xVar->PutLong( aDocPt.Y() - aSttPt.Y() );
+ xArgs->Put(xVar.get(), ++nPos);
+
+ xVar = new SbxVariable;
+ xVar->PutUShort( 1 );
+ xArgs->Put(xVar.get(), ++nPos);
+
+ ReleaseMouse();
+
+ rSh.ExecMacro( *pMacro, nullptr, xArgs.get() );
+
+ CaptureMouse();
+ }
+
+ if (pFlyFormat)
+ {
+ // See if the fly frame's anchor is in a content control. If so,
+ // interact with it.
+ const SwFormatAnchor& rFormatAnchor = pFlyFormat->GetAnchor();
+ const SwPosition* pAnchorPos = rFormatAnchor.GetContentAnchor();
+ if (pAnchorPos)
+ {
+ SwTextNode* pTextNode = pAnchorPos->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ SwTextAttr* pAttr = pTextNode->GetTextAttrAt(
+ pAnchorPos->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL,
+ SwTextNode::PARENT);
+ if (pAttr)
+ {
+ SwTextContentControl* pTextContentControl
+ = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ const SwFormatContentControl& rFormatContentControl
+ = pTextContentControl->GetContentControl();
+ rSh.GotoContentControl(rFormatContentControl);
+ }
+ }
+ }
+ }
+ }
+ rSh.EndDrag( &aDocPt, false );
+ }
+ g_bFrameDrag = false;
+ bCallBase = false;
+ break;
+ }
+ bPopMode = true;
+ [[fallthrough]];
+ case MOUSE_LEFT + KEY_SHIFT:
+ if (rSh.IsSelFrameMode())
+ {
+
+ rSh.EndDrag( &aDocPt, false );
+ g_bFrameDrag = false;
+ bCallBase = false;
+ break;
+ }
+
+ if( g_bHoldSelection )
+ {
+ // the EndDrag should be called in any case
+ g_bHoldSelection = false;
+ rSh.EndDrag( &aDocPt, false );
+ }
+ else
+ {
+ SwContentAtPos aFieldAtPos ( IsAttrAtPos::Field );
+ if ( !rSh.IsInSelect() && rSh.TestCurrPam( aDocPt ) &&
+ !rSh.GetContentAtPos( aDocPt, aFieldAtPos ) )
+ {
+ const bool bTmpNoInterrupt = g_bNoInterrupt;
+ g_bNoInterrupt = false;
+ { // create only temporary move context because otherwise
+ // the query to the content form doesn't work!!!
+ SwMvContext aMvContext( &rSh );
+ const Point aDocPos( PixelToLogic( m_aStartPos ) );
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false));
+ }
+ g_bNoInterrupt = bTmpNoInterrupt;
+
+ }
+ else
+ {
+ bool bInSel = rSh.IsInSelect();
+ rSh.EndDrag( &aDocPt, false );
+
+ // Internetfield? --> call link (load doc!!)
+ if( !bInSel )
+ {
+ LoadUrlFlags nFilter = LoadUrlFlags::NONE;
+ if( KEY_MOD1 == rMEvt.GetModifier() )
+ nFilter |= LoadUrlFlags::NewView;
+
+ bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly();
+ if ( !bExecHyperlinks )
+ {
+ const bool bSecureOption = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink );
+ if ( ( bSecureOption && rMEvt.GetModifier() == KEY_MOD1 ) ||
+ ( !bSecureOption && rMEvt.GetModifier() != KEY_MOD1 ) )
+ bExecHyperlinks = true;
+ }
+
+ const bool bExecSmarttags = rMEvt.GetModifier() == KEY_MOD1;
+
+ if(m_pApplyTempl)
+ bExecHyperlinks = false;
+
+ SwContentAtPos aContentAtPos( IsAttrAtPos::Field |
+ IsAttrAtPos::InetAttr |
+ IsAttrAtPos::SmartTag | IsAttrAtPos::FormControl |
+ IsAttrAtPos::ContentControl);
+
+ if( rSh.GetContentAtPos( aDocPt, aContentAtPos ) )
+ {
+ // Do it again if we're not on a field/hyperlink to update the cursor accordingly
+ if ( IsAttrAtPos::Field != aContentAtPos.eContentAtPos
+ && IsAttrAtPos::InetAttr != aContentAtPos.eContentAtPos )
+ rSh.GetContentAtPos( aDocPt, aContentAtPos, true );
+
+ bool bViewLocked = rSh.IsViewLocked();
+ if( !bViewLocked && !rSh.IsReadOnlyAvailable() &&
+ aContentAtPos.IsInProtectSect() )
+ rSh.LockView( true );
+
+ ReleaseMouse();
+
+ if( IsAttrAtPos::Field == aContentAtPos.eContentAtPos )
+ {
+ bool bAddMode(false);
+ // AdditionalMode if applicable
+ if (KEY_MOD1 == rMEvt.GetModifier()
+ && !rSh.IsAddMode())
+ {
+ bAddMode = true;
+ rSh.EnterAddMode();
+ }
+ if ( aContentAtPos.pFndTextAttr != nullptr
+ && aContentAtPos.pFndTextAttr->Which() == RES_TXTATR_INPUTFIELD )
+ {
+ if (!rSh.IsInSelect())
+ {
+ // create only temporary move context because otherwise
+ // the query to the content form doesn't work!!!
+ SwMvContext aMvContext( &rSh );
+ const Point aDocPos( PixelToLogic( m_aStartPos ) );
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false));
+ }
+ else
+ {
+ g_bValidCursorPos = true;
+ }
+ }
+ else
+ {
+ rSh.ClickToField(*aContentAtPos.aFnd.pField, bExecHyperlinks);
+ // a bit of a mystery what this is good for?
+ // in this case we assume it's valid since we
+ // just selected a field
+ g_bValidCursorPos = true;
+ }
+ if (bAddMode)
+ {
+ rSh.LeaveAddMode();
+ }
+ }
+ else if (aContentAtPos.eContentAtPos == IsAttrAtPos::ContentControl)
+ {
+ auto pTextContentControl
+ = static_txtattr_cast<const SwTextContentControl*>(
+ aContentAtPos.pFndTextAttr);
+ const SwFormatContentControl& rFormatContentControl
+ = pTextContentControl->GetContentControl();
+ rSh.GotoContentControl(rFormatContentControl);
+ }
+ else if ( IsAttrAtPos::SmartTag == aContentAtPos.eContentAtPos )
+ {
+ // execute smarttag menu
+ if ( bExecSmarttags && SwSmartTagMgr::Get().IsSmartTagsEnabled() )
+ m_rView.ExecSmartTagPopup( aDocPt );
+ }
+ else if ( IsAttrAtPos::FormControl == aContentAtPos.eContentAtPos )
+ {
+ OSL_ENSURE( aContentAtPos.aFnd.pFieldmark != nullptr, "where is my field ptr???");
+ if ( aContentAtPos.aFnd.pFieldmark != nullptr)
+ {
+ IFieldmark *fieldBM = const_cast< IFieldmark* > ( aContentAtPos.aFnd.pFieldmark );
+ if ( fieldBM->GetFieldname( ) == ODF_FORMCHECKBOX )
+ {
+ ICheckboxFieldmark& rCheckboxFm = dynamic_cast<ICheckboxFieldmark&>(*fieldBM);
+ rCheckboxFm.SetChecked(!rCheckboxFm.IsChecked());
+ rCheckboxFm.Invalidate();
+ rSh.InvalidateWindows( SwRect(m_rView.GetVisArea()) );
+ }
+ }
+ }
+ else if ( IsAttrAtPos::InetAttr == aContentAtPos.eContentAtPos )
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString val((*static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)).GetValue());
+ if (val.startsWith("#"))
+ bExecHyperlinks = true;
+ }
+ if ( bExecHyperlinks && aContentAtPos.aFnd.pAttr )
+ rSh.ClickToINetAttr( *static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr), nFilter );
+ }
+
+ rSh.LockView( bViewLocked );
+ bCallShadowCursor = false;
+ }
+ else
+ {
+ aContentAtPos = SwContentAtPos( IsAttrAtPos::Ftn );
+ if( !rSh.GetContentAtPos( aDocPt, aContentAtPos, true ) && bExecHyperlinks )
+ {
+ SdrViewEvent aVEvt;
+
+ if (pSdrView)
+ pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
+
+ if (pSdrView && aVEvt.meEvent == SdrEventKind::ExecuteUrl)
+ {
+ // hit URL field
+ const SvxURLField *pField = aVEvt.mpURLField;
+ if (pField)
+ {
+ const OUString& sURL(pField->GetURL());
+ const OUString& sTarget(pField->GetTargetFrame());
+ ::LoadURL(rSh, sURL, nFilter, sTarget);
+ }
+ bCallShadowCursor = false;
+ }
+ else
+ {
+ // hit graphic
+ ReleaseMouse();
+ if( rSh.ClickToINetGrf( aDocPt, nFilter ))
+ bCallShadowCursor = false;
+ }
+ }
+ }
+
+ if( bCallShadowCursor &&
+ rSh.GetViewOptions()->IsShadowCursor() &&
+ MOUSE_LEFT == (rMEvt.GetModifier() + rMEvt.GetButtons()) &&
+ !rSh.HasSelection() &&
+ !GetOutDev()->GetConnectMetaFile() &&
+ rSh.VisArea().Contains( aDocPt ))
+ {
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (rSh.GetLastUndoInfo(nullptr, & nLastUndoId))
+ {
+ if (SwUndoId::INS_FROM_SHADOWCRSR == nLastUndoId)
+ {
+ rSh.Undo();
+ }
+ }
+ SwFillMode eMode = rSh.GetViewOptions()->GetShdwCursorFillMode();
+ rSh.SetShadowCursorPos( aDocPt, eMode );
+ }
+ }
+ }
+ bCallBase = false;
+
+ }
+
+ // reset pushed mode in Down again if applicable
+ if ( bPopMode && g_bModePushed )
+ {
+ rSh.PopMode();
+ g_bModePushed = false;
+ bCallBase = false;
+ }
+ break;
+
+ default:
+ ReleaseMouse();
+ return;
+ }
+
+ if( m_pApplyTempl )
+ {
+ SelectionType eSelection = rSh.GetSelectionType();
+ SwFormatClipboard* pFormatClipboard = m_pApplyTempl->m_pFormatClipboard;
+ if( pFormatClipboard )//apply format paintbrush
+ {
+ //get some parameters
+ SwWrtShell& rWrtShell = m_rView.GetWrtShell();
+ SfxStyleSheetBasePool* pPool=nullptr;
+ bool bNoCharacterFormats = false;
+ bool bNoParagraphFormats = true;
+ {
+ SwDocShell* pDocSh = m_rView.GetDocShell();
+ if(pDocSh)
+ pPool = pDocSh->GetStyleSheetPool();
+ if( (rMEvt.GetModifier()&KEY_MOD1) && (rMEvt.GetModifier()&KEY_SHIFT) )
+ {
+ bNoCharacterFormats = true;
+ bNoParagraphFormats = false;
+ }
+ else if( rMEvt.GetModifier() & KEY_MOD1 )
+ bNoParagraphFormats = false;
+ }
+ //execute paste
+ pFormatClipboard->Paste( rWrtShell, pPool, bNoCharacterFormats, bNoParagraphFormats );
+
+ //if the clipboard is empty after paste remove the ApplyTemplate
+ if(!pFormatClipboard->HasContent())
+ SetApplyTemplate(SwApplyTemplate());
+
+ //tdf#38101 remove temporary highlighting
+ m_pUserMarker.reset();
+ }
+ else if( m_pApplyTempl->nColor )
+ {
+ sal_uInt16 nId = 0;
+ switch( m_pApplyTempl->nColor )
+ {
+ case SID_ATTR_CHAR_COLOR_EXT:
+ nId = RES_CHRATR_COLOR;
+ break;
+ case SID_ATTR_CHAR_COLOR_BACKGROUND_EXT:
+ nId = RES_CHRATR_BACKGROUND;
+ break;
+ }
+ if( nId && (SelectionType::Text|SelectionType::Table) & eSelection)
+ {
+ if( rSh.IsSelection() && !rSh.HasReadonlySel() )
+ {
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ if (nId == RES_CHRATR_BACKGROUND)
+ ApplyCharBackground(m_aWaterCanTextBackColor, rSh);
+ else
+ rSh.SetAttrItem( SvxColorItem( m_aWaterCanTextColor, nId ) );
+ rSh.UnSetVisibleCursor();
+ rSh.EnterStdMode();
+ rSh.SetVisibleCursor(aDocPt);
+ bCallBase = false;
+ m_aTemplateTimer.Stop();
+ }
+ else if(rMEvt.GetClicks() == 1)
+ {
+ // no selection -> so turn off watering can
+ m_aTemplateTimer.Start();
+ }
+ }
+ }
+ else
+ {
+ OUString aStyleName;
+ switch ( m_pApplyTempl->eType )
+ {
+ case SfxStyleFamily::Para:
+ if( (( SelectionType::Text | SelectionType::Table )
+ & eSelection ) && !rSh.HasReadonlySel() )
+ {
+ rSh.SetTextFormatColl( m_pApplyTempl->aColl.pTextColl );
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ bCallBase = false;
+ if ( m_pApplyTempl->aColl.pTextColl )
+ aStyleName = m_pApplyTempl->aColl.pTextColl->GetName();
+ }
+ break;
+ case SfxStyleFamily::Char:
+ if( (( SelectionType::Text | SelectionType::Table )
+ & eSelection ) && !rSh.HasReadonlySel() )
+ {
+ rSh.SetAttrItem( SwFormatCharFormat(m_pApplyTempl->aColl.pCharFormat) );
+ rSh.UnSetVisibleCursor();
+ rSh.EnterStdMode();
+ rSh.SetVisibleCursor(aDocPt);
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ bCallBase = false;
+ if ( m_pApplyTempl->aColl.pCharFormat )
+ aStyleName = m_pApplyTempl->aColl.pCharFormat->GetName();
+ }
+ break;
+ case SfxStyleFamily::Frame :
+ {
+ const SwFrameFormat* pFormat = rSh.GetFormatFromObj( aDocPt );
+ if(dynamic_cast<const SwFlyFrameFormat*>( pFormat) )
+ {
+ rSh.SetFrameFormat( m_pApplyTempl->aColl.pFrameFormat, false, &aDocPt );
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ bCallBase = false;
+ if( m_pApplyTempl->aColl.pFrameFormat )
+ aStyleName = m_pApplyTempl->aColl.pFrameFormat->GetName();
+ }
+ break;
+ }
+ case SfxStyleFamily::Page:
+ // no Undo with page templates
+ rSh.ChgCurPageDesc( *m_pApplyTempl->aColl.pPageDesc );
+ if ( m_pApplyTempl->aColl.pPageDesc )
+ aStyleName = m_pApplyTempl->aColl.pPageDesc->GetName();
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ bCallBase = false;
+ break;
+ case SfxStyleFamily::Pseudo:
+ if( !rSh.HasReadonlySel() )
+ {
+ rSh.SetCurNumRule( *m_pApplyTempl->aColl.pNumRule,
+ false,
+ m_pApplyTempl->aColl.pNumRule->GetDefaultListId() );
+ bCallBase = false;
+ m_pApplyTempl->nUndo =
+ std::min(m_pApplyTempl->nUndo, rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount());
+ if( m_pApplyTempl->aColl.pNumRule )
+ aStyleName = m_pApplyTempl->aColl.pNumRule->GetName();
+ }
+ break;
+ default: break;
+ }
+
+ uno::Reference< frame::XDispatchRecorder > xRecorder =
+ m_rView.GetViewFrame()->GetBindings().GetRecorder();
+ if ( !aStyleName.isEmpty() && xRecorder.is() )
+ {
+ SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView );
+ if ( pSfxShell )
+ {
+ SfxRequest aReq( m_rView.GetViewFrame(), SID_STYLE_APPLY );
+ aReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aStyleName ) );
+ aReq.AppendItem( SfxUInt16Item( SID_STYLE_FAMILY, static_cast<sal_uInt16>(m_pApplyTempl->eType) ) );
+ aReq.Done();
+ }
+ }
+ }
+
+ }
+ ReleaseMouse();
+ // Only processed MouseEvents arrive here; only at these this mode can
+ // be reset.
+ m_bMBPressed = false;
+
+ // Make this call just to be sure. Selecting has finished surely by now.
+ // Otherwise the timeout's timer could give problems.
+ EnterArea();
+ g_bNoInterrupt = false;
+
+ if (bCallBase)
+ Window::MouseButtonUp(rMEvt);
+
+ if (!(pSdrView && rMEvt.GetClicks() == 1 && comphelper::LibreOfficeKit::isActive()))
+ return;
+
+ // When tiled rendering, single click on a shape text starts editing already.
+ SdrViewEvent aViewEvent;
+ SdrHitKind eHit = pSdrView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aViewEvent);
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ if (eHit == SdrHitKind::TextEditObj && rMarkList.GetMarkCount() == 1)
+ {
+ if (SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj())
+ {
+ EnterDrawTextMode(pObj->GetLogicRect().Center());
+ if ( auto pSwDrawTextShell = dynamic_cast< SwDrawTextShell *>( m_rView.GetCurShell() ) )
+ pSwDrawTextShell->Init();
+ }
+ }
+}
+
+/**
+ * Apply template
+ */
+void SwEditWin::SetApplyTemplate(const SwApplyTemplate &rTempl)
+{
+ static bool bIdle = false;
+ m_pApplyTempl.reset();
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+
+ if(rTempl.m_pFormatClipboard)
+ {
+ m_pApplyTempl.reset(new SwApplyTemplate( rTempl ));
+ m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount();
+ SetPointer( PointerStyle::Fill );//@todo #i20119# maybe better a new brush pointer here in future
+ rSh.NoEdit( false );
+ bIdle = rSh.GetViewOptions()->IsIdle();
+ rSh.GetViewOptions()->SetIdle( false );
+ }
+ else if(rTempl.nColor)
+ {
+ m_pApplyTempl.reset(new SwApplyTemplate( rTempl ));
+ m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount();
+ SetPointer( PointerStyle::Fill );
+ rSh.NoEdit( false );
+ bIdle = rSh.GetViewOptions()->IsIdle();
+ rSh.GetViewOptions()->SetIdle( false );
+ }
+ else if( rTempl.eType != SfxStyleFamily::None )
+ {
+ m_pApplyTempl.reset(new SwApplyTemplate( rTempl ));
+ m_pApplyTempl->nUndo = rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount();
+ SetPointer( PointerStyle::Fill );
+ rSh.NoEdit( false );
+ bIdle = rSh.GetViewOptions()->IsIdle();
+ rSh.GetViewOptions()->SetIdle( false );
+ }
+ else
+ {
+ SetPointer( PointerStyle::Text );
+ rSh.UnSetVisibleCursor();
+
+ rSh.GetViewOptions()->SetIdle( bIdle );
+ if ( !rSh.IsSelFrameMode() )
+ rSh.Edit();
+ }
+
+ static sal_uInt16 aInva[] =
+ {
+ SID_STYLE_WATERCAN,
+ SID_ATTR_CHAR_COLOR_EXT,
+ SID_ATTR_CHAR_COLOR_BACKGROUND_EXT,
+ 0
+ };
+ m_rView.GetViewFrame()->GetBindings().Invalidate(aInva);
+}
+
+/**
+ * Ctor
+ */
+SwEditWin::SwEditWin(vcl::Window *pParent, SwView &rMyView):
+ Window(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)),
+ DropTargetHelper( this ),
+ DragSourceHelper( this ),
+
+ m_aTimer("SwEditWin"),
+ m_aKeyInputFlushTimer("SwEditWin m_aKeyInputFlushTimer"),
+ m_eBufferLanguage(LANGUAGE_DONTKNOW),
+ m_aTemplateTimer("SwEditWin m_aTemplateTimer"),
+ m_pUserMarkerObj( nullptr ),
+
+ m_rView( rMyView ),
+
+ m_aActHitType(SdrHitKind::NONE),
+ m_nDropFormat( SotClipboardFormatId::NONE ),
+ m_nDropAction( 0 ),
+ m_nDropDestination( SotExchangeDest::NONE ),
+
+ m_eBezierMode(SID_BEZIER_INSERT),
+ m_nInsFrameColCount( 1 ),
+ m_eDrawMode(SdrObjKind::NONE),
+
+ m_bMBPressed(false),
+ m_bInsDraw(false),
+ m_bInsFrame(false),
+ m_bIsInMove(false),
+ m_bIsInDrag(false),
+ m_bOldIdle(false),
+ m_bOldIdleSet(false),
+ m_bChainMode(false),
+ m_bWasShdwCursor(false),
+ m_bLockInput(false),
+ m_bIsRowDrag(false),
+ m_bUseInputLanguage(false),
+ m_bObjectSelect(false),
+ m_nKS_NUMDOWN_Count(0),
+ m_nKS_NUMINDENTINC_Count(0),
+ m_pFrameControlsManager(new SwFrameControlsManager(this))
+{
+ set_id("writer_edit");
+ SetHelpId(HID_EDIT_WIN);
+ EnableChildTransparentMode();
+ SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );
+
+ m_bMBPressed = m_bInsDraw = m_bInsFrame =
+ m_bIsInDrag = m_bOldIdle = m_bOldIdleSet = m_bChainMode = m_bWasShdwCursor = false;
+ // initially use the input language
+ m_bUseInputLanguage = true;
+
+ SetMapMode(MapMode(MapUnit::MapTwip));
+
+ SetPointer( PointerStyle::Text );
+ m_aTimer.SetInvokeHandler(LINK(this, SwEditWin, TimerHandler));
+
+ m_aKeyInputFlushTimer.SetTimeout( 20 );
+ m_aKeyInputFlushTimer.SetInvokeHandler(LINK(this, SwEditWin, KeyInputFlushHandler));
+
+ // TemplatePointer for colors should be reset without
+ // selection after single click, but not after double-click (tdf#122442)
+ m_aTemplateTimer.SetTimeout(GetSettings().GetMouseSettings().GetDoubleClickTime());
+ m_aTemplateTimer.SetInvokeHandler(LINK(this, SwEditWin, TemplateTimerHdl));
+
+ // temporary solution!!! Should set the font of the current
+ // insert position at every cursor movement!
+ if( !rMyView.GetDocShell()->IsReadOnly() )
+ {
+ vcl::Font aFont;
+ SetInputContext( InputContext( aFont, InputContextFlags::Text |
+ InputContextFlags::ExtText ) );
+ }
+}
+
+SwEditWin::~SwEditWin()
+{
+ disposeOnce();
+}
+
+void SwEditWin::dispose()
+{
+ m_pShadCursor.reset();
+
+ if( s_pQuickHlpData->m_bIsDisplayed && m_rView.GetWrtShellPtr() )
+ s_pQuickHlpData->Stop( m_rView.GetWrtShell() );
+ g_bExecuteDrag = false;
+ m_pApplyTempl.reset();
+
+ m_rView.SetDrawFuncPtr(nullptr);
+
+ m_pUserMarker.reset();
+
+ m_pAnchorMarker.reset();
+
+ m_pFrameControlsManager->dispose();
+ m_pFrameControlsManager.reset();
+
+ DragSourceHelper::dispose();
+ DropTargetHelper::dispose();
+ vcl::Window::dispose();
+}
+
+/**
+ * Turn on DrawTextEditMode
+ */
+void SwEditWin::EnterDrawTextMode( const Point& aDocPos )
+{
+ if ( m_rView.EnterDrawTextMode(aDocPos) )
+ {
+ if (m_rView.GetDrawFuncPtr())
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ }
+ m_rView.NoRotate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+}
+
+/**
+ * Turn on DrawMode
+ */
+bool SwEditWin::EnterDrawMode(const MouseEvent& rMEvt, const Point& aDocPos)
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ SdrView *pSdrView = rSh.GetDrawView();
+
+ if ( m_rView.GetDrawFuncPtr() )
+ {
+ if (rSh.IsDrawCreate())
+ return true;
+
+ bool bRet = m_rView.GetDrawFuncPtr()->MouseButtonDown( rMEvt );
+ m_rView.AttrChangedNotify(nullptr);
+ return bRet;
+ }
+
+ if ( pSdrView && pSdrView->IsTextEdit() )
+ {
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+
+ rSh.EndTextEdit(); // clicked aside, end Edit
+ rSh.SelectObj( aDocPos );
+ if ( !rSh.IsObjSelected() && !rSh.IsFrameSelected() )
+ rSh.LeaveSelFrameMode();
+ else
+ {
+ SwEditWin::s_nDDStartPosY = aDocPos.Y();
+ SwEditWin::s_nDDStartPosX = aDocPos.X();
+ g_bFrameDrag = true;
+ }
+ if( bUnLockView )
+ rSh.LockView( false );
+ m_rView.AttrChangedNotify(nullptr);
+ return true;
+ }
+ return false;
+}
+
+bool SwEditWin::IsDrawSelMode() const
+{
+ return IsObjectSelect();
+}
+
+void SwEditWin::GetFocus()
+{
+ if ( m_rView.GetPostItMgr()->HasActiveSidebarWin() )
+ {
+ m_rView.GetPostItMgr()->GrabFocusOnActiveSidebarWin();
+ }
+ else
+ {
+ m_rView.GotFocus();
+ Window::GetFocus();
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ m_rView.GetWrtShell().InvalidateAccessibleFocus();
+#endif
+ }
+}
+
+void SwEditWin::LoseFocus()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_rView.GetWrtShellPtr())
+ m_rView.GetWrtShell().InvalidateAccessibleFocus();
+#endif
+ Window::LoseFocus();
+ if( s_pQuickHlpData && s_pQuickHlpData->m_bIsDisplayed )
+ s_pQuickHlpData->Stop( m_rView.GetWrtShell() );
+}
+
+void SwEditWin::Command( const CommandEvent& rCEvt )
+{
+ if (!m_rView.GetViewFrame() || isDisposed())
+ {
+ // If ViewFrame dies shortly, no popup anymore!
+ Window::Command(rCEvt);
+ return;
+ }
+
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+
+ // The command event is send to the window after a possible context
+ // menu from an inplace client has been closed. Now we have the chance
+ // to deactivate the inplace client without any problem regarding parent
+ // windows and code on the stack.
+ SfxInPlaceClient* pIPClient = rSh.GetSfxViewShell()->GetIPClient();
+ bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() );
+ if ( bIsOleActive && ( rCEvt.GetCommand() == CommandEventId::ContextMenu ))
+ {
+ rSh.FinishOLEObj();
+ return;
+ }
+
+ bool bCallBase = true;
+
+ switch ( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ const sal_uInt16 nId = SwInputChild::GetChildWindowId();
+ SwInputChild* pChildWin = static_cast<SwInputChild*>(GetView().GetViewFrame()->
+ GetChildWindow( nId ));
+
+ if (m_rView.GetPostItMgr()->IsHit(rCEvt.GetMousePosPixel()))
+ return;
+
+ Point aDocPos( PixelToLogic( rCEvt.GetMousePosPixel() ) );
+ if ( !rCEvt.IsMouseEvent() )
+ aDocPos = rSh.GetCharRect().Center();
+
+ // Don't trigger the command on a frame anchored to header/footer is not editing it
+ FrameControlType eControl;
+ bool bOverFly = false;
+ bool bPageAnchored = false;
+ bool bOverHeaderFooterFly = IsOverHeaderFooterFly( aDocPos, eControl, bOverFly, bPageAnchored );
+ // !bOverHeaderFooterFly doesn't mean we have a frame to select
+ if ( !bPageAnchored && rCEvt.IsMouseEvent( ) &&
+ ( ( rSh.IsHeaderFooterEdit( ) && !bOverHeaderFooterFly && bOverFly ) ||
+ ( !rSh.IsHeaderFooterEdit( ) && bOverHeaderFooterFly ) ) )
+ {
+ return;
+ }
+
+ if((!pChildWin || pChildWin->GetView() != &m_rView) &&
+ !rSh.IsDrawCreate() && !IsDrawAction())
+ {
+ CurrShell aCurr( &rSh );
+ if (!m_pApplyTempl)
+ {
+ if (g_bNoInterrupt)
+ {
+ ReleaseMouse();
+ g_bNoInterrupt = false;
+ m_bMBPressed = false;
+ }
+ if ( rCEvt.IsMouseEvent() )
+ {
+ SelectMenuPosition(rSh, rCEvt.GetMousePosPixel());
+ m_rView.StopShellTimer();
+ }
+ const Point aPixPos = LogicToPixel( aDocPos );
+
+ if ( m_rView.GetDocShell()->IsReadOnly() )
+ {
+ SwReadOnlyPopup aROPopup(aDocPos, m_rView);
+
+ ui::ContextMenuExecuteEvent aEvent;
+ aEvent.SourceWindow = VCLUnoHelper::GetInterface( this );
+ aEvent.ExecutePosition.X = aPixPos.X();
+ aEvent.ExecutePosition.Y = aPixPos.Y();
+ css::uno::Reference<css::awt::XPopupMenu> xMenu;
+ auto xMenuInterface = aROPopup.CreateMenuInterface();
+ if (GetView().TryContextMenuInterception(xMenuInterface, "private:resource/ReadonlyContextMenu", xMenu, aEvent))
+ {
+ if (xMenu.is())
+ {
+ css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY);
+ sal_uInt16 nExecId = xMenu->execute(xParent, css::awt::Rectangle(aPixPos.X(), aPixPos.Y(), 1, 1),
+ css::awt::PopupMenuDirection::EXECUTE_DOWN);
+ if (!::ExecuteMenuCommand(xMenu, *m_rView.GetViewFrame(), nExecId))
+ aROPopup.Execute(this, nExecId);
+ }
+ else
+ aROPopup.Execute(this, aPixPos);
+ }
+ }
+ else if ( !m_rView.ExecSpellPopup( aDocPos ) )
+ SfxDispatcher::ExecutePopup(this, &aPixPos);
+ }
+ else if (m_pApplyTempl->nUndo < rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount())
+ {
+ // Undo until we reach the point when we entered this context.
+ rSh.Do(SwWrtShell::UNDO);
+ }
+ bCallBase = false;
+ }
+ }
+ break;
+
+ case CommandEventId::Wheel:
+ case CommandEventId::StartAutoScroll:
+ case CommandEventId::AutoScroll:
+ if (m_pSavedOutlineFrame && rSh.GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ GetFrameControlsManager().RemoveControlsByType(FrameControlType::Outline, m_pSavedOutlineFrame);
+ m_pSavedOutlineFrame = nullptr;
+ }
+ m_pShadCursor.reset();
+ bCallBase = !m_rView.HandleWheelCommands( rCEvt );
+ break;
+
+ case CommandEventId::LongPress:
+ case CommandEventId::Swipe: //nothing yet
+ break;
+
+ case CommandEventId::StartExtTextInput:
+ {
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() &&
+ rSh.IsCursorReadonly();
+ if(!bIsDocReadOnly)
+ {
+ if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() )
+ {
+ bCallBase = false;
+ rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt );
+ }
+ else
+ {
+ if( rSh.HasSelection() )
+ rSh.DelRight();
+
+ bCallBase = false;
+ LanguageType eInputLanguage = GetInputLanguage();
+ rSh.CreateExtTextInput(eInputLanguage);
+ }
+ }
+ break;
+ }
+ case CommandEventId::EndExtTextInput:
+ {
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() &&
+ rSh.IsCursorReadonly();
+ if(!bIsDocReadOnly)
+ {
+ if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() )
+ {
+ bCallBase = false;
+ rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt );
+ }
+ else
+ {
+ bCallBase = false;
+ OUString sRecord = rSh.DeleteExtTextInput();
+ uno::Reference< frame::XDispatchRecorder > xRecorder =
+ m_rView.GetViewFrame()->GetBindings().GetRecorder();
+
+ if ( !sRecord.isEmpty() )
+ {
+ // convert quotes in IME text
+ // works on the last input character, this is especially in Korean text often done
+ // quotes that are inside of the string are not replaced!
+ const sal_Unicode aCh = sRecord[sRecord.getLength() - 1];
+ SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get();
+ SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect();
+ if(pACorr &&
+ (( pACorr->IsAutoCorrFlag( ACFlags::ChgQuotes ) && ('\"' == aCh ))||
+ ( pACorr->IsAutoCorrFlag( ACFlags::ChgSglQuotes ) && ( '\'' == aCh))))
+ {
+ rSh.DelLeft();
+ rSh.AutoCorrect( *pACorr, aCh );
+ }
+
+ if ( xRecorder.is() )
+ {
+ // determine Shell
+ SfxShell *pSfxShell = lcl_GetTextShellFromDispatcher( m_rView );
+ // generate request and record
+ if (pSfxShell)
+ {
+ SfxRequest aReq( m_rView.GetViewFrame(), FN_INSERT_STRING );
+ aReq.AppendItem( SfxStringItem( FN_INSERT_STRING, sRecord ) );
+ aReq.Done();
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CommandEventId::ExtTextInput:
+ {
+ bool bIsDocReadOnly = m_rView.GetDocShell()->IsReadOnly() &&
+ rSh.IsCursorReadonly();
+ if (!bIsDocReadOnly && !rSh.HasReadonlySel())
+ {
+ if( s_pQuickHlpData->m_bIsDisplayed )
+ s_pQuickHlpData->Stop( rSh );
+
+ if( rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit() )
+ {
+ bCallBase = false;
+ rSh.GetDrawView()->GetTextEditOutlinerView()->Command( rCEvt );
+ }
+ else
+ {
+ const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
+ if( pData )
+ {
+ bCallBase = false;
+ rSh.SetExtTextInputData( *pData );
+ }
+ }
+ uno::Reference< frame::XDispatchRecorder > xRecorder =
+ m_rView.GetViewFrame()->GetBindings().GetRecorder();
+ if(!xRecorder.is())
+ {
+ SvxAutoCorrCfg& rACfg = SvxAutoCorrCfg::Get();
+ if (!rACfg.IsAutoTextTip() || !ShowAutoText(rSh.GetChunkForAutoText()))
+ {
+ SvxAutoCorrect* pACorr = rACfg.GetAutoCorrect();
+ if (pACorr && pACorr->GetSwFlags().bAutoCompleteWords)
+ ShowAutoCorrectQuickHelp(rSh.GetPrevAutoCorrWord(*pACorr), *pACorr);
+ }
+ }
+ }
+
+ if (rSh.HasReadonlySel())
+ {
+ // Inform the user that the request has been ignored.
+ auto xInfo = std::make_shared<weld::GenericDialogController>(
+ GetFrameWeld(), "modules/swriter/ui/inforeadonlydialog.ui", "InfoReadonlyDialog");
+ weld::DialogController::runAsync(xInfo, [](sal_Int32 /*nResult*/) {});
+ }
+ }
+ break;
+ case CommandEventId::CursorPos:
+ // will be handled by the base class
+ break;
+
+ case CommandEventId::PasteSelection:
+ if( !m_rView.GetDocShell()->IsReadOnly() )
+ {
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromPrimarySelection());
+ if( !aDataHelper.GetXTransferable().is() )
+ break;
+
+ SotExchangeDest nDropDestination = GetDropDestination( rCEvt.GetMousePosPixel() );
+ if( nDropDestination == SotExchangeDest::NONE )
+ break;
+ SotClipboardFormatId nDropFormat;
+ sal_uInt8 nEventAction, nDropAction;
+ SotExchangeActionFlags nActionFlags;
+ nDropAction = SotExchange::GetExchangeAction(
+ aDataHelper.GetDataFlavorExVector(),
+ nDropDestination, EXCHG_IN_ACTION_COPY,
+ EXCHG_IN_ACTION_COPY, nDropFormat,
+ nEventAction,
+ SotClipboardFormatId::NONE, nullptr,
+ &nActionFlags );
+ if( EXCHG_INOUT_ACTION_NONE != nDropAction )
+ {
+ const Point aDocPt( PixelToLogic( rCEvt.GetMousePosPixel() ) );
+ SwTransferable::PasteData( aDataHelper, rSh, nDropAction, nActionFlags,
+ nDropFormat, nDropDestination, false,
+ false, &aDocPt, EXCHG_IN_ACTION_COPY,
+ true );
+ }
+ }
+ break;
+ case CommandEventId::ModKeyChange :
+ {
+ const CommandModKeyData* pCommandData = rCEvt.GetModKeyData();
+ if (!pCommandData->IsDown() && pCommandData->IsMod1() && !pCommandData->IsMod2())
+ {
+ sal_uInt16 nSlot = 0;
+ if(pCommandData->IsLeftShift() && !pCommandData->IsRightShift())
+ nSlot = SID_ATTR_PARA_LEFT_TO_RIGHT;
+ else if(!pCommandData->IsLeftShift() && pCommandData->IsRightShift())
+ nSlot = SID_ATTR_PARA_RIGHT_TO_LEFT;
+ if(nSlot && SW_MOD()->GetCTLOptions().IsCTLFontEnabled())
+ GetView().GetViewFrame()->GetDispatcher()->Execute(nSlot);
+ }
+ }
+ break;
+ case CommandEventId::InputLanguageChange :
+ // i#42732 - update state of fontname if input language changes
+ g_bInputLanguageSwitched = true;
+ SetUseInputLanguage( true );
+ break;
+ case CommandEventId::SelectionChange:
+ {
+ const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
+ rSh.SttCursorMove();
+ rSh.GoStartSentence();
+ rSh.GetCursor()->GetPoint()->nContent += sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetStart());
+ rSh.SetMark();
+ rSh.GetCursor()->GetMark()->nContent += sal::static_int_cast<sal_uInt16, sal_uLong>(pData->GetEnd() - pData->GetStart());
+ rSh.EndCursorMove( true );
+ }
+ break;
+ case CommandEventId::PrepareReconversion:
+ if( rSh.HasSelection() )
+ {
+ SwPaM *pCursor = rSh.GetCursor();
+
+ if( rSh.IsMultiSelection() )
+ {
+ if (pCursor && !pCursor->HasMark() &&
+ pCursor->GetPoint() == pCursor->GetMark())
+ {
+ rSh.GoPrevCursor();
+ pCursor = rSh.GetCursor();
+ }
+
+ // Cancel all selections other than the last selected one.
+ while( rSh.GetCursor()->GetNext() != rSh.GetCursor() )
+ delete rSh.GetCursor()->GetNext();
+ }
+
+ if( pCursor )
+ {
+ SwNodeOffset nPosNodeIdx = pCursor->GetPoint()->nNode.GetIndex();
+ const sal_Int32 nPosIdx = pCursor->GetPoint()->nContent.GetIndex();
+ SwNodeOffset nMarkNodeIdx = pCursor->GetMark()->nNode.GetIndex();
+ const sal_Int32 nMarkIdx = pCursor->GetMark()->nContent.GetIndex();
+
+ if( !rSh.GetCursor()->HasMark() )
+ rSh.GetCursor()->SetMark();
+
+ rSh.SttCursorMove();
+
+ if( nPosNodeIdx < nMarkNodeIdx )
+ {
+ rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx;
+ rSh.GetCursor()->GetPoint()->nContent = nPosIdx;
+ rSh.GetCursor()->GetMark()->nNode = nPosNodeIdx;
+ rSh.GetCursor()->GetMark()->nContent =
+ rSh.GetCursor()->GetContentNode()->Len();
+ }
+ else if( nPosNodeIdx == nMarkNodeIdx )
+ {
+ rSh.GetCursor()->GetPoint()->nNode = nPosNodeIdx;
+ rSh.GetCursor()->GetPoint()->nContent = nPosIdx;
+ rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx;
+ rSh.GetCursor()->GetMark()->nContent = nMarkIdx;
+ }
+ else
+ {
+ rSh.GetCursor()->GetMark()->nNode = nMarkNodeIdx;
+ rSh.GetCursor()->GetMark()->nContent = nMarkIdx;
+ rSh.GetCursor()->GetPoint()->nNode = nMarkNodeIdx;
+ rSh.GetCursor()->GetPoint()->nContent =
+ rSh.GetCursor()->GetContentNode( false )->Len();
+ }
+
+ rSh.EndCursorMove( true );
+ }
+ }
+ break;
+ case CommandEventId::QueryCharPosition:
+ {
+ bool bVertical = rSh.IsInVerticalText();
+ const SwPosition& rPos = *rSh.GetCursor()->GetPoint();
+ SwDocShell* pDocSh = m_rView.GetDocShell();
+ SwDoc *pDoc = pDocSh->GetDoc();
+ SwExtTextInput* pInput = pDoc->GetExtTextInput( rPos.nNode.GetNode(), rPos.nContent.GetIndex() );
+ if ( pInput )
+ {
+ const SwPosition& rStart = *pInput->Start();
+ const SwPosition& rEnd = *pInput->End();
+ int nSize = 0;
+ for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex )
+ {
+ ++nSize;
+ }
+ vcl::Window& rWin = rSh.GetView().GetEditWin();
+ if ( nSize == 0 )
+ {
+ // When the composition does not exist, use Caret rect instead.
+ const SwRect& aCaretRect ( rSh.GetCharRect() );
+ tools::Rectangle aRect( aCaretRect.Left(), aCaretRect.Top(), aCaretRect.Right(), aCaretRect.Bottom() );
+ rWin.SetCompositionCharRect( &aRect, 1, bVertical );
+ }
+ else
+ {
+ std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ nSize ]);
+ int nRectIndex = 0;
+ for ( SwIndex nIndex = rStart.nContent; nIndex < rEnd.nContent; ++nIndex )
+ {
+ const SwPosition aPos( rStart.nNode, nIndex );
+ SwRect aRect ( rSh.GetCharRect() );
+ rSh.GetCharRectAt( aRect, &aPos );
+ aRects[ nRectIndex ] = tools::Rectangle( aRect.Left(), aRect.Top(), aRect.Right(), aRect.Bottom() );
+ ++nRectIndex;
+ }
+ rWin.SetCompositionCharRect( aRects.get(), nSize, bVertical );
+ }
+ }
+ bCallBase = false;
+ }
+ break;
+ default:
+ SAL_WARN("sw.ui", "unknown command.");
+ break;
+ }
+ if (bCallBase)
+ Window::Command(rCEvt);
+}
+
+/* i#18686 select the object/cursor at the mouse
+ position of the context menu request */
+void SwEditWin::SelectMenuPosition(SwWrtShell& rSh, const Point& rMousePos )
+{
+ const Point aDocPos( PixelToLogic( rMousePos ) );
+ const bool bIsInsideSelectedObj( rSh.IsInsideSelectedObj( aDocPos ) );
+ //create a synthetic mouse event out of the coordinates
+ MouseEvent aMEvt(rMousePos);
+ SdrView *pSdrView = rSh.GetDrawView();
+ if ( pSdrView )
+ {
+ // no close of insert_draw and reset of
+ // draw mode, if context menu position is inside a selected object.
+ if ( !bIsInsideSelectedObj && m_rView.GetDrawFuncPtr() )
+ {
+
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ SfxBindings& rBind = m_rView.GetViewFrame()->GetBindings();
+ rBind.Invalidate( SID_ATTR_SIZE );
+ rBind.Invalidate( SID_TABLE_CELL );
+ }
+
+ // if draw text is active and there's a text selection
+ // at the mouse position then do nothing
+ if(rSh.GetSelectionType() & SelectionType::DrawObjectEditMode)
+ {
+ OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView();
+ ESelection aSelection = pOLV->GetSelection();
+ if(!aSelection.IsZero())
+ {
+ SdrOutliner* pOutliner = pSdrView->GetTextEditOutliner();
+ bool bVertical = pOutliner->IsVertical();
+ const EditEngine& rEditEng = pOutliner->GetEditEngine();
+ Point aEEPos(aDocPos);
+ const tools::Rectangle& rOutputArea = pOLV->GetOutputArea();
+ // regard vertical mode
+ if(bVertical)
+ {
+ aEEPos -= rOutputArea.TopRight();
+ //invert the horizontal direction and exchange X and Y
+ tools::Long nTemp = -aEEPos.X();
+ aEEPos.setX( aEEPos.Y() );
+ aEEPos.setY( nTemp );
+ }
+ else
+ aEEPos -= rOutputArea.TopLeft();
+
+ EPosition aDocPosition = rEditEng.FindDocPosition(aEEPos);
+ ESelection aCompare(aDocPosition.nPara, aDocPosition.nIndex);
+ // make it a forward selection - otherwise the IsLess/IsGreater do not work :-(
+ aSelection.Adjust();
+ if(!(aCompare < aSelection) && !(aCompare > aSelection))
+ {
+ return;
+ }
+ }
+
+ }
+
+ if (pSdrView->MouseButtonDown( aMEvt, GetOutDev() ) )
+ {
+ pSdrView->MouseButtonUp( aMEvt, GetOutDev() );
+ rSh.GetView().GetViewFrame()->GetBindings().InvalidateAll(false);
+ return;
+ }
+ }
+ rSh.ResetCursorStack();
+
+ if ( EnterDrawMode( aMEvt, aDocPos ) )
+ {
+ return;
+ }
+ if ( m_rView.GetDrawFuncPtr() && m_bInsFrame )
+ {
+ StopInsFrame();
+ rSh.Edit();
+ }
+
+ UpdatePointer( aDocPos );
+
+ if( !rSh.IsSelFrameMode() &&
+ !GetView().GetViewFrame()->GetDispatcher()->IsLocked() )
+ {
+ // Test if there is a draw object at that position and if it should be selected.
+ bool bShould = rSh.ShouldObjectBeSelected(aDocPos);
+
+ if(bShould)
+ {
+ m_rView.NoRotate();
+ rSh.HideCursor();
+
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+ bool bSelObj = rSh.SelectObj( aDocPos );
+ if( bUnLockView )
+ rSh.LockView( false );
+
+ if( bSelObj )
+ {
+ // in case the frame was deselected in the macro
+ // just the cursor has to be displayed again.
+ if( FrameTypeFlags::NONE == rSh.GetSelFrameType() )
+ rSh.ShowCursor();
+ else
+ {
+ if (rSh.IsFrameSelected() && m_rView.GetDrawFuncPtr())
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+
+ rSh.EnterSelFrameMode( &aDocPos );
+ g_bFrameDrag = true;
+ UpdatePointer( aDocPos );
+ return;
+ }
+ }
+
+ if (!m_rView.GetDrawFuncPtr())
+ rSh.ShowCursor();
+ }
+ }
+ else if ( rSh.IsSelFrameMode() &&
+ (m_aActHitType == SdrHitKind::NONE ||
+ !bIsInsideSelectedObj))
+ {
+ m_rView.NoRotate();
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView( true );
+
+ if ( rSh.IsSelFrameMode() )
+ {
+ rSh.UnSelectFrame();
+ rSh.LeaveSelFrameMode();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+
+ bool bSelObj = rSh.SelectObj( aDocPos, 0/*nFlag*/ );
+ if( bUnLockView )
+ rSh.LockView( false );
+
+ if( !bSelObj )
+ {
+ // move cursor here so that it is not drawn in the
+ // frame at first; ShowCursor() happens in LeaveSelFrameMode()
+ g_bValidCursorPos = !(CRSR_POSCHG & rSh.CallSetCursor(&aDocPos, false));
+ rSh.LeaveSelFrameMode();
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+ else
+ {
+ rSh.HideCursor();
+ rSh.EnterSelFrameMode( &aDocPos );
+ rSh.SelFlyGrabCursor();
+ rSh.MakeSelVisible();
+ g_bFrameDrag = true;
+ if( rSh.IsFrameSelected() &&
+ m_rView.GetDrawFuncPtr() )
+ {
+ m_rView.GetDrawFuncPtr()->Deactivate();
+ m_rView.SetDrawFuncPtr(nullptr);
+ m_rView.LeaveDrawCreate();
+ m_rView.AttrChangedNotify(nullptr);
+ }
+ UpdatePointer( aDocPos );
+ }
+ }
+ else if ( rSh.IsSelFrameMode() && bIsInsideSelectedObj )
+ {
+ // Object at the mouse cursor is already selected - do nothing
+ return;
+ }
+
+ if ( rSh.IsGCAttr() )
+ {
+ rSh.GCAttr();
+ rSh.ClearGCAttr();
+ }
+
+ bool bOverSelect = rSh.TestCurrPam( aDocPos );
+ bool bOverURLGrf = false;
+ if( !bOverSelect )
+ bOverURLGrf = bOverSelect = nullptr != rSh.IsURLGrfAtPos( aDocPos );
+
+ if ( !bOverSelect )
+ {
+ // create only temporary move context because otherwise
+ // the query against the content form doesn't work!!!
+ SwMvContext aMvContext( &rSh );
+ rSh.CallSetCursor(&aDocPos, false);
+ }
+ if( !bOverURLGrf )
+ {
+ const SelectionType nSelType = rSh.GetSelectionType();
+ if( nSelType == SelectionType::Ole ||
+ nSelType == SelectionType::Graphic )
+ {
+ SwMvContext aMvContext( &rSh );
+ if( !rSh.IsFrameSelected() )
+ rSh.GotoNextFly();
+ rSh.EnterSelFrameMode();
+ }
+ }
+}
+
+static SfxShell* lcl_GetTextShellFromDispatcher( SwView const & rView )
+{
+ // determine Shell
+ SfxShell* pShell;
+ SfxDispatcher* pDispatcher = rView.GetViewFrame()->GetDispatcher();
+ for(sal_uInt16 i = 0; true; ++i )
+ {
+ pShell = pDispatcher->GetShell( i );
+ if( !pShell || dynamic_cast< const SwTextShell *>( pShell ) != nullptr )
+ break;
+ }
+ return pShell;
+}
+
+IMPL_LINK_NOARG(SwEditWin, KeyInputFlushHandler, Timer *, void)
+{
+ FlushInBuffer();
+}
+
+void SwEditWin::InitStaticData()
+{
+ s_pQuickHlpData = new QuickHelpData();
+}
+
+void SwEditWin::FinitStaticData()
+{
+ delete s_pQuickHlpData;
+}
+/* i#3370 - remove quick help to prevent saving
+ * of autocorrection suggestions */
+void SwEditWin::StopQuickHelp()
+{
+ if( HasFocus() && s_pQuickHlpData && s_pQuickHlpData->m_bIsDisplayed )
+ s_pQuickHlpData->Stop( m_rView.GetWrtShell() );
+}
+
+IMPL_LINK_NOARG(SwEditWin, TemplateTimerHdl, Timer *, void)
+{
+ SetApplyTemplate(SwApplyTemplate());
+}
+
+void SwEditWin::SetChainMode( bool bOn )
+{
+ if ( !m_bChainMode )
+ StopInsFrame();
+
+ m_pUserMarker.reset();
+
+ m_bChainMode = bOn;
+
+ static sal_uInt16 aInva[] =
+ {
+ FN_FRAME_CHAIN, FN_FRAME_UNCHAIN, 0
+ };
+ m_rView.GetViewFrame()->GetBindings().Invalidate(aInva);
+}
+
+uno::Reference< css::accessibility::XAccessible > SwEditWin::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SolarMutexGuard aGuard; // this should have happened already!!!
+ SwWrtShell *pSh = m_rView.GetWrtShellPtr();
+ OSL_ENSURE( pSh, "no writer shell, no accessible object" );
+ uno::Reference<
+ css::accessibility::XAccessible > xAcc;
+ if( pSh )
+ xAcc = pSh->CreateAccessible();
+
+ return xAcc;
+#else
+ return nullptr;
+#endif
+}
+
+void QuickHelpData::Move( QuickHelpData& rCpy )
+{
+ m_aHelpStrings.clear();
+ m_aHelpStrings.swap( rCpy.m_aHelpStrings );
+
+ m_bIsDisplayed = rCpy.m_bIsDisplayed;
+ nCurArrPos = rCpy.nCurArrPos;
+ m_bAppendSpace = rCpy.m_bAppendSpace;
+ m_bIsTip = rCpy.m_bIsTip;
+ m_bIsAutoText = rCpy.m_bIsAutoText;
+}
+
+void QuickHelpData::ClearContent()
+{
+ nCurArrPos = nNoPos;
+ m_bIsDisplayed = m_bAppendSpace = false;
+ nTipId = nullptr;
+ m_aHelpStrings.clear();
+ m_bIsTip = true;
+ m_bIsAutoText = true;
+}
+
+void QuickHelpData::Start(SwWrtShell& rSh, const bool bRestart)
+{
+ if (bRestart)
+ {
+ nCurArrPos = 0;
+ }
+ m_bIsDisplayed = true;
+
+ vcl::Window& rWin = rSh.GetView().GetEditWin();
+ if( m_bIsTip )
+ {
+ Point aPt( rWin.OutputToScreenPixel( rWin.LogicToPixel(
+ rSh.GetCharRect().Pos() )));
+ aPt.AdjustY( -3 );
+ nTipId = Help::ShowPopover(&rWin, tools::Rectangle( aPt, Size( 1, 1 )),
+ CurStr(),
+ QuickHelpFlags::Left | QuickHelpFlags::Bottom);
+ }
+ else
+ {
+ OUString sStr(CurStr());
+ sStr = sStr.copy(CurLen());
+ sal_uInt16 nL = sStr.getLength();
+ const ExtTextInputAttr nVal = ExtTextInputAttr::DottedUnderline |
+ ExtTextInputAttr::Highlight;
+ const std::vector<ExtTextInputAttr> aAttrs( nL, nVal );
+ CommandExtTextInputData aCETID( sStr, aAttrs.data(), nL,
+ 0, false );
+
+ //fdo#33092. If the current input language is the default
+ //language that text would appear in if typed, then don't
+ //force a language on for the ExtTextInput.
+ LanguageType eInputLanguage = rWin.GetInputLanguage();
+ if (lcl_isNonDefaultLanguage(eInputLanguage,
+ rSh.GetView(), sStr) == INVALID_HINT)
+ {
+ eInputLanguage = LANGUAGE_DONTKNOW;
+ }
+
+ rSh.CreateExtTextInput(eInputLanguage);
+ rSh.SetExtTextInputData( aCETID );
+ }
+}
+
+void QuickHelpData::Stop( SwWrtShell& rSh )
+{
+ if( !m_bIsTip )
+ rSh.DeleteExtTextInput( false );
+ else if( nTipId )
+ {
+ vcl::Window& rWin = rSh.GetView().GetEditWin();
+ Help::HidePopover(&rWin, nTipId);
+ }
+ ClearContent();
+}
+
+void QuickHelpData::FillStrArr( SwWrtShell const & rSh, const OUString& rWord )
+{
+ enum Capitalization { CASE_LOWER, CASE_UPPER, CASE_SENTENCE, CASE_OTHER };
+
+ // Determine word capitalization
+ const CharClass& rCC = GetAppCharClass();
+ const OUString sWordLower = rCC.lowercase( rWord );
+ Capitalization aWordCase = CASE_OTHER;
+ if ( !rWord.isEmpty() )
+ {
+ if ( rWord[0] == sWordLower[0] )
+ {
+ if ( rWord == sWordLower )
+ aWordCase = CASE_LOWER;
+ }
+ else
+ {
+ // First character is not lower case i.e. assume upper or title case
+ OUString sWordSentence = sWordLower.replaceAt( 0, 1, rtl::OUStringChar(rWord[0]) );
+ if ( rWord == sWordSentence )
+ aWordCase = CASE_SENTENCE;
+ else
+ {
+ if ( rWord == rCC.uppercase( rWord ) )
+ aWordCase = CASE_UPPER;
+ }
+ }
+ }
+
+ salhelper::SingletonRef<SwCalendarWrapper>* pCalendar = s_getCalendarWrapper();
+ (*pCalendar)->LoadDefaultCalendar( rSh.GetCurLang() );
+
+ // Add matching calendar month and day names
+ for ( const auto& aNames : { (*pCalendar)->getMonths(), (*pCalendar)->getDays() } )
+ {
+ for ( const auto& rName : aNames )
+ {
+ const OUString& rStr( rName.FullName );
+ // Check string longer than word and case insensitive match
+ if( rStr.getLength() > rWord.getLength() &&
+ rCC.lowercase( rStr, 0, rWord.getLength() ) == sWordLower )
+ {
+ OUString sStr;
+
+ //fdo#61251 if it's an exact match, ensure unchanged replacement
+ //exists as a candidate
+ if (rStr.startsWith(rWord))
+ m_aHelpStrings.emplace_back(rStr, rWord.getLength());
+ else
+ sStr = rStr; // to be added if no case conversion is performed below
+
+ if ( aWordCase == CASE_LOWER )
+ sStr = rCC.lowercase(rStr);
+ else if ( aWordCase == CASE_SENTENCE )
+ sStr = rCC.lowercase(rStr).replaceAt(0, 1, rtl::OUStringChar(rStr[0]));
+ else if ( aWordCase == CASE_UPPER )
+ sStr = rCC.uppercase(rStr);
+
+ if (!sStr.isEmpty())
+ m_aHelpStrings.emplace_back(sStr, rWord.getLength());
+ }
+ }
+ }
+
+ // Add matching current date in ISO 8601 format, for example 2016-01-30
+ OUString rStrToday;
+
+ // do not suggest for single years, for example for "2016",
+ // only for "201" or "2016-..." (to avoid unintentional text
+ // insertion at line ending, for example typing "30 January 2016")
+ if (!rWord.isEmpty() && rWord.getLength() != 4 && rWord[0] == '2')
+ {
+ rStrToday = utl::toISO8601(DateTime(Date(Date::SYSTEM)).GetUNODateTime());
+ if (rStrToday.startsWith(rWord))
+ m_aHelpStrings.emplace_back(rStrToday, rWord.getLength());
+ }
+
+ // Add matching words from AutoCompleteWord list
+ const SwAutoCompleteWord& rACList = SwEditShell::GetAutoCompleteWords();
+ std::vector<OUString> strings;
+
+ if ( !rACList.GetWordsMatching( rWord, strings ) )
+ return;
+
+ for (const OUString & aCompletedString : strings)
+ {
+ // when we have a matching current date, avoid to suggest
+ // other words with the same matching starting characters,
+ // for example 2016-01-3 instead of 2016-01-30
+ if (!rStrToday.isEmpty() && aCompletedString.startsWith(rWord))
+ continue;
+
+ OUString sStr;
+
+ //fdo#61251 if it's an exact match, ensure unchanged replacement
+ //exists as a candidate
+ if (aCompletedString.startsWith(rWord))
+ m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength());
+ else
+ sStr = aCompletedString; // to be added if no case conversion is performed below
+
+ if (aWordCase == CASE_LOWER)
+ sStr = rCC.lowercase(aCompletedString);
+ else if (aWordCase == CASE_SENTENCE)
+ sStr = rCC.lowercase(aCompletedString)
+ .replaceAt(0, 1, rtl::OUStringChar(aCompletedString[0]));
+ else if (aWordCase == CASE_UPPER)
+ sStr = rCC.uppercase(aCompletedString);
+
+ if (!sStr.isEmpty())
+ m_aHelpStrings.emplace_back(aCompletedString, rWord.getLength());
+ }
+}
+
+namespace {
+
+class CompareIgnoreCaseAsciiFavorExact
+{
+ const OUString &m_rOrigWord;
+public:
+ explicit CompareIgnoreCaseAsciiFavorExact(const OUString& rOrigWord)
+ : m_rOrigWord(rOrigWord)
+ {
+ }
+
+ bool operator()(const std::pair<OUString, sal_uInt16>& s1,
+ const std::pair<OUString, sal_uInt16>& s2) const
+ {
+ int nRet = s1.first.compareToIgnoreAsciiCase(s2.first);
+ if (nRet == 0)
+ {
+ //fdo#61251 sort stuff that starts with the exact rOrigWord before
+ //another ignore-case candidate
+ int n1StartsWithOrig = s1.first.startsWith(m_rOrigWord) ? 0 : 1;
+ int n2StartsWithOrig = s2.first.startsWith(m_rOrigWord) ? 0 : 1;
+ return n1StartsWithOrig < n2StartsWithOrig;
+ }
+ return nRet < 0;
+ }
+};
+
+struct EqualIgnoreCaseAscii
+{
+ bool operator()(const std::pair<OUString, sal_uInt16>& s1,
+ const std::pair<OUString, sal_uInt16>& s2) const
+ {
+ return s1.first.equalsIgnoreAsciiCase(s2.first);
+ }
+};
+
+} // anonymous namespace
+
+// TODO Implement an i18n aware sort
+void QuickHelpData::SortAndFilter(const OUString &rOrigWord)
+{
+ std::sort( m_aHelpStrings.begin(),
+ m_aHelpStrings.end(),
+ CompareIgnoreCaseAsciiFavorExact(rOrigWord) );
+
+ const auto& it
+ = std::unique(m_aHelpStrings.begin(), m_aHelpStrings.end(), EqualIgnoreCaseAscii());
+ m_aHelpStrings.erase( it, m_aHelpStrings.end() );
+
+ nCurArrPos = 0;
+}
+
+// For a given chunk of typed text between 3 and 9 characters long that may start at a word boundary
+// or in a whitespace and may include whitespaces, SwEditShell::GetChunkForAutoTextcreates a list of
+// possible candidates for long AutoText names. Let's say, we have typed text "lorem ipsum dr f";
+// and the cursor is right after the "f". SwEditShell::GetChunkForAutoText would take " dr f",
+// since it's the longest chunk to the left of the cursor no longer than 9 characters, not starting
+// in the middle of a word. Then it would create this list from it (in this order, longest first):
+// " dr f"
+// " dr f"
+// "dr f"
+// It cannot add "r f", because it starts in the middle of the word "dr"; also it cannot give " f",
+// because it's only 2 characters long.
+// Now the result of SwEditShell::GetChunkForAutoText is passed here to SwEditWin::ShowAutoText, and
+// then to SwGlossaryList::HasLongName, where all existing autotext entries' long names are tested
+// if they start with one of the list elements. The matches are sorted according the position of the
+// candidate that matched first, then alphabetically inside the group of suggestions for a given
+// candidate. Say, if we have these AutoText entry long names:
+// "Dr Frodo"
+// "Dr Credo"
+// "Or Bilbo"
+// "dr foo"
+// " Dr Fuzz"
+// " dr Faust"
+// the resulting list would be:
+// " Dr Fuzz" -> matches the first (longest) item in the candidates list
+// " dr Faust" -> matches the second candidate item
+// "Dr Foo" -> first item of the two matching the third candidate; alphabetically sorted
+// "Dr Frodo" -> second item of the two matching the third candidate; alphabetically sorted
+// Each of the resulting suggestions knows the length of the candidate it replaces, so accepting the
+// first suggestion would replace 6 characters before cursor, while tabbing to and accepting the
+// last suggestion would replace only 4 characters to the left of cursor.
+bool SwEditWin::ShowAutoText(const std::vector<OUString>& rChunkCandidates)
+{
+ s_pQuickHlpData->ClearContent();
+ if (!rChunkCandidates.empty())
+ {
+ SwGlossaryList* pList = ::GetGlossaryList();
+ pList->HasLongName(rChunkCandidates, s_pQuickHlpData->m_aHelpStrings);
+ }
+
+ if (!s_pQuickHlpData->m_aHelpStrings.empty())
+ {
+ s_pQuickHlpData->Start(m_rView.GetWrtShell(), true);
+ }
+ return !s_pQuickHlpData->m_aHelpStrings.empty();
+}
+
+void SwEditWin::ShowAutoCorrectQuickHelp(
+ const OUString& rWord, SvxAutoCorrect& rACorr )
+{
+ if (rWord.isEmpty())
+ return;
+ SwWrtShell& rSh = m_rView.GetWrtShell();
+ s_pQuickHlpData->ClearContent();
+
+ if( s_pQuickHlpData->m_aHelpStrings.empty() &&
+ rACorr.GetSwFlags().bAutoCompleteWords )
+ {
+ s_pQuickHlpData->m_bIsAutoText = false;
+ s_pQuickHlpData->m_bIsTip = rACorr.GetSwFlags().bAutoCmpltShowAsTip;
+
+ // Get the necessary data to show help text.
+ s_pQuickHlpData->FillStrArr( rSh, rWord );
+ }
+
+ if( !s_pQuickHlpData->m_aHelpStrings.empty() )
+ {
+ s_pQuickHlpData->SortAndFilter(rWord);
+ s_pQuickHlpData->Start(rSh, true);
+ }
+}
+
+bool SwEditWin::IsInHeaderFooter( const Point &rDocPt, FrameControlType &rControl ) const
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ const SwPageFrame* pPageFrame = rSh.GetLayout()->GetPageAtPos( rDocPt );
+
+ if ( pPageFrame && pPageFrame->IsOverHeaderFooterArea( rDocPt, rControl ) )
+ return true;
+
+ if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) || rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
+ {
+ SwFrameControlsManager &rMgr = rSh.GetView().GetEditWin().GetFrameControlsManager();
+ Point aPoint( LogicToPixel( rDocPt ) );
+
+ if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Header ) )
+ {
+ SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Header, pPageFrame );
+ if ( pControl && pControl->Contains( aPoint ) )
+ {
+ rControl = FrameControlType::Header;
+ return true;
+ }
+ }
+
+ if ( rSh.IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
+ {
+ SwFrameControlPtr pControl = rMgr.GetControl( FrameControlType::Footer, pPageFrame );
+ if ( pControl && pControl->Contains( aPoint ) )
+ {
+ rControl = FrameControlType::Footer;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SwEditWin::IsOverHeaderFooterFly( const Point& rDocPos, FrameControlType& rControl, bool& bOverFly, bool& bPageAnchored ) const
+{
+ bool bRet = false;
+ Point aPt( rDocPos );
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ SwPaM aPam( *rSh.GetCurrentShellCursor().GetPoint() );
+ rSh.GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aPt, nullptr, true );
+
+ const SwStartNode* pStartFly = aPam.GetPoint()->nNode.GetNode().FindFlyStartNode();
+ if ( pStartFly )
+ {
+ bOverFly = true;
+ SwFrameFormat* pFlyFormat = pStartFly->GetFlyFormat( );
+ if ( pFlyFormat )
+ {
+ const SwPosition* pAnchor = pFlyFormat->GetAnchor( ).GetContentAnchor( );
+ if ( pAnchor )
+ {
+ bool bInHeader = pAnchor->nNode.GetNode( ).FindHeaderStartNode( ) != nullptr;
+ bool bInFooter = pAnchor->nNode.GetNode( ).FindFooterStartNode( ) != nullptr;
+
+ bRet = bInHeader || bInFooter;
+ if ( bInHeader )
+ rControl = FrameControlType::Header;
+ else if ( bInFooter )
+ rControl = FrameControlType::Footer;
+ }
+ else
+ bPageAnchored = pFlyFormat->GetAnchor( ).GetAnchorId( ) == RndStdIds::FLY_AT_PAGE;
+ }
+ }
+ else
+ bOverFly = false;
+ return bRet;
+}
+
+void SwEditWin::SetUseInputLanguage( bool bNew )
+{
+ if ( bNew || m_bUseInputLanguage )
+ {
+ SfxBindings& rBind = GetView().GetViewFrame()->GetBindings();
+ rBind.Invalidate( SID_ATTR_CHAR_FONT );
+ rBind.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
+ }
+ m_bUseInputLanguage = bNew;
+}
+
+OUString SwEditWin::GetSurroundingText() const
+{
+ SwWrtShell& rSh = m_rView.GetWrtShell();
+
+ if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit())
+ return rSh.GetDrawView()->GetTextEditOutlinerView()->GetSurroundingText();
+
+ OUString sReturn;
+ if( rSh.HasSelection() && !rSh.IsMultiSelection() && rSh.IsSelOnePara() )
+ rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR );
+ else if( !rSh.HasSelection() )
+ {
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView(true);
+ rSh.Push();
+
+ // disable accessible events for internal-only helper cursor
+ const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents();
+ rSh.SetSendAccessibleCursorEvents(false);
+
+ // get the sentence around the cursor
+ rSh.HideCursor();
+ rSh.GoStartSentence();
+ rSh.SetMark();
+ rSh.GoEndSentence();
+ rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR );
+
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld);
+ rSh.HideCursor();
+
+ if (bUnLockView)
+ rSh.LockView(false);
+ }
+
+ return sReturn;
+}
+
+Selection SwEditWin::GetSurroundingTextSelection() const
+{
+ SwWrtShell& rSh = m_rView.GetWrtShell();
+
+ if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit())
+ return rSh.GetDrawView()->GetTextEditOutlinerView()->GetSurroundingTextSelection();
+
+ Selection aSel(0, 0);
+ if( rSh.HasSelection() )
+ {
+ OUString sReturn;
+ rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR );
+ aSel = Selection( 0, sReturn.getLength() );
+ }
+ else if (rSh.GetCursor()->GetPoint()->nNode.GetNode().GetTextNode())
+ {
+ bool bUnLockView = !rSh.IsViewLocked();
+ rSh.LockView(true);
+
+ // Return the position of the visible cursor in the sentence
+ // around the visible cursor.
+ TextFrameIndex const nPos(rSh.GetCursorPointAsViewIndex());
+
+ // store shell state *before* Push
+ ::std::unique_ptr<SwCallLink> pLink(::std::make_unique<SwCallLink>(rSh));
+ rSh.Push();
+
+ // disable accessible events for internal-only helper cursor
+ const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents();
+ rSh.SetSendAccessibleCursorEvents(false);
+
+ rSh.HideCursor();
+ rSh.GoStartSentence();
+ TextFrameIndex const nStartPos(rSh.GetCursorPointAsViewIndex());
+
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent, ::std::move(pLink));
+ rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld);
+ rSh.ShowCursor();
+
+ if (bUnLockView)
+ rSh.LockView(false);
+
+ aSel = Selection(sal_Int32(nPos - nStartPos), sal_Int32(nPos - nStartPos));
+ }
+
+ return aSel;
+}
+
+bool SwEditWin::DeleteSurroundingText(const Selection& rSelection)
+{
+ SwWrtShell& rSh = m_rView.GetWrtShell();
+
+ if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit())
+ return rSh.GetDrawView()->GetTextEditOutlinerView()->DeleteSurroundingText(rSelection);
+
+ if (rSh.HasSelection())
+ return false;
+
+ // rSelection is relative to the start of the sentence, so find that and
+ // adjust the range by it
+ rSh.Push();
+
+ // disable accessible events for internal-only helper cursor
+ const bool bSendAccessibleEventOld = rSh.IsSendAccessibleCursorEvents();
+ rSh.SetSendAccessibleCursorEvents(false);
+
+ rSh.HideCursor();
+ rSh.GoStartSentence();
+ TextFrameIndex const nStartPos(rSh.GetCursorPointAsViewIndex());
+
+ rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ rSh.SetSendAccessibleCursorEvents(bSendAccessibleEventOld);
+ rSh.ShowCursor();
+
+ if (rSh.SelectTextView(nStartPos + TextFrameIndex(rSelection.Min()), nStartPos + TextFrameIndex(rSelection.Max())))
+ {
+ rSh.Delete(false);
+ return true;
+ }
+
+ return false;
+}
+
+void SwEditWin::LogicInvalidate(const tools::Rectangle* pRectangle)
+{
+ SfxLokHelper::notifyInvalidation(&m_rView, pRectangle);
+}
+
+void SwEditWin::LogicMouseButtonDown(const MouseEvent& rMouseEvent)
+{
+ // When we're not doing tiled rendering, then positions must be passed as pixels.
+ assert(comphelper::LibreOfficeKit::isActive());
+
+ Point aPoint = GetPointerPosPixel();
+ SetLastMousePos(rMouseEvent.GetPosPixel());
+
+ MouseButtonDown(rMouseEvent);
+
+ SetPointerPosPixel(aPoint);
+}
+
+void SwEditWin::LogicMouseButtonUp(const MouseEvent& rMouseEvent)
+{
+ // When we're not doing tiled rendering, then positions must be passed as pixels.
+ assert(comphelper::LibreOfficeKit::isActive());
+
+ Point aPoint = GetPointerPosPixel();
+ SetLastMousePos(rMouseEvent.GetPosPixel());
+
+ MouseButtonUp(rMouseEvent);
+
+ SetPointerPosPixel(aPoint);
+}
+
+void SwEditWin::LogicMouseMove(const MouseEvent& rMouseEvent)
+{
+ // When we're not doing tiled rendering, then positions must be passed as pixels.
+ assert(comphelper::LibreOfficeKit::isActive());
+
+ Point aPoint = GetPointerPosPixel();
+ SetLastMousePos(rMouseEvent.GetPosPixel());
+
+ MouseMove(rMouseEvent);
+
+ SetPointerPosPixel(aPoint);
+}
+
+void SwEditWin::SetCursorTwipPosition(const Point& rPosition, bool bPoint, bool bClearMark)
+{
+ if (SdrView* pSdrView = m_rView.GetWrtShell().GetDrawView())
+ {
+ // Editing shape text, then route the call to editeng.
+ if (pSdrView->GetTextEditObject())
+ {
+ EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView();
+ rEditView.SetCursorLogicPosition(rPosition, bPoint, bClearMark);
+ return;
+ }
+ }
+
+ if (m_rView.GetPostItMgr())
+ {
+ if (sw::annotation::SwAnnotationWin* pWin = m_rView.GetPostItMgr()->GetActiveSidebarWin())
+ {
+ // Editing postit text.
+ pWin->SetCursorLogicPosition(rPosition, bPoint, bClearMark);
+ return;
+ }
+ }
+
+ // Not an SwWrtShell, as that would make SwCursorShell::GetCursor() inaccessible.
+ SwEditShell& rShell = m_rView.GetWrtShell();
+
+ bool bCreateSelection = false;
+ {
+ SwMvContext aMvContext(&rShell);
+ if (bClearMark)
+ rShell.ClearMark();
+ else
+ bCreateSelection = !rShell.HasMark();
+
+ if (bCreateSelection)
+ m_rView.GetWrtShell().SttSelect();
+
+ // If the mark is to be updated, then exchange the point and mark before
+ // and after, as we can't easily set the mark.
+ if (!bPoint)
+ rShell.getShellCursor(/*bBlock=*/false)->Exchange();
+ rShell.SetCursor(rPosition);
+ if (!bPoint)
+ rShell.getShellCursor(/*bBlock=*/false)->Exchange();
+ }
+
+ if (bCreateSelection)
+ m_rView.GetWrtShell().EndSelect();
+}
+
+void SwEditWin::SetGraphicTwipPosition(bool bStart, const Point& rPosition)
+{
+ if (bStart)
+ {
+ MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ MouseButtonDown(aClickEvent);
+ MouseEvent aMoveEvent(Point(rPosition.getX() + MIN_MOVE + 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ MouseMove(aMoveEvent);
+ }
+ else
+ {
+ MouseEvent aMoveEvent(Point(rPosition.getX() - MIN_MOVE - 1, rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT);
+ MouseMove(aMoveEvent);
+ MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT);
+ MouseButtonUp(aClickEvent);
+ }
+}
+
+SwFrameControlsManager& SwEditWin::GetFrameControlsManager()
+{
+ return *m_pFrameControlsManager;
+}
+
+void SwEditWin::ToggleOutlineContentVisibility(const size_t nOutlinePos, const bool bSubs)
+{
+ SwWrtShell& rSh = GetView().GetWrtShell();
+
+ if (GetView().GetDrawView()->IsTextEdit())
+ rSh.EndTextEdit();
+ if (GetView().IsDrawMode())
+ GetView().LeaveDrawCreate();
+ rSh.EnterStdMode();
+
+ if (!bSubs || rSh.GetViewOptions()->IsTreatSubOutlineLevelsAsContent())
+ {
+ SwNode* pNode = rSh.GetNodes().GetOutLineNds()[nOutlinePos];
+ bool bVisible = true;
+ pNode->GetTextNode()->GetAttrOutlineContentVisible(bVisible);
+ pNode->GetTextNode()->SetAttrOutlineContentVisible(!bVisible);
+ }
+ else if (bSubs)
+ {
+ // toggle including sub levels
+ SwOutlineNodes::size_type nPos = nOutlinePos;
+ SwOutlineNodes::size_type nOutlineNodesCount
+ = rSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+ int nLevel = rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nOutlinePos);
+ bool bVisible = rSh.IsOutlineContentVisible(nOutlinePos);
+ do
+ {
+ if (rSh.IsOutlineContentVisible(nPos) == bVisible)
+ rSh.GetNodes().GetOutLineNds()[nPos]->GetTextNode()->SetAttrOutlineContentVisible(!bVisible);
+ } while (++nPos < nOutlineNodesCount
+ && rSh.getIDocumentOutlineNodesAccess()->getOutlineLevel(nPos) > nLevel);
+ }
+
+ rSh.InvalidateOutlineContentVisibility();
+ rSh.GotoOutline(nOutlinePos);
+ GetView().GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx
new file mode 100644
index 000000000..a7aeb028c
--- /dev/null
+++ b/sw/source/uibase/docvw/edtwin2.cxx
@@ -0,0 +1,500 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <doc.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <vcl/help.hxx>
+#include <tools/urlobj.hxx>
+#include <fmtrfmrk.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <docsh.hxx>
+#include <edtwin.hxx>
+#include <dpage.hxx>
+#include <docufld.hxx>
+#include <reffld.hxx>
+#include <cellatr.hxx>
+#include <shdwcrsr.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtftn.hxx>
+#include <redline.hxx>
+#include <tox.hxx>
+#include <txatbase.hxx>
+#include <uitool.hxx>
+#include <viewopt.hxx>
+#include <strings.hrc>
+
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <comphelper/lok.hxx>
+#include <authfld.hxx>
+
+static OUString lcl_GetRedlineHelp( const SwRangeRedline& rRedl, bool bBalloon, bool bTableChange )
+{
+ TranslateId pResId;
+ switch( rRedl.GetType() )
+ {
+ case RedlineType::Insert: pResId = bTableChange
+ ? STR_REDLINE_TABLE_ROW_INSERT
+ : rRedl.IsMoved()
+ ? STR_REDLINE_INSERT_MOVED
+ : STR_REDLINE_INSERT;
+ break;
+ case RedlineType::Delete: pResId = bTableChange
+ ? STR_REDLINE_TABLE_ROW_DELETE
+ : rRedl.IsMoved()
+ ? STR_REDLINE_DELETE_MOVED
+ : STR_REDLINE_DELETE;
+ break;
+ case RedlineType::Format: pResId = STR_REDLINE_FORMAT; break;
+ case RedlineType::Table: pResId = STR_REDLINE_TABLE; break;
+ case RedlineType::FmtColl: pResId = STR_REDLINE_FMTCOLL; break;
+ case RedlineType::ParagraphFormat: pResId = STR_REDLINE_PARAGRAPH_FORMAT; break;
+ case RedlineType::TableRowInsert: pResId = STR_REDLINE_TABLE_ROW_INSERT; break;
+ case RedlineType::TableRowDelete: pResId = STR_REDLINE_TABLE_ROW_DELETE; break;
+ case RedlineType::TableCellInsert: pResId = STR_REDLINE_TABLE_CELL_INSERT; break;
+ case RedlineType::TableCellDelete: pResId = STR_REDLINE_TABLE_CELL_DELETE; break;
+ default: break;
+ }
+
+ OUStringBuffer sBuf;
+ if (pResId)
+ {
+ sBuf.append(SwResId(pResId));
+ sBuf.append(": ");
+ sBuf.append(rRedl.GetAuthorString());
+ sBuf.append(" - ");
+ sBuf.append(GetAppLangDateTimeString(rRedl.GetTimeStamp()));
+ if( bBalloon && !rRedl.GetComment().isEmpty() )
+ sBuf.append('\n').append(rRedl.GetComment());
+ }
+ return sBuf.makeStringAndClear();
+}
+
+OUString SwEditWin::ClipLongToolTip(const OUString& rText)
+{
+ OUString sDisplayText(rText);
+ tools::Long nTextWidth = GetTextWidth(sDisplayText);
+ tools::Long nMaxWidth = GetDesktopRectPixel().GetWidth() * 2 / 3;
+ nMaxWidth = PixelToLogic(Size(nMaxWidth, 0)).Width();
+ if (nTextWidth > nMaxWidth)
+ sDisplayText = GetOutDev()->GetEllipsisString(sDisplayText, nMaxWidth, DrawTextFlags::CenterEllipsis);
+ return sDisplayText;
+}
+
+void SwEditWin::RequestHelp(const HelpEvent &rEvt)
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ bool bQuickBalloon = bool(rEvt.GetMode() & ( HelpEventMode::QUICK | HelpEventMode::BALLOON ));
+ if(bQuickBalloon && !rSh.GetViewOptions()->IsShowContentTips())
+ return;
+ bool bContinue = true;
+ CurrShell aCurr(&rSh);
+ OUString sText;
+ Point aPos( PixelToLogic( ScreenToOutputPixel( rEvt.GetMousePosPixel() ) ));
+ bool bBalloon = bool(rEvt.GetMode() & HelpEventMode::BALLOON);
+
+ SdrView *pSdrView = rSh.GetDrawView();
+
+ if( bQuickBalloon && pSdrView )
+ {
+ SdrPageView* pPV = pSdrView->GetSdrPageView();
+ SwDPage* pPage = pPV ? static_cast<SwDPage*>(pPV->GetPage()) : nullptr;
+ bContinue = pPage && pPage->RequestHelp(this, pSdrView, rEvt);
+ }
+
+ if( bContinue && bQuickBalloon)
+ {
+ SwRect aFieldRect;
+ SwContentAtPos aContentAtPos( IsAttrAtPos::Field |
+ IsAttrAtPos::InetAttr |
+ IsAttrAtPos::Ftn |
+ IsAttrAtPos::Redline |
+ IsAttrAtPos::ToxMark |
+ IsAttrAtPos::RefMark |
+ IsAttrAtPos::SmartTag |
+#ifdef DBG_UTIL
+ IsAttrAtPos::TableBoxValue |
+ ( bBalloon ? IsAttrAtPos::CurrAttrs : IsAttrAtPos::NONE) |
+#endif
+ IsAttrAtPos::TableBoxFml |
+ IsAttrAtPos::TableRedline );
+
+ if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) )
+ {
+ QuickHelpFlags nStyle = QuickHelpFlags::NONE; // style of quick help
+ switch( aContentAtPos.eContentAtPos )
+ {
+ case IsAttrAtPos::TableBoxFml:
+ sText = "= " + static_cast<const SwTableBoxFormula*>(aContentAtPos.aFnd.pAttr)->GetFormula();
+ break;
+#ifdef DBG_UTIL
+ case IsAttrAtPos::TableBoxValue:
+ {
+ if(aContentAtPos.aFnd.pAttr)
+ {
+ sText = OStringToOUString(OString::number(
+ static_cast<const SwTableBoxValue*>(aContentAtPos.aFnd.pAttr)->GetValue()),
+ osl_getThreadTextEncoding());
+ }
+ break;
+ }
+ case IsAttrAtPos::CurrAttrs:
+ sText = aContentAtPos.sStr;
+ break;
+#endif
+
+ case IsAttrAtPos::InetAttr:
+ {
+ sText = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr)->GetValue();
+ sText = URIHelper::removePassword( sText,
+ INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous);
+ //#i63832# remove the link target type
+ sal_Int32 nFound = sText.indexOf(cMarkSeparator);
+ if( nFound != -1 && (++nFound) < sText.getLength() )
+ {
+ std::u16string_view sSuffix( sText.subView(nFound) );
+ if( sSuffix == u"table" ||
+ sSuffix == u"frame" ||
+ sSuffix == u"region" ||
+ sSuffix == u"outline" ||
+ sSuffix == u"text" ||
+ sSuffix == u"graphic" ||
+ sSuffix == u"ole" ||
+ sSuffix == u"drawingobject" )
+ sText = sText.copy( 0, nFound - 1);
+ }
+ // #i104300#
+ // special handling if target is a cross-reference bookmark
+ {
+ OUString sTmpSearchStr = sText.copy( 1 );
+ IDocumentMarkAccess* pMarkAccess = rSh.getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk =
+ pMarkAccess->findBookmark( sTmpSearchStr );
+ if ( ppBkmk != pMarkAccess->getBookmarksEnd() &&
+ IDocumentMarkAccess::GetType(**ppBkmk)
+ == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK )
+ {
+ SwTextNode* pTextNode = (*ppBkmk)->GetMarkStart().nNode.GetNode().GetTextNode();
+ if ( pTextNode )
+ {
+ sText = sw::GetExpandTextMerged(rSh.GetLayout(), *pTextNode, true, false, ExpandMode(0));
+
+ if( !sText.isEmpty() )
+ {
+ OUStringBuffer sTmp(sText.replaceAll(u"\u00ad", ""));
+ for (sal_Int32 i = 0; i < sTmp.getLength(); ++i)
+ {
+ if (sTmp[i] < 0x20)
+ sTmp[i] = 0x20;
+ else if (sTmp[i] == 0x2011)
+ sTmp[i] = '-';
+ }
+ sText = sTmp.makeStringAndClear();
+ }
+ }
+ }
+ }
+ // #i80029#
+ bool bExecHyperlinks = m_rView.GetDocShell()->IsReadOnly();
+ if ( !bExecHyperlinks )
+ {
+ sText = SfxHelp::GetURLHelpText(sText);
+ }
+ break;
+ }
+ case IsAttrAtPos::SmartTag:
+ {
+ vcl::KeyCode aCode( KEY_SPACE );
+ vcl::KeyCode aModifiedCode( KEY_SPACE, KEY_MOD1 );
+ OUString aModStr( aModifiedCode.GetName() );
+ aModStr = aModStr.replaceFirst(aCode.GetName(), "");
+ aModStr = aModStr.replaceAll("+", "");
+ sText = SwResId(STR_SMARTTAG_CLICK).replaceAll("%s", aModStr);
+ break;
+ }
+
+ case IsAttrAtPos::Ftn:
+ if( aContentAtPos.pFndTextAttr && aContentAtPos.aFnd.pAttr )
+ {
+ const SwFormatFootnote* pFootnote = static_cast<const SwFormatFootnote*>(aContentAtPos.aFnd.pAttr);
+ OUString sTmp(pFootnote->GetFootnoteText(*rSh.GetLayout()));
+ sText = SwResId( pFootnote->IsEndNote()
+ ? STR_ENDNOTE : STR_FTNNOTE ) + sTmp;
+ bBalloon = true;
+ if( aContentAtPos.IsInRTLText() )
+ nStyle |= QuickHelpFlags::BiDiRtl;
+ }
+ break;
+
+ case IsAttrAtPos::TableRedline:
+ case IsAttrAtPos::Redline:
+ {
+ const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() );
+ const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips();
+ if ( bShowTrackChanges && bShowInlineTooltips )
+ {
+ sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon, IsAttrAtPos::TableRedline == aContentAtPos.eContentAtPos );
+ }
+ break;
+ }
+
+ case IsAttrAtPos::ToxMark:
+ sText = aContentAtPos.sStr;
+ if( !sText.isEmpty() && aContentAtPos.pFndTextAttr )
+ {
+ const SwTOXType* pTType = aContentAtPos.pFndTextAttr->
+ GetTOXMark().GetTOXType();
+ if( pTType && !pTType->GetTypeName().isEmpty() )
+ {
+ sText = ": " + sText;
+ sText = pTType->GetTypeName() + sText;
+ }
+ }
+ break;
+
+ case IsAttrAtPos::RefMark:
+ if(aContentAtPos.aFnd.pAttr)
+ {
+ sText = SwResId(STR_CONTENT_TYPE_SINGLE_REFERENCE) + ": ";
+ sText += static_cast<const SwFormatRefMark*>(aContentAtPos.aFnd.pAttr)->GetRefName();
+ }
+ break;
+
+ default:
+ {
+ SwModuleOptions* pModOpt = SW_MOD()->GetModuleConfig();
+ if(!pModOpt->IsHideFieldTips())
+ {
+ const SwField* pField = aContentAtPos.aFnd.pField;
+ switch( pField->Which() )
+ {
+ case SwFieldIds::SetExp:
+ case SwFieldIds::Table:
+ case SwFieldIds::GetExp:
+ {
+ sal_uInt16 nOldSubType = pField->GetSubType();
+ const_cast<SwField*>(pField)->SetSubType(nsSwExtendedSubType::SUB_CMD);
+ sText = pField->ExpandField(true, rSh.GetLayout());
+ const_cast<SwField*>(pField)->SetSubType(nOldSubType);
+ }
+ break;
+
+ case SwFieldIds::Postit:
+ {
+ break;
+ }
+ case SwFieldIds::Input: // BubbleHelp, because the suggestion could be quite long
+ bBalloon = true;
+ [[fallthrough]];
+ case SwFieldIds::Dropdown:
+ case SwFieldIds::JumpEdit:
+ sText = pField->GetPar2();
+ break;
+
+ case SwFieldIds::Database:
+ sText = pField->GetFieldName();
+ break;
+
+ case SwFieldIds::User:
+ {
+ OUString aTitle = pField->GetTitle();
+ if (!aTitle.isEmpty())
+ {
+ sText = aTitle;
+ }
+ else
+ {
+ sText = pField->GetPar1();
+ }
+ break;
+ }
+ case SwFieldIds::HiddenText:
+ sText = pField->GetPar1();
+ break;
+
+ case SwFieldIds::DocStat:
+ break;
+
+ case SwFieldIds::Macro:
+ sText = static_cast<const SwMacroField*>(pField)->GetMacro();
+ break;
+
+ case SwFieldIds::GetRef:
+ {
+ // #i85090#
+ const SwGetRefField* pRefField( dynamic_cast<const SwGetRefField*>(pField) );
+ OSL_ENSURE( pRefField,
+ "<SwEditWin::RequestHelp(..)> - unexpected type of <pField>" );
+ if ( pRefField )
+ {
+ if ( pRefField->IsRefToHeadingCrossRefBookmark() ||
+ pRefField->IsRefToNumItemCrossRefBookmark() )
+ {
+ sText = pRefField->GetExpandedTextOfReferencedTextNode(*rSh.GetLayout());
+ if ( sText.getLength() > 80 )
+ {
+ sText = OUString::Concat(sText.subView(0, 80)) + "...";
+ }
+ }
+ else
+ {
+ sText = pRefField->GetSetRefName();
+ }
+ }
+ break;
+ }
+ case SwFieldIds::TableOfAuthorities:
+ {
+ const auto pAuthorityField
+ = static_cast<const SwAuthorityField*>(pField);
+ sText = pAuthorityField->GetAuthority(aContentAtPos.pFndTextAttr,
+ rSh.GetLayout());
+ if (pAuthorityField->HasURL())
+ {
+ const OUString& rURL
+ = pAuthorityField->GetAuthEntry()->GetAuthorField(
+ AUTH_FIELD_URL);
+ sText += "\n" + SfxHelp::GetURLHelpText(rURL);
+ }
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ if( sText.isEmpty() )
+ {
+ const bool bShowTrackChanges = IDocumentRedlineAccess::IsShowChanges( m_rView.GetDocShell()->GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() );
+ const bool bShowInlineTooltips = rSh.GetViewOptions()->IsShowInlineTooltips();
+ if ( bShowTrackChanges && bShowInlineTooltips )
+ {
+ aContentAtPos.eContentAtPos = IsAttrAtPos::Redline;
+ if( rSh.GetContentAtPos( aPos, aContentAtPos, false, &aFieldRect ) )
+ sText = lcl_GetRedlineHelp(*aContentAtPos.aFnd.pRedl, bBalloon, /*bTableChange=*/false);
+ }
+ }
+ }
+ }
+ if (!sText.isEmpty())
+ {
+ tools::Rectangle aRect( aFieldRect.SVRect() );
+ Point aPt( OutputToScreenPixel( LogicToPixel( aRect.TopLeft() )));
+ aRect.SetLeft( aPt.X() );
+ aRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( LogicToPixel( aRect.BottomRight() ));
+ aRect.SetRight( aPt.X() );
+ aRect.SetBottom( aPt.Y() );
+
+ // tdf#136336 ensure tooltip area surrounds the current mouse position with at least a pixel margin
+ aRect.Union(tools::Rectangle(rEvt.GetMousePosPixel(), Size(1, 1)));
+ aRect.AdjustLeft(-1);
+ aRect.AdjustRight(1);
+ aRect.AdjustTop(-1);
+ aRect.AdjustBottom(1);
+
+ if( bBalloon )
+ Help::ShowBalloon( this, rEvt.GetMousePosPixel(), aRect, sText );
+ else
+ {
+ // the show the help
+ OUString sDisplayText(ClipLongToolTip(sText));
+ Help::ShowQuickHelp(this, aRect, sDisplayText, nStyle);
+ }
+ }
+
+ bContinue = false;
+ }
+
+ }
+
+ if( bContinue )
+ Window::RequestHelp( rEvt );
+}
+
+void SwEditWin::PrePaint(vcl::RenderContext& /*rRenderContext*/)
+{
+ SwWrtShell* pWrtShell = GetView().GetWrtShellPtr();
+
+ if(pWrtShell)
+ {
+ pWrtShell->PrePaint();
+ }
+}
+
+void SwEditWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ SwWrtShell* pWrtShell = GetView().GetWrtShellPtr();
+ if(!pWrtShell)
+ return;
+ bool bPaintShadowCursor = false;
+ if( m_pShadCursor )
+ {
+ tools::Rectangle aRect( m_pShadCursor->GetRect());
+ // fully resides inside?
+ if( rRect.Contains( aRect ) )
+ {
+ // then cancel
+ m_pShadCursor.reset();
+ }
+ else if( rRect.Overlaps( aRect ))
+ {
+ // resides somewhat above, then everything is clipped outside
+ // and we have to make the "inner part" at the end of the
+ // Paint visible again. Otherwise Paint errors occur!
+ bPaintShadowCursor = true;
+ }
+ }
+
+ if ( GetView().GetVisArea().GetWidth() <= 0 ||
+ GetView().GetVisArea().GetHeight() <= 0 )
+ Invalidate( rRect );
+ else
+ {
+ pWrtShell->setOutputToWindow(true);
+ bool bTiledPainting = false;
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting();
+ comphelper::LibreOfficeKit::setTiledPainting(true);
+ }
+ pWrtShell->Paint(rRenderContext, rRect);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
+ }
+ pWrtShell->setOutputToWindow(false);
+ }
+
+ if( bPaintShadowCursor )
+ m_pShadCursor->Paint();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/edtwin3.cxx b/sw/source/uibase/docvw/edtwin3.cxx
new file mode 100644
index 000000000..d417b08b0
--- /dev/null
+++ b/sw/source/uibase/docvw/edtwin3.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <svx/ruler.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <basesh.hxx>
+#include <pview.hxx>
+#include <mdiexp.hxx>
+#include <edtwin.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <docsh.hxx>
+#include <uiobject.hxx>
+
+// Core-Notify
+void ScrollMDI( SwViewShell const * pVwSh, const SwRect &rRect,
+ sal_uInt16 nRangeX, sal_uInt16 nRangeY)
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell))
+ pSwView->Scroll(rRect.SVRect(), nRangeX, nRangeY);
+}
+
+// Docmdi - movable
+bool IsScrollMDI( SwViewShell const * pVwSh, const SwRect &rRect )
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell))
+ return pSwView->IsScroll(rRect.SVRect());
+
+ return false;
+}
+
+// Notify for size change
+void SizeNotify(SwViewShell const * pVwSh, const Size &rSize)
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell))
+ pSwView->DocSzChgd(rSize);
+ else if (SwPagePreview* pSwPageView = dynamic_cast<SwPagePreview *>(pSfxViewShell))
+ pSwPageView->DocSzChgd(rSize);
+}
+
+// Notify for page number update
+void PageNumNotify(SwViewShell const * pVwSh)
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell))
+ {
+ if (pSwView->GetCurShell())
+ pSwView->UpdatePageNums();
+ }
+}
+
+void FrameNotify( SwViewShell* pVwSh, FlyMode eMode )
+{
+ if (SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell *>(pVwSh))
+ SwBaseShell::SetFrameMode(eMode, pWrtShell);
+}
+
+// Notify for page number update
+bool SwEditWin::RulerColumnDrag( const MouseEvent& rMEvt, bool bVerticalMode)
+{
+ SvxRuler& rRuler = bVerticalMode ? m_rView.GetVRuler() : m_rView.GetHRuler();
+ return (!rRuler.StartDocDrag( rMEvt, RulerType::Border ) &&
+ !rRuler.StartDocDrag( rMEvt, RulerType::Margin1) &&
+ !rRuler.StartDocDrag( rMEvt, RulerType::Margin2));
+}
+
+// #i23726#
+// #i42921# - add 3rd parameter <bVerticalMode> in order
+// to consider vertical layout
+bool SwEditWin::RulerMarginDrag( const MouseEvent& rMEvt,
+ const bool bVerticalMode )
+{
+ SvxRuler& rRuler = bVerticalMode ? m_rView.GetVRuler() : m_rView.GetHRuler();
+ return !rRuler.StartDocDrag( rMEvt, RulerType::Indent);
+}
+
+TableChgMode GetTableChgDefaultMode()
+{
+ SwModuleOptions* pOpt = SW_MOD()->GetModuleConfig();
+ return pOpt ? pOpt->GetTableMode() : TableChgMode::VarWidthChangeAbs;
+}
+
+void RepaintPagePreview( SwViewShell const * pVwSh, const SwRect& rRect )
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwPagePreview* pSwPagePreview = dynamic_cast<SwPagePreview *>(pSfxViewShell))
+ pSwPagePreview->RepaintCoreRect(rRect);
+}
+
+bool JumpToSwMark( SwViewShell const * pVwSh, std::u16string_view rMark )
+{
+ SfxViewShell *pSfxViewShell = pVwSh->GetSfxViewShell();
+
+ if (SwView* pSwView = dynamic_cast<SwView *>(pSfxViewShell))
+ return pSwView->JumpToSwMark(rMark);
+
+ return false;
+}
+
+void SwEditWin::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ SwWrtShell* pSh = GetView().GetWrtShellPtr();
+ // DataChanged() is sometimes called prior to creating
+ // the SwWrtShell
+ if(!pSh)
+ return;
+ bool bViewWasLocked = pSh->IsViewLocked(), bUnlockPaint = false;
+ pSh->LockView( true );
+ switch( rDCEvt.GetType() )
+ {
+ case DataChangedEventType::SETTINGS:
+ // rearrange ScrollBars, respectively trigger resize, because
+ // the ScrollBar size can have change. For that, in the reset
+ // handler, the size of the ScrollBars also has to be queried
+ // from the settings.
+ if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
+ {
+ pSh->LockPaint();
+ bUnlockPaint = true;
+ pSh->DeleteReplacementBitmaps();
+ GetView().InvalidateBorder(); //Scrollbar work
+ }
+ break;
+
+ case DataChangedEventType::PRINTER:
+ case DataChangedEventType::DISPLAY:
+ case DataChangedEventType::FONTS:
+ case DataChangedEventType::FONTSUBSTITUTION:
+ pSh->LockPaint();
+ bUnlockPaint = true;
+ GetView().GetDocShell()->UpdateFontList(); //e.g. printer change
+ pSh->InvalidateLayout(true);
+ break;
+ default: break;
+ }
+ pSh->LockView( bViewWasLocked );
+ if( bUnlockPaint )
+ pSh->UnlockPaint();
+}
+
+FactoryFunction SwEditWin::GetUITestFactory() const
+{
+ return SwEditWinUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/frmsidebarwincontainer.cxx b/sw/source/uibase/docvw/frmsidebarwincontainer.cxx
new file mode 100644
index 000000000..17da0beee
--- /dev/null
+++ b/sw/source/uibase/docvw/frmsidebarwincontainer.cxx
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "frmsidebarwincontainer.hxx"
+
+#include <map>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <AnnotationWin.hxx>
+
+namespace {
+ struct SidebarWinKey
+ {
+ const sal_Int32 mnIndex;
+
+ explicit SidebarWinKey( const sal_Int32 nIndex )
+ : mnIndex( nIndex )
+ {}
+
+ bool operator < ( const SidebarWinKey& rSidebarWinKey ) const
+ {
+ return mnIndex < rSidebarWinKey.mnIndex;
+ }
+ };
+
+ typedef std::map < SidebarWinKey, VclPtr<sw::annotation::SwAnnotationWin> > SidebarWinContainer;
+
+ struct FrameKey
+ {
+ const SwFrame* mpFrame;
+
+ explicit FrameKey( const SwFrame* pFrame )
+ : mpFrame( pFrame )
+ {}
+
+ bool operator < ( const FrameKey& rFrameKey ) const
+ {
+ return mpFrame < rFrameKey.mpFrame;
+ }
+ };
+
+ typedef std::map < FrameKey, SidebarWinContainer > FrameSidebarWinContainer_;
+}
+
+namespace sw::sidebarwindows {
+
+class FrameSidebarWinContainer : public FrameSidebarWinContainer_
+{
+};
+
+SwFrameSidebarWinContainer::SwFrameSidebarWinContainer()
+ : mpFrameSidebarWinContainer( new FrameSidebarWinContainer )
+{}
+
+SwFrameSidebarWinContainer::~SwFrameSidebarWinContainer()
+{
+ mpFrameSidebarWinContainer->clear();
+ mpFrameSidebarWinContainer.reset();
+}
+
+bool SwFrameSidebarWinContainer::insert( const SwFrame& rFrame,
+ const SwFormatField& rFormatField,
+ sw::annotation::SwAnnotationWin& rSidebarWin )
+{
+ bool bInserted( false );
+
+ FrameKey aFrameKey( &rFrame );
+ SidebarWinContainer& rSidebarWinContainer = (*mpFrameSidebarWinContainer)[ aFrameKey ];
+
+ SidebarWinKey aSidebarWinKey( rFormatField.GetTextField()->GetStart() );
+ if ( rSidebarWinContainer.empty() ||
+ rSidebarWinContainer.find( aSidebarWinKey) == rSidebarWinContainer.end() )
+ {
+ rSidebarWinContainer[ aSidebarWinKey ] = &rSidebarWin;
+ bInserted = true;
+ }
+
+ return bInserted;
+}
+
+bool SwFrameSidebarWinContainer::remove( const SwFrame& rFrame,
+ const sw::annotation::SwAnnotationWin & rSidebarWin )
+{
+ bool bRemoved( false );
+
+ FrameKey aFrameKey( &rFrame );
+ FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey );
+ if ( aFrameIter != mpFrameSidebarWinContainer->end() )
+ {
+ SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second;
+ auto aIter = std::find_if(rSidebarWinContainer.begin(), rSidebarWinContainer.end(),
+ [&rSidebarWin](const SidebarWinContainer::value_type& rEntry) { return rEntry.second == &rSidebarWin; });
+ if ( aIter != rSidebarWinContainer.end() )
+ {
+ rSidebarWinContainer.erase( aIter );
+ bRemoved = true;
+ }
+ }
+
+ return bRemoved;
+}
+
+bool SwFrameSidebarWinContainer::empty( const SwFrame& rFrame )
+{
+ bool bEmpty( true );
+
+ FrameKey aFrameKey( &rFrame );
+ FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey );
+ if ( aFrameIter != mpFrameSidebarWinContainer->end() )
+ {
+ bEmpty = (*aFrameIter).second.empty();
+ }
+
+ return bEmpty;
+}
+
+sw::annotation::SwAnnotationWin* SwFrameSidebarWinContainer::get( const SwFrame& rFrame,
+ const sal_Int32 nIndex )
+{
+ sw::annotation::SwAnnotationWin* pRet( nullptr );
+
+ FrameKey aFrameKey( &rFrame );
+ FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey );
+ if ( aFrameIter != mpFrameSidebarWinContainer->end() && nIndex >= 0 )
+ {
+ SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second;
+ if (nIndex < sal_Int32(rSidebarWinContainer.size()))
+ {
+ auto aIter = rSidebarWinContainer.begin();
+ std::advance(aIter, nIndex);
+ pRet = (*aIter).second;
+ }
+ }
+ return pRet;
+}
+
+void SwFrameSidebarWinContainer::getAll( const SwFrame& rFrame,
+ std::vector< vcl::Window* >* pSidebarWins )
+{
+ pSidebarWins->clear();
+
+ FrameKey aFrameKey( &rFrame );
+ FrameSidebarWinContainer::iterator aFrameIter = mpFrameSidebarWinContainer->find( aFrameKey );
+ if ( aFrameIter != mpFrameSidebarWinContainer->end() )
+ {
+ SidebarWinContainer& rSidebarWinContainer = (*aFrameIter).second;
+ for ( const auto& rEntry : rSidebarWinContainer )
+ {
+ pSidebarWins->push_back( rEntry.second );
+ }
+ }
+}
+
+} // eof of namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/frmsidebarwincontainer.hxx b/sw/source/uibase/docvw/frmsidebarwincontainer.hxx
new file mode 100644
index 000000000..33ae9a9d4
--- /dev/null
+++ b/sw/source/uibase/docvw/frmsidebarwincontainer.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <memory>
+#include <vector>
+
+class SwFrame;
+class SwFormatField;
+namespace vcl { class Window; }
+namespace sw::annotation { class SwAnnotationWin; }
+
+namespace sw::sidebarwindows {
+
+class FrameSidebarWinContainer;
+
+class SwFrameSidebarWinContainer
+{
+ public:
+ SwFrameSidebarWinContainer();
+ ~SwFrameSidebarWinContainer();
+
+ bool insert( const SwFrame& rFrame,
+ const SwFormatField& rFormatField,
+ sw::annotation::SwAnnotationWin& rSidebarWin );
+
+ bool remove( const SwFrame& rFrame,
+ const sw::annotation::SwAnnotationWin& rSidebarWin );
+
+ bool empty( const SwFrame& rFrame );
+
+ sw::annotation::SwAnnotationWin* get( const SwFrame& rFrame,
+ const sal_Int32 nIndex );
+
+ void getAll( const SwFrame& rFrame,
+ std::vector< vcl::Window* >* pSidebarWins );
+
+ private:
+ std::unique_ptr<FrameSidebarWinContainer> mpFrameSidebarWinContainer;
+};
+
+} // namespace sw::sidebarwindows
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/romenu.cxx b/sw/source/uibase/docvw/romenu.cxx
new file mode 100644
index 000000000..a9319e70d
--- /dev/null
+++ b/sw/source/uibase/docvw/romenu.cxx
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+
+#include <svl/eitem.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/transfer.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svx/gallery.hxx>
+#include <svx/graphichelper.hxx>
+#include <editeng/brushitem.hxx>
+
+#include <fmtinfmt.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <viewopt.hxx>
+#include <swmodule.hxx>
+#include "romenu.hxx"
+#include <pagedesc.hxx>
+#include <modcfg.hxx>
+
+#include <cmdid.h>
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::sfx2;
+
+SwReadOnlyPopup::~SwReadOnlyPopup()
+{
+ m_xMenu.disposeAndClear();
+}
+
+void SwReadOnlyPopup::Check( sal_uInt16 nMID, sal_uInt16 nSID, SfxDispatcher const &rDis )
+{
+ std::unique_ptr<SfxPoolItem> _pItem;
+ SfxItemState eState = rDis.GetBindings()->QueryState( nSID, _pItem );
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ m_xMenu->EnableItem(nMID);
+ if (_pItem)
+ {
+ m_xMenu->CheckItem(nMID, !_pItem->IsVoidItem() &&
+ dynamic_cast< const SfxBoolItem *>( _pItem.get() ) != nullptr &&
+ static_cast<SfxBoolItem*>(_pItem.get())->GetValue());
+ //remove full screen entry when not in full screen mode
+ if (SID_WIN_FULLSCREEN == nSID && !m_xMenu->IsItemChecked(m_nReadonlyFullscreen))
+ m_xMenu->EnableItem(nMID, false);
+ }
+ }
+ else
+ m_xMenu->EnableItem(nMID, false);
+}
+
+#define MN_READONLY_GRAPHICTOGALLERY 1000
+#define MN_READONLY_BACKGROUNDTOGALLERY 2000
+
+SwReadOnlyPopup::SwReadOnlyPopup(const Point &rDPos, SwView &rV)
+ : m_aBuilder(nullptr, AllSettings::GetUIRootDir(), "modules/swriter/ui/readonlymenu.ui", "")
+ , m_xMenu(m_aBuilder.get_menu("menu"))
+ , m_nReadonlyOpenurl(m_xMenu->GetItemId("openurl"))
+ , m_nReadonlyOpendoc(m_xMenu->GetItemId("opendoc"))
+ , m_nReadonlyEditdoc(m_xMenu->GetItemId("edit"))
+ , m_nReadonlySelectionMode(m_xMenu->GetItemId("selection"))
+ , m_nReadonlyReload(m_xMenu->GetItemId("reload"))
+ , m_nReadonlyReloadFrame(m_xMenu->GetItemId("reloadframe"))
+ , m_nReadonlySourceview(m_xMenu->GetItemId("html"))
+ , m_nReadonlyBrowseBackward(m_xMenu->GetItemId("backward"))
+ , m_nReadonlyBrowseForward(m_xMenu->GetItemId("forward"))
+ , m_nReadonlySaveGraphic(m_xMenu->GetItemId("savegraphic"))
+ , m_nReadonlyGraphictogallery(m_xMenu->GetItemId("graphictogallery"))
+ , m_nReadonlyTogallerylink(m_xMenu->GetItemId("graphicaslink"))
+ , m_nReadonlyTogallerycopy(m_xMenu->GetItemId("graphicascopy"))
+ , m_nReadonlySaveBackground(m_xMenu->GetItemId("savebackground"))
+ , m_nReadonlyBackgroundtogallery(m_xMenu->GetItemId("backgroundtogallery"))
+ , m_nReadonlyBackgroundTogallerylink(m_xMenu->GetItemId("backaslink"))
+ , m_nReadonlyBackgroundTogallerycopy(m_xMenu->GetItemId("backascopy"))
+ , m_nReadonlyCopylink(m_xMenu->GetItemId("copylink"))
+ , m_nReadonlyLoadGraphic(m_xMenu->GetItemId("loadgraphic"))
+ , m_nReadonlyGraphicoff(m_xMenu->GetItemId("imagesoff"))
+ , m_nReadonlyFullscreen(m_xMenu->GetItemId("fullscreen"))
+ , m_nReadonlyCopy(m_xMenu->GetItemId("copy"))
+ , m_rView(rV)
+ , m_xBrushItem(std::make_unique<SvxBrushItem>(RES_BACKGROUND))
+{
+ m_bGrfToGalleryAsLnk = SW_MOD()->GetModuleConfig()->IsGrfToGalleryAsLnk();
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ OUString sDescription;
+ rSh.IsURLGrfAtPos( rDPos, &m_sURL, &m_sTargetFrameName, &sDescription );
+ if ( m_sURL.isEmpty() )
+ {
+ SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr );
+ if( rSh.GetContentAtPos( rDPos, aContentAtPos))
+ {
+ const SwFormatINetFormat &rIItem = *static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr);
+ m_sURL = rIItem.GetValue();
+ m_sTargetFrameName = rIItem.GetTargetFrame();
+ }
+ }
+
+ bool bLink = false;
+ const Graphic *pGrf = rSh.GetGrfAtPos( rDPos, m_sGrfName, bLink );
+ if ( nullptr == pGrf )
+ {
+ m_xMenu->EnableItem(m_nReadonlySaveGraphic, false);
+ }
+ else
+ {
+ m_aGraphic = *pGrf;
+ }
+
+ bool bEnableGraphicToGallery = bLink;
+ if ( bEnableGraphicToGallery )
+ {
+ if (GalleryExplorer::FillThemeList( m_aThemeList ))
+ {
+ PopupMenu *pMenu = m_xMenu->GetPopupMenu(m_nReadonlyGraphictogallery);
+ pMenu->CheckItem(m_nReadonlyTogallerylink, m_bGrfToGalleryAsLnk);
+ pMenu->CheckItem(m_nReadonlyTogallerycopy, !m_bGrfToGalleryAsLnk);
+
+ for ( size_t i=0; i < m_aThemeList.size(); ++i )
+ pMenu->InsertItem(MN_READONLY_GRAPHICTOGALLERY + i, m_aThemeList[i]);
+ }
+ else
+ bEnableGraphicToGallery = false;
+ }
+
+ m_xMenu->EnableItem(m_nReadonlyGraphictogallery, bEnableGraphicToGallery);
+
+ SfxViewFrame * pVFrame = rV.GetViewFrame();
+ SfxDispatcher &rDis = *pVFrame->GetDispatcher();
+ const SwPageDesc &rDesc = rSh.GetPageDesc( rSh.GetCurPageDesc() );
+ m_xBrushItem = rDesc.GetMaster().makeBackgroundBrushItem();
+ bool bEnableBackGallery = false,
+ bEnableBack = false;
+
+ if ( m_xBrushItem && GPOS_NONE != m_xBrushItem->GetGraphicPos() )
+ {
+ bEnableBack = true;
+ if ( !m_xBrushItem->GetGraphicLink().isEmpty() )
+ {
+ if ( m_aThemeList.empty() )
+ GalleryExplorer::FillThemeList( m_aThemeList );
+
+ if ( !m_aThemeList.empty() )
+ {
+ PopupMenu *pMenu = m_xMenu->GetPopupMenu(m_nReadonlyBackgroundtogallery);
+ pMenu->CheckItem(m_nReadonlyBackgroundTogallerylink, m_bGrfToGalleryAsLnk);
+ pMenu->CheckItem(m_nReadonlyBackgroundTogallerycopy, !m_bGrfToGalleryAsLnk);
+ bEnableBackGallery = true;
+
+ for ( size_t i=0; i < m_aThemeList.size(); ++i )
+ pMenu->InsertItem(MN_READONLY_BACKGROUNDTOGALLERY + i, m_aThemeList[i]);
+ }
+ }
+ }
+ m_xMenu->EnableItem(m_nReadonlySaveBackground, bEnableBack);
+ m_xMenu->EnableItem(m_nReadonlyBackgroundtogallery, bEnableBackGallery);
+
+ if ( !rSh.GetViewOptions()->IsGraphic() )
+ m_xMenu->CheckItem(m_nReadonlyGraphicoff);
+ else
+ m_xMenu->EnableItem(m_nReadonlyLoadGraphic, false);
+
+ m_xMenu->EnableItem(m_nReadonlyReloadFrame, false);
+ m_xMenu->EnableItem(m_nReadonlyReload);
+
+ Check(m_nReadonlyEditdoc, SID_EDITDOC, rDis);
+ Check(m_nReadonlySelectionMode, FN_READONLY_SELECTION_MODE, rDis);
+ Check(m_nReadonlySourceview, SID_SOURCEVIEW, rDis);
+ Check(m_nReadonlyBrowseBackward, SID_BROWSE_BACKWARD, rDis);
+ Check(m_nReadonlyBrowseForward,SID_BROWSE_FORWARD, rDis);
+ Check(m_nReadonlyOpenurl, SID_OPENDOC, rDis);
+ Check(m_nReadonlyOpendoc, SID_OPENDOC, rDis);
+
+ std::unique_ptr<SfxPoolItem> pState;
+
+ SfxItemState eState = pVFrame->GetBindings().QueryState( SID_COPY, pState );
+ Check(m_nReadonlyCopy, SID_COPY, rDis);
+ if (eState < SfxItemState::DEFAULT)
+ m_xMenu->EnableItem(m_nReadonlyCopy, false);
+
+ eState = pVFrame->GetBindings().QueryState( SID_EDITDOC, pState );
+ if (
+ eState < SfxItemState::DEFAULT ||
+ (rSh.IsGlobalDoc() && m_rView.GetDocShell()->IsReadOnlyUI())
+ )
+ {
+ m_xMenu->EnableItem(m_nReadonlyEditdoc, false);
+ }
+
+ if ( m_sURL.isEmpty() )
+ {
+ m_xMenu->EnableItem(m_nReadonlyOpenurl, false);
+ m_xMenu->EnableItem(m_nReadonlyOpendoc, false);
+ m_xMenu->EnableItem(m_nReadonlyCopylink, false);
+ }
+ Check(m_nReadonlyFullscreen, SID_WIN_FULLSCREEN, rDis);
+
+ m_xMenu->RemoveDisabledEntries( true );
+}
+
+void SwReadOnlyPopup::Execute( vcl::Window* pWin, const Point &rPixPos )
+{
+ sal_uInt16 nId = m_xMenu->Execute(pWin, rPixPos);
+ Execute(pWin, nId);
+}
+
+// execute the resulting ID only - necessary to support XContextMenuInterception
+void SwReadOnlyPopup::Execute( vcl::Window* pWin, sal_uInt16 nId )
+{
+ SwWrtShell &rSh = m_rView.GetWrtShell();
+ SfxDispatcher &rDis = *m_rView.GetViewFrame()->GetDispatcher();
+ if (nId >= MN_READONLY_GRAPHICTOGALLERY)
+ {
+ OUString sTmp;
+ sal_uInt16 nSaveId;
+ if (m_xBrushItem && nId >= MN_READONLY_BACKGROUNDTOGALLERY)
+ {
+ nId -= MN_READONLY_BACKGROUNDTOGALLERY;
+ nSaveId = m_nReadonlySaveBackground;
+ sTmp = m_xBrushItem->GetGraphicLink();
+ }
+ else
+ {
+ nId -= MN_READONLY_GRAPHICTOGALLERY;
+ nSaveId = m_nReadonlySaveGraphic;
+ sTmp = m_sGrfName;
+ }
+ if ( !m_bGrfToGalleryAsLnk )
+ sTmp = SaveGraphic(nSaveId);
+
+ if ( !sTmp.isEmpty() )
+ GalleryExplorer::InsertURL( m_aThemeList[nId], sTmp );
+
+ return;
+ }
+
+ rtl::Reference<TransferDataContainer> pClipCntnr;
+
+ sal_uInt16 nExecId = USHRT_MAX;
+ bool bFilterSet = false;
+ LoadUrlFlags nFilter = LoadUrlFlags::NONE;
+ if (nId == m_nReadonlyFullscreen)
+ nExecId = SID_WIN_FULLSCREEN;
+ else if (nId == m_nReadonlyOpenurl)
+ {
+ nFilter = LoadUrlFlags::NONE;
+ bFilterSet = true;
+ }
+ else if (nId == m_nReadonlyOpendoc)
+ {
+ nFilter = LoadUrlFlags::NewView;
+ bFilterSet = true;
+ }
+ else if (nId == m_nReadonlyCopy)
+ nExecId = SID_COPY;
+ else if (nId == m_nReadonlyEditdoc)
+ nExecId = SID_EDITDOC;
+ else if (nId == m_nReadonlySelectionMode)
+ nExecId = FN_READONLY_SELECTION_MODE;
+ else if (nId == m_nReadonlyReload || nId == m_nReadonlyReloadFrame)
+ rSh.GetView().GetViewFrame()->GetDispatcher()->Execute(SID_RELOAD);
+ else if (nId == m_nReadonlyBrowseBackward)
+ nExecId = SID_BROWSE_BACKWARD;
+ else if (nId == m_nReadonlyBrowseForward)
+ nExecId = SID_BROWSE_FORWARD;
+ else if (nId == m_nReadonlySourceview)
+ nExecId = SID_SOURCEVIEW;
+ else if (nId == m_nReadonlySaveGraphic || nId == m_nReadonlySaveBackground)
+ SaveGraphic(nId);
+ else if (nId == m_nReadonlyCopylink)
+ {
+ pClipCntnr = new TransferDataContainer;
+ pClipCntnr->CopyString( m_sURL );
+ }
+ else if (nId == m_nReadonlyLoadGraphic)
+ {
+ bool bModified = rSh.IsModified();
+ SwViewOption aOpt( *rSh.GetViewOptions() );
+ aOpt.SetGraphic( true );
+ rSh.ApplyViewOptions( aOpt );
+ if(!bModified)
+ rSh.ResetModified();
+ }
+ else if (nId == m_nReadonlyGraphicoff)
+ nExecId = FN_VIEW_GRAPHIC;
+ else if (nId == m_nReadonlyTogallerylink || nId == m_nReadonlyBackgroundTogallerylink)
+ SW_MOD()->GetModuleConfig()->SetGrfToGalleryAsLnk(true);
+ else if (nId == m_nReadonlyTogallerycopy || nId == m_nReadonlyBackgroundTogallerycopy)
+ SW_MOD()->GetModuleConfig()->SetGrfToGalleryAsLnk(false);
+
+ if( USHRT_MAX != nExecId )
+ rDis.GetBindings()->Execute( nExecId );
+ if( bFilterSet )
+ ::LoadURL(rSh, m_sURL, nFilter, m_sTargetFrameName);
+
+ if( pClipCntnr && pClipCntnr->HasAnyData() )
+ {
+ pClipCntnr->CopyToClipboard( pWin );
+ }
+}
+
+OUString SwReadOnlyPopup::SaveGraphic(sal_uInt16 nId)
+{
+ // fish out the graphic's name
+ if (nId == m_nReadonlySaveBackground)
+ {
+ if ( m_xBrushItem && !m_xBrushItem->GetGraphicLink().isEmpty() )
+ m_sGrfName = m_xBrushItem->GetGraphicLink();
+ const Graphic *pGrf = m_xBrushItem ? m_xBrushItem->GetGraphic() : nullptr;
+ if ( pGrf )
+ {
+ m_aGraphic = *pGrf;
+ if ( !m_xBrushItem->GetGraphicLink().isEmpty() )
+ m_sGrfName = m_xBrushItem->GetGraphicLink();
+ }
+ else
+ return OUString();
+ }
+ return GraphicHelper::ExportGraphic(m_rView.GetFrameWeld(), m_aGraphic, m_sGrfName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/romenu.hxx b/sw/source/uibase/docvw/romenu.hxx
new file mode 100644
index 000000000..8961b93d7
--- /dev/null
+++ b/sw/source/uibase/docvw/romenu.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/brushitem.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/menu.hxx>
+
+class SwView;
+class SfxDispatcher;
+class ImageMap;
+class INetImage;
+
+class SwReadOnlyPopup
+{
+ VclBuilder m_aBuilder;
+ ScopedVclPtr<PopupMenu> m_xMenu;
+ sal_uInt16 m_nReadonlyOpenurl;
+ sal_uInt16 m_nReadonlyOpendoc;
+ sal_uInt16 m_nReadonlyEditdoc;
+ sal_uInt16 m_nReadonlySelectionMode;
+ sal_uInt16 m_nReadonlyReload;
+ sal_uInt16 m_nReadonlyReloadFrame;
+ sal_uInt16 m_nReadonlySourceview;
+ sal_uInt16 m_nReadonlyBrowseBackward;
+ sal_uInt16 m_nReadonlyBrowseForward;
+ sal_uInt16 m_nReadonlySaveGraphic;
+ sal_uInt16 m_nReadonlyGraphictogallery;
+ sal_uInt16 m_nReadonlyTogallerylink;
+ sal_uInt16 m_nReadonlyTogallerycopy;
+ sal_uInt16 m_nReadonlySaveBackground;
+ sal_uInt16 m_nReadonlyBackgroundtogallery;
+ sal_uInt16 m_nReadonlyBackgroundTogallerylink;
+ sal_uInt16 m_nReadonlyBackgroundTogallerycopy;
+ sal_uInt16 m_nReadonlyCopylink;
+ sal_uInt16 m_nReadonlyLoadGraphic;
+ sal_uInt16 m_nReadonlyGraphicoff;
+ sal_uInt16 m_nReadonlyFullscreen;
+ sal_uInt16 m_nReadonlyCopy;
+
+ SwView & m_rView;
+ std::unique_ptr<SvxBrushItem> m_xBrushItem;
+ Graphic m_aGraphic;
+ OUString m_sURL,
+ m_sTargetFrameName;
+ OUString m_sGrfName;
+ std::vector<OUString> m_aThemeList;
+ bool m_bGrfToGalleryAsLnk;
+
+ void Check( sal_uInt16 nMID, sal_uInt16 nSID, SfxDispatcher const &rDis );
+ OUString SaveGraphic( sal_uInt16 nId );
+
+public:
+ SwReadOnlyPopup(const Point &rDPos, SwView &rV);
+ css::uno::Reference<css::awt::XPopupMenu> CreateMenuInterface() { return m_xMenu->CreateMenuInterface(); }
+ ~SwReadOnlyPopup();
+
+ void Execute( vcl::Window* pWin, const Point &rPPos );
+ void Execute( vcl::Window* pWin, sal_uInt16 nId );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/docvw/srcedtw.cxx b/sw/source/uibase/docvw/srcedtw.cxx
new file mode 100644
index 000000000..cbd0f132a
--- /dev/null
+++ b/sw/source/uibase/docvw/srcedtw.cxx
@@ -0,0 +1,984 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <hintids.hxx>
+#include <cmdid.h>
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/textview.hxx>
+#include <vcl/scrbar.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/htmltokn.h>
+#include <vcl/txtattr.hxx>
+#include <vcl/settings.hxx>
+#include <svtools/colorcfg.hxx>
+#include <editeng/flstitem.hxx>
+#include <vcl/metric.hxx>
+#include <svtools/ctrltool.hxx>
+#include <tools/time.hxx>
+#include <swmodule.hxx>
+#include <docsh.hxx>
+#include <srcview.hxx>
+#include <helpids.h>
+#include <vector>
+
+namespace
+{
+
+struct TextPortion
+{
+ sal_uInt16 nStart, nEnd;
+ svtools::ColorConfigEntry eType;
+};
+
+}
+
+#define MAX_SYNTAX_HIGHLIGHT 20
+#define MAX_HIGHLIGHTTIME 200
+
+typedef std::vector<TextPortion> TextPortions;
+
+static void lcl_Highlight(const OUString& rSource, TextPortions& aPortionList)
+{
+ const sal_Unicode cOpenBracket = '<';
+ const sal_Unicode cCloseBracket= '>';
+ const sal_Unicode cSlash = '/';
+ const sal_Unicode cExclamation = '!';
+ const sal_Unicode cMinus = '-';
+ const sal_Unicode cSpace = ' ';
+ const sal_Unicode cTab = 0x09;
+ const sal_Unicode cLF = 0x0a;
+ const sal_Unicode cCR = 0x0d;
+
+ const sal_uInt16 nStrLen = rSource.getLength();
+ sal_uInt16 nInsert = 0; // number of inserted portions
+ sal_uInt16 nActPos = 0; // position, where '<' was found
+ sal_uInt16 nPortStart = USHRT_MAX; // for the TextPortion
+ sal_uInt16 nPortEnd = 0;
+ TextPortion aText;
+ while(nActPos < nStrLen)
+ {
+ if((nActPos < nStrLen - 2) && (rSource[nActPos] == cOpenBracket))
+ {
+ svtools::ColorConfigEntry eFoundType = svtools::HTMLUNKNOWN;
+ // insert 'empty' portion
+ if(nPortEnd < nActPos - 1 )
+ {
+ // don't move at the beginning
+ aText.nStart = nPortEnd;
+ if(nInsert)
+ aText.nStart += 1;
+ aText.nEnd = nActPos - 1;
+ aText.eType = svtools::HTMLUNKNOWN;
+ aPortionList.push_back( aText );
+ nInsert++;
+ }
+ sal_Unicode cFollowFirst = rSource[nActPos + 1];
+ sal_Unicode cFollowNext = rSource[nActPos + 2];
+ if(cExclamation == cFollowFirst)
+ {
+ // "<!" SGML or comment
+ if(cMinus == cFollowNext &&
+ nActPos < nStrLen - 3 && cMinus == rSource[nActPos + 3])
+ {
+ eFoundType = svtools::HTMLCOMMENT;
+ }
+ else
+ eFoundType = svtools::HTMLSGML;
+ nPortStart = nActPos;
+ nPortEnd = nActPos + 1;
+ }
+ else if(cSlash == cFollowFirst)
+ {
+ // "</" ignore slash
+ nPortStart = nActPos;
+ nActPos++;
+ }
+ if(svtools::HTMLUNKNOWN == eFoundType)
+ {
+ // now here a keyword could follow
+ sal_uInt16 nSrchPos = nActPos;
+ while(++nSrchPos < nStrLen - 1)
+ {
+ sal_Unicode cNext = rSource[nSrchPos];
+ if( cNext == cSpace ||
+ cNext == cTab ||
+ cNext == cLF ||
+ cNext == cCR)
+ break;
+ else if(cNext == cCloseBracket)
+ {
+ break;
+ }
+ }
+ if(nSrchPos > nActPos + 1)
+ {
+ // some string was found
+ OUString sToken = rSource.copy(nActPos + 1, nSrchPos - nActPos - 1 );
+ sToken = sToken.toAsciiUpperCase();
+ HtmlTokenId nToken = ::GetHTMLToken(sToken);
+ if(nToken != HtmlTokenId::NONE)
+ {
+ eFoundType = svtools::HTMLKEYWORD;
+ nPortEnd = nSrchPos;
+ nPortStart = nActPos;
+ }
+ else
+ SAL_WARN("sw", "HTML token " << sToken << " not recognised!");
+ }
+ }
+ // now we still have to look for '>'
+ if(svtools::HTMLUNKNOWN != eFoundType)
+ {
+ bool bFound = false;
+ for(sal_uInt16 i = nPortEnd; i < nStrLen; i++)
+ if(cCloseBracket == rSource[i])
+ {
+ bFound = true;
+ nPortEnd = i;
+ break;
+ }
+ if(!bFound && (eFoundType == svtools::HTMLCOMMENT))
+ {
+ // comment without ending in this line
+ bFound = true;
+ nPortEnd = nStrLen - 1;
+ }
+
+ if(bFound ||(eFoundType == svtools::HTMLCOMMENT))
+ {
+ TextPortion aTextPortion;
+ aTextPortion.nStart = nPortStart + 1;
+ aTextPortion.nEnd = nPortEnd;
+ aTextPortion.eType = eFoundType;
+ aPortionList.push_back( aTextPortion );
+ nInsert++;
+ }
+
+ }
+ }
+ nActPos++;
+ }
+ if(nInsert && nPortEnd < nActPos - 1)
+ {
+ aText.nStart = nPortEnd + 1;
+ aText.nEnd = nActPos - 1;
+ aText.eType = svtools::HTMLUNKNOWN;
+ aPortionList.push_back( aText );
+ nInsert++;
+ }
+}
+
+class SwSrcEditWindow::ChangesListener:
+ public cppu::WeakImplHelper< css::beans::XPropertiesChangeListener >
+{
+public:
+ explicit ChangesListener(SwSrcEditWindow & editor): m_Editor(editor) {}
+
+private:
+ virtual ~ChangesListener() override {}
+
+ virtual void SAL_CALL disposing(css::lang::EventObject const &) override
+ {
+ std::unique_lock g(m_Editor.mutex_);
+ m_Editor.m_xNotifier.clear();
+ }
+
+ virtual void SAL_CALL propertiesChange(
+ css::uno::Sequence< css::beans::PropertyChangeEvent > const &) override
+ {
+ SolarMutexGuard g;
+ m_Editor.SetFont();
+ }
+
+ SwSrcEditWindow & m_Editor;
+};
+
+SwSrcEditWindow::SwSrcEditWindow( vcl::Window* pParent, SwSrcView* pParentView ) :
+ Window( pParent, WB_BORDER|WB_CLIPCHILDREN ),
+
+ m_pOutWin(nullptr),
+ m_pHScrollbar(nullptr),
+ m_pVScrollbar(nullptr),
+
+ m_pSrcView(pParentView),
+
+ m_nCurTextWidth(0),
+ m_nStartLine(USHRT_MAX),
+ m_eSourceEncoding(osl_getThreadTextEncoding()),
+ m_bReadonly(false),
+ m_bHighlighting(false),
+ m_aSyntaxIdle("sw uibase SwSrcEditWindow Syntax")
+{
+ SetHelpId(HID_SOURCE_EDITWIN);
+ CreateTextEngine();
+
+ // Using "this" in ctor is a little fishy, but should work here at least as
+ // long as there are no derivations:
+ m_xListener = new ChangesListener(*this);
+ css::uno::Reference< css::beans::XMultiPropertySet > n(
+ officecfg::Office::Common::Font::SourceViewFont::get(),
+ css::uno::UNO_QUERY_THROW);
+ {
+ std::unique_lock g(mutex_);
+ m_xNotifier = n;
+ }
+ n->addPropertiesChangeListener({ "FontHeight", "FontName" }, m_xListener);
+}
+
+SwSrcEditWindow::~SwSrcEditWindow()
+{
+ disposeOnce();
+}
+
+void SwSrcEditWindow::dispose()
+{
+ css::uno::Reference< css::beans::XMultiPropertySet > n;
+ {
+ std::unique_lock g(mutex_);
+ n = m_xNotifier;
+ }
+ if (n.is()) {
+ n->removePropertiesChangeListener(m_xListener);
+ }
+ m_aSyntaxIdle.Stop();
+ if ( m_pOutWin )
+ m_pOutWin->SetTextView( nullptr );
+
+ if ( m_pTextEngine )
+ {
+ EndListening( *m_pTextEngine );
+ m_pTextEngine->RemoveView( m_pTextView.get() );
+
+ m_pTextView.reset();
+ m_pTextEngine.reset();
+ }
+ m_pHScrollbar.disposeAndClear();
+ m_pVScrollbar.disposeAndClear();
+ m_pOutWin.disposeAndClear();
+ vcl::Window::dispose();
+}
+
+void SwSrcEditWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ switch ( rDCEvt.GetType() )
+ {
+ case DataChangedEventType::SETTINGS:
+ // newly rearrange ScrollBars or trigger Resize, because
+ // ScrollBar size could have changed. For this, in the
+ // Resize handler the size of ScrollBars has to be queried
+ // from the settings as well.
+ if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
+ Resize();
+ break;
+ default: break;
+ }
+}
+
+void SwSrcEditWindow::Resize()
+{
+ // ScrollBars, etc. happens in Adjust...
+ if ( !m_pTextView )
+ return;
+
+ tools::Long nVisY = m_pTextView->GetStartDocPos().Y();
+ m_pTextView->ShowCursor();
+ Size aOutSz( GetOutputSizePixel() );
+ tools::Long nMaxVisAreaStart = m_pTextView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
+ if ( nMaxVisAreaStart < 0 )
+ nMaxVisAreaStart = 0;
+ if ( m_pTextView->GetStartDocPos().Y() > nMaxVisAreaStart )
+ {
+ Point aStartDocPos( m_pTextView->GetStartDocPos() );
+ aStartDocPos.setY( nMaxVisAreaStart );
+ m_pTextView->SetStartDocPos( aStartDocPos );
+ m_pTextView->ShowCursor();
+ }
+ tools::Long nScrollStd = GetSettings().GetStyleSettings().GetScrollBarSize();
+ Size aScrollSz(aOutSz.Width() - nScrollStd, nScrollStd );
+ Point aScrollPos(0, aOutSz.Height() - nScrollStd);
+
+ m_pHScrollbar->SetPosSizePixel( aScrollPos, aScrollSz);
+
+ aScrollSz.setWidth( aScrollSz.Height() );
+ aScrollSz.setHeight( aOutSz.Height() );
+ aScrollPos = Point(aOutSz.Width() - nScrollStd, 0);
+
+ m_pVScrollbar->SetPosSizePixel( aScrollPos, aScrollSz);
+ aOutSz.AdjustWidth( -nScrollStd );
+ aOutSz.AdjustHeight( -nScrollStd );
+ m_pOutWin->SetOutputSizePixel(aOutSz);
+ InitScrollBars();
+
+ // set line in first Resize
+ if(USHRT_MAX != m_nStartLine)
+ {
+ if(m_nStartLine < m_pTextEngine->GetParagraphCount())
+ {
+ TextSelection aSel(TextPaM( m_nStartLine, 0 ), TextPaM( m_nStartLine, 0x0 ));
+ m_pTextView->SetSelection(aSel);
+ m_pTextView->ShowCursor();
+ }
+ m_nStartLine = USHRT_MAX;
+ }
+
+ if ( nVisY != m_pTextView->GetStartDocPos().Y() )
+ Invalidate();
+
+
+}
+
+void TextViewOutWin::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ switch( rDCEvt.GetType() )
+ {
+ case DataChangedEventType::SETTINGS:
+ // query settings
+ if( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
+ {
+ const Color &rCol = GetSettings().GetStyleSettings().GetWindowColor();
+ SetBackground( rCol );
+ vcl::Font aFont( m_pTextView->GetTextEngine()->GetFont() );
+ aFont.SetFillColor( rCol );
+ m_pTextView->GetTextEngine()->SetFont( aFont );
+ }
+ break;
+ default: break;
+ }
+}
+
+void TextViewOutWin::MouseMove( const MouseEvent &rEvt )
+{
+ if ( m_pTextView )
+ m_pTextView->MouseMove( rEvt );
+}
+
+void TextViewOutWin::MouseButtonUp( const MouseEvent &rEvt )
+{
+ if ( m_pTextView )
+ {
+ m_pTextView->MouseButtonUp( rEvt );
+ SfxViewFrame *pFrame = static_cast<SwSrcEditWindow*>(GetParent())->GetSrcView()->GetViewFrame();
+ if ( pFrame )
+ {
+ SfxBindings& rBindings = pFrame->GetBindings();
+ rBindings.Invalidate( SID_TABLE_CELL );
+ rBindings.Invalidate( SID_CUT );
+ rBindings.Invalidate( SID_COPY );
+ }
+ }
+}
+
+void TextViewOutWin::MouseButtonDown( const MouseEvent &rEvt )
+{
+ GrabFocus();
+ if ( m_pTextView )
+ m_pTextView->MouseButtonDown( rEvt );
+}
+
+void TextViewOutWin::Command( const CommandEvent& rCEvt )
+{
+ switch(rCEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ SfxDispatcher::ExecutePopup();
+ break;
+ case CommandEventId::Wheel:
+ case CommandEventId::StartAutoScroll:
+ case CommandEventId::AutoScroll:
+ {
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if( !pWData || CommandWheelMode::ZOOM != pWData->GetMode() )
+ {
+ static_cast<SwSrcEditWindow*>(GetParent())->HandleWheelCommand( rCEvt );
+ }
+ }
+ break;
+
+ default:
+ if ( m_pTextView )
+ m_pTextView->Command( rCEvt );
+ else
+ Window::Command(rCEvt);
+ }
+}
+
+void TextViewOutWin::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bDone = false;
+ SwSrcEditWindow* pSrcEditWin = static_cast<SwSrcEditWindow*>(GetParent());
+ bool bChange = !pSrcEditWin->IsReadonly() || !TextEngine::DoesKeyChangeText( rKEvt );
+ if(bChange)
+ bDone = m_pTextView->KeyInput( rKEvt );
+
+ SfxBindings& rBindings = static_cast<SwSrcEditWindow*>(GetParent())->GetSrcView()->GetViewFrame()->GetBindings();
+ if ( !bDone )
+ {
+ if ( !SfxViewShell::Current()->KeyInput( rKEvt ) )
+ Window::KeyInput( rKEvt );
+ }
+ else
+ {
+ rBindings.Invalidate( SID_TABLE_CELL );
+ if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
+ rBindings.Update( SID_BASICIDE_STAT_POS );
+ if (pSrcEditWin->GetTextEngine()->IsModified() )
+ {
+ rBindings.Invalidate( SID_SAVEDOC );
+ rBindings.Invalidate( SID_DOC_MODIFIED );
+ }
+ if( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
+ rBindings.Invalidate( SID_ATTR_INSERT );
+ }
+
+ rBindings.Invalidate( SID_CUT );
+ rBindings.Invalidate( SID_COPY );
+
+ SwDocShell* pDocShell = pSrcEditWin->GetSrcView()->GetDocShell();
+ if(pSrcEditWin->GetTextEngine()->IsModified())
+ {
+ pDocShell->SetModified();
+ }
+}
+
+void TextViewOutWin::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ m_pTextView->Paint(rRenderContext, rRect);
+}
+
+void SwSrcEditWindow::CreateTextEngine()
+{
+ // FIXME RenderContext
+
+ const Color &rCol = GetSettings().GetStyleSettings().GetWindowColor();
+ m_pOutWin = VclPtr<TextViewOutWin>::Create(this, 0);
+ m_pOutWin->SetBackground(Wallpaper(rCol));
+ m_pOutWin->SetPointer(PointerStyle::Text);
+ m_pOutWin->Show();
+
+ // create Scrollbars
+ m_pHScrollbar = VclPtr<ScrollBar>::Create(this, WB_3DLOOK |WB_HSCROLL|WB_DRAG);
+ m_pHScrollbar->EnableRTL( false );
+ m_pHScrollbar->SetScrollHdl(LINK(this, SwSrcEditWindow, ScrollHdl));
+ m_pHScrollbar->Show();
+
+ m_pVScrollbar = VclPtr<ScrollBar>::Create(this, WB_3DLOOK |WB_VSCROLL|WB_DRAG);
+ m_pVScrollbar->EnableRTL( false );
+ m_pVScrollbar->SetScrollHdl(LINK(this, SwSrcEditWindow, ScrollHdl));
+ m_pHScrollbar->EnableDrag();
+ m_pVScrollbar->Show();
+
+ m_pTextEngine.reset(new ExtTextEngine);
+ m_pTextView.reset(new TextView( m_pTextEngine.get(), m_pOutWin ));
+ m_pTextView->SetAutoIndentMode(true);
+ m_pOutWin->SetTextView(m_pTextView.get());
+
+ m_pTextEngine->SetUpdateMode( false );
+ m_pTextEngine->InsertView( m_pTextView.get() );
+
+ vcl::Font aFont;
+ aFont.SetTransparent( false );
+ aFont.SetFillColor( rCol );
+ SetPointFont(*GetOutDev(), aFont);
+ aFont = GetFont();
+ aFont.SetFillColor( rCol );
+ m_pOutWin->SetFont( aFont );
+ m_pTextEngine->SetFont( aFont );
+
+ m_aSyntaxIdle.SetInvokeHandler( LINK( this, SwSrcEditWindow, SyntaxTimerHdl ) );
+
+ m_pTextEngine->EnableUndo( true );
+ m_pTextEngine->SetUpdateMode( true );
+
+ m_pTextView->ShowCursor();
+ InitScrollBars();
+ StartListening( *m_pTextEngine );
+
+ SfxBindings& rBind = GetSrcView()->GetViewFrame()->GetBindings();
+ rBind.Invalidate( SID_TABLE_CELL );
+}
+
+void SwSrcEditWindow::SetScrollBarRanges()
+{
+ // Extra method, not InitScrollBars, because also for TextEngine events.
+
+ m_pHScrollbar->SetRange( Range( 0, m_nCurTextWidth-1 ) );
+ m_pVScrollbar->SetRange( Range(0, m_pTextEngine->GetTextHeight()-1) );
+}
+
+void SwSrcEditWindow::InitScrollBars()
+{
+ SetScrollBarRanges();
+
+ Size aOutSz( m_pOutWin->GetOutputSizePixel() );
+ m_pVScrollbar->SetVisibleSize( aOutSz.Height() );
+ m_pVScrollbar->SetPageSize( aOutSz.Height() * 8 / 10 );
+ m_pVScrollbar->SetLineSize( m_pOutWin->GetTextHeight() );
+ m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() );
+ m_pHScrollbar->SetVisibleSize( aOutSz.Width() );
+ m_pHScrollbar->SetPageSize( aOutSz.Width() * 8 / 10 );
+ m_pHScrollbar->SetLineSize( m_pOutWin->GetTextWidth(OUString('x')) );
+ m_pHScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().X() );
+
+}
+
+IMPL_LINK(SwSrcEditWindow, ScrollHdl, ScrollBar*, pScroll, void)
+{
+ if(pScroll == m_pVScrollbar)
+ {
+ tools::Long nDiff = m_pTextView->GetStartDocPos().Y() - pScroll->GetThumbPos();
+ GetTextView()->Scroll( 0, nDiff );
+ m_pTextView->ShowCursor( false );
+ pScroll->SetThumbPos( m_pTextView->GetStartDocPos().Y() );
+ }
+ else
+ {
+ tools::Long nDiff = m_pTextView->GetStartDocPos().X() - pScroll->GetThumbPos();
+ GetTextView()->Scroll( nDiff, 0 );
+ m_pTextView->ShowCursor( false );
+ pScroll->SetThumbPos( m_pTextView->GetStartDocPos().X() );
+ }
+ GetSrcView()->GetViewFrame()->GetBindings().Invalidate( SID_TABLE_CELL );
+}
+
+IMPL_LINK( SwSrcEditWindow, SyntaxTimerHdl, Timer*, pIdle, void )
+{
+ tools::Time aSyntaxCheckStart( tools::Time::SYSTEM );
+ SAL_WARN_IF(m_pTextView == nullptr, "sw", "No View yet, but syntax highlighting?!");
+
+ m_bHighlighting = true;
+ sal_uInt16 nCount = 0;
+ // at first the region around the cursor is processed
+ TextSelection aSel = m_pTextView->GetSelection();
+ sal_uInt16 nCur = o3tl::narrowing<sal_uInt16>(aSel.GetStart().GetPara());
+ if(nCur > 40)
+ nCur -= 40;
+ else
+ nCur = 0;
+ if(!m_aSyntaxLineTable.empty())
+ for(sal_uInt16 i = 0; i < 80 && nCount < 40; i++, nCur++)
+ {
+ if(m_aSyntaxLineTable.find(nCur) != m_aSyntaxLineTable.end())
+ {
+ DoSyntaxHighlight( nCur );
+ m_aSyntaxLineTable.erase( nCur );
+ nCount++;
+ if(m_aSyntaxLineTable.empty())
+ break;
+ if((tools::Time( tools::Time::SYSTEM ).GetTime() - aSyntaxCheckStart.GetTime()) > MAX_HIGHLIGHTTIME )
+ {
+ break;
+ }
+ }
+ }
+
+ // when there is still anything left by then, go on from the beginning
+ while ( !m_aSyntaxLineTable.empty() && nCount < MAX_SYNTAX_HIGHLIGHT)
+ {
+ sal_uInt16 nLine = *m_aSyntaxLineTable.begin();
+ DoSyntaxHighlight( nLine );
+ m_aSyntaxLineTable.erase(nLine);
+ nCount ++;
+ if(tools::Time( tools::Time::SYSTEM ).GetTime() - aSyntaxCheckStart.GetTime() > MAX_HIGHLIGHTTIME)
+ {
+ break;
+ }
+ }
+
+ if(!m_aSyntaxLineTable.empty() && !pIdle->IsActive())
+ pIdle->Start();
+ // SyntaxTimerHdl is called when text changed
+ // => good opportunity to determine text width!
+ tools::Long nPrevTextWidth = m_nCurTextWidth;
+ m_nCurTextWidth = m_pTextEngine->CalcTextWidth() + 25; // small tolerance
+ if ( m_nCurTextWidth != nPrevTextWidth )
+ SetScrollBarRanges();
+ m_bHighlighting = false;
+}
+
+void SwSrcEditWindow::DoSyntaxHighlight( sal_uInt16 nPara )
+{
+ // Because of DelayedSyntaxHighlight it could happen,
+ // that the line doesn't exist anymore!
+ if ( nPara >= m_pTextEngine->GetParagraphCount() )
+ return;
+
+ bool bTempModified = IsModified();
+ m_pTextEngine->RemoveAttribs( nPara );
+ OUString aSource( m_pTextEngine->GetText( nPara ) );
+ m_pTextEngine->SetUpdateMode( false );
+ ImpDoHighlight( aSource, nPara );
+ TextView* pTmp = m_pTextEngine->GetActiveView();
+ pTmp->SetAutoScroll(false);
+ m_pTextEngine->SetActiveView(nullptr);
+ m_pTextEngine->SetUpdateMode( true );
+ m_pTextEngine->SetActiveView(pTmp);
+ pTmp->SetAutoScroll(true);
+ pTmp->ShowCursor( false/*pTmp->IsAutoScroll()*/ );
+
+ if(!bTempModified)
+ ClearModifyFlag();
+
+}
+
+void SwSrcEditWindow::ImpDoHighlight( const OUString& rSource, sal_uInt16 nLineOff )
+{
+ TextPortions aPortionList;
+ lcl_Highlight(rSource, aPortionList);
+
+ size_t nCount = aPortionList.size();
+ if ( !nCount )
+ return;
+
+ TextPortion& rLast = aPortionList[nCount-1];
+ if ( rLast.nStart > rLast.nEnd ) // Only until Bug from MD is resolved
+ {
+ nCount--;
+ aPortionList.pop_back();
+ if ( !nCount )
+ return;
+ }
+
+ {
+ // Only blanks and tabs have to be attributed along.
+ // When two identical attributes are placed consecutively,
+ // it optimises the TextEngine.
+ sal_uInt16 nLastEnd = 0;
+
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ TextPortion& r = aPortionList[i];
+ if ( r.nStart > r.nEnd ) // only until Bug from MD is resolved
+ continue;
+
+ if ( r.nStart > nLastEnd )
+ {
+ // Can I rely on the fact that all except blank and tab
+ // are being highlighted?!
+ r.nStart = nLastEnd;
+ }
+ nLastEnd = r.nEnd+1;
+ if ( ( i == (nCount-1) ) && ( r.nEnd < rSource.getLength() ) )
+ r.nEnd = rSource.getLength();
+ }
+ }
+
+ for (TextPortion & r : aPortionList)
+ {
+ if ( r.nStart > r.nEnd ) // only until Bug from MD is resolved
+ continue;
+ if(r.eType != svtools::HTMLSGML &&
+ r.eType != svtools::HTMLCOMMENT &&
+ r.eType != svtools::HTMLKEYWORD &&
+ r.eType != svtools::HTMLUNKNOWN)
+ r.eType = svtools::HTMLUNKNOWN;
+ Color aColor(SW_MOD()->GetColorConfig().GetColorValue(r.eType).nColor);
+ m_pTextEngine->SetAttrib( TextAttribFontColor( aColor ), nLineOff, r.nStart, r.nEnd+1 );
+ }
+}
+
+void SwSrcEditWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ const TextHint* pTextHint = dynamic_cast<const TextHint*>(&rHint);
+ if (!pTextHint)
+ return;
+
+ switch (pTextHint->GetId())
+ {
+ case SfxHintId::TextViewScrolled:
+ m_pHScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().X() );
+ m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() );
+ break;
+
+ case SfxHintId::TextHeightChanged:
+ if ( m_pTextEngine->GetTextHeight() < m_pOutWin->GetOutputSizePixel().Height() )
+ m_pTextView->Scroll( 0, m_pTextView->GetStartDocPos().Y() );
+ m_pVScrollbar->SetThumbPos( m_pTextView->GetStartDocPos().Y() );
+ SetScrollBarRanges();
+ break;
+
+ case SfxHintId::TextParaInserted:
+ case SfxHintId::TextParaContentChanged:
+ if ( !m_bHighlighting )
+ {
+ m_aSyntaxLineTable.insert( o3tl::narrowing<sal_uInt16>(pTextHint->GetValue()) );
+ m_aSyntaxIdle.Start();
+ }
+ break;
+ default: break;
+ }
+}
+
+void SwSrcEditWindow::Invalidate(InvalidateFlags )
+{
+ m_pOutWin->Invalidate();
+ Window::Invalidate();
+}
+
+void SwSrcEditWindow::Command( const CommandEvent& rCEvt )
+{
+ switch(rCEvt.GetCommand())
+ {
+ case CommandEventId::Wheel:
+ case CommandEventId::StartAutoScroll:
+ case CommandEventId::AutoScroll:
+ {
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if( !pWData || CommandWheelMode::ZOOM != pWData->GetMode() )
+ HandleScrollCommand( rCEvt, m_pHScrollbar, m_pVScrollbar );
+ }
+ break;
+ default:
+ Window::Command(rCEvt);
+ }
+}
+
+void SwSrcEditWindow::HandleWheelCommand( const CommandEvent& rCEvt )
+{
+ m_pTextView->Command(rCEvt);
+ HandleScrollCommand( rCEvt, m_pHScrollbar, m_pVScrollbar );
+}
+
+void SwSrcEditWindow::GetFocus()
+{
+ if (m_pOutWin)
+ m_pOutWin->GrabFocus();
+}
+
+static bool lcl_GetLanguagesForEncoding(rtl_TextEncoding eEnc, LanguageType aLanguages[])
+{
+ switch(eEnc)
+ {
+ case RTL_TEXTENCODING_UTF7 :
+ case RTL_TEXTENCODING_UTF8 :
+ // don#t fill - all LANGUAGE_SYSTEM means unicode font has to be used
+ break;
+
+ case RTL_TEXTENCODING_ISO_8859_3:
+ case RTL_TEXTENCODING_ISO_8859_1 :
+ case RTL_TEXTENCODING_MS_1252 :
+ case RTL_TEXTENCODING_APPLE_ROMAN :
+ case RTL_TEXTENCODING_IBM_850 :
+ case RTL_TEXTENCODING_ISO_8859_14 :
+ case RTL_TEXTENCODING_ISO_8859_15 :
+ //fill with western languages
+ aLanguages[0] = LANGUAGE_GERMAN;
+ aLanguages[1] = LANGUAGE_FRENCH;
+ aLanguages[2] = LANGUAGE_ITALIAN;
+ aLanguages[3] = LANGUAGE_SPANISH;
+ break;
+
+ case RTL_TEXTENCODING_IBM_865 :
+ //scandinavian
+ aLanguages[0] = LANGUAGE_FINNISH;
+ aLanguages[1] = LANGUAGE_NORWEGIAN;
+ aLanguages[2] = LANGUAGE_SWEDISH;
+ aLanguages[3] = LANGUAGE_DANISH;
+ break;
+
+ case RTL_TEXTENCODING_ISO_8859_10 :
+ case RTL_TEXTENCODING_ISO_8859_13 :
+ case RTL_TEXTENCODING_ISO_8859_2 :
+ case RTL_TEXTENCODING_IBM_852 :
+ case RTL_TEXTENCODING_MS_1250 :
+ case RTL_TEXTENCODING_APPLE_CENTEURO :
+ aLanguages[0] = LANGUAGE_POLISH;
+ aLanguages[1] = LANGUAGE_CZECH;
+ aLanguages[2] = LANGUAGE_HUNGARIAN;
+ aLanguages[3] = LANGUAGE_SLOVAK;
+ break;
+
+ case RTL_TEXTENCODING_ISO_8859_4 :
+ case RTL_TEXTENCODING_IBM_775 :
+ case RTL_TEXTENCODING_MS_1257 :
+ aLanguages[0] = LANGUAGE_LATVIAN ;
+ aLanguages[1] = LANGUAGE_LITHUANIAN;
+ aLanguages[2] = LANGUAGE_ESTONIAN ;
+ break;
+
+ case RTL_TEXTENCODING_IBM_863 : aLanguages[0] = LANGUAGE_FRENCH_CANADIAN; break;
+ case RTL_TEXTENCODING_APPLE_FARSI : aLanguages[0] = LANGUAGE_FARSI; break;
+ case RTL_TEXTENCODING_APPLE_ROMANIAN:aLanguages[0] = LANGUAGE_ROMANIAN; break;
+
+ case RTL_TEXTENCODING_IBM_861 :
+ case RTL_TEXTENCODING_APPLE_ICELAND :
+ aLanguages[0] = LANGUAGE_ICELANDIC;
+ break;
+
+ case RTL_TEXTENCODING_APPLE_CROATIAN:aLanguages[0] = LANGUAGE_CROATIAN; break;
+
+ case RTL_TEXTENCODING_IBM_437 :
+ case RTL_TEXTENCODING_ASCII_US : aLanguages[0] = LANGUAGE_ENGLISH; break;
+
+ case RTL_TEXTENCODING_IBM_862 :
+ case RTL_TEXTENCODING_MS_1255 :
+ case RTL_TEXTENCODING_APPLE_HEBREW :
+ case RTL_TEXTENCODING_ISO_8859_8 :
+ aLanguages[0] = LANGUAGE_HEBREW;
+ break;
+
+ case RTL_TEXTENCODING_IBM_857 :
+ case RTL_TEXTENCODING_MS_1254 :
+ case RTL_TEXTENCODING_APPLE_TURKISH:
+ case RTL_TEXTENCODING_ISO_8859_9 :
+ aLanguages[0] = LANGUAGE_TURKISH;
+ break;
+
+ case RTL_TEXTENCODING_IBM_860 :
+ aLanguages[0] = LANGUAGE_PORTUGUESE;
+ break;
+
+ case RTL_TEXTENCODING_IBM_869 :
+ case RTL_TEXTENCODING_MS_1253 :
+ case RTL_TEXTENCODING_APPLE_GREEK :
+ case RTL_TEXTENCODING_ISO_8859_7 :
+ case RTL_TEXTENCODING_IBM_737 :
+ aLanguages[0] = LANGUAGE_GREEK;
+ break;
+
+ case RTL_TEXTENCODING_KOI8_R :
+ case RTL_TEXTENCODING_ISO_8859_5 :
+ case RTL_TEXTENCODING_IBM_855 :
+ case RTL_TEXTENCODING_MS_1251 :
+ case RTL_TEXTENCODING_IBM_866 :
+ case RTL_TEXTENCODING_APPLE_CYRILLIC :
+ aLanguages[0] = LANGUAGE_RUSSIAN;
+ break;
+
+ case RTL_TEXTENCODING_APPLE_UKRAINIAN:
+ case RTL_TEXTENCODING_KOI8_U:
+ aLanguages[0] = LANGUAGE_UKRAINIAN;
+ break;
+
+ case RTL_TEXTENCODING_IBM_864 :
+ case RTL_TEXTENCODING_MS_1256 :
+ case RTL_TEXTENCODING_ISO_8859_6 :
+ case RTL_TEXTENCODING_APPLE_ARABIC :
+ aLanguages[0] = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ break;
+
+ case RTL_TEXTENCODING_APPLE_CHINTRAD :
+ case RTL_TEXTENCODING_MS_950 :
+ case RTL_TEXTENCODING_GBT_12345 :
+ case RTL_TEXTENCODING_BIG5 :
+ case RTL_TEXTENCODING_EUC_TW :
+ case RTL_TEXTENCODING_BIG5_HKSCS :
+ aLanguages[0] = LANGUAGE_CHINESE_TRADITIONAL;
+ break;
+
+ case RTL_TEXTENCODING_EUC_JP :
+ case RTL_TEXTENCODING_ISO_2022_JP :
+ case RTL_TEXTENCODING_JIS_X_0201 :
+ case RTL_TEXTENCODING_JIS_X_0208 :
+ case RTL_TEXTENCODING_JIS_X_0212 :
+ case RTL_TEXTENCODING_APPLE_JAPANESE :
+ case RTL_TEXTENCODING_MS_932 :
+ case RTL_TEXTENCODING_SHIFT_JIS :
+ aLanguages[0] = LANGUAGE_JAPANESE;
+ break;
+
+ case RTL_TEXTENCODING_GB_2312 :
+ case RTL_TEXTENCODING_MS_936 :
+ case RTL_TEXTENCODING_GBK :
+ case RTL_TEXTENCODING_GB_18030 :
+ case RTL_TEXTENCODING_APPLE_CHINSIMP :
+ case RTL_TEXTENCODING_EUC_CN :
+ case RTL_TEXTENCODING_ISO_2022_CN :
+ aLanguages[0] = LANGUAGE_CHINESE_SIMPLIFIED;
+ break;
+
+ case RTL_TEXTENCODING_APPLE_KOREAN :
+ case RTL_TEXTENCODING_MS_949 :
+ case RTL_TEXTENCODING_EUC_KR :
+ case RTL_TEXTENCODING_ISO_2022_KR :
+ case RTL_TEXTENCODING_MS_1361 :
+ aLanguages[0] = LANGUAGE_KOREAN;
+ break;
+
+ case RTL_TEXTENCODING_APPLE_THAI :
+ case RTL_TEXTENCODING_MS_874 :
+ case RTL_TEXTENCODING_TIS_620 :
+ aLanguages[0] = LANGUAGE_THAI;
+ break;
+ default: aLanguages[0] = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ }
+ return aLanguages[0] != LANGUAGE_SYSTEM;
+}
+void SwSrcEditWindow::SetFont()
+{
+ OUString sFontName(
+ officecfg::Office::Common::Font::SourceViewFont::FontName::get().
+ value_or(OUString()));
+ if(sFontName.isEmpty())
+ {
+ LanguageType aLanguages[5] =
+ {
+ LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM, LANGUAGE_SYSTEM
+ };
+ vcl::Font aFont;
+ if(lcl_GetLanguagesForEncoding(m_eSourceEncoding, aLanguages))
+ {
+ //TODO: check for multiple languages
+ aFont = OutputDevice::GetDefaultFont(DefaultFontType::FIXED, aLanguages[0], GetDefaultFontFlags::NONE, GetOutDev());
+ }
+ else
+ aFont = OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE,
+ Application::GetSettings().GetLanguageTag().getLanguageType(), GetDefaultFontFlags::NONE, GetOutDev());
+ sFontName = aFont.GetFamilyName();
+ }
+ const SvxFontListItem* pFontListItem =
+ static_cast<const SvxFontListItem* >(m_pSrcView->GetDocShell()->GetItem( SID_ATTR_CHAR_FONTLIST ));
+ const FontList* pList = pFontListItem->GetFontList();
+ FontMetric aFontMetric = pList->Get(sFontName,WEIGHT_NORMAL, ITALIC_NONE);
+
+ const vcl::Font& rFont = GetTextEngine()->GetFont();
+ vcl::Font aFont(aFontMetric);
+ Size aSize(rFont.GetFontSize());
+ //font height is stored in point and set in twip
+ aSize.setHeight(
+ officecfg::Office::Common::Font::SourceViewFont::FontHeight::get() * 20 );
+ aFont.SetFontSize(m_pOutWin->LogicToPixel(aSize, MapMode(MapUnit::MapTwip)));
+ GetTextEngine()->SetFont( aFont );
+ m_pOutWin->SetFont(aFont);
+}
+
+void SwSrcEditWindow::SetTextEncoding(rtl_TextEncoding eEncoding)
+{
+ m_eSourceEncoding = eEncoding;
+ SetFont();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */