diff options
Diffstat (limited to 'svx/source/sdr/contact')
45 files changed, 10101 insertions, 0 deletions
diff --git a/svx/source/sdr/contact/displayinfo.cxx b/svx/source/sdr/contact/displayinfo.cxx new file mode 100644 index 0000000000..1c76b70afc --- /dev/null +++ b/svx/source/sdr/contact/displayinfo.cxx @@ -0,0 +1,78 @@ +/* -*- 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/sdr/contact/displayinfo.hxx> + + +namespace sdr::contact +{ + DisplayInfo::DisplayInfo() + : maProcessLayers(true), // init layer info with all bits set to draw everything on default + mbControlLayerProcessingActive(false), + mbGhostedDrawModeActive(false), + mbSubContentActive(false) + { + } + + // Access to LayerInfos (which layers to process) + void DisplayInfo::SetProcessLayers(const SdrLayerIDSet& rSet) + { + maProcessLayers = rSet; + } + + // access to RedrawArea + void DisplayInfo::SetRedrawArea(const vcl::Region& rRegion) + { + maRedrawArea = rRegion; + } + + void DisplayInfo::SetWriterPageFrame(basegfx::B2IRectangle const& rPageFrame) + { + m_WriterPageFrame = rPageFrame; + } + + void DisplayInfo::SetControlLayerProcessingActive(bool bDoProcess) + { + if(mbControlLayerProcessingActive != bDoProcess) + { + mbControlLayerProcessingActive = bDoProcess; + } + } + + void DisplayInfo::ClearGhostedDrawMode() + { + mbGhostedDrawModeActive = false; + } + + void DisplayInfo::SetGhostedDrawMode() + { + mbGhostedDrawModeActive = true; + } + + void DisplayInfo::SetSubContentActive(bool bNew) + { + if(mbSubContentActive != bNew) + { + mbSubContentActive = bNew; + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/objectcontact.cxx b/svx/source/sdr/contact/objectcontact.cxx new file mode 100644 index 0000000000..f36c5412b1 --- /dev/null +++ b/svx/source/sdr/contact/objectcontact.cxx @@ -0,0 +1,234 @@ +/* -*- 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/sdr/contact/objectcontact.hxx> +#include <tools/debug.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> + +using namespace com::sun::star; + +namespace sdr::contact { + +bool ObjectContact::supportsGridOffsets() const +{ + // default does not support GridOffset + return false; +} + +void ObjectContact::calculateGridOffsetForViewObjectContact( + basegfx::B2DVector& /*rTarget*/, + const ViewObjectContact& /*rClient*/) const +{ + // default does not on-demand calculate GridOffset +} + +void ObjectContact::calculateGridOffsetForB2DRange( + basegfx::B2DVector& /*rTarget*/, + const basegfx::B2DRange& /*rB2DRange*/) const +{ + // default does not on-demand calculate GridOffset +} + +ObjectContact::ObjectContact() +: mpViewObjectContactRedirector(nullptr), + mbIsPreviewRenderer(false) +{ +} + +ObjectContact::~ObjectContact() COVERITY_NOEXCEPT_FALSE +{ + // get rid of all registered contacts + // #i84257# To avoid that each 'delete pCandidate' again uses + // the local RemoveViewObjectContact with a search and removal in the + // vector, simply copy and clear local vector. + std::vector< ViewObjectContact* > aLocalVOCList; + aLocalVOCList.swap(maViewObjectContactVector); + + for (const auto & pCandidate : aLocalVOCList) + // ViewObjectContacts only make sense with View and Object contacts. + // When the contact to the SdrObject is deleted like in this case, + // all ViewObjectContacts can be deleted, too. + delete pCandidate; + + // assert when there were new entries added during deletion + DBG_ASSERT(maViewObjectContactVector.empty(), "Corrupted ViewObjectContactList (!)"); +} + +// LazyInvalidate request. Default implementation directly handles +// this by calling back triggerLazyInvalidate() at the VOC +void ObjectContact::setLazyInvalidate(ViewObjectContact& rVOC) +{ + rVOC.triggerLazyInvalidate(); +} + +// call this to support evtl. preparations for repaint. Default does nothing +void ObjectContact::PrepareProcessDisplay() +{ +} + +// A new ViewObjectContact was created and shall be remembered. +void ObjectContact::AddViewObjectContact(ViewObjectContact& rVOContact) +{ + maViewObjectContactVector.push_back(&rVOContact); +} + +// A ViewObjectContact was deleted and shall be forgotten. +void ObjectContact::RemoveViewObjectContact(ViewObjectContact& rVOContact) +{ + std::vector< ViewObjectContact* >::iterator aFindResult = std::find(maViewObjectContactVector.begin(), maViewObjectContactVector.end(), &rVOContact); + + if(aFindResult != maViewObjectContactVector.end()) + { + maViewObjectContactVector.erase(aFindResult); + } +} + +// Process the whole displaying +void ObjectContact::ProcessDisplay(DisplayInfo& /*rDisplayInfo*/) +{ + // default does nothing +} + +// test if visualizing of entered groups is switched on at all +bool ObjectContact::DoVisualizeEnteredGroup() const +{ + // Do not do that as default + return false; +} + +// get active group's (the entered group) ViewContact +const ViewContact* ObjectContact::getActiveViewContact() const +{ + // default has no active VC + return nullptr; +} + +// Invalidate given rectangle at the window/output which is represented by +// this ObjectContact. +void ObjectContact::InvalidatePartOfView(const basegfx::B2DRange& /*rRange*/) const +{ + // nothing to do here in the default version +} + +// Get info about the need to visualize GluePoints +bool ObjectContact::AreGluePointsVisible() const +{ + return false; +} + +// check if text animation is allowed. Default is sal_true. +bool ObjectContact::IsTextAnimationAllowed() const +{ + return true; +} + +// check if graphic animation is allowed. Default is sal_true. +bool ObjectContact::IsGraphicAnimationAllowed() const +{ + return true; +} + +void ObjectContact::SetViewObjectContactRedirector(ViewObjectContactRedirector* pNew) +{ + if(mpViewObjectContactRedirector != pNew) + { + mpViewObjectContactRedirector = pNew; + } +} + +// print? Default is false +bool ObjectContact::isOutputToPrinter() const +{ + return false; +} + +// display page decoration? Default is true +bool ObjectContact::isPageDecorationActive() const +{ + return true; +} + +// display mster page content (ViewContactOfMasterPage)? Default is true +bool ObjectContact::isMasterPageActive() const +{ + return true; +} + +// recording MetaFile? Default is false +bool ObjectContact::isOutputToRecordingMetaFile() const +{ + return false; +} + +// pdf export? Default is false +bool ObjectContact::isOutputToPDFFile() const +{ + return false; +} + +bool ObjectContact::isExportTaggedPDF() const +{ + return false; +} + +::vcl::PDFExtOutDevData const* ObjectContact::GetPDFExtOutDevData() const +{ + return nullptr; +} + +// gray display mode +bool ObjectContact::isDrawModeGray() const +{ + return false; +} + +// high contrast display mode +bool ObjectContact::isDrawModeHighContrast() const +{ + return false; +} + +// access to SdrPageView. Default implementation returns NULL +SdrPageView* ObjectContact::TryToGetSdrPageView() const +{ + return nullptr; +} + +// access to OutputDevice. Default implementation returns NULL +OutputDevice* ObjectContact::TryToGetOutputDevice() const +{ + return nullptr; +} + +void ObjectContact::resetAllGridOffsets() +{ + const sal_uInt32 nVOCCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nVOCCount; a++) + { + ViewObjectContact* pVOC(getViewObjectContact(a)); + assert(pVOC && "ObjectContact: ViewObjectContact list Corrupt (!)"); + pVOC->resetGridOffset(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx new file mode 100644 index 0000000000..d20b1426e6 --- /dev/null +++ b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx @@ -0,0 +1,207 @@ +/* -*- 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 <sdr/contact/objectcontactofobjlistpainter.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdobj.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <svx/unoapi.hxx> +#include <tools/debug.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <memory> + +namespace sdr::contact { + +ObjectContactPainter::ObjectContactPainter() +{ +} + +// The destructor. +ObjectContactPainter::~ObjectContactPainter() +{ +} + +sal_uInt32 ObjectContactOfObjListPainter::GetPaintObjectCount() const +{ + return maStartObjects.size(); +} + +ViewContact& ObjectContactOfObjListPainter::GetPaintObjectViewContact(sal_uInt32 nIndex) +{ + const SdrObject* pObj = maStartObjects[nIndex]; + DBG_ASSERT(pObj, "ObjectContactOfObjListPainter: Corrupt SdrObjectVector (!)"); + return pObj->GetViewContact(); +} + +ObjectContactOfObjListPainter::ObjectContactOfObjListPainter( + OutputDevice& rTargetDevice, + SdrObjectVector&& rObjects, + const SdrPage* pProcessedPage) +: mrTargetOutputDevice(rTargetDevice), + maStartObjects(std::move(rObjects)), + mpProcessedPage(pProcessedPage) +{ +} + +ObjectContactOfObjListPainter::~ObjectContactOfObjListPainter() +{ +} + +// Process the whole displaying +void ObjectContactOfObjListPainter::ProcessDisplay(DisplayInfo& rDisplayInfo) +{ + const sal_uInt32 nCount(GetPaintObjectCount()); + + if(!nCount) + return; + + OutputDevice* pTargetDevice = TryToGetOutputDevice(); + + if(!pTargetDevice) + return; + + // update current ViewInformation2D at the ObjectContact + const GDIMetaFile* pMetaFile = pTargetDevice->GetConnectMetaFile(); + const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); + basegfx::B2DRange aViewRange; + + // create ViewRange + if(!bOutputToRecordingMetaFile) + { + // use visible pixels, but transform to world coordinates + const Size aOutputSizePixel(pTargetDevice->GetOutputSizePixel()); + aViewRange = ::basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight()); + aViewRange.transform(pTargetDevice->GetInverseViewTransformation()); + } + + // update local ViewInformation2D + drawinglayer::geometry::ViewInformation2D aNewViewInformation2D; + aNewViewInformation2D.setViewTransformation(pTargetDevice->GetViewTransformation()); + aNewViewInformation2D.setViewport(aViewRange); + aNewViewInformation2D.setVisualizedPage(GetXDrawPageForSdrPage(const_cast< SdrPage* >(mpProcessedPage))); + updateViewInformation2D(aNewViewInformation2D); + + // collect primitive data in a sequence; this will already use the updated ViewInformation2D + drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence; + + for(sal_uInt32 a(0); a < nCount; a++) + { + const ViewObjectContact& rViewObjectContact = GetPaintObjectViewContact(a).GetViewObjectContact(*this); + + rViewObjectContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence); + } + + // if there is something to show, use a vclProcessor to render it + if(!xPrimitiveSequence.empty()) + { + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createProcessor2DFromOutputDevice( + *pTargetDevice, + getViewInformation2D())); + + pProcessor2D->process(xPrimitiveSequence); + } +} + +// recording MetaFile? +bool ObjectContactOfObjListPainter::isOutputToRecordingMetaFile() const +{ + GDIMetaFile* pMetaFile = mrTargetOutputDevice.GetConnectMetaFile(); + return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); +} + +// pdf export? +bool ObjectContactOfObjListPainter::isOutputToPDFFile() const +{ + return OUTDEV_PDF == mrTargetOutputDevice.GetOutDevType(); +} + +bool ObjectContactOfObjListPainter::isExportTaggedPDF() const +{ + if (isOutputToPDFFile()) + { + vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>( + mrTargetOutputDevice.GetExtOutDevData())); + + if (nullptr != pPDFExtOutDevData) + { + return pPDFExtOutDevData->GetIsExportTaggedPDF(); + } + } + return false; +} + +::vcl::PDFExtOutDevData const* ObjectContactOfObjListPainter::GetPDFExtOutDevData() const +{ + if (!isOutputToPDFFile()) + { + return nullptr; + } + vcl::PDFExtOutDevData *const pPDFExtOutDevData( + dynamic_cast<vcl::PDFExtOutDevData*>(mrTargetOutputDevice.GetExtOutDevData())); + return pPDFExtOutDevData; +} + +OutputDevice* ObjectContactOfObjListPainter::TryToGetOutputDevice() const +{ + return &mrTargetOutputDevice; +} + +sal_uInt32 ObjectContactOfPagePainter::GetPaintObjectCount() const +{ + return (GetStartPage() ? 1 : 0); +} + +ViewContact& ObjectContactOfPagePainter::GetPaintObjectViewContact(sal_uInt32 /*nIndex*/) +{ + DBG_ASSERT(GetStartPage(), "ObjectContactOfPagePainter::GetPaintObjectViewContact: no StartPage set (!)"); + return GetStartPage()->GetViewContact(); +} + +ObjectContactOfPagePainter::ObjectContactOfPagePainter( + ObjectContact& rOriginalObjectContact) +: mrOriginalObjectContact(rOriginalObjectContact) +{ +} + +ObjectContactOfPagePainter::~ObjectContactOfPagePainter() +{ +} + +void ObjectContactOfPagePainter::SetStartPage(const SdrPage* pPage) +{ + if(pPage != GetStartPage()) + { + mxStartPage = const_cast< SdrPage* >(pPage); // no tools::WeakReference<SdrPage> available to hold a const SdrPage* + } +} + +OutputDevice* ObjectContactOfPagePainter::TryToGetOutputDevice() const +{ + return mrOriginalObjectContact.TryToGetOutputDevice(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/objectcontactofpageview.cxx b/svx/source/sdr/contact/objectcontactofpageview.cxx new file mode 100644 index 0000000000..c777d069ea --- /dev/null +++ b/svx/source/sdr/contact/objectcontactofpageview.cxx @@ -0,0 +1,490 @@ +/* -*- 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_feature_desktop.h> + +#include <svx/sdr/contact/objectcontactofpageview.hxx> +#include <sdr/contact/viewobjectcontactofunocontrol.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdpage.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/svdview.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/animation/objectanimator.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <osl/diagnose.h> +#include <svx/unoapi.hxx> +#include <unotools/configmgr.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/pdfextoutdevdata.hxx> +#include <comphelper/lok.hxx> + +#include <memory> + +using namespace com::sun::star; + +namespace sdr::contact +{ + // internal access to SdrPage of SdrPageView + SdrPage* ObjectContactOfPageView::GetSdrPage() const + { + return GetPageWindow().GetPageView().GetPage(); + } + + ObjectContactOfPageView::ObjectContactOfPageView( + SdrPageWindow& rPageWindow, const char *pDebugName) + : Idle(pDebugName) + , mrPageWindow(rPageWindow) + { + // init PreviewRenderer flag + setPreviewRenderer(static_cast<SdrPaintView&>(rPageWindow.GetPageView().GetView()).IsPreviewRenderer()); + + // init timer + SetPriority(TaskPriority::HIGH_IDLE); + Stop(); + } + + ObjectContactOfPageView::~ObjectContactOfPageView() + { + // execute missing LazyInvalidates and stop timer + Invoke(); + } + + // LazyInvalidate request. Take action. + void ObjectContactOfPageView::setLazyInvalidate(ViewObjectContact& /*rVOC*/) + { + // do NOT call parent, but remember that something is to do by + // starting the LazyInvalidateTimer + Start(); + } + + // call this to support evtl. preparations for repaint + void ObjectContactOfPageView::PrepareProcessDisplay() + { + if(IsActive()) + // there are still non-triggered LazyInvalidate events, trigger these + Invoke(); + } + + // From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism + void ObjectContactOfPageView::Invoke() + { + // stop the timer + Stop(); + + // invalidate all LazyInvalidate VOCs new situations + const sal_uInt32 nVOCCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nVOCCount; a++) + { + ViewObjectContact* pCandidate = getViewObjectContact(a); + pCandidate->triggerLazyInvalidate(); + } + } + + // Process the whole displaying + void ObjectContactOfPageView::ProcessDisplay(DisplayInfo& rDisplayInfo) + { + const SdrPage* pStartPage = GetSdrPage(); + + if(pStartPage && !rDisplayInfo.GetProcessLayers().IsEmpty()) + { + const ViewContact& rDrawPageVC = pStartPage->GetViewContact(); + + if(rDrawPageVC.GetObjectCount()) + { + DoProcessDisplay(rDisplayInfo); + } + } + } + + // Process the whole displaying. Only use given DisplayInfo, do not access other + // OutputDevices then the given ones. + void ObjectContactOfPageView::DoProcessDisplay(DisplayInfo& rDisplayInfo) + { + OutputDevice& rTargetOutDev = GetPageWindow().GetPaintWindow().GetTargetOutputDevice(); + const Size aOutputSizePixel(rTargetOutDev.GetOutputSizePixel()); + if (!isOutputToRecordingMetaFile() // do those have outdev too? + && (0 == aOutputSizePixel.getWidth() || + 0 == aOutputSizePixel.getHeight())) + { + return; + } + + // visualize entered group when that feature is switched on and it's not + // a print output. #i29129# No ghosted display for printing. + bool bVisualizeEnteredGroup(DoVisualizeEnteredGroup() && !isOutputToPrinter()); + + // Visualize entered groups: Set to ghosted as default + // start. Do this only for the DrawPage, not for MasterPages + if(bVisualizeEnteredGroup) + { + rDisplayInfo.SetGhostedDrawMode(); + } + + // #114359# save old and set clip region + OutputDevice* pOutDev = TryToGetOutputDevice(); + OSL_ENSURE(nullptr != pOutDev, "ObjectContactOfPageView without OutDev, someone has overridden TryToGetOutputDevice wrong (!)"); + bool bClipRegionPushed(false); + const vcl::Region& rRedrawArea(rDisplayInfo.GetRedrawArea()); + + // tdf#153102 using the given RedrawArea is needed e.g. for Writer's + // visual clipping against PageBounds (also for android viewer) + if(!rRedrawArea.IsEmpty()) + { + bClipRegionPushed = true; + pOutDev->Push(vcl::PushFlags::CLIPREGION); + pOutDev->IntersectClipRegion(rRedrawArea); + } + + // Get start node and process DrawPage contents + const ViewObjectContact& rDrawPageVOContact = GetSdrPage()->GetViewContact().GetViewObjectContact(*this); + + // update current ViewInformation2D at the ObjectContact + const double fCurrentTime(getPrimitiveAnimator().GetTime()); + basegfx::B2DRange aViewRange; + + // create ViewRange + if(isOutputToRecordingMetaFile()) + { + if (!rDisplayInfo.GetRedrawArea().IsEmpty()) + { + // #i98402# if it's a PDF export, set the ClipRegion as ViewRange. This is + // mainly because SW does not use DrawingLayer Page-Oriented and if not doing this, + // all existing objects will be collected as primitives and processed. + // OD 2009-03-05 #i99876# perform the same also for SW on printing. + // fdo#78149 same thing also needed for plain MetaFile + // export, so why not do it always + const tools::Rectangle aLogicClipRectangle(rDisplayInfo.GetRedrawArea().GetBoundRect()); + + aViewRange = vcl::unotools::b2DRectangleFromRectangle(aLogicClipRectangle); + } + } + else + { + // use visible pixels, but transform to world coordinates + aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight()); + // if a clip region is set, use it + if(!rDisplayInfo.GetRedrawArea().IsEmpty()) + { + // get logic clip range and create discrete one + const tools::Rectangle aLogicClipRectangle(rDisplayInfo.GetRedrawArea().GetBoundRect()); + basegfx::B2DRange aDiscreteClipRange = vcl::unotools::b2DRectangleFromRectangle(aLogicClipRectangle); + aDiscreteClipRange.transform(rTargetOutDev.GetViewTransformation()); + + // align the discrete one to discrete boundaries (pixel bounds). Also + // expand X and Y max by one due to Rectangle definition source + aDiscreteClipRange.expand(basegfx::B2DTuple( + floor(aDiscreteClipRange.getMinX()), + floor(aDiscreteClipRange.getMinY()))); + aDiscreteClipRange.expand(basegfx::B2DTuple( + 1.0 + ceil(aDiscreteClipRange.getMaxX()), + 1.0 + ceil(aDiscreteClipRange.getMaxY()))); + + // intersect current ViewRange with ClipRange + aViewRange.intersect(aDiscreteClipRange); + } + + // transform to world coordinates + aViewRange.transform(rTargetOutDev.GetInverseViewTransformation()); + } + + // update local ViewInformation2D + drawinglayer::geometry::ViewInformation2D aNewViewInformation2D; + aNewViewInformation2D.setViewTransformation(rTargetOutDev.GetViewTransformation()); + aNewViewInformation2D.setViewport(aViewRange); + aNewViewInformation2D.setVisualizedPage(GetXDrawPageForSdrPage(GetSdrPage())); + aNewViewInformation2D.setViewTime(fCurrentTime); + updateViewInformation2D(aNewViewInformation2D); + + drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence; + +#if HAVE_FEATURE_DESKTOP || defined( ANDROID ) + // get whole Primitive2DContainer; this will already make use of updated ViewInformation2D + // and may use the MapMode from the Target OutDev in the DisplayInfo + rDrawPageVOContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence); +#else + // Hmm, !HAVE_FEATURE_DESKTOP && !ANDROID means iOS, + // right? But does it makes sense to use a different code + // path for iOS than for Android; both use tiled rendering + // etc now. + + // HACK: this only works when we are drawing sdr shapes via + // drawinglayer; but it can happen that the hierarchy contains + // more than just the shapes, and then it fails. + // + // This is good enough for the tiled rendering for the moment, but + // we need to come up with the real solution shortly. + + // Only get the expensive hierarchy if we can be sure that the + // returned sequence won't be empty anyway. + bool bGetHierarchy = rRedrawArea.IsEmpty(); + if (!bGetHierarchy) + { + // Not empty? Then not doing a full redraw, check if + // getPrimitive2DSequenceHierarchy() is still needed. + for (const rtl::Reference<SdrObject>& pObject : *GetSdrPage()) + { + if (rRedrawArea.Overlaps(pObject->GetCurrentBoundRect())) + { + bGetHierarchy = true; + break; + } + } + } + + if (bGetHierarchy) + // get whole Primitive2DContainer; this will already make use of updated ViewInformation2D + // and may use the MapMode from the Target OutDev in the DisplayInfo + rDrawPageVOContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence); +#endif + + // if there is something to show, use a primitive processor to render it. There + // is a choice between VCL and Canvas processors currently. The decision is made in + // createProcessor2DFromOutputDevice and takes into account things like the + // Target is a MetaFile, a VDev or something else. The Canvas renderer is triggered + // currently using the shown boolean. Canvas is not yet the default. + if(!xPrimitiveSequence.empty()) + { + // prepare OutputDevice (historical stuff, maybe soon removed) + rDisplayInfo.ClearGhostedDrawMode(); // reset, else the VCL-paint with the processor will not do the right thing + pOutDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default); // reset, default is no BiDi/RTL + // create renderer + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( + drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rTargetOutDev, getViewInformation2D())); + pProcessor2D->process(xPrimitiveSequence); + } + + // #114359# restore old ClipReghion + if(bClipRegionPushed) + { + pOutDev->Pop(); + } + + // Visualize entered groups: Reset to original DrawMode + if(bVisualizeEnteredGroup) + { + rDisplayInfo.ClearGhostedDrawMode(); + } + } + + // test if visualizing of entered groups is switched on at all + bool ObjectContactOfPageView::DoVisualizeEnteredGroup() const + { + return true; + } + + // get active group's (the entered group) ViewContact + const ViewContact* ObjectContactOfPageView::getActiveViewContact() const + { + SdrObjList* pActiveGroupList = GetPageWindow().GetPageView().GetObjList(); + + if(pActiveGroupList) + { + // tdf#122735 + // Here it is necessary to check for SdrObject 1st, that may + // return nullptr if it is not a SdrObject/SdrObjGroup. + // Checking for SrPage OTOH will *always* try to return + // something useful due to SdrObjGroup::getSdrPageFromSdrObjList + // using getSdrPageFromSdrObject which will recursively go up the + // hierarchy to get the SdrPage the SdrObject belongs to, so + // this will *not* be nullptr for e.g. a SdrObjGroup if the + // SdrObjGroup is inserted to a SdrPage. + // NOTE: It is also possible to use dynamic_cast<SdrObjGroup*> + // here, but getSdrObjectFromSdrObjList and + // getSdrPageFromSdrObjListexist to not need to do that + SdrObject* pSdrObject(pActiveGroupList->getSdrObjectFromSdrObjList()); + + if(nullptr != pSdrObject) + { + // It is a group object + return &(pSdrObject->GetViewContact()); + } + else + { + SdrPage* pSdrPage(pActiveGroupList->getSdrPageFromSdrObjList()); + + if(nullptr != pSdrPage) + { + // It's a Page itself + return &(pSdrPage->GetViewContact()); + } + } + } + else if(GetSdrPage()) + { + // use page of associated SdrPageView + return &(GetSdrPage()->GetViewContact()); + } + + return nullptr; + } + + // Invalidate given rectangle at the window/output which is represented by + // this ObjectContact. + void ObjectContactOfPageView::InvalidatePartOfView(const basegfx::B2DRange& rRange) const + { + // invalidate at associated PageWindow + GetPageWindow().InvalidatePageWindow(rRange); + } + + // Get info about the need to visualize GluePoints + bool ObjectContactOfPageView::AreGluePointsVisible() const + { + bool bTiledRendering = comphelper::LibreOfficeKit::isActive(); + return !bTiledRendering && GetPageWindow().GetPageView().GetView().ImpIsGlueVisible(); + } + + // check if text animation is allowed. + bool ObjectContactOfPageView::IsTextAnimationAllowed() const + { + if (utl::ConfigManager::IsFuzzing()) + return true; + return SvtAccessibilityOptions::GetIsAllowAnimatedText(); + } + + // check if graphic animation is allowed. + bool ObjectContactOfPageView::IsGraphicAnimationAllowed() const + { + if (utl::ConfigManager::IsFuzzing()) + return true; + + // Related tdf#156630 respect system animation setting + return SvtAccessibilityOptions::GetIsAllowAnimatedGraphics() && !MiscSettings::GetUseReducedAnimation(); + } + + // print? + bool ObjectContactOfPageView::isOutputToPrinter() const + { + return (OUTDEV_PRINTER == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType()); + } + + // display page decoration? Default is true + bool ObjectContactOfPageView::isPageDecorationActive() const + { + return GetPageWindow().GetPageView().GetView().IsPageDecorationAllowed(); + } + + // display mster page content (ViewContactOfMasterPage)? Default is true + bool ObjectContactOfPageView::isMasterPageActive() const + { + return GetPageWindow().GetPageView().GetView().IsMasterPageVisualizationAllowed(); + } + + // recording MetaFile? + bool ObjectContactOfPageView::isOutputToRecordingMetaFile() const + { + GDIMetaFile* pMetaFile = mrPageWindow.GetPaintWindow().GetOutputDevice().GetConnectMetaFile(); + return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause()); + } + + // pdf export? + bool ObjectContactOfPageView::isOutputToPDFFile() const + { + return OUTDEV_PDF == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType(); + } + + bool ObjectContactOfPageView::isExportTaggedPDF() const + { + if (isOutputToPDFFile()) + { + vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>( + mrPageWindow.GetPaintWindow().GetOutputDevice().GetExtOutDevData())); + + if (nullptr != pPDFExtOutDevData) + { + return pPDFExtOutDevData->GetIsExportTaggedPDF(); + } + } + return false; + } + + ::vcl::PDFExtOutDevData const* ObjectContactOfPageView::GetPDFExtOutDevData() const + { + if (!isOutputToPDFFile()) + { + return nullptr; + } + vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>( + mrPageWindow.GetPaintWindow().GetOutputDevice().GetExtOutDevData())); + return pPDFExtOutDevData; + } + + // gray display mode + bool ObjectContactOfPageView::isDrawModeGray() const + { + const DrawModeFlags nDrawMode(mrPageWindow.GetPaintWindow().GetOutputDevice().GetDrawMode()); + return (nDrawMode == (DrawModeFlags::GrayLine|DrawModeFlags::GrayFill|DrawModeFlags::BlackText|DrawModeFlags::GrayBitmap|DrawModeFlags::GrayGradient)); + } + + // high contrast display mode + bool ObjectContactOfPageView::isDrawModeHighContrast() const + { + const DrawModeFlags nDrawMode(mrPageWindow.GetPaintWindow().GetOutputDevice().GetDrawMode()); + return (nDrawMode == (DrawModeFlags::SettingsLine|DrawModeFlags::SettingsFill|DrawModeFlags::SettingsText|DrawModeFlags::SettingsGradient)); + } + + // access to SdrPageView + SdrPageView* ObjectContactOfPageView::TryToGetSdrPageView() const + { + return &(mrPageWindow.GetPageView()); + } + + + // access to OutputDevice + OutputDevice* ObjectContactOfPageView::TryToGetOutputDevice() const + { + SdrPreRenderDevice* pPreRenderDevice = mrPageWindow.GetPaintWindow().GetPreRenderDevice(); + + if(pPreRenderDevice) + { + return &(pPreRenderDevice->GetPreRenderDevice()); + } + else + { + return &(mrPageWindow.GetPaintWindow().GetOutputDevice()); + } + } + + // set all UNO controls displayed in the view to design/alive mode + void ObjectContactOfPageView::SetUNOControlsDesignMode( bool _bDesignMode ) const + { + const sal_uInt32 nCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const ViewObjectContact* pVOC = getViewObjectContact(a); + const ViewObjectContactOfUnoControl* pUnoObjectVOC = dynamic_cast< const ViewObjectContactOfUnoControl* >(pVOC); + + if(pUnoObjectVOC) + { + pUnoObjectVOC->setControlDesignMode(_bDesignMode); + } + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/sdrmediawindow.cxx b/svx/source/sdr/contact/sdrmediawindow.cxx new file mode 100644 index 0000000000..086e12cee0 --- /dev/null +++ b/svx/source/sdr/contact/sdrmediawindow.cxx @@ -0,0 +1,174 @@ +/* -*- 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 "sdrmediawindow.hxx" +#include <vcl/transfer.hxx> + +#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx> +#include <vcl/window.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> + +namespace sdr::contact { + + +SdrMediaWindow::SdrMediaWindow( vcl::Window* pParent, ViewObjectContactOfSdrMediaObj& rViewObjContact ) : + ::avmedia::MediaWindow( pParent, false ), + mrViewObjectContactOfSdrMediaObj( rViewObjContact ) +{ +} + + +SdrMediaWindow::~SdrMediaWindow() +{ +} + + +void SdrMediaWindow::MouseMove( const MouseEvent& rMEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow && getWindow() ) + { + const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ), + rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() ); + + pWindow->MouseMove( aTransformedEvent ); + setPointer( pWindow->GetPointer() ); + } +} + + +void SdrMediaWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow && getWindow() ) + { + const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ), + rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() ); + + pWindow->MouseButtonDown( aTransformedEvent ); + } +} + + +void SdrMediaWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow && getWindow() ) + { + const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ), + rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() ); + + pWindow->MouseButtonUp( aTransformedEvent ); + } +} + + +void SdrMediaWindow::KeyInput( const KeyEvent& rKEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow ) + pWindow->KeyInput( rKEvt ); +} + + +void SdrMediaWindow::KeyUp( const KeyEvent& rKEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow ) + pWindow->KeyUp( rKEvt ); +} + + +void SdrMediaWindow::Command( const CommandEvent& rCEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow && getWindow() ) + { + const CommandEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rCEvt.GetMousePosPixel() ) ), + rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData() ); + + pWindow->Command( aTransformedEvent ); + } +} + + +sal_Int8 SdrMediaWindow::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + sal_Int8 nRet = DND_ACTION_NONE; + + if( pWindow ) + { + DropTargetHelper* pDropTargetHelper = dynamic_cast< DropTargetHelper* >( pWindow ); + + if( pDropTargetHelper ) + { + nRet = pDropTargetHelper->AcceptDrop( rEvt ); + } + } + + return nRet; +} + + +sal_Int8 SdrMediaWindow::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + sal_Int8 nRet = DND_ACTION_NONE; + + if( pWindow ) + { + DropTargetHelper* pDropTargetHelper = dynamic_cast< DropTargetHelper* >( pWindow ); + + if( pDropTargetHelper ) + { + nRet = pDropTargetHelper->ExecuteDrop( rEvt ); + } + } + + return nRet; +} + + +void SdrMediaWindow::StartDrag( sal_Int8 nAction, const Point& rPosPixel ) +{ + vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow(); + + if( pWindow ) + { + DragSourceHelper* pDragSourceHelper = dynamic_cast< DragSourceHelper* >( pWindow ); + + if( pDragSourceHelper ) + { + pDragSourceHelper->StartDrag( nAction, rPosPixel ); + } + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/sdrmediawindow.hxx b/svx/source/sdr/contact/sdrmediawindow.hxx new file mode 100644 index 0000000000..fb3cda6fcb --- /dev/null +++ b/svx/source/sdr/contact/sdrmediawindow.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX +#define INCLUDED_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX + +#include <avmedia/mediawindow.hxx> + +namespace sdr::contact { + + +class ViewObjectContactOfSdrMediaObj; + +class SdrMediaWindow : public ::avmedia::MediaWindow +{ +public: + + SdrMediaWindow( vcl::Window* pParent, ViewObjectContactOfSdrMediaObj& rViewObjContact ); + virtual ~SdrMediaWindow() override; + + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + + virtual void KeyInput( const KeyEvent& rKEvt ) override; + virtual void KeyUp( const KeyEvent& rKEvt ) override; + + virtual void Command( const CommandEvent& rCEvt ) override; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + + virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override; + +private: + + ViewObjectContactOfSdrMediaObj& mrViewObjectContactOfSdrMediaObj; +}; + +} + +#endif // INCLUDED_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontact.cxx b/svx/source/sdr/contact/viewcontact.cxx new file mode 100644 index 0000000000..99106d0d6e --- /dev/null +++ b/svx/source/sdr/contact/viewcontact.cxx @@ -0,0 +1,304 @@ +/* -*- 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/sdr/contact/viewcontact.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/color/bcolor.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <osl/diagnose.h> +#include <tools/debug.hxx> + +namespace sdr::contact +{ +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. Default is to create +// a standard ViewObjectContact containing the given ObjectContact and *this +ViewObjectContact& ViewContact::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + return *(new ViewObjectContact(rObjectContact, *this)); +} + +ViewContact::ViewContact() {} + +ViewContact::~ViewContact() { deleteAllVOCs(); } + +void ViewContact::deleteAllVOCs() +{ + // get rid of all VOCs + // #i84257# To avoid that each 'delete pCandidate' again uses + // the local RemoveViewObjectContact with a search and removal in the + // vector, simply copy and clear local vector. + std::vector<ViewObjectContact*> aLocalVOCList; + aLocalVOCList.swap(maViewObjectContactVector); + + for (const auto& pCandidate : aLocalVOCList) + // ViewObjectContacts only make sense with View and Object contacts. + // When the contact to the SdrObject is deleted like in this case, + // all ViewObjectContacts can be deleted, too. + delete pCandidate; + + // assert when there were new entries added during deletion + DBG_ASSERT(maViewObjectContactVector.empty(), "Corrupted ViewObjectContactList in VC (!)"); + + mxViewIndependentPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer(); +} + +// get an Object-specific ViewObjectContact for a specific +// ObjectContact (->View). Always needs to return something. +ViewObjectContact& ViewContact::GetViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = nullptr; + const sal_uInt32 nCount(maViewObjectContactVector.size()); + + // first search if there exists a VOC for the given OC + for (sal_uInt32 a(0); !pRetval && a < nCount; a++) + { + ViewObjectContact* pCandidate = maViewObjectContactVector[a]; + DBG_ASSERT(pCandidate, "Corrupted ViewObjectContactList (!)"); + + if (&(pCandidate->GetObjectContact()) == &rObjectContact) + { + pRetval = pCandidate; + } + } + + if (!pRetval) + { + // create a new one. It's inserted to the local list from the + // ViewObjectContact constructor via AddViewObjectContact() + pRetval = &CreateObjectSpecificViewObjectContact(rObjectContact); + } + + return *pRetval; +} + +// A new ViewObjectContact was created and shall be remembered. +void ViewContact::AddViewObjectContact(ViewObjectContact& rVOContact) +{ + maViewObjectContactVector.push_back(&rVOContact); +} + +// A ViewObjectContact was deleted and shall be forgotten. +void ViewContact::RemoveViewObjectContact(ViewObjectContact& rVOContact) +{ + std::vector<ViewObjectContact*>::iterator aFindResult = std::find( + maViewObjectContactVector.begin(), maViewObjectContactVector.end(), &rVOContact); + + if (aFindResult != maViewObjectContactVector.end()) + { + maViewObjectContactVector.erase(aFindResult); + } +} + +// Test if this ViewContact has ViewObjectContacts at all. This can +// be used to test if this ViewContact is visualized ATM or not +bool ViewContact::HasViewObjectContacts() const +{ + const sal_uInt32 nCount(maViewObjectContactVector.size()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + if (!maViewObjectContactVector[a]->GetObjectContact().IsPreviewRenderer()) + { + return true; + } + } + return false; +} + +// Test if this ViewContact has ViewObjectContacts at all. This can +// be used to test if this ViewContact is visualized ATM or not +bool ViewContact::isAnimatedInAnyViewObjectContact() const +{ + const sal_uInt32 nCount(maViewObjectContactVector.size()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + if (maViewObjectContactVector[a]->isAnimated()) + { + return true; + } + } + + return false; +} + +// Access to possible sub-hierarchy and parent. GetObjectCount() default is 0L +// and GetViewContact default pops up an assert since it's an error if +// GetObjectCount has a result != 0 and it's not overridden. +sal_uInt32 ViewContact::GetObjectCount() const +{ + // no sub-objects + return 0; +} + +ViewContact& ViewContact::GetViewContact(sal_uInt32 /*nIndex*/) const +{ + // This is the default implementation; call would be an error + OSL_FAIL("ViewContact::GetViewContact: This call needs to be overridden when GetObjectCount() " + "can return results != 0 (!)"); + return const_cast<ViewContact&>(*this); +} + +ViewContact* ViewContact::GetParentContact() const +{ + // default has no parent + return nullptr; +} + +void ViewContact::ActionChildInserted(ViewContact& rChild) +{ + // propagate change to all existing visualisations which + // will force a VOC for the new child and invalidate its range + const sal_uInt32 nCount(maViewObjectContactVector.size()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + ViewObjectContact* pCandidate = maViewObjectContactVector[a]; + DBG_ASSERT(pCandidate, + "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)"); + + // take action at all VOCs. At the VOCs ObjectContact the initial + // rectangle will be invalidated at the associated OutputDevice. + pCandidate->ActionChildInserted(rChild); + } +} + +// React on changes of the object of this ViewContact +void ViewContact::ActionChanged() +{ + // propagate change to all existing VOCs. This will invalidate + // all drawn visualisations in all known views + const sal_uInt32 nCount(maViewObjectContactVector.size()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + ViewObjectContact* pCandidate = maViewObjectContactVector[a]; + DBG_ASSERT(pCandidate, + "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)"); + + if (pCandidate) + { + pCandidate->ActionChanged(); + } + } +} + +// access to SdrObject and/or SdrPage. May return 0L like the default +// implementations do. Override as needed. +SdrObject* ViewContact::TryToGetSdrObject() const { return nullptr; } + +// primitive stuff + +void ViewContact::createViewIndependentPrimitive2DSequence( + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // This is the default implementation and should never be called (see header). If this is called, + // someone implemented a ViewContact (VC) visualisation object without defining the visualisation by + // providing a sequence of primitives -> which cannot be correct. + // Since we have no access to any known model data here, the default implementation creates a yellow placeholder + // hairline polygon with a default size of (1000, 1000, 5000, 3000) + OSL_FAIL("ViewContact::createViewIndependentPrimitive2DSequence(): Never call the fallback " + "base implementation, this is always an error (!)"); + basegfx::B2DPolygon aOutline( + basegfx::utils::createPolygonFromRect(basegfx::B2DRange(1000.0, 1000.0, 5000.0, 3000.0))); + const basegfx::BColor aYellow(1.0, 1.0, 0.0); + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aOutline), aYellow)); + + rVisitor.visit(xReference); +} + +void ViewContact::getViewIndependentPrimitive2DContainer( + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + /* Local up-to-date checks. Create new list and compare. + We cannot just always use the new data because the old data has cached bitmaps in it e.g. see the document in tdf#146108. + */ + drawinglayer::primitive2d::Primitive2DContainer xNew; + createViewIndependentPrimitive2DSequence(xNew); + + if (!xNew.empty()) + { + // allow evtl. embedding in object-specific infos, e.g. Name, Title, Description + xNew = embedToObjectSpecificInformation(std::move(xNew)); + } + + if (mxViewIndependentPrimitive2DSequence != xNew) + { + // has changed, copy content + const_cast<ViewContact*>(this)->mxViewIndependentPrimitive2DSequence = std::move(xNew); + } + + // return current Primitive2DContainer + rVisitor.visit(mxViewIndependentPrimitive2DSequence); +} + +// add Gluepoints (if available) +drawinglayer::primitive2d::Primitive2DContainer +ViewContact::createGluePointPrimitive2DSequence() const +{ + // default returns empty reference + return drawinglayer::primitive2d::Primitive2DContainer(); +} + +drawinglayer::primitive2d::Primitive2DContainer ViewContact::embedToObjectSpecificInformation( + drawinglayer::primitive2d::Primitive2DContainer aSource) const +{ + // nothing to do for default + return aSource; +} + +basegfx::B2DRange +ViewContact::getRange(const drawinglayer::geometry::ViewInformation2D& /*rViewInfo2D*/) const +{ + // Return empty range. + return basegfx::B2DRange(); +} + +void ViewContact::flushViewObjectContacts(bool bWithHierarchy) +{ + if (bWithHierarchy) + { + // flush DrawingLayer hierarchy + const sal_uInt32 nCount(GetObjectCount()); + + for (sal_uInt32 a(0); a < nCount; a++) + { + ViewContact& rChild = GetViewContact(a); + rChild.flushViewObjectContacts(bWithHierarchy); + } + } + + // delete local VOCs + deleteAllVOCs(); +} + +void ViewContact::getPrimitive2DSequenceHierarchyOfIndex( + sal_uInt32 a, DisplayInfo& rDisplayInfo, ObjectContact& rObjectContact, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + const ViewObjectContact& rCandidate(GetViewContact(a).GetViewObjectContact(rObjectContact)); + rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor); +} +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3d.cxx b/svx/source/sdr/contact/viewcontactofe3d.cxx new file mode 100644 index 0000000000..8766192157 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3d.cxx @@ -0,0 +1,195 @@ +/* -*- 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 <sdr/contact/viewcontactofe3d.hxx> +#include <sdr/contact/viewobjectcontactofe3d.hxx> +#include <svx/obj3d.hxx> +#include <drawinglayer/primitive2d/embedded3dprimitive2d.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/scene3d.hxx> +#include <drawinglayer/primitive3d/transformprimitive3d.hxx> +#include <drawinglayer/attribute/sdrsceneattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightingattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightattribute3d.hxx> + +namespace { + +const sdr::contact::ViewContactOfE3dScene* tryToFindVCOfE3DScene( + const sdr::contact::ViewContact& rCandidate, + basegfx::B3DHomMatrix& o_rInBetweenObjectTransform) +{ + const sdr::contact::ViewContactOfE3dScene* pSceneParent = + dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(rCandidate.GetParentContact()); + + if(pSceneParent) + { + // each 3d object (including in-between scenes) should have a scene as parent + const sdr::contact::ViewContactOfE3dScene* pSceneParentParent = + dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(pSceneParent->GetParentContact()); + + if(pSceneParentParent) + { + // the parent scene of rCandidate is an in-between scene, call recursively and collect + // the in-between scene's object transformation part in o_rInBetweenObjectTransform + const basegfx::B3DHomMatrix& rSceneParentTransform = pSceneParent->GetE3dScene().GetTransform(); + o_rInBetweenObjectTransform = rSceneParentTransform * o_rInBetweenObjectTransform; + return tryToFindVCOfE3DScene(*pSceneParent, o_rInBetweenObjectTransform); + } + else + { + // the parent scene is the outmost scene + return pSceneParent; + } + } + + // object hierarchy structure is incorrect; no result + return nullptr; +} + +} // end of anonymous namespace + +namespace sdr::contact { + +drawinglayer::primitive2d::Primitive2DContainer ViewContactOfE3d::impCreateWithGivenPrimitive3DContainer( + const drawinglayer::primitive3d::Primitive3DContainer& rxContent3D) const +{ + drawinglayer::primitive2d::Primitive2DContainer xRetval; + + if(!rxContent3D.empty()) + { + // try to get the outmost ViewObjectContactOfE3dScene for this single 3d object, + // the ones on the way there are grouping scenes. Collect the in-between scene's + // transformations to build a correct object transformation for the embedded + // object + basegfx::B3DHomMatrix aInBetweenObjectTransform; + const ViewContactOfE3dScene* pVCOfE3DScene = tryToFindVCOfE3DScene(*this, aInBetweenObjectTransform); + + if(pVCOfE3DScene) + { + basegfx::B3DVector aLightNormal; + const double fShadowSlant(pVCOfE3DScene->getSdrSceneAttribute().getShadowSlant()); + const basegfx::B3DRange& rAllContentRange = pVCOfE3DScene->getAllContentRange3D(); + drawinglayer::geometry::ViewInformation3D aViewInformation3D(pVCOfE3DScene->getViewInformation3D()); + + if(!pVCOfE3DScene->getSdrLightingAttribute().getLightVector().empty()) + { + // get light normal from first light and normalize + aLightNormal = pVCOfE3DScene->getSdrLightingAttribute().getLightVector()[0].getDirection(); + aLightNormal.normalize(); + } + + if(!aInBetweenObjectTransform.isIdentity()) + { + // if aInBetweenObjectTransform is used, create combined ViewInformation3D which + // contains the correct object transformation for the embedded 3d object + aViewInformation3D = drawinglayer::geometry::ViewInformation3D( + aViewInformation3D.getObjectTransformation() * aInBetweenObjectTransform, + aViewInformation3D.getOrientation(), + aViewInformation3D.getProjection(), + aViewInformation3D.getDeviceToView(), + aViewInformation3D.getViewTime(), + aViewInformation3D.getExtendedInformationSequence()); + } + + // create embedded 2d primitive and add. LightNormal and ShadowSlant are needed for evtl. + // 3D shadow extraction for correct B2DRange calculation (shadow is part of the object) + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::Embedded3DPrimitive2D( + rxContent3D, + pVCOfE3DScene->getObjectTransformation(), + std::move(aViewInformation3D), + aLightNormal, + fShadowSlant, + rAllContentRange)); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + + return xRetval; +} + +ViewContactOfE3d::ViewContactOfE3d(E3dObject& rSdrObject) +: ViewContactOfSdrObj(rSdrObject) +{ +} + +ViewContactOfE3d::~ViewContactOfE3d() +{ +} + +drawinglayer::primitive3d::Primitive3DContainer const & ViewContactOfE3d::getVIP3DSWithoutObjectTransform() const +{ + // local up-to-date checks. Create new list and compare. + drawinglayer::primitive3d::Primitive3DContainer xNew(createViewIndependentPrimitive3DContainer()); + + if(mxViewIndependentPrimitive3DContainer != xNew) + { + // has changed, copy content + const_cast< ViewContactOfE3d* >(this)->mxViewIndependentPrimitive3DContainer = xNew; + } + + // return current Primitive2DContainer + return mxViewIndependentPrimitive3DContainer; +} + +drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3d::getViewIndependentPrimitive3DContainer() const +{ + // get sequence without object transform + drawinglayer::primitive3d::Primitive3DContainer xRetval(getVIP3DSWithoutObjectTransform()); + + if(!xRetval.empty()) + { + // add object transform if it's used + const basegfx::B3DHomMatrix& rObjectTransform(GetE3dObject().GetTransform()); + + if(!rObjectTransform.isIdentity()) + { + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::TransformPrimitive3D( + rObjectTransform, + xRetval)); + + xRetval = { xReference }; + } + } + + // return current Primitive2DContainer + return xRetval; +} + +void ViewContactOfE3d::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // also need to create a 2D embedding when the view-independent part is requested, + // see view-dependent part in ViewObjectContactOfE3d::createPrimitive2DSequence + // get 3d primitive vector, isPrimitiveVisible() is done in 3d creator + return rVisitor.visit(impCreateWithGivenPrimitive3DContainer(getViewIndependentPrimitive3DContainer())); +} + +ViewObjectContact& ViewContactOfE3d::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfE3d(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContactOfE3d::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dcube.cxx b/svx/source/sdr/contact/viewcontactofe3dcube.cxx new file mode 100644 index 0000000000..2687aab32f --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dcube.cxx @@ -0,0 +1,88 @@ +/* -*- 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 <sdr/contact/viewcontactofe3dcube.hxx> +#include <svx/cube3d.hxx> +#include <drawinglayer/primitive3d/sdrcubeprimitive3d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive3d/sdrattributecreator3d.hxx> +#include <basegfx/range/b3drange.hxx> + + +namespace sdr::contact +{ + ViewContactOfE3dCube::ViewContactOfE3dCube(E3dCubeObj& rCubeObj) + : ViewContactOfE3d(rCubeObj) + { + } + + ViewContactOfE3dCube::~ViewContactOfE3dCube() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dCube::createViewIndependentPrimitive3DContainer() const + { + drawinglayer::primitive3d::Primitive3DContainer xRetval; + const SfxItemSet& rItemSet = GetE3dCubeObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false)); + + // get cube geometry and use as translation and scaling for unit cube + basegfx::B3DRange aCubeRange; + const basegfx::B3DVector aCubeSize(GetE3dCubeObj().GetCubeSize()); + const basegfx::B3DPoint aCubePosition(GetE3dCubeObj().GetCubePos()); + basegfx::B3DHomMatrix aWorldTransform; + + if(GetE3dCubeObj().GetPosIsCenter()) + { + const basegfx::B3DVector aHalfCubeSize(aCubeSize / 2.0); + aCubeRange.expand(aCubePosition - aHalfCubeSize); + aCubeRange.expand(aCubePosition + aHalfCubeSize); + } + else + { + aCubeRange.expand(aCubePosition); + aCubeRange.expand(aCubePosition + aCubeSize); + } + + // add scale and translate to world transformation + const basegfx::B3DVector abjectRange(aCubeRange.getRange()); + aWorldTransform.scale(abjectRange.getX(), abjectRange.getY(), abjectRange.getZ()); + aWorldTransform.translate(aCubeRange.getMinX(), aCubeRange.getMinY(), aCubeRange.getMinZ()); + + // get 3D Object Attributes + drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet)); + + // calculate texture size to get a perfect mapping for + // the front/back sides + const basegfx::B2DVector aTextureSize(aCubeSize.getX(), aCubeSize.getY()); + + // create primitive and add + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::SdrCubePrimitive3D( + aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute)); + xRetval = { xReference }; + + return xRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dextrude.cxx b/svx/source/sdr/contact/viewcontactofe3dextrude.cxx new file mode 100644 index 0000000000..146a06305d --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dextrude.cxx @@ -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 . + */ + + +#include <sdr/contact/viewcontactofe3dextrude.hxx> +#include <extrud3d.hxx> +#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive3d/sdrattributecreator3d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> + + +namespace sdr::contact +{ + ViewContactOfE3dExtrude::ViewContactOfE3dExtrude(E3dExtrudeObj& rExtrude) + : ViewContactOfE3d(rExtrude) + { + } + + ViewContactOfE3dExtrude::~ViewContactOfE3dExtrude() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dExtrude::createViewIndependentPrimitive3DContainer() const + { + drawinglayer::primitive3d::Primitive3DContainer xRetval; + const SfxItemSet& rItemSet = GetE3dExtrudeObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false)); + + // get extrude geometry + basegfx::B2DPolyPolygon aPolyPolygon(GetE3dExtrudeObj().GetExtrudePolygon()); + + // get 3D Object Attributes + drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet)); + + // calculate texture size; use size of top/bottom cap to get a perfect mapping + // for the caps. The in-between geometry will get a stretched size with a + // relative factor size of caps to extrude depth + const basegfx::B2DRange aRange(basegfx::utils::getRange(aPolyPolygon)); + const basegfx::B2DVector aTextureSize(aRange.getWidth(), aRange.getHeight()); + + // get more data + const double fDepth(static_cast<double>(GetE3dExtrudeObj().GetExtrudeDepth())); + const double fDiagonal(static_cast<double>(GetE3dExtrudeObj().GetPercentDiagonal()) / 100.0); + const double fBackScale(static_cast<double>(GetE3dExtrudeObj().GetPercentBackScale()) / 100.0); + const bool bSmoothNormals(GetE3dExtrudeObj().GetSmoothNormals()); // Plane itself + const bool bSmoothLids(GetE3dExtrudeObj().GetSmoothLids()); // Front/back + const bool bCharacterMode(GetE3dExtrudeObj().GetCharacterMode()); + const bool bCloseFront(GetE3dExtrudeObj().GetCloseFront()); + const bool bCloseBack(GetE3dExtrudeObj().GetCloseBack()); + + // create primitive and add + const basegfx::B3DHomMatrix aWorldTransform; + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::SdrExtrudePrimitive3D( + aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute, + std::move(aPolyPolygon), fDepth, fDiagonal, fBackScale, bSmoothNormals, bSmoothLids, + bCharacterMode, bCloseFront, bCloseBack)); + xRetval = { xReference }; + + return xRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dlathe.cxx b/svx/source/sdr/contact/viewcontactofe3dlathe.cxx new file mode 100644 index 0000000000..a7e73acc58 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dlathe.cxx @@ -0,0 +1,96 @@ +/* -*- 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 <sdr/contact/viewcontactofe3dlathe.hxx> +#include <svx/lathe3d.hxx> +#include <drawinglayer/primitive3d/sdrlatheprimitive3d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive3d/sdrattributecreator3d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + + +namespace sdr::contact +{ + ViewContactOfE3dLathe::ViewContactOfE3dLathe(E3dLatheObj& rLathe) + : ViewContactOfE3d(rLathe) + { + } + + ViewContactOfE3dLathe::~ViewContactOfE3dLathe() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dLathe::createViewIndependentPrimitive3DContainer() const + { + drawinglayer::primitive3d::Primitive3DContainer xRetval; + const SfxItemSet& rItemSet = GetE3dLatheObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false)); + + // get extrude geometry + basegfx::B2DPolyPolygon aPolyPolygon(GetE3dLatheObj().GetPolyPoly2D()); + + // get 3D Object Attributes + drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet)); + + // calculate texture size. Use the polygon length of the longest polygon for + // height and the rotated radius for width (using polygon center) to get a good + // texture mapping + double fPolygonMaxLength(0.0); + + for(auto const& rCandidate : aPolyPolygon) + { + const double fPolygonLength(basegfx::utils::getLength(rCandidate)); + fPolygonMaxLength = std::max(fPolygonMaxLength, fPolygonLength); + } + + const basegfx::B2DRange aPolyPolygonRange(basegfx::utils::getRange(aPolyPolygon)); + const basegfx::B2DVector aTextureSize( + M_PI * fabs(aPolyPolygonRange.getCenter().getX()), // PI * d + fPolygonMaxLength); + + // get more data + const sal_uInt32 nHorizontalSegments(GetE3dLatheObj().GetHorizontalSegments()); + const sal_uInt32 nVerticalSegments(GetE3dLatheObj().GetVerticalSegments()); + const double fDiagonal(static_cast<double>(GetE3dLatheObj().GetPercentDiagonal()) / 100.0); + const double fBackScale(static_cast<double>(GetE3dLatheObj().GetBackScale()) / 100.0); + const double fRotation(basegfx::deg2rad<10>(GetE3dLatheObj().GetEndAngle())); + const bool bSmoothNormals(GetE3dLatheObj().GetSmoothNormals()); // Plane itself + const bool bSmoothLids(GetE3dLatheObj().GetSmoothLids()); // Front/back + const bool bCharacterMode(GetE3dLatheObj().GetCharacterMode()); + const bool bCloseFront(GetE3dLatheObj().GetCloseFront()); + const bool bCloseBack(GetE3dLatheObj().GetCloseBack()); + + // create primitive and add + const basegfx::B3DHomMatrix aWorldTransform; + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::SdrLathePrimitive3D( + aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute, + std::move(aPolyPolygon), nHorizontalSegments, nVerticalSegments, + fDiagonal, fBackScale, fRotation, + bSmoothNormals, bSmoothLids, bCharacterMode, bCloseFront, bCloseBack)); + xRetval = { xReference }; + + return xRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx b/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx new file mode 100644 index 0000000000..f0e2e02d54 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx @@ -0,0 +1,169 @@ +/* -*- 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 <sdr/contact/viewcontactofe3dpolygon.hxx> +#include <polygn3d.hxx> +#include <drawinglayer/primitive3d/sdrpolypolygonprimitive3d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive3d/sdrattributecreator3d.hxx> +#include <basegfx/polygon/b3dpolygon.hxx> +#include <basegfx/polygon/b3dpolypolygontools.hxx> + + +namespace sdr::contact +{ + ViewContactOfE3dPolygon::ViewContactOfE3dPolygon(E3dPolygonObj& rPolygon) + : ViewContactOfE3d(rPolygon) + { + } + + ViewContactOfE3dPolygon::~ViewContactOfE3dPolygon() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dPolygon::createViewIndependentPrimitive3DContainer() const + { + drawinglayer::primitive3d::Primitive3DContainer xRetval; + const SfxItemSet& rItemSet = GetE3dPolygonObj().GetMergedItemSet(); + const bool bSuppressFill(GetE3dPolygonObj().GetLineOnly()); + const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, bSuppressFill)); + + // get extrude geometry + basegfx::B3DPolyPolygon aPolyPolygon3D(GetE3dPolygonObj().GetPolyPolygon3D()); + const basegfx::B3DPolyPolygon aPolyNormals3D(GetE3dPolygonObj().GetPolyNormals3D()); + const basegfx::B2DPolyPolygon aPolyTexture2D(GetE3dPolygonObj().GetPolyTexture2D()); + const bool bNormals(aPolyNormals3D.count() && aPolyNormals3D.count() == aPolyPolygon3D.count()); + const bool bTexture(aPolyTexture2D.count() && aPolyTexture2D.count() == aPolyPolygon3D.count()); + + if(bNormals || bTexture) + { + for(sal_uInt32 a(0); a < aPolyPolygon3D.count(); a++) + { + basegfx::B3DPolygon aCandidate3D(aPolyPolygon3D.getB3DPolygon(a)); + basegfx::B3DPolygon aNormals3D; + basegfx::B2DPolygon aTexture2D; + + if(bNormals) + { + aNormals3D = aPolyNormals3D.getB3DPolygon(a); + } + + if(bTexture) + { + aTexture2D = aPolyTexture2D.getB2DPolygon(a); + } + + for(sal_uInt32 b(0); b < aCandidate3D.count(); b++) + { + if(bNormals) + { + sal_uInt32 nNormalCount = aNormals3D.count(); + if( b < nNormalCount ) + aCandidate3D.setNormal(b, aNormals3D.getB3DPoint(b)); + else if( nNormalCount > 0 ) + aCandidate3D.setNormal(b, aNormals3D.getB3DPoint(0)); + } + if(bTexture) + { + sal_uInt32 nTextureCount = aTexture2D.count(); + if( b < nTextureCount ) + aCandidate3D.setTextureCoordinate(b, aTexture2D.getB2DPoint(b)); + else if( nTextureCount > 0 ) + aCandidate3D.setTextureCoordinate(b, aTexture2D.getB2DPoint(0)); + } + } + + aPolyPolygon3D.setB3DPolygon(a, aCandidate3D); + } + } + + // get 3D Object Attributes + drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet)); + + // calculate texture size + basegfx::B2DVector aTextureSize(1.0, 1.0); + + if(bTexture) + { + // #i98314# + // create texture size from object's size + const basegfx::B3DRange aObjectRange(basegfx::utils::getRange(aPolyPolygon3D)); + + double fWidth(0.0); + double fHeight(0.0); + + // this is a polygon object, so Width/Height and/or Depth may be zero (e.g. left + // wall of chart). Take this into account + if(basegfx::fTools::equalZero(aObjectRange.getWidth())) + { + // width is zero, use height and depth + fWidth = aObjectRange.getHeight(); + fHeight = aObjectRange.getDepth(); + } + else if(basegfx::fTools::equalZero(aObjectRange.getHeight())) + { + // height is zero, use width and depth + fWidth = aObjectRange.getWidth(); + fHeight = aObjectRange.getDepth(); + } + else + { + // use width and height + fWidth = aObjectRange.getWidth(); + fHeight = aObjectRange.getHeight(); + } + + if(basegfx::fTools::lessOrEqual(fWidth, 0.0) ||basegfx::fTools::lessOrEqual(fHeight, 0.0)) + { + // no texture; fallback to very small size + aTextureSize.setX(0.01); + aTextureSize.setY(0.01); + } + else + { + aTextureSize.setX(fWidth); + aTextureSize.setY(fHeight); + } + } + + // #i98295# + // unfortunately, this SdrObject type which allows a free 3d geometry definition was defined + // wrong topologically in relation to its plane normal and 3D visibility when it was invented + // a long time ago. Since the API allows creation of this SDrObject type, it is not possible to + // simply change this definition. Only the chart should use it, and at least this object type + // only exists at Runtime (is not saved and/or loaded in any FileFormat). Still someone external + // may have used it in its API. To not risk wrong 3D lightings, I have to switch the orientation + // of the polygon here + aPolyPolygon3D.flip(); + + // create primitive and add + const basegfx::B3DHomMatrix aWorldTransform; + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::SdrPolyPolygonPrimitive3D( + std::move(aPolyPolygon3D), aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute)); + xRetval = { xReference }; + + return xRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dscene.cxx b/svx/source/sdr/contact/viewcontactofe3dscene.cxx new file mode 100644 index 0000000000..63b031fdeb --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dscene.cxx @@ -0,0 +1,447 @@ +/* -*- 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/sdr/contact/viewcontactofe3dscene.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/contact/viewobjectcontactofe3dscene.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b3drange.hxx> +#include <drawinglayer/primitive3d/baseprimitive3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <drawinglayer/primitive2d/sceneprimitive2d.hxx> +#include <drawinglayer/primitive3d/transformprimitive3d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <osl/diagnose.h> + +using namespace com::sun::star; + +namespace { + +// pActiveVC is only true if ghosted is still activated and maybe needs to be switched off in this path +void createSubPrimitive3DVector( + const sdr::contact::ViewContact& rCandidate, + drawinglayer::primitive3d::Primitive3DContainer& o_rAllTarget, + drawinglayer::primitive3d::Primitive3DContainer* o_pVisibleTarget, + const SdrLayerIDSet* pVisibleSdrLayerIDSet, + const bool bTestSelectedVisibility) +{ + const sdr::contact::ViewContactOfE3dScene* pViewContactOfE3dScene = dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(&rCandidate); + + if(pViewContactOfE3dScene) + { + const sal_uInt32 nChildrenCount(rCandidate.GetObjectCount()); + + if(nChildrenCount) + { + // provide new collection sequences + drawinglayer::primitive3d::Primitive3DContainer aNewAllTarget; + drawinglayer::primitive3d::Primitive3DContainer aNewVisibleTarget; + + // add children recursively + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector( + rCandidate.GetViewContact(a), + aNewAllTarget, + o_pVisibleTarget ? &aNewVisibleTarget : nullptr, + pVisibleSdrLayerIDSet, + bTestSelectedVisibility); + } + + // create transform primitive for the created content combining content and transformtion + const drawinglayer::primitive3d::Primitive3DReference xReference(new drawinglayer::primitive3d::TransformPrimitive3D( + pViewContactOfE3dScene->GetE3dScene().GetTransform(), + aNewAllTarget)); + + // add created content to all target + o_rAllTarget.push_back(xReference); + + // add created content to visible target if exists + if(o_pVisibleTarget) + { + o_pVisibleTarget->push_back(xReference); + } + } + } + else + { + // access view independent representation of rCandidate + const sdr::contact::ViewContactOfE3d* pViewContactOfE3d = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&rCandidate); + + if(pViewContactOfE3d) + { + drawinglayer::primitive3d::Primitive3DContainer xPrimitive3DSeq(pViewContactOfE3d->getViewIndependentPrimitive3DContainer()); + + if(!xPrimitive3DSeq.empty()) + { + // add to all target vector + o_rAllTarget.append(xPrimitive3DSeq); + + if(o_pVisibleTarget) + { + // test visibility. Primitive is visible when both tests are true (AND) + bool bVisible(true); + + if(pVisibleSdrLayerIDSet) + { + // test layer visibility + const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject(); + const SdrLayerID aLayerID(rE3dObject.GetLayer()); + + bVisible = pVisibleSdrLayerIDSet->IsSet(aLayerID); + } + + if(bVisible && bTestSelectedVisibility) + { + // test selected visibility (see 3D View's DrawMarkedObj implementation) + const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject(); + + bVisible = rE3dObject.GetSelected(); + } + + if (bVisible) + { + // add to visible target vector + o_pVisibleTarget->append(xPrimitive3DSeq); + } + } + } + } + } +} + +} + +namespace sdr::contact { + +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. +ViewObjectContact& ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfE3dScene(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +ViewContactOfE3dScene::ViewContactOfE3dScene(E3dScene& rScene) +: ViewContactOfSdrObj(rScene) +{ +} + +void ViewContactOfE3dScene::createViewInformation3D(const basegfx::B3DRange& rContentRange) +{ + basegfx::B3DHomMatrix aTransformation; + basegfx::B3DHomMatrix aOrientation; + basegfx::B3DHomMatrix aProjection; + basegfx::B3DHomMatrix aDeviceToView; + + // create transformation (scene as group's transformation) + // For historical reasons, the outmost scene's transformation is handles as part of the + // view transformation. This means that the BoundRect of the contained 3D Objects is + // without that transformation and makes it necessary to NOT add the first scene to the + // Primitive3DContainer of contained objects. + { + aTransformation = GetE3dScene().GetTransform(); + } + + // create orientation (world to camera coordinate system) + { + // calculate orientation from VRP, VPN and VUV + const B3dCamera& rSceneCamera = GetE3dScene().GetCameraSet(); + const basegfx::B3DPoint& aVRP(rSceneCamera.GetVRP()); + const basegfx::B3DVector& aVPN(rSceneCamera.GetVPN()); + const basegfx::B3DVector& aVUV(rSceneCamera.GetVUV()); + + aOrientation.orientation(aVRP, aVPN, aVUV); + } + + // create projection (camera coordinate system to relative 2d where X,Y and Z are [0.0 .. 1.0]) + { + const basegfx::B3DHomMatrix aWorldToCamera(aOrientation * aTransformation); + basegfx::B3DRange aCameraRange(rContentRange); + aCameraRange.transform(aWorldToCamera); + + // remember Z-Values, but change orientation + const double fMinZ(-aCameraRange.getMaxZ()); + const double fMaxZ(-aCameraRange.getMinZ()); + + // construct temporary matrix from world to device. Use unit values here to measure expansion + basegfx::B3DHomMatrix aWorldToDevice(aWorldToCamera); + const drawinglayer::attribute::SdrSceneAttribute& rSdrSceneAttribute = getSdrSceneAttribute(); + + if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode()) + { + aWorldToDevice.frustum(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ); + } + else + { + aWorldToDevice.ortho(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ); + } + + // create B3DRange in device. This will create the real used ranges + // in camera space. Do not use the Z-Values, though. + basegfx::B3DRange aDeviceRange(rContentRange); + aDeviceRange.transform(aWorldToDevice); + + // set projection + if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode()) + { + aProjection.frustum( + aDeviceRange.getMinX(), aDeviceRange.getMaxX(), + aDeviceRange.getMinY(), aDeviceRange.getMaxY(), + fMinZ, fMaxZ); + } + else + { + aProjection.ortho( + aDeviceRange.getMinX(), aDeviceRange.getMaxX(), + aDeviceRange.getMinY(), aDeviceRange.getMaxY(), + fMinZ, fMaxZ); + } + } + + // create device to view transform + { + // create standard deviceToView projection for geometry + // input is [-1.0 .. 1.0] in X,Y and Z. bring to [0.0 .. 1.0]. Also + // necessary to flip Y due to screen orientation + // Z is not needed, but will also be brought to [0.0 .. 1.0] + aDeviceToView.scale(0.5, -0.5, 0.5); + aDeviceToView.translate(0.5, 0.5, 0.5); + } + + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + maViewInformation3D = drawinglayer::geometry::ViewInformation3D( + aTransformation, aOrientation, aProjection, + aDeviceToView, 0.0, aEmptyProperties); +} + +void ViewContactOfE3dScene::createObjectTransformation() +{ + // create 2d Object Transformation from relative point in 2d scene to world + const tools::Rectangle aRectangle(GetE3dScene().GetSnapRect()); + + maObjectTransformation.set(0, 0, aRectangle.getOpenWidth()); + maObjectTransformation.set(1, 1, aRectangle.getOpenHeight()); + maObjectTransformation.set(0, 2, aRectangle.Left()); + maObjectTransformation.set(1, 2, aRectangle.Top()); +} + +void ViewContactOfE3dScene::createSdrSceneAttribute() +{ + const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet(); + maSdrSceneAttribute = drawinglayer::primitive2d::createNewSdrSceneAttribute(rItemSet); +} + +void ViewContactOfE3dScene::createSdrLightingAttribute() +{ + const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet(); + maSdrLightingAttribute = drawinglayer::primitive2d::createNewSdrLightingAttribute(rItemSet); +} + +drawinglayer::primitive2d::Primitive2DContainer ViewContactOfE3dScene::createScenePrimitive2DSequence( + const SdrLayerIDSet* pLayerVisibility) const +{ + drawinglayer::primitive2d::Primitive2DContainer xRetval; + const sal_uInt32 nChildrenCount(GetObjectCount()); + + if(nChildrenCount) + { + // create 3d scene primitive with visible content tested against rLayerVisibility + drawinglayer::primitive3d::Primitive3DContainer aAllSequence; + drawinglayer::primitive3d::Primitive3DContainer aVisibleSequence; + const bool bTestLayerVisibility(nullptr != pLayerVisibility); + const bool bTestSelectedVisibility(GetE3dScene().GetDrawOnlySelected()); + const bool bTestVisibility(bTestLayerVisibility || bTestSelectedVisibility); + + // add children recursively. Do NOT start with (*this), this would create + // a 3D transformPrimitive for the start scene. While this is theoretically not + // a bad thing, for historical reasons the transformation of the outmost scene + // is seen as part of the ViewTransformation (see text in createViewInformation3D) + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector( + GetViewContact(a), + aAllSequence, + bTestLayerVisibility ? &aVisibleSequence : nullptr, + bTestLayerVisibility ? pLayerVisibility : nullptr, + bTestSelectedVisibility); + } + + const size_t nAllSize(!aAllSequence.empty() ? aAllSequence.size() : 0); + const size_t nVisibleSize(!aVisibleSequence.empty() ? aVisibleSequence.size() : 0); + + if((bTestVisibility && nVisibleSize) || nAllSize) + { + // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D + // needs to be given for evtl. decompositions. At the same time createViewInformation3D + // currently is based on creating the target-ViewInformation3D using a given range. To + // get the true range, use a neutral ViewInformation3D here. This leaves all matrices + // on identity and the time on 0.0. + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties); + const basegfx::B3DRange aContentRange(aAllSequence.getB3DRange(aNeutralViewInformation3D)); + + // create 2d primitive 3dscene with generated sub-list from collector + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::ScenePrimitive2D( + bTestVisibility ? aVisibleSequence : aAllSequence, + getSdrSceneAttribute(), + getSdrLightingAttribute(), + getObjectTransformation(), + getViewInformation3D(aContentRange))); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xReference }; + } + } + + // always append an invisible outline for the cases where no visible content exists + xRetval.push_back( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + getObjectTransformation())); + + return xRetval; +} + +void ViewContactOfE3dScene::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + if(GetObjectCount()) + { + // create a default ScenePrimitive2D (without visibility test of members) + rVisitor.visit(createScenePrimitive2DSequence(nullptr)); + } +} + +void ViewContactOfE3dScene::ActionChanged() +{ + // call parent + ViewContactOfSdrObj::ActionChanged(); + + // mark locally cached values as invalid + maViewInformation3D = drawinglayer::geometry::ViewInformation3D(); + maObjectTransformation.identity(); + maSdrSceneAttribute = drawinglayer::attribute::SdrSceneAttribute(); + maSdrLightingAttribute = drawinglayer::attribute::SdrLightingAttribute(); +} + +const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D() const +{ + if(maViewInformation3D.isDefault()) + { + // this version will create the content range on demand locally and thus is less + // performant than the other one. Since the information is buffered the planned + // behaviour is that the version with the given range is used initially. + basegfx::B3DRange aContentRange(getAllContentRange3D()); + + if(aContentRange.isEmpty()) + { + // empty scene, no 3d action should be necessary. Prepare some + // fallback size + OSL_FAIL("No need to get ViewInformation3D from an empty scene (!)"); + aContentRange.expand(basegfx::B3DPoint(-100.0, -100.0, -100.0)); + aContentRange.expand(basegfx::B3DPoint( 100.0, 100.0, 100.0)); + } + + const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(aContentRange); + } + + return maViewInformation3D; +} + +const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D(const basegfx::B3DRange& rContentRange) const +{ + if(maViewInformation3D.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(rContentRange); + } + + return maViewInformation3D; +} + +const basegfx::B2DHomMatrix& ViewContactOfE3dScene::getObjectTransformation() const +{ + if(maObjectTransformation.isIdentity()) + { + const_cast < ViewContactOfE3dScene* >(this)->createObjectTransformation(); + } + + return maObjectTransformation; +} + +const drawinglayer::attribute::SdrSceneAttribute& ViewContactOfE3dScene::getSdrSceneAttribute() const +{ + if(maSdrSceneAttribute.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createSdrSceneAttribute(); + } + + return maSdrSceneAttribute; +} + +const drawinglayer::attribute::SdrLightingAttribute& ViewContactOfE3dScene::getSdrLightingAttribute() const +{ + if(maSdrLightingAttribute.isDefault()) + { + const_cast < ViewContactOfE3dScene* >(this)->createSdrLightingAttribute(); + } + + return maSdrLightingAttribute; +} + +drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dScene::getAllPrimitive3DContainer() const +{ + drawinglayer::primitive3d::Primitive3DContainer aAllPrimitive3DContainer; + const sal_uInt32 nChildrenCount(GetObjectCount()); + + // add children recursively. Do NOT start with (*this), this would create + // a 3D transformPrimitive for the start scene. While this is theoretically not + // a bad thing, for historical reasons the transformation of the outmost scene + // is seen as part of the ViewTransformation (see text in createViewInformation3D) + for(sal_uInt32 a(0); a < nChildrenCount; a++) + { + createSubPrimitive3DVector(GetViewContact(a), aAllPrimitive3DContainer, nullptr, nullptr, false); + } + + return aAllPrimitive3DContainer; +} + +basegfx::B3DRange ViewContactOfE3dScene::getAllContentRange3D() const +{ + const drawinglayer::primitive3d::Primitive3DContainer xAllSequence(getAllPrimitive3DContainer()); + basegfx::B3DRange aAllContentRange3D; + + if(!xAllSequence.empty()) + { + // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D + // needs to be given for evtl. decompositions. Use a neutral ViewInformation3D here. This + // leaves all matrices on identity and the time on 0.0. + const uno::Sequence< beans::PropertyValue > aEmptyProperties; + const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties); + + aAllContentRange3D = xAllSequence.getB3DRange(aNeutralViewInformation3D); + } + + return aAllContentRange3D; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofe3dsphere.cxx b/svx/source/sdr/contact/viewcontactofe3dsphere.cxx new file mode 100644 index 0000000000..7a99719b81 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dsphere.cxx @@ -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 . + */ + + +#include <sdr/contact/viewcontactofe3dsphere.hxx> +#include <svx/sphere3d.hxx> +#include <drawinglayer/primitive3d/sdrsphereprimitive3d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive3d/sdrattributecreator3d.hxx> + + +namespace sdr::contact +{ + ViewContactOfE3dSphere::ViewContactOfE3dSphere(E3dSphereObj& rSphere) + : ViewContactOfE3d(rSphere) + { + } + + ViewContactOfE3dSphere::~ViewContactOfE3dSphere() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dSphere::createViewIndependentPrimitive3DContainer() const + { + drawinglayer::primitive3d::Primitive3DContainer xRetval; + const SfxItemSet& rItemSet = GetE3dSphereObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false)); + + // get sphere center and size for geometry + const basegfx::B3DPoint aSpherePosition(GetE3dSphereObj().Center()); + const basegfx::B3DVector aSphereSize(GetE3dSphereObj().Size()); + basegfx::B3DHomMatrix aWorldTransform; + + aWorldTransform.translate(-0.5, -0.5, -0.5); + aWorldTransform.scale(aSphereSize.getX(), aSphereSize.getY(), aSphereSize.getZ()); + aWorldTransform.translate(aSpherePosition.getX(), aSpherePosition.getY(), aSpherePosition.getZ()); + + // get 3D Object Attributes + drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet)); + + // get segment count + const sal_uInt32 nHorizontalSegments(GetE3dSphereObj().GetHorizontalSegments()); + const sal_uInt32 nVerticalSegments(GetE3dSphereObj().GetVerticalSegments()); + + // calculate texture size, use radii for (2 * PI * r) to get a perfect + // mapping on the sphere + const basegfx::B2DVector aTextureSize( + M_PI * ((aSphereSize.getX() + aSphereSize.getZ()) / 2.0), // PI * d + M_PI_2 * aSphereSize.getY()); // half outline, (PI * d)/2 -> PI/2 * d + + // create primitive and add + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::SdrSpherePrimitive3D( + aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute, + nHorizontalSegments, nVerticalSegments)); + xRetval = { xReference }; + + return xRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofgraphic.cxx b/svx/source/sdr/contact/viewcontactofgraphic.cxx new file mode 100644 index 0000000000..53fc46fff6 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofgraphic.cxx @@ -0,0 +1,387 @@ +/* -*- 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 <sdr/contact/viewcontactofgraphic.hxx> +#include <sdr/contact/viewobjectcontactofgraphic.hxx> +#include <svx/svdograf.hxx> +#include <sdgtritm.hxx> +#include <svx/sdgluitm.hxx> +#include <sdgcoitm.hxx> +#include <svx/sdggaitm.hxx> +#include <sdginitm.hxx> +#include <svx/sdgmoitm.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <svl/itemset.hxx> +#include <tools/debug.hxx> + +#include <svx/sdgcpitm.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <sdr/primitive2d/sdrgrafprimitive2d.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/colritem.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <bitmaps.hlst> + +namespace sdr::contact +{ + // Create an Object-Specific ViewObjectContact, set ViewContact and + // ObjectContact. Always needs to return something. + ViewObjectContact& ViewContactOfGraphic::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) + { + ViewObjectContact* pRetval = new ViewObjectContactOfGraphic(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; + } + + ViewContactOfGraphic::ViewContactOfGraphic(SdrGrafObj& rGrafObj) + : ViewContactOfTextObj(rGrafObj) + { + } + + ViewContactOfGraphic::~ViewContactOfGraphic() + { + } + + drawinglayer::primitive2d::Primitive2DContainer ViewContactOfGraphic::createVIP2DSForPresObj( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute& rAttribute) const + { + drawinglayer::primitive2d::Primitive2DContainer xRetval; + GraphicObject aEmptyGraphicObject; + GraphicAttr aEmptyGraphicAttr; + + // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts + const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + rObjectMatrix, + rAttribute, + aEmptyGraphicObject, + aEmptyGraphicAttr)); + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReferenceA }; + + // SdrGrafPrimitive2D with content (which is the preview graphic) scaled to smaller size and + // without attributes + basegfx::B2DHomMatrix aSmallerMatrix; + + // #i94431# for some reason, i forgot to take the PrefMapMode of the graphic + // into account. Since EmptyPresObj's are only used in Draw/Impress, it is + // safe to assume 100th mm as target. + Size aPrefSize(GetGrafObject().GetGrafPrefSize()); + + if(MapUnit::MapPixel == GetGrafObject().GetGrafPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + { + aPrefSize = OutputDevice::LogicToLogic(aPrefSize, GetGrafObject().GetGrafPrefMapMode(), MapMode(MapUnit::Map100thMM)); + } + + // decompose object matrix to get single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0); + const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0); + + if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0)) + { + // create the EmptyPresObj fallback visualisation. The fallback graphic + // is already provided in rGraphicObject in this case, use it + aSmallerMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY); + aSmallerMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) + * aSmallerMatrix; + + const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(); + const GraphicAttr aLocalGrafInfo; + const drawinglayer::primitive2d::Primitive2DReference xReferenceB(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + aSmallerMatrix, + drawinglayer::attribute::SdrLineFillEffectsTextAttribute(), + rGraphicObject, + aLocalGrafInfo)); + + xRetval.push_back(xReferenceB); + } + + return xRetval; + } + + drawinglayer::primitive2d::Primitive2DContainer ViewContactOfGraphic::createVIP2DSForDraft( + const basegfx::B2DHomMatrix& rObjectMatrix, + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute& rAttribute) const + { + drawinglayer::primitive2d::Primitive2DContainer xRetval; + GraphicObject aEmptyGraphicObject; + GraphicAttr aEmptyGraphicAttr; + + // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts + const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D( + rObjectMatrix, + rAttribute, + aEmptyGraphicObject, + aEmptyGraphicAttr)); + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReferenceA }; + + if(rAttribute.getLine().isDefault()) + { + // create a surrounding frame when no linestyle given + const Color aColor(Application::GetSettings().GetStyleSettings().GetShadowColor()); + const basegfx::BColor aBColor(aColor.getBColor()); + basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon()); + aOutline.transform(rObjectMatrix); + + xRetval.push_back( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + std::move(aOutline), + aBColor))); + } + + // decompose object matrix to get single values + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // define a distance value, used for distance from bitmap to borders and from bitmap + // to text, too (2 mm) + const double fDistance(200.0); + + // consume borders from values + aScale.setX(std::max(0.0, aScale.getX() - (2.0 * fDistance))); + aScale.setY(std::max(0.0, aScale.getY() - (2.0 * fDistance))); + aTranslate.setX(aTranslate.getX() + fDistance); + aTranslate.setY(aTranslate.getY() + fDistance); + + // draw a draft bitmap + const BitmapEx aDraftBitmap(BMAP_GrafikEi); + + if(!aDraftBitmap.IsEmpty()) + { + Size aPrefSize(aDraftBitmap.GetPrefSize()); + + if(MapUnit::MapPixel == aDraftBitmap.GetPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aDraftBitmap.GetSizePixel(), MapMode(MapUnit::Map100thMM)); + } + else + { + aPrefSize = OutputDevice::LogicToLogic(aPrefSize, aDraftBitmap.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + } + + const double fBitmapScaling(2.0); + const double fWidth(aPrefSize.getWidth() * fBitmapScaling); + const double fHeight(aPrefSize.getHeight() * fBitmapScaling); + + if(basegfx::fTools::more(fWidth, 1.0) + && basegfx::fTools::more(fHeight, 1.0) + && basegfx::fTools::lessOrEqual(fWidth, aScale.getX()) + && basegfx::fTools::lessOrEqual(fHeight, aScale.getY())) + { + const basegfx::B2DHomMatrix aBitmapMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + fWidth, fHeight, fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); + + xRetval.push_back( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::BitmapPrimitive2D( + aDraftBitmap, + aBitmapMatrix))); + + // consume bitmap size in X + aScale.setX(std::max(0.0, aScale.getX() - (fWidth + fDistance))); + aTranslate.setX(aTranslate.getX() + fWidth + fDistance); + } + } + + // Build the text for the draft object + OUString aDraftText = GetGrafObject().GetFileName(); + + if (aDraftText.isEmpty()) + { + aDraftText = GetGrafObject().GetName() + " ..."; + } + + if (!aDraftText.isEmpty()) + { + // #i103255# Goal is to produce TextPrimitives which hold the given text as + // BlockText in the available space. It would be very tricky to do + // an own word wrap/line layout here. + // Using SdrBlockTextPrimitive2D OTOH is critical since it internally + // uses the SdrObject it references. To solve this, create a temp + // SdrObject with Attributes and Text, generate a SdrBlockTextPrimitive2D + // directly and immediately decompose it. After that, it is no longer + // needed and can be deleted. + + // create temp RectObj as TextObj and set needed attributes + rtl::Reference<SdrRectObj> pRectObj(new SdrRectObj(GetGrafObject().getSdrModelFromSdrObject(), SdrObjKind::Text)); + pRectObj->NbcSetText(aDraftText); + pRectObj->SetMergedItem(SvxColorItem(COL_LIGHTRED, EE_CHAR_COLOR)); + + // get SdrText and OPO + SdrText* pSdrText(pRectObj->getText(0)); + OutlinerParaObject* pOPO(pRectObj->GetOutlinerParaObject()); + + if(pSdrText && pOPO) + { + // directly use the remaining space as TextRangeTransform + const basegfx::B2DHomMatrix aTextRangeTransform(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aScale, fShearX, fRotate, aTranslate)); + + // directly create temp SdrBlockTextPrimitive2D + rtl::Reference< drawinglayer::primitive2d::SdrBlockTextPrimitive2D > xBlockTextPrimitive(new drawinglayer::primitive2d::SdrBlockTextPrimitive2D( + pSdrText, + *pOPO, + aTextRangeTransform, + SDRTEXTHORZADJUST_LEFT, + SDRTEXTVERTADJUST_TOP, + false, + false, + false, + false)); + + // decompose immediately with neutral ViewInformation. This will + // layout the text to more simple TextPrimitives from drawinglayer + const drawinglayer::geometry::ViewInformation2D aViewInformation2D; + xBlockTextPrimitive->get2DDecomposition(xRetval, aViewInformation2D); + } + } + + return xRetval; + } + + void ViewContactOfGraphic::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SfxItemSet& rItemSet = GetGrafObject().GetMergedItemSet(); + + // create and fill GraphicAttr + GraphicAttr aLocalGrafInfo; + const sal_uInt16 nTrans(rItemSet.Get(SDRATTR_GRAFTRANSPARENCE).GetValue()); + const SdrGrafCropItem& rCrop(rItemSet.Get(SDRATTR_GRAFCROP)); + aLocalGrafInfo.SetLuminance(rItemSet.Get(SDRATTR_GRAFLUMINANCE).GetValue()); + aLocalGrafInfo.SetContrast(rItemSet.Get(SDRATTR_GRAFCONTRAST).GetValue()); + aLocalGrafInfo.SetChannelR(rItemSet.Get(SDRATTR_GRAFRED).GetValue()); + aLocalGrafInfo.SetChannelG(rItemSet.Get(SDRATTR_GRAFGREEN).GetValue()); + aLocalGrafInfo.SetChannelB(rItemSet.Get(SDRATTR_GRAFBLUE).GetValue()); + aLocalGrafInfo.SetGamma(rItemSet.Get(SDRATTR_GRAFGAMMA).GetValue() * 0.01); + aLocalGrafInfo.SetAlpha(255 - static_cast<sal_uInt8>(::basegfx::fround(std::min(nTrans, sal_uInt16(100)) * 2.55))); + aLocalGrafInfo.SetInvert(rItemSet.Get(SDRATTR_GRAFINVERT).GetValue()); + aLocalGrafInfo.SetDrawMode(rItemSet.Get(SDRATTR_GRAFMODE).GetValue()); + aLocalGrafInfo.SetCrop(rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom()); + + // we have content if graphic is not completely transparent + const bool bHasContent(0 != aLocalGrafInfo.GetAlpha()); + drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + GetGrafObject().getText(0), + bHasContent)); + + // take unrotated snap rect for position and size. Directly use model data, not getBoundRect() or getSnapRect() + // which will use the primitive data we just create in the near future + const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(GetGrafObject().GetGeoRect()); + + // look for mirroring + const GeoStat& rGeoStat(GetGrafObject().GetGeoStat()); + const Degree100 nRotationAngle(rGeoStat.m_nRotationAngle); + const bool bMirrored(GetGrafObject().IsMirrored()); + + if (bMirrored) + aLocalGrafInfo.SetMirrorFlags(BmpMirrorFlags::Horizontal); + + // fill object matrix + const double fShearX(-rGeoStat.mfTanShearAngle); + const double fRotate(nRotationAngle ? toRadians(36000_deg100 - nRotationAngle) : 0.0); + const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + fShearX, fRotate, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // get the current, unchanged graphic object from SdrGrafObj + const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(); + + if(visualisationUsesPresObj()) + { + // it's an EmptyPresObj, create the SdrGrafPrimitive2D without content and another scaled one + // with the content which is the placeholder graphic + rVisitor.visit(createVIP2DSForPresObj(aObjectMatrix, aAttribute)); + } +#ifndef IOS // Enforce swap-in for tiled rendering for now, while we have no delayed updating mechanism + else if(visualisationUsesDraft()) + { + // #i102380# The graphic is swapped out. To not force a swap-in here, there is a mechanism + // which shows a swapped-out-visualisation (which gets created here now) and an asynchronous + // visual update mechanism for swapped-out graphics when they were loaded (see AsynchGraphicLoadingEvent + // and ViewObjectContactOfGraphic implementation). Not forcing the swap-in here allows faster + // (non-blocking) processing here and thus in the effect e.g. fast scrolling through pages + rVisitor.visit(createVIP2DSForDraft(aObjectMatrix, aAttribute)); + } +#endif + else + { + // create primitive. Info: Calling the copy-constructor of GraphicObject in this + // SdrGrafPrimitive2D constructor will force a full swap-in of the graphic + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrGrafPrimitive2D( + aObjectMatrix, + aAttribute, + rGraphicObject, + aLocalGrafInfo)); + + rVisitor.visit(xReference); + } + + // always append an invisible outline for the cases where no visible content exists + rVisitor.visit( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + aObjectMatrix)); + } + + bool ViewContactOfGraphic::visualisationUsesPresObj() const + { + return GetGrafObject().IsEmptyPresObj(); + } + + bool ViewContactOfGraphic::visualisationUsesDraft() const + { + // no draft when already PresObj + if(visualisationUsesPresObj()) + return false; + + // draft when swapped out + const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject(); + + // draft when no graphic + return GraphicType::NONE == rGraphicObject.GetType() || GraphicType::Default == rGraphicObject.GetType(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofgroup.cxx b/svx/source/sdr/contact/viewcontactofgroup.cxx new file mode 100644 index 0000000000..18e5e07aa2 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofgroup.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <sdr/contact/viewcontactofgroup.hxx> +#include <svx/svdogrp.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <sdr/contact/viewobjectcontactofgroup.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <tools/debug.hxx> +#include <vcl/canvastools.hxx> + + +namespace sdr::contact +{ + // Create an Object-Specific ViewObjectContact, set ViewContact and + // ObjectContact. Always needs to return something. + ViewObjectContact& ViewContactOfGroup::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) + { + ViewObjectContact* pRetval = new ViewObjectContactOfGroup(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContactOfGroup::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; + } + + ViewContactOfGroup::ViewContactOfGroup(SdrObjGroup& rGroup) + : ViewContactOfSdrObj(rGroup) + { + } + + ViewContactOfGroup::~ViewContactOfGroup() + { + } + + void ViewContactOfGroup::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const sal_uInt32 nObjectCount(GetObjectCount()); + + if(nObjectCount) + { + // collect all sub-primitives + for(sal_uInt32 a(0); a < nObjectCount; a++) + { + const ViewContact& rCandidate(GetViewContact(a)); + rCandidate.getViewIndependentPrimitive2DContainer(rVisitor); + } + } + else + { + // append an invisible outline for the cases where no visible content exists + const basegfx::B2DRange aCurrentRange = vcl::unotools::b2DRectangleFromRectangle(GetSdrObjGroup().GetLastBoundRect()); + + const drawinglayer::primitive2d::Primitive2DReference xReference( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + false, aCurrentRange)); + + rVisitor.visit(xReference); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx b/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx new file mode 100644 index 0000000000..20463d0fc2 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx @@ -0,0 +1,103 @@ +/* -*- 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 <sdr/contact/viewcontactofmasterpagedescriptor.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdobj.hxx> +#include <sdr/contact/viewobjectcontactofmasterpagedescriptor.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> + + +namespace sdr::contact +{ + ViewObjectContact& ViewContactOfMasterPageDescriptor::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) + { + return *(new ViewObjectContactOfMasterPageDescriptor(rObjectContact, *this)); + } + + void ViewContactOfMasterPageDescriptor::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + drawinglayer::attribute::SdrFillAttribute aFill; + const SdrPageProperties* pCorrectProperties = GetMasterPageDescriptor().getCorrectSdrPageProperties(); + + if(pCorrectProperties) + { + // create page fill attributes when correct properties were identified + aFill = drawinglayer::primitive2d::createNewSdrFillAttribute(pCorrectProperties->GetItemSet()); + } + + if(!aFill.isDefault()) + { + // direct model data is the page size, get and use it + const SdrPage& rOwnerPage = GetMasterPageDescriptor().GetOwnerPage(); + const basegfx::B2DRange aInnerRange( + rOwnerPage.GetLeftBorder(), rOwnerPage.GetUpperBorder(), + rOwnerPage.GetWidth() - rOwnerPage.GetRightBorder(), + rOwnerPage.GetHeight() - rOwnerPage.GetLowerBorder()); + const basegfx::B2DRange aOuterRange( + 0, 0, rOwnerPage.GetWidth(), rOwnerPage.GetHeight()); + // ??? somehow only the master page's bit is used + bool const isFullSize(GetMasterPageDescriptor().GetUsedPage().IsBackgroundFullSize()); + const basegfx::B2DPolygon aFillPolygon( + basegfx::utils::createPolygonFromRect(isFullSize ? aOuterRange : aInnerRange)); + const drawinglayer::primitive2d::Primitive2DReference xReference( + drawinglayer::primitive2d::createPolyPolygonFillPrimitive( + basegfx::B2DPolyPolygon(aFillPolygon), + aFill, + drawinglayer::attribute::FillGradientAttribute())); + + rVisitor.visit(xReference); + } + } + + // basic constructor + ViewContactOfMasterPageDescriptor::ViewContactOfMasterPageDescriptor(sdr::MasterPageDescriptor& rDescriptor) + : mrMasterPageDescriptor(rDescriptor) + { + } + + // The destructor. + ViewContactOfMasterPageDescriptor::~ViewContactOfMasterPageDescriptor() + { + } + + sal_uInt32 ViewContactOfMasterPageDescriptor::GetObjectCount() const + { + return GetMasterPageDescriptor().GetUsedPage().GetObjCount(); + } + + ViewContact& ViewContactOfMasterPageDescriptor::GetViewContact(sal_uInt32 nIndex) const + { + return GetMasterPageDescriptor().GetUsedPage().GetObj(nIndex)->GetViewContact(); + } + + ViewContact* ViewContactOfMasterPageDescriptor::GetParentContact() const + { + return &(GetMasterPageDescriptor().GetOwnerPage().GetViewContact()); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofpageobj.cxx b/svx/source/sdr/contact/viewcontactofpageobj.cxx new file mode 100644 index 0000000000..3736faefa2 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofpageobj.cxx @@ -0,0 +1,79 @@ +/* -*- 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 <sdr/contact/viewcontactofpageobj.hxx> +#include <svx/svdopage.hxx> +#include <vcl/canvastools.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <sdr/contact/viewobjectcontactofpageobj.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> + +namespace sdr::contact +{ +ViewObjectContact& +ViewContactOfPageObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageObj(rObjectContact, *this); + return *pRetval; +} + +ViewContactOfPageObj::ViewContactOfPageObj(SdrPageObj& rPageObj) + : ViewContactOfSdrObj(rPageObj) +{ +} + +ViewContactOfPageObj::~ViewContactOfPageObj() {} + +// #i35972# React on changes of the object of this ViewContact +void ViewContactOfPageObj::ActionChanged() +{ + static bool bIsInActionChange(false); + + if (!bIsInActionChange) + { + // set recursion flag, see description in *.hxx + bIsInActionChange = true; + + // call parent + ViewContactOfSdrObj::ActionChanged(); + + // reset recursion flag, see description in *.hxx + bIsInActionChange = false; + } +} + +void ViewContactOfPageObj::createViewIndependentPrimitive2DSequence( + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // create graphical visualisation data. Since this is the view-independent version which should not be used, + // create a replacement graphic visualisation here. Use GetLastBoundRect to access the model data directly + // which is aOutRect for SdrPageObj. + const tools::Rectangle aModelRectangle(GetPageObj().GetLastBoundRect()); + const basegfx::B2DRange aModelRange = vcl::unotools::b2DRectangleFromRectangle(aModelRectangle); + basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aModelRange)); + const basegfx::BColor aYellow(1.0, 1.0, 0.0); + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aOutline), aYellow)); + + rVisitor.visit(xReference); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx b/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx new file mode 100644 index 0000000000..d6d6446e58 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx @@ -0,0 +1,192 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrcaptionobj.hxx> +#include <svx/svdocapt.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrcaptionprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + + +// includes for special text box shadow (SC) + +#include <svl/itemset.hxx> +#include <svx/xhatch.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdprcitm.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <vcl/canvastools.hxx> + +using namespace com::sun::star; + +namespace sdr::contact +{ + ViewContactOfSdrCaptionObj::ViewContactOfSdrCaptionObj(SdrCaptionObj& rCaptionObj) + : ViewContactOfSdrRectObj(rCaptionObj) + { + } + + ViewContactOfSdrCaptionObj::~ViewContactOfSdrCaptionObj() + { + } + + void ViewContactOfSdrCaptionObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SdrCaptionObj& rCaptionObj(static_cast<const SdrCaptionObj&>(GetSdrObject())); + const SfxItemSet& rItemSet = rCaptionObj.GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + rCaptionObj.getText(0), + false, rCaptionObj.GetSpecialTextBoxShadow())); + + // take unrotated snap rect (direct model data) for position and size + const tools::Rectangle aRectangle(rCaptionObj.GetGeoRect()); + const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle); + const GeoStat& rGeoStat(rCaptionObj.GetGeoStat()); + + // fill object matrix + basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + -rGeoStat.mfTanShearAngle, + rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // calculate corner radius + double fCornerRadiusX; + double fCornerRadiusY; + drawinglayer::primitive2d::calculateRelativeCornerRadius( + rCaptionObj.GetEckenradius(), aObjectRange, fCornerRadiusX, fCornerRadiusY); + basegfx::B2DPolygon aTail(rCaptionObj.getTailPolygon()); + + // create primitive. Always create one (even if invisible) to let the decomposition + // of SdrCaptionPrimitive2D create needed invisible elements for HitTest and BoundRect + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrCaptionPrimitive2D( + aObjectMatrix, + aAttribute, + std::move(aTail), + fCornerRadiusX, + fCornerRadiusY)); + + if(!aAttribute.isDefault() && rCaptionObj.GetSpecialTextBoxShadow()) + { + // for SC, the caption object may have a specialized shadow. The usual object shadow is off + // and a specialized shadow gets created here (see old paint) + const XColorItem& rShadColItem = rItemSet.Get(SDRATTR_SHADOWCOLOR); + const sal_uInt16 nShadowTransparence(rItemSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue()); + const Color aShadowColor(rShadColItem.GetColorValue()); + const drawing::FillStyle eShadowStyle = rItemSet.Get(XATTR_FILLSTYLE).GetValue(); + + // Create own ItemSet and modify as needed + // Always hide lines for special calc shadow + SfxItemSet aSet(rItemSet); + aSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + + if(drawing::FillStyle_HATCH == eShadowStyle) + { + // #41666# Hatch color is set hard to shadow color + XHatch aHatch = rItemSet.Get(XATTR_FILLHATCH).GetHatchValue(); + aHatch.SetColor(aShadowColor); + aSet.Put(XFillHatchItem(OUString(),aHatch)); + } + else + { + if(drawing::FillStyle_SOLID != eShadowStyle) + { + // force fill to solid (for Gradient, Bitmap and *no* fill (#119750# not filled comments *have* shadow)) + aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + } + + aSet.Put(XFillColorItem(OUString(),aShadowColor)); + aSet.Put(XFillTransparenceItem(nShadowTransparence)); + } + + // create FillAttribute from modified ItemSet + const drawinglayer::attribute::SdrFillAttribute aFill( + drawinglayer::primitive2d::createNewSdrFillAttribute(aSet)); + drawinglayer::primitive2d::Primitive2DReference xSpecialShadow; + + if(!aFill.isDefault() && 1.0 != aFill.getTransparence()) + { + // add shadow offset to object matrix + const bool bShadow(rItemSet.Get(SDRATTR_SHADOW).GetValue()); + const sal_uInt32 nXDist(rItemSet.Get(SDRATTR_SHADOWXDIST).GetValue()); + const sal_uInt32 nYDist(rItemSet.Get(SDRATTR_SHADOWYDIST).GetValue()); + + if (bShadow && (nXDist || nYDist)) + { + // #119750# create object and shadow outline, clip shadow outline + // on object outline. If there is a rest, create shadow. Do this to + // emulate that shadow is *not* visible behind the object for + // transparent object fill for comments in excel + basegfx::B2DPolygon aObjectOutline( + basegfx::utils::createPolygonFromRect( + basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), + fCornerRadiusX, + fCornerRadiusY)); + aObjectOutline.transform(aObjectMatrix); + + // create shadow outline + basegfx::B2DPolygon aShadowOutline(aObjectOutline); + aShadowOutline.transform( + basegfx::utils::createTranslateB2DHomMatrix(nXDist, nYDist)); + + // clip shadow outline against object outline + const basegfx::B2DPolyPolygon aClippedShadow( + basegfx::utils::clipPolygonOnPolyPolygon( + aShadowOutline, + basegfx::B2DPolyPolygon(aObjectOutline), + false, // take the outside + false)); + + if(aClippedShadow.count()) + { + // if there is shadow, create the specialized shadow primitive + xSpecialShadow = drawinglayer::primitive2d::createPolyPolygonFillPrimitive( + aClippedShadow, + aFill, + drawinglayer::attribute::FillGradientAttribute()); + } + } + } + + if(xSpecialShadow.is()) + { + // if we really got a special shadow, create a two-element retval with the shadow + // behind the standard object's geometry + rVisitor.visit(std::move(xSpecialShadow)); + } + } + + rVisitor.visit(std::move(xReference)); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrcircobj.cxx b/svx/source/sdr/contact/viewcontactofsdrcircobj.cxx new file mode 100644 index 0000000000..25cd0048ba --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrcircobj.cxx @@ -0,0 +1,102 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrcircobj.hxx> +#include <svx/svdocirc.hxx> +#include <svx/sdangitm.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrellipseprimitive2d.hxx> +#include <svl/itemset.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <vcl/canvastools.hxx> + + +namespace sdr::contact +{ + ViewContactOfSdrCircObj::ViewContactOfSdrCircObj(SdrCircObj& rCircObj) + : ViewContactOfSdrRectObj(rCircObj) + { + } + + ViewContactOfSdrCircObj::~ViewContactOfSdrCircObj() + { + } + + void ViewContactOfSdrCircObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SfxItemSet& rItemSet = GetCircObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + GetCircObj().getText(0), + false)); + + // take unrotated snap rect (direct model data) for position and size + const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(GetCircObj().GetGeoRect()); + const GeoStat& rGeoStat(GetCircObj().GetGeoStat()); + + // fill object matrix + const basegfx::B2DHomMatrix aObjectMatrix( + basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + -rGeoStat.mfTanShearAngle, + rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // create primitive data + const SdrObjKind nIdentifier(GetCircObj().GetObjIdentifier()); + + // always create primitives to allow the decomposition of SdrEllipsePrimitive2D + // or SdrEllipseSegmentPrimitive2D to create needed invisible elements for HitTest + // and/or BoundRect + if(SdrObjKind::CircleOrEllipse == nIdentifier) + { + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrEllipsePrimitive2D( + aObjectMatrix, + aAttribute)); + + rVisitor.visit(xReference); + } + else + { + const auto nNewStart(rItemSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue()); + const auto nNewEnd(rItemSet.Get(SDRATTR_CIRCENDANGLE).GetValue()); + const double fStart(toRadians((36000_deg100 - nNewEnd) % 36000_deg100)); + const double fEnd(toRadians((36000_deg100 - nNewStart) % 36000_deg100)); + const bool bCloseSegment(SdrObjKind::CircleArc != nIdentifier); + const bool bCloseUsingCenter(SdrObjKind::CircleSection == nIdentifier); + + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrEllipseSegmentPrimitive2D( + aObjectMatrix, + aAttribute, + fStart, + fEnd, + bCloseSegment, + bCloseUsingCenter)); + + rVisitor.visit(xReference); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdredgeobj.cxx b/svx/source/sdr/contact/viewcontactofsdredgeobj.cxx new file mode 100644 index 0000000000..a0e26c5bf2 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdredgeobj.cxx @@ -0,0 +1,66 @@ +/* -*- 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 <sdr/contact/viewcontactofsdredgeobj.hxx> +#include <svx/svdoedge.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrconnectorprimitive2d.hxx> +#include <osl/diagnose.h> + + +namespace sdr::contact +{ + ViewContactOfSdrEdgeObj::ViewContactOfSdrEdgeObj(SdrEdgeObj& rEdgeObj) + : ViewContactOfTextObj(rEdgeObj) + { + } + + ViewContactOfSdrEdgeObj::~ViewContactOfSdrEdgeObj() + { + } + + void ViewContactOfSdrEdgeObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + basegfx::B2DPolygon aEdgeTrack(GetEdgeObj().getEdgeTrack()); + + // what to do when no EdgeTrack is provided (HitTest and selectability) ? + OSL_ENSURE(0 != aEdgeTrack.count(), "Connectors with no geometry are not allowed (!)"); + + // ckeck attributes + const SfxItemSet& rItemSet = GetEdgeObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineEffectsTextAttribute( + rItemSet, + GetEdgeObj().getText(0))); + + // create primitive. Always create primitives to allow the decomposition of + // SdrConnectorPrimitive2D to create needed invisible elements for HitTest + // and/or BoundRect + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrConnectorPrimitive2D( + aAttribute, + std::move(aEdgeTrack))); + + rVisitor.visit(xReference); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx b/svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx new file mode 100644 index 0000000000..eb04efd715 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx @@ -0,0 +1,127 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrmeasureobj.hxx> +#include <svx/svdomeas.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <svl/itemset.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sxmbritm.hxx> +#include <svx/sxmtritm.hxx> +#include <sxmtaitm.hxx> +#include <sdr/primitive2d/sdrmeasureprimitive2d.hxx> +#include <svx/sxmtpitm.hxx> + + +namespace sdr::contact +{ + ViewContactOfSdrMeasureObj::ViewContactOfSdrMeasureObj(SdrMeasureObj& rMeasureObj) + : ViewContactOfTextObj(rMeasureObj) + { + } + + ViewContactOfSdrMeasureObj::~ViewContactOfSdrMeasureObj() + { + } + + void ViewContactOfSdrMeasureObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SfxItemSet& rItemSet = GetMeasureObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineEffectsTextAttribute( + rItemSet, + GetMeasureObj().getText(0))); + + // take properties which are the model data. + const ::basegfx::B2DPoint aStart(GetMeasureObj().GetPoint(0).X(), GetMeasureObj().GetPoint(0).Y()); + const ::basegfx::B2DPoint aEnd(GetMeasureObj().GetPoint(1).X(), GetMeasureObj().GetPoint(1).Y()); + const double fDistance(rItemSet.Get(SDRATTR_MEASURELINEDIST).GetValue()); + const double fUpperDistance(rItemSet.Get(SDRATTR_MEASUREHELPLINEOVERHANG).GetValue()); + const double fLowerDistance(rItemSet.Get(SDRATTR_MEASUREHELPLINEDIST).GetValue()); + const double fLeftDelta(rItemSet.Get(SDRATTR_MEASUREHELPLINE1LEN).GetValue()); + const double fRightDelta(rItemSet.Get(SDRATTR_MEASUREHELPLINE2LEN).GetValue()); + const bool bBelow(rItemSet.Get(SDRATTR_MEASUREBELOWREFEDGE).GetValue()); + const bool bTextRotation(rItemSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue()); + const bool bTextAutoAngle(rItemSet.Get(SDRATTR_MEASURETEXTAUTOANGLE).GetValue()); + drawinglayer::primitive2d::MeasureTextPosition aMTPHor(drawinglayer::primitive2d::MEASURETEXTPOSITION_AUTOMATIC); + drawinglayer::primitive2d::MeasureTextPosition aMTPVer(drawinglayer::primitive2d::MEASURETEXTPOSITION_AUTOMATIC); + + switch(rItemSet.Get(SDRATTR_MEASURETEXTHPOS).GetValue()) + { + case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_LEFTOUTSIDE: + { + aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_NEGATIVE; + break; + } + case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_INSIDE: + { + aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_CENTERED; + break; + } + case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_RIGHTOUTSIDE: + { + aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_POSITIVE; + break; + } + default: // css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_AUTO + { + break; + } + } + + switch(rItemSet.Get(SDRATTR_MEASURETEXTVPOS).GetValue()) + { + case css::drawing::MeasureTextVertPos_EAST: + { + aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_NEGATIVE; + break; + } + case css::drawing::MeasureTextVertPos_CENTERED: + { + aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_CENTERED; + break; + } + case css::drawing::MeasureTextVertPos_WEST: + { + aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_POSITIVE; + break; + } + default : // css::drawing::MeasureTextVertPos_AUTO + { + break; + } + } + + // create primitive with the model data. Always create primitives to allow the + // decomposition of SdrMeasurePrimitive2D to create needed invisible elements for HitTest + // and/or BoundRect + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrMeasurePrimitive2D( + aAttribute, aStart, aEnd, + aMTPHor, aMTPVer, fDistance, + fUpperDistance, fLowerDistance, + fLeftDelta, fRightDelta, bBelow, + bTextRotation, bTextAutoAngle)); + + rVisitor.visit(xReference); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx b/svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx new file mode 100644 index 0000000000..7c713bc4b7 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx @@ -0,0 +1,129 @@ +/* -*- 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/sdr/contact/viewcontactofsdrmediaobj.hxx> +#include <svx/svdomedia.hxx> +#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx> +#include <drawinglayer/primitive2d/mediaprimitive2d.hxx> +#include <vcl/canvastools.hxx> + +namespace sdr::contact { + +ViewContactOfSdrMediaObj::ViewContactOfSdrMediaObj( SdrMediaObj& rMediaObj ) : + ViewContactOfSdrObj( rMediaObj ) +{ +} + +ViewContactOfSdrMediaObj::~ViewContactOfSdrMediaObj() +{ +} + +ViewObjectContact& ViewContactOfSdrMediaObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + return *( new ViewObjectContactOfSdrMediaObj( rObjectContact, *this, static_cast< SdrMediaObj& >( GetSdrObject() ).getMediaProperties() ) ); +} + +Size ViewContactOfSdrMediaObj::getPreferredSize() const +{ + // #i71805# Since we may have a whole bunch of VOCs here, make a loop + // return first useful size -> the size from the first which is visualized as a window + const sal_uInt32 nCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + ViewObjectContact* pCandidate = getViewObjectContact(a); + Size aSize(pCandidate ? static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->getPreferredSize() : Size()); + + if(0 != aSize.getWidth() || 0 != aSize.getHeight()) + { + return aSize; + } + } + + return Size(); +} + +void ViewContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const +{ + // #i71805# Since we may have a whole bunch of VOCs here, make a loop + const sal_uInt32 nCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + ViewObjectContact* pCandidate = getViewObjectContact(a); + + if(pCandidate) + { + static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->updateMediaItem(rItem); + } + } +} + +void ViewContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem ) +{ + const sal_uInt32 nCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nCount; a++) + { + ViewObjectContact* pCandidate = getViewObjectContact(a); + + if(pCandidate) + { + static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->executeMediaItem(rItem); + } + } +} + +void ViewContactOfSdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState ) +{ + static_cast< SdrMediaObj& >(GetSdrObject()).mediaPropertiesChanged(rNewState); +} + +void ViewContactOfSdrMediaObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // create range using the model data directly. This is in SdrTextObj::aRect which i will access using + // GetGeoRect() to not trigger any calculations. It's the unrotated geometry which is okay for MediaObjects ATM. + const tools::Rectangle aRectangle(GetSdrMediaObj().GetGeoRect()); + const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle); + + // create object transform + basegfx::B2DHomMatrix aTransform; + aTransform.set(0, 0, aRange.getWidth()); + aTransform.set(1, 1, aRange.getHeight()); + aTransform.set(0, 2, aRange.getMinX()); + aTransform.set(1, 2, aRange.getMinY()); + + // create media primitive. Always create primitives to allow the + // decomposition of MediaPrimitive2D to create needed invisible elements for HitTest + // and/or BoundRect + const basegfx::BColor aBackgroundColor(67.0 / 255.0, 67.0 / 255.0, 67.0 / 255.0); + const OUString& rURL(GetSdrMediaObj().getURL()); + const sal_uInt32 nPixelBorder(4); + const drawinglayer::primitive2d::Primitive2DReference xRetval( + new drawinglayer::primitive2d::MediaPrimitive2D( + aTransform, rURL, aBackgroundColor, nPixelBorder, + GetSdrMediaObj().getSnapshot())); + + rVisitor.visit(xRetval); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrobj.cxx b/svx/source/sdr/contact/viewcontactofsdrobj.cxx new file mode 100644 index 0000000000..5f13af5cbe --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrobj.cxx @@ -0,0 +1,179 @@ +/* -*- 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/sdr/contact/viewcontactofsdrobj.hxx> +#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/svdobj.hxx> +#include <tools/debug.hxx> +#include <svx/svdpage.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx> +#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> +#include <svx/svdhdl.hxx> + +namespace sdr::contact { + +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. +ViewObjectContact& ViewContactOfSdrObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfSdrObj(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContactOfSdrObj::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +ViewContactOfSdrObj::ViewContactOfSdrObj(SdrObject& rObj) +: mrObject(rObj) +{ +} + +ViewContactOfSdrObj::~ViewContactOfSdrObj() +{ +} + +// Access to possible sub-hierarchy +sal_uInt32 ViewContactOfSdrObj::GetObjectCount() const +{ + if(GetSdrObject().GetSubList()) + { + return GetSdrObject().GetSubList()->GetObjCount(); + } + + return 0; +} + +ViewContact& ViewContactOfSdrObj::GetViewContact(sal_uInt32 nIndex) const +{ + assert(GetSdrObject().GetSubList() && + "ViewContactOfSdrObj::GetViewContact: Access to non-existent Sub-List (!)"); + SdrObject* pObj = GetSdrObject().GetSubList()->GetObj(nIndex); + assert(pObj && "ViewContactOfSdrObj::GetViewContact: Corrupt SdrObjList (!)"); + return pObj->GetViewContact(); +} + +ViewContact* ViewContactOfSdrObj::GetParentContact() const +{ + ViewContact* pRetval = nullptr; + SdrObjList* pObjList = GetSdrObject().getParentSdrObjListFromSdrObject(); + + if(pObjList) + { + if(auto pPage = dynamic_cast<SdrPage*>( pObjList)) + { + // Is a page + pRetval = &(pPage->GetViewContact()); + } + else + { + // Is a group? + if(pObjList->getSdrObjectFromSdrObjList()) + { + pRetval = &(pObjList->getSdrObjectFromSdrObjList()->GetViewContact()); + } + } + } + + return pRetval; +} + +// React on changes of the object of this ViewContact +void ViewContactOfSdrObj::ActionChanged() +{ + // look for own changes + if (SdrTextObj* pTextObj = DynCastSdrTextObj(&GetSdrObject())) + { + // tdf#146860 no idea why, but calling this makes the text boxes render properly + pTextObj->GetTextAniKind(); + } + + // call parent + ViewContact::ActionChanged(); +} + +// override for accessing the SdrObject +SdrObject* ViewContactOfSdrObj::TryToGetSdrObject() const +{ + return &GetSdrObject(); +} + + +// primitive stuff + +// add Gluepoints (if available) +drawinglayer::primitive2d::Primitive2DContainer ViewContactOfSdrObj::createGluePointPrimitive2DSequence() const +{ + drawinglayer::primitive2d::Primitive2DContainer xRetval; + const SdrGluePointList* pGluePointList = GetSdrObject().GetGluePointList(); + + if(pGluePointList) + { + const sal_uInt32 nCount(pGluePointList->GetCount()); + + if(nCount) + { + // prepare point vector + std::vector< basegfx::B2DPoint > aGluepointVector; + + // create GluePoint primitives. ATM these are relative to the SnapRect + for(sal_uInt32 a(0); a < nCount; a++) + { + const SdrGluePoint& rCandidate = (*pGluePointList)[static_cast<sal_uInt16>(a)]; + const Point aPosition(rCandidate.GetAbsolutePos(GetSdrObject())); + + aGluepointVector.emplace_back(aPosition.X(), aPosition.Y()); + } + + if(!aGluepointVector.empty()) + { + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::MarkerArrayPrimitive2D( + std::move(aGluepointVector), SdrHdl::createGluePointBitmap())); + xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xReference }; + } + } + } + + return xRetval; +} + +drawinglayer::primitive2d::Primitive2DContainer ViewContactOfSdrObj::embedToObjectSpecificInformation(drawinglayer::primitive2d::Primitive2DContainer aSource) const +{ + if(!aSource.empty() && + (!GetSdrObject().GetName().isEmpty() || + !GetSdrObject().GetTitle().isEmpty() || + !GetSdrObject().GetDescription().isEmpty())) + { + const drawinglayer::primitive2d::Primitive2DReference xRef( + new drawinglayer::primitive2d::ObjectInfoPrimitive2D( + std::move(aSource), + GetSdrObject().GetName(), + GetSdrObject().GetTitle(), + GetSdrObject().GetDescription())); + + return drawinglayer::primitive2d::Primitive2DContainer { xRef }; + } + + return aSource; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx b/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx new file mode 100644 index 0000000000..9b6e287d80 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx @@ -0,0 +1,238 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrobjcustomshape.hxx> +#include <svx/svdoashp.hxx> +#include <svx/sdooitm.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrcustomshapeprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <svx/obj3d.hxx> +#include <vcl/canvastools.hxx> + + +namespace sdr::contact +{ + ViewContactOfSdrObjCustomShape::ViewContactOfSdrObjCustomShape(SdrObjCustomShape& rCustomShape) + : ViewContactOfTextObj(rCustomShape) + { + } + + ViewContactOfSdrObjCustomShape::~ViewContactOfSdrObjCustomShape() + { + } + + basegfx::B2DRange ViewContactOfSdrObjCustomShape::getCorrectedTextBoundRect() const + { + const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect()); + tools::Rectangle aTextBound(aObjectBound); + GetCustomShapeObj().GetTextBounds(aTextBound); + basegfx::B2DRange aTextRange = vcl::unotools::b2DRectangleFromRectangle(aTextBound); + const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound); + + // no need to correct if no extra text range + if(aTextRange != aObjectRange) + { + const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat()); + + // only correct when rotation and/or shear is used + if(rGeoStat.m_nShearAngle || rGeoStat.m_nRotationAngle ) + { + // text range needs to be corrected by + // aObjectRange.getCenter() - aRotObjectRange.getCenter() since it's + // defined differently by using rotation around object center. Start + // with positive part + basegfx::B2DVector aTranslation(aObjectRange.getCenter()); + + // get rotated and sheared object's range + basegfx::B2DRange aRotObjectRange(aObjectRange); + basegfx::B2DHomMatrix aRotMatrix; + + aRotMatrix.translate(-aObjectRange.getMinimum().getX(), -aObjectRange.getMinimum().getY()); + + if(rGeoStat.m_nShearAngle) + { + aRotMatrix.shearX(-rGeoStat.mfTanShearAngle); + } + + if(rGeoStat.m_nRotationAngle) + { + aRotMatrix.rotate(toRadians(36000_deg100 - rGeoStat.m_nRotationAngle)); + } + + aRotMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY()); + aRotObjectRange.transform(aRotMatrix); + + // add negative translation part + aTranslation -= aRotObjectRange.getCenter(); + + // create new range + aTextRange = basegfx::B2DRange( + aTextRange.getMinX() + aTranslation.getX(), aTextRange.getMinY() + aTranslation.getY(), + aTextRange.getMaxX() + aTranslation.getX(), aTextRange.getMaxY() + aTranslation.getY()); + } + + // NbcMirror() of SdrTextObj (from which SdrObjCustomShape is derived), adds a + // 180deg rotation around the shape center to GeoStat.nRotationAngle. So remove here the + // 180° rotation, which was added by GetTextBounds(). + if(GetCustomShapeObj().IsMirroredY()) + { + basegfx::B2DHomMatrix aRotMatrix(basegfx::utils::createRotateAroundPoint( + aObjectRange.getCenterX(), aObjectRange.getCenterY(), M_PI)); + aTextRange.transform(aRotMatrix); + } + } + + return aTextRange; + } + + void ViewContactOfSdrObjCustomShape::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SfxItemSet& rItemSet = GetCustomShapeObj().GetMergedItemSet(); + + // #i98072# Get shadow and text; eventually suppress the text if it's + // a TextPath FontworkGallery object + const drawinglayer::attribute::SdrEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrEffectsTextAttribute( + rItemSet, + GetCustomShapeObj().getText(0), + GetCustomShapeObj().IsTextPath())); + drawinglayer::primitive2d::Primitive2DContainer xGroup; + bool bHasText(!aAttribute.getText().isDefault()); + + // create Primitive2DContainer from sub-geometry + const SdrObject* pSdrObjRepresentation = GetCustomShapeObj().GetSdrObjectFromCustomShape(); + bool b3DShape(false); + + if(pSdrObjRepresentation) + { + // tdf#118498 The processing of SdrObjListIter for SdrIterMode::DeepNoGroups + // did change for 3D-Objects, it now correctly enters and iterates the + // SdrObjects in the E3dScene (same as for SdrObjGroup). This is more correct + // as the old version which just checked for dynamic_cast<const SdrObjGroup*> + // and *only* entered these, ignoring E3dScene as grouping-object. + // But how to fix that? Taking back the SdrObjListIter change would be easy, but + // not correct. After checking ViewContactOfE3dScene and ViewContactOfGroup + // I see that both traverse their children by themselves (on VC-Level, + // see createViewIndependentPrimitive2DSequence implementations and usage of + // GetObjectCount()). Thus in principle iterating here (esp. 'deep') seems to + // be wrong anyways, it might have even created wrong and double geometries + // (only with complex CustomShapes with multiple representation SdrObjects and + // only visible when transparency involved, but runtime-expensive). + // Thus: Just do not iterate, will check behaviour deeply. + b3DShape = (nullptr != DynCastE3dObject(pSdrObjRepresentation)); + pSdrObjRepresentation->GetViewContact().getViewIndependentPrimitive2DContainer(xGroup); + } + + if(bHasText || !xGroup.empty()) + { + // prepare text box geometry + basegfx::B2DHomMatrix aTextBoxMatrix; + bool bWordWrap(false); + + // take unrotated snap rect as default, then get the + // unrotated text box. Rotation needs to be done centered + const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect()); + const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound); + + if(bHasText) + { + // #i101684# get the text range unrotated and absolute to the object range + const basegfx::B2DRange aTextRange(getCorrectedTextBoundRect()); + + // Rotation before scaling + if(!basegfx::fTools::equalZero(GetCustomShapeObj().GetExtraTextRotation(true))) + { + basegfx::B2DVector aTranslation(0.5, 0.5); + aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() ); + aTextBoxMatrix.rotate(basegfx::deg2rad( + 360.0 - GetCustomShapeObj().GetExtraTextRotation(true))); + aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() ); + } + // give text object a size + aTextBoxMatrix.scale(aTextRange.getWidth(), aTextRange.getHeight()); + + // check if we have a rotation/shear at all to take care of + const double fExtraTextRotation(GetCustomShapeObj().GetExtraTextRotation()); + const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat()); + + if(rGeoStat.m_nShearAngle || rGeoStat.m_nRotationAngle || !basegfx::fTools::equalZero(fExtraTextRotation)) + { + if(aObjectRange != aTextRange) + { + // move relative to unrotated object range + aTextBoxMatrix.translate( + aTextRange.getMinX() - aObjectRange.getMinimum().getX(), + aTextRange.getMinY() - aObjectRange.getMinimum().getY()); + } + + if(!basegfx::fTools::equalZero(fExtraTextRotation)) + { + basegfx::B2DVector aTranslation( + ( aTextRange.getWidth() / 2 ) + ( aTextRange.getMinX() - aObjectRange.getMinimum().getX() ), + ( aTextRange.getHeight() / 2 ) + ( aTextRange.getMinY() - aObjectRange.getMinimum().getY() ) ); + aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() ); + aTextBoxMatrix.rotate(basegfx::deg2rad(360.0 - fExtraTextRotation)); + aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() ); + } + + if(rGeoStat.m_nShearAngle) + { + aTextBoxMatrix.shearX(-rGeoStat.mfTanShearAngle); + } + + if(rGeoStat.m_nRotationAngle) + { + aTextBoxMatrix.rotate(toRadians(36000_deg100 - rGeoStat.m_nRotationAngle)); + } + + // give text it's target position + aTextBoxMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY()); + } + else + { + aTextBoxMatrix.translate(aTextRange.getMinX(), aTextRange.getMinY()); + } + + // check if SdrTextWordWrapItem is set + bWordWrap = GetCustomShapeObj().GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue(); + } + + // fill object matrix + const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + /*fShearX=*/0, /*fRotate=*/0, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // create primitive + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrCustomShapePrimitive2D( + aAttribute, + std::move(xGroup), + aTextBoxMatrix, + bWordWrap, + b3DShape, + aObjectMatrix)); + rVisitor.visit(xReference); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrole2obj.cxx b/svx/source/sdr/contact/viewcontactofsdrole2obj.cxx new file mode 100644 index 0000000000..7871675400 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrole2obj.cxx @@ -0,0 +1,179 @@ +/* -*- 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 <drawinglayer/primitive2d/Tools.hxx> +#include <sdr/contact/viewcontactofsdrole2obj.hxx> +#include <svx/svdoole2.hxx> +#include <sdr/contact/viewobjectcontactofsdrole2obj.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <sdr/primitive2d/sdrole2primitive2d.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <vcl/canvastools.hxx> +#include <tools/debug.hxx> +#include <sdr/primitive2d/sdrolecontentprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <svx/charthelper.hxx> +#include <svtools/embedhlp.hxx> + +namespace sdr::contact { + +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. +ViewObjectContact& ViewContactOfSdrOle2Obj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfSdrOle2Obj(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +ViewContactOfSdrOle2Obj::ViewContactOfSdrOle2Obj(SdrOle2Obj& rOle2Obj) +: ViewContactOfSdrRectObj(rOle2Obj) +{ +} + +ViewContactOfSdrOle2Obj::~ViewContactOfSdrOle2Obj() +{ +} + +basegfx::B2DHomMatrix ViewContactOfSdrOle2Obj::createObjectTransform() const +{ + // take unrotated snap rect (direct model data) for position and size + const tools::Rectangle aRectangle(GetOle2Obj().GetGeoRect()); + const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle); + + // create object matrix + const GeoStat& rGeoStat(GetOle2Obj().GetGeoStat()); + const double fShearX(-rGeoStat.mfTanShearAngle); + const double fRotate(rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0); + + return basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + fShearX, + fRotate, + aObjectRange.getMinX(), aObjectRange.getMinY()); +} + +void ViewContactOfSdrOle2Obj::createPrimitive2DSequenceWithParameters(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // get object transformation + const basegfx::B2DHomMatrix aObjectMatrix(createObjectTransform()); + + // Prepare attribute settings, will be used soon anyways + const SfxItemSet& rItemSet = GetOle2Obj().GetMergedItemSet(); + + // this may be refined more granular; if no content, attributes may get simpler + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + GetOle2Obj().getText(0), + true)); + drawinglayer::primitive2d::Primitive2DReference xContent; + + if(GetOle2Obj().IsChart()) + { + // try to get chart primitives and chart range directly from xChartModel + basegfx::B2DRange aChartContentRange; + drawinglayer::primitive2d::Primitive2DContainer aChartSequence( + ChartHelper::tryToGetChartContentAsPrimitive2DSequence( + GetOle2Obj().getXModel(), + aChartContentRange)); + const double fWidth(aChartContentRange.getWidth()); + const double fHeight(aChartContentRange.getHeight()); + + if(!aChartSequence.empty() + && basegfx::fTools::more(fWidth, 0.0) + && basegfx::fTools::more(fHeight, 0.0)) + { + // create embedding transformation + basegfx::B2DHomMatrix aEmbed( + basegfx::utils::createTranslateB2DHomMatrix( + -aChartContentRange.getMinX(), + -aChartContentRange.getMinY())); + + aEmbed.scale(1.0 / fWidth, 1.0 / fHeight); + aEmbed = aObjectMatrix * aEmbed; + xContent = new drawinglayer::primitive2d::TransformPrimitive2D( + aEmbed, + std::move(aChartSequence)); + } + } + + if(!xContent.is()) + { + // #i102063# embed OLE content in an own primitive; this will be able to decompose accessing + // the weak SdrOle2 reference and will also implement getB2DRange() for fast BoundRect + // calculations without OLE Graphic access (which may trigger e.g. chart recalculation). + // It will also take care of HighContrast and ScaleContent + xContent = new drawinglayer::primitive2d::SdrOleContentPrimitive2D( + GetOle2Obj(), + aObjectMatrix, + + // #i104867# add GraphicVersion number to be able to check for + // content change in the primitive later + GetOle2Obj().getEmbeddedObjectRef().getGraphicVersion() ); + } + + // create primitive. Use Ole2 primitive here. Prepare attribute settings, will + // be used soon anyways. Always create primitives to allow the decomposition of + // SdrOle2Primitive2D to create needed invisible elements for HitTest and/or BoundRect + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrOle2Primitive2D( + drawinglayer::primitive2d::Primitive2DContainer { xContent }, + aObjectMatrix, + aAttribute)); + + rVisitor.visit(xReference); +} + +basegfx::B2DRange ViewContactOfSdrOle2Obj::getRange( const drawinglayer::geometry::ViewInformation2D& rViewInfo2D ) const +{ + // this may be refined more granular; if no content, attributes may get simpler + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute = + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + GetOle2Obj().GetMergedItemSet(), + GetOle2Obj().getText(0), + true); + + basegfx::B2DHomMatrix aObjectMatrix = createObjectTransform(); + + drawinglayer::primitive2d::Primitive2DReference xContent = + new drawinglayer::primitive2d::SdrOleContentPrimitive2D( + GetOle2Obj(), + aObjectMatrix, + GetOle2Obj().getEmbeddedObjectRef().getGraphicVersion()); + + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrOle2Primitive2D( + drawinglayer::primitive2d::Primitive2DContainer { xContent }, + aObjectMatrix, + aAttribute)); + + return drawinglayer::primitive2d::getB2DRangeFromPrimitive2DReference(xReference, rViewInfo2D); +} + +void ViewContactOfSdrOle2Obj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + createPrimitive2DSequenceWithParameters(rVisitor); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrpage.cxx b/svx/source/sdr/contact/viewcontactofsdrpage.cxx new file mode 100644 index 0000000000..43ca1fd5c0 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrpage.cxx @@ -0,0 +1,623 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrpage.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/svdpage.hxx> +#include <sdr/contact/viewobjectcontactofsdrpage.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svtools/colorcfg.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx> +#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/settings.hxx> +#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx> +#include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <bitmaps.hlst> + +namespace sdr::contact { + +ViewContactOfPageSubObject::ViewContactOfPageSubObject(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: mrParentViewContactOfSdrPage(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfPageSubObject::~ViewContactOfPageSubObject() +{ +} + +ViewContact* ViewContactOfPageSubObject::GetParentContact() const +{ + return &mrParentViewContactOfSdrPage; +} + +const SdrPage& ViewContactOfPageSubObject::getPage() const +{ + return mrParentViewContactOfSdrPage.GetSdrPage(); +} + +ViewObjectContact& ViewContactOfPageBackground::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageBackground(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfPageBackground::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // We have only the page information, not the view information. Use the + // svtools::DOCCOLOR color for initialisation + const svtools::ColorConfig aColorConfig; + const Color aInitColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor); + const basegfx::BColor aRGBColor(aInitColor.getBColor()); + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::BackgroundColorPrimitive2D(aRGBColor)); + + rVisitor.visit(xReference); +} + +ViewContactOfPageBackground::ViewContactOfPageBackground(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfPageBackground::~ViewContactOfPageBackground() +{ +} + +ViewObjectContact& ViewContactOfPageShadow::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageShadow(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfPageShadow::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + static bool bUseOldPageShadow(false); // loplugin:constvars:ignore + const SdrPage& rPage = getPage(); + basegfx::B2DHomMatrix aPageMatrix; + aPageMatrix.set(0, 0, static_cast<double>(rPage.GetWidth())); + aPageMatrix.set(1, 1, static_cast<double>(rPage.GetHeight())); + + if(bUseOldPageShadow) + { + // create page shadow polygon + const double fPageBorderFactor(1.0 / 256.0); + basegfx::B2DPolygon aPageShadowPolygon; + aPageShadowPolygon.append(basegfx::B2DPoint(1.0, fPageBorderFactor)); + aPageShadowPolygon.append(basegfx::B2DPoint(1.0 + fPageBorderFactor, fPageBorderFactor)); + aPageShadowPolygon.append(basegfx::B2DPoint(1.0 + fPageBorderFactor, 1.0 + fPageBorderFactor)); + aPageShadowPolygon.append(basegfx::B2DPoint(fPageBorderFactor, 1.0 + fPageBorderFactor)); + aPageShadowPolygon.append(basegfx::B2DPoint(fPageBorderFactor, 1.0)); + aPageShadowPolygon.append(basegfx::B2DPoint(1.0, 1.0)); + aPageShadowPolygon.setClosed(true); + aPageShadowPolygon.transform(aPageMatrix); + + // We have only the page information, not the view information. Use the + // svtools::FONTCOLOR color for initialisation + const svtools::ColorConfig aColorConfig; + const Color aShadowColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor); + const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor()); + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aPageShadowPolygon), + aRGBShadowColor)); + + rVisitor.visit(xReference); + } + else + { + static vcl::DeleteOnDeinit< drawinglayer::primitive2d::DiscreteShadow > aDiscreteShadow(( + BitmapEx(SIP_SA_PAGESHADOW35X35))); + + if(aDiscreteShadow.get()) + { + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::DiscreteShadowPrimitive2D( + aPageMatrix, + *aDiscreteShadow.get())); + + rVisitor.visit(xReference); + } + } +} + +ViewContactOfPageShadow::ViewContactOfPageShadow(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfPageShadow::~ViewContactOfPageShadow() +{ +} + +ViewObjectContact& ViewContactOfMasterPage::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfMasterPage(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfMasterPage::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // this class is used when the page is a MasterPage and is responsible to + // create a visualisation for the MPBGO, if exists. This needs to be suppressed + // when a SdrPage which uses a MasterPage creates it's output. Suppression + // is done in the corresponding VOC since DisplayInfo data is needed + const SdrPage& rPage = getPage(); + + if(rPage.IsMasterPage()) + { + if(0 == rPage.GetPageNum()) + { + // #i98063# + // filter MasterPage 0 since it's the HandoutPage. Thus, it's a + // MasterPage, but has no MPBGO, so there is nothing to do here. + } + else + { + drawinglayer::attribute::SdrFillAttribute aFill; + + // #i110846# Suppress SdrPage FillStyle for MasterPages without StyleSheets, + // else the PoolDefault (XFILL_COLOR and Blue8) will be used. Normally, all + // MasterPages should have a StyleSheet exactly for this reason, but historically + // e.g. the Notes MasterPage has no StyleSheet set (and there maybe others). + if(rPage.getSdrPageProperties().GetStyleSheet()) + { + // create page fill attributes with correct properties + aFill = drawinglayer::primitive2d::createNewSdrFillAttribute( + rPage.getSdrPageProperties().GetItemSet()); + } + + if(!aFill.isDefault()) + { + // direct model data is the page size, get and use it + const basegfx::B2DRange aOuterRange( + 0, 0, rPage.GetWidth(), rPage.GetHeight()); + const basegfx::B2DRange aInnerRange( + rPage.GetLeftBorder(), rPage.GetUpperBorder(), + rPage.GetWidth() - rPage.GetRightBorder(), rPage.GetHeight() - rPage.GetLowerBorder()); + bool const isFullSize(rPage.IsBackgroundFullSize()); + const basegfx::B2DPolygon aFillPolygon( + basegfx::utils::createPolygonFromRect(isFullSize ? aOuterRange : aInnerRange)); + const drawinglayer::primitive2d::Primitive2DReference xReference( + drawinglayer::primitive2d::createPolyPolygonFillPrimitive( + basegfx::B2DPolyPolygon(aFillPolygon), + aFill, + drawinglayer::attribute::FillGradientAttribute())); + + rVisitor.visit(xReference); + } + } + } +} + +ViewContactOfMasterPage::ViewContactOfMasterPage(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfMasterPage::~ViewContactOfMasterPage() +{ +} + +ViewObjectContact& ViewContactOfPageFill::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageFill(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfPageFill::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPage& rPage = getPage(); + const basegfx::B2DRange aPageFillRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight())); + const basegfx::B2DPolygon aPageFillPolygon(basegfx::utils::createPolygonFromRect(aPageFillRange)); + + // We have only the page information, not the view information. Use the + // svtools::DOCCOLOR color for initialisation + const svtools::ColorConfig aColorConfig; + const Color aPageFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor); + + // create and add primitive + const basegfx::BColor aRGBColor(aPageFillColor.getBColor()); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPageFillPolygon), aRGBColor))); +} + +ViewContactOfPageFill::ViewContactOfPageFill(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfPageFill::~ViewContactOfPageFill() +{ +} + +ViewObjectContact& ViewContactOfOuterPageBorder::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfOuterPageBorder(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfOuterPageBorder::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPage& rPage = getPage(); + const basegfx::B2DRange aPageBorderRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight())); + + // Changed to 0x949599 for renaissance, before svtools::FONTCOLOR was used. + // Added old case as fallback for HighContrast. + basegfx::BColor aRGBBorderColor(0x94 / double(0xff), 0x95 / double(0xff), 0x99 / double(0xff)); + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + const svtools::ColorConfig aColorConfig; + const Color aBorderColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor); + + aRGBBorderColor = aBorderColor.getBColor(); + } + + if(rPage.getPageBorderOnlyLeftRight()) + { + // #i93597# for Report Designer, the page border shall be only displayed right and left, + // but not top and bottom. Create simplified geometry. + basegfx::B2DPolygon aLeft, aRight; + + aLeft.append(basegfx::B2DPoint(aPageBorderRange.getMinX(), aPageBorderRange.getMinY())); + aLeft.append(basegfx::B2DPoint(aPageBorderRange.getMinX(), aPageBorderRange.getMaxY())); + + aRight.append(basegfx::B2DPoint(aPageBorderRange.getMaxX(), aPageBorderRange.getMinY())); + aRight.append(basegfx::B2DPoint(aPageBorderRange.getMaxX(), aPageBorderRange.getMaxY())); + + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aLeft), aRGBBorderColor))); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aRight), aRGBBorderColor))); + } + else + { + basegfx::B2DPolygon aPageBorderPolygon(basegfx::utils::createPolygonFromRect(aPageBorderRange)); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aPageBorderPolygon), aRGBBorderColor))); + } +} + +ViewContactOfOuterPageBorder::ViewContactOfOuterPageBorder(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfOuterPageBorder::~ViewContactOfOuterPageBorder() +{ +} + +ViewObjectContact& ViewContactOfInnerPageBorder::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfInnerPageBorder(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfInnerPageBorder::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPage& rPage = getPage(); + const basegfx::B2DRange aPageBorderRange( + static_cast<double>(rPage.GetLeftBorder()), static_cast<double>(rPage.GetUpperBorder()), + static_cast<double>(rPage.GetWidth() - rPage.GetRightBorder()), static_cast<double>(rPage.GetHeight() - rPage.GetLowerBorder())); + basegfx::B2DPolygon aPageBorderPolygon(basegfx::utils::createPolygonFromRect(aPageBorderRange)); + + // We have only the page information, not the view information. Use the + // svtools::FONTCOLOR or svtools::DOCBOUNDARIES color for initialisation + const svtools::ColorConfig aColorConfig; + Color aBorderColor; + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + aBorderColor = aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor; + } + else + { + svtools::ColorConfigValue aBorderConfig = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES); + aBorderColor = aBorderConfig.bIsVisible ? aBorderConfig.nColor : + aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor; + } + + // create page outer border primitive + const basegfx::BColor aRGBBorderColor(aBorderColor.getBColor()); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aPageBorderPolygon), aRGBBorderColor))); +} + +ViewContactOfInnerPageBorder::ViewContactOfInnerPageBorder(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfInnerPageBorder::~ViewContactOfInnerPageBorder() +{ +} + +ViewObjectContact& ViewContactOfPageHierarchy::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageHierarchy(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfPageHierarchy::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // collect sub-hierarchy + const sal_uInt32 nObjectCount(GetObjectCount()); + + // collect all sub-primitives + for(sal_uInt32 a(0); a < nObjectCount; a++) + { + const ViewContact& rCandidate(GetViewContact(a)); + rCandidate.getViewIndependentPrimitive2DContainer(rVisitor); + } +} + +ViewContactOfPageHierarchy::ViewContactOfPageHierarchy(ViewContactOfSdrPage& rParentViewContactOfSdrPage) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage) +{ +} + +ViewContactOfPageHierarchy::~ViewContactOfPageHierarchy() +{ +} + +sal_uInt32 ViewContactOfPageHierarchy::GetObjectCount() const +{ + return getPage().GetObjCount(); +} + +SdrObject& ViewContactOfPageHierarchy::GetSdrObject(sal_uInt32 nIndex) const +{ + SdrObject* pObj = getPage().GetObj(nIndex); + assert(pObj && "ViewContactOfPageHierarchy::GetViewContact: Corrupt SdrObjList (!)"); + return *pObj; +} + +ViewContact& ViewContactOfPageHierarchy::GetViewContact(sal_uInt32 nIndex) const +{ + return GetSdrObject(nIndex).GetViewContact(); +} + +void ViewContactOfPageHierarchy::getPrimitive2DSequenceHierarchyOfIndex( + sal_uInt32 nIndex, DisplayInfo& rDisplayInfo, ObjectContact& rObjectContact, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject& rSdrObject(GetSdrObject(nIndex)); + + // optimization over parent impl to skip SdrObject::GetViewContent(), etc if the SdrObject isn't + // shown on the target layer. ViewObjectContactOfSdrobject::getPrimitive2DSequenceHierarchy does + // the same check, but after a set of allocations which is expensive in the case of SdrCaptions + // in a calc internal layer where there can be thousands of such objects. + if (!ViewObjectContactOfSdrObj::isObjectVisibleOnAnyLayer(rSdrObject, rDisplayInfo.GetProcessLayers())) + return; + + ViewContact& rViewContact = rSdrObject.GetViewContact(); + const ViewObjectContact& rCandidate(rViewContact.GetViewObjectContact(rObjectContact)); + rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor); +} + +ViewObjectContact& ViewContactOfGrid::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageGrid(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfGrid::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const +{ + // We have only the page information, not the view information and thus no grid settings. Create empty + // default. For the view-dependent implementation, see ViewObjectContactOfPageGrid::createPrimitive2DSequence +} + +ViewContactOfGrid::ViewContactOfGrid(ViewContactOfSdrPage& rParentViewContactOfSdrPage, bool bFront) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage), + mbFront(bFront) +{ +} + +ViewContactOfGrid::~ViewContactOfGrid() +{ +} + +ViewObjectContact& ViewContactOfHelplines::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfPageHelplines(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +void ViewContactOfHelplines::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const +{ + // We have only the page information, not the view information and thus no helplines. Create empty + // default. For the view-dependent implementation, see ViewObjectContactOfPageHelplines::createPrimitive2DSequence +} + +ViewContactOfHelplines::ViewContactOfHelplines(ViewContactOfSdrPage& rParentViewContactOfSdrPage, bool bFront) +: ViewContactOfPageSubObject(rParentViewContactOfSdrPage), + mbFront(bFront) +{ +} + +ViewContactOfHelplines::~ViewContactOfHelplines() +{ +} + +// Create an Object-Specific ViewObjectContact, set ViewContact and +// ObjectContact. Always needs to return something. +ViewObjectContact& ViewContactOfSdrPage::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) +{ + ViewObjectContact* pRetval = new ViewObjectContactOfSdrPage(rObjectContact, *this); + DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)"); + + return *pRetval; +} + +ViewContactOfSdrPage::ViewContactOfSdrPage(SdrPage& rPage) +: mrPage(rPage), + maViewContactOfPageBackground(*this), + maViewContactOfPageShadow(*this), + maViewContactOfPageFill(*this), + maViewContactOfMasterPage(*this), + maViewContactOfOuterPageBorder(*this), + maViewContactOfInnerPageBorder(*this), + maViewContactOfGridBack(*this, false), + maViewContactOfHelplinesBack(*this, false), + maViewContactOfPageHierarchy(*this), + maViewContactOfGridFront(*this, true), + maViewContactOfHelplinesFront(*this, true) +{ +} + +ViewContactOfSdrPage::~ViewContactOfSdrPage() +{ +} + +// Access to possible sub-hierarchy +sal_uInt32 ViewContactOfSdrPage::GetObjectCount() const +{ + // Fixed count of content. It contains PageBackground (Wiese), PageShadow, PageFill, + // then - depending on if the page has a MasterPage - either MasterPage Hierarchy + // or MPBGO. Also OuterPageBorder, InnerPageBorder and two pairs of Grid and Helplines + // (for front and back) which internally are visible or not depending on the current + // front/back setting for those. + return 10; +} + +ViewContact& ViewContactOfSdrPage::GetViewContact(sal_uInt32 nIndex) const +{ + switch(nIndex) + { + case 0: return const_cast<ViewContactOfPageBackground&>(maViewContactOfPageBackground); + case 1: return const_cast<ViewContactOfPageShadow&>(maViewContactOfPageShadow); + case 2: return const_cast<ViewContactOfPageFill&>(maViewContactOfPageFill); + case 3: + { + const SdrPage& rPage = GetSdrPage(); + + if(rPage.TRG_HasMasterPage()) + { + return rPage.TRG_GetMasterPageDescriptorViewContact(); + } + else + { + return const_cast<ViewContactOfMasterPage&>(maViewContactOfMasterPage); + } + } + case 4: return const_cast<ViewContactOfOuterPageBorder&>(maViewContactOfOuterPageBorder); + case 5: return const_cast<ViewContactOfInnerPageBorder&>(maViewContactOfInnerPageBorder); + case 6: return const_cast<ViewContactOfGrid&>(maViewContactOfGridBack); + case 7: return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesBack); + case 8: return const_cast<ViewContactOfPageHierarchy&>(maViewContactOfPageHierarchy); + case 9: return const_cast<ViewContactOfGrid&>(maViewContactOfGridFront); + case 10: return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesFront); + default: assert(false);return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesFront); + } +} + +// React on changes of the object of this ViewContact +void ViewContactOfSdrPage::ActionChanged() +{ + // call parent + ViewContact::ActionChanged(); + + // apply to local viewContacts, they all rely on page information. Exception + // is the sub hierarchy; this will not be influenced by the change + maViewContactOfPageBackground.ActionChanged(); + maViewContactOfPageShadow.ActionChanged(); + maViewContactOfPageFill.ActionChanged(); + + const SdrPage& rPage = GetSdrPage(); + + if(rPage.TRG_HasMasterPage()) + { + rPage.TRG_GetMasterPageDescriptorViewContact().ActionChanged(); + } + else if(rPage.IsMasterPage()) + { + maViewContactOfMasterPage.ActionChanged(); + } + + maViewContactOfOuterPageBorder.ActionChanged(); + maViewContactOfInnerPageBorder.ActionChanged(); + maViewContactOfGridBack.ActionChanged(); + maViewContactOfHelplinesBack.ActionChanged(); + maViewContactOfGridFront.ActionChanged(); + maViewContactOfHelplinesFront.ActionChanged(); +} + +void ViewContactOfSdrPage::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // collect all sub-sequences including sub hierarchy. + maViewContactOfPageBackground.getViewIndependentPrimitive2DContainer(rVisitor); + maViewContactOfPageShadow.getViewIndependentPrimitive2DContainer(rVisitor); + maViewContactOfPageFill.getViewIndependentPrimitive2DContainer(rVisitor); + + const SdrPage& rPage = GetSdrPage(); + + if(rPage.TRG_HasMasterPage()) + { + rPage.TRG_GetMasterPageDescriptorViewContact().getViewIndependentPrimitive2DContainer(rVisitor); + } + else if(rPage.IsMasterPage()) + { + maViewContactOfMasterPage.getViewIndependentPrimitive2DContainer(rVisitor); + } + + maViewContactOfOuterPageBorder.getViewIndependentPrimitive2DContainer(rVisitor); + maViewContactOfInnerPageBorder.getViewIndependentPrimitive2DContainer(rVisitor); + maViewContactOfPageHierarchy.getViewIndependentPrimitive2DContainer(rVisitor); + + // Only add front versions of grid and helplines since no visibility test is done, + // so adding the back incarnations is not necessary. This makes the Front + // visualisation the default when no visibility tests are done. + + // Since we have no view here, no grid and helpline definitions are available currently. The used + // methods at ViewContactOfHelplines and ViewContactOfGrid return only empty sequences and + // do not need to be called ATM. This may change later if grid or helpline info gets + // model data (it should not). Keeping the lines commented to hold this hint. + + // drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, maViewContactOfGridFront.getViewIndependentPrimitive2DContainer()); + // drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, maViewContactOfHelplinesFront.getViewIndependentPrimitive2DContainer()); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx new file mode 100644 index 0000000000..d95d5fbeb7 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx @@ -0,0 +1,168 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrpathobj.hxx> +#include <svx/svdopath.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <sdr/primitive2d/sdrpathprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <osl/diagnose.h> +#include <unotools/configmgr.hxx> +#include <vcl/canvastools.hxx> + +namespace sdr::contact +{ + ViewContactOfSdrPathObj::ViewContactOfSdrPathObj(SdrPathObj& rPathObj) + : ViewContactOfTextObj(rPathObj) + { + } + + ViewContactOfSdrPathObj::~ViewContactOfSdrPathObj() + { + } + + /// return true if polycount == 1 + static bool ensureGeometry(basegfx::B2DPolyPolygon& rUnitPolyPolygon) + { + sal_uInt32 nPolyCount(rUnitPolyPolygon.count()); + sal_uInt32 nPointCount(0); + + for(auto const& rPolygon : std::as_const(rUnitPolyPolygon)) + { + nPointCount += rPolygon.count(); + // return early if we definitely have geometry + if (nPointCount > 1) + return nPolyCount == 1; + } + + if(!nPointCount) + { + OSL_FAIL("PolyPolygon object without geometry detected, this should not be created (!)"); + basegfx::B2DPolygon aFallbackLine; + aFallbackLine.append(basegfx::B2DPoint(0.0, 0.0)); + aFallbackLine.append(basegfx::B2DPoint(1000.0, 1000.0)); + rUnitPolyPolygon = basegfx::B2DPolyPolygon(aFallbackLine); + + nPolyCount = 1; + } + + return nPolyCount == 1; + } + + void ViewContactOfSdrPathObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const SfxItemSet& rItemSet = GetPathObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + GetPathObj().getText(0), + false)); + basegfx::B2DPolyPolygon aUnitPolyPolygon(GetPathObj().GetPathPoly()); + bool bPolyCountIsOne(ensureGeometry(aUnitPolyPolygon)); + + // prepare object transformation and unit polygon (direct model data) + basegfx::B2DHomMatrix aObjectMatrix; + basegfx::B2DPolyPolygon aUnitDefinitionPolyPolygon; + const bool bIsLine( + !aUnitPolyPolygon.areControlPointsUsed() + && bPolyCountIsOne + && 2 == aUnitPolyPolygon.getB2DPolygon(0).count()); + + if(bIsLine) + { + // special handling for single line mode (2 points) + const basegfx::B2DPolygon & rSubPolygon(aUnitPolyPolygon.getB2DPolygon(0)); + const basegfx::B2DPoint aStart(rSubPolygon.getB2DPoint(0)); + const basegfx::B2DPoint aEnd(rSubPolygon.getB2DPoint(1)); + const basegfx::B2DVector aLine(aEnd - aStart); + + // #i102548# create new unit polygon for line (horizontal) + static const basegfx::B2DPolygon aNewPolygon{basegfx::B2DPoint(0.0, 0.0), basegfx::B2DPoint(1.0, 0.0)}; + aUnitPolyPolygon.setB2DPolygon(0, aNewPolygon); + + // #i102548# fill objectMatrix with rotation and offset (no shear for lines) + aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aLine.getLength(), 1.0, + 0.0, + atan2(aLine.getY(), aLine.getX()), + aStart.getX(), aStart.getY()); + } + else + { + // #i102548# create unscaled, unsheared, unrotated and untranslated polygon + // (unit polygon) by creating the object matrix and back-transforming the polygon + const basegfx::B2DRange aObjectRange(basegfx::utils::getRange(aUnitPolyPolygon)); + const GeoStat& rGeoStat(GetPathObj().GetGeoStat()); + const double fWidth(aObjectRange.getWidth()); + const double fHeight(aObjectRange.getHeight()); + const double fScaleX(basegfx::fTools::equalZero(fWidth) ? 1.0 : fWidth); + const double fScaleY(basegfx::fTools::equalZero(fHeight) ? 1.0 : fHeight); + + aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + fScaleX, fScaleY, + -rGeoStat.mfTanShearAngle, + rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0, + aObjectRange.getMinX(), aObjectRange.getMinY()); + + // create unit polygon from object's absolute path + basegfx::B2DHomMatrix aInverse(aObjectMatrix); + aInverse.invert(); + aUnitPolyPolygon.transform(aInverse); + + // OperationSmiley: Check if a FillGeometryDefiningShape is set + const SdrObject* pFillGeometryDefiningShape(GetPathObj().getFillGeometryDefiningShape()); + + if(nullptr != pFillGeometryDefiningShape) + { + // If yes, get it's BoundRange and use as defining Geometry for the FillStyle. + // If no, aUnitDefinitionPolyPolygon will just be empty and thus be interpreted + // as unused. + // Using SnapRect will make the FillDefinition to always be extended e.g. + // for rotated/sheared objects. + const tools::Rectangle& rSnapRect(pFillGeometryDefiningShape->GetSnapRect()); + + aUnitDefinitionPolyPolygon.append( + basegfx::utils::createPolygonFromRect( + vcl::unotools::b2DRectangleFromRectangle(rSnapRect))); + + // use same coordinate system as the shape geometry -> this + // makes it relative to shape's unit geometry and thus freely + // transformable with the shape + aUnitDefinitionPolyPolygon.transform(aInverse); + } + } + + // create primitive. Always create primitives to allow the decomposition of + // SdrPathPrimitive2D to create needed invisible elements for HitTest and/or BoundRect + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrPathPrimitive2D( + aObjectMatrix, + aAttribute, + std::move(aUnitPolyPolygon), + std::move(aUnitDefinitionPolyPolygon))); + + rVisitor.visit(xReference); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofsdrrectobj.cxx b/svx/source/sdr/contact/viewcontactofsdrrectobj.cxx new file mode 100644 index 0000000000..46cdd21ad0 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrrectobj.cxx @@ -0,0 +1,88 @@ +/* -*- 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 <sdr/contact/viewcontactofsdrrectobj.hxx> +#include <svx/svdorect.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrrectangleprimitive2d.hxx> +#include <svl/itemset.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <svx/svdmodel.hxx> +#include <vcl/canvastools.hxx> +#include <svx/sdmetitm.hxx> + +namespace sdr::contact { + +ViewContactOfSdrRectObj::ViewContactOfSdrRectObj(SdrRectObj& rRectObj) +: ViewContactOfTextObj(rRectObj) +{ +} + +ViewContactOfSdrRectObj::~ViewContactOfSdrRectObj() +{ +} + +void ViewContactOfSdrRectObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SfxItemSet& rItemSet = GetRectObj().GetMergedItemSet(); + const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute( + drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute( + rItemSet, + GetRectObj().getText(0), + false)); + + // take unrotated snap rect (direct model data) for position and size + const tools::Rectangle aRectangle(GetRectObj().GetGeoRect()); + const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle); + + const GeoStat& rGeoStat(GetRectObj().GetGeoStat()); + + // fill object matrix + basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aObjectRange.getWidth(), aObjectRange.getHeight(), + -rGeoStat.mfTanShearAngle, + rGeoStat.m_nRotationAngle ? toRadians(36000_deg100 - rGeoStat.m_nRotationAngle) : 0.0, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // calculate corner radius + sal_uInt32 nCornerRadius(rItemSet.Get(SDRATTR_CORNER_RADIUS).GetValue()); + double fCornerRadiusX; + double fCornerRadiusY; + drawinglayer::primitive2d::calculateRelativeCornerRadius(nCornerRadius, aObjectRange, fCornerRadiusX, fCornerRadiusY); + + // #i105856# use knowledge about pickthrough from the model + const bool bPickThroughTransparentTextFrames(GetRectObj().getSdrModelFromSdrObject().IsPickThroughTransparentTextFrames()); + + // create primitive. Always create primitives to allow the decomposition of + // SdrRectanglePrimitive2D to create needed invisible elements for HitTest and/or BoundRect + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::SdrRectanglePrimitive2D( + aObjectMatrix, + aAttribute, + fCornerRadiusX, + fCornerRadiusY, + // #i105856# use fill for HitTest when TextFrame and not PickThrough + GetRectObj().IsTextFrame() && !bPickThroughTransparentTextFrames)); + + rVisitor.visit(xReference); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactoftextobj.cxx b/svx/source/sdr/contact/viewcontactoftextobj.cxx new file mode 100644 index 0000000000..9e10d0130d --- /dev/null +++ b/svx/source/sdr/contact/viewcontactoftextobj.cxx @@ -0,0 +1,33 @@ +/* -*- 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 <sdr/contact/viewcontactoftextobj.hxx> +#include <svx/svdotext.hxx> + +namespace sdr::contact +{ +ViewContactOfTextObj::ViewContactOfTextObj(SdrTextObj& rTextObj) + : ViewContactOfSdrObj(rTextObj) +{ +} + +ViewContactOfTextObj::~ViewContactOfTextObj() {} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofunocontrol.cxx b/svx/source/sdr/contact/viewcontactofunocontrol.cxx new file mode 100644 index 0000000000..3018551d81 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofunocontrol.cxx @@ -0,0 +1,143 @@ +/* -*- 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 <sdr/contact/viewcontactofunocontrol.hxx> +#include <sdr/contact/viewobjectcontactofunocontrol.hxx> +#include <svx/sdr/contact/objectcontactofpageview.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdview.hxx> +#include <svx/sdrpagewindow.hxx> + +#include <vcl/canvastools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <osl/diagnose.h> + + +namespace sdr::contact { + + + using ::com::sun::star::awt::XControl; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::awt::XControlContainer; + using ::com::sun::star::awt::XControlModel; + + + //= ViewContactOfUnoControl + + ViewContactOfUnoControl::ViewContactOfUnoControl( SdrUnoObj& _rUnoObject ) + :ViewContactOfSdrObj( _rUnoObject ) + { + } + + + ViewContactOfUnoControl::~ViewContactOfUnoControl() + { + } + + + Reference< XControl > ViewContactOfUnoControl::getTemporaryControlForWindow( + const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer ) const + { + SdrUnoObj* pUnoObject = dynamic_cast< SdrUnoObj* >( TryToGetSdrObject() ); + OSL_ENSURE( pUnoObject, "ViewContactOfUnoControl::getTemporaryControlForDevice: no SdrUnoObj!" ); + if ( !pUnoObject ) + return nullptr; + return ViewObjectContactOfUnoControl::getTemporaryControlForWindow( _rWindow, _inout_ControlContainer, *pUnoObject ); + } + + + ViewObjectContact& ViewContactOfUnoControl::CreateObjectSpecificViewObjectContact( ObjectContact& _rObjectContact ) + { + // print or print preview requires special handling + const OutputDevice* pDevice = _rObjectContact.TryToGetOutputDevice(); + ObjectContactOfPageView* const pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &_rObjectContact ); + + const bool bPrintOrPreview = pPageViewContact + && ( ( ( pDevice != nullptr ) && ( pDevice->GetOutDevType() == OUTDEV_PRINTER ) ) + || pPageViewContact->GetPageWindow().GetPageView().GetView().IsPrintPreview() + ) + ; + + if ( bPrintOrPreview ) + return *new UnoControlPrintOrPreviewContact( *pPageViewContact, *this ); + + // all others are nowadays served by the same implementation + return *new ViewObjectContactOfUnoControl( _rObjectContact, *this ); + } + + + void ViewContactOfUnoControl::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // create range. Use model data directly, not getBoundRect()/getSnapRect; these will use + // the primitive data themselves in the long run. Use SdrUnoObj's (which is a SdrRectObj) + // call to GetGeoRect() to access SdrTextObj::aRect directly and without executing anything + const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(GetSdrUnoObj().GetGeoRect()); + + // create object transform + basegfx::B2DHomMatrix aTransform; + + aTransform.set(0, 0, aRange.getWidth()); + aTransform.set(1, 1, aRange.getHeight()); + aTransform.set(0, 2, aRange.getMinX()); + aTransform.set(1, 2, aRange.getMinY()); + + Reference< XControlModel > xControlModel = GetSdrUnoObj().GetUnoControlModel(); + + if(xControlModel.is()) + { + void const* pAnchorKey(nullptr); + if (auto const pUserCall = GetSdrObject().GetUserCall()) + { + pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(GetSdrObject()); + } + + // create control primitive WITHOUT possibly existing XControl; this would be done in + // the VOC in createPrimitive2DSequence() + const drawinglayer::primitive2d::Primitive2DReference xRetval( + new drawinglayer::primitive2d::ControlPrimitive2D( + aTransform, + xControlModel, + nullptr, + GetSdrObject().GetTitle(), + GetSdrObject().GetDescription(), + pAnchorKey)); + + rVisitor.visit(xRetval); + } + else + { + // always append an invisible outline for the cases where no visible content exists + const drawinglayer::primitive2d::Primitive2DReference xRetval( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + aTransform)); + + rVisitor.visit(xRetval); + } + } + + +} // namespace sdr::contact + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewcontactofvirtobj.cxx b/svx/source/sdr/contact/viewcontactofvirtobj.cxx new file mode 100644 index 0000000000..f4087d036b --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofvirtobj.cxx @@ -0,0 +1,100 @@ +/* -*- 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/sdr/contact/viewcontactofvirtobj.hxx> +#include <svx/svdovirt.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + +namespace sdr::contact { + +ViewContactOfVirtObj::ViewContactOfVirtObj(SdrVirtObj& rObj) +: ViewContactOfSdrObj(rObj) +{ +} + +ViewContactOfVirtObj::~ViewContactOfVirtObj() +{ +} + +SdrVirtObj& ViewContactOfVirtObj::GetVirtObj() const +{ + return static_cast<SdrVirtObj&>(mrObject); +} + +// Access to possible sub-hierarchy +sal_uInt32 ViewContactOfVirtObj::GetObjectCount() const +{ + // Here, SdrVirtObj's need to return 0L to show that they have no + // sub-hierarchy, even when they are group objects. This is necessary + // to avoid that the same VOCs will be added to the draw hierarchy + // twice which leads to problems. + + // This solution is only a first solution to get things running. Later + // this needs to be replaced with creating real VOCs for the objects + // referenced by virtual objects to avoid the 'trick' of setting the + // offset for painting at the destination OutputDevice. + + // As can be seen, with primitives, the problem will be solved using + // a transformPrimitive, so this solution can stay with primitives. + return 0; +} + +void ViewContactOfVirtObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // create displacement transformation if we have content + basegfx::B2DHomMatrix aObjectMatrix; + Point aAnchor(GetVirtObj().GetAnchorPos()); + + if(aAnchor.X() || aAnchor.Y()) + { + aObjectMatrix.set(0, 2, aAnchor.X()); + aObjectMatrix.set(1, 2, aAnchor.Y()); + } + + // use method from referenced object to get the Primitive2DContainer + drawinglayer::primitive2d::Primitive2DContainer xSequenceVirtual; + GetVirtObj().GetReferencedObj().GetViewContact().getViewIndependentPrimitive2DContainer(xSequenceVirtual); + + if(!xSequenceVirtual.empty()) + { + // create transform primitive + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::TransformPrimitive2D( + aObjectMatrix, + drawinglayer::primitive2d::Primitive2DContainer(xSequenceVirtual))); + + rVisitor.visit(xReference); + } + else + { + // always append an invisible outline for the cases where no visible content exists + const drawinglayer::primitive2d::Primitive2DReference xReference( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + aObjectMatrix)); + + rVisitor.visit(xReference); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx b/svx/source/sdr/contact/viewobjectcontact.cxx new file mode 100644 index 0000000000..55ea968178 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontact.cxx @@ -0,0 +1,630 @@ +/* -*- 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/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/animation/animationstate.hxx> +#include <svx/sdr/contact/viewobjectcontactredirector.hxx> +#include <basegfx/color/bcolor.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> +#include <drawinglayer/primitive2d/animatedprimitive2d.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdomedia.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdotext.hxx> +#include <vcl/pdfwriter.hxx> +#include <vcl/pdfextoutdevdata.hxx> + +using namespace com::sun::star; + +namespace { + +// animated extractor + +// Necessary to filter a sequence of animated primitives from +// a sequence of primitives to find out if animated or not. The decision for +// what to decompose is hard-coded and only done for knowingly animated primitives +// to not decompose too deeply and unnecessarily. This implies that the list +// which is view-specific needs to be expanded by hand when new animated objects +// are added. This may eventually be changed to a dynamically configurable approach +// if necessary. +class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D +{ +protected: + // the found animated primitives + drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence; + + // text animation allowed? + bool mbTextAnimationAllowed : 1; + + // graphic animation allowed? + bool mbGraphicAnimationAllowed : 1; + + // as tooling, the process() implementation takes over API handling and calls this + // virtual render method when the primitive implementation is BasePrimitive2D-based. + virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override; + +public: + AnimatedExtractingProcessor2D( + const drawinglayer::geometry::ViewInformation2D& rViewInformation, + bool bTextAnimationAllowed, + bool bGraphicAnimationAllowed); + + // data access + const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; } + drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() { return std::move(maPrimitive2DSequence); } +}; + +AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D( + const drawinglayer::geometry::ViewInformation2D& rViewInformation, + bool bTextAnimationAllowed, + bool bGraphicAnimationAllowed) +: drawinglayer::processor2d::BaseProcessor2D(rViewInformation), + mbTextAnimationAllowed(bTextAnimationAllowed), + mbGraphicAnimationAllowed(bGraphicAnimationAllowed) +{ +} + +void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) +{ + // known implementation, access directly + switch(rCandidate.getPrimitive2DID()) + { + // add and accept animated primitives directly, no need to decompose + case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D : + case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D : + case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D : + { + const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate); + + if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed) + || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed)) + { + const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate)); + maPrimitive2DSequence.push_back(xReference); + } + break; + } + + // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D + // which then produces the animation infos (all when used/needed) + case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D : + case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D : + + // decompose SdrObjects with evtl. animated text + case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D : + case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D : + + // #121194# With Graphic as Bitmap FillStyle, also check + // for primitives filled with animated graphics + case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D: + case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D: + case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D: + + // decompose evtl. animated text contained in MaskPrimitive2D + // or group primitives + case PRIMITIVE2D_ID_MASKPRIMITIVE2D : + case PRIMITIVE2D_ID_GROUPPRIMITIVE2D : + { + process(rCandidate); + break; + } + + default : + { + // nothing to do for the rest + break; + } + } +} + +} // end of anonymous namespace + +namespace sdr::contact { + +ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact) +: mrObjectContact(rObjectContact), + mrViewContact(rViewContact), + maGridOffset(0.0, 0.0), + mnActionChangedCount(0), + mbLazyInvalidate(false) +{ + // make the ViewContact remember me + mrViewContact.AddViewObjectContact(*this); + + // make the ObjectContact remember me + mrObjectContact.AddViewObjectContact(*this); +} + +ViewObjectContact::~ViewObjectContact() +{ + // if the object range is empty, then we have never had the primitive range change, so nothing to invalidate + if (!maObjectRange.isEmpty()) + { + // invalidate in view + if(!getObjectRange().isEmpty()) + { + GetObjectContact().InvalidatePartOfView(maObjectRange); + } + } + + // delete PrimitiveAnimation + mpPrimitiveAnimation.reset(); + + // take care of remembered ObjectContact. Remove from + // OC first. The VC removal (below) CAN trigger a StopGettingViewed() + // which (depending of its implementation) may destroy other OCs. This + // can trigger the deletion of the helper OC of a page visualising object + // which IS the OC of this object. Eventually StopGettingViewed() needs + // to get asynchron later + GetObjectContact().RemoveViewObjectContact(*this); + + // take care of remembered ViewContact + GetViewContact().RemoveViewObjectContact(*this); +} + +const basegfx::B2DRange& ViewObjectContact::getObjectRange() const +{ + if(maObjectRange.isEmpty()) + { + const drawinglayer::geometry::ViewInformation2D& rViewInfo2D = GetObjectContact().getViewInformation2D(); + basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D); + if (!aTempRange.isEmpty()) + { + const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange; + } + else + { + // if range is not computed (new or LazyInvalidate objects), force it + const DisplayInfo aDisplayInfo; + const drawinglayer::primitive2d::Primitive2DContainer& xSequence(getPrimitive2DSequence(aDisplayInfo)); + + if(!xSequence.empty()) + { + const_cast< ViewObjectContact* >(this)->maObjectRange = + xSequence.getB2DRange(rViewInfo2D); + } + } + } + + return maObjectRange; +} + +void ViewObjectContact::ActionChanged() +{ + // clear cached primitives + mxPrimitive2DSequence.clear(); + ++mnActionChangedCount; + + if(mbLazyInvalidate) + return; + + // set local flag + mbLazyInvalidate = true; + + // force ObjectRange + getObjectRange(); + + if(!getObjectRange().isEmpty()) + { + // invalidate current valid range + GetObjectContact().InvalidatePartOfView(maObjectRange); + + // reset gridOffset, it needs to be recalculated + if (GetObjectContact().supportsGridOffsets()) + resetGridOffset(); + else + maObjectRange.reset(); + } + + // register at OC for lazy invalidate + GetObjectContact().setLazyInvalidate(*this); +} + +void ViewObjectContact::triggerLazyInvalidate() +{ + if(!mbLazyInvalidate) + return; + + // reset flag + mbLazyInvalidate = false; + + // force ObjectRange + getObjectRange(); + + if(!getObjectRange().isEmpty()) + { + // invalidate current valid range + GetObjectContact().InvalidatePartOfView(maObjectRange); + } +} + +// Take some action when new objects are inserted +void ViewObjectContact::ActionChildInserted(ViewContact& rChild) +{ + // force creation of the new VOC and trigger it's refresh, so it + // will take part in LazyInvalidate immediately + rChild.GetViewObjectContact(GetObjectContact()).ActionChanged(); + + // forward action to ObjectContact + // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact()); + // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange()); +} + +void ViewObjectContact::checkForPrimitive2DAnimations() +{ + // remove old one + mpPrimitiveAnimation.reset(); + + // check for animated primitives + if(mxPrimitive2DSequence.empty()) + return; + + const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed()); + const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed()); + + if(bTextAnimationAllowed || bGraphicAnimationAllowed) + { + AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(), + bTextAnimationAllowed, bGraphicAnimationAllowed); + aAnimatedExtractor.process(mxPrimitive2DSequence); + + if(!aAnimatedExtractor.getPrimitive2DSequence().empty()) + { + // derived primitiveList is animated, setup new PrimitiveAnimation + mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) ); + } + } +} + +void ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // get the view-independent Primitive from the viewContact + drawinglayer::primitive2d::Primitive2DContainer xRetval; + GetViewContact().getViewIndependentPrimitive2DContainer(xRetval); + + if(!xRetval.empty()) + { + // handle GluePoint + if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible()) + { + const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence()); + + if(!xGlue.empty()) + { + xRetval.append(xGlue); + } + } + + // handle ghosted + if(isPrimitiveGhosted(rDisplayInfo)) + { + const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); + const basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>( + aRGBWhite, + 0.5); + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::ModifiedColorPrimitive2D( + std::move(xRetval), + aBColorModifier)); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + + rVisitor.visit(xRetval); +} + +bool ViewObjectContact::isExportPDFTags() const +{ + return GetObjectContact().isExportTaggedPDF(); +} + +/** Check if we need to embed to a StructureTagPrimitive2D, too. This + was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before +*/ +void ViewObjectContact::createStructureTag(drawinglayer::primitive2d::Primitive2DContainer & rNewPrimitiveSequence) const +{ + SdrObject *const pSdrObj(mrViewContact.TryToGetSdrObject()); + + // Check if we need to embed to a StructureTagPrimitive2D, too. This + // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before + if (!rNewPrimitiveSequence.empty() && isExportPDFTags() + // ISO 14289-1:2014, Clause: 7.3 + && (!pSdrObj || pSdrObj->getParentSdrObjectFromSdrObject() == nullptr)) + { + if (nullptr != pSdrObj && !pSdrObj->IsDecorative()) + { + vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement); + const SdrInventor nInventor(pSdrObj->GetObjInventor()); + const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier()); + const bool bIsTextObj(nullptr != DynCastSdrTextObj(pSdrObj)); + + // Note: SwFlyDrawObj/SwVirtFlyDrawObj have SdrInventor::Swg - these + // are *not* handled here because not all of them are painted + // completely with primitives, so a tag here does not encapsulate them. + // The tag must be created by SwTaggedPDFHelper until this is fixed. + if ( nInventor == SdrInventor::Default ) + { + if ( nIdentifier == SdrObjKind::Group ) + eElement = vcl::PDFWriter::Figure; + else if (nIdentifier == SdrObjKind::Table) + eElement = vcl::PDFWriter::Table; + else if (nIdentifier == SdrObjKind::Media) + eElement = vcl::PDFWriter::Annot; + else if ( nIdentifier == SdrObjKind::TitleText ) + eElement = vcl::PDFWriter::Heading; + else if ( nIdentifier == SdrObjKind::OutlineText ) + eElement = vcl::PDFWriter::Division; + else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() ) + eElement = vcl::PDFWriter::Figure; + else + eElement = vcl::PDFWriter::Division; + } + + if(vcl::PDFWriter::NonStructElement != eElement) + { + SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject()); + + if(pSdrPage) + { + const bool bBackground(pSdrPage->IsMasterPage()); + const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier()); + // note: there must be output device here, in PDF export + void const* pAnchorKey(nullptr); + if (auto const pUserCall = pSdrObj->GetUserCall()) + { + pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(*pSdrObj); + } + + ::std::vector<sal_Int32> annotIds; + if (eElement == vcl::PDFWriter::Annot + && !static_cast<SdrMediaObj*>(pSdrObj)->getURL().isEmpty()) + { + auto const pPDFExtOutDevData(GetObjectContact().GetPDFExtOutDevData()); + assert(pPDFExtOutDevData); + annotIds = pPDFExtOutDevData->GetScreenAnnotIds(pSdrObj); + } + + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::StructureTagPrimitive2D( + eElement, + bBackground, + bImage, + std::move(rNewPrimitiveSequence), + pAnchorKey, + &annotIds)); + rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + } + else + { + // page backgrounds etc should be tagged as artifacts: + rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { + new drawinglayer::primitive2d::StructureTagPrimitive2D( + // lies to force silly VclMetafileProcessor2D to emit NonStructElement + vcl::PDFWriter::Division, + true, + true, + std::move(rNewPrimitiveSequence)) + }; + } + } +} + +drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const +{ + // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not) + SdrObject* pSdrObj(mrViewContact.TryToGetSdrObject()); + + if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable()) + { + if (!mxPrimitive2DSequence.empty()) + return mxPrimitive2DSequence; + } + + // prepare new representation + drawinglayer::primitive2d::Primitive2DContainer xNewPrimitiveSequence; + + // take care of redirectors and create new list + ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector(); + + if(pRedirector) + { + pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence); + } + else + { + createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence); + } + + // check and eventually embed to GridOffset transform primitive (calc only) + if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets()) + { + const basegfx::B2DVector& rGridOffset(getGridOffset()); + + if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY()) + { + const basegfx::B2DHomMatrix aTranslateGridOffset( + basegfx::utils::createTranslateB2DHomMatrix( + rGridOffset)); + drawinglayer::primitive2d::Primitive2DReference aEmbed( + new drawinglayer::primitive2d::TransformPrimitive2D( + aTranslateGridOffset, + std::move(xNewPrimitiveSequence))); + xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed }; + } + } + + createStructureTag(xNewPrimitiveSequence); + + // Local up-to-date checks. New list different from local one? + // This is the important point where it gets decided if the current or the new + // representation gets used. This is important for performance, since the + // current representation contains possible precious decompositions. That + // comparisons triggers exactly if something in the object visualization + // has changed. + // Note: That is the main reason for BasePrimitive2D::operator== at all. I + // have alternatively tried to invalidate the local representation on object + // change, but that is simply not reliable. + // Note2: I did that once in aw080, the lost CWS, and it worked well enough + // so that I could remove *all* operator== from all derivations of + // BasePrimitive2D, so it can be done again (with the needed resources) + if(mxPrimitive2DSequence != xNewPrimitiveSequence) + { + // has changed, copy content + const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence); + + // check for animated stuff + const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations(); + + // always update object range when PrimitiveSequence changes + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); + const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D); + } + + // return current Primitive2DContainer + return mxPrimitive2DSequence; +} + +bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const +{ + // default: always visible + return true; +} + +bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const +{ + // default: standard check + return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive()); +} + +void ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // check model-view visibility + if(!isPrimitiveVisible(rDisplayInfo)) + return; + + getPrimitive2DSequence(rDisplayInfo); + if(mxPrimitive2DSequence.empty()) + return; + + // get ranges + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); + // tdf#147164 cannot use maObjectRange here, it is unreliable + const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D)); + const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport()); + + // check geometrical visibility + bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange); + if(!bVisible) + return; + + // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it + auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence); + int nPrevCount = mnActionChangedCount; + + rVisitor.visit(tmp); + + // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back + if (mnActionChangedCount == nPrevCount) + const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp); +} + +void ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + ViewContact& rViewContact = GetViewContact(); + const sal_uInt32 nSubHierarchyCount(rViewContact.GetObjectCount()); + + for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) + rViewContact.getPrimitive2DSequenceHierarchyOfIndex(a, rDisplayInfo, GetObjectContact(), rVisitor); +} + +// Support getting a GridOffset per object and view for non-linear ViewToDevice +// transformation (calc). On-demand created by delegating to the ObjectContact +// (->View) that has then all needed information +const basegfx::B2DVector& ViewObjectContact::getGridOffset() const +{ + if (GetObjectContact().supportsGridOffsets()) + { + if (fabs(maGridOffset.getX()) > 1000.0) + { + // Huge offsets are a hint for error -> usually the conditions for + // calculation have changed. E.g. - I saw errors with +/-5740, that + // was in the environment of massive external UNO API using LO as + // target. + // If conditions for this calculation change, it is usually required to call + // - ViewObjectContact::resetGridOffset(), or + // - ObjectContact::resetAllGridOffsets() or + // - ScDrawView::resetGridOffsetsForAllSdrPageViews() + // as it is done e.g. when zoom changes (see ScDrawView::RecalcScale()). + // Theoretically these resets have to be done for any precondition + // changed that is used in the calculation of that value (see + // ScDrawView::calculateGridOffsetForSdrObject). + // This is not complete and would be hard to do so. + // Since it is just a buffered value and re-calculation is not + // expensive (linear O(n)) we can just reset suspicious values here. + // Hopefully - when that non-linear ViewTransformation problem for + // the calc-view gets solved one day - all this can be removed + // again. For now, let's just reset here and force re-calculation. + // Add a SAL_WARN to inform about this. + SAL_WARN("svx", "Suspicious GridOffset value resetted (!)"); + const_cast<ViewObjectContact*>(this)->maGridOffset.setX(0.0); + const_cast<ViewObjectContact*>(this)->maGridOffset.setY(0.0); + } + + if(0.0 == maGridOffset.getX() && 0.0 == maGridOffset.getY() && GetObjectContact().supportsGridOffsets()) + { + // create on-demand + GetObjectContact().calculateGridOffsetForViewObjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this); + } + } + + return maGridOffset; +} + +void ViewObjectContact::resetGridOffset() +{ + // reset buffered GridOffset itself + maGridOffset.setX(0.0); + maGridOffset.setY(0.0); + + // also reset sequence to get a re-calculation when GridOffset changes + mxPrimitive2DSequence.clear(); + maObjectRange.reset(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofe3d.cxx b/svx/source/sdr/contact/viewobjectcontactofe3d.cxx new file mode 100644 index 0000000000..c6d41bdc95 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofe3d.cxx @@ -0,0 +1,72 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofe3d.hxx> +#include <sdr/contact/viewcontactofe3d.hxx> +#include <basegfx/color/bcolor.hxx> +#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx> + +namespace sdr::contact +{ + ViewObjectContactOfE3d::ViewObjectContactOfE3d(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContactOfSdrObj(rObjectContact, rViewContact) + { + } + + ViewObjectContactOfE3d::~ViewObjectContactOfE3d() + { + } + + drawinglayer::primitive3d::Primitive3DContainer ViewObjectContactOfE3d::getPrimitive3DContainer(const DisplayInfo& rDisplayInfo) const + { + // get the view-independent Primitive from the viewContact + const ViewContactOfE3d& rViewContactOfE3d(dynamic_cast< const ViewContactOfE3d& >(GetViewContact())); + drawinglayer::primitive3d::Primitive3DContainer xRetval(rViewContactOfE3d.getViewIndependentPrimitive3DContainer()); + + // handle ghosted + if(isPrimitiveGhosted(rDisplayInfo)) + { + const ::basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); + const ::basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>( + aRGBWhite, + 0.5); + const drawinglayer::primitive3d::Primitive3DReference xReference( + new drawinglayer::primitive3d::ModifiedColorPrimitive3D( + xRetval, + aBColorModifier)); + + xRetval = { xReference }; + } + + return xRetval; + } + + void ViewObjectContactOfE3d::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + const ViewContactOfE3d& rViewContact = static_cast< const ViewContactOfE3d& >(GetViewContact()); + + // get 3d primitive vector, isPrimitiveVisible() is done in 3d creator + rVisitor.visit(rViewContact.impCreateWithGivenPrimitive3DContainer(getPrimitive3DContainer(rDisplayInfo))); + } + + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx b/svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx new file mode 100644 index 0000000000..ac7ad90f13 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx @@ -0,0 +1,137 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofe3dscene.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/viewcontactofe3dscene.hxx> +#include <basegfx/color/bcolormodifier.hxx> +#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx> + + +using namespace com::sun::star; + + +namespace +{ + // Helper method to recursively travel the DrawHierarchy for 3D objects contained in + // the 2D Scene. This will create all VOCs for the current OC which are needed + // for ActionChanged() functionality + void impInternalSubHierarchyTraveller(const sdr::contact::ViewObjectContact& rVOC) + { + const sdr::contact::ViewContact& rVC = rVOC.GetViewContact(); + const sal_uInt32 nSubHierarchyCount(rVC.GetObjectCount()); + + for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) + { + const sdr::contact::ViewObjectContact& rCandidate(rVC.GetViewContact(a).GetViewObjectContact(rVOC.GetObjectContact())); + impInternalSubHierarchyTraveller(rCandidate); + } + } +} // end of anonymous namespace + + +namespace sdr::contact +{ + ViewObjectContactOfE3dScene::ViewObjectContactOfE3dScene(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContactOfSdrObj(rObjectContact, rViewContact) + { + } + + ViewObjectContactOfE3dScene::~ViewObjectContactOfE3dScene() + { + } + + void ViewObjectContactOfE3dScene::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // handle ghosted, else the whole 3d group will be encapsulated to a ghosted primitive set (see below) + const bool bHandleGhostedDisplay(GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive()); + const bool bIsActiveVC(bHandleGhostedDisplay && GetObjectContact().getActiveViewContact() == &GetViewContact()); + + if(bIsActiveVC) + { + // switch off ghosted, display contents normal + const_cast< DisplayInfo& >(rDisplayInfo).ClearGhostedDrawMode(); + } + + // create 2d primitive with content, use layer visibility test + // this uses no ghosted mode, so scenes in scenes and entering them will not + // support ghosted for now. This is no problem currently but would need to be + // added when sub-groups in 3d will be added one day. + const ViewContactOfE3dScene& rViewContact = dynamic_cast< ViewContactOfE3dScene& >(GetViewContact()); + const SdrLayerIDSet& rVisibleLayers = rDisplayInfo.GetProcessLayers(); + drawinglayer::primitive2d::Primitive2DContainer xRetval(rViewContact.createScenePrimitive2DSequence(&rVisibleLayers)); + + if(!xRetval.empty()) + { + // allow evtl. embedding in object-specific infos, e.g. Name, Title, Description + xRetval = rViewContact.embedToObjectSpecificInformation(std::move(xRetval)); + + // handle GluePoint + if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible()) + { + const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence()); + + if(!xGlue.empty()) + { + xRetval.append(xGlue); + } + } + + // handle ghosted + if(isPrimitiveGhosted(rDisplayInfo)) + { + const ::basegfx::BColor aRGBWhite(1.0, 1.0, 1.0); + const ::basegfx::BColorModifierSharedPtr aBColorModifier = + std::make_shared<basegfx::BColorModifier_interpolate>( + aRGBWhite, + 0.5); + const drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::ModifiedColorPrimitive2D( + std::move(xRetval), + aBColorModifier)); + + xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + + if(bIsActiveVC) + { + // set back, display ghosted again + const_cast< DisplayInfo& >(rDisplayInfo).SetGhostedDrawMode(); + } + + rVisitor.visit(xRetval); + } + + void ViewObjectContactOfE3dScene::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // To get the VOCs for the contained 3D objects created to get the correct + // Draw hierarchy and ActionChanged() working properly, travel the DrawHierarchy + // using a local tooling method + impInternalSubHierarchyTraveller(*this); + + // call parent + ViewObjectContactOfSdrObj::getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx new file mode 100644 index 0000000000..601ec28df1 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx @@ -0,0 +1,57 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofgraphic.hxx> +#include <sdr/contact/viewcontactofgraphic.hxx> +#include <svx/sdr/contact/objectcontact.hxx> + +namespace sdr::contact +{ + void ViewObjectContactOfGraphic::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // #i103255# suppress when graphic needs draft visualisation and output + // is for PDF export/Printer + const ViewContactOfGraphic& rVCOfGraphic = static_cast< const ViewContactOfGraphic& >(GetViewContact()); + + if(rVCOfGraphic.visualisationUsesDraft()) + { + const ObjectContact& rObjectContact = GetObjectContact(); + + if(rObjectContact.isOutputToPDFFile() || rObjectContact.isOutputToPrinter()) + { + return; + } + } + + // get return value by calling parent + ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo, rVisitor); + } + + ViewObjectContactOfGraphic::ViewObjectContactOfGraphic(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContactOfSdrObj(rObjectContact, rViewContact) + { + } + + ViewObjectContactOfGraphic::~ViewObjectContactOfGraphic() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofgroup.cxx b/svx/source/sdr/contact/viewobjectcontactofgroup.cxx new file mode 100644 index 0000000000..6a59cfc335 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofgroup.cxx @@ -0,0 +1,93 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofgroup.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdobj.hxx> + + +using namespace com::sun::star; + + +namespace sdr::contact +{ + ViewObjectContactOfGroup::ViewObjectContactOfGroup(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContactOfSdrObj(rObjectContact, rViewContact) + { + } + + ViewObjectContactOfGroup::~ViewObjectContactOfGroup() + { + } + + void ViewObjectContactOfGroup::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + // check model-view visibility + if(!isPrimitiveVisible(rDisplayInfo)) + return; + + drawinglayer::primitive2d::Primitive2DContainer primitiveSequence; + + const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); + if(nSubHierarchyCount) + { + const bool bDoGhostedDisplaying( + GetObjectContact().DoVisualizeEnteredGroup() + && !GetObjectContact().isOutputToPrinter() + && GetObjectContact().getActiveViewContact() == &GetViewContact()); + + if(bDoGhostedDisplaying) + { + rDisplayInfo.ClearGhostedDrawMode(); + } + + // visit object hierarchy + getPrimitive2DSequenceSubHierarchy(rDisplayInfo, primitiveSequence); + + if(bDoGhostedDisplaying) + { + rDisplayInfo.SetGhostedDrawMode(); + } + } + else + { + // draw replacement object for group. This will use ViewContactOfGroup::createViewIndependentPrimitive2DSequence + // which creates the replacement primitives for an empty group + ViewObjectContactOfSdrObj::getPrimitive2DSequenceHierarchy(rDisplayInfo, primitiveSequence); + } + + primitiveSequence = GetViewContact().embedToObjectSpecificInformation(primitiveSequence); + + // ISO 14289-1:2014, Clause: 7.3 + createStructureTag(primitiveSequence); + + rVisitor.visit(primitiveSequence); + } + + bool ViewObjectContactOfGroup::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& aLayers) const + { + return getSdrObject().isVisibleOnAnyOfTheseLayers(aLayers); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx b/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx new file mode 100644 index 0000000000..baa039b1bb --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.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 <sdr/contact/viewobjectcontactofmasterpagedescriptor.hxx> +#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/svdpage.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + + +namespace sdr::contact +{ + ViewObjectContactOfMasterPageDescriptor::ViewObjectContactOfMasterPageDescriptor(ObjectContact& rObjectContact, ViewContact& rViewContact) + : ViewObjectContact(rObjectContact, rViewContact) + { + } + + ViewObjectContactOfMasterPageDescriptor::~ViewObjectContactOfMasterPageDescriptor() + { + } + + bool ViewObjectContactOfMasterPageDescriptor::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const + { + if(rDisplayInfo.GetControlLayerProcessingActive()) + { + return false; + } + + // display mster page content? + if (!GetObjectContact().isMasterPageActive()) + { + return false; + } + + return true; + } + + void ViewObjectContactOfMasterPageDescriptor::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + drawinglayer::primitive2d::Primitive2DContainer xMasterPageSequence; + const sdr::MasterPageDescriptor& rDescriptor = static_cast< ViewContactOfMasterPageDescriptor& >(GetViewContact()).GetMasterPageDescriptor(); + + // used range (retval) is fixed here, it's the MasterPage fill range + const SdrPage& rOwnerPage = rDescriptor.GetOwnerPage(); + const basegfx::B2DRange aInnerRange( + rOwnerPage.GetLeftBorder(), rOwnerPage.GetUpperBorder(), + rOwnerPage.GetWidth() - rOwnerPage.GetRightBorder(), rOwnerPage.GetHeight() - rOwnerPage.GetLowerBorder()); + const basegfx::B2DRange aOuterRange( + 0, 0, rOwnerPage.GetWidth(), rOwnerPage.GetHeight()); + // ??? somehow only the master page's bit is used + bool const isFullSize(rDescriptor.GetUsedPage().IsBackgroundFullSize()); + basegfx::B2DRange const& rPageFillRange(isFullSize ? aOuterRange : aInnerRange); + + // Modify DisplayInfo for MasterPageContent collection; remember original layers and + // set combined SdrLayerIDSet; set MasterPagePaint flag + const SdrLayerIDSet aRememberedLayers(rDisplayInfo.GetProcessLayers()); + SdrLayerIDSet aPreprocessedLayers(aRememberedLayers); + aPreprocessedLayers &= rDescriptor.GetVisibleLayers(); + rDisplayInfo.SetProcessLayers(aPreprocessedLayers); + rDisplayInfo.SetSubContentActive(true); + + // check layer visibility (traditionally was member of layer 1) + if(aPreprocessedLayers.IsSet(SdrLayerID(1))) + { + // hide PageBackground for special DrawModes; historical reasons + if(!GetObjectContact().isDrawModeGray() && !GetObjectContact().isDrawModeHighContrast()) + { + // if visible, create the default background primitive sequence + static_cast< ViewContactOfMasterPageDescriptor& >(GetViewContact()).getViewIndependentPrimitive2DContainer(rVisitor); + } + } + + // hide MasterPage content? Test self here for hierarchy + if(isPrimitiveVisible(rDisplayInfo)) + { + // get the VOC of the Master-SdrPage and get its object hierarchy + ViewContact& rViewContactOfMasterPage(rDescriptor.GetUsedPage().GetViewContact()); + ViewObjectContact& rVOCOfMasterPage(rViewContactOfMasterPage.GetViewObjectContact(GetObjectContact())); + + rVOCOfMasterPage.getPrimitive2DSequenceHierarchy(rDisplayInfo, xMasterPageSequence); + } + + // reset DisplayInfo changes for MasterPage paint + rDisplayInfo.SetProcessLayers(aRememberedLayers); + rDisplayInfo.SetSubContentActive(false); + + if(!xMasterPageSequence.empty()) + { + // get range of MasterPage sub hierarchy + const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D()); + basegfx::B2DRange aSubHierarchyRange(xMasterPageSequence.getB2DRange(rViewInformation2D)); + + if (rPageFillRange.isInside(aSubHierarchyRange)) + { + // completely inside, just render MasterPage content. Add to target + rVisitor.visit(xMasterPageSequence); + } + else if (rPageFillRange.overlaps(aSubHierarchyRange)) + { + // overlapping, compute common area + basegfx::B2DRange aCommonArea(rPageFillRange); + aCommonArea.intersect(aSubHierarchyRange); + + // need to create a clip primitive, add clipped list to target + const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::MaskPrimitive2D( + basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aCommonArea)), std::move(xMasterPageSequence))); + rVisitor.visit(xReference); + } + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx new file mode 100644 index 0000000000..9430ac55ba --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx @@ -0,0 +1,324 @@ +/* -*- 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/idle.hxx> +#include <sdr/contact/viewobjectcontactofpageobj.hxx> +#include <sdr/contact/viewcontactofpageobj.hxx> +#include <svx/svdopage.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svtools/colorcfg.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <sdr/contact/objectcontactofobjlistpainter.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> +#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <vcl/canvastools.hxx> + +using namespace com::sun::star; + +namespace sdr::contact { + +class PagePrimitiveExtractor : public ObjectContactOfPagePainter, public Idle +{ +private: + // the ViewObjectContactOfPageObj using this painter + ViewObjectContactOfPageObj& mrViewObjectContactOfPageObj; + +public: + // basic constructor/destructor + explicit PagePrimitiveExtractor(ViewObjectContactOfPageObj& rVOC); + virtual ~PagePrimitiveExtractor() override; + + // LazyInvalidate request. Supported here to not automatically + // invalidate the second interaction state all the time at the + // original OC + virtual void setLazyInvalidate(ViewObjectContact& rVOC) override; + + // From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism + virtual void Invoke() final override; + + // get primitive visualization + drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequenceForPage(); + + // Own reaction on changes which will be forwarded to the OC of the owner-VOC + virtual void InvalidatePartOfView(const basegfx::B2DRange& rRange) const override; + + // forward access to SdrPageView of ViewObjectContactOfPageObj + virtual bool isOutputToPrinter() const override; + virtual bool isPageDecorationActive() const override; + virtual bool isMasterPageActive() const override; + virtual bool isOutputToRecordingMetaFile() const override; + virtual bool isOutputToPDFFile() const override; + virtual bool isExportTaggedPDF() const override; + virtual ::vcl::PDFExtOutDevData const* GetPDFExtOutDevData() const override; + virtual bool isDrawModeGray() const override; + virtual bool isDrawModeHighContrast() const override; + virtual SdrPageView* TryToGetSdrPageView() const override; + virtual OutputDevice* TryToGetOutputDevice() const override; +}; + +PagePrimitiveExtractor::PagePrimitiveExtractor( + ViewObjectContactOfPageObj& rVOC) +: ObjectContactOfPagePainter(rVOC.GetObjectContact()), Idle("svx PagePrimitiveExtractor"), + mrViewObjectContactOfPageObj(rVOC) +{ + // make this renderer a preview renderer + setPreviewRenderer(true); + + // init timer + SetPriority(TaskPriority::HIGH_IDLE); + Stop(); +} + +PagePrimitiveExtractor::~PagePrimitiveExtractor() +{ + // execute missing LazyInvalidates and stop timer + Invoke(); +} + +void PagePrimitiveExtractor::setLazyInvalidate(ViewObjectContact& /*rVOC*/) +{ + // do NOT call parent, but remember that something is to do by + // starting the LazyInvalidateTimer + Start(); +} + +// From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism +void PagePrimitiveExtractor::Invoke() +{ + // stop the timer + Stop(); + + // invalidate all LazyInvalidate VOCs new situations + const sal_uInt32 nVOCCount(getViewObjectContactCount()); + + for(sal_uInt32 a(0); a < nVOCCount; a++) + { + ViewObjectContact* pCandidate = getViewObjectContact(a); + pCandidate->triggerLazyInvalidate(); + } +} + +drawinglayer::primitive2d::Primitive2DContainer PagePrimitiveExtractor::createPrimitive2DSequenceForPage() +{ + drawinglayer::primitive2d::Primitive2DContainer xRetval; + SdrPage* pStartPage = GetStartPage(); + + if(pStartPage) + { + // update own ViewInformation2D for visualized page + const drawinglayer::geometry::ViewInformation2D& rOriginalViewInformation = mrViewObjectContactOfPageObj.GetObjectContact().getViewInformation2D(); + drawinglayer::geometry::ViewInformation2D aNewViewInformation2D(rOriginalViewInformation); + + // #i101075# use empty range for page content here to force + // the content not to be physically clipped in any way. This + // would be possible, but would require the internal transformation + // which maps between the page visualisation object and the page + // content, including the aspect ratios (for details see in + // PagePreviewPrimitive2D::create2DDecomposition) + aNewViewInformation2D.setViewport(basegfx::B2DRange()); + + aNewViewInformation2D.setVisualizedPage(GetXDrawPageForSdrPage(pStartPage)); + + // no time; page previews are not animated + aNewViewInformation2D.setViewTime(0.0); + + updateViewInformation2D(aNewViewInformation2D); + + // create copy of DisplayInfo to set PagePainting + DisplayInfo aDisplayInfo; + + // get page's VOC + ViewObjectContact& rDrawPageVOContact = pStartPage->GetViewContact().GetViewObjectContact(*this); + + // get whole Primitive2DContainer + rDrawPageVOContact.getPrimitive2DSequenceHierarchy(aDisplayInfo, xRetval); + } + + return xRetval; +} + +void PagePrimitiveExtractor::InvalidatePartOfView(const basegfx::B2DRange& rRange) const +{ + // an invalidate is called at this view, this needs to be translated to an invalidate + // for the using VOC. Coordinates are in page coordinate system. + const SdrPage* pStartPage = GetStartPage(); + + if(pStartPage && !rRange.isEmpty()) + { + const basegfx::B2DRange aPageRange(0.0, 0.0, static_cast<double>(pStartPage->GetWidth()), static_cast<double>(pStartPage->GetHeight())); + + if(rRange.overlaps(aPageRange)) + { + // if object on the page is inside or overlapping with page, create ActionChanged() for + // involved VOC + mrViewObjectContactOfPageObj.ActionChanged(); + } + } +} + +// forward access to SdrPageView to VOCOfPageObj +bool PagePrimitiveExtractor::isOutputToPrinter() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPrinter(); } +bool PagePrimitiveExtractor::isPageDecorationActive() const { return mrViewObjectContactOfPageObj.GetObjectContact().isPageDecorationActive(); } +bool PagePrimitiveExtractor::isMasterPageActive() const { return mrViewObjectContactOfPageObj.GetObjectContact().isMasterPageActive(); } +bool PagePrimitiveExtractor::isOutputToRecordingMetaFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToRecordingMetaFile(); } +bool PagePrimitiveExtractor::isOutputToPDFFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPDFFile(); } +bool PagePrimitiveExtractor::isExportTaggedPDF() const { return mrViewObjectContactOfPageObj.GetObjectContact().isExportTaggedPDF(); } +::vcl::PDFExtOutDevData const* PagePrimitiveExtractor::GetPDFExtOutDevData() const { return mrViewObjectContactOfPageObj.GetObjectContact().GetPDFExtOutDevData(); } +bool PagePrimitiveExtractor::isDrawModeGray() const { return mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeGray(); } +bool PagePrimitiveExtractor::isDrawModeHighContrast() const { return mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeHighContrast(); } +SdrPageView* PagePrimitiveExtractor::TryToGetSdrPageView() const { return mrViewObjectContactOfPageObj.GetObjectContact().TryToGetSdrPageView(); } +OutputDevice* PagePrimitiveExtractor::TryToGetOutputDevice() const { return mrViewObjectContactOfPageObj.GetObjectContact().TryToGetOutputDevice(); } + +void ViewObjectContactOfPageObj::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPageObj& rPageObject(static_cast< ViewContactOfPageObj& >(GetViewContact()).GetPageObj()); + const SdrPage* pPage = rPageObject.GetReferencedPage(); + const svtools::ColorConfig aColorConfig; + + // get PageObject's geometry + basegfx::B2DHomMatrix aPageObjectTransform; + { + const tools::Rectangle aPageObjectModelData(rPageObject.GetLastBoundRect()); + const basegfx::B2DRange aPageObjectBound = vcl::unotools::b2DRectangleFromRectangle(aPageObjectModelData); + + aPageObjectTransform.set(0, 0, aPageObjectBound.getWidth()); + aPageObjectTransform.set(1, 1, aPageObjectBound.getHeight()); + aPageObjectTransform.set(0, 2, aPageObjectBound.getMinX()); + aPageObjectTransform.set(1, 2, aPageObjectBound.getMinY()); + } + + // #i102637# add gray frame also when printing and page exists (handout pages) + const bool bCreateGrayFrame(!GetObjectContact().isOutputToPrinter() || pPage); + + // get displayed page's content. This is the unscaled page content + if(mpExtractor && pPage) + { + // get displayed page's geometry + drawinglayer::primitive2d::Primitive2DContainer xPageContent; + const Size aPageSize(pPage->GetSize()); + const double fPageWidth(aPageSize.getWidth()); + const double fPageHeight(aPageSize.getHeight()); + + // The case that a PageObject contains another PageObject which visualizes the + // same page again would lead to a recursion. Limit that recursion depth to one + // by using a local static bool + static bool bInCreatePrimitive2D(false); + + if(bInCreatePrimitive2D) + { + // Recursion is possible. Create a replacement primitive + xPageContent.resize(2); + const Color aDocColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor); + svtools::ColorConfigValue aBorderConfig = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES); + const Color aBorderColor = aBorderConfig.bIsVisible ? aBorderConfig.nColor : aDocColor; + const basegfx::B2DRange aPageBound(0.0, 0.0, fPageWidth, fPageHeight); + basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aPageBound)); + + // add replacement fill + xPageContent[0] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), aDocColor.getBColor())); + + // add replacement border + xPageContent[1] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aOutline), aBorderColor.getBColor())); + } + else + { + // set recursion flag + bInCreatePrimitive2D = true; + + // init extractor, guarantee existence, set page there + mpExtractor->SetStartPage(pPage); + + // #i105548# also need to copy the VOCRedirector for sub-content creation + mpExtractor->SetViewObjectContactRedirector(GetObjectContact().GetViewObjectContactRedirector()); + + // create page content + xPageContent = mpExtractor->createPrimitive2DSequenceForPage(); + + // #i105548# reset VOCRedirector to not accidentally have a pointer to a + // temporary class, so calls to it are avoided safely + mpExtractor->SetViewObjectContactRedirector(nullptr); + + // reset recursion flag + bInCreatePrimitive2D = false; + } + + // prepare retval + if(!xPageContent.empty()) + { + const uno::Reference< drawing::XDrawPage > xDrawPage(GetXDrawPageForSdrPage(const_cast< SdrPage*>(pPage))); + const drawinglayer::primitive2d::Primitive2DReference xPagePreview(new drawinglayer::primitive2d::PagePreviewPrimitive2D( + xDrawPage, aPageObjectTransform, fPageWidth, fPageHeight, std::move(xPageContent))); + rVisitor.visit(xPagePreview); + } + } + else if(bCreateGrayFrame) + { + // #i105146# no content, but frame display. To make hitting the page preview objects + // on the handout page more simple, add hidden fill geometry + const drawinglayer::primitive2d::Primitive2DReference xFrameHit( + drawinglayer::primitive2d::createHiddenGeometryPrimitives2D( + aPageObjectTransform)); + rVisitor.visit(xFrameHit); + } + + // add a gray outline frame, except not when printing + if(bCreateGrayFrame) + { + const Color aFrameColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES).nColor); + basegfx::B2DPolygon aOwnOutline(basegfx::utils::createUnitPolygon()); + aOwnOutline.transform(aPageObjectTransform); + + const drawinglayer::primitive2d::Primitive2DReference xGrayFrame( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aOwnOutline), aFrameColor.getBColor())); + + rVisitor.visit(xGrayFrame); + } +} + +ViewObjectContactOfPageObj::ViewObjectContactOfPageObj(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfSdrObj(rObjectContact, rViewContact), + mpExtractor(new PagePrimitiveExtractor(*this)) +{ +} + +ViewObjectContactOfPageObj::~ViewObjectContactOfPageObj() +{ + // delete the helper OC + if(mpExtractor) + { + // remember candidate and reset own pointer to avoid action when createPrimitive2DSequence() + // would be called for any reason + std::unique_ptr<PagePrimitiveExtractor> pCandidate = std::move(mpExtractor); + + // also reset the StartPage to avoid ActionChanged() forwardings in the + // PagePrimitiveExtractor::InvalidatePartOfView() implementation + pCandidate->SetStartPage(nullptr); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.cxx new file mode 100644 index 0000000000..777017472b --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.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 <config_features.h> + +#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx> +#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <avmedia/mediaitem.hxx> +#include "sdrmediawindow.hxx" + +namespace sdr::contact { + +ViewObjectContactOfSdrMediaObj::ViewObjectContactOfSdrMediaObj( ObjectContact& rObjectContact, + ViewContact& rViewContact, + const ::avmedia::MediaItem& rMediaItem ) : + ViewObjectContactOfSdrObj( rObjectContact, rViewContact ) +{ +#if HAVE_FEATURE_AVMEDIA + vcl::Window* pWindow = getWindow(); + + if( pWindow ) + { + mpMediaWindow.reset( new SdrMediaWindow( pWindow, *this ) ); + mpMediaWindow->hide(); + executeMediaItem( rMediaItem ); + } +#else + (void) rMediaItem; +#endif +} + +ViewObjectContactOfSdrMediaObj::~ViewObjectContactOfSdrMediaObj() +{ +} + + +vcl::Window* ViewObjectContactOfSdrMediaObj::getWindow() const +{ + vcl::Window* pRetval = nullptr; + + const OutputDevice* oPageOutputDev = getPageViewOutputDevice(); + if( oPageOutputDev ) + { + if(OUTDEV_WINDOW == oPageOutputDev->GetOutDevType()) + { + pRetval = oPageOutputDev->GetOwnerWindow(); + } + } + + return pRetval; +} + + +Size ViewObjectContactOfSdrMediaObj::getPreferredSize() const +{ + Size aRet; + +#if HAVE_FEATURE_AVMEDIA + if( mpMediaWindow ) + aRet = mpMediaWindow->getPreferredSize(); +#else + aRet = Size(0,0); +#endif + + return aRet; +} + +void ViewObjectContactOfSdrMediaObj::ActionChanged() +{ + ViewObjectContactOfSdrObj::ActionChanged(); + updateMediaWindow(false); +} + +void ViewObjectContactOfSdrMediaObj::updateMediaWindow(bool bShow) const +{ +#if HAVE_FEATURE_AVMEDIA + if (!mpMediaWindow || (!bShow && !mpMediaWindow->isVisible())) + return; + + basegfx::B2DRange aViewRange(getObjectRange()); + aViewRange.transform(GetObjectContact().getViewInformation2D().getViewTransformation()); + + const tools::Rectangle aViewRectangle( + static_cast<sal_Int32>(floor(aViewRange.getMinX())), static_cast<sal_Int32>(floor(aViewRange.getMinY())), + static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), static_cast<sal_Int32>(ceil(aViewRange.getMaxY()))); + + // mpMediaWindow contains a SalObject window and gtk won't accept + // the size until after the SalObject widget is shown but if we + // show it before setting a size then vcl will detect that the + // vcl::Window has no size and make it invisible instead. If we + // call setPosSize twice with the same size before and after show + // then the second attempt is a no-op as vcl caches the size. + + // so call it initially with a size arbitrarily 1 pixel wider than + // we want so we have an initial size to make vcl happy + tools::Rectangle aInitialRect(aViewRectangle); + aInitialRect.AdjustRight(1); + mpMediaWindow->setPosSize(aInitialRect); + + // then make it visible + mpMediaWindow->show(); + + // set the final desired size which is different to let vcl send it + // through to gtk which will now accept it as the underlying + // m_pSocket of GtkSalObject::SetPosSize is now visible + mpMediaWindow->setPosSize(aViewRectangle); +#else + (void) bShow; +#endif +} + +void ViewObjectContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const +{ +#if HAVE_FEATURE_AVMEDIA + if( !mpMediaWindow ) + return; + + mpMediaWindow->updateMediaItem( rItem ); + + // show/hide is now dependent of play state + if(avmedia::MediaState::Stop == rItem.getState()) + { + mpMediaWindow->hide(); + } + else + { + updateMediaWindow(true); + } +#else + (void) rItem; +#endif +} + + +void ViewObjectContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem ) +{ +#if HAVE_FEATURE_AVMEDIA + if( mpMediaWindow ) + { + ::avmedia::MediaItem aUpdatedItem; + + mpMediaWindow->executeMediaItem( rItem ); + + // query new properties after trying to set the new properties + updateMediaItem( aUpdatedItem ); + static_cast< ViewContactOfSdrMediaObj& >( GetViewContact() ).mediaPropertiesChanged( aUpdatedItem ); + } +#else + (void) rItem; +#endif +} + + +} // end of namespace sdr::contact + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx new file mode 100644 index 0000000000..0cc353a5b6 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx @@ -0,0 +1,198 @@ +/* -*- 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/sdr/contact/viewobjectcontactofsdrobj.hxx> +#include <svx/sdr/contact/viewcontactofsdrobj.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontactofpageview.hxx> +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdview.hxx> +#include <vcl/outdev.hxx> +#include <vcl/canvastools.hxx> + +#include <fmobj.hxx> + +namespace sdr::contact { + +const SdrObject& ViewObjectContactOfSdrObj::getSdrObject() const +{ + return static_cast< ViewContactOfSdrObj& >(GetViewContact()).GetSdrObject(); +} + +ViewObjectContactOfSdrObj::ViewObjectContactOfSdrObj(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContact(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfSdrObj::~ViewObjectContactOfSdrObj() +{ +} + +bool ViewObjectContactOfSdrObj::isObjectVisibleOnAnyLayer(const SdrObject& rSdrObject, const SdrLayerIDSet& rLayers) +{ + return rLayers.IsSet(rSdrObject.GetLayer()); +} + +bool ViewObjectContactOfSdrObj::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& rLayers) const +{ + return ViewObjectContactOfSdrObj::isObjectVisibleOnAnyLayer(getSdrObject(), rLayers); +} + +bool ViewObjectContactOfSdrObj::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + const SdrObject& rObject = getSdrObject(); + + // Test layer visibility + if(!isPrimitiveVisibleOnAnyLayer(rDisplayInfo.GetProcessLayers())) + { + return false; + } + + if(GetObjectContact().isOutputToPrinter() ) + { + // Test if print output but not printable + if( !rObject.IsPrintable()) + return false; + } + else + { + // test is object is not visible on screen + if( !rObject.IsVisible() ) + return false; + } + + // Test for hidden object on MasterPage + if(rDisplayInfo.GetSubContentActive() && rObject.IsNotVisibleAsMaster()) + { + return false; + } + + // Test for Calc object hiding (for OLE and Graphic it's extra, see there) + const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pSdrPageView) + { + const SdrView& rSdrView = pSdrPageView->GetView(); + const bool bHideOle(rSdrView.getHideOle()); + const bool bHideChart(rSdrView.getHideChart()); + const bool bHideDraw(rSdrView.getHideDraw()); + const bool bHideFormControl(rSdrView.getHideFormControl()); + + if(bHideOle || bHideChart || bHideDraw || bHideFormControl) + { + if(SdrObjKind::OLE2 == rObject.GetObjIdentifier()) + { + if(static_cast<const SdrOle2Obj&>(rObject).IsChart()) + { + // chart + if(bHideChart) + { + return false; + } + } + else + { + // OLE + if(bHideOle) + { + return false; + } + } + } + else if(SdrObjKind::Graphic == rObject.GetObjIdentifier()) + { + // graphic handled like OLE + if(bHideOle) + { + return false; + } + } + else + { + const bool bIsFormControl = dynamic_cast< const FmFormObj * >( &rObject ) != nullptr; + if(bIsFormControl && bHideFormControl) + { + return false; + } + // any other draw object + if(!bIsFormControl && bHideDraw) + { + return false; + } + } + } + } + + // tdf#91260 check if the object is anchored on a different Writer page + // than the one being painted, and if so ignore it (Writer has only one + // SdrPage, so the part of the object that should be visible will be + // painted on the page where it is anchored) + // Note that we cannot check the ViewInformation2D ViewPort for this + // because it is only the part of the page that is currently visible. + basegfx::B2IPoint const& rAnchor(vcl::unotools::b2IPointFromPoint(getSdrObject().GetAnchorPos())); + if (rAnchor.getX() || rAnchor.getY()) // only Writer sets anchor position + { + if (!rDisplayInfo.GetWriterPageFrame().isEmpty() && + !rDisplayInfo.GetWriterPageFrame().isInside(rAnchor)) + { + return false; + } + } + + // Check if this object is in the visible range. + const drawinglayer::geometry::ViewInformation2D& rViewInfo = GetObjectContact().getViewInformation2D(); + basegfx::B2DRange aObjRange = GetViewContact().getRange(rViewInfo); + if (!aObjRange.isEmpty()) + { + const basegfx::B2DRange& rViewRange = rViewInfo.getViewport(); + bool bVisible = rViewRange.isEmpty() || rViewRange.overlaps(aObjRange); + if (!bVisible) + return false; + } + + return true; +} + +const OutputDevice* ViewObjectContactOfSdrObj::getPageViewOutputDevice() const +{ + ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &GetObjectContact() ); + if ( pPageViewContact ) + { + // if the PageWindow has a patched PaintWindow, use the original PaintWindow + // this ensures that our control is _not_ re-created just because somebody + // (temporarily) changed the window to paint onto. + // #i72429# / 2007-02-20 / frank.schoenheit (at) sun.com + SdrPageWindow& rPageWindow( pPageViewContact->GetPageWindow() ); + if ( rPageWindow.GetOriginalPaintWindow() ) + return &rPageWindow.GetOriginalPaintWindow()->GetOutputDevice(); + + return &rPageWindow.GetPaintWindow().GetOutputDevice(); + } + return nullptr; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx new file mode 100644 index 0000000000..103e3e05cb --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx @@ -0,0 +1,147 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofsdrole2obj.hxx> +#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx> +#include <sdr/contact/viewcontactofsdrole2obj.hxx> +#include <svx/svdoole2.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdview.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <com/sun/star/embed/EmbedMisc.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svtools/embedhlp.hxx> + +using namespace com::sun::star; + +namespace sdr::contact { + +void ViewObjectContactOfSdrOle2Obj::createPrimitive2DSequence( + const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // override this method to do some things the old SdrOle2Obj::DoPaintObject did. + // In the future, some of these may be solved different, but ATM try to stay compatible + // with the old behaviour + const SdrOle2Obj& rSdrOle2 = static_cast< ViewContactOfSdrOle2Obj& >(GetViewContact()).GetOle2Obj(); + sal_Int32 nState(-1); + + { + const svt::EmbeddedObjectRef& xObjRef = rSdrOle2.getEmbeddedObjectRef(); + if ( xObjRef.is() ) + nState = xObjRef->getCurrentState(); + } + + const bool bIsOutplaceActive(nState == embed::EmbedStates::ACTIVE); + const bool bIsInplaceActive((nState == embed::EmbedStates::INPLACE_ACTIVE) || (nState == embed::EmbedStates::UI_ACTIVE)); + bool bDone(false); + + if (bIsInplaceActive) + { + if( !GetObjectContact().isOutputToPrinter() && !GetObjectContact().isOutputToRecordingMetaFile() ) + { + //no need to create a primitive sequence here as the OLE object does render itself + //in case of charts the superfluous creation of a metafile is strongly performance relevant! + bDone = true; + } + } + + if( !bDone ) + { + //old stuff that should be reworked + { + //if no replacement image is available load the OLE object +// if(!rSdrOle2.GetGraphic()) //try to fetch the metafile - this can lead to the actual creation of the metafile what can be extremely expensive (e.g. for big charts)!!! #i101925# +// { +// // try to create embedded object +// rSdrOle2.GetObjRef(); //this loads the OLE object if it is not loaded already +// } + const svt::EmbeddedObjectRef& xObjRef = rSdrOle2.getEmbeddedObjectRef(); + if(xObjRef.is()) + { + const sal_Int64 nMiscStatus(xObjRef->getStatus(rSdrOle2.GetAspect())); + + // this hack (to change model data during PAINT argh(!)) should be reworked + if(!rSdrOle2.IsResizeProtect() && (nMiscStatus & embed::EmbedMisc::EMBED_NEVERRESIZE)) + { + const_cast< SdrOle2Obj* >(&rSdrOle2)->SetResizeProtect(true); + } + + SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView(); + if(pPageView && (nMiscStatus & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE)) + { + // connect plugin object + pPageView->GetView().DoConnect(const_cast< SdrOle2Obj* >(&rSdrOle2)); + } + } + }//end old stuff to rework + + // create OLE primitive stuff directly at VC with HC as parameter + const ViewContactOfSdrOle2Obj& rVC = static_cast< const ViewContactOfSdrOle2Obj& >(GetViewContact()); + rVC.createPrimitive2DSequenceWithParameters(rVisitor); + + if(bIsOutplaceActive) + { + // do not shade when printing or PDF exporting + if(!GetObjectContact().isOutputToPrinter() && !GetObjectContact().isOutputToRecordingMetaFile()) + { + // get object transformation + const basegfx::B2DHomMatrix aObjectMatrix(static_cast< ViewContactOfSdrOle2Obj& >(GetViewContact()).createObjectTransform()); + + // shade the representation if the object is activated outplace + basegfx::B2DPolygon aObjectOutline(basegfx::utils::createUnitPolygon()); + aObjectOutline.transform(aObjectMatrix); + + // Use a FillHatchPrimitive2D with necessary attributes + drawinglayer::attribute::FillHatchAttribute aFillHatch( + drawinglayer::attribute::HatchStyle::Single, // single hatch + 125.0, // 1.25 mm + basegfx::deg2rad(45.0), // 45 degree diagonal + COL_BLACK.getBColor(), // black color + 3, // same default as VCL, a minimum of three discrete units (pixels) offset + false); // no filling + + const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::PolyPolygonHatchPrimitive2D( + basegfx::B2DPolyPolygon(aObjectOutline), + COL_BLACK.getBColor(), + std::move(aFillHatch))); + + rVisitor.visit(xReference); + } + } + + } +} + +ViewObjectContactOfSdrOle2Obj::ViewObjectContactOfSdrOle2Obj(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfSdrObj(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfSdrOle2Obj::~ViewObjectContactOfSdrOle2Obj() +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx new file mode 100644 index 0000000000..3cc27104c3 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx @@ -0,0 +1,579 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofsdrpage.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <sdr/contact/viewcontactofsdrpage.hxx> +#include <svx/svdview.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdpagv.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/gridprimitive2d.hxx> +#include <drawinglayer/primitive2d/helplineprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <sdr/primitive2d/sdrprimitivetools.hxx> + +using namespace com::sun::star; + +namespace sdr::contact { + +const SdrPage& ViewObjectContactOfPageSubObject::getPage() const +{ + return static_cast< ViewContactOfPageSubObject& >(GetViewContact()).getPage(); +} + +ViewObjectContactOfPageSubObject::ViewObjectContactOfPageSubObject(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContact(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageSubObject::~ViewObjectContactOfPageSubObject() +{ +} + +bool ViewObjectContactOfPageSubObject::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(rDisplayInfo.GetSubContentActive()) + { + return false; + } + + if(rDisplayInfo.GetControlLayerProcessingActive()) + { + return false; + } + + if(!GetObjectContact().isPageDecorationActive()) + { + return false; + } + + if(GetObjectContact().isOutputToPrinter()) + { + return false; + } + + if(!GetObjectContact().TryToGetSdrPageView()) + { + return false; + } + + return true; +} + +bool ViewObjectContactOfPageSubObject::isPrimitiveGhosted(const DisplayInfo& /*rDisplayInfo*/) const +{ + // suppress ghosted for page parts + return false; +} + +ViewObjectContactOfPageBackground::ViewObjectContactOfPageBackground(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageBackground::~ViewObjectContactOfPageBackground() +{ +} + +bool ViewObjectContactOfPageBackground::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + // no page background for preview renderers + if(GetObjectContact().IsPreviewRenderer()) + { + return false; + } + + return true; +} + +void ViewObjectContactOfPageBackground::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // Initialize background. Dependent of IsPageVisible, use ApplicationBackgroundColor or ApplicationDocumentColor. Most + // old renderers for export (html, pdf, gallery, ...) set the page to not visible (SetPageVisible(false)). They expect the + // given OutputDevice to be initialized with the ApplicationDocumentColor then. + const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pPageView) + { + const SdrView& rView = pPageView->GetView(); + Color aInitColor; + + if(rView.IsPageVisible()) + { + aInitColor = pPageView->GetApplicationBackgroundColor(); + } + else + { + aInitColor = pPageView->GetApplicationDocumentColor(); + + if(COL_AUTO == aInitColor) + { + const svtools::ColorConfig aColorConfig; + aInitColor = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor; + } + } + + // init background with InitColor + const basegfx::BColor aRGBColor(aInitColor.getBColor()); + rVisitor.visit(new drawinglayer::primitive2d::BackgroundColorPrimitive2D(aRGBColor, (255 - aInitColor.GetAlpha()) / 255.0)); + } +} + +ViewObjectContactOfMasterPage::ViewObjectContactOfMasterPage(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfMasterPage::~ViewObjectContactOfMasterPage() +{ +} + +bool ViewObjectContactOfMasterPage::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + // this object is only used for MasterPages. When not the MasterPage is + // displayed as a page, but another page is using it as sub-object, the + // geometry needs to be hidden + if(rDisplayInfo.GetSubContentActive()) + { + return false; + } + + return true; +} + +ViewObjectContactOfPageFill::ViewObjectContactOfPageFill(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageFill::~ViewObjectContactOfPageFill() +{ +} + +bool ViewObjectContactOfPageFill::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + if(!pSdrPageView->GetView().IsPageVisible()) + { + return false; + } + + return true; +} + +void ViewObjectContactOfPageFill::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pPageView) + { + const SdrPage& rPage = getPage(); + + const basegfx::B2DRange aPageFillRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight())); + const basegfx::B2DPolygon aPageFillPolygon(basegfx::utils::createPolygonFromRect(aPageFillRange)); + Color aPageFillColor; + + if(pPageView->GetApplicationDocumentColor() != COL_AUTO) + { + aPageFillColor = pPageView->GetApplicationDocumentColor(); + } + else + { + const svtools::ColorConfig aColorConfig; + aPageFillColor = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor; + } + + // create and add primitive + const basegfx::BColor aRGBColor(aPageFillColor.getBColor()); + rVisitor.visit(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPageFillPolygon), aRGBColor)); + } +} + +ViewObjectContactOfPageShadow::ViewObjectContactOfPageShadow(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageShadow::~ViewObjectContactOfPageShadow() +{ +} + +bool ViewObjectContactOfPageShadow::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + if(!pSdrPageView->GetView().IsPageVisible()) + { + return false; + } + + if(!pSdrPageView->GetView().IsPageShadowVisible()) + { + return false; + } + + // no page shadow for preview renderers + if(GetObjectContact().IsPreviewRenderer()) + { + return false; + } + + // no page shadow for high contrast mode + if(GetObjectContact().isDrawModeHighContrast()) + { + return false; + } + + return true; +} + +ViewObjectContactOfOuterPageBorder::ViewObjectContactOfOuterPageBorder(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfOuterPageBorder::~ViewObjectContactOfOuterPageBorder() +{ +} + +bool ViewObjectContactOfOuterPageBorder::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + const SdrView& rView = pSdrPageView->GetView(); + + return rView.IsPageVisible() || !rView.IsPageBorderVisible(); +} + +ViewObjectContactOfInnerPageBorder::ViewObjectContactOfInnerPageBorder(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfInnerPageBorder::~ViewObjectContactOfInnerPageBorder() +{ +} + +bool ViewObjectContactOfInnerPageBorder::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + if(!pSdrPageView->GetView().IsBordVisible()) + { + return false; + } + + const SdrPage& rPage = getPage(); + + if(!rPage.GetLeftBorder() && !rPage.GetUpperBorder() && !rPage.GetRightBorder() && !rPage.GetLowerBorder()) + { + return false; + } + + // no inner page border for preview renderers + if(GetObjectContact().IsPreviewRenderer()) + { + return false; + } + + return true; +} + +ViewObjectContactOfPageHierarchy::ViewObjectContactOfPageHierarchy(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageHierarchy::~ViewObjectContactOfPageHierarchy() +{ +} + +void ViewObjectContactOfPageHierarchy::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // process local sub-hierarchy + const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); + + if(!nSubHierarchyCount) + return; + + getPrimitive2DSequenceSubHierarchy(rDisplayInfo, rVisitor); +} + +ViewObjectContactOfPageGrid::ViewObjectContactOfPageGrid(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageGrid::~ViewObjectContactOfPageGrid() +{ +} + +bool ViewObjectContactOfPageGrid::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + const SdrView& rView = pSdrPageView->GetView(); + + if(!rView.IsGridVisible()) + { + return false; + } + + // no page grid for preview renderers + if(GetObjectContact().IsPreviewRenderer()) + { + return false; + } + + if(static_cast< ViewContactOfGrid& >(GetViewContact()).getFront() != rView.IsGridFront()) + { + return false; + } + + return true; +} + +void ViewObjectContactOfPageGrid::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pPageView) + { + const SdrView& rView = pPageView->GetView(); + const SdrPage& rPage = getPage(); + const Color aGridColor(rView.GetGridColor()); + const basegfx::BColor aRGBGridColor(aGridColor.getBColor()); + + basegfx::B2DHomMatrix aGridMatrix; + aGridMatrix.set(0, 0, static_cast<double>(rPage.GetWidth() - (rPage.GetRightBorder() + rPage.GetLeftBorder()))); + aGridMatrix.set(1, 1, static_cast<double>(rPage.GetHeight() - (rPage.GetLowerBorder() + rPage.GetUpperBorder()))); + aGridMatrix.set(0, 2, static_cast<double>(rPage.GetLeftBorder())); + aGridMatrix.set(1, 2, static_cast<double>(rPage.GetUpperBorder())); + + const Size aRaw(rView.GetGridCoarse()); + const Size aFine(rView.GetGridFine()); + const double fWidthX(aRaw.getWidth()); + const double fWidthY(aRaw.getHeight()); + const sal_uInt32 nSubdivisionsX(aFine.getWidth() ? aRaw.getWidth() / aFine.getWidth() : 0); + const sal_uInt32 nSubdivisionsY(aFine.getHeight() ? aRaw.getHeight() / aFine.getHeight() : 0); + + rVisitor.visit(new drawinglayer::primitive2d::GridPrimitive2D( + aGridMatrix, fWidthX, fWidthY, 10.0, 3.0, nSubdivisionsX, nSubdivisionsY, aRGBGridColor, + drawinglayer::primitive2d::createDefaultCross_3x3(aRGBGridColor))); + } +} + +ViewObjectContactOfPageHelplines::ViewObjectContactOfPageHelplines(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfPageHelplines::~ViewObjectContactOfPageHelplines() +{ +} + +bool ViewObjectContactOfPageHelplines::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const +{ + if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo)) + { + return false; + } + + SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(!pSdrPageView) + { + return false; + } + + const SdrView& rView = pSdrPageView->GetView(); + + if(!rView.IsHlplVisible()) + { + return false; + } + + // no helplines for preview renderers + if(GetObjectContact().IsPreviewRenderer()) + { + return false; + } + + if(static_cast< ViewContactOfHelplines& >(GetViewContact()).getFront() != rView.IsHlplFront()) + { + return false; + } + + return true; +} + +void ViewObjectContactOfPageHelplines::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pPageView) + { + const SdrHelpLineList& rHelpLineList = pPageView->GetHelpLines(); + const sal_uInt32 nCount(rHelpLineList.GetCount()); + + if(nCount) + { + const basegfx::BColor aRGBColorA(1.0, 1.0, 1.0); + const basegfx::BColor aRGBColorB(0.0, 0.0, 0.0); + + for(sal_uInt32 a(0); a < nCount; a++) + { + const SdrHelpLine& rHelpLine = rHelpLineList[static_cast<sal_uInt16>(a)]; + const basegfx::B2DPoint aPosition(static_cast<double>(rHelpLine.GetPos().X()), static_cast<double>(rHelpLine.GetPos().Y())); + const double fDiscreteDashLength(4.0); + + switch(rHelpLine.GetKind()) + { + default : // SdrHelpLineKind::Point + { + rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D( + aPosition, basegfx::B2DVector(1.0, 0.0), drawinglayer::primitive2d::HelplineStyle2D::Point, + aRGBColorA, aRGBColorB, fDiscreteDashLength)); + break; + } + case SdrHelpLineKind::Vertical : + { + rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D( + aPosition, basegfx::B2DVector(0.0, 1.0), drawinglayer::primitive2d::HelplineStyle2D::Line, + aRGBColorA, aRGBColorB, fDiscreteDashLength)); + break; + } + case SdrHelpLineKind::Horizontal : + { + rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D( + aPosition, basegfx::B2DVector(1.0, 0.0), drawinglayer::primitive2d::HelplineStyle2D::Line, + aRGBColorA, aRGBColorB, fDiscreteDashLength)); + break; + } + } + } + } + } +} + +ViewObjectContactOfSdrPage::ViewObjectContactOfSdrPage(ObjectContact& rObjectContact, ViewContact& rViewContact) +: ViewObjectContact(rObjectContact, rViewContact) +{ +} + +ViewObjectContactOfSdrPage::~ViewObjectContactOfSdrPage() +{ +} + +void ViewObjectContactOfSdrPage::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const +{ + // process local sub-hierarchy + const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); + + if(!nSubHierarchyCount) + return; + + const bool bDoGhostedDisplaying( + GetObjectContact().DoVisualizeEnteredGroup() + && !GetObjectContact().isOutputToPrinter() + && GetObjectContact().getActiveViewContact() == &GetViewContact()); + + if(bDoGhostedDisplaying) + { + rDisplayInfo.ClearGhostedDrawMode(); + } + + // visit object hierarchy + getPrimitive2DSequenceSubHierarchy(rDisplayInfo, rVisitor); + + if(bDoGhostedDisplaying) + { + rDisplayInfo.SetGhostedDrawMode(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx new file mode 100644 index 0000000000..e5292b1515 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx @@ -0,0 +1,1798 @@ +/* -*- 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 <sdr/contact/viewobjectcontactofunocontrol.hxx> +#include <sdr/contact/viewcontactofunocontrol.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <svx/sdr/contact/objectcontactofpageview.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdview.hxx> +#include <svx/sdrpagewindow.hxx> + +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XControlModel.hpp> +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/awt/XView.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/awt/InvalidateStyle.hpp> +#include <com/sun/star/util/XModeChangeListener.hpp> +#include <com/sun/star/util/XModeChangeBroadcaster.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/container/XContainer.hpp> + +#include <vcl/canvastools.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <comphelper/lok.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/scopeguard.hxx> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> + +#include <utility> +/* + +Form controls (more precise: UNO Controls) in the drawing layer are ... prone to breakage, since they have some +specialities which the drawing layer currently doesn't capture too well. In particular, having a living VCL +window as child of the document window, and coupling this Window to a drawing layer object, makes things +difficult sometimes. + +Below is a list of issues which existed in the past. Whenever you change code here, you're encouraged to +verify those issues are still fixed. (Whenever you have some additional time, you're encouraged to write +an automatic test for one or more of those issues for which this is possible :) + +https://bz.apache.org/ooo/show_bug.cgi?id=105992 +zooming documents containing (alive) form controls improperly positions the controls + +https://bz.apache.org/ooo/show_bug.cgi?id=104362 +crash when copy a control + +https://bz.apache.org/ooo/show_bug.cgi?id=104544 +Gridcontrol duplicated after design view on/off + +https://bz.apache.org/ooo/show_bug.cgi?id=102089 +print preview shows control elements with property printable=false + +https://bz.apache.org/ooo/show_bug.cgi?id=102090 +problem with setVisible on TextControl + +https://bz.apache.org/ooo/show_bug.cgi?id=103138 +loop when insert a control in draw + +https://bz.apache.org/ooo/show_bug.cgi?id=101398 +initially-displaying a document with many controls is very slow + +https://bz.apache.org/ooo/show_bug.cgi?id=72429 +repaint error in form wizard in bugdoc database + +https://bz.apache.org/ooo/show_bug.cgi?id=72694 +form control artifacts when scrolling a text fast + +*/ + + +namespace sdr::contact { + + + using namespace ::com::sun::star::awt::InvalidateStyle; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::XInterface; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::awt::XControl; + using ::com::sun::star::awt::XControlModel; + using ::com::sun::star::awt::XControlContainer; + using ::com::sun::star::awt::XWindow2; + using ::com::sun::star::awt::XWindowListener; + using ::com::sun::star::awt::PosSize::POSSIZE; + using ::com::sun::star::awt::XView; + using ::com::sun::star::awt::WindowEvent; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::XPropertySetInfo; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::awt::XWindowPeer; + using ::com::sun::star::beans::XPropertyChangeListener; + using ::com::sun::star::util::XModeChangeListener; + using ::com::sun::star::util::XModeChangeBroadcaster; + using ::com::sun::star::util::ModeChangeEvent; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::beans::PropertyChangeEvent; + using ::com::sun::star::container::XContainerListener; + using ::com::sun::star::container::XContainer; + using ::com::sun::star::container::ContainerEvent; + using ::com::sun::star::uno::Any; + + namespace { + + class ControlHolder + { + private: + Reference< XControl > m_xControl; + Reference< XWindow2 > m_xControlWindow; + Reference< XView > m_xControlView; + + public: + ControlHolder() + { + } + + explicit ControlHolder( const Reference< XControl >& _rxControl ) + { + *this = _rxControl; + } + + ControlHolder& operator=( const Reference< XControl >& _rxControl ) + { + clear(); + + m_xControl = _rxControl; + if ( m_xControl.is() ) + { + m_xControlWindow.set( m_xControl, UNO_QUERY ); + m_xControlView.set( m_xControl, UNO_QUERY ); + if ( !m_xControlWindow.is() || !m_xControlView.is() ) + { + OSL_FAIL( "ControlHolder::operator=: invalid XControl, missing required interfaces!" ); + clear(); + } + } + + return *this; + } + + public: + bool is() const { return m_xControl.is() && m_xControlWindow.is() && m_xControlView.is(); } + void clear() { m_xControl.clear(); m_xControlWindow.clear(); m_xControlView.clear(); } + + // delegators for the methods of the UNO interfaces + // Note all those will crash if called for a NULL object. + bool isDesignMode() const { return m_xControl->isDesignMode(); } + void setDesignMode( const bool _bDesign ) const { m_xControl->setDesignMode( _bDesign ); } + bool isVisible() const { return m_xControlWindow->isVisible(); } + void setVisible( const bool _bVisible ) const { m_xControlWindow->setVisible( _bVisible ); } + Reference< XControlModel > + getModel() const { return m_xControl->getModel(); } + void setModel( const Reference< XControlModel >& _m ) const { m_xControl->setModel( _m ); } + + void addWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->addWindowListener( _l ); } + void removeWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->removeWindowListener( _l ); } + void setPosSize( const tools::Rectangle& _rPosSize ) const; + tools::Rectangle + getPosSize() const; + void setZoom( const ::basegfx::B2DVector& _rScale ) const; + ::basegfx::B2DVector + getZoom() const; + + void invalidate() const; + + public: + const Reference< XControl >& getControl() const { return m_xControl; } + }; + + bool operator==( const ControlHolder& _rControl, const Reference< XInterface >& _rxCompare ) + { + return _rControl.getControl() == _rxCompare; + } + + bool operator==( const ControlHolder& _rControl, const Any& _rxCompare ) + { + return _rControl == Reference< XInterface >( _rxCompare, UNO_QUERY ); + } + + } + + void ControlHolder::setPosSize( const tools::Rectangle& _rPosSize ) const + { + // no check whether we're valid, this is the responsibility of the caller + + // don't call setPosSize when pos/size did not change #i104181# + ::tools::Rectangle aCurrentRect( getPosSize() ); + if ( aCurrentRect != _rPosSize ) + { + m_xControlWindow->setPosSize( + _rPosSize.Left(), _rPosSize.Top(), _rPosSize.GetWidth(), _rPosSize.GetHeight(), + POSSIZE + ); + } + } + + + ::tools::Rectangle ControlHolder::getPosSize() const + { + // no check whether we're valid, this is the responsibility of the caller + return VCLUnoHelper::ConvertToVCLRect( m_xControlWindow->getPosSize() ); + } + + + void ControlHolder::setZoom( const ::basegfx::B2DVector& _rScale ) const + { + // no check whether we're valid, this is the responsibility of the caller + m_xControlView->setZoom( static_cast<float>(_rScale.getX()), static_cast<float>(_rScale.getY()) ); + } + + + void ControlHolder::invalidate() const + { + Reference< XWindowPeer > xPeer( m_xControl->getPeer() ); + if ( xPeer.is() ) + { + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xPeer ); + OSL_ENSURE( pWindow, "ControlHolder::invalidate: no implementation access!" ); + if ( pWindow ) + pWindow->Invalidate(); + } + } + + + ::basegfx::B2DVector ControlHolder::getZoom() const + { + // no check whether we're valid, this is the responsibility of the caller + + // Argh. Why does XView have a setZoom only, but not a getZoom? + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( m_xControl->getPeer() ); + OSL_ENSURE( pWindow, "ControlHolder::getZoom: no implementation access!" ); + + ::basegfx::B2DVector aZoom( 1, 1 ); + if ( pWindow ) + { + const Fraction& rZoom( pWindow->GetZoom() ); + aZoom.setX( static_cast<double>(rZoom) ); + aZoom.setY( static_cast<double>(rZoom) ); + } + return aZoom; + } + + namespace UnoControlContactHelper { + + /** positions a control, and sets its zoom mode, using a given transformation and output device + */ + static void adjustControlGeometry_throw( const ControlHolder& _rControl, const tools::Rectangle& _rLogicBoundingRect, + const basegfx::B2DHomMatrix& _rViewTransformation, const ::basegfx::B2DHomMatrix& _rZoomLevelNormalization ) + { + // In the LOK case, control geometry is handled by LokControlHandler + if (comphelper::LibreOfficeKit::isActive()) + return; + + OSL_PRECOND( _rControl.is(), "UnoControlContactHelper::adjustControlGeometry_throw: illegal control!" ); + if ( !_rControl.is() ) + return; + + #if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DTuple aViewScale, aViewTranslate; + double nViewRotate(0), nViewShearX(0); + _rViewTransformation.decompose( aViewScale, aViewTranslate, nViewRotate, nViewShearX ); + + ::basegfx::B2DTuple aZoomScale, aZoomTranslate; + double nZoomRotate(0), nZoomShearX(0); + _rZoomLevelNormalization.decompose( aZoomScale, aZoomTranslate, nZoomRotate, nZoomShearX ); + #endif + + // transform the logic bound rect, using the view transformation, to pixel coordinates + ::basegfx::B2DPoint aTopLeft( _rLogicBoundingRect.Left(), _rLogicBoundingRect.Top() ); + aTopLeft *= _rViewTransformation; + ::basegfx::B2DPoint aBottomRight( _rLogicBoundingRect.Right(), _rLogicBoundingRect.Bottom() ); + aBottomRight *= _rViewTransformation; + + const tools::Rectangle aPaintRectPixel(static_cast<tools::Long>(std::round(aTopLeft.getX())), + static_cast<tools::Long>(std::round(aTopLeft.getY())), + static_cast<tools::Long>(std::round(aBottomRight.getX())), + static_cast<tools::Long>(std::round(aBottomRight.getY()))); + _rControl.setPosSize( aPaintRectPixel ); + + // determine the scale from the current view transformation, and the normalization matrix + ::basegfx::B2DHomMatrix aObtainResolutionDependentScale( _rViewTransformation * _rZoomLevelNormalization ); + ::basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aObtainResolutionDependentScale.decompose( aScale, aTranslate, fRotate, fShearX ); + _rControl.setZoom( aScale ); + } + + /** disposes the given control + */ + static void disposeAndClearControl_nothrow( ControlHolder& _rControl ) + { + try + { + Reference< XComponent > xControlComp = _rControl.getControl(); + if ( xControlComp.is() ) + xControlComp->dispose(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + _rControl.clear(); + } + + } + + namespace { + + /** interface encapsulating access to an SdrPageView, stripped down to the methods we really need + */ + class IPageViewAccess + { + public: + /** determines whether the view is currently in design mode + */ + virtual bool isDesignMode() const = 0; + + /** retrieves the control container for a given output device + */ + virtual Reference< XControlContainer > + getControlContainer( const OutputDevice& _rDevice ) const = 0; + + /** determines whether a given layer is visible + */ + virtual bool isLayerVisible( SdrLayerID _nLayerID ) const = 0; + + protected: + ~IPageViewAccess() {} + }; + + /** is a ->IPageViewAccess implementation based on a real ->SdrPageView instance + */ + class SdrPageViewAccess : public IPageViewAccess + { + const SdrPageView& m_rPageView; + public: + explicit SdrPageViewAccess( const SdrPageView& _rPageView ) : m_rPageView( _rPageView ) { } + + virtual ~SdrPageViewAccess() {} + + virtual bool isDesignMode() const override; + virtual Reference< XControlContainer > + getControlContainer( const OutputDevice& _rDevice ) const override; + virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; + }; + + } + + bool SdrPageViewAccess::isDesignMode() const + { + return m_rPageView.GetView().IsDesignMode(); + } + + + Reference< XControlContainer > SdrPageViewAccess::getControlContainer( const OutputDevice& _rDevice ) const + { + Reference< XControlContainer > xControlContainer = m_rPageView.GetControlContainer( _rDevice ); + DBG_ASSERT( xControlContainer.is() || nullptr == m_rPageView.FindPageWindow( _rDevice ), + "SdrPageViewAccess::getControlContainer: the output device is known, but there is no control container for it?" ); + return xControlContainer; + } + + + bool SdrPageViewAccess::isLayerVisible( SdrLayerID _nLayerID ) const + { + return m_rPageView.GetVisibleLayers().IsSet( _nLayerID ); + } + + namespace { + + /** is a ->IPageViewAccess implementation which can be used to create an invisible control for + an arbitrary window + */ + class InvisibleControlViewAccess : public IPageViewAccess + { + private: + Reference< XControlContainer >& m_rControlContainer; + public: + explicit InvisibleControlViewAccess( Reference< XControlContainer >& _inout_ControlContainer ) + :m_rControlContainer( _inout_ControlContainer ) + { + } + + virtual ~InvisibleControlViewAccess() {} + + virtual bool isDesignMode() const override; + virtual Reference< XControlContainer > + getControlContainer( const OutputDevice& _rDevice ) const override; + virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; + }; + + } + + bool InvisibleControlViewAccess::isDesignMode() const + { + return true; + } + + + Reference< XControlContainer > InvisibleControlViewAccess::getControlContainer( const OutputDevice& _rDevice ) const + { + if ( !m_rControlContainer.is() ) + { + const vcl::Window* pWindow = _rDevice.GetOwnerWindow(); + OSL_ENSURE( pWindow, "InvisibleControlViewAccess::getControlContainer: expected to be called for a window only!" ); + if ( pWindow ) + m_rControlContainer = VCLUnoHelper::CreateControlContainer( const_cast< vcl::Window* >( pWindow ) ); + } + return m_rControlContainer; + } + + + bool InvisibleControlViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const + { + return false; + } + + namespace { + + //= DummyPageViewAccess + + /** is a ->IPageViewAccess implementation which can be used to create a control for an arbitrary + non-Window device + + The implementation will report the "PageView" as being in design mode, all layers to be visible, + and will not return any ControlContainer, so all control container related features (notifications etc) + are not available. + */ + class DummyPageViewAccess : public IPageViewAccess + { + public: + DummyPageViewAccess() + { + } + + virtual ~DummyPageViewAccess() {} + + virtual bool isDesignMode() const override; + virtual Reference< XControlContainer > + getControlContainer( const OutputDevice& _rDevice ) const override; + virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override; + }; + + } + + bool DummyPageViewAccess::isDesignMode() const + { + return true; + } + + + Reference< XControlContainer > DummyPageViewAccess::getControlContainer( const OutputDevice& /*_rDevice*/ ) const + { + return nullptr; + } + + + bool DummyPageViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const + { + return true; + } + + + //= ViewObjectContactOfUnoControl_Impl + + typedef ::cppu::WeakImplHelper < XWindowListener + , XPropertyChangeListener + , XContainerListener + , XModeChangeListener + > ViewObjectContactOfUnoControl_Impl_Base; + + class ViewObjectContactOfUnoControl_Impl: + public ViewObjectContactOfUnoControl_Impl_Base + { + private: + // tdf#41935 note that access to members is protected with SolarMutex; + // the class previously had its own mutex but that is prone to deadlock + + /// the instance whose IMPL we are + ViewObjectContactOfUnoControl* m_pAntiImpl; + + /// are we currently inside impl_ensureControl_nothrow? + bool m_bCreatingControl; + + /// the control we're responsible for + ControlHolder m_aControl; + + /// the ControlContainer where we inserted our control + Reference< XContainer > m_xContainer; + + /// the output device for which the control was created + VclPtr<OutputDevice> m_pOutputDeviceForWindow; + + /// flag indicating whether the control is currently visible + bool m_bControlIsVisible; + + /// are we currently listening at a design mode control? + bool m_bIsDesignModeListening; + + enum ViewControlMode + { + eDesign, + eAlive, + eUnknown + }; + /// is the control currently in design mode? + mutable ViewControlMode m_eControlDesignMode; + + ::basegfx::B2DHomMatrix m_aZoomLevelNormalization; + + public: + explicit ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl ); + ViewObjectContactOfUnoControl_Impl(const ViewObjectContactOfUnoControl_Impl&) = delete; + ViewObjectContactOfUnoControl_Impl& operator=(const ViewObjectContactOfUnoControl_Impl&) = delete; + + /** disposes the instance, which is nonfunctional afterwards + */ + void dispose(); + + /** determines whether the instance is disposed + */ + bool isDisposed() const { return impl_isDisposed_nofail(); } + + /** returns the SdrUnoObject associated with the ViewContact + + @precond + We're not disposed. + */ + SdrUnoObj* getUnoObject() const; + + /** ensures that we have an ->XControl + + Must only be called if a control is needed when no DisplayInfo is present, yet. + + For creating a control, an ->OutputDevice is needed, and an ->SdrPageView. Both will be obtained + from a ->ObjectContactOfPageView. So, if our (anti-impl's) object contact is not a ->ObjectContactOfPageView, + this method fill fail. + + Failure of this method will be reported via an assertion in a non-product version. + */ + void ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL ); + + /** returns our XControl, if it already has been created + + If you want to ensure that the control exists before accessing it, use ->ensureControl + */ + const ControlHolder& + getExistentControl() const { return m_aControl; } + + bool + hasControl() const { return m_aControl.is(); } + + /** positions our XControl according to the geometry settings in the SdrUnoObj, modified by the given + transformation, and sets proper zoom settings according to our device + + @precond + ->m_pOutputDeviceForWindow and ->m_aControl are not <NULL/> + */ + void positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const; + + /** determines whether or not our control is printable + + Effectively, this method returns the value of the "Printable" property + of the control's model. If we have no control, <FALSE/> is returned. + */ + bool isPrintableControl() const; + + /** sets the design mode on the control, or at least remembers the flag for the + time the control is created + */ + void setControlDesignMode( bool _bDesignMode ) const; + + /** determines whether our control is currently visible + @nofail + */ + bool isControlVisible() const { return m_bControlIsVisible; } + + /// creates an XControl for the given device and SdrUnoObj + static bool + createControlForDevice( + IPageViewAccess const & _rPageView, + const OutputDevice& _rDevice, + const SdrUnoObj& _rUnoObject, + const basegfx::B2DHomMatrix& _rInitialViewTransformation, + const basegfx::B2DHomMatrix& _rInitialZoomNormalization, + ControlHolder& _out_rControl + ); + + const ViewContactOfUnoControl& + getViewContact() const + { + ENSURE_OR_THROW( !impl_isDisposed_nofail(), "already disposed" ); + return static_cast< const ViewContactOfUnoControl& >( m_pAntiImpl->GetViewContact() ); + } + + protected: + virtual ~ViewObjectContactOfUnoControl_Impl() override; + + // XEventListener + virtual void SAL_CALL disposing( const EventObject& Source ) override; + + // XWindowListener + virtual void SAL_CALL windowResized( const WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const EventObject& e ) override; + virtual void SAL_CALL windowHidden( const EventObject& e ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const PropertyChangeEvent& evt ) override; + + // XModeChangeListener + virtual void SAL_CALL modeChanged( const ModeChangeEvent& _rSource ) override; + + // XContainerListener + virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override; + virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override; + + private: + /** retrieves the SdrPageView which our associated SdrPageViewWindow belongs to + + @param out_rpPageView + a reference to a pointer holding, upon return, the desired SdrPageView + + @return + <TRUE/> if and only if a ->SdrPageView could be obtained + + @precond + We really belong to an SdrPageViewWindow. Perhaps (I'm not sure ATM :) + there are instance for which this might not be true, but those instances + should never have a need to call this method. + + @precond + We're not disposed. + + @postcond + The method expects success, if it returns with <FALSE/>, this will have been + asserted. + + @nothrow + */ + bool impl_getPageView_nothrow( SdrPageView*& _out_rpPageView ); + + /** adjusts the control visibility so it respects its layer's visibility + + @precond + ->m_aControl is not <NULL/> + + @precond + We're not disposed. + + @precond + We really belong to an SdrPageViewWindow. There are instance for which this + might not be true, but those instances should never have a need to call + this method. + */ + void impl_adjustControlVisibilityToLayerVisibility_throw(); + + /** adjusts the control visibility so it respects its layer's visibility + + The control must never be visible if it's in design mode. + In alive mode, it must be visibility if and only it's on a visible layer. + + @param _rxControl + the control whose visibility is to be adjusted + + @param _rPageView + provides access to the attributes of the SdrPageView which the control finally belongs to + + @param _rUnoObject + our SdrUnoObj + + @param _bIsCurrentlyVisible + determines whether the control is currently visible. Note that this is only a shortcut for + querying _rxControl for the XWindow2 interface, and calling isVisible at this interface. + This shortcut has been chosen since the caller usually already has this information. + If _bForce is <TRUE/>, _bIsCurrentlyVisible is ignored. + + @param _bForce + set to <TRUE/> if you want to force a ->XWindow::setVisible call, + no matter if the control visibility is already correct + + @precond + We're not disposed. + */ + static void impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rxControl, const SdrUnoObj& _rUnoObject, + IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce ); + + /** starts or stops listening at various aspects of our control + + @precond + ->m_aControl is not <NULL/> + */ + void impl_switchControlListening_nothrow( bool _bStart ); + + /** starts or stops listening at our control container + + @precond + ->m_xContainer is not <NULL/> + */ + void impl_switchContainerListening_nothrow( bool _bStart ); + + /** starts or stops listening at the control for design-mode relevant facets + */ + void impl_switchDesignModeListening_nothrow( bool _bStart ); + + /** starts or stops listening for all properties at our control + + @param _bStart + determines whether to start or to stop listening + + @precond + ->m_aControl is not <NULL/> + */ + void impl_switchPropertyListening_nothrow( bool _bStart ); + + /** disposes the instance + @param _bAlsoDisposeControl + determines whether the XControl should be disposed, too + */ + void impl_dispose_nothrow( bool _bAlsoDisposeControl ); + + /** determines whether the instance is disposed + @nofail + */ + bool impl_isDisposed_nofail() const { return m_pAntiImpl == nullptr; } + + /** determines whether the control currently is in design mode + + @precond + The design mode must already be known. It is known when we first had access to + an SdrPageView (which carries this flag), or somebody explicitly set it from + outside. + */ + bool impl_isControlDesignMode_nothrow() const + { + DBG_ASSERT( m_eControlDesignMode != eUnknown, "ViewObjectContactOfUnoControl_Impl::impl_isControlDesignMode_nothrow: mode is still unknown!" ); + return m_eControlDesignMode == eDesign; + } + + /** ensures that we have a control for the given PageView/OutputDevice + */ + bool impl_ensureControl_nothrow( + IPageViewAccess const & _rPageView, + const OutputDevice& _rDevice, + const basegfx::B2DHomMatrix& _rInitialViewTransformation + ); + + const OutputDevice& impl_getOutputDevice_throw() const; + }; + + namespace { + + class LazyControlCreationPrimitive2D : public ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D + { + private: + typedef ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D BufferedDecompositionPrimitive2D; + + protected: + virtual void + get2DDecomposition( + ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor, + const ::drawinglayer::geometry::ViewInformation2D& rViewInformation + ) const override; + + virtual void create2DDecomposition( + ::drawinglayer::primitive2d::Primitive2DContainer& rContainer, + const ::drawinglayer::geometry::ViewInformation2D& rViewInformation + ) const override; + + virtual ::basegfx::B2DRange + getB2DRange( + const ::drawinglayer::geometry::ViewInformation2D& rViewInformation + ) const override; + + public: + explicit LazyControlCreationPrimitive2D( ::rtl::Reference< ViewObjectContactOfUnoControl_Impl > _pVOCImpl ) + :m_pVOCImpl(std::move( _pVOCImpl )) + { + ENSURE_OR_THROW( m_pVOCImpl.is(), "Illegal argument." ); + getTransformation( m_pVOCImpl->getViewContact(), m_aTransformation ); + } + + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + // declare unique ID for this primitive class + virtual sal_uInt32 getPrimitive2DID() const override; + + static void getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation ); + + private: + void impl_positionAndZoomControl( const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const + { + if ( !_rViewInformation.getViewport().isEmpty() ) + m_pVOCImpl->positionAndZoomControl( _rViewInformation.getObjectToViewTransformation() ); + } + + private: + ::rtl::Reference< ViewObjectContactOfUnoControl_Impl > m_pVOCImpl; + /** The geometry is part of the identity of a primitive, so we cannot calculate it on demand + (since the data the calculation is based on might have changed then), but need to calc + it at construction time, and remember it. + */ + ::basegfx::B2DHomMatrix m_aTransformation; + }; + + } + + ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl ) + :m_pAntiImpl( _pAntiImpl ) + ,m_bCreatingControl( false ) + ,m_pOutputDeviceForWindow( nullptr ) + ,m_bControlIsVisible( false ) + ,m_bIsDesignModeListening( false ) + ,m_eControlDesignMode( eUnknown ) + { + DBG_ASSERT( m_pAntiImpl, "ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl: invalid AntiImpl!" ); + + const OutputDevice& rPageViewDevice( impl_getOutputDevice_throw() ); + m_aZoomLevelNormalization = rPageViewDevice.GetInverseViewTransformation(); + + #if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX ); + #endif + + ::basegfx::B2DHomMatrix aScaleNormalization; + const MapMode& aCurrentDeviceMapMode( rPageViewDevice.GetMapMode() ); + aScaleNormalization.set( 0, 0, static_cast<double>(aCurrentDeviceMapMode.GetScaleX()) ); + aScaleNormalization.set( 1, 1, static_cast<double>(aCurrentDeviceMapMode.GetScaleY()) ); + m_aZoomLevelNormalization *= aScaleNormalization; + + #if OSL_DEBUG_LEVEL > 0 + m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX ); + #endif + } + + + ViewObjectContactOfUnoControl_Impl::~ViewObjectContactOfUnoControl_Impl() + { + if ( !impl_isDisposed_nofail() ) + { + acquire(); + dispose(); + } + + } + + + void ViewObjectContactOfUnoControl_Impl::impl_dispose_nothrow( bool _bAlsoDisposeControl ) + { + if ( impl_isDisposed_nofail() ) + return; + + if ( m_aControl.is() ) + impl_switchControlListening_nothrow( false ); + + if ( m_xContainer.is() ) + impl_switchContainerListening_nothrow( false ); + + // dispose the control + if ( _bAlsoDisposeControl ) + UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl ); + + m_aControl.clear(); + m_xContainer.clear(); + m_pOutputDeviceForWindow = nullptr; + m_bControlIsVisible = false; + + m_pAntiImpl = nullptr; + } + + + void ViewObjectContactOfUnoControl_Impl::dispose() + { + SolarMutexGuard aSolarGuard; + impl_dispose_nothrow( true ); + } + + + SdrUnoObj* ViewObjectContactOfUnoControl_Impl::getUnoObject() const + { + OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::getUnoObject: already disposed()" ); + if ( impl_isDisposed_nofail() ) + return nullptr; + auto pRet = dynamic_cast< SdrUnoObj* >( m_pAntiImpl->GetViewContact().TryToGetSdrObject() ); + DBG_ASSERT( pRet || !m_pAntiImpl->GetViewContact().TryToGetSdrObject(), + "ViewObjectContactOfUnoControl_Impl::getUnoObject: invalid SdrObject!" ); + return pRet; + } + + + void ViewObjectContactOfUnoControl_Impl::positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const + { + OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no output device or no control!" ); + if ( !m_aControl.is() ) + return; + + try + { + SdrUnoObj* pUnoObject = getUnoObject(); + if ( pUnoObject ) + { + const tools::Rectangle aRect( pUnoObject->GetLogicRect() ); + UnoControlContactHelper::adjustControlGeometry_throw( m_aControl, aRect, _rViewTransformation, m_aZoomLevelNormalization ); + } + else + OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no SdrUnoObj!" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + void ViewObjectContactOfUnoControl_Impl::ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL ) + { + OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::ensureControl: already disposed()" ); + if ( impl_isDisposed_nofail() ) + return; + + ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() ); + if ( pPageViewContact ) + { + SdrPageViewAccess aPVAccess( pPageViewContact->GetPageWindow().GetPageView() ); + const OutputDevice& rDevice( *m_pAntiImpl->getPageViewOutputDevice() ); + impl_ensureControl_nothrow( + aPVAccess, + rDevice, + _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation() + ); + return; + } + + DummyPageViewAccess aNoPageView; + const OutputDevice& rDevice( impl_getOutputDevice_throw() ); + impl_ensureControl_nothrow( + aNoPageView, + rDevice, + _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation() + ); + } + + + const OutputDevice& ViewObjectContactOfUnoControl_Impl::impl_getOutputDevice_throw() const + { + // do not use ObjectContact::TryToGetOutputDevice, it would not care for the PageWindow's + // OriginalPaintWindow + const OutputDevice* oPageOutputDev = m_pAntiImpl->getPageViewOutputDevice(); + if( oPageOutputDev ) + return *oPageOutputDev; + + const OutputDevice* pDevice = m_pAntiImpl->GetObjectContact().TryToGetOutputDevice(); + ENSURE_OR_THROW( pDevice, "no output device -> no control" ); + return *pDevice; + } + + + namespace + { + void lcl_resetFlag( bool& rbFlag ) + { + rbFlag = false; + } + } + + + bool ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow( IPageViewAccess const & _rPageView, const OutputDevice& _rDevice, + const basegfx::B2DHomMatrix& _rInitialViewTransformation ) + { + if ( m_bCreatingControl ) + { + OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: reentrance is not really good here!" ); + // We once had a situation where this was called reentrantly, which lead to all kind of strange effects. All + // those affected the grid control, which is the only control so far which is visible in design mode (and + // not only in alive mode). + // Creating the control triggered a Window::Update on some of its child windows, which triggered a + // Paint on parent of the grid control (e.g. the SwEditWin), which triggered a reentrant call to this method, + // which it is not really prepared for. + + // /me thinks that re-entrance should be caught on a higher level, i.e. the Drawing Layer should not allow + // reentrant paint requests. For the moment, until /me can discuss this with AW, catch it here. #i104544# + return false; + } + + m_bCreatingControl = true; + ::comphelper::ScopeGuard aGuard([&] () { lcl_resetFlag(m_bCreatingControl); }); + + if ( m_aControl.is() ) + { + if ( m_pOutputDeviceForWindow.get() == &_rDevice ) + return true; + + // Somebody requested a control for a new device, which means either of + // - our PageView's paint window changed since we were last here + // - we don't belong to a page view, and are simply painted onto different devices + // The first sounds strange (doesn't it?), the second means we could perhaps + // optimize this in the future - there is no need to re-create the control every time, + // is it? #i74523# + if ( m_xContainer.is() ) + impl_switchContainerListening_nothrow( false ); + impl_switchControlListening_nothrow( false ); + UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl ); + } + + SdrUnoObj* pUnoObject = getUnoObject(); + if ( !pUnoObject ) + return false; + + ControlHolder aControl; + if ( !createControlForDevice( _rPageView, _rDevice, *pUnoObject, _rInitialViewTransformation, m_aZoomLevelNormalization, aControl ) ) + return false; + + m_pOutputDeviceForWindow = const_cast< OutputDevice * >( &_rDevice ); + m_aControl = aControl; + m_xContainer.set(_rPageView.getControlContainer( _rDevice ), css::uno::UNO_QUERY); + DBG_ASSERT( ( m_xContainer.is() // either have a XControlContainer + || ( ( !_rPageView.getControlContainer( _rDevice ).is() ) // or don't have any container, + && ( _rDevice.GetOwnerWindow() == nullptr ) // which is allowed for non-Window instances only + ) + ), + "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: no XContainer at the ControlContainer!" ); + + try + { + m_eControlDesignMode = m_aControl.isDesignMode() ? eDesign : eAlive; + m_bControlIsVisible = m_aControl.isVisible(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + + // start listening at all aspects of the control which are interesting to us ... + impl_switchControlListening_nothrow( true ); + + // start listening at the control container, in case somebody tampers with our control + if ( m_xContainer.is() ) + impl_switchContainerListening_nothrow( true ); + + return m_aControl.is(); + } + + + bool ViewObjectContactOfUnoControl_Impl::createControlForDevice( IPageViewAccess const & _rPageView, + const OutputDevice& _rDevice, const SdrUnoObj& _rUnoObject, const basegfx::B2DHomMatrix& _rInitialViewTransformation, + const basegfx::B2DHomMatrix& _rInitialZoomNormalization, ControlHolder& _out_rControl ) + { + _out_rControl.clear(); + + const Reference< XControlModel >& xControlModel( _rUnoObject.GetUnoControlModel() ); + DBG_ASSERT( xControlModel.is(), "ViewObjectContactOfUnoControl_Impl::createControlForDevice: no control model at the SdrUnoObject!?" ); + if ( !xControlModel.is() ) + return false; + + bool bSuccess = false; + try + { + const OUString& sControlServiceName( _rUnoObject.GetUnoControlTypeName() ); + + Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + _out_rControl = Reference<XControl>( xContext->getServiceManager()->createInstanceWithContext(sControlServiceName, xContext), UNO_QUERY_THROW ); + + // tdf#150886 for calc/writer/impress make forms ignore the platform theme + Reference<XPropertySet> xModelProperties(xControlModel, UNO_QUERY); + Reference<XPropertySetInfo> xInfo = xModelProperties ? xModelProperties->getPropertySetInfo() : nullptr; + if (xInfo && xInfo->hasPropertyByName("StandardTheme")) + xModelProperties->setPropertyValue("StandardTheme", Any(!_rUnoObject.getSdrModelFromSdrObject().AreControlsThemed())); + + // knit the model and the control + _out_rControl.setModel( xControlModel ); + const tools::Rectangle aRect( _rUnoObject.GetLogicRect() ); + + // proper geometry + UnoControlContactHelper::adjustControlGeometry_throw( + _out_rControl, + aRect, + _rInitialViewTransformation, + _rInitialZoomNormalization + ); + + // set design mode before peer is created, + // this is also needed for accessibility + _out_rControl.setDesignMode( _rPageView.isDesignMode() ); + + // adjust the initial visibility according to the visibility of the layer + impl_adjustControlVisibilityToLayerVisibility_throw( _out_rControl, _rUnoObject, _rPageView, false, true ); + + // add the control to the respective control container + // do this last + Reference< XControlContainer > xControlContainer( _rPageView.getControlContainer( _rDevice ) ); + if ( xControlContainer.is() ) + xControlContainer->addControl( sControlServiceName, _out_rControl.getControl() ); + + bSuccess = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + + if ( !bSuccess ) + { + // delete the control which might have been created already + UnoControlContactHelper::disposeAndClearControl_nothrow( _out_rControl ); + } + + return _out_rControl.is(); + } + + + bool ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow( SdrPageView*& _out_rpPageView ) + { + OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: already disposed!" ); + + _out_rpPageView = nullptr; + if ( impl_isDisposed_nofail() ) + return false; + + ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() ); + if ( pPageViewContact ) + _out_rpPageView = &pPageViewContact->GetPageWindow().GetPageView(); + + DBG_ASSERT( _out_rpPageView != nullptr, "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: this method is expected to always have success!" ); + return ( _out_rpPageView != nullptr ); + } + + + void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw() + { + OSL_PRECOND( m_aControl.is(), + "ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw: only valid if we have a control!" ); + + SdrPageView* pPageView( nullptr ); + if ( !impl_getPageView_nothrow( pPageView ) ) + return; + + SdrUnoObj* pUnoObject = getUnoObject(); + if ( !pUnoObject ) + return; + + SdrPageViewAccess aPVAccess( *pPageView ); + impl_adjustControlVisibilityToLayerVisibility_throw( m_aControl, *pUnoObject, aPVAccess, m_bControlIsVisible, false/*_bForce*/ ); + } + + + void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rControl, + const SdrUnoObj& _rUnoObject, IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce ) + { + // in design mode, there is no problem with the visibility: The XControl is hidden by + // default, and the Drawing Layer will simply not call our paint routine, if we're in + // a hidden layer. So, only alive mode matters. + if ( !_rControl.isDesignMode() ) + { + // the layer of our object + SdrLayerID nObjectLayer = _rUnoObject.GetLayer(); + // is the object we're residing in visible in this view? + bool bIsObjectVisible = _rUnoObject.IsVisible() && _rPageView.isLayerVisible( nObjectLayer ); + + if ( _bForce || ( bIsObjectVisible != _bIsCurrentlyVisible ) ) + { + _rControl.setVisible( bIsObjectVisible ); + } + } + } + + + void ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow( bool _bStart ) + { + OSL_PRECOND( m_xContainer.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow: no control container!" ); + if ( !m_xContainer.is() ) + return; + + try + { + if ( _bStart ) + m_xContainer->addContainerListener( this ); + else + m_xContainer->removeContainerListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + void ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow( bool _bStart ) + { + OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow: invalid control!" ); + if ( !m_aControl.is() ) + return; + + try + { + // listen for visibility changes + if ( _bStart ) + m_aControl.addWindowListener( this ); + else + m_aControl.removeWindowListener( this ); + + // in design mode, listen for some more aspects + impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() && _bStart ); + + // listen for design mode changes + Reference< XModeChangeBroadcaster > xDesignModeChanges( m_aControl.getControl(), UNO_QUERY_THROW ); + if ( _bStart ) + xDesignModeChanges->addModeChangeListener( this ); + else + xDesignModeChanges->removeModeChangeListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + void ViewObjectContactOfUnoControl_Impl::impl_switchDesignModeListening_nothrow( bool _bStart ) + { + if ( m_bIsDesignModeListening != _bStart ) + { + m_bIsDesignModeListening = _bStart; + impl_switchPropertyListening_nothrow( _bStart ); + } + } + + + void ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow( bool _bStart ) + { + OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow: no control!" ); + if ( !m_aControl.is() ) + return; + + try + { + Reference< XPropertySet > xModelProperties( m_aControl.getModel(), UNO_QUERY_THROW ); + if ( _bStart ) + xModelProperties->addPropertyChangeListener( OUString(), this ); + else + xModelProperties->removePropertyChangeListener( OUString(), this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + bool ViewObjectContactOfUnoControl_Impl::isPrintableControl() const + { + SdrUnoObj* pUnoObject = getUnoObject(); + if ( !pUnoObject ) + return false; + + bool bIsPrintable = false; + try + { + Reference< XPropertySet > xModelProperties( pUnoObject->GetUnoControlModel(), UNO_QUERY_THROW ); + OSL_VERIFY( xModelProperties->getPropertyValue( "Printable" ) >>= bIsPrintable ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + return bIsPrintable; + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::disposing( const EventObject& Source ) + { + SolarMutexGuard aSolarGuard; + // some code below - in particular our disposal - might trigger actions which require the + // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control, + // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex - + // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169# + + if ( !m_aControl.is() ) + return; + + if ( ( m_aControl == Source.Source ) + || ( m_aControl.getModel() == Source.Source ) + ) + { + // the model or the control is dying ... hmm, not much sense in that we ourself continue + // living + impl_dispose_nothrow( false ); + return; + } + + DBG_ASSERT( Source.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::disposing: Who's this?" ); + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowResized( const WindowEvent& /*e*/ ) + { + // not interested in + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowMoved( const WindowEvent& /*e*/ ) + { + // not interested in + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowShown( const EventObject& /*e*/ ) + { + SolarMutexGuard aSolarGuard; + m_bControlIsVisible = true; + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowHidden( const EventObject& /*e*/ ) + { + SolarMutexGuard aSolarGuard; + m_bControlIsVisible = false; + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::propertyChange( const PropertyChangeEvent& /*_rEvent*/ ) + { + SolarMutexGuard aSolarGuard; + // (re)painting might require VCL operations, which need the SolarMutex + + OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::propertyChange: already disposed()" ); + if ( impl_isDisposed_nofail() ) + return; + + DBG_ASSERT( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::propertyChange: " ); + if ( !m_aControl.is() ) + return; + + // a generic property changed. If we're in design mode, we need to repaint the control + if ( impl_isControlDesignMode_nothrow() ) + { + m_pAntiImpl->propertyChange(); + } + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::modeChanged( const ModeChangeEvent& _rSource ) + { + SolarMutexGuard aSolarGuard; + + DBG_ASSERT( _rSource.NewMode == "design" || _rSource.NewMode == "alive", "ViewObjectContactOfUnoControl_Impl::modeChanged: unexpected mode!" ); + + m_eControlDesignMode = _rSource.NewMode == "design" ? eDesign : eAlive; + + impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() ); + + try + { + // if the control is part of an invisible layer, we need to explicitly hide it in alive mode + impl_adjustControlVisibilityToLayerVisibility_throw(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementInserted( const ContainerEvent& /*_Event*/ ) + { + // not interested in + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementRemoved( const ContainerEvent& Event ) + { + SolarMutexGuard aSolarGuard; + // some code below - in particular our disposal - might trigger actions which require the + // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control, + // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex - + // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169# + DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementRemoved: where did this come from?" ); + + if ( m_aControl == Event.Element ) + impl_dispose_nothrow( false ); + } + + + void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementReplaced( const ContainerEvent& Event ) + { + SolarMutexGuard aSolarGuard; + DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementReplaced: where did this come from?" ); + + if ( ! ( m_aControl == Event.ReplacedElement ) ) + return; + + Reference< XControl > xNewControl( Event.Element, UNO_QUERY ); + DBG_ASSERT( xNewControl.is(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: invalid new control!" ); + if ( !xNewControl.is() ) + return; + + ENSURE_OR_THROW( m_pOutputDeviceForWindow, "calling this without /me having an output device should be impossible." ); + + DBG_ASSERT( xNewControl->getModel() == m_aControl.getModel(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: another model at the new control?" ); + // another model should - in the drawing layer - also imply another SdrUnoObj, which + // should also result in new ViewContact, and thus in new ViewObjectContacts + + impl_switchControlListening_nothrow( false ); + + ControlHolder aNewControl( xNewControl ); + aNewControl.setZoom( m_aControl.getZoom() ); + aNewControl.setPosSize( m_aControl.getPosSize() ); + aNewControl.setDesignMode( impl_isControlDesignMode_nothrow() ); + + m_aControl = xNewControl; + m_bControlIsVisible = m_aControl.isVisible(); + + impl_switchControlListening_nothrow( true ); + + m_pAntiImpl->onControlChangedOrModified( ViewObjectContactOfUnoControl::ImplAccess() ); + } + + + void ViewObjectContactOfUnoControl_Impl::setControlDesignMode( bool _bDesignMode ) const + { + if ( ( m_eControlDesignMode != eUnknown ) && ( _bDesignMode == impl_isControlDesignMode_nothrow() ) ) + // nothing to do + return; + m_eControlDesignMode = _bDesignMode ? eDesign : eAlive; + + if ( !m_aControl.is() ) + // nothing to do, the setting will be respected as soon as the control + // is created + return; + + try + { + m_aControl.setDesignMode( _bDesignMode ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + //= LazyControlCreationPrimitive2D + + + bool LazyControlCreationPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if ( !BufferedDecompositionPrimitive2D::operator==( rPrimitive ) ) + return false; + + const LazyControlCreationPrimitive2D* pRHS = dynamic_cast< const LazyControlCreationPrimitive2D* >( &rPrimitive ); + if ( !pRHS ) + return false; + + if ( m_pVOCImpl != pRHS->m_pVOCImpl ) + return false; + + if ( m_aTransformation != pRHS->m_aTransformation ) + return false; + + return true; + } + + + void LazyControlCreationPrimitive2D::getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation ) + { + // Do use model data directly to create the correct geometry. Do NOT + // use getBoundRect()/getSnapRect() here; these will use the sequence of + // primitives themselves in the long run. + const tools::Rectangle aSdrGeoData( _rVOC.GetSdrUnoObj().GetGeoRect() ); + const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aSdrGeoData); + + _out_Transformation.identity(); + _out_Transformation.set( 0, 0, aRange.getWidth() ); + _out_Transformation.set( 1, 1, aRange.getHeight() ); + _out_Transformation.set( 0, 2, aRange.getMinX() ); + _out_Transformation.set( 1, 2, aRange.getMinY() ); + } + + + ::basegfx::B2DRange LazyControlCreationPrimitive2D::getB2DRange( const ::drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/ ) const + { + ::basegfx::B2DRange aRange( 0.0, 0.0, 1.0, 1.0 ); + aRange.transform( m_aTransformation ); + return aRange; + } + + + void LazyControlCreationPrimitive2D::get2DDecomposition( ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor, const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const + { + #if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); + #endif + if ( m_pVOCImpl->hasControl() ) + impl_positionAndZoomControl( _rViewInformation ); + BufferedDecompositionPrimitive2D::get2DDecomposition( rVisitor, _rViewInformation ); + } + + + void LazyControlCreationPrimitive2D::create2DDecomposition( ::drawinglayer::primitive2d::Primitive2DContainer& rContainer, const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const + { + #if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); + #endif + const bool bHadControl = m_pVOCImpl->getExistentControl().is(); + + // force control here to make it a VCL ChildWindow. Will be fetched + // and used below by getExistentControl() + m_pVOCImpl->ensureControl( &_rViewInformation.getObjectToViewTransformation() ); + impl_positionAndZoomControl( _rViewInformation ); + + // get needed data + const ViewContactOfUnoControl& rViewContactOfUnoControl( m_pVOCImpl->getViewContact() ); + Reference< XControlModel > xControlModel( rViewContactOfUnoControl.GetSdrUnoObj().GetUnoControlModel() ); + const ControlHolder& rControl( m_pVOCImpl->getExistentControl() ); + + if ( !bHadControl && rControl.is() && rControl.isVisible() ) + rControl.invalidate(); + + // check if we already have an XControl. + if ( !xControlModel.is() || !rControl.is() ) + { + // use the default mechanism. This will create a ControlPrimitive2D without + // handing over a XControl. If not even a XControlModel exists, it will + // create the SdrObject fallback visualisation + rViewContactOfUnoControl.getViewIndependentPrimitive2DContainer(rContainer); + return; + } + + SdrObject const& rSdrObj(m_pVOCImpl->getViewContact().GetSdrObject()); + void const* pAnchorKey(nullptr); + if (auto const pUserCall = rSdrObj.GetUserCall()) + { + pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(rSdrObj); + } + + // create a primitive and hand over the existing xControl. This will + // allow the primitive to not need to create another one on demand. + rContainer.push_back( new ::drawinglayer::primitive2d::ControlPrimitive2D( + m_aTransformation, xControlModel, rControl.getControl(), + rSdrObj.GetTitle(), rSdrObj.GetDescription(), pAnchorKey) ); + } + + sal_uInt32 LazyControlCreationPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D; + } + + ViewObjectContactOfUnoControl::ViewObjectContactOfUnoControl( ObjectContact& _rObjectContact, ViewContactOfUnoControl& _rViewContact ) + :ViewObjectContactOfSdrObj( _rObjectContact, _rViewContact ) + ,m_pImpl( new ViewObjectContactOfUnoControl_Impl( this ) ) + { + } + + + ViewObjectContactOfUnoControl::~ViewObjectContactOfUnoControl() + { + m_pImpl->dispose(); + m_pImpl = nullptr; + + } + + + Reference< XControl > ViewObjectContactOfUnoControl::getControl() + { + SolarMutexGuard aSolarGuard; + m_pImpl->ensureControl( nullptr ); + return m_pImpl->getExistentControl().getControl(); + } + + + Reference< XControl > ViewObjectContactOfUnoControl::getTemporaryControlForWindow( + const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer, const SdrUnoObj& _rUnoObject ) + { + ControlHolder aControl; + + InvisibleControlViewAccess aSimulatePageView( _inout_ControlContainer ); + OSL_VERIFY( ViewObjectContactOfUnoControl_Impl::createControlForDevice( aSimulatePageView, *_rWindow.GetOutDev(), _rUnoObject, + _rWindow.GetOutDev()->GetViewTransformation(), _rWindow.GetOutDev()->GetInverseViewTransformation(), aControl ) ); + return aControl.getControl(); + } + + + void ViewObjectContactOfUnoControl::ensureControlVisibility( bool _bVisible ) const + { + SolarMutexGuard aSolarGuard; + + try + { + const ControlHolder& rControl( m_pImpl->getExistentControl() ); + if ( !rControl.is() ) + return; + + // only need to care for alive mode + if ( rControl.isDesignMode() ) + return; + + // is the visibility correct? + if ( m_pImpl->isControlVisible() == _bVisible ) + return; + + // no -> adjust it + rControl.setVisible( _bVisible ); + DBG_ASSERT( m_pImpl->isControlVisible() == _bVisible, "ViewObjectContactOfUnoControl::ensureControlVisibility: this didn't work!" ); + // now this would mean that either isControlVisible is not reliable, + // or that showing/hiding the window did not work as intended. + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + } + + + void ViewObjectContactOfUnoControl::setControlDesignMode( bool _bDesignMode ) const + { + SolarMutexGuard aSolarGuard; + m_pImpl->setControlDesignMode( _bDesignMode ); + + if(!_bDesignMode) + { + // when live mode is switched on, a refresh is needed. The edit mode visualisation + // needs to be repainted and the now used VCL-Window needs to be positioned and + // sized. Both is done from the repaint refresh. + const_cast< ViewObjectContactOfUnoControl* >(this)->ActionChanged(); + } + } + + + void ViewObjectContactOfUnoControl::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const + { + if ( m_pImpl->isDisposed() ) + // our control already died. + // TODO: Is it worth re-creating the control? Finally, this is a pathological situation, it means some instance + // disposed the control though it doesn't own it. So, /me thinks we should not bother here. + return; + + if ( GetObjectContact().getViewInformation2D().getViewTransformation().isIdentity() ) + // remove this when #i115754# is fixed + return; + + // ignore existing controls which are in alive mode and manually switched to "invisible" #i102090# + const ControlHolder& rControl( m_pImpl->getExistentControl() ); + if ( rControl.is() && !rControl.isDesignMode() && !rControl.isVisible() ) + return; + + rVisitor.visit( new LazyControlCreationPrimitive2D( m_pImpl ) ); + } + + + bool ViewObjectContactOfUnoControl::isPrimitiveVisible( const DisplayInfo& _rDisplayInfo ) const + { + SolarMutexGuard aSolarGuard; + + if ( m_pImpl->hasControl() ) + { + const ::drawinglayer::geometry::ViewInformation2D& rViewInformation( GetObjectContact().getViewInformation2D() ); + #if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX ); + #endif + + if ( !rViewInformation.getViewport().isEmpty() ) + { + // tdf#121963 check and eventually pre-multiply ViewTransformation + // with GridOffset transformation to avoid alternating positions of + // FormControls which are victims of the non-linear calc ViewTransformation + // aka GridOffset. For other paths (e.g. repaint) this is included already + // as part of the object's sequence of B2DPrimitive - representation + // (see ViewObjectContact::getPrimitive2DSequence and how getGridOffset is used there) + basegfx::B2DHomMatrix aViewTransformation(rViewInformation.getObjectToViewTransformation()); + + if(GetObjectContact().supportsGridOffsets()) + { + const basegfx::B2DVector& rGridOffset(getGridOffset()); + + if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY()) + { + // pre-multiply: GridOffset needs to be applied directly to logic model data + // of object coordinates, so multiply GridOffset from right to make it + // work as 1st change - these objects may still be part of groups/hierarchies + aViewTransformation = aViewTransformation * basegfx::utils::createTranslateB2DHomMatrix(rGridOffset); + } + } + + m_pImpl->positionAndZoomControl(aViewTransformation); + } + } + + return ViewObjectContactOfSdrObj::isPrimitiveVisible( _rDisplayInfo ); + } + + + void ViewObjectContactOfUnoControl::propertyChange() + { + impl_onControlChangedOrModified(); + } + + + void ViewObjectContactOfUnoControl::ActionChanged() + { + // call parent + ViewObjectContactOfSdrObj::ActionChanged(); + const ControlHolder& rControl(m_pImpl->getExistentControl()); + + if(!rControl.is() || rControl.isDesignMode()) + return; + + // #i93180# if layer visibility has changed and control is in live mode, it is necessary + // to correct visibility to make those control vanish on SdrObject LayerID changes + const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView(); + + if(pSdrPageView) + { + const SdrObject& rObject = getSdrObject(); + const bool bIsLayerVisible( rObject.IsVisible() && pSdrPageView->GetVisibleLayers().IsSet(rObject.GetLayer())); + + if(rControl.isVisible() != bIsLayerVisible) + { + rControl.setVisible(bIsLayerVisible); + } + } + } + + + void ViewObjectContactOfUnoControl::impl_onControlChangedOrModified() + { + // graphical invalidate at all views + ActionChanged(); + + // #i93318# flush Primitive2DContainer to force recreation with updated XControlModel + // since e.g. background color has changed and existing decompositions are possibly no + // longer valid. Unfortunately this is not detected from ControlPrimitive2D::operator== + // since it only has a uno reference to the XControlModel + flushPrimitive2DSequence(); + } + + UnoControlPrintOrPreviewContact::UnoControlPrintOrPreviewContact( ObjectContactOfPageView& _rObjectContact, ViewContactOfUnoControl& _rViewContact ) + :ViewObjectContactOfUnoControl( _rObjectContact, _rViewContact ) + { + } + + + UnoControlPrintOrPreviewContact::~UnoControlPrintOrPreviewContact() + { + } + + + void UnoControlPrintOrPreviewContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor ) const + { + if ( !m_pImpl->isPrintableControl() ) + return; + ViewObjectContactOfUnoControl::createPrimitive2DSequence( rDisplayInfo, rVisitor ); + } + + +} // namespace sdr::contact + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/viewobjectcontactredirector.cxx b/svx/source/sdr/contact/viewobjectcontactredirector.cxx new file mode 100644 index 0000000000..c499093270 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactredirector.cxx @@ -0,0 +1,39 @@ +/* -*- 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/sdr/contact/viewobjectcontactredirector.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> + +namespace sdr::contact +{ +// basic constructor. +ViewObjectContactRedirector::ViewObjectContactRedirector() {} + +// The destructor. +ViewObjectContactRedirector::~ViewObjectContactRedirector() {} + +void ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + return rOriginal.createPrimitive2DSequence(rDisplayInfo, rVisitor); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |