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