1 /*
2  *  Copyright (c) 2011 Boudewijn Rempt <boud@valdyas.org>
3  *  Copyright (c) 2014-2015 Denis Kuplyakov <dener.kup@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "KoSection.h"
22 
23 #include <KoXmlNS.h>
24 #include <KoXmlReader.h>
25 #include <KoTextSharedLoadingData.h>
26 #include <KoShapeSavingContext.h>
27 #include <KoXmlWriter.h>
28 #include <KoSectionStyle.h>
29 #include <KoSectionModel.h>
30 #include <KoSectionEnd.h>
31 #include <KoTextDocument.h>
32 #include <KoTextInlineRdf.h>
33 
34 #include <QTextBlock>
35 
36 #include "TextDebug.h"
37 
38 class KoSectionPrivate
39 {
40 public:
KoSectionPrivate(const QTextCursor & cursor,const QString & _name,KoSection * _parent)41     explicit KoSectionPrivate(const QTextCursor &cursor, const QString &_name, KoSection *_parent)
42         : document(cursor.block().document())
43         , name(_name)
44         , sectionStyle(0)
45         , boundingCursorStart(cursor)
46         , boundingCursorEnd(cursor)
47         , parent(_parent)
48         , inlineRdf(0)
49     {
50     }
51 
52     const QTextDocument *document;
53 
54     QString condition;
55     QString display;
56     QString name;
57     QString text_protected;
58     QString protection_key;
59     QString protection_key_digest_algorithm;
60     QString style_name;
61     KoSectionStyle *sectionStyle;
62 
63     QScopedPointer<KoSectionEnd> sectionEnd; ///< pointer to the corresponding section end
64     int level; ///< level of the section in document, root sections have 0 level
65     QTextCursor boundingCursorStart; ///< This cursor points to the start of the section
66     QTextCursor boundingCursorEnd; ///< This cursor points to the end of the section (excluding paragraph symbol)
67 
68     // Boundings explanation:
69     //
70     // |S|e|c|t|i|o|n|...|t|e|x|t|P|
71     // ^                         ^
72     // |--- Start                |-- End
73 
74     QVector<KoSection *> children; ///< List of the section's childrens
75     KoSection *parent; ///< Parent of the section
76 
77     KoTextInlineRdf *inlineRdf; ///< Handling associated RDF
78 };
79 
KoSection(const QTextCursor & cursor,const QString & name,KoSection * parent)80 KoSection::KoSection(const QTextCursor &cursor, const QString &name, KoSection *parent)
81     : d_ptr(new KoSectionPrivate(cursor, name, parent))
82 {
83     Q_D(KoSection);
84 
85     d->boundingCursorStart.setKeepPositionOnInsert(true); // Start cursor should stay on place
86     d->boundingCursorEnd.setKeepPositionOnInsert(false); // and end one should move forward
87 
88     if (parent) {
89         d->level = parent->level() + 1;
90     } else {
91         d->level = 0;
92     }
93 }
94 
~KoSection()95 KoSection::~KoSection()
96 {
97     // Here scoped pointer will delete sectionEnd
98 }
99 
name() const100 QString KoSection::name() const
101 {
102     Q_D(const KoSection);
103     return d->name;
104 }
105 
bounds() const106 QPair<int, int> KoSection::bounds() const
107 {
108     Q_D(const KoSection);
109     return QPair<int, int>(
110         d->boundingCursorStart.position(),
111         d->boundingCursorEnd.position()
112     );
113 }
114 
level() const115 int KoSection::level() const
116 {
117     Q_D(const KoSection);
118     return d->level;
119 }
120 
loadOdf(const KoXmlElement & element,KoTextSharedLoadingData * sharedData,bool stylesDotXml)121 bool KoSection::loadOdf(const KoXmlElement &element, KoTextSharedLoadingData *sharedData, bool stylesDotXml)
122 {
123     Q_D(KoSection);
124     // check whether we really are a section
125     if (element.namespaceURI() == KoXmlNS::text && element.localName() == "section") {
126         // get all the attributes
127         d->condition = element.attributeNS(KoXmlNS::text, "condition");
128         d->display = element.attributeNS(KoXmlNS::text, "display");
129 
130         if (d->display == "condition" && d->condition.isEmpty()) {
131             warnText << "Section display is set to \"condition\", but condition is empty.";
132         }
133 
134         QString newName = element.attributeNS(KoXmlNS::text, "name");
135         if (!KoTextDocument(d->document).sectionModel()->setName(this, newName)) {
136             warnText << "Section name \"" << newName
137                 << "\" must be unique or is invalid. Resetting it to " << name();
138         }
139 
140         d->text_protected = element.attributeNS(KoXmlNS::text, "text-protected");
141         d->protection_key = element.attributeNS(KoXmlNS::text, "protection-key");
142         d->protection_key_digest_algorithm = element.attributeNS(KoXmlNS::text, "protection-key-algorithm");
143         d->style_name = element.attributeNS(KoXmlNS::text, "style-name", "");
144 
145         if (!d->style_name.isEmpty()) {
146             d->sectionStyle = sharedData->sectionStyle(d->style_name, stylesDotXml);
147         }
148 
149         // lets handle associated xml:id
150         if (element.hasAttribute("id")) {
151             KoTextInlineRdf* inlineRdf = new KoTextInlineRdf(const_cast<QTextDocument *>(d->document), this);
152             if (inlineRdf->loadOdf(element)) {
153                 d->inlineRdf = inlineRdf;
154             } else {
155                 delete inlineRdf;
156                 inlineRdf = 0;
157             }
158         }
159 
160         return true;
161     }
162     return false;
163 }
164 
saveOdf(KoShapeSavingContext & context) const165 void KoSection::saveOdf(KoShapeSavingContext &context) const
166 {
167     Q_D(const KoSection);
168     KoXmlWriter *writer = &context.xmlWriter();
169     Q_ASSERT(writer);
170     writer->startElement("text:section", false);
171 
172     if (!d->condition.isEmpty()) writer->addAttribute("text:condition", d->condition);
173     if (!d->display.isEmpty()) writer->addAttribute("text:display", d->condition);
174     if (!d->name.isEmpty()) writer->addAttribute("text:name", d->name);
175     if (!d->text_protected.isEmpty()) writer->addAttribute("text:text-protected", d->text_protected);
176     if (!d->protection_key.isEmpty()) writer->addAttribute("text:protection-key", d->protection_key);
177     if (!d->protection_key_digest_algorithm.isEmpty()) {
178         writer->addAttribute("text:protection-key-digest-algorithm", d->protection_key_digest_algorithm);
179     }
180     if (!d->style_name.isEmpty()) writer->addAttribute("text:style-name", d->style_name);
181 
182     if (d->inlineRdf) {
183         d->inlineRdf->saveOdf(context, writer);
184     }
185 }
186 
setSectionEnd(KoSectionEnd * sectionEnd)187 void KoSection::setSectionEnd(KoSectionEnd* sectionEnd)
188 {
189     Q_D(KoSection);
190     d->sectionEnd.reset(sectionEnd);
191 }
192 
setName(const QString & name)193 void KoSection::setName(const QString &name)
194 {
195     Q_D(KoSection);
196     d->name = name;
197 }
198 
setLevel(int level)199 void KoSection::setLevel(int level)
200 {
201     Q_D(KoSection);
202     d->level = level;
203 }
204 
setKeepEndBound(bool state)205 void KoSection::setKeepEndBound(bool state)
206 {
207     Q_D(KoSection);
208     d->boundingCursorEnd.setKeepPositionOnInsert(state);
209 }
210 
parent() const211 KoSection *KoSection::parent() const
212 {
213     Q_D(const KoSection);
214     return d->parent;
215 }
216 
children() const217 QVector<KoSection *> KoSection::children() const
218 {
219     Q_D(const KoSection);
220     return d->children;
221 }
222 
insertChild(int childIdx,KoSection * section)223 void KoSection::insertChild(int childIdx, KoSection *section)
224 {
225     Q_D(KoSection);
226     d->children.insert(childIdx, section);
227 }
228 
removeChild(int childIdx)229 void KoSection::removeChild(int childIdx)
230 {
231     Q_D(KoSection);
232     d->children.remove(childIdx);
233 }
234 
inlineRdf() const235 KoTextInlineRdf *KoSection::inlineRdf() const
236 {
237     Q_D(const KoSection);
238     return d->inlineRdf;
239 }
240 
setInlineRdf(KoTextInlineRdf * inlineRdf)241 void KoSection::setInlineRdf(KoTextInlineRdf *inlineRdf)
242 {
243     Q_D(KoSection);
244     d->inlineRdf = inlineRdf;
245 }
246