You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kspread/kspread_autofill.cc

1218 lines
33 KiB

/* This file is part of the KDE project
Copyright 2005 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
Copyright 2002-2004 Ariya Hidayat <ariya@kde.org>
Copyright 2002-2003 Norbert Andres <nandres@web.de>
Copyright 2002 John Dailey <dailey@vt.edu>
Copyright 2001-2002 Philipp Mueller <philipp.mueller@gmx.de>
Copyright 2000-2002 Laurent Montel <montel@kde.org>
Copyright 2000-2001 Werner Trobin <trobin@kde.org>
Copyright 1999-2001 David Faure <faure@kde.org>
Copyright 1998-2000 Torben Weis <weis@kde.org>
Copyright 1998-1999 Stephan Kulow <coolo@kde.org>
Copyright 1998 Reginald Stadlbauer <reggie@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <math.h>
#include <tqregexp.h>
#include <kconfig.h>
#include <kdebug.h>
#include "kspread_doc.h"
#include "kspread_locale.h"
#include "kspread_sheet.h"
#include "kspread_undo.h"
#include "kspread_value.h"
#include "valueconverter.h"
#include "kspread_autofill.h"
using namespace KSpread;
TQStringList *AutoFillSequenceItem::month = 0L;
TQStringList *AutoFillSequenceItem::shortMonth = 0L;
TQStringList *AutoFillSequenceItem::day = 0L;
TQStringList *AutoFillSequenceItem::shortDay = 0L;
TQStringList *AutoFillSequenceItem::other = 0L;
/**********************************************************************************
*
* AutoFillDeltaSequence
*
**********************************************************************************/
AutoFillDeltaSequence::AutoFillDeltaSequence( AutoFillSequence *_first, AutoFillSequence *_next )
: m_ok(true),
m_sequence(0L)
{
if ( _first->count() != _next->count() )
{
m_ok = false;
return;
}
m_sequence = new TQMemArray<double> ( _first->count() );
AutoFillSequenceItem *item = _first->getFirst();
AutoFillSequenceItem *item2 = _next->getFirst();
int i = 0;
// for( item = _first->getFirst(); item != 0L && item2 != 0L; item = _first->getNext() );
for ( i = 0; i < _first->count(); i++ )
{
double d;
if ( !item->getDelta( item2, d ) )
{
m_ok = false;
return;
}
m_sequence->at( i++ ) = d;
item2 = _next->getNext();
item = _first->getNext();
}
}
AutoFillDeltaSequence::~AutoFillDeltaSequence()
{
delete m_sequence;
}
bool AutoFillDeltaSequence::equals( AutoFillDeltaSequence *_delta )
{
if ( m_sequence == 0L )
return false;
if ( _delta->getSequence() == 0L )
return false;
if ( m_sequence->size() != _delta->getSequence()->size() )
return false;
for ( unsigned int i = 0; i < m_sequence->size(); i++ )
{
if ( m_sequence->at( i ) != _delta->getSequence()->at( i ) )
return false;
}
return true;
}
double AutoFillDeltaSequence::getItemDelta( int _pos )
{
if ( m_sequence == 0L )
return 0.0;
return m_sequence->at( _pos );
}
/**********************************************************************************
*
* AutoFillSequenceItem
*
**********************************************************************************/
AutoFillSequenceItem::AutoFillSequenceItem( int _i )
{
m_IValue = _i;
m_Type = INTEGER;
}
AutoFillSequenceItem::AutoFillSequenceItem( double _d )
{
m_DValue = _d;
m_Type = FLOAT;
}
AutoFillSequenceItem::AutoFillSequenceItem( const TQString &_str )
{
m_String = _str;
m_Type = STRING;
if ( month == 0L )
{
month = new TQStringList();
month->append( i18n("January") );
month->append( i18n("February") );
month->append( i18n("March") );
month->append( i18n("April") );
month->append( i18n("May") );
month->append( i18n("June") );
month->append( i18n("July") );
month->append( i18n("August") );
month->append( i18n("September") );
month->append( i18n("October") );
month->append( i18n("November") );
month->append( i18n("December") );
}
if ( shortMonth == 0L )
{
shortMonth = new TQStringList();
shortMonth->append( i18n("Jan") );
shortMonth->append( i18n("Feb") );
shortMonth->append( i18n("Mar") );
shortMonth->append( i18n("Apr") );
shortMonth->append( i18n("May short", "May") );
shortMonth->append( i18n("Jun") );
shortMonth->append( i18n("Jul") );
shortMonth->append( i18n("Aug") );
shortMonth->append( i18n("Sep") );
shortMonth->append( i18n("Oct") );
shortMonth->append( i18n("Nov") );
shortMonth->append( i18n("Dec") );
}
if ( day == 0L )
{
day = new TQStringList();
day->append( i18n("Monday") );
day->append( i18n("Tuesday") );
day->append( i18n("Wednesday") );
day->append( i18n("Thursday") );
day->append( i18n("Friday") );
day->append( i18n("Saturday") );
day->append( i18n("Sunday") );
}
if ( shortDay == 0L )
{
shortDay = new TQStringList();
shortDay->append( i18n("Mon") );
shortDay->append( i18n("Tue") );
shortDay->append( i18n("Wed") );
shortDay->append( i18n("Thu") );
shortDay->append( i18n("Fri") );
shortDay->append( i18n("Sat") );
shortDay->append( i18n("Sun") );
}
if( other==0L)
{
// other=new TQStringList();
TDEConfig *config = Factory::global()->config();
config->setGroup( "Parameters" );
other=new TQStringList(config->readListEntry("Other list"));
}
if ( month->find( _str ) != month->end() )
{
m_Type = MONTH;
return;
}
if ( shortMonth->find( _str ) != shortMonth->end() )
{
m_Type = SHORTMONTH;
return;
}
if ( day->find( _str ) != day->end() )
{
m_Type = DAY;
return;
}
if ( shortDay->find( _str ) != shortDay->end() )
{
m_Type = SHORTDAY;
return;
}
if( other->find(_str)!=other->end())
{
m_Type = OTHER;
m_OtherBegin=0;
m_OtherEnd=other->count();
int index= other->findIndex(_str);
//find end and begin of qstringlist of other.
for ( TQStringList::Iterator it = other->find(_str); it != other->end();++it )
{
if((*it)=="\\")
{
m_OtherEnd=index;
break;
}
index++;
}
index= other->findIndex(_str);
for ( TQStringList::Iterator it = other->find(_str); it != other->begin();--it )
{
if((*it)=="\\")
{
m_OtherBegin=index;
break;
}
index--;
}
return;
}
if ( m_String[0] == '=' )
m_Type = FORMULA;
}
bool AutoFillSequenceItem::getDelta( AutoFillSequenceItem *seq, double &_delta )
{
if ( seq->getType() != m_Type )
return false;
switch( m_Type )
{
case INTEGER:
_delta = (double)( seq->getIValue() - m_IValue );
return true;
case FLOAT:
_delta = seq->getDValue() - m_DValue;
return true;
case FORMULA:
case STRING:
if ( m_String == seq->getString() )
{
_delta = 0.0;
return true;
}
return false;
case MONTH:
{
int i = month->findIndex( m_String );
int j = month->findIndex( seq->getString() );
int k = j;
if ( j + 1 == i )
_delta = -1.0;
else
_delta = ( double )( k - i );
return true;
}
case SHORTMONTH:
{
int i = shortMonth->findIndex( m_String );
int j = shortMonth->findIndex( seq->getString() );
int k = j;
if ( j + 1 == i )
_delta = -1.0;
else
_delta = ( double )( k - i );
return true;
}
case DAY:
{
int i = day->findIndex( m_String );
int j = day->findIndex( seq->getString() );
int k = j;
if ( j + 1 == i )
_delta = -1.0;
else
_delta = ( double )( k - i );
kdDebug() << m_String << " i: " << i << " j: " << j << " k: " << k << " delta: " << _delta << endl;
return true;
}
case SHORTDAY:
{
int i = shortDay->findIndex( m_String );
int j = shortDay->findIndex( seq->getString() );
int k = j;
if ( j + 1 == i )
_delta = -1.0;
else
_delta = ( double )( k - i );
return true;
}
case OTHER:
{
if( m_OtherEnd!= seq->getIOtherEnd() || m_OtherBegin!= seq->getIOtherBegin())
return false;
int i = other->findIndex( m_String );
int j = other->findIndex( seq->getString() );
int k = j;
if ( j < i )
k += (m_OtherEnd - m_OtherBegin - 1);
/*if ( j + 1 == i )
_delta = -1.0;
else*/
_delta = ( double )( k - i );
return true;
}
default:
return false;
}
}
TQString AutoFillSequenceItem::getSuccessor( int _no, double _delta )
{
TQString erg;
switch( m_Type )
{
case INTEGER:
erg.sprintf("%i", m_IValue + _no * (int)_delta );
break;
case FLOAT:
erg.sprintf("%f", m_DValue + (double)_no * _delta );
break;
case FORMULA:
case STRING:
erg = m_String;
break;
case MONTH:
{
int i = month->findIndex( m_String );
int j = i + _no * (int) _delta;
while (j < 0)
j += month->count();
int k = j % month->count();
erg = (*month->at( k ));
}
break;
case SHORTMONTH:
{
int i = shortMonth->findIndex( m_String );
int j = i + _no * (int) _delta;
while (j < 0)
j += shortMonth->count();
int k = j % shortMonth->count();
erg = (*shortMonth->at( k ));
}
break;
case DAY:
{
int i = day->findIndex( m_String );
int j = i + _no * (int) _delta;
while (j < 0)
j += day->count();
int k = j % day->count();
erg = (*day->at( k ));
}
break;
case SHORTDAY:
{
int i = shortDay->findIndex( m_String );
int j = i + _no * (int) _delta;
while (j < 0)
j += shortDay->count();
int k = j % shortDay->count();
erg = (*shortDay->at( k ));
}
break;
case OTHER:
{
int i = other->findIndex( m_String )-(m_OtherBegin+1);
int j = i + _no * (int) _delta;
int k = j % (m_OtherEnd - m_OtherBegin-1);
erg = (*other->at( (k+m_OtherBegin+1) ));
}
case TIME:
case DATE:
// gets never called but fixes a warning while compiling
break;
}
return TQString( erg );
}
TQString AutoFillSequenceItem::getPredecessor( int _no, double _delta )
{
TQString erg;
switch( m_Type )
{
case INTEGER:
erg.sprintf("%i", m_IValue - _no * (int)_delta );
break;
case FLOAT:
erg.sprintf("%f", m_DValue - (double)_no * _delta );
break;
case FORMULA:
case STRING:
erg = m_String;
break;
case MONTH:
{
int i = month->findIndex( m_String );
int j = i - _no * (int) _delta;
while ( j < 0 )
j += month->count();
int k = j % month->count();
erg = (*month->at( k ));
}
break;
case SHORTMONTH:
{
int i = shortMonth->findIndex( m_String );
int j = i - _no * (int) _delta;
while ( j < 0 )
j += shortMonth->count();
int k = j % shortMonth->count();
erg = (*shortMonth->at( k ));
}
break;
case DAY:
{
int i = day->findIndex( m_String );
int j = i - _no * (int) _delta;
while ( j < 0 )
j += day->count();
int k = j % day->count();
erg = (*day->at( k ));
}
break;
case SHORTDAY:
{
int i = shortDay->findIndex( m_String );
int j = i - _no * (int) _delta;
while ( j < 0 )
j += shortDay->count();
int k = j % shortDay->count();
erg = (*shortDay->at( k ));
}
break;
case OTHER:
{
int i = other->findIndex( m_String ) - (m_OtherBegin + 1);
int j = i - _no * (int) _delta;
while ( j < 0 )
j += (m_OtherEnd - m_OtherBegin - 1);
int k = j % (m_OtherEnd - m_OtherBegin - 1);
erg = (*other->at( (k + m_OtherBegin + 1) ));
}
case TIME:
case DATE:
// gets never called but fixes a warning while compiling
break;
}
return TQString( erg );
}
/**********************************************************************************
*
* AutoFillSequence
*
**********************************************************************************/
AutoFillSequence::AutoFillSequence( Cell *_cell )
{
sequence.setAutoDelete( true );
if ( _cell->isFormula() )
{
TQString d = _cell->encodeFormula();
sequence.append( new AutoFillSequenceItem( d ) );
}
else if ( _cell->value().isNumber() )
{
if ( floor( _cell->value().asFloat() ) == _cell->value().asFloat() )
{
sequence.append( new AutoFillSequenceItem( (int)_cell->value().asFloat()) );
}
else
sequence.append( new AutoFillSequenceItem(_cell->value().asFloat() ) );
}
else if ( !_cell->text().isEmpty() )
sequence.append( new AutoFillSequenceItem( _cell->text() ) );
}
bool AutoFillSequence::matches( AutoFillSequence* _seq, AutoFillDeltaSequence *_delta )
{
AutoFillDeltaSequence delta( this, _seq );
if ( !delta.isOk() )
return false;
if ( delta.equals( _delta ) )
return true;
return false;
}
void AutoFillSequence::fillCell( Cell *src, Cell *dest, AutoFillDeltaSequence *delta, int _block, bool down )
{
TQString erg = "";
// Special handling for formulas
if ( sequence.first() != 0L && sequence.first()->getType() == AutoFillSequenceItem::FORMULA )
{
TQString f = dest->decodeFormula( sequence.first()->getString() );
dest->setCellText( f );
dest->copyFormat( src );
return;
}
AutoFillSequenceItem *item;
int i = 0;
if (down)
{
for ( item = sequence.first(); item != 0L; item = sequence.next() )
erg += item->getSuccessor( _block, delta->getItemDelta( i++ ) );
}
else
{
for ( item = sequence.first(); item != 0L; item = sequence.next() )
erg += item->getPredecessor( _block, delta->getItemDelta( i++ ) );
}
dest->setCellText( erg );
dest->copyFormat( src );
}
/**********************************************************************************
*
* Sheet
*
**********************************************************************************/
void Sheet::autofill( TQRect &src, TQRect &dest )
{
if (src == dest)
{
return;
}
setRegionPaintDirty( dest );
doc()->emitBeginOperation();
if ( !doc()->undoLocked() )
{
UndoAutofill *undo = new UndoAutofill( doc(), this, dest );
doc()->addCommand( undo );
}
// disable the update of the max sroll range on each cell insertion
// Bug 124806: creating series takes extremely long time
enableScrollBarUpdates(false);
// Fill from left to right
if ( src.left() == dest.left() && src.right() < dest.right() )
{
for ( int y = src.top(); y <= src.bottom(); y++ )
{
int x;
TQPtrList<Cell> destList;
for ( x = src.right() + 1; x <= dest.right(); x++ )
destList.append( nonDefaultCell( x, y ) );
TQPtrList<Cell> srcList;
for ( x = src.left(); x <= src.right(); x++ )
srcList.append( cellAt( x, y ) );
TQPtrList<AutoFillSequence> seqList;
seqList.setAutoDelete( true );
for ( x = src.left(); x <= src.right(); x++ )
seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
fillSequence( srcList, destList, seqList );
}
}
// Fill from top to bottom
if ( src.top() == dest.top() && src.bottom() < dest.bottom() )
{
for ( int x = src.left(); x <= dest.right(); x++ )
{
int y;
TQPtrList<Cell> destList;
for ( y = src.bottom() + 1; y <= dest.bottom(); y++ )
destList.append( nonDefaultCell( x, y ) );
TQPtrList<Cell> srcList;
for ( y = src.top(); y <= src.bottom(); y++ )
{
srcList.append( cellAt( x, y ) );
}
TQPtrList<AutoFillSequence> seqList;
seqList.setAutoDelete( true );
for ( y = src.top(); y <= src.bottom(); y++ )
seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
fillSequence( srcList, destList, seqList );
}
}
// Fill from right to left
if ( ( src.left() == dest.right() || src.left() == dest.right() - 1) && src.right() >= dest.right() )
{
if ( src.left() != dest.right() )
dest.setRight( dest.right() - 1 );
for ( int y = dest.top(); y <= dest.bottom(); y++ )
{
int x;
TQPtrList<Cell> destList;
for ( x = dest.left(); x < src.left(); x++ )
{
destList.append( nonDefaultCell( x, y ) );
}
TQPtrList<Cell> srcList;
for ( x = src.left(); x <= src.right(); x++ )
{
srcList.append( cellAt( x, y ) );
}
TQPtrList<AutoFillSequence> seqList;
seqList.setAutoDelete( true );
for ( x = src.left(); x <= src.right(); x++ )
seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
fillSequence( srcList, destList, seqList, false );
}
}
// Fill from bottom to top
if ( (src.top() == dest.bottom() || src.top() == (dest.bottom() - 1) ) && src.bottom() >= dest.bottom() )
{
if (src.top() != dest.bottom() )
dest.setBottom(dest.bottom() - 1);
int startVal = TQMIN( dest.left(), src.left());
int endVal = TQMAX(src.right(), dest.right());
for ( int x = startVal; x <= endVal; x++ )
{
int y;
TQPtrList<Cell> destList;
for ( y = dest.top(); y < src.top(); y++ )
destList.append( nonDefaultCell( x, y ) );
TQPtrList<Cell> srcList;
for ( y = src.top(); y <= src.bottom(); ++y )
{
srcList.append( cellAt( x, y ) );
}
TQPtrList<AutoFillSequence> seqList;
seqList.setAutoDelete( true );
for ( y = src.top(); y <= src.bottom(); y++ )
seqList.append( new AutoFillSequence( cellAt( x, y ) ) );
fillSequence( srcList, destList, seqList, false );
}
}
// update the max sroll range ONCE here
enableScrollBarUpdates(true);
checkRangeHBorder(dest.right());
checkRangeVBorder(dest.bottom());
emit sig_updateView( this );
// doc()->emitEndOperation();
}
void Sheet::fillSequence( TQPtrList<Cell>& _srcList,
TQPtrList<Cell>& _destList,
TQPtrList<AutoFillSequence>& _seqList,
bool down)
{
doc()->emitBeginOperation(true);
/* try finding an interval to use to fill the sequence */
if (!FillSequenceWithInterval(_srcList, _destList, _seqList, down))
{
/* if no interval was found, just copy down through */
FillSequenceWithCopy(_srcList, _destList, down);
}
doc()->emitEndOperation();
}
TQVariant getDiff( const Value& value1, const Value& value2 , AutoFillSequenceItem::Type type )
{
if ( type == AutoFillSequenceItem::FLOAT )
return TQVariant( value2.asFloat() - value1.asFloat() );
if ( type == AutoFillSequenceItem::TIME || type == AutoFillSequenceItem::DATE )
return TQVariant( (int)( value2.asInteger() - value1.asInteger() ) );
return TQVariant( (int)0 );
// note: date and time difference can be calculated as
// the difference of the serial number
/* if( (type == AutoFillSequenceItem::FLOAT) ||
(type == AutoFillSequenceItem::DATE) ||
(type == AutoFillSequenceItem::TIME) )
return ( value2.asFloat() - value1.asFloat() );
else
return 0.0;*/
}
bool Sheet::FillSequenceWithInterval(TQPtrList<Cell>& _srcList,
TQPtrList<Cell>& _destList,
TQPtrList<AutoFillSequence>& _seqList,
bool down)
{
if (_srcList.first()->isFormula())
return false;
TQPtrList<AutoFillDeltaSequence> deltaList;
deltaList.setAutoDelete( true );
bool ok = false;
if ( _srcList.first()->value().isNumber() || _srcList.first()->isDate() || _srcList.first()->isTime() )
{
AutoFillSequenceItem::Type type;
TQValueVector< TQVariant > tmp( _seqList.count() ); /*= new TQValueList< TQVariant > ( _seqList.count() )*/;
TQValueVector< TQVariant > diff( _seqList.count() ); /*= new TQValueList< TQVariant > ( _seqList.count() )*/;
int p = -1;
int count = 0;
int tmpcount = 0;
Cell * cell = _srcList.first();
Cell * cell2 = _srcList.next();
bool singleCellOnly = (cell2 == 0);
if ( cell->isDate() )
type = AutoFillSequenceItem::DATE;
else if ( cell->isTime() )
type = AutoFillSequenceItem::TIME;
else if ( cell->value().isNumber() )
type = AutoFillSequenceItem::FLOAT;
else
return false; // Cannot happen due to if condition
while ( cell && (cell2 || singleCellOnly) )
{
Value cellValue = cell->value();
Value cell2Value;
//If we only have a single cell, the interval will depend upon the data type.
//- For numeric values, set the interval to 0 as we don't know what might be useful as a sequence
//- For time values, set the interval to one hour, as this will probably be the most useful setting
//- For date values, set the interval to one day, as this will probably be the most useful setting
//
//Note that the above options were chosen for consistency with Excel. Gnumeric (1.59) sets
//the interval to 0 for all types, OpenOffice.org (2.00) uses increments of 1.00, 1 hour and 1 day
//respectively
if (singleCellOnly)
{
if (type == AutoFillSequenceItem::FLOAT)
cell2Value = cellValue;
else if ( type == AutoFillSequenceItem::TIME)
cell2Value = Value( cellValue.asTime().addSecs( 60*60 ) );
else if ( type == AutoFillSequenceItem::DATE)
cell2Value = Value ( cellValue.asDate().addDays( 1 ) );
}
else
{
cell2Value = cell2->value();
// check if both cells contain the same type
if ( ( !cellValue.isNumber() )
|| ( cell2->isDate() && type != AutoFillSequenceItem::DATE )
|| ( cell2->isTime() && type != AutoFillSequenceItem::TIME ) )
{
count = 0;
ok = false;
break;
}
}
TQVariant delta = getDiff(cellValue , cell2Value , type );
if (count < 1)
{
p = count;
diff[ count++ ] = delta;
}
else
{
// same value again?
if (diff[ p ] == delta)
{
// store it somewhere else for the case we need it later
++p;
tmp[ tmpcount++ ] = delta;
}
else
{
// if we have saved values in another buffer we have to insert them first
if ( tmpcount > 0 )
{
for ( int i = 0; i < tmpcount; ++i )
{
diff[ count++ ] = tmp.at( i );
}
tmpcount = 0;
}
// insert the value
p = 0;
diff[ count++ ] = delta;
}
}
// check next cell pair
cell = cell2;
cell2 = _srcList.next();
}
// we have found something:
if (count > 0 && (tmpcount > 0 || count == 1))
{
TQVariant cellValue( (int) 0 );
Cell * dest;
Cell * src;
int i = tmpcount;
if (down)
{
dest = _destList.first();
src = _srcList.last();
}
else
{
dest = _destList.last();
src = _srcList.first();
i *= -1;
}
if ( type == AutoFillSequenceItem::FLOAT )
cellValue = src->value().asFloat();
else
cellValue = (int)src->value().asInteger();
TQString res;
// copy all the data
while (dest)
{
if (down)
{
while ( i >= count )
i -= count;
}
else
{
while ( i < 0)
i += count;
}
TQVariant currentDiff = diff.at( i );
if (cellValue.type() == TQVariant::Double)
if (down)
cellValue = cellValue.asDouble() + currentDiff.asDouble();
else
cellValue = cellValue.asDouble() - currentDiff.asDouble();
else
if (down)
cellValue = cellValue.asInt() + currentDiff.asInt();
else
cellValue = cellValue.asInt() - currentDiff.asInt();
if ( type == AutoFillSequenceItem::TIME)
{
Value timeValue = doc()->converter()->asTime( Value(cellValue.asInt()) );
Value stringValue = doc()->converter()->asString( timeValue );
dest->setCellText( stringValue.asString() );
}
else if ( type == AutoFillSequenceItem::DATE)
{
Value dateValue = doc()->converter()->asDate( Value(cellValue.asInt()) );
Value stringValue = doc()->converter()->asString( dateValue );
dest->setCellText( stringValue.asString() );
}
else
dest->setCellText( cellValue.asString() );
dest->copyFormat( src );
if (down)
{
++i;
dest = _destList.next();
src = _srcList.next();
}
else
{
--i;
dest = _destList.prev();
src = _srcList.prev();
}
if (!src)
src = _srcList.last();
}
ok = true;
}
else
{
ok = false;
}
// delete tmp;
// delete diff;
return ok;
}
// What is the interval (block)? If your sheet looks like this:
// 1 3 5 7 9
// then the interval has the length 1 and the delta list is [2].
// 2 200 3 300 4 400
// Here the interval has length 2 and the delta list is [1,100]
// How big is the interval. It is in the range from [2...n/2].
// The case of an interval of length n is handled below.
//
// We try to find the shortest interval.
for ( unsigned int step = 1; step <= _seqList.count() / 2; step++ )
{
kdDebug() << "Looking for interval: " << step << " seqList count: " << _seqList.count() << endl;
// If the interval is of length 'step' then the _seqList size must
// be a multiple of 'step'
if ( _seqList.count() % step == 0 )
{
// Be optimistic
ok = true;
deltaList.clear();
// Guess the delta by looking at cells 0...2*step-1
//
// Since the interval may be of length 'step' we calculate the delta
// between cells 0 and step, 1 and step+1, ...., step-1 and 2*step-1
for ( unsigned int t = 0; t < step; t++ )
{
deltaList.append( new AutoFillDeltaSequence( _seqList.at(t),
_seqList.at(t+step) ) );
ok = deltaList.getLast()->isOk();
}
/* Verify the delta by looking at cells step..._seqList.count()
We only looked at the cells 0 ... '2*step-1'.
Now test wether the cells from "(tst-1) * step + s" share the same delta
with the cell "tst * step + s" for all test=1..._seqList.count()/step
and for all s=0...step-1.
*/
for ( unsigned int tst = 1; ok && ( tst * step < _seqList.count() );
tst++ )
{
for ( unsigned int s = 0; ok && ( s < step ); s++ )
{
if ( !_seqList.at( (tst-1) * step + s )->
matches( _seqList.at( tst * step + s ), deltaList.at( s ) ) )
ok = false;
}
}
// Did we find a valid interval ?
if ( ok )
{
unsigned int s = 0;
// Amount of intervals (blocks)
int block = _seqList.count() / step;
// Start iterating with the first cell
Cell * cell;
if (down)
cell = _destList.first();
else
{
cell = _destList.last();
block -= (_seqList.count() - 1);
}
// Loop over all destination cells
while ( cell )
{
kdDebug() << "Valid interval, cell: " << cell->row() << " block: " << block << endl;
// End of block? -> start again from beginning
if (down)
{
if ( s == step )
{
++block;
s = 0;
}
}
else
{
if ( s >= step )
{
s = step - 1;
++block;
}
}
kdDebug() << "Step: " << step << " S: " << s << " Block " << block
<< " SeqList: " << _seqList.count()
<< " SrcList: " << _srcList.count() << " DeltaList: " << deltaList.count()
<< endl;
// Set the value of 'cell' by adding 'block' times the delta tp the
// value of cell 's'.
_seqList.at( s )->fillCell( _srcList.at( s ), cell,
deltaList.at( s ), block, down );
if (down)
{
// Next cell
cell = _destList.next();
++s;
}
else
{
// Previous cell
cell = _destList.prev();
--s;
}
}
}
}
}
return ok;
}
void Sheet::FillSequenceWithCopy(TQPtrList<Cell>& _srcList,
TQPtrList<Cell>& _destList,
bool down)
{
// We did not find any valid interval. So just copy over the marked
// area.
Cell * cell;
if (down)
cell = _destList.first();
else
cell = _destList.last();
int incr = 1;
unsigned int s = 0;
double factor = 1;
if (!down)
s = _srcList.count() - 1;
if ( _srcList.at( s )->value().isNumber() &&
!(_srcList.at( s )->isDate() || _srcList.at( s )->isTime() ) )
factor = _srcList.at( s )->value().asFloat();
while ( cell )
{
if (down)
{
if ( s == _srcList.count() )
s = 0;
}
else
{
if ( s >= _srcList.count() )
s = _srcList.count() - 1;
}
if ( !_srcList.at( s )->text().isEmpty() )
{
if ( _srcList.at( s )->isFormula() )
{
TQString d = _srcList.at( s )->encodeFormula();
cell->setCellText( cell->decodeFormula( d ) );
}
else if(_srcList.at( s )->value().isNumber() && _srcList.count()==1)
{
double val;
int format_type = _srcList.at( s )->formatType();
if ( format_type == Percentage_format )
{
factor = 0.01; // one percent
}
else if ( _srcList.at( s )->isTime() )
{
// FIXME this is a workaround to avoid those nasty one minute off
// "dragging down" time is inaccurate overa large lists!
// This is the best approximation I could find (raphael)
if (down)
{
// factor = 1.000002/24. + 0.000000001;
factor = 0.041666751;
}
else
{ //when dragging "up" the error must of course be the other way round
factor = 0.0416665;
}
}
if (!down)
val = (_srcList.at( s )->value().asFloat() - (incr * factor));
else
val = (_srcList.at( s )->value().asFloat() + (incr * factor));
TQString tmp;
tmp = tmp.setNum(val);
cell->setCellText( tmp );
++incr;
}
else if((AutoFillSequenceItem::month != 0L)
&& AutoFillSequenceItem::month->find( _srcList.at( s )->text()) != 0L
&& AutoFillSequenceItem::month->find( _srcList.at( s )->text()) != AutoFillSequenceItem::month->end()
&& _srcList.count() == 1)
{
TQString strMonth=_srcList.at( s )->text();
int i = AutoFillSequenceItem::month->findIndex( strMonth )+incr;
int k = (i) % AutoFillSequenceItem::month->count();
cell->setCellText((*AutoFillSequenceItem::month->at( k )));
incr++;
}
else if(AutoFillSequenceItem::day != 0L
&& AutoFillSequenceItem::day->find( _srcList.at( s )->text()) != 0L
&& AutoFillSequenceItem::day->find( _srcList.at( s )->text())
!= AutoFillSequenceItem::day->end()
&& _srcList.count()==1)
{
TQString strDay=_srcList.at( s )->text();
int i = AutoFillSequenceItem::day->findIndex( strDay )+incr;
int k = (i) % AutoFillSequenceItem::day->count();
cell->setCellText((*AutoFillSequenceItem::day->at( k )));
incr++;
}
else
{
TQRegExp number("(\\d+)");
int pos =number.search(_srcList.at( s )->text());
if( pos!=-1 )
{
TQString tmp=number.cap(1);
int num=tmp.toInt()+incr;
cell->setCellText(_srcList.at( s )->text().replace(number,TQString::number(num)));
++incr;
}
else if ( !_srcList.at( s )->link().isEmpty() )
{
cell->setCellText( _srcList.at( s )->text() );
cell->setLink( _srcList.at( s )->link() );
}
else
{
cell->setCellText( _srcList.at( s )->text() );
}
}
}
else
cell->setCellText( "" );
cell->copyFormat( _srcList.at( s ) );
if (down)
{
cell = _destList.next();
++s;
}
else
{
cell = _destList.prev();
--s;
}
}
return;
}