1 /* This file is part of the KDE project
2 Copyright (C) 2002 Norbert Andres <nandres@web.de>
3 Copyright (C) 2004 - 2005 Laurent Montel <montel@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "opencalcimport.h"
22
23 #include <float.h>
24 #include <math.h>
25
26 #include <QColor>
27 #include <QFile>
28 #include <QFont>
29 #include <QPen>
30 #include <QList>
31 #include <QByteArray>
32
33 #include <kdebug.h>
34 #include <KoDocumentInfo.h>
35 #include <kpluginfactory.h>
36 #include <kmessagebox.h>
37 #include <kcodecs.h>
38 #include <KoFilterChain.h>
39 #include <KoUnit.h>
40 #include <KoStyleStack.h>
41 #include <ooutils.h>
42
43 #include <sheets/Cell.h>
44 #include <sheets/CellStorage.h>
45 #include <sheets/Condition.h>
46 #include <sheets/part/Doc.h>
47 #include <sheets/Global.h>
48 #include <sheets/HeaderFooter.h>
49 #include <sheets/Map.h>
50 #include <sheets/NamedAreaManager.h>
51 #include <sheets/PrintSettings.h>
52 #include <sheets/Region.h>
53 #include <sheets/RowColumnFormat.h>
54 #include <sheets/RowFormatStorage.h>
55 #include <sheets/Sheet.h>
56 #include <sheets/Style.h>
57 #include <sheets/StyleManager.h>
58 #include <sheets/Validity.h>
59 #include <sheets/Value.h>
60 #include <sheets/ValueParser.h>
61
62 #define SECSPERDAY (24 * 60 * 60)
63
64 using namespace Calligra::Sheets;
65
66 K_PLUGIN_FACTORY_WITH_JSON(OpenCalcImportFactory, "calligra_filter_opencalc2sheets.json",
67 registerPlugin<OpenCalcImport>();)
68
OpenCalcPoint(QString const & str)69 OpenCalcImport::OpenCalcPoint::OpenCalcPoint(QString const & str)
70 : isRange(false)
71 {
72 bool inQuote = false;
73
74 int l = str.length();
75 int colonPos = -1;
76 QString range;
77
78 // replace '.' with '!'
79 for (int i = 0; i < l; ++i) {
80 if (str[i] == '$')
81 continue;
82 if (str[i] == '\'') {
83 inQuote = !inQuote;
84 } else if (str[i] == '.') {
85 if (!inQuote) {
86 if (i != 0 && i != (colonPos + 1)) // no empty table names
87 range += '!';
88 } else
89 range += '.';
90 } else if (str[i] == ':') {
91 if (!inQuote) {
92 isRange = true;
93 colonPos = i;
94 }
95 range += ':';
96 } else
97 range += str[i];
98 }
99
100 translation = range;
101
102 const Calligra::Sheets::Region region(range);
103 table = region.firstSheet()->sheetName();
104 topLeft = region.firstRange().topLeft();
105 botRight = region.firstRange().bottomRight();
106 }
107
108
OpenCalcImport(QObject * parent,const QVariantList &)109 OpenCalcImport::OpenCalcImport(QObject* parent, const QVariantList &)
110 : KoFilter(parent)
111 {
112 }
113
~OpenCalcImport()114 OpenCalcImport::~OpenCalcImport()
115 {
116 foreach(KoXmlElement* style, m_styles) delete style;
117 foreach(Calligra::Sheets::Style* style, m_defaultStyles) delete style;
118 foreach(QString* format, m_formats) delete format;
119 }
120
timeToNum(int h,int m,int s)121 double timeToNum(int h, int m, int s)
122 {
123 int secs = h * 3600 + m * 60 + s;
124 return (double) secs / (double) SECSPERDAY;
125 }
126
readRowFormat(KoXmlElement & rowNode,KoXmlElement * rowStyle,Sheet * table,int & row,int & number,bool isLast)127 bool OpenCalcImport::readRowFormat(KoXmlElement & rowNode, KoXmlElement * rowStyle,
128 Sheet * table, int & row, int & number,
129 bool isLast)
130 {
131 if (rowNode.isNull())
132 return false;
133
134 KoXmlNode node;
135 if (rowStyle) {
136 node = rowStyle->firstChild();
137 kDebug(30518) << "RowStyle:" << rowStyle << "," << rowStyle->tagName();
138 }
139
140 double height = -1.0;
141 bool insertPageBreak = false;
142 Style layout;
143
144 while (!node.isNull()) {
145 KoXmlElement property = node.toElement();
146
147 kDebug(30518) << "Row: Child exists:" << property.tagName();
148 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) {
149 if (property.hasAttributeNS(ooNS::style, "row-height")) {
150 height = KoUnit::parseValue(property.attributeNS(ooNS::style, "row-height", QString()) , -1);
151 }
152
153 if (property.hasAttributeNS(ooNS::fo, "break-before")) {
154 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") {
155 insertPageBreak = true;
156 }
157 }
158
159 loadStyleProperties(&layout, property);
160 }
161
162 node = node.nextSibling();
163 }
164
165 if (rowNode.hasAttributeNS(ooNS::table, "number-rows-repeated")) {
166 bool ok = true;
167 int n = rowNode.attributeNS(ooNS::table, "number-rows-repeated", QString()).toInt(&ok);
168 if (ok)
169 number = n;
170 kDebug(30518) << "Row repeated:" << number;
171 }
172
173 if (isLast) {
174 if (number > 30)
175 number = 30;
176 } else {
177 if (number > 256)
178 number = 256;
179 }
180
181 if (height != -1)
182 table->rowFormats()->setRowHeight(row, row+number-1, height);
183 for (int i = 0; i < number; ++i) {
184 table->cellStorage()->setStyle(Calligra::Sheets::Region(QRect(1, row, KS_colMax, 1)), layout);
185
186
187 Q_UNUSED(insertPageBreak); //for now, as long as below code is commented.
188 // if ( insertPageBreak ) TODO:
189 // rowL->setPageBreak( true )
190
191 // kDebug(30518) <<"Added RowFormat:" << row;
192 ++row;
193 }
194
195 return true;
196 }
197
translatePar(QString & par) const198 QString OpenCalcImport::translatePar(QString & par) const
199 {
200 OpenCalcPoint point(par);
201 kDebug(30518) << " Parameter:" << par << ", Translation:" << point.translation;
202
203 return point.translation;
204 }
205
checkForNamedAreas(QString & formula) const206 void OpenCalcImport::checkForNamedAreas(QString & formula) const
207 {
208 int l = formula.length();
209 int i = 0;
210 QString word;
211 int start = 0;
212 while (i < l) {
213 if (formula[i].isLetterOrNumber()) {
214 word += formula[i];
215 ++i;
216 continue;
217 }
218 if (word.length() > 0) {
219 if (m_namedAreas.contains(word)) {
220 formula.replace(start, word.length(), '\'' + word + '\'');
221 l = formula.length();
222 ++i;
223 kDebug(30518) << "Formula:" << formula << ", L:" << l << ", i:" << i + 1;
224 }
225 }
226
227 ++i;
228 word = "";
229 start = i;
230 }
231 if (word.length() > 0) {
232 if (m_namedAreas.contains(word)) {
233 formula.replace(start, word.length(), '\'' + word + '\'');
234 l = formula.length();
235 ++i;
236 kDebug(30518) << "Formula:" << formula << ", L:" << l << ", i:" << i + 1;
237 }
238 }
239 }
240
convertFormula(QString & text,QString const & f) const241 void OpenCalcImport::convertFormula(QString & text, QString const & f) const
242 {
243 // TODO Stefan: Check if Oasis::decodeFormula could be used instead
244 kDebug(30518) << "Parsing formula:" << f;
245
246 QString formula;
247 QString parameter;
248
249 int l = f.length();
250 int p = 0;
251
252 while (p < l) {
253 if (f[p] == '(' || f[p] == '[') {
254 break;
255 }
256
257 formula += f[p];
258 ++p;
259 }
260
261 if (parameter.isEmpty()) {
262 checkForNamedAreas(formula);
263 }
264
265 kDebug(30518) << "Formula:" << formula << ", Parameter:" << parameter << ", P:" << p;
266
267
268 // replace formula names here
269 if (formula == "=MULTIPLE.OPERATIONS")
270 formula = "=MULTIPLEOPERATIONS";
271
272 QString par;
273 bool isPar = false;
274 bool inQuote = false;
275
276 while (p < l) {
277 if (f[p] == '"') {
278 inQuote = !inQuote;
279 parameter += '"';
280 } else if (f[p] == '[') {
281 if (!inQuote)
282 isPar = true;
283 else
284 parameter += '[';
285 } else if (f[p] == ']') {
286 if (inQuote) {
287 parameter += ']';
288 continue;
289 }
290
291 isPar = false;
292 parameter += translatePar(par);
293 par.clear();
294 } else if (isPar) {
295 par += f[p];
296 } else if (f[p] == '=') { // TODO: check if StarCalc has a '==' sometimes
297 if (inQuote)
298 parameter += '=';
299 else
300 parameter += "==";
301 } else if (f[p] == ')') {
302 if (!inQuote)
303 parameter += ')';
304 } else
305 parameter += f[p];
306
307 ++p;
308 if (p == l)
309 checkForNamedAreas(parameter);
310 }
311
312 text = formula + parameter;
313 kDebug(30518) << "New formula:" << text;
314 }
315
readCells(KoXmlElement & rowNode,Sheet * table,int row,int & columns)316 bool OpenCalcImport::readCells(KoXmlElement & rowNode, Sheet * table, int row, int & columns)
317 {
318 ValueParser *const parser = table->map()->parser();
319
320 bool ok = true;
321 int spanC = 1;
322 int spanR = 1;
323 //Cell* defCell = table->defaultCell();
324
325 KoXmlNode cellNode = KoXml::namedItemNS(rowNode, ooNS::table, "table-cell");
326
327 while (!cellNode.isNull()) {
328 spanR = 1; spanC = 1;
329
330 KoXmlElement e = cellNode.toElement();
331 if (e.isNull()) {
332 ++columns;
333
334 cellNode = cellNode.nextSibling();
335 continue;
336 }
337
338 Cell cell;
339
340 kDebug(30518) << " Cell:" << columns << "," << row;
341
342 // ="3" table:number-rows-spanned="1"
343 if (e.hasAttributeNS(ooNS::table, "number-columns-spanned")) {
344 int span = e.attributeNS(ooNS::table, "number-columns-spanned", QString()).toInt(&ok);
345 if (ok)
346 spanC = span;
347 }
348 if (e.hasAttributeNS(ooNS::table, "number-rows-spanned")) {
349 int span = e.attributeNS(ooNS::table, "number-rows-spanned", QString()).toInt(&ok);
350 if (ok)
351 spanR = span;
352 }
353
354 QString text;
355 KoXmlElement textP = KoXml::namedItemNS(e, ooNS::text, "p");
356 if (!textP.isNull()) {
357 KoXmlElement subText = textP.firstChild().toElement(); // ## wrong
358 if (!subText.isNull()) {
359 // something in <text:p>, e.g. links
360 text = subText.text();
361
362 if (subText.hasAttributeNS(ooNS::xlink, "href")) {
363 QString link = subText.attributeNS(ooNS::xlink, "href", QString());
364 if (link[0] == '#')
365 link = link.remove(0, 1);
366 if (!cell)
367 cell = Cell(table, columns, row);
368 cell.setLink(link);
369 }
370 } else
371 text = textP.text(); // our text, could contain formatting for value or result of formula
372 }
373 KoXmlElement annotation = KoXml::namedItemNS(e, ooNS::office, "annotation");
374 if (!annotation.isNull()) {
375 QString comment;
376 KoXmlNode node = annotation.firstChild();
377 while (!node.isNull()) {
378 KoXmlElement commentElement = node.toElement();
379 if (!commentElement.isNull())
380 if (commentElement.localName() == "p" && e.namespaceURI() == ooNS::text) {
381 if (!comment.isEmpty()) comment.append('\n');
382 comment.append(commentElement.text());
383 }
384
385 node = node.nextSibling();
386 }
387
388 if (!comment.isEmpty()) {
389 kDebug(30518) << " columns :" << columns << " row :" << row;
390 Cell(table, columns, row).setComment(comment);
391 }
392 }
393
394 kDebug(30518) << "Contains:" << text;
395 bool isFormula = false;
396
397 if (e.hasAttributeNS(ooNS::table, "style-name")) {
398 if (!cell)
399 cell = Cell(table, columns, row);
400 Style style = cell.style();
401
402 QString psName("Default");
403 if (e.hasAttributeNS(ooNS::style, "parent-style-name"))
404 psName = e.attributeNS(ooNS::style, "parent-style-name", QString());
405
406 kDebug(30518) << "Default style:" << psName;
407 Style * layout = m_defaultStyles[psName];
408
409 if (layout)
410 style = *layout;
411
412 KoXmlElement * st = 0;
413 if (e.hasAttributeNS(ooNS::table, "style-name")) {
414 kDebug(30518) << "Style:" << e.attributeNS(ooNS::table, "style-name", QString());
415 st = m_styles[ e.attributeNS(ooNS::table, "style-name", QString())];
416 }
417 if (st) {
418 kDebug(30518) << "Style: adapting";
419 KoXmlNode node = st->firstChild();
420 bool foundValidation = false;
421 while (!node.isNull()) {
422 KoXmlElement property = node.toElement();
423 if (!property.isNull()) {
424 kDebug(30518) << "property.tagName() :" << property.tagName();
425 if (property.localName() == "map" && property.namespaceURI() == ooNS::style && !foundValidation) {
426 loadCondition(cell, property);
427 foundValidation = true;
428 }
429 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) {
430 loadStyleProperties(&style, property);
431 if (style.angle() != 0) {
432 QFontMetrics fm(style.font());
433 int tmpAngle = style.angle();
434 int textHeight = static_cast<int>(cos(tmpAngle * M_PI / 180)
435 * (fm.ascent() + fm.descent())
436 + abs((int)(fm.width(cell.displayText())
437 * sin(tmpAngle * M_PI / 180))));
438 /*
439 int textWidth = static_cast<int>( abs ( ( int ) ( sin( tmpAngle * M_PI / 180 )
440 * ( fm.ascent() + fm.descent() ) ) )
441 + fm.width( cell.displayText() )
442 * cos( tmpAngle * M_PI / 180 ) );
443 */
444 kDebug(30518) << "Rotation: height:" << textHeight;
445
446 if (table->rowFormats()->rowHeight(row) < textHeight)
447 table->rowFormats()->setRowHeight(row, row, textHeight + 2);
448 }
449 }
450 }
451 node = node.nextSibling();
452 }
453 }
454 } else {
455 QString psName("Default");
456 kDebug(30518) << "Default style:" << psName;
457 Style * layout = m_defaultStyles[psName];
458
459 if (layout)
460 table->cellStorage()->setStyle(Calligra::Sheets::Region(QPoint(columns, row)), *layout);
461 }
462 if (e.hasAttributeNS(ooNS::table, "formula")) {
463 isFormula = true;
464 QString formula;
465 convertFormula(formula, e.attributeNS(ooNS::table, "formula", QString()));
466
467 if (!cell)
468 cell = Cell(table, columns, row);
469 cell.setUserInput(formula);
470 }
471 if (e.hasAttributeNS(ooNS::table, "validation-name")) {
472 kDebug(30518) << " Celle has a validation :" << e.attributeNS(ooNS::table, "validation-name", QString());
473 loadOasisValidation(Cell(table, columns, row).validity(), e.attributeNS(ooNS::table, "validation-name", QString()), parser);
474 }
475 if (e.hasAttributeNS(ooNS::table, "value-type")) {
476 if (!cell)
477 cell = Cell(table, columns, row);
478 Style style;
479
480 cell.setUserInput(text);
481
482 QString value = e.attributeNS(ooNS::table, "value", QString());
483 QString type = e.attributeNS(ooNS::table, "value-type", QString());
484
485 kDebug(30518) << "Value:" << value << ", type:" << type;
486
487 bool ok = false;
488 double dv = 0.0;
489
490 if ((type == "float") || (type == "currency")) {
491 dv = value.toDouble(&ok);
492 if (ok) {
493 if (!isFormula)
494 cell.setValue(Value(dv));
495
496 if (type == "currency") {
497 Currency currency(e.attributeNS(ooNS::table, "currency", QString()));
498 style.setCurrency(currency);
499 style.setFormatType(Format::Money);
500 }
501 }
502 } else if (type == "percentage") {
503 dv = value.toDouble(&ok);
504 if (ok) {
505 if (!isFormula)
506 cell.setValue(Value(dv));
507 //TODO fixme
508 //cell.setFactor( 100 );
509 // TODO: replace with custom...
510 style.setFormatType(Format::Percentage);
511 }
512 } else if (type == "boolean") {
513 if (value.isEmpty())
514 value = e.attributeNS(ooNS::table, "boolean-value", QString());
515
516 kDebug(30518) << "Type: boolean";
517 if (value == "true")
518 cell.setValue(Value(true));
519 else
520 cell.setValue(Value(false));
521 ok = true;
522 style.setFormatType(Format::Custom);
523 } else if (type == "date") {
524 if (value.isEmpty())
525 value = e.attributeNS(ooNS::table, "date-value", QString());
526 kDebug(30518) << "Type: date, value:" << value;
527
528 // "1980-10-15"
529 int year = 0, month = 0, day = 0;
530 ok = false;
531
532 int p1 = value.indexOf('-');
533 if (p1 > 0)
534 year = value.left(p1).toInt(&ok);
535
536 kDebug(30518) << "year:" << value.left(p1);
537
538 int p2 = value.indexOf('-', ++p1);
539
540 if (ok)
541 month = value.mid(p1, p2 - p1).toInt(&ok);
542
543 kDebug(30518) << "month:" << value.mid(p1, p2 - p1);
544
545 if (ok)
546 day = value.right(value.length() - p2 - 1).toInt(&ok);
547
548 kDebug(30518) << "day:" << value.right(value.length() - p2);
549
550 if (ok) {
551 QDateTime dt(QDate(year, month, day));
552 // KSpreadValue kval( dt );
553 // cell.setValue( kval );
554 cell.setValue(Value(QDate(year, month, day), cell.sheet()->map()->calculationSettings()));
555 kDebug(30518) << "Set QDate:" << year << " -" << month << " -" << day;
556 }
557 } else if (type == "time") {
558 if (value.isEmpty())
559 value = e.attributeNS(ooNS::table, "time-value", QString());
560
561 kDebug(30518) << "Type: time:" << value;
562 // "PT15H10M12S"
563 int hours = 0, minutes = 0, seconds = 0;
564 int l = value.length();
565 QString num;
566
567 for (int i = 0; i < l; ++i) {
568 if (value[i].isNumber()) {
569 num += value[i];
570 continue;
571 } else if (value[i] == 'H')
572 hours = num.toInt(&ok);
573 else if (value[i] == 'M')
574 minutes = num.toInt(&ok);
575 else if (value[i] == 'S')
576 seconds = num.toInt(&ok);
577 else
578 continue;
579
580 kDebug(30518) << "Num:" << num;
581
582 num.clear();
583 if (!ok)
584 break;
585 }
586
587 kDebug(30518) << "Hours:" << hours << "," << minutes << "," << seconds;
588
589 if (ok) {
590 // KSpreadValue kval( timeToNum( hours, minutes, seconds ) );
591 // cell.setValue( kval );
592 cell.setValue(Value(QTime(hours % 24, minutes, seconds)));
593 style.setFormatType(Format::Custom);
594 }
595 }
596
597 cell.setStyle(style);
598 if (!ok) // just in case we couldn't set the value directly
599 cell.parseUserInput(text);
600 } else if (!text.isEmpty()) {
601 if (!cell)
602 cell = Cell(table, columns, row);
603 cell.parseUserInput(text);
604 }
605
606 if (spanR > 1 || spanC > 1) {
607 if (!cell)
608 cell = Cell(table, columns, row);
609 cell.mergeCells(columns, row, spanC - 1, spanR - 1);
610 }
611
612 cellNode = cellNode.nextSibling();
613
614 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) {
615 // copy cell from left
616 bool ok = false;
617 int number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok);
618 Cell cellDest;
619
620 // don't repeat more than 10 if it is the last cell and empty
621 if (!ok || cellNode.isNull()) {
622 if (number > 10)
623 number = 10;
624 }
625
626 for (int i = 1; i < number; ++i) {
627 ++columns;
628
629 if (!cell.isNull()) {
630 cellDest = Cell(table, columns, row);
631 cellDest.copyAll(cell);
632 }
633 }
634 }
635
636 ++columns;
637 }
638
639 return true;
640 }
641
642
loadCondition(const Cell & cell,const KoXmlElement & property)643 void OpenCalcImport::loadCondition(const Cell& cell, const KoXmlElement &property)
644 {
645 kDebug(30518) << "void OpenCalcImport::loadCondition( Cell*cell,const KoXmlElement &property )*******";
646 loadOasisCondition(cell, property);
647 }
648
loadOasisCondition(const Cell & cell,const KoXmlElement & property)649 void OpenCalcImport::loadOasisCondition(const Cell& cell, const KoXmlElement &property)
650 {
651 KoXmlElement elementItem(property);
652 Map *const map = cell.sheet()->map();
653 ValueParser *const parser = map->parser();
654
655 QLinkedList<Conditional> cond;
656 while (!elementItem.isNull()) {
657 kDebug(30518) << "elementItem.tagName() :" << elementItem.tagName();
658
659 if (elementItem.localName() == "map" && property.namespaceURI() == ooNS::style) {
660 bool ok = true;
661 kDebug(30518) << "elementItem.attribute(style:condition ) :" << elementItem.attributeNS(ooNS::style, "condition", QString());
662 Conditional newCondition;
663 loadOasisConditionValue(elementItem.attributeNS(ooNS::style, "condition", QString()), newCondition, parser);
664 if (elementItem.hasAttributeNS(ooNS::style, "apply-style-name")) {
665 kDebug(30518) << "elementItem.attribute( style:apply-style-name ) :" << elementItem.attributeNS(ooNS::style, "apply-style-name", QString());
666 newCondition.styleName = elementItem.attributeNS(ooNS::style, "apply-style-name", QString());
667 ok = !newCondition.styleName.isEmpty();
668 }
669
670 if (ok)
671 cond.append(newCondition);
672 else
673 kDebug(30518) << "Error loading condition" << elementItem.nodeName();
674 }
675 elementItem = elementItem.nextSibling().toElement();
676 }
677 if (!cond.isEmpty()) {
678 Conditions conditions;
679 conditions.setConditionList(cond);
680 Cell(cell).setConditions(conditions);
681 }
682 }
683
loadOasisConditionValue(const QString & styleCondition,Conditional & newCondition,const ValueParser * parser)684 void OpenCalcImport::loadOasisConditionValue(const QString &styleCondition, Conditional &newCondition,
685 const ValueParser *parser)
686 {
687 QString val(styleCondition);
688 if (val.contains("cell-content()")) {
689 val = val.remove("cell-content()");
690 loadOasisCondition(val, newCondition, parser);
691 }
692 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value)
693 //for the moment we support just int/double value, not text/date/time :(
694 if (val.contains("cell-content-is-between(")) {
695 val.remove("cell-content-is-between(");
696 val.remove(')');
697 QStringList listVal = val.split(',');
698 kDebug(30518) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1];
699 newCondition.value1 = parser->parse(listVal[0]);
700 newCondition.value2 = parser->parse(listVal[1]);
701 newCondition.cond = Conditional::Between;
702 }
703 if (val.contains("cell-content-is-not-between(")) {
704 val.remove("cell-content-is-not-between(");
705 val.remove(')');
706 QStringList listVal = val.split(',');
707 kDebug(30518) << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1];
708 newCondition.value1 = parser->parse(listVal[0]);
709 newCondition.value2 = parser->parse(listVal[1]);
710 newCondition.cond = Conditional::Different;
711 }
712
713 }
714
715
loadOasisCondition(QString & valExpression,Conditional & newCondition,const ValueParser * parser)716 void OpenCalcImport::loadOasisCondition(QString &valExpression, Conditional &newCondition,
717 const ValueParser *parser)
718 {
719 QString value;
720 if (valExpression.indexOf("<=") == 0) {
721 value = valExpression.remove(0, 2);
722 newCondition.cond = Conditional::InferiorEqual;
723 } else if (valExpression.indexOf(">=") == 0) {
724 value = valExpression.remove(0, 2);
725 newCondition.cond = Conditional::SuperiorEqual;
726 } else if (valExpression.indexOf("!=") == 0) {
727 //add Differentto attribute
728 value = valExpression.remove(0, 2);
729 newCondition.cond = Conditional::DifferentTo;
730 } else if (valExpression.indexOf('<') == 0) {
731 value = valExpression.remove(0, 1);
732 newCondition.cond = Conditional::Inferior;
733 } else if (valExpression.indexOf('>') == 0) {
734 value = valExpression.remove(0, 1);
735 newCondition.cond = Conditional::Superior;
736 } else if (valExpression.indexOf('=') == 0) {
737 value = valExpression.remove(0, 1);
738 newCondition.cond = Conditional::Equal;
739 } else
740 kDebug(30518) << " I don't know how to parse it :" << valExpression;
741 kDebug(30518) << " value :" << value;
742 newCondition.value1 = parser->parse(value);
743 }
744
readRowsAndCells(KoXmlElement & content,Sheet * table)745 bool OpenCalcImport::readRowsAndCells(KoXmlElement & content, Sheet * table)
746 {
747 kDebug(30518) << "Reading in rows";
748
749 int i = 1;
750 int row = 1;
751 int columns = 1;
752 int backupRow = 1;
753 KoXmlElement * rowStyle = 0;
754 //Cell cell;
755 //Cell cellDest;
756 //Cell defCell = table->defaultCell();
757 KoXmlNode rowNode = KoXml::namedItemNS(content, ooNS::table, "table-row");
758
759 while (!rowNode.isNull()) {
760 bool collapsed = false;
761
762 int number = 1;
763 KoXmlElement r = rowNode.toElement();
764
765 if (r.isNull())
766 return false;
767
768 if (r.hasAttributeNS(ooNS::table, "style-name")) {
769 QString style = r.attributeNS(ooNS::table, "style-name", QString());
770 rowStyle = m_styles[ style ];
771 kDebug(30518) << "Row style:" << style;
772 }
773
774 collapsed = (r.attributeNS(ooNS::table, "visibility", QString()) == "collapse");
775
776 backupRow = row;
777
778 rowNode = rowNode.nextSibling();
779
780 if (!readRowFormat(r, rowStyle, table, row, number, rowNode.isNull())) // updates "row"
781 return false;
782
783 if (!readCells(r, table, backupRow, columns))
784 return false;
785
786 // RowFormat * srcLayout = table->nonDefaultRowFormat(backupRow);
787 // RowFormat * layout = 0;
788
789 if (collapsed)
790 table->rowFormats()->setHidden(backupRow, backupRow, true);
791
792 for (i = 1; i < number; ++i) {
793 // FIXME CALLIGRA_SHEETS_NEW_STYLE_STORAGE
794 // layout = table->nonDefaultRowFormat( backupRow + i );
795 //
796 // table->setStyle( Calligra::Sheets::Region(QRect(1,backupRow + i,KS_colMax,1)), srcLayout );
797
798 /*
799 * TODO: Test: do we need to copy the cells, too?
800 * if so we will probably also need to copy them for repeated col layouts.
801 for ( j = 1; j <= columns; ++j )
802 {
803 Cell cell( table, j, backupRow );
804
805 kDebug(30518) <<"Cell:" << cell <<"DefCell:" << defCell;
806 if ( cell && (cell != defCell) )
807 {
808 cellDest = Cell( table, j, backupRow + i );
809 cellDest->copyAll( cell );
810 }
811 }
812 */
813 }
814
815 rowStyle = 0;
816 columns = 1;
817 }
818
819 kDebug(30518) << "Reading in rows done";
820
821 return true;
822 }
823
readColLayouts(KoXmlElement & content,Sheet * table)824 bool OpenCalcImport::readColLayouts(KoXmlElement & content, Sheet * table)
825 {
826 kDebug(30518) << "Reading in columns...";
827
828 KoXmlNode colLayout = KoXml::namedItemNS(content, ooNS::table, "table-column");
829 int column = 1;
830
831 while (!colLayout.isNull()) {
832 if (colLayout.nodeName() != "table:table-column")
833 return true; // all cols read in.
834
835 KoXmlElement e = colLayout.toElement();
836
837 if (e.isNull())
838 return false; // error, that's it...
839
840 kDebug(30518) << "New column:" << column;
841
842 int number = 1;
843 double width = -1.0;
844 bool collapsed = (e.attributeNS(ooNS::table, "visibility", QString()) == "collapse");
845 bool insertPageBreak = false;
846 Style styleLayout;
847
848 kDebug(30518) << "Check table:number-columns-repeated";
849 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) {
850 bool ok = true;
851 number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok);
852 if (!ok)
853 number = 1;
854
855 kDebug(30518) << "Repeated:" << number;
856 }
857
858 kDebug(30518) << "Checking table:default-cell-style-name";
859 if (e.hasAttributeNS(ooNS::table, "default-cell-style-name")) {
860 QString n(e.attributeNS(ooNS::table, "default-cell-style-name", QString()));
861 kDebug(30518) << "Has attribute default-cell-style-name:" << n;
862 Style * defaultStyle = m_defaultStyles[ n ];
863 if (!defaultStyle) {
864 QString name = e.attributeNS(ooNS::table, "default-cell-style-name", QString());
865 KoXmlElement * st = m_styles[ name ];
866
867 kDebug(30518) << "Default cell style:" << name;
868
869 if (st && !st->isNull()) {
870 Style * layout = new Style();
871
872 readInStyle(layout, *st);
873
874 m_defaultStyles.insert(name, layout);
875 kDebug(30518) << "Insert default cell style:" << name;
876
877 defaultStyle = layout;
878 }
879 }
880
881 if (defaultStyle) {
882 // kDebug(30518) <<"Copying default style, Font:" << defaultStyle->font().toString();
883 styleLayout = *defaultStyle;
884 }
885 }
886
887 KoXmlElement * colStyle = 0;
888 if (e.hasAttributeNS(ooNS::table, "style-name")) {
889 QString style = e.attributeNS(ooNS::table, "style-name", QString());
890 colStyle = m_styles[ style ];
891
892 kDebug(30518) << "Col Style:" << style;
893 }
894
895 KoXmlNode node;
896
897 if (colStyle)
898 node = colStyle->firstChild();
899
900 while (!node.isNull()) {
901 KoXmlElement property = node.toElement();
902 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) {
903 if (property.hasAttributeNS(ooNS::style, "column-width")) {
904 QString sWidth = property.attributeNS(ooNS::style, "column-width", QString());
905 width = KoUnit::parseValue(property.attributeNS(ooNS::style, "column-width", QString()), width);
906 kDebug(30518) << "Col Width:" << sWidth;
907 }
908
909 if (property.hasAttributeNS(ooNS::fo, "break-before")) {
910 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") {
911 insertPageBreak = true;
912 }
913 }
914
915 loadStyleProperties(&styleLayout, property);
916 }
917
918 node = node.nextSibling();
919 }
920
921 colLayout = colLayout.nextSibling();
922
923 if (colLayout.isNull() && (number > 30))
924 number = 30;
925
926 for (int i = 0; i < number; ++i) {
927 kDebug(30518) << "Inserting colLayout:" << column;
928
929 ColumnFormat * col = new ColumnFormat();
930 col->setSheet(table);
931 col->setColumn(column);
932 table->cellStorage()->setStyle(Calligra::Sheets::Region(QRect(column, 1, 1, KS_rowMax)), styleLayout);
933 if (width != -1.0)
934 col->setWidth(width);
935
936 Q_UNUSED(insertPageBreak); //TODO
937 // if ( insertPageBreak )
938 // col->setPageBreak( true )
939
940 if (collapsed)
941 col->setHidden(true);
942
943 table->insertColumnFormat(col);
944 ++column;
945 }
946 }
947
948 return true;
949 }
950
replaceMacro(QString & text,QString const & old,QString const & newS)951 void replaceMacro(QString & text, QString const & old, QString const & newS)
952 {
953 int n = text.indexOf(old);
954 if (n != -1)
955 text.replace(n, old.length(), newS);
956 }
957
getPart(KoXmlNode const & part)958 QString getPart(KoXmlNode const & part)
959 {
960 QString result;
961 KoXmlElement e = KoXml::namedItemNS(part, ooNS::text, "p");
962 while (!e.isNull()) {
963 QString text = e.text();
964 kDebug(30518) << "PART:" << text;
965
966 KoXmlElement macro = KoXml::namedItemNS(e, ooNS::text, "time");
967 if (!macro.isNull())
968 replaceMacro(text, macro.text(), "<time>");
969
970 macro = KoXml::namedItemNS(e, ooNS::text, "date");
971 if (!macro.isNull())
972 replaceMacro(text, macro.text(), "<date>");
973
974 macro = KoXml::namedItemNS(e, ooNS::text, "page-number");
975 if (!macro.isNull())
976 replaceMacro(text, macro.text(), "<page>");
977
978 macro = KoXml::namedItemNS(e, ooNS::text, "page-count");
979 if (!macro.isNull())
980 replaceMacro(text, macro.text(), "<pages>");
981
982 macro = KoXml::namedItemNS(e, ooNS::text, "sheet-name");
983 if (!macro.isNull())
984 replaceMacro(text, macro.text(), "<sheet>");
985
986 macro = KoXml::namedItemNS(e, ooNS::text, "title");
987 if (!macro.isNull())
988 replaceMacro(text, macro.text(), "<name>");
989
990 macro = KoXml::namedItemNS(e, ooNS::text, "file-name");
991 if (!macro.isNull())
992 replaceMacro(text, macro.text(), "<file>");
993
994 if (!result.isEmpty())
995 result += '\n';
996 result += text;
997 e = e.nextSibling().toElement();
998 }
999
1000 return result;
1001 }
1002
loadTableMasterStyle(Sheet * table,QString const & stylename)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
loadOasisMasterLayoutPage(Sheet * table,KoStyleStack & styleStack)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
parseBody(int numOfTables)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
insertStyles(KoXmlElement const & element)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
loadOasisAreaName(const KoXmlElement & body)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
loadOasisCellValidation(const KoXmlElement & body,const ValueParser * parser)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
loadFormat(KoXmlElement * element,Format::Type & formatType,QString name)1409 QString * OpenCalcImport::loadFormat(KoXmlElement * element,
1410 Format::Type & formatType,
1411 QString name)
1412 {
1413 if (!element)
1414 return 0;
1415
1416 int i;
1417 bool ok;
1418
1419 QString * format = 0;
1420 KoXmlElement e = element->firstChild().toElement();
1421 int precision = 0;
1422 int leadingZ = 1;
1423 #ifdef __GNUC__
1424 #warning (coverity) what is the purpose of this constant?
1425 #endif
1426 bool thousandsSep = false;
1427 bool negRed = false;
1428
1429 if (element->localName() == "time-style")
1430 formatType = Format::Custom;
1431 else if (element->localName() == "date-style")
1432 formatType = Format::Custom;
1433 else if (element->localName() == "percentage-style")
1434 formatType = Format::Custom;
1435 else if (element->localName() == "number-style")
1436 formatType = Format::Custom;
1437 else if (element->localName() == "currency-style")
1438 formatType = Format::Custom;
1439 else if (element->localName() == "boolean-style")
1440 formatType = Format::Custom;
1441
1442 if (!e.isNull())
1443 format = new QString();
1444
1445 // TODO (element):
1446 // number:automatic-order="true"
1447 // number:truncate-on-overflow="false"
1448 // style:volatile="true"
1449
1450 while (!e.isNull()) {
1451 if (e.localName() == "properties" && e.namespaceURI() == ooNS::style) {
1452 if (e.hasAttributeNS(ooNS::fo, "color"))
1453 negRed = true; // we only support red...
1454 } else if (e.localName() == "text" && e.namespaceURI() == ooNS::number) {
1455 if (negRed && (e.text() == "-"))
1456 ;
1457 else
1458 format->append(e.text());
1459 } else if (e.localName() == "currency-symbol" && e.namespaceURI() == ooNS::number) {
1460 QString sym(e.text());
1461 kDebug(30518) << "Currency:" << sym;
1462 format->append(sym);
1463 // number:language="de" number:country="DE">€</number:currency-symbol>
1464 } else if (e.localName() == "day-of-week" && e.namespaceURI() == ooNS::number) {
1465 if (e.hasAttributeNS(ooNS::number, "style")) {
1466 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1467 format->append("dddd");
1468 else
1469 format->append("ddd");
1470 } else
1471 format->append("ddd");
1472 } else if (e.localName() == "day" && e.namespaceURI() == ooNS::number) {
1473 if (e.hasAttributeNS(ooNS::number, "style")) {
1474 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1475 format->append("dd");
1476 else
1477 format->append("d");
1478 } else
1479 format->append("d");
1480 } else if (e.localName() == "month" && e.namespaceURI() == ooNS::number) {
1481 if (e.hasAttributeNS(ooNS::number, "textual")) {
1482 if (e.attributeNS(ooNS::number, "textual", QString()) == "true")
1483 format->append("mm");
1484 }
1485
1486 if (e.hasAttributeNS(ooNS::number, "style")) {
1487 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1488 format->append("mm");
1489 else
1490 format->append("m");
1491 } else
1492 format->append("m");
1493 } else if (e.localName() == "year" && e.namespaceURI() == ooNS::number) {
1494 if (e.hasAttributeNS(ooNS::number, "style")) {
1495 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1496 format->append("yyyy");
1497 else
1498 format->append("yy");
1499 } else
1500 format->append("yy");
1501 } else if (e.localName() == "hours" && e.namespaceURI() == ooNS::number) {
1502 if (e.hasAttributeNS(ooNS::number, "style")) {
1503 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1504 format->append("hh");
1505 else
1506 format->append("h");
1507 } else
1508 format->append("h");
1509 } else if (e.localName() == "minutes" && e.namespaceURI() == ooNS::number) {
1510 if (e.hasAttributeNS(ooNS::number, "style")) {
1511 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1512 format->append("mm");
1513 else
1514 format->append("m");
1515 } else
1516 format->append("m");
1517 } else if (e.localName() == "seconds" && e.namespaceURI() == ooNS::number) {
1518 if (e.hasAttributeNS(ooNS::number, "style")) {
1519 if (e.attributeNS(ooNS::number, "style", QString()) == "long")
1520 format->append("ss");
1521 else
1522 format->append("s");
1523 } else
1524 format->append("s");
1525 } else if (e.localName() == "am-pm" && e.namespaceURI() == ooNS::number) {
1526 format->append("AM/PM");
1527 } else if (e.localName() == "number" && e.namespaceURI() == ooNS::number) {
1528 // TODO: number:grouping="true"
1529
1530 if (e.hasAttributeNS(ooNS::number, "decimal-places")) {
1531 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok);
1532 if (ok)
1533 precision = d;
1534 }
1535
1536 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) {
1537 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok);
1538 if (ok)
1539 leadingZ = d;
1540 }
1541
1542 #ifdef __GNUC__
1543 #warning thousandsSep can not be true (CID 3200)
1544 #endif
1545 if (thousandsSep && leadingZ <= 3) {
1546 format->append("#,");
1547 for (i = leadingZ; i <= 3; ++i)
1548 format->append('#');
1549 }
1550
1551 for (i = 1; i <= leadingZ; ++i) {
1552 format->append('0');
1553 if ((i % 3 == 0) && thousandsSep)
1554 format->append(',');
1555 }
1556
1557 format->append('.');
1558 for (i = 0; i < precision; ++i)
1559 format->append('0');
1560 } else if (e.localName() == "scientific-number" && e.namespaceURI() == ooNS::number) {
1561 int exp = 2;
1562
1563 if (e.hasAttributeNS(ooNS::number, "decimal-places")) {
1564 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok);
1565 if (ok)
1566 precision = d;
1567 }
1568
1569 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) {
1570 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok);
1571 if (ok)
1572 leadingZ = d;
1573 }
1574
1575 if (e.hasAttributeNS(ooNS::number, "min-exponent-digits")) {
1576 int d = e.attributeNS(ooNS::number, "min-exponent-digits", QString()).toInt(&ok);
1577 if (ok)
1578 exp = d;
1579 if (exp <= 0)
1580 exp = 1;
1581 }
1582
1583 if (thousandsSep && leadingZ <= 3) {
1584 format->append("#,");
1585 for (i = leadingZ; i <= 3; ++i)
1586 format->append('#');
1587 }
1588
1589 for (i = 1; i <= leadingZ; ++i) {
1590 format->append('0');
1591 if ((i % 3 == 0) && thousandsSep)
1592 format->append(',');
1593 }
1594
1595 format->append('.');
1596 for (i = 0; i < precision; ++i)
1597 format->append('0');
1598
1599 format->append("E+");
1600 for (i = 0; i < exp; ++i)
1601 format->append('0');
1602
1603 formatType = Format::Custom;
1604 } else if (e.localName() == "fraction" && e.namespaceURI() == ooNS::number) {
1605 int integer = 0;
1606 int numerator = 1;
1607 int denominator = 1;
1608
1609 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) {
1610 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok);
1611 if (ok)
1612 integer = d;
1613 }
1614 if (e.hasAttributeNS(ooNS::number, "min-numerator-digits")) {
1615 int d = e.attributeNS(ooNS::number, "min-numerator-digits", QString()).toInt(&ok);
1616 if (ok)
1617 numerator = d;
1618 }
1619 if (e.hasAttributeNS(ooNS::number, "min-denominator-digits")) {
1620 int d = e.attributeNS(ooNS::number, "min-denominator-digits", QString()).toInt(&ok);
1621 if (ok)
1622 denominator = d;
1623 }
1624
1625 for (i = 0; i <= integer; ++i)
1626 format->append('#');
1627
1628 format->append(' ');
1629
1630 for (i = 0; i <= numerator; ++i)
1631 format->append('?');
1632
1633 format->append('/');
1634
1635 for (i = 0; i <= denominator; ++i)
1636 format->append('?');
1637 }
1638 // Not needed:
1639 // <style:map style:condition="value()>=0" style:apply-style-name="N106P0"/>
1640 // we handle painting negative numbers in red differently
1641
1642 e = e.nextSibling().toElement();
1643 }
1644
1645 if (negRed) {
1646 QString f(*format);
1647 format->append(";[Red]");
1648 format->append(f);
1649 }
1650
1651 kDebug(30518) << "*** New FormatString:" << *format;
1652
1653 m_formats.insert(name, format);
1654
1655 return format;
1656 }
1657
loadFontStyle(Style * layout,KoXmlElement const * font) const1658 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
loadBorder(Style * layout,QString const & borderDef,bPos pos) const1690 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
loadStyleProperties(Style * layout,KoXmlElement const & property) const1747 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
readInStyle(Style * layout,KoXmlElement const & style)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
createStyleMap(KoXmlDocument const & styles)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
loadOasisValidation(Validity validity,const QString & validationName,const ValueParser * parser)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
loadOasisValidationValue(Validity validity,const QStringList & listVal,const ValueParser * parser)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
loadOasisValidationCondition(Validity validity,QString & valExpression,const ValueParser * parser)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
readMetaData()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
convert(QByteArray const & from,QByteArray const & to)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
openFile()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
loadAndParse(KoXmlDocument & doc,const QString & fileName,KoStore * m_store)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
2368