blob: 96f2dc4fd8c37a8ace62a2f0a405c5a01e43d1ad [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
// System - Includes -----------------------------------------------------
#ifdef _MSC_VER
#pragma optimize("",off)
// sonst Absturz Win beim Fuellen
#endif
// INCLUDE ---------------------------------------------------------------
#include "scitems.hxx"
#include <svx/algitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/cntritem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/crsditem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <svx/rotmodit.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editeng.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/escpitem.hxx>
#include <svl/zforlist.hxx>
#include <vcl/keycodes.hxx>
#include <rtl/math.hxx>
#include <unotools/charclass.hxx>
#include "attrib.hxx"
#include "patattr.hxx"
#include "cell.hxx"
#include "table.hxx"
#include "globstr.hrc"
#include "global.hxx"
#include "document.hxx"
#include "autoform.hxx"
#include "userlist.hxx"
#include "zforauto.hxx"
#include "subtotal.hxx"
#include "formula/errorcodes.hxx"
#include "rangenam.hxx"
#include "docpool.hxx"
#include "progress.hxx"
#include "segmenttree.hxx"
#include <math.h>
// STATIC DATA -----------------------------------------------------------
#define _D_MAX_LONG_ (double) 0x7fffffff
extern sal_uInt16 nScFillModeMouseModifier; // global.cxx
// -----------------------------------------------------------------------
short lcl_DecompValueString( String& aValue, sal_Int32& nVal, sal_uInt16* pMinDigits = NULL )
{
if ( !aValue.Len() )
{
nVal = 0;
return 0;
}
const sal_Unicode* p = aValue.GetBuffer();
xub_StrLen nNeg = 0;
xub_StrLen nNum = 0;
if ( p[nNum] == '-' )
nNum = nNeg = 1;
while ( p[nNum] && CharClass::isAsciiNumeric( p[nNum] ) )
nNum++;
sal_Unicode cNext = p[nNum]; // 0 if at the end
sal_Unicode cLast = p[aValue.Len()-1];
// #i5550# If there are numbers at the beginning and the end,
// prefer the one at the beginning only if it's followed by a space.
// Otherwise, use the number at the end, to enable things like IP addresses.
if ( nNum > nNeg && ( cNext == 0 || cNext == ' ' || !CharClass::isAsciiNumeric(cLast) ) )
{ // number at the beginning
nVal = aValue.Copy( 0, nNum ).ToInt32();
// #60893# any number with a leading zero sets the minimum number of digits
if ( p[nNeg] == '0' && pMinDigits && ( nNum - nNeg > *pMinDigits ) )
*pMinDigits = nNum - nNeg;
aValue.Erase( 0, nNum );
return -1;
}
else
{
nNeg = 0;
xub_StrLen nEnd = nNum = aValue.Len() - 1;
while ( nNum && CharClass::isAsciiNumeric( p[nNum] ) )
nNum--;
if ( p[nNum] == '-' )
{
nNum--;
nNeg = 1;
}
if ( nNum < nEnd - nNeg )
{ // number at the end
nVal = aValue.Copy( nNum + 1 ).ToInt32();
// #60893# any number with a leading zero sets the minimum number of digits
if ( p[nNum+1+nNeg] == '0' && pMinDigits && ( nEnd - nNum - nNeg > *pMinDigits ) )
*pMinDigits = nEnd - nNum - nNeg;
aValue.Erase( nNum + 1 );
return 1;
}
}
nVal = 0;
return 0;
}
String lcl_ValueString( sal_Int32 nValue, sal_uInt16 nMinDigits )
{
if ( nMinDigits <= 1 )
return String::CreateFromInt32( nValue ); // simple case...
else
{
String aStr = String::CreateFromInt32( Abs( nValue ) );
if ( aStr.Len() < nMinDigits )
{
String aZero;
aZero.Fill( nMinDigits - aStr.Len(), '0' );
aStr.Insert( aZero, 0 );
}
// nMinDigits doesn't include the '-' sign -> add after inserting zeros
if ( nValue < 0 )
aStr.Insert( '-', 0 );
return aStr;
}
}
static ScBaseCell * lcl_getSuffixCell( ScDocument* pDocument, sal_Int32 nValue,
sal_uInt16 nDigits, const String& rSuffix, CellType eCellType,
sal_Bool bIsOrdinalSuffix )
{
String aValue( lcl_ValueString( nValue, nDigits ));
if (!bIsOrdinalSuffix)
return new ScStringCell( aValue += rSuffix);
String aOrdinalSuffix( ScGlobal::GetOrdinalSuffix( nValue));
if (eCellType != CELLTYPE_EDIT)
return new ScStringCell( aValue += aOrdinalSuffix);
EditEngine aEngine( pDocument->GetEnginePool() );
SfxItemSet aAttr = aEngine.GetEmptyItemSet();
aAttr.Put( SvxEscapementItem( SVX_ESCAPEMENT_SUPERSCRIPT, EE_CHAR_ESCAPEMENT));
aEngine.SetText( aValue );
aEngine.QuickInsertText( aOrdinalSuffix, ESelection( 0, aValue.Len(), 0,
aValue.Len() + aOrdinalSuffix.Len()));
aEngine.QuickSetAttribs( aAttr, ESelection( 0, aValue.Len(), 0, aValue.Len() +
aOrdinalSuffix.Len()));
return new ScEditCell( aEngine.CreateTextObject(), pDocument, NULL );
}
void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
FillCmd& rCmd, FillDateCmd& rDateCmd,
double& rInc, sal_uInt16& rMinDigits,
ScUserListData*& rListData, sal_uInt16& rListIndex)
{
DBG_ASSERT( nCol1==nCol2 || nRow1==nRow2, "FillAnalyse: falscher Bereich" );
rInc = 0.0;
rMinDigits = 0;
rListData = NULL;
rCmd = FILL_SIMPLE;
if (( nScFillModeMouseModifier & KEY_MOD1 )||IsDataFiltered()) //i89232
return ; // Ctrl-Taste: Copy
SCCOL nAddX;
SCROW nAddY;
SCSIZE nCount;
if (nCol1 == nCol2)
{
nAddX = 0;
nAddY = 1;
nCount = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
}
else
{
nAddX = 1;
nAddY = 0;
nCount = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
}
SCCOL nCol = nCol1;
SCROW nRow = nRow1;
ScBaseCell* pFirstCell = GetCell( nCol, nRow );
CellType eCellType = pFirstCell ? pFirstCell->GetCellType() : CELLTYPE_NONE;
if (eCellType == CELLTYPE_VALUE)
{
sal_uInt32 nFormat = ((const SfxUInt32Item*)GetAttr(nCol,nRow,ATTR_VALUE_FORMAT))->GetValue();
sal_Bool bDate = ( pDocument->GetFormatTable()->GetType(nFormat) == NUMBERFORMAT_DATE );
if (bDate)
{
if (nCount > 1)
{
long nCmpInc = 0;
double nVal;
Date aNullDate = *pDocument->GetFormatTable()->GetNullDate();
Date aDate1 = aNullDate;
nVal = ((ScValueCell*)pFirstCell)->GetValue();
aDate1 += (long)nVal;
Date aDate2 = aNullDate;
nVal = GetValue(nCol+nAddX, nRow+nAddY);
aDate2 += (long)nVal;
if ( aDate1 != aDate2 )
{
FillDateCmd eType;
long nDDiff = aDate2.GetDay() - (long) aDate1.GetDay();
long nMDiff = aDate2.GetMonth() - (long) aDate1.GetMonth();
long nYDiff = aDate2.GetYear() - (long) aDate1.GetYear();
if ( nDDiff )
{
eType = FILL_DAY;
nCmpInc = aDate2 - aDate1;
}
else
{
eType = FILL_MONTH;
nCmpInc = nMDiff + 12 * nYDiff;
}
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
sal_Bool bVal = sal_True;
for (sal_uInt16 i=1; i<nCount && bVal; i++)
{
ScBaseCell* pCell = GetCell(nCol,nRow);
if (pCell && pCell->GetCellType() == CELLTYPE_VALUE)
{
nVal = ((ScValueCell*)pCell)->GetValue();
aDate2 = aNullDate + (long) nVal;
if ( eType == FILL_DAY )
{
if ( aDate2-aDate1 != nCmpInc )
bVal = sal_False;
}
else
{
nDDiff = aDate2.GetDay() - (long) aDate1.GetDay();
nMDiff = aDate2.GetMonth() - (long) aDate1.GetMonth();
nYDiff = aDate2.GetYear() - (long) aDate1.GetYear();
if (nDDiff || ( nMDiff + 12 * nYDiff != nCmpInc ))
bVal = sal_False;
}
aDate1 = aDate2;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
}
else
bVal = sal_False; // #50965# kein Datum passt auch nicht
}
if (bVal)
{
if ( eType == FILL_MONTH && ( nCmpInc % 12 == 0 ) )
{
eType = FILL_YEAR;
nCmpInc /= 12;
}
rCmd = FILL_DATE;
rDateCmd = eType;
rInc = nCmpInc;
}
}
}
else // einzelnes Datum -> Tage hochzaehlen
{
rCmd = FILL_DATE;
rDateCmd = FILL_DAY;
rInc = 1.0;
}
}
else
{
if (nCount > 1)
{
double nVal1 = ((ScValueCell*)pFirstCell)->GetValue();
double nVal2 = GetValue(nCol+nAddX, nRow+nAddY);
rInc = nVal2 - nVal1;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
sal_Bool bVal = sal_True;
for (sal_uInt16 i=1; i<nCount && bVal; i++)
{
ScBaseCell* pCell = GetCell(nCol,nRow);
if (pCell && pCell->GetCellType() == CELLTYPE_VALUE)
{
nVal2 = ((ScValueCell*)pCell)->GetValue();
double nDiff = nVal2 - nVal1;
if ( !::rtl::math::approxEqual( nDiff, rInc ) )
bVal = sal_False;
nVal1 = nVal2;
}
else
bVal = sal_False;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
}
if (bVal)
rCmd = FILL_LINEAR;
}
}
}
else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
{
String aStr;
GetString(nCol, nRow, aStr);
rListData = (ScUserListData*)(ScGlobal::GetUserList()->GetData(aStr));
if (rListData)
{
rListData->GetSubIndex(aStr, rListIndex);
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
for (sal_uInt16 i=1; i<nCount && rListData; i++)
{
GetString(nCol, nRow, aStr);
if (!rListData->GetSubIndex(aStr, rListIndex))
rListData = NULL;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
}
}
else if ( nCount > 1 )
{
// pass rMinDigits to all DecompValueString calls
// -> longest number defines rMinDigits
sal_Int32 nVal1;
short nFlag1 = lcl_DecompValueString( aStr, nVal1, &rMinDigits );
if ( nFlag1 )
{
sal_Int32 nVal2;
GetString( nCol+nAddX, nRow+nAddY, aStr );
short nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits );
if ( nFlag1 == nFlag2 )
{
rInc = (double)nVal2 - (double)nVal1;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
sal_Bool bVal = sal_True;
for (sal_uInt16 i=1; i<nCount && bVal; i++)
{
ScBaseCell* pCell = GetCell(nCol,nRow);
CellType eType = pCell ? pCell->GetCellType() : CELLTYPE_NONE;
if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT )
{
if ( eType == CELLTYPE_STRING )
((ScStringCell*)pCell)->GetString( aStr );
else
((ScEditCell*)pCell)->GetString( aStr );
nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits );
if ( nFlag1 == nFlag2 )
{
double nDiff = (double)nVal2 - (double)nVal1;
if ( !::rtl::math::approxEqual( nDiff, rInc ) )
bVal = sal_False;
nVal1 = nVal2;
}
else
bVal = sal_False;
}
else
bVal = sal_False;
nCol = sal::static_int_cast<SCCOL>( nCol + nAddX );
nRow = sal::static_int_cast<SCROW>( nRow + nAddY );
}
if (bVal)
rCmd = FILL_LINEAR;
}
}
}
else
{
// call DecompValueString to set rMinDigits
sal_Int32 nDummy;
lcl_DecompValueString( aStr, nDummy, &rMinDigits );
}
}
}
void ScTable::FillFormula(sal_uLong& /* nFormulaCounter */, sal_Bool /* bFirst */, ScFormulaCell* pSrcCell,
SCCOL nDestCol, SCROW nDestRow, sal_Bool bLast )
{
/* sal_uInt16 nTokArrLen = pSrcCell->GetTokenArrayLen();
if ( nTokArrLen > 15 ) // mehr als =A1 oder =67
{
ScRangeName* pRangeName = pDocument->GetRangeName();
String aName("___SC_"); // Wird dieser String veraendert,
// auch in document2 EraseNonUsed...
// mitaendern!!
aName += pRangeName->GetSharedMaxIndex() + 1;
aName += '_';
aName += nFormulaCounter;
nFormulaCounter++;
if (bFirst)
{
ScRangeData *pAktRange = new ScRangeData(
pDocument, aName, pSrcCell->GetTokenArray(), nTokArrLen,
pSrcCell->GetCol(), pSrcCell->GetRow(), nTab ,RT_SHARED);
if (!pRangeName->Insert( pAktRange ))
delete pAktRange;
else
bSharedNameInserted = sal_True;
}
sal_uInt16 nIndex;
pRangeName->SearchName(aName, nIndex);
if (!pRangeName)
{
DBG_ERROR("ScTable::FillFormula: Falscher Name");
return;
}
nIndex = ((ScRangeData*) ((*pRangeName)[nIndex]))->GetIndex();
ScTokenArray aArr;
aArr.AddName(nIndex);
aArr.AddOpCode(ocStop);
ScFormulaCell* pDestCell = new ScFormulaCell
(pDocument, ScAddress( nDestCol, nDestRow, nTab ), aArr );
aCol[nDestCol].Insert(nDestRow, pDestCell);
}
else
*/ {
pDocument->SetNoListening( sal_True ); // noch falsche Referenzen
ScAddress aAddr( nDestCol, nDestRow, nTab );
ScFormulaCell* pDestCell = new ScFormulaCell( *pSrcCell, *pDocument, aAddr );
aCol[nDestCol].Insert(nDestRow, pDestCell);
#if 0
// mit RelRefs unnoetig
pDestCell->UpdateReference(URM_COPY,
ScRange( aAddr, aAddr ),
nDestCol - pSrcCell->aPos.Col(),
nDestRow - pSrcCell->aPos.Row(), 0);
#endif
if ( bLast && pDestCell->GetMatrixFlag() )
{
ScAddress aOrg;
if ( pDestCell->GetMatrixOrigin( aOrg ) )
{
if ( nDestCol >= aOrg.Col() && nDestRow >= aOrg.Row() )
{
ScBaseCell* pOrgCell = pDocument->GetCell( aOrg );
if ( pOrgCell && pOrgCell->GetCellType() == CELLTYPE_FORMULA
&& ((ScFormulaCell*)pOrgCell)->GetMatrixFlag() == MM_FORMULA )
{
((ScFormulaCell*)pOrgCell)->SetMatColsRows(
nDestCol - aOrg.Col() + 1,
nDestRow - aOrg.Row() + 1 );
}
else
{
DBG_ERRORFILE( "FillFormula: MatrixOrigin keine Formelzelle mit MM_FORMULA" );
}
}
else
{
DBG_ERRORFILE( "FillFormula: MatrixOrigin rechts unten" );
}
}
else
{
DBG_ERRORFILE( "FillFormula: kein MatrixOrigin" );
}
}
pDocument->SetNoListening( sal_False );
pDestCell->StartListeningTo( pDocument );
}
}
void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
sal_uLong nFillCount, FillDir eFillDir, ScProgress& rProgress )
{
if ( (nFillCount == 0) || !ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2) )
return;
//
// Richtung auswerten
//
sal_Bool bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP);
sal_Bool bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT);
sal_uLong nCol = 0;
sal_uLong nRow = 0;
sal_uLong& rInner = bVertical ? nRow : nCol; // Schleifenvariablen
sal_uLong& rOuter = bVertical ? nCol : nRow;
sal_uLong nOStart;
sal_uLong nOEnd;
sal_uLong nIStart;
sal_uLong nIEnd;
sal_uLong nISrcStart;
sal_uLong nISrcEnd;
if (bVertical)
{
nOStart = nCol1;
nOEnd = nCol2;
if (bPositive)
{
nISrcStart = nRow1;
nISrcEnd = nRow2;
nIStart = nRow2 + 1;
nIEnd = nRow2 + nFillCount;
}
else
{
nISrcStart = nRow2;
nISrcEnd = nRow1;
nIStart = nRow1 - 1;
nIEnd = nRow1 - nFillCount;
}
}
else
{
nOStart = nRow1;
nOEnd = nRow2;
if (bPositive)
{
nISrcStart = nCol1;
nISrcEnd = nCol2;
nIStart = nCol2 + 1;
nIEnd = nCol2 + nFillCount;
}
else
{
nISrcStart = nCol2;
nISrcEnd = nCol1;
nIStart = nCol1 - 1;
nIEnd = nCol1 - nFillCount;
}
}
sal_uLong nIMin = nIStart;
sal_uLong nIMax = nIEnd;
PutInOrder(nIMin,nIMax);
sal_Bool bHasFiltered = IsDataFiltered();
if (!bHasFiltered) //modify for i89232
{
if (bVertical)
DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), IDF_AUTOFILL);
else
DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, IDF_AUTOFILL);
}
sal_uLong nProgress = rProgress.GetState();
//
// ausfuehren
//
sal_uLong nActFormCnt = 0;
for (rOuter = nOStart; rOuter <= nOEnd; rOuter++)
{
sal_uLong nMaxFormCnt = 0; // fuer Formeln
// Attributierung uebertragen
const ScPatternAttr* pSrcPattern = NULL;
const ScStyleSheet* pStyleSheet = NULL;
sal_uLong nAtSrc = nISrcStart;
ScPatternAttr* pNewPattern = NULL;
sal_Bool bGetPattern = sal_True;
rInner = nIStart;
while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
{
if ( bGetPattern )
{
if ( pNewPattern )
delete pNewPattern;
if (bVertical) // rInner&:=nRow, rOuter&:=nCol
pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nAtSrc));
else // rInner&:=nCol, rOuter&:=nRow
pSrcPattern = aCol[nAtSrc].GetPattern(static_cast<SCROW>(nRow));
bGetPattern = sal_False;
pStyleSheet = pSrcPattern->GetStyleSheet();
// Merge/Mergeflag nicht uebernehmen,
const SfxItemSet& rSet = pSrcPattern->GetItemSet();
if ( rSet.GetItemState(ATTR_MERGE, sal_False) == SFX_ITEM_SET
|| rSet.GetItemState(ATTR_MERGE_FLAG, sal_False) == SFX_ITEM_SET )
{
pNewPattern = new ScPatternAttr( *pSrcPattern );
SfxItemSet& rNewSet = pNewPattern->GetItemSet();
rNewSet.ClearItem(ATTR_MERGE);
rNewSet.ClearItem(ATTR_MERGE_FLAG);
}
else
pNewPattern = NULL;
}
if ( bVertical && nISrcStart == nISrcEnd && !bHasFiltered )
{
// Attribute komplett am Stueck setzen
if (pNewPattern || pSrcPattern != pDocument->GetDefPattern())
{
// Default steht schon da (DeleteArea)
SCROW nY1 = static_cast<SCROW>(Min( nIStart, nIEnd ));
SCROW nY2 = static_cast<SCROW>(Max( nIStart, nIEnd ));
if ( pStyleSheet )
aCol[nCol].ApplyStyleArea( nY1, nY2, *pStyleSheet );
if ( pNewPattern )
aCol[nCol].ApplyPatternArea( nY1, nY2, *pNewPattern );
else
aCol[nCol].ApplyPatternArea( nY1, nY2, *pSrcPattern );
}
break; // Schleife abbrechen
}
if ( !RowFiltered(nRow) )
{
if ( bHasFiltered )
DeleteArea(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow),
static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), IDF_AUTOFILL);
if ( pSrcPattern != aCol[nCol].GetPattern( static_cast<SCROW>(nRow) ) )
{
// Vorlage auch uebernehmen
//! am AttrArray mit ApplyPattern zusammenfassen ??
if ( pStyleSheet )
aCol[nCol].ApplyStyle( static_cast<SCROW>(nRow), *pStyleSheet );
// ApplyPattern statt SetPattern um alte MergeFlags stehenzulassen
if ( pNewPattern )
aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pNewPattern );
else
aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pSrcPattern );
}
if (nAtSrc==nISrcEnd)
{
if ( nAtSrc != nISrcStart )
{ // mehr als eine Source-Zelle
nAtSrc = nISrcStart;
bGetPattern = sal_True;
}
}
else if (bPositive)
{
++nAtSrc;
bGetPattern = sal_True;
}
else
{
--nAtSrc;
bGetPattern = sal_True;
}
}
if (rInner == nIEnd) break;
if (bPositive) ++rInner; else --rInner;
}
if ( pNewPattern )
delete pNewPattern;
// Analyse
FillCmd eFillCmd;
FillDateCmd eDateCmd;
double nInc;
sal_uInt16 nMinDigits;
ScUserListData* pListData = NULL;
sal_uInt16 nListIndex;
if (bVertical)
FillAnalyse(static_cast<SCCOL>(nCol),nRow1,
static_cast<SCCOL>(nCol),nRow2, eFillCmd,eDateCmd,
nInc,nMinDigits, pListData,nListIndex);
else
FillAnalyse(nCol1,static_cast<SCROW>(nRow),
nCol2,static_cast<SCROW>(nRow), eFillCmd,eDateCmd,
nInc,nMinDigits, pListData,nListIndex);
if (bVertical)
aCol[nCol].Resize( aCol[nCol].GetCellCount() + nFillCount );
if (pListData)
{
sal_uInt16 nListCount = pListData->GetSubCount();
if ( !bPositive )
{
// nListIndex auf FillAnalyse zeigt auf den letzten Eintrag -> anpassen
sal_uLong nSub = nISrcStart - nISrcEnd;
for (sal_uLong i=0; i<nSub; i++)
{
if (nListIndex == 0) nListIndex = nListCount;
--nListIndex;
}
}
rInner = nIStart;
while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
{
if (bPositive)
{
++nListIndex;
if (nListIndex >= nListCount) nListIndex = 0;
}
else
{
if (nListIndex == 0) nListIndex = nListCount;
--nListIndex;
}
aCol[nCol].Insert(static_cast<SCROW>(nRow), new ScStringCell(pListData->GetSubStr(nListIndex)));
if (rInner == nIEnd) break;
if (bPositive) ++rInner; else --rInner;
}
nProgress += nIMax - nIMin + 1;
rProgress.SetStateOnPercent( nProgress );
}
else if (eFillCmd == FILL_SIMPLE) // Auffuellen mit Muster
{
sal_uLong nSource = nISrcStart;
double nDelta;
if (( nScFillModeMouseModifier & KEY_MOD1 )||bHasFiltered) //i89232
nDelta = 0.0;
else if ( bPositive )
nDelta = 1.0;
else
nDelta = -1.0;
double nVal = 0.0;
sal_uLong nFormulaCounter = nActFormCnt;
sal_Bool bFirst = sal_True;
sal_Bool bGetCell = sal_True;
sal_uInt16 nCellDigits = 0;
short nHeadNoneTail = 0;
sal_Int32 nStringValue = 0;
String aValue;
ScBaseCell* pSrcCell = NULL;
CellType eCellType = CELLTYPE_NONE;
sal_Bool bIsOrdinalSuffix = sal_False;
sal_Bool bRowFiltered = sal_False; //i89232
rInner = nIStart;
while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
{
if ( bGetCell )
{
if (bVertical) // rInner&:=nRow, rOuter&:=nCol
pSrcCell = aCol[nCol].GetCell( static_cast<SCROW>(nSource) );
else // rInner&:=nCol, rOuter&:=nRow
pSrcCell = aCol[nSource].GetCell( static_cast<SCROW>(nRow) );
bGetCell = sal_False;
if ( pSrcCell )
{
eCellType = pSrcCell->GetCellType();
switch ( eCellType )
{
case CELLTYPE_VALUE:
nVal = ((ScValueCell*)pSrcCell)->GetValue();
break;
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
if ( eCellType == CELLTYPE_STRING )
((ScStringCell*)pSrcCell)->GetString( aValue );
else
((ScEditCell*)pSrcCell)->GetString( aValue );
if ( !(nScFillModeMouseModifier & KEY_MOD1) && !bHasFiltered) //i89232
{
nCellDigits = 0; // look at each source cell individually
nHeadNoneTail = lcl_DecompValueString(
aValue, nStringValue, &nCellDigits );
bIsOrdinalSuffix = aValue.Equals(
ScGlobal::GetOrdinalSuffix( nStringValue));
}
break;
default:
{
// added to avoid warnings
}
}
}
else
eCellType = CELLTYPE_NONE;
}
//Modify for i89232
bRowFiltered = mpFilteredRows->getValue(nRow);
if (!bRowFiltered)
{
//End of i89232
switch (eCellType)
{
case CELLTYPE_VALUE:
aCol[nCol].Insert(static_cast<SCROW>(nRow), new ScValueCell(nVal + nDelta));
break;
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
if ( nHeadNoneTail )
{
// #i48009# with the "nStringValue+(long)nDelta" expression within the
// lcl_ValueString calls, gcc 3.4.1 makes wrong optimizations (ok in 3.4.3),
// so nNextValue is now calculated ahead.
sal_Int32 nNextValue = nStringValue+(sal_Int32)nDelta;
String aStr;
if ( nHeadNoneTail < 0 )
{
aCol[nCol].Insert( static_cast<SCROW>(nRow),
lcl_getSuffixCell( pDocument,
nNextValue, nCellDigits, aValue,
eCellType, bIsOrdinalSuffix));
}
else
{
aStr = aValue;
aStr += lcl_ValueString( nNextValue, nCellDigits );
aCol[nCol].Insert( static_cast<SCROW>(nRow),
new ScStringCell( aStr));
}
}
else
{
ScAddress aDestPos( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), nTab );
switch ( eCellType )
{
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
aCol[nCol].Insert( aDestPos.Row(), pSrcCell->CloneWithoutNote( *pDocument ) );
break;
default:
{
// added to avoid warnings
}
}
}
break;
case CELLTYPE_FORMULA :
FillFormula( nFormulaCounter, bFirst,
(ScFormulaCell*) pSrcCell,
static_cast<SCCOL>(nCol),
static_cast<SCROW>(nRow), (rInner == nIEnd) );
if (nFormulaCounter - nActFormCnt > nMaxFormCnt)
nMaxFormCnt = nFormulaCounter - nActFormCnt;
break;
default:
{
// added to avoid warnings
}
}
if (nSource==nISrcEnd)
{
if ( nSource != nISrcStart )
{ // mehr als eine Source-Zelle
nSource = nISrcStart;
bGetCell = sal_True;
}
if ( !(nScFillModeMouseModifier & KEY_MOD1) && !bHasFiltered ) //i89232
{
if ( bPositive )
nDelta += 1.0;
else
nDelta -= 1.0;
}
nFormulaCounter = nActFormCnt;
bFirst = sal_False;
}
else if (bPositive)
{
++nSource;
bGetCell = sal_True;
}
else
{
--nSource;
bGetCell = sal_True;
}
}
// Progress in der inneren Schleife nur bei teuren Zellen,
// und auch dann nicht fuer jede einzelne
++nProgress;
if ( eCellType == CELLTYPE_FORMULA || eCellType == CELLTYPE_EDIT )
rProgress.SetStateOnPercent( nProgress );
if (rInner == nIEnd) break;
if (bPositive) ++rInner; else --rInner;
}
rProgress.SetStateOnPercent( nProgress );
}
else
{
if (!bPositive)
nInc = -nInc;
double nEndVal = (nInc>=0.0) ? MAXDOUBLE : -MAXDOUBLE;
if (bVertical)
FillSeries( static_cast<SCCOL>(nCol), nRow1,
static_cast<SCCOL>(nCol), nRow2, nFillCount, eFillDir,
eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, sal_False,
rProgress );
else
FillSeries( nCol1, static_cast<SCROW>(nRow), nCol2,
static_cast<SCROW>(nRow), nFillCount, eFillDir,
eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, sal_False,
rProgress );
nProgress = rProgress.GetState();
}
nActFormCnt += nMaxFormCnt;
}
}
String ScTable::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY )
{
String aValue;
SCCOL nCol1 = rSource.aStart.Col();
SCROW nRow1 = rSource.aStart.Row();
SCCOL nCol2 = rSource.aEnd.Col();
SCROW nRow2 = rSource.aEnd.Row();
sal_Bool bOk = sal_True;
long nIndex = 0;
sal_uLong nSrcCount = 0;
FillDir eFillDir = FILL_TO_BOTTOM;
if ( nEndX == nCol2 && nEndY == nRow2 ) // leer
bOk = sal_False;
else if ( nEndX == nCol2 ) // nach oben/unten
{
nEndX = nCol2 = nCol1; // nur erste Spalte ansehen
nSrcCount = nRow2 - nRow1 + 1;
nIndex = ((long)nEndY) - nRow1; // kann negativ werden
if ( nEndY >= nRow1 )
eFillDir = FILL_TO_BOTTOM;
else
eFillDir = FILL_TO_TOP;
}
else if ( nEndY == nRow2 ) // nach links/rechts
{
nEndY = nRow2 = nRow1; // nur erste Zeile ansehen
nSrcCount = nCol2 - nCol1 + 1;
nIndex = ((long)nEndX) - nCol1; // kann negativ werden
if ( nEndX >= nCol1 )
eFillDir = FILL_TO_RIGHT;
else
eFillDir = FILL_TO_LEFT;
}
else // Richtung nicht eindeutig
bOk = sal_False;
if ( bOk )
{
FillCmd eFillCmd;
FillDateCmd eDateCmd;
double nInc;
sal_uInt16 nMinDigits;
ScUserListData* pListData = NULL;
sal_uInt16 nListIndex;
FillAnalyse(nCol1,nRow1, nCol2,nRow2, eFillCmd,eDateCmd, nInc,nMinDigits, pListData,nListIndex);
if ( pListData ) // benutzerdefinierte Liste
{
sal_uInt16 nListCount = pListData->GetSubCount();
if ( nListCount )
{
sal_uLong nSub = nSrcCount - 1; // nListIndex ist vom letzten Source-Eintrag
while ( nIndex < sal::static_int_cast<long>(nSub) )
nIndex += nListCount;
sal_uLong nPos = ( nListIndex + nIndex - nSub ) % nListCount;
aValue = pListData->GetSubStr(sal::static_int_cast<sal_uInt16>(nPos));
}
}
else if ( eFillCmd == FILL_SIMPLE ) // Auffuellen mit Muster
{
//Add for i89232
if ((eFillDir == FILL_TO_BOTTOM)||(eFillDir == FILL_TO_TOP))
{
long nBegin = 0;
long nEnd = 0;
if (nEndY > nRow1)
{
nBegin = nRow2+1;
nEnd = nEndY;
}
else
{
nBegin = nEndY;
nEnd = nRow1 -1;
}
long nNonFiltered = CountNonFilteredRows(nBegin, nEnd);
long nFiltered = nEnd + 1 - nBegin - nNonFiltered;
if (nIndex >0)
nIndex = nIndex - nFiltered;
else
nIndex = nIndex + nFiltered;
}
//End of i89232
long nPosIndex = nIndex;
while ( nPosIndex < 0 )
nPosIndex += nSrcCount;
sal_uLong nPos = nPosIndex % nSrcCount;
SCCOL nSrcX = nCol1;
SCROW nSrcY = nRow1;
if ( eFillDir == FILL_TO_TOP || eFillDir == FILL_TO_BOTTOM )
nSrcY = sal::static_int_cast<SCROW>( nSrcY + static_cast<SCROW>(nPos) );
else
nSrcX = sal::static_int_cast<SCCOL>( nSrcX + static_cast<SCCOL>(nPos) );
ScBaseCell* pCell = GetCell( nSrcX, nSrcY );
if ( pCell )
{
sal_Int32 nDelta;
if (nIndex >= 0)
nDelta = nIndex / nSrcCount;
else
nDelta = ( nIndex - nSrcCount + 1 ) / nSrcCount; // -1 -> -1
CellType eType = pCell->GetCellType();
switch ( eType )
{
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
{
if ( eType == CELLTYPE_STRING )
((ScStringCell*)pCell)->GetString( aValue );
else
((ScEditCell*)pCell)->GetString( aValue );
if ( !(nScFillModeMouseModifier & KEY_MOD1) && !IsDataFiltered() ) //i89232
{
sal_Int32 nVal;
sal_uInt16 nCellDigits = 0; // look at each source cell individually
short nFlag = lcl_DecompValueString( aValue, nVal, &nCellDigits );
if ( nFlag < 0 )
{
if (aValue.Equals( ScGlobal::GetOrdinalSuffix( nVal)))
aValue = ScGlobal::GetOrdinalSuffix( nVal + nDelta);
aValue.Insert( lcl_ValueString( nVal + nDelta, nCellDigits ), 0 );
}
else if ( nFlag > 0 )
aValue += lcl_ValueString( nVal + nDelta, nCellDigits );
}
}
break;
case CELLTYPE_VALUE:
{
// dabei kann's keinen Ueberlauf geben...
double nVal = ((ScValueCell*)pCell)->GetValue();
if ( !(nScFillModeMouseModifier & KEY_MOD1) && !IsDataFiltered() ) //i89232
nVal += (double) nDelta;
Color* pColor;
sal_uLong nNumFmt = GetNumberFormat( nSrcX, nSrcY );
pDocument->GetFormatTable()->
GetOutputString( nVal, nNumFmt, aValue, &pColor );
}
break;
// Formeln nicht
default:
{
// added to avoid warnings
}
}
}
}
else if ( eFillCmd == FILL_LINEAR || eFillCmd == FILL_DATE ) // Werte
{
sal_Bool bValueOk;
double nStart;
sal_Int32 nVal = 0;
short nHeadNoneTail = 0;
ScBaseCell* pCell = GetCell( nCol1, nRow1 );
if ( pCell )
{
CellType eType = pCell->GetCellType();
switch ( eType )
{
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
{
if ( eType == CELLTYPE_STRING )
((ScStringCell*)pCell)->GetString( aValue );
else
((ScEditCell*)pCell)->GetString( aValue );
nHeadNoneTail = lcl_DecompValueString( aValue, nVal );
if ( nHeadNoneTail )
nStart = (double)nVal;
else
nStart = 0.0;
}
break;
case CELLTYPE_VALUE:
nStart = ((ScValueCell*)pCell)->GetValue();
break;
case CELLTYPE_FORMULA:
nStart = ((ScFormulaCell*)pCell)->GetValue();
break;
default:
nStart = 0.0;
}
}
else
nStart = 0.0;
if ( eFillCmd == FILL_LINEAR )
{
double nAdd = nInc;
bValueOk = ( SubTotal::SafeMult( nAdd, (double) nIndex ) &&
SubTotal::SafePlus( nStart, nAdd ) );
}
else // Datum
{
bValueOk = sal_True;
sal_uInt16 nDayOfMonth = 0;
if ( nIndex < 0 )
{
nIndex = -nIndex;
nInc = -nInc;
}
for (long i=0; i<nIndex; i++)
IncDate( nStart, nDayOfMonth, nInc, eDateCmd );
}
if (bValueOk)
{
if ( nHeadNoneTail )
{
if ( nHeadNoneTail < 0 )
{
if (aValue.Equals( ScGlobal::GetOrdinalSuffix( nVal)))
aValue = ScGlobal::GetOrdinalSuffix( (sal_Int32)nStart );
aValue.Insert( lcl_ValueString( (sal_Int32)nStart, nMinDigits ), 0 );
}
else
aValue += lcl_ValueString( (sal_Int32)nStart, nMinDigits );
}
else
{
//! Zahlformat je nach Index holen?
Color* pColor;
sal_uLong nNumFmt = GetNumberFormat( nCol1, nRow1 );
pDocument->GetFormatTable()->
GetOutputString( nStart, nNumFmt, aValue, &pColor );
}
}
}
else
{
DBG_ERROR("GetAutoFillPreview: falscher Modus");
}
}
return aValue;
}
void ScTable::IncDate(double& rVal, sal_uInt16& nDayOfMonth, double nStep, FillDateCmd eCmd)
{
if (eCmd == FILL_DAY)
{
rVal += nStep;
return;
}
// class Date Grenzen
const sal_uInt16 nMinYear = 1583;
const sal_uInt16 nMaxYear = 9956;
long nInc = (long) nStep; // nach oben/unten begrenzen ?
Date aNullDate = *pDocument->GetFormatTable()->GetNullDate();
Date aDate = aNullDate;
aDate += (long)rVal;
switch (eCmd)
{
case FILL_WEEKDAY:
{
aDate += nInc;
DayOfWeek eWeekDay = aDate.GetDayOfWeek();
if (nInc >= 0)
{
if (eWeekDay == SATURDAY)
aDate += 2;
else if (eWeekDay == SUNDAY)
aDate += 1;
}
else
{
if (eWeekDay == SATURDAY)
aDate -= 1;
else if (eWeekDay == SUNDAY)
aDate -= 2;
}
}
break;
case FILL_MONTH:
{
if ( nDayOfMonth == 0 )
nDayOfMonth = aDate.GetDay(); // init
long nMonth = aDate.GetMonth();
long nYear = aDate.GetYear();
nMonth += nInc;
if (nInc >= 0)
{
if (nMonth > 12)
{
long nYAdd = (nMonth-1) / 12;
nMonth -= nYAdd * 12;
nYear += nYAdd;
}
}
else
{
if (nMonth < 1)
{
long nYAdd = 1 - nMonth / 12; // positiv
nMonth += nYAdd * 12;
nYear -= nYAdd;
}
}
if ( nYear < nMinYear )
aDate = Date( 1,1, nMinYear );
else if ( nYear > nMaxYear )
aDate = Date( 31,12, nMaxYear );
else
{
aDate.SetMonth((sal_uInt16) nMonth);
aDate.SetYear((sal_uInt16) nYear);
if ( nDayOfMonth > 28 )
aDate.SetDay( Min( aDate.GetDaysInMonth(), nDayOfMonth ) );
}
}
break;
case FILL_YEAR:
{
long nYear = aDate.GetYear();
nYear += nInc;
if ( nYear < nMinYear )
aDate = Date( 1,1, nMinYear );
else if ( nYear > nMaxYear )
aDate = Date( 31,12, nMaxYear );
else
aDate.SetYear((sal_uInt16) nYear);
}
break;
default:
{
// added to avoid warnings
}
}
rVal = aDate - aNullDate;
}
void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
sal_uLong nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
double nStepValue, double nMaxValue, sal_uInt16 nArgMinDigits,
sal_Bool bAttribs, ScProgress& rProgress )
{
//
// Richtung auswerten
//
sal_Bool bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP);
sal_Bool bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT);
sal_uLong nCol = 0;
sal_uLong nRow = 0;
sal_uLong& rInner = bVertical ? nRow : nCol; // Schleifenvariablen
sal_uLong& rOuter = bVertical ? nCol : nRow;
sal_uLong nOStart;
sal_uLong nOEnd;
sal_uLong nIStart;
sal_uLong nIEnd;
sal_uLong nISource;
if (bVertical)
{
nFillCount += (nRow2 - nRow1);
if (nFillCount == 0)
return;
nOStart = nCol1;
nOEnd = nCol2;
if (bPositive)
{
nISource = nRow1;
nIStart = nRow1 + 1;
nIEnd = nRow1 + nFillCount;
}
else
{
nISource = nRow2;
nIStart = nRow2 - 1;
nIEnd = nRow2 - nFillCount;
}
}
else
{
nFillCount += (nCol2 - nCol1);
if (nFillCount == 0)
return;
nOStart = nRow1;
nOEnd = nRow2;
if (bPositive)
{
nISource = nCol1;
nIStart = nCol1 + 1;
nIEnd = nCol1 + nFillCount;
}
else
{
nISource = nCol2;
nIStart = nCol2 - 1;
nIEnd = nCol2 - nFillCount;
}
}
sal_uLong nIMin = nIStart;
sal_uLong nIMax = nIEnd;
PutInOrder(nIMin,nIMax);
sal_uInt16 nDel = bAttribs ? IDF_AUTOFILL : (IDF_AUTOFILL & IDF_CONTENTS);
if (bVertical)
DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), nDel);
else
DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, nDel);
sal_uLong nProgress = rProgress.GetState();
//
// ausfuehren
//
sal_uLong nActFormCnt = 0;
for (rOuter = nOStart; rOuter <= nOEnd; rOuter++)
{
sal_Bool bFirst = sal_True;
rInner = nISource;
ScBaseCell* pSrcCell = aCol[nCol].GetCell(static_cast<SCROW>(nRow));
if (bVertical && bAttribs)
aCol[nCol].Resize( aCol[nCol].GetCellCount() + nFillCount );
if (bAttribs)
{
const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow));
if (bVertical)
aCol[nCol].SetPatternArea( static_cast<SCROW>(nIMin),
static_cast<SCROW>(nIMax), *pSrcPattern, sal_True );
else
for (SCCOL nAtCol = static_cast<SCCOL>(nIMin); nAtCol <= sal::static_int_cast<SCCOL>(nIMax); nAtCol++)
aCol[nAtCol].SetPattern(static_cast<SCROW>(nRow), *pSrcPattern, sal_True);
}
if (pSrcCell)
{
CellType eCellType = pSrcCell->GetCellType();
if (eFillCmd == FILL_SIMPLE) // kopieren
{
if (eCellType == CELLTYPE_FORMULA)
{
for (rInner = nIMin; rInner <= nIMax; rInner++)
{
sal_uLong nInd = nActFormCnt;
FillFormula(nInd, bFirst, (ScFormulaCell*)pSrcCell,
static_cast<SCCOL>(nCol), nRow, (rInner == nIEnd) );
bFirst = sal_False;
rProgress.SetStateOnPercent( ++nProgress );
}
}
else if (eCellType != CELLTYPE_NOTE)
{
for (rInner = nIMin; rInner <= nIMax; rInner++)
{
ScAddress aDestPos( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), nTab );
aCol[nCol].Insert( aDestPos.Row(), pSrcCell->CloneWithoutNote( *pDocument ) );
}
nProgress += nIMax - nIMin + 1;
rProgress.SetStateOnPercent( nProgress );
}
}
else if (eCellType == CELLTYPE_VALUE || eCellType == CELLTYPE_FORMULA)
{
double nStartVal;
if (eCellType == CELLTYPE_VALUE)
nStartVal = ((ScValueCell*)pSrcCell)->GetValue();
else
nStartVal = ((ScFormulaCell*)pSrcCell)->GetValue();
double nVal = nStartVal;
long nIndex = 0;
sal_Bool bError = sal_False;
sal_Bool bOverflow = sal_False;
sal_uInt16 nDayOfMonth = 0;
rInner = nIStart;
while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
{
if (!bError && !bOverflow)
{
switch (eFillCmd)
{
case FILL_LINEAR:
{
// #86365# use multiplication instead of repeated addition
// to avoid accumulating rounding errors
nVal = nStartVal;
double nAdd = nStepValue;
if ( !SubTotal::SafeMult( nAdd, (double) ++nIndex ) ||
!SubTotal::SafePlus( nVal, nAdd ) )
bError = sal_True;
}
break;
case FILL_GROWTH:
if (!SubTotal::SafeMult(nVal, nStepValue))
bError = sal_True;
break;
case FILL_DATE:
if (fabs(nVal) > _D_MAX_LONG_)
bError = sal_True;
else
IncDate(nVal, nDayOfMonth, nStepValue, eFillDateCmd);
break;
default:
{
// added to avoid warnings
}
}
if (nStepValue >= 0)
{
if (nVal > nMaxValue) // Zielwert erreicht?
{
nVal = nMaxValue;
bOverflow = sal_True;
}
}
else
{
if (nVal < nMaxValue)
{
nVal = nMaxValue;
bOverflow = sal_True;
}
}
}
if (bError)
aCol[nCol].SetError(static_cast<SCROW>(nRow), errNoValue);
else if (!bOverflow)
aCol[nCol].SetValue(static_cast<SCROW>(nRow), nVal);
if (rInner == nIEnd) break;
if (bPositive) ++rInner; else --rInner;
}
nProgress += nIMax - nIMin + 1;
rProgress.SetStateOnPercent( nProgress );
}
else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT)
{
if ( nStepValue >= 0 )
{
if ( nMaxValue >= (double)LONG_MAX )
nMaxValue = (double)LONG_MAX - 1;
}
else
{
if ( nMaxValue <= (double)LONG_MIN )
nMaxValue = (double)LONG_MIN + 1;
}
String aValue;
if (eCellType == CELLTYPE_STRING)
((ScStringCell*)pSrcCell)->GetString( aValue );
else
((ScEditCell*)pSrcCell)->GetString( aValue );
sal_Int32 nStringValue;
sal_uInt16 nMinDigits = nArgMinDigits;
short nHeadNoneTail = lcl_DecompValueString( aValue, nStringValue, &nMinDigits );
if ( nHeadNoneTail )
{
double nStartVal = (double)nStringValue;
double nVal = nStartVal;
long nIndex = 0;
sal_Bool bError = sal_False;
sal_Bool bOverflow = sal_False;
sal_Bool bIsOrdinalSuffix = aValue.Equals( ScGlobal::GetOrdinalSuffix(
(sal_Int32)nStartVal));
rInner = nIStart;
while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes
{
if (!bError && !bOverflow)
{
switch (eFillCmd)
{
case FILL_LINEAR:
{
// #86365# use multiplication instead of repeated addition
// to avoid accumulating rounding errors
nVal = nStartVal;
double nAdd = nStepValue;
if ( !SubTotal::SafeMult( nAdd, (double) ++nIndex ) ||
!SubTotal::SafePlus( nVal, nAdd ) )
bError = sal_True;
}
break;
case FILL_GROWTH:
if (!SubTotal::SafeMult(nVal, nStepValue))
bError = sal_True;
break;
default:
{
// added to avoid warnings
}
}
if (nStepValue >= 0)
{
if (nVal > nMaxValue) // Zielwert erreicht?
{
nVal = nMaxValue;
bOverflow = sal_True;
}
}
else
{
if (nVal < nMaxValue)
{
nVal = nMaxValue;
bOverflow = sal_True;
}
}
}
if (bError)
aCol[nCol].SetError(static_cast<SCROW>(nRow), errNoValue);
else if (!bOverflow)
{
nStringValue = (sal_Int32)nVal;
String aStr;
if ( nHeadNoneTail < 0 )
{
aCol[nCol].Insert( static_cast<SCROW>(nRow),
lcl_getSuffixCell( pDocument,
nStringValue, nMinDigits, aValue,
eCellType, bIsOrdinalSuffix ));
}
else
{
aStr = aValue;
aStr += lcl_ValueString( nStringValue, nMinDigits );
ScStringCell* pCell = new ScStringCell( aStr );
aCol[nCol].Insert( static_cast<SCROW>(nRow), pCell );
}
}
if (rInner == nIEnd) break;
if (bPositive) ++rInner; else --rInner;
}
}
nProgress += nIMax - nIMin + 1;
rProgress.SetStateOnPercent( nProgress );
}
}
else
{
nProgress += nIMax - nIMin + 1;
rProgress.SetStateOnPercent( nProgress );
}
++nActFormCnt;
}
}
void ScTable::Fill( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
sal_uLong nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
double nStepValue, double nMaxValue)
{
sal_uLong nProgCount;
if (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP)
nProgCount = nCol2 - nCol1 + 1;
else
nProgCount = nRow2 - nRow1 + 1;
nProgCount *= nFillCount;
ScProgress aProgress( pDocument->GetDocumentShell(),
ScGlobal::GetRscString(STR_FILL_SERIES_PROGRESS), nProgCount );
bSharedNameInserted = sal_False;
if (eFillCmd == FILL_AUTO)
FillAuto(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir, aProgress);
else
FillSeries(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir,
eFillCmd, eFillDateCmd, nStepValue, nMaxValue, 0, sal_True, aProgress);
if (bSharedNameInserted) // Wurde Shared-Name eingefuegt?
pDocument->GetRangeName()->SetSharedMaxIndex(
pDocument->GetRangeName()->GetSharedMaxIndex()+1); // dann hochzaehlen
}
void ScTable::AutoFormatArea(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
const ScPatternAttr& rAttr, sal_uInt16 nFormatNo)
{
ScAutoFormat* pAutoFormat = ScGlobal::GetAutoFormat();
if (pAutoFormat)
{
ScAutoFormatData* pData = (*pAutoFormat)[nFormatNo];
if (pData)
{
// ScPatternAttr aPattern(pDocument->GetPool());
// pData->FillToItemSet(nIndex, aPattern.GetItemSet(), *pDocument);
ApplyPatternArea(nStartCol, nStartRow, nEndCol, nEndRow, rAttr);
}
}
}
void ScTable::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
sal_uInt16 nFormatNo )
{
if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
{
ScAutoFormat* pAutoFormat = ScGlobal::GetAutoFormat();
if (pAutoFormat)
{
ScAutoFormatData* pData = (*pAutoFormat)[nFormatNo];
if (pData)
{
ScPatternAttr* pPatternAttrs[16];
for (sal_uInt8 i = 0; i < 16; ++i)
{
pPatternAttrs[i] = new ScPatternAttr(pDocument->GetPool());
pData->FillToItemSet(i, pPatternAttrs[i]->GetItemSet(), *pDocument);
}
SCCOL nCol = nStartCol;
SCROW nRow = nStartRow;
sal_uInt16 nIndex = 0;
// Linke obere Ecke
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
// Linke Spalte
if (pData->IsEqualData(4, 8))
AutoFormatArea(nStartCol, nStartRow + 1, nStartCol, nEndRow - 1, *pPatternAttrs[4], nFormatNo);
else
{
nIndex = 4;
for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
{
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
if (nIndex == 4)
nIndex = 8;
else
nIndex = 4;
}
}
// Linke untere Ecke
nRow = nEndRow;
nIndex = 12;
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
// Rechte obere Ecke
nCol = nEndCol;
nRow = nStartRow;
nIndex = 3;
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
// Rechte Spalte
if (pData->IsEqualData(7, 11))
AutoFormatArea(nEndCol, nStartRow + 1, nEndCol, nEndRow - 1, *pPatternAttrs[7], nFormatNo);
else
{
nIndex = 7;
for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
{
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
if (nIndex == 7)
nIndex = 11;
else
nIndex = 7;
}
}
// Rechte untere Ecke
nRow = nEndRow;
nIndex = 15;
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
nRow = nStartRow;
nIndex = 1;
for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
{
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
if (nIndex == 1)
nIndex = 2;
else
nIndex = 1;
}
// Untere Zeile
nRow = nEndRow;
nIndex = 13;
for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
{
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
if (nIndex == 13)
nIndex = 14;
else
nIndex = 13;
}
// Boddy
if ((pData->IsEqualData(5, 6)) && (pData->IsEqualData(9, 10)) && (pData->IsEqualData(5, 9)))
AutoFormatArea(nStartCol + 1, nStartRow + 1, nEndCol-1, nEndRow - 1, *pPatternAttrs[5], nFormatNo);
else
{
if ((pData->IsEqualData(5, 9)) && (pData->IsEqualData(6, 10)))
{
nIndex = 5;
for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
{
AutoFormatArea(nCol, nStartRow + 1, nCol, nEndRow - 1, *pPatternAttrs[nIndex], nFormatNo);
if (nIndex == 5)
nIndex = 6;
else
nIndex = 5;
}
}
else
{
nIndex = 5;
for (nCol = nStartCol + 1; nCol < nEndCol; nCol++)
{
for (nRow = nStartRow + 1; nRow < nEndRow; nRow++)
{
AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo);
if ((nIndex == 5) || (nIndex == 9))
{
if (nIndex == 5)
nIndex = 9;
else
nIndex = 5;
}
else
{
if (nIndex == 6)
nIndex = 10;
else
nIndex = 6;
}
} // for nRow
if ((nIndex == 5) || (nIndex == 9))
nIndex = 6;
else
nIndex = 5;
} // for nCol
} // if not equal Column
} // if not all equal
for (sal_uInt8 j = 0; j < 16; ++j)
delete pPatternAttrs[j];
} // if AutoFormatData != NULL
} // if AutoFormat != NULL
} // if ValidColRow
}
void ScTable::GetAutoFormatAttr(SCCOL nCol, SCROW nRow, sal_uInt16 nIndex, ScAutoFormatData& rData)
{
sal_uInt32 nFormatIndex = GetNumberFormat( nCol, nRow );
ScNumFormatAbbrev aNumFormat( nFormatIndex, *pDocument->GetFormatTable() );
rData.GetFromItemSet( nIndex, GetPattern( nCol, nRow )->GetItemSet(), aNumFormat );
}
#define LF_LEFT 1
#define LF_TOP 2
#define LF_RIGHT 4
#define LF_BOTTOM 8
#define LF_ALL (LF_LEFT | LF_TOP | LF_RIGHT | LF_BOTTOM)
void ScTable::GetAutoFormatFrame(SCCOL nCol, SCROW nRow, sal_uInt16 nFlags, sal_uInt16 nIndex, ScAutoFormatData& rData)
{
const SvxBoxItem* pTheBox = (SvxBoxItem*)GetAttr(nCol, nRow, ATTR_BORDER);
const SvxBoxItem* pLeftBox = (SvxBoxItem*)GetAttr(nCol - 1, nRow, ATTR_BORDER);
const SvxBoxItem* pTopBox = (SvxBoxItem*)GetAttr(nCol, nRow - 1, ATTR_BORDER);
const SvxBoxItem* pRightBox = (SvxBoxItem*)GetAttr(nCol + 1, nRow, ATTR_BORDER);
const SvxBoxItem* pBottomBox = (SvxBoxItem*)GetAttr(nCol, nRow + 1, ATTR_BORDER);
SvxBoxItem aBox( ATTR_BORDER );
if (nFlags & LF_LEFT)
{
if (pLeftBox)
{
if (ScHasPriority(pTheBox->GetLeft(), pLeftBox->GetRight()))
aBox.SetLine(pTheBox->GetLeft(), BOX_LINE_LEFT);
else
aBox.SetLine(pLeftBox->GetRight(), BOX_LINE_LEFT);
}
else
aBox.SetLine(pTheBox->GetLeft(), BOX_LINE_LEFT);
}
if (nFlags & LF_TOP)
{
if (pTopBox)
{
if (ScHasPriority(pTheBox->GetTop(), pTopBox->GetBottom()))
aBox.SetLine(pTheBox->GetTop(), BOX_LINE_TOP);
else
aBox.SetLine(pTopBox->GetBottom(), BOX_LINE_TOP);
}
else
aBox.SetLine(pTheBox->GetTop(), BOX_LINE_TOP);
}
if (nFlags & LF_RIGHT)
{
if (pRightBox)
{
if (ScHasPriority(pTheBox->GetRight(), pRightBox->GetLeft()))
aBox.SetLine(pTheBox->GetRight(), BOX_LINE_RIGHT);
else
aBox.SetLine(pRightBox->GetLeft(), BOX_LINE_RIGHT);
}
else
aBox.SetLine(pTheBox->GetRight(), BOX_LINE_RIGHT);
}
if (nFlags & LF_BOTTOM)
{
if (pBottomBox)
{
if (ScHasPriority(pTheBox->GetBottom(), pBottomBox->GetTop()))
aBox.SetLine(pTheBox->GetBottom(), BOX_LINE_BOTTOM);
else
aBox.SetLine(pBottomBox->GetTop(), BOX_LINE_BOTTOM);
}
else
aBox.SetLine(pTheBox->GetBottom(), BOX_LINE_BOTTOM);
}
rData.PutItem( nIndex, aBox );
}
void ScTable::GetAutoFormatData(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, ScAutoFormatData& rData)
{
if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow))
{
if ((nEndCol - nStartCol >= 3) && (nEndRow - nStartRow >= 3))
{
// Linke obere Ecke
GetAutoFormatAttr(nStartCol, nStartRow, 0, rData);
GetAutoFormatFrame(nStartCol, nStartRow, LF_ALL, 0, rData);
// Linke Spalte
GetAutoFormatAttr(nStartCol, nStartRow + 1, 4, rData);
GetAutoFormatAttr(nStartCol, nStartRow + 2, 8, rData);
GetAutoFormatFrame(nStartCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 4, rData);
if (nEndRow - nStartRow >= 4)
GetAutoFormatFrame(nStartCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 8, rData);
else
rData.CopyItem( 8, 4, ATTR_BORDER );
// Linke untere Ecke
GetAutoFormatAttr(nStartCol, nEndRow, 12, rData);
GetAutoFormatFrame(nStartCol, nEndRow, LF_ALL, 12, rData);
// Rechte obere Ecke
GetAutoFormatAttr(nEndCol, nStartRow, 3, rData);
GetAutoFormatFrame(nEndCol, nStartRow, LF_ALL, 3, rData);
// Rechte Spalte
GetAutoFormatAttr(nEndCol, nStartRow + 1, 7, rData);
GetAutoFormatAttr(nEndCol, nStartRow + 2, 11, rData);
GetAutoFormatFrame(nEndCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 7, rData);
if (nEndRow - nStartRow >= 4)
GetAutoFormatFrame(nEndCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 11, rData);
else
rData.CopyItem( 11, 7, ATTR_BORDER );
// Rechte untere Ecke
GetAutoFormatAttr(nEndCol, nEndRow, 15, rData);
GetAutoFormatFrame(nEndCol, nEndRow, LF_ALL, 15, rData);
// Ober Zeile
GetAutoFormatAttr(nStartCol + 1, nStartRow, 1, rData);
GetAutoFormatAttr(nStartCol + 2, nStartRow, 2, rData);
GetAutoFormatFrame(nStartCol + 1, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 1, rData);
if (nEndCol - nStartCol >= 4)
GetAutoFormatFrame(nStartCol + 2, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 2, rData);
else
rData.CopyItem( 2, 1, ATTR_BORDER );
// Untere Zeile
GetAutoFormatAttr(nStartCol + 1, nEndRow, 13, rData);
GetAutoFormatAttr(nStartCol + 2, nEndRow, 14, rData);
GetAutoFormatFrame(nStartCol + 1, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 13, rData);
if (nEndCol - nStartCol >= 4)
GetAutoFormatFrame(nStartCol + 2, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 14, rData);
else
rData.CopyItem( 14, 13, ATTR_BORDER );
// Body
GetAutoFormatAttr(nStartCol + 1, nStartRow + 1, 5, rData);
GetAutoFormatAttr(nStartCol + 2, nStartRow + 1, 6, rData);
GetAutoFormatAttr(nStartCol + 1, nStartRow + 2, 9, rData);
GetAutoFormatAttr(nStartCol + 2, nStartRow + 2, 10, rData);
GetAutoFormatFrame(nStartCol + 1, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 5, rData);
if ((nEndCol - nStartCol >= 4) && (nEndRow - nStartRow >= 4))
{
GetAutoFormatFrame(nStartCol + 2, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 6, rData);
GetAutoFormatFrame(nStartCol + 1, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 9, rData);
GetAutoFormatFrame(nStartCol + 2, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 10, rData);
}
else
{
rData.CopyItem( 6, 5, ATTR_BORDER );
rData.CopyItem( 9, 5, ATTR_BORDER );
rData.CopyItem( 10, 5, ATTR_BORDER );
}
}
}
}
void ScTable::SetError( SCCOL nCol, SCROW nRow, sal_uInt16 nError)
{
if (ValidColRow(nCol, nRow))
aCol[nCol].SetError( nRow, nError );
}
void ScTable::UpdateInsertTabAbs(SCTAB nTable)
{
for (SCCOL i=0; i <= MAXCOL; i++)
aCol[i].UpdateInsertTabAbs(nTable);
}
//UNUSED2008-05 sal_uInt16 ScTable::GetErrorData( SCCOL nCol, SCROW nRow ) const
//UNUSED2008-05 {
//UNUSED2008-05 if (ValidColRow(nCol,nRow))
//UNUSED2008-05 return aCol[nCol].GetErrorData( nRow );
//UNUSED2008-05 else
//UNUSED2008-05 return 0;
//UNUSED2008-05 }
sal_Bool ScTable::GetNextSpellingCell(SCCOL& rCol, SCROW& rRow, sal_Bool bInSel,
const ScMarkData& rMark) const
{
if (rRow == MAXROW+2) // Tabellenende
{
rRow = 0;
rCol = 0;
}
else
{
rRow++;
if (rRow == MAXROW+1)
{
rCol++;
rRow = 0;
}
}
if (rCol == MAXCOL+1)
return sal_True;
else
{
sal_Bool bStop = sal_False;
while (!bStop)
{
if (ValidCol(rCol))
{
bStop = aCol[rCol].GetNextSpellingCell(rRow, bInSel, rMark);
if (bStop)
return sal_True;
else /*if (rRow == MAXROW+1) */
{
rCol++;
rRow = 0;
}
}
else
return sal_True;
}
}
return sal_False;
}
void ScTable::RemoveAutoSpellObj()
{
for (SCCOL i=0; i <= MAXCOL; i++)
aCol[i].RemoveAutoSpellObj();
}
sal_Bool ScTable::TestTabRefAbs(SCTAB nTable)
{
sal_Bool bRet = sal_False;
for (SCCOL i=0; i <= MAXCOL; i++)
if (aCol[i].TestTabRefAbs(nTable))
bRet = sal_True;
return bRet;
}
void ScTable::CompileDBFormula()
{
for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileDBFormula();
}
void ScTable::CompileDBFormula( sal_Bool bCreateFormulaString )
{
for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileDBFormula( bCreateFormulaString );
}
void ScTable::CompileNameFormula( sal_Bool bCreateFormulaString )
{
for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileNameFormula( bCreateFormulaString );
}
void ScTable::CompileColRowNameFormula()
{
for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileColRowNameFormula();
}
SCSIZE ScTable::GetPatternCount( SCCOL nCol )
{
if( ValidCol( nCol ) )
return aCol[nCol].GetPatternCount();
else
return 0;
}
SCSIZE ScTable::GetPatternCount( SCCOL nCol, SCROW nRw1, SCROW nRw2 )
{
if( ValidCol( nCol ) && ValidRow( nRw1 ) && ValidRow( nRw2 ) )
return aCol[nCol].GetPatternCount( nRw1, nRw2 );
else
return 0;
}
bool ScTable::ReservedPatternCount( SCCOL nCol, SCSIZE nReserved )
{
if( ValidCol( nCol ) )
return aCol[nCol].ReservedPatternCount( nReserved );
else
return false;
}