Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / detfunc.cxx
blobd69ba93a98ec639e0497a7f8d836e5df1d7528f9
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 <editeng/outlobj.hxx>
24 #include <svx/sdshitm.hxx>
25 #include <svx/sdsxyitm.hxx>
26 #include <svx/sdtditm.hxx>
27 #include <svx/svditer.hxx>
28 #include <svx/svdocapt.hxx>
29 #include <svx/svdocirc.hxx>
30 #include <svx/svdopath.hxx>
31 #include <svx/svdorect.hxx>
32 #include <svx/svdpage.hxx>
33 #include <svx/svdundo.hxx>
34 #include <svx/xfillit0.hxx>
35 #include <svx/xflclit.hxx>
36 #include <svx/xlnclit.hxx>
37 #include <svx/xlnedcit.hxx>
38 #include <svx/xlnedit.hxx>
39 #include <svx/xlnedwit.hxx>
40 #include <svx/xlnstcit.hxx>
41 #include <svx/xlnstit.hxx>
42 #include <svx/xlnstwit.hxx>
43 #include <svx/xlnwtit.hxx>
44 #include <svx/xtable.hxx>
45 #include <editeng/outliner.hxx>
46 #include <editeng/editobj.hxx>
47 #include <svx/sxcecitm.hxx>
48 #include <svl/whiter.hxx>
49 #include <editeng/writingmodeitem.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/polygon/b2dpolygontools.hxx>
53 #include <basegfx/polygon/b2dpolygon.hxx>
55 #include "detfunc.hxx"
56 #include "document.hxx"
57 #include "dociter.hxx"
58 #include "drwlayer.hxx"
59 #include "userdat.hxx"
60 #include "validat.hxx"
61 #include "formulacell.hxx"
62 #include "docpool.hxx"
63 #include "patattr.hxx"
64 #include "attrib.hxx"
65 #include "scmod.hxx"
66 #include "postit.hxx"
67 #include "rangelst.hxx"
68 #include "reftokenhelper.hxx"
69 #include "formulaiter.hxx"
70 #include "cellvalue.hxx"
72 #include <vector>
74 using ::std::vector;
76 // line ends are now created with an empty name.
77 // The checkForUniqueItem method then finds a unique name for the item's value.
78 #define SC_LINEEND_NAME EMPTY_OUSTRING
80 enum DetInsertResult { // Return-Werte beim Einfuegen in einen Level
81 DET_INS_CONTINUE,
82 DET_INS_INSERTED,
83 DET_INS_EMPTY,
84 DET_INS_CIRCULAR };
86 class ScDetectiveData
88 private:
89 SfxItemSet aBoxSet;
90 SfxItemSet aArrowSet;
91 SfxItemSet aToTabSet;
92 SfxItemSet aFromTabSet;
93 SfxItemSet aCircleSet; //! einzeln ?
94 sal_uInt16 nMaxLevel;
96 public:
97 ScDetectiveData( SdrModel* pModel );
99 SfxItemSet& GetBoxSet() { return aBoxSet; }
100 SfxItemSet& GetArrowSet() { return aArrowSet; }
101 SfxItemSet& GetToTabSet() { return aToTabSet; }
102 SfxItemSet& GetFromTabSet() { return aFromTabSet; }
103 SfxItemSet& GetCircleSet() { return aCircleSet; }
105 void SetMaxLevel( sal_uInt16 nVal ) { nMaxLevel = nVal; }
106 sal_uInt16 GetMaxLevel() const { return nMaxLevel; }
109 class ScCommentData
111 public:
112 ScCommentData( ScDocument& rDoc, SdrModel* pModel );
114 SfxItemSet& GetCaptionSet() { return aCaptionSet; }
115 void UpdateCaptionSet( const SfxItemSet& rItemSet );
117 private:
118 SfxItemSet aCaptionSet;
121 ColorData ScDetectiveFunc::nArrowColor = 0;
122 ColorData ScDetectiveFunc::nErrorColor = 0;
123 ColorData ScDetectiveFunc::nCommentColor = 0;
124 sal_Bool ScDetectiveFunc::bColorsInitialized = false;
126 static sal_Bool lcl_HasThickLine( SdrObject& rObj )
128 // thin lines get width 0 -> everything greater 0 is a thick line
130 return ( ((const XLineWidthItem&)rObj.GetMergedItem(XATTR_LINEWIDTH)).GetValue() > 0 );
133 ScDetectiveData::ScDetectiveData( SdrModel* pModel ) :
134 aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
135 aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
136 aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
137 aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
138 aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END )
140 nMaxLevel = 0;
142 aBoxSet.Put( XLineColorItem( EMPTY_OUSTRING, Color( ScDetectiveFunc::GetArrowColor() ) ) );
143 aBoxSet.Put( XFillStyleItem( XFILL_NONE ) );
145 // Standard-Linienenden (wie aus XLineEndList::Create) selber zusammenbasteln,
146 // um von den konfigurierten Linienenden unabhaengig zu sein
148 basegfx::B2DPolygon aTriangle;
149 aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
150 aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
151 aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
152 aTriangle.setClosed(true);
154 basegfx::B2DPolygon aSquare;
155 aSquare.append(basegfx::B2DPoint(0.0, 0.0));
156 aSquare.append(basegfx::B2DPoint(10.0, 0.0));
157 aSquare.append(basegfx::B2DPoint(10.0, 10.0));
158 aSquare.append(basegfx::B2DPoint(0.0, 10.0));
159 aSquare.setClosed(true);
161 basegfx::B2DPolygon aCircle(basegfx::tools::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
162 aCircle.setClosed(true);
164 OUString aName = SC_LINEEND_NAME;
166 aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
167 aArrowSet.Put( XLineStartWidthItem( 200 ) );
168 aArrowSet.Put( XLineStartCenterItem( sal_True ) );
169 aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
170 aArrowSet.Put( XLineEndWidthItem( 200 ) );
171 aArrowSet.Put( XLineEndCenterItem( false ) );
173 aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
174 aToTabSet.Put( XLineStartWidthItem( 200 ) );
175 aToTabSet.Put( XLineStartCenterItem( sal_True ) );
176 aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
177 aToTabSet.Put( XLineEndWidthItem( 300 ) );
178 aToTabSet.Put( XLineEndCenterItem( false ) );
180 aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
181 aFromTabSet.Put( XLineStartWidthItem( 300 ) );
182 aFromTabSet.Put( XLineStartCenterItem( sal_True ) );
183 aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
184 aFromTabSet.Put( XLineEndWidthItem( 200 ) );
185 aFromTabSet.Put( XLineEndCenterItem( false ) );
187 aCircleSet.Put( XLineColorItem( OUString(), Color( ScDetectiveFunc::GetErrorColor() ) ) );
188 aCircleSet.Put( XFillStyleItem( XFILL_NONE ) );
189 sal_uInt16 nWidth = 55; // 54 = 1 Pixel
190 aCircleSet.Put( XLineWidthItem( nWidth ) );
193 ScCommentData::ScCommentData( ScDocument& rDoc, SdrModel* pModel ) :
194 aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END, EE_ITEMS_START, EE_ITEMS_END, 0, 0 )
196 basegfx::B2DPolygon aTriangle;
197 aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
198 aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
199 aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
200 aTriangle.setClosed(true);
202 OUString aName = SC_LINEEND_NAME;
204 aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
205 aCaptionSet.Put( XLineStartWidthItem( 200 ) );
206 aCaptionSet.Put( XLineStartCenterItem( false ) );
207 aCaptionSet.Put( XFillStyleItem( XFILL_SOLID ) );
208 Color aYellow( ScDetectiveFunc::GetCommentColor() );
209 aCaptionSet.Put( XFillColorItem( OUString(), aYellow ) );
211 // shadow
212 // SdrShadowItem has sal_False, instead the shadow is set for the rectangle
213 // only with SetSpecialTextBoxShadow when the object is created
214 // (item must be set to adjust objects from older files)
215 aCaptionSet.Put( SdrShadowItem( false ) );
216 aCaptionSet.Put( SdrShadowXDistItem( 100 ) );
217 aCaptionSet.Put( SdrShadowYDistItem( 100 ) );
219 // text attributes
220 aCaptionSet.Put( SdrTextLeftDistItem( 100 ) );
221 aCaptionSet.Put( SdrTextRightDistItem( 100 ) );
222 aCaptionSet.Put( SdrTextUpperDistItem( 100 ) );
223 aCaptionSet.Put( SdrTextLowerDistItem( 100 ) );
225 aCaptionSet.Put( SdrTextAutoGrowWidthItem( false ) );
226 aCaptionSet.Put( SdrTextAutoGrowHeightItem( sal_True ) );
228 // do use the default cell style, so the user has a chance to
229 // modify the font for the annotations
230 ((const ScPatternAttr&)rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN)).
231 FillEditItemSet( &aCaptionSet );
233 // support the best position for the tail connector now that
234 // that notes can be resized and repositioned.
235 aCaptionSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT) );
238 void ScCommentData::UpdateCaptionSet( const SfxItemSet& rItemSet )
240 SfxWhichIter aWhichIter( rItemSet );
241 const SfxPoolItem* pPoolItem = 0;
243 for( sal_uInt16 nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() )
245 if(rItemSet.GetItemState(nWhich, false, &pPoolItem) == SFX_ITEM_SET)
247 switch(nWhich)
249 case SDRATTR_SHADOW:
250 // use existing Caption default - appears that setting this
251 // to true screws up the tail appearance. See also comment
252 // for default setting above.
253 break;
254 case SDRATTR_SHADOWXDIST:
255 // use existing Caption default - svx sets a value of 35
256 // but default 100 gives a better appearance.
257 break;
258 case SDRATTR_SHADOWYDIST:
259 // use existing Caption default - svx sets a value of 35
260 // but default 100 gives a better appearance.
261 break;
263 default:
264 aCaptionSet.Put(*pPoolItem);
270 void ScDetectiveFunc::Modified()
272 if (pDoc->IsStreamValid(nTab))
273 pDoc->SetStreamValid(nTab, false);
276 inline sal_Bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1,
277 SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 )
279 return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
280 nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
283 sal_Bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
285 rErrPos = rRange.aStart;
286 sal_uInt16 nError = 0;
288 ScCellIterator aIter( pDoc, rRange);
289 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
291 if (aIter.getType() != CELLTYPE_FORMULA)
292 continue;
294 nError = aIter.getFormulaCell()->GetErrCode();
295 if (nError)
296 rErrPos = aIter.GetPos();
299 return (nError != 0);
302 Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
304 OSL_ENSURE( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
305 SanitizeCol( nCol );
306 SanitizeRow( nRow );
308 Point aPos;
310 switch( eMode )
312 case DRAWPOS_TOPLEFT:
313 break;
314 case DRAWPOS_BOTTOMRIGHT:
315 ++nCol;
316 ++nRow;
317 break;
318 case DRAWPOS_DETARROW:
319 aPos.X() += pDoc->GetColWidth( nCol, nTab ) / 4;
320 aPos.Y() += pDoc->GetRowHeight( nRow, nTab ) / 2;
321 break;
322 case DRAWPOS_CAPTIONLEFT:
323 aPos.X() += 6;
324 break;
325 case DRAWPOS_CAPTIONRIGHT:
327 // find right end of passed cell position
328 const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE ) );
329 if ( pMerge->GetColMerge() > 1 )
330 nCol = nCol + pMerge->GetColMerge();
331 else
332 ++nCol;
333 aPos.X() -= 6;
335 break;
338 for ( SCCOL i = 0; i < nCol; ++i )
339 aPos.X() += pDoc->GetColWidth( i, nTab );
340 aPos.Y() += pDoc->GetRowHeight( 0, nRow - 1, nTab );
342 aPos.X() = static_cast< long >( aPos.X() * HMM_PER_TWIPS );
343 aPos.Y() = static_cast< long >( aPos.Y() * HMM_PER_TWIPS );
345 if ( pDoc->IsNegativePage( nTab ) )
346 aPos.X() *= -1;
348 return aPos;
351 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
353 Rectangle aRect(
354 GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DRAWPOS_TOPLEFT ),
355 GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DRAWPOS_BOTTOMRIGHT ) );
356 aRect.Justify(); // reorder left/right in RTL sheets
357 return aRect;
360 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
362 return GetDrawRect( nCol, nRow, nCol, nRow );
365 static sal_Bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
367 // test if rPolygon is the line end for "other table" (rectangle)
368 if(1L == rPolyPolygon.count())
370 const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0L));
372 // #i73305# circle consists of 4 segments, too, distinguishable from square by
373 // the use of control points
374 if(4L == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed())
376 return true;
380 return false;
383 sal_Bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
384 SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
386 sal_Bool bStartAlien = ( rStart.Tab() != nTab );
387 sal_Bool bEndAlien = ( nEndTab != nTab );
389 if (bStartAlien && bEndAlien)
391 OSL_FAIL("bStartAlien && bEndAlien");
392 return true;
395 Rectangle aStartRect;
396 Rectangle aEndRect;
397 if (!bStartAlien)
398 aStartRect = GetDrawRect( rStart.Col(), rStart.Row() );
399 if (!bEndAlien)
400 aEndRect = GetDrawRect( nEndCol, nEndRow );
402 ScDrawLayer* pModel = pDoc->GetDrawLayer();
403 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
404 OSL_ENSURE(pPage,"Page ?");
406 sal_Bool bFound = false;
407 SdrObjListIter aIter( *pPage, IM_FLAT );
408 SdrObject* pObject = aIter.Next();
409 while (pObject && !bFound)
411 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
412 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
414 const SfxItemSet& rSet = pObject->GetMergedItemSet();
416 sal_Bool bObjStartAlien =
417 lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
418 sal_Bool bObjEndAlien =
419 lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
421 sal_Bool bStartHit = bStartAlien ? bObjStartAlien :
422 ( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) );
423 sal_Bool bEndHit = bEndAlien ? bObjEndAlien :
424 ( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) );
426 if ( bStartHit && bEndHit )
427 bFound = sal_True;
429 pObject = aIter.Next();
432 return bFound;
435 sal_Bool ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject )
437 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
438 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
440 const SfxItemSet& rSet = pObject->GetMergedItemSet();
442 sal_Bool bObjStartAlien =
443 lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
444 sal_Bool bObjEndAlien =
445 lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
447 return !bObjStartAlien && !bObjEndAlien;
450 return false;
453 // InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
455 sal_Bool ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow,
456 SCCOL nRefStartCol, SCROW nRefStartRow,
457 SCCOL nRefEndCol, SCROW nRefEndRow,
458 sal_Bool bFromOtherTab, sal_Bool bRed,
459 ScDetectiveData& rData )
461 ScDrawLayer* pModel = pDoc->GetDrawLayer();
462 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
464 sal_Bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
465 if (bArea && !bFromOtherTab)
467 // insert the rectangle before the arrow - this is relied on in FindFrameForObject
469 Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow );
470 SdrRectObj* pBox = new SdrRectObj( aRect );
472 pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
474 pBox->SetLayer( SC_LAYER_INTERN );
475 pPage->InsertObject( pBox );
476 pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
478 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True );
479 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
480 pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
483 Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DRAWPOS_DETARROW );
484 Point aEndPos = GetDrawPos( nCol, nRow, DRAWPOS_DETARROW );
486 if (bFromOtherTab)
488 sal_Bool bNegativePage = pDoc->IsNegativePage( nTab );
489 long nPageSign = bNegativePage ? -1 : 1;
491 aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 );
492 if (aStartPos.X() * nPageSign < 0)
493 aStartPos.X() += 2000 * nPageSign;
494 if (aStartPos.Y() < 0)
495 aStartPos.Y() += 2000;
498 SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
500 if (bArea && !bFromOtherTab)
501 rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich
502 else
503 rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz
505 ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
506 rAttrSet.Put( XLineColorItem( OUString(), Color( nColorData ) ) );
508 basegfx::B2DPolygon aTempPoly;
509 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
510 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
511 SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
512 pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ???
513 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
515 pArrow->SetLayer( SC_LAYER_INTERN );
516 pPage->InsertObject( pArrow );
517 pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
519 ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow, true);
520 if (bFromOtherTab)
521 pData->maStart.SetInvalid();
522 else
523 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
525 pData->maEnd.Set( nCol, nRow, nTab);
526 pData->meType = ScDrawObjData::DetectiveArrow;
528 Modified();
529 return true;
532 sal_Bool ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
533 SCCOL nEndCol, SCROW nEndRow, sal_Bool bRed,
534 ScDetectiveData& rData )
536 ScDrawLayer* pModel = pDoc->GetDrawLayer();
537 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
539 sal_Bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
540 if (bArea)
542 Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
543 SdrRectObj* pBox = new SdrRectObj( aRect );
545 pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
547 pBox->SetLayer( SC_LAYER_INTERN );
548 pPage->InsertObject( pBox );
549 pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
551 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True );
552 pData->maStart.Set( nStartCol, nStartRow, nTab);
553 pData->maEnd.Set( nEndCol, nEndRow, nTab);
556 sal_Bool bNegativePage = pDoc->IsNegativePage( nTab );
557 long nPageSign = bNegativePage ? -1 : 1;
559 Point aStartPos = GetDrawPos( nStartCol, nStartRow, DRAWPOS_DETARROW );
560 Point aEndPos = Point( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 );
561 if (aEndPos.Y() < 0)
562 aEndPos.Y() += 2000;
564 SfxItemSet& rAttrSet = rData.GetToTabSet();
565 if (bArea)
566 rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich
567 else
568 rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz
570 ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
571 rAttrSet.Put( XLineColorItem( OUString(), Color( nColorData ) ) );
573 basegfx::B2DPolygon aTempPoly;
574 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
575 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
576 SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
577 pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ???
579 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
581 pArrow->SetLayer( SC_LAYER_INTERN );
582 pPage->InsertObject( pArrow );
583 pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
585 ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, sal_True );
586 pData->maStart.Set( nStartCol, nStartRow, nTab);
587 pData->maEnd.SetInvalid();
589 Modified();
590 return sal_True;
593 // DrawEntry: Formel auf dieser Tabelle,
594 // Referenz auf dieser oder anderer
595 // DrawAlienEntry: Formel auf anderer Tabelle,
596 // Referenz auf dieser
598 // return FALSE: da war schon ein Pfeil
600 sal_Bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
601 const ScRange& rRef,
602 ScDetectiveData& rData )
604 if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
605 return false;
607 ScAddress aErrorPos;
608 sal_Bool bError = HasError( rRef, aErrorPos );
609 sal_Bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
611 return InsertArrow( nCol, nRow,
612 rRef.aStart.Col(), rRef.aStart.Row(),
613 rRef.aEnd.Col(), rRef.aEnd.Row(),
614 bAlien, bError, rData );
617 sal_Bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
618 ScDetectiveData& rData )
620 if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
621 return false;
623 ScAddress aErrorPos;
624 sal_Bool bError = HasError( rRef, aErrorPos );
626 return InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
627 rRef.aEnd.Col(), rRef.aEnd.Row(),
628 bError, rData );
631 void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
633 ScDrawLayer* pModel = pDoc->GetDrawLayer();
634 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
636 Rectangle aRect = GetDrawRect( nCol, nRow );
637 aRect.Left() -= 250;
638 aRect.Right() += 250;
639 aRect.Top() -= 70;
640 aRect.Bottom() += 70;
642 SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect );
643 SfxItemSet& rAttrSet = rData.GetCircleSet();
645 pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
647 pCircle->SetLayer( SC_LAYER_INTERN );
648 pPage->InsertObject( pCircle );
649 pModel->AddCalcUndo( new SdrUndoInsertObj( *pCircle ) );
651 ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, sal_True );
652 pData->maStart.Set( nCol, nRow, nTab);
653 pData->maEnd.SetInvalid();
654 pData->meType = ScDrawObjData::ValidationCircle;
656 Modified();
659 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, sal_Bool bDestPnt )
661 Rectangle aRect = GetDrawRect( nCol, nRow );
663 ScDrawLayer* pModel = pDoc->GetDrawLayer();
664 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
665 OSL_ENSURE(pPage,"Page ?");
667 pPage->RecalcObjOrdNums();
669 sal_uLong nObjCount = pPage->GetObjCount();
670 if (nObjCount)
672 long nDelCount = 0;
673 SdrObject** ppObj = new SdrObject*[nObjCount];
675 SdrObjListIter aIter( *pPage, IM_FLAT );
676 SdrObject* pObject = aIter.Next();
677 while (pObject)
679 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
680 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
682 if (aRect.IsInside(pObject->GetPoint(bDestPnt))) // Start/Zielpunkt
683 ppObj[nDelCount++] = pObject;
686 pObject = aIter.Next();
689 long i;
690 for (i=1; i<=nDelCount; i++)
691 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
693 for (i=1; i<=nDelCount; i++)
694 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
696 delete[] ppObj;
698 Modified();
702 // Box um Referenz loeschen
704 #define SC_DET_TOLERANCE 50
706 inline sal_Bool RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd )
708 return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE
709 && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE
710 && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE
711 && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE
712 && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE
713 && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE
714 && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE
715 && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE;
718 #undef SC_DET_TOLERANCE
720 void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
722 Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
723 Point aStartCorner = aCornerRect.TopLeft();
724 Point aEndCorner = aCornerRect.BottomRight();
725 Rectangle aObjRect;
727 ScDrawLayer* pModel = pDoc->GetDrawLayer();
728 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
729 OSL_ENSURE(pPage,"Page ?");
731 pPage->RecalcObjOrdNums();
733 sal_uLong nObjCount = pPage->GetObjCount();
734 if (nObjCount)
736 long nDelCount = 0;
737 SdrObject** ppObj = new SdrObject*[nObjCount];
739 SdrObjListIter aIter( *pPage, IM_FLAT );
740 SdrObject* pObject = aIter.Next();
741 while (pObject)
743 if ( pObject->GetLayer() == SC_LAYER_INTERN &&
744 pObject->Type() == TYPE(SdrRectObj) )
746 aObjRect = ((SdrRectObj*)pObject)->GetLogicRect();
747 aObjRect.Justify();
748 if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
749 ppObj[nDelCount++] = pObject;
752 pObject = aIter.Next();
755 long i;
756 for (i=1; i<=nDelCount; i++)
757 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
759 for (i=1; i<=nDelCount; i++)
760 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
762 delete[] ppObj;
764 Modified();
768 sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
769 ScDetectiveData& rData, sal_uInt16 nLevel )
771 sal_uInt16 nResult = DET_INS_EMPTY;
773 ScCellIterator aIter( pDoc, rRef);
774 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
776 if (aIter.getType() != CELLTYPE_FORMULA)
777 continue;
779 const ScAddress& rPos = aIter.GetPos();
780 switch (InsertPredLevel(rPos.Col(), rPos.Row(), rData, nLevel))
782 case DET_INS_INSERTED:
783 nResult = DET_INS_INSERTED;
784 break;
785 case DET_INS_CONTINUE:
786 if (nResult != DET_INS_INSERTED)
787 nResult = DET_INS_CONTINUE;
788 break;
789 case DET_INS_CIRCULAR:
790 if (nResult == DET_INS_EMPTY)
791 nResult = DET_INS_CIRCULAR;
792 break;
793 default:
798 return nResult;
801 sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
802 sal_uInt16 nLevel )
804 ScRefCellValue aCell;
805 aCell.assign(*pDoc, ScAddress(nCol, nRow, nTab));
806 if (aCell.meType != CELLTYPE_FORMULA)
807 return DET_INS_EMPTY;
809 ScFormulaCell* pFCell = aCell.mpFormula;
810 if (pFCell->IsRunning())
811 return DET_INS_CIRCULAR;
813 if (pFCell->GetDirty())
814 pFCell->Interpret(); // nach SetRunning geht's nicht mehr!
815 pFCell->SetRunning(sal_True);
817 sal_uInt16 nResult = DET_INS_EMPTY;
819 ScDetectiveRefIter aIter(pFCell);
820 ScRange aRef;
821 while ( aIter.GetNextRef( aRef ) )
823 if (DrawEntry( nCol, nRow, aRef, rData ))
825 nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen
827 else
829 // weiterverfolgen
831 if ( nLevel < rData.GetMaxLevel() )
833 sal_uInt16 nSubResult;
834 sal_Bool bArea = (aRef.aStart != aRef.aEnd);
835 if (bArea)
836 nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 );
837 else
838 nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(),
839 rData, nLevel+1 );
841 switch (nSubResult)
843 case DET_INS_INSERTED:
844 nResult = DET_INS_INSERTED;
845 break;
846 case DET_INS_CONTINUE:
847 if (nResult != DET_INS_INSERTED)
848 nResult = DET_INS_CONTINUE;
849 break;
850 case DET_INS_CIRCULAR:
851 if (nResult == DET_INS_EMPTY)
852 nResult = DET_INS_CIRCULAR;
853 break;
854 // DET_INS_EMPTY: unveraendert lassen
857 else // nMaxLevel erreicht
858 if (nResult != DET_INS_INSERTED)
859 nResult = DET_INS_CONTINUE;
863 pFCell->SetRunning(false);
865 return nResult;
868 sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
869 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
871 sal_uInt16 nResult = nLevel;
873 ScCellIterator aCellIter( pDoc, rRef);
874 for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
876 if (aCellIter.getType() != CELLTYPE_FORMULA)
877 continue;
879 sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
880 if (nTemp > nResult)
881 nResult = nTemp;
884 return nResult;
887 // nDeleteLevel != 0 -> loeschen
889 sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
891 OSL_ENSURE( nLevel<1000, "Level" );
893 ScRefCellValue aCell;
894 aCell.assign(*pDoc, ScAddress(nCol, nRow, nTab));
895 if (aCell.meType != CELLTYPE_FORMULA)
896 return nLevel;
898 ScFormulaCell* pFCell = aCell.mpFormula;
899 if (pFCell->IsRunning())
900 return nLevel;
902 if (pFCell->GetDirty())
903 pFCell->Interpret(); // nach SetRunning geht's nicht mehr!
904 pFCell->SetRunning(sal_True);
906 sal_uInt16 nResult = nLevel;
907 sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
909 if ( bDelete )
911 DeleteArrowsAt( nCol, nRow, sal_True ); // Pfeile, die hierher zeigen
914 ScDetectiveRefIter aIter(pFCell);
915 ScRange aRef;
916 while ( aIter.GetNextRef( aRef) )
918 sal_Bool bArea = ( aRef.aStart != aRef.aEnd );
920 if ( bDelete ) // Rahmen loeschen ?
922 if (bArea)
924 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
927 else // weitersuchen
929 if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) )
931 sal_uInt16 nTemp;
932 if (bArea)
933 nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel );
934 else
935 nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(),
936 nLevel+1, nDeleteLevel );
937 if (nTemp > nResult)
938 nResult = nTemp;
943 pFCell->SetRunning(false);
945 return nResult;
948 sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
949 sal_uInt16 nLevel )
951 ScRefCellValue aCell;
952 aCell.assign(*pDoc, ScAddress(nCol, nRow, nTab));
953 if (aCell.meType != CELLTYPE_FORMULA)
954 return DET_INS_EMPTY;
956 ScFormulaCell* pFCell = aCell.mpFormula;
957 if (pFCell->IsRunning())
958 return DET_INS_CIRCULAR;
960 if (pFCell->GetDirty())
961 pFCell->Interpret(); // nach SetRunning geht's nicht mehr!
962 pFCell->SetRunning(sal_True);
964 sal_uInt16 nResult = DET_INS_EMPTY;
966 ScDetectiveRefIter aIter(pFCell);
967 ScRange aRef;
968 ScAddress aErrorPos;
969 sal_Bool bHasError = false;
970 while ( aIter.GetNextRef( aRef ) )
972 if (HasError( aRef, aErrorPos ))
974 bHasError = sal_True;
975 if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
976 nResult = DET_INS_INSERTED;
978 // und weiterverfolgen
980 if ( nLevel < rData.GetMaxLevel() ) // praktisch immer
982 if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
983 rData, nLevel+1 ) == DET_INS_INSERTED)
984 nResult = DET_INS_INSERTED;
989 pFCell->SetRunning(false);
991 // Blaetter ?
992 if (!bHasError)
993 if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
994 nResult = DET_INS_INSERTED;
996 return nResult;
999 sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1000 ScDetectiveData& rData, sal_uInt16 nLevel )
1002 // over the entire document.
1004 sal_uInt16 nResult = DET_INS_EMPTY;
1005 ScCellIterator aCellIter(pDoc, ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB)); // all sheets
1006 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1008 if (aCellIter.getType() != CELLTYPE_FORMULA)
1009 continue;
1011 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1012 bool bRunning = pFCell->IsRunning();
1014 if (pFCell->GetDirty())
1015 pFCell->Interpret(); // nach SetRunning geht's nicht mehr!
1016 pFCell->SetRunning(true);
1018 ScDetectiveRefIter aIter(pFCell);
1019 ScRange aRef;
1020 while ( aIter.GetNextRef( aRef) )
1022 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1024 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1025 aRef.aStart.Col(),aRef.aStart.Row(),
1026 aRef.aEnd.Col(),aRef.aEnd.Row() ))
1028 sal_Bool bAlien = ( aCellIter.GetPos().Tab() != nTab );
1029 sal_Bool bDrawRet;
1030 if (bAlien)
1031 bDrawRet = DrawAlienEntry( aRef, rData );
1032 else
1033 bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1034 aRef, rData );
1035 if (bDrawRet)
1037 nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen
1039 else
1041 if (bRunning)
1043 if (nResult == DET_INS_EMPTY)
1044 nResult = DET_INS_CIRCULAR;
1046 else
1048 // weiterverfolgen
1050 if ( nLevel < rData.GetMaxLevel() )
1052 sal_uInt16 nSubResult = InsertSuccLevel(
1053 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1054 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1055 rData, nLevel+1 );
1056 switch (nSubResult)
1058 case DET_INS_INSERTED:
1059 nResult = DET_INS_INSERTED;
1060 break;
1061 case DET_INS_CONTINUE:
1062 if (nResult != DET_INS_INSERTED)
1063 nResult = DET_INS_CONTINUE;
1064 break;
1065 case DET_INS_CIRCULAR:
1066 if (nResult == DET_INS_EMPTY)
1067 nResult = DET_INS_CIRCULAR;
1068 break;
1069 // DET_INS_EMPTY: unveraendert lassen
1072 else // nMaxLevel erreicht
1073 if (nResult != DET_INS_INSERTED)
1074 nResult = DET_INS_CONTINUE;
1080 pFCell->SetRunning(bRunning);
1083 return nResult;
1086 sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1087 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
1089 OSL_ENSURE( nLevel<1000, "Level" );
1091 sal_uInt16 nResult = nLevel;
1092 sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
1094 ScCellIterator aCellIter( pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab) );
1095 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1097 if (aCellIter.getType() != CELLTYPE_FORMULA)
1098 continue;
1100 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1101 bool bRunning = pFCell->IsRunning();
1103 if (pFCell->GetDirty())
1104 pFCell->Interpret(); // nach SetRunning geht's nicht mehr!
1105 pFCell->SetRunning(true);
1107 ScDetectiveRefIter aIter(pFCell);
1108 ScRange aRef;
1109 while ( aIter.GetNextRef( aRef) )
1111 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1113 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1114 aRef.aStart.Col(),aRef.aStart.Row(),
1115 aRef.aEnd.Col(),aRef.aEnd.Row() ))
1117 if ( bDelete ) // Pfeile, die hier anfangen
1119 if (aRef.aStart != aRef.aEnd)
1121 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
1122 aRef.aEnd.Col(), aRef.aEnd.Row() );
1124 DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), false );
1126 else if ( !bRunning &&
1127 HasArrow( aRef.aStart,
1128 aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
1130 sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1131 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1132 nLevel+1, nDeleteLevel );
1133 if (nTemp > nResult)
1134 nResult = nTemp;
1140 pFCell->SetRunning(bRunning);
1143 return nResult;
1150 sal_Bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1152 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1153 if (!pModel)
1154 return false;
1156 ScDetectiveData aData( pModel );
1158 sal_uInt16 nMaxLevel = 0;
1159 sal_uInt16 nResult = DET_INS_CONTINUE;
1160 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1162 aData.SetMaxLevel( nMaxLevel );
1163 nResult = InsertPredLevel( nCol, nRow, aData, 0 );
1164 ++nMaxLevel;
1167 return ( nResult == DET_INS_INSERTED );
1170 sal_Bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1172 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1173 if (!pModel)
1174 return false;
1176 ScDetectiveData aData( pModel );
1178 sal_uInt16 nMaxLevel = 0;
1179 sal_uInt16 nResult = DET_INS_CONTINUE;
1180 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1182 aData.SetMaxLevel( nMaxLevel );
1183 nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
1184 ++nMaxLevel;
1187 return ( nResult == DET_INS_INSERTED );
1190 sal_Bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1192 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1193 if (!pModel)
1194 return false;
1196 ScRange aRange( nCol, nRow, nTab );
1197 ScAddress aErrPos;
1198 if ( !HasError( aRange,aErrPos ) )
1199 return false;
1201 ScDetectiveData aData( pModel );
1203 aData.SetMaxLevel( 1000 );
1204 sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
1206 return ( nResult == DET_INS_INSERTED );
1209 sal_Bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
1211 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1212 if (!pModel)
1213 return false;
1215 sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1216 if ( nLevelCount )
1217 FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // loeschen
1219 return ( nLevelCount != 0 );
1222 sal_Bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
1224 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1225 if (!pModel)
1226 return false;
1228 sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1229 if ( nLevelCount )
1230 FindPredLevel( nCol, nRow, 0, nLevelCount ); // loeschen
1232 return ( nLevelCount != 0 );
1235 sal_Bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1237 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1238 if (!pModel)
1239 return false;
1241 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1242 OSL_ENSURE(pPage,"Page ?");
1244 pPage->RecalcObjOrdNums();
1246 long nDelCount = 0;
1247 sal_uLong nObjCount = pPage->GetObjCount();
1248 if (nObjCount)
1250 SdrObject** ppObj = new SdrObject*[nObjCount];
1252 SdrObjListIter aIter( *pPage, IM_FLAT );
1253 SdrObject* pObject = aIter.Next();
1254 while (pObject)
1256 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1258 sal_Bool bDoThis = sal_True;
1259 if ( eWhat != SC_DET_ALL )
1261 sal_Bool bCircle = ( pObject->ISA(SdrCircObj) );
1262 sal_Bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
1263 if ( eWhat == SC_DET_DETECTIVE ) // Detektiv, aus Menue
1264 bDoThis = !bCaption; // auch Kreise
1265 else if ( eWhat == SC_DET_CIRCLES ) // Kreise, wenn neue erzeugt werden
1266 bDoThis = bCircle;
1267 else if ( eWhat == SC_DET_ARROWS ) // DetectiveRefresh
1268 bDoThis = !bCaption && !bCircle; // don't include circles
1269 else
1271 OSL_FAIL("what?");
1274 if ( bDoThis )
1275 ppObj[nDelCount++] = pObject;
1278 pObject = aIter.Next();
1281 long i;
1282 for (i=1; i<=nDelCount; i++)
1283 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
1285 for (i=1; i<=nDelCount; i++)
1286 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
1288 delete[] ppObj;
1290 Modified();
1293 return ( nDelCount != 0 );
1296 sal_Bool ScDetectiveFunc::MarkInvalid(sal_Bool& rOverflow)
1298 rOverflow = false;
1299 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1300 if (!pModel)
1301 return false;
1303 sal_Bool bDeleted = DeleteAll( SC_DET_CIRCLES ); // nur die Kreise
1305 ScDetectiveData aData( pModel );
1306 long nInsCount = 0;
1308 // Stellen suchen, wo Gueltigkeit definiert ist
1310 ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW );
1311 SCCOL nCol;
1312 SCROW nRow1;
1313 SCROW nRow2;
1314 const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1315 while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
1317 sal_uLong nIndex = ((const SfxUInt32Item&)pPattern->GetItem(ATTR_VALIDDATA)).GetValue();
1318 if (nIndex)
1320 const ScValidationData* pData = pDoc->GetValidationEntry( nIndex );
1321 if ( pData )
1323 // Zellen in dem Bereich durchgehen
1325 sal_Bool bMarkEmpty = !pData->IsIgnoreBlank();
1326 SCROW nNextRow = nRow1;
1327 SCROW nRow;
1328 ScCellIterator aCellIter( pDoc, ScRange(nCol, nRow1, nTab, nCol, nRow2, nTab) );
1329 for (bool bHas = aCellIter.first(); bHas && nInsCount < SC_DET_MAXCIRCLE; bHas = aCellIter.next())
1331 SCROW nCellRow = aCellIter.GetPos().Row();
1332 if ( bMarkEmpty )
1333 for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1335 DrawCircle( nCol, nRow, aData );
1336 ++nInsCount;
1338 ScRefCellValue aCell = aCellIter.getRefCellValue();
1339 if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
1341 DrawCircle( nCol, nCellRow, aData );
1342 ++nInsCount;
1344 nNextRow = nCellRow + 1;
1346 if ( bMarkEmpty )
1347 for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1349 DrawCircle( nCol, nRow, aData );
1350 ++nInsCount;
1355 pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1358 if ( nInsCount >= SC_DET_MAXCIRCLE )
1359 rOverflow = sal_True;
1361 return ( bDeleted || nInsCount != 0 );
1364 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1365 vector<ScTokenRef>& rRefTokens)
1367 ScCellIterator aIter(pDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1368 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1370 if (aIter.getType() != CELLTYPE_FORMULA)
1371 continue;
1373 ScFormulaCell* pFCell = aIter.getFormulaCell();
1374 ScDetectiveRefIter aRefIter(pFCell);
1375 for (ScToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1377 ScTokenRef pRef(static_cast<ScToken*>(p->Clone()));
1378 ScRefTokenHelper::join(rRefTokens, pRef, aIter.GetPos());
1383 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1384 vector<ScTokenRef>& rRefTokens)
1386 vector<ScTokenRef> aSrcRange;
1387 aSrcRange.push_back(
1388 ScRefTokenHelper::createRefToken(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
1390 ScCellIterator aIter(pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab));
1391 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1393 if (aIter.getType() != CELLTYPE_FORMULA)
1394 continue;
1396 ScFormulaCell* pFCell = aIter.getFormulaCell();
1397 ScDetectiveRefIter aRefIter(pFCell);
1398 for (ScToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1400 const ScAddress& aPos = aIter.GetPos();
1401 ScTokenRef pRef(static_cast<ScToken*>(p->Clone()));
1402 if (ScRefTokenHelper::intersects(aSrcRange, pRef, aPos))
1404 // This address is absolute.
1405 pRef = ScRefTokenHelper::createRefToken(aPos);
1406 ScRefTokenHelper::join(rRefTokens, pRef, ScAddress());
1412 void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
1414 // for all caption objects, update attributes and SpecialTextBoxShadow flag
1415 // (on all tables - nTab is ignored!)
1417 // no undo actions, this is refreshed after undo
1419 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1420 if (!pModel)
1421 return;
1423 for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1425 SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1426 OSL_ENSURE( pPage, "Page ?" );
1427 if( pPage )
1429 SdrObjListIter aIter( *pPage, IM_FLAT );
1430 for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1432 if ( ScDrawObjData* pData = ScDrawLayer::GetNoteCaptionData( pObject, nObjTab ) )
1434 ScPostIt* pNote = rDoc.GetNote( pData->maStart );
1435 // caption should exist, we iterate over drawing objects...
1436 OSL_ENSURE( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" );
1437 if( pNote )
1439 ScCommentData aData( rDoc, pModel );
1440 SfxItemSet aAttrColorSet = pObject->GetMergedItemSet();
1441 aAttrColorSet.Put( XFillColorItem( OUString(), GetCommentColor() ) );
1442 aData.UpdateCaptionSet( aAttrColorSet );
1443 pObject->SetMergedItemSetAndBroadcast( aData.GetCaptionSet() );
1444 if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
1446 pCaption->SetSpecialTextBoxShadow();
1447 pCaption->SetFixedTail();
1456 void ScDetectiveFunc::UpdateAllArrowColors()
1458 // no undo actions necessary
1460 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1461 if (!pModel)
1462 return;
1464 for( SCTAB nObjTab = 0, nTabCount = pDoc->GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1466 SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1467 OSL_ENSURE( pPage, "Page ?" );
1468 if( pPage )
1470 SdrObjListIter aIter( *pPage, IM_FLAT );
1471 for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1473 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1475 sal_Bool bArrow = false;
1476 sal_Bool bError = false;
1478 ScAddress aPos;
1479 ScRange aSource;
1480 bool bDummy;
1481 ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy );
1482 if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
1484 // source is valid, determine error flag from source range
1486 ScAddress aErrPos;
1487 if ( HasError( aSource, aErrPos ) )
1488 bError = sal_True;
1489 else
1490 bArrow = sal_True;
1492 else if ( eType == SC_DETOBJ_FROMOTHERTAB )
1494 // source range is no longer known, take error flag from formula itself
1495 // (this means, if the formula has an error, all references to other tables
1496 // are marked red)
1498 ScAddress aErrPos;
1499 if ( HasError( ScRange( aPos), aErrPos ) )
1500 bError = sal_True;
1501 else
1502 bArrow = sal_True;
1504 else if ( eType == SC_DETOBJ_CIRCLE )
1506 // circles (error marks) are always red
1508 bError = sal_True;
1510 else if ( eType == SC_DETOBJ_NONE )
1512 // frame for area reference has no ObjType, always gets arrow color
1514 if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) )
1516 bArrow = sal_True;
1520 if ( bArrow || bError )
1522 ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() );
1523 pObject->SetMergedItem( XLineColorItem( OUString(), Color( nColorData ) ) );
1525 // repaint only
1526 pObject->ActionChanged();
1534 sal_Bool ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange )
1536 // find the rectangle for an arrow (always the object directly before the arrow)
1537 // rRange must be initialized to the source cell of the arrow (start of area)
1539 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1540 if (!pModel) return false;
1542 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1543 OSL_ENSURE(pPage,"Page ?");
1544 if (!pPage) return false;
1546 // test if the object is a direct page member
1547 if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) )
1549 // Is there a previous object?
1550 const sal_uInt32 nOrdNum(pObject->GetOrdNum());
1552 if(nOrdNum > 0)
1554 SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
1556 if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) )
1558 ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
1559 if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
1561 rRange.aEnd = pPrevData->maEnd;
1562 return sal_True;
1567 return false;
1570 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1571 ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
1573 rRedLine = false;
1574 ScDetectiveObjType eType = SC_DETOBJ_NONE;
1576 if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
1578 if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
1580 bool bValidStart = pData->maStart.IsValid();
1581 bool bValidEnd = pData->maEnd.IsValid();
1583 if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
1585 // line object -> arrow
1587 if ( bValidStart )
1588 eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1589 else if ( bValidEnd )
1590 eType = SC_DETOBJ_FROMOTHERTAB;
1592 if ( bValidStart )
1593 rSource = pData->maStart;
1594 if ( bValidEnd )
1595 rPosition = pData->maEnd;
1597 if ( bValidStart && lcl_HasThickLine( *pObject ) )
1599 // thick line -> look for frame before this object
1601 FindFrameForObject( pObject, rSource ); // modifies rSource
1604 ColorData nObjColor = ((const XLineColorItem&)pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor();
1605 if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
1606 rRedLine = true;
1608 else if ( pObject->ISA(SdrCircObj) )
1610 if ( bValidStart )
1612 // cell position is returned in rPosition
1614 rPosition = pData->maStart;
1615 eType = SC_DETOBJ_CIRCLE;
1621 return eType;
1624 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1625 const ScAddress& rPosition, const ScRange& rSource,
1626 sal_Bool bRedLine )
1628 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1629 if (!pModel) return;
1630 ScDetectiveData aData( pModel );
1632 switch (eType)
1634 case SC_DETOBJ_ARROW:
1635 case SC_DETOBJ_FROMOTHERTAB:
1636 InsertArrow( rPosition.Col(), rPosition.Row(),
1637 rSource.aStart.Col(), rSource.aStart.Row(),
1638 rSource.aEnd.Col(), rSource.aEnd.Row(),
1639 (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData );
1640 break;
1641 case SC_DETOBJ_TOOTHERTAB:
1642 InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
1643 rSource.aEnd.Col(), rSource.aEnd.Row(),
1644 bRedLine, aData );
1645 break;
1646 case SC_DETOBJ_CIRCLE:
1647 DrawCircle( rPosition.Col(), rPosition.Row(), aData );
1648 break;
1649 default:
1651 // added to avoid warnings
1656 ColorData ScDetectiveFunc::GetArrowColor()
1658 if (!bColorsInitialized)
1659 InitializeColors();
1660 return nArrowColor;
1663 ColorData ScDetectiveFunc::GetErrorColor()
1665 if (!bColorsInitialized)
1666 InitializeColors();
1667 return nErrorColor;
1670 ColorData ScDetectiveFunc::GetCommentColor()
1672 if (!bColorsInitialized)
1673 InitializeColors();
1674 return nCommentColor;
1677 void ScDetectiveFunc::InitializeColors()
1679 // may be called several times to update colors from configuration
1681 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1682 nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
1683 nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
1684 nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
1686 bColorsInitialized = sal_True;
1689 sal_Bool ScDetectiveFunc::IsColorsInitialized()
1691 return bColorsInitialized;
1694 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */