xref: /AOO41X/main/sc/source/core/tool/detfunc.cxx (revision 4d7c9de063a797b8b4f3d45e3561e82ad1f8ef1f)
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 
GetBoxSet()107     SfxItemSet& GetBoxSet()     { return aBoxSet; }
GetArrowSet()108     SfxItemSet& GetArrowSet()   { return aArrowSet; }
GetToTabSet()109     SfxItemSet& GetToTabSet()   { return aToTabSet; }
GetFromTabSet()110     SfxItemSet& GetFromTabSet() { return aFromTabSet; }
GetCircleSet()111     SfxItemSet& GetCircleSet()  { return aCircleSet; }
112 
SetMaxLevel(sal_uInt16 nVal)113     void        SetMaxLevel( sal_uInt16 nVal )      { nMaxLevel = nVal; }
GetMaxLevel() const114     sal_uInt16      GetMaxLevel() const             { return nMaxLevel; }
115 };
116 
117 class ScCommentData
118 {
119 public:
120                         ScCommentData( ScDocument& rDoc, SdrModel* pModel );
121 
GetCaptionSet()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 
lcl_HasThickLine(SdrObject & rObj)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 
ScDetectiveData(SdrModel * pModel)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 
ScCommentData(ScDocument & rDoc,SdrModel * pModel)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 
UpdateCaptionSet(const SfxItemSet & rItemSet)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 
Modified()286 void ScDetectiveFunc::Modified()
287 {
288     if (pDoc->IsStreamValid(nTab))
289         pDoc->SetStreamValid(nTab, sal_False);
290 }
291 
Intersect(SCCOL nStartCol1,SCROW nStartRow1,SCCOL nEndCol1,SCROW nEndRow1,SCCOL nStartCol2,SCROW nStartRow2,SCCOL nEndCol2,SCROW nEndRow2)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 
HasError(const ScRange & rRange,ScAddress & rErrPos)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 
GetDrawPos(SCCOL nCol,SCROW nRow,DrawPosMode eMode) const320 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 
GetDrawRect(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2) const369 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 
GetDrawRect(SCCOL nCol,SCROW nRow) const378 Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const
379 {
380     return GetDrawRect( nCol, nRow, nCol, nRow );
381 }
382 
lcl_IsOtherTab(const basegfx::B2DPolyPolygon & rPolyPolygon)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 
HasArrow(const ScAddress & rStart,SCCOL nEndCol,SCROW nEndRow,SCTAB nEndTab)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 
IsNonAlienArrow(SdrObject * pObject)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 
InsertArrow(SCCOL nCol,SCROW nRow,SCCOL nRefStartCol,SCROW nRefStartRow,SCCOL nRefEndCol,SCROW nRefEndRow,sal_Bool bFromOtherTab,sal_Bool bRed,ScDetectiveData & rData)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 
InsertToOtherTab(SCCOL nStartCol,SCROW nStartRow,SCCOL nEndCol,SCROW nEndRow,sal_Bool bRed,ScDetectiveData & rData)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 
DrawEntry(SCCOL nCol,SCROW nRow,const ScRange & rRef,ScDetectiveData & rData)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 
DrawAlienEntry(const ScRange & rRef,ScDetectiveData & rData)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 
DrawCircle(SCCOL nCol,SCROW nRow,ScDetectiveData & rData)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 
DeleteArrowsAt(SCCOL nCol,SCROW nRow,sal_Bool bDestPnt)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 
RectIsPoints(const Rectangle & rRect,const Point & rStart,const Point & rEnd)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 
DeleteBox(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2)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 
InsertPredLevelArea(const ScRange & rRef,ScDetectiveData & rData,sal_uInt16 nLevel)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 
InsertPredLevel(SCCOL nCol,SCROW nRow,ScDetectiveData & rData,sal_uInt16 nLevel)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 
FindPredLevelArea(const ScRange & rRef,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)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 
FindPredLevel(SCCOL nCol,SCROW nRow,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)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 
InsertErrorLevel(SCCOL nCol,SCROW nRow,ScDetectiveData & rData,sal_uInt16 nLevel)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 
InsertSuccLevel(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,ScDetectiveData & rData,sal_uInt16 nLevel)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 
FindSuccLevel(SCCOL nCol1,SCROW nRow1,SCCOL nCol2,SCROW nRow2,sal_uInt16 nLevel,sal_uInt16 nDeleteLevel)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 
ShowPred(SCCOL nCol,SCROW nRow)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 
ShowSucc(SCCOL nCol,SCROW nRow)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 
ShowError(SCCOL nCol,SCROW nRow)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 
DeleteSucc(SCCOL nCol,SCROW nRow)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 
DeletePred(SCCOL nCol,SCROW nRow)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 
DeleteAll(ScDetectiveDelete eWhat)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 
MarkInvalid(sal_Bool & rOverflow)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 
UpdateAllComments(ScDocument & rDoc)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 
UpdateAllArrowColors()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 
FindFrameForObject(SdrObject * pObject,ScRange & rRange)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 
GetDetectiveObjectType(SdrObject * pObject,SCTAB nObjTab,ScAddress & rPosition,ScRange & rSource,sal_Bool & rRedLine)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 
InsertObject(ScDetectiveObjType eType,const ScAddress & rPosition,const ScRange & rSource,sal_Bool bRedLine)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
GetArrowColor()1667 ColorData ScDetectiveFunc::GetArrowColor()
1668 {
1669     if (!bColorsInitialized)
1670         InitializeColors();
1671     return nArrowColor;
1672 }
1673 
1674 // static
GetErrorColor()1675 ColorData ScDetectiveFunc::GetErrorColor()
1676 {
1677     if (!bColorsInitialized)
1678         InitializeColors();
1679     return nErrorColor;
1680 }
1681 
1682 // static
GetCommentColor()1683 ColorData ScDetectiveFunc::GetCommentColor()
1684 {
1685     if (!bColorsInitialized)
1686         InitializeColors();
1687     return nCommentColor;
1688 }
1689 
1690 // static
InitializeColors()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
IsColorsInitialized()1704 sal_Bool ScDetectiveFunc::IsColorsInitialized()
1705 {
1706     return bColorsInitialized;
1707 }
1708 
AppendChangTrackNoteSeparator(String & aDisplay)1709 void ScDetectiveFunc::AppendChangTrackNoteSeparator(String &aDisplay)
1710 {
1711     aDisplay.AppendAscii( RTL_CONSTASCII_STRINGPARAM("\n--------\n") );
1712 }
1713 
1714