/* -*- 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 <notemark.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>
#include <officecfg/Office/Calc.hxx>
#define SC_NOTEOVERLAY_TIME 800
#define SC_NOTEOVERLAY_SHORT 70
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)
{
SetTimeout(bForce ? SC_NOTEOVERLAY_SHORT : SC_NOTEOVERLAY_TIME);
Start();
}
const drawinglayer::primitive2d::Primitive2DContainer& ScNoteOverlay::getOrCreatePrimitive2DSequence()
{
if (!maSequence.empty())
// sequence already created, return it
return maSequence;
// get some local data ptrs
ScViewData& rViewData(mrScGridWindow.getViewData());
ScDocument& rDoc(rViewData.GetDocument());
ScDrawLayer* pScDrawLayer(rDoc.GetDrawLayer());
// use existing SdrPage - old version did allocate a SdrModel/SdrPage
// for every visualization
SdrPage* pSdrPage(pScDrawLayer->GetPage(0));
if (nullptr == pSdrPage)
return maSequence;
// 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());
// 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);
// cleanup: remove again immediately
pSdrPage->NbcRemoveObject(mxObject->GetOrdNum());
// show the visualization with slight transparency
static bool bUseTransparency(officecfg::Office::Calc::Content::Display::NoteTransparency::get());
if (bUseTransparency && !maSequence.empty())
{
maSequence = drawinglayer::primitive2d::Primitive2DContainer{
rtl::Reference<drawinglayer::primitive2d::UnifiedTransparencePrimitive2D>(
new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(maSequence), 0.1))};
}
}
// restore changed state of DrawingLayer
pScDrawLayer->SetChanged(bModelChanged);
return maSequence;
}
tools::Rectangle ScNoteOverlay::calculateVisibleRectangle()
{
// 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");
/* 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());
if ((&mrScGridWindow == pRight) || (&mrScGridWindow == pDiagonal))
aOrigin.AdjustX(aLeftSize.Width());
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);
}
void ScNoteOverlay::createOverlaySubContent(
ScGridWindow* pTarget,
const basegfx::B2DHomMatrix& rTransformToPixels,
const basegfx::B2DPoint& rTopLeft)
{
// 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 its 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 ScNoteOverlay::createAdditionalRepresentations()
{
// 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;
// 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;
{
// 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 ScNoteOverlay::Invoke()
{
if (0 != maNoteOverlayGroup.count())
// already visualized, done
return;
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager(
mrScGridWindow.getOverlayManager());
if (!xOverlayManager.is())
// no OverlayManager, no display
return;
if (getOrCreatePrimitive2DSequence().empty())
// no visualization data, no display
return;
// 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 decompose
// 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: */
↑ V1053 Calling the 'Start' virtual function in the constructor may lead to unexpected result at runtime.