cid#1640468 Dereference after null check
[LibreOffice.git] / sc / source / core / tool / detfunc.cxx
blobe5babdbdbc54c73f994bcbd2e862285a789cd77c
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 <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>
54 #include <attrib.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>
68 #include <scmod.hxx>
69 #include <postit.hxx>
70 #include <reftokenhelper.hxx>
71 #include <formulaiter.hxx>
72 #include <cellvalue.hxx>
74 #include <vector>
75 #include <memory>
77 using ::std::vector;
78 using namespace com::sun::star;
80 namespace {
82 enum DetInsertResult { // return-values for inserting in one level
83 DET_INS_CONTINUE,
84 DET_INS_INSERTED,
85 DET_INS_EMPTY,
86 DET_INS_CIRCULAR };
90 class ScDetectiveData
92 private:
93 SfxItemSet aBoxSet;
94 SfxItemSet aArrowSet;
95 SfxItemSet aToTabSet;
96 SfxItemSet aFromTabSet;
97 SfxItemSet aCircleSet; //TODO: individually ?
98 sal_uInt16 nMaxLevel;
100 public:
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> ),
131 nMaxLevel(0)
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)
205 continue;
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 );
221 Point aPos;
223 switch( eMode )
225 case DrawPosMode::TopLeft:
226 break;
227 case DrawPosMode::BottomRight:
228 ++nCol;
229 ++nRow;
230 break;
231 case DrawPosMode::DetectiveArrow:
232 aPos.AdjustX(rDoc.GetColWidth( nCol, nTab ) / 4 );
233 aPos.AdjustY(rDoc.GetRowHeight( nRow, nTab ) / 2 );
234 break;
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 );
247 return aPos;
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
256 return aRect;
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())
275 return true;
279 return false;
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");
291 return true;
294 tools::Rectangle aStartRect;
295 tools::Rectangle aEndRect;
296 if (!bStartAlien)
297 aStartRect = GetDrawRect( rStart.Col(), rStart.Row() );
298 if (!bEndAlien)
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 ?");
305 bool bFound = false;
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() );
317 bool bObjEndAlien =
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 )
326 bFound = true;
328 pObject = aIter.Next();
331 return bFound;
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() );
343 bool bObjEndAlien =
344 lcl_IsOtherTab( rSet.Get(XATTR_LINEEND).GetLineEndValue() );
346 return !bObjStartAlien && !bObjEndAlien;
349 return false;
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(
370 *pModel,
371 aRect);
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 );
389 if (bFromOtherTab)
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
405 else
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(
415 *pModel,
416 SdrObjKind::Line,
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);
428 if (bFromOtherTab)
429 pData->maStart.SetInvalid();
430 else
431 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
433 pData->maEnd.Set( nCol, nRow, nTab);
434 pData->meType = ScDrawObjData::DetectiveArrow;
436 Modified();
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 );
447 if (bArea)
449 tools::Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
450 rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
451 *pModel,
452 aRect);
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 );
471 if (aEndPos.Y() < 0)
472 aEndPos.AdjustY(2000 );
474 SfxItemSet& rAttrSet = rData.GetToTabSet();
475 if (bArea)
476 rAttrSet.Put( XLineWidthItem( 50 ) ); // range
477 else
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(
487 *pModel,
488 SdrObjKind::Line,
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();
503 Modified();
506 // DrawEntry: formula from this spreadsheet,
507 // reference on this or other
508 // DrawAlienEntry: formula from other spreadsheet,
509 // reference on this
511 // return FALSE: there was already an arrow
513 bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
514 const ScRange& rRef,
515 ScDetectiveData& rData )
517 if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
518 return false;
520 ScAddress aErrorPos;
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 );
528 return true;
531 bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
532 ScDetectiveData& rData )
534 if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
535 return false;
537 ScAddress aErrorPos;
538 bool bError = HasError( rRef, aErrorPos );
540 InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
541 rRef.aEnd.Col(), rRef.aEnd.Row(),
542 bError, rData );
543 return true;
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(
558 *pModel,
559 SdrCircKind::Full,
560 aRect);
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;
576 Modified();
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();
590 if (!nObjCount)
591 return;
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();
598 while (pObject)
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();
612 if (bRecording)
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());
624 ppObj.reset();
626 Modified();
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();
661 if (!nObjCount)
662 return;
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();
669 while (pObject)
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() );
689 ppObj.reset();
691 Modified();
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)
703 continue;
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;
710 break;
711 case DET_INS_CONTINUE:
712 if (nResult != DET_INS_INSERTED)
713 nResult = DET_INS_CONTINUE;
714 break;
715 case DET_INS_CIRCULAR:
716 if (nResult == DET_INS_EMPTY)
717 nResult = DET_INS_CIRCULAR;
718 break;
719 default:
724 return nResult;
727 sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
728 sal_uInt16 nLevel )
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);
745 ScRange aRef;
746 while ( aIter.GetNextRef( aRef ) )
748 if (DrawEntry( nCol, nRow, aRef, rData ))
750 nResult = DET_INS_INSERTED; // insert new arrow
752 else
754 // continue
756 if ( nLevel < rData.GetMaxLevel() )
758 sal_uInt16 nSubResult;
759 bool bArea = (aRef.aStart != aRef.aEnd);
760 if (bArea)
761 nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 );
762 else
763 nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(),
764 rData, nLevel+1 );
766 switch (nSubResult)
768 case DET_INS_INSERTED:
769 nResult = DET_INS_INSERTED;
770 break;
771 case DET_INS_CONTINUE:
772 if (nResult != DET_INS_INSERTED)
773 nResult = DET_INS_CONTINUE;
774 break;
775 case DET_INS_CIRCULAR:
776 if (nResult == DET_INS_EMPTY)
777 nResult = DET_INS_CIRCULAR;
778 break;
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);
790 return nResult;
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)
802 continue;
804 sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
805 if (nTemp > nResult)
806 nResult = nTemp;
809 return nResult;
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)
820 return nLevel;
822 ScFormulaCell* pFCell = aCell.getFormula();
823 if (pFCell->IsRunning())
824 return nLevel;
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 );
833 if ( bDelete )
835 DeleteArrowsAt( nCol, nRow, true ); // arrows, that are pointing here
838 ScDetectiveRefIter aIter(rDoc, pFCell);
839 ScRange aRef;
840 while ( aIter.GetNextRef( aRef) )
842 bool bArea = ( aRef.aStart != aRef.aEnd );
844 if ( bDelete ) // delete frame ?
846 if (bArea)
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 ) )
855 sal_uInt16 nTemp;
856 if (bArea)
857 nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel );
858 else
859 nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(),
860 nLevel+1, nDeleteLevel );
861 if (nTemp > nResult)
862 nResult = nTemp;
867 pFCell->SetRunning(false);
869 return nResult;
872 sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
873 sal_uInt16 nLevel )
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);
890 ScRange aRef;
891 ScAddress aErrorPos;
892 bool bHasError = false;
893 while ( aIter.GetNextRef( aRef ) )
895 if (HasError( aRef, aErrorPos ))
897 bHasError = true;
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);
912 // leaves ?
913 if (!bHasError)
914 if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
915 nResult = DET_INS_INSERTED;
917 return nResult;
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)
930 continue;
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);
940 ScRange aRef;
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 );
950 bool bDrawRet;
951 if (bAlien)
952 bDrawRet = DrawAlienEntry( aRef, rData );
953 else
954 bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
955 aRef, rData );
956 if (bDrawRet)
958 nResult = DET_INS_INSERTED; // insert new arrow
960 else
962 if (bRunning)
964 if (nResult == DET_INS_EMPTY)
965 nResult = DET_INS_CIRCULAR;
967 else
970 if ( nLevel < rData.GetMaxLevel() )
972 sal_uInt16 nSubResult = InsertSuccLevel(
973 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
974 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
975 rData, nLevel+1 );
976 switch (nSubResult)
978 case DET_INS_INSERTED:
979 nResult = DET_INS_INSERTED;
980 break;
981 case DET_INS_CONTINUE:
982 if (nResult != DET_INS_INSERTED)
983 nResult = DET_INS_CONTINUE;
984 break;
985 case DET_INS_CIRCULAR:
986 if (nResult == DET_INS_EMPTY)
987 nResult = DET_INS_CIRCULAR;
988 break;
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);
1003 return nResult;
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)
1018 continue;
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);
1028 ScRange aRef;
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)
1054 nResult = nTemp;
1060 pFCell->SetRunning(bRunning);
1063 return nResult;
1066 bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1068 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1069 if (!pModel)
1070 return false;
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 );
1080 ++nMaxLevel;
1083 return ( nResult == DET_INS_INSERTED );
1086 bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1088 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1089 if (!pModel)
1090 return false;
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 );
1100 ++nMaxLevel;
1103 return ( nResult == DET_INS_INSERTED );
1106 bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1108 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1109 if (!pModel)
1110 return false;
1112 ScRange aRange( nCol, nRow, nTab );
1113 ScAddress aErrPos;
1114 if ( !HasError( aRange,aErrPos ) )
1115 return false;
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();
1128 if (!pModel)
1129 return false;
1131 sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1132 if ( nLevelCount )
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();
1141 if (!pModel)
1142 return false;
1144 sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1145 if ( nLevelCount )
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();
1163 if (!pModel)
1164 return false;
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;
1173 if (nObjCount)
1175 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1177 SdrObjListIter aIter(pPage, SdrIterMode::Flat);
1178 SdrObject* pObject = aIter.Next();
1179 while (pObject)
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());
1198 ppObj.reset();
1200 Modified();
1203 return (nDelCount != 0);
1206 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1208 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1209 if (!pModel)
1210 return false;
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();
1219 if (nObjCount)
1221 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1223 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
1224 SdrObject* pObject = aIter.Next();
1225 while (pObject)
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
1235 bDoThis = bCircle;
1236 else if ( eWhat == ScDetectiveDelete::Arrows ) // DetectiveRefresh
1237 bDoThis = !bCaption && !bCircle; // don't include circles
1238 else
1240 OSL_FAIL("what?");
1242 if ( bDoThis )
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() );
1255 ppObj.reset();
1257 Modified();
1260 return ( nDelCount != 0 );
1263 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
1265 rOverflow = false;
1266 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1267 if (!pModel)
1268 return false;
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() );
1277 SCCOL nCol;
1278 SCROW nRow1;
1279 SCROW nRow2;
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();
1284 if (nIndex)
1286 const ScValidationData* pData = rDoc.GetValidationEntry( nIndex );
1287 if ( pData )
1289 // pass cells in this area
1291 bool bMarkEmpty = !pData->IsIgnoreBlank();
1292 SCROW nNextRow = nRow1;
1293 SCROW nRow;
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();
1298 if ( bMarkEmpty )
1299 for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1301 if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1302 DrawCircle( nCol, nRow, aData );
1303 ++nInsCount;
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 );
1310 ++nInsCount;
1312 nNextRow = nCellRow + 1;
1314 if ( bMarkEmpty )
1315 for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1317 if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1318 DrawCircle(nCol, nRow, aData);
1319 ++nInsCount;
1324 pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1327 if ( nInsCount >= SC_DET_MAXCIRCLE )
1328 rOverflow = true;
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)
1340 continue;
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)
1363 continue;
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();
1384 if (!pModel)
1385 return;
1387 auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame);
1388 if (!pStyleSheet)
1389 return;
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();
1411 if (!pModel)
1412 return;
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 ?" );
1418 if( pPage )
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;
1428 ScAddress aPos;
1429 ScRange aSource;
1430 bool bDummy;
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
1436 ScAddress aErrPos;
1437 if ( HasError( aSource, aErrPos ) )
1438 bError = true;
1439 else
1440 bArrow = true;
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
1446 // are marked red)
1448 ScAddress aErrPos;
1449 if ( HasError( ScRange( aPos), aErrPos ) )
1450 bError = true;
1451 else
1452 bArrow = true;
1454 else if ( eType == SC_DETOBJ_CIRCLE )
1456 // circles (error marks) are always red
1458 bError = true;
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 )
1466 bArrow = true;
1470 if ( bArrow || bError )
1472 Color nColor = ( bError ? GetErrorColor() : GetArrowColor() );
1473 pObject->SetMergedItem( XLineColorItem( OUString(), nColor ) );
1475 // repaint only
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 ?");
1494 if (!pPage) return;
1496 // test if the object is a direct page member
1497 if( !(pObject && pObject->getSdrPageFromSdrObject() && (pObject->getSdrPageFromSdrObject() == pObject->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
1498 return;
1500 // Is there a previous object?
1501 const size_t nOrdNum = pObject->GetOrdNum();
1503 if(nOrdNum <= 0)
1504 return;
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;
1514 return;
1519 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1520 ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
1522 rRedLine = false;
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
1536 if ( bValidStart )
1537 eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1538 else if ( bValidEnd )
1539 eType = SC_DETOBJ_FROMOTHERTAB;
1541 if ( bValidStart )
1542 rSource = pData->maStart;
1543 if ( bValidEnd )
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() )
1555 rRedLine = true;
1557 else if (dynamic_cast<const SdrCircObj*>(pObject) != nullptr)
1559 if (bValidStart)
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)
1568 if (bValidStart)
1570 // cell position is returned in rPosition
1571 rPosition = pData->maStart;
1572 eType = SC_DETOBJ_RECTANGLE;
1578 return eType;
1581 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1582 const ScAddress& rPosition, const ScRange& rSource,
1583 bool bRedLine )
1585 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1586 if (!pModel) return;
1587 ScDetectiveData aData( pModel );
1589 switch (eType)
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 );
1597 break;
1598 case SC_DETOBJ_TOOTHERTAB:
1599 InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
1600 rSource.aEnd.Col(), rSource.aEnd.Row(),
1601 bRedLine, aData );
1602 break;
1603 case SC_DETOBJ_CIRCLE:
1604 DrawCircle( rPosition.Col(), rPosition.Row(), aData );
1605 break;
1606 default:
1608 // added to avoid warnings
1613 Color ScDetectiveFunc::GetArrowColor()
1615 if (!bColorsInitialized)
1616 InitializeColors();
1617 return nArrowColor;
1620 Color ScDetectiveFunc::GetErrorColor()
1622 if (!bColorsInitialized)
1623 InitializeColors();
1624 return nErrorColor;
1627 Color ScDetectiveFunc::GetCommentColor()
1629 if (!bColorsInitialized)
1630 InitializeColors();
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: */