1 /* This file is part of the KDE project
2  * Copyright (C) 2006-2010 Thomas Zander <zander@kde.org>
3  * Copyright (C) 2007-2010 Sebastian Sauer <mail@dipe.org>
4  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
5  * Copyright (C) 2011-2012 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "KoListStyle.h"
24 
25 #include "KoListLevelProperties.h"
26 #include "KoList.h"
27 
28 #include <KoXmlNS.h>
29 #include <KoXmlWriter.h>
30 #include <KoGenStyle.h>
31 #include "TextDebug.h"
32 #include <QBuffer>
33 
34 class Q_DECL_HIDDEN KoListStyle::Private
35 {
36 public:
Private()37     Private() : styleId(0) { }
38 
39     QString name;
40     int styleId;
41     QMap<int, KoListLevelProperties> levels;
42 };
43 
KoListStyle(QObject * parent)44 KoListStyle::KoListStyle(QObject *parent)
45         : QObject(parent), d(new Private())
46 {
47 }
48 
~KoListStyle()49 KoListStyle::~KoListStyle()
50 {
51     delete d;
52 }
53 
operator ==(const KoListStyle & other) const54 bool KoListStyle::operator==(const KoListStyle &other) const
55 {
56     Q_FOREACH (int level, d->levels.keys()) {
57         if (! other.hasLevelProperties(level))
58             return false;
59         if (!(other.levelProperties(level) == d->levels[level]))
60             return false;
61     }
62     Q_FOREACH (int level, other.d->levels.keys()) {
63         if (! hasLevelProperties(level))
64             return false;
65     }
66     return true;
67 }
68 
operator !=(const KoListStyle & other) const69 bool KoListStyle::operator!=(const KoListStyle &other) const
70 {
71     return !KoListStyle::operator==(other);
72 }
73 
copyProperties(KoListStyle * other)74 void KoListStyle::copyProperties(KoListStyle *other)
75 {
76     d->styleId = other->d->styleId;
77     d->levels = other->d->levels;
78     setName(other->name());
79 }
80 
clone(QObject * parent)81 KoListStyle *KoListStyle::clone(QObject *parent)
82 {
83     KoListStyle *newStyle = new KoListStyle(parent);
84     newStyle->copyProperties(this);
85     return newStyle;
86 }
87 
name() const88 QString KoListStyle::name() const
89 {
90     return d->name;
91 }
92 
setName(const QString & name)93 void KoListStyle::setName(const QString &name)
94 {
95     if (d->name == name)
96         return;
97     d->name = name;
98     emit nameChanged(d->name);
99 }
100 
styleId() const101 int KoListStyle::styleId() const
102 {
103     return d->styleId;
104 }
105 
setStyleId(int id)106 void KoListStyle::setStyleId(int id)
107 {
108     d->styleId = id;
109     Q_FOREACH (int level, d->levels.keys()) {
110         d->levels[level].setStyleId(id);
111     }
112 }
113 
levelProperties(int level) const114 KoListLevelProperties KoListStyle::levelProperties(int level) const
115 {
116     if (d->levels.contains(level))
117         return d->levels.value(level);
118 
119     level = qMax(1, level);
120     if (d->levels.count()) {
121         KoListLevelProperties llp = d->levels.begin().value();
122         llp.setLevel(level);
123         return llp;
124     }
125     KoListLevelProperties llp;
126     llp.setLevel(level);
127     if (d->styleId)
128         llp.setStyleId(d->styleId);
129     return llp;
130 }
131 
listFormat(int level) const132 QTextListFormat KoListStyle::listFormat(int level) const
133 {
134     KoListLevelProperties llp = levelProperties(level);
135     QTextListFormat format;
136     llp.applyStyle(format);
137     return format;
138 }
139 
setLevelProperties(const KoListLevelProperties & properties)140 void KoListStyle::setLevelProperties(const KoListLevelProperties &properties)
141 {
142     int level = qMax(1, properties.level());
143     refreshLevelProperties(properties);
144     emit styleChanged(level);
145 }
146 
refreshLevelProperties(const KoListLevelProperties & properties)147 void KoListStyle::refreshLevelProperties(const KoListLevelProperties &properties)
148 {
149     int level = qMax(1, properties.level());
150     KoListLevelProperties llp = properties;
151     if (isOulineStyle()) {
152         llp.setOutlineList(true);
153     }
154     llp.setLevel(level);
155     d->levels.insert(level, llp);
156 }
157 
hasLevelProperties(int level) const158 bool KoListStyle::hasLevelProperties(int level) const
159 {
160     return d->levels.contains(level);
161 }
162 
removeLevelProperties(int level)163 void KoListStyle::removeLevelProperties(int level)
164 {
165     d->levels.remove(level);
166 }
167 
applyStyle(const QTextBlock & block,int level)168 void KoListStyle::applyStyle(const QTextBlock &block, int level)
169 {
170     KoList::applyStyle(block, this, level);
171 }
172 
loadOdf(KoShapeLoadingContext & scontext,const KoXmlElement & style)173 void KoListStyle::loadOdf(KoShapeLoadingContext& scontext, const KoXmlElement& style)
174 {
175     d->name = style.attributeNS(KoXmlNS::style, "display-name", QString());
176     // if no style:display-name is given us the style:name
177     if (d->name.isEmpty()) {
178         d->name = style.attributeNS(KoXmlNS::style, "name", QString());
179     }
180     d->name = style.attributeNS(KoXmlNS::style, "name", QString());
181 
182     KoXmlElement styleElem;
183     forEachElement(styleElem, style) {
184         KoListLevelProperties properties;
185         properties.loadOdf(scontext, styleElem);
186         if (d->styleId)
187             properties.setStyleId(d->styleId);
188         setLevelProperties(properties);
189     }
190 
191     if (d->levels.isEmpty()) {
192         KoListLevelProperties llp;
193         llp.setLevel(1);
194         llp.setStartValue(1);
195         llp.setStyle(KoListStyle::DecimalItem);
196         llp.setListItemSuffix(".");
197         setLevelProperties(llp);
198     }
199 }
200 
saveOdf(KoGenStyle & style,KoShapeSavingContext & context) const201 void KoListStyle::saveOdf(KoGenStyle &style, KoShapeSavingContext &context) const
202 {
203     // style:display-name can be used in list styles but not in outline styles
204     if (!d->name.isEmpty() && !style.isDefaultStyle() && !isOulineStyle()) {
205         style.addAttribute("style:display-name", d->name);
206     }
207     QBuffer buffer;
208     buffer.open(QIODevice::WriteOnly);
209     KoXmlWriter elementWriter(&buffer);    // TODO pass indentation level
210     QMapIterator<int, KoListLevelProperties> it(d->levels);
211     while (it.hasNext()) {
212         it.next();
213         it.value().saveOdf(&elementWriter, context);
214     }
215     QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
216     style.addChildElement("text-list-level-style-content", elementContents);
217 }
218 
isNumberingStyle() const219 bool KoListStyle::isNumberingStyle() const
220 {
221     QMap<int, KoListLevelProperties>::const_iterator it(d->levels.constBegin());
222     for (; it != d->levels.constEnd(); ++it) {
223         if (isNumberingStyle(it.value().style())) {
224             return true;
225         }
226     }
227     return false;
228 }
229 
isNumberingStyle(int style)230 bool KoListStyle::isNumberingStyle(int style)
231 {
232     bool retval = true;
233     switch (style) {
234     case KoListStyle::SquareItem:
235     case KoListStyle::DiscItem:
236     case KoListStyle::CircleItem:
237     case KoListStyle::None:
238     case KoListStyle::Bullet:
239     case KoListStyle::BlackCircle:
240     case KoListStyle::BoxItem:
241     case KoListStyle::RhombusItem:
242     case KoListStyle::HeavyCheckMarkItem:
243     case KoListStyle::BallotXItem:
244     case KoListStyle::RightArrowItem:
245     case KoListStyle::RightArrowHeadItem:
246     case KoListStyle::CustomCharItem:
247     case KoListStyle::ImageItem:
248         retval = false;
249         break;
250     default:
251         retval = true;
252     }
253     return retval;
254 }
255 
isOulineStyle() const256 bool KoListStyle::isOulineStyle() const
257 {
258     QMap<int, KoListLevelProperties>::const_iterator it(d->levels.constBegin());
259     for (; it != d->levels.constEnd(); ++it) {
260         if (it.value().isOutlineList()) {
261             return true;
262         }
263     }
264     return false;
265 }
266 
listLevels() const267 QList<int> KoListStyle::listLevels() const
268 {
269     return d->levels.keys();
270 }
271 
bulletCharacter(int style)272 int KoListStyle::bulletCharacter(int style)
273 {
274     int bullet;
275     switch (style) {
276     case KoListStyle::Bullet:               bullet = 0x2022; break;
277     case KoListStyle::CircleItem:           bullet = 0x25CB; break;
278     case KoListStyle::RhombusItem:          bullet = 0x25C6; break;
279     case KoListStyle::SquareItem:           bullet = 0x25A0; break;
280     case KoListStyle::RightArrowHeadItem:   bullet = 0x27A2; break;
281     case KoListStyle::RightArrowItem:       bullet = 0x2794; break;
282     case KoListStyle::HeavyCheckMarkItem:   bullet = 0x2714; break;
283     case KoListStyle::BallotXItem:          bullet = 0x2717; break;
284     case KoListStyle::BlackCircle:
285     case KoListStyle::DiscItem:             bullet = 0x25CF; break;
286     default:                                bullet = 0; break; //empty character
287     }
288     return bullet;
289 }
290