1 /*
2 * Copyright (c) 2012 C. Boemann <cbo@boemann.dk>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 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 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser 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 #include "ChangeStylesCommand.h"
21
22 #include "KoList.h"
23
24 #include <KoStyleManager.h>
25 #include <KoCharacterStyle.h>
26 #include <KoParagraphStyle.h>
27 #include <KoTextDocument.h>
28 #include <KoTextEditor.h>
29
30 #include <QTextDocument>
31 #include <QTextCursor>
32 #include <QTextBlock>
33
ChangeStylesCommand(QTextDocument * qDoc,const QList<KoCharacterStyle * > & origCharacterStyles,const QList<KoParagraphStyle * > & origParagraphStyles,const QSet<int> & changedStyles,KUndo2Command * parent)34 ChangeStylesCommand::ChangeStylesCommand(QTextDocument *qDoc
35 , const QList<KoCharacterStyle *> &origCharacterStyles
36 , const QList<KoParagraphStyle *> &origParagraphStyles
37 , const QSet<int> &changedStyles
38 , KUndo2Command *parent)
39 : KUndo2Command(kundo2_noi18n("stylechangecommand"),parent)
40 , m_origCharacterStyles(origCharacterStyles)
41 , m_origParagraphStyles(origParagraphStyles)
42 , m_changedStyles(changedStyles)
43 , m_document(qDoc)
44 , m_first(true)
45 {
46 // TODO optimization strategy; store the formatid of the formats we checked into
47 // a qset for 'hits' and 'ignores' and avoid the copying of the format
48 // (fragment.charFormat() / block.blockFormat()) when the formatId is
49 // already checked previously
50
51 KoStyleManager *sm = KoTextDocument(m_document).styleManager();
52 QTextCursor cursor(m_document);
53 QTextBlock block = cursor.block();
54 Memento *memento = new Memento;
55
56 while (block.isValid()) {
57 memento->blockPosition = block.position();
58 memento->blockParentCharFormat = block.charFormat();
59 memento->blockParentFormat = KoTextDocument(m_document).frameBlockFormat();
60 memento->paragraphStyleId = 0;
61
62 if (!memento->blockParentCharFormat.isTableCellFormat()) {
63 memento->blockParentCharFormat = KoTextDocument(m_document).frameCharFormat();
64 }
65
66 bool blockChanged = false;
67 int id = block.blockFormat().intProperty(KoParagraphStyle::StyleId);
68 if (id > 0 && changedStyles.contains(id)) {
69 KoParagraphStyle *style = sm->paragraphStyle(id);
70 Q_ASSERT(style);
71
72 // Calculate block format of direct formatting.
73 memento->blockDirectFormat = block.blockFormat(); // frame + style + direct
74 style->applyStyle(memento->blockParentFormat);
75 clearCommonProperties(&memento->blockDirectFormat, memento->blockParentFormat);
76
77 // Calculate char format of direct formatting.
78 memento->blockDirectCharFormat = block.charFormat(); // frame + style + direct
79 style->KoCharacterStyle::applyStyle(memento->blockParentCharFormat);
80 style->KoCharacterStyle::ensureMinimalProperties(memento->blockParentCharFormat);
81 clearCommonProperties(&memento->blockDirectCharFormat, memento->blockParentCharFormat);
82
83 memento->paragraphStyleId = id;
84 blockChanged = true;
85 }
86
87 QTextBlock::iterator iter = block.begin();
88 while (!iter.atEnd()) {
89 QTextFragment fragment = iter.fragment();
90 QTextCharFormat cf(fragment.charFormat());
91 id = cf.intProperty(KoCharacterStyle::StyleId);
92 if (blockChanged || (id > 0 && changedStyles.contains(id))) {
93 // create selection
94 cursor.setPosition(fragment.position());
95 cursor.setPosition(fragment.position() + fragment.length(), QTextCursor::KeepAnchor);
96 QTextCharFormat blockCharFormat = block.charFormat(); // with old parstyle applied
97
98 KoCharacterStyle *style = sm->characterStyle(id);
99 if (style) {
100 style->applyStyle(blockCharFormat);
101 style->ensureMinimalProperties(blockCharFormat);
102 }
103
104 clearCommonProperties(&cf, blockCharFormat);
105
106 memento->fragmentStyleId.append(id);
107 memento->fragmentDirectFormats.append(cf);
108 memento->fragmentCursors.append(cursor);
109 }
110 ++iter;
111 }
112 if (blockChanged || memento->fragmentCursors.length()) {
113 m_mementos.append(memento);
114 memento = new Memento;
115 }
116 block = block.next();
117 }
118
119 delete memento; // we always have one that is unused
120 }
121
~ChangeStylesCommand()122 ChangeStylesCommand::~ChangeStylesCommand()
123 {
124 }
125
redo()126 void ChangeStylesCommand::redo()
127 {
128 KUndo2Command::redo();
129
130 if (m_first) {
131 m_first = false;
132 KoStyleManager *sm = KoTextDocument(m_document).styleManager();
133
134 QTextCursor cursor(m_document);
135 foreach (Memento *memento, m_mementos) {
136
137 cursor.setPosition(memento->blockPosition);
138 QTextBlock block = cursor.block();
139
140 if (memento->paragraphStyleId > 0) {
141 KoParagraphStyle *style = sm->paragraphStyle(memento->paragraphStyleId);
142 Q_ASSERT(style);
143
144 // apply paragraph style with direct formatting on top.
145 style->applyStyle(memento->blockParentFormat);
146 memento->blockParentFormat.merge(memento->blockDirectFormat);
147 cursor.setBlockFormat(memento->blockParentFormat);
148
149 // apply list style formatting
150 if (KoTextDocument(m_document).list(block.textList())) {
151 if (style->list() == KoTextDocument(m_document).list(block.textList())) {
152 style->applyParagraphListStyle(block, memento->blockParentFormat);
153 }
154 } else {
155 style->applyParagraphListStyle(block, memento->blockParentFormat);
156 }
157
158 // apply character style with direct formatting on top.
159 style->KoCharacterStyle::applyStyle(memento->blockParentCharFormat);
160 style->KoCharacterStyle::ensureMinimalProperties(memento->blockParentCharFormat);
161 memento->blockParentCharFormat.merge(memento->blockDirectCharFormat);
162
163 cursor.setBlockCharFormat(memento->blockParentCharFormat);
164 }
165
166 QList<QTextCharFormat>::Iterator fmtIt = memento->fragmentDirectFormats.begin();
167 QList<int>::Iterator idIt = memento->fragmentStyleId.begin();
168 Q_FOREACH (QTextCursor fragCursor, memento->fragmentCursors) {
169 QTextCharFormat cf(block.charFormat()); // start with block formatting
170
171 if (*idIt > 0) {
172 KoCharacterStyle *style = sm->characterStyle(*idIt);
173 if (style) {
174 style->applyStyle(cf); // possibly apply charstyle formatting
175 }
176 }
177
178 cf.merge(*fmtIt); //apply direct formatting
179
180 fragCursor.setCharFormat(cf);
181
182 ++idIt;
183 ++fmtIt;
184 }
185 }
186 qDeleteAll(m_mementos);
187 m_mementos.clear();
188 }
189 }
190
undo()191 void ChangeStylesCommand::undo()
192 {
193 KUndo2Command::undo();
194 }
195
196
clearCommonProperties(QTextFormat * firstFormat,const QTextFormat & secondFormat)197 void ChangeStylesCommand::clearCommonProperties(QTextFormat *firstFormat, const QTextFormat &secondFormat)
198 {
199 Q_ASSERT(firstFormat);
200 Q_FOREACH (int key, secondFormat.properties().keys()) {
201 if (firstFormat->property(key) == secondFormat.property(key)) {
202 firstFormat->clearProperty(key);
203 }
204 }
205 }
206
207