File indexing completed on 2025-01-12 13:05:55
0001 /* This file is part of the KDE project 0002 Copyright (C) 2002 Norbert Andres <nandres@web.de> 0003 Copyright (C) 2004 - 2005 Laurent Montel <montel@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "opencalcimport.h" 0022 0023 #include <float.h> 0024 #include <math.h> 0025 0026 #include <QColor> 0027 #include <QFile> 0028 #include <QFont> 0029 #include <QPen> 0030 #include <QList> 0031 #include <QByteArray> 0032 0033 #include <kdebug.h> 0034 #include <KoDocumentInfo.h> 0035 #include <kpluginfactory.h> 0036 #include <kmessagebox.h> 0037 #include <kcodecs.h> 0038 #include <KoFilterChain.h> 0039 #include <KoUnit.h> 0040 #include <KoStyleStack.h> 0041 #include <ooutils.h> 0042 0043 #include <sheets/Cell.h> 0044 #include <sheets/CellStorage.h> 0045 #include <sheets/Condition.h> 0046 #include <sheets/part/Doc.h> 0047 #include <sheets/Global.h> 0048 #include <sheets/HeaderFooter.h> 0049 #include <sheets/Map.h> 0050 #include <sheets/NamedAreaManager.h> 0051 #include <sheets/PrintSettings.h> 0052 #include <sheets/Region.h> 0053 #include <sheets/RowColumnFormat.h> 0054 #include <sheets/RowFormatStorage.h> 0055 #include <sheets/Sheet.h> 0056 #include <sheets/Style.h> 0057 #include <sheets/StyleManager.h> 0058 #include <sheets/Validity.h> 0059 #include <sheets/Value.h> 0060 #include <sheets/ValueParser.h> 0061 0062 #define SECSPERDAY (24 * 60 * 60) 0063 0064 using namespace Calligra::Sheets; 0065 0066 K_PLUGIN_FACTORY_WITH_JSON(OpenCalcImportFactory, "calligra_filter_opencalc2sheets.json", 0067 registerPlugin<OpenCalcImport>();) 0068 0069 OpenCalcImport::OpenCalcPoint::OpenCalcPoint(QString const & str) 0070 : isRange(false) 0071 { 0072 bool inQuote = false; 0073 0074 int l = str.length(); 0075 int colonPos = -1; 0076 QString range; 0077 0078 // replace '.' with '!' 0079 for (int i = 0; i < l; ++i) { 0080 if (str[i] == '$') 0081 continue; 0082 if (str[i] == '\'') { 0083 inQuote = !inQuote; 0084 } else if (str[i] == '.') { 0085 if (!inQuote) { 0086 if (i != 0 && i != (colonPos + 1)) // no empty table names 0087 range += '!'; 0088 } else 0089 range += '.'; 0090 } else if (str[i] == ':') { 0091 if (!inQuote) { 0092 isRange = true; 0093 colonPos = i; 0094 } 0095 range += ':'; 0096 } else 0097 range += str[i]; 0098 } 0099 0100 translation = range; 0101 0102 const Calligra::Sheets::Region region(range); 0103 table = region.firstSheet()->sheetName(); 0104 topLeft = region.firstRange().topLeft(); 0105 botRight = region.firstRange().bottomRight(); 0106 } 0107 0108 0109 OpenCalcImport::OpenCalcImport(QObject* parent, const QVariantList &) 0110 : KoFilter(parent) 0111 { 0112 } 0113 0114 OpenCalcImport::~OpenCalcImport() 0115 { 0116 foreach(KoXmlElement* style, m_styles) delete style; 0117 foreach(Calligra::Sheets::Style* style, m_defaultStyles) delete style; 0118 foreach(QString* format, m_formats) delete format; 0119 } 0120 0121 double timeToNum(int h, int m, int s) 0122 { 0123 int secs = h * 3600 + m * 60 + s; 0124 return (double) secs / (double) SECSPERDAY; 0125 } 0126 0127 bool OpenCalcImport::readRowFormat(KoXmlElement & rowNode, KoXmlElement * rowStyle, 0128 Sheet * table, int & row, int & number, 0129 bool isLast) 0130 { 0131 if (rowNode.isNull()) 0132 return false; 0133 0134 KoXmlNode node; 0135 if (rowStyle) { 0136 node = rowStyle->firstChild(); 0137 kDebug(30518) << "RowStyle:" << rowStyle << "," << rowStyle->tagName(); 0138 } 0139 0140 double height = -1.0; 0141 bool insertPageBreak = false; 0142 Style layout; 0143 0144 while (!node.isNull()) { 0145 KoXmlElement property = node.toElement(); 0146 0147 kDebug(30518) << "Row: Child exists:" << property.tagName(); 0148 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0149 if (property.hasAttributeNS(ooNS::style, "row-height")) { 0150 height = KoUnit::parseValue(property.attributeNS(ooNS::style, "row-height", QString()) , -1); 0151 } 0152 0153 if (property.hasAttributeNS(ooNS::fo, "break-before")) { 0154 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") { 0155 insertPageBreak = true; 0156 } 0157 } 0158 0159 loadStyleProperties(&layout, property); 0160 } 0161 0162 node = node.nextSibling(); 0163 } 0164 0165 if (rowNode.hasAttributeNS(ooNS::table, "number-rows-repeated")) { 0166 bool ok = true; 0167 int n = rowNode.attributeNS(ooNS::table, "number-rows-repeated", QString()).toInt(&ok); 0168 if (ok) 0169 number = n; 0170 kDebug(30518) << "Row repeated:" << number; 0171 } 0172 0173 if (isLast) { 0174 if (number > 30) 0175 number = 30; 0176 } else { 0177 if (number > 256) 0178 number = 256; 0179 } 0180 0181 if (height != -1) 0182 table->rowFormats()->setRowHeight(row, row+number-1, height); 0183 for (int i = 0; i < number; ++i) { 0184 table->cellStorage()->setStyle(Calligra::Sheets::Region(QRect(1, row, KS_colMax, 1)), layout); 0185 0186 0187 Q_UNUSED(insertPageBreak); //for now, as long as below code is commented. 0188 // if ( insertPageBreak ) TODO: 0189 // rowL->setPageBreak( true ) 0190 0191 // kDebug(30518) <<"Added RowFormat:" << row; 0192 ++row; 0193 } 0194 0195 return true; 0196 } 0197 0198 QString OpenCalcImport::translatePar(QString & par) const 0199 { 0200 OpenCalcPoint point(par); 0201 kDebug(30518) << " Parameter:" << par << ", Translation:" << point.translation; 0202 0203 return point.translation; 0204 } 0205 0206 void OpenCalcImport::checkForNamedAreas(QString & formula) const 0207 { 0208 int l = formula.length(); 0209 int i = 0; 0210 QString word; 0211 int start = 0; 0212 while (i < l) { 0213 if (formula[i].isLetterOrNumber()) { 0214 word += formula[i]; 0215 ++i; 0216 continue; 0217 } 0218 if (word.length() > 0) { 0219 if (m_namedAreas.contains(word)) { 0220 formula.replace(start, word.length(), '\'' + word + '\''); 0221 l = formula.length(); 0222 ++i; 0223 kDebug(30518) << "Formula:" << formula << ", L:" << l << ", i:" << i + 1; 0224 } 0225 } 0226 0227 ++i; 0228 word = ""; 0229 start = i; 0230 } 0231 if (word.length() > 0) { 0232 if (m_namedAreas.contains(word)) { 0233 formula.replace(start, word.length(), '\'' + word + '\''); 0234 l = formula.length(); 0235 ++i; 0236 kDebug(30518) << "Formula:" << formula << ", L:" << l << ", i:" << i + 1; 0237 } 0238 } 0239 } 0240 0241 void OpenCalcImport::convertFormula(QString & text, QString const & f) const 0242 { 0243 // TODO Stefan: Check if Oasis::decodeFormula could be used instead 0244 kDebug(30518) << "Parsing formula:" << f; 0245 0246 QString formula; 0247 QString parameter; 0248 0249 int l = f.length(); 0250 int p = 0; 0251 0252 while (p < l) { 0253 if (f[p] == '(' || f[p] == '[') { 0254 break; 0255 } 0256 0257 formula += f[p]; 0258 ++p; 0259 } 0260 0261 if (parameter.isEmpty()) { 0262 checkForNamedAreas(formula); 0263 } 0264 0265 kDebug(30518) << "Formula:" << formula << ", Parameter:" << parameter << ", P:" << p; 0266 0267 0268 // replace formula names here 0269 if (formula == "=MULTIPLE.OPERATIONS") 0270 formula = "=MULTIPLEOPERATIONS"; 0271 0272 QString par; 0273 bool isPar = false; 0274 bool inQuote = false; 0275 0276 while (p < l) { 0277 if (f[p] == '"') { 0278 inQuote = !inQuote; 0279 parameter += '"'; 0280 } else if (f[p] == '[') { 0281 if (!inQuote) 0282 isPar = true; 0283 else 0284 parameter += '['; 0285 } else if (f[p] == ']') { 0286 if (inQuote) { 0287 parameter += ']'; 0288 continue; 0289 } 0290 0291 isPar = false; 0292 parameter += translatePar(par); 0293 par.clear(); 0294 } else if (isPar) { 0295 par += f[p]; 0296 } else if (f[p] == '=') { // TODO: check if StarCalc has a '==' sometimes 0297 if (inQuote) 0298 parameter += '='; 0299 else 0300 parameter += "=="; 0301 } else if (f[p] == ')') { 0302 if (!inQuote) 0303 parameter += ')'; 0304 } else 0305 parameter += f[p]; 0306 0307 ++p; 0308 if (p == l) 0309 checkForNamedAreas(parameter); 0310 } 0311 0312 text = formula + parameter; 0313 kDebug(30518) << "New formula:" << text; 0314 } 0315 0316 bool OpenCalcImport::readCells(KoXmlElement & rowNode, Sheet * table, int row, int & columns) 0317 { 0318 ValueParser *const parser = table->map()->parser(); 0319 0320 bool ok = true; 0321 int spanC = 1; 0322 int spanR = 1; 0323 //Cell* defCell = table->defaultCell(); 0324 0325 KoXmlNode cellNode = KoXml::namedItemNS(rowNode, ooNS::table, "table-cell"); 0326 0327 while (!cellNode.isNull()) { 0328 spanR = 1; spanC = 1; 0329 0330 KoXmlElement e = cellNode.toElement(); 0331 if (e.isNull()) { 0332 ++columns; 0333 0334 cellNode = cellNode.nextSibling(); 0335 continue; 0336 } 0337 0338 Cell cell; 0339 0340 kDebug(30518) << " Cell:" << columns << "," << row; 0341 0342 // ="3" table:number-rows-spanned="1" 0343 if (e.hasAttributeNS(ooNS::table, "number-columns-spanned")) { 0344 int span = e.attributeNS(ooNS::table, "number-columns-spanned", QString()).toInt(&ok); 0345 if (ok) 0346 spanC = span; 0347 } 0348 if (e.hasAttributeNS(ooNS::table, "number-rows-spanned")) { 0349 int span = e.attributeNS(ooNS::table, "number-rows-spanned", QString()).toInt(&ok); 0350 if (ok) 0351 spanR = span; 0352 } 0353 0354 QString text; 0355 KoXmlElement textP = KoXml::namedItemNS(e, ooNS::text, "p"); 0356 if (!textP.isNull()) { 0357 KoXmlElement subText = textP.firstChild().toElement(); // ## wrong 0358 if (!subText.isNull()) { 0359 // something in <text:p>, e.g. links 0360 text = subText.text(); 0361 0362 if (subText.hasAttributeNS(ooNS::xlink, "href")) { 0363 QString link = subText.attributeNS(ooNS::xlink, "href", QString()); 0364 if (link[0] == '#') 0365 link = link.remove(0, 1); 0366 if (!cell) 0367 cell = Cell(table, columns, row); 0368 cell.setLink(link); 0369 } 0370 } else 0371 text = textP.text(); // our text, could contain formatting for value or result of formula 0372 } 0373 KoXmlElement annotation = KoXml::namedItemNS(e, ooNS::office, "annotation"); 0374 if (!annotation.isNull()) { 0375 QString comment; 0376 KoXmlNode node = annotation.firstChild(); 0377 while (!node.isNull()) { 0378 KoXmlElement commentElement = node.toElement(); 0379 if (!commentElement.isNull()) 0380 if (commentElement.localName() == "p" && e.namespaceURI() == ooNS::text) { 0381 if (!comment.isEmpty()) comment.append('\n'); 0382 comment.append(commentElement.text()); 0383 } 0384 0385 node = node.nextSibling(); 0386 } 0387 0388 if (!comment.isEmpty()) { 0389 kDebug(30518) << " columns :" << columns << " row :" << row; 0390 Cell(table, columns, row).setComment(comment); 0391 } 0392 } 0393 0394 kDebug(30518) << "Contains:" << text; 0395 bool isFormula = false; 0396 0397 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0398 if (!cell) 0399 cell = Cell(table, columns, row); 0400 Style style = cell.style(); 0401 0402 QString psName("Default"); 0403 if (e.hasAttributeNS(ooNS::style, "parent-style-name")) 0404 psName = e.attributeNS(ooNS::style, "parent-style-name", QString()); 0405 0406 kDebug(30518) << "Default style:" << psName; 0407 Style * layout = m_defaultStyles[psName]; 0408 0409 if (layout) 0410 style = *layout; 0411 0412 KoXmlElement * st = 0; 0413 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0414 kDebug(30518) << "Style:" << e.attributeNS(ooNS::table, "style-name", QString()); 0415 st = m_styles[ e.attributeNS(ooNS::table, "style-name", QString())]; 0416 } 0417 if (st) { 0418 kDebug(30518) << "Style: adapting"; 0419 KoXmlNode node = st->firstChild(); 0420 bool foundValidation = false; 0421 while (!node.isNull()) { 0422 KoXmlElement property = node.toElement(); 0423 if (!property.isNull()) { 0424 kDebug(30518) << "property.tagName() :" << property.tagName(); 0425 if (property.localName() == "map" && property.namespaceURI() == ooNS::style && !foundValidation) { 0426 loadCondition(cell, property); 0427 foundValidation = true; 0428 } 0429 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0430 loadStyleProperties(&style, property); 0431 if (style.angle() != 0) { 0432 QFontMetrics fm(style.font()); 0433 int tmpAngle = style.angle(); 0434 int textHeight = static_cast<int>(cos(tmpAngle * M_PI / 180) 0435 * (fm.ascent() + fm.descent()) 0436 + abs((int)(fm.width(cell.displayText()) 0437 * sin(tmpAngle * M_PI / 180)))); 0438 /* 0439 int textWidth = static_cast<int>( abs ( ( int ) ( sin( tmpAngle * M_PI / 180 ) 0440 * ( fm.ascent() + fm.descent() ) ) ) 0441 + fm.width( cell.displayText() ) 0442 * cos( tmpAngle * M_PI / 180 ) ); 0443 */ 0444 kDebug(30518) << "Rotation: height:" << textHeight; 0445 0446 if (table->rowFormats()->rowHeight(row) < textHeight) 0447 table->rowFormats()->setRowHeight(row, row, textHeight + 2); 0448 } 0449 } 0450 } 0451 node = node.nextSibling(); 0452 } 0453 } 0454 } else { 0455 QString psName("Default"); 0456 kDebug(30518) << "Default style:" << psName; 0457 Style * layout = m_defaultStyles[psName]; 0458 0459 if (layout) 0460 table->cellStorage()->setStyle(Calligra::Sheets::Region(QPoint(columns, row)), *layout); 0461 } 0462 if (e.hasAttributeNS(ooNS::table, "formula")) { 0463 isFormula = true; 0464 QString formula; 0465 convertFormula(formula, e.attributeNS(ooNS::table, "formula", QString())); 0466 0467 if (!cell) 0468 cell = Cell(table, columns, row); 0469 cell.setUserInput(formula); 0470 } 0471 if (e.hasAttributeNS(ooNS::table, "validation-name")) { 0472 kDebug(30518) << " Celle has a validation :" << e.attributeNS(ooNS::table, "validation-name", QString()); 0473 loadOasisValidation(Cell(table, columns, row).validity(), e.attributeNS(ooNS::table, "validation-name", QString()), parser); 0474 } 0475 if (e.hasAttributeNS(ooNS::table, "value-type")) { 0476 if (!cell) 0477 cell = Cell(table, columns, row); 0478 Style style; 0479 0480 cell.setUserInput(text); 0481 0482 QString value = e.attributeNS(ooNS::table, "value", QString()); 0483 QString type = e.attributeNS(ooNS::table, "value-type", QString()); 0484 0485 kDebug(30518) << "Value:" << value << ", type:" << type; 0486 0487 bool ok = false; 0488 double dv = 0.0; 0489 0490 if ((type == "float") || (type == "currency")) { 0491 dv = value.toDouble(&ok); 0492 if (ok) { 0493 if (!isFormula) 0494 cell.setValue(Value(dv)); 0495 0496 if (type == "currency") { 0497 Currency currency(e.attributeNS(ooNS::table, "currency", QString())); 0498 style.setCurrency(currency); 0499 style.setFormatType(Format::Money); 0500 } 0501 } 0502 } else if (type == "percentage") { 0503 dv = value.toDouble(&ok); 0504 if (ok) { 0505 if (!isFormula) 0506 cell.setValue(Value(dv)); 0507 //TODO fixme 0508 //cell.setFactor( 100 ); 0509 // TODO: replace with custom... 0510 style.setFormatType(Format::Percentage); 0511 } 0512 } else if (type == "boolean") { 0513 if (value.isEmpty()) 0514 value = e.attributeNS(ooNS::table, "boolean-value", QString()); 0515 0516 kDebug(30518) << "Type: boolean"; 0517 if (value == "true") 0518 cell.setValue(Value(true)); 0519 else 0520 cell.setValue(Value(false)); 0521 ok = true; 0522 style.setFormatType(Format::Custom); 0523 } else if (type == "date") { 0524 if (value.isEmpty()) 0525 value = e.attributeNS(ooNS::table, "date-value", QString()); 0526 kDebug(30518) << "Type: date, value:" << value; 0527 0528 // "1980-10-15" 0529 int year = 0, month = 0, day = 0; 0530 ok = false; 0531 0532 int p1 = value.indexOf('-'); 0533 if (p1 > 0) 0534 year = value.left(p1).toInt(&ok); 0535 0536 kDebug(30518) << "year:" << value.left(p1); 0537 0538 int p2 = value.indexOf('-', ++p1); 0539 0540 if (ok) 0541 month = value.mid(p1, p2 - p1).toInt(&ok); 0542 0543 kDebug(30518) << "month:" << value.mid(p1, p2 - p1); 0544 0545 if (ok) 0546 day = value.right(value.length() - p2 - 1).toInt(&ok); 0547 0548 kDebug(30518) << "day:" << value.right(value.length() - p2); 0549 0550 if (ok) { 0551 QDateTime dt(QDate(year, month, day)); 0552 // KSpreadValue kval( dt ); 0553 // cell.setValue( kval ); 0554 cell.setValue(Value(QDate(year, month, day), cell.sheet()->map()->calculationSettings())); 0555 kDebug(30518) << "Set QDate:" << year << " -" << month << " -" << day; 0556 } 0557 } else if (type == "time") { 0558 if (value.isEmpty()) 0559 value = e.attributeNS(ooNS::table, "time-value", QString()); 0560 0561 kDebug(30518) << "Type: time:" << value; 0562 // "PT15H10M12S" 0563 int hours = 0, minutes = 0, seconds = 0; 0564 int l = value.length(); 0565 QString num; 0566 0567 for (int i = 0; i < l; ++i) { 0568 if (value[i].isNumber()) { 0569 num += value[i]; 0570 continue; 0571 } else if (value[i] == 'H') 0572 hours = num.toInt(&ok); 0573 else if (value[i] == 'M') 0574 minutes = num.toInt(&ok); 0575 else if (value[i] == 'S') 0576 seconds = num.toInt(&ok); 0577 else 0578 continue; 0579 0580 kDebug(30518) << "Num:" << num; 0581 0582 num.clear(); 0583 if (!ok) 0584 break; 0585 } 0586 0587 kDebug(30518) << "Hours:" << hours << "," << minutes << "," << seconds; 0588 0589 if (ok) { 0590 // KSpreadValue kval( timeToNum( hours, minutes, seconds ) ); 0591 // cell.setValue( kval ); 0592 cell.setValue(Value(QTime(hours % 24, minutes, seconds))); 0593 style.setFormatType(Format::Custom); 0594 } 0595 } 0596 0597 cell.setStyle(style); 0598 if (!ok) // just in case we couldn't set the value directly 0599 cell.parseUserInput(text); 0600 } else if (!text.isEmpty()) { 0601 if (!cell) 0602 cell = Cell(table, columns, row); 0603 cell.parseUserInput(text); 0604 } 0605 0606 if (spanR > 1 || spanC > 1) { 0607 if (!cell) 0608 cell = Cell(table, columns, row); 0609 cell.mergeCells(columns, row, spanC - 1, spanR - 1); 0610 } 0611 0612 cellNode = cellNode.nextSibling(); 0613 0614 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) { 0615 // copy cell from left 0616 bool ok = false; 0617 int number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok); 0618 Cell cellDest; 0619 0620 // don't repeat more than 10 if it is the last cell and empty 0621 if (!ok || cellNode.isNull()) { 0622 if (number > 10) 0623 number = 10; 0624 } 0625 0626 for (int i = 1; i < number; ++i) { 0627 ++columns; 0628 0629 if (!cell.isNull()) { 0630 cellDest = Cell(table, columns, row); 0631 cellDest.copyAll(cell); 0632 } 0633 } 0634 } 0635 0636 ++columns; 0637 } 0638 0639 return true; 0640 } 0641 0642 0643 void OpenCalcImport::loadCondition(const Cell& cell, const KoXmlElement &property) 0644 { 0645 kDebug(30518) << "void OpenCalcImport::loadCondition( Cell*cell,const KoXmlElement &property )*******"; 0646 loadOasisCondition(cell, property); 0647 } 0648 0649 void OpenCalcImport::loadOasisCondition(const Cell& cell, const KoXmlElement &property) 0650 { 0651 KoXmlElement elementItem(property); 0652 Map *const map = cell.sheet()->map(); 0653 ValueParser *const parser = map->parser(); 0654 0655 QLinkedList<Conditional> cond; 0656 while (!elementItem.isNull()) { 0657 kDebug(30518) << "elementItem.tagName() :" << elementItem.tagName(); 0658 0659 if (elementItem.localName() == "map" && property.namespaceURI() == ooNS::style) { 0660 bool ok = true; 0661 kDebug(30518) << "elementItem.attribute(style:condition ) :" << elementItem.attributeNS(ooNS::style, "condition", QString()); 0662 Conditional newCondition; 0663 loadOasisConditionValue(elementItem.attributeNS(ooNS::style, "condition", QString()), newCondition, parser); 0664 if (elementItem.hasAttributeNS(ooNS::style, "apply-style-name")) { 0665 kDebug(30518) << "elementItem.attribute( style:apply-style-name ) :" << elementItem.attributeNS(ooNS::style, "apply-style-name", QString()); 0666 newCondition.styleName = elementItem.attributeNS(ooNS::style, "apply-style-name", QString()); 0667 ok = !newCondition.styleName.isEmpty(); 0668 } 0669 0670 if (ok) 0671 cond.append(newCondition); 0672 else 0673 kDebug(30518) << "Error loading condition" << elementItem.nodeName(); 0674 } 0675 elementItem = elementItem.nextSibling().toElement(); 0676 } 0677 if (!cond.isEmpty()) { 0678 Conditions conditions; 0679 conditions.setConditionList(cond); 0680 Cell(cell).setConditions(conditions); 0681 } 0682 } 0683 0684 void OpenCalcImport::loadOasisConditionValue(const QString &styleCondition, Conditional &newCondition, 0685 const ValueParser *parser) 0686 { 0687 QString val(styleCondition); 0688 if (val.contains("cell-content()")) { 0689 val = val.remove("cell-content()"); 0690 loadOasisCondition(val, newCondition, parser); 0691 } 0692 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 0693 //for the moment we support just int/double value, not text/date/time :( 0694 if (val.contains("cell-content-is-between(")) { 0695 val.remove("cell-content-is-between("); 0696 val.remove(')'); 0697 QStringList listVal = val.split(','); 0698 kDebug(30518) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 0699 newCondition.value1 = parser->parse(listVal[0]); 0700 newCondition.value2 = parser->parse(listVal[1]); 0701 newCondition.cond = Conditional::Between; 0702 } 0703 if (val.contains("cell-content-is-not-between(")) { 0704 val.remove("cell-content-is-not-between("); 0705 val.remove(')'); 0706 QStringList listVal = val.split(','); 0707 kDebug(30518) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 0708 newCondition.value1 = parser->parse(listVal[0]); 0709 newCondition.value2 = parser->parse(listVal[1]); 0710 newCondition.cond = Conditional::Different; 0711 } 0712 0713 } 0714 0715 0716 void OpenCalcImport::loadOasisCondition(QString &valExpression, Conditional &newCondition, 0717 const ValueParser *parser) 0718 { 0719 QString value; 0720 if (valExpression.indexOf("<=") == 0) { 0721 value = valExpression.remove(0, 2); 0722 newCondition.cond = Conditional::InferiorEqual; 0723 } else if (valExpression.indexOf(">=") == 0) { 0724 value = valExpression.remove(0, 2); 0725 newCondition.cond = Conditional::SuperiorEqual; 0726 } else if (valExpression.indexOf("!=") == 0) { 0727 //add Differentto attribute 0728 value = valExpression.remove(0, 2); 0729 newCondition.cond = Conditional::DifferentTo; 0730 } else if (valExpression.indexOf('<') == 0) { 0731 value = valExpression.remove(0, 1); 0732 newCondition.cond = Conditional::Inferior; 0733 } else if (valExpression.indexOf('>') == 0) { 0734 value = valExpression.remove(0, 1); 0735 newCondition.cond = Conditional::Superior; 0736 } else if (valExpression.indexOf('=') == 0) { 0737 value = valExpression.remove(0, 1); 0738 newCondition.cond = Conditional::Equal; 0739 } else 0740 kDebug(30518) << " I don't know how to parse it :" << valExpression; 0741 kDebug(30518) << " value :" << value; 0742 newCondition.value1 = parser->parse(value); 0743 } 0744 0745 bool OpenCalcImport::readRowsAndCells(KoXmlElement & content, Sheet * table) 0746 { 0747 kDebug(30518) << "Reading in rows"; 0748 0749 int i = 1; 0750 int row = 1; 0751 int columns = 1; 0752 int backupRow = 1; 0753 KoXmlElement * rowStyle = 0; 0754 //Cell cell; 0755 //Cell cellDest; 0756 //Cell defCell = table->defaultCell(); 0757 KoXmlNode rowNode = KoXml::namedItemNS(content, ooNS::table, "table-row"); 0758 0759 while (!rowNode.isNull()) { 0760 bool collapsed = false; 0761 0762 int number = 1; 0763 KoXmlElement r = rowNode.toElement(); 0764 0765 if (r.isNull()) 0766 return false; 0767 0768 if (r.hasAttributeNS(ooNS::table, "style-name")) { 0769 QString style = r.attributeNS(ooNS::table, "style-name", QString()); 0770 rowStyle = m_styles[ style ]; 0771 kDebug(30518) << "Row style:" << style; 0772 } 0773 0774 collapsed = (r.attributeNS(ooNS::table, "visibility", QString()) == "collapse"); 0775 0776 backupRow = row; 0777 0778 rowNode = rowNode.nextSibling(); 0779 0780 if (!readRowFormat(r, rowStyle, table, row, number, rowNode.isNull())) // updates "row" 0781 return false; 0782 0783 if (!readCells(r, table, backupRow, columns)) 0784 return false; 0785 0786 // RowFormat * srcLayout = table->nonDefaultRowFormat(backupRow); 0787 // RowFormat * layout = 0; 0788 0789 if (collapsed) 0790 table->rowFormats()->setHidden(backupRow, backupRow, true); 0791 0792 for (i = 1; i < number; ++i) { 0793 // FIXME CALLIGRA_SHEETS_NEW_STYLE_STORAGE 0794 // layout = table->nonDefaultRowFormat( backupRow + i ); 0795 // 0796 // table->setStyle( Calligra::Sheets::Region(QRect(1,backupRow + i,KS_colMax,1)), srcLayout ); 0797 0798 /* 0799 * TODO: Test: do we need to copy the cells, too? 0800 * if so we will probably also need to copy them for repeated col layouts. 0801 for ( j = 1; j <= columns; ++j ) 0802 { 0803 Cell cell( table, j, backupRow ); 0804 0805 kDebug(30518) <<"Cell:" << cell <<"DefCell:" << defCell; 0806 if ( cell && (cell != defCell) ) 0807 { 0808 cellDest = Cell( table, j, backupRow + i ); 0809 cellDest->copyAll( cell ); 0810 } 0811 } 0812 */ 0813 } 0814 0815 rowStyle = 0; 0816 columns = 1; 0817 } 0818 0819 kDebug(30518) << "Reading in rows done"; 0820 0821 return true; 0822 } 0823 0824 bool OpenCalcImport::readColLayouts(KoXmlElement & content, Sheet * table) 0825 { 0826 kDebug(30518) << "Reading in columns..."; 0827 0828 KoXmlNode colLayout = KoXml::namedItemNS(content, ooNS::table, "table-column"); 0829 int column = 1; 0830 0831 while (!colLayout.isNull()) { 0832 if (colLayout.nodeName() != "table:table-column") 0833 return true; // all cols read in. 0834 0835 KoXmlElement e = colLayout.toElement(); 0836 0837 if (e.isNull()) 0838 return false; // error, that's it... 0839 0840 kDebug(30518) << "New column:" << column; 0841 0842 int number = 1; 0843 double width = -1.0; 0844 bool collapsed = (e.attributeNS(ooNS::table, "visibility", QString()) == "collapse"); 0845 bool insertPageBreak = false; 0846 Style styleLayout; 0847 0848 kDebug(30518) << "Check table:number-columns-repeated"; 0849 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) { 0850 bool ok = true; 0851 number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok); 0852 if (!ok) 0853 number = 1; 0854 0855 kDebug(30518) << "Repeated:" << number; 0856 } 0857 0858 kDebug(30518) << "Checking table:default-cell-style-name"; 0859 if (e.hasAttributeNS(ooNS::table, "default-cell-style-name")) { 0860 QString n(e.attributeNS(ooNS::table, "default-cell-style-name", QString())); 0861 kDebug(30518) << "Has attribute default-cell-style-name:" << n; 0862 Style * defaultStyle = m_defaultStyles[ n ]; 0863 if (!defaultStyle) { 0864 QString name = e.attributeNS(ooNS::table, "default-cell-style-name", QString()); 0865 KoXmlElement * st = m_styles[ name ]; 0866 0867 kDebug(30518) << "Default cell style:" << name; 0868 0869 if (st && !st->isNull()) { 0870 Style * layout = new Style(); 0871 0872 readInStyle(layout, *st); 0873 0874 m_defaultStyles.insert(name, layout); 0875 kDebug(30518) << "Insert default cell style:" << name; 0876 0877 defaultStyle = layout; 0878 } 0879 } 0880 0881 if (defaultStyle) { 0882 // kDebug(30518) <<"Copying default style, Font:" << defaultStyle->font().toString(); 0883 styleLayout = *defaultStyle; 0884 } 0885 } 0886 0887 KoXmlElement * colStyle = 0; 0888 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0889 QString style = e.attributeNS(ooNS::table, "style-name", QString()); 0890 colStyle = m_styles[ style ]; 0891 0892 kDebug(30518) << "Col Style:" << style; 0893 } 0894 0895 KoXmlNode node; 0896 0897 if (colStyle) 0898 node = colStyle->firstChild(); 0899 0900 while (!node.isNull()) { 0901 KoXmlElement property = node.toElement(); 0902 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0903 if (property.hasAttributeNS(ooNS::style, "column-width")) { 0904 QString sWidth = property.attributeNS(ooNS::style, "column-width", QString()); 0905 width = KoUnit::parseValue(property.attributeNS(ooNS::style, "column-width", QString()), width); 0906 kDebug(30518) << "Col Width:" << sWidth; 0907 } 0908 0909 if (property.hasAttributeNS(ooNS::fo, "break-before")) { 0910 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") { 0911 insertPageBreak = true; 0912 } 0913 } 0914 0915 loadStyleProperties(&styleLayout, property); 0916 } 0917 0918 node = node.nextSibling(); 0919 } 0920 0921 colLayout = colLayout.nextSibling(); 0922 0923 if (colLayout.isNull() && (number > 30)) 0924 number = 30; 0925 0926 for (int i = 0; i < number; ++i) { 0927 kDebug(30518) << "Inserting colLayout:" << column; 0928 0929 ColumnFormat * col = new ColumnFormat(); 0930 col->setSheet(table); 0931 col->setColumn(column); 0932 table->cellStorage()->setStyle(Calligra::Sheets::Region(QRect(column, 1, 1, KS_rowMax)), styleLayout); 0933 if (width != -1.0) 0934 col->setWidth(width); 0935 0936 Q_UNUSED(insertPageBreak); //TODO 0937 // if ( insertPageBreak ) 0938 // col->setPageBreak( true ) 0939 0940 if (collapsed) 0941 col->setHidden(true); 0942 0943 table->insertColumnFormat(col); 0944 ++column; 0945 } 0946 } 0947 0948 return true; 0949 } 0950 0951 void replaceMacro(QString & text, QString const & old, QString const & newS) 0952 { 0953 int n = text.indexOf(old); 0954 if (n != -1) 0955 text.replace(n, old.length(), newS); 0956 } 0957 0958 QString getPart(KoXmlNode const & part) 0959 { 0960 QString result; 0961 KoXmlElement e = KoXml::namedItemNS(part, ooNS::text, "p"); 0962 while (!e.isNull()) { 0963 QString text = e.text(); 0964 kDebug(30518) << "PART:" << text; 0965 0966 KoXmlElement macro = KoXml::namedItemNS(e, ooNS::text, "time"); 0967 if (!macro.isNull()) 0968 replaceMacro(text, macro.text(), "<time>"); 0969 0970 macro = KoXml::namedItemNS(e, ooNS::text, "date"); 0971 if (!macro.isNull()) 0972 replaceMacro(text, macro.text(), "<date>"); 0973 0974 macro = KoXml::namedItemNS(e, ooNS::text, "page-number"); 0975 if (!macro.isNull()) 0976 replaceMacro(text, macro.text(), "<page>"); 0977 0978 macro = KoXml::namedItemNS(e, ooNS::text, "page-count"); 0979 if (!macro.isNull()) 0980 replaceMacro(text, macro.text(), "<pages>"); 0981 0982 macro = KoXml::namedItemNS(e, ooNS::text, "sheet-name"); 0983 if (!macro.isNull()) 0984 replaceMacro(text, macro.text(), "<sheet>"); 0985 0986 macro = KoXml::namedItemNS(e, ooNS::text, "title"); 0987 if (!macro.isNull()) 0988 replaceMacro(text, macro.text(), "<name>"); 0989 0990 macro = KoXml::namedItemNS(e, ooNS::text, "file-name"); 0991 if (!macro.isNull()) 0992 replaceMacro(text, macro.text(), "<file>"); 0993 0994 if (!result.isEmpty()) 0995 result += '\n'; 0996 result += text; 0997 e = e.nextSibling().toElement(); 0998 } 0999 1000 return result; 1001 } 1002 1003 void OpenCalcImport::loadTableMasterStyle(Sheet * table, 1004 QString const & stylename) 1005 { 1006 kDebug(30518) << "Loading table master style:" << stylename; 1007 1008 KoXmlElement * style = m_styles[ stylename ]; 1009 1010 if (!style) { 1011 kDebug(30518) << "Master style not found!"; 1012 return; 1013 } 1014 1015 KoXmlNode header = KoXml::namedItemNS(*style, ooNS::style, "header"); 1016 kDebug(30518) << "Style header"; 1017 1018 QString hleft, hmiddle, hright; 1019 QString fleft, fmiddle, fright; 1020 1021 if (!header.isNull()) { 1022 kDebug(30518) << "Header exists"; 1023 KoXmlNode part = KoXml::namedItemNS(header, ooNS::style, "region-left"); 1024 if (!part.isNull()) { 1025 hleft = getPart(part); 1026 kDebug(30518) << "Header left:" << hleft; 1027 } else 1028 kDebug(30518) << "Style:region:left doesn't exist!"; 1029 part = KoXml::namedItemNS(header, ooNS::style, "region-center"); 1030 if (!part.isNull()) { 1031 hmiddle = getPart(part); 1032 kDebug(30518) << "Header middle:" << hmiddle; 1033 } 1034 part = KoXml::namedItemNS(header, ooNS::style, "region-right"); 1035 if (!part.isNull()) { 1036 hright = getPart(part); 1037 kDebug(30518) << "Header right:" << hright; 1038 } 1039 } 1040 1041 KoXmlNode footer = KoXml::namedItemNS(*style, ooNS::style, "footer"); 1042 1043 if (!footer.isNull()) { 1044 KoXmlNode part = KoXml::namedItemNS(footer, ooNS::style, "region-left"); 1045 if (!part.isNull()) { 1046 fleft = getPart(part); 1047 kDebug(30518) << "Footer left:" << fleft; 1048 } 1049 part = KoXml::namedItemNS(footer, ooNS::style, "region-center"); 1050 if (!part.isNull()) { 1051 fmiddle = getPart(part); 1052 kDebug(30518) << "Footer middle:" << fmiddle; 1053 } 1054 part = KoXml::namedItemNS(footer, ooNS::style, "region-right"); 1055 if (!part.isNull()) { 1056 fright = getPart(part); 1057 kDebug(30518) << "Footer right:" << fright; 1058 } 1059 } 1060 1061 table->headerFooter()->setHeadFootLine(hleft, hmiddle, hright, 1062 fleft, fmiddle, fright); 1063 if (style->hasAttributeNS(ooNS::style, "page-master-name")) { 1064 QString masterPageLayoutStyleName = style->attributeNS(ooNS::style, "page-master-name", QString()); 1065 kDebug(30518) << "masterPageLayoutStyleName :" << masterPageLayoutStyleName; 1066 KoXmlElement *masterLayoutStyle = m_styles[masterPageLayoutStyleName]; 1067 kDebug(30518) << "masterLayoutStyle :" << masterLayoutStyle; 1068 if (!masterLayoutStyle) 1069 return; 1070 KoStyleStack styleStack(ooNS::style, ooNS::fo); 1071 styleStack.push(*masterLayoutStyle); 1072 loadOasisMasterLayoutPage(table, styleStack); 1073 } 1074 } 1075 1076 void OpenCalcImport::loadOasisMasterLayoutPage(Sheet * table, KoStyleStack &styleStack) 1077 { 1078 float leftMargin = 0.0; 1079 float rightMargin = 0.0; 1080 float topMargin = 0.0; 1081 float bottomMargin = 0.0; 1082 float width = 0.0; 1083 float height = 0.0; 1084 QString orientation = "Portrait"; 1085 QString format; 1086 1087 if (styleStack.hasProperty(ooNS::fo, "page-width")) { 1088 width = KoUnit::parseValue(styleStack.property(ooNS::fo, "page-width")); 1089 } 1090 if (styleStack.hasProperty(ooNS::fo, "page-height")) { 1091 height = KoUnit::parseValue(styleStack.property(ooNS::fo, "page-height")); 1092 } 1093 if (styleStack.hasProperty(ooNS::fo, "margin-top")) { 1094 topMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-top")); 1095 } 1096 if (styleStack.hasProperty(ooNS::fo, "margin-bottom")) { 1097 bottomMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-bottom")); 1098 } 1099 if (styleStack.hasProperty(ooNS::fo, "margin-left")) { 1100 leftMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-left")); 1101 } 1102 if (styleStack.hasProperty(ooNS::fo, "margin-right")) { 1103 rightMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-right")); 1104 } 1105 if (styleStack.hasProperty(ooNS::style, "writing-mode")) { 1106 kDebug(30518) << "styleStack.hasAttribute( style:writing-mode ) :" << styleStack.hasProperty(ooNS::style, "writing-mode"); 1107 } 1108 if (styleStack.hasProperty(ooNS::style, "print-orientation")) { 1109 orientation = (styleStack.property(ooNS::style, "print-orientation") == "landscape") ? "Landscape" : "Portrait" ; 1110 } 1111 if (styleStack.hasProperty(ooNS::style, "num-format")) { 1112 kDebug(30518) << " num-format :" << styleStack.property(ooNS::style, "num-format"); 1113 //todo fixme 1114 } 1115 if (styleStack.hasProperty(ooNS::fo, "background-color")) { 1116 //todo 1117 kDebug(30518) << " fo:background-color :" << styleStack.property(ooNS::fo, "background-color"); 1118 } 1119 if (styleStack.hasProperty(ooNS::style, "print")) { 1120 //todo parsing 1121 QString str = styleStack.property(ooNS::style, "print"); 1122 kDebug(30518) << " style:print :" << str; 1123 1124 if (str.contains("headers")) { 1125 //todo implement it into kspread 1126 } 1127 if (str.contains("grid")) { 1128 table->printSettings()->setPrintGrid(true); 1129 } 1130 if (str.contains("annotations")) { 1131 //todo it's not implemented 1132 } 1133 if (str.contains("objects")) { 1134 //todo it's not implemented 1135 } 1136 if (str.contains("charts")) { 1137 //todo it's not implemented 1138 } 1139 if (str.contains("drawings")) { 1140 //todo it's not implemented 1141 } 1142 if (str.contains("formulas")) { 1143 table->setShowFormula(true); 1144 } 1145 if (str.contains("zero-values")) { 1146 //todo it's not implemented 1147 } 1148 } 1149 if (styleStack.hasProperty(ooNS::style, "table-centering")) { 1150 QString str = styleStack.property(ooNS::style, "table-centering"); 1151 //not implemented into kspread 1152 kDebug(30518) << " styleStack.attribute( style:table-centering ) :" << str; 1153 #if 0 1154 if (str == "horizontal") { 1155 } else if (str == "vertical") { 1156 } else if (str == "both") { 1157 } else if (str == "none") { 1158 } else 1159 kDebug(30518) << " table-centering unknown :" << str; 1160 #endif 1161 } 1162 format = QString("%1x%2").arg(width).arg(height); 1163 kDebug(30518) << " format :" << format; 1164 1165 KoPageLayout pageLayout; 1166 pageLayout.format = KoPageFormat::formatFromString(format); 1167 pageLayout.orientation = (orientation == "Portrait") 1168 ? KoPageFormat::Portrait : KoPageFormat::Landscape; 1169 pageLayout.leftMargin = leftMargin; 1170 pageLayout.rightMargin = rightMargin; 1171 pageLayout.topMargin = topMargin; 1172 pageLayout.bottomMargin = bottomMargin; 1173 table->printSettings()->setPageLayout(pageLayout); 1174 1175 kDebug(30518) << " left margin :" << leftMargin << " right :" << rightMargin 1176 << " top :" << topMargin << " bottom :" << bottomMargin; 1177 //<style:properties fo:page-width="21.8cm" fo:page-height="28.801cm" fo:margin-top="2cm" fo:margin-bottom="2.799cm" fo:margin-left="1.3cm" fo:margin-right="1.3cm" style:writing-mode="lr-tb"/> 1178 // QString format = paper.attribute( "format" ); 1179 // QString orientation = paper.attribute( "orientation" ); 1180 // m_pPrint->setPaperLayout( left, top, right, bottom, format, orientation ); 1181 // } 1182 } 1183 1184 1185 bool OpenCalcImport::parseBody(int numOfTables) 1186 { 1187 KoXmlElement content = m_content.documentElement(); 1188 KoXmlNode body = KoXml::namedItemNS(content, ooNS::office, "body"); 1189 1190 if (body.isNull()) 1191 return false; 1192 1193 loadOasisAreaName(body.toElement()); 1194 loadOasisCellValidation(body.toElement(), m_doc->map()->parser()); 1195 1196 Sheet * table; 1197 KoXmlNode sheet = KoXml::namedItemNS(body, ooNS::table, "table"); 1198 1199 kDebug() << " sheet :" << sheet.isNull(); 1200 if (sheet.isNull()) 1201 return false; 1202 1203 while (!sheet.isNull()) { 1204 KoXmlElement t = sheet.toElement(); 1205 if (t.isNull()) { 1206 sheet = sheet.nextSibling(); 1207 continue; 1208 } 1209 if (t.nodeName() != "table:table") { 1210 sheet = sheet.nextSibling(); 1211 continue; 1212 } 1213 1214 table = m_doc->map()->addNewSheet(); 1215 1216 table->setSheetName(t.attributeNS(ooNS::table, "name", QString()), true); 1217 kDebug() << " table->name()" << table->objectName(); 1218 sheet = sheet.nextSibling(); 1219 } 1220 1221 sheet = body.firstChild(); 1222 1223 int step = (int)(80 / numOfTables); 1224 int progress = 15; 1225 1226 m_doc->map()->setDefaultColumnWidth(MM_TO_POINT(22.7)); 1227 m_doc->map()->setDefaultRowHeight(MM_TO_POINT(4.3)); 1228 kDebug(30518) << "Global Height:" << MM_TO_POINT(4.3) << ", Global width:" << MM_TO_POINT(22.7); 1229 1230 while (!sheet.isNull()) { 1231 KoXmlElement t = sheet.toElement(); 1232 if (t.isNull()) { 1233 KMessageBox::sorry(0, i18n("The file seems to be corrupt. Skipping a table.")); 1234 sheet = sheet.nextSibling(); 1235 continue; 1236 } 1237 if (t.nodeName() != "table:table") { 1238 sheet = sheet.nextSibling(); 1239 continue; 1240 } 1241 1242 table = m_doc->map()->findSheet(t.attributeNS(ooNS::table, "name", QString())); 1243 if (!table) { 1244 KMessageBox::sorry(0, i18n("Skipping a table.")); 1245 sheet = sheet.nextSibling(); 1246 continue; 1247 } 1248 1249 Style * defaultStyle = m_defaultStyles[ "Default" ]; 1250 if (defaultStyle) { 1251 kDebug(30518) << "Copy default style to default cell"; 1252 table->map()->styleManager()->defaultStyle()->merge(*defaultStyle); 1253 } 1254 table->doc()->map()->setDefaultRowHeight(MM_TO_POINT(4.3)); 1255 table->doc()->map()->setDefaultColumnWidth(MM_TO_POINT(22.7)); 1256 1257 kDebug(30518) << "Added table:" << t.attributeNS(ooNS::table, "name", QString()); 1258 1259 if (t.hasAttributeNS(ooNS::table, "style-name")) { 1260 QString style = t.attributeNS(ooNS::table, "style-name", QString()); 1261 KoXmlElement * tableStyle = m_styles[ style ]; 1262 1263 KoXmlNode node; 1264 1265 if (tableStyle) 1266 node = tableStyle->firstChild(); 1267 1268 while (!node.isNull()) { 1269 KoXmlElement property = node.toElement(); 1270 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 1271 if (property.hasAttributeNS(ooNS::table, "display")) { 1272 bool visible = (property.attributeNS(ooNS::table, "display", QString()) == "true" ? true : false); 1273 table->hideSheet(!visible); 1274 kDebug(30518) << "Table:" << table->sheetName() << ", hidden:" << !visible; 1275 } 1276 } 1277 1278 node = node.nextSibling(); 1279 } 1280 1281 if (tableStyle && tableStyle->hasAttributeNS(ooNS::style, "master-page-name")) { 1282 QString stylename = "pm" + tableStyle->attributeNS(ooNS::style, "master-page-name", QString()); 1283 1284 loadTableMasterStyle(table, stylename); 1285 1286 } 1287 } 1288 if (t.hasAttributeNS(ooNS::table, "print-ranges")) { 1289 // e.g.: Sheet4.A1:Sheet4.E28 1290 QString range = t.attributeNS(ooNS::table, "print-ranges", QString()); 1291 OpenCalcPoint point(range); 1292 1293 kDebug(30518) << "Print range:" << point.translation; 1294 const Calligra::Sheets::Region region(point.translation); 1295 1296 kDebug(30518) << "Print table:" << region.firstSheet()->sheetName(); 1297 1298 if (table == region.firstSheet()) 1299 table->printSettings()->setPrintRegion(region); 1300 } 1301 1302 if (!readColLayouts(t, table)) 1303 return false; 1304 1305 if (!readRowsAndCells(t, table)) 1306 return false; 1307 1308 if (t.hasAttributeNS(ooNS::table, "protected")) { 1309 QByteArray passwd(""); 1310 if (t.hasAttributeNS(ooNS::table, "protection-key")) { 1311 QString p = t.attributeNS(ooNS::table, "protection-key", QString()); 1312 QByteArray str(p.toLatin1()); 1313 kDebug(30518) << "Decoding password:" << str; 1314 passwd = KCodecs::base64Decode(str); 1315 } 1316 kDebug(30518) << "Password hash: '" << passwd << "'"; 1317 table->setProtected(passwd); 1318 } 1319 1320 progress += step; 1321 emit sigProgress(progress); 1322 sheet = sheet.nextSibling(); 1323 } 1324 1325 KoXmlElement b = body.toElement(); 1326 if (b.hasAttributeNS(ooNS::table, "structure-protected")) { 1327 QByteArray passwd(""); 1328 if (b.hasAttributeNS(ooNS::table, "protection-key")) { 1329 QString p = b.attributeNS(ooNS::table, "protection-key", QString()); 1330 QByteArray str(p.toLatin1()); 1331 kDebug(30518) << "Decoding password:" << str; 1332 passwd = KCodecs::base64Decode(str); 1333 } 1334 kDebug(30518) << "Password hash: '" << passwd << "'"; 1335 1336 m_doc->map()->setProtected(passwd); 1337 } 1338 1339 emit sigProgress(98); 1340 1341 return true; 1342 } 1343 1344 void OpenCalcImport::insertStyles(KoXmlElement const & element) 1345 { 1346 if (element.isNull()) 1347 return; 1348 1349 KoXmlElement e; 1350 forEachElement(e, element) { 1351 if (e.isNull() || !e.hasAttributeNS(ooNS::style, "name")) { 1352 continue; 1353 } 1354 1355 QString name = e.attributeNS(ooNS::style, "name", QString()); 1356 kDebug(30518) << "Style: '" << name << "' loaded"; 1357 m_styles.insert(name, new KoXmlElement(e)); 1358 } 1359 } 1360 1361 1362 void OpenCalcImport::loadOasisAreaName(const KoXmlElement&body) 1363 { 1364 KoXmlNode namedAreas = KoXml::namedItemNS(body, ooNS::table, "named-expressions"); 1365 if (!namedAreas.isNull()) { 1366 KoXmlElement e; 1367 forEachElement(e, namedAreas) { 1368 if (e.isNull() || !e.hasAttributeNS(ooNS::table, "name") || !e.hasAttributeNS(ooNS::table, "cell-range-address")) { 1369 kDebug(30518) << "Reading in named area failed"; 1370 continue; 1371 } 1372 1373 // TODO: what is: table:base-cell-address 1374 QString name = e.attributeNS(ooNS::table, "name", QString()); 1375 QString areaPoint = e.attributeNS(ooNS::table, "cell-range-address", QString()); 1376 1377 m_namedAreas.append(name); 1378 kDebug(30518) << "Reading in named area, name:" << name << ", area:" << areaPoint; 1379 1380 OpenCalcPoint point(areaPoint); 1381 kDebug(30518) << "Area:" << point.translation; 1382 1383 const Calligra::Sheets::Region region(point.translation); 1384 1385 m_doc->map()->namedAreaManager()->insert(region, name); 1386 kDebug(30518) << "Area range:" << region.name(); 1387 } 1388 } 1389 } 1390 1391 void OpenCalcImport::loadOasisCellValidation(const KoXmlElement&body, const ValueParser *parser) 1392 { 1393 Q_UNUSED(parser) 1394 KoXmlNode validation = KoXml::namedItemNS(body, ooNS::table, "content-validations"); 1395 if (!validation.isNull()) { 1396 KoXmlElement element; 1397 forEachElement(element, validation) { 1398 if (element.localName() == "content-validation") { 1399 m_validationList.insert(element.attributeNS(ooNS::table, "name", QString()), element); 1400 kDebug(30518) << " validation found :" << element.attributeNS(ooNS::table, "name", QString()); 1401 } else { 1402 kDebug(30518) << " Tag not recognize :" << element.tagName(); 1403 } 1404 } 1405 } 1406 } 1407 1408 1409 QString * OpenCalcImport::loadFormat(KoXmlElement * element, 1410 Format::Type & formatType, 1411 QString name) 1412 { 1413 if (!element) 1414 return 0; 1415 1416 int i; 1417 bool ok; 1418 1419 QString * format = 0; 1420 KoXmlElement e = element->firstChild().toElement(); 1421 int precision = 0; 1422 int leadingZ = 1; 1423 #ifdef __GNUC__ 1424 #warning (coverity) what is the purpose of this constant? 1425 #endif 1426 bool thousandsSep = false; 1427 bool negRed = false; 1428 1429 if (element->localName() == "time-style") 1430 formatType = Format::Custom; 1431 else if (element->localName() == "date-style") 1432 formatType = Format::Custom; 1433 else if (element->localName() == "percentage-style") 1434 formatType = Format::Custom; 1435 else if (element->localName() == "number-style") 1436 formatType = Format::Custom; 1437 else if (element->localName() == "currency-style") 1438 formatType = Format::Custom; 1439 else if (element->localName() == "boolean-style") 1440 formatType = Format::Custom; 1441 1442 if (!e.isNull()) 1443 format = new QString(); 1444 1445 // TODO (element): 1446 // number:automatic-order="true" 1447 // number:truncate-on-overflow="false" 1448 // style:volatile="true" 1449 1450 while (!e.isNull()) { 1451 if (e.localName() == "properties" && e.namespaceURI() == ooNS::style) { 1452 if (e.hasAttributeNS(ooNS::fo, "color")) 1453 negRed = true; // we only support red... 1454 } else if (e.localName() == "text" && e.namespaceURI() == ooNS::number) { 1455 if (negRed && (e.text() == "-")) 1456 ; 1457 else 1458 format->append(e.text()); 1459 } else if (e.localName() == "currency-symbol" && e.namespaceURI() == ooNS::number) { 1460 QString sym(e.text()); 1461 kDebug(30518) << "Currency:" << sym; 1462 format->append(sym); 1463 // number:language="de" number:country="DE">€</number:currency-symbol> 1464 } else if (e.localName() == "day-of-week" && e.namespaceURI() == ooNS::number) { 1465 if (e.hasAttributeNS(ooNS::number, "style")) { 1466 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1467 format->append("dddd"); 1468 else 1469 format->append("ddd"); 1470 } else 1471 format->append("ddd"); 1472 } else if (e.localName() == "day" && e.namespaceURI() == ooNS::number) { 1473 if (e.hasAttributeNS(ooNS::number, "style")) { 1474 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1475 format->append("dd"); 1476 else 1477 format->append("d"); 1478 } else 1479 format->append("d"); 1480 } else if (e.localName() == "month" && e.namespaceURI() == ooNS::number) { 1481 if (e.hasAttributeNS(ooNS::number, "textual")) { 1482 if (e.attributeNS(ooNS::number, "textual", QString()) == "true") 1483 format->append("mm"); 1484 } 1485 1486 if (e.hasAttributeNS(ooNS::number, "style")) { 1487 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1488 format->append("mm"); 1489 else 1490 format->append("m"); 1491 } else 1492 format->append("m"); 1493 } else if (e.localName() == "year" && e.namespaceURI() == ooNS::number) { 1494 if (e.hasAttributeNS(ooNS::number, "style")) { 1495 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1496 format->append("yyyy"); 1497 else 1498 format->append("yy"); 1499 } else 1500 format->append("yy"); 1501 } else if (e.localName() == "hours" && e.namespaceURI() == ooNS::number) { 1502 if (e.hasAttributeNS(ooNS::number, "style")) { 1503 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1504 format->append("hh"); 1505 else 1506 format->append("h"); 1507 } else 1508 format->append("h"); 1509 } else if (e.localName() == "minutes" && e.namespaceURI() == ooNS::number) { 1510 if (e.hasAttributeNS(ooNS::number, "style")) { 1511 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1512 format->append("mm"); 1513 else 1514 format->append("m"); 1515 } else 1516 format->append("m"); 1517 } else if (e.localName() == "seconds" && e.namespaceURI() == ooNS::number) { 1518 if (e.hasAttributeNS(ooNS::number, "style")) { 1519 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1520 format->append("ss"); 1521 else 1522 format->append("s"); 1523 } else 1524 format->append("s"); 1525 } else if (e.localName() == "am-pm" && e.namespaceURI() == ooNS::number) { 1526 format->append("AM/PM"); 1527 } else if (e.localName() == "number" && e.namespaceURI() == ooNS::number) { 1528 // TODO: number:grouping="true" 1529 1530 if (e.hasAttributeNS(ooNS::number, "decimal-places")) { 1531 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok); 1532 if (ok) 1533 precision = d; 1534 } 1535 1536 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1537 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1538 if (ok) 1539 leadingZ = d; 1540 } 1541 1542 #ifdef __GNUC__ 1543 #warning thousandsSep can not be true (CID 3200) 1544 #endif 1545 if (thousandsSep && leadingZ <= 3) { 1546 format->append("#,"); 1547 for (i = leadingZ; i <= 3; ++i) 1548 format->append('#'); 1549 } 1550 1551 for (i = 1; i <= leadingZ; ++i) { 1552 format->append('0'); 1553 if ((i % 3 == 0) && thousandsSep) 1554 format->append(','); 1555 } 1556 1557 format->append('.'); 1558 for (i = 0; i < precision; ++i) 1559 format->append('0'); 1560 } else if (e.localName() == "scientific-number" && e.namespaceURI() == ooNS::number) { 1561 int exp = 2; 1562 1563 if (e.hasAttributeNS(ooNS::number, "decimal-places")) { 1564 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok); 1565 if (ok) 1566 precision = d; 1567 } 1568 1569 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1570 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1571 if (ok) 1572 leadingZ = d; 1573 } 1574 1575 if (e.hasAttributeNS(ooNS::number, "min-exponent-digits")) { 1576 int d = e.attributeNS(ooNS::number, "min-exponent-digits", QString()).toInt(&ok); 1577 if (ok) 1578 exp = d; 1579 if (exp <= 0) 1580 exp = 1; 1581 } 1582 1583 if (thousandsSep && leadingZ <= 3) { 1584 format->append("#,"); 1585 for (i = leadingZ; i <= 3; ++i) 1586 format->append('#'); 1587 } 1588 1589 for (i = 1; i <= leadingZ; ++i) { 1590 format->append('0'); 1591 if ((i % 3 == 0) && thousandsSep) 1592 format->append(','); 1593 } 1594 1595 format->append('.'); 1596 for (i = 0; i < precision; ++i) 1597 format->append('0'); 1598 1599 format->append("E+"); 1600 for (i = 0; i < exp; ++i) 1601 format->append('0'); 1602 1603 formatType = Format::Custom; 1604 } else if (e.localName() == "fraction" && e.namespaceURI() == ooNS::number) { 1605 int integer = 0; 1606 int numerator = 1; 1607 int denominator = 1; 1608 1609 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1610 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1611 if (ok) 1612 integer = d; 1613 } 1614 if (e.hasAttributeNS(ooNS::number, "min-numerator-digits")) { 1615 int d = e.attributeNS(ooNS::number, "min-numerator-digits", QString()).toInt(&ok); 1616 if (ok) 1617 numerator = d; 1618 } 1619 if (e.hasAttributeNS(ooNS::number, "min-denominator-digits")) { 1620 int d = e.attributeNS(ooNS::number, "min-denominator-digits", QString()).toInt(&ok); 1621 if (ok) 1622 denominator = d; 1623 } 1624 1625 for (i = 0; i <= integer; ++i) 1626 format->append('#'); 1627 1628 format->append(' '); 1629 1630 for (i = 0; i <= numerator; ++i) 1631 format->append('?'); 1632 1633 format->append('/'); 1634 1635 for (i = 0; i <= denominator; ++i) 1636 format->append('?'); 1637 } 1638 // Not needed: 1639 // <style:map style:condition="value()>=0" style:apply-style-name="N106P0"/> 1640 // we handle painting negative numbers in red differently 1641 1642 e = e.nextSibling().toElement(); 1643 } 1644 1645 if (negRed) { 1646 QString f(*format); 1647 format->append(";[Red]"); 1648 format->append(f); 1649 } 1650 1651 kDebug(30518) << "*** New FormatString:" << *format; 1652 1653 m_formats.insert(name, format); 1654 1655 return format; 1656 } 1657 1658 void OpenCalcImport::loadFontStyle(Style * layout, KoXmlElement const * font) const 1659 { 1660 if (!font || !layout) 1661 return; 1662 1663 kDebug(30518) << "Copy font style from the layout" << font->tagName() << "," << font->nodeName(); 1664 1665 if (font->hasAttributeNS(ooNS::fo, "font-family")) 1666 layout->setFontFamily(font->attributeNS(ooNS::fo, "font-family", QString())); 1667 if (font->hasAttributeNS(ooNS::fo, "color")) 1668 layout->setFontColor(QColor(font->attributeNS(ooNS::fo, "color", QString()))); 1669 if (font->hasAttributeNS(ooNS::fo, "font-size")) 1670 layout->setFontSize(int(KoUnit::parseValue(font->attributeNS(ooNS::fo, "font-size", QString()), 10))); 1671 else 1672 layout->setFontSize(10); 1673 if (font->hasAttributeNS(ooNS::fo, "font-style")) { 1674 kDebug(30518) << "italic"; 1675 layout->setFontItalic(true); // only thing we support 1676 } 1677 if (font->hasAttributeNS(ooNS::fo, "font-weight")) 1678 layout->setFontBold(true); // only thing we support 1679 if (font->hasAttributeNS(ooNS::fo, "text-underline") || font->hasAttributeNS(ooNS::style, "text-underline")) 1680 layout->setFontUnderline(true); // only thing we support 1681 if (font->hasAttributeNS(ooNS::style, "text-crossing-out")) 1682 layout->setFontStrikeOut(true); // only thing we support 1683 if (font->hasAttributeNS(ooNS::style, "font-pitch")) { 1684 // TODO: possible values: fixed, variable 1685 } 1686 // TODO: 1687 // text-underline-color 1688 } 1689 1690 void OpenCalcImport::loadBorder(Style * layout, QString const & borderDef, bPos pos) const 1691 { 1692 if (borderDef == "none") 1693 return; 1694 1695 int p = borderDef.indexOf(' '); 1696 if (p < 0) 1697 return; 1698 1699 QPen pen; 1700 QString w = borderDef.left(p); 1701 pen.setWidth((int) KoUnit::parseValue(w)); 1702 1703 1704 ++p; 1705 int p2 = borderDef.indexOf(' ', p); 1706 QString s = borderDef.mid(p, p2 - p); 1707 1708 kDebug(30518) << "Borderstyle:" << s; 1709 1710 if (s == "solid" || s == "double") 1711 pen.setStyle(Qt::SolidLine); 1712 else { 1713 #if 0 1714 // TODO: not supported by oocalc 1715 pen.setStyle(Qt::DashLine); 1716 pen.setStyle(Qt::DotLine); 1717 pen.setStyle(Qt::DashDotLine); 1718 pen.setStyle(Qt::DashDotDotLine); 1719 #endif 1720 pen.setStyle(Qt::SolidLine); //default. 1721 } 1722 1723 ++p2; 1724 p = borderDef.indexOf(' ', p2); 1725 if (p == -1) 1726 p = borderDef.length(); 1727 1728 pen.setColor(QColor(borderDef.right(p - p2))); 1729 1730 if (pos == Left) 1731 layout->setLeftBorderPen(pen); 1732 else if (pos == Top) 1733 layout->setTopBorderPen(pen); 1734 else if (pos == Right) 1735 layout->setRightBorderPen(pen); 1736 else if (pos == Bottom) 1737 layout->setBottomBorderPen(pen); 1738 else if (pos == Border) { 1739 layout->setLeftBorderPen(pen); 1740 layout->setTopBorderPen(pen); 1741 layout->setRightBorderPen(pen); 1742 layout->setBottomBorderPen(pen); 1743 } 1744 // TODO Diagonals not supported by oocalc 1745 } 1746 1747 void OpenCalcImport::loadStyleProperties(Style * layout, KoXmlElement const & property) const 1748 { 1749 kDebug(30518) << "*** Loading style properties *****"; 1750 1751 if (property.hasAttributeNS(ooNS::style, "decimal-places")) { 1752 bool ok = false; 1753 int p = property.attributeNS(ooNS::style, "decimal-places", QString()).toInt(&ok); 1754 if (ok) 1755 layout->setPrecision(p); 1756 } 1757 1758 if (property.hasAttributeNS(ooNS::style, "font-name")) { 1759 KoXmlElement * font = m_styles[ property.attributeNS(ooNS::style, "font-name", QString())]; 1760 loadFontStyle(layout, font); // generell font style 1761 } 1762 1763 loadFontStyle(layout, &property); // specific font style 1764 1765 // TODO: 1766 // diagonal: fall + goup 1767 // fo:direction="ltr" 1768 // style:text-align-source ("fix") 1769 // style:shadow 1770 // style:text-outline 1771 // indents from right, top, bottom 1772 // style:condition="cell-content()=15" 1773 // => style:apply-style-name="Result" style:base-cell-address="Sheet6.A5"/> 1774 1775 if (property.hasAttributeNS(ooNS::style, "rotation-angle")) { 1776 bool ok = false; 1777 int a = property.attributeNS(ooNS::style, "rotation-angle", QString()).toInt(&ok); 1778 if (ok) 1779 layout->setAngle(-a + 1); 1780 } 1781 1782 if (property.hasAttributeNS(ooNS::fo, "direction")) { 1783 layout->setVerticalText(true); 1784 } 1785 if (property.hasAttributeNS(ooNS::fo, "text-align")) { 1786 QString s = property.attributeNS(ooNS::fo, "text-align", QString()); 1787 if (s == "center") 1788 layout->setHAlign(Style::Center); 1789 else if (s == "end") 1790 layout->setHAlign(Style::Right); 1791 else if (s == "start") 1792 layout->setHAlign(Style::Left); 1793 else if (s == "justify") // TODO in KSpread! 1794 layout->setHAlign(Style::Center); 1795 } 1796 if (property.hasAttributeNS(ooNS::fo, "margin-left")) { 1797 kDebug(30518) << "margin-left :" << KoUnit::parseValue(property.attributeNS(ooNS::fo, "margin-left", QString()), 0.0); 1798 layout->setIndentation(KoUnit::parseValue(property.attributeNS(ooNS::fo, "margin-left", QString()), 0.0)); 1799 } 1800 if (property.hasAttributeNS(ooNS::fo, "background-color")) 1801 layout->setBackgroundColor(QColor(property.attributeNS(ooNS::fo, "background-color", QString()))); 1802 1803 if (property.hasAttributeNS(ooNS::style, "print-content")) { 1804 if (property.attributeNS(ooNS::style, "print-content", QString()) == "false") 1805 layout->setDontPrintText(false); 1806 } 1807 if (property.hasAttributeNS(ooNS::style, "cell-protect")) { 1808 QString prot(property.attributeNS(ooNS::style, "cell-protect", QString())); 1809 if (prot == "none") { 1810 layout->setNotProtected(true); 1811 layout->setHideFormula(false); 1812 layout->setHideAll(false); 1813 } else if (prot == "formula-hidden") { 1814 layout->setNotProtected(true); 1815 layout->setHideFormula(true); 1816 layout->setHideAll(false); 1817 } else if (prot == "protected formula-hidden") { 1818 layout->setNotProtected(false); 1819 layout->setHideFormula(true); 1820 layout->setHideAll(false); 1821 } else if (prot == "hidden-and-protected") { 1822 layout->setNotProtected(false); 1823 layout->setHideFormula(false); 1824 layout->setHideAll(true); 1825 } else if (prot == "protected") { 1826 layout->setNotProtected(false); 1827 layout->setHideFormula(false); 1828 layout->setHideAll(false); 1829 } 1830 kDebug(30518) << "Cell" << prot; 1831 } 1832 1833 if (property.hasAttributeNS(ooNS::fo, "padding-left")) 1834 layout->setIndentation(KoUnit::parseValue(property.attributeNS(ooNS::fo, "padding-left", QString()))); 1835 1836 if (property.hasAttributeNS(ooNS::fo, "vertical-align")) { 1837 QString s = property.attributeNS(ooNS::fo, "vertical-align", QString()); 1838 if (s == "middle") 1839 layout->setVAlign(Style::Middle); 1840 else if (s == "bottom") 1841 layout->setVAlign(Style::Bottom); 1842 else 1843 layout->setVAlign(Style::Top); 1844 } else 1845 layout->setVAlign(Style::Bottom); 1846 1847 if (property.hasAttributeNS(ooNS::fo, "wrap-option")) { 1848 layout->setWrapText(true); 1849 1850 /* we do not support anything else yet 1851 QString s = property.attributeNS( ooNS::fo, "wrap-option", QString() ); 1852 if ( s == "wrap" ) 1853 layout->setMultiRow( true ); 1854 */ 1855 } 1856 1857 if (property.hasAttributeNS(ooNS::fo, "border-bottom")) { 1858 loadBorder(layout, property.attributeNS(ooNS::fo, "border-bottom", QString()), Bottom); 1859 // TODO: style:border-line-width-bottom if double! 1860 } 1861 1862 if (property.hasAttributeNS(ooNS::fo, "border-right")) { 1863 loadBorder(layout, property.attributeNS(ooNS::fo, "border-right", QString()), Right); 1864 // TODO: style:border-line-width-right 1865 } 1866 1867 if (property.hasAttributeNS(ooNS::fo, "border-top")) { 1868 loadBorder(layout, property.attributeNS(ooNS::fo, "border-top", QString()), Top); 1869 // TODO: style:border-line-width-top 1870 } 1871 1872 if (property.hasAttributeNS(ooNS::fo, "border-left")) { 1873 loadBorder(layout, property.attributeNS(ooNS::fo, "border-left", QString()), Left); 1874 // TODO: style:border-line-width-left 1875 } 1876 1877 if (property.hasAttributeNS(ooNS::fo, "border")) { 1878 loadBorder(layout, property.attributeNS(ooNS::fo, "border", QString()), Border); 1879 // TODO: style:border-line-width-left 1880 } 1881 } 1882 1883 void OpenCalcImport::readInStyle(Style * layout, KoXmlElement const & style) 1884 { 1885 kDebug(30518) << "** Reading Style:" << style.tagName() << ";" << style.attributeNS(ooNS::style, "name", QString()); 1886 if (style.localName() == "style" && style.namespaceURI() == ooNS::style) { 1887 if (style.hasAttributeNS(ooNS::style, "parent-style-name")) { 1888 Style * cp 1889 = m_defaultStyles.value(style.attributeNS(ooNS::style, "parent-style-name", QString())); 1890 kDebug(30518) << "Copying layout from" << style.attributeNS(ooNS::style, "parent-style-name", QString()); 1891 1892 if (cp != 0) 1893 layout = cp; 1894 } else if (style.hasAttributeNS(ooNS::style, "family")) { 1895 QString name = style.attribute("style-family") + "default"; 1896 Style * cp = m_defaultStyles.value(name); 1897 1898 kDebug(30518) << "Copying layout from" << name << "," << !cp; 1899 1900 if (cp != 0) 1901 layout = cp; 1902 } 1903 1904 if (style.hasAttributeNS(ooNS::style, "data-style-name")) { 1905 QString * format = m_formats[ style.attributeNS(ooNS::style, "data-style-name", QString())]; 1906 Format::Type formatType = Format::Generic; 1907 1908 if (!format) { 1909 // load and convert it 1910 QString name(style.attributeNS(ooNS::style, "data-style-name", QString())); 1911 format = loadFormat(m_styles[ name ], formatType, name); 1912 } 1913 1914 if (format) { 1915 layout->setCustomFormat(*format); 1916 layout->setFormatType(formatType); 1917 } 1918 1919 // <number:currency-symbol number:language="de" number:country="DE">€</number:currency-symbol> 1920 } 1921 } 1922 1923 KoXmlElement property; 1924 forEachElement(property, style) { 1925 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) 1926 loadStyleProperties(layout, property); 1927 1928 kDebug(30518) << layout->fontFamily(); 1929 } 1930 } 1931 1932 bool OpenCalcImport::createStyleMap(KoXmlDocument const & styles) 1933 { 1934 KoXmlElement content = styles.documentElement(); 1935 KoXmlNode docStyles = KoXml::namedItemNS(content, ooNS::office, "document-styles"); 1936 1937 if (content.hasAttributeNS(ooNS::office, "version")) { 1938 bool ok = true; 1939 double d = content.attributeNS(ooNS::office, "version", QString()).toDouble(&ok); 1940 1941 if (ok) { 1942 kDebug(30518) << "OpenCalc version:" << d; 1943 if (d > 1.0) { 1944 QString message(i18n("This document was created with OpenOffice.org version '%1'. This filter was written for version 1.0. Reading this file could cause strange behavior, crashes or incorrect display of the data. Do you want to continue converting the document?", content.attributeNS(ooNS::office, "version", QString()))); 1945 if (KMessageBox::warningYesNo(0, message, i18n("Unsupported document version")) == KMessageBox::No) 1946 return false; 1947 } 1948 } 1949 } 1950 1951 KoXmlNode fontStyles = KoXml::namedItemNS(content, ooNS::office, "font-decls"); 1952 1953 if (!fontStyles.isNull()) { 1954 kDebug(30518) << "Starting reading in font-decl..."; 1955 1956 insertStyles(fontStyles.toElement()); 1957 } else 1958 kDebug(30518) << "No items found"; 1959 1960 kDebug(30518) << "Starting reading in auto:styles"; 1961 1962 KoXmlNode autoStyles = KoXml::namedItemNS(content, ooNS::office, "automatic-styles"); 1963 if (!autoStyles.isNull()) 1964 insertStyles(autoStyles.toElement()); 1965 else 1966 kDebug(30518) << "No items found"; 1967 1968 1969 kDebug(30518) << "Reading in master styles"; 1970 1971 KoXmlNode masterStyles = KoXml::namedItemNS(content, ooNS::office, "master-styles"); 1972 1973 if (masterStyles.isNull()) { 1974 kDebug(30518) << "Nothing found"; 1975 } 1976 1977 KoXmlElement master = KoXml::namedItemNS(masterStyles, ooNS::style, "master-page"); 1978 if (!master.isNull()) { 1979 QString name("pm"); 1980 name += master.attributeNS(ooNS::style, "name", QString()); 1981 kDebug(30518) << "Master style: '" << name << "' loaded"; 1982 m_styles.insert(name, new KoXmlElement(master)); 1983 1984 master = master.nextSibling().toElement(); 1985 } 1986 1987 1988 kDebug(30518) << "Starting reading in office:styles"; 1989 1990 KoXmlNode fixedStyles = KoXml::namedItemNS(content, ooNS::office, "styles"); 1991 1992 kDebug(30518) << "Reading in default styles"; 1993 1994 KoXmlNode def = KoXml::namedItemNS(fixedStyles, ooNS::style, "default-style"); 1995 kDebug() << " def !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! :" << def.isNull(); 1996 while (!def.isNull()) { 1997 KoXmlElement e = def.toElement(); 1998 kDebug(30518) << "Style found" << e.nodeName() << ", tag:" << e.tagName(); 1999 2000 if (e.nodeName() != "style:default-style") { 2001 def = def.nextSibling(); 2002 continue; 2003 } 2004 2005 if (!e.isNull()) { 2006 Style * layout = new Style(); 2007 2008 readInStyle(layout, e); 2009 kDebug(30518) << "Default style" << e.attributeNS(ooNS::style, "family", QString()) << "default" << " loaded"; 2010 2011 m_defaultStyles.insert(e.attributeNS(ooNS::style, "family", QString()) + "default", layout); 2012 // QFont font = layout->font(); 2013 // kDebug(30518) <<"Font:" << font.family() <<"," << font.toString(); 2014 } 2015 2016 def = def.nextSibling(); 2017 } 2018 2019 KoXmlElement defs = KoXml::namedItemNS(fixedStyles, ooNS::style, "style"); 2020 while (!defs.isNull()) { 2021 if (defs.nodeName() != "style:style") 2022 break; // done 2023 2024 if (!defs.hasAttributeNS(ooNS::style, "name")) { 2025 // ups... 2026 defs = defs.nextSibling().toElement(); 2027 continue; 2028 } 2029 2030 Style * layout = new Style(); 2031 readInStyle(layout, defs); 2032 kDebug(30518) << "Default style" << defs.attributeNS(ooNS::style, "name", QString()) << " loaded"; 2033 2034 m_defaultStyles.insert(defs.attributeNS(ooNS::style, "name", QString()), layout); 2035 // kDebug(30518) <<"Font:" << layout->font().family() <<"," << layout->font().toString(); 2036 2037 defs = defs.nextSibling().toElement(); 2038 } 2039 2040 if (!fixedStyles.isNull()) 2041 insertStyles(fixedStyles.toElement()); 2042 2043 kDebug(30518) << "Starting reading in automatic styles"; 2044 2045 content = m_content.documentElement(); 2046 autoStyles = KoXml::namedItemNS(content, ooNS::office, "automatic-styles"); 2047 2048 if (!autoStyles.isNull()) 2049 insertStyles(autoStyles.toElement()); 2050 2051 fontStyles = KoXml::namedItemNS(content, ooNS::office, "font-decls"); 2052 2053 if (!fontStyles.isNull()) { 2054 kDebug(30518) << "Starting reading in special font decl"; 2055 2056 insertStyles(fontStyles.toElement()); 2057 } 2058 2059 kDebug(30518) << "Styles read in."; 2060 2061 return true; 2062 } 2063 2064 void OpenCalcImport::loadOasisValidation(Validity validity, const QString& validationName, const ValueParser *parser) 2065 { 2066 kDebug(30518) << "validationName:" << validationName; 2067 KoXmlElement element = m_validationList[validationName]; 2068 if (element.hasAttributeNS(ooNS::table, "condition")) { 2069 QString valExpression = element.attributeNS(ooNS::table, "condition", QString()); 2070 kDebug(30518) << " element.attribute( table:condition )" << valExpression; 2071 //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition 2072 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() 2073 //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value 2074 //TrueCondition ::= GetFunction | cell-content() Operator Value 2075 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 2076 //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) 2077 //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!=' 2078 //Value ::= NumberValue | String | Formula 2079 //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information. 2080 //A String comprises one or more characters surrounded by quotation marks. 2081 //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater. 2082 2083 //ExtendedTrueCondition 2084 if (valExpression.contains("cell-content-text-length()")) { 2085 //"cell-content-text-length()>45" 2086 valExpression.remove("cell-content-text-length()"); 2087 kDebug(30518) << " valExpression = :" << valExpression; 2088 validity.setRestriction(Validity::TextLength); 2089 2090 loadOasisValidationCondition(validity, valExpression, parser); 2091 } 2092 //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) 2093 else if (valExpression.contains("cell-content-text-length-is-between")) { 2094 validity.setRestriction(Validity::TextLength); 2095 validity.setCondition(Conditional::Between); 2096 valExpression.remove("cell-content-text-length-is-between("); 2097 kDebug(30518) << " valExpression :" << valExpression; 2098 valExpression.remove(')'); 2099 QStringList listVal = valExpression.split(','); 2100 loadOasisValidationValue(validity, listVal, parser); 2101 } else if (valExpression.contains("cell-content-text-length-is-not-between")) { 2102 validity.setRestriction(Validity::TextLength); 2103 validity.setCondition(Conditional::Different); 2104 valExpression.remove("cell-content-text-length-is-not-between("); 2105 kDebug(30518) << " valExpression :" << valExpression; 2106 valExpression.remove(')'); 2107 kDebug(30518) << " valExpression :" << valExpression; 2108 QStringList listVal = valExpression.split(','); 2109 loadOasisValidationValue(validity, listVal, parser); 2110 2111 } 2112 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() 2113 else { 2114 if (valExpression.contains("cell-content-is-whole-number()")) { 2115 validity.setRestriction(Validity::Number); 2116 valExpression.remove("cell-content-is-whole-number() and "); 2117 } else if (valExpression.contains("cell-content-is-decimal-number()")) { 2118 validity.setRestriction(Validity::Integer); 2119 valExpression.remove("cell-content-is-decimal-number() and "); 2120 } else if (valExpression.contains("cell-content-is-date()")) { 2121 validity.setRestriction(Validity::Date); 2122 valExpression.remove("cell-content-is-date() and "); 2123 } else if (valExpression.contains("cell-content-is-time()")) { 2124 validity.setRestriction(Validity::Time); 2125 valExpression.remove("cell-content-is-time() and "); 2126 } 2127 kDebug(30518) << "valExpression :" << valExpression; 2128 2129 if (valExpression.contains("cell-content()")) { 2130 valExpression.remove("cell-content()"); 2131 loadOasisValidationCondition(validity, valExpression, parser); 2132 } 2133 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 2134 //for the moment we support just int/double value, not text/date/time :( 2135 if (valExpression.contains("cell-content-is-between(")) { 2136 valExpression.remove("cell-content-is-between("); 2137 valExpression.remove(')'); 2138 QStringList listVal = valExpression.split(','); 2139 loadOasisValidationValue(validity, listVal, parser); 2140 2141 validity.setCondition(Conditional::Between); 2142 } 2143 if (valExpression.contains("cell-content-is-not-between(")) { 2144 valExpression.remove("cell-content-is-not-between("); 2145 valExpression.remove(')'); 2146 QStringList listVal = valExpression.split(','); 2147 loadOasisValidationValue(validity, listVal, parser); 2148 validity.setCondition(Conditional::Different); 2149 } 2150 } 2151 } 2152 if (element.hasAttributeNS(ooNS::table, "allow-empty-cell")) { 2153 validity.setAllowEmptyCell(((element.attributeNS(ooNS::table, "allow-empty-cell", QString()) == "true") ? true : false)); 2154 2155 } 2156 if (element.hasAttributeNS(ooNS::table, "base-cell-address")) { 2157 //todo what is it ? 2158 } 2159 2160 KoXmlElement help = KoXml::namedItemNS(element, ooNS::table, "help-message"); 2161 if (!help.isNull()) { 2162 if (help.hasAttributeNS(ooNS::table, "title")) 2163 validity.setTitleInfo(help.attributeNS(ooNS::table, "title", QString())); 2164 if (help.hasAttributeNS(ooNS::table, "display")) 2165 validity.setDisplayValidationInformation(((help.attributeNS(ooNS::table, "display", QString()) == "true") ? true : false)); 2166 KoXmlElement attrText = KoXml::namedItemNS(help, ooNS::text, "p"); 2167 if (!attrText.isNull()) 2168 validity.setMessageInfo(attrText.text()); 2169 } 2170 2171 KoXmlElement error = KoXml::namedItemNS(element, ooNS::table, "error-message"); 2172 if (!error.isNull()) { 2173 if (error.hasAttributeNS(ooNS::table, "title")) 2174 validity.setTitle(error.attributeNS(ooNS::table, "title", QString())); 2175 if (error.hasAttributeNS(ooNS::table, "message-type")) { 2176 QString str = error.attributeNS(ooNS::table, "message-type", QString()); 2177 if (str == "warning") 2178 validity.setAction(Validity::Warning); 2179 else if (str == "information") 2180 validity.setAction(Validity::Information); 2181 else if (str == "stop") 2182 validity.setAction(Validity::Stop); 2183 else 2184 kDebug(30518) << "validation : message type unknown :" << str; 2185 } 2186 2187 if (error.hasAttributeNS(ooNS::table, "display")) { 2188 kDebug(30518) << " display message :" << error.attributeNS(ooNS::table, "display", QString()); 2189 validity.setDisplayMessage((error.attributeNS(ooNS::table, "display", QString()) == "true")); 2190 } 2191 KoXmlElement attrText = KoXml::namedItemNS(error, ooNS::text, "p"); 2192 if (!attrText.isNull()) 2193 validity.setMessage(attrText.text()); 2194 } 2195 } 2196 2197 void OpenCalcImport::loadOasisValidationValue(Validity validity, const QStringList &listVal, const ValueParser *parser) 2198 { 2199 kDebug(30518) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 2200 2201 validity.setMinimumValue(parser->parse(listVal[0])); 2202 validity.setMaximumValue(parser->parse(listVal[1])); 2203 } 2204 2205 2206 void OpenCalcImport::loadOasisValidationCondition(Validity validity, QString &valExpression, const ValueParser *parser) 2207 { 2208 QString value; 2209 if (valExpression.contains("<=")) { 2210 value = valExpression.remove("<="); 2211 validity.setCondition(Conditional::InferiorEqual); 2212 } else if (valExpression.contains(">=")) { 2213 value = valExpression.remove(">="); 2214 validity.setCondition(Conditional::SuperiorEqual); 2215 } else if (valExpression.contains("!=")) { 2216 //add Differentto attribute 2217 value = valExpression.remove("!="); 2218 validity.setCondition(Conditional::DifferentTo); 2219 } else if (valExpression.contains('<')) { 2220 value = valExpression.remove('<'); 2221 validity.setCondition(Conditional::Inferior); 2222 } else if (valExpression.contains('>')) { 2223 value = valExpression.remove('>'); 2224 validity.setCondition(Conditional::Superior); 2225 } else if (valExpression.contains('=')) { 2226 value = valExpression.remove('='); 2227 validity.setCondition(Conditional::Equal); 2228 } else 2229 kDebug(30518) << " I don't know how to parse it :" << valExpression; 2230 2231 kDebug(30518) << " value :" << value; 2232 validity.setMinimumValue(parser->parse(value)); 2233 } 2234 2235 2236 int OpenCalcImport::readMetaData() 2237 { 2238 int result = 5; 2239 KoDocumentInfo * docInfo = m_doc->documentInfo(); 2240 2241 KoXmlNode meta = KoXml::namedItemNS(m_meta, ooNS::office, "document-meta"); 2242 KoXmlNode office = KoXml::namedItemNS(meta, ooNS::office, "meta"); 2243 2244 if (office.isNull()) 2245 return 2; 2246 2247 KoXmlElement e = KoXml::namedItemNS(office, ooNS::dc, "creator"); 2248 if (!e.isNull() && !e.text().isEmpty()) 2249 docInfo->setAuthorInfo("creator", e.text()); 2250 2251 e = KoXml::namedItemNS(office, ooNS::dc, "title"); 2252 if (!e.isNull() && !e.text().isEmpty()) 2253 docInfo->setAboutInfo("title", e.text()); 2254 2255 e = KoXml::namedItemNS(office, ooNS::dc, "description"); 2256 if (!e.isNull() && !e.text().isEmpty()) 2257 docInfo->setAboutInfo("description", e.text()); // ### was: abstract 2258 2259 e = KoXml::namedItemNS(office, ooNS::dc, "subject"); 2260 if (!e.isNull() && !e.text().isEmpty()) 2261 docInfo->setAboutInfo("subject", e.text()); 2262 2263 e = KoXml::namedItemNS(office, ooNS::meta, "keywords"); 2264 if (!e.isNull()) { 2265 e = KoXml::namedItemNS(e, ooNS::meta, "keyword"); 2266 if (!e.isNull() && !e.text().isEmpty()) 2267 docInfo->setAboutInfo("keyword", e.text()); 2268 } 2269 2270 e = KoXml::namedItemNS(office, ooNS::meta, "document-statistic"); 2271 if (!e.isNull() && e.hasAttributeNS(ooNS::meta, "table-count")) { 2272 bool ok = false; 2273 result = e.attributeNS(ooNS::meta, "table-count", QString()).toInt(&ok); 2274 if (!ok) 2275 result = 5; 2276 } 2277 2278 m_meta.clear(); // not needed anymore 2279 2280 return result; 2281 } 2282 2283 KoFilter::ConversionStatus OpenCalcImport::convert(QByteArray const & from, QByteArray const & to) 2284 { 2285 kDebug(30518) << "Entering OpenCalc Import filter:" << from << " -" << to; 2286 2287 KoDocument * document = m_chain->outputDocument(); 2288 if (!document) 2289 return KoFilter::StupidError; 2290 2291 if (!qobject_cast<const Calligra::Sheets::Doc *>(document)) { // it's safer that way :) 2292 kWarning(30518) << "document isn't a Calligra::Sheets::Doc but a " << document->metaObject()->className(); 2293 return KoFilter::NotImplemented; 2294 } 2295 2296 if ((from != "application/vnd.sun.xml.calc" && from != "application/vnd.sun.xml.calc.template") || to != "application/x-kspread") { 2297 kWarning(30518) << "Invalid mimetypes " << from << " " << to; 2298 return KoFilter::NotImplemented; 2299 } 2300 2301 m_doc = (Doc *) document; 2302 2303 if (m_doc->mimeType() != "application/x-kspread") { 2304 kWarning(30518) << "Invalid document mimetype " << m_doc->mimeType(); 2305 return KoFilter::NotImplemented; 2306 } 2307 2308 kDebug(30518) << "Opening file"; 2309 2310 KoFilter::ConversionStatus preStatus = openFile(); 2311 2312 if (preStatus != KoFilter::OK) 2313 return preStatus; 2314 2315 emit sigProgress(13); 2316 int tables = readMetaData(); 2317 2318 emit sigProgress(15); 2319 2320 if (!parseBody(tables)) 2321 return KoFilter::StupidError; 2322 2323 emit sigProgress(100); 2324 return KoFilter::OK; 2325 } 2326 2327 KoFilter::ConversionStatus OpenCalcImport::openFile() 2328 { 2329 KoStore * store = KoStore::createStore(m_chain->inputFile(), KoStore::Read); 2330 2331 kDebug(30518) << "Store created"; 2332 2333 if (!store) { 2334 kWarning(30518) << "Couldn't open the requested file."; 2335 return KoFilter::FileNotFound; 2336 } 2337 2338 kDebug(30518) << "Trying to open content.xml"; 2339 QString messageError; 2340 loadAndParse(m_content, "content.xml", store); 2341 kDebug(30518) << "Opened"; 2342 2343 KoXmlDocument styles; 2344 kDebug(30518) << "file content.xml loaded"; 2345 2346 loadAndParse(styles, "styles.xml", store); 2347 2348 loadAndParse(m_meta, "meta.xml", store); 2349 loadAndParse(m_settings, "settings.xml", store); 2350 2351 delete store; 2352 2353 emit sigProgress(10); 2354 2355 if (!createStyleMap(styles)) 2356 return KoFilter::UserCancelled; 2357 2358 return KoFilter::OK; 2359 } 2360 2361 KoFilter::ConversionStatus OpenCalcImport::loadAndParse(KoXmlDocument& doc, const QString& fileName, KoStore *m_store) 2362 { 2363 return OoUtils::loadAndParse(fileName, doc, m_store); 2364 } 2365 2366 #include "opencalcimport.moc" 2367