kspread

kspread_dlg_cons.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002-2003 Norbert Andres <nandres@web.de>
3 (C) 2002 Ariya Hidayat <ariya@kde.org>
4 (C) 2002 Philipp Mueller <philipp.mueller@gmx.de>
5 (C) 2002 John Dailey <dailey@vt.edu>
6 (C) 2000-2001 Werner Trobin <trobin@kde.org>
7 (C) 2000-2001 Laurent Montel <montel@kde.org>
8 (C) 1999-2002 David Faure <faure@kde.org>
9 (C) 1999 Stephan Kulow <coolo@kde.org>
10 (C) 1999 Reginald Stadlbauer <reggie@kde.org>
11 (C) 1998-1999 Torben Weis <weis@kde.org>
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Library General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Library General Public License for more details.
22
23 You should have received a copy of the GNU Library General Public License
24 along with this library; see the file COPYING.LIB. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27*/
28
29#include <assert.h>
30
31#include <tqcheckbox.h>
32#include <tqcombobox.h>
33#include <tqlabel.h>
34#include <tqlayout.h>
35#include <tqpushbutton.h>
36
37#include <kdebug.h>
38#include <kdialogbase.h>
39#include <tdemessagebox.h>
40
41#include "kspread_dlg_cons.h"
42
43#include <kspread_canvas.h>
44#include <kspread_doc.h>
45#include <kspread_global.h>
46#include <kspread_locale.h>
47#include "selection.h"
48#include <kspread_sheet.h>
49#include <kspread_util.h>
50#include <kspread_view.h>
51
52#include <formula.h>
53#include <valueconverter.h>
54
55using namespace KSpread;
56
57ConsolidateDialog::ConsolidateDialog( View* parent, const char* name )
58 : KDialogBase( parent, name, false, i18n("Consolidate"), Ok|Cancel )
59{
60 m_pView = parent;
61
62 TQWidget* page = new TQWidget( this );
63 setMainWidget( page );
64
65 TQGridLayout *grid1 = new TQGridLayout( page, 12, 2, marginHint(), spacingHint() );
66
67 TQLabel* tmpTQLabel;
68 tmpTQLabel = new TQLabel( page, "Label_1" );
69 grid1->addWidget(tmpTQLabel,0,0);
70 tmpTQLabel->setText( i18n("&Function:") );
71
72 m_pFunction = new TQComboBox( page );
73 grid1->addWidget(m_pFunction,1,0);
74 tmpTQLabel->setBuddy(m_pFunction);
75
76 m_pFunction->insertItem( i18n("Sum"), Sum );
77 m_pFunction->insertItem( i18n("Average"), Average );
78 m_pFunction->insertItem( i18n("Count"), Count );
79 m_pFunction->insertItem( i18n("Max"), Max );
80 m_pFunction->insertItem( i18n("Min"), Min );
81 m_pFunction->insertItem( i18n("Product"), Product );
82 m_pFunction->insertItem( i18n("Standard Deviation"), StdDev );
83 m_pFunction->insertItem( i18n("Variance"), Var );
84
85 tmpTQLabel = new TQLabel( page, "Label_1" );
86 tmpTQLabel->setText( i18n("Re&ference:") );
87 grid1->addWidget(tmpTQLabel,2,0);
88
89 m_pRef = new TQLineEdit( page );
90 grid1->addWidget(m_pRef,3,0);
91 tmpTQLabel->setBuddy(m_pRef);
92
93 tmpTQLabel = new TQLabel( page, "Label_1" );
94 grid1->addWidget(tmpTQLabel,4,0);
95 tmpTQLabel->setText( i18n("&Entered references:") );
96
97 m_pRefs = new TQListBox( page );
98 grid1->addMultiCellWidget( m_pRefs,5,8,0,0);
99 tmpTQLabel->setBuddy(m_pRefs);
100
101 m_pRow = new TQCheckBox( i18n("&Description in row"), page );
102 grid1->addWidget( m_pRow,9,0);
103 m_pCol = new TQCheckBox( i18n("De&scription in column"), page );
104 grid1->addWidget(m_pCol,10,0);
105 m_pCopy = new TQCheckBox( i18n("Co&py data"), page );
106 grid1->addWidget(m_pCopy,11,0);
107
108 m_pAdd = new TQPushButton( i18n("&Add"), page );
109 grid1->addWidget(m_pAdd,2,1);
110 m_pRemove = new TQPushButton( i18n("&Remove"), page );
111 grid1->addWidget(m_pRemove,3,1);
112
113
114 connect( m_pAdd, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotAdd() ) );
115 connect( m_pRemove, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotRemove() ) );
116 connect( m_pRef, TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( slotReturnPressed() ) );
117
118 connect(m_pView->selectionInfo(), TQ_SIGNAL(changed(const Region&)),
119 this, TQ_SLOT(slotSelectionChanged()));
120}
121
122ConsolidateDialog::~ConsolidateDialog()
123{
124 kdDebug(36001)<<"Consolidate::~Consolidate()\n";
125}
126
127enum Description { D_ROW, D_COL, D_NONE, D_BOTH };
128
129struct st_cell
130{
131 TQString xdesc;
132 TQString ydesc;
133 Cell* cell;
134 TQString sheet;
135 int x;
136 int y;
137};
138
139void ConsolidateDialog::slotOk()
140{
141 m_pView->doc()->emitBeginOperation( false );
142
143 Map *map = m_pView->doc()->map();
144
145 Sheet* sheet = m_pView->activeSheet();
146 int dx = m_pView->selectionInfo()->selection().left();
147 int dy = m_pView->selectionInfo()->selection().top();
148
149 TQString function;
150
151 switch( m_pFunction->currentItem() )
152 {
153 case Sum: function = "SUM"; break;
154 case Average: function = "AVERAGE"; break;
155 case Count: function = "COUNT"; break;
156 case Max: function = "MAX"; break;
157 case Min: function = "MIN"; break;
158 case Product: function = "PRODUCT"; break;
159 case StdDev: function = "STDDEV"; break;
160 case Var: function = "VARIANCE"; break;
161 default: break; // bad bad !
162 }
163
164 TQStringList r = refs();
165 TQValueList<Range> ranges;
166 TQStringList::Iterator s = r.begin();
167 for( ; s != r.end(); ++s )
168 {
169 Range r( *s, map );
170 // TODO: Check for valid
171 Q_ASSERT( r.isValid() );
172
173 if ( r.sheet() == 0 )
174 {
175 r.setSheet(sheet);
176 r.setSheetName(sheet->sheetName());
177 }
178 ranges.append( r );
179 }
180
181 Description desc;
182 if ( m_pRow->isChecked() && m_pCol->isChecked() )
183 desc = D_BOTH;
184 else if ( m_pRow->isChecked() )
185 desc = D_ROW;
186 else if ( m_pCol->isChecked() )
187 desc = D_COL;
188 else
189 desc = D_NONE;
190
191 // Check whether all ranges have same size
192 Q_ASSERT( ranges.count() > 0 );
193 TQValueList<Range>::Iterator it = ranges.begin();
194 int w = (*it).range().right() - (*it).range().left() + 1;
195 int h = (*it).range().bottom() - (*it).range().top() + 1;
196 if ( w <= ( ( desc == D_BOTH || desc == D_COL ) ? 1 : 0 ) ||
197 h <= ( ( desc == D_BOTH || desc == D_ROW ) ? 1 : 0 ) )
198 {
199 m_pView->slotUpdateView( m_pView->activeSheet() );
200 KMessageBox::error( this, i18n( "The range\n%1\nis too small" ).arg( *( r.begin() ) ));
201 return;
202 }
203
204 if( (*it).range().bottom()==KS_rowMax || (*it).range().right()== KS_colMax )
205 {
206 m_pView->slotUpdateView( m_pView->activeSheet() );
207 KMessageBox::error( this, i18n( "The range\n%1\nis too large" ).arg( *( r.begin() ) ));
208 return;
209 }
210
211 ++it;
212 int i = 1;
213 for( ; it != ranges.end(); ++it, i++ )
214 {
215 TQRect currentRange=(*it).range();
216
217 int w2 = currentRange.right() - currentRange.left() + 1;
218 int h2 = currentRange.bottom() - currentRange.top() + 1;
219
220 if(currentRange.bottom()==KS_rowMax || currentRange.right()== KS_colMax)
221 {
222 m_pView->slotUpdateView( m_pView->activeSheet() );
223 KMessageBox::error( this, i18n( "The range\n%1\nis too large" ).arg( r[i]));
224 return;
225 }
226 if ( ( desc == D_NONE && ( w != w2 || h != h2 ) ) ||
227 ( desc == D_ROW && h != h2 ) ||
228 ( desc == D_COL && w != w2 ) )
229 {
230 m_pView->slotUpdateView( m_pView->activeSheet() );
231 TQString tmp = i18n( "The ranges\n%1\nand\n%2\nhave different size").arg( *( r.begin() ) ).arg( r[i] );
232 KMessageBox::error( this, tmp);
233 return;
234 }
235 }
236
237 // Create the consolidation sheet
238 if ( desc == D_NONE )
239 {
240 // Check whether the destination is part of the source ...
241 TQRect dest;
242 dest.setCoords( dx, dy, dx + w - 1, dy + h - 1 );
243 it = ranges.begin();
244 for( ; it != ranges.end(); ++it )
245 {
246 Sheet *t = (*it).sheet();
247 Q_ASSERT( t );
248 TQRect r;
249
250 TQRect currentRange=(*it).range();
251
252 r.setCoords( currentRange.left(), currentRange.top(), currentRange.right(), currentRange.bottom() );
253 if ( t == sheet && r.intersects( dest ) )
254 {
255 m_pView->slotUpdateView( m_pView->activeSheet() );
256 TQString tmp( i18n("The source tables intersect with the destination table") );
257 KMessageBox::error( this, tmp);
258 return;
259 }
260 }
261
262 for( int x = 0; x < w; x++ )
263 {
264 for( int y = 0; y < h; y++ )
265 {
266 bool novalue=true;
267 TQString formula = "=" + function + "(";
268 it = ranges.begin();
269 for( ; it != ranges.end(); ++it )
270 {
271 Sheet *t = (*it).sheet();
272 assert( t );
273 Cell *c = t->cellAt( x + (*it).range().left(), y + (*it).range().top() );
274 if(!c->isDefault())
275 novalue=false;
276 if ( it != ranges.begin() )
277 formula += ";";
278 formula += (*it).sheetName() + "!";
279 formula += c->name();
280 }
281 formula += ")";
282
283 if(!novalue)
284 sheet->setText( dy + y, dx + x,
285 m_pCopy->isChecked() ? evaluate( formula, sheet ) : formula );
286 }
287 }
288 }
289 else if ( desc == D_ROW )
290 {
291 // Get list of all descriptions in the rows
292 TQStringList lst;
293 it = ranges.begin();
294 for( ; it != ranges.end(); ++it )
295 {
296 Sheet *t = (*it).sheet();
297 assert( t );
298// kdDebug(36001) << "FROM " << (*it).range.left() << " to " << (*it).range.right() << endl;
299 for( int x = (*it).range().left(); x <= (*it).range().right() ; ++x )
300 {
301 Cell *c = t->cellAt( x, (*it).range().top() );
302 if ( c )
303 {
304 TQString s = c->value().asString();
305 if ( !lst.contains( s ) )
306 lst.append( s );
307 }
308 }
309 }
310 lst.sort();
311
312 // Check whether the destination is part of the source ...
313 TQRect dest;
314 dest.setCoords( dx, dy, dx + lst.count() - 1, dy + h - 1 );
315 it = ranges.begin();
316 for( ; it != ranges.end(); ++it )
317 {
318 Sheet *t = (*it).sheet();
319 assert( t );
320 TQRect r;
321 TQRect currentRange=(*it).range();
322 r.setCoords( currentRange.left(), currentRange.top(), currentRange.right(), currentRange.bottom() );
323 if ( t == sheet && r.intersects( dest ) )
324 {
325 m_pView->slotUpdateView( m_pView->activeSheet() );
326 TQString tmp( i18n("The source tables intersect with the destination table") );
327 KMessageBox::error( this, tmp);
328 return;
329 }
330 }
331
332 // Now create the consolidation sheet
333 int x = 0;
334 TQStringList::Iterator s = lst.begin();
335 for( ; s != lst.end(); ++s, ++x )
336 {
337 sheet->setText( dy, dx + x, *s );
338
339 for( int y = 1; y < h; ++y )
340 {
341 int count = 0;
342 TQString formula = "=" + function + "(";
343 it = ranges.begin();
344 for( ; it != ranges.end(); ++it )
345 {
346 for( int i = (*it).range().left(); i <= (*it).range().right(); ++i )
347 {
348 Sheet *t = (*it).sheet();
349 assert( t );
350 Cell *c = t->cellAt( i, (*it).range().top() );
351 if ( c )
352 {
353 if ( c->value().asString() == *s )
354 {
355// Cell *c2 = t->cellAt( i, y + (*it).range.top() );
356 count++;
357 if ( it != ranges.begin() )
358 formula += ";";
359 formula += (*it).sheetName() + "!";
360 formula += Cell::name( i, y + (*it).range().top() );
361 }
362 }
363 }
364 }
365 formula += ")";
366
367 sheet->setText( dy + y, dx + x,
368 m_pCopy->isChecked() ? evaluate( formula, sheet ) : formula );
369 }
370 }
371 }
372 else if ( desc == D_COL )
373 {
374 // Get list of all descriptions in the columns
375 TQStringList lst;
376 it = ranges.begin();
377 for( ; it != ranges.end(); ++it )
378 {
379 Sheet *t = (*it).sheet();
380 assert( t );
381 for( int y = (*it).range().top(); y <= (*it).range().bottom() ; ++y )
382 {
383 Cell *c = t->cellAt( (*it).range().left(), y );
384 if ( c )
385 {
386 TQString s = c->value().asString();
387 if ( !s.isEmpty() && lst.find( s ) == lst.end() )
388 lst.append( s );
389 }
390 }
391 }
392 lst.sort();
393
394 // Check whether the destination is part of the source ...
395 TQRect dest;
396 dest.setCoords( dx, dy, dx + w - 1, dy + lst.count() - 1 );
397 it = ranges.begin();
398 for( ; it != ranges.end(); ++it )
399 {
400 Sheet *t = (*it).sheet();
401 assert( t );
402 TQRect r;
403 TQRect currentRange=(*it).range();
404 r.setCoords( currentRange.left(), currentRange.top(), currentRange.right(), currentRange.bottom() );
405 if ( t == sheet && r.intersects( dest ) )
406 {
407 m_pView->slotUpdateView( m_pView->activeSheet() );
408 TQString tmp( i18n("The source tables intersect with the destination table") );
409 KMessageBox::error( this, tmp);
410 return;
411 }
412 }
413
414 // Now create the consolidation sheet
415 int y = 0;
416 TQStringList::Iterator s = lst.begin();
417 for( ; s != lst.end(); ++s, ++y )
418 {
419 sheet->setText( dy + y, dx, *s );
420
421 for( int x = 1; x < w; ++x )
422 {
423 int count = 0;
424 TQString formula = "=" + function + "(";
425 it = ranges.begin();
426 for( ; it != ranges.end(); ++it )
427 {
428 for( int i = (*it).range().top(); i <= (*it).range().bottom(); i++ )
429 {
430 Sheet *t = (*it).sheet();
431 assert( t );
432 Cell *c = t->cellAt( (*it).range().left(), i );
433 if ( c )
434 {
435 TQString v = c->value().asString();
436 if ( !v.isEmpty() && *s == v )
437 {
438// Cell *c2 = t->cellAt( x + (*it).range.left(), i );
439 count++;
440 if ( it != ranges.begin() ) formula += ";";
441 formula += (*it).sheetName() + "!";
442 formula += Cell::name( i, y + (*it).range().top() );
443 }
444 }
445 }
446 }
447
448 formula += ")";
449
450 sheet->setText( dy + y, dx + x,
451 m_pCopy->isChecked() ? evaluate( formula, sheet ) : formula );
452 }
453 }
454 }
455 else if ( desc == D_BOTH )
456 {
457 // Get list of all descriptions in the columns
458 TQStringList cols;
459 it = ranges.begin();
460 for( ; it != ranges.end(); ++it )
461 {
462 Sheet *t = (*it).sheet();
463 assert( t );
464 for( int y = (*it).range().top() + 1; y <= (*it).range().bottom() ; ++y )
465 {
466 Cell *c = t->cellAt( (*it).range().left(), y );
467 if ( c )
468 {
469 TQString s = c->value().asString();
470 if ( !s.isEmpty() && cols.find( s ) == cols.end() )
471 cols.append( s );
472 }
473 }
474 }
475 cols.sort();
476
477 // Get list of all descriptions in the rows
478 TQStringList rows;
479 it = ranges.begin();
480 for( ; it != ranges.end(); ++it )
481 {
482 Sheet *t = (*it).sheet();
483 assert( t );
484 for( int x = (*it).range().left() + 1; x <= (*it).range().right() ; ++x )
485 {
486 Cell *c = t->cellAt( x, (*it).range().top() );
487 if ( c )
488 {
489 TQString s = c->value().asString();
490 if ( !s.isEmpty() && rows.find( s ) == rows.end() )
491 rows.append( s );
492 }
493 }
494 }
495 rows.sort();
496
497 // Check whether the destination is part of the source ...
498 TQRect dest;
499 dest.setCoords( dx, dy, dx + cols.count(), dy + rows.count() );
500 it = ranges.begin();
501 for( ; it != ranges.end(); ++it )
502 {
503 Sheet *t = (*it).sheet();
504 assert( t );
505 TQRect r;
506 TQRect currentRange=(*it).range();
507 r.setCoords( currentRange.left(), currentRange.top(), currentRange.right(), currentRange.bottom() );
508 if ( t == sheet && r.intersects( dest ) )
509 {
510 m_pView->slotUpdateView( m_pView->activeSheet() );
511 TQString tmp( i18n("The source tables intersect with the destination table") );
512 KMessageBox::error( this, tmp);
513 return;
514 }
515 }
516
517 // Fill the list with all interesting cells
518 TQValueList<st_cell> lst;
519 it = ranges.begin();
520 for( ; it != ranges.end(); ++it )
521 {
522 Sheet *t = (*it).sheet();
523 assert( t );
524 for( int x = (*it).range().left() + 1; x <= (*it).range().right() ; ++x )
525 {
526 Cell *c = t->cellAt( x, (*it).range().top() );
527 if ( c )
528 {
529 TQString ydesc = c->value().asString();
530 for( int y = (*it).range().top() + 1; y <= (*it).range().bottom() ; ++y )
531 {
532 Cell *c2 = t->cellAt( (*it).range().left(), y );
533 if ( c2 )
534 {
535 TQString xdesc = c2->value().asString();
536 Cell *c3 = t->cellAt( x, y );
537 if ( c3 && c3->value().isNumber() )
538 {
539 st_cell k;
540 k.xdesc = xdesc;
541 k.ydesc = ydesc;
542 k.cell = c3;
543 k.sheet = (*it).sheetName();
544 k.x = x;
545 k.y = y;
546 lst.append( k );
547 }
548 }
549 }
550 }
551 }
552 }
553
554 // Draw the row description
555 int i = 1;
556 TQStringList::Iterator s = rows.begin();
557 for( ; s != rows.end(); ++s, ++i )
558 sheet->setText( dy, dx + i, *s );
559
560 // Draw the column description
561 i = 1;
562 s = cols.begin();
563 for( ; s != cols.end(); ++s, ++i )
564 sheet->setText( dy + i, dx, *s );
565
566 // Draw the data
567 int x = 1;
568 TQStringList::Iterator ydesc = rows.begin();
569 for( ; ydesc != rows.end(); ++ydesc, x++ )
570 {
571 int y = 1;
572 TQStringList::Iterator xdesc = cols.begin();
573 for( ; xdesc != cols.end(); ++xdesc, y++ )
574 {
575 int count = 0;
576 TQString formula = "=" + function + "(";
577 TQValueList<st_cell>::Iterator lit = lst.begin();
578 for( ; lit != lst.end(); ++lit )
579 {
580 if ( (*lit).xdesc == *xdesc && (*lit).ydesc == *ydesc )
581 {
582 count++;
583 if ( it != ranges.begin() ) formula += ";";
584 formula += (*it).sheetName() + "!";
585 formula += Cell::name( i, y + (*it).range().top() );
586 }
587 }
588 formula += ")";
589
590 sheet->setText( dy + y, dx + x,
591 m_pCopy->isChecked() ? evaluate( formula, sheet ) : formula );
592 }
593 }
594 }
595 m_pView->updateEditWidget();
596 m_pView->slotUpdateView( m_pView->activeSheet() );
597 accept();
598 delete this;
599}
600
601void ConsolidateDialog::slotCancel()
602{
603 reject();
604 delete this;
605}
606
607void ConsolidateDialog::slotAdd()
608{
609 slotReturnPressed();
610}
611
612void ConsolidateDialog::slotRemove()
613{
614 int i = m_pRefs->currentItem();
615 if ( i < 0 )
616 return;
617
618 m_pRefs->removeItem( i );
619
620 if ( m_pRefs->count() == 0 )
621 actionButton( Ok )->setEnabled( false );
622}
623
624TQStringList ConsolidateDialog::refs()
625{
626 TQStringList list;
627 int c = m_pRefs->count();
628
629 for( int i = 0; i < c; i++ )
630 list.append( m_pRefs->text( i ) );
631
632 return list;
633}
634
635void ConsolidateDialog::slotSelectionChanged()
636{
637 if (!m_pView->selectionInfo()->isValid())
638 {
639 m_pRef->setText( "" );
640 return;
641 }
642
643 TQString area = m_pView->selectionInfo()->name();
644 m_pRef->setText( area );
645 m_pRef->setSelection( 0, area.length() );
646}
647
648void ConsolidateDialog::slotReturnPressed()
649{
650 TQString txt = m_pRef->text();
651
652 Range r( txt, m_pView->doc()->map() );
653 if ( !r.isValid() )
654 {
655 KMessageBox::error( this, i18n("The range\n%1\n is malformed").arg( txt ));
656 return;
657 }
658
659 if ( !txt.isEmpty() )
660 {
661 m_pRefs->insertItem( txt );
662 actionButton( Ok )->setEnabled( true );
663 }
664}
665
666void ConsolidateDialog::closeEvent ( TQCloseEvent * )
667{
668 delete this;
669}
670
671TQString ConsolidateDialog::evaluate( const TQString& formula, Sheet* sheet )
672{
673 TQString result = "###";
674 Formula *f = new Formula (sheet);
675 f->setExpression (formula);
676 if (!f->isValid()) {
677 delete f;
678 return result;
679 }
680
681 Value res = f->eval ();
682 delete f;
683 result = sheet->doc()->converter()->asString (res).asString ();
684 return result;
685}
686
687#include "kspread_dlg_cons.moc"
For every cell in the spread sheet there is a Cell object.
TQString name() const
Returns the name of the cell.
const Value value() const
Returns the value that this cell holds.
bool isDefault() const
Returns true if this is a default cell (with row and column equal to zero).
Class Formula encapsulates a formula for a cell.
Definition formula.h:239
void setExpression(const TQString &expr)
Sets the expression for this formula.
Definition formula.cpp:407
Value eval() const
Evaluates the formula and returns the result.
Definition formula.cpp:1172
bool isValid() const
Returns true if the specified expression is valid, i.e.
Definition formula.cpp:424
A map is a simple container for all sheets.
Definition kspread_map.h:59
Represents a region within a workbook.
The one for all class for points and ranges.
Definition region.h:44
Provides a wrapper for cell value.
bool isNumber() const
Returns true if the type of this value is either integer or floating-point.
TQString asString() const
Returns the string value of this value.
The View class displays a KSpread document.
The KSpread namespace.
Definition commands.h:39