fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / detfunc.cxx
blobd21476f6e5623e04b9ad0b957a2913de1dc6e5cc
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>
73 #include <boost/scoped_array.hpp>
75 using ::std::vector;
76 using namespace com::sun::star;
78 // line ends are now created with an empty name.
79 // The checkForUniqueItem method then finds a unique name for the item's value.
80 #define SC_LINEEND_NAME EMPTY_OUSTRING
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 };
88 class ScDetectiveData
90 private:
91 SfxItemSet aBoxSet;
92 SfxItemSet aArrowSet;
93 SfxItemSet aToTabSet;
94 SfxItemSet aFromTabSet;
95 SfxItemSet aCircleSet; //TODO: individually ?
96 sal_uInt16 nMaxLevel;
98 public:
99 ScDetectiveData( SdrModel* pModel );
101 SfxItemSet& GetBoxSet() { return aBoxSet; }
102 SfxItemSet& GetArrowSet() { return aArrowSet; }
103 SfxItemSet& GetToTabSet() { return aToTabSet; }
104 SfxItemSet& GetFromTabSet() { return aFromTabSet; }
105 SfxItemSet& GetCircleSet() { return aCircleSet; }
107 void SetMaxLevel( sal_uInt16 nVal ) { nMaxLevel = nVal; }
108 sal_uInt16 GetMaxLevel() const { return nMaxLevel; }
111 class ScCommentData
113 public:
114 ScCommentData( ScDocument& rDoc, SdrModel* pModel );
116 SfxItemSet& GetCaptionSet() { return aCaptionSet; }
117 void UpdateCaptionSet( const SfxItemSet& rItemSet );
119 private:
120 SfxItemSet aCaptionSet;
123 ColorData ScDetectiveFunc::nArrowColor = 0;
124 ColorData ScDetectiveFunc::nErrorColor = 0;
125 ColorData ScDetectiveFunc::nCommentColor = 0;
126 bool ScDetectiveFunc::bColorsInitialized = false;
128 static bool lcl_HasThickLine( SdrObject& rObj )
130 // thin lines get width 0 -> everything greater 0 is a thick line
132 return static_cast<const XLineWidthItem&>(rObj.GetMergedItem(XATTR_LINEWIDTH)).GetValue() > 0;
135 ScDetectiveData::ScDetectiveData( SdrModel* pModel ) :
136 aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
137 aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
138 aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
139 aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
140 aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END )
142 nMaxLevel = 0;
144 aBoxSet.Put( XLineColorItem( EMPTY_OUSTRING, Color( ScDetectiveFunc::GetArrowColor() ) ) );
145 aBoxSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
147 // create default line endings (like XLineEndList::Create)
148 // to be independent from the configured line endings
150 basegfx::B2DPolygon aTriangle;
151 aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
152 aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
153 aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
154 aTriangle.setClosed(true);
156 basegfx::B2DPolygon aSquare;
157 aSquare.append(basegfx::B2DPoint(0.0, 0.0));
158 aSquare.append(basegfx::B2DPoint(10.0, 0.0));
159 aSquare.append(basegfx::B2DPoint(10.0, 10.0));
160 aSquare.append(basegfx::B2DPoint(0.0, 10.0));
161 aSquare.setClosed(true);
163 basegfx::B2DPolygon aCircle(basegfx::tools::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
164 aCircle.setClosed(true);
166 OUString aName = SC_LINEEND_NAME;
168 aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
169 aArrowSet.Put( XLineStartWidthItem( 200 ) );
170 aArrowSet.Put( XLineStartCenterItem( true ) );
171 aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
172 aArrowSet.Put( XLineEndWidthItem( 200 ) );
173 aArrowSet.Put( XLineEndCenterItem( false ) );
175 aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
176 aToTabSet.Put( XLineStartWidthItem( 200 ) );
177 aToTabSet.Put( XLineStartCenterItem( true ) );
178 aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
179 aToTabSet.Put( XLineEndWidthItem( 300 ) );
180 aToTabSet.Put( XLineEndCenterItem( false ) );
182 aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
183 aFromTabSet.Put( XLineStartWidthItem( 300 ) );
184 aFromTabSet.Put( XLineStartCenterItem( true ) );
185 aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
186 aFromTabSet.Put( XLineEndWidthItem( 200 ) );
187 aFromTabSet.Put( XLineEndCenterItem( false ) );
189 aCircleSet.Put( XLineColorItem( OUString(), Color( ScDetectiveFunc::GetErrorColor() ) ) );
190 aCircleSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
191 sal_uInt16 nWidth = 55; // 54 = 1 Pixel
192 aCircleSet.Put( XLineWidthItem( nWidth ) );
195 ScCommentData::ScCommentData( ScDocument& rDoc, SdrModel* pModel ) :
196 aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END, EE_ITEMS_START, EE_ITEMS_END, 0, 0 )
198 basegfx::B2DPolygon aTriangle;
199 aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
200 aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
201 aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
202 aTriangle.setClosed(true);
204 OUString aName = SC_LINEEND_NAME;
206 aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
207 aCaptionSet.Put( XLineStartWidthItem( 200 ) );
208 aCaptionSet.Put( XLineStartCenterItem( false ) );
209 aCaptionSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
210 Color aYellow( ScDetectiveFunc::GetCommentColor() );
211 aCaptionSet.Put( XFillColorItem( OUString(), aYellow ) );
213 // shadow
214 // SdrShadowItem has sal_False, instead the shadow is set for the rectangle
215 // only with SetSpecialTextBoxShadow when the object is created
216 // (item must be set to adjust objects from older files)
217 aCaptionSet.Put( makeSdrShadowItem( false ) );
218 aCaptionSet.Put( makeSdrShadowXDistItem( 100 ) );
219 aCaptionSet.Put( makeSdrShadowYDistItem( 100 ) );
221 // text attributes
222 aCaptionSet.Put( makeSdrTextLeftDistItem( 100 ) );
223 aCaptionSet.Put( makeSdrTextRightDistItem( 100 ) );
224 aCaptionSet.Put( makeSdrTextUpperDistItem( 100 ) );
225 aCaptionSet.Put( makeSdrTextLowerDistItem( 100 ) );
227 aCaptionSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
228 aCaptionSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
230 // do use the default cell style, so the user has a chance to
231 // modify the font for the annotations
232 static_cast<const ScPatternAttr&>(rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN)).
233 FillEditItemSet( &aCaptionSet );
235 // support the best position for the tail connector now that
236 // that notes can be resized and repositioned.
237 aCaptionSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT) );
240 void ScCommentData::UpdateCaptionSet( const SfxItemSet& rItemSet )
242 SfxWhichIter aWhichIter( rItemSet );
243 const SfxPoolItem* pPoolItem = 0;
245 for( sal_uInt16 nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() )
247 if(rItemSet.GetItemState(nWhich, false, &pPoolItem) == SfxItemState::SET)
249 switch(nWhich)
251 case SDRATTR_SHADOW:
252 // use existing Caption default - appears that setting this
253 // to true screws up the tail appearance. See also comment
254 // for default setting above.
255 break;
256 case SDRATTR_SHADOWXDIST:
257 // use existing Caption default - svx sets a value of 35
258 // but default 100 gives a better appearance.
259 break;
260 case SDRATTR_SHADOWYDIST:
261 // use existing Caption default - svx sets a value of 35
262 // but default 100 gives a better appearance.
263 break;
265 default:
266 aCaptionSet.Put(*pPoolItem);
272 void ScDetectiveFunc::Modified()
274 if (pDoc->IsStreamValid(nTab))
275 pDoc->SetStreamValid(nTab, false);
278 inline bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1,
279 SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 )
281 return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
282 nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
285 bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
287 rErrPos = rRange.aStart;
288 sal_uInt16 nError = 0;
290 ScCellIterator aIter( pDoc, rRange);
291 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
293 if (aIter.getType() != CELLTYPE_FORMULA)
294 continue;
296 nError = aIter.getFormulaCell()->GetErrCode();
297 if (nError)
298 rErrPos = aIter.GetPos();
301 return (nError != 0);
304 Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
306 OSL_ENSURE( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
307 nCol = SanitizeCol( nCol );
308 nRow = SanitizeRow( nRow );
310 Point aPos;
312 switch( eMode )
314 case DRAWPOS_TOPLEFT:
315 break;
316 case DRAWPOS_BOTTOMRIGHT:
317 ++nCol;
318 ++nRow;
319 break;
320 case DRAWPOS_DETARROW:
321 aPos.X() += pDoc->GetColWidth( nCol, nTab ) / 4;
322 aPos.Y() += pDoc->GetRowHeight( nRow, nTab ) / 2;
323 break;
324 case DRAWPOS_CAPTIONLEFT:
325 aPos.X() += 6;
326 break;
327 case DRAWPOS_CAPTIONRIGHT:
329 // find right end of passed cell position
330 const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE ) );
331 if ( pMerge->GetColMerge() > 1 )
332 nCol = nCol + pMerge->GetColMerge();
333 else
334 ++nCol;
335 aPos.X() -= 6;
337 break;
340 for ( SCCOL i = 0; i < nCol; ++i )
341 aPos.X() += pDoc->GetColWidth( i, nTab );
342 aPos.Y() += pDoc->GetRowHeight( 0, nRow - 1, nTab );
344 aPos.X() = static_cast< long >( aPos.X() * HMM_PER_TWIPS );
345 aPos.Y() = static_cast< long >( aPos.Y() * HMM_PER_TWIPS );
347 if ( pDoc->IsNegativePage( nTab ) )
348 aPos.X() *= -1;
350 return aPos;
353 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
355 Rectangle aRect(
356 GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DRAWPOS_TOPLEFT ),
357 GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DRAWPOS_BOTTOMRIGHT ) );
358 aRect.Justify(); // reorder left/right in RTL sheets
359 return aRect;
362 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
364 return GetDrawRect( nCol, nRow, nCol, nRow );
367 static bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
369 // test if rPolygon is the line end for "other table" (rectangle)
370 if(1L == rPolyPolygon.count())
372 const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0L));
374 // #i73305# circle consists of 4 segments, too, distinguishable from square by
375 // the use of control points
376 if(4L == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed())
378 return true;
382 return false;
385 bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
386 SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
388 bool bStartAlien = ( rStart.Tab() != nTab );
389 bool bEndAlien = ( nEndTab != nTab );
391 if (bStartAlien && bEndAlien)
393 OSL_FAIL("bStartAlien && bEndAlien");
394 return true;
397 Rectangle aStartRect;
398 Rectangle aEndRect;
399 if (!bStartAlien)
400 aStartRect = GetDrawRect( rStart.Col(), rStart.Row() );
401 if (!bEndAlien)
402 aEndRect = GetDrawRect( nEndCol, nEndRow );
404 ScDrawLayer* pModel = pDoc->GetDrawLayer();
405 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
406 OSL_ENSURE(pPage,"Page ?");
408 bool bFound = false;
409 SdrObjListIter aIter( *pPage, IM_FLAT );
410 SdrObject* pObject = aIter.Next();
411 while (pObject && !bFound)
413 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
414 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
416 const SfxItemSet& rSet = pObject->GetMergedItemSet();
418 bool bObjStartAlien =
419 lcl_IsOtherTab( static_cast<const XLineStartItem&>(rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
420 bool bObjEndAlien =
421 lcl_IsOtherTab( static_cast<const XLineEndItem&>(rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
423 bool bStartHit = bStartAlien ? bObjStartAlien :
424 ( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) );
425 bool bEndHit = bEndAlien ? bObjEndAlien :
426 ( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) );
428 if ( bStartHit && bEndHit )
429 bFound = true;
431 pObject = aIter.Next();
434 return bFound;
437 bool ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject )
439 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
440 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
442 const SfxItemSet& rSet = pObject->GetMergedItemSet();
444 bool bObjStartAlien =
445 lcl_IsOtherTab( static_cast<const XLineStartItem&>(rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
446 bool bObjEndAlien =
447 lcl_IsOtherTab( static_cast<const XLineEndItem&>(rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
449 return !bObjStartAlien && !bObjEndAlien;
452 return false;
455 // InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
457 bool ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow,
458 SCCOL nRefStartCol, SCROW nRefStartRow,
459 SCCOL nRefEndCol, SCROW nRefEndRow,
460 bool bFromOtherTab, bool bRed,
461 ScDetectiveData& rData )
463 ScDrawLayer* pModel = pDoc->GetDrawLayer();
464 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
466 bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
467 if (bArea && !bFromOtherTab)
469 // insert the rectangle before the arrow - this is relied on in FindFrameForObject
471 Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow );
472 SdrRectObj* pBox = new SdrRectObj( aRect );
474 pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
476 pBox->SetLayer( SC_LAYER_INTERN );
477 pPage->InsertObject( pBox );
478 pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
480 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, true );
481 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
482 pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
485 Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DRAWPOS_DETARROW );
486 Point aEndPos = GetDrawPos( nCol, nRow, DRAWPOS_DETARROW );
488 if (bFromOtherTab)
490 bool bNegativePage = pDoc->IsNegativePage( nTab );
491 long nPageSign = bNegativePage ? -1 : 1;
493 aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 );
494 if (aStartPos.X() * nPageSign < 0)
495 aStartPos.X() += 2000 * nPageSign;
496 if (aStartPos.Y() < 0)
497 aStartPos.Y() += 2000;
500 SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
502 if (bArea && !bFromOtherTab)
503 rAttrSet.Put( XLineWidthItem( 50 ) ); // range
504 else
505 rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
507 ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
508 rAttrSet.Put( XLineColorItem( OUString(), Color( nColorData ) ) );
510 basegfx::B2DPolygon aTempPoly;
511 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
512 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
513 SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
514 pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //TODO: needed ???
515 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
517 pArrow->SetLayer( SC_LAYER_INTERN );
518 pPage->InsertObject( pArrow );
519 pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
521 ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow, true);
522 if (bFromOtherTab)
523 pData->maStart.SetInvalid();
524 else
525 pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
527 pData->maEnd.Set( nCol, nRow, nTab);
528 pData->meType = ScDrawObjData::DetectiveArrow;
530 Modified();
531 return true;
534 bool ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
535 SCCOL nEndCol, SCROW nEndRow, bool bRed,
536 ScDetectiveData& rData )
538 ScDrawLayer* pModel = pDoc->GetDrawLayer();
539 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
541 bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
542 if (bArea)
544 Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
545 SdrRectObj* pBox = new SdrRectObj( aRect );
547 pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
549 pBox->SetLayer( SC_LAYER_INTERN );
550 pPage->InsertObject( pBox );
551 pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
553 ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, true );
554 pData->maStart.Set( nStartCol, nStartRow, nTab);
555 pData->maEnd.Set( nEndCol, nEndRow, nTab);
558 bool bNegativePage = pDoc->IsNegativePage( nTab );
559 long nPageSign = bNegativePage ? -1 : 1;
561 Point aStartPos = GetDrawPos( nStartCol, nStartRow, DRAWPOS_DETARROW );
562 Point aEndPos = Point( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 );
563 if (aEndPos.Y() < 0)
564 aEndPos.Y() += 2000;
566 SfxItemSet& rAttrSet = rData.GetToTabSet();
567 if (bArea)
568 rAttrSet.Put( XLineWidthItem( 50 ) ); // range
569 else
570 rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
572 ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
573 rAttrSet.Put( XLineColorItem( OUString(), Color( nColorData ) ) );
575 basegfx::B2DPolygon aTempPoly;
576 aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
577 aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
578 SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
579 pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //TODO: needed ???
581 pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
583 pArrow->SetLayer( SC_LAYER_INTERN );
584 pPage->InsertObject( pArrow );
585 pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
587 ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, true );
588 pData->maStart.Set( nStartCol, nStartRow, nTab);
589 pData->maEnd.SetInvalid();
591 Modified();
592 return true;
595 // DrawEntry: formula from this spreadsheet,
596 // reference on this or other
597 // DrawAlienEntry: formula from other spreadsheet,
598 // reference on this
600 // return FALSE: there was already an arrow
602 bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
603 const ScRange& rRef,
604 ScDetectiveData& rData )
606 if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
607 return false;
609 ScAddress aErrorPos;
610 bool bError = HasError( rRef, aErrorPos );
611 bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
613 return InsertArrow( nCol, nRow,
614 rRef.aStart.Col(), rRef.aStart.Row(),
615 rRef.aEnd.Col(), rRef.aEnd.Row(),
616 bAlien, bError, rData );
619 bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
620 ScDetectiveData& rData )
622 if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
623 return false;
625 ScAddress aErrorPos;
626 bool bError = HasError( rRef, aErrorPos );
628 return InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
629 rRef.aEnd.Col(), rRef.aEnd.Row(),
630 bError, rData );
633 void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
635 ScDrawLayer* pModel = pDoc->GetDrawLayer();
636 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
638 Rectangle aRect = GetDrawRect( nCol, nRow );
639 aRect.Left() -= 250;
640 aRect.Right() += 250;
641 aRect.Top() -= 70;
642 aRect.Bottom() += 70;
644 SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect );
645 SfxItemSet& rAttrSet = rData.GetCircleSet();
647 pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
649 pCircle->SetLayer( SC_LAYER_INTERN );
650 pPage->InsertObject( pCircle );
651 pModel->AddCalcUndo( new SdrUndoInsertObj( *pCircle ) );
653 ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, true );
654 pData->maStart.Set( nCol, nRow, nTab);
655 pData->maEnd.SetInvalid();
656 pData->meType = ScDrawObjData::ValidationCircle;
658 Modified();
661 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, bool bDestPnt )
663 Rectangle aRect = GetDrawRect( nCol, nRow );
665 ScDrawLayer* pModel = pDoc->GetDrawLayer();
666 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
667 OSL_ENSURE(pPage,"Page ?");
669 pPage->RecalcObjOrdNums();
671 const size_t nObjCount = pPage->GetObjCount();
672 if (nObjCount)
674 size_t nDelCount = 0;
675 boost::scoped_array<SdrObject*> ppObj(new SdrObject*[nObjCount]);
677 SdrObjListIter aIter( *pPage, IM_FLAT );
678 SdrObject* pObject = aIter.Next();
679 while (pObject)
681 if ( pObject->GetLayer()==SC_LAYER_INTERN &&
682 pObject->IsPolyObj() && pObject->GetPointCount()==2 )
684 if (aRect.IsInside(pObject->GetPoint(bDestPnt ? 1 : 0))) // start/destinationpoint
685 ppObj[nDelCount++] = pObject;
688 pObject = aIter.Next();
691 for (size_t i=1; i<=nDelCount; ++i)
692 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
694 for (size_t i=1; i<=nDelCount; ++i)
695 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
697 ppObj.reset();
699 Modified();
703 // delete box around reference
705 #define SC_DET_TOLERANCE 50
707 inline bool RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd )
709 return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE
710 && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE
711 && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE
712 && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE
713 && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE
714 && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE
715 && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE
716 && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE;
719 #undef SC_DET_TOLERANCE
721 void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
723 Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
724 Point aStartCorner = aCornerRect.TopLeft();
725 Point aEndCorner = aCornerRect.BottomRight();
726 Rectangle aObjRect;
728 ScDrawLayer* pModel = pDoc->GetDrawLayer();
729 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
730 OSL_ENSURE(pPage,"Page ?");
732 pPage->RecalcObjOrdNums();
734 const size_t nObjCount = pPage->GetObjCount();
735 if (nObjCount)
737 size_t nDelCount = 0;
738 boost::scoped_array<SdrObject*> ppObj(new SdrObject*[nObjCount]);
740 SdrObjListIter aIter( *pPage, IM_FLAT );
741 SdrObject* pObject = aIter.Next();
742 while (pObject)
744 if ( pObject->GetLayer() == SC_LAYER_INTERN &&
745 pObject->Type() == TYPE(SdrRectObj) )
747 aObjRect = static_cast<SdrRectObj*>(pObject)->GetLogicRect();
748 aObjRect.Justify();
749 if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
750 ppObj[nDelCount++] = pObject;
753 pObject = aIter.Next();
756 for (size_t i=1; i<=nDelCount; ++i)
757 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
759 for (size_t i=1; i<=nDelCount; ++i)
760 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
762 ppObj.reset();
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(); // can't be called after SetRunning
815 pFCell->SetRunning(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; // insert new arrow
827 else
829 // continue
831 if ( nLevel < rData.GetMaxLevel() )
833 sal_uInt16 nSubResult;
834 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: no change
857 else // nMaxLevel reached
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 -> delete
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(); // can't be called after SetRunning
904 pFCell->SetRunning(true);
906 sal_uInt16 nResult = nLevel;
907 bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
909 if ( bDelete )
911 DeleteArrowsAt( nCol, nRow, true ); // arrows, that are pointing here
914 ScDetectiveRefIter aIter(pFCell);
915 ScRange aRef;
916 while ( aIter.GetNextRef( aRef) )
918 bool bArea = ( aRef.aStart != aRef.aEnd );
920 if ( bDelete ) // delete frame ?
922 if (bArea)
924 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
927 else // continue searching
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(); // can't be called after SetRunning
962 pFCell->SetRunning(true);
964 sal_uInt16 nResult = DET_INS_EMPTY;
966 ScDetectiveRefIter aIter(pFCell);
967 ScRange aRef;
968 ScAddress aErrorPos;
969 bool bHasError = false;
970 while ( aIter.GetNextRef( aRef ) )
972 if (HasError( aRef, aErrorPos ))
974 bHasError = true;
975 if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
976 nResult = DET_INS_INSERTED;
978 if ( nLevel < rData.GetMaxLevel() ) // hits most of the time
980 if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
981 rData, nLevel+1 ) == DET_INS_INSERTED)
982 nResult = DET_INS_INSERTED;
987 pFCell->SetRunning(false);
989 // leaves ?
990 if (!bHasError)
991 if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
992 nResult = DET_INS_INSERTED;
994 return nResult;
997 sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
998 ScDetectiveData& rData, sal_uInt16 nLevel )
1000 // over the entire document.
1002 sal_uInt16 nResult = DET_INS_EMPTY;
1003 ScCellIterator aCellIter(pDoc, ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB)); // all sheets
1004 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1006 if (aCellIter.getType() != CELLTYPE_FORMULA)
1007 continue;
1009 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1010 bool bRunning = pFCell->IsRunning();
1012 if (pFCell->GetDirty())
1013 pFCell->Interpret(); // can't be called after SetRunning
1014 pFCell->SetRunning(true);
1016 ScDetectiveRefIter aIter(pFCell);
1017 ScRange aRef;
1018 while ( aIter.GetNextRef( aRef) )
1020 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1022 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1023 aRef.aStart.Col(),aRef.aStart.Row(),
1024 aRef.aEnd.Col(),aRef.aEnd.Row() ))
1026 bool bAlien = ( aCellIter.GetPos().Tab() != nTab );
1027 bool bDrawRet;
1028 if (bAlien)
1029 bDrawRet = DrawAlienEntry( aRef, rData );
1030 else
1031 bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1032 aRef, rData );
1033 if (bDrawRet)
1035 nResult = DET_INS_INSERTED; // insert new arrow
1037 else
1039 if (bRunning)
1041 if (nResult == DET_INS_EMPTY)
1042 nResult = DET_INS_CIRCULAR;
1044 else
1047 if ( nLevel < rData.GetMaxLevel() )
1049 sal_uInt16 nSubResult = InsertSuccLevel(
1050 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1051 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1052 rData, nLevel+1 );
1053 switch (nSubResult)
1055 case DET_INS_INSERTED:
1056 nResult = DET_INS_INSERTED;
1057 break;
1058 case DET_INS_CONTINUE:
1059 if (nResult != DET_INS_INSERTED)
1060 nResult = DET_INS_CONTINUE;
1061 break;
1062 case DET_INS_CIRCULAR:
1063 if (nResult == DET_INS_EMPTY)
1064 nResult = DET_INS_CIRCULAR;
1065 break;
1066 // DET_INS_EMPTY: leave unchanged
1069 else // nMaxLevel reached
1070 if (nResult != DET_INS_INSERTED)
1071 nResult = DET_INS_CONTINUE;
1077 pFCell->SetRunning(bRunning);
1080 return nResult;
1083 sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1084 sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
1086 OSL_ENSURE( nLevel<1000, "Level" );
1088 sal_uInt16 nResult = nLevel;
1089 bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
1091 ScCellIterator aCellIter( pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab) );
1092 for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
1094 if (aCellIter.getType() != CELLTYPE_FORMULA)
1095 continue;
1097 ScFormulaCell* pFCell = aCellIter.getFormulaCell();
1098 bool bRunning = pFCell->IsRunning();
1100 if (pFCell->GetDirty())
1101 pFCell->Interpret(); // can't be called after SetRunning
1102 pFCell->SetRunning(true);
1104 ScDetectiveRefIter aIter(pFCell);
1105 ScRange aRef;
1106 while ( aIter.GetNextRef( aRef) )
1108 if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1110 if (Intersect( nCol1,nRow1,nCol2,nRow2,
1111 aRef.aStart.Col(),aRef.aStart.Row(),
1112 aRef.aEnd.Col(),aRef.aEnd.Row() ))
1114 if ( bDelete ) // arrows, that are starting here
1116 if (aRef.aStart != aRef.aEnd)
1118 DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
1119 aRef.aEnd.Col(), aRef.aEnd.Row() );
1121 DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), false );
1123 else if ( !bRunning &&
1124 HasArrow( aRef.aStart,
1125 aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
1127 sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1128 aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
1129 nLevel+1, nDeleteLevel );
1130 if (nTemp > nResult)
1131 nResult = nTemp;
1137 pFCell->SetRunning(bRunning);
1140 return nResult;
1143 bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1145 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1146 if (!pModel)
1147 return false;
1149 ScDetectiveData aData( pModel );
1151 sal_uInt16 nMaxLevel = 0;
1152 sal_uInt16 nResult = DET_INS_CONTINUE;
1153 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1155 aData.SetMaxLevel( nMaxLevel );
1156 nResult = InsertPredLevel( nCol, nRow, aData, 0 );
1157 ++nMaxLevel;
1160 return ( nResult == DET_INS_INSERTED );
1163 bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1165 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1166 if (!pModel)
1167 return false;
1169 ScDetectiveData aData( pModel );
1171 sal_uInt16 nMaxLevel = 0;
1172 sal_uInt16 nResult = DET_INS_CONTINUE;
1173 while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1175 aData.SetMaxLevel( nMaxLevel );
1176 nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
1177 ++nMaxLevel;
1180 return ( nResult == DET_INS_INSERTED );
1183 bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1185 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1186 if (!pModel)
1187 return false;
1189 ScRange aRange( nCol, nRow, nTab );
1190 ScAddress aErrPos;
1191 if ( !HasError( aRange,aErrPos ) )
1192 return false;
1194 ScDetectiveData aData( pModel );
1196 aData.SetMaxLevel( 1000 );
1197 sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
1199 return ( nResult == DET_INS_INSERTED );
1202 bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
1204 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1205 if (!pModel)
1206 return false;
1208 sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1209 if ( nLevelCount )
1210 FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // delete
1212 return ( nLevelCount != 0 );
1215 bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
1217 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1218 if (!pModel)
1219 return false;
1221 sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1222 if ( nLevelCount )
1223 FindPredLevel( nCol, nRow, 0, nLevelCount ); // delete
1225 return ( nLevelCount != 0 );
1228 bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1230 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1231 if (!pModel)
1232 return false;
1234 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1235 OSL_ENSURE(pPage,"Page ?");
1237 pPage->RecalcObjOrdNums();
1239 size_t nDelCount = 0;
1240 const size_t nObjCount = pPage->GetObjCount();
1241 if (nObjCount)
1243 boost::scoped_array<SdrObject*> ppObj(new SdrObject*[nObjCount]);
1245 SdrObjListIter aIter( *pPage, IM_FLAT );
1246 SdrObject* pObject = aIter.Next();
1247 while (pObject)
1249 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1251 bool bDoThis = true;
1252 if ( eWhat != SC_DET_ALL )
1254 bool bCircle = ( pObject->ISA(SdrCircObj) );
1255 bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
1256 if ( eWhat == SC_DET_DETECTIVE ) // detektive, from menue
1257 bDoThis = !bCaption; // also circles
1258 else if ( eWhat == SC_DET_CIRCLES ) // circles, if new created
1259 bDoThis = bCircle;
1260 else if ( eWhat == SC_DET_ARROWS ) // DetectiveRefresh
1261 bDoThis = !bCaption && !bCircle; // don't include circles
1262 else
1264 OSL_FAIL("what?");
1267 if ( bDoThis )
1268 ppObj[nDelCount++] = pObject;
1271 pObject = aIter.Next();
1274 for (size_t i=1; i<=nDelCount; ++i)
1275 pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
1277 for (size_t i=1; i<=nDelCount; ++i)
1278 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
1280 ppObj.reset();
1282 Modified();
1285 return ( nDelCount != 0 );
1288 bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
1290 rOverflow = false;
1291 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1292 if (!pModel)
1293 return false;
1295 bool bDeleted = DeleteAll( SC_DET_CIRCLES ); // just circles
1297 ScDetectiveData aData( pModel );
1298 long nInsCount = 0;
1300 // search for valid places
1301 ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW );
1302 SCCOL nCol;
1303 SCROW nRow1;
1304 SCROW nRow2;
1305 const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1306 while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
1308 sal_uLong nIndex = static_cast<const SfxUInt32Item&>(pPattern->GetItem(ATTR_VALIDDATA)).GetValue();
1309 if (nIndex)
1311 const ScValidationData* pData = pDoc->GetValidationEntry( nIndex );
1312 if ( pData )
1314 // pass cells in this area
1316 bool bMarkEmpty = !pData->IsIgnoreBlank();
1317 SCROW nNextRow = nRow1;
1318 SCROW nRow;
1319 ScCellIterator aCellIter( pDoc, ScRange(nCol, nRow1, nTab, nCol, nRow2, nTab) );
1320 for (bool bHas = aCellIter.first(); bHas && nInsCount < SC_DET_MAXCIRCLE; bHas = aCellIter.next())
1322 SCROW nCellRow = aCellIter.GetPos().Row();
1323 if ( bMarkEmpty )
1324 for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1326 DrawCircle( nCol, nRow, aData );
1327 ++nInsCount;
1329 ScRefCellValue aCell = aCellIter.getRefCellValue();
1330 if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
1332 DrawCircle( nCol, nCellRow, aData );
1333 ++nInsCount;
1335 nNextRow = nCellRow + 1;
1337 if ( bMarkEmpty )
1338 for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1340 DrawCircle( nCol, nRow, aData );
1341 ++nInsCount;
1346 pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1349 if ( nInsCount >= SC_DET_MAXCIRCLE )
1350 rOverflow = true;
1352 return ( bDeleted || nInsCount != 0 );
1355 void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1356 vector<ScTokenRef>& rRefTokens)
1358 ScCellIterator aIter(pDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
1359 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1361 if (aIter.getType() != CELLTYPE_FORMULA)
1362 continue;
1364 ScFormulaCell* pFCell = aIter.getFormulaCell();
1365 ScDetectiveRefIter aRefIter(pFCell);
1366 for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1368 ScTokenRef pRef(p->Clone());
1369 ScRefTokenHelper::join(rRefTokens, pRef, aIter.GetPos());
1374 void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1375 vector<ScTokenRef>& rRefTokens)
1377 vector<ScTokenRef> aSrcRange;
1378 aSrcRange.push_back(
1379 ScRefTokenHelper::createRefToken(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
1381 ScCellIterator aIter(pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab));
1382 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
1384 if (aIter.getType() != CELLTYPE_FORMULA)
1385 continue;
1387 ScFormulaCell* pFCell = aIter.getFormulaCell();
1388 ScDetectiveRefIter aRefIter(pFCell);
1389 for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
1391 const ScAddress& aPos = aIter.GetPos();
1392 ScTokenRef pRef(p->Clone());
1393 if (ScRefTokenHelper::intersects(aSrcRange, pRef, aPos))
1395 // This address is absolute.
1396 pRef = ScRefTokenHelper::createRefToken(aPos);
1397 ScRefTokenHelper::join(rRefTokens, pRef, ScAddress());
1403 void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
1405 // for all caption objects, update attributes and SpecialTextBoxShadow flag
1406 // (on all tables - nTab is ignored!)
1408 // no undo actions, this is refreshed after undo
1410 ScDrawLayer* pModel = rDoc.GetDrawLayer();
1411 if (!pModel)
1412 return;
1414 for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1416 SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1417 OSL_ENSURE( pPage, "Page ?" );
1418 if( pPage )
1420 SdrObjListIter aIter( *pPage, IM_FLAT );
1421 for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1423 if ( ScDrawObjData* pData = ScDrawLayer::GetNoteCaptionData( pObject, nObjTab ) )
1425 ScPostIt* pNote = rDoc.GetNote( pData->maStart );
1426 // caption should exist, we iterate over drawing objects...
1427 OSL_ENSURE( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" );
1428 if( pNote )
1430 ScCommentData aData( rDoc, pModel );
1431 SfxItemSet aAttrColorSet = pObject->GetMergedItemSet();
1432 aAttrColorSet.Put( XFillColorItem( OUString(), GetCommentColor() ) );
1433 aData.UpdateCaptionSet( aAttrColorSet );
1434 pObject->SetMergedItemSetAndBroadcast( aData.GetCaptionSet() );
1435 if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
1437 pCaption->SetSpecialTextBoxShadow();
1438 pCaption->SetFixedTail();
1447 void ScDetectiveFunc::UpdateAllArrowColors()
1449 // no undo actions necessary
1451 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1452 if (!pModel)
1453 return;
1455 for( SCTAB nObjTab = 0, nTabCount = pDoc->GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1457 SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1458 OSL_ENSURE( pPage, "Page ?" );
1459 if( pPage )
1461 SdrObjListIter aIter( *pPage, IM_FLAT );
1462 for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1464 if ( pObject->GetLayer() == SC_LAYER_INTERN )
1466 bool bArrow = false;
1467 bool bError = false;
1469 ScAddress aPos;
1470 ScRange aSource;
1471 bool bDummy;
1472 ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy );
1473 if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
1475 // source is valid, determine error flag from source range
1477 ScAddress aErrPos;
1478 if ( HasError( aSource, aErrPos ) )
1479 bError = true;
1480 else
1481 bArrow = true;
1483 else if ( eType == SC_DETOBJ_FROMOTHERTAB )
1485 // source range is no longer known, take error flag from formula itself
1486 // (this means, if the formula has an error, all references to other tables
1487 // are marked red)
1489 ScAddress aErrPos;
1490 if ( HasError( ScRange( aPos), aErrPos ) )
1491 bError = true;
1492 else
1493 bArrow = true;
1495 else if ( eType == SC_DETOBJ_CIRCLE )
1497 // circles (error marks) are always red
1499 bError = true;
1501 else if ( eType == SC_DETOBJ_NONE )
1503 // frame for area reference has no ObjType, always gets arrow color
1505 if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) )
1507 bArrow = true;
1511 if ( bArrow || bError )
1513 ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() );
1514 pObject->SetMergedItem( XLineColorItem( OUString(), Color( nColorData ) ) );
1516 // repaint only
1517 pObject->ActionChanged();
1525 bool ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange )
1527 // find the rectangle for an arrow (always the object directly before the arrow)
1528 // rRange must be initialized to the source cell of the arrow (start of area)
1530 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1531 if (!pModel) return false;
1533 SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1534 OSL_ENSURE(pPage,"Page ?");
1535 if (!pPage) return false;
1537 // test if the object is a direct page member
1538 if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) )
1540 // Is there a previous object?
1541 const size_t nOrdNum = pObject->GetOrdNum();
1543 if(nOrdNum > 0)
1545 SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
1547 if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) )
1549 ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
1550 if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
1552 rRange.aEnd = pPrevData->maEnd;
1553 return true;
1558 return false;
1561 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1562 ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
1564 rRedLine = false;
1565 ScDetectiveObjType eType = SC_DETOBJ_NONE;
1567 if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
1569 if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
1571 bool bValidStart = pData->maStart.IsValid();
1572 bool bValidEnd = pData->maEnd.IsValid();
1574 if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
1576 // line object -> arrow
1578 if ( bValidStart )
1579 eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1580 else if ( bValidEnd )
1581 eType = SC_DETOBJ_FROMOTHERTAB;
1583 if ( bValidStart )
1584 rSource = pData->maStart;
1585 if ( bValidEnd )
1586 rPosition = pData->maEnd;
1588 if ( bValidStart && lcl_HasThickLine( *pObject ) )
1590 // thick line -> look for frame before this object
1592 FindFrameForObject( pObject, rSource ); // modifies rSource
1595 ColorData nObjColor = static_cast<const XLineColorItem&>(pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor();
1596 if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
1597 rRedLine = true;
1599 else if ( pObject->ISA(SdrCircObj) )
1601 if ( bValidStart )
1603 // cell position is returned in rPosition
1605 rPosition = pData->maStart;
1606 eType = SC_DETOBJ_CIRCLE;
1612 return eType;
1615 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1616 const ScAddress& rPosition, const ScRange& rSource,
1617 bool bRedLine )
1619 ScDrawLayer* pModel = pDoc->GetDrawLayer();
1620 if (!pModel) return;
1621 ScDetectiveData aData( pModel );
1623 switch (eType)
1625 case SC_DETOBJ_ARROW:
1626 case SC_DETOBJ_FROMOTHERTAB:
1627 InsertArrow( rPosition.Col(), rPosition.Row(),
1628 rSource.aStart.Col(), rSource.aStart.Row(),
1629 rSource.aEnd.Col(), rSource.aEnd.Row(),
1630 (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData );
1631 break;
1632 case SC_DETOBJ_TOOTHERTAB:
1633 InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
1634 rSource.aEnd.Col(), rSource.aEnd.Row(),
1635 bRedLine, aData );
1636 break;
1637 case SC_DETOBJ_CIRCLE:
1638 DrawCircle( rPosition.Col(), rPosition.Row(), aData );
1639 break;
1640 default:
1642 // added to avoid warnings
1647 ColorData ScDetectiveFunc::GetArrowColor()
1649 if (!bColorsInitialized)
1650 InitializeColors();
1651 return nArrowColor;
1654 ColorData ScDetectiveFunc::GetErrorColor()
1656 if (!bColorsInitialized)
1657 InitializeColors();
1658 return nErrorColor;
1661 ColorData ScDetectiveFunc::GetCommentColor()
1663 if (!bColorsInitialized)
1664 InitializeColors();
1665 return nCommentColor;
1668 void ScDetectiveFunc::InitializeColors()
1670 // may be called several times to update colors from configuration
1672 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1673 nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
1674 nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
1675 nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
1677 bColorsInitialized = true;
1680 bool ScDetectiveFunc::IsColorsInitialized()
1682 return bColorsInitialized;
1685 void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString &rDisplay)
1687 rDisplay += "\n--------\n";
1690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */