1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <scitems.hxx>
21 #include <svtools/colorcfg.hxx>
22 #include <editeng/eeitem.hxx>
23 #include <formula/errorcodes.hxx>
24 #include <o3tl/unit_conversion.hxx>
25 #include <svx/sdshitm.hxx>
26 #include <svx/sdsxyitm.hxx>
27 #include <svx/sdtditm.hxx>
28 #include <svx/svditer.hxx>
29 #include <svx/svdocapt.hxx>
30 #include <svx/svdocirc.hxx>
31 #include <svx/svdopath.hxx>
32 #include <svx/svdorect.hxx>
33 #include <svx/svdpage.hxx>
34 #include <svx/svdundo.hxx>
35 #include <svx/xfillit0.hxx>
36 #include <svx/xflclit.hxx>
37 #include <svx/xlnclit.hxx>
38 #include <svx/xlnedcit.hxx>
39 #include <svx/xlnedit.hxx>
40 #include <svx/xlnedwit.hxx>
41 #include <svx/xlnstcit.hxx>
42 #include <svx/xlnstit.hxx>
43 #include <svx/xlnstwit.hxx>
44 #include <svx/xlnwtit.hxx>
45 #include <svx/sdtagitm.hxx>
46 #include <svx/sxcecitm.hxx>
47 #include <svl/whiter.hxx>
48 #include <osl/diagnose.h>
50 #include <basegfx/point/b2dpoint.hxx>
51 #include <basegfx/polygon/b2dpolygontools.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include <detfunc.hxx>
56 #include <document.hxx>
57 #include <dociter.hxx>
58 #include <drwlayer.hxx>
59 #include <userdat.hxx>
60 #include <validat.hxx>
61 #include <formulacell.hxx>
62 #include <globstr.hrc>
63 #include <scresid.hxx>
64 #include <undostyl.hxx>
65 #include <stlpool.hxx>
66 #include <docpool.hxx>
67 #include <patattr.hxx>
70 #include <reftokenhelper.hxx>
71 #include <formulaiter.hxx>
72 #include <cellvalue.hxx>
78 using namespace com::sun::star
;
82 enum DetInsertResult
{ // return-values for inserting in one level
96 SfxItemSet aFromTabSet
;
97 SfxItemSet aCircleSet
; //TODO: individually ?
101 explicit ScDetectiveData( SdrModel
* pModel
);
103 SfxItemSet
& GetBoxSet() { return aBoxSet
; }
104 SfxItemSet
& GetArrowSet() { return aArrowSet
; }
105 SfxItemSet
& GetToTabSet() { return aToTabSet
; }
106 SfxItemSet
& GetFromTabSet() { return aFromTabSet
; }
107 SfxItemSet
& GetCircleSet() { return aCircleSet
; }
109 void SetMaxLevel( sal_uInt16 nVal
) { nMaxLevel
= nVal
; }
110 sal_uInt16
GetMaxLevel() const { return nMaxLevel
; }
113 Color
ScDetectiveFunc::nArrowColor
= Color(0);
114 Color
ScDetectiveFunc::nErrorColor
= Color(0);
115 Color
ScDetectiveFunc::nCommentColor
= Color(0);
116 bool ScDetectiveFunc::bColorsInitialized
= false;
118 static bool lcl_HasThickLine( const SdrObject
& rObj
)
120 // thin lines get width 0 -> everything greater 0 is a thick line
122 return rObj
.GetMergedItem(XATTR_LINEWIDTH
).GetValue() > 0;
125 ScDetectiveData::ScDetectiveData( SdrModel
* pModel
) :
126 aBoxSet( pModel
->GetItemPool(), svl::Items
<SDRATTR_START
, SDRATTR_END
> ),
127 aArrowSet( pModel
->GetItemPool(), svl::Items
<SDRATTR_START
, SDRATTR_END
> ),
128 aToTabSet( pModel
->GetItemPool(), svl::Items
<SDRATTR_START
, SDRATTR_END
> ),
129 aFromTabSet( pModel
->GetItemPool(), svl::Items
<SDRATTR_START
, SDRATTR_END
> ),
130 aCircleSet( pModel
->GetItemPool(), svl::Items
<SDRATTR_START
, SDRATTR_END
> ),
134 aBoxSet
.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetArrowColor() ) );
135 aBoxSet
.Put( XFillStyleItem( drawing::FillStyle_NONE
) );
137 // create default line endings (like XLineEndList::Create)
138 // to be independent from the configured line endings
140 basegfx::B2DPolygon aTriangle
;
141 aTriangle
.append(basegfx::B2DPoint(10.0, 0.0));
142 aTriangle
.append(basegfx::B2DPoint(0.0, 30.0));
143 aTriangle
.append(basegfx::B2DPoint(20.0, 30.0));
144 aTriangle
.setClosed(true);
146 basegfx::B2DPolygon aSquare
;
147 aSquare
.append(basegfx::B2DPoint(0.0, 0.0));
148 aSquare
.append(basegfx::B2DPoint(10.0, 0.0));
149 aSquare
.append(basegfx::B2DPoint(10.0, 10.0));
150 aSquare
.append(basegfx::B2DPoint(0.0, 10.0));
151 aSquare
.setClosed(true);
153 basegfx::B2DPolygon
aCircle(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
154 aCircle
.setClosed(true);
156 const OUString aName
;
158 aArrowSet
.Put( XLineStartItem( aName
, basegfx::B2DPolyPolygon(aCircle
) ) );
159 aArrowSet
.Put( XLineStartWidthItem( 200 ) );
160 aArrowSet
.Put( XLineStartCenterItem( true ) );
161 aArrowSet
.Put( XLineEndItem( aName
, basegfx::B2DPolyPolygon(aTriangle
) ) );
162 aArrowSet
.Put( XLineEndWidthItem( 200 ) );
163 aArrowSet
.Put( XLineEndCenterItem( false ) );
165 aToTabSet
.Put( XLineStartItem( aName
, basegfx::B2DPolyPolygon(aCircle
) ) );
166 aToTabSet
.Put( XLineStartWidthItem( 200 ) );
167 aToTabSet
.Put( XLineStartCenterItem( true ) );
168 aToTabSet
.Put( XLineEndItem( aName
, basegfx::B2DPolyPolygon(aSquare
) ) );
169 aToTabSet
.Put( XLineEndWidthItem( 300 ) );
170 aToTabSet
.Put( XLineEndCenterItem( false ) );
172 aFromTabSet
.Put( XLineStartItem( aName
, basegfx::B2DPolyPolygon(aSquare
) ) );
173 aFromTabSet
.Put( XLineStartWidthItem( 300 ) );
174 aFromTabSet
.Put( XLineStartCenterItem( true ) );
175 aFromTabSet
.Put( XLineEndItem( aName
, basegfx::B2DPolyPolygon(aTriangle
) ) );
176 aFromTabSet
.Put( XLineEndWidthItem( 200 ) );
177 aFromTabSet
.Put( XLineEndCenterItem( false ) );
179 aCircleSet
.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetErrorColor() ) );
180 aCircleSet
.Put( XFillStyleItem( drawing::FillStyle_NONE
) );
181 aCircleSet
.Put( XLineWidthItem( 55 ) ); // 54 = 1 Pixel
184 void ScDetectiveFunc::Modified()
186 rDoc
.SetStreamValid(nTab
, false);
189 static bool Intersect( SCCOL nStartCol1
, SCROW nStartRow1
, SCCOL nEndCol1
, SCROW nEndRow1
,
190 SCCOL nStartCol2
, SCROW nStartRow2
, SCCOL nEndCol2
, SCROW nEndRow2
)
192 return nEndCol1
>= nStartCol2
&& nEndCol2
>= nStartCol1
&&
193 nEndRow1
>= nStartRow2
&& nEndRow2
>= nStartRow1
;
196 bool ScDetectiveFunc::HasError( const ScRange
& rRange
, ScAddress
& rErrPos
)
198 rErrPos
= rRange
.aStart
;
199 FormulaError nError
= FormulaError::NONE
;
201 ScCellIterator
aIter( rDoc
, rRange
);
202 for (bool bHasCell
= aIter
.first(); bHasCell
; bHasCell
= aIter
.next())
204 if (aIter
.getType() != CELLTYPE_FORMULA
)
207 nError
= aIter
.getFormulaCell()->GetErrCode();
208 if (nError
!= FormulaError::NONE
)
209 rErrPos
= aIter
.GetPos();
212 return (nError
!= FormulaError::NONE
);
215 Point
ScDetectiveFunc::GetDrawPos( SCCOL nCol
, SCROW nRow
, DrawPosMode eMode
) const
217 OSL_ENSURE( rDoc
.ValidColRow( nCol
, nRow
), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
218 nCol
= rDoc
.SanitizeCol( nCol
);
219 nRow
= rDoc
.SanitizeRow( nRow
);
225 case DrawPosMode::TopLeft
:
227 case DrawPosMode::BottomRight
:
231 case DrawPosMode::DetectiveArrow
:
232 aPos
.AdjustX(rDoc
.GetColWidth( nCol
, nTab
) / 4 );
233 aPos
.AdjustY(rDoc
.GetRowHeight( nRow
, nTab
) / 2 );
237 for ( SCCOL i
= 0; i
< nCol
; ++i
)
238 aPos
.AdjustX(rDoc
.GetColWidth( i
, nTab
) );
239 aPos
.AdjustY(rDoc
.GetRowHeight( 0, nRow
- 1, nTab
) );
241 aPos
.setX(o3tl::convert(aPos
.X(), o3tl::Length::twip
, o3tl::Length::mm100
));
242 aPos
.setY(o3tl::convert(aPos
.Y(), o3tl::Length::twip
, o3tl::Length::mm100
));
244 if ( rDoc
.IsNegativePage( nTab
) )
245 aPos
.setX( aPos
.X() * -1 );
250 tools::Rectangle
ScDetectiveFunc::GetDrawRect( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
) const
252 tools::Rectangle
aRect(
253 GetDrawPos( ::std::min( nCol1
, nCol2
), ::std::min( nRow1
, nRow2
), DrawPosMode::TopLeft
),
254 GetDrawPos( ::std::max( nCol1
, nCol2
), ::std::max( nRow1
, nRow2
), DrawPosMode::BottomRight
) );
255 aRect
.Normalize(); // reorder left/right in RTL sheets
259 tools::Rectangle
ScDetectiveFunc::GetDrawRect( SCCOL nCol
, SCROW nRow
) const
261 return GetDrawRect( nCol
, nRow
, nCol
, nRow
);
264 static bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon
& rPolyPolygon
)
266 // test if rPolygon is the line end for "other table" (rectangle)
267 if(1 == rPolyPolygon
.count())
269 const basegfx::B2DPolygon
& aSubPoly(rPolyPolygon
.getB2DPolygon(0));
271 // #i73305# circle consists of 4 segments, too, distinguishable from square by
272 // the use of control points
273 if(4 == aSubPoly
.count() && aSubPoly
.isClosed() && !aSubPoly
.areControlPointsUsed())
282 bool ScDetectiveFunc::HasArrow( const ScAddress
& rStart
,
283 SCCOL nEndCol
, SCROW nEndRow
, SCTAB nEndTab
)
285 bool bStartAlien
= ( rStart
.Tab() != nTab
);
286 bool bEndAlien
= ( nEndTab
!= nTab
);
288 if (bStartAlien
&& bEndAlien
)
290 OSL_FAIL("bStartAlien && bEndAlien");
294 tools::Rectangle aStartRect
;
295 tools::Rectangle aEndRect
;
297 aStartRect
= GetDrawRect( rStart
.Col(), rStart
.Row() );
299 aEndRect
= GetDrawRect( nEndCol
, nEndRow
);
301 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
302 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
303 OSL_ENSURE(pPage
,"Page ?");
306 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
307 SdrObject
* pObject
= aIter
.Next();
308 while (pObject
&& !bFound
)
310 if ( pObject
->GetLayer()==SC_LAYER_INTERN
&&
311 pObject
->IsPolyObj() && pObject
->GetPointCount()==2 )
313 const SfxItemSet
& rSet
= pObject
->GetMergedItemSet();
315 bool bObjStartAlien
=
316 lcl_IsOtherTab( rSet
.Get(XATTR_LINESTART
).GetLineStartValue() );
318 lcl_IsOtherTab( rSet
.Get(XATTR_LINEEND
).GetLineEndValue() );
320 bool bStartHit
= bStartAlien
? bObjStartAlien
:
321 ( !bObjStartAlien
&& aStartRect
.Contains(pObject
->GetPoint(0)) );
322 bool bEndHit
= bEndAlien
? bObjEndAlien
:
323 ( !bObjEndAlien
&& aEndRect
.Contains(pObject
->GetPoint(1)) );
325 if ( bStartHit
&& bEndHit
)
328 pObject
= aIter
.Next();
334 bool ScDetectiveFunc::IsNonAlienArrow( const SdrObject
* pObject
)
336 if ( pObject
->GetLayer()==SC_LAYER_INTERN
&&
337 pObject
->IsPolyObj() && pObject
->GetPointCount()==2 )
339 const SfxItemSet
& rSet
= pObject
->GetMergedItemSet();
341 bool bObjStartAlien
=
342 lcl_IsOtherTab( rSet
.Get(XATTR_LINESTART
).GetLineStartValue() );
344 lcl_IsOtherTab( rSet
.Get(XATTR_LINEEND
).GetLineEndValue() );
346 return !bObjStartAlien
&& !bObjEndAlien
;
352 // InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
354 void ScDetectiveFunc::InsertArrow( SCCOL nCol
, SCROW nRow
,
355 SCCOL nRefStartCol
, SCROW nRefStartRow
,
356 SCCOL nRefEndCol
, SCROW nRefEndRow
,
357 bool bFromOtherTab
, bool bRed
,
358 ScDetectiveData
& rData
)
360 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
361 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
363 bool bArea
= ( nRefStartCol
!= nRefEndCol
|| nRefStartRow
!= nRefEndRow
);
364 if (bArea
&& !bFromOtherTab
)
366 // insert the rectangle before the arrow - this is relied on in FindFrameForObject
368 tools::Rectangle aRect
= GetDrawRect( nRefStartCol
, nRefStartRow
, nRefEndCol
, nRefEndRow
);
369 rtl::Reference
<SdrRectObj
> pBox
= new SdrRectObj(
373 pBox
->NbcSetStyleSheet(nullptr, true);
374 pBox
->SetMergedItemSetAndBroadcast(rData
.GetBoxSet());
376 pBox
->SetLayer( SC_LAYER_INTERN
);
377 pPage
->InsertObject( pBox
.get() );
378 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pBox
) );
380 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pBox
.get(), true );
381 pData
->maStart
.Set( nRefStartCol
, nRefStartRow
, nTab
);
382 pData
->maEnd
.Set( nRefEndCol
, nRefEndRow
, nTab
);
385 Point aStartPos
= GetDrawPos( nRefStartCol
, nRefStartRow
, DrawPosMode::DetectiveArrow
);
386 Point aEndPos
= GetDrawPos( nCol
, nRow
, DrawPosMode::DetectiveArrow
);
390 bool bNegativePage
= rDoc
.IsNegativePage( nTab
);
391 tools::Long nPageSign
= bNegativePage
? -1 : 1;
393 aStartPos
= Point( aEndPos
.X() - 1000 * nPageSign
, aEndPos
.Y() - 1000 );
394 if (aStartPos
.X() * nPageSign
< 0)
395 aStartPos
.AdjustX(2000 * nPageSign
);
396 if (aStartPos
.Y() < 0)
397 aStartPos
.AdjustY(2000 );
400 SfxItemSet
& rAttrSet
= bFromOtherTab
? rData
.GetFromTabSet() : rData
.GetArrowSet();
402 if (bArea
&& !bFromOtherTab
)
403 rAttrSet
.Put( XLineWidthItem( 50 ) ); // range
405 rAttrSet
.Put( XLineWidthItem( 0 ) ); // single reference
407 Color nColor
= ( bRed
? GetErrorColor() : GetArrowColor() );
408 rAttrSet
.Put( XLineColorItem( OUString(), nColor
) );
410 basegfx::B2DPolygon aTempPoly
;
411 aTempPoly
.append(basegfx::B2DPoint(aStartPos
.X(), aStartPos
.Y()));
412 aTempPoly
.append(basegfx::B2DPoint(aEndPos
.X(), aEndPos
.Y()));
413 rtl::Reference
<SdrPathObj
> pArrow
= new SdrPathObj(
416 basegfx::B2DPolyPolygon(aTempPoly
));
417 pArrow
->NbcSetStyleSheet(nullptr, true);
418 pArrow
->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos
,aEndPos
)); //TODO: needed ???
419 pArrow
->SetMergedItemSetAndBroadcast(rAttrSet
);
421 pArrow
->SetLayer( SC_LAYER_INTERN
);
422 pPage
->InsertObject( pArrow
.get() );
423 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pArrow
) );
425 ScDrawObjData
* pData
= ScDrawLayer::GetObjData(pArrow
.get(), true);
427 pData
->maStart
.SetInvalid();
429 pData
->maStart
.Set( nRefStartCol
, nRefStartRow
, nTab
);
431 pData
->maEnd
.Set( nCol
, nRow
, nTab
);
432 pData
->meType
= ScDrawObjData::DetectiveArrow
;
437 void ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol
, SCROW nStartRow
,
438 SCCOL nEndCol
, SCROW nEndRow
, bool bRed
,
439 ScDetectiveData
& rData
)
441 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
442 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
444 bool bArea
= ( nStartCol
!= nEndCol
|| nStartRow
!= nEndRow
);
447 tools::Rectangle aRect
= GetDrawRect( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
448 rtl::Reference
<SdrRectObj
> pBox
= new SdrRectObj(
452 pBox
->NbcSetStyleSheet(nullptr, true);
453 pBox
->SetMergedItemSetAndBroadcast(rData
.GetBoxSet());
455 pBox
->SetLayer( SC_LAYER_INTERN
);
456 pPage
->InsertObject( pBox
.get() );
457 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pBox
) );
459 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pBox
.get(), true );
460 pData
->maStart
.Set( nStartCol
, nStartRow
, nTab
);
461 pData
->maEnd
.Set( nEndCol
, nEndRow
, nTab
);
464 bool bNegativePage
= rDoc
.IsNegativePage( nTab
);
465 tools::Long nPageSign
= bNegativePage
? -1 : 1;
467 Point aStartPos
= GetDrawPos( nStartCol
, nStartRow
, DrawPosMode::DetectiveArrow
);
468 Point
aEndPos( aStartPos
.X() + 1000 * nPageSign
, aStartPos
.Y() - 1000 );
470 aEndPos
.AdjustY(2000 );
472 SfxItemSet
& rAttrSet
= rData
.GetToTabSet();
474 rAttrSet
.Put( XLineWidthItem( 50 ) ); // range
476 rAttrSet
.Put( XLineWidthItem( 0 ) ); // single reference
478 Color nColor
= ( bRed
? GetErrorColor() : GetArrowColor() );
479 rAttrSet
.Put( XLineColorItem( OUString(), nColor
) );
481 basegfx::B2DPolygon aTempPoly
;
482 aTempPoly
.append(basegfx::B2DPoint(aStartPos
.X(), aStartPos
.Y()));
483 aTempPoly
.append(basegfx::B2DPoint(aEndPos
.X(), aEndPos
.Y()));
484 rtl::Reference
<SdrPathObj
> pArrow
= new SdrPathObj(
487 basegfx::B2DPolyPolygon(aTempPoly
));
488 pArrow
->NbcSetStyleSheet(nullptr, true);
489 pArrow
->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos
,aEndPos
)); //TODO: needed ???
491 pArrow
->SetMergedItemSetAndBroadcast(rAttrSet
);
493 pArrow
->SetLayer( SC_LAYER_INTERN
);
494 pPage
->InsertObject( pArrow
.get() );
495 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pArrow
) );
497 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pArrow
.get(), true );
498 pData
->maStart
.Set( nStartCol
, nStartRow
, nTab
);
499 pData
->maEnd
.SetInvalid();
504 // DrawEntry: formula from this spreadsheet,
505 // reference on this or other
506 // DrawAlienEntry: formula from other spreadsheet,
509 // return FALSE: there was already an arrow
511 bool ScDetectiveFunc::DrawEntry( SCCOL nCol
, SCROW nRow
,
513 ScDetectiveData
& rData
)
515 if ( HasArrow( rRef
.aStart
, nCol
, nRow
, nTab
) )
519 bool bError
= HasError( rRef
, aErrorPos
);
520 bool bAlien
= ( rRef
.aEnd
.Tab() < nTab
|| rRef
.aStart
.Tab() > nTab
);
522 InsertArrow( nCol
, nRow
,
523 rRef
.aStart
.Col(), rRef
.aStart
.Row(),
524 rRef
.aEnd
.Col(), rRef
.aEnd
.Row(),
525 bAlien
, bError
, rData
);
529 bool ScDetectiveFunc::DrawAlienEntry( const ScRange
& rRef
,
530 ScDetectiveData
& rData
)
532 if ( HasArrow( rRef
.aStart
, 0, 0, nTab
+1 ) )
536 bool bError
= HasError( rRef
, aErrorPos
);
538 InsertToOtherTab( rRef
.aStart
.Col(), rRef
.aStart
.Row(),
539 rRef
.aEnd
.Col(), rRef
.aEnd
.Row(),
544 void ScDetectiveFunc::DrawCircle( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
)
546 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
547 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
549 tools::Rectangle aRect
= ScDrawLayer::GetCellRect(rDoc
, ScAddress(nCol
, nRow
, nTab
), true);
550 aRect
.AdjustLeft( -250 );
551 aRect
.AdjustRight(250 );
552 aRect
.AdjustTop( -70 );
553 aRect
.AdjustBottom(70 );
555 rtl::Reference
<SdrCircObj
> pCircle
= new SdrCircObj(
559 SfxItemSet
& rAttrSet
= rData
.GetCircleSet();
561 pCircle
->NbcSetStyleSheet(nullptr, true);
562 pCircle
->SetMergedItemSetAndBroadcast(rAttrSet
);
564 pCircle
->SetLayer( SC_LAYER_INTERN
);
565 pPage
->InsertObject( pCircle
.get() );
566 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pCircle
) );
568 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pCircle
.get(), true );
569 pData
->maStart
.Set( nCol
, nRow
, nTab
);
570 pData
->maEnd
.SetInvalid();
571 pData
->meType
= ScDrawObjData::ValidationCircle
;
576 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol
, SCROW nRow
, bool bDestPnt
)
578 tools::Rectangle aRect
= GetDrawRect( nCol
, nRow
);
580 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
581 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
582 OSL_ENSURE(pPage
,"Page ?");
584 pPage
->RecalcObjOrdNums();
586 const size_t nObjCount
= pPage
->GetObjCount();
590 size_t nDelCount
= 0;
591 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
593 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
594 SdrObject
* pObject
= aIter
.Next();
597 if ( pObject
->GetLayer()==SC_LAYER_INTERN
&&
598 pObject
->IsPolyObj() && pObject
->GetPointCount()==2 )
600 if (aRect
.Contains(pObject
->GetPoint(bDestPnt
? 1 : 0))) // start/destinationpoint
601 ppObj
[nDelCount
++] = pObject
;
604 pObject
= aIter
.Next();
607 const bool bRecording
= pModel
->IsRecording();
611 for (size_t i
=1; i
<=nDelCount
; ++i
)
612 pModel
->AddCalcUndo(std::make_unique
<SdrUndoDelObj
>(*ppObj
[nDelCount
-i
]));
615 for (size_t i
=1; i
<=nDelCount
; ++i
)
617 // remove the object from the drawing page, delete if undo is disabled
618 pPage
->RemoveObject(ppObj
[nDelCount
-i
]->GetOrdNum());
626 // delete box around reference
628 #define SC_DET_TOLERANCE 50
630 static bool RectIsPoints( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
632 return rRect
.Left() >= rStart
.X() - SC_DET_TOLERANCE
633 && rRect
.Left() <= rStart
.X() + SC_DET_TOLERANCE
634 && rRect
.Right() >= rEnd
.X() - SC_DET_TOLERANCE
635 && rRect
.Right() <= rEnd
.X() + SC_DET_TOLERANCE
636 && rRect
.Top() >= rStart
.Y() - SC_DET_TOLERANCE
637 && rRect
.Top() <= rStart
.Y() + SC_DET_TOLERANCE
638 && rRect
.Bottom() >= rEnd
.Y() - SC_DET_TOLERANCE
639 && rRect
.Bottom() <= rEnd
.Y() + SC_DET_TOLERANCE
;
642 #undef SC_DET_TOLERANCE
644 void ScDetectiveFunc::DeleteBox( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
646 tools::Rectangle aCornerRect
= GetDrawRect( nCol1
, nRow1
, nCol2
, nRow2
);
647 Point aStartCorner
= aCornerRect
.TopLeft();
648 Point aEndCorner
= aCornerRect
.BottomRight();
649 tools::Rectangle aObjRect
;
651 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
652 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
653 OSL_ENSURE(pPage
,"Page ?");
655 pPage
->RecalcObjOrdNums();
657 const size_t nObjCount
= pPage
->GetObjCount();
661 size_t nDelCount
= 0;
662 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
664 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
665 SdrObject
* pObject
= aIter
.Next();
668 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
669 if ( auto pSdrRectObj
= dynamic_cast< const SdrRectObj
* >(pObject
) )
671 aObjRect
= pSdrRectObj
->GetLogicRect();
672 aObjRect
.Normalize();
673 if ( RectIsPoints( aObjRect
, aStartCorner
, aEndCorner
) )
674 ppObj
[nDelCount
++] = pObject
;
677 pObject
= aIter
.Next();
680 for (size_t i
=1; i
<=nDelCount
; ++i
)
681 pModel
->AddCalcUndo( std::make_unique
<SdrUndoRemoveObj
>( *ppObj
[nDelCount
-i
] ) );
683 for (size_t i
=1; i
<=nDelCount
; ++i
)
684 pPage
->RemoveObject( ppObj
[nDelCount
-i
]->GetOrdNum() );
691 sal_uInt16
ScDetectiveFunc::InsertPredLevelArea( const ScRange
& rRef
,
692 ScDetectiveData
& rData
, sal_uInt16 nLevel
)
694 sal_uInt16 nResult
= DET_INS_EMPTY
;
696 ScCellIterator
aIter( rDoc
, rRef
);
697 for (bool bHasCell
= aIter
.first(); bHasCell
; bHasCell
= aIter
.next())
699 if (aIter
.getType() != CELLTYPE_FORMULA
)
702 const ScAddress
& rPos
= aIter
.GetPos();
703 switch (InsertPredLevel(rPos
.Col(), rPos
.Row(), rData
, nLevel
))
705 case DET_INS_INSERTED
:
706 nResult
= DET_INS_INSERTED
;
708 case DET_INS_CONTINUE
:
709 if (nResult
!= DET_INS_INSERTED
)
710 nResult
= DET_INS_CONTINUE
;
712 case DET_INS_CIRCULAR
:
713 if (nResult
== DET_INS_EMPTY
)
714 nResult
= DET_INS_CIRCULAR
;
724 sal_uInt16
ScDetectiveFunc::InsertPredLevel( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
,
727 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
728 if (aCell
.getType() != CELLTYPE_FORMULA
)
729 return DET_INS_EMPTY
;
731 ScFormulaCell
* pFCell
= aCell
.getFormula();
732 if (pFCell
->IsRunning())
733 return DET_INS_CIRCULAR
;
735 if (pFCell
->GetDirty())
736 pFCell
->Interpret(); // can't be called after SetRunning
737 pFCell
->SetRunning(true);
739 sal_uInt16 nResult
= DET_INS_EMPTY
;
741 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
743 while ( aIter
.GetNextRef( aRef
) )
745 if (DrawEntry( nCol
, nRow
, aRef
, rData
))
747 nResult
= DET_INS_INSERTED
; // insert new arrow
753 if ( nLevel
< rData
.GetMaxLevel() )
755 sal_uInt16 nSubResult
;
756 bool bArea
= (aRef
.aStart
!= aRef
.aEnd
);
758 nSubResult
= InsertPredLevelArea( aRef
, rData
, nLevel
+1 );
760 nSubResult
= InsertPredLevel( aRef
.aStart
.Col(), aRef
.aStart
.Row(),
765 case DET_INS_INSERTED
:
766 nResult
= DET_INS_INSERTED
;
768 case DET_INS_CONTINUE
:
769 if (nResult
!= DET_INS_INSERTED
)
770 nResult
= DET_INS_CONTINUE
;
772 case DET_INS_CIRCULAR
:
773 if (nResult
== DET_INS_EMPTY
)
774 nResult
= DET_INS_CIRCULAR
;
776 // DET_INS_EMPTY: no change
779 else // nMaxLevel reached
780 if (nResult
!= DET_INS_INSERTED
)
781 nResult
= DET_INS_CONTINUE
;
785 pFCell
->SetRunning(false);
790 sal_uInt16
ScDetectiveFunc::FindPredLevelArea( const ScRange
& rRef
,
791 sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
793 sal_uInt16 nResult
= nLevel
;
795 ScCellIterator
aCellIter( rDoc
, rRef
);
796 for (bool bHasCell
= aCellIter
.first(); bHasCell
; bHasCell
= aCellIter
.next())
798 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
801 sal_uInt16 nTemp
= FindPredLevel(aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(), nLevel
, nDeleteLevel
);
809 // nDeleteLevel != 0 -> delete
811 sal_uInt16
ScDetectiveFunc::FindPredLevel( SCCOL nCol
, SCROW nRow
, sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
813 OSL_ENSURE( nLevel
<1000, "Level" );
815 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
816 if (aCell
.getType() != CELLTYPE_FORMULA
)
819 ScFormulaCell
* pFCell
= aCell
.getFormula();
820 if (pFCell
->IsRunning())
823 if (pFCell
->GetDirty())
824 pFCell
->Interpret(); // can't be called after SetRunning
825 pFCell
->SetRunning(true);
827 sal_uInt16 nResult
= nLevel
;
828 bool bDelete
= ( nDeleteLevel
&& nLevel
== nDeleteLevel
-1 );
832 DeleteArrowsAt( nCol
, nRow
, true ); // arrows, that are pointing here
835 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
837 while ( aIter
.GetNextRef( aRef
) )
839 bool bArea
= ( aRef
.aStart
!= aRef
.aEnd
);
841 if ( bDelete
) // delete frame ?
845 DeleteBox( aRef
.aStart
.Col(), aRef
.aStart
.Row(), aRef
.aEnd
.Col(), aRef
.aEnd
.Row() );
848 else // continue searching
850 if ( HasArrow( aRef
.aStart
, nCol
,nRow
,nTab
) )
854 nTemp
= FindPredLevelArea( aRef
, nLevel
+1, nDeleteLevel
);
856 nTemp
= FindPredLevel( aRef
.aStart
.Col(),aRef
.aStart
.Row(),
857 nLevel
+1, nDeleteLevel
);
864 pFCell
->SetRunning(false);
869 sal_uInt16
ScDetectiveFunc::InsertErrorLevel( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
,
872 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
873 if (aCell
.getType() != CELLTYPE_FORMULA
)
874 return DET_INS_EMPTY
;
876 ScFormulaCell
* pFCell
= aCell
.getFormula();
877 if (pFCell
->IsRunning())
878 return DET_INS_CIRCULAR
;
880 if (pFCell
->GetDirty())
881 pFCell
->Interpret(); // can't be called after SetRunning
882 pFCell
->SetRunning(true);
884 sal_uInt16 nResult
= DET_INS_EMPTY
;
886 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
889 bool bHasError
= false;
890 while ( aIter
.GetNextRef( aRef
) )
892 if (HasError( aRef
, aErrorPos
))
895 if (DrawEntry( nCol
, nRow
, ScRange( aErrorPos
), rData
))
896 nResult
= DET_INS_INSERTED
;
898 if ( nLevel
< rData
.GetMaxLevel() ) // hits most of the time
900 if (InsertErrorLevel( aErrorPos
.Col(), aErrorPos
.Row(),
901 rData
, nLevel
+1 ) == DET_INS_INSERTED
)
902 nResult
= DET_INS_INSERTED
;
907 pFCell
->SetRunning(false);
911 if (InsertPredLevel( nCol
, nRow
, rData
, rData
.GetMaxLevel() ) == DET_INS_INSERTED
)
912 nResult
= DET_INS_INSERTED
;
917 sal_uInt16
ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
918 ScDetectiveData
& rData
, sal_uInt16 nLevel
)
920 // over the entire document.
922 sal_uInt16 nResult
= DET_INS_EMPTY
;
923 ScCellIterator
aCellIter(rDoc
, ScRange(0,0,0,rDoc
.MaxCol(),rDoc
.MaxRow(),MAXTAB
)); // all sheets
924 for (bool bHas
= aCellIter
.first(); bHas
; bHas
= aCellIter
.next())
926 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
929 ScFormulaCell
* pFCell
= aCellIter
.getFormulaCell();
930 bool bRunning
= pFCell
->IsRunning();
932 if (pFCell
->GetDirty())
933 pFCell
->Interpret(); // can't be called after SetRunning
934 pFCell
->SetRunning(true);
936 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
938 while ( aIter
.GetNextRef( aRef
) )
940 if (aRef
.aStart
.Tab() <= nTab
&& aRef
.aEnd
.Tab() >= nTab
)
942 if (Intersect( nCol1
,nRow1
,nCol2
,nRow2
,
943 aRef
.aStart
.Col(),aRef
.aStart
.Row(),
944 aRef
.aEnd
.Col(),aRef
.aEnd
.Row() ))
946 bool bAlien
= ( aCellIter
.GetPos().Tab() != nTab
);
949 bDrawRet
= DrawAlienEntry( aRef
, rData
);
951 bDrawRet
= DrawEntry( aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
955 nResult
= DET_INS_INSERTED
; // insert new arrow
961 if (nResult
== DET_INS_EMPTY
)
962 nResult
= DET_INS_CIRCULAR
;
967 if ( nLevel
< rData
.GetMaxLevel() )
969 sal_uInt16 nSubResult
= InsertSuccLevel(
970 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
971 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
975 case DET_INS_INSERTED
:
976 nResult
= DET_INS_INSERTED
;
978 case DET_INS_CONTINUE
:
979 if (nResult
!= DET_INS_INSERTED
)
980 nResult
= DET_INS_CONTINUE
;
982 case DET_INS_CIRCULAR
:
983 if (nResult
== DET_INS_EMPTY
)
984 nResult
= DET_INS_CIRCULAR
;
986 // DET_INS_EMPTY: leave unchanged
989 else // nMaxLevel reached
990 if (nResult
!= DET_INS_INSERTED
)
991 nResult
= DET_INS_CONTINUE
;
997 pFCell
->SetRunning(bRunning
);
1003 sal_uInt16
ScDetectiveFunc::FindSuccLevel( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1004 sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
1006 OSL_ENSURE( nLevel
<1000, "Level" );
1008 sal_uInt16 nResult
= nLevel
;
1009 bool bDelete
= ( nDeleteLevel
&& nLevel
== nDeleteLevel
-1 );
1011 ScCellIterator
aCellIter( rDoc
, ScRange(0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
) );
1012 for (bool bHas
= aCellIter
.first(); bHas
; bHas
= aCellIter
.next())
1014 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
1017 ScFormulaCell
* pFCell
= aCellIter
.getFormulaCell();
1018 bool bRunning
= pFCell
->IsRunning();
1020 if (pFCell
->GetDirty())
1021 pFCell
->Interpret(); // can't be called after SetRunning
1022 pFCell
->SetRunning(true);
1024 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
1026 while ( aIter
.GetNextRef( aRef
) )
1028 if (aRef
.aStart
.Tab() <= nTab
&& aRef
.aEnd
.Tab() >= nTab
)
1030 if (Intersect( nCol1
,nRow1
,nCol2
,nRow2
,
1031 aRef
.aStart
.Col(),aRef
.aStart
.Row(),
1032 aRef
.aEnd
.Col(),aRef
.aEnd
.Row() ))
1034 if ( bDelete
) // arrows, that are starting here
1036 if (aRef
.aStart
!= aRef
.aEnd
)
1038 DeleteBox( aRef
.aStart
.Col(), aRef
.aStart
.Row(),
1039 aRef
.aEnd
.Col(), aRef
.aEnd
.Row() );
1041 DeleteArrowsAt( aRef
.aStart
.Col(), aRef
.aStart
.Row(), false );
1043 else if ( !bRunning
&&
1044 HasArrow( aRef
.aStart
,
1045 aCellIter
.GetPos().Col(),aCellIter
.GetPos().Row(),aCellIter
.GetPos().Tab() ) )
1047 sal_uInt16 nTemp
= FindSuccLevel( aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
1048 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
1049 nLevel
+1, nDeleteLevel
);
1050 if (nTemp
> nResult
)
1057 pFCell
->SetRunning(bRunning
);
1063 bool ScDetectiveFunc::ShowPred( SCCOL nCol
, SCROW nRow
)
1065 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1069 ScDetectiveData
aData( pModel
);
1071 sal_uInt16 nMaxLevel
= 0;
1072 sal_uInt16 nResult
= DET_INS_CONTINUE
;
1073 while (nResult
== DET_INS_CONTINUE
&& nMaxLevel
< 1000)
1075 aData
.SetMaxLevel( nMaxLevel
);
1076 nResult
= InsertPredLevel( nCol
, nRow
, aData
, 0 );
1080 return ( nResult
== DET_INS_INSERTED
);
1083 bool ScDetectiveFunc::ShowSucc( SCCOL nCol
, SCROW nRow
)
1085 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1089 ScDetectiveData
aData( pModel
);
1091 sal_uInt16 nMaxLevel
= 0;
1092 sal_uInt16 nResult
= DET_INS_CONTINUE
;
1093 while (nResult
== DET_INS_CONTINUE
&& nMaxLevel
< 1000)
1095 aData
.SetMaxLevel( nMaxLevel
);
1096 nResult
= InsertSuccLevel( nCol
, nRow
, nCol
, nRow
, aData
, 0 );
1100 return ( nResult
== DET_INS_INSERTED
);
1103 bool ScDetectiveFunc::ShowError( SCCOL nCol
, SCROW nRow
)
1105 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1109 ScRange
aRange( nCol
, nRow
, nTab
);
1111 if ( !HasError( aRange
,aErrPos
) )
1114 ScDetectiveData
aData( pModel
);
1116 aData
.SetMaxLevel( 1000 );
1117 sal_uInt16 nResult
= InsertErrorLevel( nCol
, nRow
, aData
, 0 );
1119 return ( nResult
== DET_INS_INSERTED
);
1122 bool ScDetectiveFunc::DeleteSucc( SCCOL nCol
, SCROW nRow
)
1124 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1128 sal_uInt16 nLevelCount
= FindSuccLevel( nCol
, nRow
, nCol
, nRow
, 0, 0 );
1130 FindSuccLevel( nCol
, nRow
, nCol
, nRow
, 0, nLevelCount
); // delete
1132 return ( nLevelCount
!= 0 );
1135 bool ScDetectiveFunc::DeletePred( SCCOL nCol
, SCROW nRow
)
1137 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1141 sal_uInt16 nLevelCount
= FindPredLevel( nCol
, nRow
, 0, 0 );
1143 FindPredLevel( nCol
, nRow
, 0, nLevelCount
); // delete
1145 return ( nLevelCount
!= 0 );
1148 bool ScDetectiveFunc::DeleteCirclesAt( SCCOL nCol
, SCROW nRow
)
1150 tools::Rectangle aRect
= ScDrawLayer::GetCellRect(rDoc
, ScAddress(nCol
, nRow
, nTab
), true);
1151 aRect
.AdjustLeft(-250);
1152 aRect
.AdjustRight(250);
1153 aRect
.AdjustTop(-70);
1154 aRect
.AdjustBottom(70);
1156 Point aStartCorner
= aRect
.TopLeft();
1157 Point aEndCorner
= aRect
.BottomRight();
1159 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1163 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1164 OSL_ENSURE(pPage
, "Page ?");
1166 pPage
->RecalcObjOrdNums();
1168 const size_t nObjCount
= pPage
->GetObjCount();
1169 size_t nDelCount
= 0;
1172 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
1174 SdrObjListIter
aIter(pPage
, SdrIterMode::Flat
);
1175 SdrObject
* pObject
= aIter
.Next();
1178 if (pObject
->GetLayer() == SC_LAYER_INTERN
)
1179 if (auto pSdrCircObj
= dynamic_cast<const SdrCircObj
*>(pObject
) )
1181 tools::Rectangle aObjRect
= pSdrCircObj
->GetLogicRect();
1182 if (RectIsPoints(aObjRect
, aStartCorner
, aEndCorner
))
1183 ppObj
[nDelCount
++] = pObject
;
1186 pObject
= aIter
.Next();
1189 for (size_t i
= 1; i
<= nDelCount
; ++i
)
1190 pModel
->AddCalcUndo(std::make_unique
<SdrUndoRemoveObj
>(*ppObj
[nDelCount
- i
]));
1192 for (size_t i
= 1; i
<= nDelCount
; ++i
)
1193 pPage
->RemoveObject(ppObj
[nDelCount
- i
]->GetOrdNum());
1200 return (nDelCount
!= 0);
1203 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat
)
1205 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1209 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1210 OSL_ENSURE(pPage
,"Page ?");
1212 pPage
->RecalcObjOrdNums();
1214 size_t nDelCount
= 0;
1215 const size_t nObjCount
= pPage
->GetObjCount();
1218 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
1220 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1221 SdrObject
* pObject
= aIter
.Next();
1224 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
1226 bool bDoThis
= true;
1227 bool bCircle
= ( dynamic_cast<const SdrCircObj
*>( pObject
) != nullptr );
1228 bool bCaption
= ScDrawLayer::IsNoteCaption( pObject
);
1229 if ( eWhat
== ScDetectiveDelete::Detective
) // detective, from menu
1230 bDoThis
= !bCaption
; // also circles
1231 else if ( eWhat
== ScDetectiveDelete::Circles
) // circles, if new created
1233 else if ( eWhat
== ScDetectiveDelete::Arrows
) // DetectiveRefresh
1234 bDoThis
= !bCaption
&& !bCircle
; // don't include circles
1240 ppObj
[nDelCount
++] = pObject
;
1243 pObject
= aIter
.Next();
1246 for (size_t i
=1; i
<=nDelCount
; ++i
)
1247 pModel
->AddCalcUndo( std::make_unique
<SdrUndoRemoveObj
>( *ppObj
[nDelCount
-i
] ) );
1249 for (size_t i
=1; i
<=nDelCount
; ++i
)
1250 pPage
->RemoveObject( ppObj
[nDelCount
-i
]->GetOrdNum() );
1257 return ( nDelCount
!= 0 );
1260 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow
)
1263 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1267 bool bDeleted
= DeleteAll( ScDetectiveDelete::Circles
); // just circles
1269 ScDetectiveData
aData( pModel
);
1270 tools::Long nInsCount
= 0;
1272 // search for valid places
1273 ScDocAttrIterator
aAttrIter( rDoc
, nTab
, 0,0,rDoc
.MaxCol(),rDoc
.MaxRow() );
1277 const ScPatternAttr
* pPattern
= aAttrIter
.GetNext( nCol
, nRow1
, nRow2
);
1278 while ( pPattern
&& nInsCount
< SC_DET_MAXCIRCLE
)
1280 sal_uInt32 nIndex
= pPattern
->GetItem(ATTR_VALIDDATA
).GetValue();
1283 const ScValidationData
* pData
= rDoc
.GetValidationEntry( nIndex
);
1286 // pass cells in this area
1288 bool bMarkEmpty
= !pData
->IsIgnoreBlank();
1289 SCROW nNextRow
= nRow1
;
1291 ScCellIterator
aCellIter( rDoc
, ScRange(nCol
, nRow1
, nTab
, nCol
, nRow2
, nTab
) );
1292 for (bool bHas
= aCellIter
.first(); bHas
&& nInsCount
< SC_DET_MAXCIRCLE
; bHas
= aCellIter
.next())
1294 SCROW nCellRow
= aCellIter
.GetPos().Row();
1296 for ( nRow
= nNextRow
; nRow
< nCellRow
&& nInsCount
< SC_DET_MAXCIRCLE
; nRow
++ )
1298 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1299 DrawCircle( nCol
, nRow
, aData
);
1302 ScRefCellValue aCell
= aCellIter
.getRefCellValue();
1303 if (!pData
->IsDataValid(aCell
, aCellIter
.GetPos()))
1305 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1306 DrawCircle( nCol
, nCellRow
, aData
);
1309 nNextRow
= nCellRow
+ 1;
1312 for ( nRow
= nNextRow
; nRow
<= nRow2
&& nInsCount
< SC_DET_MAXCIRCLE
; nRow
++ )
1314 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1315 DrawCircle(nCol
, nRow
, aData
);
1321 pPattern
= aAttrIter
.GetNext( nCol
, nRow1
, nRow2
);
1324 if ( nInsCount
>= SC_DET_MAXCIRCLE
)
1327 return ( bDeleted
|| nInsCount
!= 0 );
1330 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1331 vector
<ScTokenRef
>& rRefTokens
)
1333 ScCellIterator
aIter(rDoc
, ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
));
1334 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
1336 if (aIter
.getType() != CELLTYPE_FORMULA
)
1339 ScFormulaCell
* pFCell
= aIter
.getFormulaCell();
1340 ScDetectiveRefIter
aRefIter(rDoc
, pFCell
);
1341 for (formula::FormulaToken
* p
= aRefIter
.GetNextRefToken(); p
; p
= aRefIter
.GetNextRefToken())
1343 ScTokenRef
pRef(p
->Clone());
1344 ScRefTokenHelper::join(&rDoc
, rRefTokens
, pRef
, aIter
.GetPos());
1349 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1350 vector
<ScTokenRef
>& rRefTokens
)
1352 vector
<ScTokenRef
> aSrcRange
;
1353 aSrcRange
.push_back(
1354 ScRefTokenHelper::createRefToken(rDoc
, ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
)));
1356 ScCellIterator
aIter(rDoc
, ScRange(0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
));
1357 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
1359 if (aIter
.getType() != CELLTYPE_FORMULA
)
1362 ScFormulaCell
* pFCell
= aIter
.getFormulaCell();
1363 ScDetectiveRefIter
aRefIter(rDoc
, pFCell
);
1364 for (formula::FormulaToken
* p
= aRefIter
.GetNextRefToken(); p
; p
= aRefIter
.GetNextRefToken())
1366 const ScAddress
& aPos
= aIter
.GetPos();
1367 ScTokenRef
pRef(p
->Clone());
1368 if (ScRefTokenHelper::intersects(&rDoc
, aSrcRange
, pRef
, aPos
))
1370 // This address is absolute.
1371 pRef
= ScRefTokenHelper::createRefToken(rDoc
, aPos
);
1372 ScRefTokenHelper::join(&rDoc
, rRefTokens
, pRef
, ScAddress());
1378 void ScDetectiveFunc::UpdateAllComments( ScDocument
& rDoc
)
1380 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1384 auto pStyleSheet
= rDoc
.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE
), SfxStyleFamily::Frame
);
1388 ScStyleSaveData aOldData
, aNewData
;
1389 aOldData
.InitFromStyle(pStyleSheet
);
1391 auto& rSet
= pStyleSheet
->GetItemSet();
1392 rSet
.Put(XFillStyleItem(drawing::FillStyle_SOLID
));
1393 rSet
.Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
1394 static_cast<SfxStyleSheet
*>(pStyleSheet
)->Broadcast(SfxHint(SfxHintId::DataChanged
));
1396 aNewData
.InitFromStyle(pStyleSheet
);
1398 ScDocShell
* pDocSh
= rDoc
.GetDocumentShell();
1399 pDocSh
->GetUndoManager()->AddUndoAction(
1400 std::make_unique
<ScUndoModifyStyle
>(pDocSh
, pStyleSheet
->GetFamily(), aOldData
, aNewData
));
1403 void ScDetectiveFunc::UpdateAllArrowColors()
1405 // no undo actions necessary
1407 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1411 for( SCTAB nObjTab
= 0, nTabCount
= rDoc
.GetTableCount(); nObjTab
< nTabCount
; ++nObjTab
)
1413 SdrPage
* pPage
= pModel
->GetPage( static_cast< sal_uInt16
>( nObjTab
) );
1414 OSL_ENSURE( pPage
, "Page ?" );
1417 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1418 for( SdrObject
* pObject
= aIter
.Next(); pObject
; pObject
= aIter
.Next() )
1420 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
1422 bool bArrow
= false;
1423 bool bError
= false;
1428 ScDetectiveObjType eType
= GetDetectiveObjectType( pObject
, nObjTab
, aPos
, aSource
, bDummy
);
1429 if ( eType
== SC_DETOBJ_ARROW
|| eType
== SC_DETOBJ_TOOTHERTAB
)
1431 // source is valid, determine error flag from source range
1434 if ( HasError( aSource
, aErrPos
) )
1439 else if ( eType
== SC_DETOBJ_FROMOTHERTAB
)
1441 // source range is no longer known, take error flag from formula itself
1442 // (this means, if the formula has an error, all references to other tables
1446 if ( HasError( ScRange( aPos
), aErrPos
) )
1451 else if ( eType
== SC_DETOBJ_CIRCLE
)
1453 // circles (error marks) are always red
1457 else if ( eType
== SC_DETOBJ_NONE
)
1459 // frame for area reference has no ObjType, always gets arrow color
1461 if ( dynamic_cast<const SdrRectObj
*>( pObject
) != nullptr && dynamic_cast<const SdrCaptionObj
*>( pObject
) == nullptr )
1467 if ( bArrow
|| bError
)
1469 Color nColor
= ( bError
? GetErrorColor() : GetArrowColor() );
1470 pObject
->SetMergedItem( XLineColorItem( OUString(), nColor
) );
1473 pObject
->ActionChanged();
1481 void ScDetectiveFunc::FindFrameForObject( const SdrObject
* pObject
, ScRange
& rRange
)
1483 // find the rectangle for an arrow (always the object directly before the arrow)
1484 // rRange must be initialized to the source cell of the arrow (start of area)
1486 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1487 if (!pModel
) return;
1489 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1490 OSL_ENSURE(pPage
,"Page ?");
1493 // test if the object is a direct page member
1494 if( !(pObject
&& pObject
->getSdrPageFromSdrObject() && (pObject
->getSdrPageFromSdrObject() == pObject
->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
1497 // Is there a previous object?
1498 const size_t nOrdNum
= pObject
->GetOrdNum();
1503 SdrObject
* pPrevObj
= pPage
->GetObj(nOrdNum
- 1);
1505 if ( pPrevObj
&& pPrevObj
->GetLayer() == SC_LAYER_INTERN
&& dynamic_cast<const SdrRectObj
*>( pPrevObj
) != nullptr )
1507 ScDrawObjData
* pPrevData
= ScDrawLayer::GetObjDataTab( pPrevObj
, rRange
.aStart
.Tab() );
1508 if ( pPrevData
&& pPrevData
->maStart
.IsValid() && pPrevData
->maEnd
.IsValid() && (pPrevData
->maStart
== rRange
.aStart
) )
1510 rRange
.aEnd
= pPrevData
->maEnd
;
1516 ScDetectiveObjType
ScDetectiveFunc::GetDetectiveObjectType( SdrObject
* pObject
, SCTAB nObjTab
,
1517 ScAddress
& rPosition
, ScRange
& rSource
, bool& rRedLine
)
1520 ScDetectiveObjType eType
= SC_DETOBJ_NONE
;
1522 if ( pObject
&& pObject
->GetLayer() == SC_LAYER_INTERN
)
1524 if ( ScDrawObjData
* pData
= ScDrawLayer::GetObjDataTab( pObject
, nObjTab
) )
1526 bool bValidStart
= pData
->maStart
.IsValid();
1527 bool bValidEnd
= pData
->maEnd
.IsValid();
1529 if ( pObject
->IsPolyObj() && pObject
->GetPointCount() == 2 )
1531 // line object -> arrow
1534 eType
= bValidEnd
? SC_DETOBJ_ARROW
: SC_DETOBJ_TOOTHERTAB
;
1535 else if ( bValidEnd
)
1536 eType
= SC_DETOBJ_FROMOTHERTAB
;
1539 rSource
= pData
->maStart
;
1541 rPosition
= pData
->maEnd
;
1543 if ( bValidStart
&& lcl_HasThickLine( *pObject
) )
1545 // thick line -> look for frame before this object
1547 FindFrameForObject( pObject
, rSource
); // modifies rSource
1550 Color nObjColor
= pObject
->GetMergedItem(XATTR_LINECOLOR
).GetColorValue();
1551 if ( nObjColor
== GetErrorColor() && nObjColor
!= GetArrowColor() )
1554 else if (dynamic_cast<const SdrCircObj
*>(pObject
) != nullptr)
1558 // cell position is returned in rPosition
1559 rPosition
= pData
->maStart
;
1560 eType
= SC_DETOBJ_CIRCLE
;
1563 else if (dynamic_cast<const SdrRectObj
*>(pObject
) != nullptr)
1567 // cell position is returned in rPosition
1568 rPosition
= pData
->maStart
;
1569 eType
= SC_DETOBJ_RECTANGLE
;
1578 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType
,
1579 const ScAddress
& rPosition
, const ScRange
& rSource
,
1582 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1583 if (!pModel
) return;
1584 ScDetectiveData
aData( pModel
);
1588 case SC_DETOBJ_ARROW
:
1589 case SC_DETOBJ_FROMOTHERTAB
:
1590 InsertArrow( rPosition
.Col(), rPosition
.Row(),
1591 rSource
.aStart
.Col(), rSource
.aStart
.Row(),
1592 rSource
.aEnd
.Col(), rSource
.aEnd
.Row(),
1593 (eType
== SC_DETOBJ_FROMOTHERTAB
), bRedLine
, aData
);
1595 case SC_DETOBJ_TOOTHERTAB
:
1596 InsertToOtherTab( rSource
.aStart
.Col(), rSource
.aStart
.Row(),
1597 rSource
.aEnd
.Col(), rSource
.aEnd
.Row(),
1600 case SC_DETOBJ_CIRCLE
:
1601 DrawCircle( rPosition
.Col(), rPosition
.Row(), aData
);
1605 // added to avoid warnings
1610 Color
ScDetectiveFunc::GetArrowColor()
1612 if (!bColorsInitialized
)
1617 Color
ScDetectiveFunc::GetErrorColor()
1619 if (!bColorsInitialized
)
1624 Color
ScDetectiveFunc::GetCommentColor()
1626 if (!bColorsInitialized
)
1628 return nCommentColor
;
1631 void ScDetectiveFunc::InitializeColors()
1633 // may be called several times to update colors from configuration
1635 const svtools::ColorConfig
& rColorCfg
= SC_MOD()->GetColorConfig();
1636 nArrowColor
= rColorCfg
.GetColorValue(svtools::CALCDETECTIVE
).nColor
;
1637 nErrorColor
= rColorCfg
.GetColorValue(svtools::CALCDETECTIVEERROR
).nColor
;
1638 nCommentColor
= rColorCfg
.GetColorValue(svtools::CALCNOTESBACKGROUND
).nColor
;
1640 bColorsInitialized
= true;
1643 bool ScDetectiveFunc::IsColorsInitialized()
1645 return bColorsInitialized
;
1648 void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString
&rDisplay
)
1650 rDisplay
+= "\n--------\n";
1653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */