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 | |
42 | using namespace Calligra::Sheets; |
43 | |
44 | StyleManager::StyleManager() |
45 | : m_defaultStyle(new CustomStyle()) |
46 | { |
47 | } |
48 | |
49 | StyleManager::~StyleManager() |
50 | { |
51 | delete m_defaultStyle; |
52 | qDeleteAll(m_styles); |
53 | } |
54 | |
55 | void 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 | |
72 | void 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 | |
154 | QDomElement 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 | |
174 | bool 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 | |
225 | void StyleManager::resetDefaultStyle() |
226 | { |
227 | delete m_defaultStyle; |
228 | m_defaultStyle = new CustomStyle; |
229 | } |
230 | |
231 | void StyleManager::createBuiltinStyles() |
232 | { |
233 | CustomStyle * = 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 * = 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 | |
252 | CustomStyle * 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 | |
270 | void 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 | |
292 | bool 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 | |
303 | bool 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 | |
321 | void 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 | |
341 | void 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 | |
358 | QStringList 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 | |
376 | Styles 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 | |
408 | void 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 | |
417 | QString StyleManager::openDocumentName(const QString& name) const |
418 | { |
419 | return m_oasisStyles.value(name); |
420 | } |
421 | |
422 | void StyleManager::dump() const |
423 | { |
424 | kDebug(36006) << "Custom styles:" ; |
425 | foreach(const QString &name, m_styles.keys()) { |
426 | kDebug(36006) << name; |
427 | } |
428 | } |
429 | |