Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / tool / detfunc.cxx
blobb5dc71b92a6323d2bd56295ee1eeee11c2f3a623
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->SetLayer( SC_LAYER_INTERN );
377 pPage->InsertObject( pBox.get() );
378 pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
380 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
381 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
382 pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
385 Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DrawPosMode::DetectiveArrow );
386 Point aEndPos = GetDrawPos( nCol, nRow, DrawPosMode::DetectiveArrow );
388 if (bFromOtherTab)
390 bool bNegativePage = rDoc.IsNegativePage( nTab );
391 tools::Long nPageSign = bNegativePage ? -1 : 1;
393 aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 );
394 if (aStartPos.X() * nPageSign < 0)
395 aStartPos.AdjustX(2000 * nPageSign );
396 if (aStartPos.Y() < 0)
397 aStartPos.AdjustY(2000 );
400 SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
402 if (bArea && !bFromOtherTab)
403 rAttrSet.Put( XLineWidthItem( 50 ) ); // range
404 else
405 rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
407 Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
408 rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
410 basegfx::B2DPolygon aTempPoly;
411 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
412 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
413 rtl::Reference<SdrPathObj> pArrow = new SdrPathObj(
414 *pModel,
415 SdrObjKind::Line,
416 basegfx::B2DPolyPolygon(aTempPoly));
417 pArrow->NbcSetStyleSheet(nullptr, true);
418 pArrow->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos,aEndPos)); //TODO: needed ???
419 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
421 pArrow->SetLayer( SC_LAYER_INTERN );
422 pPage->InsertObject( pArrow.get() );
423 pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
425 ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow.get(), true);
426 if (bFromOtherTab)
427 pData->maStart.SetInvalid();
428 else
429 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
431 pData->maEnd.Set( nCol, nRow, nTab);
432 pData->meType = ScDrawObjData::DetectiveArrow;
434 Modified();
437 void ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
438 SCCOL nEndCol, SCROW nEndRow, bool bRed,
439 ScDetectiveData& rData )
441 ScDrawLayer* pModel = rDoc.GetDrawLayer();
442 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
444 bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
445 if (bArea)
447 tools::Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
448 rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
449 *pModel,
450 aRect);
452 pBox->NbcSetStyleSheet(nullptr, true);
453 pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
455 pBox->SetLayer( SC_LAYER_INTERN );
456 pPage->InsertObject( pBox.get() );
457 pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
459 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
460 pData->maStart.Set( nStartCol, nStartRow, nTab);
461 pData->maEnd.Set( nEndCol, nEndRow, nTab);
464 bool bNegativePage = rDoc.IsNegativePage( nTab );
465 tools::Long nPageSign = bNegativePage ? -1 : 1;
467 Point aStartPos = GetDrawPos( nStartCol, nStartRow, DrawPosMode::DetectiveArrow );
468 Point aEndPos( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 );
469 if (aEndPos.Y() < 0)
470 aEndPos.AdjustY(2000 );
472 SfxItemSet& rAttrSet = rData.GetToTabSet();
473 if (bArea)
474 rAttrSet.Put( XLineWidthItem( 50 ) ); // range
475 else
476 rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
478 Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
479 rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
481 basegfx::B2DPolygon aTempPoly;
482 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
483 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
484 rtl::Reference<SdrPathObj> pArrow = new SdrPathObj(
485 *pModel,
486 SdrObjKind::Line,
487 basegfx::B2DPolyPolygon(aTempPoly));
488 pArrow->NbcSetStyleSheet(nullptr, true);
489 pArrow->NbcSetLogicRect(tools::Rectangle::Normalize(aStartPos,aEndPos)); //TODO: needed ???
491 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
493 pArrow->SetLayer( SC_LAYER_INTERN );
494 pPage->InsertObject( pArrow.get() );
495 pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
497 ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow.get(), true );
498 pData->maStart.Set( nStartCol, nStartRow, nTab);
499 pData->maEnd.SetInvalid();
501 Modified();
504 // DrawEntry: formula from this spreadsheet,
505 // reference on this or other
506 // DrawAlienEntry: formula from other spreadsheet,
507 // reference on this
509 // return FALSE: there was already an arrow
511 bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
512 const ScRange& rRef,
513 ScDetectiveData& rData )
515 if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
516 return false;
518 ScAddress aErrorPos;
519 bool bError = HasError( rRef, aErrorPos );
520 bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
522 InsertArrow( nCol, nRow,
523 rRef.aStart.Col(), rRef.aStart.Row(),
524 rRef.aEnd.Col(), rRef.aEnd.Row(),
525 bAlien, bError, rData );
526 return true;
529 bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
530 ScDetectiveData& rData )
532 if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
533 return false;
535 ScAddress aErrorPos;
536 bool bError = HasError( rRef, aErrorPos );
538 InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
539 rRef.aEnd.Col(), rRef.aEnd.Row(),
540 bError, rData );
541 return true;
544 void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
546 ScDrawLayer* pModel = rDoc.GetDrawLayer();
547 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
549 tools::Rectangle aRect = ScDrawLayer::GetCellRect(rDoc, ScAddress(nCol, nRow, nTab), true);
550 aRect.AdjustLeft( -250 );
551 aRect.AdjustRight(250 );
552 aRect.AdjustTop( -70 );
553 aRect.AdjustBottom(70 );
555 rtl::Reference<SdrCircObj> pCircle = new SdrCircObj(
556 *pModel,
557 SdrCircKind::Full,
558 aRect);
559 SfxItemSet& rAttrSet = rData.GetCircleSet();
561 pCircle->NbcSetStyleSheet(nullptr, true);
562 pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
564 pCircle->SetLayer( SC_LAYER_INTERN );
565 pPage->InsertObject( pCircle.get() );
566 pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pCircle ) );
568 ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle.get(), true );
569 pData->maStart.Set( nCol, nRow, nTab);
570 pData->maEnd.SetInvalid();
571 pData->meType = ScDrawObjData::ValidationCircle;
573 Modified();
576 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, bool bDestPnt )
578 tools::Rectangle aRect = GetDrawRect( nCol, nRow );
580 ScDrawLayer* pModel = rDoc.GetDrawLayer();
581 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
582 OSL_ENSURE(pPage,"Page ?");
584 pPage->RecalcObjOrdNums();
586 const size_t nObjCount = pPage->GetObjCount();
587 if (!nObjCount)
588 return;
590 size_t nDelCount = 0;
591 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
593 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
594 SdrObject* pObject = aIter.Next();
595 while (pObject)
597 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
598 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
600 if (aRect.Contains(pObject->GetPoint(bDestPnt ? 1 : 0))) // start/destinationpoint
601 ppObj[nDelCount++] = pObject;
604 pObject = aIter.Next();
607 const bool bRecording = pModel->IsRecording();
609 if (bRecording)
611 for (size_t i=1; i<=nDelCount; ++i)
612 pModel->AddCalcUndo(std::make_unique<SdrUndoDelObj>(*ppObj[nDelCount-i]));
615 for (size_t i=1; i<=nDelCount; ++i)
617 // remove the object from the drawing page, delete if undo is disabled
618 pPage->RemoveObject(ppObj[nDelCount-i]->GetOrdNum());
621 ppObj.reset();
623 Modified();
626 // delete box around reference
628 #define SC_DET_TOLERANCE 50
630 static bool RectIsPoints( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
632 return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE
633 && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE
634 && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE
635 && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE
636 && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE
637 && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE
638 && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE
639 && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE;
642 #undef SC_DET_TOLERANCE
644 void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
646 tools::Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
647 Point aStartCorner = aCornerRect.TopLeft();
648 Point aEndCorner = aCornerRect.BottomRight();
649 tools::Rectangle aObjRect;
651 ScDrawLayer* pModel = rDoc.GetDrawLayer();
652 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
653 OSL_ENSURE(pPage,"Page ?");
655 pPage->RecalcObjOrdNums();
657 const size_t nObjCount = pPage->GetObjCount();
658 if (!nObjCount)
659 return;
661 size_t nDelCount = 0;
662 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
664 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
665 SdrObject* pObject = aIter.Next();
666 while (pObject)
668 if ( pObject->GetLayer() == SC_LAYER_INTERN )
669 if ( auto pSdrRectObj = dynamic_cast< const SdrRectObj* >(pObject) )
671 aObjRect = pSdrRectObj->GetLogicRect();
672 aObjRect.Normalize();
673 if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
674 ppObj[nDelCount++] = pObject;
677 pObject = aIter.Next();
680 for (size_t i=1; i<=nDelCount; ++i)
681 pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
683 for (size_t i=1; i<=nDelCount; ++i)
684 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
686 ppObj.reset();
688 Modified();
691 sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
692 ScDetectiveData& rData, sal_uInt16 nLevel )
694 sal_uInt16 nResult = DET_INS_EMPTY;
696 ScCellIterator aIter( rDoc, rRef);
697 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
699 if (aIter.getType() != CELLTYPE_FORMULA)
700 continue;
702 const ScAddress& rPos = aIter.GetPos();
703 switch (InsertPredLevel(rPos.Col(), rPos.Row(), rData, nLevel))
705 case DET_INS_INSERTED:
706 nResult = DET_INS_INSERTED;
707 break;
708 case DET_INS_CONTINUE:
709 if (nResult != DET_INS_INSERTED)
710 nResult = DET_INS_CONTINUE;
711 break;
712 case DET_INS_CIRCULAR:
713 if (nResult == DET_INS_EMPTY)
714 nResult = DET_INS_CIRCULAR;
715 break;
716 default:
721 return nResult;
724 sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
725 sal_uInt16 nLevel )
727 ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
728 if (aCell.getType() != CELLTYPE_FORMULA)
729 return DET_INS_EMPTY;
731 ScFormulaCell* pFCell = aCell.getFormula();
732 if (pFCell->IsRunning())
733 return DET_INS_CIRCULAR;
735 if (pFCell->GetDirty())
736 pFCell->Interpret(); // can't be called after SetRunning
737 pFCell->SetRunning(true);
739 sal_uInt16 nResult = DET_INS_EMPTY;
741 ScDetectiveRefIter aIter(rDoc, pFCell);
742 ScRange aRef;
743 while ( aIter.GetNextRef( aRef ) )
745 if (DrawEntry( nCol, nRow, aRef, rData ))
747 nResult = DET_INS_INSERTED; // insert new arrow
749 else
751 // continue
753 if ( nLevel < rData.GetMaxLevel() )
755 sal_uInt16 nSubResult;
756 bool bArea = (aRef.aStart != aRef.aEnd);
757 if (bArea)
758 nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 );
759 else
760 nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(),
761 rData, nLevel+1 );
763 switch (nSubResult)
765 case DET_INS_INSERTED:
766 nResult = DET_INS_INSERTED;
767 break;
768 case DET_INS_CONTINUE:
769 if (nResult != DET_INS_INSERTED)
770 nResult = DET_INS_CONTINUE;
771 break;
772 case DET_INS_CIRCULAR:
773 if (nResult == DET_INS_EMPTY)
774 nResult = DET_INS_CIRCULAR;
775 break;
776 // DET_INS_EMPTY: no change
779 else // nMaxLevel reached
780 if (nResult != DET_INS_INSERTED)
781 nResult = DET_INS_CONTINUE;
785 pFCell->SetRunning(false);
787 return nResult;
790 sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
791 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
793 sal_uInt16 nResult = nLevel;
795 ScCellIterator aCellIter( rDoc, rRef);
796 for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
798 if (aCellIter.getType() != CELLTYPE_FORMULA)
799 continue;
801 sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
802 if (nTemp > nResult)
803 nResult = nTemp;
806 return nResult;
809 // nDeleteLevel != 0 -> delete
811 sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
813 OSL_ENSURE( nLevel<1000, "Level" );
815 ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
816 if (aCell.getType() != CELLTYPE_FORMULA)
817 return nLevel;
819 ScFormulaCell* pFCell = aCell.getFormula();
820 if (pFCell->IsRunning())
821 return nLevel;
823 if (pFCell->GetDirty())
824 pFCell->Interpret(); // can't be called after SetRunning
825 pFCell->SetRunning(true);
827 sal_uInt16 nResult = nLevel;
828 bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
830 if ( bDelete )
832 DeleteArrowsAt( nCol, nRow, true ); // arrows, that are pointing here
835 ScDetectiveRefIter aIter(rDoc, pFCell);
836 ScRange aRef;
837 while ( aIter.GetNextRef( aRef) )
839 bool bArea = ( aRef.aStart != aRef.aEnd );
841 if ( bDelete ) // delete frame ?
843 if (bArea)
845 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
848 else // continue searching
850 if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) )
852 sal_uInt16 nTemp;
853 if (bArea)
854 nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel );
855 else
856 nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(),
857 nLevel+1, nDeleteLevel );
858 if (nTemp > nResult)
859 nResult = nTemp;
864 pFCell->SetRunning(false);
866 return nResult;
869 sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
870 sal_uInt16 nLevel )
872 ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
873 if (aCell.getType() != CELLTYPE_FORMULA)
874 return DET_INS_EMPTY;
876 ScFormulaCell* pFCell = aCell.getFormula();
877 if (pFCell->IsRunning())
878 return DET_INS_CIRCULAR;
880 if (pFCell->GetDirty())
881 pFCell->Interpret(); // can't be called after SetRunning
882 pFCell->SetRunning(true);
884 sal_uInt16 nResult = DET_INS_EMPTY;
886 ScDetectiveRefIter aIter(rDoc, pFCell);
887 ScRange aRef;
888 ScAddress aErrorPos;
889 bool bHasError = false;
890 while ( aIter.GetNextRef( aRef ) )
892 if (HasError( aRef, aErrorPos ))
894 bHasError = true;
895 if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
896 nResult = DET_INS_INSERTED;
898 if ( nLevel < rData.GetMaxLevel() ) // hits most of the time
900 if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
901 rData, nLevel+1 ) == DET_INS_INSERTED)
902 nResult = DET_INS_INSERTED;
907 pFCell->SetRunning(false);
909 // leaves ?
910 if (!bHasError)
911 if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
912 nResult = DET_INS_INSERTED;
914 return nResult;
917 sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
918 ScDetectiveData& rData, sal_uInt16 nLevel )
920 // over the entire document.
922 sal_uInt16 nResult = DET_INS_EMPTY;
923 ScCellIterator aCellIter(rDoc, ScRange(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB)); // all sheets
924 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
926 if (aCellIter.getType() != CELLTYPE_FORMULA)
927 continue;
929 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
930 bool bRunning = pFCell->IsRunning();
932 if (pFCell->GetDirty())
933 pFCell->Interpret(); // can't be called after SetRunning
934 pFCell->SetRunning(true);
936 ScDetectiveRefIter aIter(rDoc, pFCell);
937 ScRange aRef;
938 while ( aIter.GetNextRef( aRef) )
940 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
942 if (Intersect( nCol1,nRow1,nCol2,nRow2,
943 aRef.aStart.Col(),aRef.aStart.Row(),
944 aRef.aEnd.Col(),aRef.aEnd.Row() ))
946 bool bAlien = ( aCellIter.GetPos().Tab() != nTab );
947 bool bDrawRet;
948 if (bAlien)
949 bDrawRet = DrawAlienEntry( aRef, rData );
950 else
951 bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
952 aRef, rData );
953 if (bDrawRet)
955 nResult = DET_INS_INSERTED; // insert new arrow
957 else
959 if (bRunning)
961 if (nResult == DET_INS_EMPTY)
962 nResult = DET_INS_CIRCULAR;
964 else
967 if ( nLevel < rData.GetMaxLevel() )
969 sal_uInt16 nSubResult = InsertSuccLevel(
970 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
971 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
972 rData, nLevel+1 );
973 switch (nSubResult)
975 case DET_INS_INSERTED:
976 nResult = DET_INS_INSERTED;
977 break;
978 case DET_INS_CONTINUE:
979 if (nResult != DET_INS_INSERTED)
980 nResult = DET_INS_CONTINUE;
981 break;
982 case DET_INS_CIRCULAR:
983 if (nResult == DET_INS_EMPTY)
984 nResult = DET_INS_CIRCULAR;
985 break;
986 // DET_INS_EMPTY: leave unchanged
989 else // nMaxLevel reached
990 if (nResult != DET_INS_INSERTED)
991 nResult = DET_INS_CONTINUE;
997 pFCell->SetRunning(bRunning);
1000 return nResult;
1003 sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1004 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
1006 OSL_ENSURE( nLevel<1000, "Level" );
1008 sal_uInt16 nResult = nLevel;
1009 bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
1011 ScCellIterator aCellIter( rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab) );
1012 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1014 if (aCellIter.getType() != CELLTYPE_FORMULA)
1015 continue;
1017 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1018 bool bRunning = pFCell->IsRunning();
1020 if (pFCell->GetDirty())
1021 pFCell->Interpret(); // can't be called after SetRunning
1022 pFCell->SetRunning(true);
1024 ScDetectiveRefIter aIter(rDoc, pFCell);
1025 ScRange aRef;
1026 while ( aIter.GetNextRef( aRef) )
1028 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1030 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1031 aRef.aStart.Col(),aRef.aStart.Row(),
1032 aRef.aEnd.Col(),aRef.aEnd.Row() ))
1034 if ( bDelete ) // arrows, that are starting here
1036 if (aRef.aStart != aRef.aEnd)
1038 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
1039 aRef.aEnd.Col(), aRef.aEnd.Row() );
1041 DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), false );
1043 else if ( !bRunning &&
1044 HasArrow( aRef.aStart,
1045 aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
1047 sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1048 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1049 nLevel+1, nDeleteLevel );
1050 if (nTemp > nResult)
1051 nResult = nTemp;
1057 pFCell->SetRunning(bRunning);
1060 return nResult;
1063 bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1065 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1066 if (!pModel)
1067 return false;
1069 ScDetectiveData aData( pModel );
1071 sal_uInt16 nMaxLevel = 0;
1072 sal_uInt16 nResult = DET_INS_CONTINUE;
1073 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1075 aData.SetMaxLevel( nMaxLevel );
1076 nResult = InsertPredLevel( nCol, nRow, aData, 0 );
1077 ++nMaxLevel;
1080 return ( nResult == DET_INS_INSERTED );
1083 bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1085 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1086 if (!pModel)
1087 return false;
1089 ScDetectiveData aData( pModel );
1091 sal_uInt16 nMaxLevel = 0;
1092 sal_uInt16 nResult = DET_INS_CONTINUE;
1093 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1095 aData.SetMaxLevel( nMaxLevel );
1096 nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
1097 ++nMaxLevel;
1100 return ( nResult == DET_INS_INSERTED );
1103 bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1105 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1106 if (!pModel)
1107 return false;
1109 ScRange aRange( nCol, nRow, nTab );
1110 ScAddress aErrPos;
1111 if ( !HasError( aRange,aErrPos ) )
1112 return false;
1114 ScDetectiveData aData( pModel );
1116 aData.SetMaxLevel( 1000 );
1117 sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
1119 return ( nResult == DET_INS_INSERTED );
1122 bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
1124 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1125 if (!pModel)
1126 return false;
1128 sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1129 if ( nLevelCount )
1130 FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // delete
1132 return ( nLevelCount != 0 );
1135 bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
1137 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1138 if (!pModel)
1139 return false;
1141 sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1142 if ( nLevelCount )
1143 FindPredLevel( nCol, nRow, 0, nLevelCount ); // delete
1145 return ( nLevelCount != 0 );
1148 bool ScDetectiveFunc::DeleteCirclesAt( SCCOL nCol, SCROW nRow )
1150 tools::Rectangle aRect = ScDrawLayer::GetCellRect(rDoc, ScAddress(nCol, nRow, nTab), true);
1151 aRect.AdjustLeft(-250);
1152 aRect.AdjustRight(250);
1153 aRect.AdjustTop(-70);
1154 aRect.AdjustBottom(70);
1156 Point aStartCorner = aRect.TopLeft();
1157 Point aEndCorner = aRect.BottomRight();
1159 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1160 if (!pModel)
1161 return false;
1163 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1164 OSL_ENSURE(pPage, "Page ?");
1166 pPage->RecalcObjOrdNums();
1168 const size_t nObjCount = pPage->GetObjCount();
1169 size_t nDelCount = 0;
1170 if (nObjCount)
1172 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1174 SdrObjListIter aIter(pPage, SdrIterMode::Flat);
1175 SdrObject* pObject = aIter.Next();
1176 while (pObject)
1178 if (pObject->GetLayer() == SC_LAYER_INTERN)
1179 if (auto pSdrCircObj = dynamic_cast<const SdrCircObj*>(pObject) )
1181 tools::Rectangle aObjRect = pSdrCircObj->GetLogicRect();
1182 if (RectIsPoints(aObjRect, aStartCorner, aEndCorner))
1183 ppObj[nDelCount++] = pObject;
1186 pObject = aIter.Next();
1189 for (size_t i = 1; i <= nDelCount; ++i)
1190 pModel->AddCalcUndo(std::make_unique<SdrUndoRemoveObj>(*ppObj[nDelCount - i]));
1192 for (size_t i = 1; i <= nDelCount; ++i)
1193 pPage->RemoveObject(ppObj[nDelCount - i]->GetOrdNum());
1195 ppObj.reset();
1197 Modified();
1200 return (nDelCount != 0);
1203 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1205 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1206 if (!pModel)
1207 return false;
1209 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1210 OSL_ENSURE(pPage,"Page ?");
1212 pPage->RecalcObjOrdNums();
1214 size_t nDelCount = 0;
1215 const size_t nObjCount = pPage->GetObjCount();
1216 if (nObjCount)
1218 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1220 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
1221 SdrObject* pObject = aIter.Next();
1222 while (pObject)
1224 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1226 bool bDoThis = true;
1227 bool bCircle = ( dynamic_cast<const SdrCircObj*>( pObject) != nullptr );
1228 bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
1229 if ( eWhat == ScDetectiveDelete::Detective ) // detective, from menu
1230 bDoThis = !bCaption; // also circles
1231 else if ( eWhat == ScDetectiveDelete::Circles ) // circles, if new created
1232 bDoThis = bCircle;
1233 else if ( eWhat == ScDetectiveDelete::Arrows ) // DetectiveRefresh
1234 bDoThis = !bCaption && !bCircle; // don't include circles
1235 else
1237 OSL_FAIL("what?");
1239 if ( bDoThis )
1240 ppObj[nDelCount++] = pObject;
1243 pObject = aIter.Next();
1246 for (size_t i=1; i<=nDelCount; ++i)
1247 pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
1249 for (size_t i=1; i<=nDelCount; ++i)
1250 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
1252 ppObj.reset();
1254 Modified();
1257 return ( nDelCount != 0 );
1260 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
1262 rOverflow = false;
1263 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1264 if (!pModel)
1265 return false;
1267 bool bDeleted = DeleteAll( ScDetectiveDelete::Circles ); // just circles
1269 ScDetectiveData aData( pModel );
1270 tools::Long nInsCount = 0;
1272 // search for valid places
1273 ScDocAttrIterator aAttrIter( rDoc, nTab, 0,0,rDoc.MaxCol(),rDoc.MaxRow() );
1274 SCCOL nCol;
1275 SCROW nRow1;
1276 SCROW nRow2;
1277 const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1278 while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
1280 sal_uInt32 nIndex = pPattern->GetItem(ATTR_VALIDDATA).GetValue();
1281 if (nIndex)
1283 const ScValidationData* pData = rDoc.GetValidationEntry( nIndex );
1284 if ( pData )
1286 // pass cells in this area
1288 bool bMarkEmpty = !pData->IsIgnoreBlank();
1289 SCROW nNextRow = nRow1;
1290 SCROW nRow;
1291 ScCellIterator aCellIter( rDoc, ScRange(nCol, nRow1, nTab, nCol, nRow2, nTab) );
1292 for (bool bHas = aCellIter.first(); bHas && nInsCount < SC_DET_MAXCIRCLE; bHas = aCellIter.next())
1294 SCROW nCellRow = aCellIter.GetPos().Row();
1295 if ( bMarkEmpty )
1296 for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1298 if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1299 DrawCircle( nCol, nRow, aData );
1300 ++nInsCount;
1302 ScRefCellValue aCell = aCellIter.getRefCellValue();
1303 if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
1305 if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1306 DrawCircle( nCol, nCellRow, aData );
1307 ++nInsCount;
1309 nNextRow = nCellRow + 1;
1311 if ( bMarkEmpty )
1312 for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1314 if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1315 DrawCircle(nCol, nRow, aData);
1316 ++nInsCount;
1321 pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1324 if ( nInsCount >= SC_DET_MAXCIRCLE )
1325 rOverflow = true;
1327 return ( bDeleted || nInsCount != 0 );
1330 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1331 vector<ScTokenRef>& rRefTokens)
1333 ScCellIterator aIter(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1334 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1336 if (aIter.getType() != CELLTYPE_FORMULA)
1337 continue;
1339 ScFormulaCell* pFCell = aIter.getFormulaCell();
1340 ScDetectiveRefIter aRefIter(rDoc, pFCell);
1341 for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1343 ScTokenRef pRef(p->Clone());
1344 ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, aIter.GetPos());
1349 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1350 vector<ScTokenRef>& rRefTokens)
1352 vector<ScTokenRef> aSrcRange;
1353 aSrcRange.push_back(
1354 ScRefTokenHelper::createRefToken(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
1356 ScCellIterator aIter(rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab));
1357 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1359 if (aIter.getType() != CELLTYPE_FORMULA)
1360 continue;
1362 ScFormulaCell* pFCell = aIter.getFormulaCell();
1363 ScDetectiveRefIter aRefIter(rDoc, pFCell);
1364 for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1366 const ScAddress& aPos = aIter.GetPos();
1367 ScTokenRef pRef(p->Clone());
1368 if (ScRefTokenHelper::intersects(&rDoc, aSrcRange, pRef, aPos))
1370 // This address is absolute.
1371 pRef = ScRefTokenHelper::createRefToken(rDoc, aPos);
1372 ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, ScAddress());
1378 void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
1380 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1381 if (!pModel)
1382 return;
1384 auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame);
1385 if (!pStyleSheet)
1386 return;
1388 ScStyleSaveData aOldData, aNewData;
1389 aOldData.InitFromStyle(pStyleSheet);
1391 auto& rSet = pStyleSheet->GetItemSet();
1392 rSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
1393 rSet.Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
1394 static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
1396 aNewData.InitFromStyle(pStyleSheet);
1398 ScDocShell* pDocSh = rDoc.GetDocumentShell();
1399 pDocSh->GetUndoManager()->AddUndoAction(
1400 std::make_unique<ScUndoModifyStyle>(pDocSh, pStyleSheet->GetFamily(), aOldData, aNewData));
1403 void ScDetectiveFunc::UpdateAllArrowColors()
1405 // no undo actions necessary
1407 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1408 if (!pModel)
1409 return;
1411 for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1413 SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1414 OSL_ENSURE( pPage, "Page ?" );
1415 if( pPage )
1417 SdrObjListIter aIter( pPage, SdrIterMode::Flat );
1418 for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1420 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1422 bool bArrow = false;
1423 bool bError = false;
1425 ScAddress aPos;
1426 ScRange aSource;
1427 bool bDummy;
1428 ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy );
1429 if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
1431 // source is valid, determine error flag from source range
1433 ScAddress aErrPos;
1434 if ( HasError( aSource, aErrPos ) )
1435 bError = true;
1436 else
1437 bArrow = true;
1439 else if ( eType == SC_DETOBJ_FROMOTHERTAB )
1441 // source range is no longer known, take error flag from formula itself
1442 // (this means, if the formula has an error, all references to other tables
1443 // are marked red)
1445 ScAddress aErrPos;
1446 if ( HasError( ScRange( aPos), aErrPos ) )
1447 bError = true;
1448 else
1449 bArrow = true;
1451 else if ( eType == SC_DETOBJ_CIRCLE )
1453 // circles (error marks) are always red
1455 bError = true;
1457 else if ( eType == SC_DETOBJ_NONE )
1459 // frame for area reference has no ObjType, always gets arrow color
1461 if ( dynamic_cast<const SdrRectObj*>( pObject) != nullptr && dynamic_cast<const SdrCaptionObj*>( pObject) == nullptr )
1463 bArrow = true;
1467 if ( bArrow || bError )
1469 Color nColor = ( bError ? GetErrorColor() : GetArrowColor() );
1470 pObject->SetMergedItem( XLineColorItem( OUString(), nColor ) );
1472 // repaint only
1473 pObject->ActionChanged();
1481 void ScDetectiveFunc::FindFrameForObject( const SdrObject* pObject, ScRange& rRange )
1483 // find the rectangle for an arrow (always the object directly before the arrow)
1484 // rRange must be initialized to the source cell of the arrow (start of area)
1486 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1487 if (!pModel) return;
1489 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1490 OSL_ENSURE(pPage,"Page ?");
1491 if (!pPage) return;
1493 // test if the object is a direct page member
1494 if( !(pObject && pObject->getSdrPageFromSdrObject() && (pObject->getSdrPageFromSdrObject() == pObject->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
1495 return;
1497 // Is there a previous object?
1498 const size_t nOrdNum = pObject->GetOrdNum();
1500 if(nOrdNum <= 0)
1501 return;
1503 SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
1505 if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && dynamic_cast<const SdrRectObj*>( pPrevObj) != nullptr )
1507 ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
1508 if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
1510 rRange.aEnd = pPrevData->maEnd;
1511 return;
1516 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1517 ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
1519 rRedLine = false;
1520 ScDetectiveObjType eType = SC_DETOBJ_NONE;
1522 if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
1524 if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
1526 bool bValidStart = pData->maStart.IsValid();
1527 bool bValidEnd = pData->maEnd.IsValid();
1529 if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
1531 // line object -> arrow
1533 if ( bValidStart )
1534 eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1535 else if ( bValidEnd )
1536 eType = SC_DETOBJ_FROMOTHERTAB;
1538 if ( bValidStart )
1539 rSource = pData->maStart;
1540 if ( bValidEnd )
1541 rPosition = pData->maEnd;
1543 if ( bValidStart && lcl_HasThickLine( *pObject ) )
1545 // thick line -> look for frame before this object
1547 FindFrameForObject( pObject, rSource ); // modifies rSource
1550 Color nObjColor = pObject->GetMergedItem(XATTR_LINECOLOR).GetColorValue();
1551 if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
1552 rRedLine = true;
1554 else if (dynamic_cast<const SdrCircObj*>(pObject) != nullptr)
1556 if (bValidStart)
1558 // cell position is returned in rPosition
1559 rPosition = pData->maStart;
1560 eType = SC_DETOBJ_CIRCLE;
1563 else if (dynamic_cast<const SdrRectObj*>(pObject) != nullptr)
1565 if (bValidStart)
1567 // cell position is returned in rPosition
1568 rPosition = pData->maStart;
1569 eType = SC_DETOBJ_RECTANGLE;
1575 return eType;
1578 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1579 const ScAddress& rPosition, const ScRange& rSource,
1580 bool bRedLine )
1582 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1583 if (!pModel) return;
1584 ScDetectiveData aData( pModel );
1586 switch (eType)
1588 case SC_DETOBJ_ARROW:
1589 case SC_DETOBJ_FROMOTHERTAB:
1590 InsertArrow( rPosition.Col(), rPosition.Row(),
1591 rSource.aStart.Col(), rSource.aStart.Row(),
1592 rSource.aEnd.Col(), rSource.aEnd.Row(),
1593 (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData );
1594 break;
1595 case SC_DETOBJ_TOOTHERTAB:
1596 InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
1597 rSource.aEnd.Col(), rSource.aEnd.Row(),
1598 bRedLine, aData );
1599 break;
1600 case SC_DETOBJ_CIRCLE:
1601 DrawCircle( rPosition.Col(), rPosition.Row(), aData );
1602 break;
1603 default:
1605 // added to avoid warnings
1610 Color ScDetectiveFunc::GetArrowColor()
1612 if (!bColorsInitialized)
1613 InitializeColors();
1614 return nArrowColor;
1617 Color ScDetectiveFunc::GetErrorColor()
1619 if (!bColorsInitialized)
1620 InitializeColors();
1621 return nErrorColor;
1624 Color ScDetectiveFunc::GetCommentColor()
1626 if (!bColorsInitialized)
1627 InitializeColors();
1628 return nCommentColor;
1631 void ScDetectiveFunc::InitializeColors()
1633 // may be called several times to update colors from configuration
1635 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1636 nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
1637 nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
1638 nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
1640 bColorsInitialized = true;
1643 bool ScDetectiveFunc::IsColorsInitialized()
1645 return bColorsInitialized;
1648 void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString &rDisplay)
1650 rDisplay += "\n--------\n";
1653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */