sc/source/ui/inc/gridwin.hxx | 8 sc/source/ui/inc/notemark.hxx | 67 +++--- sc/source/ui/inc/tabview.hxx | 2 sc/source/ui/view/gridwin.cxx | 18 - sc/source/ui/view/gridwin4.cxx | 3 sc/source/ui/view/gridwin5.cxx | 42 --- sc/source/ui/view/notemark.cxx | 435 +++++++++++++++++++++++++++-------------- sc/source/ui/view/tabview.cxx | 6 sc/source/ui/view/tabview2.cxx | 4 sc/source/ui/view/tabview3.cxx | 2 sc/source/ui/view/tabvwsh3.cxx | 2 sc/source/ui/view/tabvwsh4.cxx | 4 sc/source/ui/view/tabvwsh5.cxx | 4 sc/source/ui/view/viewfun6.cxx | 4 14 files changed, 366 insertions(+), 235 deletions(-)
New commits: commit 256ad05887a23484ef4494cc90ea168696bbba37 Author: Armin Le Grand (Collabora) <armin.le.gr...@me.com> AuthorDate: Thu May 15 21:01:49 2025 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Mon May 19 11:51:16 2025 +0200 NDOO: NotesDisplayOnOverlay For Calc, showing Notes/Postit info on Mouse Hoovering was done by painting directly to the SplitWindows and invalidating there again. This created unneccessary repaints of the whole sheet. It was even painted 'after' the existing Overlay was painted to make that work/not collide. This is a perfect task for the Overlay where this info can be shown and hidden again without the need to repaint the calc sheet at that places at all. The new visualization does exactly that. It still is Timer- Based to not trigger this all the time (timings unchanged), but now creates the needed visualization on the Overlay. While the visualization data was not changed, the mechanism had to be changed to use transformations/overlay/primitives. This was partially complicated due to the 'combined split view' used to show that: The SdrCaptionObj can span over multiple windows in split view, separated by the window framing lines - it looks like a glass window with segments, with the visualisation being shown 'behind' the cross. Technically this means that the same visualisation data has to be shown - dependent on the view split used - in up to four windows. This just uses the same primitives for visualisation embedded in the needed transformation primitives. Also the previous version created and destroyed a SdrModel and SdrPage (complete with ItemPoos and ItemSets) for every visualization, this is no longer needed, too. Change-Id: I18c6d63d346ea89fca21a4d856ad66c82156a774 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185417 Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> Tested-by: Jenkins diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx index 850cdad98601..70076cb40bf7 100644 --- a/sc/source/ui/inc/gridwin.hxx +++ b/sc/source/ui/inc/gridwin.hxx @@ -48,7 +48,7 @@ class ScDPFieldButton; class ScOutputData; class SdrObject; class SdrEditView; -class ScNoteMarker; +class ScNoteOverlay; class SdrHdlList; class ScTransferObj; struct SpellCallbackInfo; @@ -160,7 +160,7 @@ class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTarget ScHSplitPos eHWhich; ScVSplitPos eVWhich; - std::unique_ptr<ScNoteMarker, o3tl::default_delete<ScNoteMarker>> mpNoteMarker; + std::unique_ptr<ScNoteOverlay, o3tl::default_delete<ScNoteOverlay>> mpNoteOverlay; std::shared_ptr<ScFilterListBox> mpFilterBox; std::unique_ptr<ScCheckListMenuControl> mpAutoFilterPopup; @@ -400,6 +400,8 @@ public: void FakeButtonUp(); const Point& GetMousePosPixel() const { return aCurMousePos; } + ScSplitPos getScSplitPos() const { return eWhich; } + void UpdateStatusPosSize(); void ClickExtern(); @@ -450,7 +452,7 @@ public: void UpdateListValPos( bool bVisible, const ScAddress& rPos ); bool ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard ); - void HideNoteMarker(); + void HideNoteOverlay(); /// MapMode for the drawinglayer objects. MapMode GetDrawMapMode( bool bForce = false ); diff --git a/sc/source/ui/inc/notemark.hxx b/sc/source/ui/inc/notemark.hxx index 31965be07802..e66f10f326e7 100644 --- a/sc/source/ui/inc/notemark.hxx +++ b/sc/source/ui/inc/notemark.hxx @@ -19,51 +19,48 @@ #pragma once -#include <vcl/mapmod.hxx> #include <vcl/timer.hxx> -#include <vcl/vclptr.hxx> -#include <tools/gen.hxx> #include <address.hxx> -#include <postit.hxx> - -namespace vcl { class Window; } +#include <rtl/ref.hxx> +#include <tools/gen.hxx> +#include <svx/sdr/overlay/overlayobjectlist.hxx> -class SdrModel; +class ScGridWindow; class SdrCaptionObj; -class ScNoteMarker +class ScNoteOverlay : public Timer { -private: - VclPtr<vcl::Window> m_pWindow; - VclPtr<vcl::Window> m_pRightWin; - VclPtr<vcl::Window> m_pBottomWin; - VclPtr<vcl::Window> m_pDiagWin; - ScDocument* m_pDoc; - ScAddress m_aDocPos; - OUString m_aUserText; - tools::Rectangle m_aVisRect; - Timer m_aTimer; - MapMode m_aMapMode; - bool m_bLeft; - bool m_bByKeyboard; + ScGridWindow& mrScGridWindow; + ScAddress maDocPos; + OUString maUserText; + sdr::overlay::OverlayObjectList maNoteOverlayGroup; + rtl::Reference<SdrCaptionObj> mxObject; + drawinglayer::primitive2d::Primitive2DContainer maSequence; + bool mbLeft; + bool mbKeyboard; - tools::Rectangle m_aRect; - std::unique_ptr<SdrModel> m_pModel; - rtl::Reference<SdrCaptionObj> m_xObject; - bool m_bVisible; - DECL_LINK( TimeHdl, Timer*, void ); + tools::Rectangle calculateVisibleRectangle(); + const drawinglayer::primitive2d::Primitive2DContainer& getOrCreatePrimitive2DSequence(); + void createAdditionalRepresentations(); + void createOverlaySubContent( + ScGridWindow* pTarget, + const basegfx::B2DHomMatrix& rTransformToPixels, + const basegfx::B2DPoint& rTopLeft); public: - ScNoteMarker( vcl::Window* pWin, vcl::Window* pRight, vcl::Window* pBottom, vcl::Window* pDiagonal, - ScDocument* pD, const ScAddress& aPos, OUString aUser, - const MapMode& rMap, bool bLeftEdge, bool bForce, bool bKeyboard); - ~ScNoteMarker(); + ScNoteOverlay( + ScGridWindow& rScGridWindow, + ScAddress& aPos, + OUString aUser, + bool bLeftEdge, + bool bForce, + bool bKeyboard); + ~ScNoteOverlay(); - void Draw(); - void InvalidateWin(); - - const ScAddress& GetDocPos() const { return m_aDocPos; } - bool IsByKeyboard() const { return m_bByKeyboard; } + virtual void Invoke() override; + bool IsByKeyboard() const { return mbKeyboard; } + const ScAddress& GetDocPos() const { return maDocPos; } }; + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx index 2a6827b87b1a..008abf80b1c9 100644 --- a/sc/source/ui/inc/tabview.hxx +++ b/sc/source/ui/inc/tabview.hxx @@ -284,7 +284,7 @@ protected: void MakeDrawView( TriState nForceDesignMode ); - void HideNoteMarker(); + void HideNoteOverlay(); void UpdateIMap( SdrObject* pObj ); diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx index 2bf7211e6467..49a170da267f 100644 --- a/sc/source/ui/view/gridwin.cxx +++ b/sc/source/ui/view/gridwin.cxx @@ -458,7 +458,7 @@ void ScGridWindow::dispose() ImpDestroyOverlayObjects(); mpFilterBox.reset(); - mpNoteMarker.reset(); + mpNoteOverlay.reset(); mpAutoFilterPopup.reset(); mpDPFieldPopup.reset(); aComboButton.SetOutputDevice(nullptr); @@ -1858,7 +1858,7 @@ void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventSta // so the following query is no longer necessary: ClickExtern(); // deletes FilterBox when available - HideNoteMarker(); + HideNoteOverlay(); bEEMouse = false; @@ -2784,8 +2784,8 @@ void ScGridWindow::MouseMove( const MouseEvent& rMEvt ) { aCurMousePos = rMEvt.GetPosPixel(); - if (rMEvt.IsLeaveWindow() && mpNoteMarker && !mpNoteMarker->IsByKeyboard()) - HideNoteMarker(); + if (rMEvt.IsLeaveWindow() && mpNoteOverlay && !mpNoteOverlay->IsByKeyboard()) + HideNoteOverlay(); ScModule* pScMod = ScModule::get(); if (pScMod->IsModalMode(mrViewData.GetSfxDocShell())) @@ -3134,7 +3134,7 @@ void ScGridWindow::StartDrag( sal_Int8 /* nAction */, const Point& rPosPixel ) if (mpFilterBox || nPagebreakMouse) return; - HideNoteMarker(); + HideNoteOverlay(); CommandEvent aDragEvent( rPosPixel, CommandEventId::StartDrag, true ); @@ -3778,7 +3778,7 @@ void ScGridWindow::KeyInput(const KeyEvent& rKEvt) } // query for existing note marker before calling ViewShell's keyboard handling // which may remove the marker - bool bHadKeyMarker = mpNoteMarker && mpNoteMarker->IsByKeyboard(); + bool bHadKeyMarker(mpNoteOverlay && mpNoteOverlay->IsByKeyboard()); ScTabViewShell* pViewSh = mrViewData.GetViewShell(); if (mrViewData.GetDocShell()->GetProgress()) @@ -3813,7 +3813,7 @@ void ScGridWindow::KeyInput(const KeyEvent& rKEvt) if ( aCode.GetCode() == KEY_ESCAPE && aCode.GetModifier() == 0 ) { if ( bHadKeyMarker ) - HideNoteMarker(); + HideNoteOverlay(); else pViewSh->Escape(); return; @@ -3824,7 +3824,7 @@ void ScGridWindow::KeyInput(const KeyEvent& rKEvt) // (hard-coded because F1 can't be configured) if ( bHadKeyMarker ) - HideNoteMarker(); // hide when previously visible + HideNoteOverlay(); // hide when previously visible else ShowNoteMarker( mrViewData.GetCurX(), mrViewData.GetCurY(), true ); return; @@ -5119,7 +5119,7 @@ void ScGridWindow::UpdateEditViewPos() void ScGridWindow::ScrollPixel( tools::Long nDifX, tools::Long nDifY ) { ClickExtern(); - HideNoteMarker(); + HideNoteOverlay(); SetMapMode(MapMode(MapUnit::MapPixel)); Scroll( nDifX, nDifY, ScrollFlags::Children ); diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx index a254bbafe946..13ee7636d703 100644 --- a/sc/source/ui/view/gridwin4.cxx +++ b/sc/source/ui/view/gridwin4.cxx @@ -1315,9 +1315,6 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableI else rDevice.SetMapMode(aDrawMode); - if (mpNoteMarker) - mpNoteMarker->Draw(); // Above the cursor, in drawing map mode - if (bPage && bInitialPageBreaks) SetupInitialPageBreaks(rDoc, nTab); } diff --git a/sc/source/ui/view/gridwin5.cxx b/sc/source/ui/view/gridwin5.cxx index ad1987f59382..4d028ea91e22 100644 --- a/sc/source/ui/view/gridwin5.cxx +++ b/sc/source/ui/view/gridwin5.cxx @@ -152,15 +152,15 @@ bool ScGridWindow::ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard ) { bool bNew = true; bool bFast = false; - if (mpNoteMarker) // A note already shown + if (mpNoteOverlay) // A note already shown { - if (mpNoteMarker->GetDocPos() == aCellPos) + if (mpNoteOverlay->GetDocPos() == aCellPos) bNew = false; // then stop else bFast = true; // otherwise, at once // marker which was shown for ctrl-F1 isn't removed by mouse events - if (mpNoteMarker->IsByKeyboard() && !bKeyboard) + if (mpNoteOverlay->IsByKeyboard() && !bKeyboard) bNew = false; } if (bNew) @@ -168,31 +168,7 @@ bool ScGridWindow::ShowNoteMarker( SCCOL nPosX, SCROW nPosY, bool bKeyboard ) if (bKeyboard) bFast = true; // keyboard also shows the marker immediately - mpNoteMarker.reset(); - - bool bHSplit = mrViewData.GetHSplitMode() != SC_SPLIT_NONE; - bool bVSplit = mrViewData.GetVSplitMode() != SC_SPLIT_NONE; - - vcl::Window* pLeft = mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT ); - vcl::Window* pRight = bHSplit ? mrViewData.GetView()->GetWindowByPos( bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT ) : nullptr; - vcl::Window* pBottom = bVSplit ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMLEFT ) : nullptr; - vcl::Window* pDiagonal = (bHSplit && bVSplit) ? mrViewData.GetView()->GetWindowByPos( SC_SPLIT_BOTTOMRIGHT ) : nullptr; - assert(pLeft && "ScGridWindow::ShowNoteMarker - missing top-left grid window"); - - /* If caption is shown from right or bottom windows, adjust - mapmode to include size of top-left window. */ - MapMode aMapMode = GetDrawMapMode( true ); - Size aLeftSize = pLeft->PixelToLogic( pLeft->GetOutputSizePixel(), aMapMode ); - Point aOrigin = aMapMode.GetOrigin(); - if( (this == pRight) || (this == pDiagonal) ) - aOrigin.AdjustX(aLeftSize.Width() ); - if( (this == pBottom) || (this == pDiagonal) ) - aOrigin.AdjustY(aLeftSize.Height() ); - aMapMode.SetOrigin( aOrigin ); - - mpNoteMarker.reset(new ScNoteMarker(pLeft, pRight, pBottom, pDiagonal, - &rDoc, aCellPos, aTrackText, - aMapMode, bLeftEdge, bFast, bKeyboard)); + mpNoteOverlay.reset(new ScNoteOverlay(*this, aCellPos, aTrackText, bLeftEdge, bFast, bKeyboard)); } bDone = true; // something is shown (old or new) @@ -238,15 +214,15 @@ void ScGridWindow::RequestHelp(const HelpEvent& rHEvt) } } - if (!bDone && mpNoteMarker) + if (!bDone && mpNoteOverlay) { - if (mpNoteMarker->IsByKeyboard()) + if (mpNoteOverlay->IsByKeyboard()) { // marker which was shown for ctrl-F1 isn't removed by mouse events } else { - mpNoteMarker.reset(); + mpNoteOverlay.reset(); } } @@ -390,9 +366,9 @@ bool ScGridWindow::IsMyModel(const SdrEditView* pSdrView) &pSdrView->GetModel() == mrViewData.GetDocument().GetDrawLayer(); } -void ScGridWindow::HideNoteMarker() +void ScGridWindow::HideNoteOverlay() { - mpNoteMarker.reset(); + mpNoteOverlay.reset(); } css::uno::Reference< css::accessibility::XAccessible > diff --git a/sc/source/ui/view/notemark.cxx b/sc/source/ui/view/notemark.cxx index 9106e291eeb0..91876c45f6b9 100644 --- a/sc/source/ui/view/notemark.cxx +++ b/sc/source/ui/view/notemark.cxx @@ -17,186 +17,345 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <svx/svdoutl.hxx> -#include <svx/svdmodel.hxx> -#include <svx/svdpage.hxx> -#include <svx/svdocapt.hxx> -#include <svl/itempool.hxx> -#include <utility> -#include <vcl/svapp.hxx> -#include <vcl/settings.hxx> -#include <vcl/window.hxx> - #include <notemark.hxx> -#include <document.hxx> #include <postit.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdpage.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <dbfunc.hxx> + +#define SC_NOTEOVERLAY_TIME 800 +#define SC_NOTEOVERLAY_SHORT 70 -#define SC_NOTEMARK_TIME 800 -#define SC_NOTEMARK_SHORT 70 - -ScNoteMarker::ScNoteMarker( vcl::Window* pWin, vcl::Window* pRight, vcl::Window* pBottom, vcl::Window* pDiagonal, - ScDocument* pD, const ScAddress& aPos, OUString aUser, - const MapMode& rMap, bool bLeftEdge, bool bForce, bool bKeyboard) : - m_pWindow( pWin ), - m_pRightWin( pRight ), - m_pBottomWin( pBottom ), - m_pDiagWin( pDiagonal ), - m_pDoc( pD ), - m_aDocPos( aPos ), - m_aUserText(std::move( aUser )), - m_aTimer("ScNoteMarker m_aTimer"), - m_aMapMode( rMap ), - m_bLeft( bLeftEdge ), - m_bByKeyboard( bKeyboard ), - m_bVisible( false ) +ScNoteOverlay::ScNoteOverlay( + ScGridWindow& rScGridWindow, + ScAddress& aPos, + OUString aUser, + bool bLeftEdge, + bool bForce, + bool bKeyboard) +: Timer("ScNoteOverlay Timer") +, mrScGridWindow(rScGridWindow) +, maDocPos(aPos) +, maUserText(std::move(aUser)) +, maNoteOverlayGroup() +, mxObject() +, maSequence() +, mbLeft(bLeftEdge) +, mbKeyboard(bKeyboard) { - Size aSizePixel = m_pWindow->GetOutputSizePixel(); - if( m_pRightWin ) - aSizePixel.AdjustWidth(m_pRightWin->GetOutputSizePixel().Width() ); - if( m_pBottomWin ) - aSizePixel.AdjustHeight(m_pBottomWin->GetOutputSizePixel().Height() ); - tools::Rectangle aVisPixel( Point( 0, 0 ), aSizePixel ); - m_aVisRect = m_pWindow->PixelToLogic( aVisPixel, m_aMapMode ); - - m_aTimer.SetInvokeHandler( LINK( this, ScNoteMarker, TimeHdl ) ); - m_aTimer.SetTimeout( bForce ? SC_NOTEMARK_SHORT : SC_NOTEMARK_TIME ); - m_aTimer.Start(); + SetTimeout(bForce ? SC_NOTEOVERLAY_SHORT : SC_NOTEOVERLAY_TIME); + Start(); } -ScNoteMarker::~ScNoteMarker() +const drawinglayer::primitive2d::Primitive2DContainer& ScNoteOverlay::getOrCreatePrimitive2DSequence() { - m_xObject.clear(); + if (!maSequence.empty()) + // sequence already created, return it + return maSequence; - InvalidateWin(); + // get some local data ptrs + ScViewData& rViewData(mrScGridWindow.getViewData()); + ScDocument& rDoc(rViewData.GetDocument()); + ScDrawLayer* pScDrawLayer(rDoc.GetDrawLayer()); - m_pModel.reset(); -} + // use existing SdrPage - old version did allocate a SdrModel/SdrPage + // for every visualization + SdrPage* pSdrPage(pScDrawLayer->GetPage(0)); + if (nullptr == pSdrPage) + return maSequence; -IMPL_LINK_NOARG(ScNoteMarker, TimeHdl, Timer *, void) -{ - if (!m_bVisible) - { - m_pModel.reset( new SdrModel() ); - m_pModel->SetScaleUnit(MapUnit::Map100thMM); - SfxItemPool& rPool = m_pModel->GetItemPool(); - rPool.SetDefaultMetric(MapUnit::Map100thMM); + // when using existing SdrPage do not forget to save change state of + // the DrawingLayer. Unfortunately CreateTempCaption below *does* add + // the SdrObject to the SdrPage - not necessary for the model stuff, + // but a comment there claims that else the text cannot be set (?) + const bool bModelChanged(pScDrawLayer->IsChanged()); + const tools::Rectangle aVisibleRectangle(calculateVisibleRectangle()); - OutputDevice* pPrinter = m_pDoc->GetRefDevice(); - if (pPrinter) - { - // On the outliner of the draw model also the printer is set as RefDevice, - // and it should look uniform. - Outliner& rOutliner = m_pModel->GetDrawOutliner(); - rOutliner.SetRefDevice(pPrinter); - } + // create the temporary SdrObject + mxObject = ScNoteUtil::CreateTempCaption( + rDoc, + maDocPos, + *pSdrPage, + maUserText, + aVisibleRectangle, + mbLeft); + + if (mxObject.is()) + { + // get the primitives from it + mxObject->GetViewContact().getViewIndependentPrimitive2DContainer(maSequence); - if( rtl::Reference<SdrPage> pPage = m_pModel->AllocPage( false ) ) + // cleanup: remove again immediately + pSdrPage->NbcRemoveObject(mxObject->GetOrdNum()); + // show the visualization with slight transparency + static bool bUseTransparency(true); + if (bUseTransparency && !maSequence.empty()) { - m_xObject = ScNoteUtil::CreateTempCaption( *m_pDoc, m_aDocPos, *pPage, m_aUserText, m_aVisRect, m_bLeft ); - if( m_xObject ) - { - // Here, SyncForGrid and GetGridOffset was used with the comment: - // // Need to include grid offset: GetCurrentBoundRect is removing it - // // but we need to know actual rect position - // This is no longer true - SdrObject::RecalcBoundRect() uses the - // GetViewContact().getViewIndependentPrimitive2DContainer()) call - // that now by default adds the eventually needed GridOffset. Thus - // I have removed that adaptation stuff. - m_aRect = m_xObject->GetCurrentBoundRect(); - } - - // Insert page so that the model recognise it and also deleted - m_pModel->InsertPage( pPage.get() ); - + maSequence = drawinglayer::primitive2d::Primitive2DContainer{ + rtl::Reference<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(maSequence), 0.1))}; } - m_bVisible = true; } - Draw(); + // restore changed state of DrawingLayer + pScDrawLayer->SetChanged(bModelChanged); + return maSequence; } -static void lcl_DrawWin( const SdrObject* pObject, vcl::RenderContext* pWindow, const MapMode& rMap ) +tools::Rectangle ScNoteOverlay::calculateVisibleRectangle() { - MapMode aOld = pWindow->GetMapMode(); - pWindow->SetMapMode( rMap ); + // to not change anything for now in positioning/object + // creation sticked together from previous versions. This + // can (should) be converted to transformation stuff + // later. In principle it calculates the size of the + // merged WindowSpace (all SplitWindows) and transforms + // that range to logical coordinates in one of the + // ScGridWindow's + const ScViewData& rViewData(mrScGridWindow.getViewData()); + const bool bHSplit(SC_SPLIT_NONE != rViewData.GetHSplitMode()); + const bool bVSplit(SC_SPLIT_NONE != rViewData.GetVSplitMode()); + const ScTabView* pScTabView(rViewData.GetView()); + const vcl::Window* pLeft(pScTabView->GetWindowByPos(bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT)); + const vcl::Window* pRight(bHSplit ? pScTabView->GetWindowByPos( bVSplit ? SC_SPLIT_TOPRIGHT : SC_SPLIT_BOTTOMRIGHT ) : nullptr); + const vcl::Window* pBottom(bVSplit ? pScTabView->GetWindowByPos( SC_SPLIT_BOTTOMLEFT ) : nullptr); + const vcl::Window* pDiagonal((bHSplit && bVSplit) ? pScTabView->GetWindowByPos(SC_SPLIT_BOTTOMRIGHT) : nullptr); + assert(pLeft && "ScNoteOverlay - missing top-left grid window"); - DrawModeFlags nOldDrawMode = pWindow->GetDrawMode(); - if ( Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) - { - pWindow->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | - DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient ); - } + /* If caption is shown from right or bottom windows, adjust + mapmode to include size of top-left window. */ + Size aSizePixel(pLeft->GetOutputSizePixel()); + MapMode aMapMode(mrScGridWindow.GetDrawMapMode(true)); + const Size aLeftSize(pLeft->PixelToLogic(aSizePixel, aMapMode)); + Point aOrigin(aMapMode.GetOrigin()); - pObject->SingleObjectPainter( *pWindow ); // #110094#-17 + if ((&mrScGridWindow == pRight) || (&mrScGridWindow == pDiagonal)) + aOrigin.AdjustX(aLeftSize.Width()); - pWindow->SetDrawMode( nOldDrawMode ); - pWindow->SetMapMode( aOld ); + if ((&mrScGridWindow == pBottom) || (&mrScGridWindow == pDiagonal)) + aOrigin.AdjustY(aLeftSize.Height()); + + aMapMode.SetOrigin(aOrigin); + + if (nullptr != pRight) + aSizePixel.AdjustWidth(pRight->GetOutputSizePixel().Width()); + + if (nullptr != pBottom) + aSizePixel.AdjustHeight(pBottom->GetOutputSizePixel().Height()); + + return mrScGridWindow.PixelToLogic(tools::Rectangle(Point(0, 0), aSizePixel), aMapMode); } -static MapMode lcl_MoveMapMode( const MapMode& rMap, const Size& rMove ) +void ScNoteOverlay::createOverlaySubContent( + ScGridWindow* pTarget, + const basegfx::B2DHomMatrix& rTransformToPixels, + const basegfx::B2DPoint& rTopLeft) { - MapMode aNew = rMap; - Point aOrigin = aNew.GetOrigin(); - aOrigin.AdjustX( -(rMove.Width()) ); - aOrigin.AdjustY( -rMove.Height() ); - aNew.SetOrigin(aOrigin); - return aNew; + // create additional visualization in given ScGridWindow + // with given partial transform and discrete offset + rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager(pTarget->getOverlayManager()); + if (!xOverlayManager.is()) + // no OverlayManager, no visualization + return; + + // create a transformation from initial ScGridWindow for which the + // visualization was created and in who's DrawMapMode it is to the + // target ScGridWindow. That transformation spans over the merged + // SplitWindow display. To do so, transform: + // 1 from initial ScGridWindow logic DrawMapMode to discrete (pixels) + // in the displaying window + // 2 to merged SplitWindow display (may have different top-left) + // 3 to window displaying target ScGridWindow + // 4 to logic coordinates in it's DrawMapMode + // NOTE: 1+2 are already in rTransformToPixels + basegfx::B2DHomMatrix aTransformToTarget(rTransformToPixels); + aTransformToTarget.translate(-rTopLeft); + const MapMode aOrig(pTarget->GetMapMode()); + pTarget->SetMapMode(pTarget->GetDrawMapMode(true)); + aTransformToTarget = pTarget->GetOutDev()->GetInverseViewTransformation() * aTransformToTarget; + pTarget->SetMapMode(aOrig); + + // embed to TransformPrimitive + drawinglayer::primitive2d::Primitive2DContainer aSequence(getOrCreatePrimitive2DSequence()); + aSequence = drawinglayer::primitive2d::Primitive2DContainer{ + rtl::Reference<drawinglayer::primitive2d::TransformPrimitive2D>( + new drawinglayer::primitive2d::TransformPrimitive2D( + aTransformToTarget, + std::move(aSequence)))}; + + // create OverlayObject + std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject( + new sdr::overlay::OverlayPrimitive2DSequenceObject( + std::move(aSequence))); + + // add to OverlayManager and local data holder (for + // destruction) + xOverlayManager->add(*pOverlayObject); + maNoteOverlayGroup.append(std::move(pOverlayObject)); } -void ScNoteMarker::Draw() +void ScNoteOverlay::createAdditionalRepresentations() { - if ( !(m_xObject && m_bVisible) ) + // check if we have a split at all + const ScViewData& rViewData(mrScGridWindow.getViewData()); + const ScTabView* pScTabView(rViewData.GetView()); + const bool bHSplit(SC_SPLIT_NONE != rViewData.GetHSplitMode()); + const bool bVSplit(SC_SPLIT_NONE != rViewData.GetVSplitMode()); + + if (!bHSplit && !bVSplit) + // no split screen, no additional visualizations needed + return; + + if (getOrCreatePrimitive2DSequence().empty()) + // no visualization, done return; - lcl_DrawWin( m_xObject.get(), m_pWindow->GetOutDev(), m_aMapMode ); + // if we have a split screen with multiple ScGridWindow's + // the visualization might overlap with other ones than the + // one this gets initially constructed. get the logic range + // of the original visualization in the original ScGridWindow + const drawinglayer::geometry::ViewInformation2D aViewInformation2D; + const basegfx::B2DRange aContentRange(getOrCreatePrimitive2DSequence().getB2DRange(aViewInformation2D)); + + // prep values to be filled in own scope to not hold the vars + basegfx::B2DRange aGlobalPixel(aContentRange); + basegfx::B2DVector aTopLeftOffset; + basegfx::B2DHomMatrix aTransformToPixels; - if ( m_pRightWin || m_pBottomWin ) { - Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode ); - if ( m_pRightWin ) - lcl_DrawWin( m_xObject.get(), m_pRightWin->GetOutDev(), - lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ) ); - if ( m_pBottomWin ) - lcl_DrawWin( m_xObject.get(), m_pBottomWin->GetOutDev(), - lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ) ); - if ( m_pDiagWin ) - lcl_DrawWin( m_xObject.get(), m_pDiagWin->GetOutDev(), lcl_MoveMapMode( m_aMapMode, aWinSize ) ); + // calculate general TopLeft offsets for potential other windows + // from top-left window + const ScSplitPos myScSplitPos(mrScGridWindow.getScSplitPos()); + const ScGridWindow* pLeft(static_cast<ScGridWindow*>( + pScTabView->GetWindowByPos(bVSplit ? SC_SPLIT_TOPLEFT : SC_SPLIT_BOTTOMLEFT))); + aTopLeftOffset.setX(pLeft->GetOutputSizePixel().Width()); + aTopLeftOffset.setY(pLeft->GetOutputSizePixel().Height()); + + // create transformation from logic coordinates in original + // ScGridWindow to discrete coordinates (pixels), then to + // merged SplitWindow display + const MapMode aOrig(mrScGridWindow.GetMapMode()); + mrScGridWindow.SetMapMode(mrScGridWindow.GetDrawMapMode(true)); + aTransformToPixels = mrScGridWindow.GetOutDev()->GetViewTransformation(); + if (SC_SPLIT_TOPRIGHT == myScSplitPos || SC_SPLIT_BOTTOMRIGHT == myScSplitPos) + aTransformToPixels.translate(aTopLeftOffset.getX(), 0); + if (SC_SPLIT_BOTTOMLEFT == myScSplitPos || SC_SPLIT_BOTTOMRIGHT == myScSplitPos) + aTransformToPixels.translate(0, aTopLeftOffset.getY()); + aGlobalPixel.transform(aTransformToPixels); + mrScGridWindow.SetMapMode(aOrig); + } + + // try all four possible ScGridWindows + ScGridWindow* pBottomLeft(static_cast<ScGridWindow*>(pScTabView->GetWindowByPos(SC_SPLIT_BOTTOMLEFT))); + + // nothing to do when ScGridWindow does not exists or is same as + // original ScGridWindow for which visualization is already done + if (nullptr != pBottomLeft && pBottomLeft != &mrScGridWindow) + { + // calculate pixel range relative to merged SplitWindow display + const basegfx::B2DPoint aTopLeft(0, bVSplit ? aTopLeftOffset.getY() : 0); + const basegfx::B2DVector aSize(pBottomLeft->GetOutputSizePixel().Width(), pBottomLeft->GetOutputSizePixel().Height()); + const basegfx::B2DRange aLocalPixel(aTopLeft, aTopLeft + aSize); + + if(aLocalPixel.overlaps(aGlobalPixel)) + { + // if needed visualization is visible there, create an OverlayObject there + // with the same content but at corrected position + createOverlaySubContent(pBottomLeft, aTransformToPixels, aTopLeft); + } + } + + ScGridWindow* pBottomRight(static_cast<ScGridWindow*>(pScTabView->GetWindowByPos(SC_SPLIT_BOTTOMRIGHT))); + if (nullptr != pBottomRight && pBottomRight != &mrScGridWindow) + { + const basegfx::B2DPoint aTopLeft(bHSplit ? aTopLeftOffset.getX() : 0, bVSplit ? aTopLeftOffset.getY() : 0); + const basegfx::B2DVector aSize(pBottomRight->GetOutputSizePixel().Width(), pBottomRight->GetOutputSizePixel().Height()); + const basegfx::B2DRange aLocalPixel(aTopLeft, aTopLeft + aSize); + + if(aLocalPixel.overlaps(aGlobalPixel)) + { + createOverlaySubContent(pBottomRight, aTransformToPixels, aTopLeft); + } + } + + ScGridWindow* pTopLeft(static_cast<ScGridWindow*>(pScTabView->GetWindowByPos(SC_SPLIT_TOPLEFT))); + if (nullptr != pTopLeft && pTopLeft != &mrScGridWindow) + { + const basegfx::B2DPoint aTopLeft(0, 0); + const basegfx::B2DVector aSize(pTopLeft->GetOutputSizePixel().Width(), pTopLeft->GetOutputSizePixel().Height()); + const basegfx::B2DRange aLocalPixel(aTopLeft, aTopLeft + aSize); + + if(aLocalPixel.overlaps(aGlobalPixel)) + { + createOverlaySubContent(pTopLeft, aTransformToPixels, aTopLeft); + } + } + + ScGridWindow* pTopRight(static_cast<ScGridWindow*>(pScTabView->GetWindowByPos(SC_SPLIT_TOPRIGHT))); + if (nullptr != pTopRight && pTopRight != &mrScGridWindow) + { + const basegfx::B2DPoint aTopLeft(bHSplit ? aTopLeftOffset.getX() : 0, 0); + const basegfx::B2DVector aSize(pTopRight->GetOutputSizePixel().Width(), pTopRight->GetOutputSizePixel().Height()); + const basegfx::B2DRange aLocalPixel(aTopLeft, aTopLeft + aSize); + + if(aLocalPixel.overlaps(aGlobalPixel)) + { + createOverlaySubContent(pTopRight, aTransformToPixels, aTopLeft); + } } } -void ScNoteMarker::InvalidateWin() +void ScNoteOverlay::Invoke() { - if (!m_bVisible) + if (0 != maNoteOverlayGroup.count()) + // already visualized, done return; - // Extend the invalidated rectangle by 1 pixel in each direction in case AA would slightly - // paint outside the nominal area. - tools::Rectangle aRect(m_aRect); - const Size aPixelSize = m_pWindow->PixelToLogic(Size(1, 1)); - aRect.AdjustLeft(-aPixelSize.getWidth()); - aRect.AdjustTop(-aPixelSize.getHeight()); - aRect.AdjustRight(aPixelSize.getWidth()); - aRect.AdjustBottom(aPixelSize.getHeight()); - - m_pWindow->Invalidate( OutputDevice::LogicToLogic(aRect, m_aMapMode, m_pWindow->GetMapMode()) ); + rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager( + mrScGridWindow.getOverlayManager()); + if (!xOverlayManager.is()) + // no OverlayManager, no display + return; - if ( !(m_pRightWin || m_pBottomWin) ) + if (getOrCreatePrimitive2DSequence().empty()) + // no visualization data, no display return; - Size aWinSize = m_pWindow->PixelToLogic( m_pWindow->GetOutputSizePixel(), m_aMapMode ); - if ( m_pRightWin ) - m_pRightWin->Invalidate( OutputDevice::LogicToLogic(aRect, - lcl_MoveMapMode( m_aMapMode, Size( aWinSize.Width(), 0 ) ), - m_pRightWin->GetMapMode()) ); - if ( m_pBottomWin ) - m_pBottomWin->Invalidate( OutputDevice::LogicToLogic(aRect, - lcl_MoveMapMode( m_aMapMode, Size( 0, aWinSize.Height() ) ), - m_pBottomWin->GetMapMode()) ); - if ( m_pDiagWin ) - m_pDiagWin->Invalidate( OutputDevice::LogicToLogic(aRect, - lcl_MoveMapMode( m_aMapMode, aWinSize ), - m_pDiagWin->GetMapMode()) ); + // create OverlayObject for initial ScGridWindow + drawinglayer::primitive2d::Primitive2DContainer aSequence( + getOrCreatePrimitive2DSequence()); + std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject( + new sdr::overlay::OverlayPrimitive2DSequenceObject( + std::move(aSequence))); + + // add to OverlayManager and local data holder (for + // destruction) + xOverlayManager->add(*pNewOverlayObject); + maNoteOverlayGroup.append(std::move(pNewOverlayObject)); + + // check and evtl. create visualizations for other ScGridWindows + // in SplitScreen mode + createAdditionalRepresentations(); +} + +ScNoteOverlay::~ScNoteOverlay() +{ + // cleanup OverlayObjects - would also be done by destructor + maNoteOverlayGroup.clear(); + + // destruct temporary SdrObject. It *needs* to be kept alive + // during visualization due to it being used for decmpose + // of the TextPrimitive. That is an old compromize in the + // primitives: the text primitive is not self-contained in + // the sense that it needs the SdrTextObj for decompose. + // Would be hard to correct, but would be good for the future + mxObject.clear(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx index 7168cdff702e..f0c64d88064f 100644 --- a/sc/source/ui/view/tabview.cxx +++ b/sc/source/ui/view/tabview.cxx @@ -976,7 +976,7 @@ void ScTabView::SetZoomPercentFromCommand(sal_uInt16 nZoomPercent) bool ScTabView::ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos ) { - HideNoteMarker(); + HideNoteOverlay(); bool bDone = false; const CommandWheelData* pData = rCEvt.GetWheelData(); @@ -1016,7 +1016,7 @@ bool ScTabView::ScrollCommand( const CommandEvent& rCEvt, ScSplitPos ePos ) bool ScTabView::GesturePanCommand(const CommandEvent& rCEvt) { - HideNoteMarker(); + HideNoteOverlay(); bool bDone = false; const CommandGesturePanData* pData = rCEvt.GetGesturePanData(); @@ -1039,7 +1039,7 @@ bool ScTabView::GesturePanCommand(const CommandEvent& rCEvt) bool ScTabView::GestureZoomCommand(const CommandEvent& rCEvt) { - HideNoteMarker(); + HideNoteOverlay(); const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData(); if (!pData) diff --git a/sc/source/ui/view/tabview2.cxx b/sc/source/ui/view/tabview2.cxx index 7f78e9575e83..c9b6d811b558 100644 --- a/sc/source/ui/view/tabview2.cxx +++ b/sc/source/ui/view/tabview2.cxx @@ -1607,11 +1607,11 @@ void ScTabView::StopMarking() pRowBar[eV]->StopMarking(); } -void ScTabView::HideNoteMarker() +void ScTabView::HideNoteOverlay() { for (VclPtr<ScGridWindow> & pWin : pGridWin) if (pWin && pWin->IsVisible()) - pWin->HideNoteMarker(); + pWin->HideNoteOverlay(); } void ScTabView::MakeDrawLayer() diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx index c6a274d89c28..726180915de8 100644 --- a/sc/source/ui/view/tabview3.cxx +++ b/sc/source/ui/view/tabview3.cxx @@ -3596,7 +3596,7 @@ void ScTabView::ZoomChanged() rBindings.Invalidate(SID_ZOOM_IN); rBindings.Invalidate(SID_ZOOM_OUT); - HideNoteMarker(); + HideNoteOverlay(); // To not change too much, use pWin here ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get(); diff --git a/sc/source/ui/view/tabvwsh3.cxx b/sc/source/ui/view/tabvwsh3.cxx index 8d5888a5b5f3..c5082b8b1484 100644 --- a/sc/source/ui/view/tabvwsh3.cxx +++ b/sc/source/ui/view/tabvwsh3.cxx @@ -991,7 +991,7 @@ void ScTabViewShell::Execute( SfxRequest& rReq ) case SID_ZOOM_IN: case SID_ZOOM_OUT: { - HideNoteMarker(); + HideNoteOverlay(); if (!GetViewData().GetViewShell()->GetViewFrame().GetFrame().IsInPlace()) { diff --git a/sc/source/ui/view/tabvwsh4.cxx b/sc/source/ui/view/tabvwsh4.cxx index a50842624ae6..b1bff569e6a6 100644 --- a/sc/source/ui/view/tabvwsh4.cxx +++ b/sc/source/ui/view/tabvwsh4.cxx @@ -270,7 +270,7 @@ void ScTabViewShell::Deactivate(bool bMDI) } else { - HideNoteMarker(); // note marker + HideNoteOverlay(); // note marker // in LOK case this could be triggered on every action from other view (doc_setView) // we don't want to hide tooltip only because other view did some action @@ -1232,7 +1232,7 @@ bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt) bool bAnyEdit = pScMod->IsInputMode(); // only characters & backspace bool bDraw = IsDrawTextEdit(); - HideNoteMarker(); // note marker + HideNoteOverlay(); // note marker // don't do extra HideCursor/ShowCursor calls if EnterHandler will switch to a different sheet bool bOnRefSheet = ( GetViewData().GetRefTabNo() == GetViewData().GetTabNo() ); diff --git a/sc/source/ui/view/tabvwsh5.cxx b/sc/source/ui/view/tabvwsh5.cxx index ba947d7aa43c..cab987534122 100644 --- a/sc/source/ui/view/tabvwsh5.cxx +++ b/sc/source/ui/view/tabvwsh5.cxx @@ -76,7 +76,7 @@ void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) if (nParts & ( PaintPartFlags::Left | PaintPartFlags::Top )) // only if widths or heights changed UpdateAllOverlays(); - HideNoteMarker(); + HideNoteOverlay(); } } else if (rHint.GetId() == SfxHintId::ScEditView) // create Edit-View @@ -90,7 +90,7 @@ void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) SCCOL nCol = pEditViewHint->GetCol(); SCROW nRow = pEditViewHint->GetRow(); { - HideNoteMarker(); + HideNoteOverlay(); MakeEditView( pEditViewHint->GetEngine(), nCol, nRow ); diff --git a/sc/source/ui/view/viewfun6.cxx b/sc/source/ui/view/viewfun6.cxx index 0c614b80aa8c..b405c01f743a 100644 --- a/sc/source/ui/view/viewfun6.cxx +++ b/sc/source/ui/view/viewfun6.cxx @@ -498,7 +498,7 @@ void ScViewFunc::InsertCurrentTime(SvNumFormatType nReqFmt, const OUString& rUnd void ScViewFunc::ShowNote( bool bShow ) { if( bShow ) - HideNoteMarker(); + HideNoteOverlay(); const ScViewData& rViewData = GetViewData(); ScAddress aPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() ); // show note moved to ScDocFunc, to be able to use it in notesuno.cxx @@ -532,7 +532,7 @@ void ScViewFunc::EditNote() return; // hide temporary note caption - HideNoteMarker(); + HideNoteOverlay(); // show caption object without changing internal visibility state pNote->ShowCaptionTemp( aPos );