/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifdef _MSC_VER
#pragma hdrstop
#endif

// INCLUDE ---------------------------------------------------------------

#include "scitems.hxx"
#include <bf_svtools/colorcfg.hxx>
#include <bf_svx/outlobj.hxx>

#include <bf_svx/xdef.hxx>

#include <bf_svx/svditer.hxx>
#include <bf_svx/svdocapt.hxx>
#include <bf_svx/svdocirc.hxx>
#include <bf_svx/svdopath.hxx>
#include <bf_svx/svdpage.hxx>
#include <bf_svx/svdundo.hxx>
#include <bf_svx/xflclit.hxx>
#include <bf_svx/xlnclit.hxx>
#include <bf_svx/xlnedcit.hxx>
#include <bf_svx/xlnedit.hxx>
#include <bf_svx/xlnedwit.hxx>
#include <bf_svx/xlnstcit.hxx>
#include <bf_svx/xlnstit.hxx>
#include <bf_svx/xlnstwit.hxx>
#include <bf_svx/xlnwtit.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 "scmod.hxx"
namespace binfilter {

//------------------------------------------------------------------------

// #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 };

//  maximale Textlaenge (Zeichen), die noch in "kleines" Objekt passt
#define SC_NOTE_SMALLTEXT   100

//------------------------------------------------------------------------

/*N*/ class ScDetectiveData
/*N*/ {
/*N*/ private:
/*N*/   SfxItemSet  aBoxSet;
/*N*/   SfxItemSet  aArrowSet;
/*N*/   SfxItemSet  aToTabSet;
/*N*/   SfxItemSet  aFromTabSet;
/*N*/   SfxItemSet  aCircleSet;         //! einzeln ?
/*N*/   USHORT      nMaxLevel;
/*N*/
/*N*/ public:
/*N*/               ScDetectiveData( SdrModel* pModel );
/*N*/
/*N*/   SfxItemSet& GetBoxSet()     { return aBoxSet; }
/*N*/   SfxItemSet& GetArrowSet()   { return aArrowSet; }
/*N*/   SfxItemSet& GetToTabSet()   { return aToTabSet; }
/*N*/   SfxItemSet& GetFromTabSet() { return aFromTabSet; }
/*N*/   SfxItemSet& GetCircleSet()  { return aCircleSet; }
/*N*/
/*N*/   void        SetMaxLevel( USHORT nVal )      { nMaxLevel = nVal; }
/*N*/   USHORT      GetMaxLevel() const             { return nMaxLevel; }
/*N*/ };

/*N*/ class ScCommentData
/*N*/ {
/*N*/ private:
/*N*/   SfxItemSet  aCaptionSet;
/*N*/
/*N*/ public:
/*N*/               ScCommentData( ScDocument* pDoc, SdrModel* pModel );
/*N*/
/*N*/   SfxItemSet& GetCaptionSet() { return aCaptionSet; }
/*N*/ };

//------------------------------------------------------------------------

/*N*/ ColorData ScDetectiveFunc::nArrowColor = 0;
/*N*/ ColorData ScDetectiveFunc::nErrorColor = 0;
/*N*/ ColorData ScDetectiveFunc::nCommentColor = 0;
/*N*/ BOOL ScDetectiveFunc::bColorsInitialized = FALSE;

//------------------------------------------------------------------------

/*N*/ BOOL lcl_HasThickLine( SdrObject& rObj ) // detective.sdc
/*N*/ {
/*N*/   // thin lines get width 0 -> everything greater 0 is a thick line
/*N*/
/*N*/   return ( ((const XLineWidthItem&)rObj.GetItem(XATTR_LINEWIDTH)).GetValue() > 0 );
/*N*/ }

//------------------------------------------------------------------------

/*N*/ ScDetectiveData::ScDetectiveData( SdrModel* pModel ) :
/*N*/   aBoxSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
/*N*/   aArrowSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
/*N*/   aToTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
/*N*/   aFromTabSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END ),
/*N*/   aCircleSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END )
/*N*/ {
/*N*/   nMaxLevel = 0;
/*N*/
/*N*/   aBoxSet.Put( XLineColorItem( EMPTY_STRING, Color( ScDetectiveFunc::GetArrowColor() ) ) );
/*N*/   aBoxSet.Put( XFillStyleItem( XFILL_NONE ) );
/*N*/
/*N*/   //  #66479# Standard-Linienenden (wie aus XLineEndList::Create) selber zusammenbasteln,
/*N*/   //  um von den konfigurierten Linienenden unabhaengig zu sein
/*N*/
/*N*/   XPolygon aTriangle(4);
/*N*/   aTriangle[0].X()=10; aTriangle[0].Y()= 0;
/*N*/   aTriangle[1].X()= 0; aTriangle[1].Y()=30;
/*N*/   aTriangle[2].X()=20; aTriangle[2].Y()=30;
/*N*/   aTriangle[3].X()=10; aTriangle[3].Y()= 0;   // #99319# line end polygon must be closed
/*N*/
/*N*/   XPolygon aSquare(5);
/*N*/   aSquare[0].X()= 0; aSquare[0].Y()= 0;
/*N*/   aSquare[1].X()=10; aSquare[1].Y()= 0;
/*N*/   aSquare[2].X()=10; aSquare[2].Y()=10;
/*N*/   aSquare[3].X()= 0; aSquare[3].Y()=10;
/*N*/   aSquare[4].X()= 0; aSquare[4].Y()= 0;       // #99319# line end polygon must be closed
/*N*/
/*N*/   XPolygon aCircle(Point(0,0),100,100);
/*N*/
/*N*/   String aName = SC_LINEEND_NAME;
/*N*/
/*N*/   aArrowSet.Put( XLineStartItem( aName, aCircle ) );
/*N*/   aArrowSet.Put( XLineStartWidthItem( 200 ) );
/*N*/   aArrowSet.Put( XLineStartCenterItem( TRUE ) );
/*N*/   aArrowSet.Put( XLineEndItem( aName, aTriangle ) );
/*N*/   aArrowSet.Put( XLineEndWidthItem( 200 ) );
/*N*/   aArrowSet.Put( XLineEndCenterItem( FALSE ) );
/*N*/
/*N*/   aToTabSet.Put( XLineStartItem( aName, aCircle ) );
/*N*/   aToTabSet.Put( XLineStartWidthItem( 200 ) );
/*N*/   aToTabSet.Put( XLineStartCenterItem( TRUE ) );
/*N*/   aToTabSet.Put( XLineEndItem( aName, aSquare ) );
/*N*/   aToTabSet.Put( XLineEndWidthItem( 300 ) );
/*N*/   aToTabSet.Put( XLineEndCenterItem( FALSE ) );
/*N*/
/*N*/   aFromTabSet.Put( XLineStartItem( aName, aSquare ) );
/*N*/   aFromTabSet.Put( XLineStartWidthItem( 300 ) );
/*N*/   aFromTabSet.Put( XLineStartCenterItem( TRUE ) );
/*N*/   aFromTabSet.Put( XLineEndItem( aName, aTriangle ) );
/*N*/   aFromTabSet.Put( XLineEndWidthItem( 200 ) );
/*N*/   aFromTabSet.Put( XLineEndCenterItem( FALSE ) );
/*N*/
/*N*/   aCircleSet.Put( XLineColorItem( String(), Color( ScDetectiveFunc::GetErrorColor() ) ) );
/*N*/   aCircleSet.Put( XFillStyleItem( XFILL_NONE ) );
/*N*/   USHORT nWidth = 55;     // 54 = 1 Pixel
/*N*/   aCircleSet.Put( XLineWidthItem( nWidth ) );
/*N*/ }

/*N*/ ScCommentData::ScCommentData( ScDocument* pDoc, SdrModel* pModel ) :
/*N*/   aCaptionSet( pModel->GetItemPool(), SDRATTR_START, SDRATTR_END,
/*N*/                                       EE_CHAR_START, EE_CHAR_END, 0 )
/*N*/ {
/*N*/   XPolygon aTriangle(4);
/*N*/   aTriangle[0].X()=10; aTriangle[0].Y()= 0;
/*N*/   aTriangle[1].X()= 0; aTriangle[1].Y()=30;
/*N*/   aTriangle[2].X()=20; aTriangle[2].Y()=30;
/*N*/   aTriangle[3].X()=10; aTriangle[3].Y()= 0;   // #99319# line end polygon must be closed
/*N*/
/*N*/   String aName = SC_LINEEND_NAME;
/*N*/
/*N*/   aCaptionSet.Put( XLineStartItem( aName, aTriangle ) );
/*N*/   aCaptionSet.Put( XLineStartWidthItem( 200 ) );
/*N*/   aCaptionSet.Put( XLineStartCenterItem( FALSE ) );
/*N*/   aCaptionSet.Put( XFillStyleItem( XFILL_SOLID ) );
/*N*/   Color aYellow( ScDetectiveFunc::GetCommentColor() );
/*N*/   aCaptionSet.Put( XFillColorItem( String(), aYellow ) );
/*N*/
/*N*/   //  shadow
/*N*/   //  SdrShadowItem has FALSE, instead the shadow is set for the rectangle
/*N*/   //  only with SetSpecialTextBoxShadow when the object is created
/*N*/   //  (item must be set to adjust objects from older files)
/*N*/   aCaptionSet.Put( SdrShadowItem( FALSE ) );
/*N*/   aCaptionSet.Put( SdrShadowXDistItem( 100 ) );
/*N*/   aCaptionSet.Put( SdrShadowYDistItem( 100 ) );
/*N*/
/*N*/   //  text attributes
/*N*/   aCaptionSet.Put( SdrTextLeftDistItem( 100 ) );
/*N*/   aCaptionSet.Put( SdrTextRightDistItem( 100 ) );
/*N*/   aCaptionSet.Put( SdrTextUpperDistItem( 100 ) );
/*N*/   aCaptionSet.Put( SdrTextLowerDistItem( 100 ) );
/*N*/
/*N*/   //  #78943# do use the default cell style, so the user has a chance to
/*N*/   //  modify the font for the annotations
/*N*/   ((const ScPatternAttr&)pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN)).
/*N*/       FillEditItemSet( &aCaptionSet );
/*N*/ }

//------------------------------------------------------------------------

/*N*/ inline BOOL Intersect( USHORT nStartCol1, USHORT nStartRow1, USHORT nEndCol1, USHORT nEndRow1,
/*N*/                       USHORT nStartCol2, USHORT nStartRow2, USHORT nEndCol2, USHORT nEndRow2 )
/*N*/ {
/*N*/   return nEndCol1 >= nStartCol2 && nEndCol2 >= nStartCol1 &&
/*N*/           nEndRow1 >= nStartRow2 && nEndRow2 >= nStartRow1;
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::HasError( const ScTripel& rStart, const ScTripel& rEnd, ScTripel& rErrPos )
/*N*/ {
/*N*/   rErrPos = rStart;
/*N*/   USHORT nError = 0;
/*N*/
/*N*/   ScCellIterator aCellIter( pDoc, rStart.GetCol(), rStart.GetRow(), rStart.GetTab(),
/*N*/                                   rEnd.GetCol(), rEnd.GetRow(), rEnd.GetTab() );
/*N*/   ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/   while (pCell)
/*N*/   {
/*N*/       if (pCell->GetCellType() == CELLTYPE_FORMULA)
/*N*/       {
/*N*/           nError = ((ScFormulaCell*)pCell)->GetErrCode();
/*N*/           if (nError)
/*N*/               rErrPos.Put( aCellIter.GetCol(), aCellIter.GetRow(), aCellIter.GetTab() );
/*N*/       }
/*N*/       pCell = aCellIter.GetNext();
/*N*/   }
/*N*/
/*N*/   return (nError != 0);
/*N*/ }

/*N*/ Point ScDetectiveFunc::GetDrawPos( USHORT nCol, USHORT nRow, BOOL bArrow )
/*N*/ {
/*N*/   //  MAXCOL/ROW+1 ist erlaubt fuer Ende von Rahmen
/*N*/   if (nCol > MAXCOL+1)
/*N*/   {
/*N*/       OSL_FAIL("falsche Col in ScDetectiveFunc::GetDrawPos");
/*N*/       nCol = MAXCOL+1;
/*N*/   }
/*N*/   if (nRow > MAXROW+1)
/*N*/   {
/*N*/       OSL_FAIL("falsche Row in ScDetectiveFunc::GetDrawPos");
/*N*/       nRow = MAXROW+1;
/*N*/   }
/*N*/
/*N*/   Point aPos;
/*N*/   USHORT i;
/*N*/   USHORT nLocalTab = nTab;        // nicht ueber this
/*N*/
/*N*/   for (i=0; i<nCol; i++)
/*N*/       aPos.X() += pDoc->GetColWidth( i,nLocalTab );
/*N*/   for (i=0; i<nRow; i++)
/*N*/       aPos.Y() += pDoc->FastGetRowHeight( i,nLocalTab );
/*N*/
/*N*/   if (bArrow)
/*N*/   {
/*N*/       if (nCol<=MAXCOL)
/*N*/           aPos.X() += pDoc->GetColWidth( nCol, nLocalTab ) / 4;
/*N*/       if (nCol<=MAXROW)
/*N*/           aPos.Y() += pDoc->GetRowHeight( nRow, nLocalTab ) / 2;
/*N*/   }
/*N*/
/*N*/   aPos.X() = (long) ( aPos.X() * HMM_PER_TWIPS );
/*N*/   aPos.Y() = (long) ( aPos.Y() * HMM_PER_TWIPS );
/*N*/
/*N*/   return aPos;
/*N*/ }

/*N*/ BOOL lcl_IsOtherTab( const XPolygon& rPolygon )
/*N*/ {
/*N*/   //  test if rPolygon is the line end for "other table" (rectangle)
/*N*/
/*N*/   USHORT nCount = rPolygon.GetPointCount();
/*N*/   if ( nCount == 4 )
/*N*/   {
/*N*/       //  4 points -> it is a rectangle (not closed) only if the first and last point are different
/*N*/
/*N*/       return rPolygon[0] != rPolygon[3];
/*N*/   }
/*N*/   else if ( nCount == 5 )
/*N*/   {
/*N*/       //  5 points -> it is a rectangle (closed) only if the first and last point are equal
/*N*/
/*N*/       return rPolygon[0] == rPolygon[4];
/*N*/   }
/*N*/   return FALSE;
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::HasArrow( USHORT nStartCol, USHORT nStartRow, USHORT nStartTab,
/*N*/                                   USHORT nEndCol, USHORT nEndRow, USHORT nEndTab )
/*N*/ {
/*N*/   BOOL bStartAlien = ( nStartTab != nTab );
/*N*/   BOOL bEndAlien   = ( nEndTab != nTab );
/*N*/
/*N*/   if (bStartAlien && bEndAlien)
/*N*/   {
/*N*/       OSL_FAIL("bStartAlien && bEndAlien");
/*N*/       return TRUE;
/*N*/   }
/*N*/
/*N*/   Rectangle aStartRect;
/*N*/   Rectangle aEndRect;
/*N*/   if (!bStartAlien)
/*N*/   {
/*N*/       Point aStartPos = GetDrawPos( nStartCol, nStartRow, FALSE );
/*N*/       Size aStartSize = Size(
/*N*/                           (long) ( pDoc->GetColWidth( nStartCol, nTab) * HMM_PER_TWIPS ),
/*N*/                           (long) ( pDoc->GetRowHeight( nStartRow, nTab) * HMM_PER_TWIPS ) );
/*N*/       aStartRect = Rectangle( aStartPos, aStartSize );
/*N*/   }
/*N*/   if (!bEndAlien)
/*N*/   {
/*N*/       Point aEndPos = GetDrawPos( nEndCol, nEndRow, FALSE );
/*N*/       Size aEndSize = Size(
/*N*/                           (long) ( pDoc->GetColWidth( nEndCol, nTab) * HMM_PER_TWIPS ),
/*N*/                           (long) ( pDoc->GetRowHeight( nEndRow, nTab) * HMM_PER_TWIPS ) );
/*N*/       aEndRect = Rectangle( aEndPos, aEndSize );
/*N*/   }
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/
/*N*/   BOOL bFound = FALSE;
/*N*/   SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/   SdrObject* pObject = aIter.Next();
/*N*/   while (pObject && !bFound)
/*N*/   {
/*N*/       if ( pObject->GetLayer()==SC_LAYER_INTERN &&
/*N*/               pObject->IsPolyObj() && pObject->GetPointCount()==2 )
/*N*/       {
/*N*/           BOOL bObjStartAlien =
/*N*/               lcl_IsOtherTab( ((const XLineStartItem&)pObject->GetItem(XATTR_LINESTART)).GetValue() );
/*N*/           BOOL bObjEndAlien =
/*N*/               lcl_IsOtherTab( ((const XLineEndItem&)pObject->GetItem(XATTR_LINEEND)).GetValue() );
/*N*/
/*N*/           BOOL bStartHit = bStartAlien ? bObjStartAlien :
/*N*/                               ( !bObjStartAlien && aStartRect.IsInside(pObject->GetPoint(0)) );
/*N*/           BOOL bEndHit = bEndAlien ? bObjEndAlien :
/*N*/                               ( !bObjEndAlien && aEndRect.IsInside(pObject->GetPoint(1)) );
/*N*/
/*N*/           if ( bStartHit && bEndHit )
/*N*/               bFound = TRUE;
/*N*/       }
/*N*/       pObject = aIter.Next();
/*N*/   }
/*N*/
/*N*/   return bFound;
/*N*/ }


//------------------------------------------------------------------------

//  InsertXXX: called from DrawEntry/DrawAlienEntry and InsertObject

/*N*/ BOOL ScDetectiveFunc::InsertArrow( USHORT nCol, USHORT nRow,
/*N*/                               USHORT nRefStartCol, USHORT nRefStartRow,
/*N*/                               USHORT nRefEndCol, USHORT nRefEndRow,
/*N*/                               BOOL bFromOtherTab, BOOL bRed,
/*N*/                               ScDetectiveData& rData )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/
/*N*/   BOOL bArea = ( nRefStartCol != nRefEndCol || nRefStartRow != nRefEndRow );
/*N*/
/*N*/   Point aStartPos = GetDrawPos( nRefStartCol, nRefStartRow, TRUE );
/*N*/   Point aEndPos   = GetDrawPos( nCol, nRow, TRUE );
/*N*/
/*N*/   if (bFromOtherTab)
/*N*/   {
/*N*/       aStartPos = Point( aEndPos.X() - 1000, aEndPos.Y() - 1000 );
/*N*/       if (aStartPos.X() < 0)
/*N*/           aStartPos.X() += 2000;
/*N*/       if (aStartPos.Y() < 0)
/*N*/           aStartPos.Y() += 2000;
/*N*/   }
/*N*/
/*N*/   SfxItemSet& rAttrSet = bFromOtherTab ? rData.GetFromTabSet() : rData.GetArrowSet();
/*N*/
/*N*/   if (bArea && !bFromOtherTab)
/*N*/       rAttrSet.Put( XLineWidthItem( 50 ) );               // Bereich
/*N*/   else
/*N*/       rAttrSet.Put( XLineWidthItem( 0 ) );                // einzelne Referenz
/*N*/
/*N*/   ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
/*N*/   rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) );
/*N*/   Point aPointArr[2] = {aStartPos, aEndPos};
/*N*/   SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE,
/*N*/               XPolyPolygon(XPolygon(Polygon(2, aPointArr))));
/*N*/
/*N*/   pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos));  //! noetig ???
/*N*/
/*N*/   pArrow->SetItemSetAndBroadcast(rAttrSet);
/*N*/
/*N*/   ScDrawLayer::SetAnchor( pArrow, SCA_CELL );
/*N*/   pArrow->SetLayer( SC_LAYER_INTERN );
/*N*/   pPage->InsertObject( pArrow );
/*N*/   pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
/*N*/
/*N*/   ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, TRUE );
/*N*/   if (bFromOtherTab)
/*N*/       pData->bValidStart = FALSE;
/*N*/   else
/*N*/   {
/*N*/       pData->aStt.nCol = nRefStartCol;
/*N*/       pData->aStt.nRow = nRefStartRow;
/*N*/       pData->aStt.nTab = nTab;
/*N*/       pData->bValidStart = TRUE;
/*N*/   }
/*N*/
/*N*/   pData->aEnd.nCol = nCol;
/*N*/   pData->aEnd.nRow = nRow;
/*N*/   pData->aEnd.nTab = nTab;
/*N*/   pData->bValidEnd = TRUE;
/*N*/
/*N*/   return TRUE;
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::InsertToOtherTab( USHORT nStartCol, USHORT nStartRow,
/*N*/                               USHORT nEndCol, USHORT nEndRow, BOOL bRed,
/*N*/                               ScDetectiveData& rData )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/
/*N*/   BOOL bArea = ( nStartCol != nEndCol || nStartRow != nEndRow );
/*N*/   if (bArea)
/*N*/   {
/*N*/       Point aStartCorner = GetDrawPos( nStartCol, nStartRow, FALSE );
/*N*/       Point aEndCorner = GetDrawPos( nEndCol+1, nEndRow+1, FALSE );
/*N*/
/*N*/       SdrRectObj* pBox = new SdrRectObj(Rectangle(aStartCorner,aEndCorner));
/*N*/
/*N*/       pBox->SetItemSetAndBroadcast(rData.GetBoxSet());
/*N*/
/*N*/       ScDrawLayer::SetAnchor( pBox, SCA_CELL );
/*N*/       pBox->SetLayer( SC_LAYER_INTERN );
/*N*/       pPage->InsertObject( pBox );
/*N*/       pModel->AddCalcUndo( new SdrUndoInsertObj( *pBox ) );
/*N*/
/*N*/       ScDrawObjData* pData = ScDrawLayer::GetObjData( pBox, TRUE );
/*N*/       pData->aStt.nCol = nStartCol;
/*N*/       pData->aStt.nRow = nStartRow;
/*N*/       pData->aStt.nTab = nTab;
/*N*/       pData->aEnd.nCol = nEndCol;
/*N*/       pData->aEnd.nRow = nEndRow;
/*N*/       pData->aEnd.nTab = nTab;
/*N*/       pData->bValidStart = TRUE;
/*N*/       pData->bValidEnd = TRUE;
/*N*/   }
/*N*/
/*N*/   Point aStartPos = GetDrawPos( nStartCol, nStartRow, TRUE );
/*N*/   Point aEndPos   = Point( aStartPos.X() + 1000, aStartPos.Y() - 1000 );
/*N*/   if (aEndPos.Y() < 0)
/*N*/       aEndPos.Y() += 2000;
/*N*/
/*N*/   SfxItemSet& rAttrSet = rData.GetToTabSet();
/*N*/   if (bArea)
/*N*/       rAttrSet.Put( XLineWidthItem( 50 ) );               // Bereich
/*N*/   else
/*N*/       rAttrSet.Put( XLineWidthItem( 0 ) );                // einzelne Referenz
/*N*/
/*N*/   ColorData nColorData = ( bRed ? GetErrorColor() : GetArrowColor() );
/*N*/   rAttrSet.Put( XLineColorItem( String(), Color( nColorData ) ) );
/*N*/   Point aPointArr[2] = {aStartPos, aEndPos};
/*N*/   SdrPathObj* pArrow = new SdrPathObj(OBJ_LINE,
/*N*/               XPolyPolygon(XPolygon(Polygon(2, aPointArr))));
/*N*/
/*N*/   pArrow->NbcSetLogicRect(Rectangle(aStartPos,aEndPos));  //! noetig ???
/*N*/
/*N*/   pArrow->SetItemSetAndBroadcast(rAttrSet);
/*N*/
/*N*/   ScDrawLayer::SetAnchor( pArrow, SCA_CELL );
/*N*/   pArrow->SetLayer( SC_LAYER_INTERN );
/*N*/   pPage->InsertObject( pArrow );
/*N*/   pModel->AddCalcUndo( new SdrUndoInsertObj( *pArrow ) );
/*N*/
/*N*/   ScDrawObjData* pData = ScDrawLayer::GetObjData( pArrow, TRUE );
/*N*/   pData->aStt.nCol = nStartCol;
/*N*/   pData->aStt.nRow = nStartRow;
/*N*/   pData->aStt.nTab = nTab;
/*N*/   pData->bValidStart = TRUE;
/*N*/   pData->bValidEnd = FALSE;
/*N*/
/*N*/   return TRUE;
/*N*/ }

//------------------------------------------------------------------------

//  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

/*N*/ BOOL ScDetectiveFunc::DrawEntry( USHORT nCol, USHORT nRow,
/*N*/                                   const ScTripel& rRefStart, const ScTripel& rRefEnd,
/*N*/                                   ScDetectiveData& rData )
/*N*/ {
/*N*/   if ( HasArrow( rRefStart.GetCol(), rRefStart.GetRow(), rRefStart.GetTab(),
/*N*/                   nCol, nRow, nTab ) )
/*N*/       return FALSE;
/*N*/
/*N*/   ScTripel aErrorPos;
/*N*/   BOOL bError = HasError( rRefStart, rRefEnd, aErrorPos );
/*N*/   BOOL bAlien = ( rRefEnd.GetTab() < nTab || rRefStart.GetTab() > nTab );
/*N*/
/*N*/   return InsertArrow( nCol, nRow,
/*N*/                       rRefStart.GetCol(), rRefStart.GetRow(),
/*N*/                       rRefEnd.GetCol(), rRefEnd.GetRow(),
/*N*/                       bAlien, bError, rData );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::DrawAlienEntry( const ScTripel& rRefStart, const ScTripel& rRefEnd,
/*N*/                                       ScDetectiveData& rData )
/*N*/ {
/*N*/   if ( HasArrow( rRefStart.GetCol(), rRefStart.GetRow(), rRefStart.GetTab(),
/*N*/                   0, 0, nTab+1 ) )
/*N*/       return FALSE;
/*N*/
/*N*/   ScTripel aErrorPos;
/*N*/   BOOL bError = HasError( rRefStart, rRefEnd, aErrorPos );
/*N*/
/*N*/   return InsertToOtherTab( rRefStart.GetCol(), rRefStart.GetRow(),
/*N*/                               rRefEnd.GetCol(), rRefEnd.GetRow(),
/*N*/                               bError, rData );
/*N*/ }

/*N*/ void ScDetectiveFunc::DrawCircle( USHORT nCol, USHORT nRow, ScDetectiveData& rData )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/
/*N*/   Point aStartPos = GetDrawPos( nCol, nRow, FALSE );
/*N*/   Size aSize( (long) ( pDoc->GetColWidth(nCol, nTab) * HMM_PER_TWIPS ),
/*N*/               (long) ( pDoc->GetRowHeight(nRow, nTab) * HMM_PER_TWIPS ) );
/*N*/   Rectangle aRect( aStartPos, aSize );
/*N*/   aRect.Left()    -= 250;
/*N*/   aRect.Right()   += 250;
/*N*/   aRect.Top()     -= 70;
/*N*/   aRect.Bottom()  += 70;
/*N*/
/*N*/   SdrCircObj* pCircle = new SdrCircObj( OBJ_CIRC, aRect );
/*N*/   SfxItemSet& rAttrSet = rData.GetCircleSet();
/*N*/
/*N*/   pCircle->SetItemSetAndBroadcast(rAttrSet);
/*N*/
/*N*/   ScDrawLayer::SetAnchor( pCircle, SCA_CELL );
/*N*/   pCircle->SetLayer( SC_LAYER_INTERN );
/*N*/   pPage->InsertObject( pCircle );
/*N*/   pModel->AddCalcUndo( new SdrUndoInsertObj( *pCircle ) );
/*N*/
/*N*/   ScDrawObjData* pData = ScDrawLayer::GetObjData( pCircle, TRUE );
/*N*/   pData->aStt.nCol = nCol;
/*N*/   pData->aStt.nRow = nRow;
/*N*/   pData->aStt.nTab = nTab;
/*N*/   pData->bValidStart = TRUE;
/*N*/   pData->bValidEnd = FALSE;
/*N*/ }


/*N*/ void ScDetectiveFunc::DeleteArrowsAt( USHORT nCol, USHORT nRow, BOOL bDestPnt )
/*N*/ {
/*N*/   Point aPos = GetDrawPos( nCol, nRow, FALSE );
/*N*/   Size aSize = Size(  (long) ( pDoc->GetColWidth( nCol, nTab) * HMM_PER_TWIPS ),
/*N*/                       (long) ( pDoc->GetRowHeight( nRow, nTab) * HMM_PER_TWIPS ) );
/*N*/   Rectangle aRect( aPos, aSize );
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/
/*N*/   pPage->RecalcObjOrdNums();
/*N*/
/*N*/   long    nDelCount = 0;
/*N*/   ULONG   nObjCount = pPage->GetObjCount();
/*N*/   if (nObjCount)
/*N*/   {
/*N*/       SdrObject** ppObj = new SdrObject*[nObjCount];
/*N*/
/*N*/       SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/       SdrObject* pObject = aIter.Next();
/*N*/       while (pObject)
/*N*/       {
/*N*/           if ( pObject->GetLayer()==SC_LAYER_INTERN &&
/*N*/                   pObject->IsPolyObj() && pObject->GetPointCount()==2 )
/*N*/           {
/*N*/               if (aRect.IsInside(pObject->GetPoint(bDestPnt)))            // Start/Zielpunkt
/*N*/                   ppObj[nDelCount++] = pObject;
/*N*/           }
/*N*/
/*N*/           pObject = aIter.Next();
/*N*/       }
/*N*/
/*N*/       long i;
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
/*N*/
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
/*N*/
/*N*/       delete[] ppObj;
/*N*/   }
/*N*/ }

        //      Box um Referenz loeschen

#define SC_DET_TOLERANCE    50

/*N*/ inline BOOL RectIsPoints( const Rectangle& rRect, const Point& rStart, const Point& rEnd )
/*N*/ {
/*N*/   return rRect.Left()   >= rStart.X() - SC_DET_TOLERANCE
/*N*/       && rRect.Left()   <= rStart.X() + SC_DET_TOLERANCE
/*N*/       && rRect.Right()  >= rEnd.X()   - SC_DET_TOLERANCE
/*N*/       && rRect.Right()  <= rEnd.X()   + SC_DET_TOLERANCE
/*N*/       && rRect.Top()    >= rStart.Y() - SC_DET_TOLERANCE
/*N*/       && rRect.Top()    <= rStart.Y() + SC_DET_TOLERANCE
/*N*/       && rRect.Bottom() >= rEnd.Y()   - SC_DET_TOLERANCE
/*N*/       && rRect.Bottom() <= rEnd.Y()   + SC_DET_TOLERANCE;
/*N*/ }

#undef SC_DET_TOLERANCE

/*N*/ void ScDetectiveFunc::DeleteBox( USHORT nCol1, USHORT nRow1, USHORT nCol2, USHORT nRow2 )
/*N*/ {
/*N*/   Point aStartCorner = GetDrawPos( nCol1, nRow1, FALSE );
/*N*/   Point aEndCorner = GetDrawPos( nCol2+1, nRow2+1, FALSE );
/*N*/   Rectangle aObjRect;
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/
/*N*/   pPage->RecalcObjOrdNums();
/*N*/
/*N*/   long    nDelCount = 0;
/*N*/   ULONG   nObjCount = pPage->GetObjCount();
/*N*/   if (nObjCount)
/*N*/   {
/*N*/       SdrObject** ppObj = new SdrObject*[nObjCount];
/*N*/
/*N*/       SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/       SdrObject* pObject = aIter.Next();
/*N*/       while (pObject)
/*N*/       {
/*N*/           if ( pObject->GetLayer() == SC_LAYER_INTERN &&
/*N*/                   pObject->Type() == TYPE(SdrRectObj) )
/*N*/           {
/*N*/               aObjRect = ((SdrRectObj*)pObject)->GetLogicRect();
/*N*/               if ( RectIsPoints( aObjRect, aStartCorner, aEndCorner ) )
/*N*/                   ppObj[nDelCount++] = pObject;
/*N*/           }
/*N*/
/*N*/           pObject = aIter.Next();
/*N*/       }
/*N*/
/*N*/       long i;
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
/*N*/
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
/*N*/
/*N*/       delete[] ppObj;
/*N*/   }
/*N*/ }

//------------------------------------------------------------------------

/*N*/ USHORT ScDetectiveFunc::InsertPredLevelArea( const ScTripel& rRefStart, const ScTripel& rRefEnd,
/*N*/                                       ScDetectiveData& rData, USHORT nLevel )
/*N*/ {
/*N*/   USHORT nResult = DET_INS_EMPTY;
/*N*/
/*N*/   ScCellIterator aCellIter( pDoc, rRefStart.GetCol(), rRefStart.GetRow(), rRefStart.GetTab(),
/*N*/                                   rRefEnd.GetCol(), rRefEnd.GetRow(), rRefEnd.GetTab() );
/*N*/   ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/   while (pCell)
/*N*/   {
/*N*/       if (pCell->GetCellType() == CELLTYPE_FORMULA)
/*N*/           switch( InsertPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), rData, nLevel ) )
/*N*/           {
/*N*/               case DET_INS_INSERTED:
/*N*/                   nResult = DET_INS_INSERTED;
/*N*/                   break;
/*N*/               case DET_INS_CONTINUE:
/*N*/                   if (nResult != DET_INS_INSERTED)
/*N*/                       nResult = DET_INS_CONTINUE;
/*N*/                   break;
/*N*/               case DET_INS_CIRCULAR:
/*N*/                   if (nResult == DET_INS_EMPTY)
/*N*/                       nResult = DET_INS_CIRCULAR;
/*N*/                   break;
/*N*/           }
/*N*/
/*N*/       pCell = aCellIter.GetNext();
/*N*/   }
/*N*/
/*N*/   return nResult;
/*N*/ }

/*N*/ USHORT ScDetectiveFunc::InsertPredLevel( USHORT nCol, USHORT nRow, ScDetectiveData& rData,
/*N*/                                           USHORT nLevel )
/*N*/ {
/*N*/   ScBaseCell* pCell;
/*N*/   pDoc->GetCell( nCol, nRow, nTab, pCell );
/*N*/   if (!pCell)
/*N*/       return DET_INS_EMPTY;
/*N*/   if (pCell->GetCellType() != CELLTYPE_FORMULA)
/*N*/       return DET_INS_EMPTY;
/*N*/
/*N*/   ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
/*N*/   if (pFCell->IsRunning())
/*N*/       return DET_INS_CIRCULAR;
/*N*/
/*N*/   if (pFCell->GetDirty())
/*N*/       pFCell->Interpret();                // nach SetRunning geht's nicht mehr!
/*N*/   pFCell->SetRunning(TRUE);
/*N*/
/*N*/   USHORT nResult = DET_INS_EMPTY;
/*N*/
/*N*/   ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
/*N*/   ScTripel aRefStart;
/*N*/   ScTripel aRefEnd;
/*N*/   while ( aIter.GetNextRef( aRefStart, aRefEnd ) )
/*N*/   {
/*N*/       if (DrawEntry( nCol, nRow, aRefStart, aRefEnd, rData ))
/*N*/       {
/*N*/           nResult = DET_INS_INSERTED;         //  neuer Pfeil eingetragen
/*N*/       }
/*N*/       else
/*N*/       {
/*N*/           //  weiterverfolgen
/*N*/
/*N*/           if ( nLevel < rData.GetMaxLevel() )
/*N*/           {
/*N*/               USHORT nSubResult;
/*N*/               BOOL bArea = (aRefStart != aRefEnd);
/*N*/               if (bArea)
/*N*/                   nSubResult = InsertPredLevelArea( aRefStart, aRefEnd, rData, nLevel+1 );
/*N*/               else
/*N*/                   nSubResult = InsertPredLevel( aRefStart.GetCol(), aRefStart.GetRow(),
/*N*/                                                   rData, nLevel+1 );
/*N*/
/*N*/               switch (nSubResult)
/*N*/               {
/*N*/                   case DET_INS_INSERTED:
/*N*/                       nResult = DET_INS_INSERTED;
/*N*/                       break;
/*N*/                   case DET_INS_CONTINUE:
/*N*/                       if (nResult != DET_INS_INSERTED)
/*N*/                           nResult = DET_INS_CONTINUE;
/*N*/                       break;
/*N*/                   case DET_INS_CIRCULAR:
/*N*/                       if (nResult == DET_INS_EMPTY)
/*N*/                           nResult = DET_INS_CIRCULAR;
/*N*/                       break;
/*N*/                   // DET_INS_EMPTY: unveraendert lassen
/*N*/               }
/*N*/           }
/*N*/           else                                    //  nMaxLevel erreicht
/*N*/               if (nResult != DET_INS_INSERTED)
/*N*/                   nResult = DET_INS_CONTINUE;
/*N*/       }
/*N*/   }
/*N*/
/*N*/   pFCell->SetRunning(FALSE);
/*N*/
/*N*/   return nResult;
/*N*/ }

/*N*/ USHORT ScDetectiveFunc::FindPredLevelArea( const ScTripel& rRefStart, const ScTripel& rRefEnd,
/*N*/                                               USHORT nLevel, USHORT nDeleteLevel )
/*N*/ {
/*N*/   USHORT nResult = nLevel;
/*N*/
/*N*/   ScCellIterator aCellIter( pDoc, rRefStart.GetCol(), rRefStart.GetRow(), rRefStart.GetTab(),
/*N*/                                   rRefEnd.GetCol(), rRefEnd.GetRow(), rRefEnd.GetTab() );
/*N*/   ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/   while (pCell)
/*N*/   {
/*N*/       if (pCell->GetCellType() == CELLTYPE_FORMULA)
/*N*/       {
/*N*/           USHORT nTemp = FindPredLevel( aCellIter.GetCol(), aCellIter.GetRow(), nLevel, nDeleteLevel );
/*N*/           if (nTemp > nResult)
/*N*/               nResult = nTemp;
/*N*/       }
/*N*/       pCell = aCellIter.GetNext();
/*N*/   }
/*N*/
/*N*/   return nResult;
/*N*/ }

                                            //  nDeleteLevel != 0   -> loeschen

/*N*/ USHORT ScDetectiveFunc::FindPredLevel( USHORT nCol, USHORT nRow, USHORT nLevel, USHORT nDeleteLevel )
/*N*/ {
/*N*/   DBG_ASSERT( nLevel<1000, "Level" );
/*N*/
/*N*/   ScBaseCell* pCell;
/*N*/   pDoc->GetCell( nCol, nRow, nTab, pCell );
/*N*/   if (!pCell)
/*N*/       return nLevel;
/*N*/   if (pCell->GetCellType() != CELLTYPE_FORMULA)
/*N*/       return nLevel;
/*N*/
/*N*/   ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
/*N*/   if (pFCell->IsRunning())
/*N*/       return nLevel;
/*N*/
/*N*/   if (pFCell->GetDirty())
/*N*/       pFCell->Interpret();                // nach SetRunning geht's nicht mehr!
/*N*/   pFCell->SetRunning(TRUE);
/*N*/
/*N*/   USHORT nResult = nLevel;
/*N*/   BOOL bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
/*N*/
/*N*/   if ( bDelete )
/*N*/   {
/*N*/       DeleteArrowsAt( nCol, nRow, TRUE );                 // Pfeile, die hierher zeigen
/*N*/   }
/*N*/
/*N*/   ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
/*N*/   ScTripel aRefStart;
/*N*/   ScTripel aRefEnd;
/*N*/   while ( aIter.GetNextRef( aRefStart, aRefEnd ) )
/*N*/   {
/*N*/       BOOL bArea = ( aRefStart != aRefEnd );
/*N*/
/*N*/       if ( bDelete )                  // Rahmen loeschen ?
/*N*/       {
/*N*/           if (bArea)
/*N*/           {
/*N*/               DeleteBox( aRefStart.GetCol(), aRefStart.GetRow(), aRefEnd.GetCol(), aRefEnd.GetRow() );
/*N*/           }
/*N*/       }
/*N*/       else                            // weitersuchen
/*N*/       {
/*N*/           if ( HasArrow( aRefStart.GetCol(),aRefStart.GetRow(),aRefStart.GetTab(),
/*N*/                           nCol,nRow,nTab ) )
/*N*/           {
/*N*/               USHORT nTemp;
/*N*/               if (bArea)
/*N*/                   nTemp = FindPredLevelArea( aRefStart, aRefEnd, nLevel+1, nDeleteLevel );
/*N*/               else
/*N*/                   nTemp = FindPredLevel( aRefStart.GetCol(),aRefStart.GetRow(),
/*N*/                                                       nLevel+1, nDeleteLevel );
/*N*/               if (nTemp > nResult)
/*N*/                   nResult = nTemp;
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/
/*N*/   pFCell->SetRunning(FALSE);
/*N*/
/*N*/   return nResult;
/*N*/ }

//------------------------------------------------------------------------

/*N*/ USHORT ScDetectiveFunc::InsertErrorLevel( USHORT nCol, USHORT nRow, ScDetectiveData& rData,
/*N*/                                           USHORT nLevel )
/*N*/ {
/*N*/   ScBaseCell* pCell;
/*N*/   pDoc->GetCell( nCol, nRow, nTab, pCell );
/*N*/   if (!pCell)
/*N*/       return DET_INS_EMPTY;
/*N*/   if (pCell->GetCellType() != CELLTYPE_FORMULA)
/*N*/       return DET_INS_EMPTY;
/*N*/
/*N*/   ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
/*N*/   if (pFCell->IsRunning())
/*N*/       return DET_INS_CIRCULAR;
/*N*/
/*N*/   if (pFCell->GetDirty())
/*N*/       pFCell->Interpret();                // nach SetRunning geht's nicht mehr!
/*N*/   pFCell->SetRunning(TRUE);
/*N*/
/*N*/   USHORT nResult = DET_INS_EMPTY;
/*N*/
/*N*/   ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
/*N*/   ScTripel aRefStart;
/*N*/   ScTripel aRefEnd;
/*N*/   ScTripel aErrorPos;
/*N*/   BOOL bHasError = FALSE;
/*N*/   while ( aIter.GetNextRef( aRefStart, aRefEnd ) )
/*N*/   {
/*N*/       if (HasError( aRefStart, aRefEnd, aErrorPos ))
/*N*/       {
/*N*/           bHasError = TRUE;
/*N*/           if (DrawEntry( nCol, nRow, aErrorPos, aErrorPos, rData ))
/*N*/               nResult = DET_INS_INSERTED;
/*N*/
/*N*/           //  und weiterverfolgen
/*N*/
/*N*/           if ( nLevel < rData.GetMaxLevel() )         // praktisch immer
/*N*/           {
/*N*/               if (InsertErrorLevel( aErrorPos.GetCol(), aErrorPos.GetRow(),
/*N*/                                                       rData, nLevel+1 ) == DET_INS_INSERTED)
/*N*/                   nResult = DET_INS_INSERTED;
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/
/*N*/   pFCell->SetRunning(FALSE);
/*N*/
/*N*/                                                   // Blaetter ?
/*N*/   if (!bHasError)
/*N*/       if (InsertPredLevel( nCol, nRow, rData, rData.GetMaxLevel() ) == DET_INS_INSERTED)
/*N*/           nResult = DET_INS_INSERTED;
/*N*/
/*N*/   return nResult;
/*N*/ }

//------------------------------------------------------------------------

/*N*/ USHORT ScDetectiveFunc::InsertSuccLevel( USHORT nCol1, USHORT nRow1, USHORT nCol2, USHORT nRow2,
/*N*/                                       ScDetectiveData& rData, USHORT nLevel )
/*N*/ {
/*N*/   //  ueber ganzes Dokument
/*N*/
/*N*/   USHORT nResult = DET_INS_EMPTY;
/*N*/ //    ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab );
/*N*/   ScCellIterator aCellIter( pDoc, 0,0,0, MAXCOL,MAXROW,MAXTAB );          // alle Tabellen
/*N*/   ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/   while (pCell)
/*N*/   {
/*N*/       if (pCell->GetCellType() == CELLTYPE_FORMULA)
/*N*/       {
/*N*/           ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
/*N*/           BOOL bRunning = pFCell->IsRunning();
/*N*/
/*N*/           if (pFCell->GetDirty())
/*N*/               pFCell->Interpret();                // nach SetRunning geht's nicht mehr!
/*N*/           pFCell->SetRunning(TRUE);
/*N*/
/*N*/           ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
/*N*/           ScTripel aRefStart;
/*N*/           ScTripel aRefEnd;
/*N*/           while ( aIter.GetNextRef( aRefStart, aRefEnd ) )
/*N*/           {
/*N*/               if (aRefStart.GetTab() <= nTab && aRefEnd.GetTab() >= nTab)
/*N*/               {
/*N*/                   if (Intersect( nCol1,nRow1,nCol2,nRow2,
/*N*/                           aRefStart.GetCol(),aRefStart.GetRow(),
/*N*/                           aRefEnd.GetCol(),aRefEnd.GetRow() ))
/*N*/                   {
/*N*/                       BOOL bAlien = ( aCellIter.GetTab() != nTab );
/*N*/                       BOOL bDrawRet;
/*N*/                       if (bAlien)
/*N*/                           bDrawRet = DrawAlienEntry( aRefStart, aRefEnd, rData );
/*N*/                       else
/*N*/                           bDrawRet = DrawEntry( aCellIter.GetCol(), aCellIter.GetRow(),
/*N*/                                                   aRefStart, aRefEnd, rData );
/*N*/                       if (bDrawRet)
/*N*/                       {
/*N*/                           nResult = DET_INS_INSERTED;         //  neuer Pfeil eingetragen
/*N*/                       }
/*N*/                       else
/*N*/                       {
/*N*/                           if (bRunning)
/*N*/                           {
/*N*/                               if (nResult == DET_INS_EMPTY)
/*N*/                                   nResult = DET_INS_CIRCULAR;
/*N*/                           }
/*N*/                           else
/*N*/                           {
/*N*/                                       //  weiterverfolgen
/*N*/
/*N*/                               if ( nLevel < rData.GetMaxLevel() )
/*N*/                               {
/*N*/                                   USHORT nSubResult = InsertSuccLevel(
/*N*/                                                           aCellIter.GetCol(), aCellIter.GetRow(),
/*N*/                                                           aCellIter.GetCol(), aCellIter.GetRow(),
/*N*/                                                           rData, nLevel+1 );
/*N*/                                   switch (nSubResult)
/*N*/                                   {
/*N*/                                       case DET_INS_INSERTED:
/*N*/                                           nResult = DET_INS_INSERTED;
/*N*/                                           break;
/*N*/                                       case DET_INS_CONTINUE:
/*N*/                                           if (nResult != DET_INS_INSERTED)
/*N*/                                               nResult = DET_INS_CONTINUE;
/*N*/                                           break;
/*N*/                                       case DET_INS_CIRCULAR:
/*N*/                                           if (nResult == DET_INS_EMPTY)
/*N*/                                               nResult = DET_INS_CIRCULAR;
/*N*/                                           break;
/*N*/                                       // DET_INS_EMPTY: unveraendert lassen
/*N*/                                   }
/*N*/                               }
/*N*/                               else                                    //  nMaxLevel erreicht
/*N*/                                   if (nResult != DET_INS_INSERTED)
/*N*/                                       nResult = DET_INS_CONTINUE;
/*N*/                           }
/*N*/                       }
/*N*/                   }
/*N*/               }
/*N*/           }
/*N*/           pFCell->SetRunning(bRunning);
/*N*/       }
/*N*/       pCell = aCellIter.GetNext();
/*N*/   }
/*N*/
/*N*/   return nResult;
/*N*/ }

/*N*/ USHORT ScDetectiveFunc::FindSuccLevel( USHORT nCol1, USHORT nRow1, USHORT nCol2, USHORT nRow2,
/*N*/                                       USHORT nLevel, USHORT nDeleteLevel )
/*N*/ {
/*N*/   DBG_ASSERT( nLevel<1000, "Level" );
/*N*/
/*N*/   USHORT nResult = nLevel;
/*N*/   BOOL bDelete = ( nDeleteLevel && nLevel == nDeleteLevel-1 );
/*N*/
/*N*/   ScCellIterator aCellIter( pDoc, 0,0, nTab, MAXCOL,MAXROW, nTab );
/*N*/   ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/   while (pCell)
/*N*/   {
/*N*/       if (pCell->GetCellType() == CELLTYPE_FORMULA)
/*N*/       {
/*N*/           ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
/*N*/           BOOL bRunning = pFCell->IsRunning();
/*N*/
/*N*/           if (pFCell->GetDirty())
/*N*/               pFCell->Interpret();                // nach SetRunning geht's nicht mehr!
/*N*/           pFCell->SetRunning(TRUE);
/*N*/
/*N*/           ScDetectiveRefIter aIter( (ScFormulaCell*) pCell );
/*N*/           ScTripel aRefStart;
/*N*/           ScTripel aRefEnd;
/*N*/           while ( aIter.GetNextRef( aRefStart, aRefEnd ) )
/*N*/           {
/*N*/               if (aRefStart.GetTab() <= nTab && aRefEnd.GetTab() >= nTab)
/*N*/               {
/*N*/                   if (Intersect( nCol1,nRow1,nCol2,nRow2,
/*N*/                           aRefStart.GetCol(),aRefStart.GetRow(),
/*N*/                           aRefEnd.GetCol(),aRefEnd.GetRow() ))
/*N*/                   {
/*N*/                       if ( bDelete )                          // Pfeile, die hier anfangen
/*N*/                       {
/*N*/                           if (aRefStart != aRefEnd)
/*N*/                           {
/*N*/                               DeleteBox( aRefStart.GetCol(), aRefStart.GetRow(),
/*N*/                                               aRefEnd.GetCol(), aRefEnd.GetRow() );
/*N*/                           }
/*N*/                           DeleteArrowsAt( aRefStart.GetCol(), aRefStart.GetRow(), FALSE );
/*N*/                       }
/*N*/                       else if ( !bRunning &&
/*N*/                               HasArrow( aRefStart.GetCol(),aRefStart.GetRow(),aRefStart.GetTab(),
/*N*/                                           aCellIter.GetCol(),aCellIter.GetRow(),aCellIter.GetTab() ) )
/*N*/                       {
/*N*/                           USHORT nTemp = FindSuccLevel( aCellIter.GetCol(), aCellIter.GetRow(),
/*N*/                                                           aCellIter.GetCol(), aCellIter.GetRow(),
/*N*/                                                           nLevel+1, nDeleteLevel );
/*N*/                           if (nTemp > nResult)
/*N*/                               nResult = nTemp;
/*N*/                       }
/*N*/                   }
/*N*/               }
/*N*/           }
/*N*/
/*N*/           pFCell->SetRunning(bRunning);
/*N*/       }
/*N*/       pCell = aCellIter.GetNext();
/*N*/   }
/*N*/
/*N*/   return nResult;
/*N*/ }


//
//  --------------------------------------------------------------------------------
//

/*N*/ BOOL ScDetectiveFunc::ShowPred( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   (void)pPage;
/*N*/
/*N*/   ScDetectiveData aData( pModel );
/*N*/
/*N*/   USHORT nMaxLevel = 0;
/*N*/   USHORT nResult = DET_INS_CONTINUE;
/*N*/   while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
/*N*/   {
/*N*/       aData.SetMaxLevel( nMaxLevel );
/*N*/       nResult = InsertPredLevel( nCol, nRow, aData, 0 );
/*N*/       ++nMaxLevel;
/*N*/   }
/*N*/
/*N*/   return ( nResult == DET_INS_INSERTED );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::ShowSucc( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   (void)pPage;
/*N*/
/*N*/   ScDetectiveData aData( pModel );
/*N*/
/*N*/   USHORT nMaxLevel = 0;
/*N*/   USHORT nResult = DET_INS_CONTINUE;
/*N*/   while (nResult == DET_INS_CONTINUE && nMaxLevel < 1000)
/*N*/   {
/*N*/       aData.SetMaxLevel( nMaxLevel );
/*N*/       nResult = InsertSuccLevel( nCol, nRow, nCol, nRow, aData, 0 );
/*N*/       ++nMaxLevel;
/*N*/   }
/*N*/
/*N*/   return ( nResult == DET_INS_INSERTED );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::ShowError( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   (void)pPage;
/*N*/
/*N*/   ScTripel aPos( nCol, nRow, nTab );
/*N*/   ScTripel aErrPos;
/*N*/   if ( !HasError( aPos,aPos,aErrPos ) )
/*N*/       return FALSE;
/*N*/
/*N*/   ScDetectiveData aData( pModel );
/*N*/
/*N*/   aData.SetMaxLevel( 1000 );
/*N*/   USHORT nResult = InsertErrorLevel( nCol, nRow, aData, 0 );
/*N*/
/*N*/   return ( nResult == DET_INS_INSERTED );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::DeleteSucc( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   (void)pPage;
/*N*/
/*N*/   USHORT nLevelCount = FindSuccLevel( nCol, nRow, nCol, nRow, 0, 0 );
/*N*/   if ( nLevelCount )
/*N*/       FindSuccLevel( nCol, nRow, nCol, nRow, 0, nLevelCount );            // loeschen
/*N*/
/*N*/   return ( nLevelCount != 0 );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::DeletePred( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   (void)pPage;
/*N*/
/*N*/   USHORT nLevelCount = FindPredLevel( nCol, nRow, 0, 0 );
/*N*/   if ( nLevelCount )
/*N*/       FindPredLevel( nCol, nRow, 0, nLevelCount );            // loeschen
/*N*/
/*N*/   return ( nLevelCount != 0 );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::DeleteAll( ScDetectiveDelete eWhat )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/
/*N*/   pPage->RecalcObjOrdNums();
/*N*/
/*N*/   long    nDelCount = 0;
/*N*/   ULONG   nObjCount = pPage->GetObjCount();
/*N*/   if (nObjCount)
/*N*/   {
/*N*/       SdrObject** ppObj = new SdrObject*[nObjCount];
/*N*/
/*N*/       SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/       SdrObject* pObject = aIter.Next();
/*N*/       while (pObject)
/*N*/       {
/*N*/           if ( pObject->GetLayer() == SC_LAYER_INTERN )
/*N*/           {
/*N*/               BOOL bDoThis = TRUE;
/*N*/               if ( eWhat != SC_DET_ALL )
/*N*/               {
/*N*/                   BOOL bCircle = ( pObject->ISA(SdrCircObj) );
/*N*/                   BOOL bCaption = ( pObject->ISA(SdrCaptionObj) );
/*N*/                   if ( eWhat == SC_DET_DETECTIVE )        // Detektiv, aus Menue
/*N*/                       bDoThis = !bCaption;                // auch Kreise
/*N*/                   else if ( eWhat == SC_DET_CIRCLES )     // Kreise, wenn neue erzeugt werden
/*N*/                       bDoThis = bCircle;
/*N*/                   else if ( eWhat == SC_DET_COMMENTS )
/*N*/                       bDoThis = bCaption;
/*N*/                   else if ( eWhat == SC_DET_ARROWS )      // DetectiveRefresh
/*N*/                       bDoThis = !bCaption && !bCircle;    // don't include circles
/*N*/                   else
/*N*/                       OSL_FAIL("wat?");
/*N*/               }
/*N*/               if ( bDoThis )
/*N*/                   ppObj[nDelCount++] = pObject;
/*N*/           }
/*N*/
/*N*/           pObject = aIter.Next();
/*N*/       }
/*N*/
/*N*/       long i;
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pModel->AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) );
/*N*/
/*N*/       for (i=1; i<=nDelCount; i++)
/*N*/           pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() );
/*N*/
/*N*/       delete[] ppObj;
/*N*/   }
/*N*/
/*N*/   return ( nDelCount != 0 );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::MarkInvalid(BOOL& rOverflow)
/*N*/ {
/*N*/   rOverflow = FALSE;
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/
/*N*/   BOOL bDeleted = DeleteAll( SC_DET_CIRCLES );        // nur die Kreise
/*N*/
/*N*/   ScDetectiveData aData( pModel );
/*N*/   long nInsCount = 0;
/*N*/
/*N*/   //  Stellen suchen, wo Gueltigkeit definiert ist
/*N*/
/*N*/   ScDocAttrIterator aAttrIter( pDoc, nTab, 0,0,MAXCOL,MAXROW );
/*N*/   USHORT nCol, nRow1, nRow2;
/*N*/   const ScPatternAttr* pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
/*N*/   while ( pPattern && nInsCount < SC_DET_MAXCIRCLE )
/*N*/   {
/*N*/       ULONG nIndex = ((const SfxUInt32Item&)pPattern->GetItem(ATTR_VALIDDATA)).GetValue();
/*N*/       if (nIndex)
/*N*/       {
/*N*/           const ScValidationData* pData = pDoc->GetValidationEntry( nIndex );
/*N*/           if ( pData )
/*N*/           {
/*N*/               //  Zellen in dem Bereich durchgehen
/*N*/
/*N*/               BOOL bMarkEmpty = !pData->IsIgnoreBlank();
/*N*/               USHORT nNextRow = nRow1;
/*N*/               USHORT nRow;
/*N*/               ScCellIterator aCellIter( pDoc, nCol,nRow1,nTab, nCol,nRow2,nTab );
/*N*/               ScBaseCell* pCell = aCellIter.GetFirst();
/*N*/               while ( pCell && nInsCount < SC_DET_MAXCIRCLE )
/*N*/               {
/*N*/                   USHORT nCellRow = aCellIter.GetRow();
/*N*/                   if ( bMarkEmpty )
/*N*/                       for ( nRow = nNextRow; nRow < nCellRow && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
/*N*/                       {
/*N*/                           DrawCircle( nCol, nRow, aData );
/*N*/                           ++nInsCount;
/*N*/                       }
/*N*/                   DrawCircle( nCol, nCellRow, aData );
/*N*/                   ++nInsCount;
/*N*/                   nNextRow = nCellRow + 1;
/*N*/                   pCell = aCellIter.GetNext();
/*N*/               }
/*N*/               if ( bMarkEmpty )
/*N*/                   for ( nRow = nNextRow; nRow <= nRow2 && nInsCount < SC_DET_MAXCIRCLE; nRow++ )
/*N*/                   {
/*N*/                       DrawCircle( nCol, nRow, aData );
/*N*/                       ++nInsCount;
/*N*/                   }
/*N*/           }
/*N*/       }
/*N*/
/*N*/       pPattern = aAttrIter.GetNext( nCol, nRow1, nRow2 );
/*N*/   }
/*N*/
/*N*/   if ( nInsCount >= SC_DET_MAXCIRCLE )
/*N*/       rOverflow = TRUE;
/*N*/
/*N*/   return ( bDeleted || nInsCount != 0 );
/*N*/ }

SdrObject* ScDetectiveFunc::ShowCommentUser(
    USHORT nCol,
    USHORT nRow,
    const String& /* rUserText */,
    const Rectangle& /* rVisible */,
    BOOL /* bLeft */,
    BOOL /* bForce */,
    SdrPage* pDestPage
)
{
    ScDrawLayer* pModel = pDoc->GetDrawLayer();
    if (!pModel && !pDestPage)
        return NULL;

    ScPostIt aNote;
    pDoc->GetNote( nCol, nRow, nTab, aNote );

    return NULL;
}

/*N*/ SdrObject* ScDetectiveFunc::ShowComment( USHORT nCol, USHORT nRow, BOOL bForce, SdrPage* pDestPage )
/*N*/ {
/*N*/   return ShowCommentUser( nCol, nRow, String(), Rectangle(0,0,0,0), FALSE, bForce, pDestPage );
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::HideComment( USHORT nCol, USHORT nRow )
/*N*/ {
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return FALSE;
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/
/*N*/   pPage->RecalcObjOrdNums();
/*N*/   BOOL bDone = FALSE;
/*N*/
/*N*/   SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/   SdrObject* pObject = aIter.Next();
/*N*/   while (pObject && !bDone)
/*N*/   {
/*N*/       if ( pObject->GetLayer() == SC_LAYER_INTERN && pObject->ISA( SdrCaptionObj ) )
/*N*/       {
/*N*/           ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject );
/*N*/           if ( pData && nCol == pData->aStt.nCol && nRow == pData->aStt.nRow )
/*N*/           {
/*N*/               pModel->AddCalcUndo( new SdrUndoRemoveObj( *pObject ) );
/*N*/               pPage->RemoveObject( pObject->GetOrdNum() );
/*N*/               bDone = TRUE;
/*N*/           }
/*N*/       }
/*N*/
/*N*/       pObject = aIter.Next();
/*N*/   }
/*N*/
/*N*/   return bDone;
/*N*/ }

/*N*/ void ScDetectiveFunc::UpdateAllComments()
/*N*/ {
/*N*/   //  for all caption objects, update attributes and SpecialTextBoxShadow flag
/*N*/   //  (on all tables - nTab is ignored!)
/*N*/
/*N*/   //  no undo actions, this is refreshed after undo
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return;
/*N*/
/*N*/   ScCommentData aData( pDoc, pModel );
/*N*/
/*N*/   USHORT nTabCount = pDoc->GetTableCount();
/*N*/   for (USHORT nObjTab=0; nObjTab<nTabCount; nObjTab++)
/*N*/   {
/*N*/       SdrPage* pPage = pModel->GetPage(nObjTab);
/*N*/       DBG_ASSERT(pPage,"Page ?");
/*N*/       if (pPage)
/*N*/       {
/*N*/           SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/           SdrObject* pObject = aIter.Next();
/*N*/           while (pObject)
/*N*/           {
/*?*/               if ( pObject->GetLayer() == SC_LAYER_INTERN && pObject->ISA( SdrCaptionObj ) )
/*?*/               {
/*?*/                   SdrCaptionObj* pCaption = (SdrCaptionObj*)pObject;
/*?*/
/*?*/                   SfxItemSet& rAttrSet = aData.GetCaptionSet();
/*?*/
/*?*/                   pCaption->SetItemSetAndBroadcast(rAttrSet);
/*?*/
/*?*/                   pCaption->SetSpecialTextBoxShadow();
/*?*/               }
/*?*/
/*?*/               pObject = aIter.Next();
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/ }

/*N*/ void ScDetectiveFunc::UpdateAllArrowColors() // detective.sdc
/*N*/ {
/*N*/   //  no undo actions necessary
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel)
/*N*/       return;
/*N*/
/*N*/   USHORT nTabCount = pDoc->GetTableCount();
/*N*/   for (USHORT nObjTab=0; nObjTab<nTabCount; nObjTab++)
/*N*/   {
/*N*/       SdrPage* pPage = pModel->GetPage(nObjTab);
/*N*/       DBG_ASSERT(pPage,"Page ?");
/*N*/       if (pPage)
/*N*/       {
/*N*/           SdrObjListIter aIter( *pPage, IM_FLAT );
/*N*/           SdrObject* pObject = aIter.Next();
/*N*/           while (pObject)
/*N*/           {
/*N*/               if ( pObject->GetLayer() == SC_LAYER_INTERN )
/*N*/               {
/*N*/                   BOOL bArrow = FALSE;
/*N*/                   BOOL bError = FALSE;
/*N*/
/*N*/                   ScAddress aPos;
/*N*/                   ScRange aSource;
/*N*/                   BOOL bDummy;
/*N*/                   ScDetectiveObjType eType = GetDetectiveObjectType( pObject, aPos, aSource, bDummy );
/*N*/                   if ( eType == SC_DETOBJ_ARROW || eType == SC_DETOBJ_TOOTHERTAB )
/*N*/                   {
/*N*/                       //  source is valid, determine error flag from source range
/*N*/
/*N*/                       ScTripel aStart( aSource.aStart.Col(), aSource.aStart.Row(), aSource.aStart.Tab() );
/*N*/                       ScTripel aEnd( aSource.aEnd.Col(), aSource.aEnd.Row(), aSource.aEnd.Tab() );
/*N*/                       ScTripel aErrPos;
/*N*/                       if ( HasError( aStart, aEnd, aErrPos ) )
/*N*/                           bError = TRUE;
/*N*/                       else
/*N*/                           bArrow = TRUE;
/*N*/                   }
/*N*/                   else if ( eType == SC_DETOBJ_FROMOTHERTAB )
/*N*/                   {
/*N*/                       //  source range is no longer known, take error flag from formula itself
/*N*/                       //  (this means, if the formula has an error, all references to other tables
/*N*/                       //  are marked red)
/*N*/
/*N*/                       ScTripel aFormulaPos( aPos.Col(), aPos.Row(), aPos.Tab() );
/*N*/                       ScTripel aErrPos;
/*N*/                       if ( HasError( aFormulaPos, aFormulaPos, aErrPos ) )
/*N*/                           bError = TRUE;
/*N*/                       else
/*N*/                           bArrow = TRUE;
/*N*/                   }
/*N*/                   else if ( eType == SC_DETOBJ_CIRCLE )
/*N*/                   {
/*N*/                       //  circles (error marks) are always red
/*N*/
/*N*/                       bError = TRUE;
/*N*/                   }
/*N*/                   else if ( eType == SC_DETOBJ_NONE )
/*N*/                   {
/*N*/                       //  frame for area reference has no ObjType, always gets arrow color
/*N*/
/*N*/                       if ( pObject->ISA( SdrRectObj ) && !pObject->ISA( SdrCaptionObj ) )
/*N*/                       {
/*N*/                           bArrow = TRUE;
/*N*/                       }
/*N*/                   }
/*N*/
/*N*/                   if ( bArrow || bError )
/*N*/                   {
/*N*/                       ColorData nColorData = ( bError ? GetErrorColor() : GetArrowColor() );
/*N*/                       pObject->SetItem( XLineColorItem( String(), Color( nColorData ) ) );
/*N*/                   }
/*N*/               }
/*N*/
/*N*/               pObject = aIter.Next();
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/ }

/*N*/ BOOL ScDetectiveFunc::FindFrameForObject( SdrObject* pObject, ScRange& rRange ) // detective.sdc
/*N*/ {
/*N*/   //  find the rectangle for an arrow (always the object directly before the arrow)
/*N*/   //  rRange must be initialized to the source cell of the arrow (start of area)
/*N*/
/*N*/   ScDrawLayer* pModel = pDoc->GetDrawLayer();
/*N*/   if (!pModel) return FALSE;
/*N*/
/*N*/   SdrPage* pPage = pModel->GetPage(nTab);
/*N*/   DBG_ASSERT(pPage,"Page ?");
/*N*/   if (!pPage) return FALSE;
/*N*/
/*N*/   ULONG nPos = pPage->GetContainer().GetPos( pObject );
/*N*/   if ( nPos != CONTAINER_ENTRY_NOTFOUND && nPos > 0 )
/*N*/   {
/*N*/       SdrObject* pPrevObj = pPage->GetObj( nPos - 1 );
/*N*/       if ( pPrevObj && pPrevObj->GetLayer() == SC_LAYER_INTERN && pPrevObj->ISA(SdrRectObj) )
/*N*/       {
/*N*/           ScDrawObjData* pPrevData = ScDrawLayer::GetObjData( pPrevObj );
/*N*/           if ( pPrevData && pPrevData->bValidStart && pPrevData->bValidEnd )
/*N*/           {
/*N*/               if ( pPrevData->aStt.nCol == rRange.aStart.Col() &&
/*N*/                    pPrevData->aStt.nRow == rRange.aStart.Row() &&
/*N*/                    pPrevData->aStt.nTab == rRange.aStart.Tab() )
/*N*/               {
/*N*/                   rRange.aEnd.Set( pPrevData->aEnd.nCol,
/*N*/                                    pPrevData->aEnd.nRow,
/*N*/                                    pPrevData->aEnd.nTab );
/*N*/                   return TRUE;
/*N*/               }
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/   return FALSE;
/*N*/ }

/*N*/ ScDetectiveObjType ScDetectiveFunc::GetDetectiveObjectType( SdrObject* pObject,
/*N*/                               ScAddress& rPosition, ScRange& rSource, BOOL& rRedLine )
/*N*/ {
/*N*/   rRedLine = FALSE; // detective.sdc
/*N*/   ScDetectiveObjType eType = SC_DETOBJ_NONE;
/*N*/
/*N*/   if ( pObject && pObject->GetLayer() == SC_LAYER_INTERN )
/*N*/   {
/*N*/       ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject );
/*N*/       if ( pObject->IsPolyObj() && pObject->GetPointCount() == 2 )
/*N*/       {
/*N*/           // line object -> arrow
/*N*/
/*N*/           if ( pData->bValidStart )
/*N*/               eType = ( pData->bValidEnd ) ? SC_DETOBJ_ARROW : SC_DETOBJ_TOOTHERTAB;
/*N*/           else if ( pData->bValidEnd )
/*N*/               eType = SC_DETOBJ_FROMOTHERTAB;
/*N*/
/*N*/           if ( pData->bValidStart )
/*N*/               rSource = ScRange( pData->aStt.nCol, pData->aStt.nRow, pData->aStt.nTab );
/*N*/           if ( pData->bValidEnd )
/*N*/               rPosition = ScAddress( pData->aEnd.nCol, pData->aEnd.nRow, pData->aEnd.nTab );
/*N*/
/*N*/           if ( pData->bValidStart && lcl_HasThickLine( *pObject ) )
/*N*/           {
/*N*/               // thick line -> look for frame before this object
/*N*/
/*N*/               FindFrameForObject( pObject, rSource );     // modifies rSource
/*N*/           }
/*N*/
/*N*/           ColorData nObjColor = ((const XLineColorItem&)pObject->GetItem(XATTR_LINECOLOR)).GetValue().GetColor();
/*N*/           if ( nObjColor == GetErrorColor() && nObjColor != GetArrowColor() )
/*N*/               rRedLine = TRUE;
/*N*/       }
/*N*/       else if ( pObject->ISA(SdrCircObj) )
/*N*/       {
/*N*/           if ( pData->bValidStart )
/*N*/           {
/*N*/               // cell position is returned in rPosition
/*N*/
/*N*/               rPosition = ScAddress( pData->aStt.nCol, pData->aStt.nRow, pData->aStt.nTab );
/*N*/               eType = SC_DETOBJ_CIRCLE;
/*N*/           }
/*N*/       }
/*N*/   }
/*N*/
/*N*/   return eType;
/*N*/ }

// static
/*N*/ ColorData ScDetectiveFunc::GetArrowColor()
/*N*/ {
/*N*/   if (!bColorsInitialized)
/*N*/       InitializeColors();
/*N*/   return nArrowColor;
/*N*/ }

// static
/*N*/ ColorData ScDetectiveFunc::GetErrorColor()
/*N*/ {
/*N*/   if (!bColorsInitialized)
/*N*/       InitializeColors();
/*N*/   return nErrorColor;
/*N*/ }

// static
/*N*/ ColorData ScDetectiveFunc::GetCommentColor()
/*N*/ {
/*N*/   if (!bColorsInitialized)
/*N*/       InitializeColors();
/*N*/   return nCommentColor;
/*N*/ }

// static
/*N*/ void ScDetectiveFunc::InitializeColors()
/*N*/ {
/*N*/   // may be called several times to update colors from configuration
/*N*/
/*N*/     const ColorConfig& rColorCfg = SC_MOD()->GetColorConfig();
/*N*/     nArrowColor   = rColorCfg.GetColorValue(CALCDETECTIVE).nColor;
/*N*/     nErrorColor   = rColorCfg.GetColorValue(CALCDETECTIVEERROR).nColor;
/*N*/     nCommentColor = rColorCfg.GetColorValue(CALCNOTESBACKGROUND).nColor;
/*N*/
/*N*/   bColorsInitialized = TRUE;
/*N*/ }

// static

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
