Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / svx / source / sdr / contact / viewobjectcontact.cxx
blobea1418f45850b6d4c86bc54337d8facbb102b53c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/sdr/contact/viewobjectcontact.hxx>
21 #include <svx/sdr/contact/viewcontact.hxx>
22 #include <svx/sdr/contact/objectcontact.hxx>
23 #include <svx/sdr/contact/displayinfo.hxx>
24 #include <svx/sdr/animation/animationstate.hxx>
25 #include <svx/sdr/contact/viewobjectcontactredirector.hxx>
26 #include <basegfx/color/bcolor.hxx>
27 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
29 #include <drawinglayer/processor2d/baseprocessor2d.hxx>
30 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
31 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
33 #include <svx/svdobj.hxx>
34 #include <svx/svdomedia.hxx>
35 #include <svx/svdmodel.hxx>
36 #include <svx/svdpage.hxx>
37 #include <svx/svdotext.hxx>
38 #include <vcl/pdfwriter.hxx>
39 #include <vcl/pdfextoutdevdata.hxx>
41 using namespace com::sun::star;
43 namespace {
45 // animated extractor
47 // Necessary to filter a sequence of animated primitives from
48 // a sequence of primitives to find out if animated or not. The decision for
49 // what to decompose is hard-coded and only done for knowingly animated primitives
50 // to not decompose too deeply and unnecessarily. This implies that the list
51 // which is view-specific needs to be expanded by hand when new animated objects
52 // are added. This may eventually be changed to a dynamically configurable approach
53 // if necessary.
54 class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
56 protected:
57 // the found animated primitives
58 drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence;
60 // text animation allowed?
61 bool mbTextAnimationAllowed : 1;
63 // graphic animation allowed?
64 bool mbGraphicAnimationAllowed : 1;
66 // as tooling, the process() implementation takes over API handling and calls this
67 // virtual render method when the primitive implementation is BasePrimitive2D-based.
68 virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override;
70 public:
71 AnimatedExtractingProcessor2D(
72 const drawinglayer::geometry::ViewInformation2D& rViewInformation,
73 bool bTextAnimationAllowed,
74 bool bGraphicAnimationAllowed);
76 // data access
77 const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
78 drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() { return std::move(maPrimitive2DSequence); }
81 AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
82 const drawinglayer::geometry::ViewInformation2D& rViewInformation,
83 bool bTextAnimationAllowed,
84 bool bGraphicAnimationAllowed)
85 : drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
86 mbTextAnimationAllowed(bTextAnimationAllowed),
87 mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
91 void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
93 // known implementation, access directly
94 switch(rCandidate.getPrimitive2DID())
96 // add and accept animated primitives directly, no need to decompose
97 case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D :
98 case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D :
99 case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D :
100 case PRIMITIVE2D_ID_ANIMATEDGRAPHICPRIMITIVE2D :
102 const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate);
104 if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed)
105 || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed))
107 const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
108 maPrimitive2DSequence.push_back(xReference);
110 break;
113 // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
114 // which then produces the animation infos (all when used/needed)
115 case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D :
116 case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
118 // decompose SdrObjects with evtl. animated text
119 case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D :
120 case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D :
121 case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D :
122 case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D :
123 case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D :
124 case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D :
125 case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D :
126 case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D :
128 // #121194# With Graphic as Bitmap FillStyle, also check
129 // for primitives filled with animated graphics
130 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
131 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
133 // decompose evtl. animated text contained in MaskPrimitive2D
134 // or group primitives
135 case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
136 case PRIMITIVE2D_ID_GROUPPRIMITIVE2D :
138 process(rCandidate);
139 break;
142 default :
144 // nothing to do for the rest
145 break;
150 } // end of anonymous namespace
152 namespace sdr::contact {
154 ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact)
155 : mrObjectContact(rObjectContact),
156 mrViewContact(rViewContact),
157 maGridOffset(0.0, 0.0),
158 mnActionChangedCount(0),
159 mbLazyInvalidate(false)
161 // make the ViewContact remember me
162 mrViewContact.AddViewObjectContact(*this);
164 // make the ObjectContact remember me
165 mrObjectContact.AddViewObjectContact(*this);
168 ViewObjectContact::~ViewObjectContact()
170 // if the object range is empty, then we have never had the primitive range change, so nothing to invalidate
171 if (!maObjectRange.isEmpty())
173 // invalidate in view
174 if(!getObjectRange().isEmpty())
176 GetObjectContact().InvalidatePartOfView(maObjectRange);
180 // delete PrimitiveAnimation
181 mpPrimitiveAnimation.reset();
183 // take care of remembered ObjectContact. Remove from
184 // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
185 // which (depending of its implementation) may destroy other OCs. This
186 // can trigger the deletion of the helper OC of a page visualising object
187 // which IS the OC of this object. Eventually StopGettingViewed() needs
188 // to get asynchron later
189 GetObjectContact().RemoveViewObjectContact(*this);
191 // take care of remembered ViewContact
192 GetViewContact().RemoveViewObjectContact(*this);
195 const basegfx::B2DRange& ViewObjectContact::getObjectRange() const
197 if(maObjectRange.isEmpty())
199 const drawinglayer::geometry::ViewInformation2D& rViewInfo2D = GetObjectContact().getViewInformation2D();
200 basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
201 if (!aTempRange.isEmpty())
203 const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
205 else
207 // if range is not computed (new or LazyInvalidate objects), force it
208 const DisplayInfo aDisplayInfo;
209 const drawinglayer::primitive2d::Primitive2DContainer& xSequence(getPrimitive2DSequence(aDisplayInfo));
211 if(!xSequence.empty())
213 const_cast< ViewObjectContact* >(this)->maObjectRange =
214 xSequence.getB2DRange(rViewInfo2D);
219 return maObjectRange;
222 void ViewObjectContact::ActionChanged()
224 // clear cached primitives
225 mxPrimitive2DSequence.clear();
226 ++mnActionChangedCount;
228 if(mbLazyInvalidate)
229 return;
231 // set local flag
232 mbLazyInvalidate = true;
234 // force ObjectRange
235 getObjectRange();
237 if(!getObjectRange().isEmpty())
239 // invalidate current valid range
240 GetObjectContact().InvalidatePartOfView(maObjectRange);
242 // reset gridOffset, it needs to be recalculated
243 if (GetObjectContact().supportsGridOffsets())
244 resetGridOffset();
245 else
246 maObjectRange.reset();
249 // register at OC for lazy invalidate
250 GetObjectContact().setLazyInvalidate(*this);
253 // IASS: helper for IASS invalidates
254 void ViewObjectContact::ActionChangedIfDifferentPageView(SdrPageView& rSdrPageView)
256 SdrPageView* pSdrPageView(GetObjectContact().TryToGetSdrPageView());
258 // if there is no SdrPageView or different from given one, force
259 // invalidate/repaint
260 if (nullptr == pSdrPageView || pSdrPageView != &rSdrPageView)
261 ActionChanged();
264 void ViewObjectContact::triggerLazyInvalidate()
266 if(!mbLazyInvalidate)
267 return;
269 // reset flag
270 mbLazyInvalidate = false;
272 // force ObjectRange
273 getObjectRange();
275 if(!getObjectRange().isEmpty())
277 // invalidate current valid range
278 GetObjectContact().InvalidatePartOfView(maObjectRange);
282 // Take some action when new objects are inserted
283 void ViewObjectContact::ActionChildInserted(ViewContact& rChild)
285 // force creation of the new VOC and trigger it's refresh, so it
286 // will take part in LazyInvalidate immediately
287 rChild.GetViewObjectContact(GetObjectContact()).ActionChanged();
289 // forward action to ObjectContact
290 // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
291 // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
294 void ViewObjectContact::checkForPrimitive2DAnimations()
296 // remove old one
297 mpPrimitiveAnimation.reset();
299 // check for animated primitives
300 if(mxPrimitive2DSequence.empty())
301 return;
303 const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
304 const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
306 if(bTextAnimationAllowed || bGraphicAnimationAllowed)
308 AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
309 bTextAnimationAllowed, bGraphicAnimationAllowed);
310 aAnimatedExtractor.process(mxPrimitive2DSequence);
312 if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
314 // derived primitiveList is animated, setup new PrimitiveAnimation
315 mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) );
320 void ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
322 // get the view-independent Primitive from the viewContact
323 drawinglayer::primitive2d::Primitive2DContainer xRetval;
324 GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
326 if(!xRetval.empty())
328 // handle GluePoint
329 if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
331 drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
333 if(!xGlue.empty())
335 xRetval.append(std::move(xGlue));
339 // handle ghosted
340 if(isPrimitiveGhosted(rDisplayInfo))
342 const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
343 const basegfx::BColorModifierSharedPtr aBColorModifier =
344 std::make_shared<basegfx::BColorModifier_interpolate>(
345 aRGBWhite,
346 0.5);
347 xRetval = drawinglayer::primitive2d::Primitive2DContainer{
348 new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
349 std::move(xRetval),
350 aBColorModifier)
355 rVisitor.visit(xRetval);
358 bool ViewObjectContact::isExportPDFTags() const
360 return GetObjectContact().isExportTaggedPDF();
363 /** Check if we need to embed to a StructureTagPrimitive2D, too. This
364 was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before
366 void ViewObjectContact::createStructureTag(drawinglayer::primitive2d::Primitive2DContainer & rNewPrimitiveSequence) const
368 SdrObject *const pSdrObj(mrViewContact.TryToGetSdrObject());
370 // Check if we need to embed to a StructureTagPrimitive2D, too. This
371 // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before
372 if (!rNewPrimitiveSequence.empty() && isExportPDFTags()
373 // ISO 14289-1:2014, Clause: 7.3
374 && (!pSdrObj || pSdrObj->getParentSdrObjectFromSdrObject() == nullptr))
376 if (nullptr != pSdrObj && !pSdrObj->IsDecorative())
378 vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement);
379 const SdrInventor nInventor(pSdrObj->GetObjInventor());
380 const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier());
381 const bool bIsTextObj(nullptr != DynCastSdrTextObj(pSdrObj));
383 // Note: SwFlyDrawObj/SwVirtFlyDrawObj have SdrInventor::Swg - these
384 // are *not* handled here because not all of them are painted
385 // completely with primitives, so a tag here does not encapsulate them.
386 // The tag must be created by SwTaggedPDFHelper until this is fixed.
387 if ( nInventor == SdrInventor::Default )
389 if ( nIdentifier == SdrObjKind::Group )
390 eElement = vcl::PDFWriter::Figure;
391 else if (nIdentifier == SdrObjKind::Table)
392 eElement = vcl::PDFWriter::Table;
393 else if (nIdentifier == SdrObjKind::Media)
394 eElement = vcl::PDFWriter::Annot;
395 else if ( nIdentifier == SdrObjKind::TitleText )
396 eElement = vcl::PDFWriter::Heading;
397 else if ( nIdentifier == SdrObjKind::OutlineText )
398 eElement = vcl::PDFWriter::Division;
399 else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() )
400 eElement = vcl::PDFWriter::Figure;
401 else
402 eElement = vcl::PDFWriter::Division;
405 if(vcl::PDFWriter::NonStructElement != eElement)
407 SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject());
409 if(pSdrPage)
411 const bool bBackground(pSdrPage->IsMasterPage());
412 const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier());
413 // note: there must be output device here, in PDF export
414 void const* pAnchorKey(nullptr);
415 if (auto const pUserCall = pSdrObj->GetUserCall())
417 pAnchorKey = pUserCall->GetPDFAnchorStructureElementKey(*pSdrObj);
420 ::std::vector<sal_Int32> annotIds;
421 if (eElement == vcl::PDFWriter::Annot
422 && !static_cast<SdrMediaObj*>(pSdrObj)->getURL().isEmpty())
424 auto const pPDFExtOutDevData(GetObjectContact().GetPDFExtOutDevData());
425 assert(pPDFExtOutDevData);
426 annotIds = pPDFExtOutDevData->GetScreenAnnotIds(pSdrObj);
429 rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer {
430 new drawinglayer::primitive2d::StructureTagPrimitive2D(
431 eElement,
432 bBackground,
433 bImage,
434 false, // Decorative
435 std::move(rNewPrimitiveSequence),
436 pAnchorKey,
437 &annotIds)
442 else
444 // page backgrounds etc should be tagged as artifacts:
445 rNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer {
446 new drawinglayer::primitive2d::StructureTagPrimitive2D(
447 // lies to force silly VclMetafileProcessor2D to emit NonStructElement
448 vcl::PDFWriter::Division,
449 true,
450 true,
451 true, // Decorative
452 std::move(rNewPrimitiveSequence))
458 drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
460 // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not)
461 SdrObject* pSdrObj(mrViewContact.TryToGetSdrObject());
463 if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable())
465 if (!mxPrimitive2DSequence.empty())
466 return mxPrimitive2DSequence;
469 // prepare new representation
470 drawinglayer::primitive2d::Primitive2DContainer xNewPrimitiveSequence;
472 // take care of redirectors and create new list
473 ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
475 if(pRedirector)
477 pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence);
479 else
481 createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence);
484 // check and eventually embed to GridOffset transform primitive (calc only)
485 if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets())
487 const basegfx::B2DVector& rGridOffset(getGridOffset());
489 if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
491 const basegfx::B2DHomMatrix aTranslateGridOffset(
492 basegfx::utils::createTranslateB2DHomMatrix(
493 rGridOffset));
494 xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer {
495 new drawinglayer::primitive2d::TransformPrimitive2D(
496 aTranslateGridOffset,
497 std::move(xNewPrimitiveSequence))
502 createStructureTag(xNewPrimitiveSequence);
504 // Local up-to-date checks. New list different from local one?
505 // This is the important point where it gets decided if the current or the new
506 // representation gets used. This is important for performance, since the
507 // current representation contains possible precious decompositions. That
508 // comparisons triggers exactly if something in the object visualization
509 // has changed.
510 // Note: That is the main reason for BasePrimitive2D::operator== at all. I
511 // have alternatively tried to invalidate the local representation on object
512 // change, but that is simply not reliable.
513 // Note2: I did that once in aw080, the lost CWS, and it worked well enough
514 // so that I could remove *all* operator== from all derivations of
515 // BasePrimitive2D, so it can be done again (with the needed resources)
516 if(mxPrimitive2DSequence != xNewPrimitiveSequence)
518 // has changed, copy content
519 const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
521 // check for animated stuff
522 const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
524 // always update object range when PrimitiveSequence changes
525 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
526 const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
529 // return current Primitive2DContainer
530 return mxPrimitive2DSequence;
533 bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
535 // default: always visible
536 return true;
539 bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
541 // default: standard check
542 return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
545 void ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
547 // check model-view visibility
548 if(!isPrimitiveVisible(rDisplayInfo))
549 return;
551 getPrimitive2DSequence(rDisplayInfo);
552 if(mxPrimitive2DSequence.empty())
553 return;
555 // get ranges
556 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
557 // tdf#147164 cannot use maObjectRange here, it is unreliable
558 const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D));
559 const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
561 // check geometrical visibility
562 bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
563 if(!bVisible)
564 return;
566 // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it
567 auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence);
568 int nPrevCount = mnActionChangedCount;
570 rVisitor.visit(tmp);
572 // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back
573 if (mnActionChangedCount == nPrevCount)
574 const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp);
577 void ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
579 ViewContact& rViewContact = GetViewContact();
580 const sal_uInt32 nSubHierarchyCount(rViewContact.GetObjectCount());
582 for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
583 rViewContact.getPrimitive2DSequenceHierarchyOfIndex(a, rDisplayInfo, GetObjectContact(), rVisitor);
586 // Support getting a GridOffset per object and view for non-linear ViewToDevice
587 // transformation (calc). On-demand created by delegating to the ObjectContact
588 // (->View) that has then all needed information
589 const basegfx::B2DVector& ViewObjectContact::getGridOffset() const
591 if (GetObjectContact().supportsGridOffsets())
593 if (fabs(maGridOffset.getX()) > 1000.0)
595 // Huge offsets are a hint for error -> usually the conditions for
596 // calculation have changed. E.g. - I saw errors with +/-5740, that
597 // was in the environment of massive external UNO API using LO as
598 // target.
599 // If conditions for this calculation change, it is usually required to call
600 // - ViewObjectContact::resetGridOffset(), or
601 // - ObjectContact::resetAllGridOffsets() or
602 // - ScDrawView::resetGridOffsetsForAllSdrPageViews()
603 // as it is done e.g. when zoom changes (see ScDrawView::RecalcScale()).
604 // Theoretically these resets have to be done for any precondition
605 // changed that is used in the calculation of that value (see
606 // ScDrawView::calculateGridOffsetForSdrObject).
607 // This is not complete and would be hard to do so.
608 // Since it is just a buffered value and re-calculation is not
609 // expensive (linear O(n)) we can just reset suspicious values here.
610 // Hopefully - when that non-linear ViewTransformation problem for
611 // the calc-view gets solved one day - all this can be removed
612 // again. For now, let's just reset here and force re-calculation.
613 // Add a SAL_WARN to inform about this.
614 SAL_WARN("svx", "Suspicious GridOffset value resetted (!)");
615 const_cast<ViewObjectContact*>(this)->maGridOffset.setX(0.0);
616 const_cast<ViewObjectContact*>(this)->maGridOffset.setY(0.0);
619 if(0.0 == maGridOffset.getX() && 0.0 == maGridOffset.getY() && GetObjectContact().supportsGridOffsets())
621 // create on-demand
622 GetObjectContact().calculateGridOffsetForViewObjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
626 return maGridOffset;
629 void ViewObjectContact::resetGridOffset()
631 // reset buffered GridOffset itself
632 maGridOffset.setX(0.0);
633 maGridOffset.setY(0.0);
635 // also reset sequence to get a re-calculation when GridOffset changes
636 mxPrimitive2DSequence.clear();
637 maObjectRange.reset();
642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */