diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /svx/source/sdr | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
114 files changed, 24485 insertions, 0 deletions
diff --git a/svx/source/sdr/animation/animationstate.cxx b/svx/source/sdr/animation/animationstate.cxx new file mode 100644 index 000000000..074f2082e --- /dev/null +++ b/svx/source/sdr/animation/animationstate.cxx @@ -0,0 +1,130 @@ +/* -*- 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/animation/animationstate.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/animation/objectanimator.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <drawinglayer/primitive2d/animatedprimitive2d.hxx> +#include <drawinglayer/animation/animationtiming.hxx> +#include <comphelper/lok.hxx> + + +namespace sdr::animation +{ + double PrimitiveAnimation::getSmallestNextTime(double fCurrentTime) + { + double fRetval(0.0); + + if(!maAnimatedPrimitives.empty()) + { + const sal_Int32 nCount(maAnimatedPrimitives.size()); + + for(sal_Int32 a(0); a < nCount; a++) + { + const drawinglayer::primitive2d::Primitive2DReference xRef(maAnimatedPrimitives[a]); + const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D* pCandidate = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D* >(xRef.get()); + + const drawinglayer::animation::AnimationEntry& rAnimEntry = pCandidate->getAnimationEntry(); + const double fNextTime(rAnimEntry.getNextEventTime(fCurrentTime)); + + if(!::basegfx::fTools::equalZero(fNextTime)) + { + if(::basegfx::fTools::equalZero(fRetval)) + { + fRetval = fNextTime; + } + else if(::basegfx::fTools::less(fNextTime, fRetval)) + { + fRetval = fNextTime; + } + } + } + } + + return fRetval; + } + + void PrimitiveAnimation::prepareNextEvent() + { + const double fCurrentTime(mrVOContact.GetObjectContact().getPrimitiveAnimator().GetTime()); + const double fNextTime(getSmallestNextTime(fCurrentTime)); + + // getSmallestNextTime will be zero when animation ended. If not zero, a next step + // exists + if(::basegfx::fTools::equalZero(fNextTime)) + return; + + // next time point exists, use it + sal_uInt32 nNextTime; + + if(fNextTime >= double(0xffffff00)) + { + // take care for very late points in time, e.g. when a text animation stops + // in a defined AnimationEntryFixed with endless (0xffffffff) duration + nNextTime = GetTime() + (1000 * 60 * 60); // one hour, works with vcl timers, 0xffffff00 was too much... + } + else + { + nNextTime = static_cast<sal_uInt32>(fNextTime); + } + + // ensure step forward in integer timing, the floating step difference maybe smaller than 1.0. Use + // at least 25ms for next step + const sal_uInt32 nMinimumStepTime(static_cast<sal_uInt32>(fCurrentTime) + 25); + + if(nNextTime <= nMinimumStepTime) + { + nNextTime = nMinimumStepTime; + } + + // set time and reactivate by re-adding to the scheduler + SetTime(nNextTime); + mrVOContact.GetObjectContact().getPrimitiveAnimator().InsertEvent(*this); + } + + PrimitiveAnimation::PrimitiveAnimation(sdr::contact::ViewObjectContact& rVOContact, drawinglayer::primitive2d::Primitive2DContainer&& rAnimatedPrimitives) + : mrVOContact(rVOContact), + maAnimatedPrimitives(std::move(rAnimatedPrimitives)) + { + if (!comphelper::LibreOfficeKit::isActive()) + // setup initially + prepareNextEvent(); + } + + PrimitiveAnimation::~PrimitiveAnimation() + { + // ensure that Event member is removed from PrimitiveAnimator + mrVOContact.GetObjectContact().getPrimitiveAnimator().RemoveEvent(this); + } + + // execute event, from base class Event + void PrimitiveAnimation::Trigger(sal_uInt32 /*nTime*/) + { + // schedule a repaint of associated object + mrVOContact.ActionChanged(); + + if (!comphelper::LibreOfficeKit::isActive()) + // re-setup + prepareNextEvent(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/animation/objectanimator.cxx b/svx/source/sdr/animation/objectanimator.cxx new file mode 100644 index 000000000..aad545fd7 --- /dev/null +++ b/svx/source/sdr/animation/objectanimator.cxx @@ -0,0 +1,35 @@ +/* -*- 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/animation/objectanimator.hxx> + + +namespace sdr::animation +{ + primitiveAnimator::primitiveAnimator() + { + } + + primitiveAnimator::~primitiveAnimator() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/animation/scheduler.cxx b/svx/source/sdr/animation/scheduler.cxx new file mode 100644 index 000000000..d4451c099 --- /dev/null +++ b/svx/source/sdr/animation/scheduler.cxx @@ -0,0 +1,173 @@ +/* -*- 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/animation/scheduler.hxx> + +#include <algorithm> +#include <vector> + + +// event class + +namespace sdr::animation +{ + Event::Event() : mnTime(0) + { + } + + Event::~Event() + { + } + + + void Event::SetTime(sal_uInt32 nNew) + { + if(mnTime != nNew) + { + mnTime = nNew; + } + } + + Scheduler::Scheduler() + : Timer("sdr::animation::Scheduler"), + mnTime(0), + mnDeltaTime(0), + mbIsPaused(false) + { + SetPriority(TaskPriority::POST_PAINT); + } + + Scheduler::~Scheduler() + { + Stop(); + } + + void Scheduler::Invoke() + { + // stop timer and add time + Stop(); + mnTime += mnDeltaTime; + + // execute events + triggerEvents(); + + // re-start or stop timer according to event list + checkTimeout(); + } + + void Scheduler::triggerEvents() + { + if (mvEvents.empty()) + return; + + // copy events which need to be executed to a vector. Remove them from + // the scheduler + ::std::vector< Event* > aToBeExecutedList; + + while(!mvEvents.empty() && mvEvents.front()->GetTime() <= mnTime) + { + Event* pNextEvent = mvEvents.front(); + mvEvents.erase(mvEvents.begin()); + aToBeExecutedList.push_back(pNextEvent); + } + + // execute events from the vector + for(auto& rpCandidate : aToBeExecutedList) + { + // trigger event. This may re-insert the event to the scheduler again + rpCandidate->Trigger(mnTime); + } + } + + void Scheduler::checkTimeout() + { + // re-start or stop timer according to event list + if(!IsPaused() && !mvEvents.empty()) + { + mnDeltaTime = mvEvents.front()->GetTime() - mnTime; + + if(0 != mnDeltaTime) + { + SetTimeout(mnDeltaTime); + Start(); + } + } + else + { + Stop(); + } + } + + + // #i38135# + void Scheduler::SetTime(sal_uInt32 nTime) + { + // reset time + Stop(); + mnTime = nTime; + + if (mvEvents.empty()) + return; + + // reset event time points + for (auto & rEvent : mvEvents) + { + rEvent->SetTime(nTime); + } + + if(!IsPaused()) + { + // without delta time, init events by triggering them. This will invalidate + // painted objects and add them to the scheduler again + mnDeltaTime = 0; + triggerEvents(); + checkTimeout(); + } + } + + void Scheduler::InsertEvent(Event& rNew) + { + // insert maintaining time ordering + auto it = std::find_if(mvEvents.begin(), mvEvents.end(), + [&rNew](const Event* pEvent) { return rNew.GetTime() < pEvent->GetTime(); }); + mvEvents.insert(it, &rNew); + checkTimeout(); + } + + void Scheduler::RemoveEvent(Event* pOld) + { + if(!mvEvents.empty()) + { + mvEvents.erase(std::remove(mvEvents.begin(), mvEvents.end(), pOld), mvEvents.end()); + checkTimeout(); + } + } + + void Scheduler::SetPaused(bool bNew) + { + if(bNew != mbIsPaused) + { + mbIsPaused = bNew; + checkTimeout(); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx b/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx new file mode 100644 index 000000000..261eef295 --- /dev/null +++ b/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx @@ -0,0 +1,244 @@ +/* -*- 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/attribute/sdrallfillattributeshelper.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/attribute/fillhatchattribute.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <vcl/graph.hxx> + +////////////////////////////////////////////////////////////////////////////// + +namespace drawinglayer::attribute +{ + void SdrAllFillAttributesHelper::createPrimitive2DSequence( + const basegfx::B2DRange& rPaintRange, + const basegfx::B2DRange& rDefineRange) + { + // reset and remember new target range for object geometry + maLastPaintRange = rPaintRange; + maLastDefineRange = rDefineRange; + + if(isUsed()) + { + maPrimitives.resize(1); + maPrimitives[0] = drawinglayer::primitive2d::createPolyPolygonFillPrimitive( + basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect( + maLastPaintRange)), + maLastDefineRange, + maFillAttribute ? *maFillAttribute : drawinglayer::attribute::SdrFillAttribute(), + maFillGradientAttribute ? *maFillGradientAttribute : drawinglayer::attribute::FillGradientAttribute()); + } + } + + SdrAllFillAttributesHelper::SdrAllFillAttributesHelper(const Color& rColor) + { + maFillAttribute = drawinglayer::attribute::SdrFillAttribute( + 0.0, + rColor.GetRGBColor().getBColor(), + drawinglayer::attribute::FillGradientAttribute(), + drawinglayer::attribute::FillHatchAttribute(), + drawinglayer::attribute::SdrFillGraphicAttribute()); + } + + SdrAllFillAttributesHelper::SdrAllFillAttributesHelper(const SfxItemSet& rSet) + : maFillAttribute( + drawinglayer::primitive2d::createNewSdrFillAttribute(rSet)), + maFillGradientAttribute( + drawinglayer::primitive2d::createNewTransparenceGradientAttribute(rSet)) + { + } + + SdrAllFillAttributesHelper::~SdrAllFillAttributesHelper() + { + } + + bool SdrAllFillAttributesHelper::isUsed() const + { + // only depends on fill, FillGradientAttribute alone defines no fill + return maFillAttribute && !maFillAttribute->isDefault(); + } + + bool SdrAllFillAttributesHelper::isTransparent() const + { + if(hasSdrFillAttribute() && 0.0 != maFillAttribute->getTransparence()) + { + return true; + } + + if(maFillGradientAttribute && !maFillGradientAttribute->isDefault()) + { + return true; + } + + if(hasSdrFillAttribute()) + { + const Graphic& rGraphic = getFillAttribute().getFillGraphic().getFillGraphic(); + + return rGraphic.IsSupportedGraphic() && rGraphic.IsTransparent(); + } + + return false; + } + + const drawinglayer::attribute::SdrFillAttribute& SdrAllFillAttributesHelper::getFillAttribute() const + { + if(!maFillAttribute) + { + const_cast< SdrAllFillAttributesHelper* >(this)->maFillAttribute.emplace(); + } + + return *maFillAttribute; + } + + const drawinglayer::attribute::FillGradientAttribute& SdrAllFillAttributesHelper::getFillGradientAttribute() const + { + if(!maFillGradientAttribute) + { + const_cast< SdrAllFillAttributesHelper* >(this)->maFillGradientAttribute.emplace(); + } + + return *maFillGradientAttribute; + } + + const drawinglayer::primitive2d::Primitive2DContainer& SdrAllFillAttributesHelper::getPrimitive2DSequence( + const basegfx::B2DRange& rPaintRange, + const basegfx::B2DRange& rDefineRange) const + { + if(!maPrimitives.empty() && (maLastPaintRange != rPaintRange || maLastDefineRange != rDefineRange)) + { + const_cast< SdrAllFillAttributesHelper* >(this)->maPrimitives.clear(); + } + + if(maPrimitives.empty()) + { + const_cast< SdrAllFillAttributesHelper* >(this)->createPrimitive2DSequence(rPaintRange, rDefineRange); + } + + return maPrimitives; + } + + basegfx::BColor SdrAllFillAttributesHelper::getAverageColor(const basegfx::BColor& rFallback) const + { + basegfx::BColor aRetval(rFallback); + + if(maFillAttribute && !maFillAttribute->isDefault()) + { + const drawinglayer::attribute::FillGradientAttribute& rFillGradientAttribute = maFillAttribute->getGradient(); + const drawinglayer::attribute::FillHatchAttribute& rFillHatchAttribute = maFillAttribute->getHatch(); + const drawinglayer::attribute::SdrFillGraphicAttribute& rSdrFillGraphicAttribute = maFillAttribute->getFillGraphic(); + const drawinglayer::attribute::FillGradientAttribute& rFillTransparenceGradientAttribute = getFillGradientAttribute(); + double fTransparence(maFillAttribute->getTransparence()); + + if(!rFillTransparenceGradientAttribute.isDefault()) + { + const double fTransA = rFillTransparenceGradientAttribute.getStartColor().luminance(); + const double fTransB = rFillTransparenceGradientAttribute.getEndColor().luminance(); + + fTransparence = (fTransA + fTransB) * 0.5; + } + + if(!rFillGradientAttribute.isDefault()) + { + // gradient fill + const basegfx::BColor& rStart = rFillGradientAttribute.getStartColor(); + const basegfx::BColor& rEnd = rFillGradientAttribute.getEndColor(); + + aRetval = basegfx::interpolate(rStart, rEnd, 0.5); + } + else if(!rFillHatchAttribute.isDefault()) + { + // hatch fill + const basegfx::BColor& rColor = rFillHatchAttribute.getColor(); + + if(rFillHatchAttribute.isFillBackground()) + { + const basegfx::BColor& rBackgroundColor = maFillAttribute->getColor(); + + // mix colors 50%/50% + aRetval = basegfx::interpolate(rColor, rBackgroundColor, 0.5); + } + else + { + // mix color with fallback color + aRetval = basegfx::interpolate(rColor, rFallback, 0.5); + } + } + else if(!rSdrFillGraphicAttribute.isDefault()) + { + // graphic fill + + // not used yet by purpose (see SwPageFrm::GetDrawBackgrdColor()), + // use fallback (already set) + } + else + { + // color fill + aRetval = maFillAttribute->getColor(); + } + + if(!basegfx::fTools::equalZero(fTransparence)) + { + // blend into transparency + aRetval = basegfx::interpolate(aRetval, rFallback, fTransparence); + } + } + + return aRetval.clamp(); + } + + bool SdrAllFillAttributesHelper::needCompleteRepaint() const + { + if(!isUsed() || !hasSdrFillAttribute()) + { + // not used or no fill + return false; + } + + const drawinglayer::attribute::SdrFillAttribute& rSdrFillAttribute = getFillAttribute(); + + if(!rSdrFillAttribute.getHatch().isDefault()) + { + // hatch is always top-left aligned, needs no full refreshes + return false; + } + + if(!rSdrFillAttribute.getGradient().isDefault()) + { + // gradients always scale with the object + return true; + } + + if(!rSdrFillAttribute.getFillGraphic().isDefault()) + { + // some graphic constellations may not need this, but since most do + // (stretch to fill, all but top-left aligned, ...) claim to do by default + return true; + } + + // color fill + return false; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdreffectstextattribute.cxx b/svx/source/sdr/attribute/sdreffectstextattribute.cxx new file mode 100644 index 000000000..3287b8c8e --- /dev/null +++ b/svx/source/sdr/attribute/sdreffectstextattribute.cxx @@ -0,0 +1,76 @@ +/* -*- 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/attribute/sdreffectstextattribute.hxx> + + +namespace drawinglayer::attribute +{ + SdrEffectsTextAttribute::SdrEffectsTextAttribute( + const SdrShadowAttribute& rShadow, + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow, + sal_Int32 nSoftEdgeRadius) + : maShadow(rShadow), + maTextAttribute(rTextAttribute), + maGlow(rGlow), + mnSoftEdgeRadius(nSoftEdgeRadius) + { + } + + SdrEffectsTextAttribute::SdrEffectsTextAttribute() + { + } + + SdrEffectsTextAttribute::SdrEffectsTextAttribute(const SdrEffectsTextAttribute& rCandidate) + : maShadow(rCandidate.getShadow()), + maTextAttribute(rCandidate.getText()), + maGlow(rCandidate.maGlow), + mnSoftEdgeRadius(rCandidate.mnSoftEdgeRadius) + { + } + + SdrEffectsTextAttribute& SdrEffectsTextAttribute::operator=(const SdrEffectsTextAttribute& rCandidate) + { + maShadow = rCandidate.getShadow(); + maTextAttribute = rCandidate.getText(); + maGlow = rCandidate.maGlow; + mnSoftEdgeRadius = rCandidate.mnSoftEdgeRadius; + + return *this; + } + + bool SdrEffectsTextAttribute::isDefault() const + { + return (getShadow().isDefault() + && getText().isDefault() && maGlow.isDefault() && getSoftEdgeRadius() == 0); + } + + bool SdrEffectsTextAttribute::operator==(const SdrEffectsTextAttribute& rCandidate) const + { + return (getShadow() == rCandidate.getShadow() + && getText() == rCandidate.getText() + && getGlow() == rCandidate.getGlow() + && getSoftEdgeRadius() == rCandidate.getSoftEdgeRadius()); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrfilltextattribute.cxx b/svx/source/sdr/attribute/sdrfilltextattribute.cxx new file mode 100644 index 000000000..01593c982 --- /dev/null +++ b/svx/source/sdr/attribute/sdrfilltextattribute.cxx @@ -0,0 +1,65 @@ +/* -*- 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/attribute/sdrfilltextattribute.hxx> + + +namespace drawinglayer::attribute +{ + SdrFillTextAttribute::SdrFillTextAttribute( + const SdrFillAttribute& rFill, + const FillGradientAttribute& rFillFloatTransGradient, + const SdrTextAttribute& rTextAttribute) + : maFill(rFill), + maFillFloatTransGradient(rFillFloatTransGradient), + maTextAttribute(rTextAttribute) + { + } + + SdrFillTextAttribute::SdrFillTextAttribute() + { + } + + SdrFillTextAttribute::SdrFillTextAttribute(const SdrFillTextAttribute& rCandidate) + : maFill(rCandidate.getFill()), + maFillFloatTransGradient(rCandidate.getFillFloatTransGradient()), + maTextAttribute(rCandidate.getText()) + { + } + + SdrFillTextAttribute& SdrFillTextAttribute::operator=(const SdrFillTextAttribute& rCandidate) + { + maFill = rCandidate.getFill(); + maFillFloatTransGradient = rCandidate.getFillFloatTransGradient(); + maTextAttribute = rCandidate.getText(); + + return *this; + } + + bool SdrFillTextAttribute::operator==(const SdrFillTextAttribute& rCandidate) const + { + return(getFill() == rCandidate.getFill() + && getFillFloatTransGradient() == rCandidate.getFillFloatTransGradient() + && getText() == rCandidate.getText()); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrformtextattribute.cxx b/svx/source/sdr/attribute/sdrformtextattribute.cxx new file mode 100644 index 000000000..69fff8a8d --- /dev/null +++ b/svx/source/sdr/attribute/sdrformtextattribute.cxx @@ -0,0 +1,372 @@ +/* -*- 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/attribute/sdrformtextattribute.hxx> +#include <basegfx/vector/b2enums.hxx> +#include <svl/itemset.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/svddef.hxx> +#include <svx/xftdiit.hxx> +#include <svx/xftstit.hxx> +#include <svx/xftshxy.hxx> +#include <xftshtit.hxx> +#include <svx/xtextit0.hxx> +#include <svx/xftadit.hxx> +#include <svx/xftshit.hxx> +#include <svx/xftshcit.hxx> +#include <svx/xftmrit.hxx> +#include <svx/xftouit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xdash.hxx> +#include <svx/xlndsit.hxx> +#include <drawinglayer/attribute/lineattribute.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> +#include <sdr/attribute/sdrformtextoutlineattribute.hxx> +#include <com/sun/star/drawing/LineCap.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> + + +// helper to get line, stroke and transparence attributes from SfxItemSet + +namespace +{ + basegfx::B2DLineJoin impGetB2DLineJoin(css::drawing::LineJoint eLineJoint) + { + switch(eLineJoint) + { + case css::drawing::LineJoint_BEVEL : + { + return basegfx::B2DLineJoin::Bevel; + } + case css::drawing::LineJoint_MIDDLE : + case css::drawing::LineJoint_MITER : + { + return basegfx::B2DLineJoin::Miter; + } + case css::drawing::LineJoint_ROUND : + { + return basegfx::B2DLineJoin::Round; + } + default : // css::drawing::LineJoint_NONE + { + return basegfx::B2DLineJoin::NONE; // XLINEJOINT_NONE + } + } + } + + sal_uInt8 impGetStrokeTransparence(bool bShadow, const SfxItemSet& rSet) + { + sal_uInt8 nRetval; + + if(bShadow) + { + nRetval = static_cast<sal_uInt8>((rSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue() * 255) / 100); + } + else + { + nRetval = static_cast<sal_uInt8>((rSet.Get(XATTR_LINETRANSPARENCE).GetValue() * 255) / 100); + } + + return nRetval; + } + + drawinglayer::attribute::LineAttribute impGetLineAttribute(bool bShadow, const SfxItemSet& rSet) + { + basegfx::BColor aColorAttribute; + + if(bShadow) + { + const Color aShadowColor(rSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue()); + aColorAttribute = aShadowColor.getBColor(); + } + else + { + const Color aLineColor(rSet.Get(XATTR_LINECOLOR).GetColorValue()); + aColorAttribute = aLineColor.getBColor(); + } + + const sal_uInt32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue(); + const css::drawing::LineJoint eLineJoint = rSet.Get(XATTR_LINEJOINT).GetValue(); + const css::drawing::LineCap eLineCap = rSet.Get(XATTR_LINECAP).GetValue(); + + return drawinglayer::attribute::LineAttribute( + aColorAttribute, + static_cast<double>(nLineWidth), + impGetB2DLineJoin(eLineJoint), + eLineCap); + } + + drawinglayer::attribute::StrokeAttribute impGetStrokeAttribute(const SfxItemSet& rSet) + { + const css::drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue(); + double fFullDotDashLen(0.0); + ::std::vector< double > aDotDashArray; + + if(css::drawing::LineStyle_DASH == eLineStyle) + { + const XDash& rDash = rSet.Get(XATTR_LINEDASH).GetDashValue(); + + if(rDash.GetDots() || rDash.GetDashes()) + { + const sal_uInt32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue(); + fFullDotDashLen = rDash.CreateDotDashArray(aDotDashArray, static_cast<double>(nLineWidth)); + } + } + + return drawinglayer::attribute::StrokeAttribute(std::move(aDotDashArray), fFullDotDashLen); + } +} // end of anonymous namespace + + +namespace drawinglayer::attribute +{ + class ImpSdrFormTextAttribute + { + public: + // FormText (FontWork) Attributes + sal_Int32 mnFormTextDistance; // distance from line in upright direction + sal_Int32 mnFormTextStart; // shift from polygon start + sal_Int32 mnFormTextShdwXVal; // shadow distance or 10th degrees + sal_Int32 mnFormTextShdwYVal; // shadow distance or scaling + sal_uInt16 mnFormTextShdwTransp; // shadow transparence + XFormTextStyle meFormTextStyle; // on/off and char orientation + XFormTextAdjust meFormTextAdjust; // adjustment (left/right/center) and scale + XFormTextShadow meFormTextShadow; // shadow mode + Color maFormTextShdwColor; // shadow color + + // outline attributes; used when getFormTextOutline() is true and (for + // shadow) when getFormTextShadow() != XFormTextShadow::NONE + SdrFormTextOutlineAttribute maOutline; + SdrFormTextOutlineAttribute maShadowOutline; + + bool mbFormTextMirror : 1; // change orientation + bool mbFormTextOutline : 1; // show contour of objects + + explicit ImpSdrFormTextAttribute(const SfxItemSet& rSet) + : mnFormTextDistance(rSet.Get(XATTR_FORMTXTDISTANCE).GetValue()), + mnFormTextStart(rSet.Get(XATTR_FORMTXTSTART).GetValue()), + mnFormTextShdwXVal(rSet.Get(XATTR_FORMTXTSHDWXVAL).GetValue()), + mnFormTextShdwYVal(rSet.Get(XATTR_FORMTXTSHDWYVAL).GetValue()), + mnFormTextShdwTransp(rSet.Get(XATTR_FORMTXTSHDWTRANSP).GetValue()), + meFormTextStyle(rSet.Get(XATTR_FORMTXTSTYLE).GetValue()), + meFormTextAdjust(rSet.Get(XATTR_FORMTXTADJUST).GetValue()), + meFormTextShadow(rSet.Get(XATTR_FORMTXTSHADOW).GetValue()), + maFormTextShdwColor(rSet.Get(XATTR_FORMTXTSHDWCOLOR).GetColorValue()), + mbFormTextMirror(rSet.Get(XATTR_FORMTXTMIRROR).GetValue()), + mbFormTextOutline(rSet.Get(XATTR_FORMTXTOUTLINE).GetValue()) + { + if(!getFormTextOutline()) + return; + + const StrokeAttribute aStrokeAttribute(impGetStrokeAttribute(rSet)); + + // also need to prepare attributes for outlines + { + const LineAttribute aLineAttribute(impGetLineAttribute(false, rSet)); + const sal_uInt8 nTransparence(impGetStrokeTransparence(false, rSet)); + + maOutline = SdrFormTextOutlineAttribute( + aLineAttribute, aStrokeAttribute, nTransparence); + } + + if(XFormTextShadow::NONE != getFormTextShadow()) + { + // also need to prepare attributes for shadow outlines + const LineAttribute aLineAttribute(impGetLineAttribute(true, rSet)); + const sal_uInt8 nTransparence(impGetStrokeTransparence(true, rSet)); + + maShadowOutline = SdrFormTextOutlineAttribute( + aLineAttribute, aStrokeAttribute, nTransparence); + } + } + + ImpSdrFormTextAttribute() + : mnFormTextDistance(0), + mnFormTextStart(0), + mnFormTextShdwXVal(0), + mnFormTextShdwYVal(0), + mnFormTextShdwTransp(0), + meFormTextStyle(XFormTextStyle::NONE), + meFormTextAdjust(XFormTextAdjust::Center), + meFormTextShadow(XFormTextShadow::NONE), + mbFormTextMirror(false), + mbFormTextOutline(false) + { + } + + // data read access + sal_Int32 getFormTextDistance() const { return mnFormTextDistance; } + sal_Int32 getFormTextStart() const { return mnFormTextStart; } + sal_Int32 getFormTextShdwXVal() const { return mnFormTextShdwXVal; } + sal_Int32 getFormTextShdwYVal() const { return mnFormTextShdwYVal; } + XFormTextStyle getFormTextStyle() const { return meFormTextStyle; } + XFormTextAdjust getFormTextAdjust() const { return meFormTextAdjust; } + XFormTextShadow getFormTextShadow() const { return meFormTextShadow; } + const Color& getFormTextShdwColor() const { return maFormTextShdwColor; } + const SdrFormTextOutlineAttribute& getOutline() const { return maOutline; } + const SdrFormTextOutlineAttribute& getShadowOutline() const { return maShadowOutline; } + bool getFormTextMirror() const { return mbFormTextMirror; } + bool getFormTextOutline() const { return mbFormTextOutline; } + + // compare operator + bool operator==(const ImpSdrFormTextAttribute& rCandidate) const + { + return (getFormTextDistance() == rCandidate.getFormTextDistance() + && getFormTextStart() == rCandidate.getFormTextStart() + && getFormTextShdwXVal() == rCandidate.getFormTextShdwXVal() + && getFormTextShdwYVal() == rCandidate.getFormTextShdwYVal() + && mnFormTextShdwTransp == rCandidate.mnFormTextShdwTransp + && getFormTextStyle() == rCandidate.getFormTextStyle() + && getFormTextAdjust() == rCandidate.getFormTextAdjust() + && getFormTextShadow() == rCandidate.getFormTextShadow() + && getFormTextShdwColor() == rCandidate.getFormTextShdwColor() + && getOutline() == rCandidate.getOutline() + && getShadowOutline() == rCandidate.getShadowOutline() + && getFormTextMirror() == rCandidate.getFormTextMirror() + && getFormTextOutline() == rCandidate.getFormTextOutline()); + } + }; + + namespace + { + SdrFormTextAttribute::ImplType& theGlobalDefault() + { + static SdrFormTextAttribute::ImplType SINGLETON; + return SINGLETON; + } + } + + SdrFormTextAttribute::SdrFormTextAttribute(const SfxItemSet& rSet) + : mpSdrFormTextAttribute(ImpSdrFormTextAttribute(rSet)) + { + } + + SdrFormTextAttribute::SdrFormTextAttribute() + : mpSdrFormTextAttribute(theGlobalDefault()) + { + } + + SdrFormTextAttribute::SdrFormTextAttribute(const SdrFormTextAttribute& rCandidate) + : mpSdrFormTextAttribute(rCandidate.mpSdrFormTextAttribute) + { + } + + SdrFormTextAttribute::SdrFormTextAttribute(SdrFormTextAttribute&& rCandidate) noexcept + : mpSdrFormTextAttribute(std::move(rCandidate.mpSdrFormTextAttribute)) + { + } + + SdrFormTextAttribute::~SdrFormTextAttribute() + { + } + + bool SdrFormTextAttribute::isDefault() const + { + return mpSdrFormTextAttribute.same_object(theGlobalDefault()); + } + + SdrFormTextAttribute& SdrFormTextAttribute::operator=(const SdrFormTextAttribute& rCandidate) + { + mpSdrFormTextAttribute = rCandidate.mpSdrFormTextAttribute; + return *this; + } + + SdrFormTextAttribute& SdrFormTextAttribute::operator=(SdrFormTextAttribute&& rCandidate) noexcept + { + mpSdrFormTextAttribute = std::move(rCandidate.mpSdrFormTextAttribute); + return *this; + } + + bool SdrFormTextAttribute::operator==(const SdrFormTextAttribute& rCandidate) const + { + // tdf#87509 default attr is always != non-default attr, even with same values + if(rCandidate.isDefault() != isDefault()) + return false; + + return rCandidate.mpSdrFormTextAttribute == mpSdrFormTextAttribute; + } + + sal_Int32 SdrFormTextAttribute::getFormTextDistance() const + { + return mpSdrFormTextAttribute->getFormTextDistance(); + } + + sal_Int32 SdrFormTextAttribute::getFormTextStart() const + { + return mpSdrFormTextAttribute->getFormTextStart(); + } + + sal_Int32 SdrFormTextAttribute::getFormTextShdwXVal() const + { + return mpSdrFormTextAttribute->getFormTextShdwXVal(); + } + + sal_Int32 SdrFormTextAttribute::getFormTextShdwYVal() const + { + return mpSdrFormTextAttribute->getFormTextShdwYVal(); + } + + XFormTextStyle SdrFormTextAttribute::getFormTextStyle() const + { + return mpSdrFormTextAttribute->getFormTextStyle(); + } + + XFormTextAdjust SdrFormTextAttribute::getFormTextAdjust() const + { + return mpSdrFormTextAttribute->getFormTextAdjust(); + } + + XFormTextShadow SdrFormTextAttribute::getFormTextShadow() const + { + return mpSdrFormTextAttribute->getFormTextShadow(); + } + + Color const & SdrFormTextAttribute::getFormTextShdwColor() const + { + return mpSdrFormTextAttribute->getFormTextShdwColor(); + } + + const SdrFormTextOutlineAttribute& SdrFormTextAttribute::getOutline() const + { + return mpSdrFormTextAttribute->getOutline(); + } + + const SdrFormTextOutlineAttribute& SdrFormTextAttribute::getShadowOutline() const + { + return mpSdrFormTextAttribute->getShadowOutline(); + } + + bool SdrFormTextAttribute::getFormTextMirror() const + { + return mpSdrFormTextAttribute->getFormTextMirror(); + } + + bool SdrFormTextAttribute::getFormTextOutline() const + { + return mpSdrFormTextAttribute->getFormTextOutline(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx b/svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx new file mode 100644 index 000000000..ec97cf044 --- /dev/null +++ b/svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx @@ -0,0 +1,141 @@ +/* -*- 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/attribute/sdrformtextoutlineattribute.hxx> +#include <drawinglayer/attribute/lineattribute.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> + + +namespace drawinglayer::attribute +{ + class ImpSdrFormTextOutlineAttribute + { + public: + // one set of attributes for FormText (FontWork) outline visualisation + LineAttribute maLineAttribute; + StrokeAttribute maStrokeAttribute; + sal_uInt8 mnTransparence; + + ImpSdrFormTextOutlineAttribute( + const LineAttribute& rLineAttribute, + const StrokeAttribute& rStrokeAttribute, + sal_uInt8 nTransparence) + : maLineAttribute(rLineAttribute), + maStrokeAttribute(rStrokeAttribute), + mnTransparence(nTransparence) + { + } + + ImpSdrFormTextOutlineAttribute() + : mnTransparence(0) + { + } + + // data read access + const LineAttribute& getLineAttribute() const { return maLineAttribute; } + const StrokeAttribute& getStrokeAttribute() const { return maStrokeAttribute; } + sal_uInt8 getTransparence() const { return mnTransparence; } + + // compare operator + bool operator==(const ImpSdrFormTextOutlineAttribute& rCandidate) const + { + return (getLineAttribute() == rCandidate.getLineAttribute() + && getStrokeAttribute() == rCandidate.getStrokeAttribute() + && getTransparence() == rCandidate.getTransparence()); + } + }; + + namespace + { + SdrFormTextOutlineAttribute::ImplType& theGlobalDefault() + { + static SdrFormTextOutlineAttribute::ImplType SINGLETON; + return SINGLETON; + } + } + + SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute( + const LineAttribute& rLineAttribute, + const StrokeAttribute& rStrokeAttribute, + sal_uInt8 nTransparence) + : mpSdrFormTextOutlineAttribute( + ImpSdrFormTextOutlineAttribute( + rLineAttribute, rStrokeAttribute, nTransparence)) + { + } + + SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute() + : mpSdrFormTextOutlineAttribute(theGlobalDefault()) + { + } + + SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute(const SdrFormTextOutlineAttribute& rCandidate) + : mpSdrFormTextOutlineAttribute(rCandidate.mpSdrFormTextOutlineAttribute) + { + } + + SdrFormTextOutlineAttribute::~SdrFormTextOutlineAttribute() + { + } + + bool SdrFormTextOutlineAttribute::isDefault() const + { + return mpSdrFormTextOutlineAttribute.same_object(theGlobalDefault()); + } + + SdrFormTextOutlineAttribute& SdrFormTextOutlineAttribute::operator=(const SdrFormTextOutlineAttribute& rCandidate) + { + mpSdrFormTextOutlineAttribute = rCandidate.mpSdrFormTextOutlineAttribute; + return *this; + } + + SdrFormTextOutlineAttribute& SdrFormTextOutlineAttribute::operator=(SdrFormTextOutlineAttribute&& rCandidate) noexcept + { + mpSdrFormTextOutlineAttribute = std::move(rCandidate.mpSdrFormTextOutlineAttribute); + return *this; + } + + bool SdrFormTextOutlineAttribute::operator==(const SdrFormTextOutlineAttribute& rCandidate) const + { + // tdf#87509 default attr is always != non-default attr, even with same values + if(rCandidate.isDefault() != isDefault()) + return false; + + return rCandidate.mpSdrFormTextOutlineAttribute == mpSdrFormTextOutlineAttribute; + } + + const LineAttribute& SdrFormTextOutlineAttribute::getLineAttribute() const + { + return mpSdrFormTextOutlineAttribute->getLineAttribute(); + } + + const StrokeAttribute& SdrFormTextOutlineAttribute::getStrokeAttribute() const + { + return mpSdrFormTextOutlineAttribute->getStrokeAttribute(); + } + + sal_uInt8 SdrFormTextOutlineAttribute::getTransparence() const + { + return mpSdrFormTextOutlineAttribute->getTransparence(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx new file mode 100644 index 000000000..72a3ef032 --- /dev/null +++ b/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx @@ -0,0 +1,75 @@ +/* -*- 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/attribute/sdrlineeffectstextattribute.hxx> + + +namespace drawinglayer::attribute +{ + SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute( + const SdrLineAttribute& rLine, + const SdrLineStartEndAttribute& rLineStartEnd, + const SdrShadowAttribute& rShadow, + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow, + sal_Int32 nSoftEdgeRadius) + : SdrEffectsTextAttribute(rShadow, rTextAttribute, rGlow, nSoftEdgeRadius), + maLine(rLine), + maLineStartEnd(rLineStartEnd) + { + } + + SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute() + { + } + + SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute(const SdrLineEffectsTextAttribute& rCandidate) + : SdrEffectsTextAttribute(rCandidate), + maLine(rCandidate.getLine()), + maLineStartEnd(rCandidate.getLineStartEnd()) + { + } + + SdrLineEffectsTextAttribute& SdrLineEffectsTextAttribute::operator=(const SdrLineEffectsTextAttribute& rCandidate) + { + SdrEffectsTextAttribute::operator=(rCandidate); + maLine = rCandidate.getLine(); + maLineStartEnd = rCandidate.getLineStartEnd(); + + return *this; + } + + bool SdrLineEffectsTextAttribute::isDefault() const + { + return(SdrEffectsTextAttribute::isDefault() + && getLine().isDefault() + && getLineStartEnd().isDefault()); + } + + bool SdrLineEffectsTextAttribute::operator==(const SdrLineEffectsTextAttribute& rCandidate) const + { + return(SdrEffectsTextAttribute::operator==(rCandidate) + && getLine() == rCandidate.getLine() + && getLineStartEnd() == rCandidate.getLineStartEnd()); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx new file mode 100644 index 000000000..442b51833 --- /dev/null +++ b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx @@ -0,0 +1,77 @@ +/* -*- 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/attribute/sdrlinefilleffectstextattribute.hxx> + + +namespace drawinglayer::attribute +{ + SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute( + const SdrLineAttribute& rLine, + const SdrFillAttribute& rFill, + const SdrLineStartEndAttribute& rLineStartEnd, + const SdrShadowAttribute& rShadow, + const FillGradientAttribute& rFillFloatTransGradient, + const SdrTextAttribute& rTextAttribute, + const SdrGlowAttribute& rGlow, + sal_Int32 nSoftEdgeRadius) + : SdrLineEffectsTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute, rGlow, nSoftEdgeRadius), + maFill(rFill), + maFillFloatTransGradient(rFillFloatTransGradient) + { + } + + SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute() + { + } + + SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute(const SdrLineFillEffectsTextAttribute& rCandidate) + : SdrLineEffectsTextAttribute(rCandidate), + maFill(rCandidate.getFill()), + maFillFloatTransGradient(rCandidate.getFillFloatTransGradient()) + { + } + + SdrLineFillEffectsTextAttribute& SdrLineFillEffectsTextAttribute::operator=(const SdrLineFillEffectsTextAttribute& rCandidate) + { + SdrLineEffectsTextAttribute::operator=(rCandidate); + maFill = rCandidate.getFill(); + maFillFloatTransGradient = rCandidate.getFillFloatTransGradient(); + + return *this; + } + + bool SdrLineFillEffectsTextAttribute::isDefault() const + { + return (SdrLineEffectsTextAttribute::isDefault() + && getFill().isDefault() + && getFillFloatTransGradient().isDefault()); + } + + bool SdrLineFillEffectsTextAttribute::operator==(const SdrLineFillEffectsTextAttribute& rCandidate) const + { + return(SdrLineEffectsTextAttribute::operator==(rCandidate) + && getFill() == rCandidate.getFill() + && getFillFloatTransGradient() == rCandidate.getFillFloatTransGradient()); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/attribute/sdrtextattribute.cxx b/svx/source/sdr/attribute/sdrtextattribute.cxx new file mode 100644 index 000000000..5c9ecb34e --- /dev/null +++ b/svx/source/sdr/attribute/sdrtextattribute.cxx @@ -0,0 +1,422 @@ +/* -*- 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/attribute/sdrtextattribute.hxx> +#include <sdr/attribute/sdrformtextattribute.hxx> +#include <svx/svdotext.hxx> +#include <editeng/outlobj.hxx> +#include <svx/sdr/properties/properties.hxx> + + +namespace drawinglayer::attribute +{ + class ImpSdrTextAttribute + { + public: + // all-text attributes. The SdrText itself and a copy + // of the OPO + const SdrText* mpSdrText; + std::shared_ptr<OutlinerParaObject> mxOutlinerParaObject; + + // Set when it's a FormText; contains all FormText attributes + SdrFormTextAttribute maSdrFormTextAttribute; + + // text distances + sal_Int32 maTextLeftDistance; + sal_Int32 maTextUpperDistance; + sal_Int32 maTextRightDistance; + sal_Int32 maTextLowerDistance; + + // #i101556# use versioning from text attributes to detect changes + sal_uInt32 maPropertiesVersion; + + // text alignments + SdrTextHorzAdjust maSdrTextHorzAdjust; + SdrTextVertAdjust maSdrTextVertAdjust; + + bool mbContour : 1; + bool mbFitToSize : 1; + bool mbAutoFit : 1; + bool mbHideContour : 1; + bool mbBlink : 1; + bool mbScroll : 1; + bool mbInEditMode : 1; + bool mbFixedCellHeight : 1; + bool mbWrongSpell : 1; + + bool mbChainable : 1; + + + public: + ImpSdrTextAttribute( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + XFormTextStyle eFormTextStyle, + sal_Int32 aTextLeftDistance, + sal_Int32 aTextUpperDistance, + sal_Int32 aTextRightDistance, + sal_Int32 aTextLowerDistance, + SdrTextHorzAdjust aSdrTextHorzAdjust, + SdrTextVertAdjust aSdrTextVertAdjust, + bool bContour, + bool bFitToSize, + bool bAutoFit, + bool bHideContour, + bool bBlink, + bool bScroll, + bool bInEditMode, + bool bFixedCellHeight, + bool bWrongSpell, + bool bChainable) + : mpSdrText(pSdrText), + mxOutlinerParaObject(std::make_shared<OutlinerParaObject>(rOutlinerParaObject)), + maTextLeftDistance(aTextLeftDistance), + maTextUpperDistance(aTextUpperDistance), + maTextRightDistance(aTextRightDistance), + maTextLowerDistance(aTextLowerDistance), + maPropertiesVersion(0), + maSdrTextHorzAdjust(aSdrTextHorzAdjust), + maSdrTextVertAdjust(aSdrTextVertAdjust), + mbContour(bContour), + mbFitToSize(bFitToSize), + mbAutoFit(bAutoFit), + mbHideContour(bHideContour), + mbBlink(bBlink), + mbScroll(bScroll), + mbInEditMode(bInEditMode), + mbFixedCellHeight(bFixedCellHeight), + mbWrongSpell(bWrongSpell), + mbChainable(bChainable) + { + if(!pSdrText) + return; + + if(XFormTextStyle::NONE != eFormTextStyle) + { + // text on path. Create FormText attribute + const SfxItemSet& rSet = pSdrText->GetItemSet(); + maSdrFormTextAttribute = SdrFormTextAttribute(rSet); + } + + // #i101556# init with version number to detect changes of single text + // attribute and/or style sheets in primitive data without having to + // copy that data locally (which would be better from principle) + maPropertiesVersion = pSdrText->GetObject().GetProperties().getVersion(); + } + + ImpSdrTextAttribute() + : mpSdrText(nullptr), + maTextLeftDistance(0), + maTextUpperDistance(0), + maTextRightDistance(0), + maTextLowerDistance(0), + maPropertiesVersion(0), + maSdrTextHorzAdjust(SDRTEXTHORZADJUST_LEFT), + maSdrTextVertAdjust(SDRTEXTVERTADJUST_TOP), + mbContour(false), + mbFitToSize(false), + mbAutoFit(false), + mbHideContour(false), + mbBlink(false), + mbScroll(false), + mbInEditMode(false), + mbFixedCellHeight(false), + mbWrongSpell(false), + mbChainable(false) + { + } + + // data read access + const SdrText& getSdrText() const + { + assert(mpSdrText && "Access to text of default version of ImpSdrTextAttribute (!)"); + return *mpSdrText; + } + + const OutlinerParaObject& getOutlinerParaObject() const + { + assert(mxOutlinerParaObject && "Access to OutlinerParaObject of default version of ImpSdrTextAttribute (!)"); + return *mxOutlinerParaObject; + } + + bool isContour() const { return mbContour; } + bool isFitToSize() const { return mbFitToSize; } + bool isAutoFit() const { return mbAutoFit; } + bool isHideContour() const { return mbHideContour; } + bool isBlink() const { return mbBlink; } + bool isScroll() const { return mbScroll; } + bool isInEditMode() const { return mbInEditMode; } + bool isFixedCellHeight() const { return mbFixedCellHeight; } + bool isChainable() const { return mbChainable; } + const SdrFormTextAttribute& getSdrFormTextAttribute() const { return maSdrFormTextAttribute; } + sal_Int32 getTextLeftDistance() const { return maTextLeftDistance; } + sal_Int32 getTextUpperDistance() const { return maTextUpperDistance; } + sal_Int32 getTextRightDistance() const { return maTextRightDistance; } + sal_Int32 getTextLowerDistance() const { return maTextLowerDistance; } + SdrTextHorzAdjust getSdrTextHorzAdjust() const { return maSdrTextHorzAdjust; } + SdrTextVertAdjust getSdrTextVertAdjust() const { return maSdrTextVertAdjust; } + + // compare operator + bool operator==(const ImpSdrTextAttribute& rCandidate) const + { + if (mxOutlinerParaObject.get() != rCandidate.mxOutlinerParaObject.get()) + { + if (mxOutlinerParaObject && rCandidate.mxOutlinerParaObject) + { + // compares OPO and it's contents, but traditionally not the RedLining + // which is not seen as model, but as temporary information + if(getOutlinerParaObject() != rCandidate.getOutlinerParaObject()) + { + return false; + } + + // #i102062# for primitive visualisation, the WrongList (SpellChecking) + // is important, too, so use isWrongListEqual since there is no WrongList + // comparison in the regular OutlinerParaObject compare (since it's + // not-persistent data) + if(!(getOutlinerParaObject().isWrongListEqual(rCandidate.getOutlinerParaObject()))) + { + return false; + } + } + else + { + // only one is zero; not equal + return false; + } + } + + return ( + getSdrFormTextAttribute() == rCandidate.getSdrFormTextAttribute() + && getTextLeftDistance() == rCandidate.getTextLeftDistance() + && getTextUpperDistance() == rCandidate.getTextUpperDistance() + && getTextRightDistance() == rCandidate.getTextRightDistance() + && getTextLowerDistance() == rCandidate.getTextLowerDistance() + && maPropertiesVersion == rCandidate.maPropertiesVersion + + && getSdrTextHorzAdjust() == rCandidate.getSdrTextHorzAdjust() + && getSdrTextVertAdjust() == rCandidate.getSdrTextVertAdjust() + + && isContour() == rCandidate.isContour() + && isFitToSize() == rCandidate.isFitToSize() + && isAutoFit() == rCandidate.isAutoFit() + && isHideContour() == rCandidate.isHideContour() + && isBlink() == rCandidate.isBlink() + && isScroll() == rCandidate.isScroll() + && isInEditMode() == rCandidate.isInEditMode() + && isFixedCellHeight() == rCandidate.isFixedCellHeight() + && mbWrongSpell == rCandidate.mbWrongSpell ); + } + }; + + namespace + { + SdrTextAttribute::ImplType& theGlobalDefault() + { + static SdrTextAttribute::ImplType SINGLETON; + return SINGLETON; + } + } + + SdrTextAttribute::SdrTextAttribute( + const SdrText& rSdrText, + const OutlinerParaObject& rOutlinerParaObject, + XFormTextStyle eFormTextStyle, + sal_Int32 aTextLeftDistance, + sal_Int32 aTextUpperDistance, + sal_Int32 aTextRightDistance, + sal_Int32 aTextLowerDistance, + SdrTextHorzAdjust aSdrTextHorzAdjust, + SdrTextVertAdjust aSdrTextVertAdjust, + bool bContour, + bool bFitToSize, + bool bAutoFit, + bool bHideContour, + bool bBlink, + bool bScroll, + bool bInEditMode, + bool bFixedCellHeight, + bool bWrongSpell, + bool bChainable) + : mpSdrTextAttribute( + ImpSdrTextAttribute( + &rSdrText, rOutlinerParaObject, eFormTextStyle, aTextLeftDistance, + aTextUpperDistance, aTextRightDistance, aTextLowerDistance, + aSdrTextHorzAdjust, aSdrTextVertAdjust, bContour, bFitToSize, bAutoFit, + bHideContour, bBlink, bScroll, bInEditMode, bFixedCellHeight, bWrongSpell, + bChainable)) + { + } + + SdrTextAttribute::SdrTextAttribute() + : mpSdrTextAttribute(theGlobalDefault()) + { + } + + SdrTextAttribute::SdrTextAttribute(const SdrTextAttribute& rCandidate) + : mpSdrTextAttribute(rCandidate.mpSdrTextAttribute) + { + } + + SdrTextAttribute::SdrTextAttribute(SdrTextAttribute&& rCandidate) noexcept + : mpSdrTextAttribute(std::move(rCandidate.mpSdrTextAttribute)) + { + } + + SdrTextAttribute::~SdrTextAttribute() + { + } + + bool SdrTextAttribute::isDefault() const + { + return mpSdrTextAttribute.same_object(theGlobalDefault()); + } + + SdrTextAttribute& SdrTextAttribute::operator=(const SdrTextAttribute& rCandidate) + { + mpSdrTextAttribute = rCandidate.mpSdrTextAttribute; + return *this; + } + + SdrTextAttribute& SdrTextAttribute::operator=(SdrTextAttribute&& rCandidate) noexcept + { + mpSdrTextAttribute = std::move(rCandidate.mpSdrTextAttribute); + return *this; + } + + bool SdrTextAttribute::operator==(const SdrTextAttribute& rCandidate) const + { + // tdf#87509 default attr is always != non-default attr, even with same values + if(rCandidate.isDefault() != isDefault()) + return false; + + return rCandidate.mpSdrTextAttribute == mpSdrTextAttribute; + } + + const SdrText& SdrTextAttribute::getSdrText() const + { + return mpSdrTextAttribute->getSdrText(); + } + + const OutlinerParaObject& SdrTextAttribute::getOutlinerParaObject() const + { + return mpSdrTextAttribute->getOutlinerParaObject(); + } + + bool SdrTextAttribute::isContour() const + { + return mpSdrTextAttribute->isContour(); + } + + bool SdrTextAttribute::isFitToSize() const + { + return mpSdrTextAttribute->isFitToSize(); + } + + bool SdrTextAttribute::isAutoFit() const + { + return mpSdrTextAttribute->isAutoFit(); + } + + bool SdrTextAttribute::isHideContour() const + { + return mpSdrTextAttribute->isHideContour(); + } + + bool SdrTextAttribute::isBlink() const + { + return mpSdrTextAttribute->isBlink(); + } + + bool SdrTextAttribute::isScroll() const + { + return mpSdrTextAttribute->isScroll(); + } + + bool SdrTextAttribute::isInEditMode() const + { + return mpSdrTextAttribute->isInEditMode(); + } + + bool SdrTextAttribute::isChainable() const + { + return mpSdrTextAttribute->isChainable(); + } + + + bool SdrTextAttribute::isFixedCellHeight() const + { + return mpSdrTextAttribute->isFixedCellHeight(); + } + + const SdrFormTextAttribute& SdrTextAttribute::getSdrFormTextAttribute() const + { + return mpSdrTextAttribute->getSdrFormTextAttribute(); + } + + sal_Int32 SdrTextAttribute::getTextLeftDistance() const + { + return mpSdrTextAttribute->getTextLeftDistance(); + } + + sal_Int32 SdrTextAttribute::getTextUpperDistance() const + { + return mpSdrTextAttribute->getTextUpperDistance(); + } + + sal_Int32 SdrTextAttribute::getTextRightDistance() const + { + return mpSdrTextAttribute->getTextRightDistance(); + } + + sal_Int32 SdrTextAttribute::getTextLowerDistance() const + { + return mpSdrTextAttribute->getTextLowerDistance(); + } + + SdrTextHorzAdjust SdrTextAttribute::getSdrTextHorzAdjust() const + { + return mpSdrTextAttribute->getSdrTextHorzAdjust(); + } + + SdrTextVertAdjust SdrTextAttribute::getSdrTextVertAdjust() const + { + return mpSdrTextAttribute->getSdrTextVertAdjust(); + } + + void SdrTextAttribute::getBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const + { + if(isBlink()) + { + getSdrText().GetObject().impGetBlinkTextTiming(rAnimList); + } + } + + void SdrTextAttribute::getScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const + { + if(isScroll()) + { + getSdrText().GetObject().impGetScrollTextTiming(rAnimList, fFrameLength, fTextLength); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/contact/displayinfo.cxx b/svx/source/sdr/contact/displayinfo.cxx new file mode 100644 index 000000000..405fb41a0 --- /dev/null +++ b/svx/source/sdr/contact/displayinfo.cxx @@ -0,0 +1,87 @@ +/* -*- 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), + mbPageProcessingActive(true), + 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::SetPageProcessingActive(bool bDoProcess) + { + if(mbPageProcessingActive != bDoProcess) + { + mbPageProcessingActive = 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 000000000..d135a2a29 --- /dev/null +++ b/svx/source/sdr/contact/objectcontact.cxx @@ -0,0 +1,217 @@ +/* -*- 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::calculateGridOffsetForViewOjectContact( + 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; +} + +// 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; +} + +// 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 000000000..f666eceb1 --- /dev/null +++ b/svx/source/sdr/contact/objectcontactofobjlistpainter.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 <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 + const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D( + basegfx::B2DHomMatrix(), + pTargetDevice->GetViewTransformation(), + aViewRange, + GetXDrawPageForSdrPage(const_cast< SdrPage* >(mpProcessedPage)), + 0.0); + 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; +} + +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 000000000..31b8b949e --- /dev/null +++ b/svx/source/sdr/contact/objectcontactofpageview.cxx @@ -0,0 +1,451 @@ +/* -*- 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()); + + if(!rRedrawArea.IsEmpty() && !comphelper::LibreOfficeKit::isActive()) + { + 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 + const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D( + basegfx::B2DHomMatrix(), + rTargetOutDev.GetViewTransformation(), + aViewRange, + GetXDrawPageForSdrPage(GetSdrPage()), + 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. + sal_Int32 nObjCount = GetSdrPage()->GetObjCount(); + for (sal_Int32 i = 0; i < nObjCount; ++i) + { + SdrObject* pObject = GetSdrPage()->GetObj(i); + 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) + { + if(nullptr != pActiveGroupList->getSdrPageFromSdrObjList()) + { + // It's a Page itself + return &(pActiveGroupList->getSdrPageFromSdrObjList()->GetViewContact()); + } + else if(pActiveGroupList->getSdrObjectFromSdrObjList()) + { + // Group object + return &(pActiveGroupList->getSdrObjectFromSdrObjList()->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; + SdrView& rView = GetPageWindow().GetPageView().GetView(); + const SvtAccessibilityOptions& rOpt = rView.getAccessibilityOptions(); + return rOpt.GetIsAllowAnimatedText(); + } + + // check if graphic animation is allowed. + bool ObjectContactOfPageView::IsGraphicAnimationAllowed() const + { + if (utl::ConfigManager::IsFuzzing()) + return true; + SdrView& rView = GetPageWindow().GetPageView().GetView(); + const SvtAccessibilityOptions& rOpt = rView.getAccessibilityOptions(); + return rOpt.GetIsAllowAnimatedGraphics(); + } + + // print? + bool ObjectContactOfPageView::isOutputToPrinter() const + { + return (OUTDEV_PRINTER == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType()); + } + + // 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; + } + + // 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 000000000..086e12cee --- /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 000000000..fb3cda6fc --- /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 000000000..bd79bc5ed --- /dev/null +++ b/svx/source/sdr/contact/viewcontact.cxx @@ -0,0 +1,295 @@ +/* -*- 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 (!)"); +} + +// 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 (!)"); + const 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(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(); +} +} + +/* 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 000000000..a504038ce --- /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(), + 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 000000000..2687aab32 --- /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 000000000..71100ae34 --- /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 + const 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, + 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 000000000..a3e7086b8 --- /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 + const 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, + 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 000000000..2951ead56 --- /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( + 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 000000000..6bae55263 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofe3dscene.cxx @@ -0,0 +1,446 @@ +/* -*- 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> + +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.getWidth()); + maObjectTransformation.set(1, 1, aRectangle.getHeight()); + 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 000000000..7a99719b8 --- /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 000000000..6428e5a66 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofgraphic.cxx @@ -0,0 +1,391 @@ +/* -*- 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( + 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( + VCLUnoHelper::CreateVCLXBitmap(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 + 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); + } + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pRectObj); + SdrObject::Free(pTemp); + } + + 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.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 000000000..18e5e07aa --- /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 000000000..20463d0fc --- /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 000000000..ffcc18fbc --- /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); + const 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(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 000000000..2e668b4dd --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx @@ -0,0 +1,190 @@ +/* -*- 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/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)); + + // 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.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0, + aObjectRange.getMinX(), aObjectRange.getMinY())); + + // calculate corner radius + double fCornerRadiusX; + double fCornerRadiusY; + drawinglayer::primitive2d::calculateRelativeCornerRadius( + rCaptionObj.GetEckenradius(), aObjectRange, fCornerRadiusX, fCornerRadiusY); + const 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, + 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 sal_uInt32 nXDist(rItemSet.Get(SDRATTR_SHADOWXDIST).GetValue()); + const sal_uInt32 nYDist(rItemSet.Get(SDRATTR_SHADOWYDIST).GetValue()); + + if(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 000000000..042d2160a --- /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.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.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 000000000..ab9587856 --- /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 + { + 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, + 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 000000000..eb04efd71 --- /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 000000000..7c713bc4b --- /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 000000000..1483b5764 --- /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 = dynamic_cast<SdrTextObj*>(&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 000000000..e03dfced8 --- /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.nShearAngle || rGeoStat.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.nShearAngle) + { + aRotMatrix.shearX(-rGeoStat.mfTanShearAngle); + } + + if(rGeoStat.nRotationAngle) + { + aRotMatrix.rotate(toRadians(36000_deg100 - rGeoStat.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 != dynamic_cast< const E3dObject* >(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.nShearAngle || rGeoStat.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.nShearAngle) + { + aTextBoxMatrix.shearX(-rGeoStat.mfTanShearAngle); + } + + if(rGeoStat.nRotationAngle) + { + aTextBoxMatrix.rotate(toRadians(36000_deg100 - rGeoStat.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 000000000..c1a732786 --- /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.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.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 000000000..44cccb319 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrpage.cxx @@ -0,0 +1,598 @@ +/* -*- 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/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 <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(aLeft, aRGBBorderColor))); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aRight, aRGBBorderColor))); + } + else + { + const basegfx::B2DPolygon aPageBorderPolygon(basegfx::utils::createPolygonFromRect(aPageBorderRange)); + rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(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())); + const 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(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(); +} + +ViewContact& ViewContactOfPageHierarchy::GetViewContact(sal_uInt32 nIndex) const +{ + SdrObject* pObj = getPage().GetObj(nIndex); + assert(pObj && "ViewContactOfPageHierarchy::GetViewContact: Corrupt SdrObjList (!)"); + return pObj->GetViewContact(); +} + +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 11; +} + +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: case 11: 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 000000000..8798d40fb --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx @@ -0,0 +1,210 @@ +/* -*- 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 <svtools/optionsdrawinglayer.hxx> +#include <svx/svdopath.hxx> +#include <svx/svdpage.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.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(); + if (nPointCount > 1) + return false; + } + + 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; + bool bIsLine( + !aUnitPolyPolygon.areControlPointsUsed() + && bPolyCountIsOne + && 2 == aUnitPolyPolygon.getB2DPolygon(0).count()); + + if(bIsLine) + { + //tdf#63955 if we have an extremely long line then clip it to a + //very generous range of -1 page width/height vs +1 page + //width/height to avoid oom and massive churn generating a huge + //polygon chain to cover the length in applyLineDashing if this + //line is dashed + const SdrPage* pPage(GetPathObj().getSdrPageFromSdrObject()); + sal_Int32 nPageWidth = pPage ? pPage->GetWidth() : 0; + sal_Int32 nPageHeight = pPage ? pPage->GetHeight() : 0; + + //But, see tdf#101187, only do this if our generous clip region + //would not over flow into a tiny clip region + if (nPageWidth < SAL_MAX_INT32/2 && nPageHeight < SAL_MAX_INT32/2 && !utl::ConfigManager::IsFuzzing()) + { + //But, see tdf#97276, tdf#126184 and tdf#98366. Don't clip too much if the + //underlying page dimension is unknown or a paste document + //where the page sizes use the odd default of 10x10 + const sal_Int32 nMaxPaperWidth = SvtOptionsDrawinglayer::GetMaximumPaperWidth() * 1000; + const sal_Int32 nMaxPaperHeight = SvtOptionsDrawinglayer::GetMaximumPaperHeight() * 1000; + nPageWidth = std::max<sal_Int32>(nPageWidth, nMaxPaperWidth); + nPageHeight = std::max<sal_Int32>(nPageHeight, nMaxPaperHeight); + basegfx::B2DRange aClipRange(-nPageWidth, -nPageHeight, + nPageWidth*2, nPageHeight*2); + + aUnitPolyPolygon = basegfx::utils::clipPolyPolygonOnRange(aUnitPolyPolygon, + aClipRange, true, true); + bPolyCountIsOne = ensureGeometry(aUnitPolyPolygon); + + // re-check that we have't been clipped out to oblivion + bIsLine = + !aUnitPolyPolygon.areControlPointsUsed() + && bPolyCountIsOne + && 2 == aUnitPolyPolygon.getB2DPolygon(0).count(); + } + } + + if(bIsLine) + { + + // special handling for single line mode (2 points) + const basegfx::B2DPolygon aSubPolygon(aUnitPolyPolygon.getB2DPolygon(0)); + const basegfx::B2DPoint aStart(aSubPolygon.getB2DPoint(0)); + const basegfx::B2DPoint aEnd(aSubPolygon.getB2DPoint(1)); + const basegfx::B2DVector aLine(aEnd - aStart); + + // #i102548# create new unit polygon for line (horizontal) + basegfx::B2DPolygon aNewPolygon; + aNewPolygon.append(basegfx::B2DPoint(0.0, 0.0)); + aNewPolygon.append(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.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.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, + aUnitPolyPolygon, + 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 000000000..6d9ed7bed --- /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.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.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 000000000..9e10d0130 --- /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 000000000..f99b24372 --- /dev/null +++ b/svx/source/sdr/contact/viewcontactofunocontrol.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 <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()) + { + // 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)); + + 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 000000000..f4087d036 --- /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 000000000..18acd5e52 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontact.cxx @@ -0,0 +1,570 @@ +/* -*- 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/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdotext.hxx> +#include <vcl/pdfwriter.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() const { 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() +{ + // 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); +} + +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 }; + } + } + + // Check if we need to embed to a StructureTagPrimitive2D, too. This + // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before + if (!xNewPrimitiveSequence.empty() && GetObjectContact().isExportTaggedPDF()) + { + if (nullptr != pSdrObj) + { + vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement); + const SdrInventor nInventor(pSdrObj->GetObjInventor()); + const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier()); + const bool bIsTextObj(nullptr != dynamic_cast<const SdrTextObj *>(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::Section; + else if (nIdentifier == SdrObjKind::Table) + eElement = vcl::PDFWriter::Table; + 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 + sal_Int32 nAnchorId(-1); + if (auto const pUserCall = pSdrObj->GetUserCall()) + { + nAnchorId = pUserCall->GetPDFAnchorStructureElementId( + *pSdrObj, *GetObjectContact().TryToGetOutputDevice()); + } + + drawinglayer::primitive2d::Primitive2DReference xReference( + new drawinglayer::primitive2d::StructureTagPrimitive2D( + eElement, + bBackground, + bImage, + std::move(xNewPrimitiveSequence), + nAnchorId)); + xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { xReference }; + } + } + } + else + { + // page backgrounds etc should be tagged as artifacts: + xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { + new drawinglayer::primitive2d::StructureTagPrimitive2D( + // lies to force silly VclMetafileProcessor2D to emit NonStructElement + vcl::PDFWriter::Division, + true, + true, + std::move(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 ressources) + 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 +{ + const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount()); + + for(sal_uInt32 a(0); a < nSubHierarchyCount; a++) + { + const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact())); + + rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, 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(0.0 == maGridOffset.getX() && 0.0 == maGridOffset.getY() && GetObjectContact().supportsGridOffsets()) + { + // create on-demand + GetObjectContact().calculateGridOffsetForViewOjectContact(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 000000000..c6d41bdc9 --- /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 000000000..ac7ad90f1 --- /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 000000000..601ec28df --- /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 000000000..8469a00c1 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofgroup.cxx @@ -0,0 +1,87 @@ +/* -*- 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; + + 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, rVisitor); + + 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, rVisitor); + } + } + + bool ViewObjectContactOfGroup::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& aLayers) const + { + SdrLayerIDSet aObjectLayers; + getSdrObject().getMergedHierarchySdrLayerIDSet(aObjectLayers); + aObjectLayers &= aLayers; + return !aObjectLayers.IsEmpty(); + } + +} // 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 000000000..f96ed115d --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx @@ -0,0 +1,132 @@ +/* -*- 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; + } + + if(!rDisplayInfo.GetPageProcessingActive()) + { + 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 000000000..e3431da91 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx @@ -0,0 +1,315 @@ +/* -*- 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 isOutputToRecordingMetaFile() const override; + virtual bool isOutputToPDFFile() 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(); + const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D( + rOriginalViewInformation.getObjectTransformation(), + rOriginalViewInformation.getViewTransformation(), + + // #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) + basegfx::B2DRange(), + + GetXDrawPageForSdrPage(pStartPage), + 0.0); // no time; page previews are not animated + 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::isOutputToRecordingMetaFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToRecordingMetaFile(); } +bool PagePrimitiveExtractor::isOutputToPDFFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPDFFile(); } +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); + const 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(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(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 000000000..777017472 --- /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 000000000..f87906240 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx @@ -0,0 +1,193 @@ +/* -*- 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::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& aLayers) const +{ + return aLayers.IsSet(getSdrObject().GetLayer()); +} + +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 000000000..66d1a6fb4 --- /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 + const 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(), + 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 000000000..fe9855f4c --- /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(!rDisplayInfo.GetPageProcessingActive()) + { + 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 000000000..5ca5a32d2 --- /dev/null +++ b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx @@ -0,0 +1,1777 @@ +/* -*- 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/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/processfactory.hxx> +#include <comphelper/scopeguard.hxx> +#include <cppuhelper/implbase.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <tools/debug.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/controlprimitive2d.hxx> + +/* + +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::uno::RuntimeException; + 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::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 ) + { + 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( const ::rtl::Reference< ViewObjectContactOfUnoControl_Impl >& _pVOCImpl ) + :m_pVOCImpl( _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 ); + + // 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; + } + + // 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() ) ); + } + + 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 000000000..c49909327 --- /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: */ diff --git a/svx/source/sdr/misc/ImageMapInfo.cxx b/svx/source/sdr/misc/ImageMapInfo.cxx new file mode 100644 index 000000000..c50fb2fcc --- /dev/null +++ b/svx/source/sdr/misc/ImageMapInfo.cxx @@ -0,0 +1,121 @@ +/* -*- 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/ImageMapInfo.hxx> + +#include <svx/svdobj.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <vcl/imapobj.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> + +SvxIMapInfo* SvxIMapInfo::GetIMapInfo(SdrObject const* pObject) +{ + assert(pObject); + + SvxIMapInfo* pIMapInfo = nullptr; + sal_uInt16 nCount = pObject->GetUserDataCount(); + + // Can we find IMap information within the user data? + for (sal_uInt16 i = 0; i < nCount; i++) + { + SdrObjUserData* pUserData = pObject->GetUserData(i); + + if ((pUserData->GetInventor() == SdrInventor::StarDrawUserData) + && (pUserData->GetId() == SVX_IMAPINFO_ID)) + pIMapInfo = static_cast<SvxIMapInfo*>(pUserData); + } + + return pIMapInfo; +} + +IMapObject* SvxIMapInfo::GetHitIMapObject(const SdrObject* pObj, const Point& rWinPoint, + const OutputDevice* pCmpWnd) +{ + SvxIMapInfo* pIMapInfo = GetIMapInfo(pObj); + IMapObject* pIMapObj = nullptr; + + if (pIMapInfo) + { + const MapMode aMap100(MapUnit::Map100thMM); + Size aGraphSize; + Point aRelPoint(rWinPoint); + ImageMap& rImageMap = const_cast<ImageMap&>(pIMapInfo->GetImageMap()); + tools::Rectangle& rRect = const_cast<tools::Rectangle&>(pObj->GetLogicRect()); + + if (pCmpWnd) + { + MapMode aWndMode = pCmpWnd->GetMapMode(); + aRelPoint = pCmpWnd->LogicToLogic(rWinPoint, &aWndMode, &aMap100); + rRect = pCmpWnd->LogicToLogic(pObj->GetLogicRect(), &aWndMode, &aMap100); + } + + bool bObjSupported = false; + + // execute HitTest + if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(pObj)) // simple graphics object + { + const GeoStat& rGeo = pGrafObj->GetGeoStat(); + std::unique_ptr<SdrGrafObjGeoData> pGeoData( + static_cast<SdrGrafObjGeoData*>(pGrafObj->GetGeoData().release())); + + // Undo rotation + if (rGeo.nRotationAngle) + RotatePoint(aRelPoint, rRect.TopLeft(), -rGeo.mfSinRotationAngle, + rGeo.mfCosRotationAngle); + + // Undo mirroring + if (pGeoData->bMirrored) + aRelPoint.setX(rRect.Right() + rRect.Left() - aRelPoint.X()); + + // Undo shearing + if (rGeo.nShearAngle) + ShearPoint(aRelPoint, rRect.TopLeft(), -rGeo.mfTanShearAngle); + + if (pGrafObj->GetGrafPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aGraphSize = Application::GetDefaultDevice()->PixelToLogic( + pGrafObj->GetGrafPrefSize(), aMap100); + else + aGraphSize = OutputDevice::LogicToLogic(pGrafObj->GetGrafPrefSize(), + pGrafObj->GetGrafPrefMapMode(), aMap100); + + bObjSupported = true; + } + else if (auto pOleObj = dynamic_cast<const SdrOle2Obj*>(pObj)) // OLE object + { + aGraphSize = pOleObj->GetOrigObjSize(); + bObjSupported = true; + } + + // Everything worked out well, thus execute HitTest + if (bObjSupported) + { + // Calculate relative position of mouse cursor + aRelPoint -= rRect.TopLeft(); + pIMapObj = rImageMap.GetHitIMapObject(aGraphSize, rRect.GetSize(), aRelPoint); + + // We don't care about deactivated objects + if (pIMapObj && !pIMapObj->IsActive()) + pIMapObj = nullptr; + } + } + + return pIMapObj; +} diff --git a/svx/source/sdr/overlay/overlayanimatedbitmapex.cxx b/svx/source/sdr/overlay/overlayanimatedbitmapex.cxx new file mode 100644 index 000000000..967e9665b --- /dev/null +++ b/svx/source/sdr/overlay/overlayanimatedbitmapex.cxx @@ -0,0 +1,113 @@ +/* -*- 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/overlay/overlayanimatedbitmapex.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <sdr/overlay/overlaytools.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayAnimatedBitmapEx::createOverlayObjectPrimitive2DSequence() + { + if(mbOverlayState) + { + const drawinglayer::primitive2d::Primitive2DReference aPrimitive( + new drawinglayer::primitive2d::OverlayBitmapExPrimitive( + maBitmapEx1, + getBasePosition(), + mnCenterX1, + mnCenterY1, + getShearX(), + getRotation())); + + return drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }; + } + else + { + const drawinglayer::primitive2d::Primitive2DReference aPrimitive( + new drawinglayer::primitive2d::OverlayBitmapExPrimitive( + maBitmapEx2, + getBasePosition(), + mnCenterX2, + mnCenterY2, + getShearX(), + getRotation())); + + return drawinglayer::primitive2d::Primitive2DContainer { aPrimitive }; + } + } + + OverlayAnimatedBitmapEx::OverlayAnimatedBitmapEx( + const basegfx::B2DPoint& rBasePos, + const BitmapEx& rBitmapEx1, + const BitmapEx& rBitmapEx2, + sal_uInt64 nBlinkTime, + sal_uInt16 nCenX1, + sal_uInt16 nCenY1, + sal_uInt16 nCenX2, + sal_uInt16 nCenY2, + double fShearX, + double fRotation) + : OverlayObjectWithBasePosition(rBasePos, COL_WHITE), + maBitmapEx1(rBitmapEx1), + maBitmapEx2(rBitmapEx2), + mnCenterX1(nCenX1), mnCenterY1(nCenY1), + mnCenterX2(nCenX2), mnCenterY2(nCenY2), + mnBlinkTime(impCheckBlinkTimeValueRange(nBlinkTime)), + mfShearX(fShearX), + mfRotation(fRotation), + mbOverlayState(false) + { + // set AllowsAnimation flag to mark this object as animation capable + mbAllowsAnimation = true; + } + + OverlayAnimatedBitmapEx::~OverlayAnimatedBitmapEx() + { + } + + void OverlayAnimatedBitmapEx::Trigger(sal_uInt32 nTime) + { + if(!getOverlayManager()) + return; + + // #i53216# produce event after nTime + x + SetTime(nTime + mnBlinkTime); + + // switch state + if(mbOverlayState) + { + mbOverlayState = false; + } + else + { + mbOverlayState = true; + } + + // re-insert me as event + getOverlayManager()->InsertEvent(*this); + + // register change (after change) + objectChange(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaybitmapex.cxx b/svx/source/sdr/overlay/overlaybitmapex.cxx new file mode 100644 index 000000000..54ccf788b --- /dev/null +++ b/svx/source/sdr/overlay/overlaybitmapex.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 <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <svx/sdr/overlay/overlaybitmapex.hxx> +#include <sdr/overlay/overlaytools.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayBitmapEx::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::OverlayBitmapExPrimitive( + maBitmapEx, + getBasePosition(), + mnCenterX, + mnCenterY, + mfShearX, + mfRotation)); + + if(basegfx::fTools::more(mfAlpha, 0.0)) + { + drawinglayer::primitive2d::Primitive2DContainer aNewTransPrimitiveVector { aReference }; + aReference = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aNewTransPrimitiveVector), mfAlpha)); + } + + return drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + OverlayBitmapEx::OverlayBitmapEx( + const basegfx::B2DPoint& rBasePos, + const BitmapEx& rBitmapEx, + sal_uInt16 nCenX, + sal_uInt16 nCenY, + double fAlpha, + double fShearX, + double fRotation) + : OverlayObjectWithBasePosition(rBasePos, COL_WHITE), + maBitmapEx(rBitmapEx), + mnCenterX(nCenX), + mnCenterY(nCenY), + mfAlpha(fAlpha), + mfShearX(fShearX), + mfRotation(fRotation) + { + } + + OverlayBitmapEx::~OverlayBitmapEx() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaycrosshair.cxx b/svx/source/sdr/overlay/overlaycrosshair.cxx new file mode 100644 index 000000000..942534d68 --- /dev/null +++ b/svx/source/sdr/overlay/overlaycrosshair.cxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sdr/overlay/overlaycrosshair.hxx> +#include <sdr/overlay/overlaytools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayCrosshairStriped::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(getOverlayManager()) + { + const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor()); + const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor()); + const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel()); + + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::OverlayCrosshairPrimitive( + getBasePosition(), + aRGBColorA, + aRGBColorB, + fStripeLengthPixel)); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + return aRetval; + } + + void OverlayCrosshairStriped::stripeDefinitionHasChanged() + { + // react on OverlayManager's stripe definition change + objectChange(); + } + + OverlayCrosshairStriped::OverlayCrosshairStriped(const basegfx::B2DPoint& rBasePos) + : OverlayObjectWithBasePosition(rBasePos, COL_BLACK) + { + } + + OverlayCrosshairStriped::~OverlayCrosshairStriped() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayhandle.cxx b/svx/source/sdr/overlay/overlayhandle.cxx new file mode 100644 index 000000000..c94da8e07 --- /dev/null +++ b/svx/source/sdr/overlay/overlayhandle.cxx @@ -0,0 +1,55 @@ +/* -*- 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/overlay/overlayhandle.hxx> +#include <sdr/overlay/overlaytools.hxx> + +namespace sdr::overlay { + +using namespace drawinglayer; +using namespace basegfx; + +primitive2d::Primitive2DContainer OverlayHandle::createOverlayObjectPrimitive2DSequence() +{ + basegfx::BColor aStrokeColor = maStrokeColor.getBColor(); + basegfx::BColor aFillColor = getBaseColor().getBColor(); + + const primitive2d::Primitive2DReference aReference( + new primitive2d::OverlayStaticRectanglePrimitive(maBasePosition, maSize, aStrokeColor, aFillColor, 0.3f, 0.0f)); + + return primitive2d::Primitive2DContainer { aReference }; +} + +OverlayHandle::OverlayHandle(const B2DPoint& rBasePos, + const B2DSize& rSize, + Color const & rStrokeColor, + Color const & rFillColor) + : OverlayObjectWithBasePosition(rBasePos, rFillColor) + , maSize(rSize) + , maStrokeColor(rStrokeColor) +{ +} + +OverlayHandle::~OverlayHandle() +{ +} + +} // end of namespace sdr::overlay + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayhelpline.cxx b/svx/source/sdr/overlay/overlayhelpline.cxx new file mode 100644 index 000000000..9955122bb --- /dev/null +++ b/svx/source/sdr/overlay/overlayhelpline.cxx @@ -0,0 +1,74 @@ +/* -*- 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/overlay/overlayhelpline.hxx> +#include <sdr/overlay/overlaytools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayHelplineStriped::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(getOverlayManager()) + { + const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor()); + const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor()); + const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel()); + const drawinglayer::primitive2d::HelplineStyle aStyle( + SdrHelpLineKind::Point == getKind() ? drawinglayer::primitive2d::HELPLINESTYLE_POINT : + SdrHelpLineKind::Vertical == getKind() ? drawinglayer::primitive2d::HELPLINESTYLE_VERTICAL : + drawinglayer::primitive2d::HELPLINESTYLE_HORIZONTAL); + + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::OverlayHelplineStripedPrimitive( + getBasePosition(), + aStyle, + aRGBColorA, + aRGBColorB, + fStripeLengthPixel)); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + return aRetval; + } + + void OverlayHelplineStriped::stripeDefinitionHasChanged() + { + // react on OverlayManager's stripe definition change + objectChange(); + } + + OverlayHelplineStriped::OverlayHelplineStriped( + const basegfx::B2DPoint& rBasePos, + SdrHelpLineKind eNewKind) + : OverlayObjectWithBasePosition(rBasePos, COL_BLACK), + meKind(eNewKind) + { + } + + OverlayHelplineStriped::~OverlayHelplineStriped() + { + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayline.cxx b/svx/source/sdr/overlay/overlayline.cxx new file mode 100644 index 000000000..30c08f026 --- /dev/null +++ b/svx/source/sdr/overlay/overlayline.cxx @@ -0,0 +1,74 @@ +/* -*- 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/overlay/overlayline.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayLineStriped::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(getOverlayManager()) + { + const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor()); + const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor()); + const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel()); + basegfx::B2DPolygon aLine; + + aLine.append(getBasePosition()); + aLine.append(getSecondPosition()); + + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::PolygonMarkerPrimitive2D( + aLine, + aRGBColorA, + aRGBColorB, + fStripeLengthPixel)); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + return aRetval; + } + + void OverlayLineStriped::stripeDefinitionHasChanged() + { + // react on OverlayManager's stripe definition change + objectChange(); + } + + OverlayLineStriped::OverlayLineStriped( + const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPos) + : OverlayObjectWithBasePosition(rBasePos, COL_BLACK), + maSecondPosition(rSecondPos) + { + } + + OverlayLineStriped::~OverlayLineStriped() + { + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaymanager.cxx b/svx/source/sdr/overlay/overlaymanager.cxx new file mode 100644 index 000000000..3cbd821fb --- /dev/null +++ b/svx/source/sdr/overlay/overlaymanager.cxx @@ -0,0 +1,346 @@ +/* -*- 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/overlay/overlaymanager.hxx> +#include <basegfx/range/b2drange.hxx> +#include <comphelper/propertyvalue.hxx> +#include <tools/gen.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/outdev.hxx> +#include <vcl/window.hxx> +#include <svx/sdr/overlay/overlayobject.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <osl/diagnose.h> +#include <memory> + + +using namespace com::sun::star; + + +namespace sdr::overlay +{ + void OverlayManager::ImpDrawMembers(const basegfx::B2DRange& rRange, OutputDevice& rDestinationDevice) const + { + const sal_uInt32 nSize(maOverlayObjects.size()); + + if(!nSize) + return; + + const AntialiasingFlags nOriginalAA(rDestinationDevice.GetAntialiasing()); + const bool bIsAntiAliasing(SvtOptionsDrawinglayer::IsAntiAliasing()); + + // create processor + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice( + rDestinationDevice, + getCurrentViewInformation2D())); + + for(const auto& rpOverlayObject : maOverlayObjects) + { + OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)"); + const OverlayObject& rCandidate = *rpOverlayObject; + + if(rCandidate.isVisible()) + { + const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rCandidate.getOverlayObjectPrimitive2DSequence(); + + if(!rSequence.empty()) + { + if(rRange.overlaps(rCandidate.getBaseRange())) + { + if(bIsAntiAliasing && rCandidate.allowsAntiAliase()) + { + rDestinationDevice.SetAntialiasing(nOriginalAA | AntialiasingFlags::Enable); + } + else + { + rDestinationDevice.SetAntialiasing(nOriginalAA & ~AntialiasingFlags::Enable); + } + + pProcessor->process(rSequence); + } + } + } + } + + pProcessor.reset(); + + // restore AA settings + rDestinationDevice.SetAntialiasing(nOriginalAA); + } + + void OverlayManager::ImpStripeDefinitionChanged() + { + const sal_uInt32 nSize(maOverlayObjects.size()); + + if(nSize) + { + for(const auto& rpOverlayObject : maOverlayObjects) + { + OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)"); + OverlayObject& rCandidate = *rpOverlayObject; + rCandidate.stripeDefinitionHasChanged(); + } + } + } + + double OverlayManager::getDiscreteOne() const + { + if(basegfx::fTools::equalZero(mfDiscreteOne)) + { + const basegfx::B2DVector aDiscreteInLogic(getOutputDevice().GetInverseViewTransformation() * basegfx::B2DVector(1.0, 0.0)); + const_cast< OverlayManager* >(this)->mfDiscreteOne = aDiscreteInLogic.getLength(); + } + + return mfDiscreteOne; + } + + OverlayManager::OverlayManager(OutputDevice& rOutputDevice) + : mrOutputDevice(rOutputDevice), + maStripeColorA(COL_BLACK), + maStripeColorB(COL_WHITE), + mnStripeLengthPixel(5), + mfDiscreteOne(0.0) + { + // set Property 'ReducedDisplayQuality' to true to allow simpler interaction + // visualisations + uno::Sequence< beans::PropertyValue > xProperties{ + comphelper::makePropertyValue("ReducedDisplayQuality", true) + }; + maViewInformation2D = drawinglayer::geometry::createViewInformation2D(xProperties); + } + + rtl::Reference<OverlayManager> OverlayManager::create(OutputDevice& rOutputDevice) + { + return rtl::Reference<OverlayManager>(new OverlayManager(rOutputDevice)); + } + + drawinglayer::geometry::ViewInformation2D const & OverlayManager::getCurrentViewInformation2D() const + { + if(getOutputDevice().GetViewTransformation() != maViewTransformation) + { + basegfx::B2DRange aViewRange(maViewInformation2D.getViewport()); + + if(OUTDEV_WINDOW == getOutputDevice().GetOutDevType()) + { + const Size aOutputSizePixel(getOutputDevice().GetOutputSizePixel()); + + // only set when we *have* an output size, else let aViewRange + // stay on empty + if(aOutputSizePixel.Width() && aOutputSizePixel.Height()) + { + aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight()); + aViewRange.transform(getOutputDevice().GetInverseViewTransformation()); + } + } + + OverlayManager* pThis = const_cast< OverlayManager* >(this); + + pThis->maViewTransformation = getOutputDevice().GetViewTransformation(); + pThis->maViewInformation2D = drawinglayer::geometry::ViewInformation2D( + maViewInformation2D.getObjectTransformation(), + maViewTransformation, + aViewRange, + maViewInformation2D.getVisualizedPage(), + maViewInformation2D.getViewTime()); + pThis->mfDiscreteOne = 0.0; + } + + return maViewInformation2D; + } + + void OverlayManager::impApplyRemoveActions(OverlayObject& rTarget) + { + // handle evtl. animation + if(rTarget.allowsAnimation()) + { + // remove from event chain + RemoveEvent(&rTarget); + } + + // make invisible + invalidateRange(rTarget.getBaseRange()); + + // clear manager + rTarget.mpOverlayManager = nullptr; + } + + void OverlayManager::impApplyAddActions(OverlayObject& rTarget) + { + // set manager + rTarget.mpOverlayManager = this; + + // make visible + invalidateRange(rTarget.getBaseRange()); + + // handle evtl. animation + if(rTarget.allowsAnimation()) + { + // Trigger at current time to get alive. This will do the + // object-specific next time calculation and hand over adding + // again to the scheduler to the animated object, too. This works for + // a paused or non-paused animator. + rTarget.Trigger(GetTime()); + } + } + + OverlayManager::~OverlayManager() + { + // The OverlayManager is not the owner of the OverlayObjects + // and thus will not delete them, but remove them. Profit here + // from knowing that all will be removed + const sal_uInt32 nSize(maOverlayObjects.size()); + + if(nSize) + { + for(const auto& rpOverlayObject : maOverlayObjects) + { + OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)"); + OverlayObject& rCandidate = *rpOverlayObject; + impApplyRemoveActions(rCandidate); + } + + // erase vector + maOverlayObjects.clear(); + } + } + + void OverlayManager::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const + { + if(rRegion.IsEmpty() || maOverlayObjects.empty()) + return; + + // check for changed MapModes. That may influence the + // logical size of pixel based OverlayObjects (like BitmapHandles) + //ImpCheckMapModeChange(); + + // paint members + const tools::Rectangle aRegionBoundRect(rRegion.GetBoundRect()); + const basegfx::B2DRange aRegionRange = vcl::unotools::b2DRectangleFromRectangle(aRegionBoundRect); + + OutputDevice& rTarget = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice(); + ImpDrawMembers(aRegionRange, rTarget); + } + + void OverlayManager::flush() + { + // default has nothing to do + } + + void OverlayManager::add(OverlayObject& rOverlayObject) + { + OSL_ENSURE(nullptr == rOverlayObject.mpOverlayManager, "OverlayObject is added twice to an OverlayManager (!)"); + + // add to the end of chain to preserve display order in paint + maOverlayObjects.push_back(&rOverlayObject); + + // execute add actions + impApplyAddActions(rOverlayObject); + } + + void OverlayManager::remove(OverlayObject& rOverlayObject) + { + OSL_ENSURE(rOverlayObject.mpOverlayManager == this, "OverlayObject is removed from wrong OverlayManager (!)"); + + // execute remove actions + impApplyRemoveActions(rOverlayObject); + + // remove from vector + const OverlayObjectVector::iterator aFindResult = ::std::find(maOverlayObjects.begin(), maOverlayObjects.end(), &rOverlayObject); + const bool bFound(aFindResult != maOverlayObjects.end()); + OSL_ENSURE(bFound, "OverlayObject NOT found at OverlayManager (!)"); + + if(bFound) + { + maOverlayObjects.erase(aFindResult); + } + } + + tools::Rectangle OverlayManager::RangeToInvalidateRectangle(const basegfx::B2DRange& rRange) const + { + if (rRange.isEmpty()) { + return {}; + } + if (SvtOptionsDrawinglayer::IsAntiAliasing()) + { + // assume AA needs one pixel more and invalidate one pixel more + const double fDiscreteOne(getDiscreteOne()); + const tools::Rectangle aInvalidateRectangle( + static_cast<tools::Long>(floor(rRange.getMinX() - fDiscreteOne)), + static_cast<tools::Long>(floor(rRange.getMinY() - fDiscreteOne)), + static_cast<tools::Long>(ceil(rRange.getMaxX() + fDiscreteOne)), + static_cast<tools::Long>(ceil(rRange.getMaxY() + fDiscreteOne))); + return aInvalidateRectangle; + } + else + { + // #i77674# transform to rectangle. Use floor/ceil to get all covered + // discrete pixels, see #i75163# and OverlayManagerBuffered::invalidateRange + const tools::Rectangle aInvalidateRectangle( + static_cast<sal_Int32>(floor(rRange.getMinX())), static_cast<sal_Int32>(floor(rRange.getMinY())), + static_cast<sal_Int32>(ceil(rRange.getMaxX())), static_cast<sal_Int32>(ceil(rRange.getMaxY()))); + return aInvalidateRectangle; + } + } + + void OverlayManager::invalidateRange(const basegfx::B2DRange& rRange) + { + if (OUTDEV_WINDOW == getOutputDevice().GetOutDevType()) + { + tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange)); + // simply invalidate + getOutputDevice().GetOwnerWindow()->Invalidate(aInvalidateRectangle, InvalidateFlags::NoErase); + } + } + + // stripe support ColA + void OverlayManager::setStripeColorA(Color aNew) + { + if(aNew != maStripeColorA) + { + maStripeColorA = aNew; + ImpStripeDefinitionChanged(); + } + } + + // stripe support ColB + void OverlayManager::setStripeColorB(Color aNew) + { + if(aNew != maStripeColorB) + { + maStripeColorB = aNew; + ImpStripeDefinitionChanged(); + } + } + + // stripe support StripeLengthPixel + void OverlayManager::setStripeLengthPixel(sal_uInt32 nNew) + { + if(nNew != mnStripeLengthPixel) + { + mnStripeLengthPixel = nNew; + ImpStripeDefinitionChanged(); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaymanagerbuffered.cxx b/svx/source/sdr/overlay/overlaymanagerbuffered.cxx new file mode 100644 index 000000000..ae37c2ce4 --- /dev/null +++ b/svx/source/sdr/overlay/overlaymanagerbuffered.cxx @@ -0,0 +1,441 @@ +/* -*- 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/overlay/overlaymanagerbuffered.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <vcl/outdev.hxx> +#include <basegfx/range/b2drange.hxx> +#include <vcl/window.hxx> +#include <tools/fract.hxx> +#include <vcl/cursor.hxx> +#include <svtools/optionsdrawinglayer.hxx> + + +namespace sdr::overlay +{ + void OverlayManagerBuffered::ImpPrepareBufferDevice() + { + // compare size of mpBufferDevice with size of visible area + if(mpBufferDevice->GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel()) + { + // set new buffer size, copy as much content as possible (use bool parameter for vcl). + // Newly uncovered regions will be repainted. + mpBufferDevice->SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false); + } + + // compare the MapModes for zoom/scroll changes + if(mpBufferDevice->GetMapMode() != getOutputDevice().GetMapMode()) + { + const bool bZoomed( + mpBufferDevice->GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX() + || mpBufferDevice->GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY()); + + if(!bZoomed) + { + const Point& rOriginOld = mpBufferDevice->GetMapMode().GetOrigin(); + const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin(); + const bool bScrolled(rOriginOld != rOriginNew); + + + if(bScrolled) + { + // get pixel bounds (tdf#149322 do subtraction in logic units before converting result back to pixel) + const Point aLogicOriginDiff(rOriginNew - rOriginOld); + const Size aPixelOriginDiff(mpBufferDevice->LogicToPixel(Size(aLogicOriginDiff.X(), aLogicOriginDiff.Y()))); + const Point aDestinationOffsetPixel(aPixelOriginDiff.Width(), aPixelOriginDiff.Height()); + const Size aOutputSizePixel(mpBufferDevice->GetOutputSizePixel()); + + // remember and switch off MapMode + const bool bMapModeWasEnabled(mpBufferDevice->IsMapModeEnabled()); + mpBufferDevice->EnableMapMode(false); + + // scroll internally buffered stuff + mpBufferDevice->DrawOutDev( + aDestinationOffsetPixel, aOutputSizePixel, // destination + Point(), aOutputSizePixel); // source + + // restore MapMode + mpBufferDevice->EnableMapMode(bMapModeWasEnabled); + + // scroll remembered region, too. + if(!maBufferRememberedRangePixel.isEmpty()) + { + const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y()); + const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel); + const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel); + maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum); + } + } + } + + // copy new MapMode + mpBufferDevice->SetMapMode(getOutputDevice().GetMapMode()); + } + + // #i29186# + mpBufferDevice->SetDrawMode(getOutputDevice().GetDrawMode()); + mpBufferDevice->SetSettings(getOutputDevice().GetSettings()); + mpBufferDevice->SetAntialiasing(getOutputDevice().GetAntialiasing()); + } + + void OverlayManagerBuffered::ImpRestoreBackground() const + { + const tools::Rectangle aRegionRectanglePixel( + maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), + maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); + const vcl::Region aRegionPixel(aRegionRectanglePixel); + + ImpRestoreBackground(aRegionPixel); + } + + void OverlayManagerBuffered::ImpRestoreBackground(const vcl::Region& rRegionPixel) const + { + // MapModes off + const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled()); + const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled()); + getOutputDevice().EnableMapMode(false); + const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(false); + + // local region + RectangleVector aRectangles; + rRegionPixel.GetRegionRectangles(aRectangles); + + for(const auto& rRect : aRectangles) + { + // restore the area + const Point aTopLeft(rRect.TopLeft()); + const Size aSize(rRect.GetSize()); + + getOutputDevice().DrawOutDev( + aTopLeft, aSize, // destination + aTopLeft, aSize, // source + *mpBufferDevice); + } + + // restore MapModes + getOutputDevice().EnableMapMode(bMapModeWasEnabledDest); + const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource); + } + + void OverlayManagerBuffered::ImpSaveBackground(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) + { + // prepare source + OutputDevice& rSource = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice(); + + // Ensure buffer is valid + ImpPrepareBufferDevice(); + + // build region which needs to be copied + vcl::Region aRegion(rSource.LogicToPixel(rRegion)); + + // limit to PaintRegion if it's a window. This will be evtl. the expanded one, + // but always the exact redraw area + if(OUTDEV_WINDOW == rSource.GetOutDevType()) + { + vcl::Window& rWindow = *rSource.GetOwnerWindow(); + vcl::Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion()); + aRegion.Intersect(aPaintRegionPixel); + + // #i72754# Make sure content is completely rendered, the window + // will be used as source of a DrawOutDev soon + rWindow.GetOutDev()->Flush(); + } + + // also limit to buffer size + const tools::Rectangle aBufferDeviceRectanglePixel(Point(), mpBufferDevice->GetOutputSizePixel()); + aRegion.Intersect(aBufferDeviceRectanglePixel); + + // MapModes off + const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled()); + const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled()); + rSource.EnableMapMode(false); + mpBufferDevice->EnableMapMode(false); + + // prepare to iterate over the rectangles from the region in pixels + RectangleVector aRectangles; + aRegion.GetRegionRectangles(aRectangles); + + for(const auto& rRect : aRectangles) + { + // for each rectangle, save the area + const Point aTopLeft(rRect.TopLeft()); + const Size aSize(rRect.GetSize()); + + mpBufferDevice->DrawOutDev( + aTopLeft, aSize, // destination + aTopLeft, aSize, // source + rSource); + } + + // restore MapModes + rSource.EnableMapMode(bMapModeWasEnabledDest); + mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource); + } + + IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer*, void) + { + //Resolves: fdo#46728 ensure this exists until end of scope + rtl::Reference<OverlayManager> xKeepAlive(this); + + // stop timer + maBufferIdle.Stop(); + + if(maBufferRememberedRangePixel.isEmpty()) + return; + + // logic size for impDrawMember call + basegfx::B2DRange aBufferRememberedRangeLogic( + maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), + maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); + aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation()); + + // prepare cursor handling + const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType()); + bool bCursorWasEnabled(false); + + // #i80730# switch off VCL cursor during overlay refresh + if(bTargetIsWindow) + { + vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow(); + vcl::Cursor* pCursor = rWindow.GetCursor(); + + if(pCursor && pCursor->IsVisible()) + { + pCursor->Hide(); + bCursorWasEnabled = true; + } + } + + // refresh with prerendering + { + // #i73602# ensure valid and sized mpOutputBufferDevice + const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel()); + const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel()); + + if(aDestinationSizePixel != aOutputBufferSizePixel) + { + mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel); + } + + mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode()); + mpOutputBufferDevice->EnableMapMode(false); + mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode()); + mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings()); + mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing()); + + // calculate sizes + tools::Rectangle aRegionRectanglePixel( + maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(), + maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY()); + + // truncate aRegionRectanglePixel to destination pixel size, more does + // not need to be prepared since destination is a buffer for a window. So, + // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel() + if(aRegionRectanglePixel.Left() < 0) + { + aRegionRectanglePixel.SetLeft( 0 ); + } + + if(aRegionRectanglePixel.Top() < 0) + { + aRegionRectanglePixel.SetTop( 0 ); + } + + if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth()) + { + aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() ); + } + + if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight()) + { + aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() ); + } + + // get sizes + const Point aTopLeft(aRegionRectanglePixel.TopLeft()); + const Size aSize(aRegionRectanglePixel.GetSize()); + + { + const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled()); + mpBufferDevice->EnableMapMode(false); + + mpOutputBufferDevice->DrawOutDev( + aTopLeft, aSize, // destination + aTopLeft, aSize, // source + *mpBufferDevice); + + // restore MapModes + mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest); + } + + // paint overlay content for remembered region, use + // method from base class directly + mpOutputBufferDevice->EnableMapMode(); + OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice); + mpOutputBufferDevice->EnableMapMode(false); + + // copy to output + { + const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled()); + getOutputDevice().EnableMapMode(false); + + getOutputDevice().DrawOutDev( + aTopLeft, aSize, // destination + aTopLeft, aSize, // source + *mpOutputBufferDevice); + + // debug + /*getOutputDevice().SetLineCOL_RED); + getOutputDevice().SetFillColor(); + getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/ + + // restore MapModes + getOutputDevice().EnableMapMode(bMapModeWasEnabledDest); + } + } + + // VCL hack for transparent child windows + // Problem is e.g. a radiobutton form control in life mode. The used window + // is a transparence vcl childwindow. This flag only allows the parent window to + // paint into the child windows area, but there is no mechanism which takes + // care for a repaint of the child window. A transparent child window is NOT + // a window which always keeps it's content consistent over the parent, but it's + // more like just a paint flag for the parent. + // To get the update, the windows in question are updated manually here. + if(bTargetIsWindow) + { + vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow(); + + const tools::Rectangle aRegionRectanglePixel( + maBufferRememberedRangePixel.getMinX(), + maBufferRememberedRangePixel.getMinY(), + maBufferRememberedRangePixel.getMaxX(), + maBufferRememberedRangePixel.getMaxY()); + PaintTransparentChildren(rWindow, aRegionRectanglePixel); + } + + // #i80730# restore visibility of VCL cursor + if(bCursorWasEnabled) + { + vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow(); + vcl::Cursor* pCursor = rWindow.GetCursor(); + + if(pCursor) + { + // check if cursor still exists. It may have been deleted from someone + pCursor->Show(); + } + } + + // forget remembered Region + maBufferRememberedRangePixel.reset(); + } + + OverlayManagerBuffered::OverlayManagerBuffered( + OutputDevice& rOutputDevice) + : OverlayManager(rOutputDevice), + mpBufferDevice(VclPtr<VirtualDevice>::Create()), + mpOutputBufferDevice(VclPtr<VirtualDevice>::Create()), + maBufferIdle( "sdr::overlay::OverlayManagerBuffered maBufferIdle" ) + { + // Init timer + maBufferIdle.SetPriority( TaskPriority::POST_PAINT ); + maBufferIdle.SetInvokeHandler(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler)); + } + + rtl::Reference<OverlayManager> OverlayManagerBuffered::create( + OutputDevice& rOutputDevice) + { + return rtl::Reference<OverlayManager>(new OverlayManagerBuffered(rOutputDevice)); + } + + OverlayManagerBuffered::~OverlayManagerBuffered() + { + // Clear timer + maBufferIdle.Stop(); + + if(!maBufferRememberedRangePixel.isEmpty()) + { + // Restore all rectangles for remembered region from buffer + ImpRestoreBackground(); + } + } + + void OverlayManagerBuffered::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const + { + if(!rRegion.IsEmpty()) + { + // save new background + const_cast<OverlayManagerBuffered*>(this)->ImpSaveBackground(rRegion, pPreRenderDevice); + } + + // call parent + OverlayManager::completeRedraw(rRegion, pPreRenderDevice); + } + + void OverlayManagerBuffered::flush() + { + // call timer handler direct + ImpBufferTimerHandler(nullptr); + } + + void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange) + { + if(rRange.isEmpty()) + return; + + // buffered output, do not invalidate but use the timer + // to trigger a timer event for refresh + maBufferIdle.Start(); + + // add the discrete range to the remembered region + // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even + // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel + // since it just transforms the top left and bottom right points equally without taking + // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated + // to an also empty discrete pixel rectangle - what is wrong. + basegfx::B2DRange aDiscreteRange(rRange); + aDiscreteRange.transform(getOutputDevice().GetViewTransformation()); + + if(SvtOptionsDrawinglayer::IsAntiAliasing()) + { + // assume AA needs one pixel more and invalidate one pixel more + const double fDiscreteOne(getDiscreteOne()); + const basegfx::B2IPoint aTopLeft( + static_cast<sal_Int32>(floor(aDiscreteRange.getMinX() - fDiscreteOne)), + static_cast<sal_Int32>(floor(aDiscreteRange.getMinY() - fDiscreteOne))); + const basegfx::B2IPoint aBottomRight( + static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX() + fDiscreteOne)), + static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY() + fDiscreteOne))); + + maBufferRememberedRangePixel.expand(aTopLeft); + maBufferRememberedRangePixel.expand(aBottomRight); + } + else + { + const basegfx::B2IPoint aTopLeft(static_cast<sal_Int32>(floor(aDiscreteRange.getMinX())), static_cast<sal_Int32>(floor(aDiscreteRange.getMinY()))); + const basegfx::B2IPoint aBottomRight(static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX())), static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY()))); + + maBufferRememberedRangePixel.expand(aTopLeft); + maBufferRememberedRangePixel.expand(aBottomRight); + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayobject.cxx b/svx/source/sdr/overlay/overlayobject.cxx new file mode 100644 index 000000000..4fd227977 --- /dev/null +++ b/svx/source/sdr/overlay/overlayobject.cxx @@ -0,0 +1,228 @@ +/* -*- 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/overlay/overlayobject.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/transformprimitive2d.hxx> +#include <osl/diagnose.h> + +namespace sdr::overlay +{ + void OverlayObject::objectChange() + { + const basegfx::B2DRange aPreviousRange(maBaseRange); + maBaseRange.reset(); + resetPrimitive2DSequence(); +// setPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DContainer()); + + if(getOverlayManager() && !aPreviousRange.isEmpty()) + { + getOverlayManager()->invalidateRange(aPreviousRange); + } + + const basegfx::B2DRange& rCurrentRange = getBaseRange(); + + if(getOverlayManager() && rCurrentRange != aPreviousRange && !rCurrentRange.isEmpty()) + { + getOverlayManager()->invalidateRange(rCurrentRange); + } + } + + // OverlayObject implementations. + drawinglayer::primitive2d::Primitive2DContainer OverlayObject::createOverlayObjectPrimitive2DSequence() + { + // Default implementation has to assert a missing implementation. It cannot + // be useful to have overlay object derivations which have no visualisation + // at all + OSL_FAIL("OverlayObject derivation without visualisation definition (missing createOverlayObjectPrimitive2DSequence implementation) (!)"); + return drawinglayer::primitive2d::Primitive2DContainer(); + } + + sal_uInt32 OverlayObject::impCheckBlinkTimeValueRange(sal_uInt64 nBlinkTime) + { + if(nBlinkTime < 25) + { + nBlinkTime = 25; + } + else if(nBlinkTime > 10000) + { + nBlinkTime = 10000; + } + + return nBlinkTime; + } + + void OverlayObject::allowAntiAliase(bool bNew) + { + if(bNew != mbAllowsAntiAliase) + { + // remember new value + mbAllowsAntiAliase = bNew; + + // register change (after change) + objectChange(); + } + } + + OverlayObject::OverlayObject(Color aBaseColor) + : mpOverlayManager(nullptr), + maOffset(0.0, 0.0), + maBaseColor(aBaseColor), + mbIsVisible(true), + mbIsHittable(true), + mbAllowsAnimation(false), + mbAllowsAntiAliase(true) + { + } + + OverlayObject::~OverlayObject() + { + OSL_ENSURE(nullptr == getOverlayManager(), "OverlayObject is destructed which is still registered at OverlayManager (!)"); + } + + drawinglayer::primitive2d::Primitive2DContainer OverlayObject::getOverlayObjectPrimitive2DSequence() const + { + if(getPrimitive2DSequence().empty()) + { + // no existing sequence; create one + const_cast< OverlayObject* >(this)->maPrimitive2DSequence = const_cast< OverlayObject* >(this)->createOverlayObjectPrimitive2DSequence(); + + if(!getOffset().equalZero()) + { + // embed to offset transformation + const basegfx::B2DHomMatrix aTranslateGridOffset( + basegfx::utils::createTranslateB2DHomMatrix( + getOffset())); + drawinglayer::primitive2d::Primitive2DReference aEmbed( + new drawinglayer::primitive2d::TransformPrimitive2D( + aTranslateGridOffset, + std::move(const_cast<drawinglayer::primitive2d::Primitive2DContainer&>(maPrimitive2DSequence)))); + + const_cast< OverlayObject* >(this)->maPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed }; + } + } + + return getPrimitive2DSequence(); + } + + const basegfx::B2DRange& OverlayObject::getBaseRange() const + { + if(getOverlayManager() && maBaseRange.isEmpty()) + { + const drawinglayer::primitive2d::Primitive2DContainer& rSequence = getOverlayObjectPrimitive2DSequence(); + + if(!rSequence.empty()) + { + const drawinglayer::geometry::ViewInformation2D & aViewInformation2D(getOverlayManager()->getCurrentViewInformation2D()); + + const_cast< sdr::overlay::OverlayObject* >(this)->maBaseRange = + rSequence.getB2DRange(aViewInformation2D); + } + } + + return maBaseRange; + } + + void OverlayObject::setVisible(bool bNew) + { + if(bNew != mbIsVisible) + { + // remember new value + mbIsVisible = bNew; + + // register change (after change) + objectChange(); + } + } + + void OverlayObject::setHittable(bool bNew) + { + if(bNew != mbIsHittable) + { + // remember new value + mbIsHittable = bNew; + + // register change (after change) + objectChange(); + } + } + + void OverlayObject::setBaseColor(Color aNew) + { + if(aNew != maBaseColor) + { + // remember new value + maBaseColor = aNew; + + // register change (after change) + objectChange(); + } + } + + void OverlayObject::setOffset(const basegfx::B2DVector& rOffset) + { + if(rOffset != maOffset) + { + // remember new value + maOffset = rOffset; + + // register change (after change) + objectChange(); + } + } + + void OverlayObject::Trigger(sal_uInt32 /*nTime*/) + { + // default does not register again + } + + void OverlayObject::stripeDefinitionHasChanged() + { + // default does not need to do anything + } + + + OverlayObjectWithBasePosition::OverlayObjectWithBasePosition(const basegfx::B2DPoint& rBasePos, Color aBaseColor) + : OverlayObject(aBaseColor), + maBasePosition(rBasePos) + { + } + + OverlayObjectWithBasePosition::~OverlayObjectWithBasePosition() + { + } + + void OverlayObjectWithBasePosition::setBasePosition(const basegfx::B2DPoint& rNew) + { + if(rNew != maBasePosition) + { + // remember new value + maBasePosition = rNew; + + // register change (after change) + objectChange(); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayobjectcell.cxx b/svx/source/sdr/overlay/overlayobjectcell.cxx new file mode 100644 index 000000000..a7da6a299 --- /dev/null +++ b/svx/source/sdr/overlay/overlayobjectcell.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <basegfx/numeric/ftools.hxx> +#include <sdr/overlay/overlayobjectcell.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> + +using namespace ::basegfx; + +namespace sdr::overlay +{ + OverlayObjectCell::OverlayObjectCell( const Color& rColor, RangeVector&& rRects ) + : OverlayObject( rColor ), + maRectangles( std::move(rRects) ) + { + // no AA for selection overlays + allowAntiAliase(false); + } + + OverlayObjectCell::~OverlayObjectCell() + { + } + + drawinglayer::primitive2d::Primitive2DContainer OverlayObjectCell::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + const sal_uInt32 nCount(maRectangles.size()); + + if(nCount) + { + const basegfx::BColor aRGBColor(getBaseColor().getBColor()); + aRetval.resize(nCount); + + // create primitives for all ranges + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B2DRange& rRange(maRectangles[a]); + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(rRange)); + + aRetval[a] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + aRGBColor)); + } + + + // embed in 50% transparent paint + drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + std::move(aRetval), + 0.5)); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence }; + } + + return aRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayobjectlist.cxx b/svx/source/sdr/overlay/overlayobjectlist.cxx new file mode 100644 index 000000000..d71f6c278 --- /dev/null +++ b/svx/source/sdr/overlay/overlayobjectlist.cxx @@ -0,0 +1,144 @@ +/* -*- 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/overlay/overlayobjectlist.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <vcl/outdev.hxx> +#include <tools/gen.hxx> + +#include <drawinglayer/processor2d/hittestprocessor2d.hxx> +#include <comphelper/lok.hxx> + +#define DEFAULT_VALUE_FOR_HITTEST_PIXEL (2) +#define DEFAULT_VALUE_FOR_HITTEST_TWIP (30) + +namespace sdr::overlay +{ + OverlayObjectList::~OverlayObjectList() + { + clear(); + } + + void OverlayObjectList::clear() + { + for(auto & pCandidate : maVector) + { + if(pCandidate->getOverlayManager()) + pCandidate->getOverlayManager()->remove(*pCandidate); + } + maVector.clear(); + } + + void OverlayObjectList::append(std::unique_ptr<OverlayObject> pOverlayObject) + { + assert(pOverlayObject && "tried to add invalid OverlayObject to OverlayObjectList"); + maVector.push_back(std::move(pOverlayObject)); + } + + bool OverlayObjectList::isHitLogic(const basegfx::B2DPoint& rLogicPosition, double fLogicTolerance) const + { + if(!maVector.empty()) + { + OverlayObject* pFirst = maVector.front().get(); + OverlayManager* pManager = pFirst->getOverlayManager(); + + if(pManager) + { + if(0.0 == fLogicTolerance) + { + Size aSizeLogic(pManager->getOutputDevice().PixelToLogic( + Size(DEFAULT_VALUE_FOR_HITTEST_PIXEL, DEFAULT_VALUE_FOR_HITTEST_PIXEL))); + + // When tiled rendering, we always work in logic units, use the non-pixel default. + if (comphelper::LibreOfficeKit::isActive()) + { + aSizeLogic = Size(DEFAULT_VALUE_FOR_HITTEST_TWIP, DEFAULT_VALUE_FOR_HITTEST_TWIP); + if (pManager->getOutputDevice().GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + aSizeLogic = o3tl::convert(aSizeLogic, o3tl::Length::twip, o3tl::Length::mm100); + } + + fLogicTolerance = aSizeLogic.Width(); + } + + const drawinglayer::geometry::ViewInformation2D& aViewInformation2D(pManager->getCurrentViewInformation2D()); + drawinglayer::processor2d::HitTestProcessor2D aHitTestProcessor2D( + aViewInformation2D, + rLogicPosition, + fLogicTolerance, + false); + + for(auto & pCandidate : maVector) + { + if(pCandidate->isHittable()) + { + const drawinglayer::primitive2d::Primitive2DContainer& rSequence = pCandidate->getOverlayObjectPrimitive2DSequence(); + + if(!rSequence.empty()) + { + aHitTestProcessor2D.process(rSequence); + + if(aHitTestProcessor2D.getHit()) + { + return true; + } + } + } + } + } + } + + return false; + } + + bool OverlayObjectList::isHitPixel(const Point& rDiscretePosition) const + { + constexpr sal_uInt32 nDiscreteTolerance = DEFAULT_VALUE_FOR_HITTEST_PIXEL; + if(!maVector.empty()) + { + OverlayObject* pCandidate = maVector.front().get(); + OverlayManager* pManager = pCandidate->getOverlayManager(); + + if(pManager) + { + const Point aPosLogic(pManager->getOutputDevice().PixelToLogic(rDiscretePosition)); + const basegfx::B2DPoint aPosition(aPosLogic.X(), aPosLogic.Y()); + + const Size aSizeLogic(pManager->getOutputDevice().PixelToLogic(Size(nDiscreteTolerance, nDiscreteTolerance))); + return isHitLogic(aPosition, static_cast<double>(aSizeLogic.Width())); + } + } + + return false; + } + + basegfx::B2DRange OverlayObjectList::getBaseRange() const + { + basegfx::B2DRange aRetval; + + for(auto & pCandidate : maVector) + { + aRetval.expand(pCandidate->getBaseRange()); + } + + return aRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaypolypolygon.cxx b/svx/source/sdr/overlay/overlaypolypolygon.cxx new file mode 100644 index 000000000..69dacf375 --- /dev/null +++ b/svx/source/sdr/overlay/overlaypolypolygon.cxx @@ -0,0 +1,128 @@ +/* -*- 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/overlay/overlaypolypolygon.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <svtools/optionsdrawinglayer.hxx> + + +namespace sdr::overlay +{ + OverlayPolyPolygon::OverlayPolyPolygon( + const basegfx::B2DPolyPolygon& rLinePolyPolygon, + Color const & rLineColor, + double fLineWidth, + Color const & rFillColor) + : OverlayObject(rLineColor) + , maLinePolyPolygon(rLinePolyPolygon) + , mfLineWidth(fLineWidth) + , maFillColor(rFillColor) + { + } + + OverlayPolyPolygon::~OverlayPolyPolygon() = default; + + drawinglayer::primitive2d::Primitive2DContainer OverlayPolyPolygon::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aReturnContainer; + + if (getOverlayManager()) + { + const drawinglayer::attribute::LineAttribute aLineAttribute(getBaseColor().getBColor(), mfLineWidth); + + aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer { + new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(maLinePolyPolygon, aLineAttribute) }; + + if (maFillColor.GetAlpha() != 0) + { + aReturnContainer.push_back(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(maLinePolyPolygon, maFillColor.getBColor())); + } + + sal_uInt8 nTransparency = 255 - getBaseColor().GetAlpha(); + if (nTransparency > 0) + { + drawinglayer::primitive2d::Primitive2DReference aTransparencePrimitive( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aReturnContainer), nTransparency / 255.0)); + aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer{ aTransparencePrimitive }; + } + } + + return aReturnContainer; + } + + drawinglayer::primitive2d::Primitive2DContainer OverlayPolyPolygonStripedAndFilled::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(getOverlayManager()) + { + const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor()); + const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor()); + const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel()); + const drawinglayer::primitive2d::Primitive2DReference aStriped( + new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D( + getLinePolyPolygon(), + aRGBColorA, + aRGBColorB, + fStripeLengthPixel)); + + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aStriped }; + + const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor()); + const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); + + const drawinglayer::primitive2d::Primitive2DReference aFilled( + new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D( + getLinePolyPolygon(), + aHilightColor, + fTransparence, + 3.0, + false)); + + aRetval.push_back(aFilled); + } + + return aRetval; + } + + void OverlayPolyPolygonStripedAndFilled::stripeDefinitionHasChanged() + { + // react on OverlayManager's stripe definition change + objectChange(); + } + + OverlayPolyPolygonStripedAndFilled::OverlayPolyPolygonStripedAndFilled( + const basegfx::B2DPolyPolygon& rLinePolyPolygon) + : OverlayObject(COL_BLACK), + maLinePolyPolygon(rLinePolyPolygon) + { + } + + OverlayPolyPolygonStripedAndFilled::~OverlayPolyPolygonStripedAndFilled() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx b/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx new file mode 100644 index 000000000..eabd290f8 --- /dev/null +++ b/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx @@ -0,0 +1,42 @@ +/* -*- 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/overlay/overlayprimitive2dsequenceobject.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayPrimitive2DSequenceObject::createOverlayObjectPrimitive2DSequence() + { + return maSequence; + } + + OverlayPrimitive2DSequenceObject::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer&& rSequence) + : OverlayObjectWithBasePosition(basegfx::B2DPoint(), COL_BLACK), + maSequence(std::move(rSequence)) + { + } + + OverlayPrimitive2DSequenceObject::~OverlayPrimitive2DSequenceObject() + { + } +} // end of namespace sdr::overlay + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayrectangle.cxx b/svx/source/sdr/overlay/overlayrectangle.cxx new file mode 100644 index 000000000..617c6d4dc --- /dev/null +++ b/svx/source/sdr/overlay/overlayrectangle.cxx @@ -0,0 +1,113 @@ +/* -*- 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/overlay/overlayrectangle.hxx> +#include <sdr/overlay/overlaytools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayRectangle::createOverlayObjectPrimitive2DSequence() + { + const basegfx::B2DRange aHatchRange(getBasePosition(), maSecondPosition); + basegfx::BColor aColor(getBaseColor().getBColor()); + static const double fChange(0.1); // just small optical change, do not make it annoying + + if(mbOverlayState) + { + aColor += basegfx::B3DTuple(fChange, fChange, fChange); + aColor.clamp(); + } + else + { + aColor -= basegfx::B3DTuple(fChange, fChange, fChange); + aColor.clamp(); + } + + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::OverlayRectanglePrimitive( + aHatchRange, + aColor, + mfTransparence, + mfDiscreteGrow, + mfDiscreteShrink, + mfRotation)); + + return drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + OverlayRectangle::OverlayRectangle( + const basegfx::B2DPoint& rBasePosition, + const basegfx::B2DPoint& rSecondPosition, + const Color& rHatchColor, + double fTransparence, + double fDiscreteGrow, + double fDiscreteShrink, + double fRotation, + bool bAnimate) + : OverlayObjectWithBasePosition(rBasePosition, rHatchColor), + maSecondPosition(rSecondPosition), + mfTransparence(fTransparence), + mfDiscreteGrow(fDiscreteGrow), + mfDiscreteShrink(fDiscreteShrink), + mfRotation(fRotation), + mnBlinkTime(impCheckBlinkTimeValueRange(500)), + mbOverlayState(false) + { + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + // no animation in high contrast mode + bAnimate = false; + } + + // set AllowsAnimation flag to mark this object as animation capable + mbAllowsAnimation = bAnimate; + } + + void OverlayRectangle::Trigger(sal_uInt32 nTime) + { + if(!getOverlayManager()) + return; + + // #i53216# produce event after nTime + x + SetTime(nTime + mnBlinkTime); + + // switch state + if(mbOverlayState) + { + mbOverlayState = false; + } + else + { + mbOverlayState = true; + } + + // re-insert me as event + getOverlayManager()->InsertEvent(*this); + + // register change (after change) + objectChange(); + } +} // end of namespace + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayrollingrectangle.cxx b/svx/source/sdr/overlay/overlayrollingrectangle.cxx new file mode 100644 index 000000000..f03380edd --- /dev/null +++ b/svx/source/sdr/overlay/overlayrollingrectangle.cxx @@ -0,0 +1,117 @@ +/* -*- 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/overlay/overlayrollingrectangle.hxx> +#include <sdr/overlay/overlaytools.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx> +#include <svtools/optionsdrawinglayer.hxx> + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayRollingRectangleStriped::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + if(getOverlayManager() && (mbShowBounds || mbExtendedLines)) + { + const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor()); + const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor()); + const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel()); + const basegfx::B2DRange aRollingRectangle(getBasePosition(), getSecondPosition()); + + if(mbShowBounds) + { + // view-independent part, create directly + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aRollingRectangle)); + + aRetval.resize(2); + aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + aRGBColorA, + aRGBColorB, + fStripeLengthPixel); + + const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor()); + const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); + + aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + aHilightColor, + fTransparence, + 3.0, + false); + } + + if(mbExtendedLines) + { + // view-dependent part, use helper primitive + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::OverlayRollingRectanglePrimitive( + aRollingRectangle, + aRGBColorA, + aRGBColorB, + fStripeLengthPixel)); + + aRetval.push_back(aReference); + } + } + + return aRetval; + } + + void OverlayRollingRectangleStriped::stripeDefinitionHasChanged() + { + // react on OverlayManager's stripe definition change + objectChange(); + } + + OverlayRollingRectangleStriped::OverlayRollingRectangleStriped( + const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPos, + bool bExtendedLines, + bool bShowBounds) + : OverlayObjectWithBasePosition(rBasePos, COL_BLACK), + maSecondPosition(rSecondPos), + mbExtendedLines(bExtendedLines), + mbShowBounds(bShowBounds) + { + } + + OverlayRollingRectangleStriped::~OverlayRollingRectangleStriped() + { + } + + void OverlayRollingRectangleStriped::setSecondPosition(const basegfx::B2DPoint& rNew) + { + if(rNew != maSecondPosition) + { + // remember new value + maSecondPosition = rNew; + + // register change (after change) + objectChange(); + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlayselection.cxx b/svx/source/sdr/overlay/overlayselection.cxx new file mode 100644 index 000000000..0463567c5 --- /dev/null +++ b/svx/source/sdr/overlay/overlayselection.cxx @@ -0,0 +1,218 @@ +/* -*- 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/overlay/overlayselection.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <drawinglayer/primitive2d/invertprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> + + +namespace sdr::overlay +{ + // combine rages geometrically to a single, ORed polygon + static basegfx::B2DPolyPolygon impCombineRangesToPolyPolygon(const std::vector< basegfx::B2DRange >& rRanges) + { + const sal_uInt32 nCount(rRanges.size()); + basegfx::B2DPolyPolygon aRetval; + + for(sal_uInt32 a(0); a < nCount; a++) + { + const basegfx::B2DPolygon aDiscretePolygon(basegfx::utils::createPolygonFromRect(rRanges[a])); + + if(0 == a) + { + aRetval.append(aDiscretePolygon); + } + else + { + aRetval = basegfx::utils::solvePolygonOperationOr(aRetval, basegfx::B2DPolyPolygon(aDiscretePolygon)); + } + } + + return aRetval; + } + + // check if wanted type OverlayType::Transparent or OverlayType::Solid + // is possible. If not, fallback to invert mode (classic mode) + static OverlayType impCheckPossibleOverlayType(OverlayType aOverlayType) + { + if(OverlayType::Invert != aOverlayType) + { + if(!SvtOptionsDrawinglayer::IsTransparentSelection()) + { + // not possible when switched off by user + return OverlayType::Invert; + } + else if (const OutputDevice* pOut = Application::GetDefaultDevice()) + { + + if(pOut->GetSettings().GetStyleSettings().GetHighContrastMode()) + { + // not possible when in high contrast mode + return OverlayType::Invert; + } + + if(!pOut->SupportsOperation(OutDevSupportType::TransparentRect)) + { + // not possible when no fast transparence paint is supported on the system + return OverlayType::Invert; + } + } + } + + return aOverlayType; + } + + drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + const sal_uInt32 nCount(getRanges().size()); + + if(nCount) + { + // create range primitives + const bool bInvert(OverlayType::Invert == maLastOverlayType); + basegfx::BColor aRGBColor(getBaseColor().getBColor()); + aRetval.resize(nCount); + + if(bInvert) + { + // force color to white for invert to get a full invert + aRGBColor = basegfx::BColor(1.0, 1.0, 1.0); + } + + for(sal_uInt32 a(0);a < nCount; a++) + { + const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(maRanges[a])); + aRetval[a] = drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + aRGBColor)); + } + + if(bInvert) + { + // embed all in invert primitive + drawinglayer::primitive2d::Primitive2DReference aInvert( + new drawinglayer::primitive2d::InvertPrimitive2D( + std::move(aRetval))); + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aInvert }; + } + else if(OverlayType::Transparent == maLastOverlayType) + { + // embed all rectangles in transparent paint + const double fTransparence(mnLastTransparence / 100.0); + const drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence( + new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + std::move(aRetval), + fTransparence)); + + if(mbBorder) + { + const basegfx::B2DPolyPolygon aPolyPolygon(impCombineRangesToPolyPolygon(getRanges())); + const drawinglayer::primitive2d::Primitive2DReference aSelectionOutline( + new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D( + aPolyPolygon, + aRGBColor)); + + // add both to result + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence, aSelectionOutline }; + } + else + { + // just add transparent part + aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence }; + } + } + } + + return aRetval; + } + + OverlaySelection::OverlaySelection( + OverlayType eType, + const Color& rColor, + std::vector< basegfx::B2DRange >&& rRanges, + bool bBorder) + : OverlayObject(rColor), + meOverlayType(eType), + maRanges(std::move(rRanges)), + maLastOverlayType(eType), + mnLastTransparence(0), + mbBorder(bBorder) + { + // no AA for selection overlays + allowAntiAliase(false); + } + + OverlaySelection::~OverlaySelection() + { + if(getOverlayManager()) + { + getOverlayManager()->remove(*this); + } + } + + drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::getOverlayObjectPrimitive2DSequence() const + { + // get current values + const OverlayType aNewOverlayType(impCheckPossibleOverlayType(meOverlayType)); + const sal_uInt16 nNewTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent()); + + if(!getPrimitive2DSequence().empty()) + { + if(aNewOverlayType != maLastOverlayType + || nNewTransparence != mnLastTransparence) + { + // conditions of last local decomposition have changed, delete + const_cast< OverlaySelection* >(this)->resetPrimitive2DSequence(); + } + } + + if(getPrimitive2DSequence().empty()) + { + // remember new values + const_cast< OverlaySelection* >(this)->maLastOverlayType = aNewOverlayType; + const_cast< OverlaySelection* >(this)->mnLastTransparence = nNewTransparence; + } + + // call base implementation + return OverlayObject::getOverlayObjectPrimitive2DSequence(); + } + + void OverlaySelection::setRanges(std::vector< basegfx::B2DRange >&& rNew) + { + if(rNew != maRanges) + { + maRanges = std::move(rNew); + objectChange(); + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaytools.cxx b/svx/source/sdr/overlay/overlaytools.cxx new file mode 100644 index 000000000..ceeb32b3f --- /dev/null +++ b/svx/source/sdr/overlay/overlaytools.cxx @@ -0,0 +1,601 @@ +/* -*- 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/overlay/overlaytools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/helper/vclunohelper.hxx> + + +namespace drawinglayer::primitive2d +{ + +OverlayStaticRectanglePrimitive::OverlayStaticRectanglePrimitive( + const basegfx::B2DPoint& rPosition, + const basegfx::B2DSize& rSize, + const basegfx::BColor& rStrokeColor, + const basegfx::BColor& rFillColor, + double fTransparence, + double fRotation) + : maPosition(rPosition) + , maSize(rSize) + , maStrokeColor(rStrokeColor) + , maFillColor(rFillColor) + , mfTransparence(fTransparence) + , mfRotation(fRotation) +{} + +void OverlayStaticRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + Primitive2DContainer aPrimitive2DSequence; + const double fHalfWidth = maSize.getX() * getDiscreteUnit() / 2.0; + const double fHalfHeight = maSize.getY() * getDiscreteUnit() / 2.0; + + basegfx::B2DRange aRange( + maPosition.getX() - fHalfWidth, maPosition.getY() - fHalfHeight, + maPosition.getX() + fHalfWidth, maPosition.getY() + fHalfHeight); + + if (basegfx::fTools::more(getDiscreteUnit(), 0.0) && mfTransparence <= 1.0) + { + basegfx::B2DPolygon aPolygon( + basegfx::utils::createPolygonFromRect(aRange)); + + // create filled primitive + basegfx::B2DPolyPolygon aPolyPolygon; + aPolyPolygon.append(aPolygon); + + const attribute::LineAttribute aLineAttribute(maStrokeColor, 1.0); + + // create data + const Primitive2DReference aStroke( + new PolyPolygonStrokePrimitive2D(aPolyPolygon, aLineAttribute)); + + // create fill primitive + const Primitive2DReference aFill( + new PolyPolygonColorPrimitive2D(aPolyPolygon, maFillColor)); + + aPrimitive2DSequence = Primitive2DContainer(2); + aPrimitive2DSequence[0] = aFill; + aPrimitive2DSequence[1] = aStroke; + + // embed filled to transparency (if used) + if (mfTransparence > 0.0) + { + const Primitive2DReference aFillTransparent( + new UnifiedTransparencePrimitive2D( + std::move(aPrimitive2DSequence), + mfTransparence)); + + aPrimitive2DSequence = Primitive2DContainer { aFillTransparent }; + } + } + + rContainer.append(std::move(aPrimitive2DSequence)); +} + +bool OverlayStaticRectanglePrimitive::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayStaticRectanglePrimitive& rCompare = static_cast<const OverlayStaticRectanglePrimitive&>(rPrimitive); + + return (maPosition == rCompare.maPosition + && maSize == rCompare.maSize + && maStrokeColor == rCompare.maStrokeColor + && maFillColor == rCompare.maFillColor + && mfTransparence == rCompare.mfTransparence + && mfRotation == rCompare.mfRotation); + } + + return false; +} + +sal_uInt32 OverlayStaticRectanglePrimitive::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_OVERLAYRECTANGLEPRIMITIVE; +} + + + + OverlayBitmapExPrimitive::OverlayBitmapExPrimitive( + const BitmapEx& rBitmapEx, + const basegfx::B2DPoint& rBasePosition, + sal_uInt16 nCenterX, + sal_uInt16 nCenterY, + double fShearX, + double fRotation) + : maBitmapEx(rBitmapEx), + maBasePosition(rBasePosition), + mnCenterX(nCenterX), + mnCenterY(nCenterY), + mfShearX(fShearX), + mfRotation(fRotation) + {} + + void OverlayBitmapExPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + { + const Size aBitmapSize(getBitmapEx().GetSizePixel()); + + if(!aBitmapSize.Width() || !aBitmapSize.Height() || !basegfx::fTools::more(getDiscreteUnit(), 0.0)) + return; + + // calculate back from internal bitmap's extreme coordinates (the edges) + // to logical coordinates. Only use a unified scaling value (getDiscreteUnit(), + // the prepared one which expresses how many logic units form a discrete unit) + // for this step. This primitive is to be displayed always unscaled (in its pixel size) + // and unrotated, more like a marker + const double fLeft((0.0 - getCenterX()) * getDiscreteUnit()); + const double fTop((0.0 - getCenterY()) * getDiscreteUnit()); + const double fRight((aBitmapSize.getWidth() - getCenterX()) * getDiscreteUnit()); + const double fBottom((aBitmapSize.getHeight() - getCenterY()) * getDiscreteUnit()); + + // create a BitmapPrimitive2D using those positions + basegfx::B2DHomMatrix aTransform; + + aTransform.set(0, 0, fRight - fLeft); + aTransform.set(1, 1, fBottom - fTop); + aTransform.set(0, 2, fLeft); + aTransform.set(1, 2, fTop); + + // if shearX is used, apply it, too + if(!basegfx::fTools::equalZero(getShearX())) + { + aTransform.shearX(getShearX()); + } + + // if rotation is used, apply it, too + if(!basegfx::fTools::equalZero(getRotation())) + { + aTransform.rotate(getRotation()); + } + + // add BasePosition + aTransform.translate(getBasePosition().getX(), getBasePosition().getY()); + + rContainer.push_back( + new BitmapPrimitive2D( + VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()), + aTransform)); + } + + bool OverlayBitmapExPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const + { + if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayBitmapExPrimitive& rCompare = static_cast< const OverlayBitmapExPrimitive& >(rPrimitive); + + return (getBitmapEx() == rCompare.getBitmapEx() + && getBasePosition() == rCompare.getBasePosition() + && getCenterX() == rCompare.getCenterX() + && getCenterY() == rCompare.getCenterY() + && getShearX() == rCompare.getShearX() + && getRotation() == rCompare.getRotation()); + } + + return false; + } + + sal_uInt32 OverlayBitmapExPrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OVERLAYBITMAPEXPRIMITIVE; + } + + + + OverlayCrosshairPrimitive::OverlayCrosshairPrimitive( + const basegfx::B2DPoint& rBasePosition, + const basegfx::BColor& rRGBColorA, + const basegfx::BColor& rRGBColorB, + double fDiscreteDashLength) + : maBasePosition(rBasePosition), + maRGBColorA(rRGBColorA), + maRGBColorB(rRGBColorB), + mfDiscreteDashLength(fDiscreteDashLength) + {} + + void OverlayCrosshairPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + { + // use the prepared Viewport information accessible using getViewport() + + if(getViewport().isEmpty()) + return; + + basegfx::B2DPolygon aPolygon; + + aPolygon.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY())); + aPolygon.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY())); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aPolygon, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + + aPolygon.clear(); + aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY())); + aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY())); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aPolygon, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + } + + bool OverlayCrosshairPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const + { + if(ViewportDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayCrosshairPrimitive& rCompare = static_cast< const OverlayCrosshairPrimitive& >(rPrimitive); + + return (getBasePosition() == rCompare.getBasePosition() + && getRGBColorA() == rCompare.getRGBColorA() + && getRGBColorB() == rCompare.getRGBColorB() + && getDiscreteDashLength() == rCompare.getDiscreteDashLength()); + } + + return false; + } + + sal_uInt32 OverlayCrosshairPrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OVERLAYCROSSHAIRPRIMITIVE; + } + + + + OverlayRectanglePrimitive::OverlayRectanglePrimitive( + const basegfx::B2DRange& rObjectRange, + const basegfx::BColor& rColor, + double fTransparence, + double fDiscreteGrow, + double fDiscreteShrink, + double fRotation) + : maObjectRange(rObjectRange), + maColor(rColor), + mfTransparence(fTransparence), + mfDiscreteGrow(fDiscreteGrow), + mfDiscreteShrink(fDiscreteShrink), + mfRotation(fRotation) + {} + + void OverlayRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + { + Primitive2DContainer aRetval; + basegfx::B2DRange aInnerRange(getObjectRange()); + + if(!aInnerRange.isEmpty() && basegfx::fTools::more(getDiscreteUnit(), 0.0) && getTransparence() <= 1.0) + { + basegfx::B2DRange aOuterRange(getObjectRange()); + + // grow/shrink inner/outer polygons + aOuterRange.grow(getDiscreteUnit() * getDiscreteGrow()); + aInnerRange.grow(getDiscreteUnit() * -getDiscreteShrink()); + + // convert to polygons + const double fFullGrow(getDiscreteGrow() + getDiscreteShrink()); + const double fRelativeRadiusX(fFullGrow / aOuterRange.getWidth()); + const double fRelativeRadiusY(fFullGrow / aOuterRange.getHeight()); + basegfx::B2DPolygon aOuterPolygon( + basegfx::utils::createPolygonFromRect( + aOuterRange, + fRelativeRadiusX, + fRelativeRadiusY)); + basegfx::B2DPolygon aInnerPolygon( + basegfx::utils::createPolygonFromRect( + aInnerRange)); + + // apply evtl. existing rotation + if(!basegfx::fTools::equalZero(getRotation())) + { + const basegfx::B2DHomMatrix aTransform(basegfx::utils::createRotateAroundPoint( + getObjectRange().getMinX(), getObjectRange().getMinY(), getRotation())); + + aOuterPolygon.transform(aTransform); + aInnerPolygon.transform(aTransform); + } + + // create filled primitive + basegfx::B2DPolyPolygon aPolyPolygon; + + aPolyPolygon.append(aOuterPolygon); + aPolyPolygon.append(aInnerPolygon); + + if(Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + // for high contrast, use hatch + const basegfx::BColor aHighContrastLineColor(Application::GetSettings().GetStyleSettings().GetFontColor().getBColor()); + const basegfx::BColor aEmptyColor(0.0, 0.0, 0.0); + const double fHatchRotation(basegfx::deg2rad(45)); + const double fDiscreteHatchDistance(3.0); + const drawinglayer::attribute::FillHatchAttribute aFillHatchAttribute( + drawinglayer::attribute::HatchStyle::Single, + fDiscreteHatchDistance * getDiscreteUnit(), + fHatchRotation - getRotation(), + aHighContrastLineColor, + 3, // same default as VCL, a minimum of three discrete units (pixels) offset + false); + const Primitive2DReference aHatch( + new PolyPolygonHatchPrimitive2D( + aPolyPolygon, + aEmptyColor, + aFillHatchAttribute)); + + aRetval = Primitive2DContainer { aHatch }; + } + else + { + // create fill primitive + const Primitive2DReference aFill( + new PolyPolygonColorPrimitive2D( + aPolyPolygon, + getColor())); + + aRetval = Primitive2DContainer { aFill }; + + // embed filled to transparency (if used) + if(getTransparence() > 0.0) + { + Primitive2DReference aFillTransparent( + new UnifiedTransparencePrimitive2D( + std::move(aRetval), + getTransparence())); + + aRetval = Primitive2DContainer { aFillTransparent }; + } + } + } + + rContainer.append(std::move(aRetval)); + } + + bool OverlayRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const + { + if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayRectanglePrimitive& rCompare = static_cast< const OverlayRectanglePrimitive& >(rPrimitive); + + return (getObjectRange() == rCompare.getObjectRange() + && getColor() == rCompare.getColor() + && getTransparence() == rCompare.getTransparence() + && getDiscreteGrow() == rCompare.getDiscreteGrow() + && getDiscreteShrink() == rCompare.getDiscreteShrink() + && getRotation() == rCompare.getRotation()); + } + + return false; + } + + sal_uInt32 OverlayRectanglePrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OVERLAYRECTANGLEPRIMITIVE; + } + + + + OverlayHelplineStripedPrimitive::OverlayHelplineStripedPrimitive( + const basegfx::B2DPoint& rBasePosition, + HelplineStyle eStyle, + const basegfx::BColor& rRGBColorA, + const basegfx::BColor& rRGBColorB, + double fDiscreteDashLength) + : maBasePosition(rBasePosition), + meStyle(eStyle), + maRGBColorA(rRGBColorA), + maRGBColorB(rRGBColorB), + mfDiscreteDashLength(fDiscreteDashLength) + {} + + void OverlayHelplineStripedPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const + { + // use the prepared Viewport information accessible using getViewport() + + if(getViewport().isEmpty()) + return; + + switch(getStyle()) + { + case HELPLINESTYLE_VERTICAL : + { + basegfx::B2DPolygon aLine; + + aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY())); + aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY())); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aLine, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + break; + } + + case HELPLINESTYLE_HORIZONTAL : + { + basegfx::B2DPolygon aLine; + + aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY())); + aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY())); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aLine, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + break; + } + + default: // case HELPLINESTYLE_POINT : + { + const double fDiscreteUnit((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength()); + basegfx::B2DPolygon aLineA, aLineB; + + aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() - fDiscreteUnit)); + aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() + fDiscreteUnit)); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aLineA, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + + aLineB.append(basegfx::B2DPoint(getBasePosition().getX() - fDiscreteUnit, getBasePosition().getY())); + aLineB.append(basegfx::B2DPoint(getBasePosition().getX() + fDiscreteUnit, getBasePosition().getY())); + + rContainer.push_back( + new PolygonMarkerPrimitive2D( + aLineB, + getRGBColorA(), + getRGBColorB(), + getDiscreteDashLength())); + + break; + } + } + } + + bool OverlayHelplineStripedPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const + { + if(ViewportDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayHelplineStripedPrimitive& rCompare = static_cast< const OverlayHelplineStripedPrimitive& >(rPrimitive); + + return (getBasePosition() == rCompare.getBasePosition() + && getStyle() == rCompare.getStyle() + && getRGBColorA() == rCompare.getRGBColorA() + && getRGBColorB() == rCompare.getRGBColorB() + && getDiscreteDashLength() == rCompare.getDiscreteDashLength()); + } + + return false; + } + + sal_uInt32 OverlayHelplineStripedPrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OVERLAYHELPLINESTRIPEDPRIMITIVE; + } + + + + OverlayRollingRectanglePrimitive::OverlayRollingRectanglePrimitive( + const basegfx::B2DRange& aRollingRectangle, + const basegfx::BColor& rRGBColorA, + const basegfx::BColor& rRGBColorB, + double fDiscreteDashLength) + : maRollingRectangle(aRollingRectangle), + maRGBColorA(rRGBColorA), + maRGBColorB(rRGBColorB), + mfDiscreteDashLength(fDiscreteDashLength) + {} + + void OverlayRollingRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const + { + // use the prepared Viewport information accessible using getViewport() + + if(getViewport().isEmpty()) + return; + + basegfx::B2DPolygon aLine; + + // Left lines + aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMinY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + aLine.clear(); + aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMaxY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + // Right lines + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY())); + aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMinY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY())); + aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMaxY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + // Top lines + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMinY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMinY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + // Bottom lines + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMaxY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + aLine.clear(); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY())); + aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMaxY())); + rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength())); + + } + + bool OverlayRollingRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const + { + if(ViewportDependentPrimitive2D::operator==(rPrimitive)) + { + const OverlayRollingRectanglePrimitive& rCompare = static_cast< const OverlayRollingRectanglePrimitive& >(rPrimitive); + + return (getRollingRectangle() == rCompare.getRollingRectangle() + && getRGBColorA() == rCompare.getRGBColorA() + && getRGBColorB() == rCompare.getRGBColorB() + && getDiscreteDashLength() == rCompare.getDiscreteDashLength()); + } + + return false; + } + + sal_uInt32 OverlayRollingRectanglePrimitive::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_OVERLAYROLLINGRECTANGLEPRIMITIVE; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/overlay/overlaytriangle.cxx b/svx/source/sdr/overlay/overlaytriangle.cxx new file mode 100644 index 000000000..f46fcf1e2 --- /dev/null +++ b/svx/source/sdr/overlay/overlaytriangle.cxx @@ -0,0 +1,61 @@ +/* -*- 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/overlay/overlaytriangle.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> + + +namespace sdr::overlay +{ + drawinglayer::primitive2d::Primitive2DContainer OverlayTriangle::createOverlayObjectPrimitive2DSequence() + { + basegfx::B2DPolygon aPolygon; + + aPolygon.append(getBasePosition()); + aPolygon.append(maSecondPosition); + aPolygon.append(maThirdPosition); + aPolygon.setClosed(true); + + const drawinglayer::primitive2d::Primitive2DReference aReference( + new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( + basegfx::B2DPolyPolygon(aPolygon), + getBaseColor().getBColor())); + + return drawinglayer::primitive2d::Primitive2DContainer { aReference }; + } + + OverlayTriangle::OverlayTriangle( + const basegfx::B2DPoint& rBasePos, + const basegfx::B2DPoint& rSecondPos, + const basegfx::B2DPoint& rThirdPos, + Color aTriangleColor) + : OverlayObjectWithBasePosition(rBasePos, aTriangleColor), + maSecondPosition(rSecondPos), + maThirdPosition(rThirdPos) + { + } + + OverlayTriangle::~OverlayTriangle() + { + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/primitivefactory2d.cxx b/svx/source/sdr/primitive2d/primitivefactory2d.cxx new file mode 100644 index 000000000..c6530fd13 --- /dev/null +++ b/svx/source/sdr/primitive2d/primitivefactory2d.cxx @@ -0,0 +1,98 @@ +/* -*- 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 <com/sun/star/uno/XComponentContext.hpp> +#include <sdr/primitive2d/primitivefactory2d.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdpage.hxx> +#include <svx/unoapi.hxx> + +using namespace com::sun::star; + +css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXShape( + const uno::Reference< drawing::XShape >& xShape, + const uno::Sequence< beans::PropertyValue >& /*aParms*/ ) +{ + css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval; + + if(xShape.is()) + { + SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + drawinglayer::primitive2d::Primitive2DContainer aSourceVal; + rSource.getViewIndependentPrimitive2DContainer(aSourceVal); + aRetval = aSourceVal.toSequence(); + } + } + + return aRetval; +} + +void PrimitiveFactory2D::createPrimitivesFromXShape( + const uno::Reference< drawing::XShape >& xShape, + const uno::Sequence< beans::PropertyValue >& /*aParms*/, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + if(xShape.is()) + { + SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + rSource.getViewIndependentPrimitive2DContainer(rVisitor); + } + } +} + +css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXDrawPage( + const uno::Reference< drawing::XDrawPage >& xDrawPage, + const uno::Sequence< beans::PropertyValue >& /*aParms*/ ) +{ + css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval; + + if(xDrawPage.is()) + { + SdrPage* pSource = GetSdrPageFromXDrawPage(xDrawPage); + + if(pSource) + { + const sdr::contact::ViewContact& rSource(pSource->GetViewContact()); + drawinglayer::primitive2d::Primitive2DContainer aSourceRetval; + rSource.getViewIndependentPrimitive2DContainer(aSourceRetval); + aRetval = aSourceRetval.toSequence(); + } + } + + return aRetval; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_graphic_PrimitiveFactory2D_get_implementation( + css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new PrimitiveFactory2D); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx new file mode 100644 index 000000000..bafa96f95 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx @@ -0,0 +1,1151 @@ +/* -*- 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/primitive2d/sdrattributecreator.hxx> +#include <svl/itemset.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdooitm.hxx> +#include <svx/sdprcitm.hxx> +#include <svx/xdef.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflbmpit.hxx> +#include <svx/xlntrit.hxx> +#include <svx/xlnwtit.hxx> +#include <svx/xlinjoit.hxx> +#include <svx/xlncapit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedcit.hxx> +#include <svx/xdash.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xfilluseslidebackgrounditem.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xflclit.hxx> +#include <svx/xgrscit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/xflbckit.hxx> +#include <svx/xflbmsxy.hxx> +#include <svx/xflbtoxy.hxx> +#include <svx/xflboxy.hxx> +#include <svx/xflbmtit.hxx> +#include <svx/xflbstit.hxx> +#include <svx/xtextit0.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <svx/svdotext.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <svx/xbtmpit.hxx> +#include <svl/itempool.hxx> +#include <vcl/svapp.hxx> +#include <vcl/GraphicLoader.hxx> +#include <basegfx/range/b2drange.hxx> +#include <svx/svx3ditems.hxx> +#include <com/sun/star/drawing/ProjectionMode.hpp> +#include <com/sun/star/drawing/ShadeMode.hpp> +#include <drawinglayer/attribute/sdrallattribute3d.hxx> +#include <svx/rectenum.hxx> +#include <svx/sdtfchim.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdmodel.hxx> +#include <svx/xflbmsli.hxx> +#include <editeng/editstat.hxx> +#include <osl/diagnose.h> +#include <drawinglayer/attribute/fillhatchattribute.hxx> +#include <drawinglayer/attribute/fillgradientattribute.hxx> +#include <sdr/attribute/sdreffectstextattribute.hxx> +#include <sdr/attribute/sdrlineeffectstextattribute.hxx> +#include <sdr/attribute/sdrformtextattribute.hxx> +#include <sdr/attribute/sdrlinefilleffectstextattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> +#include <drawinglayer/attribute/sdrsceneattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightingattribute3d.hxx> +#include <drawinglayer/attribute/sdrlightattribute3d.hxx> +#include <sdr/attribute/sdrfilltextattribute.hxx> +#include <com/sun/star/drawing/LineCap.hpp> + +using namespace com::sun::star; + +namespace drawinglayer +{ + namespace + { + attribute::GradientStyle XGradientStyleToGradientStyle(css::awt::GradientStyle eStyle) + { + switch(eStyle) + { + case css::awt::GradientStyle_LINEAR : + { + return attribute::GradientStyle::Linear; + } + case css::awt::GradientStyle_AXIAL : + { + return attribute::GradientStyle::Axial; + } + case css::awt::GradientStyle_RADIAL : + { + return attribute::GradientStyle::Radial; + } + case css::awt::GradientStyle_ELLIPTICAL : + { + return attribute::GradientStyle::Elliptical; + } + case css::awt::GradientStyle_SQUARE : + { + return attribute::GradientStyle::Square; + } + default : + { + return attribute::GradientStyle::Rect; // css::awt::GradientStyle_RECT + } + } + } + + attribute::HatchStyle XHatchStyleToHatchStyle(css::drawing::HatchStyle eStyle) + { + switch(eStyle) + { + case css::drawing::HatchStyle_SINGLE : + { + return attribute::HatchStyle::Single; + } + case css::drawing::HatchStyle_DOUBLE : + { + return attribute::HatchStyle::Double; + } + default : + { + return attribute::HatchStyle::Triple; // css::drawing::HatchStyle_TRIPLE + } + } + } + + basegfx::B2DLineJoin LineJointToB2DLineJoin(css::drawing::LineJoint eLineJoint) + { + switch(eLineJoint) + { + case css::drawing::LineJoint_BEVEL : + { + return basegfx::B2DLineJoin::Bevel; + } + case css::drawing::LineJoint_MIDDLE : + case css::drawing::LineJoint_MITER : + { + return basegfx::B2DLineJoin::Miter; + } + case css::drawing::LineJoint_ROUND : + { + return basegfx::B2DLineJoin::Round; + } + default : // css::drawing::LineJoint_NONE + { + return basegfx::B2DLineJoin::NONE; + } + } + } + + basegfx::B2DVector RectPointToB2DVector(RectPoint eRectPoint) + { + basegfx::B2DVector aRetval(0.0, 0.0); + + // position changes X + switch(eRectPoint) + { + case RectPoint::LT: case RectPoint::LM: case RectPoint::LB: + { + aRetval.setX(-1.0); + break; + } + + case RectPoint::RT: case RectPoint::RM: case RectPoint::RB: + { + aRetval.setX(1.0); + break; + } + + default : + { + break; + } + } + + // position changes Y + switch(eRectPoint) + { + case RectPoint::LT: case RectPoint::MT: case RectPoint::RT: + { + aRetval.setY(-1.0); + break; + } + + case RectPoint::LB: case RectPoint::MB: case RectPoint::RB: + { + aRetval.setY(1.0); + break; + } + + default : + { + break; + } + } + + return aRetval; + } + + attribute::SdrGlowAttribute createNewSdrGlowAttribute(const SfxItemSet& rSet) + { + sal_Int32 nRadius = rSet.Get(SDRATTR_GLOW_RADIUS).GetValue(); + if (!nRadius) + return attribute::SdrGlowAttribute(); + Color aColor(rSet.Get(SDRATTR_GLOW_COLOR).GetColorValue()); + sal_uInt16 nTransparency(rSet.Get(SDRATTR_GLOW_TRANSPARENCY).GetValue()); + if (nTransparency) + aColor.SetAlpha(255 - std::round(nTransparency / 100.0 * 255.0)); + + attribute::SdrGlowAttribute glowAttr{ nRadius, aColor }; + return glowAttr; + } + + sal_Int32 getSoftEdgeRadius(const SfxItemSet& rSet) + { + return rSet.Get(SDRATTR_SOFTEDGE_RADIUS).GetValue(); + } + } // end of anonymous namespace +} // end of namespace drawinglayer + + +namespace drawinglayer::primitive2d +{ + attribute::SdrLineAttribute createNewSdrLineAttribute(const SfxItemSet& rSet) + { + const css::drawing::LineStyle eStyle(rSet.Get(XATTR_LINESTYLE).GetValue()); + + if(drawing::LineStyle_NONE != eStyle) + { + sal_uInt16 nTransparence(rSet.Get(XATTR_LINETRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(100 != nTransparence) + { + const sal_uInt32 nWidth(rSet.Get(XATTR_LINEWIDTH).GetValue()); + const Color aColor(rSet.Get(XATTR_LINECOLOR).GetColorValue()); + const css::drawing::LineJoint eJoint(rSet.Get(XATTR_LINEJOINT).GetValue()); + const css::drawing::LineCap eCap(rSet.Get(XATTR_LINECAP).GetValue()); + ::std::vector< double > aDotDashArray; + double fFullDotDashLen(0.0); + + if(drawing::LineStyle_DASH == eStyle) + { + const XDash& rDash = rSet.Get(XATTR_LINEDASH).GetDashValue(); + + if(rDash.GetDots() || rDash.GetDashes()) + { + fFullDotDashLen = rDash.CreateDotDashArray(aDotDashArray, static_cast<double>(nWidth)); + } + } + + return attribute::SdrLineAttribute( + LineJointToB2DLineJoin(eJoint), + static_cast<double>(nWidth), + static_cast<double>(nTransparence) * 0.01, + aColor.getBColor(), + eCap, + std::move(aDotDashArray), + fFullDotDashLen); + } + } + + return attribute::SdrLineAttribute(); + } + + attribute::SdrLineStartEndAttribute createNewSdrLineStartEndAttribute( + const SfxItemSet& rSet, + double fWidth) + { + const sal_Int32 nTempStartWidth(rSet.Get(XATTR_LINESTARTWIDTH).GetValue()); + const sal_Int32 nTempEndWidth(rSet.Get(XATTR_LINEENDWIDTH).GetValue()); + basegfx::B2DPolyPolygon aStartPolyPolygon; + basegfx::B2DPolyPolygon aEndPolyPolygon; + double fStartWidth(0.0); + double fEndWidth(0.0); + bool bStartActive(false); + bool bEndActive(false); + bool bStartCentered(true); + bool bEndCentered(true); + + if(nTempStartWidth) + { + if(nTempStartWidth < 0) + { + fStartWidth = (static_cast<double>(-nTempStartWidth) * fWidth) * 0.01; + } + else + { + fStartWidth = static_cast<double>(nTempStartWidth); + } + + if(0.0 != fStartWidth) + { + aStartPolyPolygon = rSet.Get(XATTR_LINESTART).GetLineStartValue(); + + if(aStartPolyPolygon.count() && aStartPolyPolygon.getB2DPolygon(0).count()) + { + bStartActive = true; + bStartCentered = rSet.Get(XATTR_LINESTARTCENTER).GetValue(); + } + } + } + + if(nTempEndWidth) + { + if(nTempEndWidth < 0) + { + fEndWidth = (static_cast<double>(-nTempEndWidth) * fWidth) * 0.01; + } + else + { + fEndWidth = static_cast<double>(nTempEndWidth); + } + + if(0.0 != fEndWidth) + { + aEndPolyPolygon = rSet.Get(XATTR_LINEEND).GetLineEndValue(); + + if(aEndPolyPolygon.count() && aEndPolyPolygon.getB2DPolygon(0).count()) + { + bEndActive = true; + bEndCentered = rSet.Get(XATTR_LINEENDCENTER).GetValue(); + } + } + } + + if(bStartActive || bEndActive) + { + return attribute::SdrLineStartEndAttribute( + aStartPolyPolygon, aEndPolyPolygon, fStartWidth, fEndWidth, + bStartActive, bEndActive, bStartCentered, bEndCentered); + } + + return attribute::SdrLineStartEndAttribute(); + } + + attribute::SdrShadowAttribute createNewSdrShadowAttribute(const SfxItemSet& rSet) + { + const bool bShadow(rSet.Get(SDRATTR_SHADOW).GetValue()); + + if(bShadow) + { + sal_uInt16 nTransparence(rSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(nTransparence) + { + sal_uInt16 nFillTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue()); + + if(nFillTransparence > 100) + { + nFillTransparence = 100; + } + + if(nTransparence == nFillTransparence) + { + // shadow does not really have an own transparence, but the application + // sets the shadow transparence equal to the object transparence for + // convenience. This is not useful for primitive creation, so take + // this as no shadow transparence + nTransparence = 0; + } + } + + if(100 != nTransparence) + { + const basegfx::B2DVector aOffset( + static_cast<double>(rSet.Get(SDRATTR_SHADOWXDIST).GetValue()), + static_cast<double>(rSet.Get(SDRATTR_SHADOWYDIST).GetValue())); + + const basegfx::B2DVector aSize( + static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEX).GetValue()), + static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEY).GetValue())); + + const Color aColor(rSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue()); + + sal_Int32 nBlur(rSet.Get(SDRATTR_SHADOWBLUR).GetValue()); + + return attribute::SdrShadowAttribute(aOffset, aSize, static_cast<double>(nTransparence) * 0.01,nBlur, aColor.getBColor()); + } + } + + return attribute::SdrShadowAttribute(); + } + + attribute::SdrFillAttribute createNewSdrFillAttribute(const SfxItemSet& rSet) + { + const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue()); + + sal_uInt16 nTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue()); + + if(nTransparence > 100) + { + nTransparence = 100; + } + + if(drawing::FillStyle_NONE == eStyle) + { + XFillUseSlideBackgroundItem aBckItem(rSet.Get(XATTR_FILLUSESLIDEBACKGROUND)); + const bool bSlideBackgroundFill(aBckItem.GetValue()); + + if(bSlideBackgroundFill) + { + // we have SlideBackgroundFill mode, create a + // SdrFillAttribute accordingly + return attribute::SdrFillAttribute(true); + } + } + + if(drawing::FillStyle_NONE != eStyle) + { + if(100 != nTransparence) + { + // need to check XFillFloatTransparence, object fill may still be completely transparent + const XFillFloatTransparenceItem* pGradientItem; + + if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE, true)) + && pGradientItem->IsEnabled()) + { + const XGradient& rGradient = pGradientItem->GetGradientValue(); + const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance()); + const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance()); + const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); + + if(bCompletelyTransparent) + { + nTransparence = 100; + } + } + } + + if(100 != nTransparence) + { + const Color aColor(rSet.Get(XATTR_FILLCOLOR).GetColorValue()); + attribute::FillGradientAttribute aGradient; + attribute::FillHatchAttribute aHatch; + attribute::SdrFillGraphicAttribute aFillGraphic; + + switch(eStyle) + { + default: + { + // nothing to do, color is defined + break; + } + case drawing::FillStyle_GRADIENT : + { + XGradient aXGradient(rSet.Get(XATTR_FILLGRADIENT).GetGradientValue()); + + const Color aStartColor(aXGradient.GetStartColor()); + const sal_uInt16 nStartIntens(aXGradient.GetStartIntens()); + basegfx::BColor aStart(aStartColor.getBColor()); + + if(nStartIntens != 100) + { + const basegfx::BColor aBlack; + aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01); + } + + const Color aEndColor(aXGradient.GetEndColor()); + const sal_uInt16 nEndIntens(aXGradient.GetEndIntens()); + basegfx::BColor aEnd(aEndColor.getBColor()); + + if(nEndIntens != 100) + { + const basegfx::BColor aBlack; + aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01); + } + + aGradient = attribute::FillGradientAttribute( + XGradientStyleToGradientStyle(aXGradient.GetGradientStyle()), + static_cast<double>(aXGradient.GetBorder()) * 0.01, + static_cast<double>(aXGradient.GetXOffset()) * 0.01, + static_cast<double>(aXGradient.GetYOffset()) * 0.01, + toRadians(aXGradient.GetAngle()), + aStart, + aEnd, + rSet.Get(XATTR_GRADIENTSTEPCOUNT).GetValue()); + + break; + } + case drawing::FillStyle_HATCH : + { + const XHatch& rHatch(rSet.Get(XATTR_FILLHATCH).GetHatchValue()); + const Color aColorB(rHatch.GetColor()); + + aHatch = attribute::FillHatchAttribute( + XHatchStyleToHatchStyle(rHatch.GetHatchStyle()), + static_cast<double>(rHatch.GetDistance()), + toRadians(rHatch.GetAngle()), + aColorB.getBColor(), + 3, // same default as VCL, a minimum of three discrete units (pixels) offset + rSet.Get(XATTR_FILLBACKGROUND).GetValue()); + + break; + } + case drawing::FillStyle_BITMAP : + { + aFillGraphic = createNewSdrFillGraphicAttribute(rSet); + break; + } + } + + return attribute::SdrFillAttribute( + static_cast<double>(nTransparence) * 0.01, + aColor.getBColor(), + aGradient, + aHatch, + aFillGraphic); + } + } + + if(nTransparence == 100) + { + attribute::FillGradientAttribute aGradient; + attribute::FillHatchAttribute aHatch; + attribute::SdrFillGraphicAttribute aFillGraphic; + return attribute::SdrFillAttribute( + 1, + basegfx::BColor( 0, 0, 0 ), + aGradient, + aHatch, + aFillGraphic); + } + + return attribute::SdrFillAttribute(); + } + + // #i101508# Support handing over given text-to-border distances + attribute::SdrTextAttribute createNewSdrTextAttribute( + const SfxItemSet& rSet, + const SdrText& rText, + const sal_Int32* pLeft, + const sal_Int32* pUpper, + const sal_Int32* pRight, + const sal_Int32* pLower) + { + const SdrTextObj& rTextObj = rText.GetObject(); + + // Save chaining attributes + bool bChainable = rTextObj.IsChainable(); + + + if(rText.GetOutlinerParaObject()) + { + // added TextEdit text suppression + bool bInEditMode(false); + + if(rText.GetObject().getTextCount() > 1) + { + bInEditMode = rTextObj.IsInEditMode() && rText.GetObject().getActiveText() == &rText; + } + else + { + bInEditMode = rTextObj.IsInEditMode(); + } + + OutlinerParaObject aOutlinerParaObject(*rText.GetOutlinerParaObject()); + + if(bInEditMode) + { + std::optional<OutlinerParaObject> pTempObj = rTextObj.CreateEditOutlinerParaObject(); + + if(pTempObj) + { + aOutlinerParaObject = *pTempObj; + } + else + { + // #i100537# + // CreateEditOutlinerParaObject() returning no object does not mean that + // text edit mode is not active. Do not reset the flag here + // bInEditMode = false; + } + } + + const SdrTextAniKind eAniKind(rTextObj.GetTextAniKind()); + + // #i107346# + const SdrOutliner& rDrawTextOutliner(rText.GetObject().getSdrModelFromSdrObject().GetDrawOutliner(&rTextObj)); + const bool bWrongSpell(rDrawTextOutliner.GetControlWord() & EEControlBits::ONLINESPELLING); + + return attribute::SdrTextAttribute( + rText, + aOutlinerParaObject, + rSet.Get(XATTR_FORMTXTSTYLE).GetValue(), + pLeft ? *pLeft : rTextObj.GetTextLeftDistance(), + pUpper ? *pUpper : rTextObj.GetTextUpperDistance(), + pRight ? *pRight : rTextObj.GetTextRightDistance(), + pLower ? *pLower : rTextObj.GetTextLowerDistance(), + rTextObj.GetTextHorizontalAdjust(rSet), + rTextObj.GetTextVerticalAdjust(rSet), + rSet.Get(SDRATTR_TEXT_CONTOURFRAME).GetValue(), + rTextObj.IsFitToSize(), + rTextObj.IsAutoFit(), + rSet.Get(XATTR_FORMTXTHIDEFORM).GetValue(), + SdrTextAniKind::Blink == eAniKind, + SdrTextAniKind::Scroll == eAniKind || SdrTextAniKind::Alternate == eAniKind || SdrTextAniKind::Slide == eAniKind, + bInEditMode, + rSet.Get(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue(), + bWrongSpell, + bChainable); + } + + return attribute::SdrTextAttribute(); + } + + attribute::FillGradientAttribute createNewTransparenceGradientAttribute(const SfxItemSet& rSet) + { + const XFillFloatTransparenceItem* pGradientItem; + + if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE)) + && pGradientItem->IsEnabled()) + { + // test if float transparence is completely transparent + const XGradient& rGradient = pGradientItem->GetGradientValue(); + const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance()); + const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance()); + const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance); + const bool bNotTransparent(0x00 == nStartLuminance && 0x00 == nEndLuminance); + + // create nothing when completely transparent: This case is already checked for the + // normal fill attributes, XFILL_NONE will be used. + // create nothing when not transparent: use normal fill, no need t create a FillGradientAttribute. + // Both cases are optimizations, always creating FillGradientAttribute will work, too + if(!bNotTransparent && !bCompletelyTransparent) + { + const double fStartLum(nStartLuminance / 255.0); + const double fEndLum(nEndLuminance / 255.0); + + return attribute::FillGradientAttribute( + XGradientStyleToGradientStyle(rGradient.GetGradientStyle()), + static_cast<double>(rGradient.GetBorder()) * 0.01, + static_cast<double>(rGradient.GetXOffset()) * 0.01, + static_cast<double>(rGradient.GetYOffset()) * 0.01, + toRadians(rGradient.GetAngle()), + basegfx::BColor(fStartLum, fStartLum, fStartLum), + basegfx::BColor(fEndLum, fEndLum, fEndLum)); + } + } + + return attribute::FillGradientAttribute(); + } + + attribute::SdrFillGraphicAttribute createNewSdrFillGraphicAttribute(const SfxItemSet& rSet) + { + Graphic aGraphic(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic()); + + OUString aOriginURL = aGraphic.getOriginURL(); + if (aGraphic.GetType() == GraphicType::Default && !aOriginURL.isEmpty()) + { + aGraphic = vcl::graphic::loadFromURL(aGraphic.getOriginURL()); + aGraphic.setOriginURL(aOriginURL); + } + + if(GraphicType::Bitmap != aGraphic.GetType() && GraphicType::GdiMetafile != aGraphic.GetType()) + { + // no content if not bitmap or metafile + OSL_ENSURE(false, "No fill graphic in SfxItemSet (!)"); + return attribute::SdrFillGraphicAttribute(); + } + + Size aPrefSize(aGraphic.GetPrefSize()); + + if(!aPrefSize.Width() || !aPrefSize.Height()) + { + // if there is no logical size, create a size from pixel size and set MapMode accordingly + if(GraphicType::Bitmap == aGraphic.GetType()) + { + aGraphic.SetPrefSize(aGraphic.GetBitmapEx().GetSizePixel()); + aGraphic.SetPrefMapMode(MapMode(MapUnit::MapPixel)); + aPrefSize = aGraphic.GetPrefSize(); + } + } + + if(!aPrefSize.Width() || !aPrefSize.Height()) + { + // no content if no size + OSL_ENSURE(false, "Graphic has no size in SfxItemSet (!)"); + return attribute::SdrFillGraphicAttribute(); + } + + // convert size and MapMode to destination logical size and MapMode + const MapUnit aDestinationMapUnit(rSet.GetPool()->GetMetric(0)); + basegfx::B2DVector aGraphicLogicSize(aGraphic.GetPrefSize().Width(), aGraphic.GetPrefSize().Height()); + + if (aGraphic.GetPrefMapMode().GetMapUnit() != aDestinationMapUnit) + { + // #i100360# for MapUnit::MapPixel, LogicToLogic will not work properly, + // so fallback to Application::GetDefaultDevice() + Size aNewSize(0, 0); + + if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit()) + { + aNewSize = Application::GetDefaultDevice()->PixelToLogic( + aGraphic.GetPrefSize(), + MapMode(aDestinationMapUnit)); + } + else + { + aNewSize = OutputDevice::LogicToLogic( + aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), + MapMode(aDestinationMapUnit)); + } + + // #i124002# do not set new size using SetPrefSize at the graphic, this will lead to problems. + // Instead, adapt the GraphicLogicSize which will be used for further decompositions + aGraphicLogicSize = basegfx::B2DVector(aNewSize.Width(), aNewSize.Height()); + } + + // get size + const basegfx::B2DVector aSize( + static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEY).GetValue())); + const basegfx::B2DVector aOffset( + static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETY).GetValue())); + const basegfx::B2DVector aOffsetPosition( + static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETX).GetValue()), + static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETY).GetValue())); + + return attribute::SdrFillGraphicAttribute( + aGraphic, + aGraphicLogicSize, + aSize, + aOffset, + aOffsetPosition, + RectPointToB2DVector(rSet.GetItem<XFillBmpPosItem>(XATTR_FILLBMP_POS)->GetValue()), + rSet.Get(XATTR_FILLBMP_TILE).GetValue(), + rSet.Get(XATTR_FILLBMP_STRETCH).GetValue(), + rSet.Get(XATTR_FILLBMP_SIZELOG).GetValue()); + } + + attribute::SdrEffectsTextAttribute createNewSdrEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + bool bSuppressText) + { + attribute::SdrTextAttribute aText; + + // #i98072# added option to suppress text + // look for text first + if(!bSuppressText && pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + } + + // try shadow + const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + const attribute::SdrGlowAttribute aGlow(createNewSdrGlowAttribute(rSet)); + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrEffectsTextAttribute(aShadow, aText, aGlow, nSoftEdgeRadius); + } + + attribute::SdrLineEffectsTextAttribute createNewSdrLineEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText) + { + attribute::SdrLineAttribute aLine; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // line and fill style to empty + if(!aText.isDefault() + && !aText.getSdrFormTextAttribute().isDefault() + && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + // try line style + if(!bFontworkHideContour) + { + aLine = createNewSdrLineAttribute(rSet); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + } + + if(!aLine.isDefault() || !aText.isDefault()) + { + // try shadow + const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet)); + const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet); + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrLineEffectsTextAttribute(aLine, aLineStartEnd, aShadow, aText, + aGlow, nSoftEdgeRadius); + } + + return attribute::SdrLineEffectsTextAttribute(); + } + + attribute::SdrLineFillEffectsTextAttribute createNewSdrLineFillEffectsTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + bool bHasContent) + { + attribute::SdrLineAttribute aLine; + attribute::SdrFillAttribute aFill; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::FillGradientAttribute aFillFloatTransGradient; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // line and fill style to empty + if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + if(!bFontworkHideContour) + { + // try line style + aLine = createNewSdrLineAttribute(rSet); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + + // try fill style + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + // bHasContent is used from OLE and graphic objects. Normally a possible shadow + // depends on line, fill or text to be set, but for these objects it is possible + // to have none of these, but still content which needs to have a shadow (if set), + // so shadow needs to be tried + if(bHasContent || !aLine.isDefault() || !aFill.isDefault() || !aText.isDefault()) + { + // try shadow + const attribute::SdrShadowAttribute aShadow = createNewSdrShadowAttribute(rSet); + + // glow + const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet); + + const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet)); + + return attribute::SdrLineFillEffectsTextAttribute(aLine, aFill, aLineStartEnd, + aShadow, aFillFloatTransGradient, + aText, aGlow, nSoftEdgeRadius); + } + + return attribute::SdrLineFillEffectsTextAttribute(); + } + + attribute::SdrLineFillShadowAttribute3D createNewSdrLineFillShadowAttribute(const SfxItemSet& rSet, bool bSuppressFill) + { + attribute::SdrFillAttribute aFill; + attribute::SdrLineStartEndAttribute aLineStartEnd; + attribute::SdrShadowAttribute aShadow; + attribute::FillGradientAttribute aFillFloatTransGradient; + + // try line style + const attribute::SdrLineAttribute aLine(createNewSdrLineAttribute(rSet)); + + if(!aLine.isDefault()) + { + // try LineStartEnd + aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()); + } + + // try fill style + if(!bSuppressFill) + { + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + if(!aLine.isDefault() || !aFill.isDefault()) + { + // try shadow + aShadow = createNewSdrShadowAttribute(rSet); + + return attribute::SdrLineFillShadowAttribute3D( + aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient); + } + + return attribute::SdrLineFillShadowAttribute3D(); + } + + attribute::SdrSceneAttribute createNewSdrSceneAttribute(const SfxItemSet& rSet) + { + // get perspective + css::drawing::ProjectionMode aProjectionMode(css::drawing::ProjectionMode_PARALLEL); + const sal_uInt16 nProjectionValue(rSet.Get(SDRATTR_3DSCENE_PERSPECTIVE).GetValue()); + + if(1 == nProjectionValue) + { + aProjectionMode = css::drawing::ProjectionMode_PERSPECTIVE; + } + + // get distance + const double fDistance(rSet.Get(SDRATTR_3DSCENE_DISTANCE).GetValue()); + + // get shadow slant + const double fShadowSlant( + basegfx::deg2rad(rSet.Get(SDRATTR_3DSCENE_SHADOW_SLANT).GetValue())); + + // get shade mode + css::drawing::ShadeMode aShadeMode(css::drawing::ShadeMode_FLAT); + const sal_uInt16 nShadeValue(rSet.Get(SDRATTR_3DSCENE_SHADE_MODE).GetValue()); + + if(1 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_PHONG; + } + else if(2 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_SMOOTH; + } + else if(3 == nShadeValue) + { + aShadeMode = css::drawing::ShadeMode_DRAFT; + } + + // get two sided lighting + const bool bTwoSidedLighting(rSet.Get(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING).GetValue()); + + return attribute::SdrSceneAttribute(fDistance, fShadowSlant, aProjectionMode, aShadeMode, bTwoSidedLighting); + } + + attribute::SdrLightingAttribute createNewSdrLightingAttribute(const SfxItemSet& rSet) + { + // extract lights from given SfxItemSet (from scene) + ::std::vector< attribute::Sdr3DLightAttribute > aLightVector; + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue()); + aLightVector.emplace_back(aColor, aDirection, true); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue()) + { + const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue().getBColor()); + const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue()); + aLightVector.emplace_back(aColor, aDirection, false); + } + + // get ambient color + const Color aAmbientValue(rSet.Get(SDRATTR_3DSCENE_AMBIENTCOLOR).GetValue()); + const basegfx::BColor aAmbientLight(aAmbientValue.getBColor()); + + return attribute::SdrLightingAttribute(aAmbientLight, std::move(aLightVector)); + } + + void calculateRelativeCornerRadius(sal_Int32 nRadius, const basegfx::B2DRange& rObjectRange, double& rfCornerRadiusX, double& rfCornerRadiusY) + { + rfCornerRadiusX = rfCornerRadiusY = static_cast<double>(nRadius); + + if(0.0 != rfCornerRadiusX) + { + const double fHalfObjectWidth(rObjectRange.getWidth() * 0.5); + + if(0.0 != fHalfObjectWidth) + { + if(rfCornerRadiusX < 0.0) + { + rfCornerRadiusX = 0.0; + } + + if(rfCornerRadiusX > fHalfObjectWidth) + { + rfCornerRadiusX = fHalfObjectWidth; + } + + rfCornerRadiusX /= fHalfObjectWidth; + } + else + { + rfCornerRadiusX = 0.0; + } + } + + if(0.0 == rfCornerRadiusY) + return; + + const double fHalfObjectHeight(rObjectRange.getHeight() * 0.5); + + if(0.0 != fHalfObjectHeight) + { + if(rfCornerRadiusY < 0.0) + { + rfCornerRadiusY = 0.0; + } + + if(rfCornerRadiusY > fHalfObjectHeight) + { + rfCornerRadiusY = fHalfObjectHeight; + } + + rfCornerRadiusY /= fHalfObjectHeight; + } + else + { + rfCornerRadiusY = 0.0; + } + } + + // #i101508# Support handing over given text-to-border distances + attribute::SdrFillTextAttribute createNewSdrFillTextAttribute( + const SfxItemSet& rSet, + const SdrText* pText, + const sal_Int32* pLeft, + const sal_Int32* pUpper, + const sal_Int32* pRight, + const sal_Int32* pLower) + { + attribute::SdrFillAttribute aFill; + attribute::FillGradientAttribute aFillFloatTransGradient; + attribute::SdrTextAttribute aText; + bool bFontworkHideContour(false); + + // look for text first + if(pText) + { + aText = createNewSdrTextAttribute(rSet, *pText, pLeft, pUpper, pRight, pLower); + + // when object has text and text is fontwork and hide contour is set for fontwork, force + // fill style to empty + if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour()) + { + bFontworkHideContour = true; + } + } + + if(!bFontworkHideContour) + { + // try fill style + aFill = createNewSdrFillAttribute(rSet); + + if(!aFill.isDefault()) + { + // try fillfloattransparence + aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet); + } + } + + if(!aFill.isDefault() || !aText.isDefault()) + { + return attribute::SdrFillTextAttribute(aFill, aFillFloatTransGradient, aText); + } + + return attribute::SdrFillTextAttribute(); + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx new file mode 100644 index 000000000..50787ed39 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx @@ -0,0 +1,159 @@ +/* -*- 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/primitive2d/sdrcaptionprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrCaptionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect( + basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), + getCornerRadiusX(), + getCornerRadiusY())); + + // add fill + if(getSdrLFSTAttribute().getFill().isDefault()) + { + // create invisible fill for HitTest + aRetval.push_back( + createHiddenGeometryPrimitives2D( + true, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(getTail()), + {})); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + + aRetval.push_back( + createPolygonLinePrimitive( + getTail(), + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrCaptionPrimitive2D::SdrCaptionPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const basegfx::B2DPolygon& rTail, + double fCornerRadiusX, + double fCornerRadiusY) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + maTail(rTail), + mfCornerRadiusX(fCornerRadiusX), + mfCornerRadiusY(fCornerRadiusY) + { + } + + bool SdrCaptionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrCaptionPrimitive2D& rCompare = static_cast<const SdrCaptionPrimitive2D&>(rPrimitive); + + return (getCornerRadiusX() == rCompare.getCornerRadiusX() + && getCornerRadiusY() == rCompare.getCornerRadiusY() + && getTail() == rCompare.getTail() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrCaptionPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx new file mode 100644 index 000000000..aa663fb7a --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx @@ -0,0 +1,107 @@ +/* -*- 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/primitive2d/sdrconnectorprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrConnectorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // add line + if(getSdrLSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + basegfx::B2DPolyPolygon(getUnitPolygon()))); + } + else + { + aRetval.push_back( + createPolygonLinePrimitive( + getUnitPolygon(), + getSdrLSTAttribute().getLine(), + getSdrLSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(getUnitPolygon()), + basegfx::B2DHomMatrix(), + getSdrLSTAttribute().getText(), + getSdrLSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrConnectorPrimitive2D::SdrConnectorPrimitive2D( + const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute, + const ::basegfx::B2DPolygon& rUnitPolygon) + : maSdrLSTAttribute(rSdrLSTAttribute), + maUnitPolygon(rUnitPolygon) + { + } + + bool SdrConnectorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrConnectorPrimitive2D& rCompare = static_cast<const SdrConnectorPrimitive2D&>(rPrimitive); + + return (getUnitPolygon() == rCompare.getUnitPolygon() + && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrConnectorPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx new file mode 100644 index 000000000..dddb8aa30 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx @@ -0,0 +1,130 @@ +/* -*- 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/primitive2d/sdrcustomshapeprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/attribute/sdrlineattribute.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrCustomShapePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval(getSubPrimitives()); + + // Soft edges should be before text, since text is not affected by soft edges + if (!aRetval.empty() && getSdrSTAttribute().getSoftEdgeRadius()) + { + aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval), + getSdrSTAttribute().getSoftEdgeRadius()); + } + + // add text + if(!getSdrSTAttribute().getText().isDefault()) + { + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTextBox(), + getSdrSTAttribute().getText(), + attribute::SdrLineAttribute(), + false, + getWordWrap())); + } + + // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite + if (!aRetval.empty() && !getSdrSTAttribute().getGlow().isDefault()) + { + // glow + aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrSTAttribute().getGlow()); + } + + // add shadow + if(!aRetval.empty() && !getSdrSTAttribute().getShadow().isDefault()) + { + // #i105323# add generic shadow only for 2D shapes. For + // 3D shapes shadow will be set at the individual created + // visualisation objects and be visualized by the 3d renderer + // as a single shadow. + + // The shadow for AutoShapes could be handled uniformly by not setting any + // shadow items at the helper model objects and only adding shadow here for + // 2D and 3D (and it works, too), but this would lead to two 3D scenes for + // the 3D object; one for the shadow and one for the content. The one for the + // shadow will be correct (using ColorModifierStack), but expensive. + if(!get3DShape()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrSTAttribute().getShadow(), + maTransform); + } + } + + rContainer.append(std::move(aRetval)); + } + + SdrCustomShapePrimitive2D::SdrCustomShapePrimitive2D( + const attribute::SdrEffectsTextAttribute& rSdrSTAttribute, + Primitive2DContainer&& rSubPrimitives, + const basegfx::B2DHomMatrix& rTextBox, + bool bWordWrap, + bool b3DShape, + const basegfx::B2DHomMatrix& rTransform) + : maSdrSTAttribute(rSdrSTAttribute), + maSubPrimitives(std::move(rSubPrimitives)), + maTextBox(rTextBox), + mbWordWrap(bWordWrap), + mb3DShape(b3DShape), + maTransform(rTransform) + { + } + + bool SdrCustomShapePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrCustomShapePrimitive2D& rCompare = static_cast<const SdrCustomShapePrimitive2D&>(rPrimitive); + + return (getSdrSTAttribute() == rCompare.getSdrSTAttribute() + && getSubPrimitives() == rCompare.getSubPrimitives() + && getTextBox() == rCompare.getTextBox() + && getWordWrap() == rCompare.getWordWrap() + && get3DShape() == rCompare.get3DShape()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrCustomShapePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx new file mode 100644 index 000000000..a1838b03f --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx @@ -0,0 +1,884 @@ +/* -*- 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/primitive2d/sdrdecompositiontools.hxx> +#include <drawinglayer/primitive2d/baseprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx> +#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> +#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> +#include <drawinglayer/attribute/linestartendattribute.hxx> +#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/shadowprimitive2d.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <drawinglayer/primitive2d/glowprimitive2d.hxx> +#include <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <svx/svdotext.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/animatedprimitive2d.hxx> +#include <drawinglayer/animation/animationtiming.hxx> +#include <drawinglayer/primitive2d/maskprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <drawinglayer/attribute/sdrfillattribute.hxx> +#include <drawinglayer/attribute/sdrlineattribute.hxx> +#include <drawinglayer/attribute/sdrlinestartendattribute.hxx> +#include <drawinglayer/attribute/sdrshadowattribute.hxx> +#include <drawinglayer/attribute/sdrglowattribute.hxx> +#include <osl/diagnose.h> + +// for SlideBackgroundFillPrimitive2D +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdpage.hxx> +#include <sdr/primitive2d/sdrattributecreator.hxx> +#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx> + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ +namespace +{ +// See also: SdrTextObj::AdjustRectToTextDistance +basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText, + const basegfx::B2DRange& rSnapRange) +{ + // Take vertical text orientation into account when deciding + // which dimension is its width, and which is its height + const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject(); + const bool bVerticalWriting(rOutlinerParaObj.IsEffectivelyVertical()); + const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth(); + // create a range describing the wanted text position and size (aTextAnchorRange). This + // means to use the text distance values here + // If the margin is larger than the entire width of the text area, then limit the + // margin. + const double fTextLeftDistance + = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText); + const double nTextRightDistance + = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText); + double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB; + if (!bVerticalWriting) + { + fDistanceForTextL = fTextLeftDistance; + fDistanceForTextT = rText.getTextUpperDistance(); + fDistanceForTextR = nTextRightDistance; + fDistanceForTextB = rText.getTextLowerDistance(); + } + else if (rOutlinerParaObj.IsTopToBottom()) + { + fDistanceForTextL = rText.getTextLowerDistance(); + fDistanceForTextT = fTextLeftDistance; + fDistanceForTextR = rText.getTextUpperDistance(); + fDistanceForTextB = nTextRightDistance; + } + else + { + fDistanceForTextL = rText.getTextUpperDistance(); + fDistanceForTextT = nTextRightDistance; + fDistanceForTextR = rText.getTextLowerDistance(); + fDistanceForTextB = fTextLeftDistance; + } + const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL, + rSnapRange.getMinY() + fDistanceForTextT); + const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR, + rSnapRange.getMaxY() - fDistanceForTextB); + basegfx::B2DRange aAnchorRange; + aAnchorRange.expand(aTopLeft); + aAnchorRange.expand(aBottomRight); + + // If the shape has no width, then don't attempt to break the text into multiple + // lines, not a single character would satisfy a zero width requirement. + // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to + // effectively set no limits. + if (!bVerticalWriting && aAnchorRange.getWidth() == 0) + { + aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY())); + aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY())); + } + else if (bVerticalWriting && aAnchorRange.getHeight() == 0) + { + aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000)); + aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000)); + } + return aAnchorRange; +} + +drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute( + const geometry::ViewInformation2D& rViewInformation, + basegfx::B2DVector& rPageSize) +{ + drawinglayer::attribute::SdrFillAttribute aRetval; + + // get SdrPage + const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage())); + + if(nullptr != pVisualizedPage) + { + // copy needed values for further processing + rPageSize.setX(pVisualizedPage->GetWidth()); + rPageSize.setY(pVisualizedPage->GetHeight()); + + if(pVisualizedPage->IsMasterPage()) + { + // the page is a MasterPage, so we are in MasterPage view + // still need #i110846#, see ViewContactOfMasterPage + if(pVisualizedPage->getSdrPageProperties().GetStyleSheet()) + { + // create page fill attributes with correct properties + aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute( + pVisualizedPage->getSdrPageProperties().GetItemSet()); + } + } + else + { + // the page is *no* MasterPage, we are in normal Page view, get the MasterPage + if(pVisualizedPage->TRG_HasMasterPage()) + { + sdr::contact::ViewContact& rVC(pVisualizedPage->TRG_GetMasterPageDescriptorViewContact()); + sdr::contact::ViewContactOfMasterPageDescriptor* pVCOMPD( + dynamic_cast<sdr::contact::ViewContactOfMasterPageDescriptor*>(&rVC)); + + if(nullptr != pVCOMPD) + { + // in this case the still needed #i110846# is part of + // getCorrectSdrPageProperties, that's the main reason to re-use + // that call/functionality here + const SdrPageProperties* pCorrectProperties( + pVCOMPD->GetMasterPageDescriptor().getCorrectSdrPageProperties()); + + if(pCorrectProperties) + { + // create page fill attributes when correct properties were identified + aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute( + pCorrectProperties->GetItemSet()); + } + } + } + } + } + + return aRetval; +} + +// provide a Primitive2D for the SlideBackgroundFill-mode. It is capable +// of expressing that state of fill and it's decomposition implements all +// needed preparation of the geometry in an isolated and controllable +// space and way. +// It is currently simple buffered (due to being derived from +// BufferedDecompositionPrimitive2D) and detects if FillStyle changes +class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D +{ +private: + /// the basegfx::B2DPolyPolygon geometry + basegfx::B2DPolyPolygon maPolyPolygon; + + /// the last SdrFillAttribute the geometry was created for + drawinglayer::attribute::SdrFillAttribute maLastFill; + +protected: + // create decomposition data + virtual void create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& rViewInformation) const override; + +public: + /// constructor + SlideBackgroundFillPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon); + + /// check existing decomposition data, call parent + virtual void get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const override; + + /// data read access + const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; } + + /// compare operator + virtual bool operator==(const BasePrimitive2D& rPrimitive) const override; + + /// get range + virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override; + + /// provide unique ID + virtual sal_uInt32 getPrimitive2DID() const override; +}; + +SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D( + const basegfx::B2DPolyPolygon& rPolyPolygon) +: BufferedDecompositionPrimitive2D() +, maPolyPolygon(rPolyPolygon) +, maLastFill() +{ +} + +void SlideBackgroundFillPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DVector aPageSize; + + // get fill from target Page, this will check for all needed things + // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above) + drawinglayer::attribute::SdrFillAttribute aFill( + getMasterPageFillAttribute(rViewInformation, aPageSize)); + + // if fill is on default (empty), nothing will be shown, we are done + if(aFill.isDefault()) + return; + + // Get PolygonRange of own local geometry + const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange()); + + // if local geometry is empty, nothing will be shown, we are done + if(aPolygonRange.isEmpty()) + return; + + // Get PageRange + const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY()); + + // if local geometry does not overlap with PageRange, nothing will be shown, we are done + if(!aPageRange.overlaps(aPolygonRange)) + return; + + // create FillPrimitive2D with the geometry (the PolyPolygon) and + // the page's definitonRange to: + // - on one hand limit to geometry + // - on the other hand allow continutation of fill outside of + // MasterPage's range + const attribute::FillGradientAttribute aEmptyFillTransparenceGradient; + const Primitive2DReference aCreatedFill( + createPolyPolygonFillPrimitive( + getB2DPolyPolygon(), // geometry + aPageRange, // definition range + aFill, + aEmptyFillTransparenceGradient)); + + rContainer = Primitive2DContainer { aCreatedFill }; +} + +void SlideBackgroundFillPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const +{ + basegfx::B2DVector aPageSize; + drawinglayer::attribute::SdrFillAttribute aFill; + + if(!getBuffered2DDecomposition().empty()) + { + aFill = getMasterPageFillAttribute(rViewInformation, aPageSize); + + if(!(aFill == maLastFill)) + { + // conditions of last local decomposition have changed, delete + const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + } + + if(getBuffered2DDecomposition().empty()) + { + // remember last Fill + const_cast< SlideBackgroundFillPrimitive2D* >(this)->maLastFill = aFill; + } + + // use parent implementation + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); +} + +bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SlideBackgroundFillPrimitive2D& rCompare + = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive); + + return getB2DPolyPolygon() == rCompare.getB2DPolyPolygon(); + } + + return false; +} + +basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange( + const geometry::ViewInformation2D& /*rViewInformation*/) const +{ + // return range + return basegfx::utils::getRange(getB2DPolyPolygon()); +} + +// provide unique ID +sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D; +} + +}; // end of anonymous namespace + + class TransparencePrimitive2D; + + Primitive2DReference createPolyPolygonFillPrimitive( + const basegfx::B2DPolyPolygon& rPolyPolygon, + const attribute::SdrFillAttribute& rFill, + const attribute::FillGradientAttribute& rFillGradient) + { + // when we have no given definition range, use the range of the given geometry + // also for definition (simplest case) + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + + return createPolyPolygonFillPrimitive( + rPolyPolygon, + aRange, + rFill, + rFillGradient); + } + + Primitive2DReference createPolyPolygonFillPrimitive( + const basegfx::B2DPolyPolygon& rPolyPolygon, + const basegfx::B2DRange& rDefinitionRange, + const attribute::SdrFillAttribute& rFill, + const attribute::FillGradientAttribute& rFillGradient) + { + if(basegfx::fTools::moreOrEqual(rFill.getTransparence(), 1.0)) + { + return Primitive2DReference(); + } + + // prepare fully scaled polygon + rtl::Reference<BasePrimitive2D> pNewFillPrimitive; + + if(!rFill.getGradient().isDefault()) + { + pNewFillPrimitive = new PolyPolygonGradientPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getGradient()); + } + else if(!rFill.getHatch().isDefault()) + { + pNewFillPrimitive = new PolyPolygonHatchPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getColor(), + rFill.getHatch()); + } + else if(!rFill.getFillGraphic().isDefault()) + { + pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D( + rPolyPolygon, + rDefinitionRange, + rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange)); + } + else if(rFill.isSlideBackgroundFill()) + { + // create needed Primitive2D representation for + // SlideBackgroundFill-mode + pNewFillPrimitive = new SlideBackgroundFillPrimitive2D( + rPolyPolygon); + } + else + { + pNewFillPrimitive = new PolyPolygonColorPrimitive2D( + rPolyPolygon, + rFill.getColor()); + } + + if(0.0 != rFill.getTransparence()) + { + // create simpleTransparencePrimitive, add created fill primitive + Primitive2DContainer aContent { pNewFillPrimitive }; + return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence())); + } + else if(!rFillGradient.isDefault()) + { + // create sequence with created fill primitive + Primitive2DContainer aContent { pNewFillPrimitive }; + + // create FillGradientPrimitive2D for transparence and add to new sequence + // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways + const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon)); + Primitive2DReference xRefB( + new FillGradientPrimitive2D( + aRange, + rDefinitionRange, + rFillGradient)); + Primitive2DContainer aAlpha { xRefB }; + + // create TransparencePrimitive2D using alpha and content + return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha))); + } + else + { + // add to decomposition + return pNewFillPrimitive; + } + } + + Primitive2DReference createPolygonLinePrimitive( + const basegfx::B2DPolygon& rPolygon, + const attribute::SdrLineAttribute& rLine, + const attribute::SdrLineStartEndAttribute& rStroke) + { + // create line and stroke attribute + const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap()); + const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen()); + rtl::Reference<BasePrimitive2D> pNewLinePrimitive; + + if(!rPolygon.isClosed() && !rStroke.isDefault()) + { + attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered()); + attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered()); + + // create data + pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd); + } + else + { + // create data + pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute); + } + + if(0.0 != rLine.getTransparence()) + { + // create simpleTransparencePrimitive, add created fill primitive + Primitive2DContainer aContent { pNewLinePrimitive }; + return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rLine.getTransparence())); + } + else + { + // add to decomposition + return pNewLinePrimitive; + } + } + + Primitive2DReference createTextPrimitive( + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DHomMatrix& rObjectTransform, + const attribute::SdrTextAttribute& rText, + const attribute::SdrLineAttribute& rStroke, + bool bCellText, + bool bWordWrap) + { + basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform); + rtl::Reference<SdrTextPrimitive2D> pNew; + + if(rText.isContour()) + { + // contour text + if(!rStroke.isDefault() && 0.0 != rStroke.getWidth()) + { + // take line width into account and shrink contour polygon accordingly + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // scale outline to object's size to allow growing with value relative to that size + // and also to keep aspect ratio + basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); + aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix( + fabs(aScale.getX()), fabs(aScale.getY()))); + + // grow the polygon. To shrink, use negative value (half width) + aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5)); + + // scale back to unit polygon + aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix( + 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0, + 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0)); + + // create with unit polygon + pNew = new SdrContourTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aScaledUnitPolyPolygon, + rObjectTransform); + } + else + { + // create with unit polygon + pNew = new SdrContourTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + rUnitPolyPolygon, + rObjectTransform); + } + } + else if(!rText.getSdrFormTextAttribute().isDefault()) + { + // text on path, use scaled polygon + basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon); + aScaledPolyPolygon.transform(rObjectTransform); + pNew = new SdrPathTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aScaledPolyPolygon, + rText.getSdrFormTextAttribute()); + } + else + { + // rObjectTransform is the whole SdrObject transformation from unit rectangle + // to its size and position. Decompose to allow working with single values. + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // extract mirroring + const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); + const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); + aScale = basegfx::absolute(aScale); + + // Get the real size, since polygon outline and scale + // from the object transformation may vary (e.g. ellipse segments) + basegfx::B2DHomMatrix aJustScaleTransform; + aJustScaleTransform.set(0, 0, aScale.getX()); + aJustScaleTransform.set(1, 1, aScale.getY()); + basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon); + aScaledUnitPolyPolygon.transform(aJustScaleTransform); + const basegfx::B2DRange aTextAnchorRange + = getTextAnchorRange(rText, basegfx::utils::getRange(aScaledUnitPolyPolygon)); + + // now create a transformation from this basic range (aTextAnchorRange) + // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for + // mirror values, else these will get lost + aAnchorTransform = basegfx::utils::createScaleTranslateB2DHomMatrix( + basegfx::fTools::equalZero(aTextAnchorRange.getWidth()) ? 1.0 : aTextAnchorRange.getWidth(), + basegfx::fTools::equalZero(aTextAnchorRange.getHeight()) ? 1.0 : aTextAnchorRange.getHeight(), + aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY()); + + // apply mirroring + aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0); + + // apply object's other transforms + aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) + * aAnchorTransform; + + if(rText.isFitToSize()) + { + // stretched text in range + pNew = new SdrStretchTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + rText.isFixedCellHeight()); + } + else if(rText.isAutoFit()) + { + // isotropically scaled text in range + pNew = new SdrAutoFitTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + bWordWrap); + } + else if( rText.isChainable() && !rText.isInEditMode() ) + { + pNew = new SdrChainedTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform ); + } + else // text in range + { + // build new primitive + pNew = new SdrBlockTextPrimitive2D( + &rText.getSdrText(), + rText.getOutlinerParaObject(), + aAnchorTransform, + rText.getSdrTextHorzAdjust(), + rText.getSdrTextVertAdjust(), + rText.isFixedCellHeight(), + rText.isScroll(), + bCellText, + bWordWrap); + } + } + + OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)"); + + if(rText.isBlink()) + { + // prepare animation and primitive list + drawinglayer::animation::AnimationEntryList aAnimationList; + rText.getBlinkTextTiming(aAnimationList); + + if(0.0 != aAnimationList.getDuration()) + { + // create content sequence + Primitive2DReference xRefA(pNew); + Primitive2DContainer aContent { xRefA }; + + // create and add animated switch primitive + return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, std::move(aContent))); + } + else + { + // add to decomposition + return Primitive2DReference(pNew); + } + } + + if(rText.isScroll()) + { + // suppress scroll when FontWork + if(rText.getSdrFormTextAttribute().isDefault()) + { + // get scroll direction + const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection()); + const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection); + + // decompose to get separated values for the scroll box + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX); + + // build transform from scaled only to full AnchorTransform and inverse + const basegfx::B2DHomMatrix aSRT(basegfx::utils::createShearXRotateTranslateB2DHomMatrix( + fShearX, fRotate, aTranslate)); + basegfx::B2DHomMatrix aISRT(aSRT); + aISRT.invert(); + + // bring the primitive back to scaled only and get scaled range, create new clone for this + rtl::Reference<SdrTextPrimitive2D> pNew2 = pNew->createTransformedClone(aISRT); + OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)"); + pNew = pNew2.get(); + + // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay + // since the decompose is view-independent + geometry::ViewInformation2D aViewInformation2D; + + // get range + const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D)); + + // create left outside and right outside transformations. Also take care + // of the clip rectangle + basegfx::B2DHomMatrix aLeft, aRight; + basegfx::B2DPoint aClipTopLeft(0.0, 0.0); + basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY()); + + if(bHorizontal) + { + aClipTopLeft.setY(aScaledRange.getMinY()); + aClipBottomRight.setY(aScaledRange.getMaxY()); + aLeft.translate(-aScaledRange.getMaxX(), 0.0); + aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0); + } + else + { + aClipTopLeft.setX(aScaledRange.getMinX()); + aClipBottomRight.setX(aScaledRange.getMaxX()); + aLeft.translate(0.0, -aScaledRange.getMaxY()); + aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY()); + } + + aLeft *= aSRT; + aRight *= aSRT; + + // prepare animation list + drawinglayer::animation::AnimationEntryList aAnimationList; + + if(bHorizontal) + { + rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth()); + } + else + { + rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight()); + } + + if(0.0 != aAnimationList.getDuration()) + { + // create a new Primitive2DContainer containing the animated text in its scaled only state. + // use the decomposition to force to simple text primitives, those will no longer + // need the outliner for formatting (alternatively it is also possible to just add + // pNew to aNewPrimitiveSequence) + Primitive2DContainer aAnimSequence; + pNew->get2DDecomposition(aAnimSequence, aViewInformation2D); + pNew.clear(); + + // create a new animatedInterpolatePrimitive and add it + Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D({ aLeft, aRight }, aAnimationList, std::move(aAnimSequence))); + Primitive2DContainer aContent { xRefA }; + + // scrolling needs an encapsulating clipping primitive + const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight); + basegfx::B2DPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aClipRange)); + aClipPolygon.transform(aSRT); + return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), std::move(aContent))); + } + else + { + // add to decomposition + return Primitive2DReference(pNew); + } + } + } + + if(rText.isInEditMode()) + { + // #i97628# + // encapsulate with TextHierarchyEditPrimitive2D to allow renderers + // to suppress actively edited content if needed + Primitive2DReference xRefA(pNew); + Primitive2DContainer aContent { xRefA }; + + // create and add TextHierarchyEditPrimitive2D primitive + return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent))); + } + else + { + // add to decomposition + return pNew; + } + } + + Primitive2DContainer createEmbeddedShadowPrimitive( + Primitive2DContainer&& rContent, + const attribute::SdrShadowAttribute& rShadow, + const basegfx::B2DHomMatrix& rObjectMatrix, + const Primitive2DContainer* pContentForShadow) + { + if(rContent.empty()) + return std::move(rContent); + + basegfx::B2DHomMatrix aShadowOffset; + + if(rShadow.getSize().getX() != 100000) + { + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate = 0; + double fShearX = 0; + rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + // Scale the shadow + double nTranslateX = aTranslate.getX(); + double nTranslateY = aTranslate.getY(); + + // The origin for scaling is the top left corner by default. A negative + // shadow offset changes the origin. + if (rShadow.getOffset().getX() < 0) + nTranslateX += aScale.getX(); + if (rShadow.getOffset().getY() < 0) + nTranslateY += aScale.getY(); + + aShadowOffset.translate(-nTranslateX, -nTranslateY); + aShadowOffset.scale(rShadow.getSize().getX() * 0.00001, rShadow.getSize().getY() * 0.00001); + aShadowOffset.translate(nTranslateX, nTranslateY); + } + + aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY()); + + // create shadow primitive and add content + const Primitive2DContainer& rContentForShadow + = pContentForShadow ? *pContentForShadow : rContent; + int nContentWithTransparence = std::count_if( + rContentForShadow.begin(), rContentForShadow.end(), + [](const Primitive2DReference& xChild) { + auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get()); + return pChild && pChild->getTransparenceForShadow() != 0; + }); + if (nContentWithTransparence == 0) + { + Primitive2DContainer aRetval(2); + aRetval[0] = Primitive2DReference( + new ShadowPrimitive2D( + aShadowOffset, + rShadow.getColor(), + rShadow.getBlur(), + Primitive2DContainer(pContentForShadow ? *pContentForShadow : rContent))); + + if (0.0 != rShadow.getTransparence()) + { + // create SimpleTransparencePrimitive2D + Primitive2DContainer aTempContent{ aRetval[0] }; + + aRetval[0] = Primitive2DReference( + new UnifiedTransparencePrimitive2D( + std::move(aTempContent), + rShadow.getTransparence())); + } + + aRetval[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent))); + return aRetval; + } + + Primitive2DContainer aRetval; + for (const auto& xChild : rContentForShadow) + { + double fChildTransparence = 0.0; + auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get()); + if (pChild) + { + fChildTransparence = pChild->getTransparenceForShadow(); + fChildTransparence /= 100; + } + aRetval.push_back(Primitive2DReference( + new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(), + Primitive2DContainer({ xChild })))); + if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0) + { + Primitive2DContainer aTempContent{ aRetval.back() }; + + double fChildAlpha = 1.0 - fChildTransparence; + double fShadowAlpha = 1.0 - rShadow.getTransparence(); + double fTransparence = 1.0 - fChildAlpha * fShadowAlpha; + aRetval.back() = Primitive2DReference(new UnifiedTransparencePrimitive2D( + std::move(aTempContent), fTransparence)); + } + } + + aRetval.push_back( + Primitive2DReference(new GroupPrimitive2D(std::move(rContent)))); + return aRetval; + } + + Primitive2DContainer createEmbeddedGlowPrimitive( + Primitive2DContainer&& rContent, + const attribute::SdrGlowAttribute& rGlow) + { + if(rContent.empty()) + return std::move(rContent); + Primitive2DContainer aRetval(2); + aRetval[0] = Primitive2DReference( + new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), Primitive2DContainer(rContent))); + aRetval[1] = Primitive2DReference(new GroupPrimitive2D(Primitive2DContainer(rContent))); + return aRetval; + } + + Primitive2DContainer createEmbeddedSoftEdgePrimitive(Primitive2DContainer&& aContent, + sal_Int32 nRadius) + { + if (aContent.empty() || !nRadius) + return std::move(aContent); + Primitive2DContainer aRetval(1); + aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent))); + return aRetval; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx new file mode 100644 index 000000000..89c348beb --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx @@ -0,0 +1,266 @@ +/* -*- 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/primitive2d/sdrellipseprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrEllipsePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + // Do use createPolygonFromUnitCircle, but let create from first quadrant to mimic old geometry creation. + // This is needed to have the same look when stroke is used since the polygon start point defines the + // stroke start, too. + basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitCircle(1)); + + // scale and move UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1) + const basegfx::B2DHomMatrix aUnitCorrectionMatrix( + basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5)); + + // apply to the geometry + aUnitOutline.transform(aUnitCorrectionMatrix); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrEllipsePrimitive2D::SdrEllipsePrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute) + { + } + + bool SdrEllipsePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrEllipsePrimitive2D& rCompare = static_cast<const SdrEllipsePrimitive2D&>(rPrimitive); + + return (getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrEllipsePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D; + } + + + + void SdrEllipseSegmentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitEllipseSegment(mfStartAngle, mfEndAngle)); + + if(mbCloseSegment) + { + if(mbCloseUsingCenter) + { + // for compatibility, insert the center point at polygon start to get the same + // line stroking pattern as the old painting mechanisms. + aUnitOutline.insert(0, basegfx::B2DPoint(0.0, 0.0)); + } + + aUnitOutline.setClosed(true); + } + + // move and scale UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1) + const basegfx::B2DHomMatrix aUnitCorrectionMatrix( + basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5)); + + // apply to the geometry + aUnitOutline.transform(aUnitCorrectionMatrix); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault() && aUnitOutline.isClosed()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // create invisible line for HitTest/BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrEllipseSegmentPrimitive2D::SdrEllipseSegmentPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + double fStartAngle, + double fEndAngle, + bool bCloseSegment, + bool bCloseUsingCenter) + : SdrEllipsePrimitive2D(rTransform, rSdrLFSTAttribute), + mfStartAngle(fStartAngle), + mfEndAngle(fEndAngle), + mbCloseSegment(bCloseSegment), + mbCloseUsingCenter(bCloseUsingCenter) + { + } + + bool SdrEllipseSegmentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrEllipsePrimitive2D::operator==(rPrimitive)) + { + const SdrEllipseSegmentPrimitive2D& rCompare = static_cast<const SdrEllipseSegmentPrimitive2D&>(rPrimitive); + + if( mfStartAngle == rCompare.mfStartAngle + && mfEndAngle == rCompare.mfEndAngle + && mbCloseSegment == rCompare.mbCloseSegment + && mbCloseUsingCenter == rCompare.mbCloseUsingCenter) + { + return true; + } + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrEllipseSegmentPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx new file mode 100644 index 000000000..0610f43b0 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx @@ -0,0 +1,928 @@ +/* -*- 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/primitive2d/sdrframeborderprimitive2d.hxx> +#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <svtools/borderhelper.hxx> + +namespace +{ + double snapToDiscreteUnit( + double fValue, + double fMinimalDiscreteUnit) + { + if(0.0 != fValue) + { + fValue = std::max(fValue, fMinimalDiscreteUnit); + } + + return fValue; + } + + class StyleVectorCombination + { + private: + struct OffsetAndHalfWidthAndColor + { + double mfOffset; + double mfHalfWidth; + Color maColor; + + OffsetAndHalfWidthAndColor(double offset, double halfWidth, Color color) : + mfOffset(offset), + mfHalfWidth(halfWidth), + maColor(color) + {} + }; + + double mfRefModeOffset; + basegfx::B2DVector maB2DVector; + double mfAngle; + std::vector< OffsetAndHalfWidthAndColor > maOffsets; + + public: + StyleVectorCombination( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rB2DVector, + double fAngle, + bool bMirrored, + const Color* pForceColor, + double fMinimalDiscreteUnit) + : mfRefModeOffset(0.0), + maB2DVector(rB2DVector), + mfAngle(fAngle) + { + if (!rStyle.IsUsed()) + return; + + svx::frame::RefMode aRefMode(rStyle.GetRefMode()); + Color aPrim(rStyle.GetColorPrim()); + Color aSecn(rStyle.GetColorSecn()); + const bool bSecnUsed(0.0 != rStyle.Secn()); + + // Get the single segment line widths. This is the point where the + // minimal discrete unit will be used if given (fMinimalDiscreteUnit). If + // not given it's 0.0 and thus will have no influence. + double fPrim(snapToDiscreteUnit(rStyle.Prim(), fMinimalDiscreteUnit)); + const double fDist(snapToDiscreteUnit(rStyle.Dist(), fMinimalDiscreteUnit)); + double fSecn(snapToDiscreteUnit(rStyle.Secn(), fMinimalDiscreteUnit)); + + // Of course also do not use svx::frame::Style::GetWidth() for obvious + // reasons. + const double fStyleWidth(fPrim + fDist + fSecn); + + if(bMirrored) + { + switch(aRefMode) + { + case svx::frame::RefMode::Begin: aRefMode = svx::frame::RefMode::End; break; + case svx::frame::RefMode::End: aRefMode = svx::frame::RefMode::Begin; break; + default: break; + } + + if(bSecnUsed) + { + std::swap(aPrim, aSecn); + std::swap(fPrim, fSecn); + } + } + + if (svx::frame::RefMode::Centered != aRefMode) + { + const double fHalfWidth(fStyleWidth * 0.5); + + if (svx::frame::RefMode::Begin == aRefMode) + { + // move aligned below vector + mfRefModeOffset = fHalfWidth; + } + else if (svx::frame::RefMode::End == aRefMode) + { + // move aligned above vector + mfRefModeOffset = -fHalfWidth; + } + } + + if (bSecnUsed) + { + // both or all three lines used + const bool bPrimTransparent(rStyle.GetColorPrim().IsFullyTransparent()); + const bool bDistTransparent(!rStyle.UseGapColor() || rStyle.GetColorGap().IsFullyTransparent()); + const bool bSecnTransparent(aSecn.IsFullyTransparent()); + + if(!bPrimTransparent || !bDistTransparent || !bSecnTransparent) + { + const double a(mfRefModeOffset - (fStyleWidth * 0.5)); + const double b(a + fPrim); + const double c(b + fDist); + const double d(c + fSecn); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (a + b) * 0.5, + fPrim * 0.5, + nullptr != pForceColor ? *pForceColor : aPrim)); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (b + c) * 0.5, + fDist * 0.5, + rStyle.UseGapColor() + ? (nullptr != pForceColor ? *pForceColor : rStyle.GetColorGap()) + : COL_TRANSPARENT)); + + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + (c + d) * 0.5, + fSecn * 0.5, + nullptr != pForceColor ? *pForceColor : aSecn)); + } + } + else + { + // one line used, push two values, from outer to inner + if(!rStyle.GetColorPrim().IsFullyTransparent()) + { + maOffsets.push_back( + OffsetAndHalfWidthAndColor( + mfRefModeOffset, + fPrim * 0.5, + nullptr != pForceColor ? *pForceColor : aPrim)); + } + } + } + + double getRefModeOffset() const { return mfRefModeOffset; } + const basegfx::B2DVector& getB2DVector() const { return maB2DVector; } + double getAngle() const { return mfAngle; } + bool empty() const { return maOffsets.empty(); } + size_t size() const { return maOffsets.size(); } + + void getColorAndOffsetAndHalfWidth(size_t nIndex, Color& rColor, double& rfOffset, double& rfHalfWidth) const + { + if(nIndex >= maOffsets.size()) + return; + const OffsetAndHalfWidthAndColor& rCandidate(maOffsets[nIndex]); + rfOffset = rCandidate.mfOffset; + rfHalfWidth = rCandidate.mfHalfWidth; + rColor = rCandidate.maColor; + } + }; + + class StyleVectorTable + { + private: + std::vector< StyleVectorCombination > maEntries; + + public: + StyleVectorTable() + { + } + + void add( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rMyVector, + const basegfx::B2DVector& rOtherVector, + bool bMirrored, + double fMinimalDiscreteUnit) + { + if(!rStyle.IsUsed() || basegfx::areParallel(rMyVector, rOtherVector)) + return; + + // create angle between both. angle() needs vectors pointing away from the same point, + // so take the mirrored one. Add M_PI to get from -pi..+pi to [0..M_PI_2] for sorting + const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + M_PI); + maEntries.emplace_back( + rStyle, + rOtherVector, + fAngle, + bMirrored, + nullptr, + fMinimalDiscreteUnit); + } + + void sort() + { + // sort inverse from highest to lowest + std::sort( + maEntries.begin(), + maEntries.end(), + [](const StyleVectorCombination& a, const StyleVectorCombination& b) + { return a.getAngle() > b.getAngle(); }); + } + + bool empty() const { return maEntries.empty(); } + const std::vector< StyleVectorCombination >& getEntries() const{ return maEntries; } + }; + + struct CutSet + { + double mfOLML; + double mfORML; + double mfOLMR; + double mfORMR; + + CutSet() : mfOLML(0.0), mfORML(0.0), mfOLMR(0.0), mfORMR(0.0) + { + } + + bool operator<( const CutSet& rOther) const + { + const double fA(mfOLML + mfORML + mfOLMR + mfORMR); + const double fB(rOther.mfOLML + rOther.mfORML + rOther.mfOLMR + rOther.mfORMR); + + return fA < fB; + } + + double getSum() const { return mfOLML + mfORML + mfOLMR + mfORMR; } + }; + + void getCutSet( + CutSet& rCutSet, + const basegfx::B2DPoint& rLeft, + const basegfx::B2DPoint& rRight, + const basegfx::B2DVector& rX, + const basegfx::B2DPoint& rOtherLeft, + const basegfx::B2DPoint& rOtherRight, + const basegfx::B2DVector& rOtherX) + { + basegfx::utils::findCut( + rLeft, + rX, + rOtherLeft, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfOLML); + + basegfx::utils::findCut( + rRight, + rX, + rOtherLeft, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfOLMR); + + basegfx::utils::findCut( + rLeft, + rX, + rOtherRight, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfORML); + + basegfx::utils::findCut( + rRight, + rX, + rOtherRight, + rOtherX, + CutFlagValue::LINE, + &rCutSet.mfORMR); + } + + struct ExtendSet + { + double mfExtLeft; + double mfExtRight; + + ExtendSet() : mfExtLeft(0.0), mfExtRight(0.0) {} + }; + + void getExtends( + std::vector<ExtendSet>& rExtendSet, // target Left/Right values to fill + const basegfx::B2DPoint& rOrigin, // own vector start + const StyleVectorCombination& rCombination, // own vector and offsets for lines + const basegfx::B2DVector& rPerpendX, // normalized perpendicular to own vector + const std::vector< StyleVectorCombination >& rStyleVector) // other vectors emerging in this point + { + if(!(!rCombination.empty() && !rStyleVector.empty() && rCombination.size() == rExtendSet.size())) + return; + + const size_t nOffsetA(rCombination.size()); + + if(1 == nOffsetA) + { + Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0); + rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth); + + if(!aMyColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth))); + const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth))); + std::vector< CutSet > aCutSets; + + for(const auto& rStyleCandidate : rStyleVector) + { + const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector())); + const size_t nOffsetB(rStyleCandidate.size()); + + for(size_t other(0); other < nOffsetB; other++) + { + Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0); + rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth); + + if(!aOtherColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth))); + const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth))); + + CutSet aNewCutSet; + getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector()); + aCutSets.push_back(aNewCutSet); + } + } + } + + if(!aCutSets.empty()) + { + CutSet aCutSet(aCutSets[0]); + const size_t nNumCutSets(aCutSets.size()); + + if(1 != nNumCutSets) + { + double fCutSet(aCutSet.getSum()); + + for(size_t a(1); a < nNumCutSets; a++) + { + const CutSet& rCandidate(aCutSets[a]); + const double fCandidate(rCandidate.getSum()); + + if(basegfx::fTools::equalZero(fCandidate - fCutSet)) + { + // both have equal center point, use medium cut + const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML))); + const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML))); + const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR))); + const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR))); + aCutSet.mfOLML = fNewOLML; + aCutSet.mfORML = fNewORML; + aCutSet.mfOLMR = fNewOLMR; + aCutSet.mfORMR = fNewORMR; + fCutSet = aCutSet.getSum(); + } + else if(fCandidate < fCutSet) + { + // get minimum + fCutSet = fCandidate; + aCutSet = rCandidate; + } + } + } + + ExtendSet& rExt(rExtendSet[0]); + + rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML); + rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR); + } + } + } + else + { + size_t nVisEdgeUp(0); + size_t nVisEdgeDn(0); + + for(size_t my(0); my < nOffsetA; my++) + { + Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0); + rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth); + + if(!aMyColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth))); + const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth))); + const bool bUpper(my <= (nOffsetA >> 1)); + const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back()); + const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector())); + const size_t nOffsetB(rStyleCandidate.size()); + std::vector< CutSet > aCutSets; + + for(size_t other(0); other < nOffsetB; other++) + { + Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0); + rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth); + + if(!aOtherColor.IsFullyTransparent()) + { + const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth))); + const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth))); + CutSet aCutSet; + getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector()); + aCutSets.push_back(aCutSet); + } + } + + if(!aCutSets.empty()) + { + // sort: min to start, max to end + std::sort(aCutSets.begin(), aCutSets.end()); + const bool bOtherUpper(rStyleCandidate.getAngle() > M_PI); + + // check if we need min or max + // bUpper bOtherUpper MinMax + // t t max + // t f min + // f f max + // f t min + const bool bMax(bUpper == bOtherUpper); + size_t nBaseIndex(0); + const size_t nNumCutSets(aCutSets.size()); + + if(bMax) + { + // access at end + nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn); + } + else + { + // access at start + nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn; + } + + const size_t nSecuredIndex(std::clamp(nBaseIndex, size_t(0), size_t(nNumCutSets - 1))); + const CutSet& rCutSet(aCutSets[nSecuredIndex]); + ExtendSet& rExt(rExtendSet[my]); + + rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML); + rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR); + } + + if(bUpper) + { + nVisEdgeUp++; + } + else + { + nVisEdgeDn++; + } + } + } + } + } + + /** + * Helper method to create the correct drawinglayer::primitive2d::BorderLinePrimitive2D + * for the given data, especially the correct drawinglayer::primitive2d::BorderLine entries + * including the correctly solved/created LineStartEnd extends + * + * rTarget : Here the evtl. created BorderLinePrimitive2D will be appended + * rOrigin : StartPoint of the Borderline + * rX : Vector of the Borderline + * rBorder : svx::frame::Style of the of the Borderline + * rStartStyleVectorTable : All other Borderlines which have to be taken into account because + * they have the same StartPoint as the current Borderline. These will be used to calculate + * the correct LineStartEnd extends tor the BorderLinePrimitive2D. The definition should be + * built up using svx::frame::StyleVectorTable and StyleVectorTable::add and includes: + * rStyle : the svx::frame::Style of one other BorderLine + * rMyVector : the Vector of the *new* to-be-defined BorderLine, identical to rX + * rOtherVector: the Vector of one other BorderLine (may be, but does not need to be normalized), + * always *pointing away* from the common StartPoint rOrigin + * bMirrored : define if rStyle of one other BorderLine shall be mirrored (e.g. bottom-right edges) + * With multiple BorderLines the definitions have to be CounterClockWise. This will be + * ensured by StyleVectorTable sorting the entries, but knowing this may allow more efficient + * data creation. + * rEndStyleVectorTable: All other BorderLines that have the same EndPoint. There are differences to + * the Start definitions: + * - do not forget to consequently use -rX for rMyVector + * - definitions have to be ClockWise for the EndBorderLines, will be ensured by sorting + * + * If you take all this into account, you will get correctly extended BorderLinePrimitive2D + * representations for the new to be defined BorderLine. That extensions will overlap nicely + * with the corresponding BorderLines and take all multiple line definitions in the ::Style into + * account. + * The internal solver is *not limited* to ::Style(s) with three parts (Left/Gap/Right), this is + * just due to svx::frame::Style's definitions. A new solver based on this one can be created + * anytime using more mulötiple borders based on the more flexible + * std::vector< drawinglayer::primitive2d::BorderLine > if needed. + */ + void CreateBorderPrimitives( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, /// target for created primitives + const basegfx::B2DPoint& rOrigin, /// start point of borderline + const basegfx::B2DVector& rX, /// X-Axis of borderline with length + const svx::frame::Style& rBorder, /// Style of borderline + const StyleVectorTable& rStartStyleVectorTable, /// Styles and vectors (pointing away) at borderline start, ccw + const StyleVectorTable& rEndStyleVectorTable, /// Styles and vectors (pointing away) at borderline end, cw + const Color* pForceColor, /// If specified, overrides frame border color. + double fMinimalDiscreteUnit) /// minimal discrete unit to use for svx::frame::Style width values + { + // get offset color pairs for style, one per visible line + const StyleVectorCombination aCombination( + rBorder, + rX, + 0.0, + false, + pForceColor, + fMinimalDiscreteUnit); + + if(aCombination.empty()) + return; + + const basegfx::B2DVector aPerpendX(basegfx::getNormalizedPerpendicular(rX)); + const bool bHasStartStyles(!rStartStyleVectorTable.empty()); + const bool bHasEndStyles(!rEndStyleVectorTable.empty()); + const size_t nOffsets(aCombination.size()); + std::vector<ExtendSet> aExtendSetStart(nOffsets); + std::vector<ExtendSet> aExtendSetEnd(nOffsets); + + if(bHasStartStyles) + { + // create extends for line starts, use given point/vector and offsets + getExtends(aExtendSetStart, rOrigin, aCombination, aPerpendX, rStartStyleVectorTable.getEntries()); + } + + if(bHasEndStyles) + { + // Create extends for line ends, create inverse point/vector and inverse offsets. + const StyleVectorCombination aMirroredCombination( + rBorder, + -rX, + 0.0, + true, + pForceColor, + fMinimalDiscreteUnit); + + getExtends(aExtendSetEnd, rOrigin + rX, aMirroredCombination, -aPerpendX, rEndStyleVectorTable.getEntries()); + + // also need to inverse the result to apply to the correct lines + std::reverse(aExtendSetEnd.begin(), aExtendSetEnd.end()); + } + + std::vector< drawinglayer::primitive2d::BorderLine > aBorderlines; + const double fNegLength(-rX.getLength()); + + for(size_t a(0); a < nOffsets; a++) + { + Color aMyColor; + double fMyOffset(0.0); + double fMyHalfWidth(0.0); + aCombination.getColorAndOffsetAndHalfWidth(a, aMyColor, fMyOffset, fMyHalfWidth); + const ExtendSet& rExtStart(aExtendSetStart[a]); + const ExtendSet& rExtEnd(aExtendSetEnd[a]); + + if(aMyColor.IsFullyTransparent()) + { + aBorderlines.push_back( + drawinglayer::primitive2d::BorderLine( + fMyHalfWidth * 2.0)); + } + else + { + aBorderlines.push_back( + drawinglayer::primitive2d::BorderLine( + drawinglayer::attribute::LineAttribute( + aMyColor.getBColor(), + fMyHalfWidth * 2.0), + fNegLength * rExtStart.mfExtLeft, + fNegLength * rExtStart.mfExtRight, + fNegLength * rExtEnd.mfExtRight, + fNegLength * rExtEnd.mfExtLeft)); + } + } + + static const double fPatScFact(10.0); // 10.0 multiply, see old code + std::vector<double> aDashing(svtools::GetLineDashing(rBorder.Type(), rBorder.PatternScale() * fPatScFact)); + const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing)); + const basegfx::B2DPoint aStart(rOrigin + (aPerpendX * aCombination.getRefModeOffset())); + + rTarget.append( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::BorderLinePrimitive2D( + aStart, + aStart + rX, + std::move(aBorderlines), + aStrokeAttribute))); + } + + double getMinimalNonZeroValue(double fCurrent, double fNew) + { + if(0.0 != fNew) + { + if(0.0 != fCurrent) + { + fCurrent = std::min(fNew, fCurrent); + } + else + { + fCurrent = fNew; + } + } + + return fCurrent; + } + + double getMinimalNonZeroBorderWidthFromStyle(double fCurrent, const svx::frame::Style& rStyle) + { + if(rStyle.IsUsed()) + { + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Prim()); + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Dist()); + fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Secn()); + } + + return fCurrent; + } +} + +namespace drawinglayer::primitive2d +{ + SdrFrameBorderData::SdrConnectStyleData::SdrConnectStyleData( + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rNormalizedPerpendicular, + bool bStyleMirrored) + : maStyle(rStyle), + maNormalizedPerpendicular(rNormalizedPerpendicular), + mbStyleMirrored(bStyleMirrored) + { + } + + bool SdrFrameBorderData::SdrConnectStyleData::operator==(const SdrFrameBorderData::SdrConnectStyleData& rCompare) const + { + return mbStyleMirrored == rCompare.mbStyleMirrored + && maStyle == rCompare.maStyle + && maNormalizedPerpendicular == rCompare.maNormalizedPerpendicular; + } + + SdrFrameBorderData::SdrFrameBorderData( + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const svx::frame::Style& rStyle, + const Color* pForceColor) + : maOrigin(rOrigin), + maX(rX), + maStyle(rStyle), + maColor(nullptr != pForceColor ? *pForceColor : Color()), + mbForceColor(nullptr != pForceColor) + { + } + + void SdrFrameBorderData::addSdrConnectStyleData( + bool bStart, + const svx::frame::Style& rStyle, + const basegfx::B2DVector& rNormalizedPerpendicular, + bool bStyleMirrored) + { + if(rStyle.IsUsed()) + { + if(bStart) + { + maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored); + } + else + { + maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored); + } + } + } + + void SdrFrameBorderData::create2DDecomposition( + Primitive2DContainer& rContainer, + double fMinimalDiscreteUnit) const + { + StyleVectorTable aStartVector; + StyleVectorTable aEndVector; + const basegfx::B2DVector aAxis(-maX); + + for(const auto& rStart : maStart) + { + aStartVector.add( + rStart.getStyle(), + maX, + rStart.getNormalizedPerpendicular(), + rStart.getStyleMirrored(), + fMinimalDiscreteUnit); + } + + for(const auto& rEnd : maEnd) + { + aEndVector.add( + rEnd.getStyle(), + aAxis, + rEnd.getNormalizedPerpendicular(), + rEnd.getStyleMirrored(), + fMinimalDiscreteUnit); + } + + aStartVector.sort(); + aEndVector.sort(); + + CreateBorderPrimitives( + rContainer, + maOrigin, + maX, + maStyle, + aStartVector, + aEndVector, + mbForceColor ? &maColor : nullptr, + fMinimalDiscreteUnit); + } + + double SdrFrameBorderData::getMinimalNonZeroBorderWidth() const + { + double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle)); + + for(const auto& rStart : maStart) + { + fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle()); + } + + for(const auto& rEnd : maEnd) + { + fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle()); + } + + return fRetval; + } + + + bool SdrFrameBorderData::operator==(const SdrFrameBorderData& rCompare) const + { + return maOrigin == rCompare.maOrigin + && maX == rCompare.maX + && maStyle == rCompare.maStyle + && maColor == rCompare.maColor + && mbForceColor == rCompare.mbForceColor + && maStart == rCompare.maStart + && maEnd == rCompare.maEnd; + } + + + void SdrFrameBorderPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, + const geometry::ViewInformation2D& /*aViewInformation*/) const + { + if(!getFrameBorders()) + { + return; + } + + Primitive2DContainer aRetval; + + // Check and use the minimal non-zero BorderWidth for decompose + // if that is set and wanted + const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit() + ? mfMinimalNonZeroBorderWidthUsedForDecompose + : 0.0); + + { + // decompose all buffered SdrFrameBorderData entries and try to merge them + // to reduce existing number of BorderLinePrimitive2D(s) + for(const auto& rCandidate : *getFrameBorders()) + { + // get decomposition on one SdrFrameBorderData entry + Primitive2DContainer aPartial; + rCandidate.create2DDecomposition( + aPartial, + fMinimalDiscreteUnit); + + for(const auto& aCandidatePartial : aPartial) + { + if(aRetval.empty()) + { + // no local data yet, just add as 1st entry, done + aRetval.append(aCandidatePartial); + } + else + { + bool bDidMerge(false); + + for(auto& aCandidateRetval : aRetval) + { + // try to merge by appending new data to existing data + const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial( + drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D( + static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()), + static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()))); + + if(aMergeRetvalPartial.is()) + { + // could append, replace existing data with merged data, done + aCandidateRetval = aMergeRetvalPartial; + bDidMerge = true; + break; + } + + // try to merge by appending existing data to new data + const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval( + drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D( + static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()), + static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()))); + + if(aMergePartialRetval.is()) + { + // could append, replace existing data with merged data, done + aCandidateRetval = aMergePartialRetval; + bDidMerge = true; + break; + } + } + + if(!bDidMerge) + { + // no merge after checking all existing data, append as new segment + aRetval.append(aCandidatePartial); + } + } + } + } + } + + rContainer.append(std::move(aRetval)); + } + + SdrFrameBorderPrimitive2D::SdrFrameBorderPrimitive2D( + std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders, + bool bForceToSingleDiscreteUnit) + : maFrameBorders(std::move(rFrameBorders)), + mfMinimalNonZeroBorderWidth(0.0), + mfMinimalNonZeroBorderWidthUsedForDecompose(0.0), + mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit) + { + if(getFrameBorders() && doForceToSingleDiscreteUnit()) + { + // detect used minimal non-zero partial border width + for(const auto& rCandidate : *getFrameBorders()) + { + mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue( + mfMinimalNonZeroBorderWidth, + rCandidate.getMinimalNonZeroBorderWidth()); + } + } + } + + bool SdrFrameBorderPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive); + + return (getFrameBorders() == rCompare.getFrameBorders() + || (getFrameBorders() && rCompare.getFrameBorders() + && *getFrameBorders() == *rCompare.getFrameBorders())) + && doForceToSingleDiscreteUnit() == rCompare.doForceToSingleDiscreteUnit(); + } + + return false; + } + + void SdrFrameBorderPrimitive2D::get2DDecomposition( + Primitive2DDecompositionVisitor& rVisitor, + const geometry::ViewInformation2D& rViewInformation) const + { + if(doForceToSingleDiscreteUnit()) + { + // Get the current DiscreteUnit, look at X and Y and use the maximum + const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0)); + double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY()))); + + if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth) + { + // no need to use it, reset + fDiscreteUnit = 0.0; + } + + if(fDiscreteUnit != mfMinimalNonZeroBorderWidthUsedForDecompose) + { + // conditions of last local decomposition have changed, delete + // possible content + if(!getBuffered2DDecomposition().empty()) + { + const_cast< SdrFrameBorderPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + + // remember new conditions + const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit; + } + } + + // call parent. This will call back ::create2DDecomposition above + // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used + // when doForceToSingleDiscreteUnit() is true + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + // provide unique ID + sal_uInt32 SdrFrameBorderPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx new file mode 100644 index 000000000..f5ac21cfc --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx @@ -0,0 +1,167 @@ +/* -*- 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/primitive2d/sdrgrafprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +namespace drawinglayer::primitive2d +{ +void SdrGrafPrimitive2D::create2DDecomposition( + Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const +{ + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + // add fill, but only when graphic is transparent + if (!getSdrLFSTAttribute().getFill().isDefault() && isTransparent()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive(aTransformed, getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add graphic content + if (0 != getGraphicAttr().GetAlpha()) + { + // standard graphic fill + const Primitive2DReference xGraphicContentPrimitive( + new GraphicPrimitive2D(getTransform(), getGraphicObject(), getGraphicAttr())); + aRetval.push_back(xGraphicContentPrimitive); + } + + // add line + if (!getSdrLFSTAttribute().getLine().isDefault()) + { + // if line width is given, polygon needs to be grown by half of it to make the + // outline to be outside of the bitmap + if (0.0 != getSdrLFSTAttribute().getLine().getWidth()) + { + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // create expanded range (add relative half line width to unit rectangle) + double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5); + double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0); + double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0); + const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX, + 1.0 + fScaleY); + basegfx::B2DPolygon aExpandedUnitOutline( + basegfx::utils::createPolygonFromRect(aExpandedRange)); + + aExpandedUnitOutline.transform(getTransform()); + aRetval.push_back(createPolygonLinePrimitive(aExpandedUnitOutline, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back(createPolygonLinePrimitive(aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + } + + // Soft edges should be before text, since text is not affected by soft edges + if (!aRetval.empty() && getSdrLFSTAttribute().getSoftEdgeRadius()) + { + aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval), + getSdrLFSTAttribute().getSoftEdgeRadius()); + } + + // add text + if (!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back(createTextPrimitive(basegfx::B2DPolyPolygon(aUnitOutline), getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), false, false)); + } + + // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite + if (!aRetval.empty() && !getSdrLFSTAttribute().getGlow().isDefault()) + { + // glow + aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getGlow()); + } + + // add shadow + if (!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), + getSdrLFSTAttribute().getShadow(), getTransform()); + } + + rContainer.append(std::move(aRetval)); +} + +SdrGrafPrimitive2D::SdrGrafPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const GraphicObject& rGraphicObject, const GraphicAttr& rGraphicAttr) + : maTransform(rTransform) + , maSdrLFSTAttribute(rSdrLFSTAttribute) + , maGraphicObject(rGraphicObject) + , maGraphicAttr(rGraphicAttr) +{ + // reset some values from GraphicAttr which are part of transformation already + maGraphicAttr.SetRotation(0_deg10); +} + +bool SdrGrafPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const +{ + if (BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrGrafPrimitive2D& rCompare = static_cast<const SdrGrafPrimitive2D&>(rPrimitive); + + return (getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute() + && getGraphicObject() == rCompare.getGraphicObject() + && getGraphicAttr() == rCompare.getGraphicAttr()); + } + + return false; +} + +bool SdrGrafPrimitive2D::isTransparent() const +{ + return ((255 != getGraphicAttr().GetAlpha()) || (getGraphicObject().IsTransparent())); +} + +// provide unique ID +sal_uInt32 SdrGrafPrimitive2D::getPrimitive2DID() const +{ + return PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D; +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx new file mode 100644 index 000000000..aada2aee0 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx @@ -0,0 +1,498 @@ +/* -*- 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/primitive2d/sdrmeasureprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <sdr/primitive2d/sdrtextprimitive2d.hxx> +#include <sdr/attribute/sdrtextattribute.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <rtl/ref.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx> +#include <osl/diagnose.h> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + Primitive2DReference SdrMeasurePrimitive2D::impCreatePart( + const attribute::SdrLineAttribute& rLineAttribute, + const basegfx::B2DHomMatrix& rObjectMatrix, + const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + bool bLeftActive, + bool bRightActive) const + { + const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd(); + basegfx::B2DPolygon aPolygon; + + aPolygon.append(rStart); + aPolygon.append(rEnd); + aPolygon.transform(rObjectMatrix); + + if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive)) + { + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + attribute::SdrLineStartEndAttribute()); + } + + if(bLeftActive && bRightActive) + { + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + rLineStartEnd); + } + + const basegfx::B2DPolyPolygon aEmpty; + const attribute::SdrLineStartEndAttribute aLineStartEnd( + bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty, + bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0, + bLeftActive && rLineStartEnd.isStartActive(), bRightActive && rLineStartEnd.isEndActive(), + bLeftActive && rLineStartEnd.isStartCentered(), bRightActive && rLineStartEnd.isEndCentered()); + + return createPolygonLinePrimitive( + aPolygon, + rLineAttribute, + aLineStartEnd); + } + + void SdrMeasurePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + rtl::Reference<SdrBlockTextPrimitive2D> xBlockText; + basegfx::B2DRange aTextRange; + const basegfx::B2DVector aLine(getEnd() - getStart()); + const double fDistance(aLine.getLength()); + const double fAngle(atan2(aLine.getY(), aLine.getX())); + bool bAutoUpsideDown(false); + const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText(); + const basegfx::B2DHomMatrix aObjectMatrix( + basegfx::utils::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart())); + + // prepare text, but do not add yet; it needs to be aligned to + // the line geometry + if(!rTextAttribute.isDefault()) + { + basegfx::B2DHomMatrix aTextMatrix; + double fTestAngle(fAngle); + + if(getTextRotation()) + { + aTextMatrix.rotate(-M_PI_2); + fTestAngle -= (M_PI_2); + + if(getTextAutoAngle() && fTestAngle < -M_PI) + { + fTestAngle += 2 * M_PI; + } + } + + if(getTextAutoAngle()) + { + if(fTestAngle > (M_PI / 4.0) || fTestAngle < (-M_PI * (3.0 / 4.0))) + { + bAutoUpsideDown = true; + } + } + + // create primitive and get text range + xBlockText = new SdrBlockTextPrimitive2D( + &rTextAttribute.getSdrText(), + rTextAttribute.getOutlinerParaObject(), + aTextMatrix, + SDRTEXTHORZADJUST_CENTER, + SDRTEXTVERTADJUST_CENTER, + rTextAttribute.isScroll(), + false, + false, + false); + + aTextRange = xBlockText->getB2DRange(aViewInformation); + } + + // prepare line attribute and result + double fTextX; + double fTextY; + { + const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine()); + bool bArrowsOutside(false); + bool bMainLineSplitted(false); + const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd(); + double fStartArrowW(0.0); + double fStartArrowH(0.0); + double fEndArrowW(0.0); + double fEndArrowH(0.0); + + if(!rLineStartEnd.isDefault()) + { + if(rLineStartEnd.isStartActive()) + { + const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getStartPolyPolygon())); + fStartArrowW = rLineStartEnd.getStartWidth(); + fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth(); + + if(rLineStartEnd.isStartCentered()) + { + fStartArrowH *= 0.5; + } + } + + if(rLineStartEnd.isEndActive()) + { + const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getEndPolyPolygon())); + fEndArrowW = rLineStartEnd.getEndWidth(); + fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth(); + + if(rLineStartEnd.isEndCentered()) + { + fEndArrowH *= 0.5; + } + } + } + + const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5)); + const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5); + const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5); + + if(fSpaceNeededByArrows > fDistance) + { + bArrowsOutside = true; + } + + MeasureTextPosition eHorizontal(getHorizontal()); + MeasureTextPosition eVertical(getVertical()); + + if(MEASURETEXTPOSITION_AUTOMATIC == eVertical) + { + eVertical = MEASURETEXTPOSITION_NEGATIVE; + } + + if(MEASURETEXTPOSITION_CENTERED == eVertical) + { + bMainLineSplitted = true; + } + + if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal) + { + if(aTextRange.getWidth() > fDistance) + { + eHorizontal = MEASURETEXTPOSITION_NEGATIVE; + } + else + { + eHorizontal = MEASURETEXTPOSITION_CENTERED; + } + + if(bMainLineSplitted) + { + if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance) + { + bArrowsOutside = true; + } + } + else + { + const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125)); + + if(aTextRange.getWidth() + fSmallArrowNeed > fDistance) + { + bArrowsOutside = true; + } + } + } + + if(MEASURETEXTPOSITION_CENTERED != eHorizontal) + { + bArrowsOutside = true; + } + + // switch text above/below? + if(getBelow() || (bAutoUpsideDown && !getTextRotation())) + { + if(MEASURETEXTPOSITION_NEGATIVE == eVertical) + { + eVertical = MEASURETEXTPOSITION_POSITIVE; + } + else if(MEASURETEXTPOSITION_POSITIVE == eVertical) + { + eVertical = MEASURETEXTPOSITION_NEGATIVE; + } + } + + const double fMainLineOffset(getBelow() ? getDistance() : -getDistance()); + const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset); + const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset); + + // main line + if(bArrowsOutside) + { + double fLenLeft(fArrowsOutsideLen); + double fLenRight(fArrowsOutsideLen); + + if(!bMainLineSplitted) + { + if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal) + { + fLenLeft = fStartArrowH + aTextRange.getWidth(); + } + else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal) + { + fLenRight = fEndArrowH + aTextRange.getWidth(); + } + } + + const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY()); + const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY()); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true)); + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false)); + + if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal) + { + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false)); + } + } + else + { + if(bMainLineSplitted) + { + const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5); + const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY()); + const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY()); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false)); + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true)); + } + else + { + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true)); + } + } + + // left/right help line value preparation + const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance()); + const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower()); + const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower()); + + // left help line + const basegfx::B2DPoint aLeftUp(0.0, fTopEdge); + const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false)); + + // right help line + const basegfx::B2DPoint aRightUp(fDistance, fTopEdge); + const basegfx::B2DPoint aRightDown(fDistance, fBottomRight); + + aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false)); + + // text horizontal position + if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal) + { + // left + const double fSmall(fArrowsOutsideLen * 0.18); + fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth); + + if(bMainLineSplitted) + { + fTextX -= (fArrowsOutsideLen - fStartArrowH); + } + + if(!rTextAttribute.isDefault()) + { + fTextX -= rTextAttribute.getTextRightDistance(); + } + } + else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal) + { + // right + const double fSmall(fArrowsOutsideLen * 0.18); + fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth); + + if(bMainLineSplitted) + { + fTextX += (fArrowsOutsideLen - fEndArrowH); + } + + if(!rTextAttribute.isDefault()) + { + fTextX += rTextAttribute.getTextLeftDistance(); + } + } + else // MEASURETEXTPOSITION_CENTERED + { + // centered + fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5); + + if(!rTextAttribute.isDefault()) + { + fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L; + } + } + + // text vertical position + if(MEASURETEXTPOSITION_NEGATIVE == eVertical) + { + // top + const double fSmall(fArrowsOutsideLen * 0.10); + fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth); + + if(!rTextAttribute.isDefault()) + { + fTextY -= rTextAttribute.getTextLowerDistance(); + } + } + else if(MEASURETEXTPOSITION_POSITIVE == eVertical) + { + // bottom + const double fSmall(fArrowsOutsideLen * 0.10); + fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth); + + if(!rTextAttribute.isDefault()) + { + fTextY += rTextAttribute.getTextUpperDistance(); + } + } + else // MEASURETEXTPOSITION_CENTERED + { + // centered + fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5); + + if(!rTextAttribute.isDefault()) + { + fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L; + } + } + } + + if(getSdrLSTAttribute().getLine().isDefault()) + { + // embed line geometry to invisible (100% transparent) line group for HitTest + Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(aRetval))); + + aRetval = Primitive2DContainer { xHiddenLines }; + } + + if(xBlockText.is()) + { + // create transformation to text primitive end position + basegfx::B2DHomMatrix aChange; + + // handle auto text rotation + if(bAutoUpsideDown) + { + aChange.rotate(M_PI); + } + + // move from aTextRange.TopLeft to fTextX, fTextY + aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY()); + + // apply object matrix + aChange *= aObjectMatrix; + + // apply to existing text primitive + rtl::Reference<SdrTextPrimitive2D> pNewBlockText = xBlockText->createTransformedClone(aChange); + OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)"); + xBlockText.clear(); + + // add to local primitives + aRetval.push_back(pNewBlockText); + } + + // add shadow + if(!getSdrLSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrMeasurePrimitive2D::SdrMeasurePrimitive2D( + const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute, + const basegfx::B2DPoint& rStart, + const basegfx::B2DPoint& rEnd, + MeasureTextPosition eHorizontal, + MeasureTextPosition eVertical, + double fDistance, + double fUpper, + double fLower, + double fLeftDelta, + double fRightDelta, + bool bBelow, + bool bTextRotation, + bool bTextAutoAngle) + : maSdrLSTAttribute(rSdrLSTAttribute), + maStart(rStart), + maEnd(rEnd), + meHorizontal(eHorizontal), + meVertical(eVertical), + mfDistance(fDistance), + mfUpper(fUpper), + mfLower(fLower), + mfLeftDelta(fLeftDelta), + mfRightDelta(fRightDelta), + mbBelow(bBelow), + mbTextRotation(bTextRotation), + mbTextAutoAngle(bTextAutoAngle) + { + } + + bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrMeasurePrimitive2D& rCompare = static_cast<const SdrMeasurePrimitive2D&>(rPrimitive); + + return (getStart() == rCompare.getStart() + && getEnd() == rCompare.getEnd() + && getHorizontal() == rCompare.getHorizontal() + && getVertical() == rCompare.getVertical() + && getDistance() == rCompare.getDistance() + && getUpper() == rCompare.getUpper() + && getLower() == rCompare.getLower() + && getLeftDelta() == rCompare.getLeftDelta() + && getRightDelta() == rCompare.getRightDelta() + && getBelow() == rCompare.getBelow() + && getTextRotation() == rCompare.getTextRotation() + && getTextAutoAngle() == rCompare.getTextAutoAngle() + && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrMeasurePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx b/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx new file mode 100644 index 000000000..e7576b498 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sdr/primitive2d/sdrole2primitive2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + SdrOle2Primitive2D::SdrOle2Primitive2D( + Primitive2DContainer&& rOLEContent, + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute) + : maOLEContent(std::move(rOLEContent)), + maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute) + { + } + + bool SdrOle2Primitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BasePrimitive2D::operator==(rPrimitive)) + { + const SdrOle2Primitive2D& rCompare = static_cast<const SdrOle2Primitive2D&>(rPrimitive); + + // #i108636# The standard operator== on two UNO sequences did not work as i + // would have expected; it just checks the .is() states and the data type + // of the sequence. What i need here is detection of equality of the whole + // sequence content, thus i need to use the arePrimitive2DSequencesEqual helper + // here instead of the operator== which lead to always returning false and thus + // always re-decompositions of the subcontent. + if(getOLEContent() == rCompare.getOLEContent() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()) + { + return true; + } + } + + return false; + } + + void SdrOle2Primitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + // to take care of getSdrLFSTAttribute() later, the same as in SdrGrafPrimitive2D::create2DDecomposition + // should happen. For the moment we only need the OLE itself + // Added complete primitive preparation using getSdrLFSTAttribute() now. + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon()); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + + // add line + // #i97981# condition was inverse to purpose. When being compatible to paint version, + // border needs to be suppressed + if(!getSdrLFSTAttribute().getLine().isDefault()) + { + // if line width is given, polygon needs to be grown by half of it to make the + // outline to be outside of the bitmap + if(0.0 != getSdrLFSTAttribute().getLine().getWidth()) + { + // decompose to get scale + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // create expanded range (add relative half line width to unit rectangle) + double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5); + double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0); + double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0); + const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX, 1.0 + fScaleY); + basegfx::B2DPolygon aExpandedUnitOutline(basegfx::utils::createPolygonFromRect(aExpandedRange)); + + aExpandedUnitOutline.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aExpandedUnitOutline, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + } + else + { + // if initially no line is defined, create one for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add graphic content + aRetval.append(getOLEContent()); + + // add text, no need to suppress to stay compatible since text was + // always supported by the old paints, too + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rVisitor.visit(std::move(aRetval)); + } + + // provide unique ID + sal_uInt32 SdrOle2Primitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDROLE2PRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx new file mode 100644 index 000000000..7edd0e601 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.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 <sdr/primitive2d/sdrolecontentprimitive2d.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <svx/svdoole2.hxx> +#include <vcl/svapp.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <svtools/colorcfg.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + + +namespace drawinglayer::primitive2d +{ + void SdrOleContentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + const SdrOle2Obj* pSource = mpSdrOle2Obj.get(); + bool bScaleContent(false); + Graphic aGraphic; + + if(pSource) + { + const Graphic* pOLEGraphic = pSource->GetGraphic(); + + if(pOLEGraphic) + { + aGraphic = *pOLEGraphic; + bScaleContent = pSource->IsEmptyPresObj(); + } + } +#ifdef _WIN32 // Little point in displaying the "broken OLE" graphic on OSes that don't have real OLE, maybe? + if(GraphicType::NONE == aGraphic.GetType()) + { + // no source, use fallback resource empty OLE graphic + aGraphic = SdrOle2Obj::GetEmptyOLEReplacementGraphic(); + bScaleContent = true; + } +#endif + if(GraphicType::NONE == aGraphic.GetType()) + return; + + const GraphicObject aGraphicObject(aGraphic); + const GraphicAttr aGraphicAttr; + + if(bScaleContent) + { + // get transformation atoms + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX); + + // get PrefSize from the graphic in 100th mm + Size aPrefSize(aGraphic.GetPrefSize()); + + if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit()) + { + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + { + aPrefSize = OutputDevice::LogicToLogic(aPrefSize, aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + } + + 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)) + { + // if content fits into frame, create it + basegfx::B2DHomMatrix aInnerObjectMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY)); + aInnerObjectMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate) + * aInnerObjectMatrix; + + const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive( + new drawinglayer::primitive2d::GraphicPrimitive2D( + aInnerObjectMatrix, + aGraphicObject, + aGraphicAttr)); + rContainer.push_back(aGraphicPrimitive); + } + } + else + { + // create graphic primitive for content + const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive( + new drawinglayer::primitive2d::GraphicPrimitive2D( + getObjectTransform(), + aGraphicObject, + aGraphicAttr)); + rContainer.push_back(aGraphicPrimitive); + } + + // a standard gray outline is created for scaled content + if(!bScaleContent) + return; + + const svtools::ColorConfig aColorConfig; + const svtools::ColorConfigValue aColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES)); + + if(aColor.bIsVisible) + { + basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon()); + const Color aVclColor(aColor.nColor); + aOutline.transform(getObjectTransform()); + const drawinglayer::primitive2d::Primitive2DReference xOutline( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aVclColor.getBColor())); + rContainer.push_back(xOutline); + } + } + + SdrOleContentPrimitive2D::SdrOleContentPrimitive2D( + const SdrOle2Obj& rSdrOle2Obj, + const basegfx::B2DHomMatrix& rObjectTransform, + sal_uInt32 nGraphicVersion + ) + : mpSdrOle2Obj(const_cast< SdrOle2Obj* >(&rSdrOle2Obj)), + maObjectTransform(rObjectTransform), + mnGraphicVersion(nGraphicVersion) + { + } + + bool SdrOleContentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if( BufferedDecompositionPrimitive2D::operator==(rPrimitive) ) + { + const SdrOleContentPrimitive2D& rCompare = static_cast<const SdrOleContentPrimitive2D&>(rPrimitive); + const bool bBothNot(!mpSdrOle2Obj.is() && !rCompare.mpSdrOle2Obj.is()); + const bool bBothAndEqual(mpSdrOle2Obj.is() && rCompare.mpSdrOle2Obj.is() + && mpSdrOle2Obj.get() == rCompare.mpSdrOle2Obj.get()); + + return ((bBothNot || bBothAndEqual) + && getObjectTransform() == rCompare.getObjectTransform() + + // #i104867# to find out if the Graphic content of the + // OLE has changed, use GraphicVersion number + && mnGraphicVersion == rCompare.mnGraphicVersion + ); + } + + return false; + } + + basegfx::B2DRange SdrOleContentPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const + { + basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0); + aRange.transform(getObjectTransform()); + + return aRange; + } + + // provide unique ID + sal_uInt32 SdrOleContentPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDROLECONTENTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx new file mode 100644 index 000000000..829525e2b --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx @@ -0,0 +1,156 @@ +/* -*- 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/primitive2d/sdrpathprimitive2d.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrPathPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault() + && getUnitPolyPolygon().isClosed()) + { + // #i108255# no need to use correctOrientations here; target is + // straight visualisation + basegfx::B2DPolyPolygon aTransformed(getUnitPolyPolygon()); + aTransformed.transform(getTransform()); + + // OperationSmiley: Check if a UnitDefinitionPolyPolygon is set + if(getUnitDefinitionPolyPolygon().count() + && getUnitDefinitionPolyPolygon() != getUnitPolyPolygon()) + { + // if yes, use the B2DRange of it's transformed form + basegfx::B2DPolyPolygon aTransformedDefinition(getUnitDefinitionPolyPolygon()); + aTransformedDefinition.transform(getTransform()); + + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + aTransformedDefinition.getB2DRange(), + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + else + { + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + } + + // add line + if(getSdrLFSTAttribute().getLine().isDefault()) + { + // if initially no line is defined, create one for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + getUnitPolyPolygon(), + getTransform())); + } + else + { + Primitive2DContainer aTemp(getUnitPolyPolygon().count()); + + for(sal_uInt32 a(0); a < getUnitPolyPolygon().count(); a++) + { + basegfx::B2DPolygon aTransformed(getUnitPolyPolygon().getB2DPolygon(a)); + + aTransformed.transform(getTransform()); + aTemp[a] = createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + getSdrLFSTAttribute().getLineStartEnd()); + } + + aRetval.append(aTemp); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + getUnitPolyPolygon(), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrPathPrimitive2D::SdrPathPrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DPolyPolygon& rUnitDefinitionPolyPolygon) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + maUnitPolyPolygon(rUnitPolyPolygon), + maUnitDefinitionPolyPolygon(rUnitDefinitionPolyPolygon) + { + } + + bool SdrPathPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrPathPrimitive2D& rCompare = static_cast<const SdrPathPrimitive2D&>(rPrimitive); + + return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon() + && getUnitDefinitionPolyPolygon() == rCompare.getUnitDefinitionPolyPolygon() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrPathPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrprimitivetools.cxx b/svx/source/sdr/primitive2d/sdrprimitivetools.cxx new file mode 100644 index 000000000..6376fa796 --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrprimitivetools.cxx @@ -0,0 +1,64 @@ +/* -*- 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/primitive2d/sdrprimitivetools.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/BitmapTools.hxx> +#include <mutex> + + +// helper methods + +namespace drawinglayer::primitive2d +{ + BitmapEx createDefaultCross_3x3(const basegfx::BColor& rBColor) + { + static vcl::DeleteOnDeinit< BitmapEx > aRetVal(vcl::DeleteOnDeinitFlag::Empty); + static basegfx::BColor aBColor; + static std::mutex aMutex; + + std::scoped_lock aGuard(aMutex); + + if(!aRetVal.get() || rBColor != aBColor) + { + // copy values + aBColor = rBColor; + + // create bitmap + Color c(aBColor); + sal_uInt8 r = c.GetRed(); + sal_uInt8 g = c.GetGreen(); + sal_uInt8 b = c.GetBlue(); + sal_uInt8 a = 255; + const sal_uInt8 cross[] = { + 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a, + r, g, b, 0, r, g, b, 0, r, g, b, 0, + 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a + }; + BitmapEx aBitmap = vcl::bitmap::CreateFromData(cross, 3, 3, 12, vcl::PixelFormat::N32_BPP); + + // create and exchange at aRetVal + aRetVal.set(aBitmap); + } + + return aRetVal.get() ? *aRetVal.get() : BitmapEx(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx new file mode 100644 index 000000000..f289ea6bc --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx @@ -0,0 +1,152 @@ +/* -*- 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/primitive2d/sdrrectangleprimitive2d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <sdr/primitive2d/sdrdecompositiontools.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> + + +using namespace com::sun::star; + + +namespace drawinglayer::primitive2d +{ + void SdrRectanglePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const + { + Primitive2DContainer aRetval; + + // create unit outline polygon + const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect( + basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), + getCornerRadiusX(), + getCornerRadiusY())); + + // add fill + if(!getSdrLFSTAttribute().getFill().isDefault()) + { + basegfx::B2DPolyPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolyPolygonFillPrimitive( + aTransformed, + getSdrLFSTAttribute().getFill(), + getSdrLFSTAttribute().getFillFloatTransGradient())); + } + else if(getForceFillForHitTest()) + { + // if no fill and it's a text frame, create a fill for HitTest and + // BoundRect fallback + aRetval.push_back( + createHiddenGeometryPrimitives2D( + true, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add line + if(!getSdrLFSTAttribute().getLine().isDefault()) + { + basegfx::B2DPolygon aTransformed(aUnitOutline); + + aTransformed.transform(getTransform()); + aRetval.push_back( + createPolygonLinePrimitive( + aTransformed, + getSdrLFSTAttribute().getLine(), + attribute::SdrLineStartEndAttribute())); + } + else if(!getForceFillForHitTest()) + { + // if initially no line is defined and it's not a text frame, create + // a line for HitTest and BoundRect + aRetval.push_back( + createHiddenGeometryPrimitives2D( + false, + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform())); + } + + // add text + if(!getSdrLFSTAttribute().getText().isDefault()) + { + aRetval.push_back( + createTextPrimitive( + basegfx::B2DPolyPolygon(aUnitOutline), + getTransform(), + getSdrLFSTAttribute().getText(), + getSdrLFSTAttribute().getLine(), + false, + false)); + } + + // add shadow + if(!getSdrLFSTAttribute().getShadow().isDefault()) + { + aRetval = createEmbeddedShadowPrimitive( + std::move(aRetval), + getSdrLFSTAttribute().getShadow()); + } + + rContainer.append(std::move(aRetval)); + } + + SdrRectanglePrimitive2D::SdrRectanglePrimitive2D( + const basegfx::B2DHomMatrix& rTransform, + const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute, + double fCornerRadiusX, + double fCornerRadiusY, + bool bForceFillForHitTest) + : maTransform(rTransform), + maSdrLFSTAttribute(rSdrLFSTAttribute), + mfCornerRadiusX(fCornerRadiusX), + mfCornerRadiusY(fCornerRadiusY), + mbForceFillForHitTest(bForceFillForHitTest) + { + } + + bool SdrRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrRectanglePrimitive2D& rCompare = static_cast<const SdrRectanglePrimitive2D&>(rPrimitive); + + return (getCornerRadiusX() == rCompare.getCornerRadiusX() + && getCornerRadiusY() == rCompare.getCornerRadiusY() + && getTransform() == rCompare.getTransform() + && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute() + && getForceFillForHitTest() == rCompare.getForceFillForHitTest()); + } + + return false; + } + + // provide unique ID + sal_uInt32 SdrRectanglePrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx new file mode 100644 index 000000000..6311c123d --- /dev/null +++ b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx @@ -0,0 +1,545 @@ +/* -*- 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/primitive2d/sdrtextprimitive2d.hxx> +#include <svx/svdotext.hxx> +#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/editobj.hxx> +#include <editeng/flditem.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <svx/unoapi.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdoutl.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <osl/diagnose.h> + + +using namespace com::sun::star; + + +namespace +{ + sal_Int16 getPageNumber(const uno::Reference< drawing::XDrawPage >& rxDrawPage) + { + sal_Int16 nRetval(0); + uno::Reference< beans::XPropertySet > xSet(rxDrawPage, uno::UNO_QUERY); + + if (xSet.is()) + { + try + { + const uno::Any aNumber(xSet->getPropertyValue("Number")); + aNumber >>= nRetval; + } + catch(const uno::Exception&) + { + OSL_ASSERT(false); + } + } + + return nRetval; + } + + sal_Int16 getPageCount(const uno::Reference< drawing::XDrawPage >& rxDrawPage) + { + sal_Int16 nRetval(0); + SdrPage* pPage = GetSdrPageFromXDrawPage(rxDrawPage); + + if(pPage) + { + if( (pPage->GetPageNum() == 0) && !pPage->IsMasterPage() ) + { + // handout page! + return pPage->getSdrModelFromSdrPage().getHandoutPageCount(); + } + else + { + const sal_uInt16 nPageCount(pPage->getSdrModelFromSdrPage().GetPageCount()); + nRetval = (static_cast<sal_Int16>(nPageCount) - 1) / 2; + } + } + + return nRetval; + } +} // end of anonymous namespace + + +namespace drawinglayer::primitive2d +{ + // support for XTEXT_PAINTSHAPE_BEGIN/XTEXT_PAINTSHAPE_END Metafile comments + // for slideshow. This uses TextHierarchyBlockPrimitive2D to mark a text block. + // ATM there is only one text block per SdrObject, this may get more in the future + void SdrTextPrimitive2D::encapsulateWithTextHierarchyBlockPrimitive2D(Primitive2DContainer& rContainer, Primitive2DContainer&& aCandidate) + { + rContainer.push_back(new TextHierarchyBlockPrimitive2D(drawinglayer::primitive2d::Primitive2DContainer(aCandidate))); + } + + SdrTextPrimitive2D::SdrTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject) + : mrSdrText(const_cast< SdrText* >(pSdrText)), + maOutlinerParaObject(rOutlinerParaObject), + mnLastPageNumber(0), + mnLastPageCount(0), + mbContainsPageField(false), + mbContainsPageCountField(false), + mbContainsOtherFields(false) + { + const EditTextObject& rETO = maOutlinerParaObject.GetTextObject(); + + mbContainsPageField = rETO.HasField(SvxPageField::CLASS_ID); + mbContainsPageCountField = rETO.HasField(SvxPagesField::CLASS_ID); + mbContainsOtherFields = rETO.HasField(SvxHeaderField::CLASS_ID) + || rETO.HasField(SvxFooterField::CLASS_ID) + || rETO.HasField(SvxDateTimeField::CLASS_ID) + || rETO.HasField(SvxAuthorField::CLASS_ID); + } + + const SdrText* SdrTextPrimitive2D::getSdrText() const { return mrSdrText.get(); } + + bool SdrTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(BufferedDecompositionPrimitive2D::operator==(rPrimitive)) + { + const SdrTextPrimitive2D& rCompare = static_cast<const SdrTextPrimitive2D&>(rPrimitive); + + return ( + + // compare OPO and content, but not WrongList + getOutlinerParaObject() == rCompare.getOutlinerParaObject() + + // also compare WrongList (not-persistent data, but visualized) + && getOutlinerParaObject().isWrongListEqual(rCompare.getOutlinerParaObject())); + } + + return false; + } + + void SdrTextPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const + { + uno::Reference< drawing::XDrawPage > xCurrentlyVisualizingPage; + bool bCurrentlyVisualizingPageIsSet(false); + Color aNewTextBackgroundColor; + bool bNewTextBackgroundColorIsSet(false); + sal_Int16 nCurrentlyValidPageNumber(0); + sal_Int16 nCurrentlyValidPageCount(0); + + if(!getBuffered2DDecomposition().empty()) + { + bool bDoDelete(false); + + // check visualized page + if(mbContainsPageField || mbContainsPageCountField || mbContainsOtherFields) + { + // get visualized page and remember + xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage(); + bCurrentlyVisualizingPageIsSet = true; + + if(xCurrentlyVisualizingPage != mxLastVisualizingPage) + { + bDoDelete = true; + } + + // #i98870# check visualized PageNumber + if(!bDoDelete && mbContainsPageField) + { + nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage); + + if(nCurrentlyValidPageNumber != mnLastPageNumber) + { + bDoDelete = true; + } + } + + // #i98870# check visualized PageCount, too + if(!bDoDelete && mbContainsPageCountField) + { + nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage); + + if(nCurrentlyValidPageCount != mnLastPageCount) + { + bDoDelete = true; + } + } + } + + // #i101443# check change of TextBackgroundolor + if(!bDoDelete && getSdrText()) + { + SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner(); + aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor(); + bNewTextBackgroundColorIsSet = true; + + if(aNewTextBackgroundColor != maLastTextBackgroundColor) + { + bDoDelete = true; + } + } + + if(bDoDelete) + { + const_cast< SdrTextPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer()); + } + } + + if(getBuffered2DDecomposition().empty()) + { + if(!bCurrentlyVisualizingPageIsSet && mbContainsPageField) + { + xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage(); + } + + if(!nCurrentlyValidPageNumber && mbContainsPageField) + { + nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage); + } + + if(!nCurrentlyValidPageCount && mbContainsPageCountField) + { + nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage); + } + + if(!bNewTextBackgroundColorIsSet && getSdrText()) + { + SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner(); + aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor(); + } + + const_cast< SdrTextPrimitive2D* >(this)->mxLastVisualizingPage = xCurrentlyVisualizingPage; + const_cast< SdrTextPrimitive2D* >(this)->mnLastPageNumber = nCurrentlyValidPageNumber; + const_cast< SdrTextPrimitive2D* >(this)->mnLastPageCount = nCurrentlyValidPageCount; + const_cast< SdrTextPrimitive2D* >(this)->maLastTextBackgroundColor = aNewTextBackgroundColor; + } + + // call parent + BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation); + } + + + + + void SdrContourTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeContourTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrContourTextPrimitive2D::SdrContourTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DPolyPolygon& rUnitPolyPolygon, + const basegfx::B2DHomMatrix& rObjectTransform) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maUnitPolyPolygon(rUnitPolyPolygon), + maObjectTransform(rObjectTransform) + { + } + + bool SdrContourTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrContourTextPrimitive2D& rCompare = static_cast<const SdrContourTextPrimitive2D&>(rPrimitive); + + return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon() + && getObjectTransform() == rCompare.getObjectTransform()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrContourTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrContourTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + getUnitPolyPolygon(), + rTransform * getObjectTransform()); + } + + // provide unique ID + sal_uInt32 SdrContourTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCONTOURTEXTPRIMITIVE2D; + } + + + + void SdrPathTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposePathTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrPathTextPrimitive2D::SdrPathTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DPolyPolygon& rPathPolyPolygon, + const attribute::SdrFormTextAttribute& rSdrFormTextAttribute) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maPathPolyPolygon(rPathPolyPolygon), + maSdrFormTextAttribute(rSdrFormTextAttribute) + { + } + + bool SdrPathTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrPathTextPrimitive2D& rCompare = static_cast<const SdrPathTextPrimitive2D&>(rPrimitive); + + return (getPathPolyPolygon() == rCompare.getPathPolyPolygon() + && getSdrFormTextAttribute() == rCompare.getSdrFormTextAttribute()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrPathTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + basegfx::B2DPolyPolygon aNewPolyPolygon(getPathPolyPolygon()); + aNewPolyPolygon.transform(rTransform); + + return new SdrPathTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + aNewPolyPolygon, + getSdrFormTextAttribute()); + } + + // provide unique ID + sal_uInt32 SdrPathTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRPATHTEXTPRIMITIVE2D; + } + + + + void SdrBlockTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeBlockTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrBlockTextPrimitive2D::SdrBlockTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform, + SdrTextHorzAdjust aSdrTextHorzAdjust, + SdrTextVertAdjust aSdrTextVertAdjust, + bool bFixedCellHeight, + bool bUnlimitedPage, + bool bCellText, + bool bWordWrap) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform), + maSdrTextHorzAdjust(aSdrTextHorzAdjust), + maSdrTextVertAdjust(aSdrTextVertAdjust), + mbFixedCellHeight(bFixedCellHeight), + mbUnlimitedPage(bUnlimitedPage), + mbCellText(bCellText), + mbWordWrap(bWordWrap) + { + } + + bool SdrBlockTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && getSdrTextHorzAdjust() == rCompare.getSdrTextHorzAdjust() + && getSdrTextVertAdjust() == rCompare.getSdrTextVertAdjust() + && isFixedCellHeight() == rCompare.isFixedCellHeight() + && getUnlimitedPage() == rCompare.getUnlimitedPage() + && getCellText() == rCompare.getCellText() + && getWordWrap() == rCompare.getWordWrap()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrBlockTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrBlockTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + rTransform * getTextRangeTransform(), + getSdrTextHorzAdjust(), + getSdrTextVertAdjust(), + isFixedCellHeight(), + getUnlimitedPage(), + getCellText(), + getWordWrap()); + } + + // provide unique ID + sal_uInt32 SdrBlockTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRBLOCKTEXTPRIMITIVE2D; + } + + + + void SdrAutoFitTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeAutoFitTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrAutoFitTextPrimitive2D::SdrAutoFitTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rParaObj, + const ::basegfx::B2DHomMatrix& rTextRangeTransform, + bool bWordWrap) + : SdrTextPrimitive2D(pSdrText, rParaObj), + maTextRangeTransform(rTextRangeTransform), + mbWordWrap(bWordWrap) + { + } + + bool SdrAutoFitTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && getWordWrap() == rCompare.getWordWrap()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrAutoFitTextPrimitive2D::createTransformedClone(const ::basegfx::B2DHomMatrix& rTransform) const + { + return new SdrAutoFitTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform(), getWordWrap()); + } + + // provide unique ID + sal_uInt32 SdrAutoFitTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRAUTOFITTEXTPRIMITIVE2D; + } + + + + + SdrChainedTextPrimitive2D::SdrChainedTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform) + { } + + void SdrChainedTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeChainedTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + bool SdrChainedTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrChainedTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrChainedTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform()); + } + + // provide unique ID + sal_uInt32 SdrChainedTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRCHAINEDTEXTPRIMITIVE2D; + } + + + void SdrStretchTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const + { + Primitive2DContainer aRetval; + getSdrText()->GetObject().impDecomposeStretchTextPrimitive(aRetval, *this, aViewInformation); + + encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval)); + } + + SdrStretchTextPrimitive2D::SdrStretchTextPrimitive2D( + const SdrText* pSdrText, + const OutlinerParaObject& rOutlinerParaObject, + const basegfx::B2DHomMatrix& rTextRangeTransform, + bool bFixedCellHeight) + : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject), + maTextRangeTransform(rTextRangeTransform), + mbFixedCellHeight(bFixedCellHeight) + { + } + + bool SdrStretchTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const + { + if(SdrTextPrimitive2D::operator==(rPrimitive)) + { + const SdrStretchTextPrimitive2D& rCompare = static_cast<const SdrStretchTextPrimitive2D&>(rPrimitive); + + return (getTextRangeTransform() == rCompare.getTextRangeTransform() + && isFixedCellHeight() == rCompare.isFixedCellHeight()); + } + + return false; + } + + rtl::Reference<SdrTextPrimitive2D> SdrStretchTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const + { + return new SdrStretchTextPrimitive2D( + getSdrText(), + getOutlinerParaObject(), + rTransform * getTextRangeTransform(), + isFixedCellHeight()); + } + + // provide unique ID + sal_uInt32 SdrStretchTextPrimitive2D::getPrimitive2DID() const + { + return PRIMITIVE2D_ID_SDRSTRETCHTEXTPRIMITIVE2D; + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/primitive3d/sdrattributecreator3d.cxx b/svx/source/sdr/primitive3d/sdrattributecreator3d.cxx new file mode 100644 index 000000000..6bf8f8c6e --- /dev/null +++ b/svx/source/sdr/primitive3d/sdrattributecreator3d.cxx @@ -0,0 +1,145 @@ +/* -*- 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/primitive3d/sdrattributecreator3d.hxx> +#include <svx/svx3ditems.hxx> +#include <svl/itemset.hxx> +#include <com/sun/star/drawing/NormalsKind.hpp> +#include <com/sun/star/drawing/TextureProjectionMode.hpp> +#include <com/sun/star/drawing/TextureKind2.hpp> +#include <com/sun/star/drawing/TextureMode.hpp> +#include <svx/xflclit.hxx> +#include <drawinglayer/attribute/materialattribute3d.hxx> +#include <drawinglayer/attribute/sdrobjectattribute3d.hxx> + + +namespace drawinglayer::primitive2d +{ + attribute::Sdr3DObjectAttribute createNewSdr3DObjectAttribute(const SfxItemSet& rSet) + { + // get NormalsKind + css::drawing::NormalsKind aNormalsKind(css::drawing::NormalsKind_SPECIFIC); + const sal_uInt16 nNormalsValue(rSet.Get(SDRATTR_3DOBJ_NORMALS_KIND).GetValue()); + + if(1 == nNormalsValue) + { + aNormalsKind = css::drawing::NormalsKind_FLAT; + } + else if(2 == nNormalsValue) + { + aNormalsKind = css::drawing::NormalsKind_SPHERE; + } + + // get NormalsInvert flag + const bool bInvertNormals(rSet.Get(SDRATTR_3DOBJ_NORMALS_INVERT).GetValue()); + + // get TextureProjectionX + css::drawing::TextureProjectionMode aTextureProjectionX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC); + const sal_uInt16 nTextureValueX(rSet.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_X).GetValue()); + + if(1 == nTextureValueX) + { + aTextureProjectionX = css::drawing::TextureProjectionMode_PARALLEL; + } + else if(2 == nTextureValueX) + { + aTextureProjectionX = css::drawing::TextureProjectionMode_SPHERE; + } + + // get TextureProjectionY + css::drawing::TextureProjectionMode aTextureProjectionY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC); + const sal_uInt16 nTextureValueY(rSet.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_Y).GetValue()); + + if(1 == nTextureValueY) + { + aTextureProjectionY = css::drawing::TextureProjectionMode_PARALLEL; + } + else if(2 == nTextureValueY) + { + aTextureProjectionY = css::drawing::TextureProjectionMode_SPHERE; + } + + // get DoubleSided flag + const bool bDoubleSided(rSet.Get(SDRATTR_3DOBJ_DOUBLE_SIDED).GetValue()); + + // get Shadow3D flag + const bool bShadow3D(rSet.Get(SDRATTR_3DOBJ_SHADOW_3D).GetValue()); + + // get TextureFilter flag + const bool bTextureFilter(rSet.Get(SDRATTR_3DOBJ_TEXTURE_FILTER).GetValue()); + + // get texture kind + // TextureKind: 1 == Base3DTextureLuminance, 2 == Base3DTextureIntensity, 3 == Base3DTextureColor + css::drawing::TextureKind2 aTextureKind(css::drawing::TextureKind2_LUMINANCE); + const sal_uInt16 nTextureKind(rSet.Get(SDRATTR_3DOBJ_TEXTURE_KIND).GetValue()); + + if(2 == nTextureKind) + { + aTextureKind = css::drawing::TextureKind2_INTENSITY; + } + else if(3 == nTextureKind) + { + aTextureKind = css::drawing::TextureKind2_COLOR; + } + + // get texture mode + // TextureMode: 1 == Base3DTextureReplace, 2 == Base3DTextureModulate, 3 == Base3DTextureBlend + css::drawing::TextureMode aTextureMode(css::drawing::TextureMode_REPLACE); + const sal_uInt16 nTextureMode(rSet.Get(SDRATTR_3DOBJ_TEXTURE_MODE).GetValue()); + + if(2 == nTextureMode) + { + aTextureMode = css::drawing::TextureMode_MODULATE; + } + else if(3 == nTextureMode) + { + aTextureMode = css::drawing::TextureMode_BLEND; + } + + // get object color + const ::basegfx::BColor aObjectColor(rSet.Get(XATTR_FILLCOLOR).GetColorValue().getBColor()); + + // get specular color + const ::basegfx::BColor aSpecular(rSet.Get(SDRATTR_3DOBJ_MAT_SPECULAR).GetValue().getBColor()); + + // get emissive color + const ::basegfx::BColor aEmission(rSet.Get(SDRATTR_3DOBJ_MAT_EMISSION).GetValue().getBColor()); + + // get specular intensity + sal_uInt16 nSpecularIntensity(rSet.Get(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY).GetValue()); + + if(nSpecularIntensity > 128) + { + nSpecularIntensity = 128; + } + + // get reduced line geometry + const bool bReducedLineGeometry(rSet.Get(SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY).GetValue()); + + // prepare material + attribute::MaterialAttribute3D aMaterial(aObjectColor, aSpecular, aEmission, nSpecularIntensity); + + return attribute::Sdr3DObjectAttribute( + aNormalsKind, aTextureProjectionX, aTextureProjectionY, + aTextureKind, aTextureMode, aMaterial, + bInvertNormals, bDoubleSided, bShadow3D, bTextureFilter, bReducedLineGeometry); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/attributeproperties.cxx b/svx/source/sdr/properties/attributeproperties.cxx new file mode 100644 index 000000000..7f141b8f6 --- /dev/null +++ b/svx/source/sdr/properties/attributeproperties.cxx @@ -0,0 +1,541 @@ +/* -*- 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/properties/attributeproperties.hxx> +#include <tools/debug.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svl/whiter.hxx> +#include <svl/poolitem.hxx> +#include <svx/svdobj.hxx> +#include <svx/svddef.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/xlndsit.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xflgrit.hxx> +#include <svx/xflftrit.hxx> +#include <svx/xflhtit.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdpage.hxx> +#include <osl/diagnose.h> + +namespace sdr::properties +{ + void AttributeProperties::ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr) + { + if(HasSfxItemSet() && mpStyleSheet) + { + // Delete hard attributes where items are set in the style sheet + if(!bDontRemoveHardAttr) + { + const SfxItemSet& rStyle = mpStyleSheet->GetItemSet(); + SfxWhichIter aIter(rStyle); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while(nWhich) + { + if(SfxItemState::SET == aIter.GetItemState()) + { + mxItemSet->ClearItem(nWhich); + } + + nWhich = aIter.NextWhich(); + } + } + + // set new stylesheet as parent + mxItemSet->SetParent(&mpStyleSheet->GetItemSet()); + } + else + { + OSL_ENSURE(false, "ImpSetParentAtSfxItemSet called without SfxItemSet/SfxStyleSheet (!)"); + } + } + + void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr) + { + // test if old StyleSheet is cleared, else it would be lost + // after this method -> memory leak (!) + DBG_ASSERT(!mpStyleSheet, "Old style sheet not deleted before setting new one (!)"); + + if(!pNewStyleSheet) + return; + + // local remember + mpStyleSheet = pNewStyleSheet; + + if(HasSfxItemSet()) + { + // register as listener + StartListening(*pNewStyleSheet->GetPool()); + StartListening(*pNewStyleSheet); + + // only apply the following when we have an SfxItemSet already, else + if(GetStyleSheet()) + { + ImpSetParentAtSfxItemSet(bDontRemoveHardAttr); + } + } + } + + void AttributeProperties::ImpRemoveStyleSheet() + { + // Check type since it is destroyed when the type is deleted + if(GetStyleSheet() && mpStyleSheet) + { + EndListening(*mpStyleSheet); + if (auto const pool = mpStyleSheet->GetPool()) { // TTTT + EndListening(*pool); + } + + // reset parent of ItemSet + if(HasSfxItemSet()) + { + mxItemSet->SetParent(nullptr); + } + + SdrObject& rObj = GetSdrObject(); + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + } + + mpStyleSheet = nullptr; + } + + // create a new itemset + SfxItemSet AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool, + + // ranges from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST>); + } + + AttributeProperties::AttributeProperties(SdrObject& rObj) + : DefaultProperties(rObj), + mpStyleSheet(nullptr) + { + // Do nothing else, esp. do *not* try to get and set + // a default SfxStyle sheet. Nothing is allowed to be done + // that may lead to calls to virtual functions like + // CreateObjectSpecificItemSet - these would go *wrong*. + // Thus the rest is lazy-init from here. + } + + AttributeProperties::AttributeProperties(const AttributeProperties& rProps, SdrObject& rObj) + : DefaultProperties(rProps, rObj), + mpStyleSheet(nullptr) + { + SfxStyleSheet* pTargetStyleSheet(rProps.GetStyleSheet()); + + if(pTargetStyleSheet) + { + const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject()); + + if(bModelChange) + { + // tdf#117506 + // The error shows that it is definitely necessary to solve this problem. + // Interestingly I already had a note here for 'work needed'. + // Checked in libreoffice-6-0 what happened there. In principle, the whole + // ::Clone of SdrPage and SdrObject happened in the same SdrModel, only + // afterwards a ::SetModel was used at the cloned SdrPage which went through + // all layers. The StyleSheet-problem was solved in + // AttributeProperties::MoveToItemPool at the end. There, a StyleSheet with the + // same name was searched for in the target-SdrModel. + // Start by resetting the current TargetStyleSheet so that nothing goes wrong + // when we do not find a fitting TargetStyleSheet. + // Note: The test for SdrModelChange above was wrong (compared the already set + // new SdrObject), so this never triggered and pTargetStyleSheet was never set to + // nullptr before. This means that a StyleSheet from another SdrModel was used + // what of course is very dangerous. Interestingly did not crash since when that + // other SdrModel was destroyed the ::Notify mechanism still worked reliably + // and de-connected this Properties successfully from the alien-StyleSheet. + pTargetStyleSheet = nullptr; + + // Check if we have a TargetStyleSheetPool at the target-SdrModel. This *should* + // be the case already (SdrModel::Merge and SdDrawDocument::InsertBookmarkAsPage + // have already cloned the StyleSheets to the target-SdrModel when used in Draw/impress). + // If none is found, ImpGetDefaultStyleSheet will be used to set a 'default' + // StyleSheet as StyleSheet implicitly later (that's what happened in the task, + // thus the FillStyle changed to the 'default' Blue). + // Note: It *may* be necessary to do more for StyleSheets, e.g. clone/copy the + // StyleSheet Hierarchy from the source SdrModel and/or add the Items from there + // as hard attributes. If needed, have a look at the older AttributeProperties::SetModel + // implementation from e.g. libreoffice-6-0. + SfxStyleSheetBasePool* pTargetStyleSheetPool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool()); + + if(nullptr != pTargetStyleSheetPool) + { + // If we have a TargetStyleSheetPool, search for the used StyleSheet + // in the target SdrModel using the Name from the original StyleSheet + // in the source-SdrModel. + pTargetStyleSheet = dynamic_cast< SfxStyleSheet* >( + pTargetStyleSheetPool->Find( + rProps.GetStyleSheet()->GetName(), + SfxStyleFamily::All)); + } + } + } + + if(!pTargetStyleSheet) + return; + + if(HasSfxItemSet()) + { + // The SfxItemSet has been cloned and exists, + // we can directly set the SfxStyleSheet at it + ImpAddStyleSheet(pTargetStyleSheet, true); + } + else + { + // No SfxItemSet exists yet (there is none in + // the source, so none was cloned). Remember the + // SfxStyleSheet to set it when the SfxItemSet + // got constructed on-demand + mpStyleSheet = pTargetStyleSheet; + } + } + + AttributeProperties::~AttributeProperties() + { + ImpRemoveStyleSheet(); + } + + std::unique_ptr<BaseProperties> AttributeProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new AttributeProperties(*this, rObj)); + } + + const SfxItemSet& AttributeProperties::GetObjectItemSet() const + { + // remember if we had a SfxItemSet already + const bool bHadSfxItemSet(HasSfxItemSet()); + + // call parent - this will guarantee SfxItemSet existence + DefaultProperties::GetObjectItemSet(); + + if(!bHadSfxItemSet) + { + // need to take care for SfxStyleSheet for newly + // created SfxItemSet + if(nullptr == mpStyleSheet) + { + // Set missing defaults without removal of hard attributes. + // This is more complicated historically than I first thought: + // Originally for GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj + // SetStyleSheet(..., false) was used, while for GetDefaultStyleSheet + // SetStyleSheet(..., true) was used. Thus, for SdrGrafObj and SdrOle2Obj + // bDontRemoveHardAttr == false -> *do* delete hard attributes was used. + // This was probably not done by purpose, adding the method + // GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj additionally to + // GetDefaultStyleSheet was an enhancement to allow for SdrGrafObj/SdrOle2Obj + // with full AttributeSet (adding e.g. FillAttributes). To stay as compatible + // as possible these SdrObjects got a new default-StyleSheet. + // There is no reason to delete the HardAttributes and it anyways has only + // AFAIK effects on a single Item - the SdrTextHorzAdjustItem. To get things + // unified I will stay with not deleting the HardAttributes and adapt the + // UnitTests in CppunitTest_sd_import_tests accordingly. + const_cast< AttributeProperties* >(this)->applyDefaultStyleSheetFromSdrModel(); + } + else + { + // Late-Init of setting parent to SfxStyleSheet after + // it's creation. Can only happen from copy-constructor + // (where creation of SfxItemSet is avoided due to the + // problem with constructors and virtual functions in C++), + // thus DontRemoveHardAttr is not needed. + const_cast< AttributeProperties* >(this)->SetStyleSheet( + mpStyleSheet, true, true); + } + } + + return *mxItemSet; + } + + void AttributeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/) + { + // own modifications + SdrObject& rObj = GetSdrObject(); + + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + rObj.SetChanged(); + } + + void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) + { + if(pNewItem) + { + std::unique_ptr<SfxPoolItem> pResultItem; + SdrModel& rModel(GetSdrObject().getSdrModelFromSdrObject()); + + switch( nWhich ) + { + case XATTR_FILLBITMAP: + { + // TTTT checkForUniqueItem should use SdrModel& + pResultItem = static_cast<const XFillBitmapItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINEDASH: + { + pResultItem = static_cast<const XLineDashItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINESTART: + { + pResultItem = static_cast<const XLineStartItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_LINEEND: + { + pResultItem = static_cast<const XLineEndItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLGRADIENT: + { + pResultItem = static_cast<const XFillGradientItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLFLOATTRANSPARENCE: + { + // #85953# allow all kinds of XFillFloatTransparenceItem to be set + pResultItem = static_cast<const XFillFloatTransparenceItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + case XATTR_FILLHATCH: + { + pResultItem = static_cast<const XFillHatchItem*>(pNewItem)->checkForUniqueItem( &rModel ); + break; + } + } + + // guarantee SfxItemSet existence + GetObjectItemSet(); + + if(pResultItem) + { + // force ItemSet + mxItemSet->Put(std::move(pResultItem)); + } + else + { + mxItemSet->Put(*pNewItem); + } + } + else + { + // clear item if ItemSet exists + if(HasSfxItemSet()) + { + mxItemSet->ClearItem(nWhich); + } + } + } + + void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool /*bBroadcast*/) + { + // guarantee SfxItemSet existence + GetObjectItemSet(); + + ImpRemoveStyleSheet(); + ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + + SdrObject& rObj = GetSdrObject(); + rObj.SetBoundRectDirty(); + rObj.SetBoundAndSnapRectsDirty(true); + } + + SfxStyleSheet* AttributeProperties::GetStyleSheet() const + { + return mpStyleSheet; + } + + void AttributeProperties::ForceStyleToHardAttributes() + { + if(!GetStyleSheet() || mpStyleSheet == nullptr) + return; + + // guarantee SfxItemSet existence + GetObjectItemSet(); + + // prepare copied, new itemset, but WITHOUT parent + SfxItemSet aDestItemSet(*mxItemSet); + aDestItemSet.SetParent(nullptr); + + // prepare forgetting the current stylesheet like in RemoveStyleSheet() + EndListening(*mpStyleSheet); + EndListening(*mpStyleSheet->GetPool()); + + // prepare the iter; use the mpObjectItemSet which may have less + // WhichIDs than the style. + SfxWhichIter aIter(aDestItemSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + const SfxPoolItem *pItem = nullptr; + + // now set all hard attributes of the current at the new itemset + while(nWhich) + { + // #i61284# use mpItemSet with parents, makes things easier and reduces to + // one loop + if(SfxItemState::SET == mxItemSet->GetItemState(nWhich, true, &pItem)) + { + aDestItemSet.Put(*pItem); + } + + nWhich = aIter.NextWhich(); + } + + // replace itemsets + mxItemSet.emplace(std::move(aDestItemSet)); + + // set necessary changes like in RemoveStyleSheet() + GetSdrObject().SetBoundRectDirty(); + GetSdrObject().SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + + mpStyleSheet = nullptr; + } + + void AttributeProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) + { + bool bHintUsed(false); + + const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint); + + if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet()) + { + SdrObject& rObj = GetSdrObject(); + //SdrPage* pPage = rObj.GetPage(); + + switch(pStyleHint->GetId()) + { + case SfxHintId::StyleSheetCreated : + { + // cannot happen, nothing to do + break; + } + case SfxHintId::StyleSheetModified : + case SfxHintId::StyleSheetChanged : + { + // notify change + break; + } + case SfxHintId::StyleSheetErased : + case SfxHintId::StyleSheetInDestruction : + { + // Style needs to be exchanged + SfxStyleSheet* pNewStSh = nullptr; + SdrModel& rModel(rObj.getSdrModelFromSdrObject()); + + // Do nothing if object is in destruction, else a StyleSheet may be found from + // a StyleSheetPool which is just being deleted itself. and thus it would be fatal + // to register as listener to that new StyleSheet. + if(!rObj.IsInDestruction()) + { + if(SfxStyleSheet* pStyleSheet = GetStyleSheet()) + { + pNewStSh = static_cast<SfxStyleSheet*>(rModel.GetStyleSheetPool()->Find( + pStyleSheet->GetParent(), pStyleSheet->GetFamily())); + } + + if(!pNewStSh) + { + pNewStSh = rModel.GetDefaultStyleSheet(); + } + } + + // remove used style, it's erased or in destruction + ImpRemoveStyleSheet(); + + if(pNewStSh) + { + ImpAddStyleSheet(pNewStSh, true); + } + + break; + } + default: break; + } + + // Get old BoundRect. Do this after the style change is handled + // in the ItemSet parts because GetBoundRect() may calculate a new + tools::Rectangle aBoundRect = rObj.GetLastBoundRect(); + + rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true); + + // tell the object about the change + rObj.SetChanged(); + rObj.BroadcastObjectChange(); + + //if(pPage && pPage->IsInserted()) + //{ + // rObj.BroadcastObjectChange(); + //} + + rObj.SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect); + + bHintUsed = true; + } + + if(!bHintUsed) + { + // forward to SdrObject ATM. Not sure if this will be necessary + // in the future. + GetSdrObject().Notify(rBC, rHint); + } + } + + bool AttributeProperties::isUsedByModel() const + { + const SdrObject& rObj(GetSdrObject()); + if (rObj.IsInserted()) + { + const SdrPage* const pPage(rObj.getSdrPageFromSdrObject()); + if (pPage && pPage->IsInserted()) + return true; + } + return false; + } + + void AttributeProperties::applyDefaultStyleSheetFromSdrModel() + { + SfxStyleSheet* pDefaultStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheet()); + + // tdf#118139 Only do this when StyleSheet really differs. It may e.g. + // be the case that nullptr == pDefaultStyleSheet and there is none set yet, + // so indeed no need to set it (needed for some strange old MSWord2003 + // documents with CustomShape-'Group' and added Text-Frames, see task description) + if(pDefaultStyleSheet != GetStyleSheet()) + { + // do not delete hard attributes when setting dsefault Style + SetStyleSheet(pDefaultStyleSheet, true, true); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/captionproperties.cxx b/svx/source/sdr/properties/captionproperties.cxx new file mode 100644 index 000000000..2f7a2c8a7 --- /dev/null +++ b/svx/source/sdr/properties/captionproperties.cxx @@ -0,0 +1,101 @@ +/* -*- 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/properties/captionproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdocapt.hxx> + + +namespace sdr::properties +{ + // create a new itemset + SfxItemSet CaptionProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet( + rPool, + svl::Items< + // Ranges from SdrAttrObj, SdrCaptionObj: + SDRATTR_START, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>); + } + + CaptionProperties::CaptionProperties(SdrObject& rObj) + : RectangleProperties(rObj) + { + } + + CaptionProperties::CaptionProperties(const CaptionProperties& rProps, SdrObject& rObj) + : RectangleProperties(rProps, rObj) + { + } + + CaptionProperties::~CaptionProperties() + { + } + + std::unique_ptr<BaseProperties> CaptionProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new CaptionProperties(*this, rObj)); + } + + void CaptionProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrCaptionObj& rObj = static_cast<SdrCaptionObj&>(GetSdrObject()); + + // local changes + rObj.ImpRecalcTail(); + + // call parent + RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + } + + void CaptionProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + SdrCaptionObj& rObj = static_cast<SdrCaptionObj&>(GetSdrObject()); + rObj.ImpRecalcTail(); + } + + void CaptionProperties::ForceDefaultAttributes() + { + // call parent + RectangleProperties::ForceDefaultAttributes(); + + // force ItemSet + GetObjectItemSet(); + + // this was set by TextProperties::ForceDefaultAttributes(), + // reset to default + mxItemSet->ClearItem(XATTR_LINESTYLE); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/circleproperties.cxx b/svx/source/sdr/properties/circleproperties.cxx new file mode 100644 index 000000000..0c2a5bdc1 --- /dev/null +++ b/svx/source/sdr/properties/circleproperties.cxx @@ -0,0 +1,125 @@ +/* -*- 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/properties/circleproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdocirc.hxx> +#include <sxcikitm.hxx> +#include <svx/sxciaitm.hxx> + + +namespace sdr::properties +{ + // create a new itemset + SfxItemSet CircleProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet( + rPool, + svl::Items< + // Ranges from SdrAttrObj, SdrCircObj + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_CIRC_FIRST, SDRATTR_CIRC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>); + } + + CircleProperties::CircleProperties(SdrObject& rObj) + : RectangleProperties(rObj) + { + } + + CircleProperties::CircleProperties(const CircleProperties& rProps, SdrObject& rObj) + : RectangleProperties(rProps, rObj) + { + } + + CircleProperties::~CircleProperties() + { + } + + std::unique_ptr<BaseProperties> CircleProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new CircleProperties(*this, rObj)); + } + + void CircleProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject()); + + // call parent + RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // local changes + rObj.ImpSetAttrToCircInfo(); + } + + void CircleProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject()); + rObj.SetXPolyDirty(); + + // local changes + rObj.ImpSetAttrToCircInfo(); + } + + void CircleProperties::ForceDefaultAttributes() + { + SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject()); + SdrCircKind eKind = rObj.GetCircleKind(); + + if(eKind != SdrCircKind::Full) + { + // force ItemSet + GetObjectItemSet(); + + mxItemSet->Put(SdrCircKindItem(eKind)); + + if(rObj.GetStartAngle()) + { + mxItemSet->Put(makeSdrCircStartAngleItem(rObj.GetStartAngle())); + } + + if(rObj.GetEndAngle() != 36000_deg100) + { + mxItemSet->Put(makeSdrCircEndAngleItem(rObj.GetEndAngle())); + } + } + + // call parent after SetObjectItem(SdrCircKindItem()) + // because ForceDefaultAttr() will call + // ImpSetAttrToCircInfo() which needs a correct + // SdrCircKindItem + RectangleProperties::ForceDefaultAttributes(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/connectorproperties.cxx b/svx/source/sdr/properties/connectorproperties.cxx new file mode 100644 index 000000000..375a98190 --- /dev/null +++ b/svx/source/sdr/properties/connectorproperties.cxx @@ -0,0 +1,89 @@ +/* -*- 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/properties/connectorproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdoedge.hxx> + + +namespace sdr::properties +{ + // create a new itemset + SfxItemSet ConnectorProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet( + rPool, + svl::Items< + // Ranges from SdrAttrObj, SdrEdgeObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_EDGE_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>); + } + + ConnectorProperties::ConnectorProperties(SdrObject& rObj) + : TextProperties(rObj) + { + } + + ConnectorProperties::ConnectorProperties(const ConnectorProperties& rProps, SdrObject& rObj) + : TextProperties(rProps, rObj) + { + } + + ConnectorProperties::~ConnectorProperties() + { + } + + std::unique_ptr<BaseProperties> ConnectorProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new ConnectorProperties(*this, rObj)); + } + + void ConnectorProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrEdgeObj& rObj = static_cast<SdrEdgeObj&>(GetSdrObject()); + + // call parent + TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // local changes + rObj.ImpSetAttrToEdgeInfo(); + } + + void ConnectorProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + SdrEdgeObj& rObj = static_cast<SdrEdgeObj&>(GetSdrObject()); + rObj.ImpSetAttrToEdgeInfo(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/customshapeproperties.cxx b/svx/source/sdr/properties/customshapeproperties.cxx new file mode 100644 index 000000000..59135cde1 --- /dev/null +++ b/svx/source/sdr/properties/customshapeproperties.cxx @@ -0,0 +1,230 @@ +/* -*- 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/properties/customshapeproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svdoashp.hxx> +#include <svx/sdooitm.hxx> +#include <editeng/eeitem.hxx> +#include <svl/whiter.hxx> +#include <svl/hint.hxx> + + +namespace sdr::properties +{ + void CustomShapeProperties::UpdateTextFrameStatus(bool bInvalidateRenderGeometry) + { + SdrObjCustomShape& rObj = static_cast< SdrObjCustomShape& >(GetSdrObject()); + const bool bOld(rObj.mbTextFrame); + + // change TextFrame flag when bResizeShapeToFitText changes (which is mapped + // on the item SDRATTR_TEXT_AUTOGROWHEIGHT for custom shapes, argh) + rObj.mbTextFrame = GetObjectItemSet().Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue(); + + // check if it did change + if(rObj.mbTextFrame != bOld) + { + // on change also invalidate render geometry + bInvalidateRenderGeometry = true; + + // #115391# Potential recursion, since it calls SetObjectItemSet again, but rObj.bTextFrame + // will not change again. Thus it will be only one level and terminate safely + rObj.AdaptTextMinSize(); + } + + if(bInvalidateRenderGeometry) + { + // if asked for or bResizeShapeToFitText changed, make sure that + // the render geometry is reconstructed using changed parameters + rObj.InvalidateRenderGeometry(); + } + } + + SfxItemSet CustomShapeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet( + rPool, + svl::Items< + // Ranges from SdrAttrObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + // Graphic attributes, 3D properties, CustomShape + // properties: + SDRATTR_GRAF_FIRST, SDRATTR_CUSTOMSHAPE_LAST, + SDRATTR_GLOW_FIRST, SDRATTR_SOFTEDGE_LAST, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>); + } + + bool CustomShapeProperties::AllowItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem ) const + { + bool bAllowItemChange = true; + if ( !pNewItem ) + { + if ( ( nWhich >= SDRATTR_CUSTOMSHAPE_FIRST ) && ( nWhich <= SDRATTR_CUSTOMSHAPE_LAST ) ) + bAllowItemChange = false; + } + if ( bAllowItemChange ) + bAllowItemChange = TextProperties::AllowItemChange( nWhich, pNewItem ); + return bAllowItemChange; + } + + void CustomShapeProperties::ClearObjectItem(const sal_uInt16 nWhich) + { + if ( !nWhich ) + { + SfxWhichIter aIter( *mxItemSet ); + sal_uInt16 nWhich2 = aIter.FirstWhich(); + while( nWhich2 ) + { + TextProperties::ClearObjectItemDirect( nWhich2 ); + nWhich2 = aIter.NextWhich(); + } + ItemSetChanged({}, 0); + } + else + TextProperties::ClearObjectItem( nWhich ); + } + + void CustomShapeProperties::ClearObjectItemDirect(const sal_uInt16 nWhich) + { + if ( !nWhich ) + { + SfxWhichIter aIter( *mxItemSet ); + sal_uInt16 nWhich2 = aIter.FirstWhich(); + while( nWhich2 ) + { + TextProperties::ClearObjectItemDirect( nWhich2 ); + nWhich2 = aIter.NextWhich(); + } + } + else + TextProperties::ClearObjectItemDirect( nWhich ); + } + + void CustomShapeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + // call parent + TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // update bTextFrame and RenderGeometry + UpdateTextFrameStatus(true); + } + + void CustomShapeProperties::PostItemChange(const sal_uInt16 nWhich) + { + switch(nWhich) + { + case SDRATTR_TEXT_AUTOGROWHEIGHT: + { + // #115391# update bTextFrame and RenderGeometry using AdaptTextMinSize() + UpdateTextFrameStatus(false); + break; + } + default: + { + break; + } + } + + // call parent + TextProperties::PostItemChange(nWhich); + } + + void CustomShapeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) + { + // call parent + TextProperties::ItemChange( nWhich, pNewItem ); + + // update bTextFrame and RenderGeometry + UpdateTextFrameStatus(true); + } + + void CustomShapeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + TextProperties::SetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast ); + + // update bTextFrame and RenderGeometry + UpdateTextFrameStatus(true); + } + + void CustomShapeProperties::ForceDefaultAttributes() + { + // update bTextFrame and RenderGeometry + UpdateTextFrameStatus(true); + + // SJ: Following is no good if creating customshapes, leading to objects that are white after loading via xml + // This means: Do *not* call parent here is by purpose... + } + + CustomShapeProperties::CustomShapeProperties(SdrObject& rObj) + : TextProperties(rObj) + { + } + + CustomShapeProperties::CustomShapeProperties(const CustomShapeProperties& rProps, SdrObject& rObj) + : TextProperties(rProps, rObj) + { + } + + CustomShapeProperties::~CustomShapeProperties() + { + } + + std::unique_ptr<BaseProperties> CustomShapeProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new CustomShapeProperties(*this, rObj)); + } + + void CustomShapeProperties::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) + { + TextProperties::Notify( rBC, rHint ); + + bool bRemoveRenderGeometry = false; + const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint); + + if ( pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet() ) + { + switch( pStyleHint->GetId() ) + { + case SfxHintId::StyleSheetModified : + case SfxHintId::StyleSheetChanged : + bRemoveRenderGeometry = true; + break; + default: break; + } + } + else if ( rHint.GetId() == SfxHintId::DataChanged ) + { + bRemoveRenderGeometry = true; + } + + // update bTextFrame and RenderGeometry + UpdateTextFrameStatus(bRemoveRenderGeometry); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/defaultproperties.cxx b/svx/source/sdr/properties/defaultproperties.cxx new file mode 100644 index 000000000..fa5292458 --- /dev/null +++ b/svx/source/sdr/properties/defaultproperties.cxx @@ -0,0 +1,251 @@ +/* -*- 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 <svx/sdr/properties/defaultproperties.hxx> +#include <sdr/properties/itemsettools.hxx> +#include <svl/itemset.hxx> +#include <svl/whiter.hxx> +#include <vector> +#include <svx/svdobj.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <libxml/xmlwriter.h> +#include <svx/svdmodel.hxx> +#include <svx/svdtrans.hxx> +#include <svx/xbtmpit.hxx> + +namespace sdr::properties +{ + SfxItemSet DefaultProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + // Basic implementation; Basic object has NO attributes + return SfxItemSet(rPool); + } + + DefaultProperties::DefaultProperties(SdrObject& rObj) + : BaseProperties(rObj) + { + } + + DefaultProperties::DefaultProperties(const DefaultProperties& rProps, SdrObject& rObj) + : BaseProperties(rObj) + { + if(!rProps.mxItemSet) + return; + + // Clone may be to another model and thus another ItemPool. + // SfxItemSet supports that thus we are able to Clone all + // SfxItemState::SET items to the target pool. + mxItemSet.emplace(rProps.mxItemSet->CloneAsValue( + true, + &rObj.getSdrModelFromSdrObject().GetItemPool())); + + // React on ModelChange: If metric has changed, scale items. + // As seen above, clone is supported, but scale is not included, + // thus: TTTT maybe add scale to SfxItemSet::Clone() (?) + // tdf#117707 correct ModelChange detection + const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject()); + + if(bModelChange) + { + const MapUnit aOldUnit(rProps.GetSdrObject().getSdrModelFromSdrObject().GetScaleUnit()); + const MapUnit aNewUnit(rObj.getSdrModelFromSdrObject().GetScaleUnit()); + const bool bScaleUnitChanged(aNewUnit != aOldUnit); + + if(bScaleUnitChanged) + { + const Fraction aMetricFactor(GetMapFactor(aOldUnit, aNewUnit).X()); + + ScaleItemSet(*mxItemSet, aMetricFactor); + } + } + + // do not keep parent info, this may be changed by later constructors. + // This class just copies the ItemSet, ignore parent. + if(mxItemSet && mxItemSet->GetParent()) + { + mxItemSet->SetParent(nullptr); + } + } + + std::unique_ptr<BaseProperties> DefaultProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new DefaultProperties(*this, rObj)); + } + + DefaultProperties::~DefaultProperties() {} + + const SfxItemSet& DefaultProperties::GetObjectItemSet() const + { + if(!mxItemSet) + { + mxItemSet.emplace(const_cast<DefaultProperties*>(this)->CreateObjectSpecificItemSet(GetSdrObject().GetObjectItemPool())); + const_cast<DefaultProperties*>(this)->ForceDefaultAttributes(); + } + + assert(mxItemSet && "Could not create an SfxItemSet(!)"); + + return *mxItemSet; + } + + void DefaultProperties::SetObjectItem(const SfxPoolItem& rItem) + { + const sal_uInt16 nWhichID(rItem.Which()); + + if(!AllowItemChange(nWhichID, &rItem)) + return; + + ItemChange(nWhichID, &rItem); + PostItemChange(nWhichID); + + const SfxPoolItem* pItem = &rItem; + ItemSetChanged( {&pItem, 1}, 0); + } + + void DefaultProperties::SetObjectItemDirect(const SfxPoolItem& rItem) + { + const sal_uInt16 nWhichID(rItem.Which()); + + if(AllowItemChange(nWhichID, &rItem)) + { + ItemChange(nWhichID, &rItem); + } + } + + void DefaultProperties::ClearObjectItem(const sal_uInt16 nWhich) + { + if(!AllowItemChange(nWhich)) + return; + + ItemChange(nWhich); + PostItemChange(nWhich); + + if(nWhich) + { + ItemSetChanged({}, nWhich); + } + } + + void DefaultProperties::ClearObjectItemDirect(const sal_uInt16 nWhich) + { + if(AllowItemChange(nWhich)) + { + ItemChange(nWhich); + } + } + + void DefaultProperties::SetObjectItemSet(const SfxItemSet& rSet) + { + if (rSet.HasItem(XATTR_FILLBITMAP)) + { + const XFillBitmapItem* pItem = rSet.GetItem(XATTR_FILLBITMAP); + const std::shared_ptr<VectorGraphicData>& pVectorData + = pItem->GetGraphicObject().GetGraphic().getVectorGraphicData(); + if (pVectorData) + { + // Shape is filled by a vector graphic: tell it our size as a hint. + basegfx::B2DTuple aSizeHint; + aSizeHint.setX(GetSdrObject().GetSnapRect().getWidth()); + aSizeHint.setY(GetSdrObject().GetSnapRect().getHeight()); + pVectorData->setSizeHint(aSizeHint); + } + } + + SfxWhichIter aWhichIter(rSet); + sal_uInt16 nWhich(aWhichIter.FirstWhich()); + std::vector< const SfxPoolItem * > aPostItemChangeList; + // give a hint to STL_Vector + aPostItemChangeList.reserve(rSet.Count()); + + while(nWhich) + { + const SfxPoolItem* pPoolItem; + if(SfxItemState::SET == aWhichIter.GetItemState(false, &pPoolItem)) + { + if(AllowItemChange(nWhich, pPoolItem)) + { + ItemChange(nWhich, pPoolItem); + aPostItemChangeList.emplace_back( pPoolItem ); + } + } + + nWhich = aWhichIter.NextWhich(); + } + + if(!aPostItemChangeList.empty()) + { + for (const auto& rItem : aPostItemChangeList) + { + PostItemChange(rItem->Which()); + } + + ItemSetChanged(aPostItemChangeList, 0); + } + } + + void DefaultProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/) + { + } + + bool DefaultProperties::AllowItemChange(const sal_uInt16 /*nWhich*/, const SfxPoolItem* /*pNewItem*/) const + { + return true; + } + + void DefaultProperties::ItemChange(const sal_uInt16 /*nWhich*/, const SfxPoolItem* /*pNewItem*/) + { + } + + void DefaultProperties::PostItemChange(const sal_uInt16 nWhich ) + { + if( (nWhich == XATTR_FILLSTYLE) && mxItemSet ) + CleanupFillProperties(*mxItemSet); + } + + void DefaultProperties::SetStyleSheet(SfxStyleSheet* /*pNewStyleSheet*/, bool /*bDontRemoveHardAttr*/, + bool /*bBroadcast*/) + { + // no StyleSheet in DefaultProperties + } + + SfxStyleSheet* DefaultProperties::GetStyleSheet() const + { + // no StyleSheet in DefaultProperties + return nullptr; + } + + void DefaultProperties::ForceDefaultAttributes() + { + } + + void DefaultProperties::dumpAsXml(xmlTextWriterPtr pWriter) const + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DefaultProperties")); + BaseProperties::dumpAsXml(pWriter); + if (mxItemSet) + { + mxItemSet->dumpAsXml(pWriter); + } + (void)xmlTextWriterEndElement(pWriter); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dcompoundproperties.cxx b/svx/source/sdr/properties/e3dcompoundproperties.cxx new file mode 100644 index 000000000..c31d85fdc --- /dev/null +++ b/svx/source/sdr/properties/e3dcompoundproperties.cxx @@ -0,0 +1,144 @@ +/* -*- 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/properties/e3dcompoundproperties.hxx> +#include <svl/itemset.hxx> +#include <svx/obj3d.hxx> +#include <svx/scene3d.hxx> + + +namespace sdr::properties +{ + E3dCompoundProperties::E3dCompoundProperties(SdrObject& rObj) + : E3dProperties(rObj) + { + } + + E3dCompoundProperties::E3dCompoundProperties(const E3dCompoundProperties& rProps, SdrObject& rObj) + : E3dProperties(rProps, rObj) + { + } + + E3dCompoundProperties::~E3dCompoundProperties() + { + } + + std::unique_ptr<BaseProperties> E3dCompoundProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dCompoundProperties(*this, rObj)); + } + + const SfxItemSet& E3dCompoundProperties::GetMergedItemSet() const + { + // include Items of scene this object belongs to + const E3dCompoundObject& rObj = static_cast<const E3dCompoundObject&>(GetSdrObject()); + E3dScene* pScene(rObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + // force ItemSet + GetObjectItemSet(); + + // add filtered scene properties (SDRATTR_3DSCENE_) to local itemset + SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aSet(*mxItemSet->GetPool() ); + aSet.Put(pScene->GetProperties().GetObjectItemSet()); + mxItemSet->Put(aSet); + } + + // call parent + return E3dProperties::GetMergedItemSet(); + } + + void E3dCompoundProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems) + { + // Set scene specific items at scene + E3dCompoundObject& rObj = static_cast<E3dCompoundObject&>(GetSdrObject()); + E3dScene* pScene(rObj.getRootE3dSceneFromE3dObject()); + + if(nullptr != pScene) + { + // force ItemSet + GetObjectItemSet(); + + // Generate filtered scene properties (SDRATTR_3DSCENE_) itemset + SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aSet(*mxItemSet->GetPool()); + aSet.Put(rSet); + + if(bClearAllItems) + { + pScene->GetProperties().ClearObjectItem(); + } + + if(aSet.Count()) + { + pScene->GetProperties().SetObjectItemSet(aSet); + } + } + + // call parent. This will set items on local object, too. + E3dProperties::SetMergedItemSet(rSet, bClearAllItems); + } + + void E3dCompoundProperties::PostItemChange(const sal_uInt16 nWhich) + { + // call parent + E3dProperties::PostItemChange(nWhich); + + // handle value change + E3dCompoundObject& rObj = static_cast<E3dCompoundObject&>(GetSdrObject()); + + switch(nWhich) + { + // #i28528# + // Added extra Item (Bool) for chart2 to be able to show reduced line geometry + case SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_DOUBLE_SIDED: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_NORMALS_KIND: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_NORMALS_INVERT: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_TEXTURE_PROJ_X: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_TEXTURE_PROJ_Y: + { + rObj.ActionChanged(); + break; + } + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dextrudeproperties.cxx b/svx/source/sdr/properties/e3dextrudeproperties.cxx new file mode 100644 index 000000000..9b9ffd8be --- /dev/null +++ b/svx/source/sdr/properties/e3dextrudeproperties.cxx @@ -0,0 +1,74 @@ +/* -*- 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/properties/e3dextrudeproperties.hxx> +#include <extrud3d.hxx> + + +namespace sdr::properties +{ + E3dExtrudeProperties::E3dExtrudeProperties(SdrObject& rObj) + : E3dCompoundProperties(rObj) + { + } + + E3dExtrudeProperties::E3dExtrudeProperties(const E3dExtrudeProperties& rProps, SdrObject& rObj) + : E3dCompoundProperties(rProps, rObj) + { + } + + E3dExtrudeProperties::~E3dExtrudeProperties() + { + } + + std::unique_ptr<BaseProperties> E3dExtrudeProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dExtrudeProperties(*this, rObj)); + } + + void E3dExtrudeProperties::PostItemChange(const sal_uInt16 nWhich) + { + // call parent + E3dCompoundProperties::PostItemChange(nWhich); + + // handle value change + E3dExtrudeObj& rObj = static_cast<E3dExtrudeObj&>(GetSdrObject()); + + switch(nWhich) + { + case SDRATTR_3DOBJ_PERCENT_DIAGONAL: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_BACKSCALE: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_DEPTH: + { + rObj.ActionChanged(); + break; + } + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dlatheproperties.cxx b/svx/source/sdr/properties/e3dlatheproperties.cxx new file mode 100644 index 000000000..feb546534 --- /dev/null +++ b/svx/source/sdr/properties/e3dlatheproperties.cxx @@ -0,0 +1,84 @@ +/* -*- 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/properties/e3dlatheproperties.hxx> +#include <svx/lathe3d.hxx> + + +namespace sdr::properties +{ + E3dLatheProperties::E3dLatheProperties(SdrObject& rObj) + : E3dCompoundProperties(rObj) + { + } + + E3dLatheProperties::E3dLatheProperties(const E3dLatheProperties& rProps, SdrObject& rObj) + : E3dCompoundProperties(rProps, rObj) + { + } + + E3dLatheProperties::~E3dLatheProperties() + { + } + + std::unique_ptr<BaseProperties> E3dLatheProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dLatheProperties(*this, rObj)); + } + + void E3dLatheProperties::PostItemChange(const sal_uInt16 nWhich) + { + // call parent + E3dCompoundProperties::PostItemChange(nWhich); + + // handle value change + E3dLatheObj& rObj = static_cast<E3dLatheObj&>(GetSdrObject()); + + switch(nWhich) + { + case SDRATTR_3DOBJ_HORZ_SEGS: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_VERT_SEGS: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_PERCENT_DIAGONAL: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_BACKSCALE: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_END_ANGLE: + { + rObj.ActionChanged(); + break; + } + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dproperties.cxx b/svx/source/sdr/properties/e3dproperties.cxx new file mode 100644 index 000000000..946f879de --- /dev/null +++ b/svx/source/sdr/properties/e3dproperties.cxx @@ -0,0 +1,75 @@ +/* -*- 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/properties/e3dproperties.hxx> +#include <svl/itemset.hxx> +#include <svx/svddef.hxx> +#include <svx/obj3d.hxx> + + +namespace sdr::properties +{ + // create a new itemset + SfxItemSet E3dProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool, + + // ranges from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + + // ranges from E3dObject, contains object and scene because of GetMergedItemSet() + SDRATTR_3D_FIRST, SDRATTR_3D_LAST>); + } + + E3dProperties::E3dProperties(SdrObject& rObj) + : AttributeProperties(rObj) + { + } + + E3dProperties::E3dProperties(const E3dProperties& rProps, SdrObject& rObj) + : AttributeProperties(rProps, rObj) + { + } + + E3dProperties::~E3dProperties() + { + } + + std::unique_ptr<BaseProperties> E3dProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dProperties(*this, rObj)); + } + + void E3dProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + E3dObject& rObj = static_cast<E3dObject&>(GetSdrObject()); + + // call parent + AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // local changes + rObj.StructureChanged(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dsceneproperties.cxx b/svx/source/sdr/properties/e3dsceneproperties.cxx new file mode 100644 index 000000000..c156db33e --- /dev/null +++ b/svx/source/sdr/properties/e3dsceneproperties.cxx @@ -0,0 +1,296 @@ +/* -*- 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/properties/e3dsceneproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/whiter.hxx> +#include <svx/svddef.hxx> +#include <svx/scene3d.hxx> +#include <osl/diagnose.h> +#include <memory> + + +namespace sdr::properties +{ + E3dSceneProperties::E3dSceneProperties(SdrObject& rObj) + : E3dProperties(rObj) + { + } + + E3dSceneProperties::E3dSceneProperties(const E3dSceneProperties& rProps, SdrObject& rObj) + : E3dProperties(rProps, rObj) + { + } + + E3dSceneProperties::~E3dSceneProperties() + { + } + + std::unique_ptr<BaseProperties> E3dSceneProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dSceneProperties(*this, rObj)); + } + + const SfxItemSet& E3dSceneProperties::GetMergedItemSet() const + { + // prepare ItemSet + if(mxItemSet) + { + // filter for SDRATTR_3DSCENE_ items, only keep those items + SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aNew(*mxItemSet->GetPool()); + aNew.Put(*mxItemSet); + mxItemSet->ClearItem(); + mxItemSet->Put(aNew); + } + else + { + // No ItemSet yet, force local ItemSet + GetObjectItemSet(); + } + + // collect all ItemSets of contained 3d objects + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + SdrObject* pObj = pSub->GetObj(a); + + if(dynamic_cast<const E3dCompoundObject* >(pObj)) + { + const SfxItemSet& rSet = pObj->GetMergedItemSet(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + + while(nWhich) + { + // Leave out the SDRATTR_3DSCENE_ range, this would only be double + // and always equal. + if(nWhich <= SDRATTR_3DSCENE_FIRST || nWhich >= SDRATTR_3DSCENE_LAST) + { + if(SfxItemState::DONTCARE == aIter.GetItemState(false)) + { + mxItemSet->InvalidateItem(nWhich); + } + else + { + mxItemSet->MergeValue(rSet.Get(nWhich), true); + } + } + + nWhich = aIter.NextWhich(); + } + } + } + + // call parent + return E3dProperties::GetMergedItemSet(); + } + + void E3dSceneProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems) + { + // Set SDRATTR_3DOBJ_ range at contained objects. + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + if(nCount) + { + // Generate filtered ItemSet which contains all but the SDRATTR_3DSCENE items. + // #i50808# Leak fix, Clone produces a new instance and we get ownership here + SfxItemSet aNewSet(rSet.CloneAsValue()); + + for(sal_uInt16 b(SDRATTR_3DSCENE_FIRST); b <= SDRATTR_3DSCENE_LAST; b++) + { + aNewSet.ClearItem(b); + } + + if(aNewSet.Count()) + { + for(size_t a = 0; a < nCount; ++a) + { + SdrObject* pObj = pSub->GetObj(a); + + if(dynamic_cast<const E3dCompoundObject* >(pObj)) + { + // set merged ItemSet at contained 3d object. + pObj->SetMergedItemSet(aNewSet, bClearAllItems); + } + } + } + } + + // call parent. This will set items on local object, too. + E3dProperties::SetMergedItemSet(rSet, bClearAllItems); + } + + void E3dSceneProperties::SetMergedItem(const SfxPoolItem& rItem) + { + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + pSub->GetObj(a)->SetMergedItem(rItem); + } + + // #i43809# call parent. This will set items on local object, too. + E3dProperties::SetMergedItem(rItem); + } + + void E3dSceneProperties::ClearMergedItem(const sal_uInt16 nWhich) + { + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + pSub->GetObj(a)->ClearMergedItem(nWhich); + } + + // #i43809# call parent. This will clear items on local object, too. + E3dProperties::ClearMergedItem(nWhich); + } + + void E3dSceneProperties::PostItemChange(const sal_uInt16 nWhich) + { + // call parent + E3dProperties::PostItemChange(nWhich); + + // local changes + E3dScene& rObj = static_cast<E3dScene&>(GetSdrObject()); + rObj.StructureChanged(); + + switch(nWhich) + { + case SDRATTR_3DSCENE_PERSPECTIVE : + case SDRATTR_3DSCENE_DISTANCE : + case SDRATTR_3DSCENE_FOCAL_LENGTH : + { + // #83387#, #83391# + // one common function for the camera attributes + // since SetCamera() sets all three back to the ItemSet + Camera3D aSceneCam(rObj.GetCamera()); + bool bChange(false); + + // for SDRATTR_3DSCENE_PERSPECTIVE: + if(aSceneCam.GetProjection() != rObj.GetPerspective()) + { + aSceneCam.SetProjection(rObj.GetPerspective()); + bChange = true; + } + + // for SDRATTR_3DSCENE_DISTANCE: + basegfx::B3DPoint aActualPosition(aSceneCam.GetPosition()); + double fNew = rObj.GetDistance(); + + if(fNew != aActualPosition.getZ()) + { + aSceneCam.SetPosition(basegfx::B3DPoint(aActualPosition.getX(), aActualPosition.getY(), fNew)); + bChange = true; + } + + // for SDRATTR_3DSCENE_FOCAL_LENGTH: + fNew = rObj.GetFocalLength() / 100.0; + + if(aSceneCam.GetFocalLength() != fNew) + { + aSceneCam.SetFocalLength(fNew); + bChange = true; + } + + // for all + if(bChange) + { + rObj.SetCamera(aSceneCam); + } + + break; + } + } + } + + void E3dSceneProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + if(bBroadcast) + pSub->GetObj(a)->SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + else + pSub->GetObj(a)->NbcSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + } + } + + SfxStyleSheet* E3dSceneProperties::GetStyleSheet() const + { + SfxStyleSheet* pRetval = nullptr; + + const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + SfxStyleSheet* pCandidate = pSub->GetObj(a)->GetStyleSheet(); + + if(pRetval) + { + if(pCandidate != pRetval) + { + // different StyleSheelts, return none + return nullptr; + } + } + else + { + pRetval = pCandidate; + } + } + + return pRetval; + } + + void E3dSceneProperties::SetSceneItemsFromCamera() + { + // force ItemSet + GetObjectItemSet(); + + E3dScene& rObj = static_cast<E3dScene&>(GetSdrObject()); + const Camera3D& aSceneCam(rObj.GetCamera()); + + // ProjectionType + mxItemSet->Put(Svx3DPerspectiveItem(aSceneCam.GetProjection())); + + // CamPos + mxItemSet->Put(makeSvx3DDistanceItem(static_cast<sal_uInt32>(aSceneCam.GetPosition().getZ() + 0.5))); + + // FocalLength + mxItemSet->Put(makeSvx3DFocalLengthItem(static_cast<sal_uInt32>((aSceneCam.GetFocalLength() * 100.0) + 0.5))); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/e3dsphereproperties.cxx b/svx/source/sdr/properties/e3dsphereproperties.cxx new file mode 100644 index 000000000..66f01748f --- /dev/null +++ b/svx/source/sdr/properties/e3dsphereproperties.cxx @@ -0,0 +1,69 @@ +/* -*- 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/properties/e3dsphereproperties.hxx> +#include <svx/sphere3d.hxx> + + +namespace sdr::properties +{ + E3dSphereProperties::E3dSphereProperties(SdrObject& rObj) + : E3dCompoundProperties(rObj) + { + } + + E3dSphereProperties::E3dSphereProperties(const E3dSphereProperties& rProps, SdrObject& rObj) + : E3dCompoundProperties(rProps, rObj) + { + } + + E3dSphereProperties::~E3dSphereProperties() + { + } + + std::unique_ptr<BaseProperties> E3dSphereProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new E3dSphereProperties(*this, rObj)); + } + + void E3dSphereProperties::PostItemChange(const sal_uInt16 nWhich) + { + // call parent + E3dCompoundProperties::PostItemChange(nWhich); + + // handle value change + E3dSphereObj& rObj = static_cast<E3dSphereObj&>(GetSdrObject()); + + switch(nWhich) + { + case SDRATTR_3DOBJ_HORZ_SEGS: + { + rObj.ActionChanged(); + break; + } + case SDRATTR_3DOBJ_VERT_SEGS: + { + rObj.ActionChanged(); + break; + } + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/emptyproperties.cxx b/svx/source/sdr/properties/emptyproperties.cxx new file mode 100644 index 000000000..1cad150b2 --- /dev/null +++ b/svx/source/sdr/properties/emptyproperties.cxx @@ -0,0 +1,89 @@ +/* -*- 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/properties/emptyproperties.hxx> +#include <svl/itemset.hxx> +#include <svx/svdobj.hxx> + + +namespace sdr::properties +{ + EmptyProperties::EmptyProperties(SdrObject& rObj) + : BaseProperties(rObj) + { + } + + std::unique_ptr<BaseProperties> EmptyProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new EmptyProperties(rObj)); + } + + const SfxItemSet& EmptyProperties::GetObjectItemSet() const + { + assert(!"EmptyProperties::GetObjectItemSet() should never be called"); + abort(); + } + + SfxItemSet EmptyProperties::CreateObjectSpecificItemSet(SfxItemPool&) + { + assert(!"EmptyProperties::CreateObjectSpecificItemSet() should never be called"); + abort(); + } + + void EmptyProperties::SetObjectItem(const SfxPoolItem& /*rItem*/) + { + assert(!"EmptyProperties::SetObjectItem() should never be called"); + } + + void EmptyProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/) + { + assert(!"EmptyProperties::SetObjectItemDirect() should never be called"); + } + + void EmptyProperties::ClearObjectItem(const sal_uInt16 /*nWhich*/) + { + assert(!"EmptyProperties::ClearObjectItem() should never be called"); + } + + void EmptyProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/) + { + assert(!"EmptyProperties::ClearObjectItemDirect() should never be called"); + } + + void EmptyProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/) + { + assert(!"EmptyProperties::SetObjectItemSet() should never be called"); + } + + void EmptyProperties::SetStyleSheet(SfxStyleSheet* /*pNewStyleSheet*/, bool /*bDontRemoveHardAttr*/, + bool /*bBroadcast*/) + { + assert(!"EmptyProperties::SetStyleSheet() should never be called"); + } + + SfxStyleSheet* EmptyProperties::GetStyleSheet() const + { + assert(!"EmptyProperties::GetStyleSheet() should never be called"); + return nullptr; + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/graphicproperties.cxx b/svx/source/sdr/properties/graphicproperties.cxx new file mode 100644 index 000000000..be9b87800 --- /dev/null +++ b/svx/source/sdr/properties/graphicproperties.cxx @@ -0,0 +1,150 @@ +/* -*- 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/properties/graphicproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdograf.hxx> +#include <svx/sdgcpitm.hxx> +#include <svx/svdmodel.hxx> +#include <svx/sdgluitm.hxx> +#include <sdgcoitm.hxx> +#include <svx/sdggaitm.hxx> +#include <sdgtritm.hxx> +#include <sdginitm.hxx> +#include <svx/sdgmoitm.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> + +namespace sdr::properties +{ + void GraphicProperties::applyDefaultStyleSheetFromSdrModel() + { + SfxStyleSheet* pStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj()); + + if(pStyleSheet) + { + // do not delete hard attributes when setting dsefault Style + SetStyleSheet(pStyleSheet, true, true); + } + else + { + SetMergedItem(XFillStyleItem(com::sun::star::drawing::FillStyle_NONE)); + SetMergedItem(XLineStyleItem(com::sun::star::drawing::LineStyle_NONE)); + } + } + + // create a new itemset + SfxItemSet GraphicProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool, + + // range from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + + // range from SdrGrafObj + SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, + + SDRATTR_GLOW_FIRST, SDRATTR_SOFTEDGE_LAST, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + + // range from SdrTextObj + EE_ITEMS_START, EE_ITEMS_END>); + } + + GraphicProperties::GraphicProperties(SdrObject& rObj) + : RectangleProperties(rObj) + { + } + + GraphicProperties::GraphicProperties(const GraphicProperties& rProps, SdrObject& rObj) + : RectangleProperties(rProps, rObj) + { + } + + GraphicProperties::~GraphicProperties() + { + } + + std::unique_ptr<BaseProperties> GraphicProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new GraphicProperties(*this, rObj)); + } + + void GraphicProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrGrafObj& rObj = static_cast<SdrGrafObj&>(GetSdrObject()); + + // local changes + rObj.SetXPolyDirty(); + + // #i29367# Update GraphicAttr, too. This was formerly + // triggered by SdrGrafObj::Notify, which is no longer + // called nowadays. BTW: strictly speaking, the whole + // ImpSetAttrToGrafInfostuff could + // be dumped, when SdrGrafObj::aGrafInfo is removed and + // always created on the fly for repaint. + rObj.ImpSetAttrToGrafInfo(); + + // call parent + RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + } + + void GraphicProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + SdrGrafObj& rObj = static_cast<SdrGrafObj&>(GetSdrObject()); + rObj.SetXPolyDirty(); + + // local changes + rObj.ImpSetAttrToGrafInfo(); + } + + void GraphicProperties::ForceDefaultAttributes() + { + // call parent + RectangleProperties::ForceDefaultAttributes(); + + // force ItemSet + GetObjectItemSet(); + + mxItemSet->Put( SdrGrafLuminanceItem( 0 ) ); + mxItemSet->Put( SdrGrafContrastItem( 0 ) ); + mxItemSet->Put( SdrGrafRedItem( 0 ) ); + mxItemSet->Put( SdrGrafGreenItem( 0 ) ); + mxItemSet->Put( SdrGrafBlueItem( 0 ) ); + mxItemSet->Put( SdrGrafGamma100Item( 100 ) ); + mxItemSet->Put( SdrGrafTransparenceItem( 0 ) ); + mxItemSet->Put( SdrGrafInvertItem( false ) ); + mxItemSet->Put( SdrGrafModeItem( GraphicDrawMode::Standard ) ); + mxItemSet->Put( SdrGrafCropItem( 0, 0, 0, 0 ) ); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/groupproperties.cxx b/svx/source/sdr/properties/groupproperties.cxx new file mode 100644 index 000000000..6ec522aa3 --- /dev/null +++ b/svx/source/sdr/properties/groupproperties.cxx @@ -0,0 +1,240 @@ +/* -*- 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/properties/groupproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/whiter.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svdpage.hxx> +#include <osl/diagnose.h> + + +namespace sdr::properties +{ + GroupProperties::GroupProperties(SdrObject& rObj) + : BaseProperties(rObj) + { + } + + GroupProperties::~GroupProperties() + { + } + + std::unique_ptr<BaseProperties> GroupProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new GroupProperties(rObj)); + } + + SfxItemSet GroupProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool); + } + + const SfxItemSet& GroupProperties::GetObjectItemSet() const + { + assert(!"GroupProperties::GetObjectItemSet() should never be called"); + abort(); + } + + const SfxItemSet& GroupProperties::GetMergedItemSet() const + { + // prepare ItemSet + if(mxItemSet) + // clear local itemset for merge + mxItemSet->ClearItem(); + else if(!mxItemSet) + // force local itemset + mxItemSet.emplace(GetSdrObject().GetObjectItemPool()); + + // collect all ItemSets in mpItemSet + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + const SfxItemSet& rSet = pSub->GetObj(a)->GetMergedItemSet(); + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + + while(nWhich) + { + if(SfxItemState::DONTCARE == aIter.GetItemState(false)) + { + mxItemSet->InvalidateItem(nWhich); + } + else + { + mxItemSet->MergeValue(rSet.Get(nWhich), true); + } + + nWhich = aIter.NextWhich(); + } + } + + // For group properties, do not call parent since groups do + // not have local ItemSets. + return *mxItemSet; + } + + void GroupProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems) + { + // iterate over contained SdrObjects + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + SdrObject* pObj = pSub->GetObj(a); + + if(pObj) + { + // Set merged ItemSet at contained object + pObj->SetMergedItemSet(rSet, bClearAllItems); + } + } + + // Do not call parent here. Group objects do not have local ItemSets + // where items need to be set. + } + + void GroupProperties::SetObjectItem(const SfxPoolItem& /*rItem*/) + { + assert(!"GroupProperties::SetObjectItem() should never be called"); + } + + void GroupProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/) + { + assert(!"GroupProperties::SetObjectItemDirect() should never be called"); + } + + void GroupProperties::ClearObjectItem(const sal_uInt16 nWhich) + { + // iterate over contained SdrObjects + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + SdrObject* pObj = pSub->GetObj(a); + + if(pObj) + { + pObj->GetProperties().ClearObjectItem(nWhich); + } + } + } + + void GroupProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/) + { + assert(!"GroupProperties::ClearObjectItemDirect() should never be called"); + } + + void GroupProperties::SetMergedItem(const SfxPoolItem& rItem) + { + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + pSub->GetObj(a)->GetProperties().SetMergedItem(rItem); + } + } + + void GroupProperties::ClearMergedItem(const sal_uInt16 nWhich) + { + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + pSub->GetObj(a)->GetProperties().ClearMergedItem(nWhich); + } + } + + void GroupProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/) + { + assert(!"GroupProperties::SetObjectItemSet() should never be called"); + } + + SfxStyleSheet* GroupProperties::GetStyleSheet() const + { + SfxStyleSheet* pRetval = nullptr; + + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + SfxStyleSheet* pCandidate = pSub->GetObj(a)->GetStyleSheet(); + + if(pRetval) + { + if(pCandidate != pRetval) + { + // different StyleSheelts, return none + return nullptr; + } + } + else + { + pRetval = pCandidate; + } + } + + return pRetval; + } + + void GroupProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + if(bBroadcast) + pSub->GetObj(a)->SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + else + pSub->GetObj(a)->NbcSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr); + } + } + + void GroupProperties::ForceStyleToHardAttributes() + { + const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList()); + OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)"); + const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount()); + + for(size_t a = 0; a < nCount; ++a) + { + pSub->GetObj(a)->GetProperties().ForceStyleToHardAttributes(); + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/itemsettools.cxx b/svx/source/sdr/properties/itemsettools.cxx new file mode 100644 index 000000000..11431d367 --- /dev/null +++ b/svx/source/sdr/properties/itemsettools.cxx @@ -0,0 +1,87 @@ +/* -*- 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/properties/itemsettools.hxx> +#include <tools/fract.hxx> +#include <svl/itemset.hxx> +#include <svl/whiter.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svditer.hxx> +#include <memory> + +// class to remember broadcast start positions + +namespace sdr::properties +{ + ItemChangeBroadcaster::ItemChangeBroadcaster(const SdrObject& rObj) + { + if (rObj.GetObjIdentifier() == SdrObjKind::Group) + { + const SdrObjGroup* pGroupObj = static_cast<const SdrObjGroup*>(&rObj); + SdrObjListIter aIter(pGroupObj->GetSubList(), SdrIterMode::DeepNoGroups); + maRectangles.reserve(aIter.Count()); + + while(aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + + if(pObj) + { + maRectangles.push_back(pObj->GetLastBoundRect()); + } + } + } + else + { + maRectangles.push_back(rObj.GetLastBoundRect()); + } + } + + + void ScaleItemSet(SfxItemSet& rSet, const Fraction& rScale) + { + sal_Int32 nMul(rScale.GetNumerator()); + sal_Int32 nDiv(rScale.GetDenominator()); + + if(!rScale.IsValid() || !nDiv) + { + return; + } + + SfxWhichIter aIter(rSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + const SfxPoolItem *pItem = nullptr; + + while(nWhich) + { + if(SfxItemState::SET == aIter.GetItemState(false, &pItem)) + { + if(pItem->HasMetrics()) + { + std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone()); + pNewItem->ScaleMetrics(nMul, nDiv); + rSet.Put(std::move(pNewItem)); + } + } + nWhich = aIter.NextWhich(); + } + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/measureproperties.cxx b/svx/source/sdr/properties/measureproperties.cxx new file mode 100644 index 000000000..07441c385 --- /dev/null +++ b/svx/source/sdr/properties/measureproperties.cxx @@ -0,0 +1,130 @@ +/* -*- 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/properties/measureproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svx/svddef.hxx> +#include <editeng/eeitem.hxx> +#include <svx/svdomeas.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnstit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnedit.hxx> +#include <svx/xlnedwit.hxx> +#include <svx/sdynitm.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + + +namespace sdr::properties +{ + // create a new itemset + SfxItemSet MeasureProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet( + rPool, + svl::Items< + // Ranges from SdrAttrObj, SdrMeasureObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_MEASURE_FIRST, SDRATTR_MEASURE_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END>); + } + + MeasureProperties::MeasureProperties(SdrObject& rObj) + : TextProperties(rObj) + { + } + + MeasureProperties::MeasureProperties(const MeasureProperties& rProps, SdrObject& rObj) + : TextProperties(rProps, rObj) + { + } + + MeasureProperties::~MeasureProperties() + { + } + + std::unique_ptr<BaseProperties> MeasureProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new MeasureProperties(*this, rObj)); + } + + void MeasureProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrMeasureObj& rObj = static_cast<SdrMeasureObj&>(GetSdrObject()); + + // call parent + TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // local changes + rObj.SetTextDirty(); + } + + void MeasureProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + // get access to dimension line object + SdrMeasureObj& rObj = static_cast<SdrMeasureObj&>(GetSdrObject()); + + // mark dimension line text as changed (dirty) in the dimension line object + rObj.SetTextDirty(); + + // tdf#98525 ask the dimension line object to redraw the changed text + rObj.UndirtyText(); + } + + void MeasureProperties::ForceDefaultAttributes() + { + // call parent + TextProperties::ForceDefaultAttributes(); + + // force ItemSet + GetObjectItemSet(); + + //#71958# by default, the show units Bool-Item is set as hard + // attribute to sal_True to avoid confusion when copying SdrMeasureObj's + // from one application to another + mxItemSet->Put(SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, true)); + + basegfx::B2DPolygon aNewPolygon; + aNewPolygon.append(basegfx::B2DPoint(100.0, 0.0)); + aNewPolygon.append(basegfx::B2DPoint(200.0, 400.0)); + aNewPolygon.append(basegfx::B2DPoint(0.0, 400.0)); + aNewPolygon.setClosed(true); + + mxItemSet->Put(XLineStartItem(OUString(), basegfx::B2DPolyPolygon(aNewPolygon))); + mxItemSet->Put(XLineStartWidthItem(200)); + mxItemSet->Put(XLineEndItem(OUString(), basegfx::B2DPolyPolygon(aNewPolygon))); + mxItemSet->Put(XLineEndWidthItem(200)); + mxItemSet->Put(XLineStyleItem(css::drawing::LineStyle_SOLID)); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/oleproperties.cxx b/svx/source/sdr/properties/oleproperties.cxx new file mode 100644 index 000000000..587ff1d3f --- /dev/null +++ b/svx/source/sdr/properties/oleproperties.cxx @@ -0,0 +1,73 @@ +/* -*- 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/properties/oleproperties.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xlineit0.hxx> +#include <svx/svdobj.hxx> +#include <svx/svdmodel.hxx> + +namespace sdr::properties +{ + void OleProperties::applyDefaultStyleSheetFromSdrModel() + { + SfxStyleSheet* pStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj()); + + if(pStyleSheet) + { + // do not delete hard attributes when setting dsefault Style + SetStyleSheet(pStyleSheet, true, true); + } + else + { + SetMergedItem(XFillStyleItem(com::sun::star::drawing::FillStyle_NONE)); + SetMergedItem(XLineStyleItem(com::sun::star::drawing::LineStyle_NONE)); + } + } + + OleProperties::OleProperties(SdrObject& rObj) + : RectangleProperties(rObj) + { + } + + OleProperties::OleProperties(const OleProperties& rProps, SdrObject& rObj) + : RectangleProperties(rProps, rObj) + { + } + + OleProperties::~OleProperties() + { + } + + std::unique_ptr<BaseProperties> OleProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new OleProperties(*this, rObj)); + } + + void OleProperties::ForceDefaultAttributes() + { + // call parent + RectangleProperties::ForceDefaultAttributes(); + + // force ItemSet + GetObjectItemSet(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/pageproperties.cxx b/svx/source/sdr/properties/pageproperties.cxx new file mode 100644 index 000000000..011ab2192 --- /dev/null +++ b/svx/source/sdr/properties/pageproperties.cxx @@ -0,0 +1,106 @@ +/* -*- 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/properties/pageproperties.hxx> +#include <svl/itemset.hxx> +#include <svx/svdobj.hxx> +#include <tools/debug.hxx> + + +namespace sdr::properties +{ + PageProperties::PageProperties(SdrObject& rObj) + : BaseProperties(rObj) + { + } + + PageProperties::PageProperties(const PageProperties& /*rProps*/, SdrObject& rObj) + : BaseProperties(rObj) + { + } + + PageProperties::~PageProperties() + { + } + + std::unique_ptr<BaseProperties> PageProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new PageProperties(*this, rObj)); + } + + SfxItemSet PageProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool); + } + + // get itemset. Override here to allow creating the empty itemset + // without asserting + const SfxItemSet& PageProperties::GetObjectItemSet() const + { + if(!mxEmptyItemSet) + { + mxEmptyItemSet.emplace(GetSdrObject().GetObjectItemPool()); + } + + DBG_ASSERT(mxEmptyItemSet, "Could not create an SfxItemSet(!)"); + + return *mxEmptyItemSet; + } + + SfxStyleSheet* PageProperties::GetStyleSheet() const + { + // override to legally return a 0L pointer here + return nullptr; + } + + void PageProperties::SetStyleSheet(SfxStyleSheet* /*pStyleSheet*/, bool /*bDontRemoveHardAttr*/, + bool /*bBroadcast*/) + { + // override to legally ignore the StyleSheet here + } + + void PageProperties::ClearObjectItem(const sal_uInt16 /*nWhich*/) + { + // simply ignore item clearing on page objects + } + + void PageProperties::SetObjectItem(const SfxPoolItem& /*rItem*/) + { + assert(!"PageProperties::SetObjectItem() should never be called"); + } + + void PageProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/) + { + assert(!"PageProperties::SetObjectItemDirect() should never be called"); + } + + void PageProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/) + { + assert(!"PageProperties::ClearObjectItemDirect() should never be called"); + } + + void PageProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/) + { + assert(!"PageProperties::SetObjectItemSet() should never be called"); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/properties.cxx b/svx/source/sdr/properties/properties.cxx new file mode 100644 index 000000000..97a264712 --- /dev/null +++ b/svx/source/sdr/properties/properties.cxx @@ -0,0 +1,178 @@ +/* -*- 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 <libxml/xmlwriter.h> + +#include <svx/sdr/properties/properties.hxx> +#include <sdr/properties/itemsettools.hxx> +#include <svl/itemset.hxx> +#include <svx/svdogrp.hxx> +#include <svx/svditer.hxx> +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> + +using namespace com::sun::star; + +namespace sdr::properties +{ + BaseProperties::BaseProperties(SdrObject& rObj) + : mrObject(rObj) + { + } + + BaseProperties::~BaseProperties() + { + } + + const SdrObject& BaseProperties::GetSdrObject() const + { + return mrObject; + } + + SdrObject& BaseProperties::GetSdrObject() + { + return mrObject; + } + + const SfxItemSet& BaseProperties::GetMergedItemSet() const + { + // default implementation falls back to GetObjectItemSet() + return GetObjectItemSet(); + } + + void BaseProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems) + { + // clear items if requested + if(bClearAllItems) + { + ClearObjectItem(); + } + + // default implementation falls back to SetObjectItemSet() + SetObjectItemSet(rSet); + } + + void BaseProperties::SetMergedItem(const SfxPoolItem& rItem) + { + // default implementation falls back to SetObjectItem() + SetObjectItem(rItem); + } + + void BaseProperties::ClearMergedItem(const sal_uInt16 nWhich) + { + // default implementation falls back to ClearObjectItem() + ClearObjectItem(nWhich); + } + + void BaseProperties::ForceStyleToHardAttributes() + { + // force all attributes which come from styles to hard attributes + // to be able to live without the style. Default implementation does nothing. + // Override where an ItemSet is implemented. + } + + void BaseProperties::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems) + { + ItemChangeBroadcaster aC(GetSdrObject()); + + if(bClearAllItems) + { + ClearObjectItem(); + } + + SetMergedItemSet(rSet); + BroadcastItemChange(aC); + } + + const SfxPoolItem& BaseProperties::GetItem(const sal_uInt16 nWhich) const + { + return GetObjectItemSet().Get(nWhich); + } + + void BaseProperties::BroadcastItemChange(const ItemChangeBroadcaster& rChange) + { + const sal_uInt32 nCount(rChange.GetRectangleCount()); + + // invalidate all new rectangles + SdrObject* pObj = &GetSdrObject(); + if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + SdrObjGroup* pObjGroup = static_cast<SdrObjGroup*>(pObj); + SdrObjListIter aIter(pObjGroup, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore()) + { + SdrObject* pChildObj = aIter.Next(); + pChildObj->BroadcastObjectChange(); + } + } + else + { + pObj->BroadcastObjectChange(); + } + + // also send the user calls + for(sal_uInt32 a(0); a < nCount; a++) + { + pObj->SendUserCall(SdrUserCallType::ChangeAttr, rChange.GetRectangle(a)); + } + } + + sal_uInt32 BaseProperties::getVersion() const + { + return 0; + } + + void BaseProperties::dumpAsXml(xmlTextWriterPtr pWriter) const + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("BaseProperties")); + (void)xmlTextWriterEndElement(pWriter); + } + + void CleanupFillProperties( SfxItemSet& rItemSet ) + { + const bool bFillBitmap = rItemSet.GetItemState(XATTR_FILLBITMAP, false) == SfxItemState::SET; + const bool bFillGradient = rItemSet.GetItemState(XATTR_FILLGRADIENT, false) == SfxItemState::SET; + const bool bFillHatch = rItemSet.GetItemState(XATTR_FILLHATCH, false) == SfxItemState::SET; + if( !(bFillBitmap || bFillGradient || bFillHatch) ) + return; + + const XFillStyleItem* pFillStyleItem = rItemSet.GetItem(XATTR_FILLSTYLE); + if( !pFillStyleItem ) + return; + + if( bFillBitmap && (pFillStyleItem->GetValue() != drawing::FillStyle_BITMAP) ) + { + rItemSet.ClearItem( XATTR_FILLBITMAP ); + } + + if( bFillGradient && (pFillStyleItem->GetValue() != drawing::FillStyle_GRADIENT) ) + { + rItemSet.ClearItem( XATTR_FILLGRADIENT ); + } + + if( bFillHatch && (pFillStyleItem->GetValue() != drawing::FillStyle_HATCH) ) + { + rItemSet.ClearItem( XATTR_FILLHATCH ); + } + } + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/rectangleproperties.cxx b/svx/source/sdr/properties/rectangleproperties.cxx new file mode 100644 index 000000000..4c3a72a2f --- /dev/null +++ b/svx/source/sdr/properties/rectangleproperties.cxx @@ -0,0 +1,69 @@ +/* -*- 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/properties/rectangleproperties.hxx> +#include <svx/svdorect.hxx> + + +namespace sdr::properties +{ + RectangleProperties::RectangleProperties(SdrObject& rObj) + : TextProperties(rObj) + { + } + + RectangleProperties::RectangleProperties(const RectangleProperties& rProps, SdrObject& rObj) + : TextProperties(rProps, rObj) + { + } + + RectangleProperties::~RectangleProperties() + { + } + + std::unique_ptr<BaseProperties> RectangleProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new RectangleProperties(*this, rObj)); + } + + void RectangleProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrRectObj& rObj = static_cast<SdrRectObj&>(GetSdrObject()); + + // call parent + TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + + // local changes + rObj.SetXPolyDirty(); + } + + // set a new StyleSheet and broadcast + void RectangleProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // local changes + SdrRectObj& rObj = static_cast<SdrRectObj&>(GetSdrObject()); + rObj.SetXPolyDirty(); + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/sdr/properties/textproperties.cxx b/svx/source/sdr/properties/textproperties.cxx new file mode 100644 index 000000000..d8cdf80df --- /dev/null +++ b/svx/source/sdr/properties/textproperties.cxx @@ -0,0 +1,631 @@ +/* -*- 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/properties/textproperties.hxx> +#include <svl/itemset.hxx> +#include <svl/style.hxx> +#include <svl/itemiter.hxx> +#include <svl/hint.hxx> +#include <svx/svddef.hxx> +#include <svx/svdotext.hxx> +#include <svx/svdoutl.hxx> +#include <svx/sdmetitm.hxx> +#include <svx/sdtditm.hxx> +#include <editeng/writingmodeitem.hxx> +#include <svx/svdmodel.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outlobj.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <editeng/adjustitem.hxx> +#include <svx/svdetc.hxx> +#include <editeng/editeng.hxx> +#include <editeng/flditem.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnwtit.hxx> + +using namespace com::sun::star; + +namespace sdr::properties +{ + SfxItemSet TextProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool) + { + return SfxItemSet(rPool, + + // range from SdrAttrObj + svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST, + + // range from SdrTextObj + EE_ITEMS_START, EE_ITEMS_END>); + } + + TextProperties::TextProperties(SdrObject& rObj) + : AttributeProperties(rObj), + maVersion(0) + { + } + + TextProperties::TextProperties(const TextProperties& rProps, SdrObject& rObj) + : AttributeProperties(rProps, rObj), + maVersion(rProps.getVersion()) + { + } + + TextProperties::~TextProperties() + { + } + + std::unique_ptr<BaseProperties> TextProperties::Clone(SdrObject& rObj) const + { + return std::unique_ptr<BaseProperties>(new TextProperties(*this, rObj)); + } + + void TextProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) + { + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + + // #i101556# ItemSet has changed -> new version + maVersion++; + + if (auto pOutliner = rObj.GetTextEditOutliner()) + { + pOutliner->SetTextColumns(rObj.GetTextColumnsNumber(), + rObj.GetTextColumnsSpacing()); + } + + const svx::ITextProvider& rTextProvider(getTextProvider()); + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + SdrText* pText = rTextProvider.getText( nText ); + + OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; + + if(pParaObj) + { + const bool bTextEdit = rObj.IsTextEditActive() && (rObj.getActiveText() == pText); + + // handle outliner attributes + GetObjectItemSet(); + Outliner* pOutliner = rObj.GetTextEditOutliner(); + + if(!bTextEdit) + { + pOutliner = &rObj.ImpGetDrawOutliner(); + pOutliner->SetText(*pParaObj); + } + + sal_Int32 nParaCount(pOutliner->GetParagraphCount()); + + for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++) + { + SfxItemSet aSet(pOutliner->GetParaAttribs(nPara)); + for (const SfxPoolItem* pItem : aChangedItems) + aSet.Put(*pItem); + if (nDeletedWhich) + aSet.ClearItem(nDeletedWhich); + pOutliner->SetParaAttribs(nPara, aSet); + } + + if(!bTextEdit) + { + if(nParaCount) + { + // force ItemSet + GetObjectItemSet(); + + SfxItemSet aNewSet(pOutliner->GetParaAttribs(0)); + mxItemSet->Put(aNewSet); + } + + std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount); + pOutliner->Clear(); + + rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp),pText); + } + } + } + + // Extra-Repaint for radical layout changes (#43139#) + for (const SfxPoolItem* pItem : aChangedItems) + if (pItem->Which() == SDRATTR_TEXT_CONTOURFRAME) + { + // Here only repaint wanted + rObj.ActionChanged(); + //rObj.BroadcastObjectChange(); + break; + } + + // call parent + AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich); + } + + void TextProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem) + { + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + + // #i25616# + sal_Int32 nOldLineWidth(0); + + if(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange()) + { + nOldLineWidth = GetItem(XATTR_LINEWIDTH).GetValue(); + } + + if(pNewItem && (SDRATTR_TEXTDIRECTION == nWhich)) + { + bool bVertical(css::text::WritingMode_TB_RL == static_cast<const SvxWritingModeItem*>(pNewItem)->GetValue()); + rObj.SetVerticalWriting(bVertical); + } + + // #95501# reset to default + if(!pNewItem && !nWhich && rObj.HasText() ) + { + SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner(); + + const svx::ITextProvider& rTextProvider(getTextProvider()); + sal_Int32 nCount = rTextProvider.getTextCount(); + while (nCount--) + { + SdrText* pText = rTextProvider.getText( nCount ); + OutlinerParaObject* pParaObj = pText->GetOutlinerParaObject(); + if( pParaObj ) + { + rOutliner.SetText(*pParaObj); + sal_Int32 nParaCount(rOutliner.GetParagraphCount()); + + if(nParaCount) + { + ESelection aSelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL); + rOutliner.RemoveAttribs(aSelection, true, 0); + + std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount); + rOutliner.Clear(); + + rObj.NbcSetOutlinerParaObjectForText( std::move(pTemp), pText ); + } + } + } + } + + // call parent + AttributeProperties::ItemChange( nWhich, pNewItem ); + + // #i25616# + if(!(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange())) + return; + + const sal_Int32 nNewLineWidth(GetItem(XATTR_LINEWIDTH).GetValue()); + const sal_Int32 nDifference((nNewLineWidth - nOldLineWidth) / 2); + + if(!nDifference) + return; + + const bool bLineVisible(drawing::LineStyle_NONE != GetItem(XATTR_LINESTYLE).GetValue()); + + if(bLineVisible) + { + const sal_Int32 nLeftDist(GetItem(SDRATTR_TEXT_LEFTDIST).GetValue()); + const sal_Int32 nRightDist(GetItem(SDRATTR_TEXT_RIGHTDIST).GetValue()); + const sal_Int32 nUpperDist(GetItem(SDRATTR_TEXT_UPPERDIST).GetValue()); + const sal_Int32 nLowerDist(GetItem(SDRATTR_TEXT_LOWERDIST).GetValue()); + + SetObjectItemDirect(makeSdrTextLeftDistItem(nLeftDist + nDifference)); + SetObjectItemDirect(makeSdrTextRightDistItem(nRightDist + nDifference)); + SetObjectItemDirect(makeSdrTextUpperDistItem(nUpperDist + nDifference)); + SetObjectItemDirect(makeSdrTextLowerDistItem(nLowerDist + nDifference)); + } + } + + const svx::ITextProvider& TextProperties::getTextProvider() const + { + return static_cast<const SdrTextObj&>(GetSdrObject()); + } + + void TextProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, + bool bBroadcast) + { + // call parent (always first thing to do, may create the SfxItemSet) + AttributeProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast); + + // #i101556# StyleSheet has changed -> new version + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + maVersion++; + + if(!rObj.IsLinkedText() ) + { + SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner(); + + const svx::ITextProvider& rTextProvider(getTextProvider()); + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + SdrText* pText = rTextProvider.getText( nText ); + + OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; + if( !pParaObj ) + continue; + + // apply StyleSheet to all paragraphs + rOutliner.SetText(*pParaObj); + sal_Int32 nParaCount(rOutliner.GetParagraphCount()); + + if(nParaCount) + { + for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++) + { + std::optional<SfxItemSet> pTempSet; + + // since setting the stylesheet removes all para attributes + if(bDontRemoveHardAttr) + { + // we need to remember them if we want to keep them + pTempSet.emplace(rOutliner.GetParaAttribs(nPara)); + } + + if(GetStyleSheet()) + { + if((SdrObjKind::OutlineText == rObj.GetTextKind()) && (SdrInventor::Default == rObj.GetObjInventor())) + { + OUString aNewStyleSheetName(GetStyleSheet()->GetName()); + aNewStyleSheetName = aNewStyleSheetName.copy(0, aNewStyleSheetName.getLength() - 1); + sal_Int16 nDepth = rOutliner.GetDepth(nPara); + aNewStyleSheetName += OUString::number( nDepth <= 0 ? 1 : nDepth + 1); + SfxStyleSheetBasePool* pStylePool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool()); + SfxStyleSheet* pNewStyle = nullptr; + if(pStylePool) + pNewStyle = static_cast<SfxStyleSheet*>(pStylePool->Find(aNewStyleSheetName, GetStyleSheet()->GetFamily())); + DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" ); + + if(pNewStyle) + { + rOutliner.SetStyleSheet(nPara, pNewStyle); + } + } + else + { + rOutliner.SetStyleSheet(nPara, GetStyleSheet()); + } + } + else + { + // remove StyleSheet + rOutliner.SetStyleSheet(nPara, nullptr); + } + + if(bDontRemoveHardAttr) + { + if(pTempSet) + { + // restore para attributes + rOutliner.SetParaAttribs(nPara, *pTempSet); + } + } + else + { + if(pNewStyleSheet) + { + // remove all hard paragraph attributes + // which occur in StyleSheet, take care of + // parents (!) + SfxItemIter aIter(pNewStyleSheet->GetItemSet()); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; + pItem = aIter.NextItem()) + { + if(!IsInvalidItem(pItem)) + { + sal_uInt16 nW(pItem->Which()); + + if(nW >= EE_ITEMS_START && nW <= EE_ITEMS_END) + { + rOutliner.RemoveCharAttribs(nPara, nW); + } + } + } + } + } + } + + std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount); + rOutliner.Clear(); + rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText); + } + } + } + + if(rObj.IsTextFrame()) + { + rObj.NbcAdjustTextFrameWidthAndHeight(); + } + } + + void TextProperties::ForceDefaultAttributes() + { + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + + if( rObj.GetObjInventor() == SdrInventor::Default ) + { + const SdrObjKind nSdrObjKind = rObj.GetObjIdentifier(); + + if( nSdrObjKind == SdrObjKind::TitleText || nSdrObjKind == SdrObjKind::OutlineText ) + return; // no defaults for presentation objects + } + + bool bTextFrame(rObj.IsTextFrame()); + + // force ItemSet + GetObjectItemSet(); + + if(bTextFrame) + { + mxItemSet->Put(XLineStyleItem(drawing::LineStyle_NONE)); + mxItemSet->Put(XFillColorItem(OUString(), COL_WHITE)); + mxItemSet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + else + { + mxItemSet->Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST)); + mxItemSet->Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); + mxItemSet->Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + } + } + + void TextProperties::ForceStyleToHardAttributes() + { + // #i61284# call parent first to get the hard ObjectItemSet + AttributeProperties::ForceStyleToHardAttributes(); + + // #i61284# push hard ObjectItemSet to OutlinerParaObject attributes + // using existing functionality + GetObjectItemSet(); // force ItemSet + std::vector<const SfxPoolItem*> aChangedItems; + SfxItemIter aIter(*mxItemSet); + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if(!IsInvalidItem(pItem)) + aChangedItems.push_back(pItem); + } + ItemSetChanged(aChangedItems, 0); + + // now the standard TextProperties stuff + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + + if(rObj.IsTextEditActive() || rObj.IsLinkedText()) + return; + + std::unique_ptr<Outliner> pOutliner = SdrMakeOutliner(OutlinerMode::OutlineObject, rObj.getSdrModelFromSdrObject()); + const svx::ITextProvider& rTextProvider(getTextProvider()); + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + SdrText* pText = rTextProvider.getText( nText ); + + OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; + if( !pParaObj ) + continue; + + pOutliner->SetText(*pParaObj); + + sal_Int32 nParaCount(pOutliner->GetParagraphCount()); + + if(nParaCount) + { + bool bBurnIn(false); + + for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++) + { + SfxStyleSheet* pSheet = pOutliner->GetStyleSheet(nPara); + + if(pSheet) + { + SfxItemSet aParaSet(pOutliner->GetParaAttribs(nPara)); + SfxItemSet aSet(*aParaSet.GetPool()); + aSet.Put(pSheet->GetItemSet()); + + /** the next code handles a special case for paragraphs that contain a + url field. The color for URL fields is either the system color for + urls or the char color attribute that formats the portion in which the + url field is contained. + When we set a char color attribute to the paragraphs item set from the + styles item set, we would have this char color attribute as an attribute + that is spanned over the complete paragraph after xml import due to some + problems in the xml import (using a XCursor on import so it does not know + the paragraphs and can't set char attributes to paragraphs ). + + To avoid this, as soon as we try to set a char color attribute from the style + we + 1. check if we have at least one url field in this paragraph + 2. if we found at least one url field, we span the char color attribute over + all portions that are not url fields and remove the char color attribute + from the paragraphs item set + */ + + bool bHasURL(false); + + if(aSet.GetItemState(EE_CHAR_COLOR) == SfxItemState::SET) + { + EditEngine* pEditEngine = const_cast<EditEngine*>(&(pOutliner->GetEditEngine())); + std::vector<EECharAttrib> aAttribs; + pEditEngine->GetCharAttribs(nPara, aAttribs); + + for(const auto& rAttrib : aAttribs) + { + if(rAttrib.pAttr && EE_FEATURE_FIELD == rAttrib.pAttr->Which()) + { + const SvxFieldItem* pFieldItem = static_cast<const SvxFieldItem*>(rAttrib.pAttr); + + if(pFieldItem) + { + const SvxFieldData* pData = pFieldItem->GetField(); + + if(dynamic_cast<const SvxURLField*>( pData)) + { + bHasURL = true; + break; + } + } + } + } + + if(bHasURL) + { + SfxItemSetFixed<EE_CHAR_COLOR, EE_CHAR_COLOR> aColorSet(*aSet.GetPool()); + aColorSet.Put(aSet, false); + + ESelection aSel(nPara, 0); + + for(const auto& rAttrib : aAttribs) + { + if(EE_FEATURE_FIELD == rAttrib.pAttr->Which()) + { + aSel.nEndPos = rAttrib.nStart; + + if(aSel.nStartPos != aSel.nEndPos) + pEditEngine->QuickSetAttribs(aColorSet, aSel); + + aSel.nStartPos = rAttrib.nEnd; + } + } + + aSel.nEndPos = pEditEngine->GetTextLen(nPara); + + if(aSel.nStartPos != aSel.nEndPos) + { + pEditEngine->QuickSetAttribs( aColorSet, aSel ); + } + } + + } + + aSet.Put(aParaSet, false); + + if(bHasURL) + { + aSet.ClearItem(EE_CHAR_COLOR); + } + + pOutliner->SetParaAttribs(nPara, aSet); + bBurnIn = true; // #i51163# Flag was set wrong + } + } + + if(bBurnIn) + { + std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount); + rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp),pText); + } + } + + pOutliner->Clear(); + } + } + + void TextProperties::SetObjectItemNoBroadcast(const SfxPoolItem& rItem) + { + GetObjectItemSet(); + mxItemSet->Put(rItem); + } + + + void TextProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) + { + // call parent + AttributeProperties::Notify(rBC, rHint); + + SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject()); + if(!rObj.HasText()) + return; + + const svx::ITextProvider& rTextProvider(getTextProvider()); + if(dynamic_cast<const SfxStyleSheet *>(&rBC) != nullptr) + { + SfxHintId nId(rHint.GetId()); + + if(SfxHintId::DataChanged == nId) + { + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject(); + if( pParaObj ) + pParaObj->ClearPortionInfo(); + } + rObj.SetTextSizeDirty(); + + if(rObj.IsTextFrame() && rObj.NbcAdjustTextFrameWidthAndHeight()) + { + // here only repaint wanted + rObj.ActionChanged(); + //rObj.BroadcastObjectChange(); + } + + // #i101556# content of StyleSheet has changed -> new version + maVersion++; + } + + if(SfxHintId::Dying == nId) + { + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject(); + if( pParaObj ) + pParaObj->ClearPortionInfo(); + } + } + } + else if(dynamic_cast<const SfxStyleSheetBasePool *>(&rBC) != nullptr) + { + const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint); + + if(pExtendedHint + && SfxHintId::StyleSheetModified == pExtendedHint->GetId()) + { + const OUString& aOldName(pExtendedHint->GetOldName()); + OUString aNewName(pExtendedHint->GetStyleSheet()->GetName()); + SfxStyleFamily eFamily = pExtendedHint->GetStyleSheet()->GetFamily(); + + if(aOldName != aNewName) + { + sal_Int32 nText = rTextProvider.getTextCount(); + while (nText--) + { + OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject(); + if( pParaObj ) + pParaObj->ChangeStyleSheetName(eFamily, aOldName, aNewName); + } + } + } + } + } + + // #i101556# Handout version information + sal_uInt32 TextProperties::getVersion() const + { + return maVersion; + } +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |