File indexing completed on 2024-04-28 16:21:30
0001 /* This file is part of the KDE project 0002 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> 0003 Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0004 Copyright 1998,1999 Torben Weis <weis@kde.org> 0005 Copyright 1999-2007 The KSpread Team <calligra-devel@kde.org> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 Boston, MA 02110-1301, USA. 0021 */ 0022 0023 // Local 0024 #include "Sheet.h" 0025 0026 #include <QApplication> 0027 0028 #include <kcodecs.h> 0029 0030 #include <KoShape.h> 0031 #include <KoDocumentResourceManager.h> 0032 #include <KoUnit.h> 0033 #include <KoCanvasResourceIdentities.h> 0034 0035 #include "CellStorage.h" 0036 #include "Cluster.h" 0037 #include "Damages.h" 0038 #include "DependencyManager.h" 0039 #include "DocBase.h" 0040 #include "FormulaStorage.h" 0041 #include "HeaderFooter.h" 0042 #include "LoadingInfo.h" 0043 #include "Map.h" 0044 #include "NamedAreaManager.h" 0045 #include "PrintSettings.h" 0046 #include "RecalcManager.h" 0047 #include "RowColumnFormat.h" 0048 #include "RowFormatStorage.h" 0049 #include "ShapeApplicationData.h" 0050 #include "SheetPrint.h" 0051 #include "SheetModel.h" 0052 #include "StyleStorage.h" 0053 #include "Validity.h" 0054 #include "ValueConverter.h" 0055 #include "ValueStorage.h" 0056 #include "database/Filter.h" 0057 0058 namespace Calligra 0059 { 0060 namespace Sheets 0061 { 0062 0063 static QString createObjectName(const QString &sheetName) 0064 { 0065 QString objectName; 0066 for (int i = 0; i < sheetName.count(); ++i) { 0067 if (sheetName[i].isLetterOrNumber() || sheetName[i] == '_') 0068 objectName.append(sheetName[i]); 0069 else 0070 objectName.append('_'); 0071 } 0072 return objectName; 0073 } 0074 0075 0076 class Q_DECL_HIDDEN Sheet::Private 0077 { 0078 public: 0079 Private(Sheet* sheet) : rows(sheet) {} 0080 0081 Map* workbook; 0082 SheetModel *model; 0083 0084 QString name; 0085 0086 Qt::LayoutDirection layoutDirection; 0087 0088 // true if sheet is hidden 0089 bool hide; 0090 0091 bool showGrid; 0092 bool showFormula; 0093 bool showFormulaIndicator; 0094 bool showCommentIndicator; 0095 bool autoCalc; 0096 bool lcMode; 0097 bool showColumnNumber; 0098 bool hideZero; 0099 bool firstLetterUpper; 0100 0101 // clusters to hold objects 0102 CellStorage* cellStorage; 0103 RowFormatStorage rows; 0104 ColumnCluster columns; 0105 QList<KoShape*> shapes; 0106 0107 // hold the print object 0108 SheetPrint* print; 0109 0110 // Indicates whether the sheet should paint the page breaks. 0111 // Doing so costs some time, so by default it should be turned off. 0112 bool showPageOutline; 0113 0114 // Max range of canvas in x and y direction. 0115 // Depends on KS_colMax/KS_rowMax and the width/height of all columns/rows 0116 QSizeF documentSize; 0117 0118 QImage backgroundImage; 0119 Sheet::BackgroundImageProperties backgroundProperties; 0120 }; 0121 0122 0123 Sheet::Sheet(Map* map, const QString &sheetName) 0124 : KoShapeUserData(map) 0125 , KoShapeBasedDocumentBase() 0126 , d(new Private(this)) 0127 { 0128 d->workbook = map; 0129 if (map->doc()) { 0130 resourceManager()->setUndoStack(map->doc()->undoStack()); 0131 QVariant variant; 0132 variant.setValue<void*>(map->doc()->sheetAccessModel()); 0133 resourceManager()->setResource(::Sheets::CanvasResource::AccessModel, variant); 0134 } 0135 d->model = new SheetModel(this); 0136 0137 d->layoutDirection = QApplication::layoutDirection(); 0138 0139 d->name = sheetName; 0140 0141 // Set a valid object name, so that we can offer scripting. 0142 setObjectName(createObjectName(d->name)); 0143 0144 d->cellStorage = new CellStorage(this); 0145 d->columns.setAutoDelete(true); 0146 0147 d->documentSize = QSizeF(KS_colMax * d->workbook->defaultColumnFormat()->width(), 0148 KS_rowMax * d->workbook->defaultRowFormat()->height()); 0149 0150 d->hide = false; 0151 d->showGrid = true; 0152 d->showFormula = false; 0153 d->showFormulaIndicator = false; 0154 d->showCommentIndicator = true; 0155 d->showPageOutline = false; 0156 0157 d->lcMode = false; 0158 d->showColumnNumber = false; 0159 d->hideZero = false; 0160 d->firstLetterUpper = false; 0161 d->autoCalc = true; 0162 d->print = new SheetPrint(this); 0163 0164 // document size changes always trigger a visible size change 0165 connect(this, SIGNAL(documentSizeChanged(QSizeF)), SIGNAL(visibleSizeChanged())); 0166 // CellStorage connections 0167 connect(d->cellStorage, SIGNAL(insertNamedArea(Region,QString)), 0168 d->workbook->namedAreaManager(), SLOT(insert(Region,QString))); 0169 connect(d->cellStorage, SIGNAL(namedAreaRemoved(QString)), 0170 d->workbook->namedAreaManager(), SLOT(remove(QString))); 0171 } 0172 0173 Sheet::Sheet(const Sheet &other) 0174 : KoShapeUserData(other.d->workbook) 0175 , KoShapeBasedDocumentBase() 0176 , ProtectableObject(other) 0177 , d(new Private(this)) 0178 { 0179 d->workbook = other.d->workbook; 0180 d->model = new SheetModel(this); 0181 0182 // create a unique name 0183 int i = 1; 0184 do 0185 d->name = other.d->name + QString("_%1").arg(i++); 0186 while (d->workbook->findSheet(d->name)); 0187 0188 // Set a valid object name, so that we can offer scripting. 0189 setObjectName(createObjectName(d->name)); 0190 0191 d->layoutDirection = other.d->layoutDirection; 0192 d->hide = other.d->hide; 0193 d->showGrid = other.d->showGrid; 0194 d->showFormula = other.d->showFormula; 0195 d->showFormulaIndicator = other.d->showFormulaIndicator; 0196 d->showCommentIndicator = other.d->showCommentIndicator; 0197 d->autoCalc = other.d->autoCalc; 0198 d->lcMode = other.d->lcMode; 0199 d->showColumnNumber = other.d->showColumnNumber; 0200 d->hideZero = other.d->hideZero; 0201 d->firstLetterUpper = other.d->firstLetterUpper; 0202 0203 d->cellStorage = new CellStorage(*other.d->cellStorage, this); 0204 d->rows = other.d->rows; 0205 d->columns = other.d->columns; 0206 0207 // flake 0208 #if 0 // CALLIGRA_SHEETS_WIP_COPY_SHEET_(SHAPES) 0209 //FIXME This does not work as copySettings does not work. Also createDefaultShapeAndInit without the correct settings can not work 0210 //I think this should use ODF load/save for copying 0211 KoShape* shape; 0212 const QList<KoShape*> shapes = other.d->shapes; 0213 for (int i = 0; i < shapes.count(); ++i) { 0214 KoShapeFactoryBase *factory = KoShapeRegistry::instance()->value(shapes[i]->shapeId()); 0215 if (factory) { 0216 shape = factory->createDefaultShapeAndInit(0); 0217 shape->copySettings(shapes[i]); 0218 addShape(shape); 0219 } 0220 } 0221 #endif // CALLIGRA_SHEETS_WIP_COPY_SHEET_(SHAPES) 0222 0223 d->print = new SheetPrint(this); // FIXME = new SheetPrint(*other.d->print); 0224 0225 d->showPageOutline = other.d->showPageOutline; 0226 d->documentSize = other.d->documentSize; 0227 } 0228 0229 Sheet::~Sheet() 0230 { 0231 //Disable automatic recalculation of dependencies on this sheet to prevent crashes 0232 //in certain situations: 0233 // 0234 //For example, suppose a cell in SheetB depends upon a cell in SheetA. If the cell in SheetB is emptied 0235 //after SheetA has already been deleted, the program would try to remove dependencies from the cell in SheetA 0236 //causing a crash. 0237 setAutoCalculationEnabled(false); 0238 0239 delete d->print; 0240 delete d->cellStorage; 0241 qDeleteAll(d->shapes); 0242 delete d; 0243 } 0244 0245 QAbstractItemModel* Sheet::model() const 0246 { 0247 return d->model; 0248 } 0249 0250 QString Sheet::sheetName() const 0251 { 0252 return d->name; 0253 } 0254 0255 Map* Sheet::map() const 0256 { 0257 return d->workbook; 0258 } 0259 0260 DocBase* Sheet::doc() const 0261 { 0262 return d->workbook->doc(); 0263 } 0264 0265 void Sheet::addShape(KoShape* shape) 0266 { 0267 if (!shape) 0268 return; 0269 d->shapes.append(shape); 0270 shape->setApplicationData(new ShapeApplicationData()); 0271 emit shapeAdded(this, shape); 0272 } 0273 0274 void Sheet::removeShape(KoShape* shape) 0275 { 0276 if (!shape) 0277 return; 0278 d->shapes.removeAll(shape); 0279 emit shapeRemoved(this, shape); 0280 } 0281 0282 void Sheet::deleteShapes() 0283 { 0284 qDeleteAll(d->shapes); 0285 d->shapes.clear(); 0286 } 0287 0288 KoDocumentResourceManager* Sheet::resourceManager() const 0289 { 0290 return map()->resourceManager(); 0291 } 0292 0293 QList<KoShape*> Sheet::shapes() const 0294 { 0295 return d->shapes; 0296 } 0297 0298 Qt::LayoutDirection Sheet::layoutDirection() const 0299 { 0300 return d->layoutDirection; 0301 } 0302 0303 void Sheet::setLayoutDirection(Qt::LayoutDirection dir) 0304 { 0305 d->layoutDirection = dir; 0306 } 0307 0308 bool Sheet::isHidden() const 0309 { 0310 return d->hide; 0311 } 0312 0313 void Sheet::setHidden(bool hidden) 0314 { 0315 d->hide = hidden; 0316 } 0317 0318 bool Sheet::getShowGrid() const 0319 { 0320 return d->showGrid; 0321 } 0322 0323 void Sheet::setShowGrid(bool _showGrid) 0324 { 0325 d->showGrid = _showGrid; 0326 } 0327 0328 bool Sheet::getShowFormula() const 0329 { 0330 return d->showFormula; 0331 } 0332 0333 void Sheet::setShowFormula(bool _showFormula) 0334 { 0335 d->showFormula = _showFormula; 0336 } 0337 0338 bool Sheet::getShowFormulaIndicator() const 0339 { 0340 return d->showFormulaIndicator; 0341 } 0342 0343 void Sheet::setShowFormulaIndicator(bool _showFormulaIndicator) 0344 { 0345 d->showFormulaIndicator = _showFormulaIndicator; 0346 } 0347 0348 bool Sheet::getShowCommentIndicator() const 0349 { 0350 return d->showCommentIndicator; 0351 } 0352 0353 void Sheet::setShowCommentIndicator(bool _indic) 0354 { 0355 d->showCommentIndicator = _indic; 0356 } 0357 0358 bool Sheet::getLcMode() const 0359 { 0360 return d->lcMode; 0361 } 0362 0363 void Sheet::setLcMode(bool _lcMode) 0364 { 0365 d->lcMode = _lcMode; 0366 } 0367 0368 bool Sheet::isAutoCalculationEnabled() const 0369 { 0370 return d->autoCalc; 0371 } 0372 0373 void Sheet::setAutoCalculationEnabled(bool enable) 0374 { 0375 //Avoid possible recalculation of dependencies if the auto calc setting hasn't changed 0376 if (d->autoCalc == enable) 0377 return; 0378 0379 d->autoCalc = enable; 0380 //If enabling automatic calculation, make sure that the dependencies are up-to-date 0381 if (enable == true) { 0382 map()->dependencyManager()->addSheet(this); 0383 map()->recalcManager()->recalcSheet(this); 0384 } else { 0385 map()->dependencyManager()->removeSheet(this); 0386 } 0387 } 0388 0389 bool Sheet::getShowColumnNumber() const 0390 { 0391 return d->showColumnNumber; 0392 } 0393 0394 void Sheet::setShowColumnNumber(bool _showColumnNumber) 0395 { 0396 d->showColumnNumber = _showColumnNumber; 0397 } 0398 0399 bool Sheet::getHideZero() const 0400 { 0401 return d->hideZero; 0402 } 0403 0404 void Sheet::setHideZero(bool _hideZero) 0405 { 0406 d->hideZero = _hideZero; 0407 } 0408 0409 bool Sheet::getFirstLetterUpper() const 0410 { 0411 return d->firstLetterUpper; 0412 } 0413 0414 void Sheet::setFirstLetterUpper(bool _firstUpper) 0415 { 0416 d->firstLetterUpper = _firstUpper; 0417 } 0418 0419 bool Sheet::isShowPageOutline() const 0420 { 0421 return d->showPageOutline; 0422 } 0423 0424 const ColumnFormat* Sheet::columnFormat(int _column) const 0425 { 0426 const ColumnFormat *p = d->columns.lookup(_column); 0427 if (p != 0) 0428 return p; 0429 0430 return map()->defaultColumnFormat(); 0431 } 0432 0433 // needed in loading code 0434 ColumnFormat *Sheet::nextColumn(int col) const 0435 { 0436 return d->columns.next(col); 0437 } 0438 0439 CellStorage* Sheet::cellStorage() const 0440 { 0441 return d->cellStorage; 0442 } 0443 0444 const CommentStorage* Sheet::commentStorage() const 0445 { 0446 return d->cellStorage->commentStorage(); 0447 } 0448 0449 const ConditionsStorage* Sheet::conditionsStorage() const 0450 { 0451 return d->cellStorage->conditionsStorage(); 0452 } 0453 0454 const FormulaStorage* Sheet::formulaStorage() const 0455 { 0456 return d->cellStorage->formulaStorage(); 0457 } 0458 0459 const FusionStorage* Sheet::fusionStorage() const 0460 { 0461 return d->cellStorage->fusionStorage(); 0462 } 0463 0464 const LinkStorage* Sheet::linkStorage() const 0465 { 0466 return d->cellStorage->linkStorage(); 0467 } 0468 0469 const StyleStorage* Sheet::styleStorage() const 0470 { 0471 return d->cellStorage->styleStorage(); 0472 } 0473 0474 const ValidityStorage* Sheet::validityStorage() const 0475 { 0476 return d->cellStorage->validityStorage(); 0477 } 0478 0479 const ValueStorage* Sheet::valueStorage() const 0480 { 0481 return d->cellStorage->valueStorage(); 0482 } 0483 0484 SheetPrint* Sheet::print() const 0485 { 0486 return d->print; 0487 } 0488 0489 PrintSettings* Sheet::printSettings() const 0490 { 0491 return d->print->settings(); 0492 } 0493 0494 void Sheet::setPrintSettings(const PrintSettings &settings) 0495 { 0496 d->print->setSettings(settings); 0497 // Repaint, if page borders are shown and this is the active sheet. 0498 if (isShowPageOutline()) { 0499 // Just repaint everything visible; no need to invalidate the visual cache. 0500 map()->addDamage(new SheetDamage(this, SheetDamage::ContentChanged)); 0501 } 0502 } 0503 0504 HeaderFooter *Sheet::headerFooter() const 0505 { 0506 return d->print->headerFooter(); 0507 } 0508 0509 QSizeF Sheet::documentSize() const 0510 { 0511 return d->documentSize; 0512 } 0513 0514 void Sheet::adjustDocumentWidth(double deltaWidth) 0515 { 0516 d->documentSize.rwidth() += deltaWidth; 0517 emit documentSizeChanged(d->documentSize); 0518 } 0519 0520 void Sheet::adjustDocumentHeight(double deltaHeight) 0521 { 0522 d->documentSize.rheight() += deltaHeight; 0523 emit documentSizeChanged(d->documentSize); 0524 } 0525 0526 void Sheet::adjustCellAnchoredShapesX(qreal minX, qreal maxX, qreal delta) 0527 { 0528 foreach (KoShape* s, d->shapes) { 0529 if (dynamic_cast<ShapeApplicationData*>(s->applicationData())->isAnchoredToCell()) { 0530 if (s->position().x() >= minX && s->position().x() < maxX) { 0531 QPointF p = s->position(); 0532 p.setX(qMax(minX, p.x() + delta)); 0533 s->setPosition(p); 0534 } 0535 } 0536 } 0537 } 0538 0539 void Sheet::adjustCellAnchoredShapesX(qreal delta, int firstCol, int lastCol) 0540 { 0541 adjustCellAnchoredShapesX(columnPosition(firstCol), columnPosition(lastCol+1), delta); 0542 } 0543 0544 void Sheet::adjustCellAnchoredShapesY(qreal minY, qreal maxY, qreal delta) 0545 { 0546 foreach (KoShape* s, d->shapes) { 0547 if (dynamic_cast<ShapeApplicationData*>(s->applicationData())->isAnchoredToCell()) { 0548 if (s->position().y() >= minY && s->position().y() < maxY) { 0549 QPointF p = s->position(); 0550 p.setY(qMax(minY, p.y() + delta)); 0551 s->setPosition(p); 0552 } 0553 } 0554 } 0555 } 0556 0557 void Sheet::adjustCellAnchoredShapesY(qreal delta, int firstRow, int lastRow) 0558 { 0559 adjustCellAnchoredShapesY(rowPosition(firstRow), rowPosition(lastRow+1), delta); 0560 } 0561 0562 int Sheet::leftColumn(qreal _xpos, qreal &_left) const 0563 { 0564 _left = 0.0; 0565 int col = 1; 0566 double x = columnFormat(col)->visibleWidth(); 0567 while (x < _xpos && col < KS_colMax) { 0568 _left += columnFormat(col)->visibleWidth(); 0569 x += columnFormat(++col)->visibleWidth(); 0570 } 0571 return col; 0572 } 0573 0574 int Sheet::rightColumn(double _xpos) const 0575 { 0576 int col = 1; 0577 double x = columnFormat(col)->visibleWidth(); 0578 while (x <= _xpos && col < KS_colMax) 0579 x += columnFormat(++col)->visibleWidth(); 0580 return col; 0581 } 0582 0583 int Sheet::topRow(qreal _ypos, qreal & _top) const 0584 { 0585 qreal top; 0586 int row = rowFormats()->rowForPosition(_ypos, &top); 0587 _top = top; 0588 return row; 0589 } 0590 0591 int Sheet::bottomRow(double _ypos) const 0592 { 0593 return rowFormats()->rowForPosition(_ypos+1e-9); 0594 } 0595 0596 QRectF Sheet::cellCoordinatesToDocument(const QRect& cellRange) const 0597 { 0598 // TODO Stefan: Rewrite to save some iterations over the columns/rows. 0599 QRectF rect; 0600 rect.setLeft(columnPosition(cellRange.left())); 0601 rect.setRight(columnPosition(cellRange.right()) + columnFormat(cellRange.right())->width()); 0602 rect.setTop(rowPosition(cellRange.top())); 0603 rect.setBottom(rowPosition(cellRange.bottom()) + rowFormats()->rowHeight(cellRange.bottom())); 0604 return rect; 0605 } 0606 0607 QRect Sheet::documentToCellCoordinates(const QRectF &area) const 0608 { 0609 double width = 0.0; 0610 int left = 0; 0611 while (width <= area.left()) 0612 width += columnFormat(++left)->visibleWidth(); 0613 int right = left; 0614 while (width < area.right()) 0615 width += columnFormat(++right)->visibleWidth(); 0616 int top = rowFormats()->rowForPosition(area.top()); 0617 int bottom = rowFormats()->rowForPosition(area.bottom()); 0618 return QRect(left, top, right - left + 1, bottom - top + 1); 0619 } 0620 0621 double Sheet::columnPosition(int _col) const 0622 { 0623 const int max = qMin(_col, KS_colMax); 0624 double x = 0.0; 0625 for (int col = 1; col < max; ++col) 0626 x += columnFormat(col)->visibleWidth(); 0627 return x; 0628 } 0629 0630 0631 double Sheet::rowPosition(int _row) const 0632 { 0633 const int max = qMin(_row, KS_rowMax+1); 0634 return rowFormats()->totalVisibleRowHeight(1, max-1); 0635 } 0636 0637 ColumnFormat* Sheet::firstCol() const 0638 { 0639 return d->columns.first(); 0640 } 0641 0642 ColumnFormat* Sheet::nonDefaultColumnFormat(int _column, bool force_creation) 0643 { 0644 Q_ASSERT(_column >= 1 && _column <= KS_colMax); 0645 ColumnFormat *p = d->columns.lookup(_column); 0646 if (p != 0 || !force_creation) 0647 return p; 0648 0649 p = new ColumnFormat(*map()->defaultColumnFormat()); 0650 p->setSheet(this); 0651 p->setColumn(_column); 0652 0653 d->columns.insertElement(p, _column); 0654 0655 return p; 0656 } 0657 0658 void Sheet::changeCellTabName(QString const & old_name, QString const & new_name) 0659 { 0660 for (int c = 0; c < formulaStorage()->count(); ++c) { 0661 if (formulaStorage()->data(c).expression().contains(old_name)) { 0662 int nb = formulaStorage()->data(c).expression().count(old_name + '!'); 0663 QString tmp = old_name + '!'; 0664 int len = tmp.length(); 0665 tmp = formulaStorage()->data(c).expression(); 0666 0667 for (int i = 0; i < nb; ++i) { 0668 int pos = tmp.indexOf(old_name + '!'); 0669 tmp.replace(pos, len, new_name + '!'); 0670 } 0671 Cell cell(this, formulaStorage()->col(c), formulaStorage()->row(c)); 0672 Formula formula(this, cell); 0673 formula.setExpression(tmp); 0674 cell.setFormula(formula); 0675 cell.makeFormula(); 0676 } 0677 } 0678 } 0679 0680 void Sheet::insertShiftRight(const QRect& rect) 0681 { 0682 foreach(Sheet* sheet, map()->sheetList()) { 0683 for (int i = rect.top(); i <= rect.bottom(); ++i) { 0684 sheet->changeNameCellRef(QPoint(rect.left(), i), false, 0685 Sheet::ColumnInsert, sheetName(), 0686 rect.right() - rect.left() + 1); 0687 } 0688 } 0689 } 0690 0691 void Sheet::insertShiftDown(const QRect& rect) 0692 { 0693 foreach(Sheet* sheet, map()->sheetList()) { 0694 for (int i = rect.left(); i <= rect.right(); ++i) { 0695 sheet->changeNameCellRef(QPoint(i, rect.top()), false, 0696 Sheet::RowInsert, sheetName(), 0697 rect.bottom() - rect.top() + 1); 0698 } 0699 } 0700 } 0701 0702 void Sheet::removeShiftUp(const QRect& rect) 0703 { 0704 foreach(Sheet* sheet, map()->sheetList()) { 0705 for (int i = rect.left(); i <= rect.right(); ++i) { 0706 sheet->changeNameCellRef(QPoint(i, rect.top()), false, 0707 Sheet::RowRemove, sheetName(), 0708 rect.bottom() - rect.top() + 1); 0709 } 0710 } 0711 } 0712 0713 void Sheet::removeShiftLeft(const QRect& rect) 0714 { 0715 foreach(Sheet* sheet, map()->sheetList()) { 0716 for (int i = rect.top(); i <= rect.bottom(); ++i) { 0717 sheet->changeNameCellRef(QPoint(rect.left(), i), false, 0718 Sheet::ColumnRemove, sheetName(), 0719 rect.right() - rect.left() + 1); 0720 } 0721 } 0722 } 0723 0724 void Sheet::insertColumns(int col, int number) 0725 { 0726 double deltaWidth = 0.0; 0727 for (int i = 0; i < number; i++) { 0728 deltaWidth -= columnFormat(KS_colMax)->width(); 0729 d->columns.insertColumn(col); 0730 deltaWidth += columnFormat(col + i)->width(); 0731 } 0732 // Adjust document width (plus widths of new columns; minus widths of removed columns). 0733 adjustDocumentWidth(deltaWidth); 0734 0735 foreach(Sheet* sheet, map()->sheetList()) { 0736 sheet->changeNameCellRef(QPoint(col, 1), true, 0737 Sheet::ColumnInsert, sheetName(), 0738 number); 0739 } 0740 //update print settings 0741 d->print->insertColumn(col, number); 0742 } 0743 0744 void Sheet::insertRows(int row, int number) 0745 { 0746 d->rows.insertRows(row, number); 0747 0748 foreach(Sheet* sheet, map()->sheetList()) { 0749 sheet->changeNameCellRef(QPoint(1, row), true, 0750 Sheet::RowInsert, sheetName(), 0751 number); 0752 } 0753 //update print settings 0754 d->print->insertRow(row, number); 0755 } 0756 0757 void Sheet::removeColumns(int col, int number) 0758 { 0759 double deltaWidth = 0.0; 0760 for (int i = 0; i < number; ++i) { 0761 deltaWidth -= columnFormat(col)->width(); 0762 d->columns.removeColumn(col); 0763 deltaWidth += columnFormat(KS_colMax)->width(); 0764 } 0765 // Adjust document width (plus widths of new columns; minus widths of removed columns). 0766 adjustDocumentWidth(deltaWidth); 0767 0768 foreach(Sheet* sheet, map()->sheetList()) { 0769 sheet->changeNameCellRef(QPoint(col, 1), true, 0770 Sheet::ColumnRemove, sheetName(), 0771 number); 0772 } 0773 //update print settings 0774 d->print->removeColumn(col, number); 0775 } 0776 0777 void Sheet::removeRows(int row, int number) 0778 { 0779 d->rows.removeRows(row, number); 0780 0781 foreach(Sheet* sheet, map()->sheetList()) { 0782 sheet->changeNameCellRef(QPoint(1, row), true, 0783 Sheet::RowRemove, sheetName(), 0784 number); 0785 } 0786 0787 //update print settings 0788 d->print->removeRow(row, number); 0789 } 0790 0791 QString Sheet::changeNameCellRefHelper(const QPoint& pos, bool fullRowOrColumn, ChangeRef ref, 0792 int nbCol, const QPoint& point, bool isColumnFixed, 0793 bool isRowFixed) 0794 { 0795 QString newPoint; 0796 int col = point.x(); 0797 int row = point.y(); 0798 // update column 0799 if (isColumnFixed) 0800 newPoint.append('$'); 0801 if (ref == ColumnInsert && 0802 col + nbCol <= KS_colMax && 0803 col >= pos.x() && // Column after the new one : +1 0804 (fullRowOrColumn || row == pos.y())) { // All rows or just one 0805 newPoint += Cell::columnName(col + nbCol); 0806 } else if (ref == ColumnRemove && 0807 col > pos.x() && // Column after the deleted one : -1 0808 (fullRowOrColumn || row == pos.y())) { // All rows or just one 0809 newPoint += Cell::columnName(col - nbCol); 0810 } else 0811 newPoint += Cell::columnName(col); 0812 0813 // Update row 0814 if (isRowFixed) 0815 newPoint.append('$'); 0816 if (ref == RowInsert && 0817 row + nbCol <= KS_rowMax && 0818 row >= pos.y() && // Row after the new one : +1 0819 (fullRowOrColumn || col == pos.x())) { // All columns or just one 0820 newPoint += QString::number(row + nbCol); 0821 } else if (ref == RowRemove && 0822 row > pos.y() && // Row after the deleted one : -1 0823 (fullRowOrColumn || col == pos.x())) { // All columns or just one 0824 newPoint += QString::number(row - nbCol); 0825 } else 0826 newPoint += QString::number(row); 0827 0828 if (((ref == ColumnRemove 0829 && (col >= pos.x() && col < pos.x() + nbCol) // Column is the deleted one : error 0830 && (fullRowOrColumn || row == pos.y())) || 0831 (ref == RowRemove 0832 && (row >= pos.y() && row < pos.y() + nbCol) // Row is the deleted one : error 0833 && (fullRowOrColumn || col == pos.x())) || 0834 (ref == ColumnInsert 0835 && col + nbCol > KS_colMax 0836 && col >= pos.x() // Column after the new one : +1 0837 && (fullRowOrColumn || row == pos.y())) || 0838 (ref == RowInsert 0839 && row + nbCol > KS_rowMax 0840 && row >= pos.y() // Row after the new one : +1 0841 && (fullRowOrColumn || col == pos.x())))) { 0842 newPoint = '#' + i18n("Dependency") + '!'; 0843 } 0844 return newPoint; 0845 } 0846 0847 QString Sheet::changeNameCellRefHelper(const QPoint& pos, const QRect& rect, bool fullRowOrColumn, ChangeRef ref, 0848 int nbCol, const QPoint& point, bool isColumnFixed, 0849 bool isRowFixed) 0850 { 0851 const bool isFirstColumn = pos.x() == rect.left(); 0852 const bool isLastColumn = pos.x() == rect.right(); 0853 const bool isFirstRow = pos.y() == rect.top(); 0854 const bool isLastRow = pos.y() == rect.bottom(); 0855 0856 QString newPoint; 0857 int col = point.x(); 0858 int row = point.y(); 0859 // update column 0860 if (isColumnFixed) 0861 newPoint.append('$'); 0862 if (ref == ColumnInsert && 0863 col + nbCol <= KS_colMax && 0864 col >= pos.x() && // Column after the new one : +1 0865 (fullRowOrColumn || row == pos.y())) { // All rows or just one 0866 newPoint += Cell::columnName(col + nbCol); 0867 } else if (ref == ColumnRemove && 0868 (col > pos.x() || 0869 (col == pos.x() && isLastColumn)) && // Column after the deleted one : -1 0870 (fullRowOrColumn || row == pos.y())) { // All rows or just one 0871 newPoint += Cell::columnName(col - nbCol); 0872 } else 0873 newPoint += Cell::columnName(col); 0874 0875 // Update row 0876 if (isRowFixed) 0877 newPoint.append('$'); 0878 if (ref == RowInsert && 0879 row + nbCol <= KS_rowMax && 0880 row >= pos.y() && // Row after the new one : +1 0881 (fullRowOrColumn || col == pos.x())) { // All columns or just one 0882 newPoint += QString::number(row + nbCol); 0883 } else if (ref == RowRemove && 0884 (row > pos.y() || 0885 (row == pos.y() && isLastRow)) && // Row after the deleted one : -1 0886 (fullRowOrColumn || col == pos.x())) { // All columns or just one 0887 newPoint += QString::number(row - nbCol); 0888 } else 0889 newPoint += QString::number(row); 0890 0891 if (((ref == ColumnRemove 0892 && col == pos.x() // Column is the deleted one : error 0893 && (fullRowOrColumn || row == pos.y()) 0894 && (isFirstColumn && isLastColumn)) || 0895 (ref == RowRemove 0896 && row == pos.y() // Row is the deleted one : error 0897 && (fullRowOrColumn || col == pos.x()) 0898 && (isFirstRow && isLastRow)) || 0899 (ref == ColumnInsert 0900 && col + nbCol > KS_colMax 0901 && col >= pos.x() // Column after the new one : +1 0902 && (fullRowOrColumn || row == pos.y())) || 0903 (ref == RowInsert 0904 && row + nbCol > KS_rowMax 0905 && row >= pos.y() // Row after the new one : +1 0906 && (fullRowOrColumn || col == pos.x())))) { 0907 newPoint = '#' + i18n("Dependency") + '!'; 0908 } 0909 return newPoint; 0910 } 0911 0912 void Sheet::changeNameCellRef(const QPoint& pos, bool fullRowOrColumn, ChangeRef ref, 0913 const QString& tabname, int nbCol) 0914 { 0915 for (int c = 0; c < formulaStorage()->count(); ++c) { 0916 QString newText('='); 0917 const Tokens tokens = formulaStorage()->data(c).tokens(); 0918 for (int t = 0; t < tokens.count(); ++t) { 0919 const Token token = tokens[t]; 0920 switch (token.type()) { 0921 case Token::Cell: 0922 case Token::Range: { 0923 if (map()->namedAreaManager()->contains(token.text())) { 0924 newText.append(token.text()); // simply keep the area name 0925 break; 0926 } 0927 const Region region(token.text(), map()); 0928 if (!region.isValid() || !region.isContiguous()) { 0929 newText.append(token.text()); 0930 break; 0931 } 0932 if (!region.firstSheet() && tabname != sheetName()) { 0933 // nothing to do here 0934 newText.append(token.text()); 0935 break; 0936 } 0937 // actually only one element in here, but we need extended access to the element 0938 Region::ConstIterator end(region.constEnd()); 0939 for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { 0940 Region::Element* element = (*it); 0941 if (element->type() == Region::Element::Point) { 0942 if (element->sheet()) 0943 newText.append(element->sheet()->sheetName() + '!'); 0944 QString newPoint = changeNameCellRefHelper(pos, fullRowOrColumn, ref, 0945 nbCol, 0946 element->rect().topLeft(), 0947 element->isColumnFixed(), 0948 element->isRowFixed()); 0949 newText.append(newPoint); 0950 } else { // (element->type() == Region::Element::Range) 0951 if (element->sheet()) 0952 newText.append(element->sheet()->sheetName() + '!'); 0953 QString newPoint; 0954 newPoint = changeNameCellRefHelper(pos, element->rect(), fullRowOrColumn, ref, 0955 nbCol, element->rect().topLeft(), 0956 element->isColumnFixed(), 0957 element->isRowFixed()); 0958 newText.append(newPoint + ':'); 0959 newPoint = changeNameCellRefHelper(pos, element->rect(), fullRowOrColumn, ref, 0960 nbCol, element->rect().bottomRight(), 0961 element->isColumnFixed(), 0962 element->isRowFixed()); 0963 newText.append(newPoint); 0964 } 0965 } 0966 break; 0967 } 0968 default: { 0969 newText.append(token.text()); 0970 break; 0971 } 0972 } 0973 } 0974 0975 Cell cell(this, formulaStorage()->col(c), formulaStorage()->row(c)); 0976 Formula formula(this, cell); 0977 formula.setExpression(newText); 0978 cell.setFormula(formula); 0979 } 0980 } 0981 0982 // helper function for Sheet::areaIsEmpty 0983 bool Sheet::cellIsEmpty(const Cell& cell, TestType _type) 0984 { 0985 if (!cell.isPartOfMerged()) { 0986 switch (_type) { 0987 case Text : 0988 if (!cell.userInput().isEmpty()) 0989 return false; 0990 break; 0991 case Validity: 0992 if (!cell.validity().isEmpty()) 0993 return false; 0994 break; 0995 case Comment: 0996 if (!cell.comment().isEmpty()) 0997 return false; 0998 break; 0999 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 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 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 1260 bool Sheet::isLoading() 1261 { 1262 return map()->isLoading(); 1263 } 1264 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 1274 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 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 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 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 1619 QImage Sheet::backgroundImage() const 1620 { 1621 return d->backgroundImage; 1622 } 1623 1624 void Sheet::setBackgroundImage(const QImage& image) 1625 { 1626 d->backgroundImage = image; 1627 } 1628 1629 Sheet::BackgroundImageProperties Sheet::backgroundImageProperties() const 1630 { 1631 return d->backgroundProperties; 1632 } 1633 1634 void Sheet::setBackgroundImageProperties(const Sheet::BackgroundImageProperties& properties) 1635 { 1636 d->backgroundProperties = properties; 1637 } 1638 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 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 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 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 1676 RowFormatStorage* Sheet::rowFormats() 1677 { 1678 return &d->rows; 1679 } 1680 1681 const RowFormatStorage* Sheet::rowFormats() const 1682 { 1683 return &d->rows; 1684 } 1685 1686 void Sheet::showStatusMessage(const QString &message, int timeout) 1687 { 1688 emit statusMessage(message, timeout); 1689 } 1690 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 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 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 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 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 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