1 | /* This file is part of the KDE project |
2 | Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org> |
3 | Copyright (C) 1998, 1999 Torben Weis <weis@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 | // Local |
22 | #include "Map.h" |
23 | |
24 | #include <stdlib.h> |
25 | #include <time.h> |
26 | |
27 | #include <QTimer> |
28 | |
29 | #include <kcodecs.h> |
30 | #include <kcompletion.h> |
31 | #include <ktemporaryfile.h> |
32 | |
33 | #include <KoGenStyles.h> |
34 | #include <KoStyleStack.h> |
35 | #include <KoGlobal.h> |
36 | #include <KoOasisSettings.h> |
37 | #include <KoOdfLoadingContext.h> |
38 | #include <KoOdfStylesReader.h> |
39 | #include <KoEmbeddedDocumentSaver.h> |
40 | #include <KoShapeSavingContext.h> |
41 | #include <KoXmlNS.h> |
42 | #include <KoXmlWriter.h> |
43 | #include <KoStyleManager.h> |
44 | #include <KoShapeLoadingContext.h> |
45 | #include <KoTextSharedLoadingData.h> |
46 | #include <KoParagraphStyle.h> |
47 | #include <KoShapeRegistry.h> |
48 | #include <KoUpdater.h> |
49 | #include <KoProgressUpdater.h> |
50 | |
51 | #include "ApplicationSettings.h" |
52 | #include "BindingManager.h" |
53 | #include "CalculationSettings.h" |
54 | #include "CellStorage.h" |
55 | #include "Damages.h" |
56 | #include "DependencyManager.h" |
57 | #include "DocBase.h" |
58 | #include "LoadingInfo.h" |
59 | #include "Localization.h" |
60 | #include "NamedAreaManager.h" |
61 | #include "OdfLoadingContext.h" |
62 | #include "OdfSavingContext.h" |
63 | #include "RecalcManager.h" |
64 | #include "RowColumnFormat.h" |
65 | #include "Sheet.h" |
66 | #include "StyleManager.h" |
67 | #include "Validity.h" |
68 | #include "ValueCalc.h" |
69 | #include "ValueConverter.h" |
70 | #include "ValueFormatter.h" |
71 | #include "ValueParser.h" |
72 | |
73 | // database |
74 | #include "database/DatabaseManager.h" |
75 | |
76 | using namespace Calligra::Sheets; |
77 | |
78 | class Map::Private |
79 | { |
80 | public: |
81 | DocBase* doc; |
82 | |
83 | /** |
84 | * List of all sheets in this map. |
85 | */ |
86 | QList<Sheet*> lstSheets; |
87 | QList<Sheet*> lstDeletedSheets; |
88 | |
89 | // used to give every Sheet a unique default name. |
90 | int tableId; |
91 | |
92 | // used to determine the loading progress |
93 | int overallRowCount; |
94 | int loadedRowsCounter; |
95 | |
96 | LoadingInfo* loadingInfo; |
97 | bool readwrite; |
98 | |
99 | BindingManager* bindingManager; |
100 | DatabaseManager* databaseManager; |
101 | DependencyManager* dependencyManager; |
102 | NamedAreaManager* namedAreaManager; |
103 | RecalcManager* recalcManager; |
104 | StyleManager* styleManager; |
105 | KoStyleManager* textStyleManager; |
106 | |
107 | ApplicationSettings* applicationSettings; |
108 | CalculationSettings* calculationSettings; |
109 | ValueCalc* calc; |
110 | ValueConverter* converter; |
111 | ValueFormatter* formatter; |
112 | ValueParser* parser; |
113 | |
114 | // default objects |
115 | ColumnFormat* defaultColumnFormat; |
116 | RowFormat* defaultRowFormat; |
117 | |
118 | QList<Damage*> damages; |
119 | bool isLoading; |
120 | |
121 | int syntaxVersion; |
122 | |
123 | KCompletion listCompletion; |
124 | }; |
125 | |
126 | |
127 | Map::Map(DocBase* doc, int syntaxVersion) |
128 | : QObject(doc), |
129 | d(new Private) |
130 | { |
131 | setObjectName(QLatin1String("Map" )); // necessary for D-Bus |
132 | d->doc = doc; |
133 | d->tableId = 1; |
134 | d->overallRowCount = 0; |
135 | d->loadedRowsCounter = 0; |
136 | d->loadingInfo = 0; |
137 | d->readwrite = true; |
138 | |
139 | d->bindingManager = new BindingManager(this); |
140 | d->databaseManager = new DatabaseManager(this); |
141 | d->dependencyManager = new DependencyManager(this); |
142 | d->namedAreaManager = new NamedAreaManager(this); |
143 | d->recalcManager = new RecalcManager(this); |
144 | d->styleManager = new StyleManager(); |
145 | d->textStyleManager = new KoStyleManager(this); |
146 | if (doc) { |
147 | d->textStyleManager->setUndoStack(doc->undoStack()); |
148 | } |
149 | d->applicationSettings = new ApplicationSettings(); |
150 | d->calculationSettings = new CalculationSettings(); |
151 | |
152 | d->parser = new ValueParser(d->calculationSettings); |
153 | d->converter = new ValueConverter(d->parser); |
154 | d->calc = new ValueCalc(d->converter); |
155 | d->formatter = new ValueFormatter(d->converter); |
156 | |
157 | d->defaultColumnFormat = new ColumnFormat(); |
158 | d->defaultRowFormat = new RowFormat(); |
159 | |
160 | QFont font(KoGlobal::defaultFont()); |
161 | d->defaultRowFormat->setHeight(font.pointSizeF() + 4); |
162 | d->defaultColumnFormat->setWidth((font.pointSizeF() + 4) * 5); |
163 | |
164 | d->isLoading = false; |
165 | |
166 | // default document properties |
167 | d->syntaxVersion = syntaxVersion; |
168 | |
169 | connect(this, SIGNAL(sheetAdded(Sheet*)), |
170 | d->dependencyManager, SLOT(addSheet(Sheet*))); |
171 | connect(this, SIGNAL(sheetAdded(Sheet*)), |
172 | d->recalcManager, SLOT(addSheet(Sheet*))); |
173 | connect(this, SIGNAL(sheetRemoved(Sheet*)), |
174 | d->dependencyManager, SLOT(removeSheet(Sheet*))); |
175 | connect(this, SIGNAL(sheetRemoved(Sheet*)), |
176 | d->recalcManager, SLOT(removeSheet(Sheet*))); |
177 | connect(this, SIGNAL(sheetRevived(Sheet*)), |
178 | d->dependencyManager, SLOT(addSheet(Sheet*))); |
179 | connect(this, SIGNAL(sheetRevived(Sheet*)), |
180 | d->recalcManager, SLOT(addSheet(Sheet*))); |
181 | connect(d->namedAreaManager, SIGNAL(namedAreaModified(QString)), |
182 | d->dependencyManager, SLOT(namedAreaModified(QString))); |
183 | connect(this, SIGNAL(damagesFlushed(QList<Damage*>)), |
184 | this, SLOT(handleDamages(QList<Damage*>))); |
185 | } |
186 | |
187 | Map::~Map() |
188 | { |
189 | // Because some of the shapes might be using a sheet in this map, delete |
190 | // all shapes in each sheet before all sheets are deleted together. |
191 | foreach(Sheet *sheet, d->lstSheets) |
192 | sheet->deleteShapes(); |
193 | // we have to explicitly delete the Sheets, not let QObject take care of that |
194 | // as the sheet in its destructor expects the Map to still exist |
195 | qDeleteAll(d->lstSheets); |
196 | d->lstSheets.clear(); |
197 | |
198 | deleteLoadingInfo(); |
199 | |
200 | delete d->bindingManager; |
201 | delete d->databaseManager; |
202 | delete d->dependencyManager; |
203 | delete d->namedAreaManager; |
204 | delete d->recalcManager; |
205 | delete d->styleManager; |
206 | |
207 | delete d->parser; |
208 | delete d->formatter; |
209 | delete d->converter; |
210 | delete d->calc; |
211 | delete d->calculationSettings; |
212 | delete d->applicationSettings; |
213 | |
214 | delete d->defaultColumnFormat; |
215 | delete d->defaultRowFormat; |
216 | |
217 | delete d; |
218 | } |
219 | |
220 | DocBase* Map::doc() const |
221 | { |
222 | return d->doc; |
223 | } |
224 | |
225 | void Map::setReadWrite(bool readwrite) |
226 | { |
227 | d->readwrite = readwrite; |
228 | } |
229 | |
230 | bool Map::isReadWrite() const |
231 | { |
232 | return d->readwrite; |
233 | } |
234 | |
235 | bool Map::completeLoading(KoStore *store) |
236 | { |
237 | Q_UNUSED(store); |
238 | |
239 | QPointer<KoUpdater> dependencyUpdater, recalcUpdater; |
240 | if (doc() && doc()->progressUpdater()) { |
241 | dependencyUpdater = doc()->progressUpdater()->startSubtask(1, "Calligra::Sheets::DependencyManager::updateAllDependencies" ); |
242 | recalcUpdater = doc()->progressUpdater()->startSubtask(1, "Calligra::Sheets::RecalcManager::recalc" ); |
243 | } |
244 | |
245 | // Initial build of all cell dependencies. |
246 | d->dependencyManager->updateAllDependencies(this, dependencyUpdater); |
247 | // Recalc the whole workbook now, since there may be formulas other spreadsheets support, |
248 | // but KSpread does not. |
249 | d->recalcManager->recalcMap(recalcUpdater); |
250 | |
251 | return true; |
252 | } |
253 | |
254 | bool Map::completeSaving(KoStore *store, KoXmlWriter *manifestWriter, KoShapeSavingContext * context) |
255 | { |
256 | Q_UNUSED(store); |
257 | Q_UNUSED(manifestWriter); |
258 | Q_UNUSED(context); |
259 | return true; |
260 | } |
261 | |
262 | BindingManager* Map::bindingManager() const |
263 | { |
264 | return d->bindingManager; |
265 | } |
266 | |
267 | DatabaseManager* Map::databaseManager() const |
268 | { |
269 | return d->databaseManager; |
270 | } |
271 | |
272 | DependencyManager* Map::dependencyManager() const |
273 | { |
274 | return d->dependencyManager; |
275 | } |
276 | |
277 | NamedAreaManager* Map::namedAreaManager() const |
278 | { |
279 | return d->namedAreaManager; |
280 | } |
281 | |
282 | RecalcManager* Map::recalcManager() const |
283 | { |
284 | return d->recalcManager; |
285 | } |
286 | |
287 | StyleManager* Map::styleManager() const |
288 | { |
289 | return d->styleManager; |
290 | } |
291 | |
292 | KoStyleManager* Map::textStyleManager() const |
293 | { |
294 | return d->textStyleManager; |
295 | } |
296 | |
297 | ValueParser* Map::parser() const |
298 | { |
299 | return d->parser; |
300 | } |
301 | |
302 | ValueFormatter* Map::formatter() const |
303 | { |
304 | return d->formatter; |
305 | } |
306 | |
307 | ValueConverter* Map::converter() const |
308 | { |
309 | return d->converter; |
310 | } |
311 | |
312 | ValueCalc* Map::calc() const |
313 | { |
314 | return d->calc; |
315 | } |
316 | |
317 | const ColumnFormat* Map::defaultColumnFormat() const |
318 | { |
319 | return d->defaultColumnFormat; |
320 | } |
321 | |
322 | const RowFormat* Map::defaultRowFormat() const |
323 | { |
324 | return d->defaultRowFormat; |
325 | } |
326 | |
327 | void Map::setDefaultColumnWidth(double width) |
328 | { |
329 | d->defaultColumnFormat->setWidth(width); |
330 | } |
331 | |
332 | void Map::setDefaultRowHeight(double height) |
333 | { |
334 | d->defaultRowFormat->setHeight(height); |
335 | } |
336 | |
337 | ApplicationSettings* Map::settings() const |
338 | { |
339 | return d->applicationSettings; |
340 | } |
341 | |
342 | CalculationSettings* Map::calculationSettings() const |
343 | { |
344 | return d->calculationSettings; |
345 | } |
346 | |
347 | Sheet* Map::createSheet(const QString& name) |
348 | { |
349 | QString sheetName(i18n("Sheet%1" , d->tableId++)); |
350 | if ( !name.isEmpty() ) |
351 | sheetName = name; |
352 | Sheet* sheet = new Sheet(this, sheetName); |
353 | connect(sheet, SIGNAL(statusMessage(QString,int)), |
354 | this, SIGNAL(statusMessage(QString,int))); |
355 | return sheet; |
356 | } |
357 | |
358 | void Map::addSheet(Sheet *_sheet) |
359 | { |
360 | d->lstSheets.append(_sheet); |
361 | emit sheetAdded(_sheet); |
362 | } |
363 | |
364 | Sheet *Map::addNewSheet(const QString& name) |
365 | { |
366 | Sheet *t = createSheet(name); |
367 | addSheet(t); |
368 | return t; |
369 | } |
370 | |
371 | void Map::moveSheet(const QString & _from, const QString & _to, bool _before) |
372 | { |
373 | Sheet* sheetfrom = findSheet(_from); |
374 | Sheet* sheetto = findSheet(_to); |
375 | |
376 | int from = d->lstSheets.indexOf(sheetfrom) ; |
377 | int to = d->lstSheets.indexOf(sheetto) ; |
378 | if (!_before) |
379 | ++to; |
380 | |
381 | if (to > (int)d->lstSheets.count()) { |
382 | d->lstSheets.append(sheetfrom); |
383 | d->lstSheets.removeAt(from); |
384 | } else if (from < to) { |
385 | d->lstSheets.insert(to, sheetfrom); |
386 | d->lstSheets.removeAt(from); |
387 | } else { |
388 | d->lstSheets.removeAt(from); |
389 | d->lstSheets.insert(to, sheetfrom); |
390 | } |
391 | } |
392 | |
393 | void Map::loadOdfSettings(KoOasisSettings &settings) |
394 | { |
395 | KoOasisSettings::Items viewSettings = settings.itemSet("view-settings" ); |
396 | KoOasisSettings::IndexedMap viewMap = viewSettings.indexedMap("Views" ); |
397 | KoOasisSettings::Items firstView = viewMap.entry(0); |
398 | |
399 | KoOasisSettings::NamedMap sheetsMap = firstView.namedMap("Tables" ); |
400 | kDebug() << " loadOdfSettings( KoOasisSettings &settings ) exist :" << !sheetsMap.isNull(); |
401 | if (!sheetsMap.isNull()) { |
402 | foreach(Sheet* sheet, d->lstSheets) { |
403 | sheet->loadOdfSettings(sheetsMap); |
404 | } |
405 | } |
406 | |
407 | QString activeSheet = firstView.parseConfigItemString("ActiveTable" ); |
408 | kDebug() << " loadOdfSettings( KoOasisSettings &settings ) activeSheet :" << activeSheet; |
409 | |
410 | if (!activeSheet.isEmpty()) { |
411 | // Used by View's constructor |
412 | loadingInfo()->setInitialActiveSheet(findSheet(activeSheet)); |
413 | } |
414 | } |
415 | |
416 | bool Map::saveOdf(KoXmlWriter & xmlWriter, KoShapeSavingContext & savingContext) |
417 | { |
418 | // Saving the custom cell styles including the default cell style. |
419 | d->styleManager->saveOdf(savingContext.mainStyles()); |
420 | |
421 | // Saving the default column style |
422 | KoGenStyle defaultColumnStyle(KoGenStyle::TableColumnStyle, "table-column" ); |
423 | defaultColumnStyle.addPropertyPt("style:column-width" , d->defaultColumnFormat->width()); |
424 | defaultColumnStyle.setDefaultStyle(true); |
425 | savingContext.mainStyles().insert(defaultColumnStyle, "Default" , KoGenStyles::DontAddNumberToName); |
426 | |
427 | // Saving the default row style |
428 | KoGenStyle defaultRowStyle(KoGenStyle::TableRowStyle, "table-row" ); |
429 | defaultRowStyle.addPropertyPt("style:row-height" , d->defaultRowFormat->height()); |
430 | defaultRowStyle.setDefaultStyle(true); |
431 | savingContext.mainStyles().insert(defaultRowStyle, "Default" , KoGenStyles::DontAddNumberToName); |
432 | |
433 | d->calculationSettings->saveOdf(xmlWriter); // table::calculation-settings |
434 | |
435 | QByteArray password; |
436 | this->password(password); |
437 | if (!password.isNull()) { |
438 | xmlWriter.addAttribute("table:structure-protected" , "true" ); |
439 | QByteArray str = KCodecs::base64Encode(password); |
440 | // FIXME Stefan: see OpenDocument spec, ch. 17.3 Encryption |
441 | xmlWriter.addAttribute("table:protection-key" , QString(str.data())); |
442 | } |
443 | |
444 | OdfSavingContext tableContext(savingContext); |
445 | |
446 | foreach(Sheet* sheet, d->lstSheets) { |
447 | sheet->saveOdf(tableContext); |
448 | } |
449 | |
450 | tableContext.valStyle.writeStyle(xmlWriter); |
451 | |
452 | d->namedAreaManager->saveOdf(savingContext.xmlWriter()); |
453 | d->databaseManager->saveOdf(savingContext.xmlWriter()); |
454 | return true; |
455 | } |
456 | |
457 | QDomElement Map::save(QDomDocument& doc) |
458 | { |
459 | QDomElement spread = doc.documentElement(); |
460 | |
461 | QDomElement locale = static_cast<Localization*>(d->calculationSettings->locale())->save(doc); |
462 | spread.appendChild(locale); |
463 | |
464 | QDomElement areaname = d->namedAreaManager->saveXML(doc); |
465 | spread.appendChild(areaname); |
466 | |
467 | QDomElement defaults = doc.createElement("defaults" ); |
468 | defaults.setAttribute("row-height" , d->defaultRowFormat->height()); |
469 | defaults.setAttribute("col-width" , d->defaultColumnFormat->width()); |
470 | spread.appendChild(defaults); |
471 | |
472 | QDomElement s = d->styleManager->save(doc); |
473 | spread.appendChild(s); |
474 | |
475 | QDomElement mymap = doc.createElement("map" ); |
476 | |
477 | QByteArray password; |
478 | this->password(password); |
479 | if (!password.isNull()) { |
480 | if (password.size() > 0) { |
481 | QByteArray str = KCodecs::base64Encode(password); |
482 | mymap.setAttribute("protected" , QString(str.data())); |
483 | } else { |
484 | mymap.setAttribute("protected" , "" ); |
485 | } |
486 | } |
487 | |
488 | foreach(Sheet* sheet, d->lstSheets) { |
489 | QDomElement e = sheet->saveXML(doc); |
490 | if (e.isNull()) |
491 | return e; |
492 | mymap.appendChild(e); |
493 | } |
494 | return mymap; |
495 | } |
496 | |
497 | static void fixupStyle(KoCharacterStyle* style) |
498 | { |
499 | style->removeHardCodedDefaults(); |
500 | |
501 | QTextCharFormat format; |
502 | style->applyStyle(format); |
503 | switch (style->underlineStyle()) { |
504 | case KoCharacterStyle::NoLineStyle: |
505 | format.setUnderlineStyle(QTextCharFormat::NoUnderline); break; |
506 | case KoCharacterStyle::SolidLine: |
507 | format.setUnderlineStyle(QTextCharFormat::SingleUnderline); break; |
508 | case KoCharacterStyle::DottedLine: |
509 | format.setUnderlineStyle(QTextCharFormat::DotLine); break; |
510 | case KoCharacterStyle::DashLine: |
511 | format.setUnderlineStyle(QTextCharFormat::DashUnderline); break; |
512 | case KoCharacterStyle::DotDashLine: |
513 | format.setUnderlineStyle(QTextCharFormat::DashDotLine); break; |
514 | case KoCharacterStyle::DotDotDashLine: |
515 | format.setUnderlineStyle(QTextCharFormat::DashDotDotLine); break; |
516 | case KoCharacterStyle::LongDashLine: |
517 | format.setUnderlineStyle(QTextCharFormat::DashUnderline); break; |
518 | case KoCharacterStyle::WaveLine: |
519 | format.setUnderlineStyle(QTextCharFormat::WaveUnderline); break; |
520 | } |
521 | style->copyProperties(format); |
522 | } |
523 | |
524 | bool Map::loadOdf(const KoXmlElement& body, KoOdfLoadingContext& odfContext) |
525 | { |
526 | d->isLoading = true; |
527 | loadingInfo()->setFileFormat(LoadingInfo::OpenDocument); |
528 | |
529 | //load in first |
530 | d->styleManager->loadOdfStyleTemplate(odfContext.stylesReader(), this); |
531 | |
532 | OdfLoadingContext tableContext(odfContext); |
533 | tableContext.validities = Validity::preloadValidities(body); // table:content-validations |
534 | |
535 | // load text styles for rich-text content and TOS |
536 | KoShapeLoadingContext shapeContext(tableContext.odfContext, resourceManager()); |
537 | tableContext.shapeContext = &shapeContext; |
538 | KoTextSharedLoadingData * sharedData = new KoTextSharedLoadingData(); |
539 | sharedData->loadOdfStyles(shapeContext, textStyleManager()); |
540 | |
541 | fixupStyle((KoCharacterStyle*)textStyleManager()->defaultParagraphStyle()); |
542 | foreach (KoCharacterStyle* style, sharedData->characterStyles(true)) { |
543 | fixupStyle(style); |
544 | } |
545 | foreach (KoCharacterStyle* style, sharedData->characterStyles(false)) { |
546 | fixupStyle(style); |
547 | } |
548 | shapeContext.addSharedData(KOTEXT_SHARED_LOADING_ID, sharedData); |
549 | |
550 | QVariant variant; |
551 | variant.setValue(textStyleManager()); |
552 | resourceManager()->setResource(KoText::StyleManager, variant); |
553 | |
554 | |
555 | // load default column style |
556 | const KoXmlElement* defaultColumnStyle = odfContext.stylesReader().defaultStyle("table-column" ); |
557 | if (defaultColumnStyle) { |
558 | // kDebug() <<"style:default-style style:family=\"table-column\""; |
559 | KoStyleStack styleStack; |
560 | styleStack.push(*defaultColumnStyle); |
561 | styleStack.setTypeProperties("table-column" ); |
562 | if (styleStack.hasProperty(KoXmlNS::style, "column-width" )) { |
563 | const double width = KoUnit::parseValue(styleStack.property(KoXmlNS::style, "column-width" ), -1.0); |
564 | if (width != -1.0) { |
565 | // kDebug() <<"\tstyle:column-width:" << width; |
566 | d->defaultColumnFormat->setWidth(width); |
567 | } |
568 | } |
569 | } |
570 | |
571 | // load default row style |
572 | const KoXmlElement* defaultRowStyle = odfContext.stylesReader().defaultStyle("table-row" ); |
573 | if (defaultRowStyle) { |
574 | // kDebug() <<"style:default-style style:family=\"table-row\""; |
575 | KoStyleStack styleStack; |
576 | styleStack.push(*defaultRowStyle); |
577 | styleStack.setTypeProperties("table-row" ); |
578 | if (styleStack.hasProperty(KoXmlNS::style, "row-height" )) { |
579 | const double height = KoUnit::parseValue(styleStack.property(KoXmlNS::style, "row-height" ), -1.0); |
580 | if (height != -1.0) { |
581 | // kDebug() <<"\tstyle:row-height:" << height; |
582 | d->defaultRowFormat->setHeight(height); |
583 | } |
584 | } |
585 | } |
586 | |
587 | d->calculationSettings->loadOdf(body); // table::calculation-settings |
588 | if (body.hasAttributeNS(KoXmlNS::table, "structure-protected" )) { |
589 | loadOdfProtection(body); |
590 | } |
591 | |
592 | KoXmlNode sheetNode = KoXml::namedItemNS(body, KoXmlNS::table, "table" ); |
593 | |
594 | if (sheetNode.isNull()) { |
595 | // We need at least one sheet ! |
596 | doc()->setErrorMessage(i18n("This document has no sheets (tables)." )); |
597 | d->isLoading = false; |
598 | return false; |
599 | } |
600 | |
601 | d->overallRowCount = 0; |
602 | while (!sheetNode.isNull()) { |
603 | KoXmlElement sheetElement = sheetNode.toElement(); |
604 | if (!sheetElement.isNull()) { |
605 | //kDebug()<<" Map::loadOdf tableElement is not null"; |
606 | //kDebug()<<"tableElement.nodeName() :"<<sheetElement.nodeName(); |
607 | |
608 | // make it slightly faster |
609 | KoXml::load(sheetElement); |
610 | |
611 | if (sheetElement.nodeName() == "table:table" ) { |
612 | if (!sheetElement.attributeNS(KoXmlNS::table, "name" , QString()).isEmpty()) { |
613 | const QString sheetName = sheetElement.attributeNS(KoXmlNS::table, "name" , QString()); |
614 | Sheet* sheet = addNewSheet(sheetName); |
615 | sheet->setSheetName(sheetName, true); |
616 | d->overallRowCount += KoXml::childNodesCount(sheetElement); |
617 | } |
618 | } |
619 | } |
620 | |
621 | // reduce memory usage |
622 | KoXml::unload(sheetElement); |
623 | sheetNode = sheetNode.nextSibling(); |
624 | } |
625 | |
626 | //pre-load auto styles |
627 | QHash<QString, Conditions> conditionalStyles; |
628 | Styles autoStyles = d->styleManager->loadOdfAutoStyles(odfContext.stylesReader(), |
629 | conditionalStyles, parser()); |
630 | |
631 | // load the sheet |
632 | sheetNode = body.firstChild(); |
633 | while (!sheetNode.isNull()) { |
634 | KoXmlElement sheetElement = sheetNode.toElement(); |
635 | if (!sheetElement.isNull()) { |
636 | // make it slightly faster |
637 | KoXml::load(sheetElement); |
638 | |
639 | //kDebug()<<"tableElement.nodeName() bis :"<<sheetElement.nodeName(); |
640 | if (sheetElement.nodeName() == "table:table" ) { |
641 | if (!sheetElement.attributeNS(KoXmlNS::table, "name" , QString()).isEmpty()) { |
642 | QString name = sheetElement.attributeNS(KoXmlNS::table, "name" , QString()); |
643 | Sheet* sheet = findSheet(name); |
644 | if (sheet) { |
645 | sheet->loadOdf(sheetElement, tableContext, autoStyles, conditionalStyles); |
646 | } |
647 | } |
648 | } |
649 | } |
650 | |
651 | // reduce memory usage |
652 | KoXml::unload(sheetElement); |
653 | sheetNode = sheetNode.nextSibling(); |
654 | } |
655 | |
656 | // make sure always at least one sheet exists |
657 | if (count() == 0) { |
658 | addNewSheet(); |
659 | } |
660 | |
661 | //delete any styles which were not used |
662 | d->styleManager->releaseUnusedAutoStyles(autoStyles); |
663 | |
664 | // Load databases. This needs the sheets to be loaded. |
665 | d->databaseManager->loadOdf(body); // table:database-ranges |
666 | d->namedAreaManager->loadOdf(body); // table:named-expressions |
667 | |
668 | d->isLoading = false; |
669 | return true; |
670 | } |
671 | |
672 | |
673 | bool Map::loadXML(const KoXmlElement& mymap) |
674 | { |
675 | d->isLoading = true; |
676 | loadingInfo()->setFileFormat(LoadingInfo::NativeFormat); |
677 | const QString activeSheet = mymap.attribute("activeTable" ); |
678 | const QPoint marker(mymap.attribute("markerColumn" ).toInt(), mymap.attribute("markerRow" ).toInt()); |
679 | loadingInfo()->setCursorPosition(findSheet(activeSheet), marker); |
680 | const QPointF offset(mymap.attribute("xOffset" ).toDouble(), mymap.attribute("yOffset" ).toDouble()); |
681 | loadingInfo()->setScrollingOffset(findSheet(activeSheet), offset); |
682 | |
683 | KoXmlNode n = mymap.firstChild(); |
684 | if (n.isNull()) { |
685 | // We need at least one sheet ! |
686 | doc()->setErrorMessage(i18n("This document has no sheets (tables)." )); |
687 | d->isLoading = false; |
688 | return false; |
689 | } |
690 | while (!n.isNull()) { |
691 | KoXmlElement e = n.toElement(); |
692 | if (!e.isNull() && e.tagName() == "table" ) { |
693 | Sheet *t = addNewSheet(); |
694 | if (!t->loadXML(e)) { |
695 | d->isLoading = false; |
696 | return false; |
697 | } |
698 | } |
699 | n = n.nextSibling(); |
700 | } |
701 | |
702 | loadXmlProtection(mymap); |
703 | |
704 | if (!activeSheet.isEmpty()) { |
705 | // Used by View's constructor |
706 | loadingInfo()->setInitialActiveSheet(findSheet(activeSheet)); |
707 | } |
708 | |
709 | d->isLoading = false; |
710 | return true; |
711 | } |
712 | |
713 | Sheet* Map::findSheet(const QString & _name) const |
714 | { |
715 | foreach(Sheet* sheet, d->lstSheets) { |
716 | if (_name.toLower() == sheet->sheetName().toLower()) |
717 | return sheet; |
718 | } |
719 | return 0; |
720 | } |
721 | |
722 | Sheet * Map::nextSheet(Sheet * currentSheet) const |
723 | { |
724 | if (currentSheet == d->lstSheets.last()) |
725 | return currentSheet; |
726 | int index = 0; |
727 | foreach(Sheet* sheet, d->lstSheets) { |
728 | if (sheet == currentSheet) |
729 | return d->lstSheets.value(++index); |
730 | ++index; |
731 | } |
732 | return 0; |
733 | } |
734 | |
735 | Sheet * Map::previousSheet(Sheet * currentSheet) const |
736 | { |
737 | if (currentSheet == d->lstSheets.first()) |
738 | return currentSheet; |
739 | int index = 0; |
740 | foreach(Sheet* sheet, d->lstSheets) { |
741 | if (sheet == currentSheet) |
742 | return d->lstSheets.value(--index); |
743 | ++index; |
744 | } |
745 | return 0; |
746 | } |
747 | |
748 | bool Map::loadChildren(KoStore * _store) |
749 | { |
750 | foreach(Sheet* sheet, d->lstSheets) { |
751 | if (!sheet->loadChildren(_store)) |
752 | return false; |
753 | } |
754 | return true; |
755 | } |
756 | |
757 | void Map::removeSheet(Sheet* sheet) |
758 | { |
759 | d->lstSheets.removeAll(sheet); |
760 | d->lstDeletedSheets.append(sheet); |
761 | d->namedAreaManager->remove(sheet); |
762 | emit sheetRemoved(sheet); |
763 | } |
764 | |
765 | void Map::reviveSheet(Sheet* sheet) |
766 | { |
767 | d->lstDeletedSheets.removeAll(sheet); |
768 | d->lstSheets.append(sheet); |
769 | emit sheetRevived(sheet); |
770 | } |
771 | |
772 | // FIXME cache this for faster operation |
773 | QStringList Map::visibleSheets() const |
774 | { |
775 | QStringList result; |
776 | foreach(Sheet* sheet, d->lstSheets) { |
777 | if (!sheet->isHidden()) |
778 | result.append(sheet->sheetName()); |
779 | } |
780 | return result; |
781 | } |
782 | |
783 | // FIXME cache this for faster operation |
784 | QStringList Map::hiddenSheets() const |
785 | { |
786 | QStringList result; |
787 | foreach(Sheet* sheet, d->lstSheets) { |
788 | if (sheet->isHidden()) |
789 | result.append(sheet->sheetName()); |
790 | } |
791 | return result; |
792 | } |
793 | |
794 | Sheet* Map::sheet(int index) const |
795 | { |
796 | return d->lstSheets.value(index); |
797 | } |
798 | |
799 | int Map::indexOf(Sheet* sheet) const |
800 | { |
801 | return d->lstSheets.indexOf(sheet); |
802 | } |
803 | |
804 | QList<Sheet*>& Map::sheetList() const |
805 | { |
806 | return d->lstSheets; |
807 | } |
808 | |
809 | int Map::count() const |
810 | { |
811 | return d->lstSheets.count(); |
812 | } |
813 | |
814 | int Map::increaseLoadedRowsCounter(int number) |
815 | { |
816 | d->loadedRowsCounter += number; |
817 | if (d->overallRowCount) { |
818 | return 100 * d->loadedRowsCounter / d->overallRowCount; |
819 | } |
820 | return -1; |
821 | } |
822 | |
823 | bool Map::isLoading() const |
824 | { |
825 | // The KoDocument state is necessary to avoid damages while importing a file (through a filter). |
826 | return d->isLoading || (d->doc && d->doc->isLoading()); |
827 | } |
828 | |
829 | int Map::syntaxVersion() const |
830 | { |
831 | return d->syntaxVersion; |
832 | } |
833 | |
834 | void Map::setSyntaxVersion(int version) |
835 | { |
836 | d->syntaxVersion = version; |
837 | } |
838 | |
839 | LoadingInfo* Map::loadingInfo() const |
840 | { |
841 | if (!d->loadingInfo) { |
842 | d->loadingInfo = new LoadingInfo(); |
843 | } |
844 | return d->loadingInfo; |
845 | } |
846 | |
847 | void Map::deleteLoadingInfo() |
848 | { |
849 | delete d->loadingInfo; |
850 | d->loadingInfo = 0; |
851 | } |
852 | |
853 | KCompletion& Map::stringCompletion() |
854 | { |
855 | return d->listCompletion; |
856 | } |
857 | |
858 | void Map::addStringCompletion(const QString &stringCompletion) |
859 | { |
860 | if (d->listCompletion.items().contains(stringCompletion) == 0) { |
861 | d->listCompletion.addItem(stringCompletion); |
862 | } |
863 | } |
864 | |
865 | void Map::addDamage(Damage* damage) |
866 | { |
867 | // Do not create a new Damage, if we are in loading process. Check for it before |
868 | // calling this function. This prevents unnecessary memory allocations (new). |
869 | // see FIXME in Sheet::setSheetName(). |
870 | // Q_ASSERT(!isLoading()); |
871 | Q_CHECK_PTR(damage); |
872 | |
873 | #ifndef NDEBUG |
874 | if (damage->type() == Damage::Cell) { |
875 | kDebug(36007) << "Adding\t" << *static_cast<CellDamage*>(damage); |
876 | } else if (damage->type() == Damage::Sheet) { |
877 | kDebug(36007) << "Adding\t" << *static_cast<SheetDamage*>(damage); |
878 | } else if (damage->type() == Damage::Selection) { |
879 | kDebug(36007) << "Adding\t" << *static_cast<SelectionDamage*>(damage); |
880 | } else { |
881 | kDebug(36007) << "Adding\t" << *damage; |
882 | } |
883 | #endif |
884 | |
885 | d->damages.append(damage); |
886 | |
887 | if (d->damages.count() == 1) { |
888 | QTimer::singleShot(0, this, SLOT(flushDamages())); |
889 | } |
890 | } |
891 | |
892 | void Map::flushDamages() |
893 | { |
894 | // Copy the damages to process. This allows new damages while processing. |
895 | QList<Damage*> damages = d->damages; |
896 | d->damages.clear(); |
897 | emit damagesFlushed(damages); |
898 | qDeleteAll(damages); |
899 | } |
900 | |
901 | void Map::handleDamages(const QList<Damage*>& damages) |
902 | { |
903 | Region bindingChangedRegion; |
904 | Region formulaChangedRegion; |
905 | Region namedAreaChangedRegion; |
906 | Region valueChangedRegion; |
907 | WorkbookDamage::Changes workbookChanges = WorkbookDamage::None; |
908 | |
909 | QList<Damage*>::ConstIterator end(damages.end()); |
910 | for (QList<Damage*>::ConstIterator it = damages.begin(); it != end; ++it) { |
911 | Damage* damage = *it; |
912 | |
913 | if (damage->type() == Damage::Cell) { |
914 | CellDamage* cellDamage = static_cast<CellDamage*>(damage); |
915 | kDebug(36007) << "Processing\t" << *cellDamage; |
916 | Sheet* const damagedSheet = cellDamage->sheet(); |
917 | const Region& region = cellDamage->region(); |
918 | const CellDamage::Changes changes = cellDamage->changes(); |
919 | |
920 | // TODO Stefan: Detach the style cache from the CellView cache. |
921 | if ((changes.testFlag(CellDamage::Appearance))) { |
922 | // Rebuild the style storage cache. |
923 | damagedSheet->cellStorage()->invalidateStyleCache(); // FIXME more fine-grained |
924 | } |
925 | if ((cellDamage->changes() & CellDamage::Binding) && |
926 | !workbookChanges.testFlag(WorkbookDamage::Value)) { |
927 | bindingChangedRegion.add(region, damagedSheet); |
928 | } |
929 | if ((cellDamage->changes() & CellDamage::Formula) && |
930 | !workbookChanges.testFlag(WorkbookDamage::Formula)) { |
931 | formulaChangedRegion.add(region, damagedSheet); |
932 | } |
933 | if ((cellDamage->changes() & CellDamage::NamedArea) && |
934 | !workbookChanges.testFlag(WorkbookDamage::Formula)) { |
935 | namedAreaChangedRegion.add(region, damagedSheet); |
936 | } |
937 | if ((cellDamage->changes() & CellDamage::Value) && |
938 | !workbookChanges.testFlag(WorkbookDamage::Value)) { |
939 | valueChangedRegion.add(region, damagedSheet); |
940 | } |
941 | continue; |
942 | } |
943 | |
944 | if (damage->type() == Damage::Sheet) { |
945 | SheetDamage* sheetDamage = static_cast<SheetDamage*>(damage); |
946 | kDebug(36007) << "Processing\t" << *sheetDamage; |
947 | // Sheet* damagedSheet = sheetDamage->sheet(); |
948 | |
949 | if (sheetDamage->changes() & SheetDamage::PropertiesChanged) { |
950 | } |
951 | continue; |
952 | } |
953 | |
954 | if (damage->type() == Damage::Workbook) { |
955 | WorkbookDamage* workbookDamage = static_cast<WorkbookDamage*>(damage); |
956 | kDebug(36007) << "Processing\t" << *damage; |
957 | |
958 | workbookChanges |= workbookDamage->changes(); |
959 | if (workbookDamage->changes() & WorkbookDamage::Formula) { |
960 | formulaChangedRegion.clear(); |
961 | } |
962 | if (workbookDamage->changes() & WorkbookDamage::Value) { |
963 | valueChangedRegion.clear(); |
964 | } |
965 | continue; |
966 | } |
967 | // kDebug(36007) <<"Unhandled\t" << *damage; |
968 | } |
969 | |
970 | // Update the named areas. |
971 | if (!namedAreaChangedRegion.isEmpty()) { |
972 | d->namedAreaManager->regionChanged(namedAreaChangedRegion); |
973 | } |
974 | // First, update the dependencies. |
975 | if (!formulaChangedRegion.isEmpty()) { |
976 | d->dependencyManager->regionChanged(formulaChangedRegion); |
977 | } |
978 | // Tell the RecalcManager which cells have had a value change. |
979 | if (!valueChangedRegion.isEmpty()) { |
980 | d->recalcManager->regionChanged(valueChangedRegion); |
981 | } |
982 | if (workbookChanges.testFlag(WorkbookDamage::Formula)) { |
983 | d->namedAreaManager->updateAllNamedAreas(); |
984 | d->dependencyManager->updateAllDependencies(this); |
985 | } |
986 | if (workbookChanges.testFlag(WorkbookDamage::Value)) { |
987 | d->recalcManager->recalcMap(); |
988 | d->bindingManager->updateAllBindings(); |
989 | } |
990 | // Update the bindings |
991 | if (!bindingChangedRegion.isEmpty()) { |
992 | d->bindingManager->regionChanged(bindingChangedRegion); |
993 | } |
994 | } |
995 | |
996 | void Map::addCommand(KUndo2Command *command) |
997 | { |
998 | emit commandAdded(command); |
999 | } |
1000 | |
1001 | KoDocumentResourceManager* Map::resourceManager() const |
1002 | { |
1003 | if (!doc()) return 0; |
1004 | return doc()->resourceManager(); |
1005 | } |
1006 | |
1007 | #include "Map.moc" |
1008 | |