summaryrefslogtreecommitdiffstats
path: root/kspread/kspread_autofill.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kspread/kspread_autofill.cpp')
-rw-r--r--kspread/kspread_autofill.cpp1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/kspread/kspread_autofill.cpp b/kspread/kspread_autofill.cpp
new file mode 100644
index 000000000..e110e1adb
--- /dev/null
+++ b/kspread/kspread_autofill.cpp
@@ -0,0 +1,1217 @@
+/* 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 <tdeconfig.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;
+}