xref: /trunk/main/sc/source/core/tool/detfunc.cxx (revision 0deba7fb)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 // INCLUDE ---------------------------------------------------------------
28 
29 #include "scitems.hxx"
30 #include <svtools/colorcfg.hxx>
31 #include <editeng/eeitem.hxx>
32 #include <editeng/outlobj.hxx>
33 #include <svx/sdshitm.hxx>
34 #include <svx/sdsxyitm.hxx>
35 #include <svx/sdtditm.hxx>
36 #include <svx/svditer.hxx>
37 #include <svx/svdocapt.hxx>
38 #include <svx/svdocirc.hxx>
39 #include <svx/svdopath.hxx>
40 #include <svx/svdorect.hxx>
41 #include <svx/svdpage.hxx>
42 #include <svx/svdundo.hxx>
43 #include <svx/xfillit0.hxx>
44 #include <svx/xflclit.hxx>
45 #include <svx/xlnclit.hxx>
46 #include <svx/xlnedcit.hxx>
47 #include <svx/xlnedit.hxx>
48 #include <svx/xlnedwit.hxx>
49 #include <svx/xlnstcit.hxx>
50 #include <svx/xlnstit.hxx>
51 #include <svx/xlnstwit.hxx>
52 #include <svx/xlnwtit.hxx>
53 #include <svx/xtable.hxx>
54 #include <editeng/outliner.hxx>
55 #include <editeng/editobj.hxx>
56 #include <svx/sxcecitm.hxx>
57 #include <svl/whiter.hxx>
58 #include <editeng/writingmodeitem.hxx>
59 
60 #include <basegfx/point/b2dpoint.hxx>
61 #include <basegfx/polygon/b2dpolygontools.hxx>
62 #include <basegfx/polygon/b2dpolygon.hxx>
63 
64 #include "detfunc.hxx"
65 #include "document.hxx"
66 #include "dociter.hxx"
67 #include "drwlayer.hxx"
68 #include "userdat.hxx"
69 #include "validat.hxx"
70 #include "cell.hxx"
71 #include "docpool.hxx"
72 #include "patattr.hxx"
73 #include "attrib.hxx"
74 #include "scmod.hxx"
75 #include "postit.hxx"
76 
77 //------------------------------------------------------------------------
78 
79 // #99319# line ends are now created with an empty name.
80 // The checkForUniqueItem method then finds a unique name for the item's value.
81 #define SC_LINEEND_NAME		EMPTY_STRING
82 
83 //------------------------------------------------------------------------
84 
85 enum DetInsertResult {				// Return-Werte beim Einfuegen in einen Level
86 			DET_INS_CONTINUE,
87 			DET_INS_INSERTED,
88 			DET_INS_EMPTY,
89 			DET_INS_CIRCULAR };
90 
91 
92 //------------------------------------------------------------------------
93 
94 class ScDetectiveData
95 {
96 private:
97 	SfxItemSet	aBoxSet;
98 	SfxItemSet	aArrowSet;
99 	SfxItemSet	aToTabSet;
100 	SfxItemSet	aFromTabSet;
101 	SfxItemSet	aCircleSet;			//! einzeln ?
102 	sal_uInt16		nMaxLevel;
103 
104 public:
105 				ScDetectiveData( SdrModel* pModel );
106 
107 	SfxItemSet&	GetBoxSet()		{ return aBoxSet; }
108 	SfxItemSet&	GetArrowSet()	{ return aArrowSet; }
109 	SfxItemSet&	GetToTabSet()	{ return aToTabSet; }
110 	SfxItemSet&	GetFromTabSet()	{ return aFromTabSet; }
111 	SfxItemSet&	GetCircleSet()	{ return aCircleSet; }
112 
113 	void		SetMaxLevel( sal_uInt16 nVal )		{ nMaxLevel = nVal; }
114 	sal_uInt16		GetMaxLevel() const				{ return nMaxLevel; }
115 };
116 
117 class ScCommentData
118 {
119 public:
120 				        ScCommentData( ScDocument& rDoc, SdrModel* pModel );
121 
122 	SfxItemSet&	        GetCaptionSet()	{ return aCaptionSet; }
123 	void	            UpdateCaptionSet( const SfxItemSet& rItemSet );
124 
125 private:
126 	SfxItemSet	        aCaptionSet;
127 };
128 
129 //------------------------------------------------------------------------
130 
131 ColorData ScDetectiveFunc::nArrowColor = 0;
132 ColorData ScDetectiveFunc::nErrorColor = 0;
133 ColorData ScDetectiveFunc::nCommentColor = 0;
134 sal_Bool ScDetectiveFunc::bColorsInitialized = sal_False;
135 
136 //------------------------------------------------------------------------
137 
138 sal_Bool lcl_HasThickLine( SdrObject& rObj )
139 {
140 	// thin lines get width 0 -> everything greater 0 is a thick line
141 
142 	return ( ((const XLineWidthItem&)rObj.GetMergedItem(XATTR_LINEWIDTH)).GetValue() > 0 );
143 }
144 
145 //------------------------------------------------------------------------
146 
147 ScDetectiveData::ScDetectiveData( SdrModel* pModel ) :
148 	aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
149 	aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
150 	aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
151 	aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
152 	aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END )
153 {
154 	nMaxLevel = 0;
155 
156 	aBoxSet.Put( XLineColorItem( EMPTY_STRING, Color( ScDetectiveFunc::GetArrowColor() ) ) );
157 	aBoxSet.Put( XFillStyleItem( XFILL_NONE ) );
158 
159 	//	#66479# Standard-Linienenden (wie aus XLineEndList::Create) selber zusammenbasteln,
160 	//	um von den konfigurierten Linienenden unabhaengig zu sein
161 
162 	basegfx::B2DPolygon aTriangle;
163 	aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
164 	aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
165 	aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
166 	aTriangle.setClosed(true);
167 
168 	basegfx::B2DPolygon aSquare;
169 	aSquare.append(basegfx::B2DPoint(0.0, 0.0));
170 	aSquare.append(basegfx::B2DPoint(10.0, 0.0));
171 	aSquare.append(basegfx::B2DPoint(10.0, 10.0));
172 	aSquare.append(basegfx::B2DPoint(0.0, 10.0));
173 	aSquare.setClosed(true);
174 
175 	basegfx::B2DPolygon aCircle(basegfx::tools::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0));
176 	aCircle.setClosed(true);
177 
178 	String aName = SC_LINEEND_NAME;
179 
180 	aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
181 	aArrowSet.Put( XLineStartWidthItem( 200 ) );
182 	aArrowSet.Put( XLineStartCenterItem( sal_True ) );
183 	aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
184 	aArrowSet.Put( XLineEndWidthItem( 200 ) );
185 	aArrowSet.Put( XLineEndCenterItem( sal_False ) );
186 
187 	aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
188 	aToTabSet.Put( XLineStartWidthItem( 200 ) );
189 	aToTabSet.Put( XLineStartCenterItem( sal_True ) );
190 	aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
191 	aToTabSet.Put( XLineEndWidthItem( 300 ) );
192 	aToTabSet.Put( XLineEndCenterItem( sal_False ) );
193 
194 	aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
195 	aFromTabSet.Put( XLineStartWidthItem( 300 ) );
196 	aFromTabSet.Put( XLineStartCenterItem( sal_True ) );
197 	aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
198 	aFromTabSet.Put( XLineEndWidthItem( 200 ) );
199 	aFromTabSet.Put( XLineEndCenterItem( sal_False ) );
200 
201 	aCircleSet.Put( XLineColorItem( String(), Color( ScDetectiveFunc::GetErrorColor() ) ) );
202 	aCircleSet.Put( XFillStyleItem( XFILL_NONE ) );
203 	sal_uInt16 nWidth = 55;		// 54 = 1 Pixel
204 	aCircleSet.Put( XLineWidthItem( nWidth ) );
205 }
206 
207 ScCommentData::ScCommentData( ScDocument& rDoc, SdrModel* pModel ) :
208 	aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END, EE_ITEMS_START, EE_ITEMS_END, 0, 0 )
209 {
210 	basegfx::B2DPolygon aTriangle;
211 	aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
212 	aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
213 	aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
214 	aTriangle.setClosed(true);
215 
216 	String aName = SC_LINEEND_NAME;
217 
218 	aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
219 	aCaptionSet.Put( XLineStartWidthItem( 200 ) );
220 	aCaptionSet.Put( XLineStartCenterItem( sal_False ) );
221 	aCaptionSet.Put( XFillStyleItem( XFILL_SOLID ) );
222 	Color aYellow( ScDetectiveFunc::GetCommentColor() );
223 	aCaptionSet.Put( XFillColorItem( String(), aYellow ) );
224 
225 	//	shadow
226 	//	SdrShadowItem has sal_False, instead the shadow is set for the rectangle
227 	//	only with SetSpecialTextBoxShadow when the object is created
228 	//	(item must be set to adjust objects from older files)
229 	aCaptionSet.Put( SdrShadowItem( sal_False ) );
230 	aCaptionSet.Put( SdrShadowXDistItem( 100 ) );
231 	aCaptionSet.Put( SdrShadowYDistItem( 100 ) );
232 
233 	//	text attributes
234 	aCaptionSet.Put( SdrTextLeftDistItem( 100 ) );
235 	aCaptionSet.Put( SdrTextRightDistItem( 100 ) );
236 	aCaptionSet.Put( SdrTextUpperDistItem( 100 ) );
237 	aCaptionSet.Put( SdrTextLowerDistItem( 100 ) );
238 
239     aCaptionSet.Put( SdrTextAutoGrowWidthItem( sal_False ) );
240     aCaptionSet.Put( SdrTextAutoGrowHeightItem( sal_True ) );
241 
242 	//	#78943# do use the default cell style, so the user has a chance to
243 	//	modify the font for the annotations
244 	((const ScPatternAttr&)rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN)).
245 		FillEditItemSet( &aCaptionSet );
246 
247     // support the best position for the tail connector now that
248     // that notes can be resized and repositioned.
249     aCaptionSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT) );
250 }
251 
252 void ScCommentData::UpdateCaptionSet( const SfxItemSet& rItemSet )
253 {
254     SfxWhichIter aWhichIter( rItemSet );
255     const SfxPoolItem* pPoolItem = 0;
256 
257     for( sal_uInt16 nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() )
258     {
259         if(rItemSet.GetItemState(nWhich, sal_False, &pPoolItem) == SFX_ITEM_SET)
260         {
261             switch(nWhich)
262             {
263                 case SDRATTR_SHADOW:
264                     // use existing Caption default - appears that setting this
265                     // to true screws up the tail appearance. See also comment
266                     // for default setting above.
267                 break;
268     	        case SDRATTR_SHADOWXDIST:
269 	                // use existing Caption default - svx sets a value of 35
270                     // but default 100 gives a better appearance.
271                 break;
272                 case SDRATTR_SHADOWYDIST:
273                     // use existing Caption default - svx sets a value of 35
274                     // but default 100 gives a better appearance.
275                 break;
276 
277                 default:
278                     aCaptionSet.Put(*pPoolItem);
279            }
280         }
281     }
282 }
283 
284 //------------------------------------------------------------------------
285 
286 void ScDetectiveFunc::Modified()
287 {
288     if (pDoc->IsStreamValid(nTab))
289         pDoc->SetStreamValid(nTab, sal_False);
290 }
291 
292 inline sal_Bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1,
293 						SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 )
294 {
295 	return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
296 			nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
297 }
298 
299 sal_Bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
300 {
301 	rErrPos = rRange.aStart;
302 	sal_uInt16 nError = 0;
303 
304 	ScCellIterator aCellIter( pDoc, rRange);
305 	ScBaseCell* pCell = aCellIter.GetFirst();
306 	while (pCell)
307 	{
308 		if (pCell->GetCellType() == CELLTYPE_FORMULA)
309 		{
310 			nError = ((ScFormulaCell*)pCell)->GetErrCode();
311 			if (nError)
312 				rErrPos.Set( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetTab() );
313 		}
314 		pCell = aCellIter.GetNext();
315 	}
316 
317 	return (nError != 0);
318 }
319 
320 Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
321 {
322     DBG_ASSERT( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
323     SanitizeCol( nCol );
324     SanitizeRow( nRow );
325 
326     Point aPos;
327 
328     switch( eMode )
329     {
330         case DRAWPOS_TOPLEFT:
331         break;
332         case DRAWPOS_BOTTOMRIGHT:
333             ++nCol;
334             ++nRow;
335         break;
336         case DRAWPOS_DETARROW:
337 			aPos.X() += pDoc->GetColWidth( nCol, nTab ) / 4;
338 			aPos.Y() += pDoc->GetRowHeight( nRow, nTab ) / 2;
339         break;
340         case DRAWPOS_CAPTIONLEFT:
341             aPos.X() += 6;
342         break;
343         case DRAWPOS_CAPTIONRIGHT:
344         {
345             // find right end of passed cell position
346             const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE ) );
347             if ( pMerge->GetColMerge() > 1 )
348                 nCol = nCol + pMerge->GetColMerge();
349             else
350                 ++nCol;
351             aPos.X() -= 6;
352         }
353         break;
354     }
355 
356 	for ( SCCOL i = 0; i < nCol; ++i )
357 		aPos.X() += pDoc->GetColWidth( i, nTab );
358     aPos.Y() += pDoc->GetRowHeight( 0, nRow - 1, nTab );
359 
360 	aPos.X() = static_cast< long >( aPos.X() * HMM_PER_TWIPS );
361 	aPos.Y() = static_cast< long >( aPos.Y() * HMM_PER_TWIPS );
362 
363 	if ( pDoc->IsNegativePage( nTab ) )
364 		aPos.X() *= -1;
365 
366 	return aPos;
367 }
368 
369 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
370 {
371     Rectangle aRect(
372         GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DRAWPOS_TOPLEFT ),
373         GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DRAWPOS_BOTTOMRIGHT ) );
374     aRect.Justify();    // reorder left/right in RTL sheets
375     return aRect;
376 }
377 
378 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
379 {
380     return GetDrawRect( nCol, nRow, nCol, nRow );
381 }
382 
383 sal_Bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
384 {
385 	//	test if rPolygon is the line end for "other table" (rectangle)
386 	if(1L == rPolyPolygon.count())
387 	{
388 		const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0L));
389 
390         // #i73305# circle consists of 4 segments, too, distinguishable from square by
391         // the use of control points
392         if(4L == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed())
393 		{
394 			return true;
395 		}
396 	}
397 
398 	return false;
399 }
400 
401 sal_Bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
402 									SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
403 {
404 	sal_Bool bStartAlien = ( rStart.Tab() != nTab );
405 	sal_Bool bEndAlien   = ( nEndTab != nTab );
406 
407 	if (bStartAlien && bEndAlien)
408 	{
409 		DBG_ERROR("bStartAlien && bEndAlien");
410 		return sal_True;
411 	}
412 
413 	Rectangle aStartRect;
414 	Rectangle aEndRect;
415 	if (!bStartAlien)
416 		aStartRect = GetDrawRect( rStart.Col(), rStart.Row() );
417 	if (!bEndAlien)
418 		aEndRect = GetDrawRect( nEndCol, nEndRow );
419 
420 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
421 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
422 	DBG_ASSERT(pPage,"Page ?");
423 
424 	sal_Bool bFound = sal_False;
425 	SdrObjListIter aIter( *pPage, IM_FLAT );
426 	SdrObject* pObject = aIter.Next();
427 	while (pObject && !bFound)
428 	{
429 		if ( pObject->GetLayer()==SC_LAYER_INTERN &&
430 				pObject->IsPolyObj() && pObject->GetPointCount()==2 )
431 		{
432 			const SfxItemSet& rSet = pObject->GetMergedItemSet();
433 
434 			sal_Bool bObjStartAlien =
435 				lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
436 			sal_Bool bObjEndAlien =
437 				lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
438 
439 			sal_Bool bStartHit = bStartAlien ? bObjStartAlien :
440 								( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) );
441 			sal_Bool bEndHit = bEndAlien ? bObjEndAlien :
442 								( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) );
443 
444 			if ( bStartHit && bEndHit )
445 				bFound = sal_True;
446 		}
447 		pObject = aIter.Next();
448 	}
449 
450 	return bFound;
451 }
452 
453 sal_Bool ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject )			// static
454 {
455 	if ( pObject->GetLayer()==SC_LAYER_INTERN &&
456 			pObject->IsPolyObj() && pObject->GetPointCount()==2 )
457 	{
458 		const SfxItemSet& rSet = pObject->GetMergedItemSet();
459 
460 		sal_Bool bObjStartAlien =
461 			lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
462 		sal_Bool bObjEndAlien =
463 			lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
464 
465 		return !bObjStartAlien && !bObjEndAlien;
466 	}
467 
468 	return sal_False;
469 }
470 
471 //------------------------------------------------------------------------
472 
473 //	InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
474 
475 sal_Bool ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow,
476 								SCCOL nRefStartCol, SCROW nRefStartRow,
477 								SCCOL nRefEndCol, SCROW nRefEndRow,
478 								sal_Bool bFromOtherTab, sal_Bool bRed,
479 								ScDetectiveData& rData )
480 {
481 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
482 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
483 
484 	sal_Bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
485 	if (bArea && !bFromOtherTab)
486 	{
487 		// insert the rectangle before the arrow - this is relied on in FindFrameForObject
488 
489 		Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow );
490 		SdrRectObj* pBox = new SdrRectObj( aRect );
491 
492 		pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
493 
494 		ScDrawLayer::SetAnchor( pBox, SCA_CELL );
495 		pBox->SetLayer( SC_LAYER_INTERN );
496 		pPage->InsertObject( pBox );
497 	        pModel->AddCalcUndo< SdrUndoInsertObj >( *pBox );
498 
499 		ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True );
500 		pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
501 		pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab);
502 	}
503 
504 	Point aStartPos	= GetDrawPos( nRefStartCol, nRefStartRow, DRAWPOS_DETARROW );
505 	Point aEndPos = GetDrawPos( nCol, nRow, DRAWPOS_DETARROW );
506 
507 	if (bFromOtherTab)
508 	{
509 		sal_Bool bNegativePage = pDoc->IsNegativePage( nTab );
510 		long nPageSign = bNegativePage ? -1 : 1;
511 
512 		aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 );
513 		if (aStartPos.X() * nPageSign < 0)
514 			aStartPos.X() += 2000 * nPageSign;
515 		if (aStartPos.Y() < 0)
516 			aStartPos.Y() += 2000;
517 	}
518 
519 	SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
520 
521 	if (bArea && !bFromOtherTab)
522 		rAttrSet.Put( XLineWidthItem( 50 ) );				// Bereich
523 	else
524 		rAttrSet.Put( XLineWidthItem( 0 ) );				// einzelne Referenz
525 
526 	ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
527 	rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) );
528 
529 	basegfx::B2DPolygon aTempPoly;
530 	aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
531 	aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
532 	SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
533 	pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos));	//! noetig ???
534 	pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
535 
536 	ScDrawLayer::SetAnchor( pArrow, SCA_CELL );
537 	pArrow->SetLayer( SC_LAYER_INTERN );
538 	pPage->InsertObject( pArrow );
539 	    pModel->AddCalcUndo< SdrUndoInsertObj >( *pArrow );
540 
541 	ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, sal_True );
542 	if (bFromOtherTab)
543 		pData->maStart.SetInvalid();
544 	else
545 		pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
546 
547 	pData->maEnd.Set( nCol, nRow, nTab);
548 
549     Modified();
550 	return sal_True;
551 }
552 
553 sal_Bool ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
554 								SCCOL nEndCol, SCROW nEndRow, sal_Bool bRed,
555 								ScDetectiveData& rData )
556 {
557 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
558 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
559 
560 	sal_Bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
561 	if (bArea)
562 	{
563 		Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
564 		SdrRectObj* pBox = new SdrRectObj( aRect );
565 
566 		pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
567 
568 		ScDrawLayer::SetAnchor( pBox, SCA_CELL );
569 		pBox->SetLayer( SC_LAYER_INTERN );
570 		pPage->InsertObject( pBox );
571 	        pModel->AddCalcUndo< SdrUndoInsertObj >( *pBox );
572 
573 		ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True );
574 		pData->maStart.Set( nStartCol, nStartRow, nTab);
575 		pData->maEnd.Set( nEndCol, nEndRow, nTab);
576 	}
577 
578 	sal_Bool bNegativePage = pDoc->IsNegativePage( nTab );
579 	long nPageSign = bNegativePage ? -1 : 1;
580 
581 	Point aStartPos	= GetDrawPos( nStartCol, nStartRow, DRAWPOS_DETARROW );
582 	Point aEndPos   = Point( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 );
583 	if (aEndPos.Y() < 0)
584 		aEndPos.Y() += 2000;
585 
586 	SfxItemSet& rAttrSet = rData.GetToTabSet();
587 	if (bArea)
588 		rAttrSet.Put( XLineWidthItem( 50 ) );				// Bereich
589 	else
590 		rAttrSet.Put( XLineWidthItem( 0 ) );				// einzelne Referenz
591 
592 	ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
593 	rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) );
594 
595 	basegfx::B2DPolygon aTempPoly;
596 	aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
597 	aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
598 	SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
599 	pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos));	//! noetig ???
600 
601 	pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
602 
603 	ScDrawLayer::SetAnchor( pArrow, SCA_CELL );
604 	pArrow->SetLayer( SC_LAYER_INTERN );
605 	pPage->InsertObject( pArrow );
606 	    pModel->AddCalcUndo< SdrUndoInsertObj >( *pArrow );
607 
608 	ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, sal_True );
609 	pData->maStart.Set( nStartCol, nStartRow, nTab);
610 	pData->maEnd.SetInvalid();
611 
612     Modified();
613 	return sal_True;
614 }
615 
616 //------------------------------------------------------------------------
617 
618 //	DrawEntry:		Formel auf dieser Tabelle,
619 //					Referenz auf dieser oder anderer
620 //	DrawAlienEntry:	Formel auf anderer Tabelle,
621 //					Referenz auf dieser
622 
623 //		return FALSE: da war schon ein Pfeil
624 
625 sal_Bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
626 									const ScRange& rRef,
627 									ScDetectiveData& rData )
628 {
629 	if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
630 		return sal_False;
631 
632 	ScAddress aErrorPos;
633 	sal_Bool bError = HasError( rRef, aErrorPos );
634 	sal_Bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab );
635 
636 	return InsertArrow( nCol, nRow,
637 						rRef.aStart.Col(), rRef.aStart.Row(),
638 						rRef.aEnd.Col(), rRef.aEnd.Row(),
639 						bAlien, bError, rData );
640 }
641 
642 sal_Bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
643 										ScDetectiveData& rData )
644 {
645 	if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
646 		return sal_False;
647 
648 	ScAddress aErrorPos;
649 	sal_Bool bError = HasError( rRef, aErrorPos );
650 
651 	return InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(),
652 								rRef.aEnd.Col(), rRef.aEnd.Row(),
653 								bError, rData );
654 }
655 
656 void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData )
657 {
658 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
659 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
660 
661 	Rectangle aRect = GetDrawRect( nCol, nRow );
662 	aRect.Left()	-= 250;
663 	aRect.Right()	+= 250;
664 	aRect.Top()		-= 70;
665 	aRect.Bottom()	+= 70;
666 
667 	SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect );
668 	SfxItemSet& rAttrSet = rData.GetCircleSet();
669 
670 	pCircle->SetMergedItemSetAndBroadcast(rAttrSet);
671 
672 	ScDrawLayer::SetAnchor( pCircle, SCA_CELL );
673 	pCircle->SetLayer( SC_LAYER_INTERN );
674 	pPage->InsertObject( pCircle );
675 	    pModel->AddCalcUndo< SdrUndoInsertObj >( *pCircle );
676 
677 	ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, sal_True );
678 	pData->maStart.Set( nCol, nRow, nTab);
679 	pData->maEnd.SetInvalid();
680 
681     Modified();
682 }
683 
684 void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, sal_Bool bDestPnt )
685 {
686 	Rectangle aRect = GetDrawRect( nCol, nRow );
687 
688 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
689 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
690 	DBG_ASSERT(pPage,"Page ?");
691 
692 	pPage->RecalcObjOrdNums();
693 
694 	long	nDelCount = 0;
695 	sal_uLong	nObjCount = pPage->GetObjCount();
696 	if (nObjCount)
697 	{
698 		SdrObject** ppObj = new SdrObject*[nObjCount];
699 
700 		SdrObjListIter aIter( *pPage, IM_FLAT );
701 		SdrObject* pObject = aIter.Next();
702 		while (pObject)
703 		{
704 			if ( pObject->GetLayer()==SC_LAYER_INTERN &&
705 					pObject->IsPolyObj() && pObject->GetPointCount()==2 )
706 			{
707 				if (aRect.IsInside(pObject->GetPoint(bDestPnt)))			// Start/Zielpunkt
708 					ppObj[nDelCount++] = pObject;
709 			}
710 
711 			pObject = aIter.Next();
712 		}
713 
714 		long i;
715 		for (i=1; i<=nDelCount; i++)
716             pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] );
717 
718 		for (i=1; i<=nDelCount; i++)
719 			pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
720 
721 		delete[] ppObj;
722 
723         Modified();
724 	}
725 }
726 
727 		//		Box um Referenz loeschen
728 
729 #define SC_DET_TOLERANCE	50
730 
731 inline sal_Bool RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd )
732 {
733 	return rRect.Left()   >= rStart.X() - SC_DET_TOLERANCE
734 		&& rRect.Left()   <= rStart.X() + SC_DET_TOLERANCE
735 		&& rRect.Right()  >= rEnd.X()   - SC_DET_TOLERANCE
736 		&& rRect.Right()  <= rEnd.X()   + SC_DET_TOLERANCE
737 		&& rRect.Top()    >= rStart.Y() - SC_DET_TOLERANCE
738 		&& rRect.Top()    <= rStart.Y() + SC_DET_TOLERANCE
739 		&& rRect.Bottom() >= rEnd.Y()   - SC_DET_TOLERANCE
740 		&& rRect.Bottom() <= rEnd.Y()   + SC_DET_TOLERANCE;
741 }
742 
743 #undef SC_DET_TOLERANCE
744 
745 void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
746 {
747 /*	String aStr;
748 	aStr += nCol1;
749 	aStr += '/';
750 	aStr += nRow1;
751 	aStr += '/';
752 	aStr += nCol2;
753 	aStr += '/';
754 	aStr += nRow2;
755 	InfoBox(0,aStr).Execute();
756 */
757 
758 	Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 );
759 	Point aStartCorner = aCornerRect.TopLeft();
760 	Point aEndCorner = aCornerRect.BottomRight();
761 	Rectangle aObjRect;
762 
763 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
764 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
765 	DBG_ASSERT(pPage,"Page ?");
766 
767 	pPage->RecalcObjOrdNums();
768 
769 	long	nDelCount = 0;
770 	sal_uLong	nObjCount = pPage->GetObjCount();
771 	if (nObjCount)
772 	{
773 		SdrObject** ppObj = new SdrObject*[nObjCount];
774 
775 		SdrObjListIter aIter( *pPage, IM_FLAT );
776 		SdrObject* pObject = aIter.Next();
777 		while (pObject)
778 		{
779 			if ( pObject->GetLayer() == SC_LAYER_INTERN &&
780 					pObject->Type() == TYPE(SdrRectObj) )
781 			{
782 				aObjRect = ((SdrRectObj*)pObject)->GetLogicRect();
783 				aObjRect.Justify();
784 				if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
785 					ppObj[nDelCount++] = pObject;
786 			}
787 
788 			pObject = aIter.Next();
789 		}
790 
791 		long i;
792 		for (i=1; i<=nDelCount; i++)
793 	            pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] );
794 
795 		for (i=1; i<=nDelCount; i++)
796 			pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
797 
798 		delete[] ppObj;
799 
800         Modified();
801 	}
802 }
803 
804 //------------------------------------------------------------------------
805 
806 sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
807 										ScDetectiveData& rData, sal_uInt16 nLevel )
808 {
809 	sal_uInt16 nResult = DET_INS_EMPTY;
810 
811 	ScCellIterator aCellIter( pDoc, rRef);
812 	ScBaseCell* pCell = aCellIter.GetFirst();
813 	while (pCell)
814 	{
815 		if (pCell->GetCellType() == CELLTYPE_FORMULA)
816 			switch( InsertPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), rData, nLevel ) )
817 			{
818 				case DET_INS_INSERTED:
819 					nResult = DET_INS_INSERTED;
820 					break;
821 				case DET_INS_CONTINUE:
822 					if (nResult != DET_INS_INSERTED)
823 						nResult = DET_INS_CONTINUE;
824 					break;
825 				case DET_INS_CIRCULAR:
826 					if (nResult == DET_INS_EMPTY)
827 						nResult = DET_INS_CIRCULAR;
828 					break;
829 			}
830 
831 		pCell = aCellIter.GetNext();
832 	}
833 
834 	return nResult;
835 }
836 
837 sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
838 											sal_uInt16 nLevel )
839 {
840 	ScBaseCell* pCell;
841 	pDoc->GetCell( nCol, nRow, nTab, pCell );
842 	if (!pCell)
843 		return DET_INS_EMPTY;
844 	if (pCell->GetCellType() != CELLTYPE_FORMULA)
845 		return DET_INS_EMPTY;
846 
847 	ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
848 	if (pFCell->IsRunning())
849 		return DET_INS_CIRCULAR;
850 
851 	if (pFCell->GetDirty())
852 		pFCell->Interpret();				// nach SetRunning geht's nicht mehr!
853 	pFCell->SetRunning(sal_True);
854 
855 	sal_uInt16 nResult = DET_INS_EMPTY;
856 
857 	ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
858     ScRange aRef;
859 	while ( aIter.GetNextRef( aRef ) )
860 	{
861 		if (DrawEntry( nCol, nRow, aRef, rData ))
862 		{
863 			nResult = DET_INS_INSERTED;			//	neuer Pfeil eingetragen
864 		}
865 		else
866 		{
867 			//	weiterverfolgen
868 
869 			if ( nLevel < rData.GetMaxLevel() )
870 			{
871 				sal_uInt16 nSubResult;
872 				sal_Bool bArea = (aRef.aStart != aRef.aEnd);
873 				if (bArea)
874 					nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 );
875 				else
876 					nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(),
877 													rData, nLevel+1 );
878 
879 				switch (nSubResult)
880 				{
881 					case DET_INS_INSERTED:
882 						nResult = DET_INS_INSERTED;
883 						break;
884 					case DET_INS_CONTINUE:
885 						if (nResult != DET_INS_INSERTED)
886 							nResult = DET_INS_CONTINUE;
887 						break;
888 					case DET_INS_CIRCULAR:
889 						if (nResult == DET_INS_EMPTY)
890 							nResult = DET_INS_CIRCULAR;
891 						break;
892 					// DET_INS_EMPTY: unveraendert lassen
893 				}
894 			}
895 			else									//	nMaxLevel erreicht
896 				if (nResult != DET_INS_INSERTED)
897 					nResult = DET_INS_CONTINUE;
898 		}
899 	}
900 
901 	pFCell->SetRunning(sal_False);
902 
903 	return nResult;
904 }
905 
906 sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
907 												sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
908 {
909 	sal_uInt16 nResult = nLevel;
910 
911 	ScCellIterator aCellIter( pDoc, rRef);
912 	ScBaseCell* pCell = aCellIter.GetFirst();
913 	while (pCell)
914 	{
915 		if (pCell->GetCellType() == CELLTYPE_FORMULA)
916 		{
917 			sal_uInt16 nTemp = FindPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), nLevel, nDeleteLevel );
918 			if (nTemp > nResult)
919 				nResult = nTemp;
920 		}
921 		pCell = aCellIter.GetNext();
922 	}
923 
924 	return nResult;
925 }
926 
927 											//	nDeleteLevel != 0	-> loeschen
928 
929 sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
930 {
931 	DBG_ASSERT( nLevel<1000, "Level" );
932 
933 	ScBaseCell* pCell;
934 	pDoc->GetCell( nCol, nRow, nTab, pCell );
935 	if (!pCell)
936 		return nLevel;
937 	if (pCell->GetCellType() != CELLTYPE_FORMULA)
938 		return nLevel;
939 
940 	ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
941 	if (pFCell->IsRunning())
942 		return nLevel;
943 
944 	if (pFCell->GetDirty())
945 		pFCell->Interpret();				// nach SetRunning geht's nicht mehr!
946 	pFCell->SetRunning(sal_True);
947 
948 	sal_uInt16 nResult = nLevel;
949 	sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
950 
951 	if ( bDelete )
952 	{
953 		DeleteArrowsAt( nCol, nRow, sal_True );					// Pfeile, die hierher zeigen
954 	}
955 
956 	ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
957     ScRange aRef;
958 	while ( aIter.GetNextRef( aRef) )
959 	{
960 		sal_Bool bArea = ( aRef.aStart != aRef.aEnd );
961 
962 		if ( bDelete )					// Rahmen loeschen ?
963 		{
964 			if (bArea)
965 			{
966 				DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
967 			}
968 		}
969 		else							// weitersuchen
970 		{
971 			if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) )
972 			{
973 				sal_uInt16 nTemp;
974 				if (bArea)
975 					nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel );
976 				else
977 					nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(),
978 														nLevel+1, nDeleteLevel );
979 				if (nTemp > nResult)
980 					nResult = nTemp;
981 			}
982 		}
983 	}
984 
985 	pFCell->SetRunning(sal_False);
986 
987 	return nResult;
988 }
989 
990 //------------------------------------------------------------------------
991 
992 sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
993 											sal_uInt16 nLevel )
994 {
995 	ScBaseCell* pCell;
996 	pDoc->GetCell( nCol, nRow, nTab, pCell );
997 	if (!pCell)
998 		return DET_INS_EMPTY;
999 	if (pCell->GetCellType() != CELLTYPE_FORMULA)
1000 		return DET_INS_EMPTY;
1001 
1002 	ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
1003 	if (pFCell->IsRunning())
1004 		return DET_INS_CIRCULAR;
1005 
1006 	if (pFCell->GetDirty())
1007 		pFCell->Interpret();				// nach SetRunning geht's nicht mehr!
1008 	pFCell->SetRunning(sal_True);
1009 
1010 	sal_uInt16 nResult = DET_INS_EMPTY;
1011 
1012 	ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
1013     ScRange aRef;
1014 	ScAddress aErrorPos;
1015 	sal_Bool bHasError = sal_False;
1016 	while ( aIter.GetNextRef( aRef ) )
1017 	{
1018 		if (HasError( aRef, aErrorPos ))
1019 		{
1020 			bHasError = sal_True;
1021 			if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
1022 				nResult = DET_INS_INSERTED;
1023 
1024 			//	und weiterverfolgen
1025 
1026 			if ( nLevel < rData.GetMaxLevel() )			// praktisch immer
1027 			{
1028 				if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
1029 														rData, nLevel+1 ) == DET_INS_INSERTED)
1030 					nResult = DET_INS_INSERTED;
1031 			}
1032 		}
1033 	}
1034 
1035 	pFCell->SetRunning(sal_False);
1036 
1037 													// Blaetter ?
1038 	if (!bHasError)
1039 		if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
1040 			nResult = DET_INS_INSERTED;
1041 
1042 	return nResult;
1043 }
1044 
1045 //------------------------------------------------------------------------
1046 
1047 sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1048 										ScDetectiveData& rData, sal_uInt16 nLevel )
1049 {
1050 	//	ueber ganzes Dokument
1051 
1052 	sal_uInt16 nResult = DET_INS_EMPTY;
1053 //	ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab );
1054 	ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB );			// alle Tabellen
1055 	ScBaseCell* pCell = aCellIter.GetFirst();
1056 	while (pCell)
1057 	{
1058 		if (pCell->GetCellType() == CELLTYPE_FORMULA)
1059 		{
1060 			ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
1061 			sal_Bool bRunning = pFCell->IsRunning();
1062 
1063 			if (pFCell->GetDirty())
1064 				pFCell->Interpret();				// nach SetRunning geht's nicht mehr!
1065 			pFCell->SetRunning(sal_True);
1066 
1067 			ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
1068             ScRange aRef;
1069 			while ( aIter.GetNextRef( aRef) )
1070 			{
1071 				if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1072 				{
1073 					if (Intersect( nCol1,nRow1,nCol2,nRow2,
1074 							aRef.aStart.Col(),aRef.aStart.Row(),
1075 							aRef.aEnd.Col(),aRef.aEnd.Row() ))
1076 					{
1077 						sal_Bool bAlien = ( aCellIter.GetTab() != nTab );
1078 						sal_Bool bDrawRet;
1079 						if (bAlien)
1080 							bDrawRet = DrawAlienEntry( aRef, rData );
1081 						else
1082 							bDrawRet = DrawEntry( aCellIter.GetCol(), aCellIter.GetRow(),
1083 													aRef, rData );
1084 						if (bDrawRet)
1085 						{
1086 							nResult = DET_INS_INSERTED;			//	neuer Pfeil eingetragen
1087 						}
1088 						else
1089 						{
1090 							if (bRunning)
1091 							{
1092 								if (nResult == DET_INS_EMPTY)
1093 									nResult = DET_INS_CIRCULAR;
1094 							}
1095 							else
1096 							{
1097 										//	weiterverfolgen
1098 
1099 								if ( nLevel < rData.GetMaxLevel() )
1100 								{
1101 									sal_uInt16 nSubResult = InsertSuccLevel(
1102 															aCellIter.GetCol(), aCellIter.GetRow(),
1103 															aCellIter.GetCol(), aCellIter.GetRow(),
1104 															rData, nLevel+1 );
1105 									switch (nSubResult)
1106 									{
1107 										case DET_INS_INSERTED:
1108 											nResult = DET_INS_INSERTED;
1109 											break;
1110 										case DET_INS_CONTINUE:
1111 											if (nResult != DET_INS_INSERTED)
1112 												nResult = DET_INS_CONTINUE;
1113 											break;
1114 										case DET_INS_CIRCULAR:
1115 											if (nResult == DET_INS_EMPTY)
1116 												nResult = DET_INS_CIRCULAR;
1117 											break;
1118 										// DET_INS_EMPTY: unveraendert lassen
1119 									}
1120 								}
1121 								else									//	nMaxLevel erreicht
1122 									if (nResult != DET_INS_INSERTED)
1123 										nResult = DET_INS_CONTINUE;
1124 							}
1125 						}
1126 					}
1127 				}
1128 			}
1129 			pFCell->SetRunning(bRunning);
1130 		}
1131 		pCell = aCellIter.GetNext();
1132 	}
1133 
1134 	return nResult;
1135 }
1136 
1137 sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1138 										sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
1139 {
1140 	DBG_ASSERT( nLevel<1000, "Level" );
1141 
1142 	sal_uInt16 nResult = nLevel;
1143 	sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
1144 
1145 	ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab );
1146 	ScBaseCell* pCell = aCellIter.GetFirst();
1147 	while (pCell)
1148 	{
1149 		if (pCell->GetCellType() == CELLTYPE_FORMULA)
1150 		{
1151 			ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
1152 			sal_Bool bRunning = pFCell->IsRunning();
1153 
1154 			if (pFCell->GetDirty())
1155 				pFCell->Interpret();				// nach SetRunning geht's nicht mehr!
1156 			pFCell->SetRunning(sal_True);
1157 
1158 			ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
1159             ScRange aRef;
1160 			while ( aIter.GetNextRef( aRef) )
1161 			{
1162 				if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab)
1163 				{
1164 					if (Intersect( nCol1,nRow1,nCol2,nRow2,
1165 							aRef.aStart.Col(),aRef.aStart.Row(),
1166 							aRef.aEnd.Col(),aRef.aEnd.Row() ))
1167 					{
1168 						if ( bDelete )							// Pfeile, die hier anfangen
1169 						{
1170 							if (aRef.aStart != aRef.aEnd)
1171 							{
1172 								DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(),
1173 												aRef.aEnd.Col(), aRef.aEnd.Row() );
1174 							}
1175 							DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), sal_False );
1176 						}
1177 						else if ( !bRunning &&
1178 								HasArrow( aRef.aStart,
1179 											aCellIter.GetCol(),aCellIter.GetRow(),aCellIter.GetTab() ) )
1180 						{
1181 							sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetCol(), aCellIter.GetRow(),
1182 															aCellIter.GetCol(), aCellIter.GetRow(),
1183 															nLevel+1, nDeleteLevel );
1184 							if (nTemp > nResult)
1185 								nResult = nTemp;
1186 						}
1187 					}
1188 				}
1189 			}
1190 
1191 			pFCell->SetRunning(bRunning);
1192 		}
1193 		pCell = aCellIter.GetNext();
1194 	}
1195 
1196 	return nResult;
1197 }
1198 
1199 
1200 //
1201 //	--------------------------------------------------------------------------------
1202 //
1203 
1204 sal_Bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
1205 {
1206 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1207 	if (!pModel)
1208 		return sal_False;
1209 
1210 	ScDetectiveData aData( pModel );
1211 
1212 	sal_uInt16 nMaxLevel = 0;
1213 	sal_uInt16 nResult = DET_INS_CONTINUE;
1214 	while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1215 	{
1216 		aData.SetMaxLevel( nMaxLevel );
1217 		nResult = InsertPredLevel( nCol, nRow, aData, 0 );
1218 		++nMaxLevel;
1219 	}
1220 
1221 	return ( nResult == DET_INS_INSERTED );
1222 }
1223 
1224 sal_Bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
1225 {
1226 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1227 	if (!pModel)
1228 		return sal_False;
1229 
1230 	ScDetectiveData aData( pModel );
1231 
1232 	sal_uInt16 nMaxLevel = 0;
1233 	sal_uInt16 nResult = DET_INS_CONTINUE;
1234 	while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
1235 	{
1236 		aData.SetMaxLevel( nMaxLevel );
1237 		nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
1238 		++nMaxLevel;
1239 	}
1240 
1241 	return ( nResult == DET_INS_INSERTED );
1242 }
1243 
1244 sal_Bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
1245 {
1246 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1247 	if (!pModel)
1248 		return sal_False;
1249 
1250 	ScRange aRange( nCol, nRow, nTab );
1251 	ScAddress aErrPos;
1252 	if ( !HasError( aRange,aErrPos ) )
1253 		return sal_False;
1254 
1255 	ScDetectiveData aData( pModel );
1256 
1257 	aData.SetMaxLevel( 1000 );
1258 	sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
1259 
1260 	return ( nResult == DET_INS_INSERTED );
1261 }
1262 
1263 sal_Bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
1264 {
1265 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1266 	if (!pModel)
1267 		return sal_False;
1268 
1269 	sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
1270 	if ( nLevelCount )
1271 		FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount );			// loeschen
1272 
1273 	return ( nLevelCount != 0 );
1274 }
1275 
1276 sal_Bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
1277 {
1278 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1279 	if (!pModel)
1280 		return sal_False;
1281 
1282 	sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
1283 	if ( nLevelCount )
1284 		FindPredLevel( nCol, nRow, 0, nLevelCount );			// loeschen
1285 
1286 	return ( nLevelCount != 0 );
1287 }
1288 
1289 sal_Bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
1290 {
1291 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1292 	if (!pModel)
1293 		return sal_False;
1294 
1295 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1296 	DBG_ASSERT(pPage,"Page ?");
1297 
1298 	pPage->RecalcObjOrdNums();
1299 
1300 	long	nDelCount = 0;
1301 	sal_uLong	nObjCount = pPage->GetObjCount();
1302 	if (nObjCount)
1303 	{
1304 		SdrObject** ppObj = new SdrObject*[nObjCount];
1305 
1306 		SdrObjListIter aIter( *pPage, IM_FLAT );
1307 		SdrObject* pObject = aIter.Next();
1308 		while (pObject)
1309 		{
1310 			if ( pObject->GetLayer() == SC_LAYER_INTERN )
1311 			{
1312 				sal_Bool bDoThis = sal_True;
1313 				if ( eWhat != SC_DET_ALL )
1314 				{
1315 					sal_Bool bCircle = ( pObject->ISA(SdrCircObj) );
1316                     sal_Bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
1317 					if ( eWhat == SC_DET_DETECTIVE )		// Detektiv, aus Menue
1318 						bDoThis = !bCaption;				// auch Kreise
1319 					else if ( eWhat == SC_DET_CIRCLES )		// Kreise, wenn neue erzeugt werden
1320 						bDoThis = bCircle;
1321 					else if ( eWhat == SC_DET_ARROWS )		// DetectiveRefresh
1322 						bDoThis = !bCaption && !bCircle;	// don't include circles
1323 					else
1324 					{
1325 						DBG_ERROR("wat?");
1326 					}
1327 				}
1328 				if ( bDoThis )
1329 					ppObj[nDelCount++] = pObject;
1330 			}
1331 
1332 			pObject = aIter.Next();
1333 		}
1334 
1335 		long i;
1336 		for (i=1; i<=nDelCount; i++)
1337 	            pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] );
1338 
1339 		for (i=1; i<=nDelCount; i++)
1340 			pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
1341 
1342 		delete[] ppObj;
1343 
1344         Modified();
1345 	}
1346 
1347 	return ( nDelCount != 0 );
1348 }
1349 
1350 sal_Bool ScDetectiveFunc::MarkInvalid(sal_Bool& rOverflow)
1351 {
1352 	rOverflow = sal_False;
1353 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1354 	if (!pModel)
1355 		return sal_False;
1356 
1357 	sal_Bool bDeleted = DeleteAll( SC_DET_CIRCLES );		// nur die Kreise
1358 
1359 	ScDetectiveData aData( pModel );
1360 	long nInsCount = 0;
1361 
1362 	//	Stellen suchen, wo Gueltigkeit definiert ist
1363 
1364 	ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW );
1365 	SCCOL nCol;
1366 	SCROW nRow1;
1367 	SCROW nRow2;
1368 	const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1369 	while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
1370 	{
1371 		sal_uLong nIndex = ((const SfxUInt32Item&)pPattern->GetItem(ATTR_VALIDDATA)).GetValue();
1372 		if (nIndex)
1373 		{
1374 			const ScValidationData*	pData = pDoc->GetValidationEntry( nIndex );
1375 			if ( pData )
1376 			{
1377 				//	Zellen in dem Bereich durchgehen
1378 
1379 				sal_Bool bMarkEmpty = !pData->IsIgnoreBlank();
1380 				SCROW nNextRow = nRow1;
1381 				SCROW nRow;
1382 				ScCellIterator aCellIter( pDoc, nCol,nRow1,nTab, nCol,nRow2,nTab );
1383 				ScBaseCell* pCell = aCellIter.GetFirst();
1384 				while ( pCell && nInsCount < SC_DET_MAXCIRCLE )
1385 				{
1386 					SCROW nCellRow = aCellIter.GetRow();
1387 					if ( bMarkEmpty )
1388 						for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1389 						{
1390 							DrawCircle( nCol, nRow, aData );
1391 							++nInsCount;
1392 						}
1393 					if ( !pData->IsDataValid( pCell, ScAddress( nCol, nCellRow, nTab ) ) )
1394 					{
1395 						DrawCircle( nCol, nCellRow, aData );
1396 						++nInsCount;
1397 					}
1398 					nNextRow = nCellRow + 1;
1399 					pCell = aCellIter.GetNext();
1400 				}
1401 				if ( bMarkEmpty )
1402 					for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
1403 					{
1404 						DrawCircle( nCol, nRow, aData );
1405 						++nInsCount;
1406 					}
1407 			}
1408 		}
1409 
1410 		pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
1411 	}
1412 
1413 	if ( nInsCount >= SC_DET_MAXCIRCLE )
1414 		rOverflow = sal_True;
1415 
1416 	return ( bDeleted || nInsCount != 0 );
1417 }
1418 
1419 void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc )
1420 {
1421 	//	for all caption objects, update attributes and SpecialTextBoxShadow flag
1422 	//	(on all tables - nTab is ignored!)
1423 
1424 	//	no undo actions, this is refreshed after undo
1425 
1426     ScDrawLayer* pModel = rDoc.GetDrawLayer();
1427 	if (!pModel)
1428 		return;
1429 
1430     for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1431 	{
1432         rDoc.InitializeNoteCaptions( nObjTab );
1433 		SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1434 		DBG_ASSERT( pPage, "Page ?" );
1435 		if( pPage )
1436 		{
1437 			SdrObjListIter aIter( *pPage, IM_FLAT );
1438 			for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1439 			{
1440 				if ( ScDrawObjData* pData = ScDrawLayer::GetNoteCaptionData( pObject, nObjTab ) )
1441 				{
1442                     ScPostIt* pNote = rDoc.GetNote( pData->maStart );
1443                     // caption should exist, we iterate over drawing objects...
1444                     DBG_ASSERT( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" );
1445                     if( pNote )
1446                     {
1447                         ScCommentData aData( rDoc, pModel );
1448                         SfxItemSet aAttrColorSet = pObject->GetMergedItemSet();
1449                         aAttrColorSet.Put( XFillColorItem( String(), GetCommentColor() ) );
1450                         aData.UpdateCaptionSet( aAttrColorSet );
1451                         pObject->SetMergedItemSetAndBroadcast( aData.GetCaptionSet() );
1452                         if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
1453                         {
1454                             pCaption->SetSpecialTextBoxShadow();
1455                             pCaption->SetFixedTail();
1456                         }
1457                     }
1458     			}
1459 			}
1460 		}
1461 	}
1462 }
1463 
1464 void ScDetectiveFunc::UpdateAllArrowColors()
1465 {
1466 	//	no undo actions necessary
1467 
1468 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1469 	if (!pModel)
1470 		return;
1471 
1472 	for( SCTAB nObjTab = 0, nTabCount = pDoc->GetTableCount(); nObjTab < nTabCount; ++nObjTab )
1473 	{
1474 		SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
1475 		DBG_ASSERT( pPage, "Page ?" );
1476 		if( pPage )
1477 		{
1478 			SdrObjListIter aIter( *pPage, IM_FLAT );
1479 			for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
1480 			{
1481 				if ( pObject->GetLayer() == SC_LAYER_INTERN )
1482 				{
1483 					sal_Bool bArrow = sal_False;
1484 					sal_Bool bError = sal_False;
1485 
1486 					ScAddress aPos;
1487 					ScRange aSource;
1488 					sal_Bool bDummy;
1489 					ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy );
1490 					if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
1491 					{
1492 						//	source is valid, determine error flag from source range
1493 
1494 						ScAddress aErrPos;
1495 						if ( HasError( aSource, aErrPos ) )
1496 							bError = sal_True;
1497 						else
1498 							bArrow = sal_True;
1499 					}
1500 					else if ( eType == SC_DETOBJ_FROMOTHERTAB )
1501 					{
1502 						//	source range is no longer known, take error flag from formula itself
1503 						//	(this means, if the formula has an error, all references to other tables
1504 						//	are marked red)
1505 
1506 						ScAddress aErrPos;
1507 						if ( HasError( ScRange( aPos), aErrPos ) )
1508 							bError = sal_True;
1509 						else
1510 							bArrow = sal_True;
1511 					}
1512 					else if ( eType == SC_DETOBJ_CIRCLE )
1513 					{
1514 						//	circles (error marks) are always red
1515 
1516 						bError = sal_True;
1517 					}
1518 					else if ( eType == SC_DETOBJ_NONE )
1519 					{
1520 						//	frame for area reference has no ObjType, always gets arrow color
1521 
1522 						if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) )
1523 						{
1524 							bArrow = sal_True;
1525 						}
1526 					}
1527 
1528 					if ( bArrow || bError )
1529 					{
1530 						ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() );
1531 						//pObject->SendRepaintBroadcast(pObject->GetBoundRect());
1532 						pObject->SetMergedItem( XLineColorItem( String(), Color( nColorData ) ) );
1533 
1534 						// repaint only
1535 						pObject->ActionChanged();
1536 						// pObject->SendRepaintBroadcast(pObject->GetBoundRect());
1537 					}
1538 				}
1539 			}
1540 		}
1541 	}
1542 }
1543 
1544 sal_Bool ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange )
1545 {
1546 	//	find the rectangle for an arrow (always the object directly before the arrow)
1547 	//	rRange must be initialized to the source cell of the arrow (start of area)
1548 
1549 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1550 	if (!pModel) return sal_False;
1551 
1552 	SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
1553 	DBG_ASSERT(pPage,"Page ?");
1554 	if (!pPage) return sal_False;
1555 
1556 	// test if the object is a direct page member
1557 	if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) )
1558 	{
1559 		// Is there a previous object?
1560 		const sal_uInt32 nOrdNum(pObject->GetOrdNum());
1561 
1562 		if(nOrdNum > 0)
1563 		{
1564 			SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
1565 
1566 			if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) )
1567 			{
1568 				ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() );
1569 				if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) )
1570                 {
1571                     rRange.aEnd = pPrevData->maEnd;
1572                     return sal_True;
1573                 }
1574 			}
1575 		}
1576 	}
1577 	return sal_False;
1578 }
1579 
1580 ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
1581 								ScAddress& rPosition, ScRange& rSource, sal_Bool& rRedLine )
1582 {
1583 	rRedLine = sal_False;
1584 	ScDetectiveObjType eType = SC_DETOBJ_NONE;
1585 
1586 	if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
1587 	{
1588         if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) )
1589         {
1590             bool bValidStart = pData->maStart.IsValid();
1591             bool bValidEnd = pData->maEnd.IsValid();
1592 
1593             if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
1594             {
1595                 // line object -> arrow
1596 
1597                 if ( bValidStart )
1598                     eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
1599                 else if ( bValidEnd )
1600                     eType = SC_DETOBJ_FROMOTHERTAB;
1601 
1602                 if ( bValidStart )
1603                     rSource = pData->maStart;
1604                 if ( bValidEnd )
1605                     rPosition = pData->maEnd;
1606 
1607                 if ( bValidStart && lcl_HasThickLine( *pObject ) )
1608                 {
1609                     // thick line -> look for frame before this object
1610 
1611                     FindFrameForObject( pObject, rSource );     // modifies rSource
1612                 }
1613 
1614                 ColorData nObjColor = ((const XLineColorItem&)pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor();
1615                 if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
1616                     rRedLine = sal_True;
1617             }
1618             else if ( pObject->ISA(SdrCircObj) )
1619             {
1620                 if ( bValidStart )
1621                 {
1622                     // cell position is returned in rPosition
1623 
1624                     rPosition = pData->maStart;
1625                     eType = SC_DETOBJ_CIRCLE;
1626                 }
1627             }
1628         }
1629 	}
1630 
1631 	return eType;
1632 }
1633 
1634 void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType,
1635 							const ScAddress& rPosition, const ScRange& rSource,
1636 							sal_Bool bRedLine )
1637 {
1638 	ScDrawLayer* pModel = pDoc->GetDrawLayer();
1639 	if (!pModel) return;
1640 	ScDetectiveData aData( pModel );
1641 
1642 	switch (eType)
1643 	{
1644 		case SC_DETOBJ_ARROW:
1645 		case SC_DETOBJ_FROMOTHERTAB:
1646 			InsertArrow( rPosition.Col(), rPosition.Row(),
1647 						 rSource.aStart.Col(), rSource.aStart.Row(),
1648 						 rSource.aEnd.Col(), rSource.aEnd.Row(),
1649 						 (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData );
1650 			break;
1651 		case SC_DETOBJ_TOOTHERTAB:
1652 			InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(),
1653 							  rSource.aEnd.Col(), rSource.aEnd.Row(),
1654 							  bRedLine, aData );
1655 			break;
1656 		case SC_DETOBJ_CIRCLE:
1657 			DrawCircle( rPosition.Col(), rPosition.Row(), aData );
1658 			break;
1659         default:
1660         {
1661             // added to avoid warnings
1662         }
1663 	}
1664 }
1665 
1666 // static
1667 ColorData ScDetectiveFunc::GetArrowColor()
1668 {
1669 	if (!bColorsInitialized)
1670 		InitializeColors();
1671 	return nArrowColor;
1672 }
1673 
1674 // static
1675 ColorData ScDetectiveFunc::GetErrorColor()
1676 {
1677 	if (!bColorsInitialized)
1678 		InitializeColors();
1679 	return nErrorColor;
1680 }
1681 
1682 // static
1683 ColorData ScDetectiveFunc::GetCommentColor()
1684 {
1685 	if (!bColorsInitialized)
1686 		InitializeColors();
1687 	return nCommentColor;
1688 }
1689 
1690 // static
1691 void ScDetectiveFunc::InitializeColors()
1692 {
1693 	// may be called several times to update colors from configuration
1694 
1695     const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
1696     nArrowColor   = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor;
1697     nErrorColor   = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor;
1698     nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor;
1699 
1700 	bColorsInitialized = sal_True;
1701 }
1702 
1703 // static
1704 sal_Bool ScDetectiveFunc::IsColorsInitialized()
1705 {
1706 	return bColorsInitialized;
1707 }
1708 
1709 //IAccessibility2 Implementation 2009-----
1710 void ScDetectiveFunc::AppendChangTrackNoteSeparator(String &aDisplay)
1711 {
1712 	aDisplay.AppendAscii( RTL_CONSTASCII_STRINGPARAM("\n--------\n") );
1713 }
1714 //-----IAccessibility2 Implementation 2009
1715 
1716