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