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 
32 #include <KoGlobal.h>
33 #include <KoEmbeddedDocumentSaver.h>
34 #include <KoStyleManager.h>
35 #include <KoParagraphStyle.h>
36 #include <KoUpdater.h>
37 #include <KoProgressUpdater.h>
38 
39 #include "ApplicationSettings.h"
40 #include "BindingManager.h"
41 #include "CalculationSettings.h"
42 #include "CellStorage.h"
43 #include "Damages.h"
44 #include "DependencyManager.h"
45 #include "DocBase.h"
46 #include "LoadingInfo.h"
47 #include "Localization.h"
48 #include "NamedAreaManager.h"
49 #include "RecalcManager.h"
50 #include "RowColumnFormat.h"
51 #include "Sheet.h"
52 #include "StyleManager.h"
53 #include "ValueCalc.h"
54 #include "ValueConverter.h"
55 #include "ValueFormatter.h"
56 #include "ValueParser.h"
57 
58 // database
59 #include "database/DatabaseManager.h"
60 
61 using namespace Calligra::Sheets;
62 
63 class Q_DECL_HIDDEN Map::Private
64 {
65 public:
66     DocBase* doc;
67 
68     /**
69      * List of all sheets in this map.
70      */
71     QList<Sheet*> lstSheets;
72     QList<Sheet*> lstDeletedSheets;
73 
74     // used to give every Sheet a unique default name.
75     int tableId;
76 
77     // used to determine the loading progress
78     int overallRowCount;
79     int loadedRowsCounter;
80 
81     LoadingInfo* loadingInfo;
82     bool readwrite;
83 
84     BindingManager* bindingManager;
85     DatabaseManager* databaseManager;
86     DependencyManager* dependencyManager;
87     NamedAreaManager* namedAreaManager;
88     RecalcManager* recalcManager;
89     StyleManager* styleManager;
90     KoStyleManager* textStyleManager;
91 
92     ApplicationSettings* applicationSettings;
93     CalculationSettings* calculationSettings;
94     ValueCalc* calc;
95     ValueConverter* converter;
96     ValueFormatter* formatter;
97     ValueParser* parser;
98 
99     // default objects
100     ColumnFormat* defaultColumnFormat;
101     RowFormat* defaultRowFormat;
102 
103     QList<Damage*> damages;
104     bool isLoading;
105 
106     int syntaxVersion;
107 
108     KCompletion listCompletion;
109 };
110 
111 
Map(DocBase * doc,int syntaxVersion)112 Map::Map(DocBase* doc, int syntaxVersion)
113         : QObject(doc),
114         d(new Private)
115 {
116     setObjectName(QLatin1String("Map")); // necessary for D-Bus
117     d->doc = doc;
118     d->tableId = 1;
119     d->overallRowCount = 0;
120     d->loadedRowsCounter = 0;
121     d->loadingInfo = 0;
122     d->readwrite = true;
123 
124     d->bindingManager = new BindingManager(this);
125     d->databaseManager = new DatabaseManager(this);
126     d->dependencyManager = new DependencyManager(this);
127     d->namedAreaManager = new NamedAreaManager(this);
128     d->recalcManager = new RecalcManager(this);
129     d->styleManager = new StyleManager();
130     d->textStyleManager = new KoStyleManager(this);
131     d->applicationSettings = new ApplicationSettings();
132     d->calculationSettings = new CalculationSettings();
133 
134     d->parser = new ValueParser(d->calculationSettings);
135     d->converter = new ValueConverter(d->parser);
136     d->calc = new ValueCalc(d->converter);
137     d->formatter = new ValueFormatter(d->converter);
138 
139     d->defaultColumnFormat = new ColumnFormat();
140     d->defaultRowFormat = new RowFormat();
141 
142     QFont font(KoGlobal::defaultFont());
143     d->defaultRowFormat->setHeight(font.pointSizeF() + 4);
144     d->defaultColumnFormat->setWidth((font.pointSizeF() + 4) * 5);
145 
146     d->isLoading = false;
147 
148     // default document properties
149     d->syntaxVersion = syntaxVersion;
150 
151     connect(this, SIGNAL(sheetAdded(Sheet*)),
152             d->dependencyManager, SLOT(addSheet(Sheet*)));
153     connect(this, SIGNAL(sheetAdded(Sheet*)),
154             d->recalcManager, SLOT(addSheet(Sheet*)));
155     connect(this, SIGNAL(sheetRemoved(Sheet*)),
156             d->dependencyManager, SLOT(removeSheet(Sheet*)));
157     connect(this, SIGNAL(sheetRemoved(Sheet*)),
158             d->recalcManager, SLOT(removeSheet(Sheet*)));
159     connect(this, SIGNAL(sheetRevived(Sheet*)),
160             d->dependencyManager, SLOT(addSheet(Sheet*)));
161     connect(this, SIGNAL(sheetRevived(Sheet*)),
162             d->recalcManager, SLOT(addSheet(Sheet*)));
163     connect(d->namedAreaManager, SIGNAL(namedAreaModified(QString)),
164             d->dependencyManager, SLOT(namedAreaModified(QString)));
165     connect(this, SIGNAL(damagesFlushed(QList<Damage*>)),
166             this, SLOT(handleDamages(QList<Damage*>)));
167 }
168 
~Map()169 Map::~Map()
170 {
171     // Because some of the shapes might be using a sheet in this map, delete
172     // all shapes in each sheet before all sheets are deleted together.
173     foreach(Sheet *sheet, d->lstSheets)
174         sheet->deleteShapes();
175     // we have to explicitly delete the Sheets, not let QObject take care of that
176     // as the sheet in its destructor expects the Map to still exist
177     qDeleteAll(d->lstSheets);
178     d->lstSheets.clear();
179 
180     deleteLoadingInfo();
181 
182     delete d->bindingManager;
183     delete d->databaseManager;
184     delete d->dependencyManager;
185     delete d->namedAreaManager;
186     delete d->recalcManager;
187     delete d->styleManager;
188 
189     delete d->parser;
190     delete d->formatter;
191     delete d->converter;
192     delete d->calc;
193     delete d->calculationSettings;
194     delete d->applicationSettings;
195 
196     delete d->defaultColumnFormat;
197     delete d->defaultRowFormat;
198 
199     delete d;
200 }
201 
doc() const202 DocBase* Map::doc() const
203 {
204     return d->doc;
205 }
206 
setReadWrite(bool readwrite)207 void Map::setReadWrite(bool readwrite)
208 {
209     d->readwrite = readwrite;
210 }
211 
isReadWrite() const212 bool Map::isReadWrite() const
213 {
214     return d->readwrite;
215 }
216 
completeLoading(KoStore * store)217 bool Map::completeLoading(KoStore *store)
218 {
219     Q_UNUSED(store);
220 
221     QPointer<KoUpdater> dependencyUpdater, recalcUpdater;
222     if (doc() && doc()->progressUpdater()) {
223         dependencyUpdater = doc()->progressUpdater()->startSubtask(1, "Calligra::Sheets::DependencyManager::updateAllDependencies");
224         recalcUpdater = doc()->progressUpdater()->startSubtask(1, "Calligra::Sheets::RecalcManager::recalc");
225     }
226 
227     // Initial build of all cell dependencies.
228     d->dependencyManager->updateAllDependencies(this, dependencyUpdater);
229     // Recalc the whole workbook now, since there may be formulas other spreadsheets support,
230     // but Calligra Sheets does not.
231     d->recalcManager->recalcMap(recalcUpdater);
232 
233     return true;
234 }
235 
completeSaving(KoStore * store,KoXmlWriter * manifestWriter,KoShapeSavingContext * context)236 bool Map::completeSaving(KoStore *store, KoXmlWriter *manifestWriter, KoShapeSavingContext * context)
237 {
238     Q_UNUSED(store);
239     Q_UNUSED(manifestWriter);
240     Q_UNUSED(context);
241     return true;
242 }
243 
bindingManager() const244 BindingManager* Map::bindingManager() const
245 {
246     return d->bindingManager;
247 }
248 
databaseManager() const249 DatabaseManager* Map::databaseManager() const
250 {
251     return d->databaseManager;
252 }
253 
dependencyManager() const254 DependencyManager* Map::dependencyManager() const
255 {
256     return d->dependencyManager;
257 }
258 
namedAreaManager() const259 NamedAreaManager* Map::namedAreaManager() const
260 {
261     return d->namedAreaManager;
262 }
263 
recalcManager() const264 RecalcManager* Map::recalcManager() const
265 {
266     return d->recalcManager;
267 }
268 
styleManager() const269 StyleManager* Map::styleManager() const
270 {
271     return d->styleManager;
272 }
273 
textStyleManager() const274 KoStyleManager* Map::textStyleManager() const
275 {
276     return d->textStyleManager;
277 }
278 
parser() const279 ValueParser* Map::parser() const
280 {
281     return d->parser;
282 }
283 
formatter() const284 ValueFormatter* Map::formatter() const
285 {
286     return d->formatter;
287 }
288 
converter() const289 ValueConverter* Map::converter() const
290 {
291     return d->converter;
292 }
293 
calc() const294 ValueCalc* Map::calc() const
295 {
296     return d->calc;
297 }
298 
defaultColumnFormat() const299 const ColumnFormat* Map::defaultColumnFormat() const
300 {
301     return d->defaultColumnFormat;
302 }
303 
defaultRowFormat() const304 const RowFormat* Map::defaultRowFormat() const
305 {
306     return d->defaultRowFormat;
307 }
308 
setDefaultColumnWidth(double width)309 void Map::setDefaultColumnWidth(double width)
310 {
311     d->defaultColumnFormat->setWidth(width);
312 }
313 
setDefaultRowHeight(double height)314 void Map::setDefaultRowHeight(double height)
315 {
316     d->defaultRowFormat->setHeight(height);
317 }
318 
settings() const319 ApplicationSettings* Map::settings() const
320 {
321     return d->applicationSettings;
322 }
323 
calculationSettings() const324 CalculationSettings* Map::calculationSettings() const
325 {
326     return d->calculationSettings;
327 }
328 
createSheet(const QString & name)329 Sheet* Map::createSheet(const QString& name)
330 {
331     QString sheetName(i18n("Sheet%1", d->tableId++));
332     if ( !name.isEmpty() )
333         sheetName = name;
334     Sheet* sheet = new Sheet(this, sheetName);
335     connect(sheet, SIGNAL(statusMessage(QString,int)),
336             this, SIGNAL(statusMessage(QString,int)));
337     return sheet;
338 }
339 
addSheet(Sheet * _sheet)340 void Map::addSheet(Sheet *_sheet)
341 {
342     d->lstSheets.append(_sheet);
343     emit sheetAdded(_sheet);
344 }
345 
addNewSheet(const QString & name)346 Sheet *Map::addNewSheet(const QString& name)
347 {
348     Sheet *t = createSheet(name);
349     addSheet(t);
350     return t;
351 }
352 
moveSheet(const QString & _from,const QString & _to,bool _before)353 void Map::moveSheet(const QString & _from, const QString & _to, bool _before)
354 {
355     Sheet* sheetfrom = findSheet(_from);
356     Sheet* sheetto = findSheet(_to);
357 
358     int from = d->lstSheets.indexOf(sheetfrom) ;
359     int to = d->lstSheets.indexOf(sheetto) ;
360     if (!_before)
361         ++to;
362 
363     if (to > (int)d->lstSheets.count()) {
364         d->lstSheets.append(sheetfrom);
365         d->lstSheets.removeAt(from);
366     } else if (from < to) {
367         d->lstSheets.insert(to, sheetfrom);
368         d->lstSheets.removeAt(from);
369     } else {
370         d->lstSheets.removeAt(from);
371         d->lstSheets.insert(to, sheetfrom);
372     }
373 }
374 
save(QDomDocument & doc)375 QDomElement Map::save(QDomDocument& doc)
376 {
377     QDomElement spread = doc.documentElement();
378 
379     QDomElement locale = static_cast<Localization*>(d->calculationSettings->locale())->save(doc);
380     spread.appendChild(locale);
381 
382     QDomElement areaname = d->namedAreaManager->saveXML(doc);
383     spread.appendChild(areaname);
384 
385     QDomElement defaults = doc.createElement("defaults");
386     defaults.setAttribute("row-height", QString::number(d->defaultRowFormat->height()));
387     defaults.setAttribute("col-width", QString::number(d->defaultColumnFormat->width()));
388     spread.appendChild(defaults);
389 
390     QDomElement s = d->styleManager->save(doc);
391     spread.appendChild(s);
392 
393     QDomElement mymap = doc.createElement("map");
394 
395     QByteArray password;
396     this->password(password);
397     if (!password.isNull()) {
398         if (password.size() > 0) {
399             QByteArray str = KCodecs::base64Encode(password);
400             mymap.setAttribute("protected", QString(str.data()));
401         } else {
402             mymap.setAttribute("protected", "");
403         }
404     }
405 
406     foreach(Sheet* sheet, d->lstSheets) {
407         QDomElement e = sheet->saveXML(doc);
408         if (e.isNull())
409             return e;
410         mymap.appendChild(e);
411     }
412     return mymap;
413 }
414 
loadXML(const KoXmlElement & mymap)415 bool Map::loadXML(const KoXmlElement& mymap)
416 {
417     d->isLoading = true;
418     loadingInfo()->setFileFormat(LoadingInfo::NativeFormat);
419     const QString activeSheet = mymap.attribute("activeTable");
420     const QPoint marker(mymap.attribute("markerColumn").toInt(), mymap.attribute("markerRow").toInt());
421     loadingInfo()->setCursorPosition(findSheet(activeSheet), marker);
422     const QPointF offset(mymap.attribute("xOffset").toDouble(), mymap.attribute("yOffset").toDouble());
423     loadingInfo()->setScrollingOffset(findSheet(activeSheet), offset);
424 
425     KoXmlNode n = mymap.firstChild();
426     if (n.isNull()) {
427         // We need at least one sheet !
428         doc()->setErrorMessage(i18n("This document has no sheets (tables)."));
429         d->isLoading = false;
430         return false;
431     }
432     while (!n.isNull()) {
433         KoXmlElement e = n.toElement();
434         if (!e.isNull() && e.tagName() == "table") {
435             Sheet *t = addNewSheet();
436             if (!t->loadXML(e)) {
437                 d->isLoading = false;
438                 return false;
439             }
440         }
441         n = n.nextSibling();
442     }
443 
444     loadXmlProtection(mymap);
445 
446     if (!activeSheet.isEmpty()) {
447         // Used by View's constructor
448         loadingInfo()->setInitialActiveSheet(findSheet(activeSheet));
449     }
450 
451     d->isLoading = false;
452     return true;
453 }
454 
findSheet(const QString & _name) const455 Sheet* Map::findSheet(const QString & _name) const
456 {
457     foreach(Sheet* sheet, d->lstSheets) {
458         if (_name.toLower() == sheet->sheetName().toLower())
459             return sheet;
460     }
461     return 0;
462 }
463 
nextSheet(Sheet * currentSheet) const464 Sheet * Map::nextSheet(Sheet * currentSheet) const
465 {
466     if (currentSheet == d->lstSheets.last())
467         return currentSheet;
468     int index = 0;
469     foreach(Sheet* sheet, d->lstSheets) {
470         if (sheet == currentSheet)
471             return d->lstSheets.value(++index);
472         ++index;
473     }
474     return 0;
475 }
476 
previousSheet(Sheet * currentSheet) const477 Sheet * Map::previousSheet(Sheet * currentSheet) const
478 {
479     if (currentSheet == d->lstSheets.first())
480         return currentSheet;
481     int index = 0;
482     foreach(Sheet* sheet, d->lstSheets) {
483         if (sheet  == currentSheet)
484             return d->lstSheets.value(--index);
485         ++index;
486     }
487     return 0;
488 }
489 
loadChildren(KoStore * _store)490 bool Map::loadChildren(KoStore * _store)
491 {
492     foreach(Sheet* sheet, d->lstSheets) {
493         if (!sheet->loadChildren(_store))
494             return false;
495     }
496     return true;
497 }
498 
removeSheet(Sheet * sheet)499 void Map::removeSheet(Sheet* sheet)
500 {
501     d->lstSheets.removeAll(sheet);
502     d->lstDeletedSheets.append(sheet);
503     d->namedAreaManager->remove(sheet);
504     emit sheetRemoved(sheet);
505 }
506 
reviveSheet(Sheet * sheet)507 void Map::reviveSheet(Sheet* sheet)
508 {
509     d->lstDeletedSheets.removeAll(sheet);
510     d->lstSheets.append(sheet);
511     emit sheetRevived(sheet);
512 }
513 
514 // FIXME cache this for faster operation
visibleSheets() const515 QStringList Map::visibleSheets() const
516 {
517     QStringList result;
518     foreach(Sheet* sheet, d->lstSheets) {
519         if (!sheet->isHidden())
520             result.append(sheet->sheetName());
521     }
522     return result;
523 }
524 
525 // FIXME cache this for faster operation
hiddenSheets() const526 QStringList Map::hiddenSheets() const
527 {
528     QStringList result;
529     foreach(Sheet* sheet, d->lstSheets) {
530         if (sheet->isHidden())
531             result.append(sheet->sheetName());
532     }
533     return result;
534 }
535 
sheet(int index) const536 Sheet* Map::sheet(int index) const
537 {
538     return d->lstSheets.value(index);
539 }
540 
indexOf(Sheet * sheet) const541 int Map::indexOf(Sheet* sheet) const
542 {
543     return d->lstSheets.indexOf(sheet);
544 }
545 
sheetList() const546 QList<Sheet*>& Map::sheetList() const
547 {
548     return d->lstSheets;
549 }
550 
count() const551 int Map::count() const
552 {
553     return d->lstSheets.count();
554 }
555 
setOverallRowsCounter(int number)556 void Map::setOverallRowsCounter(int number)
557 {
558     d->overallRowCount = number;
559 }
560 
increaseLoadedRowsCounter(int number)561 int Map::increaseLoadedRowsCounter(int number)
562 {
563     d->loadedRowsCounter += number;
564     if (d->overallRowCount) {
565         return 100 * d->loadedRowsCounter / d->overallRowCount;
566     }
567     return -1;
568 }
569 
isLoading() const570 bool Map::isLoading() const
571 {
572     // The KoDocument state is necessary to avoid damages while importing a file (through a filter).
573     return d->isLoading || (d->doc && d->doc->isLoading());
574 }
575 
setLoading(bool l)576 void Map::setLoading(bool l) {
577     d->isLoading = l;
578 }
579 
syntaxVersion() const580 int Map::syntaxVersion() const
581 {
582     return d->syntaxVersion;
583 }
584 
setSyntaxVersion(int version)585 void Map::setSyntaxVersion(int version)
586 {
587     d->syntaxVersion = version;
588 }
589 
loadingInfo() const590 LoadingInfo* Map::loadingInfo() const
591 {
592     if (!d->loadingInfo) {
593         d->loadingInfo = new LoadingInfo();
594     }
595     return d->loadingInfo;
596 }
597 
deleteLoadingInfo()598 void Map::deleteLoadingInfo()
599 {
600     delete d->loadingInfo;
601     d->loadingInfo = 0;
602 }
603 
stringCompletion()604 KCompletion& Map::stringCompletion()
605 {
606     return d->listCompletion;
607 }
608 
addStringCompletion(const QString & stringCompletion)609 void Map::addStringCompletion(const QString &stringCompletion)
610 {
611     if (d->listCompletion.items().contains(stringCompletion) == 0) {
612         d->listCompletion.addItem(stringCompletion);
613     }
614 }
615 
addDamage(Damage * damage)616 void Map::addDamage(Damage* damage)
617 {
618     // Do not create a new Damage, if we are in loading process. Check for it before
619     // calling this function. This prevents unnecessary memory allocations (new).
620     // see FIXME in Sheet::setSheetName().
621 //     Q_ASSERT(!isLoading());
622     Q_CHECK_PTR(damage);
623 
624 #ifndef NDEBUG
625     if (damage->type() == Damage::Cell) {
626         debugSheetsDamage << "Adding\t" << *static_cast<CellDamage*>(damage);
627     } else if (damage->type() == Damage::Sheet) {
628         debugSheetsDamage << "Adding\t" << *static_cast<SheetDamage*>(damage);
629     } else if (damage->type() == Damage::Selection) {
630         debugSheetsDamage << "Adding\t" << *static_cast<SelectionDamage*>(damage);
631     } else {
632         debugSheetsDamage << "Adding\t" << *damage;
633     }
634 #endif
635 
636     d->damages.append(damage);
637 
638     if (d->damages.count() == 1) {
639         QTimer::singleShot(0, this, SLOT(flushDamages()));
640     }
641 }
642 
flushDamages()643 void Map::flushDamages()
644 {
645     // Copy the damages to process. This allows new damages while processing.
646     QList<Damage*> damages = d->damages;
647     d->damages.clear();
648     emit damagesFlushed(damages);
649     qDeleteAll(damages);
650 }
651 
handleDamages(const QList<Damage * > & damages)652 void Map::handleDamages(const QList<Damage*>& damages)
653 {
654     Region bindingChangedRegion;
655     Region formulaChangedRegion;
656     Region namedAreaChangedRegion;
657     Region valueChangedRegion;
658     WorkbookDamage::Changes workbookChanges = WorkbookDamage::None;
659 
660     QList<Damage*>::ConstIterator end(damages.end());
661     for (QList<Damage*>::ConstIterator it = damages.begin(); it != end; ++it) {
662         Damage* damage = *it;
663 
664         if (damage->type() == Damage::Cell) {
665             CellDamage* cellDamage = static_cast<CellDamage*>(damage);
666             debugSheetsDamage << "Processing\t" << *cellDamage;
667             Sheet* const damagedSheet = cellDamage->sheet();
668             const Region& region = cellDamage->region();
669             const CellDamage::Changes changes = cellDamage->changes();
670 
671             // TODO Stefan: Detach the style cache from the CellView cache.
672             if ((changes.testFlag(CellDamage::Appearance))) {
673                 // Rebuild the style storage cache.
674                 damagedSheet->cellStorage()->invalidateStyleCache(); // FIXME more fine-grained
675             }
676             if ((cellDamage->changes() & CellDamage::Binding) &&
677                     !workbookChanges.testFlag(WorkbookDamage::Value)) {
678                 bindingChangedRegion.add(region, damagedSheet);
679             }
680             if ((cellDamage->changes() & CellDamage::Formula) &&
681                     !workbookChanges.testFlag(WorkbookDamage::Formula)) {
682                 formulaChangedRegion.add(region, damagedSheet);
683             }
684             if ((cellDamage->changes() & CellDamage::NamedArea) &&
685                     !workbookChanges.testFlag(WorkbookDamage::Formula)) {
686                 namedAreaChangedRegion.add(region, damagedSheet);
687             }
688             if ((cellDamage->changes() & CellDamage::Value) &&
689                     !workbookChanges.testFlag(WorkbookDamage::Value)) {
690                 valueChangedRegion.add(region, damagedSheet);
691             }
692             continue;
693         }
694 
695         if (damage->type() == Damage::Sheet) {
696             SheetDamage* sheetDamage = static_cast<SheetDamage*>(damage);
697             debugSheetsDamage << "Processing\t" << *sheetDamage;
698 //             Sheet* damagedSheet = sheetDamage->sheet();
699 
700             if (sheetDamage->changes() & SheetDamage::PropertiesChanged) {
701             }
702             continue;
703         }
704 
705         if (damage->type() == Damage::Workbook) {
706             WorkbookDamage* workbookDamage = static_cast<WorkbookDamage*>(damage);
707             debugSheetsDamage << "Processing\t" << *damage;
708 
709             workbookChanges |= workbookDamage->changes();
710             if (workbookDamage->changes() & WorkbookDamage::Formula) {
711                 formulaChangedRegion.clear();
712             }
713             if (workbookDamage->changes() & WorkbookDamage::Value) {
714                 valueChangedRegion.clear();
715             }
716             continue;
717         }
718 //         debugSheetsDamage <<"Unhandled\t" << *damage;
719     }
720 
721     // Update the named areas.
722     if (!namedAreaChangedRegion.isEmpty()) {
723         d->namedAreaManager->regionChanged(namedAreaChangedRegion);
724     }
725     // First, update the dependencies.
726     if (!formulaChangedRegion.isEmpty()) {
727         d->dependencyManager->regionChanged(formulaChangedRegion);
728     }
729     // Tell the RecalcManager which cells have had a value change.
730     if (!valueChangedRegion.isEmpty()) {
731         d->recalcManager->regionChanged(valueChangedRegion);
732     }
733     if (workbookChanges.testFlag(WorkbookDamage::Formula)) {
734         d->namedAreaManager->updateAllNamedAreas();
735         d->dependencyManager->updateAllDependencies(this);
736     }
737     if (workbookChanges.testFlag(WorkbookDamage::Value)) {
738         d->recalcManager->recalcMap();
739         d->bindingManager->updateAllBindings();
740     }
741     // Update the bindings
742     if (!bindingChangedRegion.isEmpty()) {
743         d->bindingManager->regionChanged(bindingChangedRegion);
744     }
745 }
746 
addCommand(KUndo2Command * command)747 void Map::addCommand(KUndo2Command *command)
748 {
749     emit commandAdded(command);
750 }
751 
resourceManager() const752 KoDocumentResourceManager* Map::resourceManager() const
753 {
754     if (!doc()) return 0;
755     return doc()->resourceManager();
756 }
757