File indexing completed on 2025-01-12 13:05:55

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