xref: /core/sc/source/core/tool/detfunc.cxx (revision 0cd07e1f)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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>
49 
50 #include <basegfx/point/b2dpoint.hxx>
51 #include <basegfx/polygon/b2dpolygontools.hxx>
52 #include <basegfx/polygon/b2dpolygon.hxx>
53 
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>
73 
74 #include <vector>
75 #include <memory>
76 
77 using ::std::vector;
78 using namespace com::sun::star;
79 
80 namespace {
81 
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 };
87 
88 }
89 
90 class ScDetectiveData
91 {
92 private:
93     SfxItemSet  aBoxSet;
94     SfxItemSet  aArrowSet;
95     SfxItemSet  aToTabSet;
96     SfxItemSet  aFromTabSet;
97     SfxItemSet  aCircleSet;         //TODO: individually ?
98     sal_uInt16  nMaxLevel;
99 
100 public:
101     explicit ScDetectiveData( SdrModel* pModel );
102 
GetBoxSet()103     SfxItemSet& GetBoxSet()     { return aBoxSet; }
GetArrowSet()104     SfxItemSet& GetArrowSet()   { return aArrowSet; }
GetToTabSet()105     SfxItemSet& GetToTabSet()   { return aToTabSet; }
GetFromTabSet()106     SfxItemSet& GetFromTabSet() { return aFromTabSet; }
GetCircleSet()107     SfxItemSet& GetCircleSet()  { return aCircleSet; }
108 
SetMaxLevel(sal_uInt16 nVal)109     void        SetMaxLevel( sal_uInt16 nVal )      { nMaxLevel = nVal; }
GetMaxLevel() const110     sal_uInt16      GetMaxLevel() const             { return nMaxLevel; }
111 };
112 
113 Color ScDetectiveFunc::nArrowColor = Color(0);
114 Color ScDetectiveFunc::nErrorColor = Color(0);
115 Color ScDetectiveFunc::nCommentColor = Color(0);
116 bool ScDetectiveFunc::bColorsInitialized = false;
117 
lcl_HasThickLine(const SdrObject & rObj)118 static bool lcl_HasThickLine( const SdrObject& rObj )
119 {
120     // thin lines get width 0 -> everything greater 0 is a thick line
121 
122     return rObj.GetMergedItem(XATTR_LINEWIDTH).GetValue() > 0;
123 }
124 
ScDetectiveData(SdrModel * pModel)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)
132 {
133 
134     aBoxSet.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetArrowColor() ) );
135     aBoxSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
136 
137     //  create default line endings (like XLineEndList::Create)
138     //  to be independent from the configured line endings
139 
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);
145 
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);
152 
153     basegfx::B2DPolygon aCircle(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
154     aCircle.setClosed(true);
155 
156     const OUString aName;
157 
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 ) );
164 
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 ) );
171 
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 ) );
178 
179     aCircleSet.Put( XLineColorItem( OUString(), ScDetectiveFunc::GetErrorColor() ) );
180     aCircleSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
181     aCircleSet.Put( XLineWidthItem( 55 ) ); // 54 = 1 Pixel
182 }
183 
Modified()184 void ScDetectiveFunc::Modified()
185 {
186     rDoc.SetStreamValid(nTab, false);
187 }
188 
Intersect(SCCOL nStartCol1,SCROW nStartRow1,SCCOL nEndCol1,SCROW nEndRow1,SCCOL nStartCol2,SCROW nStartRow2,SCCOL nEndCol2,SCROW nEndRow2)189 static bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1,
190                         SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 )
191 {
192     return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
193             nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
194 }
195 
HasError(const ScRange & rRange,ScAddress & rErrPos)196 bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
197 {
198     rErrPos = rRange.aStart;
199     FormulaError nError = FormulaError::NONE;
200 
201     ScCellIterator aIter( rDoc, rRange);
202     for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
203     {
204         if (aIter.getType() != CELLTYPE_FORMULA)
205             continue;
206 
207         nError = aIter.getFormulaCell()->GetErrCode();
208         if (nError != FormulaError::NONE)
209             rErrPos = aIter.GetPos();
210     }
211 
212     return (nError != FormulaError::NONE);
213 }
214 
GetDrawPos(SCCOL nCol,SCROW nRow,DrawPosMode eMode) const215 Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
216 {
217     OSL_ENSURE( rDoc.ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
218     nCol = rDoc.SanitizeCol( nCol );
219     nRow = rDoc.SanitizeRow( nRow );
220 
221     Point aPos;
222 
223     switch( eMode )
224     {
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;
235     }
236 
237     for ( SCCOL i = 0; i < nCol; ++i )
238         aPos.AdjustX(rDoc.GetColWidth( i, nTab ) );
239     aPos.AdjustY(rDoc.GetRowHeight( 0, nRow - 1, nTab ) );
240 
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));
243 
244     if ( rDoc.IsNegativePage( nTab ) )
245         aPos.setX( aPos.X() * -1 );
246 
247     return aPos;
248 }
249 
GetDrawRect(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2) const250 tools::Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
251 {
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;
257 }
258 
GetDrawRect(SCCOL nCol,SCROW nRow) const259 tools::Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
260 {
261     return GetDrawRect( nCol, nRow, nCol, nRow );
262 }
263 
lcl_IsOtherTab(const basegfx::B2DPolyPolygon & rPolyPolygon)264 static bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
265 {
266     //  test if rPolygon is the line end for "other table" (rectangle)
267     if(1 == rPolyPolygon.count())
268     {
269         const basegfx::B2DPolygon& aSubPoly(rPolyPolygon.getB2DPolygon(0));
270 
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())
274         {
275             return true;
276         }
277     }
278 
279     return false;
280 }
281 
HasArrow(const ScAddress & rStart,SCCOL nEndCol,SCROW nEndRow,SCTAB nEndTab)282 bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
283                                     SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
284 {
285     bool bStartAlien = ( rStart.Tab() != nTab );
286     bool bEndAlien   = ( nEndTab != nTab );
287 
288     if (bStartAlien && bEndAlien)
289     {
290         OSL_FAIL("bStartAlien && bEndAlien");
291         return true;
292     }
293 
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 );
300 
301     ScDrawLayer* pModel = rDoc.GetDrawLayer();
302     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
303     OSL_ENSURE(pPage,"Page ?");
304 
305     bool bFound = false;
306     SdrObjListIter aIter( pPage, SdrIterMode::Flat );
307     SdrObject* pObject = aIter.Next();
308     while (pObject && !bFound)
309     {
310         if ( pObject->GetLayer()==SC_LAYER_INTERN &&
311                 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
312         {
313             const SfxItemSet& rSet = pObject->GetMergedItemSet();
314 
315             bool bObjStartAlien =
316                 lcl_IsOtherTab( rSet.Get(XATTR_LINESTART).GetLineStartValue() );
317             bool bObjEndAlien =
318                 lcl_IsOtherTab( rSet.Get(XATTR_LINEEND).GetLineEndValue() );
319 
320             bool bStartHit = bStartAlien ? bObjStartAlien :
321                                 ( !bObjStartAlien && aStartRect.Contains(pObject->GetPoint(0)) );
322             bool bEndHit = bEndAlien ? bObjEndAlien :
323                                 ( !bObjEndAlien && aEndRect.Contains(pObject->GetPoint(1)) );
324 
325             if ( bStartHit && bEndHit )
326                 bFound = true;
327         }
328         pObject = aIter.Next();
329     }
330 
331     return bFound;
332 }
333 
IsNonAlienArrow(const SdrObject * pObject)334 bool ScDetectiveFunc::IsNonAlienArrow( const SdrObject* pObject )
335 {
336     if ( pObject->GetLayer()==SC_LAYER_INTERN &&
337             pObject->IsPolyObj() && pObject->GetPointCount()==2 )
338     {
339         const SfxItemSet& rSet = pObject->GetMergedItemSet();
340 
341         bool bObjStartAlien =
342             lcl_IsOtherTab( rSet.Get(XATTR_LINESTART).GetLineStartValue() );
343         bool bObjEndAlien =
344             lcl_IsOtherTab( rSet.Get(XATTR_LINEEND).GetLineEndValue() );
345 
346         return !bObjStartAlien && !bObjEndAlien;
347     }
348 
349     return false;
350 }
351 
352 //  InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
353 
InsertArrow(SCCOL nCol,SCROW nRow,SCCOL nRefStartCol,SCROW nRefStartRow,SCCOL nRefEndCol,SCROW nRefEndRow,bool bFromOtherTab,bool bRed,ScDetectiveData & rData)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 )
359 {
360     ScDrawLayer* pModel = rDoc.GetDrawLayer();
361     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
362 
363     bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
364     if (bArea && !bFromOtherTab)
365     {
366         // insert the rectangle before the arrow - this is relied on in FindFrameForObject
367 
368         tools::Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow );
369         rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
370             *pModel,
371             aRect);
372 
373         pBox->NbcSetStyleSheet(nullptr, true);
374         pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
375 
376         pBox->SetDecorative(true);
377         pBox->SetLayer( SC_LAYER_INTERN );
378         pPage->InsertObject( pBox.get() );
379         pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
380 
381         ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
382         pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
383         pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
384     }
385 
386     Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DrawPosMode::DetectiveArrow );
387     Point aEndPos = GetDrawPos( nCol, nRow, DrawPosMode::DetectiveArrow );
388 
389     if (bFromOtherTab)
390     {
391         bool bNegativePage = rDoc.IsNegativePage( nTab );
392         tools::Long nPageSign = bNegativePage ? -1 : 1;
393 
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 );
399     }
400 
401     SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
402 
403     if (bArea && !bFromOtherTab)
404         rAttrSet.Put( XLineWidthItem( 50 ) );               // range
405     else
406         rAttrSet.Put( XLineWidthItem( 0 ) );                // single reference
407 
408     Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
409     rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
410 
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);
421 
422     pArrow->SetDecorative(true);
423     pArrow->SetLayer( SC_LAYER_INTERN );
424     pPage->InsertObject( pArrow.get() );
425     pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
426 
427     ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow.get(), true);
428     if (bFromOtherTab)
429         pData->maStart.SetInvalid();
430     else
431         pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
432 
433     pData->maEnd.Set( nCol, nRow, nTab);
434     pData->meType = ScDrawObjData::DetectiveArrow;
435 
436     Modified();
437 }
438 
InsertToOtherTab(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,bool bRed,ScDetectiveData & rData)439 void ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
440                                 SCCOL nEndCol, SCROW nEndRow, bool bRed,
441                                 ScDetectiveData& rData )
442 {
443     ScDrawLayer* pModel = rDoc.GetDrawLayer();
444     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
445 
446     bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
447     if (bArea)
448     {
449         tools::Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
450         rtl::Reference<SdrRectObj> pBox = new SdrRectObj(
451             *pModel,
452             aRect);
453 
454         pBox->NbcSetStyleSheet(nullptr, true);
455         pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
456 
457         pBox->SetLayer( SC_LAYER_INTERN );
458         pPage->InsertObject( pBox.get() );
459         pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pBox ) );
460 
461         ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox.get(), true );
462         pData->maStart.Set( nStartCol, nStartRow, nTab);
463         pData->maEnd.Set( nEndCol, nEndRow, nTab);
464     }
465 
466     bool bNegativePage = rDoc.IsNegativePage( nTab );
467     tools::Long nPageSign = bNegativePage ? -1 : 1;
468 
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 );
473 
474     SfxItemSet& rAttrSet = rData.GetToTabSet();
475     if (bArea)
476         rAttrSet.Put( XLineWidthItem( 50 ) );               // range
477     else
478         rAttrSet.Put( XLineWidthItem( 0 ) );                // single reference
479 
480     Color nColor = ( bRed ? GetErrorColor() : GetArrowColor() );
481     rAttrSet.Put( XLineColorItem( OUString(), nColor ) );
482 
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 ???
492 
493     pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
494 
495     pArrow->SetLayer( SC_LAYER_INTERN );
496     pPage->InsertObject( pArrow.get() );
497     pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pArrow ) );
498 
499     ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow.get(), true );
500     pData->maStart.Set( nStartCol, nStartRow, nTab);
501     pData->maEnd.SetInvalid();
502 
503     Modified();
504 }
505 
506 //  DrawEntry:      formula from this spreadsheet,
507 //                  reference on this or other
508 //  DrawAlienEntry: formula from other spreadsheet,
509 //                  reference on this
510 
511 //      return FALSE: there was already an arrow
512 
DrawEntry(SCCOL nCol,SCROW nRow,const ScRange & rRef,ScDetectiveData & rData)513 bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
514                                     const ScRange& rRef,
515                                     ScDetectiveData& rData )
516 {
517     if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
518         return false;
519 
520     ScAddress aErrorPos;
521     bool bError = HasError( rRef, aErrorPos );
522     bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
523 
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;
529 }
530 
DrawAlienEntry(const ScRange & rRef,ScDetectiveData & rData)531 bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
532                                         ScDetectiveData& rData )
533 {
534     if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
535         return false;
536 
537     ScAddress aErrorPos;
538     bool bError = HasError( rRef, aErrorPos );
539 
540     InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
541                       rRef.aEnd.Col(), rRef.aEnd.Row(),
542                       bError, rData );
543     return true;
544 }
545 
DrawCircle(SCCOL nCol,SCROW nRow,ScDetectiveData & rData)546 void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
547 {
548     ScDrawLayer* pModel = rDoc.GetDrawLayer();
549     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
550 
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 );
556 
557     rtl::Reference<SdrCircObj> pCircle = new SdrCircObj(
558         *pModel,
559         SdrCircKind::Full,
560         aRect);
561     SfxItemSet& rAttrSet = rData.GetCircleSet();
562 
563     pCircle->NbcSetStyleSheet(nullptr, true);
564     pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
565 
566     pCircle->SetDecorative(true);
567     pCircle->SetLayer( SC_LAYER_INTERN );
568     pPage->InsertObject( pCircle.get() );
569     pModel->AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pCircle ) );
570 
571     ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle.get(), true );
572     pData->maStart.Set( nCol, nRow, nTab);
573     pData->maEnd.SetInvalid();
574     pData->meType = ScDrawObjData::ValidationCircle;
575 
576     Modified();
577 }
578 
DeleteArrowsAt(SCCOL nCol,SCROW nRow,bool bDestPnt)579 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, bool bDestPnt )
580 {
581     tools::Rectangle aRect = GetDrawRect( nCol, nRow );
582 
583     ScDrawLayer* pModel = rDoc.GetDrawLayer();
584     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
585     assert(pPage && "Page ?");
586 
587     pPage->RecalcObjOrdNums();
588 
589     const size_t nObjCount = pPage->GetObjCount();
590     if (!nObjCount)
591         return;
592 
593     size_t nDelCount = 0;
594     std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
595 
596     SdrObjListIter aIter( pPage, SdrIterMode::Flat );
597     SdrObject* pObject = aIter.Next();
598     while (pObject)
599     {
600         if ( pObject->GetLayer()==SC_LAYER_INTERN &&
601                 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
602         {
603             if (aRect.Contains(pObject->GetPoint(bDestPnt ? 1 : 0))) // start/destinationpoint
604                 ppObj[nDelCount++] = pObject;
605         }
606 
607         pObject = aIter.Next();
608     }
609 
610     const bool bRecording = pModel->IsRecording();
611 
612     if (bRecording)
613     {
614         for (size_t i=1; i<=nDelCount; ++i)
615             pModel->AddCalcUndo(std::make_unique<SdrUndoDelObj>(*ppObj[nDelCount-i]));
616     }
617 
618     for (size_t i=1; i<=nDelCount; ++i)
619     {
620         // remove the object from the drawing page, delete if undo is disabled
621         pPage->RemoveObject(ppObj[nDelCount-i]->GetOrdNum());
622     }
623 
624     ppObj.reset();
625 
626     Modified();
627 }
628 
629         //      delete box around reference
630 
631 #define SC_DET_TOLERANCE    50
632 
RectIsPoints(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd)633 static bool RectIsPoints( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
634 {
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;
643 }
644 
645 #undef SC_DET_TOLERANCE
646 
DeleteBox(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)647 void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
648 {
649     tools::Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
650     Point aStartCorner = aCornerRect.TopLeft();
651     Point aEndCorner = aCornerRect.BottomRight();
652     tools::Rectangle aObjRect;
653 
654     ScDrawLayer* pModel = rDoc.GetDrawLayer();
655     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
656     assert(pPage && "Page ?");
657 
658     pPage->RecalcObjOrdNums();
659 
660     const size_t nObjCount = pPage->GetObjCount();
661     if (!nObjCount)
662         return;
663 
664     size_t nDelCount = 0;
665     std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
666 
667     SdrObjListIter aIter( pPage, SdrIterMode::Flat );
668     SdrObject* pObject = aIter.Next();
669     while (pObject)
670     {
671         if ( pObject->GetLayer() == SC_LAYER_INTERN )
672             if ( auto pSdrRectObj = dynamic_cast< const SdrRectObj* >(pObject) )
673             {
674                 aObjRect = pSdrRectObj->GetLogicRect();
675                 aObjRect.Normalize();
676                 if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
677                     ppObj[nDelCount++] = pObject;
678             }
679 
680         pObject = aIter.Next();
681     }
682 
683     for (size_t i=1; i<=nDelCount; ++i)
684         pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
685 
686     for (size_t i=1; i<=nDelCount; ++i)
687         pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
688 
689     ppObj.reset();
690 
691     Modified();
692 }
693 
InsertPredLevelArea(const ScRange & rRef,ScDetectiveData & rData,sal_uInt16 nLevel)694 sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
695                                         ScDetectiveData& rData, sal_uInt16 nLevel )
696 {
697     sal_uInt16 nResult = DET_INS_EMPTY;
698 
699     ScCellIterator aIter( rDoc, rRef);
700     for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
701     {
702         if (aIter.getType() != CELLTYPE_FORMULA)
703             continue;
704 
705         const ScAddress& rPos = aIter.GetPos();
706         switch (InsertPredLevel(rPos.Col(), rPos.Row(), rData, nLevel))
707         {
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:
720                 ;
721         }
722     }
723 
724     return nResult;
725 }
726 
InsertPredLevel(SCCOL nCol,SCROW nRow,ScDetectiveData & rData,sal_uInt16 nLevel)727 sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
728                                             sal_uInt16 nLevel )
729 {
730     ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
731     if (aCell.getType() != CELLTYPE_FORMULA)
732         return DET_INS_EMPTY;
733 
734     ScFormulaCell* pFCell = aCell.getFormula();
735     if (pFCell->IsRunning())
736         return DET_INS_CIRCULAR;
737 
738     if (pFCell->GetDirty())
739         pFCell->Interpret();                // can't be called after SetRunning
740     pFCell->SetRunning(true);
741 
742     sal_uInt16 nResult = DET_INS_EMPTY;
743 
744     ScDetectiveRefIter aIter(rDoc, pFCell);
745     ScRange aRef;
746     while ( aIter.GetNextRef( aRef ) )
747     {
748         if (DrawEntry( nCol, nRow, aRef, rData ))
749         {
750             nResult = DET_INS_INSERTED;         // insert new arrow
751         }
752         else
753         {
754             //  continue
755 
756             if ( nLevel < rData.GetMaxLevel() )
757             {
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 );
765 
766                 switch (nSubResult)
767                 {
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
780                 }
781             }
782             else                                    //  nMaxLevel reached
783                 if (nResult != DET_INS_INSERTED)
784                     nResult = DET_INS_CONTINUE;
785         }
786     }
787 
788     pFCell->SetRunning(false);
789 
790     return nResult;
791 }
792 
FindPredLevelArea(const ScRange & rRef,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)793 sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
794                                                 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
795 {
796     sal_uInt16 nResult = nLevel;
797 
798     ScCellIterator aCellIter( rDoc, rRef);
799     for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
800     {
801         if (aCellIter.getType() != CELLTYPE_FORMULA)
802             continue;
803 
804         sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
805         if (nTemp > nResult)
806             nResult = nTemp;
807     }
808 
809     return nResult;
810 }
811 
812                                             //  nDeleteLevel != 0   -> delete
813 
FindPredLevel(SCCOL nCol,SCROW nRow,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)814 sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
815 {
816     OSL_ENSURE( nLevel<1000, "Level" );
817 
818     ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
819     if (aCell.getType() != CELLTYPE_FORMULA)
820         return nLevel;
821 
822     ScFormulaCell* pFCell = aCell.getFormula();
823     if (pFCell->IsRunning())
824         return nLevel;
825 
826     if (pFCell->GetDirty())
827         pFCell->Interpret();                // can't be called after SetRunning
828     pFCell->SetRunning(true);
829 
830     sal_uInt16 nResult = nLevel;
831     bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
832 
833     if ( bDelete )
834     {
835         DeleteArrowsAt( nCol, nRow, true );                 // arrows, that are pointing here
836     }
837 
838     ScDetectiveRefIter aIter(rDoc, pFCell);
839     ScRange aRef;
840     while ( aIter.GetNextRef( aRef) )
841     {
842         bool bArea = ( aRef.aStart != aRef.aEnd );
843 
844         if ( bDelete )                  // delete frame ?
845         {
846             if (bArea)
847             {
848                 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
849             }
850         }
851         else                            // continue searching
852         {
853             if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) )
854             {
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;
863             }
864         }
865     }
866 
867     pFCell->SetRunning(false);
868 
869     return nResult;
870 }
871 
InsertErrorLevel(SCCOL nCol,SCROW nRow,ScDetectiveData & rData,sal_uInt16 nLevel)872 sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
873                                             sal_uInt16 nLevel )
874 {
875     ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
876     if (aCell.getType() != CELLTYPE_FORMULA)
877         return DET_INS_EMPTY;
878 
879     ScFormulaCell* pFCell = aCell.getFormula();
880     if (pFCell->IsRunning())
881         return DET_INS_CIRCULAR;
882 
883     if (pFCell->GetDirty())
884         pFCell->Interpret();                // can't be called after SetRunning
885     pFCell->SetRunning(true);
886 
887     sal_uInt16 nResult = DET_INS_EMPTY;
888 
889     ScDetectiveRefIter aIter(rDoc, pFCell);
890     ScRange aRef;
891     ScAddress aErrorPos;
892     bool bHasError = false;
893     while ( aIter.GetNextRef( aRef ) )
894     {
895         if (HasError( aRef, aErrorPos ))
896         {
897             bHasError = true;
898             if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
899                 nResult = DET_INS_INSERTED;
900 
901             if ( nLevel < rData.GetMaxLevel() )         // hits most of the time
902             {
903                 if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
904                                                         rData, nLevel+1 ) == DET_INS_INSERTED)
905                     nResult = DET_INS_INSERTED;
906             }
907         }
908     }
909 
910     pFCell->SetRunning(false);
911 
912                                                     // leaves ?
913     if (!bHasError)
914         if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
915             nResult = DET_INS_INSERTED;
916 
917     return nResult;
918 }
919 
InsertSuccLevel(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,ScDetectiveData & rData,sal_uInt16 nLevel)920 sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
921                                         ScDetectiveData& rData, sal_uInt16 nLevel )
922 {
923     // over the entire document.
924 
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())
928     {
929         if (aCellIter.getType() != CELLTYPE_FORMULA)
930             continue;
931 
932         ScFormulaCell* pFCell = aCellIter.getFormulaCell();
933         bool bRunning = pFCell->IsRunning();
934 
935         if (pFCell->GetDirty())
936             pFCell->Interpret();                // can't be called after SetRunning
937         pFCell->SetRunning(true);
938 
939         ScDetectiveRefIter aIter(rDoc, pFCell);
940         ScRange aRef;
941         while ( aIter.GetNextRef( aRef) )
942         {
943             if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
944             {
945                 if (Intersect( nCol1,nRow1,nCol2,nRow2,
946                         aRef.aStart.Col(),aRef.aStart.Row(),
947                         aRef.aEnd.Col(),aRef.aEnd.Row() ))
948                 {
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)
957                     {
958                         nResult = DET_INS_INSERTED;         //  insert new arrow
959                     }
960                     else
961                     {
962                         if (bRunning)
963                         {
964                             if (nResult == DET_INS_EMPTY)
965                                 nResult = DET_INS_CIRCULAR;
966                         }
967                         else
968                         {
969 
970                             if ( nLevel < rData.GetMaxLevel() )
971                             {
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)
977                                 {
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
990                                 }
991                             }
992                             else                                    //  nMaxLevel reached
993                                 if (nResult != DET_INS_INSERTED)
994                                     nResult = DET_INS_CONTINUE;
995                         }
996                     }
997                 }
998             }
999         }
1000         pFCell->SetRunning(bRunning);
1001     }
1002 
1003     return nResult;
1004 }
1005 
FindSuccLevel(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)1006 sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1007                                         sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
1008 {
1009     OSL_ENSURE( nLevel<1000, "Level" );
1010 
1011     sal_uInt16 nResult = nLevel;
1012     bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
1013 
1014     ScCellIterator aCellIter( rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab) );
1015     for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1016     {
1017         if (aCellIter.getType() != CELLTYPE_FORMULA)
1018             continue;
1019 
1020         ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1021         bool bRunning = pFCell->IsRunning();
1022 
1023         if (pFCell->GetDirty())
1024             pFCell->Interpret();                // can't be called after SetRunning
1025         pFCell->SetRunning(true);
1026 
1027         ScDetectiveRefIter aIter(rDoc, pFCell);
1028         ScRange aRef;
1029         while ( aIter.GetNextRef( aRef) )
1030         {
1031             if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1032             {
1033                 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1034                         aRef.aStart.Col(),aRef.aStart.Row(),
1035                         aRef.aEnd.Col(),aRef.aEnd.Row() ))
1036                 {
1037                     if ( bDelete )                          // arrows, that are starting here
1038                     {
1039                         if (aRef.aStart != aRef.aEnd)
1040                         {
1041                             DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
1042                                             aRef.aEnd.Col(), aRef.aEnd.Row() );
1043                         }
1044                         DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), false );
1045                     }
1046                     else if ( !bRunning &&
1047                             HasArrow( aRef.aStart,
1048                                         aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
1049                     {
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;
1055                     }
1056                 }
1057             }
1058         }
1059 
1060         pFCell->SetRunning(bRunning);
1061     }
1062 
1063     return nResult;
1064 }
1065 
ShowPred(SCCOL nCol,SCROW nRow)1066 bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1067 {
1068     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1069     if (!pModel)
1070         return false;
1071 
1072     ScDetectiveData aData( pModel );
1073 
1074     sal_uInt16 nMaxLevel = 0;
1075     sal_uInt16 nResult = DET_INS_CONTINUE;
1076     while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1077     {
1078         aData.SetMaxLevel( nMaxLevel );
1079         nResult = InsertPredLevel( nCol, nRow, aData, 0 );
1080         ++nMaxLevel;
1081     }
1082 
1083     return ( nResult == DET_INS_INSERTED );
1084 }
1085 
ShowSucc(SCCOL nCol,SCROW nRow)1086 bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1087 {
1088     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1089     if (!pModel)
1090         return false;
1091 
1092     ScDetectiveData aData( pModel );
1093 
1094     sal_uInt16 nMaxLevel = 0;
1095     sal_uInt16 nResult = DET_INS_CONTINUE;
1096     while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1097     {
1098         aData.SetMaxLevel( nMaxLevel );
1099         nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
1100         ++nMaxLevel;
1101     }
1102 
1103     return ( nResult == DET_INS_INSERTED );
1104 }
1105 
ShowError(SCCOL nCol,SCROW nRow)1106 bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1107 {
1108     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1109     if (!pModel)
1110         return false;
1111 
1112     ScRange aRange( nCol, nRow, nTab );
1113     ScAddress aErrPos;
1114     if ( !HasError( aRange,aErrPos ) )
1115         return false;
1116 
1117     ScDetectiveData aData( pModel );
1118 
1119     aData.SetMaxLevel( 1000 );
1120     sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
1121 
1122     return ( nResult == DET_INS_INSERTED );
1123 }
1124 
DeleteSucc(SCCOL nCol,SCROW nRow)1125 bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
1126 {
1127     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1128     if (!pModel)
1129         return false;
1130 
1131     sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1132     if ( nLevelCount )
1133         FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount );            // delete
1134 
1135     return ( nLevelCount != 0 );
1136 }
1137 
DeletePred(SCCOL nCol,SCROW nRow)1138 bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
1139 {
1140     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1141     if (!pModel)
1142         return false;
1143 
1144     sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1145     if ( nLevelCount )
1146         FindPredLevel( nCol, nRow, 0, nLevelCount );            // delete
1147 
1148     return ( nLevelCount != 0 );
1149 }
1150 
DeleteCirclesAt(SCCOL nCol,SCROW nRow)1151 bool ScDetectiveFunc::DeleteCirclesAt( SCCOL nCol, SCROW nRow )
1152 {
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);
1158 
1159     Point aStartCorner = aRect.TopLeft();
1160     Point aEndCorner = aRect.BottomRight();
1161 
1162     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1163     if (!pModel)
1164         return false;
1165 
1166     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1167     assert(pPage && "Page ?");
1168 
1169     pPage->RecalcObjOrdNums();
1170 
1171     const size_t nObjCount = pPage->GetObjCount();
1172     size_t nDelCount = 0;
1173     if (nObjCount)
1174     {
1175         std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1176 
1177         SdrObjListIter aIter(pPage, SdrIterMode::Flat);
1178         SdrObject* pObject = aIter.Next();
1179         while (pObject)
1180         {
1181             if (pObject->GetLayer() == SC_LAYER_INTERN)
1182                 if (auto pSdrCircObj = dynamic_cast<const SdrCircObj*>(pObject) )
1183                 {
1184                     tools::Rectangle aObjRect = pSdrCircObj->GetLogicRect();
1185                     if (RectIsPoints(aObjRect, aStartCorner, aEndCorner))
1186                         ppObj[nDelCount++] = pObject;
1187                 }
1188 
1189             pObject = aIter.Next();
1190         }
1191 
1192         for (size_t i = 1; i <= nDelCount; ++i)
1193             pModel->AddCalcUndo(std::make_unique<SdrUndoRemoveObj>(*ppObj[nDelCount - i]));
1194 
1195         for (size_t i = 1; i <= nDelCount; ++i)
1196             pPage->RemoveObject(ppObj[nDelCount - i]->GetOrdNum());
1197 
1198         ppObj.reset();
1199 
1200         Modified();
1201     }
1202 
1203     return (nDelCount != 0);
1204 }
1205 
DeleteAll(ScDetectiveDelete eWhat)1206 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1207 {
1208     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1209     if (!pModel)
1210         return false;
1211 
1212     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1213     assert(pPage && "Page ?");
1214 
1215     pPage->RecalcObjOrdNums();
1216 
1217     size_t nDelCount = 0;
1218     const size_t nObjCount = pPage->GetObjCount();
1219     if (nObjCount)
1220     {
1221         std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
1222 
1223         SdrObjListIter aIter( pPage, SdrIterMode::Flat );
1224         SdrObject* pObject = aIter.Next();
1225         while (pObject)
1226         {
1227             if ( pObject->GetLayer() == SC_LAYER_INTERN )
1228             {
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
1239                 {
1240                     OSL_FAIL("what?");
1241                 }
1242                 if ( bDoThis )
1243                     ppObj[nDelCount++] = pObject;
1244             }
1245 
1246             pObject = aIter.Next();
1247         }
1248 
1249         for (size_t i=1; i<=nDelCount; ++i)
1250             pModel->AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) );
1251 
1252         for (size_t i=1; i<=nDelCount; ++i)
1253             pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
1254 
1255         ppObj.reset();
1256 
1257         Modified();
1258     }
1259 
1260     return ( nDelCount != 0 );
1261 }
1262 
MarkInvalid(bool & rOverflow)1263 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
1264 {
1265     rOverflow = false;
1266     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1267     if (!pModel)
1268         return false;
1269 
1270     bool bDeleted = DeleteAll( ScDetectiveDelete::Circles );        // just circles
1271 
1272     ScDetectiveData aData( pModel );
1273     tools::Long nInsCount = 0;
1274 
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 )
1282     {
1283         sal_uInt32 nIndex = pPattern->GetItem(ATTR_VALIDDATA).GetValue();
1284         if (nIndex)
1285         {
1286             const ScValidationData* pData = rDoc.GetValidationEntry( nIndex );
1287             if ( pData )
1288             {
1289                 //  pass cells in this area
1290 
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())
1296                 {
1297                     SCROW nCellRow = aCellIter.GetPos().Row();
1298                     if ( bMarkEmpty )
1299                         for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1300                         {
1301                             if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1302                                DrawCircle( nCol, nRow, aData );
1303                             ++nInsCount;
1304                         }
1305                     ScRefCellValue aCell = aCellIter.getRefCellValue();
1306                     if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
1307                     {
1308                         if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1309                            DrawCircle( nCol, nCellRow, aData );
1310                         ++nInsCount;
1311                     }
1312                     nNextRow = nCellRow + 1;
1313                 }
1314                 if ( bMarkEmpty )
1315                     for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1316                     {
1317                         if(!pPattern->GetItem(ATTR_MERGE_FLAG).IsOverlapped())
1318                            DrawCircle(nCol, nRow, aData);
1319                         ++nInsCount;
1320                     }
1321             }
1322         }
1323 
1324         pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1325     }
1326 
1327     if ( nInsCount >= SC_DET_MAXCIRCLE )
1328         rOverflow = true;
1329 
1330     return ( bDeleted || nInsCount != 0 );
1331 }
1332 
GetAllPreds(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,vector<ScTokenRef> & rRefTokens)1333 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1334                                   vector<ScTokenRef>& rRefTokens)
1335 {
1336     ScCellIterator aIter(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1337     for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1338     {
1339         if (aIter.getType() != CELLTYPE_FORMULA)
1340             continue;
1341 
1342         ScFormulaCell* pFCell = aIter.getFormulaCell();
1343         ScDetectiveRefIter aRefIter(rDoc, pFCell);
1344         for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1345         {
1346             ScTokenRef pRef(p->Clone());
1347             ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, aIter.GetPos());
1348         }
1349     }
1350 }
1351 
GetAllSuccs(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,vector<ScTokenRef> & rRefTokens)1352 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1353                                   vector<ScTokenRef>& rRefTokens)
1354 {
1355     vector<ScTokenRef> aSrcRange;
1356     aSrcRange.push_back(
1357         ScRefTokenHelper::createRefToken(rDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
1358 
1359     ScCellIterator aIter(rDoc, ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab));
1360     for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1361     {
1362         if (aIter.getType() != CELLTYPE_FORMULA)
1363             continue;
1364 
1365         ScFormulaCell* pFCell = aIter.getFormulaCell();
1366         ScDetectiveRefIter aRefIter(rDoc, pFCell);
1367         for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1368         {
1369             const ScAddress& aPos = aIter.GetPos();
1370             ScTokenRef pRef(p->Clone());
1371             if (ScRefTokenHelper::intersects(&rDoc, aSrcRange, pRef, aPos))
1372             {
1373                 // This address is absolute.
1374                 pRef = ScRefTokenHelper::createRefToken(rDoc, aPos);
1375                 ScRefTokenHelper::join(&rDoc, rRefTokens, pRef, ScAddress());
1376             }
1377         }
1378     }
1379 }
1380 
UpdateAllComments(ScDocument & rDoc)1381 void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
1382 {
1383     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1384     if (!pModel)
1385         return;
1386 
1387     auto pStyleSheet = rDoc.GetStyleSheetPool()->Find(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame);
1388     if (!pStyleSheet)
1389         return;
1390 
1391     ScStyleSaveData aOldData, aNewData;
1392     aOldData.InitFromStyle(pStyleSheet);
1393 
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));
1398 
1399     aNewData.InitFromStyle(pStyleSheet);
1400 
1401     ScDocShell* pDocSh = rDoc.GetDocumentShell();
1402     pDocSh->GetUndoManager()->AddUndoAction(
1403         std::make_unique<ScUndoModifyStyle>(pDocSh, pStyleSheet->GetFamily(), aOldData, aNewData));
1404 }
1405 
UpdateAllArrowColors()1406 void ScDetectiveFunc::UpdateAllArrowColors()
1407 {
1408     //  no undo actions necessary
1409 
1410     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1411     if (!pModel)
1412         return;
1413 
1414     for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1415     {
1416         SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1417         OSL_ENSURE( pPage, "Page ?" );
1418         if( pPage )
1419         {
1420             SdrObjListIter aIter( pPage, SdrIterMode::Flat );
1421             for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1422             {
1423                 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1424                 {
1425                     bool bArrow = false;
1426                     bool bError = false;
1427 
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 )
1433                     {
1434                         //  source is valid, determine error flag from source range
1435 
1436                         ScAddress aErrPos;
1437                         if ( HasError( aSource, aErrPos ) )
1438                             bError = true;
1439                         else
1440                             bArrow = true;
1441                     }
1442                     else if ( eType == SC_DETOBJ_FROMOTHERTAB )
1443                     {
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)
1447 
1448                         ScAddress aErrPos;
1449                         if ( HasError( ScRange( aPos), aErrPos ) )
1450                             bError = true;
1451                         else
1452                             bArrow = true;
1453                     }
1454                     else if ( eType == SC_DETOBJ_CIRCLE )
1455                     {
1456                         //  circles (error marks) are always red
1457 
1458                         bError = true;
1459                     }
1460                     else if ( eType == SC_DETOBJ_NONE )
1461                     {
1462                         //  frame for area reference has no ObjType, always gets arrow color
1463 
1464                         if ( dynamic_cast<const SdrRectObj*>( pObject) != nullptr && dynamic_cast<const SdrCaptionObj*>( pObject) ==  nullptr )
1465                         {
1466                             bArrow = true;
1467                         }
1468                     }
1469 
1470                     if ( bArrow || bError )
1471                     {
1472                         Color nColor = ( bError ? GetErrorColor() : GetArrowColor() );
1473                         pObject->SetMergedItem( XLineColorItem( OUString(), nColor ) );
1474 
1475                         // repaint only
1476                         pObject->ActionChanged();
1477                     }
1478                 }
1479             }
1480         }
1481     }
1482 }
1483 
FindFrameForObject(const SdrObject * pObject,ScRange & rRange)1484 void ScDetectiveFunc::FindFrameForObject( const SdrObject* pObject, ScRange& rRange )
1485 {
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)
1488 
1489     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1490     if (!pModel) return;
1491 
1492     SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1493     OSL_ENSURE(pPage,"Page ?");
1494     if (!pPage) return;
1495 
1496     // test if the object is a direct page member
1497     if( !(pObject && pObject->getSdrPageFromSdrObject() && (pObject->getSdrPageFromSdrObject() == pObject->getParentSdrObjListFromSdrObject()->getSdrPageFromSdrObjList())) )
1498         return;
1499 
1500     // Is there a previous object?
1501     const size_t nOrdNum = pObject->GetOrdNum();
1502 
1503     if(nOrdNum <= 0)
1504         return;
1505 
1506     SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
1507 
1508     if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && dynamic_cast<const SdrRectObj*>( pPrevObj) !=  nullptr )
1509     {
1510         ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
1511         if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
1512         {
1513             rRange.aEnd = pPrevData->maEnd;
1514             return;
1515         }
1516     }
1517 }
1518 
GetDetectiveObjectType(SdrObject * pObject,SCTAB nObjTab,ScAddress & rPosition,ScRange & rSource,bool & rRedLine)1519 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1520                                 ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
1521 {
1522     rRedLine = false;
1523     ScDetectiveObjType eType = SC_DETOBJ_NONE;
1524 
1525     if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
1526     {
1527         if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
1528         {
1529             bool bValidStart = pData->maStart.IsValid();
1530             bool bValidEnd = pData->maEnd.IsValid();
1531 
1532             if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
1533             {
1534                 // line object -> arrow
1535 
1536                 if ( bValidStart )
1537                     eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1538                 else if ( bValidEnd )
1539                     eType = SC_DETOBJ_FROMOTHERTAB;
1540 
1541                 if ( bValidStart )
1542                     rSource = pData->maStart;
1543                 if ( bValidEnd )
1544                     rPosition = pData->maEnd;
1545 
1546                 if ( bValidStart && lcl_HasThickLine( *pObject ) )
1547                 {
1548                     // thick line -> look for frame before this object
1549 
1550                     FindFrameForObject( pObject, rSource );     // modifies rSource
1551                 }
1552 
1553                 Color nObjColor = pObject->GetMergedItem(XATTR_LINECOLOR).GetColorValue();
1554                 if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
1555                     rRedLine = true;
1556             }
1557             else if (dynamic_cast<const SdrCircObj*>(pObject) != nullptr)
1558             {
1559                 if (bValidStart)
1560                 {
1561                     // cell position is returned in rPosition
1562                     rPosition = pData->maStart;
1563                     eType = SC_DETOBJ_CIRCLE;
1564                 }
1565             }
1566             else if (dynamic_cast<const SdrRectObj*>(pObject) != nullptr)
1567             {
1568                 if (bValidStart)
1569                 {
1570                     // cell position is returned in rPosition
1571                     rPosition = pData->maStart;
1572                     eType = SC_DETOBJ_RECTANGLE;
1573                 }
1574             }
1575         }
1576     }
1577 
1578     return eType;
1579 }
1580 
InsertObject(ScDetectiveObjType eType,const ScAddress & rPosition,const ScRange & rSource,bool bRedLine)1581 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1582                             const ScAddress& rPosition, const ScRange& rSource,
1583                             bool bRedLine )
1584 {
1585     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1586     if (!pModel) return;
1587     ScDetectiveData aData( pModel );
1588 
1589     switch (eType)
1590     {
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:
1607         {
1608             // added to avoid warnings
1609         }
1610     }
1611 }
1612 
GetArrowColor()1613 Color ScDetectiveFunc::GetArrowColor()
1614 {
1615     if (!bColorsInitialized)
1616         InitializeColors();
1617     return nArrowColor;
1618 }
1619 
GetErrorColor()1620 Color ScDetectiveFunc::GetErrorColor()
1621 {
1622     if (!bColorsInitialized)
1623         InitializeColors();
1624     return nErrorColor;
1625 }
1626 
GetCommentColor()1627 Color ScDetectiveFunc::GetCommentColor()
1628 {
1629     if (!bColorsInitialized)
1630         InitializeColors();
1631     return nCommentColor;
1632 }
1633 
InitializeColors()1634 void ScDetectiveFunc::InitializeColors()
1635 {
1636     // may be called several times to update colors from configuration
1637 
1638     const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1639     nArrowColor   = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
1640     nErrorColor   = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
1641     nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
1642 
1643     bColorsInitialized = true;
1644 }
1645 
IsColorsInitialized()1646 bool ScDetectiveFunc::IsColorsInitialized()
1647 {
1648     return bColorsInitialized;
1649 }
1650 
AppendChangTrackNoteSeparator(OUString & rDisplay)1651 void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString &rDisplay)
1652 {
1653     rDisplay += "\n--------\n";
1654 }
1655 
1656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1657