1 /* This file is part of the KDE project
2    Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3    Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4    Copyright 1998,1999 Torben Weis <weis@kde.org>
5    Copyright 1999-2007 The KSpread Team <calligra-devel@kde.org>
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21 */
22 
23 // Local
24 #include "Sheet.h"
25 
26 #include <QApplication>
27 
28 #include <kcodecs.h>
29 
30 #include <KoShape.h>
31 #include <KoDocumentResourceManager.h>
32 #include <KoUnit.h>
33 #include <KoCanvasResourceIdentities.h>
34 
35 #include "CellStorage.h"
36 #include "Cluster.h"
37 #include "Damages.h"
38 #include "DependencyManager.h"
39 #include "DocBase.h"
40 #include "FormulaStorage.h"
41 #include "HeaderFooter.h"
42 #include "LoadingInfo.h"
43 #include "Map.h"
44 #include "NamedAreaManager.h"
45 #include "PrintSettings.h"
46 #include "RecalcManager.h"
47 #include "RowColumnFormat.h"
48 #include "RowFormatStorage.h"
49 #include "ShapeApplicationData.h"
50 #include "SheetPrint.h"
51 #include "SheetModel.h"
52 #include "StyleStorage.h"
53 #include "Validity.h"
54 #include "ValueConverter.h"
55 #include "ValueStorage.h"
56 #include "database/Filter.h"
57 
58 namespace Calligra
59 {
60 namespace Sheets
61 {
62 
createObjectName(const QString & sheetName)63 static QString createObjectName(const QString &sheetName)
64 {
65     QString objectName;
66     for (int i = 0; i < sheetName.count(); ++i) {
67         if (sheetName[i].isLetterOrNumber() || sheetName[i] == '_')
68             objectName.append(sheetName[i]);
69         else
70             objectName.append('_');
71     }
72     return objectName;
73 }
74 
75 
76 class Q_DECL_HIDDEN Sheet::Private
77 {
78 public:
Private(Sheet * sheet)79     Private(Sheet* sheet) : rows(sheet) {}
80 
81     Map* workbook;
82     SheetModel *model;
83 
84     QString name;
85 
86     Qt::LayoutDirection layoutDirection;
87 
88     // true if sheet is hidden
89     bool hide;
90 
91     bool showGrid;
92     bool showFormula;
93     bool showFormulaIndicator;
94     bool showCommentIndicator;
95     bool autoCalc;
96     bool lcMode;
97     bool showColumnNumber;
98     bool hideZero;
99     bool firstLetterUpper;
100 
101     // clusters to hold objects
102     CellStorage* cellStorage;
103     RowFormatStorage rows;
104     ColumnCluster columns;
105     QList<KoShape*> shapes;
106 
107     // hold the print object
108     SheetPrint* print;
109 
110     // Indicates whether the sheet should paint the page breaks.
111     // Doing so costs some time, so by default it should be turned off.
112     bool showPageOutline;
113 
114     // Max range of canvas in x and y direction.
115     //  Depends on KS_colMax/KS_rowMax and the width/height of all columns/rows
116     QSizeF documentSize;
117 
118     QImage backgroundImage;
119     Sheet::BackgroundImageProperties backgroundProperties;
120 };
121 
122 
Sheet(Map * map,const QString & sheetName)123 Sheet::Sheet(Map* map, const QString &sheetName)
124         : KoShapeUserData(map)
125         , KoShapeBasedDocumentBase()
126         , d(new Private(this))
127 {
128     d->workbook = map;
129     if (map->doc()) {
130         resourceManager()->setUndoStack(map->doc()->undoStack());
131         QVariant variant;
132         variant.setValue<void*>(map->doc()->sheetAccessModel());
133         resourceManager()->setResource(::Sheets::CanvasResource::AccessModel, variant);
134     }
135     d->model = new SheetModel(this);
136 
137     d->layoutDirection = QApplication::layoutDirection();
138 
139     d->name = sheetName;
140 
141     // Set a valid object name, so that we can offer scripting.
142     setObjectName(createObjectName(d->name));
143 
144     d->cellStorage = new CellStorage(this);
145     d->columns.setAutoDelete(true);
146 
147     d->documentSize = QSizeF(KS_colMax * d->workbook->defaultColumnFormat()->width(),
148                              KS_rowMax * d->workbook->defaultRowFormat()->height());
149 
150     d->hide = false;
151     d->showGrid = true;
152     d->showFormula = false;
153     d->showFormulaIndicator = false;
154     d->showCommentIndicator = true;
155     d->showPageOutline = false;
156 
157     d->lcMode = false;
158     d->showColumnNumber = false;
159     d->hideZero = false;
160     d->firstLetterUpper = false;
161     d->autoCalc = true;
162     d->print = new SheetPrint(this);
163 
164     // document size changes always trigger a visible size change
165     connect(this, SIGNAL(documentSizeChanged(QSizeF)), SIGNAL(visibleSizeChanged()));
166     // CellStorage connections
167     connect(d->cellStorage, SIGNAL(insertNamedArea(Region,QString)),
168             d->workbook->namedAreaManager(), SLOT(insert(Region,QString)));
169     connect(d->cellStorage, SIGNAL(namedAreaRemoved(QString)),
170             d->workbook->namedAreaManager(), SLOT(remove(QString)));
171 }
172 
Sheet(const Sheet & other)173 Sheet::Sheet(const Sheet &other)
174         : KoShapeUserData(other.d->workbook)
175         , KoShapeBasedDocumentBase()
176         , ProtectableObject(other)
177         , d(new Private(this))
178 {
179     d->workbook = other.d->workbook;
180     d->model = new SheetModel(this);
181 
182     // create a unique name
183     int i = 1;
184     do
185         d->name = other.d->name + QString("_%1").arg(i++);
186     while (d->workbook->findSheet(d->name));
187 
188     // Set a valid object name, so that we can offer scripting.
189     setObjectName(createObjectName(d->name));
190 
191     d->layoutDirection = other.d->layoutDirection;
192     d->hide = other.d->hide;
193     d->showGrid = other.d->showGrid;
194     d->showFormula = other.d->showFormula;
195     d->showFormulaIndicator = other.d->showFormulaIndicator;
196     d->showCommentIndicator = other.d->showCommentIndicator;
197     d->autoCalc = other.d->autoCalc;
198     d->lcMode = other.d->lcMode;
199     d->showColumnNumber = other.d->showColumnNumber;
200     d->hideZero = other.d->hideZero;
201     d->firstLetterUpper = other.d->firstLetterUpper;
202 
203     d->cellStorage = new CellStorage(*other.d->cellStorage, this);
204     d->rows = other.d->rows;
205     d->columns = other.d->columns;
206 
207     // flake
208 #if 0 // CALLIGRA_SHEETS_WIP_COPY_SHEET_(SHAPES)
209     //FIXME This does not work as copySettings does not work. Also createDefaultShapeAndInit without the correct settings can not work
210     //I think this should use ODF load/save for copying
211     KoShape* shape;
212     const QList<KoShape*> shapes = other.d->shapes;
213     for (int i = 0; i < shapes.count(); ++i) {
214         KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shapes[i]->shapeId());
215         if (factory) {
216             shape = factory->createDefaultShapeAndInit(0);
217             shape->copySettings(shapes[i]);
218             addShape(shape);
219         }
220     }
221 #endif // CALLIGRA_SHEETS_WIP_COPY_SHEET_(SHAPES)
222 
223     d->print = new SheetPrint(this); // FIXME = new SheetPrint(*other.d->print);
224 
225     d->showPageOutline = other.d->showPageOutline;
226     d->documentSize = other.d->documentSize;
227 }
228 
~Sheet()229 Sheet::~Sheet()
230 {
231     //Disable automatic recalculation of dependencies on this sheet to prevent crashes
232     //in certain situations:
233     //
234     //For example, suppose a cell in SheetB depends upon a cell in SheetA.  If the cell in SheetB is emptied
235     //after SheetA has already been deleted, the program would try to remove dependencies from the cell in SheetA
236     //causing a crash.
237     setAutoCalculationEnabled(false);
238 
239     delete d->print;
240     delete d->cellStorage;
241     qDeleteAll(d->shapes);
242     delete d;
243 }
244 
model() const245 QAbstractItemModel* Sheet::model() const
246 {
247     return d->model;
248 }
249 
sheetName() const250 QString Sheet::sheetName() const
251 {
252     return d->name;
253 }
254 
map() const255 Map* Sheet::map() const
256 {
257     return d->workbook;
258 }
259 
doc() const260 DocBase* Sheet::doc() const
261 {
262     return d->workbook->doc();
263 }
264 
addShape(KoShape * shape)265 void Sheet::addShape(KoShape* shape)
266 {
267     if (!shape)
268         return;
269     d->shapes.append(shape);
270     shape->setApplicationData(new ShapeApplicationData());
271     emit shapeAdded(this, shape);
272 }
273 
removeShape(KoShape * shape)274 void Sheet::removeShape(KoShape* shape)
275 {
276     if (!shape)
277         return;
278     d->shapes.removeAll(shape);
279     emit shapeRemoved(this, shape);
280 }
281 
deleteShapes()282 void Sheet::deleteShapes()
283 {
284     qDeleteAll(d->shapes);
285     d->shapes.clear();
286 }
287 
resourceManager() const288 KoDocumentResourceManager* Sheet::resourceManager() const
289 {
290     return map()->resourceManager();
291 }
292 
shapes() const293 QList<KoShape*> Sheet::shapes() const
294 {
295     return d->shapes;
296 }
297 
layoutDirection() const298 Qt::LayoutDirection Sheet::layoutDirection() const
299 {
300     return d->layoutDirection;
301 }
302 
setLayoutDirection(Qt::LayoutDirection dir)303 void Sheet::setLayoutDirection(Qt::LayoutDirection dir)
304 {
305     d->layoutDirection = dir;
306 }
307 
isHidden() const308 bool Sheet::isHidden() const
309 {
310     return d->hide;
311 }
312 
setHidden(bool hidden)313 void Sheet::setHidden(bool hidden)
314 {
315     d->hide = hidden;
316 }
317 
getShowGrid() const318 bool Sheet::getShowGrid() const
319 {
320     return d->showGrid;
321 }
322 
setShowGrid(bool _showGrid)323 void Sheet::setShowGrid(bool _showGrid)
324 {
325     d->showGrid = _showGrid;
326 }
327 
getShowFormula() const328 bool Sheet::getShowFormula() const
329 {
330     return d->showFormula;
331 }
332 
setShowFormula(bool _showFormula)333 void Sheet::setShowFormula(bool _showFormula)
334 {
335     d->showFormula = _showFormula;
336 }
337 
getShowFormulaIndicator() const338 bool Sheet::getShowFormulaIndicator() const
339 {
340     return d->showFormulaIndicator;
341 }
342 
setShowFormulaIndicator(bool _showFormulaIndicator)343 void Sheet::setShowFormulaIndicator(bool _showFormulaIndicator)
344 {
345     d->showFormulaIndicator = _showFormulaIndicator;
346 }
347 
getShowCommentIndicator() const348 bool Sheet::getShowCommentIndicator() const
349 {
350     return d->showCommentIndicator;
351 }
352 
setShowCommentIndicator(bool _indic)353 void Sheet::setShowCommentIndicator(bool _indic)
354 {
355     d->showCommentIndicator = _indic;
356 }
357 
getLcMode() const358 bool Sheet::getLcMode() const
359 {
360     return d->lcMode;
361 }
362 
setLcMode(bool _lcMode)363 void Sheet::setLcMode(bool _lcMode)
364 {
365     d->lcMode = _lcMode;
366 }
367 
isAutoCalculationEnabled() const368 bool Sheet::isAutoCalculationEnabled() const
369 {
370     return d->autoCalc;
371 }
372 
setAutoCalculationEnabled(bool enable)373 void Sheet::setAutoCalculationEnabled(bool enable)
374 {
375     //Avoid possible recalculation of dependencies if the auto calc setting hasn't changed
376     if (d->autoCalc == enable)
377         return;
378 
379     d->autoCalc = enable;
380     //If enabling automatic calculation, make sure that the dependencies are up-to-date
381     if (enable == true) {
382         map()->dependencyManager()->addSheet(this);
383         map()->recalcManager()->recalcSheet(this);
384     } else {
385         map()->dependencyManager()->removeSheet(this);
386     }
387 }
388 
getShowColumnNumber() const389 bool Sheet::getShowColumnNumber() const
390 {
391     return d->showColumnNumber;
392 }
393 
setShowColumnNumber(bool _showColumnNumber)394 void Sheet::setShowColumnNumber(bool _showColumnNumber)
395 {
396     d->showColumnNumber = _showColumnNumber;
397 }
398 
getHideZero() const399 bool Sheet::getHideZero() const
400 {
401     return d->hideZero;
402 }
403 
setHideZero(bool _hideZero)404 void Sheet::setHideZero(bool _hideZero)
405 {
406     d->hideZero = _hideZero;
407 }
408 
getFirstLetterUpper() const409 bool Sheet::getFirstLetterUpper() const
410 {
411     return d->firstLetterUpper;
412 }
413 
setFirstLetterUpper(bool _firstUpper)414 void Sheet::setFirstLetterUpper(bool _firstUpper)
415 {
416     d->firstLetterUpper = _firstUpper;
417 }
418 
isShowPageOutline() const419 bool Sheet::isShowPageOutline() const
420 {
421     return d->showPageOutline;
422 }
423 
columnFormat(int _column) const424 const ColumnFormat* Sheet::columnFormat(int _column) const
425 {
426     const ColumnFormat *p = d->columns.lookup(_column);
427     if (p != 0)
428         return p;
429 
430     return map()->defaultColumnFormat();
431 }
432 
433 // needed in loading code
nextColumn(int col) const434 ColumnFormat *Sheet::nextColumn(int col) const
435 {
436     return d->columns.next(col);
437 }
438 
cellStorage() const439 CellStorage* Sheet::cellStorage() const
440 {
441     return d->cellStorage;
442 }
443 
commentStorage() const444 const CommentStorage* Sheet::commentStorage() const
445 {
446     return d->cellStorage->commentStorage();
447 }
448 
conditionsStorage() const449 const ConditionsStorage* Sheet::conditionsStorage() const
450 {
451     return d->cellStorage->conditionsStorage();
452 }
453 
formulaStorage() const454 const FormulaStorage* Sheet::formulaStorage() const
455 {
456     return d->cellStorage->formulaStorage();
457 }
458 
fusionStorage() const459 const FusionStorage* Sheet::fusionStorage() const
460 {
461     return d->cellStorage->fusionStorage();
462 }
463 
linkStorage() const464 const LinkStorage* Sheet::linkStorage() const
465 {
466     return d->cellStorage->linkStorage();
467 }
468 
styleStorage() const469 const StyleStorage* Sheet::styleStorage() const
470 {
471     return d->cellStorage->styleStorage();
472 }
473 
validityStorage() const474 const ValidityStorage* Sheet::validityStorage() const
475 {
476     return d->cellStorage->validityStorage();
477 }
478 
valueStorage() const479 const ValueStorage* Sheet::valueStorage() const
480 {
481     return d->cellStorage->valueStorage();
482 }
483 
print() const484 SheetPrint* Sheet::print() const
485 {
486     return d->print;
487 }
488 
printSettings() const489 PrintSettings* Sheet::printSettings() const
490 {
491     return d->print->settings();
492 }
493 
setPrintSettings(const PrintSettings & settings)494 void Sheet::setPrintSettings(const PrintSettings &settings)
495 {
496     d->print->setSettings(settings);
497     // Repaint, if page borders are shown and this is the active sheet.
498     if (isShowPageOutline()) {
499         // Just repaint everything visible; no need to invalidate the visual cache.
500         map()->addDamage(new SheetDamage(this, SheetDamage::ContentChanged));
501     }
502 }
503 
headerFooter() const504 HeaderFooter *Sheet::headerFooter() const
505 {
506     return d->print->headerFooter();
507 }
508 
documentSize() const509 QSizeF Sheet::documentSize() const
510 {
511     return d->documentSize;
512 }
513 
adjustDocumentWidth(double deltaWidth)514 void Sheet::adjustDocumentWidth(double deltaWidth)
515 {
516     d->documentSize.rwidth() += deltaWidth;
517     emit documentSizeChanged(d->documentSize);
518 }
519 
adjustDocumentHeight(double deltaHeight)520 void Sheet::adjustDocumentHeight(double deltaHeight)
521 {
522     d->documentSize.rheight() += deltaHeight;
523     emit documentSizeChanged(d->documentSize);
524 }
525 
adjustCellAnchoredShapesX(qreal minX,qreal maxX,qreal delta)526 void Sheet::adjustCellAnchoredShapesX(qreal minX, qreal maxX, qreal delta)
527 {
528     foreach (KoShape* s, d->shapes) {
529         if (dynamic_cast<ShapeApplicationData*>(s->applicationData())->isAnchoredToCell()) {
530             if (s->position().x() >= minX && s->position().x() < maxX) {
531                 QPointF p = s->position();
532                 p.setX(qMax(minX, p.x() + delta));
533                 s->setPosition(p);
534             }
535         }
536     }
537 }
538 
adjustCellAnchoredShapesX(qreal delta,int firstCol,int lastCol)539 void Sheet::adjustCellAnchoredShapesX(qreal delta, int firstCol, int lastCol)
540 {
541     adjustCellAnchoredShapesX(columnPosition(firstCol), columnPosition(lastCol+1), delta);
542 }
543 
adjustCellAnchoredShapesY(qreal minY,qreal maxY,qreal delta)544 void Sheet::adjustCellAnchoredShapesY(qreal minY, qreal maxY, qreal delta)
545 {
546     foreach (KoShape* s, d->shapes) {
547         if (dynamic_cast<ShapeApplicationData*>(s->applicationData())->isAnchoredToCell()) {
548             if (s->position().y() >= minY && s->position().y() < maxY) {
549                 QPointF p = s->position();
550                 p.setY(qMax(minY, p.y() + delta));
551                 s->setPosition(p);
552             }
553         }
554     }
555 }
556 
adjustCellAnchoredShapesY(qreal delta,int firstRow,int lastRow)557 void Sheet::adjustCellAnchoredShapesY(qreal delta, int firstRow, int lastRow)
558 {
559     adjustCellAnchoredShapesY(rowPosition(firstRow), rowPosition(lastRow+1), delta);
560 }
561 
leftColumn(qreal _xpos,qreal & _left) const562 int Sheet::leftColumn(qreal _xpos, qreal &_left) const
563 {
564     _left = 0.0;
565     int col = 1;
566     double x = columnFormat(col)->visibleWidth();
567     while (x < _xpos && col < KS_colMax) {
568         _left += columnFormat(col)->visibleWidth();
569         x += columnFormat(++col)->visibleWidth();
570     }
571     return col;
572 }
573 
rightColumn(double _xpos) const574 int Sheet::rightColumn(double _xpos) const
575 {
576     int col = 1;
577     double x = columnFormat(col)->visibleWidth();
578     while (x <= _xpos && col < KS_colMax)
579         x += columnFormat(++col)->visibleWidth();
580     return col;
581 }
582 
topRow(qreal _ypos,qreal & _top) const583 int Sheet::topRow(qreal _ypos, qreal & _top) const
584 {
585     qreal top;
586     int row = rowFormats()->rowForPosition(_ypos, &top);
587     _top = top;
588     return row;
589 }
590 
bottomRow(double _ypos) const591 int Sheet::bottomRow(double _ypos) const
592 {
593     return rowFormats()->rowForPosition(_ypos+1e-9);
594 }
595 
cellCoordinatesToDocument(const QRect & cellRange) const596 QRectF Sheet::cellCoordinatesToDocument(const QRect& cellRange) const
597 {
598     // TODO Stefan: Rewrite to save some iterations over the columns/rows.
599     QRectF rect;
600     rect.setLeft(columnPosition(cellRange.left()));
601     rect.setRight(columnPosition(cellRange.right()) + columnFormat(cellRange.right())->width());
602     rect.setTop(rowPosition(cellRange.top()));
603     rect.setBottom(rowPosition(cellRange.bottom()) + rowFormats()->rowHeight(cellRange.bottom()));
604     return rect;
605 }
606 
documentToCellCoordinates(const QRectF & area) const607 QRect Sheet::documentToCellCoordinates(const QRectF &area) const
608 {
609     double width = 0.0;
610     int left = 0;
611     while (width <= area.left())
612         width += columnFormat(++left)->visibleWidth();
613     int right = left;
614     while (width < area.right())
615         width += columnFormat(++right)->visibleWidth();
616     int top = rowFormats()->rowForPosition(area.top());
617     int bottom = rowFormats()->rowForPosition(area.bottom());
618     return QRect(left, top, right - left + 1, bottom - top + 1);
619 }
620 
columnPosition(int _col) const621 double Sheet::columnPosition(int _col) const
622 {
623     const int max = qMin(_col, KS_colMax);
624     double x = 0.0;
625     for (int col = 1; col < max; ++col)
626         x += columnFormat(col)->visibleWidth();
627     return x;
628 }
629 
630 
rowPosition(int _row) const631 double Sheet::rowPosition(int _row) const
632 {
633     const int max = qMin(_row, KS_rowMax+1);
634     return rowFormats()->totalVisibleRowHeight(1, max-1);
635 }
636 
firstCol() const637 ColumnFormat* Sheet::firstCol() const
638 {
639     return d->columns.first();
640 }
641 
nonDefaultColumnFormat(int _column,bool force_creation)642 ColumnFormat* Sheet::nonDefaultColumnFormat(int _column, bool force_creation)
643 {
644     Q_ASSERT(_column >= 1 && _column <= KS_colMax);
645     ColumnFormat *p = d->columns.lookup(_column);
646     if (p != 0 || !force_creation)
647         return p;
648 
649     p = new ColumnFormat(*map()->defaultColumnFormat());
650     p->setSheet(this);
651     p->setColumn(_column);
652 
653     d->columns.insertElement(p, _column);
654 
655     return p;
656 }
657 
changeCellTabName(QString const & old_name,QString const & new_name)658 void Sheet::changeCellTabName(QString const & old_name, QString const & new_name)
659 {
660     for (int c = 0; c < formulaStorage()->count(); ++c) {
661         if (formulaStorage()->data(c).expression().contains(old_name)) {
662             int nb = formulaStorage()->data(c).expression().count(old_name + '!');
663             QString tmp = old_name + '!';
664             int len = tmp.length();
665             tmp = formulaStorage()->data(c).expression();
666 
667             for (int i = 0; i < nb; ++i) {
668                 int pos = tmp.indexOf(old_name + '!');
669                 tmp.replace(pos, len, new_name + '!');
670             }
671             Cell cell(this, formulaStorage()->col(c), formulaStorage()->row(c));
672             Formula formula(this, cell);
673             formula.setExpression(tmp);
674             cell.setFormula(formula);
675             cell.makeFormula();
676         }
677     }
678 }
679 
insertShiftRight(const QRect & rect)680 void Sheet::insertShiftRight(const QRect& rect)
681 {
682     foreach(Sheet* sheet, map()->sheetList()) {
683         for (int i = rect.top(); i <= rect.bottom(); ++i) {
684             sheet->changeNameCellRef(QPoint(rect.left(), i), false,
685                                      Sheet::ColumnInsert, sheetName(),
686                                      rect.right() - rect.left() + 1);
687         }
688     }
689 }
690 
insertShiftDown(const QRect & rect)691 void Sheet::insertShiftDown(const QRect& rect)
692 {
693     foreach(Sheet* sheet, map()->sheetList()) {
694         for (int i = rect.left(); i <= rect.right(); ++i) {
695             sheet->changeNameCellRef(QPoint(i, rect.top()), false,
696                                      Sheet::RowInsert, sheetName(),
697                                      rect.bottom() - rect.top() + 1);
698         }
699     }
700 }
701 
removeShiftUp(const QRect & rect)702 void Sheet::removeShiftUp(const QRect& rect)
703 {
704     foreach(Sheet* sheet, map()->sheetList()) {
705         for (int i = rect.left(); i <= rect.right(); ++i) {
706             sheet->changeNameCellRef(QPoint(i, rect.top()), false,
707                                      Sheet::RowRemove, sheetName(),
708                                      rect.bottom() - rect.top() + 1);
709         }
710     }
711 }
712 
removeShiftLeft(const QRect & rect)713 void Sheet::removeShiftLeft(const QRect& rect)
714 {
715     foreach(Sheet* sheet, map()->sheetList()) {
716         for (int i = rect.top(); i <= rect.bottom(); ++i) {
717             sheet->changeNameCellRef(QPoint(rect.left(), i), false,
718                                      Sheet::ColumnRemove, sheetName(),
719                                      rect.right() - rect.left() + 1);
720         }
721     }
722 }
723 
insertColumns(int col,int number)724 void Sheet::insertColumns(int col, int number)
725 {
726     double deltaWidth = 0.0;
727     for (int i = 0; i < number; i++) {
728         deltaWidth -= columnFormat(KS_colMax)->width();
729         d->columns.insertColumn(col);
730         deltaWidth += columnFormat(col + i)->width();
731     }
732     // Adjust document width (plus widths of new columns; minus widths of removed columns).
733     adjustDocumentWidth(deltaWidth);
734 
735     foreach(Sheet* sheet, map()->sheetList()) {
736         sheet->changeNameCellRef(QPoint(col, 1), true,
737                                  Sheet::ColumnInsert, sheetName(),
738                                  number);
739     }
740     //update print settings
741     d->print->insertColumn(col, number);
742 }
743 
insertRows(int row,int number)744 void Sheet::insertRows(int row, int number)
745 {
746     d->rows.insertRows(row, number);
747 
748     foreach(Sheet* sheet, map()->sheetList()) {
749         sheet->changeNameCellRef(QPoint(1, row), true,
750                                  Sheet::RowInsert, sheetName(),
751                                  number);
752     }
753     //update print settings
754     d->print->insertRow(row, number);
755 }
756 
removeColumns(int col,int number)757 void Sheet::removeColumns(int col, int number)
758 {
759     double deltaWidth = 0.0;
760     for (int i = 0; i < number; ++i) {
761         deltaWidth -= columnFormat(col)->width();
762         d->columns.removeColumn(col);
763         deltaWidth += columnFormat(KS_colMax)->width();
764     }
765     // Adjust document width (plus widths of new columns; minus widths of removed columns).
766     adjustDocumentWidth(deltaWidth);
767 
768     foreach(Sheet* sheet, map()->sheetList()) {
769         sheet->changeNameCellRef(QPoint(col, 1), true,
770                                  Sheet::ColumnRemove, sheetName(),
771                                  number);
772     }
773     //update print settings
774     d->print->removeColumn(col, number);
775 }
776 
removeRows(int row,int number)777 void Sheet::removeRows(int row, int number)
778 {
779     d->rows.removeRows(row, number);
780 
781     foreach(Sheet* sheet, map()->sheetList()) {
782         sheet->changeNameCellRef(QPoint(1, row), true,
783                                  Sheet::RowRemove, sheetName(),
784                                  number);
785     }
786 
787     //update print settings
788     d->print->removeRow(row, number);
789 }
790 
changeNameCellRefHelper(const QPoint & pos,bool fullRowOrColumn,ChangeRef ref,int nbCol,const QPoint & point,bool isColumnFixed,bool isRowFixed)791 QString Sheet::changeNameCellRefHelper(const QPoint& pos, bool fullRowOrColumn, ChangeRef ref,
792                                        int nbCol, const QPoint& point, bool isColumnFixed,
793                                        bool isRowFixed)
794 {
795     QString newPoint;
796     int col = point.x();
797     int row = point.y();
798     // update column
799     if (isColumnFixed)
800         newPoint.append('$');
801     if (ref == ColumnInsert &&
802             col + nbCol <= KS_colMax &&
803             col >= pos.x() &&    // Column after the new one : +1
804             (fullRowOrColumn || row == pos.y())) {  // All rows or just one
805         newPoint += Cell::columnName(col + nbCol);
806     } else if (ref == ColumnRemove &&
807                col > pos.x() &&    // Column after the deleted one : -1
808                (fullRowOrColumn || row == pos.y())) {  // All rows or just one
809         newPoint += Cell::columnName(col - nbCol);
810     } else
811         newPoint += Cell::columnName(col);
812 
813     // Update row
814     if (isRowFixed)
815         newPoint.append('$');
816     if (ref == RowInsert &&
817             row + nbCol <= KS_rowMax &&
818             row >= pos.y() &&   // Row after the new one : +1
819             (fullRowOrColumn || col == pos.x())) {  // All columns or just one
820         newPoint += QString::number(row + nbCol);
821     } else if (ref == RowRemove &&
822                row > pos.y() &&   // Row after the deleted one : -1
823                (fullRowOrColumn || col == pos.x())) {  // All columns or just one
824         newPoint += QString::number(row - nbCol);
825     } else
826         newPoint += QString::number(row);
827 
828     if (((ref == ColumnRemove
829             && (col >= pos.x() && col < pos.x() + nbCol) // Column is the deleted one : error
830             && (fullRowOrColumn || row == pos.y())) ||
831             (ref == RowRemove
832              && (row >= pos.y() && row < pos.y() + nbCol) // Row is the deleted one : error
833              && (fullRowOrColumn || col == pos.x())) ||
834             (ref == ColumnInsert
835              && col + nbCol > KS_colMax
836              && col >= pos.x()     // Column after the new one : +1
837              && (fullRowOrColumn || row == pos.y())) ||
838             (ref == RowInsert
839              && row + nbCol > KS_rowMax
840              && row >= pos.y() // Row after the new one : +1
841              && (fullRowOrColumn || col == pos.x())))) {
842         newPoint = '#' + i18n("Dependency") + '!';
843     }
844     return newPoint;
845 }
846 
changeNameCellRefHelper(const QPoint & pos,const QRect & rect,bool fullRowOrColumn,ChangeRef ref,int nbCol,const QPoint & point,bool isColumnFixed,bool isRowFixed)847 QString Sheet::changeNameCellRefHelper(const QPoint& pos, const QRect& rect, bool fullRowOrColumn, ChangeRef ref,
848                                        int nbCol, const QPoint& point, bool isColumnFixed,
849                                        bool isRowFixed)
850 {
851     const bool isFirstColumn = pos.x() == rect.left();
852     const bool isLastColumn = pos.x() == rect.right();
853     const bool isFirstRow = pos.y() == rect.top();
854     const bool isLastRow = pos.y() == rect.bottom();
855 
856     QString newPoint;
857     int col = point.x();
858     int row = point.y();
859     // update column
860     if (isColumnFixed)
861         newPoint.append('$');
862     if (ref == ColumnInsert &&
863             col + nbCol <= KS_colMax &&
864             col >= pos.x() &&    // Column after the new one : +1
865             (fullRowOrColumn || row == pos.y())) {  // All rows or just one
866         newPoint += Cell::columnName(col + nbCol);
867     } else if (ref == ColumnRemove &&
868                (col > pos.x() ||
869                 (col == pos.x() && isLastColumn)) &&    // Column after the deleted one : -1
870                (fullRowOrColumn || row == pos.y())) {  // All rows or just one
871         newPoint += Cell::columnName(col - nbCol);
872     } else
873         newPoint += Cell::columnName(col);
874 
875     // Update row
876     if (isRowFixed)
877         newPoint.append('$');
878     if (ref == RowInsert &&
879             row + nbCol <= KS_rowMax &&
880             row >= pos.y() &&   // Row after the new one : +1
881             (fullRowOrColumn || col == pos.x())) {  // All columns or just one
882         newPoint += QString::number(row + nbCol);
883     } else if (ref == RowRemove &&
884                (row > pos.y() ||
885                 (row == pos.y() && isLastRow)) &&   // Row after the deleted one : -1
886                (fullRowOrColumn || col == pos.x())) {  // All columns or just one
887         newPoint += QString::number(row - nbCol);
888     } else
889         newPoint += QString::number(row);
890 
891     if (((ref == ColumnRemove
892             && col == pos.x() // Column is the deleted one : error
893             && (fullRowOrColumn || row == pos.y())
894             && (isFirstColumn && isLastColumn)) ||
895             (ref == RowRemove
896              && row == pos.y() // Row is the deleted one : error
897              && (fullRowOrColumn || col == pos.x())
898              && (isFirstRow && isLastRow)) ||
899             (ref == ColumnInsert
900              && col + nbCol > KS_colMax
901              && col >= pos.x()     // Column after the new one : +1
902              && (fullRowOrColumn || row == pos.y())) ||
903             (ref == RowInsert
904              && row + nbCol > KS_rowMax
905              && row >= pos.y() // Row after the new one : +1
906              && (fullRowOrColumn || col == pos.x())))) {
907         newPoint = '#' + i18n("Dependency") + '!';
908     }
909     return newPoint;
910 }
911 
changeNameCellRef(const QPoint & pos,bool fullRowOrColumn,ChangeRef ref,const QString & tabname,int nbCol)912 void Sheet::changeNameCellRef(const QPoint& pos, bool fullRowOrColumn, ChangeRef ref,
913                               const QString& tabname, int nbCol)
914 {
915     for (int c = 0; c < formulaStorage()->count(); ++c) {
916         QString newText('=');
917         const Tokens tokens = formulaStorage()->data(c).tokens();
918         for (int t = 0; t < tokens.count(); ++t) {
919             const Token token = tokens[t];
920             switch (token.type()) {
921             case Token::Cell:
922             case Token::Range: {
923                 if (map()->namedAreaManager()->contains(token.text())) {
924                     newText.append(token.text()); // simply keep the area name
925                     break;
926                 }
927                 const Region region(token.text(), map());
928                 if (!region.isValid() || !region.isContiguous()) {
929                     newText.append(token.text());
930                     break;
931                 }
932                 if (!region.firstSheet() && tabname != sheetName()) {
933                     // nothing to do here
934                     newText.append(token.text());
935                     break;
936                 }
937                 // actually only one element in here, but we need extended access to the element
938                 Region::ConstIterator end(region.constEnd());
939                 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) {
940                     Region::Element* element = (*it);
941                     if (element->type() == Region::Element::Point) {
942                         if (element->sheet())
943                             newText.append(element->sheet()->sheetName() + '!');
944                         QString newPoint = changeNameCellRefHelper(pos, fullRowOrColumn, ref,
945                                            nbCol,
946                                            element->rect().topLeft(),
947                                            element->isColumnFixed(),
948                                            element->isRowFixed());
949                         newText.append(newPoint);
950                     } else { // (element->type() == Region::Element::Range)
951                         if (element->sheet())
952                             newText.append(element->sheet()->sheetName() + '!');
953                         QString newPoint;
954                         newPoint = changeNameCellRefHelper(pos, element->rect(), fullRowOrColumn, ref,
955                                                            nbCol, element->rect().topLeft(),
956                                                            element->isColumnFixed(),
957                                                            element->isRowFixed());
958                         newText.append(newPoint + ':');
959                         newPoint = changeNameCellRefHelper(pos, element->rect(), fullRowOrColumn, ref,
960                                                            nbCol, element->rect().bottomRight(),
961                                                            element->isColumnFixed(),
962                                                            element->isRowFixed());
963                         newText.append(newPoint);
964                     }
965                 }
966                 break;
967             }
968             default: {
969                 newText.append(token.text());
970                 break;
971             }
972             }
973         }
974 
975         Cell cell(this, formulaStorage()->col(c), formulaStorage()->row(c));
976         Formula formula(this, cell);
977         formula.setExpression(newText);
978         cell.setFormula(formula);
979     }
980 }
981 
982 // helper function for Sheet::areaIsEmpty
cellIsEmpty(const Cell & cell,TestType _type)983 bool Sheet::cellIsEmpty(const Cell& cell, TestType _type)
984 {
985     if (!cell.isPartOfMerged()) {
986         switch (_type) {
987         case Text :
988             if (!cell.userInput().isEmpty())
989                 return false;
990             break;
991         case Validity:
992             if (!cell.validity().isEmpty())
993                 return false;
994             break;
995         case Comment:
996             if (!cell.comment().isEmpty())
997                 return false;
998             break;
999         case ConditionalCellAttribute:
1000             if (cell.conditions().conditionList().count() > 0)
1001                 return false;
1002             break;
1003         }
1004     }
1005     return true;
1006 }
1007 
1008 // TODO: convert into a manipulator, similar to the Dilation one
areaIsEmpty(const Region & region,TestType _type)1009 bool Sheet::areaIsEmpty(const Region& region, TestType _type)
1010 {
1011     Region::ConstIterator endOfList = region.constEnd();
1012     for (Region::ConstIterator it = region.constBegin(); it != endOfList; ++it) {
1013         QRect range = (*it)->rect();
1014         // Complete rows selected ?
1015         if ((*it)->isRow()) {
1016             for (int row = range.top(); row <= range.bottom(); ++row) {
1017                 Cell cell = d->cellStorage->firstInRow(row);
1018                 while (!cell.isNull()) {
1019                     if (!cellIsEmpty(cell, _type))
1020                         return false;
1021                     cell = d->cellStorage->nextInRow(cell.column(), row);
1022                 }
1023             }
1024         }
1025         // Complete columns selected ?
1026         else if ((*it)->isColumn()) {
1027             for (int col = range.left(); col <= range.right(); ++col) {
1028                 Cell cell = d->cellStorage->firstInColumn(col);
1029                 while (!cell.isNull()) {
1030                     if (!cellIsEmpty(cell, _type))
1031                         return false;
1032                     cell = d->cellStorage->nextInColumn(col, cell.row());
1033                 }
1034             }
1035         } else {
1036             Cell cell;
1037             int right  = range.right();
1038             int bottom = range.bottom();
1039             for (int x = range.left(); x <= right; ++x)
1040                 for (int y = range.top(); y <= bottom; ++y) {
1041                     cell = Cell(this, x, y);
1042                     if (!cellIsEmpty(cell, _type))
1043                         return false;
1044                 }
1045         }
1046     }
1047     return true;
1048 }
1049 
saveXML(QDomDocument & dd)1050 QDomElement Sheet::saveXML(QDomDocument& dd)
1051 {
1052     QDomElement sheet = dd.createElement("table");
1053 
1054     // backward compatibility
1055     QString sheetName;
1056     for (int i = 0; i < d->name.count(); ++i) {
1057         if (d->name[i].isLetterOrNumber() || d->name[i] == ' ' || d->name[i] == '.')
1058             sheetName.append(d->name[i]);
1059         else
1060             sheetName.append('_');
1061     }
1062     sheet.setAttribute("name", sheetName);
1063 
1064     //Laurent: for oasis format I think that we must use style:direction...
1065     sheet.setAttribute("layoutDirection", (layoutDirection() == Qt::RightToLeft) ? "rtl" : "ltr");
1066     sheet.setAttribute("columnnumber", QString::number((int)getShowColumnNumber()));
1067     sheet.setAttribute("borders", QString::number((int)isShowPageOutline()));
1068     sheet.setAttribute("hide", QString::number((int)isHidden()));
1069     sheet.setAttribute("hidezero", QString::number((int)getHideZero()));
1070     sheet.setAttribute("firstletterupper", QString::number((int)getFirstLetterUpper()));
1071     sheet.setAttribute("grid", QString::number((int)getShowGrid()));
1072     sheet.setAttribute("printGrid", QString::number((int)print()->settings()->printGrid()));
1073     sheet.setAttribute("printCommentIndicator", QString::number((int)print()->settings()->printCommentIndicator()));
1074     sheet.setAttribute("printFormulaIndicator", QString::number((int)print()->settings()->printFormulaIndicator()));
1075     sheet.setAttribute("showFormula", QString::number((int)getShowFormula()));
1076     sheet.setAttribute("showFormulaIndicator", QString::number((int)getShowFormulaIndicator()));
1077     sheet.setAttribute("showCommentIndicator", QString::number((int)getShowCommentIndicator()));
1078     sheet.setAttribute("lcmode", QString::number((int)getLcMode()));
1079     sheet.setAttribute("autoCalc", QString::number((int)isAutoCalculationEnabled()));
1080     sheet.setAttribute("borders1.2", "1");
1081     QByteArray pwd;
1082     password(pwd);
1083     if (!pwd.isNull()) {
1084         if (pwd.size() > 0) {
1085             QByteArray str = KCodecs::base64Encode(pwd);
1086             sheet.setAttribute("protected", QString(str.data()));
1087         } else
1088             sheet.setAttribute("protected", "");
1089     }
1090 
1091     // paper parameters
1092     QDomElement paper = dd.createElement("paper");
1093     paper.setAttribute("format", printSettings()->paperFormatString());
1094     paper.setAttribute("orientation", printSettings()->orientationString());
1095     sheet.appendChild(paper);
1096 
1097     QDomElement borders = dd.createElement("borders");
1098     KoPageLayout pageLayout = print()->settings()->pageLayout();
1099     borders.setAttribute("left", QString::number(pageLayout.leftMargin));
1100     borders.setAttribute("top", QString::number(pageLayout.topMargin));
1101     borders.setAttribute("right", QString::number(pageLayout.rightMargin));
1102     borders.setAttribute("bottom", QString::number(pageLayout.bottomMargin));
1103     paper.appendChild(borders);
1104 
1105     QDomElement head = dd.createElement("head");
1106     paper.appendChild(head);
1107     if (!print()->headerFooter()->headLeft().isEmpty()) {
1108         QDomElement left = dd.createElement("left");
1109         head.appendChild(left);
1110         left.appendChild(dd.createTextNode(print()->headerFooter()->headLeft()));
1111     }
1112     if (!print()->headerFooter()->headMid().isEmpty()) {
1113         QDomElement center = dd.createElement("center");
1114         head.appendChild(center);
1115         center.appendChild(dd.createTextNode(print()->headerFooter()->headMid()));
1116     }
1117     if (!print()->headerFooter()->headRight().isEmpty()) {
1118         QDomElement right = dd.createElement("right");
1119         head.appendChild(right);
1120         right.appendChild(dd.createTextNode(print()->headerFooter()->headRight()));
1121     }
1122     QDomElement foot = dd.createElement("foot");
1123     paper.appendChild(foot);
1124     if (!print()->headerFooter()->footLeft().isEmpty()) {
1125         QDomElement left = dd.createElement("left");
1126         foot.appendChild(left);
1127         left.appendChild(dd.createTextNode(print()->headerFooter()->footLeft()));
1128     }
1129     if (!print()->headerFooter()->footMid().isEmpty()) {
1130         QDomElement center = dd.createElement("center");
1131         foot.appendChild(center);
1132         center.appendChild(dd.createTextNode(print()->headerFooter()->footMid()));
1133     }
1134     if (!print()->headerFooter()->footRight().isEmpty()) {
1135         QDomElement right = dd.createElement("right");
1136         foot.appendChild(right);
1137         right.appendChild(dd.createTextNode(print()->headerFooter()->footRight()));
1138     }
1139 
1140     // print range
1141     QDomElement printrange = dd.createElement("printrange-rect");
1142     QRect _printRange = printSettings()->printRegion().lastRange();
1143     int left = _printRange.left();
1144     int right = _printRange.right();
1145     int top = _printRange.top();
1146     int bottom = _printRange.bottom();
1147     //If whole rows are selected, then we store zeros, as KS_colMax may change in future
1148     if (left == 1 && right == KS_colMax) {
1149         left = 0;
1150         right = 0;
1151     }
1152     //If whole columns are selected, then we store zeros, as KS_rowMax may change in future
1153     if (top == 1 && bottom == KS_rowMax) {
1154         top = 0;
1155         bottom = 0;
1156     }
1157     printrange.setAttribute("left-rect", QString::number(left));
1158     printrange.setAttribute("right-rect", QString::number(right));
1159     printrange.setAttribute("bottom-rect", QString::number(bottom));
1160     printrange.setAttribute("top-rect", QString::number(top));
1161     sheet.appendChild(printrange);
1162 
1163     // Print repeat columns
1164     QDomElement printRepeatColumns = dd.createElement("printrepeatcolumns");
1165     printRepeatColumns.setAttribute("left", QString::number(printSettings()->repeatedColumns().first));
1166     printRepeatColumns.setAttribute("right", QString::number(printSettings()->repeatedColumns().second));
1167     sheet.appendChild(printRepeatColumns);
1168 
1169     // Print repeat rows
1170     QDomElement printRepeatRows = dd.createElement("printrepeatrows");
1171     printRepeatRows.setAttribute("top", QString::number(printSettings()->repeatedRows().first));
1172     printRepeatRows.setAttribute("bottom", QString::number(printSettings()->repeatedRows().second));
1173     sheet.appendChild(printRepeatRows);
1174 
1175     //Save print zoom
1176     sheet.setAttribute("printZoom", QString::number(printSettings()->zoom()));
1177 
1178     //Save page limits
1179     const QSize pageLimits = printSettings()->pageLimits();
1180     sheet.setAttribute("printPageLimitX", QString::number(pageLimits.width()));
1181     sheet.setAttribute("printPageLimitY", QString::number(pageLimits.height()));
1182 
1183     // Save all cells.
1184     const QRect usedArea = this->usedArea();
1185     for (int row = 1; row <= usedArea.height(); ++row) {
1186         Cell cell = d->cellStorage->firstInRow(row);
1187         while (!cell.isNull()) {
1188             QDomElement e = cell.save(dd);
1189             if (!e.isNull())
1190                 sheet.appendChild(e);
1191             cell = d->cellStorage->nextInRow(cell.column(), row);
1192         }
1193     }
1194 
1195     // Save all RowFormat objects.
1196     int styleIndex = styleStorage()->nextRowStyleIndex(0);
1197     int rowFormatRow = 0, lastRowFormatRow = rowFormats()->lastNonDefaultRow();
1198     while (styleIndex || rowFormatRow <= lastRowFormatRow) {
1199         int lastRow;
1200         bool isDefault = rowFormats()->isDefaultRow(rowFormatRow, &lastRow);
1201         if (isDefault && styleIndex <= lastRow) {
1202             RowFormat rowFormat(*map()->defaultRowFormat());
1203             rowFormat.setSheet(this);
1204             rowFormat.setRow(styleIndex);
1205             QDomElement e = rowFormat.save(dd);
1206             if (e.isNull())
1207                 return QDomElement();
1208             sheet.appendChild(e);
1209             styleIndex = styleStorage()->nextRowStyleIndex(styleIndex);
1210         } else if (!isDefault) {
1211             RowFormat rowFormat(rowFormats(), rowFormatRow);
1212             QDomElement e = rowFormat.save(dd);
1213             if (e.isNull())
1214                 return QDomElement();
1215             sheet.appendChild(e);
1216             if (styleIndex == rowFormatRow)
1217                 styleIndex = styleStorage()->nextRowStyleIndex(styleIndex);
1218         }
1219         if (isDefault) rowFormatRow = qMin(lastRow+1, styleIndex == 0 ? KS_rowMax : styleIndex);
1220         else rowFormatRow++;
1221     }
1222 
1223     // Save all ColumnFormat objects.
1224     ColumnFormat* columnFormat = firstCol();
1225     styleIndex = styleStorage()->nextColumnStyleIndex(0);
1226     while (columnFormat || styleIndex) {
1227         if (columnFormat && (!styleIndex || columnFormat->column() <= styleIndex)) {
1228             QDomElement e = columnFormat->save(dd);
1229             if (e.isNull())
1230                 return QDomElement();
1231             sheet.appendChild(e);
1232             if (columnFormat->column() == styleIndex)
1233                 styleIndex = styleStorage()->nextColumnStyleIndex(styleIndex);
1234             columnFormat = columnFormat->next();
1235         } else if (styleIndex) {
1236             ColumnFormat columnFormat(*map()->defaultColumnFormat());
1237             columnFormat.setSheet(this);
1238             columnFormat.setColumn(styleIndex);
1239             QDomElement e = columnFormat.save(dd);
1240             if (e.isNull())
1241                 return QDomElement();
1242             sheet.appendChild(e);
1243             styleIndex = styleStorage()->nextColumnStyleIndex(styleIndex);
1244         }
1245     }
1246 #if 0 // CALLIGRA_SHEETS_KOPART_EMBEDDING
1247     foreach(EmbeddedObject* object, doc()->embeddedObjects()) {
1248         if (object->sheet() == this) {
1249             QDomElement e = object->save(dd);
1250 
1251             if (e.isNull())
1252                 return QDomElement();
1253             sheet.appendChild(e);
1254         }
1255     }
1256 #endif // CALLIGRA_SHEETS_KOPART_EMBEDDING
1257     return sheet;
1258 }
1259 
isLoading()1260 bool Sheet::isLoading()
1261 {
1262     return map()->isLoading();
1263 }
1264 
checkContentDirection(QString const & name)1265 void Sheet::checkContentDirection(QString const & name)
1266 {
1267     /* set sheet's direction to RTL if sheet name is an RTL string */
1268     if ((name.isRightToLeft()))
1269         setLayoutDirection(Qt::RightToLeft);
1270     else
1271         setLayoutDirection(Qt::LeftToRight);
1272 }
1273 
usedArea(bool onlyContent) const1274 QRect Sheet::usedArea(bool onlyContent) const
1275 {
1276     int maxCols = d->cellStorage->columns(!onlyContent);
1277     int maxRows = d->cellStorage->rows(!onlyContent);
1278 
1279     if (!onlyContent) {
1280         maxRows = qMax(maxRows, d->rows.lastNonDefaultRow());
1281 
1282         const ColumnFormat* col = firstCol();
1283         while (col) {
1284             if (col->column() > maxCols)
1285                 maxCols = col->column();
1286 
1287             col = col->next();
1288         }
1289     }
1290 
1291     // flake
1292     QRectF shapesBoundingRect;
1293     for (int i = 0; i < d->shapes.count(); ++i)
1294         shapesBoundingRect |= d->shapes[i]->boundingRect();
1295     const QRect shapesCellRange = documentToCellCoordinates(shapesBoundingRect);
1296     maxCols = qMax(maxCols, shapesCellRange.right());
1297     maxRows = qMax(maxRows, shapesCellRange.bottom());
1298 
1299     return QRect(1, 1, maxCols, maxRows);
1300 }
1301 
loadXML(const KoXmlElement & sheet)1302 bool Sheet::loadXML(const KoXmlElement& sheet)
1303 {
1304     bool ok = false;
1305     QString sname = sheetName();
1306     if (!map()->loadingInfo()->loadTemplate()) {
1307         sname = sheet.attribute("name");
1308         if (sname.isEmpty()) {
1309             doc()->setErrorMessage(i18n("Invalid document. Sheet name is empty."));
1310             return false;
1311         }
1312     }
1313 
1314     bool detectDirection = true;
1315     QString layoutDir = sheet.attribute("layoutDirection");
1316     if (!layoutDir.isEmpty()) {
1317         if (layoutDir == "rtl") {
1318             detectDirection = false;
1319             setLayoutDirection(Qt::RightToLeft);
1320         } else if (layoutDir == "ltr") {
1321             detectDirection = false;
1322             setLayoutDirection(Qt::LeftToRight);
1323         } else
1324             debugSheets << " Direction not implemented :" << layoutDir;
1325     }
1326     if (detectDirection)
1327         checkContentDirection(sname);
1328 
1329     /* older versions of KSpread allowed all sorts of characters that
1330     the parser won't actually understand.  Replace these with '_'
1331     Also, the initial character cannot be a space.
1332     */
1333     while (sname[0] == ' ') {
1334         sname.remove(0, 1);
1335     }
1336     for (int i = 0; i < sname.length(); i++) {
1337         if (!(sname[i].isLetterOrNumber() ||
1338                 sname[i] == ' ' || sname[i] == '.' || sname[i] == '_')) {
1339             sname[i] = '_';
1340         }
1341     }
1342 
1343     // validate sheet name, if it differs from the current one
1344     if (sname != sheetName()) {
1345         /* make sure there are no name collisions with the altered name */
1346         QString testName = sname;
1347         QString baseName = sname;
1348         int nameSuffix = 0;
1349 
1350         /* so we don't panic over finding ourself in the following test*/
1351         sname.clear();
1352         while (map()->findSheet(testName) != 0) {
1353             nameSuffix++;
1354             testName = baseName + '_' + QString::number(nameSuffix);
1355         }
1356         sname = testName;
1357 
1358         debugSheets << "Sheet::loadXML: table name =" << sname;
1359         setObjectName(sname);
1360         setSheetName(sname, true);
1361     }
1362 
1363 //     (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();
1364 
1365     if (sheet.hasAttribute("grid")) {
1366         setShowGrid((int)sheet.attribute("grid").toInt(&ok));
1367         // we just ignore 'ok' - if it didn't work, go on
1368     }
1369     if (sheet.hasAttribute("printGrid")) {
1370         print()->settings()->setPrintGrid((bool)sheet.attribute("printGrid").toInt(&ok));
1371         // we just ignore 'ok' - if it didn't work, go on
1372     }
1373     if (sheet.hasAttribute("printCommentIndicator")) {
1374         print()->settings()->setPrintCommentIndicator((bool)sheet.attribute("printCommentIndicator").toInt(&ok));
1375         // we just ignore 'ok' - if it didn't work, go on
1376     }
1377     if (sheet.hasAttribute("printFormulaIndicator")) {
1378         print()->settings()->setPrintFormulaIndicator((bool)sheet.attribute("printFormulaIndicator").toInt(&ok));
1379         // we just ignore 'ok' - if it didn't work, go on
1380     }
1381     if (sheet.hasAttribute("hide")) {
1382         setHidden((bool)sheet.attribute("hide").toInt(&ok));
1383         // we just ignore 'ok' - if it didn't work, go on
1384     }
1385     if (sheet.hasAttribute("showFormula")) {
1386         setShowFormula((bool)sheet.attribute("showFormula").toInt(&ok));
1387         // we just ignore 'ok' - if it didn't work, go on
1388     }
1389     //Compatibility with KSpread 1.1.x
1390     if (sheet.hasAttribute("formular")) {
1391         setShowFormula((bool)sheet.attribute("formular").toInt(&ok));
1392         // we just ignore 'ok' - if it didn't work, go on
1393     }
1394     if (sheet.hasAttribute("showFormulaIndicator")) {
1395         setShowFormulaIndicator((bool)sheet.attribute("showFormulaIndicator").toInt(&ok));
1396         // we just ignore 'ok' - if it didn't work, go on
1397     }
1398     if (sheet.hasAttribute("showCommentIndicator")) {
1399         setShowCommentIndicator((bool)sheet.attribute("showCommentIndicator").toInt(&ok));
1400         // we just ignore 'ok' - if it didn't work, go on
1401     }
1402     if (sheet.hasAttribute("borders")) {
1403         setShowPageOutline((bool)sheet.attribute("borders").toInt(&ok));
1404         // we just ignore 'ok' - if it didn't work, go on
1405     }
1406     if (sheet.hasAttribute("lcmode")) {
1407         setLcMode((bool)sheet.attribute("lcmode").toInt(&ok));
1408         // we just ignore 'ok' - if it didn't work, go on
1409     }
1410     if (sheet.hasAttribute("autoCalc")) {
1411         setAutoCalculationEnabled((bool)sheet.attribute("autoCalc").toInt(&ok));
1412         // we just ignore 'ok' - if it didn't work, go on
1413     }
1414     if (sheet.hasAttribute("columnnumber")) {
1415         setShowColumnNumber((bool)sheet.attribute("columnnumber").toInt(&ok));
1416         // we just ignore 'ok' - if it didn't work, go on
1417     }
1418     if (sheet.hasAttribute("hidezero")) {
1419         setHideZero((bool)sheet.attribute("hidezero").toInt(&ok));
1420         // we just ignore 'ok' - if it didn't work, go on
1421     }
1422     if (sheet.hasAttribute("firstletterupper")) {
1423         setFirstLetterUpper((bool)sheet.attribute("firstletterupper").toInt(&ok));
1424         // we just ignore 'ok' - if it didn't work, go on
1425     }
1426 
1427     // Load the paper layout
1428     KoXmlElement paper = sheet.namedItem("paper").toElement();
1429     if (!paper.isNull()) {
1430         KoPageLayout pageLayout;
1431         pageLayout.format = KoPageFormat::formatFromString(paper.attribute("format"));
1432         pageLayout.orientation = (paper.attribute("orientation")  == "Portrait")
1433                                  ? KoPageFormat::Portrait : KoPageFormat::Landscape;
1434 
1435         // <borders>
1436         KoXmlElement borders = paper.namedItem("borders").toElement();
1437         if (!borders.isNull()) {
1438             pageLayout.leftMargin   = MM_TO_POINT(borders.attribute("left").toFloat());
1439             pageLayout.rightMargin  = MM_TO_POINT(borders.attribute("right").toFloat());
1440             pageLayout.topMargin    = MM_TO_POINT(borders.attribute("top").toFloat());
1441             pageLayout.bottomMargin = MM_TO_POINT(borders.attribute("bottom").toFloat());
1442         }
1443         print()->settings()->setPageLayout(pageLayout);
1444 
1445         QString hleft, hright, hcenter;
1446         QString fleft, fright, fcenter;
1447         // <head>
1448         KoXmlElement head = paper.namedItem("head").toElement();
1449         if (!head.isNull()) {
1450             KoXmlElement left = head.namedItem("left").toElement();
1451             if (!left.isNull())
1452                 hleft = left.text();
1453             KoXmlElement center = head.namedItem("center").toElement();
1454             if (!center.isNull())
1455                 hcenter = center.text();
1456             KoXmlElement right = head.namedItem("right").toElement();
1457             if (!right.isNull())
1458                 hright = right.text();
1459         }
1460         // <foot>
1461         KoXmlElement foot = paper.namedItem("foot").toElement();
1462         if (!foot.isNull()) {
1463             KoXmlElement left = foot.namedItem("left").toElement();
1464             if (!left.isNull())
1465                 fleft = left.text();
1466             KoXmlElement center = foot.namedItem("center").toElement();
1467             if (!center.isNull())
1468                 fcenter = center.text();
1469             KoXmlElement right = foot.namedItem("right").toElement();
1470             if (!right.isNull())
1471                 fright = right.text();
1472         }
1473         print()->headerFooter()->setHeadFootLine(hleft, hcenter, hright, fleft, fcenter, fright);
1474     }
1475 
1476     // load print range
1477     KoXmlElement printrange = sheet.namedItem("printrange-rect").toElement();
1478     if (!printrange.isNull()) {
1479         int left = printrange.attribute("left-rect").toInt();
1480         int right = printrange.attribute("right-rect").toInt();
1481         int bottom = printrange.attribute("bottom-rect").toInt();
1482         int top = printrange.attribute("top-rect").toInt();
1483         if (left == 0) { //whole row(s) selected
1484             left = 1;
1485             right = KS_colMax;
1486         }
1487         if (top == 0) { //whole column(s) selected
1488             top = 1;
1489             bottom = KS_rowMax;
1490         }
1491         const Region region(QRect(QPoint(left, top), QPoint(right, bottom)), this);
1492         printSettings()->setPrintRegion(region);
1493     }
1494 
1495     // load print zoom
1496     if (sheet.hasAttribute("printZoom")) {
1497         double zoom = sheet.attribute("printZoom").toDouble(&ok);
1498         if (ok) {
1499             printSettings()->setZoom(zoom);
1500         }
1501     }
1502 
1503     // load page limits
1504     if (sheet.hasAttribute("printPageLimitX")) {
1505         int pageLimit = sheet.attribute("printPageLimitX").toInt(&ok);
1506         if (ok) {
1507             printSettings()->setPageLimits(QSize(pageLimit, 0));
1508         }
1509     }
1510 
1511     // load page limits
1512     if (sheet.hasAttribute("printPageLimitY")) {
1513         int pageLimit = sheet.attribute("printPageLimitY").toInt(&ok);
1514         if (ok) {
1515             const int horizontalLimit = printSettings()->pageLimits().width();
1516             printSettings()->setPageLimits(QSize(horizontalLimit, pageLimit));
1517         }
1518     }
1519 
1520     // Load the cells
1521     KoXmlNode n = sheet.firstChild();
1522     while (!n.isNull()) {
1523         KoXmlElement e = n.toElement();
1524         if (!e.isNull()) {
1525             QString tagName = e.tagName();
1526             if (tagName == "cell")
1527                 Cell(this, 1, 1).load(e, 0, 0); // col, row will get overridden in all cases
1528             else if (tagName == "row") {
1529                 RowFormat *rl = new RowFormat();
1530                 rl->setSheet(this);
1531                 if (rl->load(e))
1532                     insertRowFormat(rl);
1533                 delete rl;
1534             } else if (tagName == "column") {
1535                 ColumnFormat *cl = new ColumnFormat();
1536                 cl->setSheet(this);
1537                 if (cl->load(e))
1538                     insertColumnFormat(cl);
1539                 else
1540                     delete cl;
1541             }
1542 #if 0 // CALLIGRA_SHEETS_KOPART_EMBEDDING
1543             else if (tagName == "object") {
1544                 EmbeddedCalligraObject *ch = new EmbeddedCalligraObject(doc(), this);
1545                 if (ch->load(e))
1546                     insertObject(ch);
1547                 else {
1548                     ch->embeddedObject()->setDeleted(true);
1549                     delete ch;
1550                 }
1551             } else if (tagName == "chart") {
1552                 EmbeddedChart *ch = new EmbeddedChart(doc(), this);
1553                 if (ch->load(e))
1554                     insertObject(ch);
1555                 else {
1556                     ch->embeddedObject()->setDeleted(true);
1557                     delete ch;
1558                 }
1559             }
1560 #endif // CALLIGRA_SHEETS_KOPART_EMBEDDING
1561         }
1562         n = n.nextSibling();
1563     }
1564 
1565     // load print repeat columns
1566     KoXmlElement printrepeatcolumns = sheet.namedItem("printrepeatcolumns").toElement();
1567     if (!printrepeatcolumns.isNull()) {
1568         int left = printrepeatcolumns.attribute("left").toInt();
1569         int right = printrepeatcolumns.attribute("right").toInt();
1570         printSettings()->setRepeatedColumns(qMakePair(left, right));
1571     }
1572 
1573     // load print repeat rows
1574     KoXmlElement printrepeatrows = sheet.namedItem("printrepeatrows").toElement();
1575     if (!printrepeatrows.isNull()) {
1576         int top = printrepeatrows.attribute("top").toInt();
1577         int bottom = printrepeatrows.attribute("bottom").toInt();
1578         printSettings()->setRepeatedRows(qMakePair(top, bottom));
1579     }
1580 
1581     if (!sheet.hasAttribute("borders1.2")) {
1582         convertObscuringBorders();
1583     }
1584 
1585     loadXmlProtection(sheet);
1586 
1587     return true;
1588 }
1589 
1590 
loadChildren(KoStore * _store)1591 bool Sheet::loadChildren(KoStore* _store)
1592 {
1593     Q_UNUSED(_store);
1594 #if 0 // CALLIGRA_SHEETS_KOPART_EMBEDDING
1595     foreach(EmbeddedObject* object, doc()->embeddedObjects()) {
1596         if (object->sheet() == this && (object->getType() == OBJECT_CALLIGRA_PART || object->getType() == OBJECT_CHART)) {
1597             debugSheets << "Calligra::Sheets::Sheet::loadChildren";
1598             if (!dynamic_cast<EmbeddedCalligraObject*>(object)->embeddedObject()->loadDocument(_store))
1599                 return false;
1600         }
1601     }
1602 #endif // CALLIGRA_SHEETS_KOPART_EMBEDDING
1603     return true;
1604 }
1605 
1606 
setShowPageOutline(bool b)1607 void Sheet::setShowPageOutline(bool b)
1608 {
1609     if (b == d->showPageOutline)
1610         return;
1611 
1612     d->showPageOutline = b;
1613     // Just repaint everything visible; no need to invalidate the visual cache.
1614     if (!map()->isLoading()) {
1615         map()->addDamage(new SheetDamage(this, SheetDamage::ContentChanged));
1616     }
1617 }
1618 
backgroundImage() const1619 QImage Sheet::backgroundImage() const
1620 {
1621     return d->backgroundImage;
1622 }
1623 
setBackgroundImage(const QImage & image)1624 void Sheet::setBackgroundImage(const QImage& image)
1625 {
1626     d->backgroundImage = image;
1627 }
1628 
backgroundImageProperties() const1629 Sheet::BackgroundImageProperties Sheet::backgroundImageProperties() const
1630 {
1631     return d->backgroundProperties;
1632 }
1633 
setBackgroundImageProperties(const Sheet::BackgroundImageProperties & properties)1634 void Sheet::setBackgroundImageProperties(const Sheet::BackgroundImageProperties& properties)
1635 {
1636     d->backgroundProperties = properties;
1637 }
1638 
insertColumnFormat(ColumnFormat * l)1639 void Sheet::insertColumnFormat(ColumnFormat *l)
1640 {
1641     d->columns.insertElement(l, l->column());
1642     if (!map()->isLoading()) {
1643         map()->addDamage(new SheetDamage(this, SheetDamage::ColumnsChanged));
1644     }
1645 }
1646 
insertRowFormat(RowFormat * l)1647 void Sheet::insertRowFormat(RowFormat *l)
1648 {
1649     const int row = l->row();
1650     d->rows.setRowHeight(row, row, l->height());
1651     d->rows.setHidden(row, row, l->isHidden());
1652     d->rows.setFiltered(row, row, l->isFiltered());
1653     d->rows.setPageBreak(row, row, l->hasPageBreak());
1654     if (!map()->isLoading()) {
1655         map()->addDamage(new SheetDamage(this, SheetDamage::RowsChanged));
1656     }
1657 }
1658 
deleteColumnFormat(int column)1659 void Sheet::deleteColumnFormat(int column)
1660 {
1661     d->columns.removeElement(column);
1662     if (!map()->isLoading()) {
1663         map()->addDamage(new SheetDamage(this, SheetDamage::ColumnsChanged));
1664     }
1665 }
1666 
deleteRowFormat(int row)1667 void Sheet::deleteRowFormat(int row)
1668 {
1669     d->rows.setDefault(row, row);
1670     if (!map()->isLoading()) {
1671         map()->addDamage(new SheetDamage(this, SheetDamage::RowsChanged));
1672     }
1673 }
1674 
1675 
rowFormats()1676 RowFormatStorage* Sheet::rowFormats()
1677 {
1678     return &d->rows;
1679 }
1680 
rowFormats() const1681 const RowFormatStorage* Sheet::rowFormats() const
1682 {
1683     return &d->rows;
1684 }
1685 
showStatusMessage(const QString & message,int timeout)1686 void Sheet::showStatusMessage(const QString &message, int timeout)
1687 {
1688     emit statusMessage(message, timeout);
1689 }
1690 
hideSheet(bool _hide)1691 void Sheet::hideSheet(bool _hide)
1692 {
1693     setHidden(_hide);
1694     if (_hide)
1695         map()->addDamage(new SheetDamage(this, SheetDamage::Hidden));
1696     else
1697         map()->addDamage(new SheetDamage(this, SheetDamage::Shown));
1698 }
1699 
setSheetName(const QString & name,bool init)1700 bool Sheet::setSheetName(const QString& name, bool init)
1701 {
1702     Q_UNUSED(init);
1703     if (map()->findSheet(name))
1704         return false;
1705 
1706     if (isProtected())
1707         return false;
1708 
1709     if (d->name == name)
1710         return true;
1711 
1712     QString old_name = d->name;
1713     d->name = name;
1714 
1715     // FIXME: Why is the change of a sheet's name not supposed to be propagated here?
1716     // If it is not, we have to manually do so in the loading process, e.g. for the
1717     // SheetAccessModel in the document's data center map.
1718     //if (init)
1719     //    return true;
1720 
1721     foreach(Sheet* sheet, map()->sheetList()) {
1722         sheet->changeCellTabName(old_name, name);
1723     }
1724 
1725     map()->addDamage(new SheetDamage(this, SheetDamage::Name));
1726 
1727     setObjectName(name);
1728 //     (dynamic_cast<SheetIface*>(dcopObject()))->sheetNameHasChanged();
1729 
1730     return true;
1731 }
1732 
1733 
updateLocale()1734 void Sheet::updateLocale()
1735 {
1736     for (int c = 0; c < valueStorage()->count(); ++c) {
1737         Cell cell(this, valueStorage()->col(c), valueStorage()->row(c));
1738         QString text = cell.userInput();
1739         cell.parseUserInput(text);
1740     }
1741     // Affects the displayed value; rebuild the visual cache.
1742     const Region region(1, 1, KS_colMax, KS_rowMax, this);
1743     map()->addDamage(new CellDamage(this, region, CellDamage::Appearance));
1744 }
1745 
convertObscuringBorders()1746 void Sheet::convertObscuringBorders()
1747 {
1748     // FIXME Stefan: Verify that this is not needed anymore.
1749 #if 0
1750     /* a word of explanation here:
1751        beginning with KSpread 1.2 (actually, cvs of Mar 28, 2002), border information
1752        is stored differently.  Previously, for a cell obscuring a region, the entire
1753        region's border's data would be stored in the obscuring cell.  This caused
1754        some data loss in certain situations.  After that date, each cell stores
1755        its own border data, and prints it even if it is an obscured cell (as long
1756        as that border isn't across an obscuring border).
1757        Anyway, this function is used when loading a file that was stored with the
1758        old way of borders.  All new files have the sheet attribute "borders1.2" so
1759        if that isn't in the file, all the border data will be converted here.
1760        It's a bit of a hack but I can't think of a better way and it's not *that*
1761        bad of a hack.:-)
1762     */
1763     Cell c = d->cellStorage->firstCell();
1764     QPen topPen, bottomPen, leftPen, rightPen;
1765     for (; c; c = c->nextCell()) {
1766         if (c->extraXCells() > 0 || c->extraYCells() > 0) {
1767             const Style* style = this->style(c->column(), c->row());
1768             topPen = style->topBorderPen();
1769             leftPen = style->leftBorderPen();
1770             rightPen = style->rightBorderPen();
1771             bottomPen = style->bottomBorderPen();
1772 
1773             c->format()->setTopBorderStyle(Qt::NoPen);
1774             c->format()->setLeftBorderStyle(Qt::NoPen);
1775             c->format()->setRightBorderStyle(Qt::NoPen);
1776             c->format()->setBottomBorderStyle(Qt::NoPen);
1777 
1778             for (int x = c->column(); x < c->column() + c->extraXCells(); x++) {
1779                 Cell(this, x, c->row())->setTopBorderPen(topPen);
1780                 Cell(this, x, c->row() + c->extraYCells())->
1781                 setBottomBorderPen(bottomPen);
1782             }
1783             for (int y = c->row(); y < c->row() + c->extraYCells(); y++) {
1784                 Cell(this, c->column(), y)->setLeftBorderPen(leftPen);
1785                 Cell(this, c->column() + c->extraXCells(), y)->
1786                 setRightBorderPen(rightPen);
1787             }
1788         }
1789     }
1790 #endif
1791 }
1792 
applyDatabaseFilter(const Database & database)1793 void Sheet::applyDatabaseFilter(const Database &database)
1794 {
1795     Sheet* const sheet = database.range().lastSheet();
1796     const QRect range = database.range().lastRange();
1797     const int start = database.orientation() == Qt::Vertical ? range.top() : range.left();
1798     const int end = database.orientation() == Qt::Vertical ? range.bottom() : range.right();
1799     for (int i = start + 1; i <= end; ++i) {
1800         const bool isFiltered = !database.filter().evaluate(database, i);
1801 //         debugSheets <<"Filtering column/row" << i <<"?" << isFiltered;
1802         if (database.orientation() == Qt::Vertical) {
1803             sheet->rowFormats()->setFiltered(i, i, isFiltered);
1804         } else { // database.orientation() == Qt::Horizontal
1805             sheet->nonDefaultColumnFormat(i)->setFiltered(isFiltered);
1806         }
1807     }
1808     if (database.orientation() == Qt::Vertical)
1809         sheet->map()->addDamage(new SheetDamage(sheet, SheetDamage::RowsChanged));
1810     else // database.orientation() == Qt::Horizontal
1811         sheet->map()->addDamage(new SheetDamage(sheet, SheetDamage::ColumnsChanged));
1812 
1813     cellStorage()->setDatabase(database.range(), Database());
1814     cellStorage()->setDatabase(database.range(), database);
1815     map()->addDamage(new CellDamage(this, database.range(), CellDamage::Appearance));
1816 }
1817 
1818 /**********************
1819  * Printout Functions *
1820  **********************/
1821 
1822 #ifndef NDEBUG
printDebug()1823 void Sheet::printDebug()
1824 {
1825     int iMaxColumn = d->cellStorage->columns();
1826     int iMaxRow = d->cellStorage->rows();
1827 
1828     debugSheets << "Cell | Content | Value  [UserInput]";
1829     Cell cell;
1830     for (int currentrow = 1 ; currentrow <= iMaxRow ; ++currentrow) {
1831         for (int currentcolumn = 1 ; currentcolumn <= iMaxColumn ; currentcolumn++) {
1832             cell = Cell(this, currentcolumn, currentrow);
1833             if (!cell.isEmpty()) {
1834                 QString cellDescr = Cell::name(currentcolumn, currentrow).rightJustified(4) +
1835                 //QString cellDescr = "Cell ";
1836                 //cellDescr += QString::number(currentrow).rightJustified(3,'0') + ',';
1837                 //cellDescr += QString::number(currentcolumn).rightJustified(3,'0') + ' ';
1838                     " | ";
1839                 QString valueType;
1840                 QTextStream stream(&valueType);
1841                 stream << cell.value().type();
1842                 cellDescr += valueType.rightJustified(7) +
1843                              " | " +
1844                              map()->converter()->asString(cell.value()).asString().rightJustified(5) +
1845                              QString("  [%1]").arg(cell.userInput());
1846                 debugSheets << cellDescr;
1847             }
1848         }
1849     }
1850 }
1851 #endif
1852 
1853 } // namespace Sheets
1854 } // namespace Calligra
1855