1/* This file is part of the KDE project
2 Copyright 2010 Marijn Kruisselbrink <mkruisselbrink@kde.org>
3 Copyright 2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
4 Copyright 2007 Thorsten Zachmann <zachmann@kde.org>
5 Copyright 2005-2006 Inge Wallin <inge@lysator.liu.se>
6 Copyright 2004 Ariya Hidayat <ariya@kde.org>
7 Copyright 2002-2003 Norbert Andres <nandres@web.de>
8 Copyright 2000-2002 Laurent Montel <montel@kde.org>
9 Copyright 2002 John Dailey <dailey@vt.edu>
10 Copyright 2002 Phillip Mueller <philipp.mueller@gmx.de>
11 Copyright 2000 Werner Trobin <trobin@kde.org>
12 Copyright 1999-2000 Simon Hausmann <hausmann@kde.org>
13 Copyright 1999 David Faure <faure@kde.org>
14 Copyright 1998-2000 Torben Weis <weis@kde.org>
15
16 This library is free software; you can redistribute it and/or
17 modify it under the terms of the GNU Library General Public
18 License as published by the Free Software Foundation; either
19 version 2 of the License, or (at your option) any later version.
20
21 This library is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 Library General Public License for more details.
25
26 You should have received a copy of the GNU Library General Public License
27 along with this library; see the file COPYING.LIB. If not, write to
28 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 Boston, MA 02110-1301, USA.
30*/
31#include "DocBase.h"
32#include "DocBase_p.h"
33#include "DocBase.moc"
34
35#include <KoOasisSettings.h>
36#include <KoOdfLoadingContext.h>
37#include <KoOdfReadStore.h>
38#include <KoOdfWriteStore.h>
39#include <KoProgressUpdater.h>
40#include <KoDocumentResourceManager.h>
41#include <KoShapeRegistry.h>
42#include <KoShapeSavingContext.h>
43#include <KoStoreDevice.h>
44#include <KoGenStyles.h>
45#include <KoUpdater.h>
46#include <KoXmlNS.h>
47#include <KoXmlWriter.h>
48#include <KoPart.h>
49
50#include "calligra_sheets_limits.h"
51#include "BindingModel.h"
52#include "CalculationSettings.h"
53#include "Map.h"
54#include "SheetAccessModel.h"
55
56#include "ElapsedTime_p.h"
57
58#include "part/View.h" // TODO: get rid of this dependency
59
60using namespace Calligra::Sheets;
61
62QList<DocBase*> DocBase::Private::s_docs;
63int DocBase::Private::s_docId = 0;
64
65Q_DECLARE_METATYPE(QPointer<QAbstractItemModel>)
66
67DocBase::DocBase(KoPart *part)
68 : KoDocument(part)
69 , d(new Private)
70{
71 Q_ASSERT(part);
72 d->resourceManager = new KoDocumentResourceManager();
73 d->map = new Map(this, CURRENT_SYNTAX_VERSION);
74
75 // Document Url for FILENAME function and page header/footer.
76 d->map->calculationSettings()->setFileName(url().prettyUrl());
77
78 KoShapeRegistry *registry = KoShapeRegistry::instance();
79 foreach (const QString &id, registry->keys()) {
80 KoShapeFactoryBase *shapeFactory = registry->value(id);
81 shapeFactory->newDocumentResourceManager(d->resourceManager);
82 }
83
84 d->configLoadFromFile = false;
85
86 documents().append(this);
87
88 d->sheetAccessModel = new SheetAccessModel(d->map);
89}
90
91DocBase::~DocBase()
92{
93 delete d->map;
94 delete d->sheetAccessModel;
95 delete d->resourceManager;
96 delete d;
97}
98
99QList<DocBase*> DocBase::documents()
100{
101 return Private::s_docs;
102}
103
104void DocBase::setReadWrite(bool readwrite)
105{
106 map()->setReadWrite(readwrite);
107 KoDocument::setReadWrite(readwrite);
108}
109
110Map *DocBase::map() const
111{
112 return d->map;
113}
114
115int DocBase::syntaxVersion() const
116{
117 return d->map->syntaxVersion();
118}
119
120KoDocumentResourceManager* DocBase::resourceManager() const
121{
122 return d->resourceManager;
123}
124
125SheetAccessModel *DocBase::sheetAccessModel() const
126{
127 return d->sheetAccessModel;
128}
129
130void DocBase::initConfig()
131{
132}
133
134bool DocBase::saveOdf(SavingContext &documentContext)
135{
136 ElapsedTime et("OpenDocument Saving", ElapsedTime::PrintOnlyTime);
137 return saveOdfHelper(documentContext, SaveAll);
138}
139
140bool DocBase::saveOdfHelper(SavingContext & documentContext, SaveFlag saveFlag,
141 QString* /*plainText*/)
142{
143 Q_UNUSED(saveFlag);
144 KoStore * store = documentContext.odfStore.store();
145 KoXmlWriter * manifestWriter = documentContext.odfStore.manifestWriter();
146
147 KoStoreDevice dev(store);
148 KoGenStyles mainStyles;//for compile
149
150 KoXmlWriter* contentWriter = documentContext.odfStore.contentWriter();
151 if (!contentWriter) {
152 return false;
153 }
154
155 // Document Url for FILENAME function and page header/footer.
156 d->map->calculationSettings()->setFileName(url().prettyUrl());
157
158 KoXmlWriter* bodyWriter = documentContext.odfStore.bodyWriter();
159 KoShapeSavingContext savingContext(*bodyWriter, mainStyles, documentContext.embeddedSaver);
160
161 //todo fixme just add a element for testing saving content.xml
162 bodyWriter->startElement("office:body");
163 bodyWriter->startElement("office:spreadsheet");
164
165 // Saving the map.
166 map()->saveOdf(*bodyWriter, savingContext);
167
168 bodyWriter->endElement(); ////office:spreadsheet
169 bodyWriter->endElement(); ////office:body
170
171 // Done with writing out the contents to the tempfile, we can now write out the automatic styles
172 mainStyles.saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
173
174 documentContext.odfStore.closeContentWriter();
175
176 //add manifest line for content.xml
177 manifestWriter->addManifestEntry("content.xml", "text/xml");
178
179 mainStyles.saveOdfStylesDotXml(store, manifestWriter);
180
181 if (!store->open("settings.xml"))
182 return false;
183
184 KoXmlWriter* settingsWriter = KoOdfWriteStore::createOasisXmlWriter(&dev, "office:document-settings");
185 settingsWriter->startElement("office:settings");
186 settingsWriter->startElement("config:config-item-set");
187 settingsWriter->addAttribute("config:name", "view-settings");
188
189 saveUnitOdf(settingsWriter);
190
191 saveOdfSettings(*settingsWriter);
192
193 settingsWriter->endElement(); // config:config-item-set
194
195 settingsWriter->startElement("config:config-item-set");
196 settingsWriter->addAttribute("config:name", "configuration-settings");
197 settingsWriter->addConfigItem("SpellCheckerIgnoreList", d->spellListIgnoreAll.join(","));
198 settingsWriter->endElement(); // config:config-item-set
199 settingsWriter->endElement(); // office:settings
200 settingsWriter->endElement(); // Root:element
201 settingsWriter->endDocument();
202 delete settingsWriter;
203
204 if (!store->close())
205 return false;
206
207 if (!savingContext.saveDataCenter(store, manifestWriter)) {
208 return false;
209 }
210
211 manifestWriter->addManifestEntry("settings.xml", "text/xml");
212
213 setModified(false);
214
215 return true;
216}
217
218bool DocBase::loadOdf(KoOdfReadStore & odfStore)
219{
220 QPointer<KoUpdater> updater;
221 if (progressUpdater()) {
222 updater = progressUpdater()->startSubtask(1, "Calligra::Sheets::DocBase::loadOdf");
223 updater->setProgress(0);
224 }
225
226 d->spellListIgnoreAll.clear();
227
228 KoXmlElement content = odfStore.contentDoc().documentElement();
229 KoXmlElement realBody(KoXml::namedItemNS(content, KoXmlNS::office, "body"));
230 if (realBody.isNull()) {
231 setErrorMessage(i18n("Invalid OASIS OpenDocument file. No office:body tag found."));
232 map()->deleteLoadingInfo();
233 return false;
234 }
235 KoXmlElement body = KoXml::namedItemNS(realBody, KoXmlNS::office, "spreadsheet");
236
237 if (body.isNull()) {
238 kError(32001) << "No office:spreadsheet found!" << endl;
239 KoXmlElement childElem;
240 QString localName;
241 forEachElement(childElem, realBody) {
242 localName = childElem.localName();
243 }
244 if (localName.isEmpty())
245 setErrorMessage(i18n("Invalid OASIS OpenDocument file. No tag found inside office:body."));
246 else
247 setErrorMessage(i18n("This document is not a spreadsheet, but %1. Please try opening it with the appropriate application." , KoDocument::tagNameToDocumentType(localName)));
248 map()->deleteLoadingInfo();
249 return false;
250 }
251
252 // Document Url for FILENAME function and page header/footer.
253 d->map->calculationSettings()->setFileName(url().prettyUrl());
254
255 KoOdfLoadingContext context(odfStore.styles(), odfStore.store());
256
257 // TODO check versions and mimetypes etc.
258
259 // all <sheet:sheet> goes to workbook
260 if (!map()->loadOdf(body, context)) {
261 map()->deleteLoadingInfo();
262 return false;
263 }
264
265 if (!odfStore.settingsDoc().isNull()) {
266 loadOdfSettings(odfStore.settingsDoc());
267 }
268 initConfig();
269
270 //update plugins that rely on bindings, as loading order can mess up the data of the plugins
271 SheetAccessModel* sheetModel = sheetAccessModel();
272 QList< Sheet* > sheets = map()->sheetList();
273 Q_FOREACH( Sheet* sheet, sheets ){
274 // This region contains the entire sheet
275 const QRect region (0, 0, KS_colMax - 1, KS_rowMax - 1);
276 QModelIndex index = sheetModel->index( 0, map()->indexOf( sheet ) );
277 QVariant bindingModelValue = sheetModel->data( index , Qt::DisplayRole );
278 BindingModel *curBindingModel = dynamic_cast< BindingModel* >( qvariant_cast< QPointer< QAbstractItemModel > >( bindingModelValue ).data() );
279 if ( curBindingModel ){
280 curBindingModel->emitDataChanged( region );
281 }
282 }
283
284 if (updater) updater->setProgress(100);
285
286 return true;
287}
288
289void DocBase::loadOdfSettings(const KoXmlDocument&settingsDoc)
290{
291 KoOasisSettings settings(settingsDoc);
292 KoOasisSettings::Items viewSettings = settings.itemSet("view-settings");
293 if (!viewSettings.isNull()) {
294 setUnit(KoUnit::fromSymbol(viewSettings.parseConfigItemString("unit")));
295 }
296 map()->loadOdfSettings(settings);
297 loadOdfIgnoreList(settings);
298}
299
300void DocBase::saveOdfSettings(KoXmlWriter &settingsWriter)
301{
302 settingsWriter.startElement("config:config-item-map-indexed");
303 settingsWriter.addAttribute("config:name", "Views");
304 settingsWriter.startElement("config:config-item-map-entry");
305 settingsWriter.addConfigItem("ViewId", QString::fromLatin1("View1"));
306 saveOdfViewSettings(settingsWriter);
307 //<config:config-item-map-named config:name="Tables">
308 settingsWriter.startElement("config:config-item-map-named");
309 settingsWriter.addAttribute("config:name", "Tables");
310 foreach (Sheet *sheet, map()->sheetList()) {
311 settingsWriter.startElement("config:config-item-map-entry");
312 settingsWriter.addAttribute("config:name", sheet->sheetName());
313 saveOdfViewSheetSettings(sheet, settingsWriter);
314 sheet->saveOdfSettings(settingsWriter);
315 settingsWriter.endElement();
316 }
317 settingsWriter.endElement();
318 settingsWriter.endElement();
319 settingsWriter.endElement();
320}
321
322void DocBase::loadOdfIgnoreList(const KoOasisSettings& settings)
323{
324 KoOasisSettings::Items configurationSettings = settings.itemSet("configuration-settings");
325 if (!configurationSettings.isNull()) {
326 const QString ignorelist = configurationSettings.parseConfigItemString("SpellCheckerIgnoreList");
327 //kDebug()<<" ignorelist :"<<ignorelist;
328 d->spellListIgnoreAll = ignorelist.split(',', QString::SkipEmptyParts);
329 }
330}
331
332void DocBase::paintContent(QPainter &, const QRect &)
333{
334}
335
336bool DocBase::loadXML(const KoXmlDocument &, KoStore *)
337{
338 return false;
339}
340
341void DocBase::saveOdfViewSettings(KoXmlWriter&)
342{
343}
344
345void DocBase::saveOdfViewSheetSettings(Sheet *, KoXmlWriter&)
346{
347}
348