File indexing completed on 2025-07-13 10:51:48

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