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