/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include "scitems.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "detfunc.hxx" #include "document.hxx" #include "dociter.hxx" #include "drwlayer.hxx" #include "userdat.hxx" #include "validat.hxx" #include "cell.hxx" #include "docpool.hxx" #include "patattr.hxx" #include "attrib.hxx" #include "scmod.hxx" #include "postit.hxx" //------------------------------------------------------------------------ // #99319# line ends are now created with an empty name. // The checkForUniqueItem method then finds a unique name for the item's value. #define SC_LINEEND_NAME EMPTY_STRING //------------------------------------------------------------------------ enum DetInsertResult { // Return-Werte beim Einfuegen in einen Level DET_INS_CONTINUE, DET_INS_INSERTED, DET_INS_EMPTY, DET_INS_CIRCULAR }; //------------------------------------------------------------------------ class ScDetectiveData { private: SfxItemSet aBoxSet; SfxItemSet aArrowSet; SfxItemSet aToTabSet; SfxItemSet aFromTabSet; SfxItemSet aCircleSet; //! einzeln ? sal_uInt16 nMaxLevel; public: ScDetectiveData( SdrModel* pModel ); SfxItemSet& GetBoxSet() { return aBoxSet; } SfxItemSet& GetArrowSet() { return aArrowSet; } SfxItemSet& GetToTabSet() { return aToTabSet; } SfxItemSet& GetFromTabSet() { return aFromTabSet; } SfxItemSet& GetCircleSet() { return aCircleSet; } void SetMaxLevel( sal_uInt16 nVal ) { nMaxLevel = nVal; } sal_uInt16 GetMaxLevel() const { return nMaxLevel; } }; class ScCommentData { public: ScCommentData( ScDocument& rDoc, SdrModel* pModel ); SfxItemSet& GetCaptionSet() { return aCaptionSet; } void UpdateCaptionSet( const SfxItemSet& rItemSet ); private: SfxItemSet aCaptionSet; }; //------------------------------------------------------------------------ ColorData ScDetectiveFunc::nArrowColor = 0; ColorData ScDetectiveFunc::nErrorColor = 0; ColorData ScDetectiveFunc::nCommentColor = 0; sal_Bool ScDetectiveFunc::bColorsInitialized = sal_False; //------------------------------------------------------------------------ sal_Bool lcl_HasThickLine( SdrObject& rObj ) { // thin lines get width 0 -> everything greater 0 is a thick line return ( ((const XLineWidthItem&)rObj.GetMergedItem(XATTR_LINEWIDTH)).GetValue() > 0 ); } //------------------------------------------------------------------------ ScDetectiveData::ScDetectiveData( SdrModel* pModel ) : aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ), aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ) { nMaxLevel = 0; aBoxSet.Put( XLineColorItem( EMPTY_STRING, Color( ScDetectiveFunc::GetArrowColor() ) ) ); aBoxSet.Put( XFillStyleItem( XFILL_NONE ) ); // #66479# Standard-Linienenden (wie aus XLineEndList::Create) selber zusammenbasteln, // um von den konfigurierten Linienenden unabhaengig zu sein basegfx::B2DPolygon aTriangle; aTriangle.append(basegfx::B2DPoint(10.0, 0.0)); aTriangle.append(basegfx::B2DPoint(0.0, 30.0)); aTriangle.append(basegfx::B2DPoint(20.0, 30.0)); aTriangle.setClosed(true); basegfx::B2DPolygon aSquare; aSquare.append(basegfx::B2DPoint(0.0, 0.0)); aSquare.append(basegfx::B2DPoint(10.0, 0.0)); aSquare.append(basegfx::B2DPoint(10.0, 10.0)); aSquare.append(basegfx::B2DPoint(0.0, 10.0)); aSquare.setClosed(true); basegfx::B2DPolygon aCircle(basegfx::tools::createPolygonFromEllipse(basegfx::B2DPoint(0.0, 0.0), 100.0, 100.0)); aCircle.setClosed(true); String aName = SC_LINEEND_NAME; aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) ); aArrowSet.Put( XLineStartWidthItem( 200 ) ); aArrowSet.Put( XLineStartCenterItem( sal_True ) ); aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); aArrowSet.Put( XLineEndWidthItem( 200 ) ); aArrowSet.Put( XLineEndCenterItem( sal_False ) ); aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) ); aToTabSet.Put( XLineStartWidthItem( 200 ) ); aToTabSet.Put( XLineStartCenterItem( sal_True ) ); aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) ); aToTabSet.Put( XLineEndWidthItem( 300 ) ); aToTabSet.Put( XLineEndCenterItem( sal_False ) ); aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) ); aFromTabSet.Put( XLineStartWidthItem( 300 ) ); aFromTabSet.Put( XLineStartCenterItem( sal_True ) ); aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); aFromTabSet.Put( XLineEndWidthItem( 200 ) ); aFromTabSet.Put( XLineEndCenterItem( sal_False ) ); aCircleSet.Put( XLineColorItem( String(), Color( ScDetectiveFunc::GetErrorColor() ) ) ); aCircleSet.Put( XFillStyleItem( XFILL_NONE ) ); sal_uInt16 nWidth = 55; // 54 = 1 Pixel aCircleSet.Put( XLineWidthItem( nWidth ) ); } ScCommentData::ScCommentData( ScDocument& rDoc, SdrModel* pModel ) : aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END, EE_ITEMS_START, EE_ITEMS_END, 0, 0 ) { basegfx::B2DPolygon aTriangle; aTriangle.append(basegfx::B2DPoint(10.0, 0.0)); aTriangle.append(basegfx::B2DPoint(0.0, 30.0)); aTriangle.append(basegfx::B2DPoint(20.0, 30.0)); aTriangle.setClosed(true); String aName = SC_LINEEND_NAME; aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) ); aCaptionSet.Put( XLineStartWidthItem( 200 ) ); aCaptionSet.Put( XLineStartCenterItem( sal_False ) ); aCaptionSet.Put( XFillStyleItem( XFILL_SOLID ) ); Color aYellow( ScDetectiveFunc::GetCommentColor() ); aCaptionSet.Put( XFillColorItem( String(), aYellow ) ); // shadow // SdrShadowItem has sal_False, instead the shadow is set for the rectangle // only with SetSpecialTextBoxShadow when the object is created // (item must be set to adjust objects from older files) aCaptionSet.Put( SdrShadowItem( sal_False ) ); aCaptionSet.Put( SdrShadowXDistItem( 100 ) ); aCaptionSet.Put( SdrShadowYDistItem( 100 ) ); // text attributes aCaptionSet.Put( SdrTextLeftDistItem( 100 ) ); aCaptionSet.Put( SdrTextRightDistItem( 100 ) ); aCaptionSet.Put( SdrTextUpperDistItem( 100 ) ); aCaptionSet.Put( SdrTextLowerDistItem( 100 ) ); aCaptionSet.Put( SdrTextAutoGrowWidthItem( sal_False ) ); aCaptionSet.Put( SdrTextAutoGrowHeightItem( sal_True ) ); // #78943# do use the default cell style, so the user has a chance to // modify the font for the annotations ((const ScPatternAttr&)rDoc.GetPool()->GetDefaultItem(ATTR_PATTERN)). FillEditItemSet( &aCaptionSet ); // support the best position for the tail connector now that // that notes can be resized and repositioned. aCaptionSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT) ); } void ScCommentData::UpdateCaptionSet( const SfxItemSet& rItemSet ) { SfxWhichIter aWhichIter( rItemSet ); const SfxPoolItem* pPoolItem = 0; for( sal_uInt16 nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() ) { if(rItemSet.GetItemState(nWhich, sal_False, &pPoolItem) == SFX_ITEM_SET) { switch(nWhich) { case SDRATTR_SHADOW: // use existing Caption default - appears that setting this // to true screws up the tail appearance. See also comment // for default setting above. break; case SDRATTR_SHADOWXDIST: // use existing Caption default - svx sets a value of 35 // but default 100 gives a better appearance. break; case SDRATTR_SHADOWYDIST: // use existing Caption default - svx sets a value of 35 // but default 100 gives a better appearance. break; default: aCaptionSet.Put(*pPoolItem); } } } } //------------------------------------------------------------------------ void ScDetectiveFunc::Modified() { if (pDoc->IsStreamValid(nTab)) pDoc->SetStreamValid(nTab, sal_False); } inline sal_Bool Intersect( SCCOL nStartCol1, SCROW nStartRow1, SCCOL nEndCol1, SCROW nEndRow1, SCCOL nStartCol2, SCROW nStartRow2, SCCOL nEndCol2, SCROW nEndRow2 ) { return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 && nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1; } sal_Bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos ) { rErrPos = rRange.aStart; sal_uInt16 nError = 0; ScCellIterator aCellIter( pDoc, rRange); ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) { nError = ((ScFormulaCell*)pCell)->GetErrCode(); if (nError) rErrPos.Set( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetTab() ); } pCell = aCellIter.GetNext(); } return (nError != 0); } Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const { DBG_ASSERT( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" ); SanitizeCol( nCol ); SanitizeRow( nRow ); Point aPos; switch( eMode ) { case DRAWPOS_TOPLEFT: break; case DRAWPOS_BOTTOMRIGHT: ++nCol; ++nRow; break; case DRAWPOS_DETARROW: aPos.X() += pDoc->GetColWidth( nCol, nTab ) / 4; aPos.Y() += pDoc->GetRowHeight( nRow, nTab ) / 2; break; case DRAWPOS_CAPTIONLEFT: aPos.X() += 6; break; case DRAWPOS_CAPTIONRIGHT: { // find right end of passed cell position const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE ) ); if ( pMerge->GetColMerge() > 1 ) nCol = nCol + pMerge->GetColMerge(); else ++nCol; aPos.X() -= 6; } break; } for ( SCCOL i = 0; i < nCol; ++i ) aPos.X() += pDoc->GetColWidth( i, nTab ); aPos.Y() += pDoc->GetRowHeight( 0, nRow - 1, nTab ); aPos.X() = static_cast< long >( aPos.X() * HMM_PER_TWIPS ); aPos.Y() = static_cast< long >( aPos.Y() * HMM_PER_TWIPS ); if ( pDoc->IsNegativePage( nTab ) ) aPos.X() *= -1; return aPos; } Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const { Rectangle aRect( GetDrawPos( ::std::min( nCol1, nCol2 ), ::std::min( nRow1, nRow2 ), DRAWPOS_TOPLEFT ), GetDrawPos( ::std::max( nCol1, nCol2 ), ::std::max( nRow1, nRow2 ), DRAWPOS_BOTTOMRIGHT ) ); aRect.Justify(); // reorder left/right in RTL sheets return aRect; } Rectangle ScDetectiveFunc::GetDrawRect( SCCOL nCol, SCROW nRow ) const { return GetDrawRect( nCol, nRow, nCol, nRow ); } sal_Bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon ) { // test if rPolygon is the line end for "other table" (rectangle) if(1L == rPolyPolygon.count()) { const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0L)); // #i73305# circle consists of 4 segments, too, distinguishable from square by // the use of control points if(4L == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed()) { return true; } } return false; } sal_Bool ScDetectiveFunc::HasArrow( const ScAddress& rStart, SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab ) { sal_Bool bStartAlien = ( rStart.Tab() != nTab ); sal_Bool bEndAlien = ( nEndTab != nTab ); if (bStartAlien && bEndAlien) { DBG_ERROR("bStartAlien && bEndAlien"); return sal_True; } Rectangle aStartRect; Rectangle aEndRect; if (!bStartAlien) aStartRect = GetDrawRect( rStart.Col(), rStart.Row() ); if (!bEndAlien) aEndRect = GetDrawRect( nEndCol, nEndRow ); ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); DBG_ASSERT(pPage,"Page ?"); sal_Bool bFound = sal_False; SdrObjListIter aIter( *pPage, IM_FLAT ); SdrObject* pObject = aIter.Next(); while (pObject && !bFound) { if ( pObject->GetLayer()==SC_LAYER_INTERN && pObject->IsPolyObj() && pObject->GetPointCount()==2 ) { const SfxItemSet& rSet = pObject->GetMergedItemSet(); sal_Bool bObjStartAlien = lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() ); sal_Bool bObjEndAlien = lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() ); sal_Bool bStartHit = bStartAlien ? bObjStartAlien : ( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) ); sal_Bool bEndHit = bEndAlien ? bObjEndAlien : ( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) ); if ( bStartHit && bEndHit ) bFound = sal_True; } pObject = aIter.Next(); } return bFound; } sal_Bool ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject ) // static { if ( pObject->GetLayer()==SC_LAYER_INTERN && pObject->IsPolyObj() && pObject->GetPointCount()==2 ) { const SfxItemSet& rSet = pObject->GetMergedItemSet(); sal_Bool bObjStartAlien = lcl_IsOtherTab( ((const XLineStartItem&)rSet.Get(XATTR_LINESTART)).GetLineStartValue() ); sal_Bool bObjEndAlien = lcl_IsOtherTab( ((const XLineEndItem&)rSet.Get(XATTR_LINEEND)).GetLineEndValue() ); return !bObjStartAlien && !bObjEndAlien; } return sal_False; } //------------------------------------------------------------------------ // InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject sal_Bool ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow, SCCOL nRefStartCol, SCROW nRefStartRow, SCCOL nRefEndCol, SCROW nRefEndRow, sal_Bool bFromOtherTab, sal_Bool bRed, ScDetectiveData& rData ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); sal_Bool bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow ); if (bArea && !bFromOtherTab) { // insert the rectangle before the arrow - this is relied on in FindFrameForObject Rectangle aRect = GetDrawRect( nRefStartCol, nRefStartRow, nRefEndCol, nRefEndRow ); SdrRectObj* pBox = new SdrRectObj( aRect ); pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet()); ScDrawLayer::SetAnchor( pBox, SCA_CELL ); pBox->SetLayer( SC_LAYER_INTERN ); pPage->InsertObject( pBox ); pModel->AddCalcUndo< SdrUndoInsertObj >( *pBox ); ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True ); pData->maStart.Set( nRefStartCol, nRefStartRow, nTab); pData->maEnd.Set( nRefEndCol, nRefEndRow, nTab); } Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, DRAWPOS_DETARROW ); Point aEndPos = GetDrawPos( nCol, nRow, DRAWPOS_DETARROW ); if (bFromOtherTab) { sal_Bool bNegativePage = pDoc->IsNegativePage( nTab ); long nPageSign = bNegativePage ? -1 : 1; aStartPos = Point( aEndPos.X() - 1000 * nPageSign, aEndPos.Y() - 1000 ); if (aStartPos.X() * nPageSign < 0) aStartPos.X() += 2000 * nPageSign; if (aStartPos.Y() < 0) aStartPos.Y() += 2000; } SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet(); if (bArea && !bFromOtherTab) rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich else rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() ); rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) ); basegfx::B2DPolygon aTempPoly; aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y())); aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y())); SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly)); pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ??? pArrow->SetMergedItemSetAndBroadcast(rAttrSet); ScDrawLayer::SetAnchor( pArrow, SCA_CELL ); pArrow->SetLayer( SC_LAYER_INTERN ); pPage->InsertObject( pArrow ); pModel->AddCalcUndo< SdrUndoInsertObj >( *pArrow ); ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, sal_True ); if (bFromOtherTab) pData->maStart.SetInvalid(); else pData->maStart.Set( nRefStartCol, nRefStartRow, nTab); pData->maEnd.Set( nCol, nRow, nTab); Modified(); return sal_True; } sal_Bool ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, sal_Bool bRed, ScDetectiveData& rData ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); sal_Bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow ); if (bArea) { Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow ); SdrRectObj* pBox = new SdrRectObj( aRect ); pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet()); ScDrawLayer::SetAnchor( pBox, SCA_CELL ); pBox->SetLayer( SC_LAYER_INTERN ); pPage->InsertObject( pBox ); pModel->AddCalcUndo< SdrUndoInsertObj >( *pBox ); ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, sal_True ); pData->maStart.Set( nStartCol, nStartRow, nTab); pData->maEnd.Set( nEndCol, nEndRow, nTab); } sal_Bool bNegativePage = pDoc->IsNegativePage( nTab ); long nPageSign = bNegativePage ? -1 : 1; Point aStartPos = GetDrawPos( nStartCol, nStartRow, DRAWPOS_DETARROW ); Point aEndPos = Point( aStartPos.X() + 1000 * nPageSign, aStartPos.Y() - 1000 ); if (aEndPos.Y() < 0) aEndPos.Y() += 2000; SfxItemSet& rAttrSet = rData.GetToTabSet(); if (bArea) rAttrSet.Put( XLineWidthItem( 50 ) ); // Bereich else rAttrSet.Put( XLineWidthItem( 0 ) ); // einzelne Referenz ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() ); rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) ); basegfx::B2DPolygon aTempPoly; aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y())); aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y())); SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly)); pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos)); //! noetig ??? pArrow->SetMergedItemSetAndBroadcast(rAttrSet); ScDrawLayer::SetAnchor( pArrow, SCA_CELL ); pArrow->SetLayer( SC_LAYER_INTERN ); pPage->InsertObject( pArrow ); pModel->AddCalcUndo< SdrUndoInsertObj >( *pArrow ); ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, sal_True ); pData->maStart.Set( nStartCol, nStartRow, nTab); pData->maEnd.SetInvalid(); Modified(); return sal_True; } //------------------------------------------------------------------------ // DrawEntry: Formel auf dieser Tabelle, // Referenz auf dieser oder anderer // DrawAlienEntry: Formel auf anderer Tabelle, // Referenz auf dieser // return FALSE: da war schon ein Pfeil sal_Bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow, const ScRange& rRef, ScDetectiveData& rData ) { if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) ) return sal_False; ScAddress aErrorPos; sal_Bool bError = HasError( rRef, aErrorPos ); sal_Bool bAlien = ( rRef.aEnd.Tab() < nTab || rRef.aStart.Tab() > nTab ); return InsertArrow( nCol, nRow, rRef.aStart.Col(), rRef.aStart.Row(), rRef.aEnd.Col(), rRef.aEnd.Row(), bAlien, bError, rData ); } sal_Bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef, ScDetectiveData& rData ) { if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) ) return sal_False; ScAddress aErrorPos; sal_Bool bError = HasError( rRef, aErrorPos ); return InsertToOtherTab( rRef.aStart.Col(), rRef.aStart.Row(), rRef.aEnd.Col(), rRef.aEnd.Row(), bError, rData ); } void ScDetectiveFunc::DrawCircle( SCCOL nCol, SCROW nRow, ScDetectiveData& rData ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); Rectangle aRect = GetDrawRect( nCol, nRow ); aRect.Left() -= 250; aRect.Right() += 250; aRect.Top() -= 70; aRect.Bottom() += 70; SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect ); SfxItemSet& rAttrSet = rData.GetCircleSet(); pCircle->SetMergedItemSetAndBroadcast(rAttrSet); ScDrawLayer::SetAnchor( pCircle, SCA_CELL ); pCircle->SetLayer( SC_LAYER_INTERN ); pPage->InsertObject( pCircle ); pModel->AddCalcUndo< SdrUndoInsertObj >( *pCircle ); ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, sal_True ); pData->maStart.Set( nCol, nRow, nTab); pData->maEnd.SetInvalid(); Modified(); } void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, sal_Bool bDestPnt ) { Rectangle aRect = GetDrawRect( nCol, nRow ); ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); DBG_ASSERT(pPage,"Page ?"); pPage->RecalcObjOrdNums(); long nDelCount = 0; sal_uLong nObjCount = pPage->GetObjCount(); if (nObjCount) { SdrObject** ppObj = new SdrObject*[nObjCount]; SdrObjListIter aIter( *pPage, IM_FLAT ); SdrObject* pObject = aIter.Next(); while (pObject) { if ( pObject->GetLayer()==SC_LAYER_INTERN && pObject->IsPolyObj() && pObject->GetPointCount()==2 ) { if (aRect.IsInside(pObject->GetPoint(bDestPnt))) // Start/Zielpunkt ppObj[nDelCount++] = pObject; } pObject = aIter.Next(); } long i; for (i=1; i<=nDelCount; i++) pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] ); for (i=1; i<=nDelCount; i++) pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); delete[] ppObj; Modified(); } } // Box um Referenz loeschen #define SC_DET_TOLERANCE 50 inline sal_Bool RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd ) { return rRect.Left() >= rStart.X() - SC_DET_TOLERANCE && rRect.Left() <= rStart.X() + SC_DET_TOLERANCE && rRect.Right() >= rEnd.X() - SC_DET_TOLERANCE && rRect.Right() <= rEnd.X() + SC_DET_TOLERANCE && rRect.Top() >= rStart.Y() - SC_DET_TOLERANCE && rRect.Top() <= rStart.Y() + SC_DET_TOLERANCE && rRect.Bottom() >= rEnd.Y() - SC_DET_TOLERANCE && rRect.Bottom() <= rEnd.Y() + SC_DET_TOLERANCE; } #undef SC_DET_TOLERANCE void ScDetectiveFunc::DeleteBox( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) { /* String aStr; aStr += nCol1; aStr += '/'; aStr += nRow1; aStr += '/'; aStr += nCol2; aStr += '/'; aStr += nRow2; InfoBox(0,aStr).Execute(); */ Rectangle aCornerRect = GetDrawRect( nCol1, nRow1, nCol2, nRow2 ); Point aStartCorner = aCornerRect.TopLeft(); Point aEndCorner = aCornerRect.BottomRight(); Rectangle aObjRect; ScDrawLayer* pModel = pDoc->GetDrawLayer(); SdrPage* pPage = pModel->GetPage(static_cast(nTab)); DBG_ASSERT(pPage,"Page ?"); pPage->RecalcObjOrdNums(); long nDelCount = 0; sal_uLong nObjCount = pPage->GetObjCount(); if (nObjCount) { SdrObject** ppObj = new SdrObject*[nObjCount]; SdrObjListIter aIter( *pPage, IM_FLAT ); SdrObject* pObject = aIter.Next(); while (pObject) { if ( pObject->GetLayer() == SC_LAYER_INTERN && pObject->Type() == TYPE(SdrRectObj) ) { aObjRect = ((SdrRectObj*)pObject)->GetLogicRect(); aObjRect.Justify(); if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) ) ppObj[nDelCount++] = pObject; } pObject = aIter.Next(); } long i; for (i=1; i<=nDelCount; i++) pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] ); for (i=1; i<=nDelCount; i++) pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); delete[] ppObj; Modified(); } } //------------------------------------------------------------------------ sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef, ScDetectiveData& rData, sal_uInt16 nLevel ) { sal_uInt16 nResult = DET_INS_EMPTY; ScCellIterator aCellIter( pDoc, rRef); ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) switch( InsertPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), rData, nLevel ) ) { case DET_INS_INSERTED: nResult = DET_INS_INSERTED; break; case DET_INS_CONTINUE: if (nResult != DET_INS_INSERTED) nResult = DET_INS_CONTINUE; break; case DET_INS_CIRCULAR: if (nResult == DET_INS_EMPTY) nResult = DET_INS_CIRCULAR; break; } pCell = aCellIter.GetNext(); } return nResult; } sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData, sal_uInt16 nLevel ) { ScBaseCell* pCell; pDoc->GetCell( nCol, nRow, nTab, pCell ); if (!pCell) return DET_INS_EMPTY; if (pCell->GetCellType() != CELLTYPE_FORMULA) return DET_INS_EMPTY; ScFormulaCell* pFCell = (ScFormulaCell*)pCell; if (pFCell->IsRunning()) return DET_INS_CIRCULAR; if (pFCell->GetDirty()) pFCell->Interpret(); // nach SetRunning geht's nicht mehr! pFCell->SetRunning(sal_True); sal_uInt16 nResult = DET_INS_EMPTY; ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); ScRange aRef; while ( aIter.GetNextRef( aRef ) ) { if (DrawEntry( nCol, nRow, aRef, rData )) { nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen } else { // weiterverfolgen if ( nLevel < rData.GetMaxLevel() ) { sal_uInt16 nSubResult; sal_Bool bArea = (aRef.aStart != aRef.aEnd); if (bArea) nSubResult = InsertPredLevelArea( aRef, rData, nLevel+1 ); else nSubResult = InsertPredLevel( aRef.aStart.Col(), aRef.aStart.Row(), rData, nLevel+1 ); switch (nSubResult) { case DET_INS_INSERTED: nResult = DET_INS_INSERTED; break; case DET_INS_CONTINUE: if (nResult != DET_INS_INSERTED) nResult = DET_INS_CONTINUE; break; case DET_INS_CIRCULAR: if (nResult == DET_INS_EMPTY) nResult = DET_INS_CIRCULAR; break; // DET_INS_EMPTY: unveraendert lassen } } else // nMaxLevel erreicht if (nResult != DET_INS_INSERTED) nResult = DET_INS_CONTINUE; } } pFCell->SetRunning(sal_False); return nResult; } sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel ) { sal_uInt16 nResult = nLevel; ScCellIterator aCellIter( pDoc, rRef); ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) { sal_uInt16 nTemp = FindPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), nLevel, nDeleteLevel ); if (nTemp > nResult) nResult = nTemp; } pCell = aCellIter.GetNext(); } return nResult; } // nDeleteLevel != 0 -> loeschen sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel ) { DBG_ASSERT( nLevel<1000, "Level" ); ScBaseCell* pCell; pDoc->GetCell( nCol, nRow, nTab, pCell ); if (!pCell) return nLevel; if (pCell->GetCellType() != CELLTYPE_FORMULA) return nLevel; ScFormulaCell* pFCell = (ScFormulaCell*)pCell; if (pFCell->IsRunning()) return nLevel; if (pFCell->GetDirty()) pFCell->Interpret(); // nach SetRunning geht's nicht mehr! pFCell->SetRunning(sal_True); sal_uInt16 nResult = nLevel; sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 ); if ( bDelete ) { DeleteArrowsAt( nCol, nRow, sal_True ); // Pfeile, die hierher zeigen } ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); ScRange aRef; while ( aIter.GetNextRef( aRef) ) { sal_Bool bArea = ( aRef.aStart != aRef.aEnd ); if ( bDelete ) // Rahmen loeschen ? { if (bArea) { DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() ); } } else // weitersuchen { if ( HasArrow( aRef.aStart, nCol,nRow,nTab ) ) { sal_uInt16 nTemp; if (bArea) nTemp = FindPredLevelArea( aRef, nLevel+1, nDeleteLevel ); else nTemp = FindPredLevel( aRef.aStart.Col(),aRef.aStart.Row(), nLevel+1, nDeleteLevel ); if (nTemp > nResult) nResult = nTemp; } } } pFCell->SetRunning(sal_False); return nResult; } //------------------------------------------------------------------------ sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData, sal_uInt16 nLevel ) { ScBaseCell* pCell; pDoc->GetCell( nCol, nRow, nTab, pCell ); if (!pCell) return DET_INS_EMPTY; if (pCell->GetCellType() != CELLTYPE_FORMULA) return DET_INS_EMPTY; ScFormulaCell* pFCell = (ScFormulaCell*)pCell; if (pFCell->IsRunning()) return DET_INS_CIRCULAR; if (pFCell->GetDirty()) pFCell->Interpret(); // nach SetRunning geht's nicht mehr! pFCell->SetRunning(sal_True); sal_uInt16 nResult = DET_INS_EMPTY; ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); ScRange aRef; ScAddress aErrorPos; sal_Bool bHasError = sal_False; while ( aIter.GetNextRef( aRef ) ) { if (HasError( aRef, aErrorPos )) { bHasError = sal_True; if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData )) nResult = DET_INS_INSERTED; // und weiterverfolgen if ( nLevel < rData.GetMaxLevel() ) // praktisch immer { if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(), rData, nLevel+1 ) == DET_INS_INSERTED) nResult = DET_INS_INSERTED; } } } pFCell->SetRunning(sal_False); // Blaetter ? if (!bHasError) if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED) nResult = DET_INS_INSERTED; return nResult; } //------------------------------------------------------------------------ sal_uInt16 ScDetectiveFunc::InsertSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScDetectiveData& rData, sal_uInt16 nLevel ) { // ueber ganzes Dokument sal_uInt16 nResult = DET_INS_EMPTY; // ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab ); ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB ); // alle Tabellen ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) { ScFormulaCell* pFCell = (ScFormulaCell*)pCell; sal_Bool bRunning = pFCell->IsRunning(); if (pFCell->GetDirty()) pFCell->Interpret(); // nach SetRunning geht's nicht mehr! pFCell->SetRunning(sal_True); ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); ScRange aRef; while ( aIter.GetNextRef( aRef) ) { if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab) { if (Intersect( nCol1,nRow1,nCol2,nRow2, aRef.aStart.Col(),aRef.aStart.Row(), aRef.aEnd.Col(),aRef.aEnd.Row() )) { sal_Bool bAlien = ( aCellIter.GetTab() != nTab ); sal_Bool bDrawRet; if (bAlien) bDrawRet = DrawAlienEntry( aRef, rData ); else bDrawRet = DrawEntry( aCellIter.GetCol(), aCellIter.GetRow(), aRef, rData ); if (bDrawRet) { nResult = DET_INS_INSERTED; // neuer Pfeil eingetragen } else { if (bRunning) { if (nResult == DET_INS_EMPTY) nResult = DET_INS_CIRCULAR; } else { // weiterverfolgen if ( nLevel < rData.GetMaxLevel() ) { sal_uInt16 nSubResult = InsertSuccLevel( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetCol(), aCellIter.GetRow(), rData, nLevel+1 ); switch (nSubResult) { case DET_INS_INSERTED: nResult = DET_INS_INSERTED; break; case DET_INS_CONTINUE: if (nResult != DET_INS_INSERTED) nResult = DET_INS_CONTINUE; break; case DET_INS_CIRCULAR: if (nResult == DET_INS_EMPTY) nResult = DET_INS_CIRCULAR; break; // DET_INS_EMPTY: unveraendert lassen } } else // nMaxLevel erreicht if (nResult != DET_INS_INSERTED) nResult = DET_INS_CONTINUE; } } } } } pFCell->SetRunning(bRunning); } pCell = aCellIter.GetNext(); } return nResult; } sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel ) { DBG_ASSERT( nLevel<1000, "Level" ); sal_uInt16 nResult = nLevel; sal_Bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 ); ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab ); ScBaseCell* pCell = aCellIter.GetFirst(); while (pCell) { if (pCell->GetCellType() == CELLTYPE_FORMULA) { ScFormulaCell* pFCell = (ScFormulaCell*)pCell; sal_Bool bRunning = pFCell->IsRunning(); if (pFCell->GetDirty()) pFCell->Interpret(); // nach SetRunning geht's nicht mehr! pFCell->SetRunning(sal_True); ScDetectiveRefIter aIter( (ScFormulaCell*) pCell ); ScRange aRef; while ( aIter.GetNextRef( aRef) ) { if (aRef.aStart.Tab() <= nTab && aRef.aEnd.Tab() >= nTab) { if (Intersect( nCol1,nRow1,nCol2,nRow2, aRef.aStart.Col(),aRef.aStart.Row(), aRef.aEnd.Col(),aRef.aEnd.Row() )) { if ( bDelete ) // Pfeile, die hier anfangen { if (aRef.aStart != aRef.aEnd) { DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() ); } DeleteArrowsAt( aRef.aStart.Col(), aRef.aStart.Row(), sal_False ); } else if ( !bRunning && HasArrow( aRef.aStart, aCellIter.GetCol(),aCellIter.GetRow(),aCellIter.GetTab() ) ) { sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetCol(), aCellIter.GetRow(), nLevel+1, nDeleteLevel ); if (nTemp > nResult) nResult = nTemp; } } } } pFCell->SetRunning(bRunning); } pCell = aCellIter.GetNext(); } return nResult; } // // -------------------------------------------------------------------------------- // sal_Bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; ScDetectiveData aData( pModel ); sal_uInt16 nMaxLevel = 0; sal_uInt16 nResult = DET_INS_CONTINUE; while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000) { aData.SetMaxLevel( nMaxLevel ); nResult = InsertPredLevel( nCol, nRow, aData, 0 ); ++nMaxLevel; } return ( nResult == DET_INS_INSERTED ); } sal_Bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; ScDetectiveData aData( pModel ); sal_uInt16 nMaxLevel = 0; sal_uInt16 nResult = DET_INS_CONTINUE; while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000) { aData.SetMaxLevel( nMaxLevel ); nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 ); ++nMaxLevel; } return ( nResult == DET_INS_INSERTED ); } sal_Bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; ScRange aRange( nCol, nRow, nTab ); ScAddress aErrPos; if ( !HasError( aRange,aErrPos ) ) return sal_False; ScDetectiveData aData( pModel ); aData.SetMaxLevel( 1000 ); sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 ); return ( nResult == DET_INS_INSERTED ); } sal_Bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 ); if ( nLevelCount ) FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // loeschen return ( nLevelCount != 0 ); } sal_Bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 ); if ( nLevelCount ) FindPredLevel( nCol, nRow, 0, nLevelCount ); // loeschen return ( nLevelCount != 0 ); } sal_Bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; SdrPage* pPage = pModel->GetPage(static_cast(nTab)); DBG_ASSERT(pPage,"Page ?"); pPage->RecalcObjOrdNums(); long nDelCount = 0; sal_uLong nObjCount = pPage->GetObjCount(); if (nObjCount) { SdrObject** ppObj = new SdrObject*[nObjCount]; SdrObjListIter aIter( *pPage, IM_FLAT ); SdrObject* pObject = aIter.Next(); while (pObject) { if ( pObject->GetLayer() == SC_LAYER_INTERN ) { sal_Bool bDoThis = sal_True; if ( eWhat != SC_DET_ALL ) { sal_Bool bCircle = ( pObject->ISA(SdrCircObj) ); sal_Bool bCaption = ScDrawLayer::IsNoteCaption( pObject ); if ( eWhat == SC_DET_DETECTIVE ) // Detektiv, aus Menue bDoThis = !bCaption; // auch Kreise else if ( eWhat == SC_DET_CIRCLES ) // Kreise, wenn neue erzeugt werden bDoThis = bCircle; else if ( eWhat == SC_DET_ARROWS ) // DetectiveRefresh bDoThis = !bCaption && !bCircle; // don't include circles else { DBG_ERROR("wat?"); } } if ( bDoThis ) ppObj[nDelCount++] = pObject; } pObject = aIter.Next(); } long i; for (i=1; i<=nDelCount; i++) pModel->AddCalcUndo< SdrUndoRemoveObj >( *ppObj[nDelCount-i] ); for (i=1; i<=nDelCount; i++) pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); delete[] ppObj; Modified(); } return ( nDelCount != 0 ); } sal_Bool ScDetectiveFunc::MarkInvalid(sal_Bool& rOverflow) { rOverflow = sal_False; ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; sal_Bool bDeleted = DeleteAll( SC_DET_CIRCLES ); // nur die Kreise ScDetectiveData aData( pModel ); long nInsCount = 0; // Stellen suchen, wo Gueltigkeit definiert ist ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW ); SCCOL nCol; SCROW nRow1; SCROW nRow2; const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 ); while ( pPattern && nInsCount < SC_DET_MAXCIRCLE ) { sal_uLong nIndex = ((const SfxUInt32Item&)pPattern->GetItem(ATTR_VALIDDATA)).GetValue(); if (nIndex) { const ScValidationData* pData = pDoc->GetValidationEntry( nIndex ); if ( pData ) { // Zellen in dem Bereich durchgehen sal_Bool bMarkEmpty = !pData->IsIgnoreBlank(); SCROW nNextRow = nRow1; SCROW nRow; ScCellIterator aCellIter( pDoc, nCol,nRow1,nTab, nCol,nRow2,nTab ); ScBaseCell* pCell = aCellIter.GetFirst(); while ( pCell && nInsCount < SC_DET_MAXCIRCLE ) { SCROW nCellRow = aCellIter.GetRow(); if ( bMarkEmpty ) for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ ) { DrawCircle( nCol, nRow, aData ); ++nInsCount; } if ( !pData->IsDataValid( pCell, ScAddress( nCol, nCellRow, nTab ) ) ) { DrawCircle( nCol, nCellRow, aData ); ++nInsCount; } nNextRow = nCellRow + 1; pCell = aCellIter.GetNext(); } if ( bMarkEmpty ) for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ ) { DrawCircle( nCol, nRow, aData ); ++nInsCount; } } } pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 ); } if ( nInsCount >= SC_DET_MAXCIRCLE ) rOverflow = sal_True; return ( bDeleted || nInsCount != 0 ); } void ScDetectiveFunc::UpdateAllComments( ScDocument& rDoc ) { // for all caption objects, update attributes and SpecialTextBoxShadow flag // (on all tables - nTab is ignored!) // no undo actions, this is refreshed after undo ScDrawLayer* pModel = rDoc.GetDrawLayer(); if (!pModel) return; for( SCTAB nObjTab = 0, nTabCount = rDoc.GetTableCount(); nObjTab < nTabCount; ++nObjTab ) { rDoc.InitializeNoteCaptions( nObjTab ); SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) ); DBG_ASSERT( pPage, "Page ?" ); if( pPage ) { SdrObjListIter aIter( *pPage, IM_FLAT ); for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() ) { if ( ScDrawObjData* pData = ScDrawLayer::GetNoteCaptionData( pObject, nObjTab ) ) { ScPostIt* pNote = rDoc.GetNote( pData->maStart ); // caption should exist, we iterate over drawing objects... DBG_ASSERT( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" ); if( pNote ) { ScCommentData aData( rDoc, pModel ); SfxItemSet aAttrColorSet = pObject->GetMergedItemSet(); aAttrColorSet.Put( XFillColorItem( String(), GetCommentColor() ) ); aData.UpdateCaptionSet( aAttrColorSet ); pObject->SetMergedItemSetAndBroadcast( aData.GetCaptionSet() ); if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) ) { pCaption->SetSpecialTextBoxShadow(); pCaption->SetFixedTail(); } } } } } } } void ScDetectiveFunc::UpdateAllArrowColors() { // no undo actions necessary ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return; for( SCTAB nObjTab = 0, nTabCount = pDoc->GetTableCount(); nObjTab < nTabCount; ++nObjTab ) { SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) ); DBG_ASSERT( pPage, "Page ?" ); if( pPage ) { SdrObjListIter aIter( *pPage, IM_FLAT ); for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() ) { if ( pObject->GetLayer() == SC_LAYER_INTERN ) { sal_Bool bArrow = sal_False; sal_Bool bError = sal_False; ScAddress aPos; ScRange aSource; sal_Bool bDummy; ScDetectiveObjType eType = GetDetectiveObjectType( pObject, nObjTab, aPos, aSource, bDummy ); if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB ) { // source is valid, determine error flag from source range ScAddress aErrPos; if ( HasError( aSource, aErrPos ) ) bError = sal_True; else bArrow = sal_True; } else if ( eType == SC_DETOBJ_FROMOTHERTAB ) { // source range is no longer known, take error flag from formula itself // (this means, if the formula has an error, all references to other tables // are marked red) ScAddress aErrPos; if ( HasError( ScRange( aPos), aErrPos ) ) bError = sal_True; else bArrow = sal_True; } else if ( eType == SC_DETOBJ_CIRCLE ) { // circles (error marks) are always red bError = sal_True; } else if ( eType == SC_DETOBJ_NONE ) { // frame for area reference has no ObjType, always gets arrow color if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) ) { bArrow = sal_True; } } if ( bArrow || bError ) { ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() ); //pObject->SendRepaintBroadcast(pObject->GetBoundRect()); pObject->SetMergedItem( XLineColorItem( String(), Color( nColorData ) ) ); // repaint only pObject->ActionChanged(); // pObject->SendRepaintBroadcast(pObject->GetBoundRect()); } } } } } } sal_Bool ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange ) { // find the rectangle for an arrow (always the object directly before the arrow) // rRange must be initialized to the source cell of the arrow (start of area) ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return sal_False; SdrPage* pPage = pModel->GetPage(static_cast(nTab)); DBG_ASSERT(pPage,"Page ?"); if (!pPage) return sal_False; // test if the object is a direct page member if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) ) { // Is there a previous object? const sal_uInt32 nOrdNum(pObject->GetOrdNum()); if(nOrdNum > 0) { SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1); if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) ) { ScDrawObjData* pPrevData = ScDrawLayer::GetObjDataTab( pPrevObj, rRange.aStart.Tab() ); if ( pPrevData && pPrevData->maStart.IsValid() && pPrevData->maEnd.IsValid() && (pPrevData->maStart == rRange.aStart) ) { rRange.aEnd = pPrevData->maEnd; return sal_True; } } } } return sal_False; } ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab, ScAddress& rPosition, ScRange& rSource, sal_Bool& rRedLine ) { rRedLine = sal_False; ScDetectiveObjType eType = SC_DETOBJ_NONE; if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN ) { if ( ScDrawObjData* pData = ScDrawLayer::GetObjDataTab( pObject, nObjTab ) ) { bool bValidStart = pData->maStart.IsValid(); bool bValidEnd = pData->maEnd.IsValid(); if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 ) { // line object -> arrow if ( bValidStart ) eType = bValidEnd ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB; else if ( bValidEnd ) eType = SC_DETOBJ_FROMOTHERTAB; if ( bValidStart ) rSource = pData->maStart; if ( bValidEnd ) rPosition = pData->maEnd; if ( bValidStart && lcl_HasThickLine( *pObject ) ) { // thick line -> look for frame before this object FindFrameForObject( pObject, rSource ); // modifies rSource } ColorData nObjColor = ((const XLineColorItem&)pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor(); if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() ) rRedLine = sal_True; } else if ( pObject->ISA(SdrCircObj) ) { if ( bValidStart ) { // cell position is returned in rPosition rPosition = pData->maStart; eType = SC_DETOBJ_CIRCLE; } } } } return eType; } void ScDetectiveFunc::InsertObject( ScDetectiveObjType eType, const ScAddress& rPosition, const ScRange& rSource, sal_Bool bRedLine ) { ScDrawLayer* pModel = pDoc->GetDrawLayer(); if (!pModel) return; ScDetectiveData aData( pModel ); switch (eType) { case SC_DETOBJ_ARROW: case SC_DETOBJ_FROMOTHERTAB: InsertArrow( rPosition.Col(), rPosition.Row(), rSource.aStart.Col(), rSource.aStart.Row(), rSource.aEnd.Col(), rSource.aEnd.Row(), (eType == SC_DETOBJ_FROMOTHERTAB), bRedLine, aData ); break; case SC_DETOBJ_TOOTHERTAB: InsertToOtherTab( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aEnd.Col(), rSource.aEnd.Row(), bRedLine, aData ); break; case SC_DETOBJ_CIRCLE: DrawCircle( rPosition.Col(), rPosition.Row(), aData ); break; default: { // added to avoid warnings } } } // static ColorData ScDetectiveFunc::GetArrowColor() { if (!bColorsInitialized) InitializeColors(); return nArrowColor; } // static ColorData ScDetectiveFunc::GetErrorColor() { if (!bColorsInitialized) InitializeColors(); return nErrorColor; } // static ColorData ScDetectiveFunc::GetCommentColor() { if (!bColorsInitialized) InitializeColors(); return nCommentColor; } // static void ScDetectiveFunc::InitializeColors() { // may be called several times to update colors from configuration const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig(); nArrowColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVE).nColor; nErrorColor = rColorCfg.GetColorValue(svtools::CALCDETECTIVEERROR).nColor; nCommentColor = rColorCfg.GetColorValue(svtools::CALCNOTESBACKGROUND).nColor; bColorsInitialized = sal_True; } // static sal_Bool ScDetectiveFunc::IsColorsInitialized() { return bColorsInitialized; }