blob: ba70f18a6786ac7ea7f770520cae1c32a70688ff [file] [log] [blame]
/**************************************************************
*
* 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") );
}