when we're not recording undo, then SdrUndoDelObj just deletes the argument its passed. And this code assumes that it is transferred to the SdrUndoDelObj and it still exists. Use the same pattern as ScPostIt::RemoveCaption for this situation Invalid read of size 8 at 0xB1713B6: SdrObject::GetOrdNum() const (svdobj.cxx:777) by 0x3C9E029C: ScDetectiveFunc::DeleteArrowsAt(short, int, bool) (detfunc.cxx:695) by 0x3C9E1D4C: ScDetectiveFunc::FindSuccLevel(short, int, short, int, unsigned short, unsigned short) (detfunc.cxx:1118) by 0x3C9E2405: ScDetectiveFunc::DeleteSucc(short, int) (detfunc.cxx:1207) by 0x3CF812BA: ScDocFunc::DetectiveRefresh(bool) (docfunc.cxx:480) by 0x3CFC9FAF: ScDocShell::DoHardRecalc(bool) (docsh4.cxx:1250) Address 0x50992d50 is 112 bytes inside a block of size 464 free'd at 0x4C2D22A: operator delete(void*) (vg_replace_malloc.c:576) by 0xB1D04BC: SdrPathObj::~SdrPathObj() (svdopath.cxx:1681) by 0xB1701F5: SdrObject::Free(SdrObject*&) (svdobj.cxx:394) by 0xB258666: SdrUndoObjList::~SdrUndoObjList() (svdundo.cxx:720) by 0xB258A39: SdrUndoRemoveObj::~SdrUndoRemoveObj() (svdundo.cxx:774) by 0xB25E29B: SdrUndoDelObj::~SdrUndoDelObj() (svdundo.hxx:298) by 0xB25E2B7: SdrUndoDelObj::~SdrUndoDelObj() (svdundo.hxx:298) by 0x3C7E5D0E: ScDrawLayer::AddCalcUndo(SdrUndoAction*) (drwlayer.cxx:1120) by 0x3C9E0238: ScDetectiveFunc::DeleteArrowsAt(short, int, bool) (detfunc.cxx:692) Change-Id: Idc6d1f8e9ad8b203dac55630f8c100e74d3e017b
1697 lines
58 KiB
C++
1697 lines
58 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* 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 .
|
|
*/
|
|
|
|
#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 "formulacell.hxx"
|
|
#include "docpool.hxx"
|
|
#include "patattr.hxx"
|
|
#include "attrib.hxx"
|
|
#include "scmod.hxx"
|
|
#include "postit.hxx"
|
|
#include "rangelst.hxx"
|
|
#include "reftokenhelper.hxx"
|
|
#include "formulaiter.hxx"
|
|
#include "cellvalue.hxx"
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
using ::std::vector;
|
|
using namespace com::sun::star;
|
|
|
|
// 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_OUSTRING
|
|
|
|
enum DetInsertResult { // return-values for inserting in one 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; //TODO: individually ?
|
|
sal_uInt16 nMaxLevel;
|
|
|
|
public:
|
|
explicit 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;
|
|
bool ScDetectiveFunc::bColorsInitialized = false;
|
|
|
|
static bool lcl_HasThickLine( SdrObject& rObj )
|
|
{
|
|
// thin lines get width 0 -> everything greater 0 is a thick line
|
|
|
|
return static_cast<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_OUSTRING, Color( ScDetectiveFunc::GetArrowColor() ) ) );
|
|
aBoxSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
|
|
|
|
// create default line endings (like XLineEndList::Create)
|
|
// to be independent from the configured line endings
|
|
|
|
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);
|
|
|
|
OUString aName = SC_LINEEND_NAME;
|
|
|
|
aArrowSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
|
|
aArrowSet.Put( XLineStartWidthItem( 200 ) );
|
|
aArrowSet.Put( XLineStartCenterItem( true ) );
|
|
aArrowSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
|
|
aArrowSet.Put( XLineEndWidthItem( 200 ) );
|
|
aArrowSet.Put( XLineEndCenterItem( false ) );
|
|
|
|
aToTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aCircle) ) );
|
|
aToTabSet.Put( XLineStartWidthItem( 200 ) );
|
|
aToTabSet.Put( XLineStartCenterItem( true ) );
|
|
aToTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
|
|
aToTabSet.Put( XLineEndWidthItem( 300 ) );
|
|
aToTabSet.Put( XLineEndCenterItem( false ) );
|
|
|
|
aFromTabSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aSquare) ) );
|
|
aFromTabSet.Put( XLineStartWidthItem( 300 ) );
|
|
aFromTabSet.Put( XLineStartCenterItem( true ) );
|
|
aFromTabSet.Put( XLineEndItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
|
|
aFromTabSet.Put( XLineEndWidthItem( 200 ) );
|
|
aFromTabSet.Put( XLineEndCenterItem( false ) );
|
|
|
|
aCircleSet.Put( XLineColorItem( OUString(), Color( ScDetectiveFunc::GetErrorColor() ) ) );
|
|
aCircleSet.Put( XFillStyleItem( drawing::FillStyle_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);
|
|
|
|
OUString aName = SC_LINEEND_NAME;
|
|
|
|
aCaptionSet.Put( XLineStartItem( aName, basegfx::B2DPolyPolygon(aTriangle) ) );
|
|
aCaptionSet.Put( XLineStartWidthItem( 200 ) );
|
|
aCaptionSet.Put( XLineStartCenterItem( false ) );
|
|
aCaptionSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
|
|
Color aYellow( ScDetectiveFunc::GetCommentColor() );
|
|
aCaptionSet.Put( XFillColorItem( OUString(), 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( makeSdrShadowItem( false ) );
|
|
aCaptionSet.Put( makeSdrShadowXDistItem( 100 ) );
|
|
aCaptionSet.Put( makeSdrShadowYDistItem( 100 ) );
|
|
|
|
// text attributes
|
|
aCaptionSet.Put( makeSdrTextLeftDistItem( 100 ) );
|
|
aCaptionSet.Put( makeSdrTextRightDistItem( 100 ) );
|
|
aCaptionSet.Put( makeSdrTextUpperDistItem( 100 ) );
|
|
aCaptionSet.Put( makeSdrTextLowerDistItem( 100 ) );
|
|
|
|
aCaptionSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
|
|
aCaptionSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
|
|
|
|
// do use the default cell style, so the user has a chance to
|
|
// modify the font for the annotations
|
|
static_cast<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 = nullptr;
|
|
|
|
for( sal_uInt16 nWhich = aWhichIter.FirstWhich(); nWhich > 0; nWhich = aWhichIter.NextWhich() )
|
|
{
|
|
if(rItemSet.GetItemState(nWhich, false, &pPoolItem) == SfxItemState::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, false);
|
|
}
|
|
|
|
inline 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;
|
|
}
|
|
|
|
bool ScDetectiveFunc::HasError( const ScRange& rRange, ScAddress& rErrPos )
|
|
{
|
|
rErrPos = rRange.aStart;
|
|
sal_uInt16 nError = 0;
|
|
|
|
ScCellIterator aIter( pDoc, rRange);
|
|
for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
|
|
{
|
|
if (aIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
nError = aIter.getFormulaCell()->GetErrCode();
|
|
if (nError)
|
|
rErrPos = aIter.GetPos();
|
|
}
|
|
|
|
return (nError != 0);
|
|
}
|
|
|
|
Point ScDetectiveFunc::GetDrawPos( SCCOL nCol, SCROW nRow, DrawPosMode eMode ) const
|
|
{
|
|
OSL_ENSURE( ValidColRow( nCol, nRow ), "ScDetectiveFunc::GetDrawPos - invalid cell address" );
|
|
nCol = SanitizeCol( nCol );
|
|
nRow = 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 );
|
|
}
|
|
|
|
static bool lcl_IsOtherTab( const basegfx::B2DPolyPolygon& rPolyPolygon )
|
|
{
|
|
// test if rPolygon is the line end for "other table" (rectangle)
|
|
if(1 == rPolyPolygon.count())
|
|
{
|
|
const basegfx::B2DPolygon aSubPoly(rPolyPolygon.getB2DPolygon(0));
|
|
|
|
// #i73305# circle consists of 4 segments, too, distinguishable from square by
|
|
// the use of control points
|
|
if(4 == aSubPoly.count() && aSubPoly.isClosed() && !aSubPoly.areControlPointsUsed())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ScDetectiveFunc::HasArrow( const ScAddress& rStart,
|
|
SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab )
|
|
{
|
|
bool bStartAlien = ( rStart.Tab() != nTab );
|
|
bool bEndAlien = ( nEndTab != nTab );
|
|
|
|
if (bStartAlien && bEndAlien)
|
|
{
|
|
OSL_FAIL("bStartAlien && bEndAlien");
|
|
return 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));
|
|
OSL_ENSURE(pPage,"Page ?");
|
|
|
|
bool bFound = 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();
|
|
|
|
bool bObjStartAlien =
|
|
lcl_IsOtherTab( static_cast<const XLineStartItem&>(rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
|
|
bool bObjEndAlien =
|
|
lcl_IsOtherTab( static_cast<const XLineEndItem&>(rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
|
|
|
|
bool bStartHit = bStartAlien ? bObjStartAlien :
|
|
( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) );
|
|
bool bEndHit = bEndAlien ? bObjEndAlien :
|
|
( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) );
|
|
|
|
if ( bStartHit && bEndHit )
|
|
bFound = true;
|
|
}
|
|
pObject = aIter.Next();
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
bool ScDetectiveFunc::IsNonAlienArrow( SdrObject* pObject )
|
|
{
|
|
if ( pObject->GetLayer()==SC_LAYER_INTERN &&
|
|
pObject->IsPolyObj() && pObject->GetPointCount()==2 )
|
|
{
|
|
const SfxItemSet& rSet = pObject->GetMergedItemSet();
|
|
|
|
bool bObjStartAlien =
|
|
lcl_IsOtherTab( static_cast<const XLineStartItem&>(rSet.Get(XATTR_LINESTART)).GetLineStartValue() );
|
|
bool bObjEndAlien =
|
|
lcl_IsOtherTab( static_cast<const XLineEndItem&>(rSet.Get(XATTR_LINEEND)).GetLineEndValue() );
|
|
|
|
return !bObjStartAlien && !bObjEndAlien;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject
|
|
|
|
bool ScDetectiveFunc::InsertArrow( SCCOL nCol, SCROW nRow,
|
|
SCCOL nRefStartCol, SCROW nRefStartRow,
|
|
SCCOL nRefEndCol, SCROW nRefEndRow,
|
|
bool bFromOtherTab, bool bRed,
|
|
ScDetectiveData& rData )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
|
|
|
|
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());
|
|
|
|
pBox->SetLayer( SC_LAYER_INTERN );
|
|
pPage->InsertObject( pBox );
|
|
pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
|
|
|
|
ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, 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)
|
|
{
|
|
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 ) ); // range
|
|
else
|
|
rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
|
|
|
|
ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
|
|
rAttrSet.Put( XLineColorItem( OUString(), 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)); //TODO: needed ???
|
|
pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
|
|
|
|
pArrow->SetLayer( SC_LAYER_INTERN );
|
|
pPage->InsertObject( pArrow );
|
|
pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
|
|
|
|
ScDrawObjData* pData = ScDrawLayer::GetObjData(pArrow, true);
|
|
if (bFromOtherTab)
|
|
pData->maStart.SetInvalid();
|
|
else
|
|
pData->maStart.Set( nRefStartCol, nRefStartRow, nTab);
|
|
|
|
pData->maEnd.Set( nCol, nRow, nTab);
|
|
pData->meType = ScDrawObjData::DetectiveArrow;
|
|
|
|
Modified();
|
|
return true;
|
|
}
|
|
|
|
bool ScDetectiveFunc::InsertToOtherTab( SCCOL nStartCol, SCROW nStartRow,
|
|
SCCOL nEndCol, SCROW nEndRow, bool bRed,
|
|
ScDetectiveData& rData )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
|
|
|
|
bool bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
|
|
if (bArea)
|
|
{
|
|
Rectangle aRect = GetDrawRect( nStartCol, nStartRow, nEndCol, nEndRow );
|
|
SdrRectObj* pBox = new SdrRectObj( aRect );
|
|
|
|
pBox->SetMergedItemSetAndBroadcast(rData.GetBoxSet());
|
|
|
|
pBox->SetLayer( SC_LAYER_INTERN );
|
|
pPage->InsertObject( pBox );
|
|
pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
|
|
|
|
ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, true );
|
|
pData->maStart.Set( nStartCol, nStartRow, nTab);
|
|
pData->maEnd.Set( nEndCol, nEndRow, nTab);
|
|
}
|
|
|
|
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 ) ); // range
|
|
else
|
|
rAttrSet.Put( XLineWidthItem( 0 ) ); // single reference
|
|
|
|
ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
|
|
rAttrSet.Put( XLineColorItem( OUString(), 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)); //TODO: needed ???
|
|
|
|
pArrow->SetMergedItemSetAndBroadcast(rAttrSet);
|
|
|
|
pArrow->SetLayer( SC_LAYER_INTERN );
|
|
pPage->InsertObject( pArrow );
|
|
pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
|
|
|
|
ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, true );
|
|
pData->maStart.Set( nStartCol, nStartRow, nTab);
|
|
pData->maEnd.SetInvalid();
|
|
|
|
Modified();
|
|
return true;
|
|
}
|
|
|
|
// DrawEntry: formula from this spreadsheet,
|
|
// reference on this or other
|
|
// DrawAlienEntry: formula from other spreadsheet,
|
|
// reference on this
|
|
|
|
// return FALSE: there was already an arrow
|
|
|
|
bool ScDetectiveFunc::DrawEntry( SCCOL nCol, SCROW nRow,
|
|
const ScRange& rRef,
|
|
ScDetectiveData& rData )
|
|
{
|
|
if ( HasArrow( rRef.aStart, nCol, nRow, nTab ) )
|
|
return false;
|
|
|
|
ScAddress aErrorPos;
|
|
bool bError = HasError( rRef, aErrorPos );
|
|
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 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::DrawAlienEntry( const ScRange& rRef,
|
|
ScDetectiveData& rData )
|
|
{
|
|
if ( HasArrow( rRef.aStart, 0, 0, nTab+1 ) )
|
|
return false;
|
|
|
|
ScAddress aErrorPos;
|
|
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);
|
|
|
|
pCircle->SetLayer( SC_LAYER_INTERN );
|
|
pPage->InsertObject( pCircle );
|
|
pModel->AddCalcUndo( new SdrUndoInsertObj( *pCircle ) );
|
|
|
|
ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, true );
|
|
pData->maStart.Set( nCol, nRow, nTab);
|
|
pData->maEnd.SetInvalid();
|
|
pData->meType = ScDrawObjData::ValidationCircle;
|
|
|
|
Modified();
|
|
}
|
|
|
|
void ScDetectiveFunc::DeleteArrowsAt( SCCOL nCol, SCROW nRow, bool bDestPnt )
|
|
{
|
|
Rectangle aRect = GetDrawRect( nCol, nRow );
|
|
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
|
|
OSL_ENSURE(pPage,"Page ?");
|
|
|
|
pPage->RecalcObjOrdNums();
|
|
|
|
const size_t nObjCount = pPage->GetObjCount();
|
|
if (nObjCount)
|
|
{
|
|
size_t nDelCount = 0;
|
|
std::unique_ptr<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 ? 1 : 0))) // start/destinationpoint
|
|
ppObj[nDelCount++] = pObject;
|
|
}
|
|
|
|
pObject = aIter.Next();
|
|
}
|
|
|
|
const bool bRecording = pModel->IsRecording();
|
|
|
|
if (bRecording)
|
|
{
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
pModel->AddCalcUndo(new SdrUndoDelObj(*ppObj[nDelCount-i]));
|
|
}
|
|
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
{
|
|
// remove the object from the drawing page, delete if undo is disabled
|
|
SdrObject* pObj = pPage->RemoveObject(ppObj[nDelCount-i]->GetOrdNum());
|
|
if( !bRecording )
|
|
SdrObject::Free( pObj );
|
|
}
|
|
|
|
ppObj.reset();
|
|
|
|
Modified();
|
|
}
|
|
}
|
|
|
|
// delete box around reference
|
|
|
|
#define SC_DET_TOLERANCE 50
|
|
|
|
inline 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 )
|
|
{
|
|
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));
|
|
OSL_ENSURE(pPage,"Page ?");
|
|
|
|
pPage->RecalcObjOrdNums();
|
|
|
|
const size_t nObjCount = pPage->GetObjCount();
|
|
if (nObjCount)
|
|
{
|
|
size_t nDelCount = 0;
|
|
std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
|
|
|
|
SdrObjListIter aIter( *pPage, IM_FLAT );
|
|
SdrObject* pObject = aIter.Next();
|
|
while (pObject)
|
|
{
|
|
if ( pObject->GetLayer() == SC_LAYER_INTERN &&
|
|
dynamic_cast< const SdrRectObj* >(pObject) != nullptr )
|
|
{
|
|
aObjRect = static_cast<SdrRectObj*>(pObject)->GetLogicRect();
|
|
aObjRect.Justify();
|
|
if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
|
|
ppObj[nDelCount++] = pObject;
|
|
}
|
|
|
|
pObject = aIter.Next();
|
|
}
|
|
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
|
|
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
|
|
|
|
ppObj.reset();
|
|
|
|
Modified();
|
|
}
|
|
}
|
|
|
|
sal_uInt16 ScDetectiveFunc::InsertPredLevelArea( const ScRange& rRef,
|
|
ScDetectiveData& rData, sal_uInt16 nLevel )
|
|
{
|
|
sal_uInt16 nResult = DET_INS_EMPTY;
|
|
|
|
ScCellIterator aIter( pDoc, rRef);
|
|
for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
|
|
{
|
|
if (aIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
const ScAddress& rPos = aIter.GetPos();
|
|
switch (InsertPredLevel(rPos.Col(), rPos.Row(), 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;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
sal_uInt16 ScDetectiveFunc::InsertPredLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
|
|
sal_uInt16 nLevel )
|
|
{
|
|
ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, nTab));
|
|
if (aCell.meType != CELLTYPE_FORMULA)
|
|
return DET_INS_EMPTY;
|
|
|
|
ScFormulaCell* pFCell = aCell.mpFormula;
|
|
if (pFCell->IsRunning())
|
|
return DET_INS_CIRCULAR;
|
|
|
|
if (pFCell->GetDirty())
|
|
pFCell->Interpret(); // can't be called after SetRunning
|
|
pFCell->SetRunning(true);
|
|
|
|
sal_uInt16 nResult = DET_INS_EMPTY;
|
|
|
|
ScDetectiveRefIter aIter(pFCell);
|
|
ScRange aRef;
|
|
while ( aIter.GetNextRef( aRef ) )
|
|
{
|
|
if (DrawEntry( nCol, nRow, aRef, rData ))
|
|
{
|
|
nResult = DET_INS_INSERTED; // insert new arrow
|
|
}
|
|
else
|
|
{
|
|
// continue
|
|
|
|
if ( nLevel < rData.GetMaxLevel() )
|
|
{
|
|
sal_uInt16 nSubResult;
|
|
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: no change
|
|
}
|
|
}
|
|
else // nMaxLevel reached
|
|
if (nResult != DET_INS_INSERTED)
|
|
nResult = DET_INS_CONTINUE;
|
|
}
|
|
}
|
|
|
|
pFCell->SetRunning(false);
|
|
|
|
return nResult;
|
|
}
|
|
|
|
sal_uInt16 ScDetectiveFunc::FindPredLevelArea( const ScRange& rRef,
|
|
sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
|
|
{
|
|
sal_uInt16 nResult = nLevel;
|
|
|
|
ScCellIterator aCellIter( pDoc, rRef);
|
|
for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
|
|
{
|
|
if (aCellIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
sal_uInt16 nTemp = FindPredLevel(aCellIter.GetPos().Col(), aCellIter.GetPos().Row(), nLevel, nDeleteLevel);
|
|
if (nTemp > nResult)
|
|
nResult = nTemp;
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
// nDeleteLevel != 0 -> delete
|
|
|
|
sal_uInt16 ScDetectiveFunc::FindPredLevel( SCCOL nCol, SCROW nRow, sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
|
|
{
|
|
OSL_ENSURE( nLevel<1000, "Level" );
|
|
|
|
ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, nTab));
|
|
if (aCell.meType != CELLTYPE_FORMULA)
|
|
return nLevel;
|
|
|
|
ScFormulaCell* pFCell = aCell.mpFormula;
|
|
if (pFCell->IsRunning())
|
|
return nLevel;
|
|
|
|
if (pFCell->GetDirty())
|
|
pFCell->Interpret(); // can't be called after SetRunning
|
|
pFCell->SetRunning(true);
|
|
|
|
sal_uInt16 nResult = nLevel;
|
|
bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
|
|
|
|
if ( bDelete )
|
|
{
|
|
DeleteArrowsAt( nCol, nRow, true ); // arrows, that are pointing here
|
|
}
|
|
|
|
ScDetectiveRefIter aIter(pFCell);
|
|
ScRange aRef;
|
|
while ( aIter.GetNextRef( aRef) )
|
|
{
|
|
bool bArea = ( aRef.aStart != aRef.aEnd );
|
|
|
|
if ( bDelete ) // delete frame ?
|
|
{
|
|
if (bArea)
|
|
{
|
|
DeleteBox( aRef.aStart.Col(), aRef.aStart.Row(), aRef.aEnd.Col(), aRef.aEnd.Row() );
|
|
}
|
|
}
|
|
else // continue searching
|
|
{
|
|
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(false);
|
|
|
|
return nResult;
|
|
}
|
|
|
|
sal_uInt16 ScDetectiveFunc::InsertErrorLevel( SCCOL nCol, SCROW nRow, ScDetectiveData& rData,
|
|
sal_uInt16 nLevel )
|
|
{
|
|
ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, nTab));
|
|
if (aCell.meType != CELLTYPE_FORMULA)
|
|
return DET_INS_EMPTY;
|
|
|
|
ScFormulaCell* pFCell = aCell.mpFormula;
|
|
if (pFCell->IsRunning())
|
|
return DET_INS_CIRCULAR;
|
|
|
|
if (pFCell->GetDirty())
|
|
pFCell->Interpret(); // can't be called after SetRunning
|
|
pFCell->SetRunning(true);
|
|
|
|
sal_uInt16 nResult = DET_INS_EMPTY;
|
|
|
|
ScDetectiveRefIter aIter(pFCell);
|
|
ScRange aRef;
|
|
ScAddress aErrorPos;
|
|
bool bHasError = false;
|
|
while ( aIter.GetNextRef( aRef ) )
|
|
{
|
|
if (HasError( aRef, aErrorPos ))
|
|
{
|
|
bHasError = true;
|
|
if (DrawEntry( nCol, nRow, ScRange( aErrorPos), rData ))
|
|
nResult = DET_INS_INSERTED;
|
|
|
|
if ( nLevel < rData.GetMaxLevel() ) // hits most of the time
|
|
{
|
|
if (InsertErrorLevel( aErrorPos.Col(), aErrorPos.Row(),
|
|
rData, nLevel+1 ) == DET_INS_INSERTED)
|
|
nResult = DET_INS_INSERTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
pFCell->SetRunning(false);
|
|
|
|
// leaves ?
|
|
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 )
|
|
{
|
|
// over the entire document.
|
|
|
|
sal_uInt16 nResult = DET_INS_EMPTY;
|
|
ScCellIterator aCellIter(pDoc, ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB)); // all sheets
|
|
for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
|
|
{
|
|
if (aCellIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = aCellIter.getFormulaCell();
|
|
bool bRunning = pFCell->IsRunning();
|
|
|
|
if (pFCell->GetDirty())
|
|
pFCell->Interpret(); // can't be called after SetRunning
|
|
pFCell->SetRunning(true);
|
|
|
|
ScDetectiveRefIter aIter(pFCell);
|
|
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() ))
|
|
{
|
|
bool bAlien = ( aCellIter.GetPos().Tab() != nTab );
|
|
bool bDrawRet;
|
|
if (bAlien)
|
|
bDrawRet = DrawAlienEntry( aRef, rData );
|
|
else
|
|
bDrawRet = DrawEntry( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
|
|
aRef, rData );
|
|
if (bDrawRet)
|
|
{
|
|
nResult = DET_INS_INSERTED; // insert new arrow
|
|
}
|
|
else
|
|
{
|
|
if (bRunning)
|
|
{
|
|
if (nResult == DET_INS_EMPTY)
|
|
nResult = DET_INS_CIRCULAR;
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( nLevel < rData.GetMaxLevel() )
|
|
{
|
|
sal_uInt16 nSubResult = InsertSuccLevel(
|
|
aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
|
|
aCellIter.GetPos().Col(), aCellIter.GetPos().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: leave unchanged
|
|
}
|
|
}
|
|
else // nMaxLevel reached
|
|
if (nResult != DET_INS_INSERTED)
|
|
nResult = DET_INS_CONTINUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pFCell->SetRunning(bRunning);
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
sal_uInt16 ScDetectiveFunc::FindSuccLevel( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
|
|
sal_uInt16 nLevel, sal_uInt16 nDeleteLevel )
|
|
{
|
|
OSL_ENSURE( nLevel<1000, "Level" );
|
|
|
|
sal_uInt16 nResult = nLevel;
|
|
bool bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
|
|
|
|
ScCellIterator aCellIter( pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab) );
|
|
for (bool bHas = aCellIter.first(); bHas; bHas = aCellIter.next())
|
|
{
|
|
if (aCellIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = aCellIter.getFormulaCell();
|
|
bool bRunning = pFCell->IsRunning();
|
|
|
|
if (pFCell->GetDirty())
|
|
pFCell->Interpret(); // can't be called after SetRunning
|
|
pFCell->SetRunning(true);
|
|
|
|
ScDetectiveRefIter aIter(pFCell);
|
|
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 ) // arrows, that are starting here
|
|
{
|
|
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(), false );
|
|
}
|
|
else if ( !bRunning &&
|
|
HasArrow( aRef.aStart,
|
|
aCellIter.GetPos().Col(),aCellIter.GetPos().Row(),aCellIter.GetPos().Tab() ) )
|
|
{
|
|
sal_uInt16 nTemp = FindSuccLevel( aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
|
|
aCellIter.GetPos().Col(), aCellIter.GetPos().Row(),
|
|
nLevel+1, nDeleteLevel );
|
|
if (nTemp > nResult)
|
|
nResult = nTemp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pFCell->SetRunning(bRunning);
|
|
}
|
|
|
|
return nResult;
|
|
}
|
|
|
|
bool ScDetectiveFunc::ShowPred( SCCOL nCol, SCROW nRow )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return 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 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::ShowSucc( SCCOL nCol, SCROW nRow )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return 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 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::ShowError( SCCOL nCol, SCROW nRow )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return false;
|
|
|
|
ScRange aRange( nCol, nRow, nTab );
|
|
ScAddress aErrPos;
|
|
if ( !HasError( aRange,aErrPos ) )
|
|
return false;
|
|
|
|
ScDetectiveData aData( pModel );
|
|
|
|
aData.SetMaxLevel( 1000 );
|
|
sal_uInt16 nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
|
|
|
|
return ( nResult == DET_INS_INSERTED );
|
|
}
|
|
|
|
bool ScDetectiveFunc::DeleteSucc( SCCOL nCol, SCROW nRow )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return false;
|
|
|
|
sal_uInt16 nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
|
|
if ( nLevelCount )
|
|
FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount ); // delete
|
|
|
|
return ( nLevelCount != 0 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::DeletePred( SCCOL nCol, SCROW nRow )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return false;
|
|
|
|
sal_uInt16 nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
|
|
if ( nLevelCount )
|
|
FindPredLevel( nCol, nRow, 0, nLevelCount ); // delete
|
|
|
|
return ( nLevelCount != 0 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
|
|
{
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return false;
|
|
|
|
SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
|
|
OSL_ENSURE(pPage,"Page ?");
|
|
|
|
pPage->RecalcObjOrdNums();
|
|
|
|
size_t nDelCount = 0;
|
|
const size_t nObjCount = pPage->GetObjCount();
|
|
if (nObjCount)
|
|
{
|
|
std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]);
|
|
|
|
SdrObjListIter aIter( *pPage, IM_FLAT );
|
|
SdrObject* pObject = aIter.Next();
|
|
while (pObject)
|
|
{
|
|
if ( pObject->GetLayer() == SC_LAYER_INTERN )
|
|
{
|
|
bool bDoThis = true;
|
|
if ( eWhat != SC_DET_ALL )
|
|
{
|
|
bool bCircle = ( dynamic_cast<const SdrCircObj*>( pObject) != nullptr );
|
|
bool bCaption = ScDrawLayer::IsNoteCaption( pObject );
|
|
if ( eWhat == SC_DET_DETECTIVE ) // detektive, from menue
|
|
bDoThis = !bCaption; // also circles
|
|
else if ( eWhat == SC_DET_CIRCLES ) // circles, if new created
|
|
bDoThis = bCircle;
|
|
else if ( eWhat == SC_DET_ARROWS ) // DetectiveRefresh
|
|
bDoThis = !bCaption && !bCircle; // don't include circles
|
|
else
|
|
{
|
|
OSL_FAIL("what?");
|
|
}
|
|
}
|
|
if ( bDoThis )
|
|
ppObj[nDelCount++] = pObject;
|
|
}
|
|
|
|
pObject = aIter.Next();
|
|
}
|
|
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
|
|
|
|
for (size_t i=1; i<=nDelCount; ++i)
|
|
pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
|
|
|
|
ppObj.reset();
|
|
|
|
Modified();
|
|
}
|
|
|
|
return ( nDelCount != 0 );
|
|
}
|
|
|
|
bool ScDetectiveFunc::MarkInvalid(bool& rOverflow)
|
|
{
|
|
rOverflow = false;
|
|
ScDrawLayer* pModel = pDoc->GetDrawLayer();
|
|
if (!pModel)
|
|
return false;
|
|
|
|
bool bDeleted = DeleteAll( SC_DET_CIRCLES ); // just circles
|
|
|
|
ScDetectiveData aData( pModel );
|
|
long nInsCount = 0;
|
|
|
|
// search for valid places
|
|
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 = static_cast<const SfxUInt32Item&>(pPattern->GetItem(ATTR_VALIDDATA)).GetValue();
|
|
if (nIndex)
|
|
{
|
|
const ScValidationData* pData = pDoc->GetValidationEntry( nIndex );
|
|
if ( pData )
|
|
{
|
|
// pass cells in this area
|
|
|
|
bool bMarkEmpty = !pData->IsIgnoreBlank();
|
|
SCROW nNextRow = nRow1;
|
|
SCROW nRow;
|
|
ScCellIterator aCellIter( pDoc, ScRange(nCol, nRow1, nTab, nCol, nRow2, nTab) );
|
|
for (bool bHas = aCellIter.first(); bHas && nInsCount < SC_DET_MAXCIRCLE; bHas = aCellIter.next())
|
|
{
|
|
SCROW nCellRow = aCellIter.GetPos().Row();
|
|
if ( bMarkEmpty )
|
|
for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
|
|
{
|
|
DrawCircle( nCol, nRow, aData );
|
|
++nInsCount;
|
|
}
|
|
ScRefCellValue aCell = aCellIter.getRefCellValue();
|
|
if (!pData->IsDataValid(aCell, aCellIter.GetPos()))
|
|
{
|
|
DrawCircle( nCol, nCellRow, aData );
|
|
++nInsCount;
|
|
}
|
|
nNextRow = nCellRow + 1;
|
|
}
|
|
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 = true;
|
|
|
|
return ( bDeleted || nInsCount != 0 );
|
|
}
|
|
|
|
void ScDetectiveFunc::GetAllPreds(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
|
|
vector<ScTokenRef>& rRefTokens)
|
|
{
|
|
ScCellIterator aIter(pDoc, ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab));
|
|
for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
|
|
{
|
|
if (aIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = aIter.getFormulaCell();
|
|
ScDetectiveRefIter aRefIter(pFCell);
|
|
for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
|
|
{
|
|
ScTokenRef pRef(p->Clone());
|
|
ScRefTokenHelper::join(rRefTokens, pRef, aIter.GetPos());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScDetectiveFunc::GetAllSuccs(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
|
|
vector<ScTokenRef>& rRefTokens)
|
|
{
|
|
vector<ScTokenRef> aSrcRange;
|
|
aSrcRange.push_back(
|
|
ScRefTokenHelper::createRefToken(ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)));
|
|
|
|
ScCellIterator aIter(pDoc, ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab));
|
|
for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
|
|
{
|
|
if (aIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = aIter.getFormulaCell();
|
|
ScDetectiveRefIter aRefIter(pFCell);
|
|
for (formula::FormulaToken* p = aRefIter.GetNextRefToken(); p; p = aRefIter.GetNextRefToken())
|
|
{
|
|
const ScAddress& aPos = aIter.GetPos();
|
|
ScTokenRef pRef(p->Clone());
|
|
if (ScRefTokenHelper::intersects(aSrcRange, pRef, aPos))
|
|
{
|
|
// This address is absolute.
|
|
pRef = ScRefTokenHelper::createRefToken(aPos);
|
|
ScRefTokenHelper::join(rRefTokens, pRef, ScAddress());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 )
|
|
{
|
|
SdrPage* pPage = pModel->GetPage( static_cast< sal_uInt16 >( nObjTab ) );
|
|
OSL_ENSURE( 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...
|
|
OSL_ENSURE( pNote && (pNote->GetCaption() == pObject), "ScDetectiveFunc::UpdateAllComments - invalid cell note" );
|
|
if( pNote )
|
|
{
|
|
ScCommentData aData( rDoc, pModel );
|
|
SfxItemSet aAttrColorSet = pObject->GetMergedItemSet();
|
|
aAttrColorSet.Put( XFillColorItem( OUString(), 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 ) );
|
|
OSL_ENSURE( pPage, "Page ?" );
|
|
if( pPage )
|
|
{
|
|
SdrObjListIter aIter( *pPage, IM_FLAT );
|
|
for( SdrObject* pObject = aIter.Next(); pObject; pObject = aIter.Next() )
|
|
{
|
|
if ( pObject->GetLayer() == SC_LAYER_INTERN )
|
|
{
|
|
bool bArrow = false;
|
|
bool bError = false;
|
|
|
|
ScAddress aPos;
|
|
ScRange aSource;
|
|
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 = true;
|
|
else
|
|
bArrow = 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 = true;
|
|
else
|
|
bArrow = true;
|
|
}
|
|
else if ( eType == SC_DETOBJ_CIRCLE )
|
|
{
|
|
// circles (error marks) are always red
|
|
|
|
bError = true;
|
|
}
|
|
else if ( eType == SC_DETOBJ_NONE )
|
|
{
|
|
// frame for area reference has no ObjType, always gets arrow color
|
|
|
|
if ( dynamic_cast<const SdrRectObj*>( pObject) != nullptr && dynamic_cast<const SdrCaptionObj*>( pObject) == nullptr )
|
|
{
|
|
bArrow = true;
|
|
}
|
|
}
|
|
|
|
if ( bArrow || bError )
|
|
{
|
|
ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() );
|
|
pObject->SetMergedItem( XLineColorItem( OUString(), Color( nColorData ) ) );
|
|
|
|
// repaint only
|
|
pObject->ActionChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void 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;
|
|
|
|
SdrPage* pPage = pModel->GetPage(static_cast<sal_uInt16>(nTab));
|
|
OSL_ENSURE(pPage,"Page ?");
|
|
if (!pPage) return;
|
|
|
|
// test if the object is a direct page member
|
|
if( pObject && pObject->GetPage() && (pObject->GetPage() == pObject->GetObjList()) )
|
|
{
|
|
// Is there a previous object?
|
|
const size_t nOrdNum = pObject->GetOrdNum();
|
|
|
|
if(nOrdNum > 0)
|
|
{
|
|
SdrObject* pPrevObj = pPage->GetObj(nOrdNum - 1);
|
|
|
|
if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && dynamic_cast<const SdrRectObj*>( pPrevObj) != nullptr )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject, SCTAB nObjTab,
|
|
ScAddress& rPosition, ScRange& rSource, bool& rRedLine )
|
|
{
|
|
rRedLine = 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 = static_cast<const XLineColorItem&>(pObject->GetMergedItem(XATTR_LINECOLOR)).GetColorValue().GetColor();
|
|
if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
|
|
rRedLine = true;
|
|
}
|
|
else if ( dynamic_cast<const SdrCircObj*>( pObject) != nullptr )
|
|
{
|
|
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,
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
ColorData ScDetectiveFunc::GetArrowColor()
|
|
{
|
|
if (!bColorsInitialized)
|
|
InitializeColors();
|
|
return nArrowColor;
|
|
}
|
|
|
|
ColorData ScDetectiveFunc::GetErrorColor()
|
|
{
|
|
if (!bColorsInitialized)
|
|
InitializeColors();
|
|
return nErrorColor;
|
|
}
|
|
|
|
ColorData ScDetectiveFunc::GetCommentColor()
|
|
{
|
|
if (!bColorsInitialized)
|
|
InitializeColors();
|
|
return nCommentColor;
|
|
}
|
|
|
|
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 = true;
|
|
}
|
|
|
|
bool ScDetectiveFunc::IsColorsInitialized()
|
|
{
|
|
return bColorsInitialized;
|
|
}
|
|
|
|
void ScDetectiveFunc::AppendChangTrackNoteSeparator(OUString &rDisplay)
|
|
{
|
|
rDisplay += "\n--------\n";
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|