| /************************************************************** |
| * |
| * 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 <svtools/colorcfg.hxx> |
| #include <editeng/eeitem.hxx> |
| #include <editeng/outlobj.hxx> |
| #include <svx/sdshitm.hxx> |
| #include <svx/sdsxyitm.hxx> |
| #include <svx/sdtditm.hxx> |
| #include <svx/svditer.hxx> |
| #include <svx/svdocapt.hxx> |
| #include <svx/svdocirc.hxx> |
| #include <svx/svdopath.hxx> |
| #include <svx/svdorect.hxx> |
| #include <svx/svdpage.hxx> |
| #include <svx/svdundo.hxx> |
| #include <svx/xfillit0.hxx> |
| #include <svx/xflclit.hxx> |
| #include <svx/xlnclit.hxx> |
| #include <svx/xlnedcit.hxx> |
| #include <svx/xlnedit.hxx> |
| #include <svx/xlnedwit.hxx> |
| #include <svx/xlnstcit.hxx> |
| #include <svx/xlnstit.hxx> |
| #include <svx/xlnstwit.hxx> |
| #include <svx/xlnwtit.hxx> |
| #include <svx/xtable.hxx> |
| #include <editeng/outliner.hxx> |
| #include <editeng/editobj.hxx> |
| #include <svx/sxcecitm.hxx> |
| #include <svl/whiter.hxx> |
| #include <editeng/writingmodeitem.hxx> |
| |
| #include <basegfx/point/b2dpoint.hxx> |
| #include <basegfx/polygon/b2dpolygontools.hxx> |
| #include <basegfx/polygon/b2dpolygon.hxx> |
| |
| #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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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<sal_uInt16>(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; |
| } |
| |
| void ScDetectiveFunc::AppendChangTrackNoteSeparator(String &aDisplay) |
| { |
| aDisplay.AppendAscii( RTL_CONSTASCII_STRINGPARAM("\n--------\n") ); |
| } |
| |