/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"

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

#include "scitems.hxx"
#include <editeng/eeitem.hxx>

#include <sfx2/app.hxx>
#define _SVSTDARR_STRINGS
#include <editeng/boxitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/scripttypeitem.hxx>
#include <svl/srchitem.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/objitem.hxx>
#include <sfx2/viewfrm.hxx>
#include <svl/stritem.hxx>
#include <svl/zforlist.hxx>
#include <svl/svstdarr.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/sound.hxx>
#include <vcl/waitobj.hxx>

#include "viewfunc.hxx"

#include "sc.hrc"
#include "globstr.hrc"

#include "attrib.hxx"
#include "autoform.hxx"
#include "cell.hxx"					// EnterAutoSum
#include "compiler.hxx"
#include "docfunc.hxx"
#include "docpool.hxx"
#include "docsh.hxx"
#include "global.hxx"
#include "patattr.hxx"
#include "printfun.hxx"
#include "rangenam.hxx"
#include "rangeutl.hxx"
#include "refundo.hxx"
#include "tablink.hxx"
#include "tabvwsh.hxx"
#include "uiitems.hxx"
#include "undoblk.hxx"
#include "undocell.hxx"
#include "undotab.hxx"
#include "sizedev.hxx"
#include "editable.hxx"
#include "scmod.hxx"
#include "inputhdl.hxx"
#include "inputwin.hxx"
#include "funcdesc.hxx"
#include "docuno.hxx"
#include "charthelper.hxx"
#include "tabbgcolor.hxx"

#include <basic/sbstar.hxx>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
using namespace com::sun::star;

// helper func defined in docfunc.cxx
void VBA_DeleteModule( ScDocShell& rDocSh, String& sModuleName );

// STATIC DATA ---------------------------------------------------------------


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

sal_Bool ScViewFunc::AdjustBlockHeight( sal_Bool bPaint, ScMarkData* pMarkData )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	if (!pMarkData)
		pMarkData = &GetViewData()->GetMarkData();

	ScDocument* pDoc = pDocSh->GetDocument();
	SCCOLROW* pRanges = new SCCOLROW[MAXCOLROWCOUNT];
	SCCOLROW nRangeCnt = pMarkData->GetMarkRowRanges( pRanges );
	if (nRangeCnt == 0)
	{
		pRanges[0] = pRanges[1] = GetViewData()->GetCurY();
		nRangeCnt = 1;
	}

	double nPPTX = GetViewData()->GetPPTX();
	double nPPTY = GetViewData()->GetPPTY();
	Fraction aZoomX = GetViewData()->GetZoomX();
	Fraction aZoomY = GetViewData()->GetZoomY();

	ScSizeDeviceProvider aProv(pDocSh);
	if (aProv.IsPrinter())
	{
		nPPTX = aProv.GetPPTX();
		nPPTY = aProv.GetPPTY();
		aZoomX = aZoomY = Fraction( 1, 1 );
	}

	sal_Bool bAnyChanged = sal_False;
	SCTAB nTabCount = pDoc->GetTableCount();
	for (SCTAB nTab=0; nTab<nTabCount; nTab++)
	{
		if (pMarkData->GetTableSelect(nTab))
		{
			SCCOLROW* pOneRange = pRanges;
			sal_Bool bChanged = sal_False;
            SCROW nPaintY = 0;
			for (SCROW nRangeNo=0; nRangeNo<nRangeCnt; nRangeNo++)
			{
				SCROW nStartNo = *(pOneRange++);
				SCROW nEndNo = *(pOneRange++);
				if (pDoc->SetOptimalHeight( nStartNo, nEndNo, nTab, 0, aProv.GetDevice(),
											nPPTX, nPPTY, aZoomX, aZoomY, sal_False ))
				{
					if (!bChanged)
						nPaintY = nStartNo;
					bAnyChanged = bChanged = sal_True;
				}
			}
			if ( bPaint && bChanged )
				pDocSh->PostPaint( 0, nPaintY, nTab, MAXCOL, MAXROW, nTab,
													PAINT_GRID | PAINT_LEFT );
		}
	}
	delete[] pRanges;

	if ( bPaint && bAnyChanged )
		pDocSh->UpdateOle(GetViewData());

	return bAnyChanged;
}


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

sal_Bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, sal_Bool bPaint )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	SCTAB nTab = GetViewData()->GetTabNo();
	double nPPTX = GetViewData()->GetPPTX();
	double nPPTY = GetViewData()->GetPPTY();
	Fraction aZoomX = GetViewData()->GetZoomX();
	Fraction aZoomY = GetViewData()->GetZoomY();
    sal_uInt16 nOldPixel = 0;
	if (nStartRow == nEndRow)
		nOldPixel = (sal_uInt16) (pDoc->GetRowHeight(nStartRow,nTab) * nPPTY);

	ScSizeDeviceProvider aProv(pDocSh);
	if (aProv.IsPrinter())
	{
		nPPTX = aProv.GetPPTX();
		nPPTY = aProv.GetPPTY();
		aZoomX = aZoomY = Fraction( 1, 1 );
	}
	sal_Bool bChanged = pDoc->SetOptimalHeight( nStartRow, nEndRow, nTab, 0, aProv.GetDevice(),
											nPPTX, nPPTY, aZoomX, aZoomY, sal_False );

	if (bChanged && ( nStartRow == nEndRow ))
	{
		sal_uInt16 nNewPixel = (sal_uInt16) (pDoc->GetRowHeight(nStartRow,nTab) * nPPTY);
		if ( nNewPixel == nOldPixel )
			bChanged = sal_False;
	}

	if ( bPaint && bChanged )
		pDocSh->PostPaint( 0, nStartRow, nTab, MAXCOL, MAXROW, nTab,
											PAINT_GRID | PAINT_LEFT );

	return bChanged;
}


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

enum ScAutoSum
{
	ScAutoSumNone = 0,
	ScAutoSumData,
	ScAutoSumSum
};


ScAutoSum lcl_IsAutoSumData( ScDocument* pDoc, SCCOL nCol, SCROW nRow,
		SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
{
	ScBaseCell* pCell;
	pDoc->GetCell( nCol, nRow, nTab, pCell );
	if ( pCell && pCell->HasValueData() )
	{
		if ( pCell->GetCellType() == CELLTYPE_FORMULA )
		{
			ScTokenArray* pCode = ((ScFormulaCell*)pCell)->GetCode();
			if ( pCode && pCode->GetOuterFuncOpCode() == ocSum )
			{
				if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend,
						ScAddress( nCol, nRow, nTab ), eDir ) )
					return ScAutoSumSum;
			}
		}
		return ScAutoSumData;
	}
	return ScAutoSumNone;
}


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

#define SC_AUTOSUM_MAXCOUNT		20

ScAutoSum lcl_SeekAutoSumData( ScDocument* pDoc, SCCOL& nCol, SCROW& nRow,
		SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
{
	sal_uInt16 nCount = 0;
	while (nCount < SC_AUTOSUM_MAXCOUNT)
	{
		if ( eDir == DIR_TOP )
		{
			if (nRow > 0)
				--nRow;
			else
				return ScAutoSumNone;
		}
		else
		{
			if (nCol > 0)
				--nCol;
			else
				return ScAutoSumNone;
		}
		ScAutoSum eSum;
		if ( (eSum = lcl_IsAutoSumData(
				pDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone )
			return eSum;
		++nCount;
	}
	return ScAutoSumNone;
}

#undef SC_AUTOSUM_MAXCOUNT

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

bool lcl_FindNextSumEntryInColumn( ScDocument* pDoc, SCCOL nCol, SCROW& nRow,
                                   SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow )
{
    const SCROW nTmp = nRow;
    ScAutoSum eSkip = ScAutoSumNone;
    while ( ( eSkip = lcl_IsAutoSumData( pDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) ) == ScAutoSumData &&
            nRow > nMinRow )
    {
        --nRow;
    }
    if ( eSkip == ScAutoSumSum && nRow < nTmp )
    {
        return true;
    }
    return false;
}

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

bool lcl_FindNextSumEntryInRow( ScDocument* pDoc, SCCOL& nCol, SCROW nRow,
                                SCTAB nTab, SCCOLROW& nExtend, SCROW nMinCol )
{
    const SCCOL nTmp = nCol;
    ScAutoSum eSkip = ScAutoSumNone;
    while ( ( eSkip = lcl_IsAutoSumData( pDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) ) == ScAutoSumData &&
            nCol > nMinCol )
    {
        --nCol;
    }
    if ( eSkip == ScAutoSumSum && nCol < nTmp )
    {
        return true;
    }
    return false;
}

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

bool lcl_GetAutoSumForColumnRange( ScDocument* pDoc, ScRangeList& rRangeList, const ScRange& rRange )
{
    const ScAddress aStart = rRange.aStart;
    const ScAddress aEnd = rRange.aEnd;
    if ( aStart.Col() != aEnd.Col() )
    {
        return false;
    }

    const SCTAB nTab = aEnd.Tab();
    const SCCOL nCol = aEnd.Col();
    SCROW nEndRow = aEnd.Row();
    SCROW nStartRow = nEndRow;
    SCCOLROW nExtend = 0;
    const ScAutoSum eSum = lcl_IsAutoSumData( pDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ );

    if ( eSum == ScAutoSumSum )
    {
        bool bContinue = false;
        do
        {
            rRangeList.Append( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
            nEndRow = static_cast< SCROW >( nExtend );
            if ( ( bContinue = lcl_FindNextSumEntryInColumn( pDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() ) ) == true )
            {
                nStartRow = nEndRow;
            }
        } while ( bContinue );
    }
    else
    {
        while ( nStartRow > aStart.Row() &&
                lcl_IsAutoSumData( pDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) != ScAutoSumSum )
        {
            --nStartRow;
        }
        rRangeList.Append( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
    }

    return true;
}

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

bool lcl_GetAutoSumForRowRange( ScDocument* pDoc, ScRangeList& rRangeList, const ScRange& rRange )
{
    const ScAddress aStart = rRange.aStart;
    const ScAddress aEnd = rRange.aEnd;
    if ( aStart.Row() != aEnd.Row() )
    {
        return false;
    }

    const SCTAB nTab = aEnd.Tab();
    const SCROW nRow = aEnd.Row();
    SCCOL nEndCol = aEnd.Col();
    SCCOL nStartCol = nEndCol;
    SCCOLROW nExtend = 0;
    const ScAutoSum eSum = lcl_IsAutoSumData( pDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ );

    if ( eSum == ScAutoSumSum )
    {
        bool bContinue = false;
        do
        {
            rRangeList.Append( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
            nEndCol = static_cast< SCCOL >( nExtend );
            if ( ( bContinue = lcl_FindNextSumEntryInRow( pDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() ) ) == true )
            {
                nStartCol = nEndCol;
            }
        } while ( bContinue );
    }
    else
    {
        while ( nStartCol > aStart.Col() &&
                lcl_IsAutoSumData( pDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ ) != ScAutoSumSum )
        {
            --nStartCol;
        }
        rRangeList.Append( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
    }

    return true;
}

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

sal_Bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList )
{
	ScDocument* pDoc = GetViewData()->GetDocument();
	SCTAB nTab = GetViewData()->GetTabNo();

	SCCOL nCol = GetViewData()->GetCurX();
	SCROW nRow = GetViewData()->GetCurY();

	SCCOL nStartCol = nCol;
	SCROW nStartRow = nRow;
	SCCOL nEndCol	 = nCol;
	SCROW nEndRow	 = nRow;
	SCCOL nSeekCol	 = nCol;
	SCROW nSeekRow	 = nRow;
	SCCOLROW nExtend;		// wird per Reference gueltig bei ScAutoSumSum

	sal_Bool bCol = sal_False;
	sal_Bool bRow = sal_False;

	ScAutoSum eSum;
	if ( nRow != 0
			&& ((eSum = lcl_IsAutoSumData( pDoc, nCol, nRow-1, nTab,
				DIR_TOP, nExtend /*out*/ )) == ScAutoSumData )
			&& ((eSum = lcl_IsAutoSumData( pDoc, nCol, nRow-1, nTab,
				DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
		)
	{
		bRow = sal_True;
		nSeekRow = nRow - 1;
	}
	else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( pDoc, nCol-1, nRow, nTab,
			DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
	{
		bCol = sal_True;
		nSeekCol = nCol - 1;
	}
    else if ( (eSum = lcl_SeekAutoSumData( pDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone )
		bRow = sal_True;
    else if (( eSum = lcl_SeekAutoSumData( pDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone )
		bCol = sal_True;

	if ( bCol || bRow )
	{
		if ( bRow )
		{
			nStartRow = nSeekRow;		// nSeekRow evtl. per Reference angepasst
			if ( eSum == ScAutoSumSum )
				nEndRow = nStartRow;		// nur Summen summieren
			else
				nEndRow = nRow - 1;		// Datenbereich evtl. nach unten erweitern
		}
		else
		{
			nStartCol = nSeekCol;		// nSeekCol evtl. per Reference angepasst
			if ( eSum == ScAutoSumSum )
				nEndCol = nStartCol;		// nur Summen summieren
			else
				nEndCol = nCol - 1;		// Datenbereich evtl. nach rechts erweitern
		}
		sal_Bool bContinue = sal_False;
		do
		{
			if ( eSum == ScAutoSumData )
			{
				if ( bRow )
				{
					while ( nStartRow != 0 && lcl_IsAutoSumData(	pDoc, nCol,
							nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum )
						--nStartRow;
				}
				else
				{
					while ( nStartCol != 0 && lcl_IsAutoSumData( pDoc, nStartCol-1,
							nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum )
						--nStartCol;
				}
			}
			rRangeList.Append(
				ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) );
			if ( eSum == ScAutoSumSum )
			{
				if ( bRow )
				{
                    nEndRow = static_cast< SCROW >( nExtend );
                    if ( ( bContinue = lcl_FindNextSumEntryInColumn( pDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 ) ) == true )
                    {
                        nStartRow = nEndRow;
                    }
				}
				else
				{
                    nEndCol = static_cast< SCCOL >( nExtend );
                    if ( ( bContinue = lcl_FindNextSumEntryInRow( pDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 ) ) == true )
                    {
                        nStartCol = nEndCol;
                    }
				}
			}
		} while ( bContinue );
		return sal_True;
	}
	return sal_False;
}

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

void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, sal_Bool bSubTotal)		// Block mit Summen fuellen
{
    String aFormula = GetAutoSumFormula( rRangeList, bSubTotal );
	EnterBlock( aFormula, NULL );
}

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

bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue )
{
	ScDocument* pDoc = GetViewData()->GetDocument();
    const SCTAB nTab = rRange.aStart.Tab();
    SCCOL nStartCol = rRange.aStart.Col();
    SCROW nStartRow = rRange.aStart.Row();
    const SCCOL nEndCol = rRange.aEnd.Col();
    const SCROW nEndRow = rRange.aEnd.Row();
    SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData

    // ignore rows at the top of the given range which don't contain autosum data
    bool bRowData = false;
    for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
    {
        for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
        {
            if ( lcl_IsAutoSumData( pDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone )
            {
                bRowData = true;
                break;
            }
        }
        if ( bRowData )
        {
            nStartRow = nRow;
            break;
        }
    }
    if ( !bRowData )
    {
        return false;
    }

    // ignore columns at the left of the given range which don't contain autosum data
    bool bColData = false;
    for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
    {
        for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
        {
            if ( lcl_IsAutoSumData( pDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone )
            {
                bColData = true;
                break;
            }
        }
        if ( bColData )
        {
            nStartCol = nCol;
            break;
        }
    }
    if ( !bColData )
    {
        return false;
    }

    const bool bEndRowEmpty = pDoc->IsBlockEmpty( nTab, nStartCol, nEndRow, nEndCol, nEndRow );
    const bool bEndColEmpty = pDoc->IsBlockEmpty( nTab, nEndCol, nStartRow, nEndCol, nEndRow );
    bool bRow = ( ( nStartRow != nEndRow ) && ( bEndRowEmpty || ( !bEndRowEmpty && !bEndColEmpty ) ) );
    bool bCol = ( ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow ) );

    // find an empty row for entering the result
    SCROW nInsRow = nEndRow;
    if ( bRow && !bEndRowEmpty )
    {
        if ( nInsRow < MAXROW )
        {
            ++nInsRow;
            while ( !pDoc->IsBlockEmpty( nTab, nStartCol, nInsRow, nEndCol, nInsRow ) )
            {
                if ( nInsRow < MAXROW )
                {
                    ++nInsRow;
                }
                else
                {
                    bRow = false;
                    break;
                }
            }
        }
        else
        {
            bRow = false;
        }
    }

    // find an empty column for entering the result
    SCCOL nInsCol = nEndCol;
    if ( bCol && !bEndColEmpty )
    {
        if ( nInsCol < MAXCOL )
        {
            ++nInsCol;
            while ( !pDoc->IsBlockEmpty( nTab, nInsCol, nStartRow, nInsCol, nEndRow ) )
            {
                if ( nInsCol < MAXCOL )
                {
                    ++nInsCol;
                }
                else
                {
                    bCol = false;
                    break;
                }
            }
        }
        else
        {
            bCol = false;
        }
    }

    if ( !bRow && !bCol )
    {
        return false;
    }

    SCCOL nMarkEndCol = nEndCol;
    SCROW nMarkEndRow = nEndRow;

    if ( bRow )
    {
        // calculate the row sums for all columns of the given range

        SCROW nSumEndRow = nEndRow;

        if ( bEndRowEmpty )
        {
            // the last row of the given range is empty;
            // don't take into account for calculating the autosum
            --nSumEndRow;
        }
        else
        {
            // increase mark range
            ++nMarkEndRow;
        }

        for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
        {
            if ( !pDoc->IsBlockEmpty( nTab, nCol, nStartRow, nCol, nSumEndRow ) )
            {
                ScRangeList aRangeList;
                const ScRange aRange( nCol, nStartRow, nTab, nCol, nSumEndRow, nTab );
                if ( lcl_GetAutoSumForColumnRange( pDoc, aRangeList, aRange ) )
                {
                    const String aFormula = GetAutoSumFormula( aRangeList, bSubTotal );
                    EnterData( nCol, nInsRow, nTab, aFormula );
                }
            }
        }
    }

    if ( bCol )
    {
        // calculate the column sums for all rows of the given range

        SCCOL nSumEndCol = nEndCol;

        if ( bEndColEmpty )
        {
            // the last column of the given range is empty;
            // don't take into account for calculating the autosum
            --nSumEndCol;
        }
        else
        {
            // increase mark range
            ++nMarkEndCol;
        }

        for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
        {
            if ( !pDoc->IsBlockEmpty( nTab, nStartCol, nRow, nSumEndCol, nRow ) )
            {
                ScRangeList aRangeList;
                const ScRange aRange( nStartCol, nRow, nTab, nSumEndCol, nRow, nTab );
                if ( lcl_GetAutoSumForRowRange( pDoc, aRangeList, aRange ) )
                {
                    const String aFormula = GetAutoSumFormula( aRangeList, bSubTotal );
                    EnterData( nInsCol, nRow, nTab, aFormula );
                }
            }
        }
    }

    // set new mark range and cursor position
    const ScRange aMarkRange( nStartCol, nStartRow, nTab, nMarkEndCol, nMarkEndRow, nTab );
    MarkRange( aMarkRange, sal_False, bContinue );
    if ( bSetCursor )
    {
        SetCursor( nMarkEndCol, nMarkEndRow );
    }

    return true;
}

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

String ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal )
{
    String aFormula = '=';
    ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
    const ScFuncDesc* pDesc = NULL;
    if ( bSubTotal )
    {
        pDesc = pFuncMgr->Get( SC_OPCODE_SUB_TOTAL );
    }
    else
    {
        pDesc = pFuncMgr->Get( SC_OPCODE_SUM );
    }
    if ( pDesc && pDesc->pFuncName )
    {
        aFormula += *pDesc->pFuncName;
        if ( bSubTotal )
        {
            aFormula.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "(9;" ) );
        }
        else
        {
            aFormula += '(';
        }
        ScDocument* pDoc = GetViewData()->GetDocument();
        String aRef;
        rRangeList.Format( aRef, SCA_VALID, pDoc );
        aFormula += aRef;
        aFormula += ')';
    }
    return aFormula;
}

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

void ScViewFunc::EnterBlock( const String& rString, const EditTextObject* pData )
{
	//	Mehrfachselektion vorher abfragen...

	SCCOL nCol = GetViewData()->GetCurX();
	SCROW nRow = GetViewData()->GetCurY();
	SCTAB nTab = GetViewData()->GetTabNo();
	ScMarkData& rMark = GetViewData()->GetMarkData();
	if ( rMark.IsMultiMarked() )
	{
		rMark.MarkToSimple();
		if ( rMark.IsMultiMarked() )
		{		// "Einfuegen auf Mehrfachselektion nicht moeglich"
			ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);

			//	insert into single cell
			if ( pData )
				EnterData( nCol, nRow, nTab, pData );
			else
				EnterData( nCol, nRow, nTab, rString );
			return;
		}
	}

	ScDocument* pDoc = GetViewData()->GetDocument();
	String aNewStr = rString;
	if ( pData )
	{
		const ScPatternAttr* pOldPattern = pDoc->GetPattern( nCol, nRow, nTab );
		ScTabEditEngine aEngine( *pOldPattern, pDoc->GetEnginePool() );
		aEngine.SetText(*pData);

		ScEditAttrTester aTester( &aEngine );
		if (!aTester.NeedsObject())
		{
			aNewStr = aEngine.GetText();
			pData = NULL;
		}
	}

	//	Einfuegen per PasteFromClip

	WaitObject aWait( GetFrameWin() );

	ScAddress aPos( nCol, nRow, nTab );

	ScDocument* pInsDoc = new ScDocument( SCDOCMODE_CLIP );
	pInsDoc->ResetClip( pDoc, nTab );

	if (aNewStr.GetChar(0) == '=')						// Formel ?
	{
		//	SetString geht nicht, weil in Clipboard-Dokumenten nicht kompiliert wird!
		ScFormulaCell* pFCell = new ScFormulaCell( pDoc, aPos, aNewStr );
		pInsDoc->PutCell( nCol, nRow, nTab, pFCell );
	}
	else if ( pData )
		pInsDoc->PutCell( nCol, nRow, nTab, new ScEditCell( pData, pDoc, NULL ) );
	else
		pInsDoc->SetString( nCol, nRow, nTab, aNewStr );

	pInsDoc->SetClipArea( ScRange(aPos) );
	// auf Block einfuegen, mit Undo etc.
	if ( PasteFromClip( IDF_CONTENTS, pInsDoc, PASTE_NOFUNC, sal_False, sal_False,
			sal_False, INS_NONE, IDF_ATTRIB ) )
	{
		const SfxUInt32Item* pItem = (SfxUInt32Item*) pInsDoc->GetAttr(
			nCol, nRow, nTab, ATTR_VALUE_FORMAT );
		if ( pItem )
		{	// Numberformat setzen wenn inkompatibel
			// MarkData wurde bereits in PasteFromClip MarkToSimple'ed
			ScRange aRange;
			rMark.GetMarkArea( aRange );
			ScPatternAttr* pPattern = new ScPatternAttr( pDoc->GetPool() );
			pPattern->GetItemSet().Put( *pItem );
			short nNewType = pDoc->GetFormatTable()->GetType( pItem->GetValue() );
			pDoc->ApplyPatternIfNumberformatIncompatible( aRange, rMark,
				*pPattern, nNewType );
			delete pPattern;
		}
	}

	delete pInsDoc;
}


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

//UNUSED2008-05  void ScViewFunc::PaintWidthHeight( sal_Bool bColumns, SCCOLROW nStart, SCCOLROW nEnd )
//UNUSED2008-05  {
//UNUSED2008-05      SCTAB nTab = GetViewData()->GetTabNo();
//UNUSED2008-05      ScDocument* pDoc = GetViewData()->GetDocument();
//UNUSED2008-05
//UNUSED2008-05      sal_uInt16 nParts = PAINT_GRID;
//UNUSED2008-05      SCCOL nStartCol = 0;
//UNUSED2008-05      SCROW nStartRow = 0;
//UNUSED2008-05      SCCOL nEndCol = MAXCOL;         // fuer Test auf Merge
//UNUSED2008-05      SCROW nEndRow = MAXROW;
//UNUSED2008-05      if ( bColumns )
//UNUSED2008-05      {
//UNUSED2008-05          nParts |= PAINT_TOP;
//UNUSED2008-05          nStartCol = static_cast<SCCOL>(nStart);
//UNUSED2008-05          nEndCol = static_cast<SCCOL>(nEnd);
//UNUSED2008-05      }
//UNUSED2008-05      else
//UNUSED2008-05      {
//UNUSED2008-05          nParts |= PAINT_LEFT;
//UNUSED2008-05          nStartRow = nStart;
//UNUSED2008-05          nEndRow = nEnd;
//UNUSED2008-05      }
//UNUSED2008-05      if (pDoc->HasAttrib( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
//UNUSED2008-05                           HASATTR_MERGED | HASATTR_OVERLAPPED ))
//UNUSED2008-05      {
//UNUSED2008-05          nStartCol = 0;
//UNUSED2008-05          nStartRow = 0;
//UNUSED2008-05      }
//UNUSED2008-05      GetViewData()->GetDocShell()->PostPaint( nStartCol,nStartRow,nTab, MAXCOL,MAXROW,nTab, nParts );
//UNUSED2008-05  }


//----------------------------------------------------------------------------
//	manueller Seitenumbruch

void ScViewFunc::InsertPageBreak( sal_Bool bColumn, sal_Bool bRecord, const ScAddress* pPos,
									sal_Bool bSetModified )
{
	SCTAB nTab = GetViewData()->GetTabNo();
	ScAddress aCursor;
	if (pPos)
		aCursor = *pPos;
	else
		aCursor = ScAddress( GetViewData()->GetCurX(), GetViewData()->GetCurY(), nTab );

	sal_Bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().
						InsertPageBreak( bColumn, aCursor, bRecord, bSetModified, sal_False );

	if ( bSuccess && bSetModified )
		UpdatePageBreakData( sal_True );	// fuer PageBreak-Modus
}


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

void ScViewFunc::DeletePageBreak( sal_Bool bColumn, sal_Bool bRecord, const ScAddress* pPos,
									sal_Bool bSetModified )
{
	SCTAB nTab = GetViewData()->GetTabNo();
	ScAddress aCursor;
	if (pPos)
		aCursor = *pPos;
	else
		aCursor = ScAddress( GetViewData()->GetCurX(), GetViewData()->GetCurY(), nTab );

	sal_Bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().
						RemovePageBreak( bColumn, aCursor, bRecord, bSetModified, sal_False );

	if ( bSuccess && bSetModified )
		UpdatePageBreakData( sal_True );	// fuer PageBreak-Modus
}

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

void ScViewFunc::RemoveManualBreaks()
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	SCTAB nTab = GetViewData()->GetTabNo();
	sal_Bool bUndo(pDoc->IsUndoEnabled());

	if (bUndo)
	{
		ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
		pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
		pDoc->CopyToDocument( 0,0,nTab, MAXCOL,MAXROW,nTab, IDF_NONE, sal_False, pUndoDoc );
		pDocSh->GetUndoManager()->AddUndoAction(
								new ScUndoRemoveBreaks( pDocSh, nTab, pUndoDoc ) );
	}

	pDoc->RemoveManualBreaks(nTab);
	pDoc->UpdatePageBreaks(nTab);

	UpdatePageBreakData( sal_True );
	pDocSh->SetDocumentModified();
	pDocSh->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab, PAINT_GRID );
}

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

void ScViewFunc::SetPrintZoom(sal_uInt16 nScale, sal_uInt16 nPages)
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	SCTAB nTab = GetViewData()->GetTabNo();
	pDocSh->SetPrintZoom( nTab, nScale, nPages );
}

void ScViewFunc::AdjustPrintZoom()
{
	ScRange aRange;
	if ( GetViewData()->GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
		GetViewData()->GetMarkData().GetMultiMarkArea( aRange );
	GetViewData()->GetDocShell()->AdjustPrintZoom( aRange );
}

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

void ScViewFunc::SetPrintRanges( sal_Bool bEntireSheet, const String* pPrint,
								const String* pRepCol, const String* pRepRow,
								sal_Bool bAddPrint )
{
	//	on all selected tables

	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	ScDocument* pDoc	= pDocSh->GetDocument();
	SCTAB nTabCount	= pDoc->GetTableCount();
	ScMarkData& rMark	= GetViewData()->GetMarkData();
	SCTAB nTab;
	sal_Bool bUndo (pDoc->IsUndoEnabled());

	ScPrintRangeSaver* pOldRanges = pDoc->CreatePrintRangeSaver();

    ScAddress::Details aDetails(pDoc->GetAddressConvention(), 0, 0);

	for (nTab=0; nTab<nTabCount; nTab++)
		if (rMark.GetTableSelect(nTab))
		{
			ScRange aRange( 0,0,nTab );

			//	print ranges

            if( !bAddPrint )
                pDoc->ClearPrintRanges( nTab );

            if( bEntireSheet )
            {
                pDoc->SetPrintEntireSheet( nTab );
            }
            else if ( pPrint )
			{
                if ( pPrint->Len() )
				{
                    const sal_Unicode sep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0);
                    sal_uInt16 nTCount = pPrint->GetTokenCount(sep);
					for (sal_uInt16 i=0; i<nTCount; i++)
					{
						String aToken = pPrint->GetToken(i, sep);
						if ( aRange.ParseAny( aToken, pDoc, aDetails ) & SCA_VALID )
                            pDoc->AddPrintRange( nTab, aRange );
					}
				}
			}
            else    // NULL = use selection (print range is always set), use empty string to delete all ranges
			{
				if ( GetViewData()->GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
				{
                    pDoc->AddPrintRange( nTab, aRange );
				}
				else if ( rMark.IsMultiMarked() )
				{
					rMark.MarkToMulti();
					ScRangeListRef aList( new ScRangeList );
					rMark.FillRangeListWithMarks( aList, sal_False );
					sal_uInt16 nCnt = (sal_uInt16) aList->Count();
					if ( nCnt )
					{
						ScRangePtr pR;
						sal_uInt16 i;
						for ( pR = aList->First(), i=0; i < nCnt;
							  pR = aList->Next(), i++ )
						{
                            pDoc->AddPrintRange( nTab, *pR );
						}
					}
				}
			}

			//	repeat columns

			if ( pRepCol )
			{
				if ( !pRepCol->Len() )
					pDoc->SetRepeatColRange( nTab, NULL );
				else
					if ( aRange.ParseAny( *pRepCol, pDoc, aDetails ) & SCA_VALID )
						pDoc->SetRepeatColRange( nTab, &aRange );
			}

			//	repeat rows

			if ( pRepRow )
			{
				if ( !pRepRow->Len() )
					pDoc->SetRepeatRowRange( nTab, NULL );
				else
					if ( aRange.ParseAny( *pRepRow, pDoc, aDetails ) & SCA_VALID )
						pDoc->SetRepeatRowRange( nTab, &aRange );
			}
		}

	//	undo (for all tables)
	if (bUndo)
	{
		SCTAB nCurTab = GetViewData()->GetTabNo();
		ScPrintRangeSaver* pNewRanges = pDoc->CreatePrintRangeSaver();
		pDocSh->GetUndoManager()->AddUndoAction(
					new ScUndoPrintRange( pDocSh, nCurTab, pOldRanges, pNewRanges ) );
	}

	//	update page breaks

	for (nTab=0; nTab<nTabCount; nTab++)
		if (rMark.GetTableSelect(nTab))
			ScPrintFunc( pDocSh, pDocSh->GetPrinter(), nTab ).UpdatePages();

	SfxBindings& rBindings = GetViewData()->GetBindings();
	rBindings.Invalidate( SID_DELETE_PRINTAREA );

	pDocSh->SetDocumentModified();
}

//----------------------------------------------------------------------------
//	Zellen zusammenfassen

sal_Bool ScViewFunc::TestMergeCells()			// Vorab-Test (fuer Menue)
{
	//	simple test: sal_True if there's a selection but no multi selection and not filtered

	const ScMarkData& rMark = GetViewData()->GetMarkData();
	if ( rMark.IsMarked() || rMark.IsMultiMarked() )
	{
        ScRange aDummy;
        return GetViewData()->GetSimpleArea( aDummy) == SC_MARK_SIMPLE;
	}
	else
		return sal_False;
}


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

sal_Bool ScViewFunc::MergeCells( sal_Bool bApi, sal_Bool& rDoContents, sal_Bool bRecord )
{
	//	Editable- und Verschachtelungs-Abfrage muss vorneweg sein (auch in DocFunc),
	//	damit dann nicht die Inhalte-QueryBox kommt
	ScEditableTester aTester( this );
	if (!aTester.IsEditable())
	{
		ErrorMessage(aTester.GetMessageId());
		return sal_False;
	}

	ScMarkData& rMark = GetViewData()->GetMarkData();
	rMark.MarkToSimple();
	if (!rMark.IsMarked())
	{
		ErrorMessage(STR_NOMULTISELECT);
		return sal_False;
	}

	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();

	ScRange aMarkRange;
	rMark.GetMarkArea( aMarkRange );
	SCCOL nStartCol = aMarkRange.aStart.Col();
	SCROW nStartRow = aMarkRange.aStart.Row();
	SCTAB nStartTab = aMarkRange.aStart.Tab();
	SCCOL nEndCol = aMarkRange.aEnd.Col();
	SCROW nEndRow = aMarkRange.aEnd.Row();
	SCTAB nEndTab = aMarkRange.aEnd.Tab();
	if ( nStartCol == nEndCol && nStartRow == nEndRow )
	{
		// nichts zu tun
		return sal_True;
	}

	if ( pDoc->HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
							HASATTR_MERGED | HASATTR_OVERLAPPED ) )
	{		// "Zusammenfassen nicht verschachteln !"
		ErrorMessage(STR_MSSG_MERGECELLS_0);
		return sal_False;
	}

	sal_Bool bOk = sal_True;

	if ( !pDoc->IsBlockEmpty( nStartTab, nStartCol,nStartRow+1, nStartCol,nEndRow, true ) ||
		 !pDoc->IsBlockEmpty( nStartTab, nStartCol+1,nStartRow, nEndCol,nEndRow, true ) )
	{
		if (!bApi)
		{
			MessBox aBox( GetViewData()->GetDialogParent(),
							WinBits(WB_YES_NO_CANCEL | WB_DEF_NO),
							ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
							ScGlobal::GetRscString( STR_MERGE_NOTEMPTY ) );
			sal_uInt16 nRetVal = aBox.Execute();

			if ( nRetVal == RET_YES )
				rDoContents = sal_True;
			else if ( nRetVal == RET_CANCEL )
				bOk = sal_False;
		}
	}

	if (bOk)
	{
		HideCursor();
		bOk = pDocSh->GetDocFunc().MergeCells( aMarkRange, rDoContents, bRecord, bApi );
		ShowCursor();

		if (bOk)
		{
			SetCursor( nStartCol, nStartRow );
			//DoneBlockMode( sal_False);
			Unmark();

			pDocSh->UpdateOle(GetViewData());
			UpdateInputLine();
		}
	}

	return bOk;
}


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

sal_Bool ScViewFunc::TestRemoveMerge()
{
	sal_Bool bMerged = sal_False;
	ScRange aRange;
	if (GetViewData()->GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
	{
		ScDocument* pDoc = GetViewData()->GetDocument();
		if ( pDoc->HasAttrib( aRange, HASATTR_MERGED ) )
			bMerged = sal_True;
	}
	return bMerged;
}


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

sal_Bool ScViewFunc::RemoveMerge( sal_Bool bRecord )
{
	ScRange aRange;
    ScEditableTester aTester( this );
    if (!aTester.IsEditable())
    {
        ErrorMessage(aTester.GetMessageId());
        return sal_False;
    }
	else if (GetViewData()->GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
	{
        ScRange aExtended( aRange );
        GetViewData()->GetDocument()->ExtendMerge( aExtended );
		ScDocShell* pDocSh = GetViewData()->GetDocShell();

		HideCursor();
		sal_Bool bOk = pDocSh->GetDocFunc().UnmergeCells( aRange, bRecord, sal_False );
        MarkRange( aExtended );
		ShowCursor();

		if (bOk)
			pDocSh->UpdateOle(GetViewData());
	}
	return sal_True;		//! bOk ??
}

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

void ScViewFunc::FillSimple( FillDir eDir, sal_Bool bRecord )
{
	ScRange aRange;
	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
	{
		ScDocShell* pDocSh = GetViewData()->GetDocShell();
		const ScMarkData& rMark = GetViewData()->GetMarkData();
		sal_Bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, bRecord, sal_False );
		if (bSuccess)
		{
			pDocSh->UpdateOle(GetViewData());
			UpdateScrollBars();
		}
	}
	else
		ErrorMessage(STR_NOMULTISELECT);
}

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

void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
							 double fStart, double fStep, double fMax, sal_Bool bRecord )
{
	ScRange aRange;
	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
	{
		ScDocShell* pDocSh = GetViewData()->GetDocShell();
		const ScMarkData& rMark = GetViewData()->GetMarkData();
		sal_Bool bSuccess = pDocSh->GetDocFunc().
						FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd,
									fStart, fStep, fMax, bRecord, sal_False );
		if (bSuccess)
		{
			pDocSh->UpdateOle(GetViewData());
			UpdateScrollBars();

            // #i97876# Spreadsheet data changes are not notified
            ScModelObj* pModelObj = ScModelObj::getImplementation( pDocSh->GetModel() );
            if ( pModelObj && pModelObj->HasChangesListeners() )
            {
                ScRangeList aChangeRanges;
                aChangeRanges.Append( aRange );
                pModelObj->NotifyChanges( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "cell-change" ) ), aChangeRanges );
            }
		}
	}
	else
		ErrorMessage(STR_NOMULTISELECT);
}

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

void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
							SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount, sal_Bool bRecord )
{
	SCTAB nTab = GetViewData()->GetTabNo();
	ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab );
    ScRange aSourceRange( aRange );
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	const ScMarkData& rMark = GetViewData()->GetMarkData();
	sal_Bool bSuccess = pDocSh->GetDocFunc().
					FillAuto( aRange, &rMark, eDir, nCount, bRecord, sal_False );
	if (bSuccess)
	{
		MarkRange( aRange, sal_False );			// aRange ist in FillAuto veraendert worden
		pDocSh->UpdateOle(GetViewData());
		UpdateScrollBars();

        // #i97876# Spreadsheet data changes are not notified
        ScModelObj* pModelObj = ScModelObj::getImplementation( pDocSh->GetModel() );
        if ( pModelObj && pModelObj->HasChangesListeners() )
        {
            ScRangeList aChangeRanges;
            ScRange aChangeRange( aRange );
            switch ( eDir )
            {
                case FILL_TO_BOTTOM:
                    {
                        aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 );
                    }
                    break;
                case FILL_TO_TOP:
                    {
                        aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 );
                    }
                    break;
                case FILL_TO_RIGHT:
                    {
                        aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 );
                    }
                    break;
                case FILL_TO_LEFT:
                    {
                        aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 );
                    }
                    break;
                default:
                    {

                    }
                    break;
            }
            aChangeRanges.Append( aChangeRange );
            pModelObj->NotifyChanges( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "cell-change" ) ), aChangeRanges );
        }
	}
}

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

void ScViewFunc::FillTab( sal_uInt16 nFlags, sal_uInt16 nFunction, sal_Bool bSkipEmpty, sal_Bool bAsLink )
{
	//! allow source sheet to be protected
	ScEditableTester aTester( this );
	if (!aTester.IsEditable())
	{
		ErrorMessage(aTester.GetMessageId());
		return;
	}

	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	ScMarkData& rMark = GetViewData()->GetMarkData();
	SCTAB nTab = GetViewData()->GetTabNo();
	sal_Bool bUndo(pDoc->IsUndoEnabled());

	ScRange aMarkRange;
	rMark.MarkToSimple();
	sal_Bool bMulti = rMark.IsMultiMarked();
	if (bMulti)
		rMark.GetMultiMarkArea( aMarkRange );
	else if (rMark.IsMarked())
		rMark.GetMarkArea( aMarkRange );
	else
		aMarkRange = ScRange( GetViewData()->GetCurX(), GetViewData()->GetCurY(), nTab );

	ScDocument* pUndoDoc = NULL;
//	if ( bRecord )
	if (bUndo)
	{
		pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
		pUndoDoc->InitUndo( pDoc, nTab, nTab );
//		pUndoDoc->SelectTable( nTab, sal_True );		// nur fuer Markierung

		SCTAB nTabCount = pDoc->GetTableCount();
		for (SCTAB i=0; i<nTabCount; i++)
			if (i != nTab && rMark.GetTableSelect(i))
			{
				pUndoDoc->AddUndoTab( i, i );
				aMarkRange.aStart.SetTab( i );
				aMarkRange.aEnd.SetTab( i );
				pDoc->CopyToDocument( aMarkRange, IDF_ALL, bMulti, pUndoDoc );
//				pUndoDoc->SelectTable( i, sal_True );
			}
	}

	if (bMulti)
		pDoc->FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
	else
	{
		aMarkRange.aStart.SetTab( nTab );
		aMarkRange.aEnd.SetTab( nTab );
		pDoc->FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
	}

//	if ( bRecord )
	if (bUndo)
	{	//! fuer ChangeTrack erst zum Schluss
		pDocSh->GetUndoManager()->AddUndoAction(
			new ScUndoFillTable( pDocSh, rMark,
								aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab,
								aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab,
								pUndoDoc, bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) );
	}

	pDocSh->PostPaintGridAll();
	pDocSh->PostDataChanged();
}

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

/** Downward fill of selected cell(s) by double-clicking cross-hair cursor

	Extends a current selection down to the last non-empty cell of an adjacent
	column when the lower-right corner of the selection is double-clicked.  It
	uses a left-adjoining non-empty column as a guide if such is available,
	otherwise a right-adjoining non-empty column is used.

	@author Kohei Yoshida (kohei@openoffice.org)

	@return No return value

	@see #i12313#
*/
void ScViewFunc::FillCrossDblClick()
{
	ScRange aRange;
	GetViewData()->GetSimpleArea( aRange );
	aRange.Justify();

	SCTAB nTab = GetViewData()->GetCurPos().Tab();
	SCCOL nStartX = aRange.aStart.Col();
	SCROW nStartY = aRange.aStart.Row();
	SCCOL nEndX   = aRange.aEnd.Col();
	SCROW nEndY   = aRange.aEnd.Row();

	ScDocument* pDoc = GetViewData()->GetDocument();

	// Make sure the selection is not empty
	if ( pDoc->IsBlockEmpty( nTab, nStartX, nStartY, nEndX, nEndY ) )
		return;

	if ( nEndY < MAXROW )
	{
		if ( nStartX > 0 )
		{
			SCCOL nMovX = nStartX - 1;
			SCROW nMovY = nStartY;

			if ( pDoc->HasData( nMovX, nStartY, nTab ) &&
				 pDoc->HasData( nMovX, nStartY + 1, nTab ) )
			{
				pDoc->FindAreaPos( nMovX, nMovY, nTab, 0, 1 );

				if ( nMovY > nEndY )
				{
					FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY,
							  nMovY - nEndY );
					return;
				}
			}
		}

		if ( nEndX < MAXCOL )
		{
			SCCOL nMovX = nEndX + 1;
			SCROW nMovY = nStartY;

			if ( pDoc->HasData( nMovX, nStartY, nTab ) &&
				 pDoc->HasData( nMovX, nStartY + 1, nTab ) )
			{
				pDoc->FindAreaPos( nMovX, nMovY, nTab, 0, 1 );

				if ( nMovY > nEndY )
				{
					FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY,
							  nMovY - nEndY );
					return;
				}
			}
		}
	}
}

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

void ScViewFunc::TransliterateText( sal_Int32 nType )
{
	ScMarkData aFuncMark = GetViewData()->GetMarkData();
	if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
	{
		//	no selection -> use cursor position

		ScAddress aCursor( GetViewData()->GetCurX(), GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
		aFuncMark.SetMarkArea( ScRange( aCursor ) );
	}

	sal_Bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().
						TransliterateText( aFuncMark, nType, sal_True, sal_False );
	if (bSuccess)
	{
		GetViewData()->GetViewShell()->UpdateInputHandler();
	}
}

//----------------------------------------------------------------------------
//	AutoFormat

ScAutoFormatData* ScViewFunc::CreateAutoFormatData()
{
	ScAutoFormatData* pData = NULL;
	SCCOL nStartCol;
	SCROW nStartRow;
	SCTAB nStartTab;
	SCCOL nEndCol;
	SCROW nEndRow;
	SCTAB nEndTab;
	if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
	{
		if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 )
		{
			ScDocument* pDoc = GetViewData()->GetDocument();
			pData = new ScAutoFormatData;
			pDoc->GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData );
		}
	}
	return pData;
}


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

void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo, sal_Bool bRecord )
{
#if 1

	ScRange aRange;
	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
	{
		ScDocShell* pDocSh = GetViewData()->GetDocShell();
		ScMarkData& rMark = GetViewData()->GetMarkData();

		sal_Bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, bRecord, sal_False );
		if (bSuccess)
			pDocSh->UpdateOle(GetViewData());
	}
	else
		ErrorMessage(STR_NOMULTISELECT);

#else

	// nur wegen Matrix nicht editierbar? Attribute trotzdem ok
	sal_Bool bOnlyNotBecauseOfMatrix;
	if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
	{
		ErrorMessage(STR_PROTECTIONERR);
		return;
	}

	SCCOL nStartCol;
	SCROW nStartRow;
	SCTAB nStartTab;
	SCCOL nEndCol;
	SCROW nEndRow;
	SCTAB nEndTab;

	if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
	{
		ScDocShell* pDocSh = GetViewData()->GetDocShell();
		ScDocument* pDoc = pDocSh->GetDocument();
		ScMarkData& rMark = GetViewData()->GetMarkData();
		sal_Bool bSize = (*ScGlobal::GetAutoFormat())[nFormatNo]->GetIncludeWidthHeight();
		if (bRecord && !pDoc->IsUndoEnabled())
			bRecord = sal_False;

		ScDocument* pUndoDoc = NULL;
		if ( bRecord )
		{
			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
			pUndoDoc->InitUndo( pDoc, nStartTab, nEndTab, bSize, bSize );
			pDoc->CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
									IDF_ATTRIB, sal_False, pUndoDoc );
			if (bSize)
			{
				pDoc->CopyToDocument( nStartCol,0,nStartTab, nEndCol,MAXROW,nEndTab,
															IDF_NONE, sal_False, pUndoDoc );
				pDoc->CopyToDocument( 0,nStartRow,nStartTab, MAXCOL,nEndRow,nEndTab,
															IDF_NONE, sal_False, pUndoDoc );
			}
			pDoc->BeginDrawUndo();
		}

		GetFrameWin()->EnterWait();
		pDoc->AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, rMark );
		GetFrameWin()->LeaveWait();

		if (bSize)
		{
			SetMarkedWidthOrHeight( sal_True, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, sal_False, sal_False );
			SetMarkedWidthOrHeight( sal_False, SC_SIZE_VISOPT, 0, sal_False, sal_False );
			pDocSh->PostPaint( 0,0,nStartTab, MAXCOL,MAXROW,nStartTab,
									PAINT_GRID | PAINT_LEFT | PAINT_TOP );
		}
		else
		{
			sal_Bool bAdj = AdjustBlockHeight( sal_False );
			if (bAdj)
				pDocSh->PostPaint( 0,nStartRow,nStartTab, MAXCOL,MAXROW,nStartTab,
									PAINT_GRID | PAINT_LEFT );
			else
				pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
									nEndCol, nEndRow, nEndTab, PAINT_GRID );
		}

		if ( bRecord )		// Draw-Undo erst jetzt verfuegbar
		{
			pDocSh->GetUndoManager()->AddUndoAction(
				new ScUndoAutoFormat( pDocSh,
						ScRange(nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab),
						pUndoDoc, rMark, bSize, nFormatNo ) );
		}

		pDocSh->UpdateOle(GetViewData());
		pDocSh->SetDocumentModified();
	}
	else
		ErrorMessage(STR_NOMULTISELECT);

#endif
}


//----------------------------------------------------------------------------
//	Suchen & Ersetzen

void ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem,
										sal_Bool bAddUndo, sal_Bool bIsApi )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	ScMarkData& rMark = GetViewData()->GetMarkData();
	if (bAddUndo && !pDoc->IsUndoEnabled())
		bAddUndo = sal_False;

	SCCOL nCol = GetViewData()->GetCurX();
	SCROW nRow = GetViewData()->GetCurY();
	SCTAB nTab = GetViewData()->GetTabNo();
//    sal_Bool bAttrib = pSearchItem->GetPattern();
	sal_uInt16 nCommand = pSearchItem->GetCommand();
	sal_Bool bAllTables = pSearchItem->IsAllTables();
	sal_Bool* pOldSelectedTables = NULL;
	sal_uInt16 nOldSelectedCount = 0;
	SCTAB nOldTab = nTab;
	SCTAB nLastTab = pDoc->GetTableCount() - 1;
	SCTAB nStartTab, nEndTab;
	if ( bAllTables )
	{
		nStartTab = 0;
		nEndTab = nLastTab;
		pOldSelectedTables = new sal_Bool [ nEndTab + 1 ];
		for ( SCTAB j = 0; j <= nEndTab; j++ )
		{
			pOldSelectedTables[j] = rMark.GetTableSelect( j );
			if ( pOldSelectedTables[j] )
				++nOldSelectedCount;
		}
	}
	else
	{	//! mindestens eine ist immer selektiert
		nStartTab = nEndTab = rMark.GetFirstSelected();
		for ( SCTAB j = nStartTab + 1; j <= nLastTab; j++ )
		{
			if ( rMark.GetTableSelect( j ) )
				nEndTab = j;
		}
	}

	if (   nCommand == SVX_SEARCHCMD_REPLACE
		|| nCommand == SVX_SEARCHCMD_REPLACE_ALL )
	{
		for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
		{
			if ( (bAllTables || rMark.GetTableSelect( j )) &&
					pDoc->IsTabProtected( j ) )
			{
				if ( pOldSelectedTables )
					delete [] pOldSelectedTables;
				ErrorMessage(STR_PROTECTIONERR);
				return;
			}
		}
	}

	if (   nCommand == SVX_SEARCHCMD_FIND
		|| nCommand == SVX_SEARCHCMD_FIND_ALL)
		bAddUndo = sal_False;

	//!		bAttrib bei Undo beruecksichtigen !!!

	ScDocument* pUndoDoc = NULL;
	ScMarkData* pUndoMark = NULL;
	String aUndoStr;
	if (bAddUndo)
	{
		pUndoMark = new ScMarkData( rMark );				// Markierung wird veraendert
		if ( nCommand == SVX_SEARCHCMD_REPLACE_ALL )
		{
			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
			pUndoDoc->InitUndo( pDoc, nStartTab, nEndTab );
		}
	}

	if ( bAllTables )
	{	//! alles selektieren, erst nachdem pUndoMark erzeugt wurde
		for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
		{
			rMark.SelectTable( j, sal_True );
		}
	}

	DoneBlockMode(sal_True);				// Markierung nicht loeschen!
	InitOwnBlockMode();

	//	wenn vom Anfang an gesucht wird, nicht nochmal fragen ob vom Anfang gesucht werden soll
	sal_Bool bFirst = sal_True;
	if ( nCol == 0 && nRow == 0 && nTab == nStartTab && !pSearchItem->GetBackward()  )
		bFirst = sal_False;

	sal_Bool bFound = sal_False;
	while (sal_True)
	{
		GetFrameWin()->EnterWait();
		if (pDoc->SearchAndReplace( *pSearchItem, nCol, nRow, nTab, rMark, aUndoStr, pUndoDoc ) )
		{
			bFound = sal_True;
			bFirst = sal_True;
			if (bAddUndo)
			{
				GetViewData()->GetDocShell()->GetUndoManager()->AddUndoAction(
					new ScUndoReplace( GetViewData()->GetDocShell(), *pUndoMark,
										nCol, nRow, nTab,
										aUndoStr, pUndoDoc, pSearchItem ) );
				pUndoDoc = NULL;
			}

			break;					// Abbruch while True
		}
		else if ( bFirst && (nCommand == SVX_SEARCHCMD_FIND ||
				nCommand == SVX_SEARCHCMD_REPLACE) )
		{
			bFirst = sal_False;
			sal_uInt16 nRetVal;
			GetFrameWin()->LeaveWait();
			if ( bIsApi )
				nRetVal = RET_NO;
			else
			{
				//	Suchen-Dialog als Parent, wenn vorhanden
				Window* pParent = GetParentOrChild(SID_SEARCH_DLG);
				sal_uInt16 nStrId;
				if ( pSearchItem->GetBackward() )
				{
					if ( nStartTab == nEndTab )
						nStrId = STR_MSSG_SEARCHANDREPLACE_1;
					else
						nStrId = STR_MSSG_SEARCHANDREPLACE_4;
				}
				else
				{
					if ( nStartTab == nEndTab )
						nStrId = STR_MSSG_SEARCHANDREPLACE_2;
					else
						nStrId = STR_MSSG_SEARCHANDREPLACE_5;
				}
				MessBox aBox( pParent, WinBits(WB_YES_NO | WB_DEF_YES),
								ScGlobal::GetRscString( STR_MSSG_SEARCHANDREPLACE_3 ),
								ScGlobal::GetRscString( nStrId ) );
				nRetVal = aBox.Execute();
			}

			if ( nRetVal == RET_YES )
			{
				ScDocument::GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
				if (pSearchItem->GetBackward())
					nTab = nEndTab;
				else
					nTab = nStartTab;
			}
			else
			{
				break;					// Abbruch while True
			}
		}
		else							// nichts gefunden
		{
			if ( nCommand == SVX_SEARCHCMD_FIND_ALL || nCommand == SVX_SEARCHCMD_REPLACE_ALL )
			{
				pDocSh->PostPaintGridAll();								// Markierung
			}

			GetFrameWin()->LeaveWait();
			if (!bIsApi)
			{
				//	Suchen-Dialog als Parent, wenn vorhanden
				Window* pParent = GetParentOrChild(SID_SEARCH_DLG);
				// "nichts gefunden"
				InfoBox aBox( pParent, ScGlobal::GetRscString( STR_MSSG_SEARCHANDREPLACE_0 ) );
				aBox.Execute();
			}

			break;						// Abbruch while True
		}
	}								// of while sal_True

	if ( pOldSelectedTables )
	{	// urspruenglich selektierte Tabellen wiederherstellen
		for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
		{
			rMark.SelectTable( j, pOldSelectedTables[j] );
		}
		if ( bFound )
		{	// durch Fundstelle neu selektierte Tabelle bleibt
			rMark.SelectTable( nTab, sal_True );
			// wenn vorher nur eine selektiert war, ist es ein Tausch
			//! wenn nicht, ist jetzt evtl. eine mehr selektiert
			if ( nOldSelectedCount == 1 && nTab != nOldTab )
				rMark.SelectTable( nOldTab, sal_False );
		}
		delete [] pOldSelectedTables;
	}

    MarkDataChanged();

	if ( bFound )
	{
		if ( nTab != GetViewData()->GetTabNo() )
			SetTabNo( nTab );

		//	wenn nichts markiert ist, DoneBlockMode, damit von hier aus
		//	direkt per Shift-Cursor markiert werden kann:
		if (!rMark.IsMarked() && !rMark.IsMultiMarked())
			DoneBlockMode(sal_True);

		AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
		SetCursor( nCol, nRow, sal_True );

		if (   nCommand == SVX_SEARCHCMD_REPLACE
			|| nCommand == SVX_SEARCHCMD_REPLACE_ALL )
		{
			if ( nCommand == SVX_SEARCHCMD_REPLACE )
				pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PAINT_GRID );
			else
				pDocSh->PostPaintGridAll();
			pDocSh->SetDocumentModified();
		}
		else if ( nCommand == SVX_SEARCHCMD_FIND_ALL )
			pDocSh->PostPaintGridAll();								// Markierung
		GetFrameWin()->LeaveWait();
	}

	delete pUndoDoc;			// loeschen wenn nicht benutzt
	delete pUndoMark;			// kann immer geloescht werden
}


//----------------------------------------------------------------------------
//	Zielwertsuche

void ScViewFunc::Solve( const ScSolveParam& rParam )
{
	ScDocument* pDoc = GetViewData()->GetDocument();

	SCCOL nDestCol = rParam.aRefVariableCell.Col();
	SCROW nDestRow = rParam.aRefVariableCell.Row();
	SCTAB nDestTab = rParam.aRefVariableCell.Tab();

	ScEditableTester aTester( pDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow );
	if (!aTester.IsEditable())
	{
		ErrorMessage(aTester.GetMessageId());
		return;
	}

	if ( pDoc )
	{
		String  aTargetValStr;
		if ( rParam.pStrTargetVal != NULL )
			aTargetValStr = *(rParam.pStrTargetVal);

		String  aMsgStr;
		String	aResStr;
		double	nSolveResult;

		GetFrameWin()->EnterWait();

		sal_Bool  	bExact =
					pDoc->Solver(
						rParam.aRefFormulaCell.Col(),
						rParam.aRefFormulaCell.Row(),
						rParam.aRefFormulaCell.Tab(),
						nDestCol, nDestRow, nDestTab,
						aTargetValStr,
						nSolveResult );

		GetFrameWin()->LeaveWait();

		SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
		sal_uLong nFormat = 0;
		const ScPatternAttr* pPattern = pDoc->GetPattern( nDestCol, nDestRow, nDestTab );
		if ( pPattern )
			nFormat = pPattern->GetNumberFormat( pFormatter );
		Color* p;
		pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p );

		if ( bExact )
		{
			aMsgStr  = ScGlobal::GetRscString( STR_MSSG_SOLVE_0 );
			aMsgStr += ScGlobal::GetRscString( STR_MSSG_SOLVE_1 );
			aMsgStr += String( aResStr );
			aMsgStr += ScGlobal::GetRscString( STR_MSSG_SOLVE_2 );
		}
		else
		{
			aMsgStr  = ScGlobal::GetRscString( STR_MSSG_SOLVE_3 );
			aMsgStr += ScGlobal::GetRscString( STR_MSSG_SOLVE_4 );
			aMsgStr += ScGlobal::GetRscString( STR_MSSG_SOLVE_5 );
			aMsgStr += String( aResStr );
			aMsgStr += ScGlobal::GetRscString( STR_MSSG_SOLVE_6 );
		}

		MessBox aBox( GetViewData()->GetDialogParent(),
						WinBits(WB_YES_NO | WB_DEF_NO),
						ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ), aMsgStr );
		sal_uInt16 nRetVal = aBox.Execute();

		if ( RET_YES == nRetVal )
			EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult );

		GetViewData()->GetViewShell()->UpdateInputHandler( sal_True );
	}
}


//----------------------------------------------------------------------------
//	Mehrfachoperation

void ScViewFunc::TabOp( const ScTabOpParam& rParam, sal_Bool bRecord )
{
	ScRange aRange;
	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
	{
		ScDocShell* pDocSh = GetViewData()->GetDocShell();
		ScMarkData& rMark = GetViewData()->GetMarkData();
		pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, sal_False );
	}
	else
		ErrorMessage(STR_NOMULTISELECT);
}


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

void ScViewFunc::MakeScenario( const String& rName, const String& rComment,
									const Color& rColor, sal_uInt16 nFlags )
{
	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	ScMarkData&	rMark	= GetViewData()->GetMarkData();
	SCTAB		nTab	= GetViewData()->GetTabNo();

	SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark );
	if (nFlags & SC_SCENARIO_COPYALL)
		SetTabNo( nNewTab, sal_True );			// SC_SCENARIO_COPYALL -> sichtbar
	else
	{
		SfxBindings& rBindings = GetViewData()->GetBindings();
		rBindings.Invalidate( SID_STATUS_DOCPOS );		// Statusbar
		rBindings.Invalidate( SID_TABLES_COUNT );
		rBindings.Invalidate( SID_SELECT_SCENARIO );
		rBindings.Invalidate( FID_TABLE_SHOW );
	}
}


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

void ScViewFunc::ExtendScenario()
{
	ScEditableTester aTester( this );
	if (!aTester.IsEditable())
	{
		ErrorMessage(aTester.GetMessageId());
		return;
	}

		//	Undo: Attribute anwenden

	ScDocument* pDoc = GetViewData()->GetDocument();
	ScPatternAttr aPattern( pDoc->GetPool() );
	aPattern.GetItemSet().Put( ScMergeFlagAttr( SC_MF_SCENARIO ) );
	aPattern.GetItemSet().Put( ScProtectionAttr( sal_True ) );
	ApplySelectionPattern(aPattern);
}


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

void ScViewFunc::UseScenario( const String& rName )
{
	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	SCTAB		nTab	= GetViewData()->GetTabNo();

	DoneBlockMode();
	InitOwnBlockMode();
	pDocSh->UseScenario( nTab, rName );
}


//----------------------------------------------------------------------------
//	Tabelle einfuegen

sal_Bool ScViewFunc::InsertTable( const String& rName, SCTAB nTab, sal_Bool bRecord )
{
	//	Reihenfolge Tabelle/Name ist bei DocFunc umgekehrt
	sal_Bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().
						InsertTable( nTab, rName, bRecord, sal_False );
	if (bSuccess)
		SetTabNo( nTab, sal_True );

	return bSuccess;
}

//----------------------------------------------------------------------------
//	Tabellen einfuegen

sal_Bool ScViewFunc::InsertTables(SvStrings *pNames, SCTAB nTab,
											SCTAB nCount, sal_Bool bRecord )
{
	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	ScDocument* pDoc 	= pDocSh->GetDocument();
	if (bRecord && !pDoc->IsUndoEnabled())
		bRecord = sal_False;

	SvStrings *pNameList= NULL;

	WaitObject aWait( GetFrameWin() );

	if (bRecord)
	{
		pNameList= new SvStrings;
		pDoc->BeginDrawUndo();							//	InsertTab erzeugt ein SdrUndoNewPage
	}

	sal_Bool bFlag=sal_False;

	String aValTabName;
	String *pStr;

	for(SCTAB i=0;i<nCount;i++)
	{
		if(pNames!=NULL)
		{
			pStr=pNames->GetObject(static_cast<sal_uInt16>(i));
		}
		else
		{
			aValTabName.Erase();
			pDoc->CreateValidTabName( aValTabName);
			pStr=&aValTabName;
		}

		if(pDoc->InsertTab( nTab+i,*pStr))
		{
			bFlag=sal_True;
			pDocSh->Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab+i ) );
		}
		else
		{
			break;
		}

		if(pNameList!=NULL)
			pNameList->Insert(new String(*pStr),pNameList->Count());

	}

	if (bFlag)
	{
		if (bRecord)
			pDocSh->GetUndoManager()->AddUndoAction(
						new ScUndoInsertTables( pDocSh, nTab, sal_False, pNameList));

		//	Views updaten:

		SetTabNo( nTab, sal_True );
		pDocSh->PostPaintExtras();
		pDocSh->SetDocumentModified();
		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
		return sal_True;
	}
	else
	{
		return sal_False;
	}
}


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

sal_Bool ScViewFunc::AppendTable( const String& rName, sal_Bool bRecord )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc   = pDocSh->GetDocument();
	if (bRecord && !pDoc->IsUndoEnabled())
		bRecord = sal_False;

	WaitObject aWait( GetFrameWin() );

	if (bRecord)
		pDoc->BeginDrawUndo();							//	InsertTab erzeugt ein SdrUndoNewPage

	if (pDoc->InsertTab( SC_TAB_APPEND, rName ))
	{
		SCTAB nTab = pDoc->GetTableCount()-1;
		if (bRecord)
			pDocSh->GetUndoManager()->AddUndoAction(
						new ScUndoInsertTab( pDocSh, nTab, sal_True, rName));
		GetViewData()->InsertTab( nTab );
		SetTabNo( nTab, sal_True );
		pDocSh->PostPaintExtras();
		pDocSh->SetDocumentModified();
		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
		return sal_True;
	}
	else
	{
		return sal_False;
	}
}


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

sal_Bool ScViewFunc::DeleteTable( SCTAB nTab, sal_Bool bRecord )
{
	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	ScDocument* pDoc 	= pDocSh->GetDocument();

	sal_Bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord, sal_False );
	if (bSuccess)
	{
		SCTAB nNewTab = nTab;
		if ( nNewTab >= pDoc->GetTableCount() )
			--nNewTab;
		SetTabNo( nNewTab, sal_True );
	}
	return bSuccess;
}

sal_Bool ScViewFunc::DeleteTables(const SvShorts &TheTabs, sal_Bool bRecord )
{
	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
	ScDocument* pDoc 	= pDocSh->GetDocument();
    sal_Bool bVbaEnabled = pDoc ? pDoc->IsInVBAMode() : sal_False;
	SCTAB		nNewTab = TheTabs.front();
	WaitObject aWait( GetFrameWin() );
	if (bRecord && !pDoc->IsUndoEnabled())
		bRecord = sal_False;

	while ( nNewTab > 0 && !pDoc->IsVisible( nNewTab ) )
		--nNewTab;

	sal_Bool bWasLinked = sal_False;
	ScDocument* pUndoDoc = NULL;
	ScRefUndoData* pUndoData = NULL;
	if (bRecord)
	{
		pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
//		pUndoDoc->InitDrawLayer( pDocSh );
		SCTAB nCount = pDoc->GetTableCount();

//		pUndoDoc->InitUndo( pDoc, 0, nCount-1 );		// incl. Ref.

		String aOldName;
        for (size_t i = 0; i < TheTabs.size(); i++)
        {
            SCTAB nTab = TheTabs[i];
			if (i==0)
				pUndoDoc->InitUndo( pDoc, nTab,nTab, sal_True,sal_True );	// incl. Spalten/Zeilenflags
			else
				pUndoDoc->AddUndoTab( nTab,nTab, sal_True,sal_True );		// incl. Spalten/Zeilenflags

			pDoc->CopyToDocument(0,0,nTab, MAXCOL,MAXROW,nTab, IDF_ALL,sal_False, pUndoDoc );
			pDoc->GetName( nTab, aOldName );
			pUndoDoc->RenameTab( nTab, aOldName, sal_False );
			if (pDoc->IsLinked(nTab))
			{
				bWasLinked = sal_True;
				pUndoDoc->SetLink( nTab, pDoc->GetLinkMode(nTab), pDoc->GetLinkDoc(nTab),
									pDoc->GetLinkFlt(nTab), pDoc->GetLinkOpt(nTab),
									pDoc->GetLinkTab(nTab),
									pDoc->GetLinkRefreshDelay(nTab) );
			}
			if ( pDoc->IsScenario(nTab) )
			{
				pUndoDoc->SetScenario( nTab, sal_True );
				String aComment;
				Color  aColor;
				sal_uInt16 nScenFlags;
				pDoc->GetScenarioData( nTab, aComment, aColor, nScenFlags );
				pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
				sal_Bool bActive = pDoc->IsActiveScenario( nTab );
				pUndoDoc->SetActiveScenario( nTab, bActive );
			}
			pUndoDoc->SetVisible( nTab, pDoc->IsVisible( nTab ) );
            pUndoDoc->SetTabBgColor( nTab, pDoc->GetTabBgColor(nTab) );
            pUndoDoc->SetSheetEvents( nTab, pDoc->GetSheetEvents( nTab ) );

			if ( pDoc->IsTabProtected( nTab ) )
                pUndoDoc->SetTabProtection(nTab, pDoc->GetTabProtection(nTab));

			//	Drawing-Layer muss sein Undo selbst in der Hand behalten !!!
			//		pUndoDoc->TransferDrawPage(pDoc, nTab,nTab);
		}

		pUndoDoc->AddUndoTab( 0, nCount-1 );			//	alle Tabs fuer Referenzen

		pDoc->BeginDrawUndo();							//	DeleteTab erzeugt ein SdrUndoDelPage

		pUndoData = new ScRefUndoData( pDoc );
	}

	sal_Bool bDelDone = sal_False;

    for (size_t i = TheTabs.size(); i > 0; i--)
    {
        String sCodeName;
        sal_Bool bHasCodeName = pDoc->GetCodeName( TheTabs[i-1], sCodeName );
        if (pDoc->DeleteTab( TheTabs[i-1], pUndoDoc ))
		{
			bDelDone = sal_True;
            if( bVbaEnabled )
            {
                if( bHasCodeName )
                {
                    VBA_DeleteModule( *pDocSh, sCodeName );
                }
            }
            pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED, TheTabs[i-1] ) );
		}
	}
	if (bRecord)
	{
		pDocSh->GetUndoManager()->AddUndoAction(
					new ScUndoDeleteTab( GetViewData()->GetDocShell(), TheTabs,
											pUndoDoc, pUndoData ));
	}


	if (bDelDone)
	{
		if ( nNewTab >= pDoc->GetTableCount() )
			nNewTab = pDoc->GetTableCount() - 1;

		SetTabNo( nNewTab, sal_True );

		if (bWasLinked)
		{
			pDocSh->UpdateLinks();				// Link-Manager updaten
			GetViewData()->GetBindings().Invalidate(SID_LINKS);
		}

		pDocSh->PostPaintExtras();
		pDocSh->SetDocumentModified();

        SfxApplication* pSfxApp = SFX_APP();                                // Navigator
        pSfxApp->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
        pSfxApp->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
        pSfxApp->Broadcast( SfxSimpleHint( SC_HINT_AREALINKS_CHANGED ) );
	}
	else
	{
		delete pUndoDoc;
		delete pUndoData;
	}
	return bDelDone;
}


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

sal_Bool ScViewFunc::RenameTable( const String& rName, SCTAB nTab )
{
	//	Reihenfolge Tabelle/Name ist bei DocFunc umgekehrt
	sal_Bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().
						RenameTable( nTab, rName, sal_True, sal_False );
	if (bSuccess)
	{
		//	Der Tabellenname koennte in einer Formel vorkommen...
		GetViewData()->GetViewShell()->UpdateInputHandler();
	}
	return bSuccess;
}


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

bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab )
{
    bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, sal_True, sal_False );
    if (bSuccess)
    {
        GetViewData()->GetViewShell()->UpdateInputHandler();
    }
    return bSuccess;
}

bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList )
{
    bool bSuccess = GetViewData()->GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, sal_True, sal_False );
    if (bSuccess)
    {
        GetViewData()->GetViewShell()->UpdateInputHandler();
    }
    return bSuccess;
}

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

void ScViewFunc::InsertAreaLink( const String& rFile,
									const String& rFilter, const String& rOptions,
									const String& rSource, sal_uLong nRefresh )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	SCCOL nPosX = GetViewData()->GetCurX();
	SCROW nPosY = GetViewData()->GetCurY();
	SCTAB nTab = GetViewData()->GetTabNo();
	ScAddress aPos( nPosX, nPosY, nTab );

    pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, aPos, nRefresh, sal_False, sal_False );
}


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

void ScViewFunc::InsertTableLink( const String& rFile,
									const String& rFilter, const String& rOptions,
									const String& rTabName )
{
	String aFilterName = rFilter;
	String aOpt = rOptions;
	ScDocumentLoader aLoader( rFile, aFilterName, aOpt );
	if (!aLoader.IsError())
	{
		ScDocShell* pSrcSh = aLoader.GetDocShell();
		ScDocument* pSrcDoc = pSrcSh->GetDocument();
		SCTAB nTab = MAXTAB+1;
		if (!rTabName.Len())				// kein Name angegeben -> erste Tabelle
			nTab = 0;
		else
		{
			String aTemp;
			SCTAB nCount = pSrcDoc->GetTableCount();
			for (SCTAB i=0; i<nCount; i++)
			{
				pSrcDoc->GetName( i, aTemp );
				if ( aTemp == rTabName )
					nTab = i;
			}
		}

		if ( nTab <= MAXTAB )
			ImportTables( pSrcSh, 1, &nTab, sal_True,
						GetViewData()->GetTabNo() );
	}
}


//----------------------------------------------------------------------------
//	Tabellen aus anderem Dokument kopieren / linken

void ScViewFunc::ImportTables( ScDocShell* pSrcShell,
								SCTAB nCount, const SCTAB* pSrcTabs, sal_Bool bLink,SCTAB nTab )
{
	ScDocument* pSrcDoc = pSrcShell->GetDocument();

	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	sal_Bool bUndo(pDoc->IsUndoEnabled());
	//SCTAB nTab = GetViewData()->GetTabNo();

	sal_Bool bError = sal_False;
	sal_Bool bRefs = sal_False;
	sal_Bool bName = sal_False;

	if (pSrcDoc->GetDrawLayer())
		pDocSh->MakeDrawLayer();

	if (bUndo)
		pDoc->BeginDrawUndo();			// drawing layer must do its own undo actions

	SCTAB nInsCount = 0;
	SCTAB i;
	for( i=0; i<nCount; i++ )
	{	// #63304# insert sheets first and update all references
		String aName;
		pSrcDoc->GetName( pSrcTabs[i], aName );
		pDoc->CreateValidTabName( aName );
		if ( !pDoc->InsertTab( nTab+i, aName ) )
		{
			bError = sal_True;		// total error
			break;	// for
		}
		++nInsCount;
	}
	for (i=0; i<nCount && !bError; i++)
	{
		SCTAB nSrcTab = pSrcTabs[i];
		SCTAB nDestTab1=nTab+i;
		sal_uLong nErrVal = pDoc->TransferTab( pSrcDoc, nSrcTab, nDestTab1,
			sal_False );		// no insert

		switch (nErrVal)
		{
			case 0:						// interner Fehler oder voll Fehler
				bError = sal_True;
				break;
			case 2:
				bRefs = sal_True;
				break;
			case 3:
				bName = sal_True;
				break;
			case 4:
				bRefs = bName = sal_True;
				break;
		}

		// TransferTab doesn't copy drawing objects with bInsertNew=FALSE
		if ( !bError )
			pDoc->TransferDrawPage( pSrcDoc, nSrcTab, nDestTab1 );

		if(!bError &&pSrcDoc->IsScenario(nSrcTab))
		{
			String aComment;
			Color  aColor;
			sal_uInt16 nFlags;

			pSrcDoc->GetScenarioData(nSrcTab, aComment,aColor, nFlags);
			pDoc->SetScenario( nDestTab1,sal_True);
			pDoc->SetScenarioData( nTab+i,aComment,aColor,nFlags);
			sal_Bool bActive = pSrcDoc->IsActiveScenario(nSrcTab );
			pDoc->SetActiveScenario( nDestTab1, bActive );
			sal_Bool bVisible=pSrcDoc->IsVisible(nSrcTab);
			pDoc->SetVisible(nDestTab1,bVisible );

		}
	}

	if (bLink)
	{
		sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();

		SfxMedium* pMed = pSrcShell->GetMedium();
		String aFileName = pMed->GetName();
		String aFilterName;
		if (pMed->GetFilter())
			aFilterName = pMed->GetFilter()->GetFilterName();
		String aOptions = ScDocumentLoader::GetOptions(*pMed);

		sal_Bool bWasThere = pDoc->HasLink( aFileName, aFilterName, aOptions );

		sal_uLong nRefresh = 0;
		String aTabStr;
		for (i=0; i<nInsCount; i++)
		{
			pSrcDoc->GetName( pSrcTabs[i], aTabStr );
			pDoc->SetLink( nTab+i, SC_LINK_NORMAL,
						aFileName, aFilterName, aOptions, aTabStr, nRefresh );
		}

		if (!bWasThere)			// Link pro Quelldokument nur einmal eintragen
		{
			ScTableLink* pLink = new ScTableLink( pDocSh, aFileName, aFilterName, aOptions, nRefresh );
			pLink->SetInCreate( sal_True );
			pLinkManager->InsertFileLink( *pLink, OBJECT_CLIENT_FILE, aFileName, &aFilterName );
			pLink->Update();
			pLink->SetInCreate( sal_False );

			SfxBindings& rBindings = GetViewData()->GetBindings();
			rBindings.Invalidate( SID_LINKS );
		}
	}


	if (bUndo)
	{
		pDocSh->GetUndoManager()->AddUndoAction(
				new ScUndoImportTab( pDocSh, nTab, nCount, bLink ) );
	}

	for (i=0; i<nInsCount; i++)
		GetViewData()->InsertTab(nTab);
	SetTabNo(nTab,sal_True);
	pDocSh->PostPaint( 0,0,0, MAXCOL,MAXROW,MAXTAB,
								PAINT_GRID | PAINT_TOP | PAINT_LEFT | PAINT_EXTRAS );

	SfxApplication* pSfxApp = SFX_APP();
	pSfxApp->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
	pSfxApp->Broadcast( SfxSimpleHint( SC_HINT_AREAS_CHANGED ) );

	pDocSh->PostPaintExtras();
	pDocSh->PostPaintGridAll();
	pDocSh->SetDocumentModified();

	if (bRefs)
		ErrorMessage(STR_ABSREFLOST);
	if (bName)
		ErrorMessage(STR_NAMECONFLICT);
}


//----------------------------------------------------------------------------
//	Tabelle in anderes Dokument verschieben / kopieren

void ScViewFunc::MoveTable( sal_uInt16 nDestDocNo, SCTAB nDestTab, sal_Bool bCopy )
{
	ScDocument* pDoc	   = GetViewData()->GetDocument();
	ScDocShell* pDocShell  = GetViewData()->GetDocShell();
	ScDocument*	pDestDoc   = NULL;
	ScDocShell* pDestShell = NULL;
	ScTabViewShell* pDestViewSh = NULL;
	sal_Bool bUndo (pDoc->IsUndoEnabled());

	sal_Bool bNewDoc = ( nDestDocNo == SC_DOC_NEW );
	if ( bNewDoc )
	{
        nDestTab = 0;           // als erstes einfuegen

		//	ohne SFX_CALLMODE_RECORD ausfuehren, weil schon im Move-Befehl enthalten:

		String aUrl = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("private:factory/"));
		aUrl.AppendAscii(RTL_CONSTASCII_STRINGPARAM( STRING_SCAPP ));				// "scalc"
		SfxStringItem aItem( SID_FILE_NAME, aUrl );
		SfxStringItem aTarget( SID_TARGETNAME, String::CreateFromAscii("_blank") );

		const SfxPoolItem* pRetItem = GetViewData()->GetDispatcher().Execute(
					SID_OPENDOC, SFX_CALLMODE_API|SFX_CALLMODE_SYNCHRON, &aItem, &aTarget, 0L );
		if ( pRetItem )
		{
			if ( pRetItem->ISA( SfxObjectItem ) )
				pDestShell = PTR_CAST( ScDocShell, ((const SfxObjectItem*)pRetItem)->GetShell() );
			else if ( pRetItem->ISA( SfxViewFrameItem ) )
			{
				SfxViewFrame* pFrm = ((const SfxViewFrameItem*)pRetItem)->GetFrame();
				if (pFrm)
					pDestShell = PTR_CAST( ScDocShell, pFrm->GetObjectShell() );
			}
			if (pDestShell)
				pDestViewSh = pDestShell->GetBestViewShell();
		}
	}
	else
		pDestShell = ScDocShell::GetShellByNum( nDestDocNo );

	if (!pDestShell)
	{
		DBG_ERROR("Dest-Doc nicht gefunden !!!");
		return;
	}

	pDestDoc = pDestShell->GetDocument();

	SCTAB nTab = GetViewData()->GetTabNo();

	if (pDestDoc != pDoc)
	{
		if (bNewDoc)
		{
			while (pDestDoc->GetTableCount() > 1)
				pDestDoc->DeleteTab(0);
			pDestDoc->RenameTab( 0,
						String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("______42_____")),
						sal_False );
		}

		ScMarkData& rMark		= GetViewData()->GetMarkData();
		SCTAB		nTabCount	= pDoc->GetTableCount();
		SCTAB		nTabSelCount = rMark.GetSelectCount();

		SvShorts	TheTabs;

		for(SCTAB i=0;i<nTabCount;i++)
		{
			if(rMark.GetTableSelect(i))
			{
				String aTabName;
				pDoc->GetName( i, aTabName);
				TheTabs.push_back(i);
				for(SCTAB j=i+1;j<nTabCount;j++)
				{
					if((!pDoc->IsVisible(j))&&(pDoc->IsScenario(j)))
					{
						pDoc->GetName( j, aTabName);
						TheTabs.push_back(j);
						i=j;
					}
					else break;
				}
			}
		}

		GetFrameWin()->EnterWait();

		if (pDoc->GetDrawLayer())
			pDestShell->MakeDrawLayer();

		if (!bNewDoc && bUndo)
			pDestDoc->BeginDrawUndo();		// drawing layer must do its own undo actions

		sal_uLong nErrVal =1;
		if(nDestTab==SC_TAB_APPEND)
            nDestTab=pDestDoc->GetTableCount();
		SCTAB nDestTab1=nDestTab;
		for( size_t j=0; j<TheTabs.size(); j++, nDestTab1++ )
		{	// #63304# insert sheets first and update all references
			String aName;
			pDoc->GetName( TheTabs[j], aName );
			pDestDoc->CreateValidTabName( aName );
			if ( !pDestDoc->InsertTab( nDestTab1, aName ) )
			{
				nErrVal = 0;		// total error
				break;	// for
			}
		}
		if ( nErrVal > 0 )
		{
			nDestTab1 = nDestTab;
			for(size_t i=0;i<TheTabs.size();i++)
			{
				nErrVal = pDestDoc->TransferTab( pDoc, TheTabs[i], nDestTab1,
					sal_False );		// no insert

				// TransferTab doesn't copy drawing objects with bInsertNew=FALSE
				if ( nErrVal > 0 )
					pDestDoc->TransferDrawPage( pDoc, TheTabs[i], nDestTab1 );

				if(nErrVal>0 && pDoc->IsScenario(TheTabs[i]))
				{
					String aComment;
					Color  aColor;
					sal_uInt16 nFlags;

					pDoc->GetScenarioData(TheTabs[i], aComment,aColor, nFlags);
					pDestDoc->SetScenario(nDestTab1,sal_True);
					pDestDoc->SetScenarioData(nDestTab1,aComment,aColor,nFlags);
					sal_Bool bActive = pDoc->IsActiveScenario(TheTabs[i]);
					pDestDoc->SetActiveScenario(nDestTab1, bActive );

					sal_Bool bVisible=pDoc->IsVisible(TheTabs[i]);
					pDestDoc->SetVisible(nDestTab1,bVisible );

				}

				if ( nErrVal > 0 && pDoc->IsTabProtected( TheTabs[i] ) )
                    pDestDoc->SetTabProtection(nDestTab1, pDoc->GetTabProtection(TheTabs[i]));

				nDestTab1++;
			}
		}
		String sName;
		if (!bNewDoc && bUndo)
		{
			pDestDoc->GetName(nDestTab, sName);
			pDestShell->GetUndoManager()->AddUndoAction(
                            new ScUndoImportTab( pDestShell, nDestTab,
                                static_cast<SCTAB>(TheTabs.size()), sal_False));

		}
		else
		{
			pDestShell->GetUndoManager()->Clear();
		}

		GetFrameWin()->LeaveWait();
		switch (nErrVal)
		{
			case 0:						// interner Fehler oder voll Fehler
			{
				ErrorMessage(STR_TABINSERT_ERROR);
				return;
			}
            //break;
			case 2:
				ErrorMessage(STR_ABSREFLOST);
			break;
			case 3:
				ErrorMessage(STR_NAMECONFLICT);
			break;
			case 4:
			{
				ErrorMessage(STR_ABSREFLOST);
				ErrorMessage(STR_NAMECONFLICT);
			}
			break;
			default:
			break;
		}
		//pDestShell->GetUndoManager()->Clear();		//! Undo implementieren !!!
/*
		String sName;
		pDestDoc->GetName(nDestTab, sName);
		pDestShell->GetUndoManager()->AddUndoAction(
						new ScUndoInsertTab( pDestShell, nDestTab, sal_True, sName ) );
*/
		if (!bCopy)
		{
			if(nTabCount!=nTabSelCount)
				DeleteTables(TheTabs);// incl. Paint & Undo
			else
				ErrorMessage(STR_TABREMOVE_ERROR);
		}

		if (bNewDoc)
		{
			//	ChartListenerCollection must be updated before DeleteTab
			if ( pDestDoc->IsChartListenerCollectionNeedsUpdate() )
				pDestDoc->UpdateChartListenerCollection();

			pDestDoc->DeleteTab(static_cast<SCTAB>(TheTabs.size()));   // first old table
//?			pDestDoc->SelectTable(0, sal_True);		// neue erste Tabelle selektieren
			if (pDestViewSh)
				pDestViewSh->TabChanged();		// Pages auf dem Drawing-Layer
			pDestShell->PostPaint( 0,0,0, MAXCOL,MAXROW,MAXTAB,
									PAINT_GRID | PAINT_TOP | PAINT_LEFT |
									PAINT_EXTRAS | PAINT_SIZE );
			//	PAINT_SIZE fuer Gliederung
		}
		else
		{
			pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestTab ) );
			pDestShell->PostPaintExtras();
			pDestShell->PostPaintGridAll();
		}

		TheTabs.clear();

		pDestShell->SetDocumentModified();
		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
	}
	else					// within the documents
	{

		ScMarkData& rMark		= GetViewData()->GetMarkData();
		SCTAB		nTabCount	= pDoc->GetTableCount();

		SvShorts	TheTabs;
		SvShorts	TheDestTabs;
		SvStrings	TheTabNames;
		String		aDestName;
		String		*pString;

		for(SCTAB i=0;i<nTabCount;i++)
		{
			if(rMark.GetTableSelect(i))
			{
				String aTabName;
				pDoc->GetName( i, aTabName);
				TheTabNames.Insert(new String(aTabName),TheTabNames.Count());

				for(SCTAB j=i+1;j<nTabCount;j++)
				{
					if((!pDoc->IsVisible(j))&&(pDoc->IsScenario(j)))
					{
						pDoc->GetName( j, aTabName);
						TheTabNames.Insert(new String(aTabName),TheTabNames.Count());
						i=j;
					}
					else break;
				}

			}
		}

		if (bCopy && bUndo)
			pDoc->BeginDrawUndo();			// drawing layer must do its own undo actions

		pDoc->GetName( nDestTab, aDestName);
		SCTAB nDestTab1=nDestTab;
		SCTAB nMovTab=0;
		for(int j=0;j<TheTabNames.Count();j++)
		{
			nTabCount	= pDoc->GetTableCount();
            pString=TheTabNames[sal::static_int_cast<sal_uInt16>(j)];
			if(!pDoc->GetTable(*pString,nMovTab))
			{
				nMovTab=nTabCount;
			}
			if(!pDoc->GetTable(aDestName,nDestTab1))
			{
				nDestTab1=nTabCount;
			}
			pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, sal_False );	// Undo ist hier

			if(bCopy && pDoc->IsScenario(nMovTab))
			{
				String aComment;
				Color  aColor;
				sal_uInt16 nFlags;

				pDoc->GetScenarioData(nMovTab, aComment,aColor, nFlags);
				pDoc->SetScenario(nDestTab1,sal_True);
				pDoc->SetScenarioData(nDestTab1,aComment,aColor,nFlags);
				sal_Bool bActive = pDoc->IsActiveScenario(nMovTab );
				pDoc->SetActiveScenario( nDestTab1, bActive );
				sal_Bool bVisible=pDoc->IsVisible(nMovTab);
				pDoc->SetVisible(nDestTab1,bVisible );
			}

			TheTabs.push_back(nMovTab);

			if(!bCopy)
			{
				if(!pDoc->GetTable(*pString,nDestTab1))
				{
					nDestTab1=nTabCount;
				}
			}

			TheDestTabs.push_back(nDestTab1);
			delete pString;
		}

		nTab = GetViewData()->GetTabNo();

		if (bUndo)
		{
			if (bCopy)
			{
				pDocShell->GetUndoManager()->AddUndoAction(
						new ScUndoCopyTab( pDocShell, TheTabs, TheDestTabs));
			}
			else
			{
				pDocShell->GetUndoManager()->AddUndoAction(
						new ScUndoMoveTab( pDocShell, TheTabs, TheDestTabs));
			}
		}

		SCTAB nNewTab = nDestTab;
		if (nNewTab == SC_TAB_APPEND)
			nNewTab = pDoc->GetTableCount()-1;
		else if (!bCopy && nTab<nDestTab)
			nNewTab--;

		SetTabNo( nNewTab, sal_True );

        //#i29848# adjust references to data on the copied sheet
        if( bCopy )
            ScChartHelper::AdjustRangesOfChartsOnDestinationPage( pDoc, pDestDoc, nTab, nNewTab );
	}
}


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

void ScViewFunc::ShowTable( const String& rName )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	sal_Bool bUndo(pDoc->IsUndoEnabled());
	sal_Bool bFound = sal_False;
	SCTAB nPos = 0;
	String aTabName;
	SCTAB nCount = pDoc->GetTableCount();
	for (SCTAB i=0; i<nCount; i++)
	{
		pDoc->GetName( i, aTabName );
		if ( aTabName == rName )
		{
			nPos = i;
			bFound = sal_True;
		}
	}

	if (bFound)
	{
		pDoc->SetVisible( nPos, sal_True );
		if (bUndo)
		{
			pDocSh->GetUndoManager()->AddUndoAction( new ScUndoShowHideTab( pDocSh, nPos, sal_True ) );
		}
		SetTabNo( nPos, sal_True );
		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
		pDocSh->PostPaint(0,0,0,MAXCOL,MAXROW,MAXTAB, PAINT_EXTRAS);
		pDocSh->SetDocumentModified();
	}
	else
		Sound::Beep();
}


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

void ScViewFunc::HideTable( SCTAB nTab )
{
	ScDocShell* pDocSh = GetViewData()->GetDocShell();
	ScDocument* pDoc = pDocSh->GetDocument();
	sal_Bool bUndo(pDoc->IsUndoEnabled());
	SCTAB nVisible = 0;
	SCTAB nCount = pDoc->GetTableCount();
	for (SCTAB i=0; i<nCount; i++)
	{
		if (pDoc->IsVisible(i))
			++nVisible;
	}

	if (nVisible > 1)
	{
		pDoc->SetVisible( nTab, sal_False );
		if (bUndo)
		{
			pDocSh->GetUndoManager()->AddUndoAction( new ScUndoShowHideTab( pDocSh, nTab, sal_False ) );
		}

		//	Views updaten:
		pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );

		SetTabNo( nTab, sal_True );
		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
		pDocSh->PostPaint(0,0,0,MAXCOL,MAXROW,MAXTAB, PAINT_EXTRAS);
		pDocSh->SetDocumentModified();
	}
	else
		Sound::Beep();
}


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

void ScViewFunc::InsertSpecialChar( const String& rStr, const Font& rFont )
{
	ScEditableTester aTester( this );
	if (!aTester.IsEditable())
	{
		ErrorMessage(aTester.GetMessageId());
		return;
	}

	const sal_Unicode* pChar	= rStr.GetBuffer();
	ScTabViewShell* pViewShell	= GetViewData()->GetViewShell();
	SvxFontItem		aFontItem( rFont.GetFamily(),
							   rFont.GetName(),
							   rFont.GetStyleName(),
							   rFont.GetPitch(),
							   rFont.GetCharSet(),
							   ATTR_FONT );

	//	if string contains WEAK characters, set all fonts
	sal_uInt8 nScript;
	ScDocument* pDoc = GetViewData()->GetDocument();
	if ( pDoc->HasStringWeakCharacters( rStr ) )
		nScript = SCRIPTTYPE_LATIN | SCRIPTTYPE_ASIAN | SCRIPTTYPE_COMPLEX;
	else
		nScript = pDoc->GetStringScriptType( rStr );

	SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, pViewShell->GetPool() );
	aSetItem.PutItemForScriptType( nScript, aFontItem );
	ApplyUserItemSet( aSetItem.GetItemSet() );

	while ( *pChar )
		pViewShell->TabKeyInput( KeyEvent( *(pChar++), KeyCode() ) );
}


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

void ScViewFunc::UpdateLineAttrs( SvxBorderLine&	   rLine,
								  const SvxBorderLine* pDestLine,
								  const SvxBorderLine* pSrcLine,
								  sal_Bool 				   bColor )
{
	if ( pSrcLine && pDestLine )
	{
		if ( bColor )
		{
			rLine.SetColor		( pSrcLine->GetColor() );
			rLine.SetOutWidth	( pDestLine->GetOutWidth() );
			rLine.SetInWidth	( pDestLine->GetInWidth() );
			rLine.SetDistance	( pDestLine->GetDistance() );
		}
		else
		{
			rLine.SetColor		( pDestLine->GetColor() );
			rLine.SetOutWidth	( pSrcLine->GetOutWidth() );
			rLine.SetInWidth	( pSrcLine->GetInWidth() );
			rLine.SetDistance	( pSrcLine->GetDistance() );
		}
	}
}


#define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \
	pBoxLine = aBoxItem.Get##LINE();								\
	if ( pBoxLine )													\
	{																\
		if ( pLine )												\
		{															\
			UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly );	\
			aBoxItem.SetLine( &aLine, BOXLINE );					\
		}															\
		else														\
			aBoxItem.SetLine( NULL, BOXLINE );						\
	}


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

void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine,
										 sal_Bool bColorOnly )
{
	// nur wegen Matrix nicht editierbar? Attribute trotzdem ok
	sal_Bool bOnlyNotBecauseOfMatrix;
	if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
	{
		ErrorMessage(STR_PROTECTIONERR);
		return;
	}

	ScDocument*				pDoc = GetViewData()->GetDocument();
    ScMarkData aFuncMark( GetViewData()->GetMarkData() );       // local copy for UnmarkFiltered
    ScViewUtil::UnmarkFiltered( aFuncMark, pDoc );
	ScDocShell*				pDocSh = GetViewData()->GetDocShell();
	const ScPatternAttr*	pSelAttrs = GetSelectionPattern();
    const SfxItemSet&       rSelItemSet = pSelAttrs->GetItemSet();

	const SfxPoolItem*		pBorderAttr	= NULL;
    SfxItemState            eItemState = rSelItemSet.GetItemState( ATTR_BORDER, sal_True, &pBorderAttr );

    const SfxPoolItem*      pTLBRItem = 0;
    SfxItemState            eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, sal_True, &pTLBRItem );

    const SfxPoolItem*      pBLTRItem = 0;
    SfxItemState            eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, sal_True, &pBLTRItem );

    // any of the lines visible?
    if( (eItemState != SFX_ITEM_DEFAULT) || (eTLBRState != SFX_ITEM_DEFAULT) || (eBLTRState != SFX_ITEM_DEFAULT) )
	{
        // none of the lines don't care?
        if( (eItemState != SFX_ITEM_DONTCARE) && (eTLBRState != SFX_ITEM_DONTCARE) && (eBLTRState != SFX_ITEM_DONTCARE) )
		{
			SfxItemSet*		pOldSet	= new SfxItemSet(
											*(pDoc->GetPool()),
											ATTR_PATTERN_START,
											ATTR_PATTERN_END );
			SfxItemSet*		pNewSet	= new SfxItemSet(
											*(pDoc->GetPool()),
											ATTR_PATTERN_START,
											ATTR_PATTERN_END );

			//------------------------------------------------------------
			const SvxBorderLine*	pBoxLine = NULL;
			SvxBorderLine			aLine;

			// hier wird die pBoxLine benutzt:

            if( pBorderAttr )
            {
                SvxBoxItem      aBoxItem( *(const SvxBoxItem*)pBorderAttr );
                SvxBoxInfoItem  aBoxInfoItem( ATTR_BORDER_INNER );

                SET_LINE_ATTRIBUTES(Top,BOX_LINE_TOP)
                SET_LINE_ATTRIBUTES(Bottom,BOX_LINE_BOTTOM)
                SET_LINE_ATTRIBUTES(Left,BOX_LINE_LEFT)
                SET_LINE_ATTRIBUTES(Right,BOX_LINE_RIGHT)

                aBoxInfoItem.SetLine( aBoxItem.GetTop(), BOXINFO_LINE_HORI );
                aBoxInfoItem.SetLine( aBoxItem.GetLeft(), BOXINFO_LINE_VERT );
                aBoxInfoItem.ResetFlags(); // Lines auf Valid setzen

                pOldSet->Put( *pBorderAttr );
                pNewSet->Put( aBoxItem );
                pNewSet->Put( aBoxInfoItem );
            }

            if( pTLBRItem && ((const SvxLineItem*)pTLBRItem)->GetLine() )
            {
                SvxLineItem aTLBRItem( *(const SvxLineItem*)pTLBRItem );
                UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly );
                aTLBRItem.SetLine( &aLine );
                pOldSet->Put( *pTLBRItem );
                pNewSet->Put( aTLBRItem );
            }

            if( pBLTRItem && ((const SvxLineItem*)pBLTRItem)->GetLine() )
            {
                SvxLineItem aBLTRItem( *(const SvxLineItem*)pBLTRItem );
                UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly );
                aBLTRItem.SetLine( &aLine );
                pOldSet->Put( *pBLTRItem );
                pNewSet->Put( aBLTRItem );
            }

			ApplyAttributes( pNewSet, pOldSet );

			delete pOldSet;
			delete pNewSet;
		}
		else // if ( eItemState == SFX_ITEM_DONTCARE )
		{
			aFuncMark.MarkToMulti();
			pDoc->ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly );
		}

		ScRange aMarkRange;
		aFuncMark.GetMultiMarkArea( aMarkRange );
		SCCOL nStartCol = aMarkRange.aStart.Col();
		SCROW nStartRow = aMarkRange.aStart.Row();
		SCTAB nStartTab = aMarkRange.aStart.Tab();
		SCCOL nEndCol = aMarkRange.aEnd.Col();
		SCROW nEndRow = aMarkRange.aEnd.Row();
		SCTAB nEndTab = aMarkRange.aEnd.Tab();
		pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
						   nEndCol, nEndRow, nEndTab,
						   PAINT_GRID, SC_PF_LINES | SC_PF_TESTMERGE );

		pDocSh->UpdateOle( GetViewData() );
		pDocSh->SetDocumentModified();
	}
}

#undef SET_LINE_ATTRIBUTES


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

void ScViewFunc::SetConditionalFormat( const ScConditionalFormat& rNew )
{
	ScDocument* pDoc = GetViewData()->GetDocument();
	sal_uLong nIndex = pDoc->AddCondFormat(rNew);			// dafuer gibt's kein Undo
	SfxUInt32Item aItem( ATTR_CONDITIONAL, nIndex );

	ApplyAttr( aItem );			// mit Paint und Undo...
}


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

void ScViewFunc::SetValidation( const ScValidationData& rNew )
{
	ScDocument* pDoc = GetViewData()->GetDocument();
	sal_uLong nIndex = pDoc->AddValidationEntry(rNew);		// dafuer gibt's kein Undo
	SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex );

	ApplyAttr( aItem );			// mit Paint und Undo...
}


