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 | |
60 | using namespace Calligra::Sheets; |
61 | |
62 | QList<DocBase*> DocBase::Private::s_docs; |
63 | int DocBase::Private::s_docId = 0; |
64 | |
65 | Q_DECLARE_METATYPE(QPointer<QAbstractItemModel>) |
66 | |
67 | DocBase::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 | |
91 | DocBase::~DocBase() |
92 | { |
93 | delete d->map; |
94 | delete d->sheetAccessModel; |
95 | delete d->resourceManager; |
96 | delete d; |
97 | } |
98 | |
99 | QList<DocBase*> DocBase::documents() |
100 | { |
101 | return Private::s_docs; |
102 | } |
103 | |
104 | void DocBase::setReadWrite(bool readwrite) |
105 | { |
106 | map()->setReadWrite(readwrite); |
107 | KoDocument::setReadWrite(readwrite); |
108 | } |
109 | |
110 | Map *DocBase::map() const |
111 | { |
112 | return d->map; |
113 | } |
114 | |
115 | int DocBase::syntaxVersion() const |
116 | { |
117 | return d->map->syntaxVersion(); |
118 | } |
119 | |
120 | KoDocumentResourceManager* DocBase::resourceManager() const |
121 | { |
122 | return d->resourceManager; |
123 | } |
124 | |
125 | SheetAccessModel *DocBase::sheetAccessModel() const |
126 | { |
127 | return d->sheetAccessModel; |
128 | } |
129 | |
130 | void DocBase::initConfig() |
131 | { |
132 | } |
133 | |
134 | bool DocBase::saveOdf(SavingContext &documentContext) |
135 | { |
136 | ElapsedTime et("OpenDocument Saving" , ElapsedTime::PrintOnlyTime); |
137 | return saveOdfHelper(documentContext, SaveAll); |
138 | } |
139 | |
140 | bool 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 | |
218 | bool 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 | |
289 | void 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 | |
300 | void 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 | |
322 | void 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 | |
332 | void DocBase::paintContent(QPainter &, const QRect &) |
333 | { |
334 | } |
335 | |
336 | bool DocBase::loadXML(const KoXmlDocument &, KoStore *) |
337 | { |
338 | return false; |
339 | } |
340 | |
341 | void DocBase::saveOdfViewSettings(KoXmlWriter&) |
342 | { |
343 | } |
344 | |
345 | void DocBase::saveOdfViewSheetSettings(Sheet *, KoXmlWriter&) |
346 | { |
347 | } |
348 | |