1/* This file is part of the KDE project
2 Copyright (C) 2003 Norbert Andres, nandres@web.de
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20// own header
21#include "StyleManager.h"
22
23#include <QBrush>
24#include <QDomDocument>
25#include <QDomElement>
26#include <QPen>
27#include <QStringList>
28
29#include <kdebug.h>
30#include <klocale.h>
31
32#include <KoGenStyle.h>
33#include <KoOdfStylesReader.h>
34#include <KoXmlReader.h>
35#include <KoXmlNS.h>
36
37#include "CalculationSettings.h"
38#include "Condition.h"
39#include "Map.h"
40#include "Style.h"
41
42using namespace Calligra::Sheets;
43
44StyleManager::StyleManager()
45 : m_defaultStyle(new CustomStyle())
46{
47}
48
49StyleManager::~StyleManager()
50{
51 delete m_defaultStyle;
52 qDeleteAll(m_styles);
53}
54
55void StyleManager::saveOdf(KoGenStyles &mainStyles)
56{
57 kDebug(36003) << "StyleManager: Saving default cell style";
58 KoGenStyle defStyle = KoGenStyle(KoGenStyle::TableCellStyle, "table-cell");
59 defaultStyle()->saveOdf(defStyle, mainStyles, this);
60
61 m_oasisStyles.clear();
62
63 CustomStyles::ConstIterator end = m_styles.constEnd();
64 for (CustomStyles::ConstIterator it(m_styles.constBegin()); it != end; ++it) {
65 kDebug(36003) << "StyleManager: Saving common cell style" << it.key();
66 KoGenStyle customStyle = KoGenStyle(KoGenStyle::TableCellStyle, "table-cell");
67 const QString oasisName = (*it)->saveOdf(customStyle, mainStyles, this);
68 m_oasisStyles[(*it)->name()] = oasisName;
69 }
70}
71
72void StyleManager::loadOdfStyleTemplate(KoOdfStylesReader& stylesReader, Map* map)
73{
74 // reset the map of OpenDocument Styles
75 m_oasisStyles.clear();
76
77 // loading default style first
78 const KoXmlElement* defStyle = stylesReader.defaultStyle("table-cell");
79 if (defStyle) {
80 kDebug(36003) << "StyleManager: Loading default cell style";
81 Conditions conditions;
82 defaultStyle()->loadOdf(stylesReader, *defStyle, "Default", conditions, this, map->parser());
83 defaultStyle()->setType(Style::BUILTIN);
84 if (map) {
85 // Load the default precision to be used, if the (default) cell style
86 // is set to arbitrary precision.
87 KoXmlNode n = defStyle->firstChild();
88 while (!n.isNull()) {
89 if (n.isElement() &&
90 n.namespaceURI() == KoXmlNS::style &&
91 n.localName() == "table-cell-properties") {
92 KoXmlElement e = n.toElement();
93 if (n.toElement().hasAttributeNS(KoXmlNS::style, "decimal-places")) {
94 bool ok;
95 const int precision = n.toElement().attributeNS(KoXmlNS::style, "decimal-places").toInt(&ok);
96 if (ok && precision > -1) {
97 kDebug(36003) << "Default decimal precision:" << precision;
98 map->calculationSettings()->setDefaultDecimalPrecision(precision);
99 }
100 }
101 }
102 n = n.nextSibling();
103 }
104 }
105 } else
106 resetDefaultStyle();
107
108 QList<KoXmlElement*> customStyles(stylesReader.customStyles("table-cell").values());
109 uint nStyles = customStyles.count();
110 for (unsigned int item = 0; item < nStyles; item++) {
111 KoXmlElement* styleElem = customStyles[item];
112 if (!styleElem) continue;
113
114 // assume the name assigned by the application
115 const QString oasisName = styleElem->attributeNS(KoXmlNS::style, "name", QString());
116
117 // then replace by user-visible one (if any)
118 const QString name = styleElem->attributeNS(KoXmlNS::style, "display-name", oasisName);
119 kDebug(36003) << " StyleManager: Loading common cell style:" << oasisName << " (display name:" << name << ")";
120
121 if (!name.isEmpty()) {
122 // The style's parent name will be set in Style::loadOdf(..).
123 // After all styles are loaded the pointer to the parent is set.
124 CustomStyle * style = new CustomStyle(name);
125
126 Conditions conditions;
127 style->loadOdf(stylesReader, *styleElem, name, conditions, this, map->parser());
128 // TODO Stefan: conditions
129 insertStyle(style);
130 // insert it into the map sorted the OpenDocument name
131 m_oasisStyles[oasisName] = style->name();
132 kDebug(36003) << "Style" << style->name() << ":" << style;
133 }
134 }
135
136 // replace all OpenDocument internal parent names by KSpread's style names
137 foreach(CustomStyle* style, m_styles) {
138 if (!style->parentName().isNull()) {
139 const QString parentOdfName = style->parentName();
140 const CustomStyle* parentStyle = this->style(m_oasisStyles.value(parentOdfName));
141 if (!parentStyle) {
142 kWarning(36003) << parentOdfName << " not found.";
143 continue;
144 }
145 style->setParentName(m_oasisStyles.value(parentOdfName));
146 kDebug(36003) << style->name() << " (" << style << ") gets" << style->parentName() << " (" << parentOdfName << ") as parent.";
147 } else {
148 style->setParentName("Default");
149 kDebug(36003) << style->name() << " (" << style << ") has" << style->parentName() << " as parent.";
150 }
151 }
152}
153
154QDomElement StyleManager::save(QDomDocument & doc)
155{
156 QDomElement styles = doc.createElement("styles");
157
158 m_defaultStyle->save(doc, styles, this);
159
160 CustomStyles::iterator iter = m_styles.begin();
161 CustomStyles::iterator end = m_styles.end();
162
163 while (iter != end) {
164 CustomStyle * styleData = iter.value();
165
166 styleData->save(doc, styles, this);
167
168 ++iter;
169 }
170
171 return styles;
172}
173
174bool StyleManager::loadXML(KoXmlElement const & styles)
175{
176 bool ok = true;
177 KoXmlElement e = styles.firstChild().toElement();
178 while (!e.isNull()) {
179 QString name;
180 if (e.hasAttribute("name"))
181 name = e.attribute("name");
182 Style::StyleType type = (Style::StyleType)(e.attribute("type").toInt(&ok));
183 if (!ok)
184 return false;
185
186 if (name == "Default" && type == Style::BUILTIN) {
187 if (!m_defaultStyle->loadXML(e, name))
188 return false;
189 m_defaultStyle->setType(Style::BUILTIN);
190 } else if (!name.isNull()) {
191 CustomStyle* style = 0;
192 if (e.hasAttribute("parent") && e.attribute("parent") == "Default")
193 style = new CustomStyle(name, m_defaultStyle);
194 else
195 style = new CustomStyle(name);
196
197 if (!style->loadXML(e, name)) {
198 delete style;
199 return false;
200 }
201
202 if (style->type() == Style::AUTO)
203 style->setType(Style::CUSTOM);
204 insertStyle(style);
205 kDebug(36003) << "Style" << name << ":" << style;
206 }
207
208 e = e.nextSibling().toElement();
209 }
210
211 // reparent all styles
212 QStringList names = styleNames();
213 QStringList::iterator it;
214 for (it = names.begin(); it != names.end(); ++it) {
215 if (*it != "Default") {
216 CustomStyle * styleData = style(*it);
217 if (styleData && !styleData->parentName().isNull() && m_styles.value(styleData->parentName()))
218 styleData->setParentName(m_styles.value(styleData->parentName())->name());
219 }
220 }
221
222 return true;
223}
224
225void StyleManager::resetDefaultStyle()
226{
227 delete m_defaultStyle;
228 m_defaultStyle = new CustomStyle;
229}
230
231void StyleManager::createBuiltinStyles()
232{
233 CustomStyle * header1 = new CustomStyle(i18n("Header"), m_defaultStyle);
234 QFont f(header1->font());
235 f.setItalic(true);
236 f.setPointSize(f.pointSize() + 2);
237 f.setBold(true);
238 header1->setFont(f);
239 header1->setType(Style::BUILTIN);
240 m_styles[ header1->name()] = header1;
241
242 CustomStyle * header2 = new CustomStyle(i18n("Header1"), header1);
243 QColor color("#F0F0FF");
244 header2->setBackgroundColor(color);
245 QPen pen(Qt::black, 1, Qt::SolidLine);
246 header2->setBottomBorderPen(pen);
247 header2->setType(Style::BUILTIN);
248
249 m_styles[ header2->name()] = header2;
250}
251
252CustomStyle * StyleManager::style(QString const & name) const
253{
254 if (name.isEmpty())
255 return 0;
256 // on OpenDocument loading
257// if ( !m_oasisStyles.isEmpty() )
258 {
259 if (m_oasisStyles.contains(name) && m_styles.contains(m_oasisStyles[name]))
260 return m_styles.value(m_oasisStyles[name]);
261// return 0;
262 }
263 if (m_styles.contains(name))
264 return m_styles[name];
265 if ((name == "Default") || (name == m_defaultStyle->name()))
266 return m_defaultStyle;
267 return 0;
268}
269
270void StyleManager::takeStyle(CustomStyle * style)
271{
272 const QString parentName = style->parentName();
273
274 CustomStyles::iterator iter = m_styles.begin();
275 CustomStyles::iterator end = m_styles.end();
276
277 while (iter != end) {
278 if (iter.value()->parentName() == style->name())
279 iter.value()->setParentName(parentName);
280
281 ++iter;
282 }
283
284 CustomStyles::iterator i(m_styles.find(style->name()));
285
286 if (i != m_styles.end()) {
287 kDebug(36003) << "Erasing style entry for" << style->name();
288 m_styles.erase(i);
289 }
290}
291
292bool StyleManager::checkCircle(QString const & name, QString const & parent)
293{
294 CustomStyle* style = this->style(parent);
295 if (!style || style->parentName().isNull())
296 return true;
297 if (style->parentName() == name)
298 return false;
299 else
300 return checkCircle(name, style->parentName());
301}
302
303bool StyleManager::validateStyleName(QString const & name, CustomStyle * style)
304{
305 if (m_defaultStyle->name() == name || name == "Default")
306 return false;
307
308 CustomStyles::const_iterator iter = m_styles.constBegin();
309 CustomStyles::const_iterator end = m_styles.constEnd();
310
311 while (iter != end) {
312 if (iter.key() == name && iter.value() != style)
313 return false;
314
315 ++iter;
316 }
317
318 return true;
319}
320
321void StyleManager::changeName(QString const & oldName, QString const & newName)
322{
323 CustomStyles::iterator iter = m_styles.begin();
324 CustomStyles::iterator end = m_styles.end();
325
326 while (iter != end) {
327 if (iter.value()->parentName() == oldName)
328 iter.value()->setParentName(newName);
329
330 ++iter;
331 }
332
333 iter = m_styles.find(oldName);
334 if (iter != end) {
335 CustomStyle * s = iter.value();
336 m_styles.erase(iter);
337 m_styles[newName] = s;
338 }
339}
340
341void StyleManager::insertStyle(CustomStyle *style)
342{
343 const QString base = style->name();
344 // do not add the default style
345 if (base == "Default" && style->type() == Style::BUILTIN)
346 return;
347 int num = 1;
348 QString name = base;
349 while (name == "Default" || (m_styles.contains(name) && (m_styles[name] != style))) {
350 name = base;
351 name += QString::number(num++);
352 }
353 if (base != name)
354 style->setName(name);
355 m_styles[name] = style;
356}
357
358QStringList StyleManager::styleNames() const
359{
360 QStringList list;
361
362 list.push_back(i18n("Default"));
363
364 CustomStyles::const_iterator iter = m_styles.begin();
365 CustomStyles::const_iterator end = m_styles.end();
366
367 while (iter != end) {
368 list.push_back(iter.key());
369
370 ++iter;
371 }
372
373 return list;
374}
375
376Styles StyleManager::loadOdfAutoStyles(KoOdfStylesReader& stylesReader,
377 QHash<QString, Conditions>& conditionalStyles,
378 const ValueParser *parser)
379{
380 Styles autoStyles;
381 foreach(KoXmlElement* element, stylesReader.autoStyles("table-cell")) {
382 if (element->hasAttributeNS(KoXmlNS::style , "name")) {
383 QString name = element->attributeNS(KoXmlNS::style , "name" , QString());
384 kDebug(36003) << "StyleManager: Preloading automatic cell style:" << name;
385 autoStyles.remove(name);
386 Conditions conditions;
387 autoStyles[name].loadOdfStyle(stylesReader, *(element), conditions, this, parser);
388 if (!conditions.isEmpty()) {
389 kDebug() << "\t\tCONDITIONS";
390 conditionalStyles[name] = conditions;
391 }
392
393 if (element->hasAttributeNS(KoXmlNS::style, "parent-style-name")) {
394 const QString parentOdfName = element->attributeNS(KoXmlNS::style, "parent-style-name", QString());
395 const CustomStyle* parentStyle = style(m_oasisStyles.value(parentOdfName));
396 if (!parentStyle) {
397 kWarning(36003) << parentOdfName << " not found.";
398 continue;
399 }
400 autoStyles[name].setParentName(parentStyle->name());
401 kDebug(36003) << "\t parent-style-name:" << autoStyles[name].parentName();
402 }
403 }
404 }
405 return autoStyles;
406}
407
408void StyleManager::releaseUnusedAutoStyles(Styles autoStyles)
409{
410 // Just clear the list. The styles are released, if not used.
411 autoStyles.clear();
412
413 // Now, we can clear the map of styles sorted by OpenDocument name.
414 m_oasisStyles.clear();
415}
416
417QString StyleManager::openDocumentName(const QString& name) const
418{
419 return m_oasisStyles.value(name);
420}
421
422void StyleManager::dump() const
423{
424 kDebug(36006) << "Custom styles:";
425 foreach(const QString &name, m_styles.keys()) {
426 kDebug(36006) << name;
427 }
428}
429