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
->SetDecorative(true);
377 pBox
->SetLayer( SC_LAYER_INTERN
);
378 pPage
->InsertObject( pBox
.get() );
379 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pBox
) );
381 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pBox
.get(), true );
382 pData
->maStart
.Set( nRefStartCol
, nRefStartRow
, nTab
);
383 pData
->maEnd
.Set( nRefEndCol
, nRefEndRow
, nTab
);
386 Point aStartPos
= GetDrawPos( nRefStartCol
, nRefStartRow
, DrawPosMode::DetectiveArrow
);
387 Point aEndPos
= GetDrawPos( nCol
, nRow
, DrawPosMode::DetectiveArrow
);
391 bool bNegativePage
= rDoc
.IsNegativePage( nTab
);
392 tools::Long nPageSign
= bNegativePage
? -1 : 1;
394 aStartPos
= Point( aEndPos
.X() - 1000 * nPageSign
, aEndPos
.Y() - 1000 );
395 if (aStartPos
.X() * nPageSign
< 0)
396 aStartPos
.AdjustX(2000 * nPageSign
);
397 if (aStartPos
.Y() < 0)
398 aStartPos
.AdjustY(2000 );
401 SfxItemSet
& rAttrSet
= bFromOtherTab
? rData
.GetFromTabSet() : rData
.GetArrowSet();
403 if (bArea
&& !bFromOtherTab
)
404 rAttrSet
.Put( XLineWidthItem( 50 ) ); // range
406 rAttrSet
.Put( XLineWidthItem( 0 ) ); // single reference
408 Color nColor
= ( bRed
? GetErrorColor() : GetArrowColor() );
409 rAttrSet
.Put( XLineColorItem( OUString(), nColor
) );
411 basegfx::B2DPolygon aTempPoly
;
412 aTempPoly
.append(basegfx::B2DPoint(aStartPos
.X(), aStartPos
.Y()));
413 aTempPoly
.append(basegfx::B2DPoint(aEndPos
.X(), aEndPos
.Y()));
414 rtl::Reference
<SdrPathObj
> pArrow
= new SdrPathObj(
417 basegfx::B2DPolyPolygon(aTempPoly
));
418 pArrow
->NbcSetStyleSheet(nullptr, true);
419 pArrow
->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos
,aEndPos
)); //TODO: needed ???
420 pArrow
->SetMergedItemSetAndBroadcast(rAttrSet
);
422 pArrow
->SetDecorative(true);
423 pArrow
->SetLayer( SC_LAYER_INTERN
);
424 pPage
->InsertObject( pArrow
.get() );
425 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pArrow
) );
427 ScDrawObjData
* pData
= ScDrawLayer::GetObjData(pArrow
.get(), true);
429 pData
->maStart
.SetInvalid();
431 pData
->maStart
.Set( nRefStartCol
, nRefStartRow
, nTab
);
433 pData
->maEnd
.Set( nCol
, nRow
, nTab
);
434 pData
->meType
= ScDrawObjData::DetectiveArrow
;
439 void ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol
, SCROW nStartRow
,
440 SCCOL nEndCol
, SCROW nEndRow
, bool bRed
,
441 ScDetectiveData
& rData
)
443 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
444 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
446 bool bArea
= ( nStartCol
!= nEndCol
|| nStartRow
!= nEndRow
);
449 tools::Rectangle aRect
= GetDrawRect( nStartCol
, nStartRow
, nEndCol
, nEndRow
);
450 rtl::Reference
<SdrRectObj
> pBox
= new SdrRectObj(
454 pBox
->NbcSetStyleSheet(nullptr, true);
455 pBox
->SetMergedItemSetAndBroadcast(rData
.GetBoxSet());
457 pBox
->SetLayer( SC_LAYER_INTERN
);
458 pPage
->InsertObject( pBox
.get() );
459 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pBox
) );
461 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pBox
.get(), true );
462 pData
->maStart
.Set( nStartCol
, nStartRow
, nTab
);
463 pData
->maEnd
.Set( nEndCol
, nEndRow
, nTab
);
466 bool bNegativePage
= rDoc
.IsNegativePage( nTab
);
467 tools::Long nPageSign
= bNegativePage
? -1 : 1;
469 Point aStartPos
= GetDrawPos( nStartCol
, nStartRow
, DrawPosMode::DetectiveArrow
);
470 Point
aEndPos( aStartPos
.X() + 1000 * nPageSign
, aStartPos
.Y() - 1000 );
472 aEndPos
.AdjustY(2000 );
474 SfxItemSet
& rAttrSet
= rData
.GetToTabSet();
476 rAttrSet
.Put( XLineWidthItem( 50 ) ); // range
478 rAttrSet
.Put( XLineWidthItem( 0 ) ); // single reference
480 Color nColor
= ( bRed
? GetErrorColor() : GetArrowColor() );
481 rAttrSet
.Put( XLineColorItem( OUString(), nColor
) );
483 basegfx::B2DPolygon aTempPoly
;
484 aTempPoly
.append(basegfx::B2DPoint(aStartPos
.X(), aStartPos
.Y()));
485 aTempPoly
.append(basegfx::B2DPoint(aEndPos
.X(), aEndPos
.Y()));
486 rtl::Reference
<SdrPathObj
> pArrow
= new SdrPathObj(
489 basegfx::B2DPolyPolygon(aTempPoly
));
490 pArrow
->NbcSetStyleSheet(nullptr, true);
491 pArrow
->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos
,aEndPos
)); //TODO: needed ???
493 pArrow
->SetMergedItemSetAndBroadcast(rAttrSet
);
495 pArrow
->SetLayer( SC_LAYER_INTERN
);
496 pPage
->InsertObject( pArrow
.get() );
497 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pArrow
) );
499 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pArrow
.get(), true );
500 pData
->maStart
.Set( nStartCol
, nStartRow
, nTab
);
501 pData
->maEnd
.SetInvalid();
506 // DrawEntry: formula from this spreadsheet,
507 // reference on this or other
508 // DrawAlienEntry: formula from other spreadsheet,
511 // return FALSE: there was already an arrow
513 bool ScDetectiveFunc::DrawEntry( SCCOL nCol
, SCROW nRow
,
515 ScDetectiveData
& rData
)
517 if ( HasArrow( rRef
.aStart
, nCol
, nRow
, nTab
) )
521 bool bError
= HasError( rRef
, aErrorPos
);
522 bool bAlien
= ( rRef
.aEnd
.Tab() < nTab
|| rRef
.aStart
.Tab() > nTab
);
524 InsertArrow( nCol
, nRow
,
525 rRef
.aStart
.Col(), rRef
.aStart
.Row(),
526 rRef
.aEnd
.Col(), rRef
.aEnd
.Row(),
527 bAlien
, bError
, rData
);
531 bool ScDetectiveFunc::DrawAlienEntry( const ScRange
& rRef
,
532 ScDetectiveData
& rData
)
534 if ( HasArrow( rRef
.aStart
, 0, 0, nTab
+1 ) )
538 bool bError
= HasError( rRef
, aErrorPos
);
540 InsertToOtherTab( rRef
.aStart
.Col(), rRef
.aStart
.Row(),
541 rRef
.aEnd
.Col(), rRef
.aEnd
.Row(),
546 void ScDetectiveFunc::DrawCircle( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
)
548 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
549 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
551 tools::Rectangle aRect
= ScDrawLayer::GetCellRect(rDoc
, ScAddress(nCol
, nRow
, nTab
), true);
552 aRect
.AdjustLeft( -250 );
553 aRect
.AdjustRight(250 );
554 aRect
.AdjustTop( -70 );
555 aRect
.AdjustBottom(70 );
557 rtl::Reference
<SdrCircObj
> pCircle
= new SdrCircObj(
561 SfxItemSet
& rAttrSet
= rData
.GetCircleSet();
563 pCircle
->NbcSetStyleSheet(nullptr, true);
564 pCircle
->SetMergedItemSetAndBroadcast(rAttrSet
);
566 pCircle
->SetDecorative(true);
567 pCircle
->SetLayer( SC_LAYER_INTERN
);
568 pPage
->InsertObject( pCircle
.get() );
569 pModel
->AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pCircle
) );
571 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pCircle
.get(), true );
572 pData
->maStart
.Set( nCol
, nRow
, nTab
);
573 pData
->maEnd
.SetInvalid();
574 pData
->meType
= ScDrawObjData::ValidationCircle
;
579 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol
, SCROW nRow
, bool bDestPnt
)
581 tools::Rectangle aRect
= GetDrawRect( nCol
, nRow
);
583 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
584 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
585 assert(pPage
&& "Page ?");
587 pPage
->RecalcObjOrdNums();
589 const size_t nObjCount
= pPage
->GetObjCount();
593 size_t nDelCount
= 0;
594 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
596 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
597 SdrObject
* pObject
= aIter
.Next();
600 if ( pObject
->GetLayer()==SC_LAYER_INTERN
&&
601 pObject
->IsPolyObj() && pObject
->GetPointCount()==2 )
603 if (aRect
.Contains(pObject
->GetPoint(bDestPnt
? 1 : 0))) // start/destinationpoint
604 ppObj
[nDelCount
++] = pObject
;
607 pObject
= aIter
.Next();
610 const bool bRecording
= pModel
->IsRecording();
614 for (size_t i
=1; i
<=nDelCount
; ++i
)
615 pModel
->AddCalcUndo(std::make_unique
<SdrUndoDelObj
>(*ppObj
[nDelCount
-i
]));
618 for (size_t i
=1; i
<=nDelCount
; ++i
)
620 // remove the object from the drawing page, delete if undo is disabled
621 pPage
->RemoveObject(ppObj
[nDelCount
-i
]->GetOrdNum());
629 // delete box around reference
631 #define SC_DET_TOLERANCE 50
633 static bool RectIsPoints( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
635 return rRect
.Left() >= rStart
.X() - SC_DET_TOLERANCE
636 && rRect
.Left() <= rStart
.X() + SC_DET_TOLERANCE
637 && rRect
.Right() >= rEnd
.X() - SC_DET_TOLERANCE
638 && rRect
.Right() <= rEnd
.X() + SC_DET_TOLERANCE
639 && rRect
.Top() >= rStart
.Y() - SC_DET_TOLERANCE
640 && rRect
.Top() <= rStart
.Y() + SC_DET_TOLERANCE
641 && rRect
.Bottom() >= rEnd
.Y() - SC_DET_TOLERANCE
642 && rRect
.Bottom() <= rEnd
.Y() + SC_DET_TOLERANCE
;
645 #undef SC_DET_TOLERANCE
647 void ScDetectiveFunc::DeleteBox( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
)
649 tools::Rectangle aCornerRect
= GetDrawRect( nCol1
, nRow1
, nCol2
, nRow2
);
650 Point aStartCorner
= aCornerRect
.TopLeft();
651 Point aEndCorner
= aCornerRect
.BottomRight();
652 tools::Rectangle aObjRect
;
654 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
655 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
656 assert(pPage
&& "Page ?");
658 pPage
->RecalcObjOrdNums();
660 const size_t nObjCount
= pPage
->GetObjCount();
664 size_t nDelCount
= 0;
665 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
667 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
668 SdrObject
* pObject
= aIter
.Next();
671 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
672 if ( auto pSdrRectObj
= dynamic_cast< const SdrRectObj
* >(pObject
) )
674 aObjRect
= pSdrRectObj
->GetLogicRect();
675 aObjRect
.Normalize();
676 if ( RectIsPoints( aObjRect
, aStartCorner
, aEndCorner
) )
677 ppObj
[nDelCount
++] = pObject
;
680 pObject
= aIter
.Next();
683 for (size_t i
=1; i
<=nDelCount
; ++i
)
684 pModel
->AddCalcUndo( std::make_unique
<SdrUndoRemoveObj
>( *ppObj
[nDelCount
-i
] ) );
686 for (size_t i
=1; i
<=nDelCount
; ++i
)
687 pPage
->RemoveObject( ppObj
[nDelCount
-i
]->GetOrdNum() );
694 sal_uInt16
ScDetectiveFunc::InsertPredLevelArea( const ScRange
& rRef
,
695 ScDetectiveData
& rData
, sal_uInt16 nLevel
)
697 sal_uInt16 nResult
= DET_INS_EMPTY
;
699 ScCellIterator
aIter( rDoc
, rRef
);
700 for (bool bHasCell
= aIter
.first(); bHasCell
; bHasCell
= aIter
.next())
702 if (aIter
.getType() != CELLTYPE_FORMULA
)
705 const ScAddress
& rPos
= aIter
.GetPos();
706 switch (InsertPredLevel(rPos
.Col(), rPos
.Row(), rData
, nLevel
))
708 case DET_INS_INSERTED
:
709 nResult
= DET_INS_INSERTED
;
711 case DET_INS_CONTINUE
:
712 if (nResult
!= DET_INS_INSERTED
)
713 nResult
= DET_INS_CONTINUE
;
715 case DET_INS_CIRCULAR
:
716 if (nResult
== DET_INS_EMPTY
)
717 nResult
= DET_INS_CIRCULAR
;
727 sal_uInt16
ScDetectiveFunc::InsertPredLevel( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
,
730 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
731 if (aCell
.getType() != CELLTYPE_FORMULA
)
732 return DET_INS_EMPTY
;
734 ScFormulaCell
* pFCell
= aCell
.getFormula();
735 if (pFCell
->IsRunning())
736 return DET_INS_CIRCULAR
;
738 if (pFCell
->GetDirty())
739 pFCell
->Interpret(); // can't be called after SetRunning
740 pFCell
->SetRunning(true);
742 sal_uInt16 nResult
= DET_INS_EMPTY
;
744 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
746 while ( aIter
.GetNextRef( aRef
) )
748 if (DrawEntry( nCol
, nRow
, aRef
, rData
))
750 nResult
= DET_INS_INSERTED
; // insert new arrow
756 if ( nLevel
< rData
.GetMaxLevel() )
758 sal_uInt16 nSubResult
;
759 bool bArea
= (aRef
.aStart
!= aRef
.aEnd
);
761 nSubResult
= InsertPredLevelArea( aRef
, rData
, nLevel
+1 );
763 nSubResult
= InsertPredLevel( aRef
.aStart
.Col(), aRef
.aStart
.Row(),
768 case DET_INS_INSERTED
:
769 nResult
= DET_INS_INSERTED
;
771 case DET_INS_CONTINUE
:
772 if (nResult
!= DET_INS_INSERTED
)
773 nResult
= DET_INS_CONTINUE
;
775 case DET_INS_CIRCULAR
:
776 if (nResult
== DET_INS_EMPTY
)
777 nResult
= DET_INS_CIRCULAR
;
779 // DET_INS_EMPTY: no change
782 else // nMaxLevel reached
783 if (nResult
!= DET_INS_INSERTED
)
784 nResult
= DET_INS_CONTINUE
;
788 pFCell
->SetRunning(false);
793 sal_uInt16
ScDetectiveFunc::FindPredLevelArea( const ScRange
& rRef
,
794 sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
796 sal_uInt16 nResult
= nLevel
;
798 ScCellIterator
aCellIter( rDoc
, rRef
);
799 for (bool bHasCell
= aCellIter
.first(); bHasCell
; bHasCell
= aCellIter
.next())
801 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
804 sal_uInt16 nTemp
= FindPredLevel(aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(), nLevel
, nDeleteLevel
);
812 // nDeleteLevel != 0 -> delete
814 sal_uInt16
ScDetectiveFunc::FindPredLevel( SCCOL nCol
, SCROW nRow
, sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
816 OSL_ENSURE( nLevel
<1000, "Level" );
818 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
819 if (aCell
.getType() != CELLTYPE_FORMULA
)
822 ScFormulaCell
* pFCell
= aCell
.getFormula();
823 if (pFCell
->IsRunning())
826 if (pFCell
->GetDirty())
827 pFCell
->Interpret(); // can't be called after SetRunning
828 pFCell
->SetRunning(true);
830 sal_uInt16 nResult
= nLevel
;
831 bool bDelete
= ( nDeleteLevel
&& nLevel
== nDeleteLevel
-1 );
835 DeleteArrowsAt( nCol
, nRow
, true ); // arrows, that are pointing here
838 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
840 while ( aIter
.GetNextRef( aRef
) )
842 bool bArea
= ( aRef
.aStart
!= aRef
.aEnd
);
844 if ( bDelete
) // delete frame ?
848 DeleteBox( aRef
.aStart
.Col(), aRef
.aStart
.Row(), aRef
.aEnd
.Col(), aRef
.aEnd
.Row() );
851 else // continue searching
853 if ( HasArrow( aRef
.aStart
, nCol
,nRow
,nTab
) )
857 nTemp
= FindPredLevelArea( aRef
, nLevel
+1, nDeleteLevel
);
859 nTemp
= FindPredLevel( aRef
.aStart
.Col(),aRef
.aStart
.Row(),
860 nLevel
+1, nDeleteLevel
);
867 pFCell
->SetRunning(false);
872 sal_uInt16
ScDetectiveFunc::InsertErrorLevel( SCCOL nCol
, SCROW nRow
, ScDetectiveData
& rData
,
875 ScRefCellValue
aCell(rDoc
, ScAddress(nCol
, nRow
, nTab
));
876 if (aCell
.getType() != CELLTYPE_FORMULA
)
877 return DET_INS_EMPTY
;
879 ScFormulaCell
* pFCell
= aCell
.getFormula();
880 if (pFCell
->IsRunning())
881 return DET_INS_CIRCULAR
;
883 if (pFCell
->GetDirty())
884 pFCell
->Interpret(); // can't be called after SetRunning
885 pFCell
->SetRunning(true);
887 sal_uInt16 nResult
= DET_INS_EMPTY
;
889 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
892 bool bHasError
= false;
893 while ( aIter
.GetNextRef( aRef
) )
895 if (HasError( aRef
, aErrorPos
))
898 if (DrawEntry( nCol
, nRow
, ScRange( aErrorPos
), rData
))
899 nResult
= DET_INS_INSERTED
;
901 if ( nLevel
< rData
.GetMaxLevel() ) // hits most of the time
903 if (InsertErrorLevel( aErrorPos
.Col(), aErrorPos
.Row(),
904 rData
, nLevel
+1 ) == DET_INS_INSERTED
)
905 nResult
= DET_INS_INSERTED
;
910 pFCell
->SetRunning(false);
914 if (InsertPredLevel( nCol
, nRow
, rData
, rData
.GetMaxLevel() ) == DET_INS_INSERTED
)
915 nResult
= DET_INS_INSERTED
;
920 sal_uInt16
ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
921 ScDetectiveData
& rData
, sal_uInt16 nLevel
)
923 // over the entire document.
925 sal_uInt16 nResult
= DET_INS_EMPTY
;
926 ScCellIterator
aCellIter(rDoc
, ScRange(0,0,0,rDoc
.MaxCol(),rDoc
.MaxRow(),MAXTAB
)); // all sheets
927 for (bool bHas
= aCellIter
.first(); bHas
; bHas
= aCellIter
.next())
929 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
932 ScFormulaCell
* pFCell
= aCellIter
.getFormulaCell();
933 bool bRunning
= pFCell
->IsRunning();
935 if (pFCell
->GetDirty())
936 pFCell
->Interpret(); // can't be called after SetRunning
937 pFCell
->SetRunning(true);
939 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
941 while ( aIter
.GetNextRef( aRef
) )
943 if (aRef
.aStart
.Tab() <= nTab
&& aRef
.aEnd
.Tab() >= nTab
)
945 if (Intersect( nCol1
,nRow1
,nCol2
,nRow2
,
946 aRef
.aStart
.Col(),aRef
.aStart
.Row(),
947 aRef
.aEnd
.Col(),aRef
.aEnd
.Row() ))
949 bool bAlien
= ( aCellIter
.GetPos().Tab() != nTab
);
952 bDrawRet
= DrawAlienEntry( aRef
, rData
);
954 bDrawRet
= DrawEntry( aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
958 nResult
= DET_INS_INSERTED
; // insert new arrow
964 if (nResult
== DET_INS_EMPTY
)
965 nResult
= DET_INS_CIRCULAR
;
970 if ( nLevel
< rData
.GetMaxLevel() )
972 sal_uInt16 nSubResult
= InsertSuccLevel(
973 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
974 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
978 case DET_INS_INSERTED
:
979 nResult
= DET_INS_INSERTED
;
981 case DET_INS_CONTINUE
:
982 if (nResult
!= DET_INS_INSERTED
)
983 nResult
= DET_INS_CONTINUE
;
985 case DET_INS_CIRCULAR
:
986 if (nResult
== DET_INS_EMPTY
)
987 nResult
= DET_INS_CIRCULAR
;
989 // DET_INS_EMPTY: leave unchanged
992 else // nMaxLevel reached
993 if (nResult
!= DET_INS_INSERTED
)
994 nResult
= DET_INS_CONTINUE
;
1000 pFCell
->SetRunning(bRunning
);
1006 sal_uInt16
ScDetectiveFunc::FindSuccLevel( SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1007 sal_uInt16 nLevel
, sal_uInt16 nDeleteLevel
)
1009 OSL_ENSURE( nLevel
<1000, "Level" );
1011 sal_uInt16 nResult
= nLevel
;
1012 bool bDelete
= ( nDeleteLevel
&& nLevel
== nDeleteLevel
-1 );
1014 ScCellIterator
aCellIter( rDoc
, ScRange(0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
) );
1015 for (bool bHas
= aCellIter
.first(); bHas
; bHas
= aCellIter
.next())
1017 if (aCellIter
.getType() != CELLTYPE_FORMULA
)
1020 ScFormulaCell
* pFCell
= aCellIter
.getFormulaCell();
1021 bool bRunning
= pFCell
->IsRunning();
1023 if (pFCell
->GetDirty())
1024 pFCell
->Interpret(); // can't be called after SetRunning
1025 pFCell
->SetRunning(true);
1027 ScDetectiveRefIter
aIter(rDoc
, pFCell
);
1029 while ( aIter
.GetNextRef( aRef
) )
1031 if (aRef
.aStart
.Tab() <= nTab
&& aRef
.aEnd
.Tab() >= nTab
)
1033 if (Intersect( nCol1
,nRow1
,nCol2
,nRow2
,
1034 aRef
.aStart
.Col(),aRef
.aStart
.Row(),
1035 aRef
.aEnd
.Col(),aRef
.aEnd
.Row() ))
1037 if ( bDelete
) // arrows, that are starting here
1039 if (aRef
.aStart
!= aRef
.aEnd
)
1041 DeleteBox( aRef
.aStart
.Col(), aRef
.aStart
.Row(),
1042 aRef
.aEnd
.Col(), aRef
.aEnd
.Row() );
1044 DeleteArrowsAt( aRef
.aStart
.Col(), aRef
.aStart
.Row(), false );
1046 else if ( !bRunning
&&
1047 HasArrow( aRef
.aStart
,
1048 aCellIter
.GetPos().Col(),aCellIter
.GetPos().Row(),aCellIter
.GetPos().Tab() ) )
1050 sal_uInt16 nTemp
= FindSuccLevel( aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
1051 aCellIter
.GetPos().Col(), aCellIter
.GetPos().Row(),
1052 nLevel
+1, nDeleteLevel
);
1053 if (nTemp
> nResult
)
1060 pFCell
->SetRunning(bRunning
);
1066 bool ScDetectiveFunc::ShowPred( SCCOL nCol
, SCROW nRow
)
1068 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1072 ScDetectiveData
aData( pModel
);
1074 sal_uInt16 nMaxLevel
= 0;
1075 sal_uInt16 nResult
= DET_INS_CONTINUE
;
1076 while (nResult
== DET_INS_CONTINUE
&& nMaxLevel
< 1000)
1078 aData
.SetMaxLevel( nMaxLevel
);
1079 nResult
= InsertPredLevel( nCol
, nRow
, aData
, 0 );
1083 return ( nResult
== DET_INS_INSERTED
);
1086 bool ScDetectiveFunc::ShowSucc( SCCOL nCol
, SCROW nRow
)
1088 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1092 ScDetectiveData
aData( pModel
);
1094 sal_uInt16 nMaxLevel
= 0;
1095 sal_uInt16 nResult
= DET_INS_CONTINUE
;
1096 while (nResult
== DET_INS_CONTINUE
&& nMaxLevel
< 1000)
1098 aData
.SetMaxLevel( nMaxLevel
);
1099 nResult
= InsertSuccLevel( nCol
, nRow
, nCol
, nRow
, aData
, 0 );
1103 return ( nResult
== DET_INS_INSERTED
);
1106 bool ScDetectiveFunc::ShowError( SCCOL nCol
, SCROW nRow
)
1108 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1112 ScRange
aRange( nCol
, nRow
, nTab
);
1114 if ( !HasError( aRange
,aErrPos
) )
1117 ScDetectiveData
aData( pModel
);
1119 aData
.SetMaxLevel( 1000 );
1120 sal_uInt16 nResult
= InsertErrorLevel( nCol
, nRow
, aData
, 0 );
1122 return ( nResult
== DET_INS_INSERTED
);
1125 bool ScDetectiveFunc::DeleteSucc( SCCOL nCol
, SCROW nRow
)
1127 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1131 sal_uInt16 nLevelCount
= FindSuccLevel( nCol
, nRow
, nCol
, nRow
, 0, 0 );
1133 FindSuccLevel( nCol
, nRow
, nCol
, nRow
, 0, nLevelCount
); // delete
1135 return ( nLevelCount
!= 0 );
1138 bool ScDetectiveFunc::DeletePred( SCCOL nCol
, SCROW nRow
)
1140 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1144 sal_uInt16 nLevelCount
= FindPredLevel( nCol
, nRow
, 0, 0 );
1146 FindPredLevel( nCol
, nRow
, 0, nLevelCount
); // delete
1148 return ( nLevelCount
!= 0 );
1151 bool ScDetectiveFunc::DeleteCirclesAt( SCCOL nCol
, SCROW nRow
)
1153 tools::Rectangle aRect
= ScDrawLayer::GetCellRect(rDoc
, ScAddress(nCol
, nRow
, nTab
), true);
1154 aRect
.AdjustLeft(-250);
1155 aRect
.AdjustRight(250);
1156 aRect
.AdjustTop(-70);
1157 aRect
.AdjustBottom(70);
1159 Point aStartCorner
= aRect
.TopLeft();
1160 Point aEndCorner
= aRect
.BottomRight();
1162 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1166 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1167 assert(pPage
&& "Page ?");
1169 pPage
->RecalcObjOrdNums();
1171 const size_t nObjCount
= pPage
->GetObjCount();
1172 size_t nDelCount
= 0;
1175 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
1177 SdrObjListIter
aIter(pPage
, SdrIterMode::Flat
);
1178 SdrObject
* pObject
= aIter
.Next();
1181 if (pObject
->GetLayer() == SC_LAYER_INTERN
)
1182 if (auto pSdrCircObj
= dynamic_cast<const SdrCircObj
*>(pObject
) )
1184 tools::Rectangle aObjRect
= pSdrCircObj
->GetLogicRect();
1185 if (RectIsPoints(aObjRect
, aStartCorner
, aEndCorner
))
1186 ppObj
[nDelCount
++] = pObject
;
1189 pObject
= aIter
.Next();
1192 for (size_t i
= 1; i
<= nDelCount
; ++i
)
1193 pModel
->AddCalcUndo(std::make_unique
<SdrUndoRemoveObj
>(*ppObj
[nDelCount
- i
]));
1195 for (size_t i
= 1; i
<= nDelCount
; ++i
)
1196 pPage
->RemoveObject(ppObj
[nDelCount
- i
]->GetOrdNum());
1203 return (nDelCount
!= 0);
1206 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat
)
1208 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1212 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1213 assert(pPage
&& "Page ?");
1215 pPage
->RecalcObjOrdNums();
1217 size_t nDelCount
= 0;
1218 const size_t nObjCount
= pPage
->GetObjCount();
1221 std::unique_ptr
<SdrObject
*[]> ppObj(new SdrObject
*[nObjCount
]);
1223 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1224 SdrObject
* pObject
= aIter
.Next();
1227 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
1229 bool bDoThis
= true;
1230 bool bCircle
= ( dynamic_cast<const SdrCircObj
*>( pObject
) != nullptr );
1231 bool bCaption
= ScDrawLayer::IsNoteCaption( pObject
);
1232 if ( eWhat
== ScDetectiveDelete::Detective
) // detective, from menu
1233 bDoThis
= !bCaption
; // also circles
1234 else if ( eWhat
== ScDetectiveDelete::Circles
) // circles, if new created
1236 else if ( eWhat
== ScDetectiveDelete::Arrows
) // DetectiveRefresh
1237 bDoThis
= !bCaption
&& !bCircle
; // don't include circles
1243 ppObj
[nDelCount
++] = pObject
;
1246 pObject
= aIter
.Next();
1249 for (size_t i
=1; i
<=nDelCount
; ++i
)
1250 pModel
->AddCalcUndo( std::make_unique
<SdrUndoRemoveObj
>( *ppObj
[nDelCount
-i
] ) );
1252 for (size_t i
=1; i
<=nDelCount
; ++i
)
1253 pPage
->RemoveObject( ppObj
[nDelCount
-i
]->GetOrdNum() );
1260 return ( nDelCount
!= 0 );
1263 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow
)
1266 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1270 bool bDeleted
= DeleteAll( ScDetectiveDelete::Circles
); // just circles
1272 ScDetectiveData
aData( pModel
);
1273 tools::Long nInsCount
= 0;
1275 // search for valid places
1276 ScDocAttrIterator
aAttrIter( rDoc
, nTab
, 0,0,rDoc
.MaxCol(),rDoc
.MaxRow() );
1280 const ScPatternAttr
* pPattern
= aAttrIter
.GetNext( nCol
, nRow1
, nRow2
);
1281 while ( pPattern
&& nInsCount
< SC_DET_MAXCIRCLE
)
1283 sal_uInt32 nIndex
= pPattern
->GetItem(ATTR_VALIDDATA
).GetValue();
1286 const ScValidationData
* pData
= rDoc
.GetValidationEntry( nIndex
);
1289 // pass cells in this area
1291 bool bMarkEmpty
= !pData
->IsIgnoreBlank();
1292 SCROW nNextRow
= nRow1
;
1294 ScCellIterator
aCellIter( rDoc
, ScRange(nCol
, nRow1
, nTab
, nCol
, nRow2
, nTab
) );
1295 for (bool bHas
= aCellIter
.first(); bHas
&& nInsCount
< SC_DET_MAXCIRCLE
; bHas
= aCellIter
.next())
1297 SCROW nCellRow
= aCellIter
.GetPos().Row();
1299 for ( nRow
= nNextRow
; nRow
< nCellRow
&& nInsCount
< SC_DET_MAXCIRCLE
; nRow
++ )
1301 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1302 DrawCircle( nCol
, nRow
, aData
);
1305 ScRefCellValue aCell
= aCellIter
.getRefCellValue();
1306 if (!pData
->IsDataValid(aCell
, aCellIter
.GetPos()))
1308 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1309 DrawCircle( nCol
, nCellRow
, aData
);
1312 nNextRow
= nCellRow
+ 1;
1315 for ( nRow
= nNextRow
; nRow
<= nRow2
&& nInsCount
< SC_DET_MAXCIRCLE
; nRow
++ )
1317 if(!pPattern
->GetItem(ATTR_MERGE_FLAG
).IsOverlapped())
1318 DrawCircle(nCol
, nRow
, aData
);
1324 pPattern
= aAttrIter
.GetNext( nCol
, nRow1
, nRow2
);
1327 if ( nInsCount
>= SC_DET_MAXCIRCLE
)
1330 return ( bDeleted
|| nInsCount
!= 0 );
1333 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1334 vector
<ScTokenRef
>& rRefTokens
)
1336 ScCellIterator
aIter(rDoc
, ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
));
1337 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
1339 if (aIter
.getType() != CELLTYPE_FORMULA
)
1342 ScFormulaCell
* pFCell
= aIter
.getFormulaCell();
1343 ScDetectiveRefIter
aRefIter(rDoc
, pFCell
);
1344 for (formula::FormulaToken
* p
= aRefIter
.GetNextRefToken(); p
; p
= aRefIter
.GetNextRefToken())
1346 ScTokenRef
pRef(p
->Clone());
1347 ScRefTokenHelper::join(&rDoc
, rRefTokens
, pRef
, aIter
.GetPos());
1352 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1
, SCROW nRow1
, SCCOL nCol2
, SCROW nRow2
,
1353 vector
<ScTokenRef
>& rRefTokens
)
1355 vector
<ScTokenRef
> aSrcRange
;
1356 aSrcRange
.push_back(
1357 ScRefTokenHelper::createRefToken(rDoc
, ScRange(nCol1
, nRow1
, nTab
, nCol2
, nRow2
, nTab
)));
1359 ScCellIterator
aIter(rDoc
, ScRange(0, 0, nTab
, rDoc
.MaxCol(), rDoc
.MaxRow(), nTab
));
1360 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
1362 if (aIter
.getType() != CELLTYPE_FORMULA
)
1365 ScFormulaCell
* pFCell
= aIter
.getFormulaCell();
1366 ScDetectiveRefIter
aRefIter(rDoc
, pFCell
);
1367 for (formula::FormulaToken
* p
= aRefIter
.GetNextRefToken(); p
; p
= aRefIter
.GetNextRefToken())
1369 const ScAddress
& aPos
= aIter
.GetPos();
1370 ScTokenRef
pRef(p
->Clone());
1371 if (ScRefTokenHelper::intersects(&rDoc
, aSrcRange
, pRef
, aPos
))
1373 // This address is absolute.
1374 pRef
= ScRefTokenHelper::createRefToken(rDoc
, aPos
);
1375 ScRefTokenHelper::join(&rDoc
, rRefTokens
, pRef
, ScAddress());
1381 void ScDetectiveFunc::UpdateAllComments( ScDocument
& rDoc
)
1383 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1387 auto pStyleSheet
= rDoc
.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE
), SfxStyleFamily::Frame
);
1391 ScStyleSaveData aOldData
, aNewData
;
1392 aOldData
.InitFromStyle(pStyleSheet
);
1394 auto& rSet
= pStyleSheet
->GetItemSet();
1395 rSet
.Put(XFillStyleItem(drawing::FillStyle_SOLID
));
1396 rSet
.Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
1397 static_cast<SfxStyleSheet
*>(pStyleSheet
)->Broadcast(SfxHint(SfxHintId::DataChanged
));
1399 aNewData
.InitFromStyle(pStyleSheet
);
1401 ScDocShell
* pDocSh
= rDoc
.GetDocumentShell();
1402 pDocSh
->GetUndoManager()->AddUndoAction(
1403 std::make_unique
<ScUndoModifyStyle
>(pDocSh
, pStyleSheet
->GetFamily(), aOldData
, aNewData
));
1406 void ScDetectiveFunc::UpdateAllArrowColors()
1408 // no undo actions necessary
1410 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1414 for( SCTAB nObjTab
= 0, nTabCount
= rDoc
.GetTableCount(); nObjTab
< nTabCount
; ++nObjTab
)
1416 SdrPage
* pPage
= pModel
->GetPage( static_cast< sal_uInt16
>( nObjTab
) );
1417 OSL_ENSURE( pPage
, "Page ?" );
1420 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1421 for( SdrObject
* pObject
= aIter
.Next(); pObject
; pObject
= aIter
.Next() )
1423 if ( pObject
->GetLayer() == SC_LAYER_INTERN
)
1425 bool bArrow
= false;
1426 bool bError
= false;
1431 ScDetectiveObjType eType
= GetDetectiveObjectType( pObject
, nObjTab
, aPos
, aSource
, bDummy
);
1432 if ( eType
== SC_DETOBJ_ARROW
|| eType
== SC_DETOBJ_TOOTHERTAB
)
1434 // source is valid, determine error flag from source range
1437 if ( HasError( aSource
, aErrPos
) )
1442 else if ( eType
== SC_DETOBJ_FROMOTHERTAB
)
1444 // source range is no longer known, take error flag from formula itself
1445 // (this means, if the formula has an error, all references to other tables
1449 if ( HasError( ScRange( aPos
), aErrPos
) )
1454 else if ( eType
== SC_DETOBJ_CIRCLE
)
1456 // circles (error marks) are always red
1460 else if ( eType
== SC_DETOBJ_NONE
)
1462 // frame for area reference has no ObjType, always gets arrow color
1464 if ( dynamic_cast<const SdrRectObj
*>( pObject
) != nullptr && dynamic_cast<const SdrCaptionObj
*>( pObject
) == nullptr )
1470 if ( bArrow
|| bError
)
1472 Color nColor
= ( bError
? GetErrorColor() : GetArrowColor() );
1473 pObject
->SetMergedItem( XLineColorItem( OUString(), nColor
) );
1476 pObject
->ActionChanged();
1484 void ScDetectiveFunc::FindFrameForObject( const SdrObject
* pObject
, ScRange
& rRange
)
1486 // find the rectangle for an arrow (always the object directly before the arrow)
1487 // rRange must be initialized to the source cell of the arrow (start of area)
1489 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1490 if (!pModel
) return;
1492 SdrPage
* pPage
= pModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1493 OSL_ENSURE(pPage
,"Page ?");
1496 // test if the object is a direct page member
1497 if( !(pObject
&& pObject
->getSdrPageFromSdrObject() && (pObject
->getSdrPageFromSdrObject() == pObject
->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
1500 // Is there a previous object?
1501 const size_t nOrdNum
= pObject
->GetOrdNum();
1506 SdrObject
* pPrevObj
= pPage
->GetObj(nOrdNum
- 1);
1508 if ( pPrevObj
&& pPrevObj
->GetLayer() == SC_LAYER_INTERN
&& dynamic_cast<const SdrRectObj
*>( pPrevObj
) != nullptr )
1510 ScDrawObjData
* pPrevData
= ScDrawLayer::GetObjDataTab( pPrevObj
, rRange
.aStart
.Tab() );
1511 if ( pPrevData
&& pPrevData
->maStart
.IsValid() && pPrevData
->maEnd
.IsValid() && (pPrevData
->maStart
== rRange
.aStart
) )
1513 rRange
.aEnd
= pPrevData
->maEnd
;
1519 ScDetectiveObjType
ScDetectiveFunc::GetDetectiveObjectType( SdrObject
* pObject
, SCTAB nObjTab
,
1520 ScAddress
& rPosition
, ScRange
& rSource
, bool& rRedLine
)
1523 ScDetectiveObjType eType
= SC_DETOBJ_NONE
;
1525 if ( pObject
&& pObject
->GetLayer() == SC_LAYER_INTERN
)
1527 if ( ScDrawObjData
* pData
= ScDrawLayer::GetObjDataTab( pObject
, nObjTab
) )
1529 bool bValidStart
= pData
->maStart
.IsValid();
1530 bool bValidEnd
= pData
->maEnd
.IsValid();
1532 if ( pObject
->IsPolyObj() && pObject
->GetPointCount() == 2 )
1534 // line object -> arrow
1537 eType
= bValidEnd
? SC_DETOBJ_ARROW
: SC_DETOBJ_TOOTHERTAB
;
1538 else if ( bValidEnd
)
1539 eType
= SC_DETOBJ_FROMOTHERTAB
;
1542 rSource
= pData
->maStart
;
1544 rPosition
= pData
->maEnd
;
1546 if ( bValidStart
&& lcl_HasThickLine( *pObject
) )
1548 // thick line -> look for frame before this object
1550 FindFrameForObject( pObject
, rSource
); // modifies rSource
1553 Color nObjColor
= pObject
->GetMergedItem(XATTR_LINECOLOR
).GetColorValue();
1554 if ( nObjColor
== GetErrorColor() && nObjColor
!= GetArrowColor() )
1557 else if (dynamic_cast<const SdrCircObj
*>(pObject
) != nullptr)
1561 // cell position is returned in rPosition
1562 rPosition
= pData
->maStart
;
1563 eType
= SC_DETOBJ_CIRCLE
;
1566 else if (dynamic_cast<const SdrRectObj
*>(pObject
) != nullptr)
1570 // cell position is returned in rPosition
1571 rPosition
= pData
->maStart
;
1572 eType
= SC_DETOBJ_RECTANGLE
;
1581 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType
,
1582 const ScAddress
& rPosition
, const ScRange
& rSource
,
1585 ScDrawLayer
* pModel
= rDoc
.GetDrawLayer();
1586 if (!pModel
) return;
1587 ScDetectiveData
aData( pModel
);
1591 case SC_DETOBJ_ARROW
:
1592 case SC_DETOBJ_FROMOTHERTAB
:
1593 InsertArrow( rPosition
.Col(), rPosition
.Row(),
1594 rSource
.aStart
.Col(), rSource
.aStart
.Row(),
1595 rSource
.aEnd
.Col(), rSource
.aEnd
.Row(),
1596 (eType
== SC_DETOBJ_FROMOTHERTAB
), bRedLine
, aData
);
1598 case SC_DETOBJ_TOOTHERTAB
:
1599 InsertToOtherTab( rSource
.aStart
.Col(), rSource
.aStart
.Row(),
1600 rSource
.aEnd
.Col(), rSource
.aEnd
.Row(),
1603 case SC_DETOBJ_CIRCLE
:
1604 DrawCircle( rPosition
.Col(), rPosition
.Row(), aData
);
1608 // added to avoid warnings
1613 Color
ScDetectiveFunc::GetArrowColor()
1615 if (!bColorsInitialized
)
1620 Color
ScDetectiveFunc::GetErrorColor()
1622 if (!bColorsInitialized
)
1627 Color
ScDetectiveFunc::GetCommentColor()
1629 if (!bColorsInitialized
)
1631 return nCommentColor
;
1634 void ScDetectiveFunc::InitializeColors()
1636 // may be called several times to update colors from configuration
1638 const svtools::ColorConfig
& rColorCfg
= ScModule::get()->GetColorConfig();
1639 nArrowColor
= rColorCfg
.GetColorValue(svtools::CALCDETECTIVE
).nColor
;
1640 nErrorColor
= rColorCfg
.GetColorValue(svtools::CALCDETECTIVEERROR
).nColor
;
1641 nCommentColor
= rColorCfg
.GetColorValue(svtools::CALCNOTESBACKGROUND
).nColor
;
1643 bColorsInitialized
= true;
1646 bool ScDetectiveFunc::IsColorsInitialized()
1648 return bColorsInitialized
;
1651 void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString
&rDisplay
)
1653 rDisplay
+= "\n--------\n";
1656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */