File indexing completed on 2024-05-12 16:35:40
0001 /* This file is part of the KDE project 0002 Copyright 1998-2016 The Calligra Team <calligra-devel@kde.org> 0003 Copyright 2016 Tomas Mecir <mecirt@gmail.com> 0004 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> 0005 Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net> 0006 Copyright 2007 Thorsten Zachmann <zachmann@kde.org> 0007 Copyright 2005-2006 Inge Wallin <inge@lysator.liu.se> 0008 Copyright 2004 Ariya Hidayat <ariya@kde.org> 0009 Copyright 2002-2003 Norbert Andres <nandres@web.de> 0010 Copyright 2000-2002 Laurent Montel <montel@kde.org> 0011 Copyright 2002 John Dailey <dailey@vt.edu> 0012 Copyright 2002 Phillip Mueller <philipp.mueller@gmx.de> 0013 Copyright 2000 Werner Trobin <trobin@kde.org> 0014 Copyright 1999-2000 Simon Hausmann <hausmann@kde.org> 0015 Copyright 1999 David Faure <faure@kde.org> 0016 Copyright 1998-2000 Torben Weis <weis@kde.org> 0017 0018 This library is free software; you can redistribute it and/or 0019 modify it under the terms of the GNU Library General Public 0020 License as published by the Free Software Foundation; either 0021 version 2 of the License, or (at your option) any later version. 0022 0023 This library is distributed in the hope that it will be useful, 0024 but WITHOUT ANY WARRANTY; without even the implied warranty of 0025 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0026 Library General Public License for more details. 0027 0028 You should have received a copy of the GNU Library General Public License 0029 along with this library; see the file COPYING.LIB. If not, write to 0030 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0031 Boston, MA 02110-1301, USA. 0032 */ 0033 0034 #include "SheetsOdf.h" 0035 #include "SheetsOdfPrivate.h" 0036 0037 #include <kcodecs.h> 0038 0039 #include <KoDocumentInfo.h> 0040 #include <KoGenStyles.h> 0041 #include <KoProgressUpdater.h> 0042 #include <KoShape.h> 0043 #include <KoShapeRegistry.h> 0044 #include "KoStore.h" 0045 #include <KoStyleStack.h> 0046 #include "KoUnit.h" 0047 #include <KoUpdater.h> 0048 #include <KoXmlNS.h> 0049 #include <KoXmlWriter.h> 0050 0051 #include "CellStorage.h" 0052 #include "Condition.h" 0053 #include "DocBase.h" 0054 #include "Formula.h" 0055 #include "HeaderFooter.h" 0056 #include "LoadingInfo.h" 0057 #include "Map.h" 0058 #include "PrintSettings.h" 0059 #include "RowColumnFormat.h" 0060 #include "RowFormatStorage.h" 0061 #include "Sheet.h" 0062 #include "SheetPrint.h" 0063 #include "ShapeApplicationData.h" 0064 #include "StyleManager.h" 0065 #include "StyleStorage.h" 0066 #include "Validity.h" 0067 0068 // This file contains functionality to load/save a Sheet 0069 0070 namespace Calligra { 0071 namespace Sheets { 0072 0073 class Cell; 0074 0075 template<typename T> class IntervalMap 0076 { 0077 public: 0078 IntervalMap() {} 0079 // from and to are inclusive, assumes no overlapping ranges 0080 // even though no checks are done 0081 void insert(int from, int to, const T& data) { 0082 m_data.insert(to, qMakePair(from, data)); 0083 } 0084 T get(int idx) const { 0085 typename QMap<int, QPair<int, T> >::ConstIterator it = m_data.lowerBound(idx); 0086 if (it != m_data.end() && it.value().first <= idx) { 0087 return it.value().second; 0088 } 0089 return T(); 0090 } 0091 private: 0092 QMap<int, QPair<int, T> > m_data; 0093 }; 0094 0095 0096 namespace Odf { 0097 // Sheet loading - helper functions 0098 /** 0099 * Inserts the styles contained in \p styleRegions into the style storage. 0100 * Looks automatic styles up in the map of preloaded automatic styles, 0101 * \p autoStyles , and custom styles in the StyleManager. 0102 * The region is restricted to \p usedArea . 0103 */ 0104 void loadSheetInsertStyles(Sheet *sheet, const Styles& autoStyles, 0105 const QHash<QString, QRegion>& styleRegions, 0106 const QHash<QString, Conditions>& conditionalStyles, 0107 const QRect& usedArea, 0108 QList<QPair<QRegion, Style> >& outStyleRegions, 0109 QList<QPair<QRegion, Conditions> >& outConditionalStyles); 0110 0111 bool loadStyleFormat(Sheet *sheet, KoXmlElement *style); 0112 void loadMasterLayoutPage(Sheet *sheet, KoStyleStack &styleStack); 0113 void loadRowNodes(Sheet *sheet, const KoXmlElement& parent, 0114 int& rowIndex, 0115 int& maxColumn, 0116 OdfLoadingContext& tableContext, 0117 QHash<QString, QRegion>& rowStyleRegions, 0118 QHash<QString, QRegion>& cellStyleRegions, 0119 const IntervalMap<QString>& columnStyles, 0120 const Styles& autoStyles, 0121 QList<ShapeLoadingData>& shapeData); 0122 void loadColumnNodes(Sheet *sheet, const KoXmlElement& parent, 0123 int& indexCol, 0124 int& maxColumn, 0125 KoOdfLoadingContext& odfContext, 0126 QHash<QString, QRegion>& columnStyleRegions, 0127 IntervalMap<QString>& columnStyles); 0128 bool loadColumnFormat(Sheet *sheet, const KoXmlElement& column, 0129 const KoOdfStylesReader& stylesReader, int & indexCol, 0130 QHash<QString, QRegion>& columnStyleRegions, IntervalMap<QString>& columnStyles); 0131 int loadRowFormat(Sheet *sheet, const KoXmlElement& row, int &rowIndex, 0132 OdfLoadingContext& tableContext, 0133 QHash<QString, QRegion>& rowStyleRegions, 0134 QHash<QString, QRegion>& cellStyleRegions, 0135 const IntervalMap<QString>& columnStyles, 0136 const Styles& autoStyles, 0137 QList<ShapeLoadingData>& shapeData); 0138 QString getPart(const KoXmlNode & part); 0139 void replaceMacro(QString & text, const QString & old, const QString & newS); 0140 0141 // Sheet saving - helper functions 0142 QString saveSheetStyleName(Sheet *sheet, KoGenStyles &mainStyles); 0143 void saveColRowCell(Sheet *sheet, int maxCols, int maxRows, OdfSavingContext& tableContext); 0144 void saveCells(Sheet *sheet, int row, int maxCols, OdfSavingContext& tableContext); 0145 void saveHeaderFooter(Sheet *sheet, KoXmlWriter &xmlWriter); 0146 void saveBackgroundImage(Sheet *sheet, KoXmlWriter& xmlWriter); 0147 void addText(const QString & text, KoXmlWriter & writer); 0148 void convertPart(Sheet *sheet, const QString & part, KoXmlWriter & xmlWriter); 0149 bool compareRows(Sheet *sheet, int row1, int row2, int maxCols, OdfSavingContext& tableContext); 0150 QString savePageLayout(PrintSettings *settings, KoGenStyles &mainStyles, bool formulas, bool zeros); 0151 } 0152 0153 // *************** Loading ***************** 0154 0155 bool Odf::loadSheet(Sheet *sheet, const KoXmlElement& sheetElement, OdfLoadingContext& tableContext, const Styles& autoStyles, const QHash<QString, Conditions>& conditionalStyles) 0156 { 0157 QPointer<KoUpdater> updater; 0158 if (sheet->doc() && sheet->doc()->progressUpdater()) { 0159 updater = sheet->doc()->progressUpdater()->startSubtask(1, 0160 "Calligra::Sheets::Odf::loadSheet"); 0161 updater->setProgress(0); 0162 } 0163 0164 KoOdfLoadingContext& odfContext = tableContext.odfContext; 0165 if (sheetElement.hasAttributeNS(KoXmlNS::table, "style-name")) { 0166 QString stylename = sheetElement.attributeNS(KoXmlNS::table, "style-name", QString()); 0167 //debugSheetsODF<<" style of table :"<<stylename; 0168 const KoXmlElement *style = odfContext.stylesReader().findStyle(stylename, "table"); 0169 Q_ASSERT(style); 0170 //debugSheetsODF<<" style :"<<style; 0171 if (style) { 0172 KoXmlElement properties(KoXml::namedItemNS(*style, KoXmlNS::style, "table-properties")); 0173 if (!properties.isNull()) { 0174 if (properties.hasAttributeNS(KoXmlNS::table, "display")) { 0175 bool visible = (properties.attributeNS(KoXmlNS::table, "display", QString()) == "true" ? true : false); 0176 sheet->setHidden(!visible); 0177 } 0178 } 0179 if (style->hasAttributeNS(KoXmlNS::style, "master-page-name")) { 0180 QString masterPageStyleName = style->attributeNS(KoXmlNS::style, "master-page-name", QString()); 0181 //debugSheets<<"style->attribute( style:master-page-name ) :"<<masterPageStyleName; 0182 KoXmlElement *masterStyle = odfContext.stylesReader().masterPages()[masterPageStyleName]; 0183 //debugSheets<<"stylesReader.styles()[masterPageStyleName] :"<<masterStyle; 0184 if (masterStyle) { 0185 loadStyleFormat(sheet, masterStyle); 0186 if (masterStyle->hasAttributeNS(KoXmlNS::style, "page-layout-name")) { 0187 QString masterPageLayoutStyleName = masterStyle->attributeNS(KoXmlNS::style, "page-layout-name", QString()); 0188 //debugSheetsODF<<"masterPageLayoutStyleName :"<<masterPageLayoutStyleName; 0189 const KoXmlElement *masterLayoutStyle = odfContext.stylesReader().findStyle(masterPageLayoutStyleName); 0190 if (masterLayoutStyle) { 0191 //debugSheetsODF<<"masterLayoutStyle :"<<masterLayoutStyle; 0192 KoStyleStack styleStack; 0193 styleStack.setTypeProperties("page-layout"); 0194 styleStack.push(*masterLayoutStyle); 0195 loadMasterLayoutPage(sheet, styleStack); 0196 } 0197 } 0198 } 0199 } 0200 0201 if (style->hasChildNodes() ) { 0202 KoXmlElement element; 0203 forEachElement(element, properties) { 0204 if (element.nodeName() == "style:background-image") { 0205 QString imagePath = element.attributeNS(KoXmlNS::xlink, "href"); 0206 KoStore* store = tableContext.odfContext.store(); 0207 if (store->hasFile(imagePath)) { 0208 QByteArray data; 0209 store->extractFile(imagePath, data); 0210 QImage image = QImage::fromData(data); 0211 0212 if( image.isNull() ) { 0213 continue; 0214 } 0215 0216 sheet->setBackgroundImage(image); 0217 0218 Sheet::BackgroundImageProperties bgProperties; 0219 if( element.hasAttribute("draw:opacity") ) { 0220 QString opacity = element.attribute("draw:opacity", ""); 0221 if( opacity.endsWith(QLatin1Char('%')) ) { 0222 opacity.chop(1); 0223 } 0224 bool ok; 0225 float opacityFloat = opacity.toFloat( &ok ); 0226 if( ok ) { 0227 bgProperties.opacity = opacityFloat; 0228 } 0229 } 0230 //TODO 0231 //if( element.hasAttribute("style:filterName") ) { 0232 //} 0233 if( element.hasAttribute("style:position") ) { 0234 const QString positionAttribute = element.attribute("style:position",""); 0235 const QStringList positionList = positionAttribute.split(' ', QString::SkipEmptyParts); 0236 if( positionList.size() == 1) { 0237 const QString position = positionList.at(0); 0238 if( position == "left" ) { 0239 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::Left; 0240 } 0241 if( position == "center" ) { 0242 //NOTE the standard is too vague to know what center alone means, we assume that it means both centered 0243 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::HorizontalCenter; 0244 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::VerticalCenter; 0245 } 0246 if( position == "right" ) { 0247 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::Right; 0248 } 0249 if( position == "top" ) { 0250 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::Top; 0251 } 0252 if( position == "bottom" ) { 0253 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::Bottom; 0254 } 0255 } 0256 else if (positionList.size() == 2) { 0257 const QString verticalPosition = positionList.at(0); 0258 const QString horizontalPosition = positionList.at(1); 0259 if( horizontalPosition == "left" ) { 0260 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::Left; 0261 } 0262 if( horizontalPosition == "center" ) { 0263 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::HorizontalCenter; 0264 } 0265 if( horizontalPosition == "right" ) { 0266 bgProperties.horizontalPosition = Sheet::BackgroundImageProperties::Right; 0267 } 0268 if( verticalPosition == "top" ) { 0269 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::Top; 0270 } 0271 if( verticalPosition == "center" ) { 0272 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::VerticalCenter; 0273 } 0274 if( verticalPosition == "bottom" ) { 0275 bgProperties.verticalPosition = Sheet::BackgroundImageProperties::Bottom; 0276 } 0277 } 0278 } 0279 if( element.hasAttribute("style:repeat") ) { 0280 const QString repeat = element.attribute("style:repeat"); 0281 if( repeat == "no-repeat" ) { 0282 bgProperties.repeat = Sheet::BackgroundImageProperties::NoRepeat; 0283 } 0284 if( repeat == "repeat" ) { 0285 bgProperties.repeat = Sheet::BackgroundImageProperties::Repeat; 0286 } 0287 if( repeat == "stretch" ) { 0288 bgProperties.repeat = Sheet::BackgroundImageProperties::Stretch; 0289 } 0290 } 0291 sheet->setBackgroundImageProperties(bgProperties); 0292 } 0293 } 0294 } 0295 0296 } 0297 } 0298 } 0299 0300 // Cell style regions 0301 QHash<QString, QRegion> cellStyleRegions; 0302 // Cell style regions (row defaults) 0303 QHash<QString, QRegion> rowStyleRegions; 0304 // Cell style regions (column defaults) 0305 QHash<QString, QRegion> columnStyleRegions; 0306 IntervalMap<QString> columnStyles; 0307 0308 // List of shapes that need to have their size recalculated after loading is complete 0309 QList<ShapeLoadingData> shapeData; 0310 0311 int rowIndex = 1; 0312 int indexCol = 1; 0313 int maxColumn = 1; 0314 KoXmlNode rowNode = sheetElement.firstChild(); 0315 // Some spreadsheet programs may support more rows than 0316 // Calligra Sheets so limit the number of repeated rows. 0317 // FIXME POSSIBLE DATA LOSS! 0318 0319 // First load all style information for rows, columns and cells 0320 while (!rowNode.isNull() && rowIndex <= KS_rowMax) { 0321 //debugSheetsODF << " rowIndex :" << rowIndex << " indexCol :" << indexCol; 0322 KoXmlElement rowElement = rowNode.toElement(); 0323 if (!rowElement.isNull()) { 0324 // slightly faster 0325 KoXml::load(rowElement); 0326 0327 //debugSheetsODF << " Odf::loadSheet rowElement.tagName() :" << rowElement.localName(); 0328 if (rowElement.namespaceURI() == KoXmlNS::table) { 0329 if (rowElement.localName() == "table-header-columns") { 0330 // NOTE Handle header cols as ordinary ones 0331 // as long as they're not supported. 0332 loadColumnNodes(sheet, rowElement, indexCol, maxColumn, odfContext, columnStyleRegions, columnStyles); 0333 } else if (rowElement.localName() == "table-column-group") { 0334 loadColumnNodes(sheet, rowElement, indexCol, maxColumn, odfContext, columnStyleRegions, columnStyles); 0335 } else if (rowElement.localName() == "table-column" && indexCol <= KS_colMax) { 0336 //debugSheetsODF << " table-column found : index column before" << indexCol; 0337 loadColumnFormat(sheet, rowElement, odfContext.stylesReader(), indexCol, columnStyleRegions, columnStyles); 0338 //debugSheetsODF << " table-column found : index column after" << indexCol; 0339 maxColumn = qMax(maxColumn, indexCol - 1); 0340 } else if (rowElement.localName() == "table-header-rows") { 0341 // NOTE Handle header rows as ordinary ones 0342 // as long as they're not supported. 0343 loadRowNodes(sheet, rowElement, rowIndex, maxColumn, tableContext, rowStyleRegions, cellStyleRegions, columnStyles, autoStyles, shapeData); 0344 } else if (rowElement.localName() == "table-row-group") { 0345 loadRowNodes(sheet, rowElement, rowIndex, maxColumn, tableContext, rowStyleRegions, cellStyleRegions, columnStyles, autoStyles, shapeData); 0346 } else if (rowElement.localName() == "table-row") { 0347 //debugSheetsODF << " table-row found :index row before" << rowIndex; 0348 int columnMaximal = loadRowFormat(sheet, rowElement, rowIndex, tableContext, 0349 rowStyleRegions, cellStyleRegions, columnStyles, autoStyles, shapeData); 0350 // allow the row to define more columns then defined via table-column 0351 maxColumn = qMax(maxColumn, columnMaximal); 0352 //debugSheetsODF << " table-row found :index row after" << rowIndex; 0353 } else if (rowElement.localName() == "shapes") { 0354 // OpenDocument v1.1, 8.3.4 Shapes: 0355 // The <table:shapes> element contains all graphic shapes 0356 // with an anchor on the table this element is a child of. 0357 KoShapeLoadingContext* shapeLoadingContext = tableContext.shapeContext; 0358 KoXmlElement element; 0359 forEachElement(element, rowElement) { 0360 if (element.namespaceURI() != KoXmlNS::draw) 0361 continue; 0362 loadSheetObject(sheet, element, *shapeLoadingContext); 0363 } 0364 } 0365 } 0366 0367 // don't need it anymore 0368 KoXml::unload(rowElement); 0369 } 0370 0371 rowNode = rowNode.nextSibling(); 0372 0373 int count = sheet->map()->increaseLoadedRowsCounter(); 0374 if (updater && count >= 0) updater->setProgress(count); 0375 } 0376 0377 // now recalculate the size for embedded shapes that had sizes specified relative to a bottom-right corner cell 0378 foreach (const ShapeLoadingData& sd, shapeData) { 0379 // subtract offset because the accumulated width and height we calculate below starts 0380 // at the top-left corner of this cell, but the shape can have an offset to that corner 0381 QSizeF size = QSizeF( sd.endPoint.x() - sd.offset.x(), sd.endPoint.y() - sd.offset.y()); 0382 for (int col = sd.startCell.x(); col < sd.endCell.firstRange().left(); ++col) 0383 size += QSizeF(sheet->columnFormat(col)->width(), 0.0); 0384 if (sd.endCell.firstRange().top() > sd.startCell.y()) 0385 size += QSizeF(0.0, sheet->rowFormats()->totalRowHeight(sd.startCell.y(), sd.endCell.firstRange().top() - 1)); 0386 sd.shape->setSize(size); 0387 } 0388 0389 QList<QPair<QRegion, Style> > styleRegions; 0390 QList<QPair<QRegion, Conditions> > conditionRegions; 0391 // insert the styles into the storage (column defaults) 0392 debugSheetsODF << "Inserting column default cell styles ..."; 0393 loadSheetInsertStyles(sheet, autoStyles, columnStyleRegions, conditionalStyles, 0394 QRect(1, 1, maxColumn, rowIndex - 1), styleRegions, conditionRegions); 0395 // insert the styles into the storage (row defaults) 0396 debugSheetsODF << "Inserting row default cell styles ..."; 0397 loadSheetInsertStyles(sheet, autoStyles, rowStyleRegions, conditionalStyles, 0398 QRect(1, 1, maxColumn, rowIndex - 1), styleRegions, conditionRegions); 0399 // insert the styles into the storage 0400 debugSheetsODF << "Inserting cell styles ..."; 0401 loadSheetInsertStyles(sheet, autoStyles, cellStyleRegions, conditionalStyles, 0402 QRect(1, 1, maxColumn, rowIndex - 1), styleRegions, conditionRegions); 0403 0404 sheet->cellStorage()->loadStyles(styleRegions); 0405 sheet->cellStorage()->loadConditions(conditionRegions); 0406 0407 if (sheetElement.hasAttributeNS(KoXmlNS::table, "print-ranges")) { 0408 // e.g.: Sheet4.A1:Sheet4.E28 0409 QString range = sheetElement.attributeNS(KoXmlNS::table, "print-ranges", QString()); 0410 Region region(loadRegion(range)); 0411 if (!region.firstSheet() || sheet->sheetName() == region.firstSheet()->sheetName()) 0412 sheet->printSettings()->setPrintRegion(region); 0413 } 0414 0415 if (sheetElement.attributeNS(KoXmlNS::table, "protected", QString()) == "true") { 0416 loadProtection(sheet, sheetElement); 0417 } 0418 return true; 0419 } 0420 0421 void Odf::loadSheetObject(Sheet *sheet, const KoXmlElement& element, KoShapeLoadingContext& shapeContext) 0422 { 0423 KoShape* shape = KoShapeRegistry::instance()->createShapeFromOdf(element, shapeContext); 0424 if (!shape) 0425 return; 0426 sheet->addShape(shape); 0427 dynamic_cast<ShapeApplicationData*>(shape->applicationData())->setAnchoredToCell(false); 0428 } 0429 0430 void Odf::loadRowNodes(Sheet *sheet, const KoXmlElement& parent, 0431 int& rowIndex, 0432 int& maxColumn, 0433 OdfLoadingContext& tableContext, 0434 QHash<QString, QRegion>& rowStyleRegions, 0435 QHash<QString, QRegion>& cellStyleRegions, 0436 const IntervalMap<QString>& columnStyles, 0437 const Styles& autoStyles, 0438 QList<ShapeLoadingData>& shapeData 0439 ) 0440 { 0441 KoXmlNode node = parent.firstChild(); 0442 while (!node.isNull()) { 0443 KoXmlElement elem = node.toElement(); 0444 if (!elem.isNull() && elem.namespaceURI() == KoXmlNS::table) { 0445 if (elem.localName() == "table-row") { 0446 int columnMaximal = loadRowFormat(sheet, elem, rowIndex, tableContext, 0447 rowStyleRegions, cellStyleRegions, 0448 columnStyles, autoStyles, shapeData); 0449 // allow the row to define more columns then defined via table-column 0450 maxColumn = qMax(maxColumn, columnMaximal); 0451 } else if (elem.localName() == "table-row-group") { 0452 loadRowNodes(sheet, elem, rowIndex, maxColumn, tableContext, rowStyleRegions, cellStyleRegions, columnStyles, autoStyles, shapeData); 0453 } 0454 } 0455 node = node.nextSibling(); 0456 } 0457 } 0458 0459 void Odf::loadColumnNodes(Sheet *sheet, const KoXmlElement& parent, 0460 int& indexCol, 0461 int& maxColumn, 0462 KoOdfLoadingContext& odfContext, 0463 QHash<QString, QRegion>& columnStyleRegions, 0464 IntervalMap<QString>& columnStyles 0465 ) 0466 { 0467 KoXmlNode node = parent.firstChild(); 0468 while (!node.isNull()) { 0469 KoXmlElement elem = node.toElement(); 0470 if (!elem.isNull() && elem.namespaceURI() == KoXmlNS::table) { 0471 if (elem.localName() == "table-column") { 0472 loadColumnFormat(sheet, elem, odfContext.stylesReader(), indexCol, columnStyleRegions, columnStyles); 0473 maxColumn = qMax(maxColumn, indexCol - 1); 0474 } else if (elem.localName() == "table-column-group") { 0475 loadColumnNodes(sheet, elem, indexCol, maxColumn, odfContext, columnStyleRegions, columnStyles); 0476 } 0477 } 0478 node = node.nextSibling(); 0479 } 0480 } 0481 0482 0483 void Odf::loadSheetInsertStyles(Sheet *sheet, const Styles& autoStyles, 0484 const QHash<QString, QRegion>& styleRegions, 0485 const QHash<QString, Conditions>& conditionalStyles, 0486 const QRect& usedArea, 0487 QList<QPair<QRegion, Style> >& outStyleRegions, 0488 QList<QPair<QRegion, Conditions> >& outConditionalStyles) 0489 { 0490 const QList<QString> styleNames = styleRegions.keys(); 0491 for (int i = 0; i < styleNames.count(); ++i) { 0492 if (!autoStyles.contains(styleNames[i]) && !sheet->map()->styleManager()->style(styleNames[i])) { 0493 warnSheetsODF << "\t" << styleNames[i] << " not used"; 0494 continue; 0495 } 0496 const bool hasConditions = conditionalStyles.contains(styleNames[i]); 0497 const QRegion styleRegion = styleRegions[styleNames[i]] & QRegion(usedArea); 0498 if (hasConditions) 0499 outConditionalStyles.append(qMakePair(styleRegion, conditionalStyles[styleNames[i]])); 0500 if (autoStyles.contains(styleNames[i])) { 0501 //debugSheetsODF << "\tautomatic:" << styleNames[i] << " at" << styleRegion.rectCount() << "rects"; 0502 Style style; 0503 style.setDefault(); // "overwrite" existing style 0504 style.merge(autoStyles[styleNames[i]]); 0505 outStyleRegions.append(qMakePair(styleRegion, style)); 0506 } else { 0507 const CustomStyle* namedStyle = sheet->map()->styleManager()->style(styleNames[i]); 0508 //debugSheetsODF << "\tcustom:" << namedStyle->name() << " at" << styleRegion.rectCount() << "rects"; 0509 Style style; 0510 style.setDefault(); // "overwrite" existing style 0511 style.merge(*namedStyle); 0512 outStyleRegions.append(qMakePair(styleRegion, style)); 0513 } 0514 } 0515 } 0516 0517 void Odf::replaceMacro(QString & text, const QString & old, const QString & newS) 0518 { 0519 int n = text.indexOf(old); 0520 if (n != -1) 0521 text = text.replace(n, old.length(), newS); 0522 } 0523 0524 QString Odf::getPart(const KoXmlNode & part) 0525 { 0526 QString result; 0527 KoXmlElement e = KoXml::namedItemNS(part, KoXmlNS::text, "p"); 0528 while (!e.isNull()) { 0529 QString text = e.text(); 0530 0531 KoXmlElement macro = KoXml::namedItemNS(e, KoXmlNS::text, "time"); 0532 if (!macro.isNull()) 0533 replaceMacro(text, macro.text(), "<time>"); 0534 0535 macro = KoXml::namedItemNS(e, KoXmlNS::text, "date"); 0536 if (!macro.isNull()) 0537 replaceMacro(text, macro.text(), "<date>"); 0538 0539 macro = KoXml::namedItemNS(e, KoXmlNS::text, "page-number"); 0540 if (!macro.isNull()) 0541 replaceMacro(text, macro.text(), "<page>"); 0542 0543 macro = KoXml::namedItemNS(e, KoXmlNS::text, "page-count"); 0544 if (!macro.isNull()) 0545 replaceMacro(text, macro.text(), "<pages>"); 0546 0547 macro = KoXml::namedItemNS(e, KoXmlNS::text, "sheet-name"); 0548 if (!macro.isNull()) 0549 replaceMacro(text, macro.text(), "<sheet>"); 0550 0551 macro = KoXml::namedItemNS(e, KoXmlNS::text, "title"); 0552 if (!macro.isNull()) 0553 replaceMacro(text, macro.text(), "<name>"); 0554 0555 macro = KoXml::namedItemNS(e, KoXmlNS::text, "file-name"); 0556 if (!macro.isNull()) 0557 replaceMacro(text, macro.text(), "<file>"); 0558 0559 //add support for multi line into kspread 0560 if (!result.isEmpty()) 0561 result += '\n'; 0562 result += text; 0563 e = e.nextSibling().toElement(); 0564 } 0565 0566 return result; 0567 } 0568 0569 0570 bool Odf::loadStyleFormat(Sheet *sheet, KoXmlElement *style) 0571 { 0572 QString hleft, hmiddle, hright; 0573 QString fleft, fmiddle, fright; 0574 KoXmlNode header = KoXml::namedItemNS(*style, KoXmlNS::style, "header"); 0575 0576 if (!header.isNull()) { 0577 debugSheetsODF << "Header exists"; 0578 KoXmlNode part = KoXml::namedItemNS(header, KoXmlNS::style, "region-left"); 0579 if (!part.isNull()) { 0580 hleft = getPart(part); 0581 debugSheetsODF << "Header left:" << hleft; 0582 } else 0583 debugSheetsODF << "Style:region:left doesn't exist!"; 0584 part = KoXml::namedItemNS(header, KoXmlNS::style, "region-center"); 0585 if (!part.isNull()) { 0586 hmiddle = getPart(part); 0587 debugSheetsODF << "Header middle:" << hmiddle; 0588 } 0589 part = KoXml::namedItemNS(header, KoXmlNS::style, "region-right"); 0590 if (!part.isNull()) { 0591 hright = getPart(part); 0592 debugSheetsODF << "Header right:" << hright; 0593 } 0594 //If Header doesn't have region tag add it to Left 0595 hleft.append(getPart(header)); 0596 } 0597 //TODO implement it under kspread 0598 KoXmlNode headerleft = KoXml::namedItemNS(*style, KoXmlNS::style, "header-left"); 0599 if (!headerleft.isNull()) { 0600 KoXmlElement e = headerleft.toElement(); 0601 if (e.hasAttributeNS(KoXmlNS::style, "display")) 0602 debugSheetsODF << "header.hasAttribute( style:display ) :" << e.hasAttributeNS(KoXmlNS::style, "display"); 0603 else 0604 debugSheetsODF << "header left doesn't has attribute style:display"; 0605 } 0606 //TODO implement it under kspread 0607 KoXmlNode footerleft = KoXml::namedItemNS(*style, KoXmlNS::style, "footer-left"); 0608 if (!footerleft.isNull()) { 0609 KoXmlElement e = footerleft.toElement(); 0610 if (e.hasAttributeNS(KoXmlNS::style, "display")) 0611 debugSheetsODF << "footer.hasAttribute( style:display ) :" << e.hasAttributeNS(KoXmlNS::style, "display"); 0612 else 0613 debugSheetsODF << "footer left doesn't has attribute style:display"; 0614 } 0615 0616 KoXmlNode footer = KoXml::namedItemNS(*style, KoXmlNS::style, "footer"); 0617 0618 if (!footer.isNull()) { 0619 KoXmlNode part = KoXml::namedItemNS(footer, KoXmlNS::style, "region-left"); 0620 if (!part.isNull()) { 0621 fleft = getPart(part); 0622 debugSheetsODF << "Footer left:" << fleft; 0623 } 0624 part = KoXml::namedItemNS(footer, KoXmlNS::style, "region-center"); 0625 if (!part.isNull()) { 0626 fmiddle = getPart(part); 0627 debugSheetsODF << "Footer middle:" << fmiddle; 0628 } 0629 part = KoXml::namedItemNS(footer, KoXmlNS::style, "region-right"); 0630 if (!part.isNull()) { 0631 fright = getPart(part); 0632 debugSheetsODF << "Footer right:" << fright; 0633 } 0634 //If Footer doesn't have region tag add it to Left 0635 fleft.append(getPart(footer)); 0636 } 0637 0638 sheet->print()->headerFooter()->setHeadFootLine(hleft, hmiddle, hright, 0639 fleft, fmiddle, fright); 0640 return true; 0641 } 0642 0643 void Odf::loadMasterLayoutPage(Sheet *sheet, KoStyleStack &styleStack) 0644 { 0645 KoPageLayout pageLayout; 0646 0647 if (styleStack.hasProperty(KoXmlNS::fo, "page-width")) { 0648 pageLayout.width = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "page-width")); 0649 } 0650 if (styleStack.hasProperty(KoXmlNS::fo, "page-height")) { 0651 pageLayout.height = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "page-height")); 0652 } 0653 if (styleStack.hasProperty(KoXmlNS::fo, "margin-top")) { 0654 pageLayout.topMargin = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "margin-top")); 0655 } 0656 if (styleStack.hasProperty(KoXmlNS::fo, "margin-bottom")) { 0657 pageLayout.bottomMargin = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "margin-bottom")); 0658 } 0659 if (styleStack.hasProperty(KoXmlNS::fo, "margin-left")) { 0660 pageLayout.leftMargin = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "margin-left")); 0661 } 0662 if (styleStack.hasProperty(KoXmlNS::fo, "margin-right")) { 0663 pageLayout.rightMargin = KoUnit::parseValue(styleStack.property(KoXmlNS::fo, "margin-right")); 0664 } 0665 /* set sheet's direction to RTL if sheet name is an RTL string */ 0666 Qt::LayoutDirection ldir = sheet->sheetName().isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; 0667 if (styleStack.hasProperty(KoXmlNS::style, "writing-mode")) { 0668 debugSheetsODF << "styleStack.hasAttribute( style:writing-mode ) :" << styleStack.hasProperty(KoXmlNS::style, "writing-mode"); 0669 const QString writingMode = styleStack.property(KoXmlNS::style, "writing-mode"); 0670 if (writingMode == "lr-tb") { 0671 ldir = Qt::LeftToRight; 0672 } else if (writingMode == "rl-tb") { 0673 ldir = Qt::RightToLeft; 0674 } 0675 //TODO 0676 //<value>lr-tb</value> 0677 //<value>rl-tb</value> 0678 //<value>tb-rl</value> 0679 //<value>tb-lr</value> 0680 //<value>lr</value> 0681 //<value>rl</value> 0682 //<value>tb</value> 0683 //<value>page</value> 0684 } 0685 sheet->setLayoutDirection(ldir); 0686 0687 if (styleStack.hasProperty(KoXmlNS::style, "print-orientation")) { 0688 pageLayout.orientation = (styleStack.property(KoXmlNS::style, "print-orientation") == "landscape") 0689 ? KoPageFormat::Landscape : KoPageFormat::Portrait; 0690 } 0691 if (styleStack.hasProperty(KoXmlNS::style, "num-format")) { 0692 //not implemented into kspread 0693 //These attributes specify the numbering style to use. 0694 //If a numbering style is not specified, the numbering style is inherited from 0695 //the page style. See section 6.7.8 for information on these attributes 0696 debugSheetsODF << " num-format :" << styleStack.property(KoXmlNS::style, "num-format"); 0697 0698 } 0699 if (styleStack.hasProperty(KoXmlNS::fo, "background-color")) { 0700 //TODO 0701 debugSheetsODF << " fo:background-color :" << styleStack.property(KoXmlNS::fo, "background-color"); 0702 } 0703 if (styleStack.hasProperty(KoXmlNS::style, "print")) { 0704 //todo parsing 0705 QString str = styleStack.property(KoXmlNS::style, "print"); 0706 debugSheetsODF << " style:print :" << str; 0707 0708 if (str.contains("headers")) { 0709 //TODO implement it into kspread 0710 } 0711 if (str.contains("grid")) { 0712 sheet->print()->settings()->setPrintGrid(true); 0713 } 0714 if (str.contains("annotations")) { 0715 //TODO it's not implemented 0716 } 0717 if (str.contains("objects")) { 0718 //TODO it's not implemented 0719 } 0720 if (str.contains("charts")) { 0721 //TODO it's not implemented 0722 } 0723 if (str.contains("drawings")) { 0724 //TODO it's not implemented 0725 } 0726 if (str.contains("formulas")) { 0727 sheet->setShowFormula(true); 0728 } 0729 if (str.contains("zero-values")) { 0730 //TODO it's not implemented 0731 } 0732 } 0733 if (styleStack.hasProperty(KoXmlNS::style, "table-centering")) { 0734 QString str = styleStack.property(KoXmlNS::style, "table-centering"); 0735 //TODO not implemented into kspread 0736 debugSheetsODF << " styleStack.attribute( style:table-centering ) :" << str; 0737 #if 0 0738 if (str == "horizontal") { 0739 } else if (str == "vertical") { 0740 } else if (str == "both") { 0741 } else if (str == "none") { 0742 } else 0743 debugSheetsODF << " table-centering unknown :" << str; 0744 #endif 0745 } 0746 sheet->print()->settings()->setPageLayout(pageLayout); 0747 } 0748 0749 bool Odf::loadColumnFormat(Sheet *sheet, const KoXmlElement& column, 0750 const KoOdfStylesReader& stylesReader, int & indexCol, 0751 QHash<QString, QRegion>& columnStyleRegions, IntervalMap<QString>& columnStyles) 0752 { 0753 // debugSheetsODF<<"bool Odf::loadColumnFormat(const KoXmlElement& column, const KoOdfStylesReader& stylesReader, unsigned int & indexCol ) index Col :"<<indexCol; 0754 0755 bool isNonDefaultColumn = false; 0756 0757 int number = 1; 0758 if (column.hasAttributeNS(KoXmlNS::table, "number-columns-repeated")) { 0759 bool ok = true; 0760 int n = column.attributeNS(KoXmlNS::table, "number-columns-repeated", QString()).toInt(&ok); 0761 if (ok) 0762 // Some spreadsheet programs may support more rows than Calligra Sheets so 0763 // limit the number of repeated rows. 0764 // FIXME POSSIBLE DATA LOSS! 0765 number = qMin(n, KS_colMax - indexCol + 1); 0766 //debugSheetsODF << "Repeated:" << number; 0767 } 0768 0769 if (column.hasAttributeNS(KoXmlNS::table, "default-cell-style-name")) { 0770 const QString styleName = column.attributeNS(KoXmlNS::table, "default-cell-style-name", QString()); 0771 if (!styleName.isEmpty()) { 0772 columnStyleRegions[styleName] += QRect(indexCol, 1, number, KS_rowMax); 0773 columnStyles.insert(indexCol, indexCol+number-1, styleName); 0774 } 0775 } 0776 0777 enum { Visible, Collapsed, Filtered } visibility = Visible; 0778 if (column.hasAttributeNS(KoXmlNS::table, "visibility")) { 0779 const QString string = column.attributeNS(KoXmlNS::table, "visibility", "visible"); 0780 if (string == "collapse") 0781 visibility = Collapsed; 0782 else if (string == "filter") 0783 visibility = Filtered; 0784 isNonDefaultColumn = true; 0785 } 0786 0787 KoStyleStack styleStack; 0788 if (column.hasAttributeNS(KoXmlNS::table, "style-name")) { 0789 QString str = column.attributeNS(KoXmlNS::table, "style-name", QString()); 0790 const KoXmlElement *style = stylesReader.findStyle(str, "table-column"); 0791 if (style) { 0792 styleStack.push(*style); 0793 isNonDefaultColumn = true; 0794 } 0795 } 0796 styleStack.setTypeProperties("table-column"); //style for column 0797 0798 double width = -1.0; 0799 if (styleStack.hasProperty(KoXmlNS::style, "column-width")) { 0800 width = KoUnit::parseValue(styleStack.property(KoXmlNS::style, "column-width") , -1.0); 0801 //debugSheetsODF << " style:column-width : width :" << width; 0802 isNonDefaultColumn = true; 0803 } 0804 0805 bool insertPageBreak = false; 0806 if (styleStack.hasProperty(KoXmlNS::fo, "break-before")) { 0807 QString str = styleStack.property(KoXmlNS::fo, "break-before"); 0808 if (str == "page") { 0809 insertPageBreak = true; 0810 } else { 0811 // debugSheetsODF << " str :" << str; 0812 } 0813 isNonDefaultColumn = true; 0814 } else if (styleStack.hasProperty(KoXmlNS::fo, "break-after")) { 0815 // TODO 0816 } 0817 0818 // If it's a default column, we can return here. 0819 // This saves the iteration, which can be caused by column cell default styles, 0820 // but which are not inserted here. 0821 if (!isNonDefaultColumn) { 0822 indexCol += number; 0823 return true; 0824 } 0825 0826 for (int i = 0; i < number; ++i) { 0827 //debugSheetsODF << " insert new column: pos :" << indexCol << " width :" << width << " hidden ?" << visibility; 0828 0829 if (isNonDefaultColumn) { 0830 ColumnFormat* cf = sheet->nonDefaultColumnFormat(indexCol); 0831 0832 if (width != -1.0) //safe 0833 cf->setWidth(width); 0834 if (insertPageBreak) { 0835 cf->setPageBreak(true); 0836 } 0837 if (visibility == Collapsed) 0838 cf->setHidden(true); 0839 else if (visibility == Filtered) 0840 cf->setFiltered(true); 0841 0842 cf->setPageBreak(insertPageBreak); 0843 } 0844 ++indexCol; 0845 } 0846 // debugSheetsODF<<" after index column !!!!!!!!!!!!!!!!!! :"<<indexCol; 0847 return true; 0848 } 0849 0850 int Odf::loadRowFormat(Sheet *sheet, const KoXmlElement& row, int &rowIndex, 0851 OdfLoadingContext& tableContext, 0852 QHash<QString, QRegion>& rowStyleRegions, 0853 QHash<QString, QRegion>& cellStyleRegions, 0854 const IntervalMap<QString>& columnStyles, 0855 const Styles& autoStyles, 0856 QList<ShapeLoadingData>& shapeData) 0857 { 0858 static const QString sStyleName = QString::fromLatin1("style-name"); 0859 static const QString sNumberRowsRepeated = QString::fromLatin1("number-rows-repeated"); 0860 static const QString sDefaultCellStyleName = QString::fromLatin1("default-cell-style-name"); 0861 static const QString sVisibility = QString::fromLatin1("visibility"); 0862 static const QString sVisible = QString::fromLatin1("visible"); 0863 static const QString sCollapse = QString::fromLatin1("collapse"); 0864 static const QString sFilter = QString::fromLatin1("filter"); 0865 static const QString sPage = QString::fromLatin1("page"); 0866 static const QString sTableCell = QString::fromLatin1("table-cell"); 0867 static const QString sCoveredTableCell = QString::fromLatin1("covered-table-cell"); 0868 static const QString sNumberColumnsRepeated = QString::fromLatin1("number-columns-repeated"); 0869 0870 // debugSheetsODF<<"Odf::loadRowFormat( const KoXmlElement& row, int &rowIndex,const KoOdfStylesReader& stylesReader, bool isLast )***********"; 0871 KoOdfLoadingContext& odfContext = tableContext.odfContext; 0872 bool isNonDefaultRow = false; 0873 0874 KoStyleStack styleStack; 0875 if (row.hasAttributeNS(KoXmlNS::table, sStyleName)) { 0876 QString str = row.attributeNS(KoXmlNS::table, sStyleName, QString()); 0877 const KoXmlElement *style = odfContext.stylesReader().findStyle(str, "table-row"); 0878 if (style) { 0879 styleStack.push(*style); 0880 isNonDefaultRow = true; 0881 } 0882 } 0883 styleStack.setTypeProperties("table-row"); 0884 0885 int number = 1; 0886 if (row.hasAttributeNS(KoXmlNS::table, sNumberRowsRepeated)) { 0887 bool ok = true; 0888 int n = row.attributeNS(KoXmlNS::table, sNumberRowsRepeated, QString()).toInt(&ok); 0889 if (ok) 0890 // Some spreadsheet programs may support more rows than Calligra Sheets so 0891 // limit the number of repeated rows. 0892 // FIXME POSSIBLE DATA LOSS! 0893 number = qMin(n, KS_rowMax - rowIndex + 1); 0894 } 0895 0896 QString rowCellStyleName; 0897 if (row.hasAttributeNS(KoXmlNS::table, sDefaultCellStyleName)) { 0898 rowCellStyleName = row.attributeNS(KoXmlNS::table, sDefaultCellStyleName, QString()); 0899 if (!rowCellStyleName.isEmpty()) { 0900 rowStyleRegions[rowCellStyleName] += QRect(1, rowIndex, KS_colMax, number); 0901 } 0902 } 0903 0904 double height = -1.0; 0905 if (styleStack.hasProperty(KoXmlNS::style, "row-height")) { 0906 height = KoUnit::parseValue(styleStack.property(KoXmlNS::style, "row-height") , -1.0); 0907 // debugSheetsODF<<" properties style:row-height : height :"<<height; 0908 isNonDefaultRow = true; 0909 } 0910 0911 enum { Visible, Collapsed, Filtered } visibility = Visible; 0912 if (row.hasAttributeNS(KoXmlNS::table, sVisibility)) { 0913 const QString string = row.attributeNS(KoXmlNS::table, sVisibility, sVisible); 0914 if (string == sCollapse) 0915 visibility = Collapsed; 0916 else if (string == sFilter) 0917 visibility = Filtered; 0918 isNonDefaultRow = true; 0919 } 0920 0921 bool insertPageBreak = false; 0922 if (styleStack.hasProperty(KoXmlNS::fo, "break-before")) { 0923 QString str = styleStack.property(KoXmlNS::fo, "break-before"); 0924 if (str == sPage) { 0925 insertPageBreak = true; 0926 } 0927 // else 0928 // debugSheetsODF<<" str :"<<str; 0929 isNonDefaultRow = true; 0930 } else if (styleStack.hasProperty(KoXmlNS::fo, "break-after")) { 0931 // TODO 0932 } 0933 0934 // debugSheetsODF<<" create non defaultrow format :"<<rowIndex<<" repeate :"<<number<<" height :"<<height; 0935 if (isNonDefaultRow) { 0936 if (height != -1.0) 0937 sheet->rowFormats()->setRowHeight(rowIndex, rowIndex + number - 1, height); 0938 sheet->rowFormats()->setPageBreak(rowIndex, rowIndex + number - 1, insertPageBreak); 0939 if (visibility == Collapsed) 0940 sheet->rowFormats()->setHidden(rowIndex, rowIndex + number - 1, true); 0941 else if (visibility == Filtered) 0942 sheet->rowFormats()->setFiltered(rowIndex, rowIndex + number - 1, true); 0943 } 0944 0945 int columnIndex = 1; 0946 int columnMaximal = 0; 0947 const int endRow = qMin(rowIndex + number - 1, KS_rowMax); 0948 0949 KoXmlElement cellElement; 0950 forEachElement(cellElement, row) { 0951 if (cellElement.namespaceURI() != KoXmlNS::table) 0952 continue; 0953 if (cellElement.localName() != sTableCell && cellElement.localName() != sCoveredTableCell) 0954 continue; 0955 0956 0957 bool ok = false; 0958 const int n = cellElement.attributeNS(KoXmlNS::table, sNumberColumnsRepeated, QString()).toInt(&ok); 0959 // Some spreadsheet programs may support more columns than 0960 // Calligra Sheets so limit the number of repeated columns. 0961 const int numberColumns = ok ? qMin(n, KS_colMax - columnIndex + 1) : 1; 0962 columnMaximal = qMax(numberColumns, columnMaximal); 0963 0964 // Styles are inserted at the end of the loading process, so check the XML directly here. 0965 const QString styleName = cellElement.attributeNS(KoXmlNS::table , sStyleName, QString()); 0966 if (!styleName.isEmpty()) 0967 cellStyleRegions[styleName] += QRect(columnIndex, rowIndex, numberColumns, number); 0968 0969 // figure out exact cell style for loading of cell content 0970 QString cellStyleName = styleName; 0971 if (cellStyleName.isEmpty()) 0972 cellStyleName = rowCellStyleName; 0973 if (cellStyleName.isEmpty()) 0974 cellStyleName = columnStyles.get(columnIndex); 0975 0976 Cell cell(sheet, columnIndex, rowIndex); 0977 loadCell(&cell, cellElement, tableContext, autoStyles, cellStyleName, shapeData); 0978 0979 if (!cell.comment().isEmpty()) 0980 sheet->cellStorage()->setComment(Region(columnIndex, rowIndex, numberColumns, number, sheet), cell.comment()); 0981 if (!cell.conditions().isEmpty()) 0982 sheet->cellStorage()->setConditions(Region(columnIndex, rowIndex, numberColumns, number, sheet), cell.conditions()); 0983 if (!cell.validity().isEmpty()) 0984 sheet->cellStorage()->setValidity(Region(columnIndex, rowIndex, numberColumns, number, sheet), cell.validity()); 0985 0986 if (!cell.hasDefaultContent()) { 0987 // Row-wise filling of PointStorages is faster than column-wise filling. 0988 QSharedPointer<QTextDocument> richText = cell.richText(); 0989 for (int r = rowIndex; r <= endRow; ++r) { 0990 for (int c = 0; c < numberColumns; ++c) { 0991 Cell target(sheet, columnIndex + c, r); 0992 target.setFormula(cell.formula()); 0993 target.setUserInput(cell.userInput()); 0994 target.setRichText(richText); 0995 target.setValue(cell.value()); 0996 if (cell.doesMergeCells()) { 0997 target.mergeCells(columnIndex + c, r, cell.mergedXCells(), cell.mergedYCells()); 0998 } 0999 } 1000 } 1001 } 1002 columnIndex += numberColumns; 1003 } 1004 1005 sheet->cellStorage()->setRowsRepeated(rowIndex, number); 1006 1007 rowIndex += number; 1008 return columnMaximal; 1009 } 1010 1011 1012 // *************** Saving ***************** 1013 1014 bool Odf::saveSheet(Sheet *sheet, OdfSavingContext& tableContext) 1015 { 1016 KoXmlWriter & xmlWriter = tableContext.shapeContext.xmlWriter(); 1017 KoGenStyles & mainStyles = tableContext.shapeContext.mainStyles(); 1018 xmlWriter.startElement("table:table"); 1019 xmlWriter.addAttribute("table:name", sheet->sheetName()); 1020 xmlWriter.addAttribute("table:style-name", saveSheetStyleName(sheet, mainStyles)); 1021 QByteArray pwd; 1022 sheet->password(pwd); 1023 if (!pwd.isNull()) { 1024 xmlWriter.addAttribute("table:protected", "true"); 1025 QByteArray str = KCodecs::base64Encode(pwd); 1026 // FIXME Stefan: see OpenDocument spec, ch. 17.3 Encryption 1027 xmlWriter.addAttribute("table:protection-key", QString(str)); 1028 } 1029 QRect _printRange = sheet->printSettings()->printRegion().lastRange(); 1030 if (!_printRange.isNull() &&_printRange != (QRect(QPoint(1, 1), QPoint(KS_colMax, KS_rowMax)))) { 1031 const Region region(_printRange, sheet); 1032 if (region.isValid()) { 1033 debugSheetsODF << region; 1034 xmlWriter.addAttribute("table:print-ranges", saveRegion(region.name())); 1035 } 1036 } 1037 1038 // flake 1039 // Create a dict of cell anchored shapes with the cell as key. 1040 int sheetAnchoredCount = 0; 1041 foreach(KoShape* shape, sheet->shapes()) { 1042 if (dynamic_cast<ShapeApplicationData*>(shape->applicationData())->isAnchoredToCell()) { 1043 qreal dummy; 1044 const QPointF position = shape->position(); 1045 const int col = sheet->leftColumn(position.x(), dummy); 1046 const int row = sheet->topRow(position.y(), dummy); 1047 tableContext.insertCellAnchoredShape(sheet, row, col, shape); 1048 } else { 1049 sheetAnchoredCount++; 1050 } 1051 } 1052 1053 // flake 1054 // Save the remaining shapes, those that are anchored in the page. 1055 if (sheetAnchoredCount) { 1056 xmlWriter.startElement("table:shapes"); 1057 foreach(KoShape* shape, sheet->shapes()) { 1058 if (dynamic_cast<ShapeApplicationData*>(shape->applicationData())->isAnchoredToCell()) 1059 continue; 1060 shape->saveOdf(tableContext.shapeContext); 1061 } 1062 xmlWriter.endElement(); 1063 } 1064 1065 const QRect usedArea = sheet->usedArea(); 1066 saveColRowCell(sheet, usedArea.width(), usedArea.height(), tableContext); 1067 1068 xmlWriter.endElement(); 1069 return true; 1070 } 1071 1072 QString Odf::saveSheetStyleName(Sheet *sheet, KoGenStyles &mainStyles) 1073 { 1074 KoGenStyle pageStyle(KoGenStyle::TableAutoStyle, "table"/*FIXME I don't know if name is sheet*/); 1075 1076 KoGenStyle pageMaster(KoGenStyle::MasterPageStyle); 1077 const QString pageLayoutName = savePageLayout(sheet->printSettings(), mainStyles, 1078 sheet->getShowFormula(), 1079 !sheet->getHideZero()); 1080 pageMaster.addAttribute("style:page-layout-name", pageLayoutName); 1081 1082 QBuffer buffer; 1083 buffer.open(QIODevice::WriteOnly); 1084 KoXmlWriter elementWriter(&buffer); // TODO pass indentation level 1085 saveHeaderFooter(sheet, elementWriter); 1086 1087 QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size()); 1088 pageMaster.addChildElement("headerfooter", elementContents); 1089 pageStyle.addAttribute("style:master-page-name", mainStyles.insert(pageMaster, "Standard")); 1090 1091 pageStyle.addProperty("table:display", !sheet->isHidden()); 1092 1093 if( !sheet->backgroundImage().isNull() ) { 1094 QBuffer bgBuffer; 1095 bgBuffer.open(QIODevice::WriteOnly); 1096 KoXmlWriter bgWriter(&bgBuffer); //TODO pass indentation level 1097 saveBackgroundImage(sheet, bgWriter); 1098 1099 const QString bgContent = QString::fromUtf8(bgBuffer.buffer(), bgBuffer.size()); 1100 pageMaster.addChildElement("backgroundImage", bgContent); 1101 } 1102 1103 return mainStyles.insert(pageStyle, "ta"); 1104 } 1105 1106 void Odf::saveColRowCell(Sheet *sheet, int maxCols, int maxRows, OdfSavingContext& tableContext) 1107 { 1108 debugSheetsODF << "Odf::saveColRowCell:" << sheet->sheetName(); 1109 1110 KoXmlWriter & xmlWriter = tableContext.shapeContext.xmlWriter(); 1111 KoGenStyles & mainStyles = tableContext.shapeContext.mainStyles(); 1112 1113 // calculate the column/row default cell styles 1114 int maxMaxRows = maxRows; // includes the max row a column default style occupies 1115 // also extends the maximum column/row to include column/row styles 1116 sheet->styleStorage()->saveCreateDefaultStyles(maxCols, maxMaxRows, tableContext.columnDefaultStyles, tableContext.rowDefaultStyles); 1117 if (tableContext.rowDefaultStyles.count() != 0) 1118 maxRows = qMax(maxRows, (--tableContext.rowDefaultStyles.constEnd()).key()); 1119 // Take the actual used area into account so we also catch shapes that are 1120 // anchored after any content. 1121 QRect r = sheet->usedArea(false); 1122 maxRows = qMax(maxRows, r.bottom()); 1123 maxCols = qMax(maxCols, r.right()); 1124 // OpenDocument needs at least one cell per sheet. 1125 maxCols = qMin(KS_colMax, qMax(1, maxCols)); 1126 maxRows = qMin(KS_rowMax, qMax(1, maxRows)); 1127 maxMaxRows = maxMaxRows; 1128 debugSheetsODF << "\t Sheet dimension:" << maxCols << " x" << maxRows; 1129 1130 // saving the columns 1131 // 1132 int i = 1; 1133 while (i <= maxCols) { 1134 const ColumnFormat* column = sheet->columnFormat(i); 1135 // debugSheetsODF << "Odf::saveColRowCell: first col loop:" 1136 // << "i:" << i 1137 // << "column:" << (column ? column->column() : 0) 1138 // << "default:" << (column ? column->isDefault() : false); 1139 1140 //style default layout for column 1141 const Style style = tableContext.columnDefaultStyles.value(i); 1142 1143 int j = i; 1144 int count = 1; 1145 1146 while (j <= maxCols) { 1147 const ColumnFormat* nextColumn = sheet->nextColumn(j); 1148 const int nextColumnIndex = nextColumn ? nextColumn->column() : 0; 1149 const QMap<int, Style>::iterator nextColumnDefaultStyle = tableContext.columnDefaultStyles.upperBound(j); 1150 const int nextStyleColumnIndex = nextColumnDefaultStyle == tableContext.columnDefaultStyles.end() 1151 ? 0 : nextColumnDefaultStyle.key(); 1152 // j becomes the index of the adjacent column 1153 ++j; 1154 1155 // debugSheetsODF <<"Odf::saveColRowCell: second col loop:" 1156 // << "j:" << j 1157 // << "next column:" << (nextColumn ? nextColumn->column() : 0) 1158 // << "next styled column:" << nextStyleColumnIndex; 1159 1160 // no next or not the adjacent column? 1161 if ((!nextColumn && !nextStyleColumnIndex) || 1162 (nextColumnIndex != j && nextStyleColumnIndex != j)) { 1163 // if the origin column was a default column, 1164 if (column->isDefault() && style.isDefault()) { 1165 // we count the default columns 1166 if (!nextColumn && !nextStyleColumnIndex) 1167 count = maxCols - i + 1; 1168 else if (nextColumn && (!nextStyleColumnIndex || nextColumn->column() <= nextStyleColumnIndex)) 1169 count = nextColumn->column() - i; 1170 else 1171 count = nextStyleColumnIndex - i; 1172 } 1173 // otherwise we just stop here to process the adjacent 1174 // column in the next iteration of the outer loop 1175 break; 1176 } 1177 1178 // stop, if the next column differs from the current one 1179 if ((nextColumn && (*column != *nextColumn)) || (!nextColumn && !column->isDefault())) 1180 break; 1181 if (style != tableContext.columnDefaultStyles.value(j)) 1182 break; 1183 ++count; 1184 } 1185 1186 xmlWriter.startElement("table:table-column"); 1187 if (!column->isDefault()) { 1188 KoGenStyle currentColumnStyle(KoGenStyle::TableColumnAutoStyle, "table-column"); 1189 currentColumnStyle.addPropertyPt("style:column-width", column->width()); 1190 if (column->hasPageBreak()) { 1191 currentColumnStyle.addProperty("fo:break-before", "page"); 1192 } 1193 xmlWriter.addAttribute("table:style-name", mainStyles.insert(currentColumnStyle, "co")); 1194 } 1195 if (!column->isDefault() || !style.isDefault()) { 1196 if (!style.isDefault()) { 1197 KoGenStyle currentDefaultCellStyle; // the type is determined in saveOdfStyle 1198 const QString name = saveStyle(&style, currentDefaultCellStyle, mainStyles, 1199 sheet->map()->styleManager()); 1200 xmlWriter.addAttribute("table:default-cell-style-name", name); 1201 } 1202 1203 if (column->isHidden()) 1204 xmlWriter.addAttribute("table:visibility", "collapse"); 1205 else if (column->isFiltered()) 1206 xmlWriter.addAttribute("table:visibility", "filter"); 1207 } 1208 if (count > 1) 1209 xmlWriter.addAttribute("table:number-columns-repeated", count); 1210 xmlWriter.endElement(); 1211 1212 debugSheetsODF << "Odf::saveColRowCell: column" << i 1213 << "repeated" << count - 1 << "time(s)"; 1214 1215 i += count; 1216 } 1217 1218 // saving the rows and the cells 1219 // we have to loop through all rows of the used area 1220 for (i = 1; i <= maxRows; ++i) { 1221 // default cell style for row 1222 const Style style = tableContext.rowDefaultStyles.value(i); 1223 1224 xmlWriter.startElement("table:table-row"); 1225 1226 const bool rowIsDefault = sheet->rowFormats()->isDefaultRow(i); 1227 if (!rowIsDefault) { 1228 KoGenStyle currentRowStyle(KoGenStyle::TableRowAutoStyle, "table-row"); 1229 currentRowStyle.addPropertyPt("style:row-height", sheet->rowFormats()->rowHeight(i)); 1230 if (sheet->rowFormats()->hasPageBreak(i)) { 1231 currentRowStyle.addProperty("fo:break-before", "page"); 1232 } 1233 xmlWriter.addAttribute("table:style-name", mainStyles.insert(currentRowStyle, "ro")); 1234 } 1235 1236 // We cannot use cellStorage()->rowRepeat(i) here cause the RowRepeatStorage only knows 1237 // about the content but not about the shapes anchored to a cell. So, we need to check 1238 // for them here to be sure to catch them even when the content in the cell is repeated. 1239 int repeated = 1; 1240 // empty row? 1241 if (!sheet->cellStorage()->firstInRow(i) && !tableContext.rowHasCellAnchoredShapes(sheet, i)) { // row is empty 1242 // debugSheetsODF <<"Odf::saveColRowCell: first row loop:" 1243 // << " i: " << i 1244 // << " row: " << row->row(); 1245 int j = i + 1; 1246 1247 // search for 1248 // next non-empty row 1249 // or 1250 // next row with different Format 1251 while (j <= maxRows && !sheet->cellStorage()->firstInRow(j) && !tableContext.rowHasCellAnchoredShapes(sheet, j)) { 1252 // debugSheetsODF <<"Odf::saveColRowCell: second row loop:" 1253 // << " j: " << j 1254 // << " row: " << nextRow->row(); 1255 1256 // if the reference row has the default row format 1257 if (rowIsDefault && style.isDefault()) { 1258 // if the next is not default, stop here 1259 if (!sheet->rowFormats()->isDefaultRow(j) || !tableContext.rowDefaultStyles.value(j).isDefault()) 1260 break; 1261 // otherwise, jump to the next 1262 ++j; 1263 continue; 1264 } 1265 1266 // stop, if the next row differs from the current one 1267 if (!sheet->rowFormats()->rowsAreEqual(i, j)) 1268 break; 1269 if (style != tableContext.rowDefaultStyles.value(j)) 1270 break; 1271 // otherwise, process the next 1272 ++j; 1273 } 1274 repeated = j - i; 1275 1276 if (repeated > 1) 1277 xmlWriter.addAttribute("table:number-rows-repeated", repeated); 1278 if (!style.isDefault()) { 1279 KoGenStyle currentDefaultCellStyle; // the type is determined in saveCellStyle 1280 const QString name = saveStyle(&style, currentDefaultCellStyle, mainStyles, 1281 sheet->map()->styleManager()); 1282 xmlWriter.addAttribute("table:default-cell-style-name", name); 1283 } 1284 if (sheet->rowFormats()->isHidden(i)) // never true for the default row 1285 xmlWriter.addAttribute("table:visibility", "collapse"); 1286 else if (sheet->rowFormats()->isFiltered(i)) // never true for the default row 1287 xmlWriter.addAttribute("table:visibility", "filter"); 1288 1289 // NOTE Stefan: Even if paragraph 8.1 states, that rows may be empty, the 1290 // RelaxNG schema does not allow that. 1291 xmlWriter.startElement("table:table-cell"); 1292 // Fill the row with empty cells, if there's a row default cell style. 1293 if (!style.isDefault()) 1294 xmlWriter.addAttribute("table:number-columns-repeated", QString::number(maxCols)); 1295 // Fill the row with empty cells up to the last column with a default cell style. 1296 else if (!tableContext.columnDefaultStyles.isEmpty()) { 1297 const int col = (--tableContext.columnDefaultStyles.constEnd()).key(); 1298 xmlWriter.addAttribute("table:number-columns-repeated", QString::number(col)); 1299 } 1300 xmlWriter.endElement(); 1301 1302 debugSheetsODF << "Odf::saveColRowCell: empty row" << i 1303 << "repeated" << repeated << "time(s)"; 1304 1305 // copy the index for the next row to process 1306 i = j - 1; /*it's already incremented in the for loop*/ 1307 } else { // row is not empty 1308 if (!style.isDefault()) { 1309 KoGenStyle currentDefaultCellStyle; // the type is determined in saveCellStyle 1310 const QString name = saveStyle(&style, currentDefaultCellStyle, mainStyles, 1311 sheet->map()->styleManager()); 1312 xmlWriter.addAttribute("table:default-cell-style-name", name); 1313 } 1314 if (sheet->rowFormats()->isHidden(i)) // never true for the default row 1315 xmlWriter.addAttribute("table:visibility", "collapse"); 1316 else if (sheet->rowFormats()->isFiltered(i)) // never true for the default row 1317 xmlWriter.addAttribute("table:visibility", "filter"); 1318 1319 int j = i + 1; 1320 while (j <= maxRows && compareRows(sheet, i, j, maxCols, tableContext)) { 1321 j++; 1322 repeated++; 1323 } 1324 repeated = j - i; 1325 if (repeated > 1) { 1326 debugSheetsODF << "Odf::saveColRowCell: NON-empty row" << i 1327 << "repeated" << repeated << "times"; 1328 1329 xmlWriter.addAttribute("table:number-rows-repeated", repeated); 1330 } 1331 1332 saveCells(sheet, i, maxCols, tableContext); 1333 1334 // copy the index for the next row to process 1335 i = j - 1; /*it's already incremented in the for loop*/ 1336 } 1337 xmlWriter.endElement(); 1338 } 1339 1340 // Fill in rows with empty cells, if there's a column default cell style. 1341 if (!tableContext.columnDefaultStyles.isEmpty()) { 1342 if (maxMaxRows > maxRows) { 1343 xmlWriter.startElement("table:table-row"); 1344 if (maxMaxRows > maxRows + 1) 1345 xmlWriter.addAttribute("table:number-rows-repeated", maxMaxRows - maxRows); 1346 xmlWriter.startElement("table:table-cell"); 1347 const int col = qMin(maxCols, (--tableContext.columnDefaultStyles.constEnd()).key()); 1348 xmlWriter.addAttribute("table:number-columns-repeated", QString::number(col)); 1349 xmlWriter.endElement(); 1350 xmlWriter.endElement(); 1351 } 1352 } 1353 } 1354 1355 void Odf::saveCells(Sheet *sheet, int row, int maxCols, OdfSavingContext& tableContext) 1356 { 1357 KoXmlWriter & xmlWriter = tableContext.shapeContext.xmlWriter(); 1358 1359 int i = 1; 1360 Cell cell(sheet, i, row); 1361 Cell nextCell = sheet->cellStorage()->nextInRow(i, row); 1362 // handle situations where the row contains shapes and nothing else 1363 if (cell.isDefault() && nextCell.isNull()) { 1364 int nextShape = tableContext.nextAnchoredShape(sheet, row, i); 1365 if (nextShape) 1366 nextCell = Cell(sheet, nextShape, row); 1367 } 1368 // while 1369 // the current cell is not a default one 1370 // or 1371 // we have a further cell in this row 1372 do { 1373 // debugSheetsODF <<"Odf::saveCells:" 1374 // << " i: " << i 1375 // << " column: " << cell.column() << endl; 1376 1377 int repeated = 1; 1378 int column = i; 1379 saveCell(&cell, repeated, tableContext); 1380 i += repeated; 1381 // stop if we reached the end column 1382 if (i > maxCols || nextCell.isNull()) 1383 break; 1384 1385 cell = Cell(sheet, i, row); 1386 // if we have a shape anchored to an empty cell, ensure that the cell gets also processed 1387 int nextShape = tableContext.nextAnchoredShape(sheet, row, column); 1388 if (nextShape && ((nextShape < i) || cell.isDefault())) { 1389 cell = Cell(sheet, nextShape, row); 1390 i = nextShape; 1391 } 1392 1393 nextCell = sheet->cellStorage()->nextInRow(i, row); 1394 } while (!cell.isDefault() || tableContext.cellHasAnchoredShapes(sheet, cell.row(), cell.column()) || !nextCell.isNull()); 1395 1396 // Fill the row with empty cells, if there's a row default cell style. 1397 if (tableContext.rowDefaultStyles.contains(row)) { 1398 if (maxCols >= i) { 1399 xmlWriter.startElement("table:table-cell"); 1400 if (maxCols > i) 1401 xmlWriter.addAttribute("table:number-columns-repeated", QString::number(maxCols - i + 1)); 1402 xmlWriter.endElement(); 1403 } 1404 } 1405 // Fill the row with empty cells up to the last column with a default cell style. 1406 else if (!tableContext.columnDefaultStyles.isEmpty()) { 1407 const int col = (--tableContext.columnDefaultStyles.constEnd()).key(); 1408 if (col >= i) { 1409 xmlWriter.startElement("table:table-cell"); 1410 if (col > i) 1411 xmlWriter.addAttribute("table:number-columns-repeated", QString::number(col - i + 1)); 1412 xmlWriter.endElement(); 1413 } 1414 } 1415 } 1416 1417 void Odf::saveBackgroundImage(Sheet *sheet, KoXmlWriter& xmlWriter) 1418 { 1419 const Sheet::BackgroundImageProperties& properties = sheet->backgroundImageProperties(); 1420 xmlWriter.startElement("style:backgroundImage"); 1421 1422 //xmlWriter.addAttribute("xlink:href", fileName); 1423 xmlWriter.addAttribute("xlink:type", "simple"); 1424 xmlWriter.addAttribute("xlink:show", "embed"); 1425 xmlWriter.addAttribute("xlink:actuate", "onLoad"); 1426 1427 QString opacity = QString("%1%").arg(properties.opacity); 1428 xmlWriter.addAttribute("draw:opacity", opacity); 1429 1430 QString position; 1431 if(properties.horizontalPosition == Sheet::BackgroundImageProperties::Left) { 1432 position += "left"; 1433 } 1434 else if(properties.horizontalPosition == Sheet::BackgroundImageProperties::HorizontalCenter) { 1435 position += "center"; 1436 } 1437 else if(properties.horizontalPosition == Sheet::BackgroundImageProperties::Right) { 1438 position += "right"; 1439 } 1440 1441 position += ' '; 1442 1443 if(properties.verticalPosition == Sheet::BackgroundImageProperties::Top) { 1444 position += "top"; 1445 } 1446 else if(properties.verticalPosition == Sheet::BackgroundImageProperties::VerticalCenter) { 1447 position += "center"; 1448 } 1449 else if(properties.verticalPosition == Sheet::BackgroundImageProperties::Bottom) { 1450 position += "right"; 1451 } 1452 xmlWriter.addAttribute("style:position", position); 1453 1454 QString repeat; 1455 if(properties.repeat == Sheet::BackgroundImageProperties::NoRepeat) { 1456 repeat = "no-repeat"; 1457 } 1458 else if(properties.repeat == Sheet::BackgroundImageProperties::Repeat) { 1459 repeat = "repeat"; 1460 } 1461 else if(properties.repeat == Sheet::BackgroundImageProperties::Stretch) { 1462 repeat = "stretch"; 1463 } 1464 xmlWriter.addAttribute("style:repeat", repeat); 1465 1466 xmlWriter.endElement(); 1467 } 1468 1469 void Odf::addText(const QString & text, KoXmlWriter & writer) 1470 { 1471 if (!text.isEmpty()) 1472 writer.addTextNode(text); 1473 } 1474 1475 void Odf::convertPart(Sheet *sheet, const QString & part, KoXmlWriter & xmlWriter) 1476 { 1477 QString text; 1478 QString var; 1479 1480 bool inVar = false; 1481 uint i = 0; 1482 uint l = part.length(); 1483 while (i < l) { 1484 if (inVar || part[i] == '<') { 1485 inVar = true; 1486 var += part[i]; 1487 if (part[i] == '>') { 1488 inVar = false; 1489 if (var == "<page>") { 1490 addText(text, xmlWriter); 1491 xmlWriter.startElement("text:page-number"); 1492 xmlWriter.addTextNode("1"); 1493 xmlWriter.endElement(); 1494 } else if (var == "<pages>") { 1495 addText(text, xmlWriter); 1496 xmlWriter.startElement("text:page-count"); 1497 xmlWriter.addTextNode("99"); //TODO I think that it can be different from 99 1498 xmlWriter.endElement(); 1499 } else if (var == "<date>") { 1500 addText(text, xmlWriter); 1501 //text:p><text:date style:data-style-name="N2" text:date-value="2005-10-02">02/10/2005</text:date>, <text:time>10:20:12</text:time></text:p> "add style" => create new style 1502 #if 0 //FIXME 1503 KoXmlElement t = dd.createElement("text:date"); 1504 t.setAttribute("text:date-value", "0-00-00"); 1505 // todo: "style:data-style-name", "N2" 1506 t.appendChild(dd.createTextNode(QDate::currentDate().toString())); 1507 parent.appendChild(t); 1508 #endif 1509 } else if (var == "<time>") { 1510 addText(text, xmlWriter); 1511 1512 xmlWriter.startElement("text:time"); 1513 xmlWriter.addTextNode(QTime::currentTime().toString()); 1514 xmlWriter.endElement(); 1515 } else if (var == "<file>") { // filepath + name 1516 addText(text, xmlWriter); 1517 xmlWriter.startElement("text:file-name"); 1518 xmlWriter.addAttribute("text:display", "full"); 1519 xmlWriter.addTextNode("???"); 1520 xmlWriter.endElement(); 1521 } else if (var == "<name>") { // filename 1522 addText(text, xmlWriter); 1523 1524 xmlWriter.startElement("text:title"); 1525 xmlWriter.addTextNode("???"); 1526 xmlWriter.endElement(); 1527 } else if (var == "<author>") { 1528 DocBase* sdoc = sheet->doc(); 1529 KoDocumentInfo* docInfo = sdoc->documentInfo(); 1530 1531 text += docInfo->authorInfo("creator"); 1532 addText(text, xmlWriter); 1533 } else if (var == "<email>") { 1534 DocBase* sdoc = sheet->doc(); 1535 KoDocumentInfo* docInfo = sdoc->documentInfo(); 1536 1537 text += docInfo->authorInfo("email"); 1538 addText(text, xmlWriter); 1539 1540 } else if (var == "<org>") { 1541 DocBase* sdoc = sheet->doc(); 1542 KoDocumentInfo* docInfo = sdoc->documentInfo(); 1543 1544 text += docInfo->authorInfo("company"); 1545 addText(text, xmlWriter); 1546 1547 } else if (var == "<sheet>") { 1548 addText(text, xmlWriter); 1549 1550 xmlWriter.startElement("text:sheet-name"); 1551 xmlWriter.addTextNode("???"); 1552 xmlWriter.endElement(); 1553 } else { 1554 // no known variable: 1555 text += var; 1556 addText(text, xmlWriter); 1557 } 1558 1559 text.clear(); 1560 var.clear(); 1561 } 1562 } else { 1563 text += part[i]; 1564 } 1565 ++i; 1566 } 1567 if (!text.isEmpty() || !var.isEmpty()) { 1568 //we don't have var at the end =>store it 1569 addText(text + var, xmlWriter); 1570 } 1571 debugSheetsODF << " text end :" << text << " var :" << var; 1572 } 1573 1574 void Odf::saveHeaderFooter(Sheet *sheet, KoXmlWriter &xmlWriter) 1575 { 1576 HeaderFooter *hf = sheet->print()->headerFooter(); 1577 QString headerLeft = hf->headLeft(); 1578 QString headerCenter = hf->headMid(); 1579 QString headerRight = hf->headRight(); 1580 1581 QString footerLeft = hf->footLeft(); 1582 QString footerCenter = hf->footMid(); 1583 QString footerRight =hf->footRight(); 1584 1585 xmlWriter.startElement("style:header"); 1586 if ((!headerLeft.isEmpty()) 1587 || (!headerCenter.isEmpty()) 1588 || (!headerRight.isEmpty())) { 1589 xmlWriter.startElement("style:region-left"); 1590 xmlWriter.startElement("text:p"); 1591 convertPart(sheet, headerLeft, xmlWriter); 1592 xmlWriter.endElement(); 1593 xmlWriter.endElement(); 1594 1595 xmlWriter.startElement("style:region-center"); 1596 xmlWriter.startElement("text:p"); 1597 convertPart(sheet, headerCenter, xmlWriter); 1598 xmlWriter.endElement(); 1599 xmlWriter.endElement(); 1600 1601 xmlWriter.startElement("style:region-right"); 1602 xmlWriter.startElement("text:p"); 1603 convertPart(sheet, headerRight, xmlWriter); 1604 xmlWriter.endElement(); 1605 xmlWriter.endElement(); 1606 } else { 1607 xmlWriter.startElement("text:p"); 1608 1609 xmlWriter.startElement("text:sheet-name"); 1610 xmlWriter.addTextNode("???"); 1611 xmlWriter.endElement(); 1612 1613 xmlWriter.endElement(); 1614 } 1615 xmlWriter.endElement(); 1616 1617 1618 xmlWriter.startElement("style:footer"); 1619 if ((!footerLeft.isEmpty()) 1620 || (!footerCenter.isEmpty()) 1621 || (!footerRight.isEmpty())) { 1622 xmlWriter.startElement("style:region-left"); 1623 xmlWriter.startElement("text:p"); 1624 convertPart(sheet, footerLeft, xmlWriter); 1625 xmlWriter.endElement(); 1626 xmlWriter.endElement(); //style:region-left 1627 1628 xmlWriter.startElement("style:region-center"); 1629 xmlWriter.startElement("text:p"); 1630 convertPart(sheet, footerCenter, xmlWriter); 1631 xmlWriter.endElement(); 1632 xmlWriter.endElement(); 1633 1634 xmlWriter.startElement("style:region-right"); 1635 xmlWriter.startElement("text:p"); 1636 convertPart(sheet, footerRight, xmlWriter); 1637 xmlWriter.endElement(); 1638 xmlWriter.endElement(); 1639 } else { 1640 xmlWriter.startElement("text:p"); 1641 1642 xmlWriter.startElement("text:sheet-name"); 1643 xmlWriter.addTextNode("Page "); // ??? 1644 xmlWriter.endElement(); 1645 1646 xmlWriter.startElement("text:page-number"); 1647 xmlWriter.addTextNode("1"); // ??? 1648 xmlWriter.endElement(); 1649 1650 xmlWriter.endElement(); 1651 } 1652 xmlWriter.endElement(); 1653 } 1654 1655 inline int compareCellInRow(const Cell &cell1, const Cell &cell2, int maxCols) 1656 { 1657 if (cell1.isNull() != cell2.isNull()) 1658 return 0; 1659 if (cell1.isNull()) 1660 return 2; 1661 if (maxCols >= 0 && cell1.column() > maxCols) 1662 return 3; 1663 if (cell1.column() != cell2.column()) 1664 return 0; 1665 if (!cell1.compareData(cell2)) 1666 return 0; 1667 return 1; 1668 } 1669 1670 inline bool compareCellsInRows(CellStorage *cellStorage, int row1, int row2, int maxCols) 1671 { 1672 Cell cell1 = cellStorage->firstInRow(row1); 1673 Cell cell2 = cellStorage->firstInRow(row2); 1674 while (true) { 1675 int r = compareCellInRow(cell1, cell2, maxCols); 1676 if (r == 0) 1677 return false; 1678 if (r != 1) 1679 break; 1680 cell1 = cellStorage->nextInRow(cell1.column(), cell1.row()); 1681 cell2 = cellStorage->nextInRow(cell2.column(), cell2.row()); 1682 } 1683 return true; 1684 } 1685 1686 bool Odf::compareRows(Sheet *sheet, int row1, int row2, int maxCols, OdfSavingContext& tableContext) 1687 { 1688 #if 0 1689 if (!sheet->rowFormats()->rowsAreEqual(row1, row2)) { 1690 return false; 1691 } 1692 if (tableContext.rowHasCellAnchoredShapes(sheet, row1) != tableContext.rowHasCellAnchoredShapes(sheet, row2)) { 1693 return false; 1694 } 1695 return compareCellsInRows(sheet->cellStorage(), row1, row2, maxCols); 1696 #else 1697 Q_UNUSED(maxCols); 1698 1699 // Optimized comparison by using the RowRepeatStorage to compare the content 1700 // rather then an expensive loop like compareCellsInRows. 1701 int row1repeated = sheet->cellStorage()->rowRepeat(row1); 1702 Q_ASSERT( row2 > row1 ); 1703 if (row2 - row1 >= row1repeated) { 1704 return false; 1705 } 1706 1707 // The RowRepeatStorage does not take to-cell anchored shapes into account 1708 // so we need to check for them explicit. 1709 if (tableContext.rowHasCellAnchoredShapes(sheet, row1) != tableContext.rowHasCellAnchoredShapes(sheet, row2)) { 1710 return false; 1711 } 1712 1713 // Some sanity-checks to be sure our RowRepeatStorage works as expected. 1714 Q_ASSERT_X( sheet->rowFormats()->rowsAreEqual(row1, row2), __FUNCTION__, QString("Bug in RowRepeatStorage").toLocal8Bit() ); 1715 //Q_ASSERT_X( compareCellsInRows(sheet->cellStorage(), row1, row2, maxCols), __FUNCTION__, QString("Bug in RowRepeatStorage").toLocal8Bit() ); 1716 Q_ASSERT_X( compareCellInRow(sheet->cellStorage()->lastInRow(row1), sheet->cellStorage()->lastInRow(row2), -1), __FUNCTION__, QString("Bug in RowRepeatStorage").toLocal8Bit() ); 1717 1718 // If we reached that point then the both rows are equal. 1719 return true; 1720 #endif 1721 } 1722 1723 1724 1725 1726 1727 1728 // *************** Settings ***************** 1729 1730 void Odf::loadSheetSettings(Sheet *sheet, const KoOasisSettings::NamedMap &settings) 1731 { 1732 // Find the entry in the map that applies to this sheet (by name) 1733 KoOasisSettings::Items items = settings.entry(sheet->sheetName()); 1734 if (items.isNull()) 1735 return; 1736 sheet->setHideZero(!items.parseConfigItemBool("ShowZeroValues")); 1737 sheet->setShowGrid(items.parseConfigItemBool("ShowGrid")); 1738 sheet->setFirstLetterUpper(items.parseConfigItemBool("FirstLetterUpper")); 1739 1740 int cursorX = qMin(KS_colMax, qMax(1, items.parseConfigItemInt("CursorPositionX") + 1)); 1741 int cursorY = qMin(KS_rowMax, qMax(1, items.parseConfigItemInt("CursorPositionY") + 1)); 1742 sheet->map()->loadingInfo()->setCursorPosition(sheet, QPoint(cursorX, cursorY)); 1743 1744 double offsetX = items.parseConfigItemDouble("xOffset"); 1745 double offsetY = items.parseConfigItemDouble("yOffset"); 1746 sheet->map()->loadingInfo()->setScrollingOffset(sheet, QPointF(offsetX, offsetY)); 1747 1748 sheet->setShowFormulaIndicator(items.parseConfigItemBool("ShowFormulaIndicator")); 1749 sheet->setShowCommentIndicator(items.parseConfigItemBool("ShowCommentIndicator")); 1750 sheet->setShowPageOutline(items.parseConfigItemBool("ShowPageOutline")); 1751 sheet->setLcMode(items.parseConfigItemBool("lcmode")); 1752 sheet->setAutoCalculationEnabled(items.parseConfigItemBool("autoCalc")); 1753 sheet->setShowColumnNumber(items.parseConfigItemBool("ShowColumnNumber")); 1754 } 1755 1756 void Odf::saveSheetSettings(Sheet *sheet, KoXmlWriter &settingsWriter) 1757 { 1758 //not into each page into oo spec 1759 settingsWriter.addConfigItem("ShowZeroValues", !sheet->getHideZero()); 1760 settingsWriter.addConfigItem("ShowGrid", sheet->getShowGrid()); 1761 //not define into oo spec 1762 settingsWriter.addConfigItem("FirstLetterUpper", sheet->getFirstLetterUpper()); 1763 settingsWriter.addConfigItem("ShowFormulaIndicator", sheet->getShowFormulaIndicator()); 1764 settingsWriter.addConfigItem("ShowCommentIndicator", sheet->getShowCommentIndicator()); 1765 settingsWriter.addConfigItem("ShowPageOutline", sheet->isShowPageOutline()); 1766 settingsWriter.addConfigItem("lcmode", sheet->getLcMode()); 1767 settingsWriter.addConfigItem("autoCalc", sheet->isAutoCalculationEnabled()); 1768 settingsWriter.addConfigItem("ShowColumnNumber", sheet->getShowColumnNumber()); 1769 } 1770 1771 QString Odf::savePageLayout(PrintSettings *settings, KoGenStyles &mainStyles, bool formulas, bool zeros) 1772 { 1773 // Create a page layout style. 1774 // 15.2.1 Page Size 1775 // 15.2.4 Print Orientation 1776 // 15.2.5 Margins 1777 KoGenStyle pageLayout = settings->pageLayout().saveOdf(); 1778 1779 // 15.2.13 Print 1780 QString printParameter; 1781 if (settings->printHeaders()) { 1782 printParameter = "headers "; 1783 } 1784 if (settings->printGrid()) { 1785 printParameter += "grid "; 1786 } 1787 /* if (settings->printComments()) { 1788 printParameter += "annotations "; 1789 }*/ 1790 if (settings->printObjects()) { 1791 printParameter += "objects "; 1792 } 1793 if (settings->printCharts()) { 1794 printParameter += "charts "; 1795 } 1796 /* if (settings->printDrawings()) { 1797 printParameter += "drawings "; 1798 }*/ 1799 if (formulas) { 1800 printParameter += "formulas "; 1801 } 1802 if (zeros) { 1803 printParameter += "zero-values "; 1804 } 1805 if (!printParameter.isEmpty()) { 1806 printParameter += "drawings"; //default print style attributes in OO 1807 pageLayout.addProperty("style:print", printParameter); 1808 } 1809 1810 // 15.2.14 Print Page Order 1811 const QString pageOrder = (settings->pageOrder() == PrintSettings::LeftToRight) ? "ltr" : "ttb"; 1812 pageLayout.addProperty("style:print-page-order", pageOrder); 1813 1814 // 15.2.16 Scale 1815 // FIXME handle cases where only one direction is limited 1816 if (settings->pageLimits().width() > 0 && settings->pageLimits().height() > 0) { 1817 const int pages = settings->pageLimits().width() * settings->pageLimits().height(); 1818 pageLayout.addProperty("style:scale-to-pages", pages); 1819 } else if (settings->zoom() != 1.0) { 1820 pageLayout.addProperty("style:scale-to", qRound(settings->zoom() * 100)); // in % 1821 } 1822 1823 // 15.2.17 Table Centering 1824 if (settings->centerHorizontally() && settings->centerVertically()) { 1825 pageLayout.addProperty("style:table-centering", "both"); 1826 } else if (settings->centerHorizontally()) { 1827 pageLayout.addProperty("style:table-centering", "horizontal"); 1828 } else if (settings->centerVertically()) { 1829 pageLayout.addProperty("style:table-centering", "vertical"); 1830 } else { 1831 pageLayout.addProperty("style:table-centering", "none"); 1832 } 1833 1834 // this is called from Sheet::saveOdfSheetStyleName for writing the SytleMaster so 1835 // the style has to be in the styles.xml file and only there 1836 pageLayout.setAutoStyleInStylesDotXml(true); 1837 1838 return mainStyles.insert(pageLayout, "pm"); 1839 } 1840 1841 1842 1843 1844 1845 } // Sheets 1846 } // Calligra 1847