Rev 18330 | Rev 18525 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8174 | avox | 1 | /* |
2 | For general Scribus (>=1.3.2) copyright and licensing information please refer |
||
3 | to the COPYING file provided with the program. Following this notice may exist |
||
4 | a copyright and/or license notice that predates the release of Scribus 1.3.2 |
||
5 | for which a new license (GPL+exception) is in place. |
||
6 | */ |
||
7 | /*************************************************************************** |
||
8 | pageitem.cpp - description |
||
9 | ------------------- |
||
10 | begin : Sat Apr 7 2001 |
||
11 | copyright : (C) 2001 by Franz Schmid |
||
12 | email : Franz.Schmid@altmuehlnet.de |
||
8931 | cbradney | 13 | ***************************************************************************/ |
8174 | avox | 14 | |
15 | /*************************************************************************** |
||
16 | * * |
||
17 | * This program is free software; you can redistribute it and/or modify * |
||
18 | * it under the terms of the GNU General Public License as published by * |
||
19 | * the Free Software Foundation; either version 2 of the License, or * |
||
20 | * (at your option) any later version. * |
||
21 | * * |
||
22 | ***************************************************************************/ |
||
23 | |||
13085 | jghali | 24 | //#include <QDebug> |
4777 | avox | 25 | //FIXME: this include must go to sctextstruct.h ! |
9803 | fschmid | 26 | #include <QList> |
5105 | mrdocs | 27 | #include <cassert> //added to make Fedora-5 happy |
4669 | avox | 28 | #include "fpoint.h" |
17826 | craig | 29 | #include "notesstyles.h" |
5980 | avox | 30 | #include "scfonts.h" |
7970 | cbradney | 31 | #include "scribusdoc.h" |
32 | #include "sctext_shared.h" |
||
17826 | craig | 33 | #include "selection.h" |
4669 | avox | 34 | #include "storytext.h" |
35 | #include "scribus.h" |
||
5184 | avox | 36 | #include "util.h" |
8455 | avox | 37 | #include "resourcecollection.h" |
8266 | avox | 38 | #include "desaxe/saxiohelper.h" |
39 | #include "desaxe/digester.h" |
||
40 | #include "desaxe/simple_actions.h" |
||
4669 | avox | 41 | |
42 | |||
18330 | jghali | 43 | StoryText::StoryText(ScribusDoc * doc_) : m_doc(doc_) |
5753 | jghali | 44 | { |
6733 | avox | 45 | if (doc_) { |
7442 | avox | 46 | d = new ScText_Shared(&doc_->paragraphStyles()); |
18330 | jghali | 47 | m_doc->paragraphStyles().connect(this, SLOT(invalidateAll())); |
48 | m_doc->charStyles().connect(this, SLOT(invalidateAll())); |
||
6733 | avox | 49 | } |
50 | else { |
||
7442 | avox | 51 | d = new ScText_Shared(NULL); |
6733 | avox | 52 | } |
18330 | jghali | 53 | m_selFirst = 0; |
54 | m_selLast = -1; |
||
7002 | avox | 55 | |
18330 | jghali | 56 | m_firstFrameItem = 0; |
57 | m_lastFrameItem = -1; |
||
7501 | avox | 58 | m_magicX = 0.0; |
59 | m_lastMagicPos = -1; |
||
7002 | avox | 60 | |
61 | d->len = 0; |
||
62 | invalidateAll(); |
||
4669 | avox | 63 | } |
64 | |||
18330 | jghali | 65 | StoryText::StoryText() : m_doc(NULL) |
6144 | avox | 66 | { |
7442 | avox | 67 | d = new ScText_Shared(NULL); |
7501 | avox | 68 | |
18330 | jghali | 69 | m_selFirst = 0; |
70 | m_selLast = -1; |
||
7501 | avox | 71 | |
18330 | jghali | 72 | m_firstFrameItem = 0; |
73 | m_lastFrameItem = -1; |
||
7501 | avox | 74 | m_magicX = 0.0; |
75 | m_lastMagicPos = -1; |
||
6144 | avox | 76 | } |
77 | |||
18330 | jghali | 78 | StoryText::StoryText(const StoryText & other) : QObject(), SaxIO(), m_doc(other.m_doc) |
4669 | avox | 79 | { |
5721 | avox | 80 | d = other.d; |
81 | d->refs++; |
||
82 | |||
18330 | jghali | 83 | if (m_doc) { |
84 | m_doc->paragraphStyles().connect(this, SLOT(invalidateAll())); |
||
85 | m_doc->charStyles().connect(this, SLOT(invalidateAll())); |
||
7958 | avox | 86 | } |
87 | |||
18330 | jghali | 88 | m_selFirst = 0; |
89 | m_selLast = -1; |
||
4706 | avox | 90 | |
18330 | jghali | 91 | m_firstFrameItem = 0; |
92 | m_lastFrameItem = -1; |
||
7501 | avox | 93 | m_magicX = 0.0; |
94 | m_lastMagicPos = -1; |
||
5721 | avox | 95 | |
4669 | avox | 96 | invalidateLayout(); |
97 | } |
||
98 | |||
99 | StoryText::~StoryText() |
||
100 | { |
||
9476 | fschmid | 101 | /* Code below is not needed imho, as all connections will be disconnected automatically |
102 | by the QObject destructor. At least removing that code fixes a crash when closing |
||
103 | documents */ |
||
104 | /* if (doc) |
||
7958 | avox | 105 | { |
106 | doc->paragraphStyles().disconnect(this, SLOT(invalidateAll())); |
||
107 | doc->charStyles().disconnect(this, SLOT(invalidateAll())); |
||
9476 | fschmid | 108 | } */ |
5721 | avox | 109 | d->refs--; |
110 | if (d->refs == 0) { |
||
5773 | avox | 111 | d->clear(); |
112 | d->len = 0; |
||
5721 | avox | 113 | delete d; |
114 | } |
||
4669 | avox | 115 | } |
116 | |||
17408 | fschmid | 117 | void StoryText::setDoc(ScribusDoc *docin) |
118 | { |
||
18330 | jghali | 119 | m_doc = docin; |
17408 | fschmid | 120 | } |
121 | |||
6144 | avox | 122 | StoryText StoryText::copy() const |
123 | { |
||
18330 | jghali | 124 | StoryText result(m_doc); |
6144 | avox | 125 | *(result.d) = *d; |
7786 | avox | 126 | return result; |
6144 | avox | 127 | } |
128 | |||
4669 | avox | 129 | StoryText& StoryText::operator= (const StoryText & other) |
130 | { |
||
7786 | avox | 131 | other.d->refs++; |
132 | |||
5721 | avox | 133 | d->refs--; |
134 | if (d->refs == 0) { |
||
135 | clear(); |
||
136 | delete d; |
||
137 | } |
||
138 | |||
18330 | jghali | 139 | if (m_doc) { |
140 | m_doc->paragraphStyles().disconnect(this, SLOT(invalidateAll())); |
||
141 | m_doc->charStyles().disconnect(this, SLOT(invalidateAll())); |
||
7958 | avox | 142 | } |
143 | |||
18330 | jghali | 144 | m_doc = other.m_doc; |
5721 | avox | 145 | d = other.d; |
146 | |||
18330 | jghali | 147 | if (m_doc) { |
148 | m_doc->paragraphStyles().connect(this, SLOT(invalidateAll())); |
||
149 | m_doc->charStyles().connect(this, SLOT(invalidateAll())); |
||
7958 | avox | 150 | } |
151 | |||
18330 | jghali | 152 | m_selFirst = 0; |
153 | m_selLast = -1; |
||
4706 | avox | 154 | |
18330 | jghali | 155 | m_firstFrameItem = 0; |
156 | m_lastFrameItem = -1; |
||
4706 | avox | 157 | |
4669 | avox | 158 | invalidateLayout(); |
159 | return *this; |
||
160 | } |
||
161 | |||
16639 | jghali | 162 | int StoryText::cursorPosition() const |
163 | { |
||
164 | return d->cursorPosition; |
||
165 | } |
||
4669 | avox | 166 | |
16639 | jghali | 167 | void StoryText::setCursorPosition(int pos, bool relative) |
168 | { |
||
169 | if (relative) |
||
170 | pos += d->cursorPosition; |
||
171 | d->cursorPosition = qMin((uint) qMax(pos, 0), d->len); |
||
172 | } |
||
173 | |||
174 | void StoryText::normalizeCursorPosition() |
||
175 | { |
||
176 | d->cursorPosition = qMax((uint) 0, qMin(d->cursorPosition, d->len)); |
||
177 | } |
||
178 | |||
179 | int StoryText::normalizedCursorPosition() |
||
180 | { |
||
181 | return (int) qMax((uint) 0, qMin(d->cursorPosition, d->len)); |
||
182 | } |
||
183 | |||
4669 | avox | 184 | void StoryText::clear() |
185 | { |
||
18330 | jghali | 186 | m_selFirst = 0; |
187 | m_selLast = -1; |
||
4669 | avox | 188 | |
18330 | jghali | 189 | m_firstFrameItem = 0; |
190 | m_lastFrameItem = -1; |
||
4669 | avox | 191 | |
6733 | avox | 192 | d->defaultStyle.erase(); |
7232 | avox | 193 | d->trailingStyle.erase(); |
17231 | fschmid | 194 | |
5721 | avox | 195 | d->clear(); |
5722 | avox | 196 | d->len = 0; |
4669 | avox | 197 | invalidateAll(); |
198 | } |
||
199 | |||
17541 | jghali | 200 | int StoryText::indexOf(const QString &str, int from, Qt::CaseSensitivity cs) const |
201 | { |
||
202 | int foundIndex = -1; |
||
203 | |||
204 | if (str.isEmpty() || (from < 0)) |
||
205 | return -1; |
||
206 | |||
207 | int strLen = str.length(); |
||
208 | int storyLen = length(); |
||
209 | |||
210 | QString qStr = str; |
||
211 | if (cs == Qt::CaseInsensitive) |
||
212 | qStr = qStr.toLower(); |
||
213 | QChar ch = qStr.at(0); |
||
214 | |||
215 | if (cs == Qt::CaseSensitive) |
||
216 | { |
||
217 | int i = indexOf(ch, from, cs); |
||
218 | while (i >= 0 && i < (int) d->len) |
||
219 | { |
||
220 | int index = 0; |
||
221 | while ((index < strLen) && ((index + i) < storyLen)) |
||
222 | { |
||
223 | if (qStr.at(index) != d->at(index + i)->ch) |
||
224 | break; |
||
225 | ++index; |
||
226 | } |
||
227 | if (index == strLen) |
||
228 | { |
||
229 | foundIndex = i; |
||
230 | break; |
||
231 | } |
||
232 | i = indexOf(ch, i + 1, cs); |
||
233 | } |
||
234 | } |
||
235 | else |
||
236 | { |
||
237 | int i = indexOf(ch, from, cs); |
||
238 | while (i >= 0 && i < (int) d->len) |
||
239 | { |
||
240 | int index = 0; |
||
241 | while ((index < strLen) && ((index + i) < storyLen)) |
||
242 | { |
||
243 | if (qStr.at(index) != d->at(index + i)->ch.toLower()) |
||
244 | break; |
||
245 | ++index; |
||
246 | } |
||
247 | if (index == strLen) |
||
248 | { |
||
249 | foundIndex = i; |
||
250 | break; |
||
251 | } |
||
252 | i = indexOf(ch, i + 1, cs); |
||
253 | } |
||
254 | } |
||
255 | |||
256 | return foundIndex; |
||
257 | } |
||
258 | |||
259 | int StoryText::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const |
||
260 | { |
||
261 | int foundIndex = -1; |
||
262 | int textLength = length(); |
||
263 | |||
264 | if (cs == Qt::CaseSensitive) |
||
265 | { |
||
266 | for (int i = from; i < textLength; ++i) |
||
267 | { |
||
268 | if (d->at(i)->ch == ch) |
||
269 | { |
||
270 | foundIndex = i; |
||
271 | break; |
||
272 | } |
||
273 | } |
||
274 | } |
||
275 | else |
||
276 | { |
||
277 | for (int i = from; i < textLength; ++i) |
||
278 | { |
||
279 | if (d->at(i)->ch.toLower() == ch) |
||
280 | { |
||
281 | foundIndex = i; |
||
282 | break; |
||
283 | } |
||
284 | } |
||
285 | } |
||
286 | return foundIndex; |
||
287 | } |
||
288 | |||
16626 | jghali | 289 | void StoryText::insert(const StoryText& other, bool onlySelection) |
290 | { |
||
291 | insert(d->cursorPosition, other, onlySelection); |
||
292 | } |
||
4669 | avox | 293 | |
6887 | avox | 294 | void StoryText::insert(int pos, const StoryText& other, bool onlySelection) |
5732 | avox | 295 | { |
8049 | avox | 296 | if (pos < 0) |
297 | pos += length()+1; |
||
298 | |||
6977 | avox | 299 | CharStyle cstyle(charStyle(pos)); |
300 | ParagraphStyle pstyle(paragraphStyle(pos)); |
||
7242 | avox | 301 | |
302 | // this style represents all differences between this and other's defaultstyles |
||
303 | ParagraphStyle otherDefault(other.defaultStyle()); |
||
304 | otherDefault.eraseStyle(defaultStyle()); |
||
305 | |||
6887 | avox | 306 | int otherStart = onlySelection? other.startOfSelection() : 0; |
307 | int otherEnd = onlySelection? other.endOfSelection() : other.length(); |
||
308 | int cstyleStart = otherStart; |
||
309 | for (int i=otherStart; i < otherEnd; ++i) { |
||
310 | if (other.charStyle(i) == cstyle |
||
311 | && other.text(i) != SpecialChars::OBJECT |
||
312 | && other.text(i) != SpecialChars::PARSEP) |
||
5732 | avox | 313 | continue; |
314 | int len = i - cstyleStart; |
||
315 | if (len > 0) { |
||
15009 | jghali | 316 | insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len)); |
7242 | avox | 317 | applyCharStyle(pos, len, otherDefault.charStyle()); |
5732 | avox | 318 | applyCharStyle(pos, len, cstyle); |
6887 | avox | 319 | pos += len; |
5732 | avox | 320 | } |
321 | if (other.text(i) == SpecialChars::PARSEP) { |
||
6887 | avox | 322 | insertChars(pos, SpecialChars::PARSEP); |
12056 | jghali | 323 | //#5845 : disable for now as it has nasty side effects when linking frames |
324 | //applyStyle(pos, otherDefault); |
||
6887 | avox | 325 | applyStyle(pos, other.paragraphStyle(i)); |
5732 | avox | 326 | cstyleStart = i+1; |
6887 | avox | 327 | pos += 1; |
5732 | avox | 328 | } |
6887 | avox | 329 | else if (other.text(i) == SpecialChars::OBJECT) { |
8174 | avox | 330 | insertChars(pos, SpecialChars::OBJECT); |
331 | item(pos)->embedded = other.item(i)->embedded; |
||
17826 | craig | 332 | item(pos)->mark = other.item(i)->mark; |
16600 | jghali | 333 | applyCharStyle(pos, 1, other.charStyle(i)); |
6887 | avox | 334 | cstyleStart = i+1; |
335 | pos += 1; |
||
336 | } |
||
5732 | avox | 337 | else { |
338 | cstyle = other.charStyle(i); |
||
339 | cstyleStart = i; |
||
340 | } |
||
341 | } |
||
6887 | avox | 342 | int len = otherEnd - cstyleStart; |
5732 | avox | 343 | if (len > 0) { |
15009 | jghali | 344 | insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len)); |
7242 | avox | 345 | applyCharStyle(pos, len, otherDefault.charStyle()); |
5732 | avox | 346 | applyCharStyle(pos, len, cstyle); |
7232 | avox | 347 | pos += len; |
7256 | avox | 348 | if (other.text(otherEnd-1) != SpecialChars::PARSEP) { |
12056 | jghali | 349 | //#5845 : disable for now as it has nasty side effects when linking frames |
350 | //applyStyle(pos, otherDefault); |
||
7256 | avox | 351 | applyStyle(pos, other.paragraphStyle(otherEnd-1)); |
352 | } |
||
5732 | avox | 353 | } |
8049 | avox | 354 | invalidate(pos, length()); |
5732 | avox | 355 | } |
356 | |||
357 | |||
5721 | avox | 358 | /** |
359 | Make sure that the paragraph CharStyle's point to the new ParagraphStyle |
||
360 | */ |
||
8174 | avox | 361 | void StoryText::insertParSep(int pos) |
5721 | avox | 362 | { |
8174 | avox | 363 | ScText* it = item_p(pos); |
6733 | avox | 364 | if(!it->parstyle) { |
8174 | avox | 365 | it->parstyle = new ParagraphStyle(paragraphStyle(pos+1)); |
8134 | avox | 366 | it->parstyle->setContext( & d->pstyleContext); |
12689 | jghali | 367 | // #7432 : when inserting a paragraph separator, apply/erase the trailing Style |
12796 | pierre | 368 | if (pos >= signed(d->len - 1)) |
12689 | jghali | 369 | { |
370 | applyStyle(pos, d->trailingStyle); |
||
371 | d->trailingStyle.erase(); |
||
372 | } |
||
8194 | avox | 373 | // it->parstyle->setName("para"); // DONT TRANSLATE |
374 | // it->parstyle->charStyle().setName("cpara"); // DONT TRANSLATE |
||
8134 | avox | 375 | // it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() ); |
6733 | avox | 376 | } |
8134 | avox | 377 | d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext()); |
5721 | avox | 378 | } |
379 | /** |
||
380 | need to remove the ParagraphStyle structure and replace all pointers |
||
381 | to it... |
||
382 | */ |
||
8174 | avox | 383 | void StoryText::removeParSep(int pos) |
5721 | avox | 384 | { |
8174 | avox | 385 | ScText* it = item_p(pos); |
5721 | avox | 386 | if (it->parstyle) { |
6733 | avox | 387 | // const CharStyle* oldP = & it->parstyle->charStyle(); |
388 | // const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle(); |
||
389 | // d->replaceParentStyle(pos, oldP, newP); |
||
5778 | avox | 390 | delete it->parstyle; |
5980 | avox | 391 | it->parstyle = 0; |
5721 | avox | 392 | } |
8134 | avox | 393 | // demote this parsep so the assert code in replaceCharStyleContextInParagraph() |
6904 | avox | 394 | // doesnt choke: |
9737 | jghali | 395 | it->ch = 0; |
8174 | avox | 396 | d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext()); |
5721 | avox | 397 | } |
398 | |||
4669 | avox | 399 | void StoryText::removeChars(int pos, uint len) |
400 | { |
||
401 | if (pos < 0) |
||
402 | pos += length(); |
||
8049 | avox | 403 | |
4669 | avox | 404 | assert( len > 0 ); |
405 | assert( pos >= 0 ); |
||
11991 | jghali | 406 | // This case can be handled more gracefully |
407 | // assert( pos + static_cast<int>(len) <= length() ); |
||
408 | if (pos >= length()) |
||
409 | return; |
||
410 | if (pos + static_cast<int>(len) > length()) |
||
411 | len = length() - pos; |
||
4749 | avox | 412 | |
5751 | avox | 413 | for ( int i=pos + static_cast<int>(len) - 1; i >= pos; --i ) |
4749 | avox | 414 | { |
5721 | avox | 415 | ScText *it = d->at(i); |
9737 | jghali | 416 | if ((it->ch == SpecialChars::PARSEP)) { |
8174 | avox | 417 | removeParSep(i); |
4749 | avox | 418 | } |
18524 | avox | 419 | if (it->mark != NULL) |
420 | { |
||
421 | delete it->mark; |
||
422 | it->mark = NULL; |
||
423 | } |
||
424 | |||
9737 | jghali | 425 | // qDebug("remove char %d at %d", (int) it->ch.unicode(), i); |
10366 | jghali | 426 | d->takeAt(i); |
5722 | avox | 427 | d->len--; |
4749 | avox | 428 | delete it; |
18330 | jghali | 429 | // #9592 : adjust m_selFirst and m_selLast, those values have to be |
16104 | jghali | 430 | // consistent in functions such as select() |
18330 | jghali | 431 | if (i <= m_selLast) --m_selLast; |
432 | if (i < m_selFirst) --m_selFirst; |
||
16639 | jghali | 433 | if ((i + 1 ) <= d->cursorPosition && d->cursorPosition > 0) d->cursorPosition -= 1; |
4749 | avox | 434 | } |
5722 | avox | 435 | |
436 | d->len = d->count(); |
||
16626 | jghali | 437 | d->cursorPosition = qMin(d->cursorPosition, d->len); |
18330 | jghali | 438 | if (m_selFirst > m_selLast) |
16104 | jghali | 439 | { |
18330 | jghali | 440 | m_selFirst = 0; |
441 | m_selLast = -1; |
||
16104 | jghali | 442 | } |
8049 | avox | 443 | invalidate(pos, length()); |
4669 | avox | 444 | } |
445 | |||
16626 | jghali | 446 | void StoryText::insertChars(QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle) |
447 | { |
||
448 | insertChars(d->cursorPosition, txt, applyNeighbourStyle); |
||
449 | } |
||
450 | |||
9645 | avox | 451 | void StoryText::insertChars(int pos, QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle) |
4669 | avox | 452 | { |
453 | if (pos < 0) |
||
8049 | avox | 454 | pos += length()+1; |
4669 | avox | 455 | |
456 | assert(pos >= 0); |
||
457 | assert(pos <= length()); |
||
4749 | avox | 458 | |
5365 | avox | 459 | if (txt.length() == 0) |
460 | return; |
||
461 | |||
8134 | avox | 462 | const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext(); |
9426 | avox | 463 | |
9645 | avox | 464 | ScText clone; |
465 | if (applyNeighbourStyle) |
||
466 | { |
||
10394 | cbradney | 467 | int referenceChar = qMax(0, qMin(pos, length()-1)); |
9645 | avox | 468 | clone.applyCharStyle(charStyle(referenceChar)); |
469 | clone.setEffects(ScStyle_Default); |
||
470 | } |
||
9607 | avox | 471 | |
8546 | cbradney | 472 | for (int i = 0; i < txt.length(); ++i) { |
9426 | avox | 473 | ScText * item = new ScText(clone); |
9737 | jghali | 474 | item->ch= txt.at(i); |
8134 | avox | 475 | item->setContext(cStyleContext); |
5721 | avox | 476 | d->insert(pos + i, item); |
5722 | avox | 477 | d->len++; |
9737 | jghali | 478 | if (item->ch == SpecialChars::PARSEP) { |
13085 | jghali | 479 | // qDebug() << QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name()); |
8174 | avox | 480 | insertParSep(pos + i); |
5721 | avox | 481 | } |
16626 | jghali | 482 | if (d->cursorPosition >= (pos + i)) { |
483 | d->cursorPosition += 1; |
||
484 | } |
||
4749 | avox | 485 | } |
486 | |||
5722 | avox | 487 | d->len = d->count(); |
4669 | avox | 488 | invalidate(pos, pos + txt.length()); |
489 | } |
||
490 | |||
15009 | jghali | 491 | void StoryText::insertCharsWithSoftHyphens(int pos, QString txt, bool applyNeighbourStyle) |
12093 | jghali | 492 | { |
493 | if (pos < 0) |
||
494 | pos += length()+1; |
||
495 | |||
496 | assert(pos >= 0); |
||
497 | assert(pos <= length()); |
||
498 | |||
499 | if (txt.length() == 0) |
||
500 | return; |
||
501 | |||
502 | const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext(); |
||
503 | |||
504 | ScText clone; |
||
505 | if (applyNeighbourStyle) |
||
506 | { |
||
507 | int referenceChar = qMax(0, qMin(pos, length()-1)); |
||
508 | clone.applyCharStyle(charStyle(referenceChar)); |
||
509 | clone.setEffects(ScStyle_Default); |
||
510 | } |
||
511 | |||
512 | int inserted = 0; |
||
513 | for (int i = 0; i < txt.length(); ++i) |
||
514 | { |
||
515 | QChar ch = txt.at(i); |
||
12112 | jghali | 516 | int index = pos + inserted; |
12093 | jghali | 517 | bool insert = true; |
12097 | jghali | 518 | if (ch == SpecialChars::SHYPHEN && index > 0) { |
519 | ScText* lastItem = this->item(index - 1); |
||
13161 | malex | 520 | // qreal SHY means user provided SHY, single SHY is automatic one |
12093 | jghali | 521 | if (lastItem->effects() & ScStyle_HyphenationPossible) |
522 | lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible); |
||
523 | else |
||
524 | { |
||
525 | lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible); |
||
526 | insert = false; |
||
527 | } |
||
528 | } |
||
529 | if (insert) |
||
530 | { |
||
531 | ScText * item = new ScText(clone); |
||
532 | item->ch = ch; |
||
533 | item->setContext(cStyleContext); |
||
12097 | jghali | 534 | d->insert(index, item); |
12093 | jghali | 535 | d->len++; |
17407 | fschmid | 536 | if (item->ch == SpecialChars::PARSEP) |
12097 | jghali | 537 | insertParSep(index); |
17407 | fschmid | 538 | if (d->cursorPosition >= static_cast<uint>(index)) |
16626 | jghali | 539 | d->cursorPosition += 1; |
12093 | jghali | 540 | ++inserted; |
541 | } |
||
542 | } |
||
543 | |||
544 | d->len = d->count(); |
||
545 | invalidate(pos, pos + inserted); |
||
546 | } |
||
547 | |||
4669 | avox | 548 | void StoryText::replaceChar(int pos, QChar ch) |
549 | { |
||
550 | if (pos < 0) |
||
551 | pos += length(); |
||
552 | |||
553 | assert(pos >= 0); |
||
554 | assert(pos < length()); |
||
555 | |||
5721 | avox | 556 | ScText* item = d->at(pos); |
9737 | jghali | 557 | if (item->ch == ch) |
5721 | avox | 558 | return; |
559 | |||
9737 | jghali | 560 | if (d->at(pos)->ch == SpecialChars::PARSEP) { |
8174 | avox | 561 | removeParSep(pos); |
5721 | avox | 562 | } |
563 | item->ch = ch; |
||
9737 | jghali | 564 | if (d->at(pos)->ch == SpecialChars::PARSEP) { |
8174 | avox | 565 | insertParSep(pos); |
5721 | avox | 566 | } |
567 | |||
4669 | avox | 568 | invalidate(pos, pos + 1); |
569 | } |
||
570 | |||
17501 | craig | 571 | int StoryText::replaceWord(int pos, QString newWord) |
572 | { |
||
573 | int eoWord=pos; |
||
574 | while(eoWord < length()) |
||
575 | { |
||
576 | if (text(eoWord).isLetterOrNumber()) |
||
577 | ++eoWord; |
||
578 | else |
||
579 | break; |
||
580 | } |
||
581 | QString word=text(pos,eoWord-pos); |
||
582 | int lengthDiff=newWord.length()-word.length(); |
||
583 | if (lengthDiff==0) |
||
584 | { |
||
585 | for (int j = 0; j < word.length(); ++j) |
||
586 | replaceChar(pos+j, newWord[j]); |
||
587 | } |
||
588 | else |
||
589 | { |
||
590 | if (lengthDiff>0) |
||
591 | { |
||
592 | for (int j = 0; j < word.length(); ++j) |
||
593 | replaceChar(pos+j, newWord[j]); |
||
594 | for (int j = word.length(); j < newWord.length(); ++j) |
||
595 | insertChars(pos+j, newWord.mid(j,1), true); |
||
596 | } |
||
597 | else |
||
598 | { |
||
599 | for (int j = 0; j < newWord.length(); ++j) |
||
600 | replaceChar(pos+j, newWord[j]); |
||
601 | removeChars(pos+newWord.length(), -lengthDiff); |
||
602 | } |
||
603 | } |
||
604 | return lengthDiff; |
||
605 | } |
||
606 | |||
5559 | avox | 607 | void StoryText::hyphenateWord(int pos, uint len, char* hyphens) |
608 | { |
||
609 | assert(pos >= 0); |
||
5691 | avox | 610 | assert(pos + signed(len) <= length()); |
5559 | avox | 611 | |
6940 | avox | 612 | // QString dump(""); |
5691 | avox | 613 | for (int i=pos; i < pos+signed(len); ++i) |
6937 | avox | 614 | { |
6940 | avox | 615 | // dump += d->at(i)->ch; |
6937 | avox | 616 | if(hyphens && hyphens[i-pos] & 1) { |
5721 | avox | 617 | d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible); |
6940 | avox | 618 | // dump += "-"; |
6937 | avox | 619 | } |
620 | else { |
||
5721 | avox | 621 | d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible); |
6937 | avox | 622 | } |
623 | } |
||
13085 | jghali | 624 | // qDebug() << QString("st: %1").arg(dump); |
5559 | avox | 625 | invalidate(pos, pos + len); |
626 | } |
||
627 | |||
17407 | fschmid | 628 | void StoryText::insertObject(int ob) |
16626 | jghali | 629 | { |
630 | insertObject(d->cursorPosition, ob); |
||
631 | } |
||
632 | |||
17407 | fschmid | 633 | void StoryText::insertObject(int pos, int ob) |
4669 | avox | 634 | { |
635 | if (pos < 0) |
||
8049 | avox | 636 | pos += length()+1; |
4669 | avox | 637 | |
638 | insertChars(pos, SpecialChars::OBJECT); |
||
17405 | fschmid | 639 | const_cast<StoryText *>(this)->d->at(pos)->embedded = ob; |
18330 | jghali | 640 | m_doc->FrameItems[ob]->isEmbedded = true; // this might not be enough... |
641 | m_doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object |
||
4669 | avox | 642 | } |
643 | |||
17826 | craig | 644 | void StoryText::insertMark(Mark* Mark, int pos) |
645 | { |
||
646 | if (Mark == NULL) |
||
647 | return; |
||
648 | if (pos < 0) |
||
649 | pos = d->cursorPosition; |
||
650 | |||
651 | insertChars(pos, SpecialChars::OBJECT, false); |
||
652 | const_cast<StoryText *>(this)->d->at(pos)->mark = Mark; |
||
653 | } |
||
654 | |||
17407 | fschmid | 655 | void StoryText::replaceObject(int pos, int ob) |
17231 | fschmid | 656 | { |
657 | if (pos < 0) |
||
658 | pos += length()+1; |
||
4669 | avox | 659 | |
17231 | fschmid | 660 | replaceChar(pos, SpecialChars::OBJECT); |
17405 | fschmid | 661 | const_cast<StoryText *>(this)->d->at(pos)->embedded = ob; |
18330 | jghali | 662 | m_doc->FrameItems[ob]->isEmbedded = true; // this might not be enough... |
663 | m_doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object |
||
17231 | fschmid | 664 | } |
665 | |||
666 | |||
4669 | avox | 667 | int StoryText::length() const |
668 | { |
||
5722 | avox | 669 | return d->len; |
4669 | avox | 670 | } |
671 | |||
17674 | jghali | 672 | QString StoryText::plainText() const |
673 | { |
||
674 | if (length() <= 0) |
||
675 | return QString(); |
||
676 | |||
677 | QChar ch; |
||
678 | QString result; |
||
679 | |||
17810 | fschmid | 680 | int len = length(); |
17674 | jghali | 681 | result.reserve(len); |
682 | |||
683 | StoryText* that(const_cast<StoryText*>(this)); |
||
684 | for (int i = 0; i < len; ++i) { |
||
685 | ch = that->d->at(i)->ch; |
||
686 | if (ch == SpecialChars::PARSEP) |
||
687 | ch = QLatin1Char('\n'); |
||
688 | result += ch; |
||
689 | } |
||
690 | |||
691 | return result; |
||
692 | } |
||
693 | |||
18524 | avox | 694 | |
695 | GlyphLayout* StoryText::getGlyphs(int pos) |
||
696 | { |
||
697 | if (pos < 0) |
||
698 | pos += length(); |
||
699 | |||
700 | assert(pos >= 0); |
||
701 | assert(pos < length()); |
||
702 | |||
703 | assert((this->d->at(pos)->glyph).scaleH > 0.5); |
||
704 | return &(this->d->at(pos)->glyph); |
||
705 | } |
||
706 | |||
707 | const GlyphLayout* StoryText::getGlyphs(int pos) const |
||
708 | { |
||
709 | if (pos < 0) |
||
710 | pos += length(); |
||
711 | |||
712 | assert(pos >= 0); |
||
713 | assert(pos < length()); |
||
714 | |||
715 | assert( (const_cast<StoryText *>(this)->d->at(pos)->glyph).scaleH > 0.5); |
||
716 | return &(const_cast<StoryText *>(this)->d->at(pos)->glyph); |
||
717 | } |
||
718 | |||
16626 | jghali | 719 | QChar StoryText::text() const |
720 | { |
||
721 | return text(d->cursorPosition); |
||
722 | } |
||
723 | |||
4669 | avox | 724 | QChar StoryText::text(int pos) const |
725 | { |
||
726 | if (pos < 0) |
||
727 | pos += length(); |
||
728 | |||
729 | assert(pos >= 0); |
||
730 | assert(pos < length()); |
||
731 | |||
9737 | jghali | 732 | return const_cast<StoryText *>(this)->d->at(pos)->ch; |
4669 | avox | 733 | } |
734 | |||
735 | QString StoryText::text(int pos, uint len) const |
||
736 | { |
||
737 | if (pos < 0) |
||
738 | pos += length(); |
||
739 | |||
740 | assert(pos >= 0); |
||
741 | assert(pos + signed(len) <= length()); |
||
742 | |||
743 | QString result; |
||
5721 | avox | 744 | StoryText* that(const_cast<StoryText*>(this)); |
745 | for (int i = pos; i < pos+signed(len); ++i) { |
||
10366 | jghali | 746 | result += that->d->at(i)->ch; |
5721 | avox | 747 | } |
4669 | avox | 748 | |
749 | return result; |
||
750 | } |
||
751 | |||
17501 | craig | 752 | QString StoryText::sentence(int pos, int &posn) |
753 | { |
||
754 | int sentencePos=qMax(0, prevSentence(pos)); |
||
755 | sentencePos=qMax(sentencePos, nextWord(sentencePos)); |
||
756 | posn=sentencePos; |
||
757 | int nextSentencePos=qMin(length(), nextSentence(pos+1)); |
||
758 | return text(sentencePos, nextSentencePos-sentencePos); |
||
759 | } |
||
760 | |||
15009 | jghali | 761 | QString StoryText::textWithSoftHyphens(int pos, uint len) const |
12093 | jghali | 762 | { |
763 | QString result(""); |
||
764 | int lastPos = pos; |
||
765 | |||
766 | len = qMin((uint) (length() - pos), len); |
||
767 | for (int i = pos; i < pos+signed(len); ++i) |
||
768 | { |
||
769 | if (this->charStyle(i).effects() & ScStyle_HyphenationPossible |
||
770 | // duplicate SHYPHEN if already present to indicate a user provided SHYPHEN: |
||
771 | || this->text(i) == SpecialChars::SHYPHEN) |
||
772 | { |
||
773 | result += text(lastPos, i + 1 - lastPos); |
||
774 | result += SpecialChars::SHYPHEN; |
||
775 | lastPos = i+1; |
||
776 | } |
||
777 | } |
||
778 | if (lastPos < pos+signed(len)) |
||
779 | result += text(lastPos, pos+signed(len) - lastPos); |
||
780 | return result; |
||
781 | } |
||
782 | |||
16600 | jghali | 783 | bool StoryText::hasObject(int pos) const |
784 | { |
||
785 | if (pos < 0) |
||
786 | pos += length(); |
||
787 | |||
788 | assert(pos >= 0); |
||
789 | assert(pos < length()); |
||
790 | |||
791 | StoryText* that = const_cast<StoryText *>(this); |
||
792 | if (that->d->at(pos)->ch == SpecialChars::OBJECT) |
||
18330 | jghali | 793 | return that->d->at(pos)->hasObject(m_doc); |
16600 | jghali | 794 | return false; |
795 | } |
||
796 | |||
4669 | avox | 797 | PageItem* StoryText::object(int pos) const |
798 | { |
||
799 | if (pos < 0) |
||
800 | pos += length(); |
||
801 | |||
802 | assert(pos >= 0); |
||
803 | assert(pos < length()); |
||
804 | |||
8174 | avox | 805 | StoryText* that = const_cast<StoryText *>(this); |
18330 | jghali | 806 | return that->d->at(pos)->getItem(m_doc); |
4669 | avox | 807 | } |
808 | |||
18524 | avox | 809 | bool StoryText::hasMark(int pos, Mark* mrk) const |
17826 | craig | 810 | { |
811 | if (pos < 0) |
||
812 | pos += length(); |
||
813 | |||
814 | assert(pos >= 0); |
||
815 | assert(pos < length()); |
||
816 | |||
817 | StoryText* that = const_cast<StoryText *>(this); |
||
818 | if (that->d->at(pos)->ch == SpecialChars::OBJECT) |
||
18524 | avox | 819 | return that->d->at(pos)->hasMark(mrk); |
17826 | craig | 820 | return false; |
821 | } |
||
822 | |||
823 | Mark* StoryText::mark(int pos) const |
||
824 | { |
||
825 | if (pos < 0) |
||
826 | pos += length(); |
||
827 | |||
828 | assert(pos >= 0); |
||
829 | assert(pos < length()); |
||
830 | |||
831 | StoryText* that = const_cast<StoryText *>(this); |
||
832 | return that->d->at(pos)->mark; |
||
833 | } |
||
834 | |||
18524 | avox | 835 | |
836 | void StoryText::replaceMark(int pos, Mark* mrk) |
||
837 | { |
||
838 | if (pos < 0) |
||
839 | pos += length(); |
||
840 | |||
841 | assert(pos >= 0); |
||
842 | assert(pos < length()); |
||
843 | |||
844 | this->d->at(pos)->mark = mrk; |
||
845 | } |
||
846 | |||
847 | |||
848 | LayoutFlags StoryText::flags(int pos) const |
||
849 | { |
||
850 | if (pos < 0) |
||
851 | pos += length(); |
||
852 | |||
853 | assert(pos >= 0); |
||
854 | assert(pos < length()); |
||
855 | |||
856 | StoryText* that = const_cast<StoryText *>(this); |
||
857 | return static_cast<LayoutFlags>((*that->d->at(pos)).effects().value & ScStyle_NonUserStyles); |
||
858 | } |
||
859 | |||
860 | bool StoryText::hasFlag(int pos, LayoutFlags flags) const |
||
861 | { |
||
862 | if (pos < 0) |
||
863 | pos += length(); |
||
864 | |||
865 | assert(pos >= 0); |
||
866 | assert(pos < length()); |
||
867 | assert(flags & ScStyle_UserStyles == ScStyle_None); |
||
868 | |||
869 | return (flags & d->at(pos)->effects().value) == flags; |
||
870 | } |
||
871 | |||
872 | void StoryText::setFlag(int pos, LayoutFlags flags) |
||
873 | { |
||
874 | if (pos < 0) |
||
875 | pos += length(); |
||
876 | |||
877 | assert(pos >= 0); |
||
878 | assert(pos < length()); |
||
879 | assert(flags & ScStyle_UserStyles == ScStyle_None); |
||
880 | |||
881 | d->at(pos)->setEffects(flags | d->at(pos)->effects().value); |
||
882 | } |
||
883 | |||
884 | void StoryText::clearFlag(int pos, LayoutFlags flags) |
||
885 | { |
||
886 | if (pos < 0) |
||
887 | pos += length(); |
||
888 | |||
889 | assert(pos >= 0); |
||
890 | assert(pos < length()); |
||
891 | |||
892 | d->at(pos)->setEffects(~(flags & ScStyle_NonUserStyles) & d->at(pos)->effects().value); |
||
893 | } |
||
894 | |||
895 | |||
16626 | jghali | 896 | const CharStyle & StoryText::charStyle() const |
897 | { |
||
898 | return charStyle(d->cursorPosition); |
||
899 | } |
||
4669 | avox | 900 | |
901 | const CharStyle & StoryText::charStyle(int pos) const |
||
902 | { |
||
903 | if (pos < 0) |
||
904 | pos += length(); |
||
905 | |||
906 | assert(pos >= 0); |
||
5732 | avox | 907 | assert(pos <= length()); |
4669 | avox | 908 | |
6144 | avox | 909 | if (length() == 0) { |
13085 | jghali | 910 | // qDebug() << "storytext::charstyle: default"; |
5732 | avox | 911 | return defaultStyle().charStyle(); |
6144 | avox | 912 | } |
913 | else if (pos == length()) { |
||
13144 | fschmid | 914 | qDebug() << "storytext::charstyle: access at end of text %i" << pos; |
5732 | avox | 915 | --pos; |
6144 | avox | 916 | } |
17826 | craig | 917 | //for notes frames - get style from note text, not from mark |
918 | if ((pos+1 < length()) && hasMark(pos) && mark(pos)->isNoteType()) |
||
919 | ++pos; |
||
8956 | avox | 920 | if (text(pos) == SpecialChars::PARSEP) |
921 | return paragraphStyle(pos).charStyle(); |
||
4669 | avox | 922 | StoryText* that = const_cast<StoryText *>(this); |
5721 | avox | 923 | return dynamic_cast<const CharStyle &> (*that->d->at(pos)); |
4669 | avox | 924 | } |
925 | |||
16639 | jghali | 926 | const ParagraphStyle & StoryText::paragraphStyle() const |
927 | { |
||
928 | return paragraphStyle(d->cursorPosition); |
||
929 | } |
||
930 | |||
4669 | avox | 931 | const ParagraphStyle & StoryText::paragraphStyle(int pos) const |
932 | { |
||
933 | if (pos < 0) |
||
934 | pos += length(); |
||
935 | |||
936 | assert(pos >= 0); |
||
5721 | avox | 937 | assert(pos <= length()); |
4669 | avox | 938 | |
6733 | avox | 939 | assert(d); |
940 | |||
5192 | avox | 941 | StoryText * that = const_cast<StoryText *> (this); |
5721 | avox | 942 | // assert( that->at(pos)->cab >= 0 ); |
943 | // assert( that->at(pos)->cab < doc->docParagraphStyles.count() ); |
||
944 | // return doc->docParagraphStyles[that->at(pos)->cab]; |
||
945 | |||
10366 | jghali | 946 | while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP) { |
5721 | avox | 947 | ++pos; |
948 | } |
||
6909 | avox | 949 | if (pos >= length()) { |
7232 | avox | 950 | return that->d->trailingStyle; |
5778 | avox | 951 | } |
10366 | jghali | 952 | else if ( !that->d->at(pos)->parstyle ) { |
953 | ScText* current = that->d->at(pos); |
||
10394 | cbradney | 954 | qDebug("inserting default parstyle at %i", pos); |
10366 | jghali | 955 | current->parstyle = new ParagraphStyle(); |
956 | current->parstyle->setContext( & d->pstyleContext); |
||
957 | // current->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE |
||
958 | // current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE |
||
959 | // current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext()); |
||
5721 | avox | 960 | } |
5778 | avox | 961 | else { |
13085 | jghali | 962 | // qDebug() << QString("using parstyle at %1").arg(pos); |
5778 | avox | 963 | } |
10366 | jghali | 964 | assert (that->d->at(pos)->parstyle); |
965 | return *that->d->at(pos)->parstyle; |
||
4669 | avox | 966 | } |
967 | |||
5559 | avox | 968 | const ParagraphStyle& StoryText::defaultStyle() const |
969 | { |
||
6733 | avox | 970 | assert(d); |
5721 | avox | 971 | return d->defaultStyle; |
5559 | avox | 972 | } |
973 | |||
5569 | avox | 974 | |
975 | void StoryText::setDefaultStyle(const ParagraphStyle& style) |
||
976 | { |
||
8134 | avox | 977 | const StyleContext * oldPContext = d->defaultStyle.context(); |
8289 | avox | 978 | // const StyleContext * oldCContext = d->defaultStyle.charStyle().context(); |
5721 | avox | 979 | d->defaultStyle = style; |
8134 | avox | 980 | d->defaultStyle.setContext( oldPContext ); |
8194 | avox | 981 | // d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE |
982 | // d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE |
||
13085 | jghali | 983 | // qDebug() << QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5") |
8134 | avox | 984 | // .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16) |
985 | // .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16) |
||
13085 | jghali | 986 | // .arg((uint)d->defaultStyle.charStyle().context(),16); |
8134 | avox | 987 | // d->defaultStyle.charStyle().setContext( oldCContext ); |
7958 | avox | 988 | invalidateAll(); |
5569 | avox | 989 | } |
990 | |||
991 | |||
5721 | avox | 992 | void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style ) |
4669 | avox | 993 | { |
994 | if (pos < 0) |
||
995 | pos += length(); |
||
996 | |||
11968 | fschmid | 997 | /* Why do we use an assert here instead of a gracefully return doing nothing ????? */ |
998 | // assert(pos >= 0); |
||
999 | // assert(pos + signed(len) <= length()); |
||
1000 | if (pos < 0) |
||
1001 | return; |
||
1002 | if (pos + signed(len) > length()) |
||
1003 | return; |
||
4669 | avox | 1004 | |
1005 | if (len == 0) |
||
1006 | return; |
||
1007 | |||
17407 | fschmid | 1008 | // int lastParStart = pos == 0? 0 : -1; |
10366 | jghali | 1009 | ScText* itText; |
5721 | avox | 1010 | for (uint i=pos; i < pos+len; ++i) { |
10366 | jghali | 1011 | itText = d->at(i); |
15658 | jghali | 1012 | // #6165 : applying style on last character applies style on whole text on next open |
1013 | /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL) |
||
1014 | itText->parstyle->charStyle().applyCharStyle(style);*/ |
||
1015 | |||
1016 | // Does not work well, do not reenable before checking #9337, #9376 and #9428 |
||
15386 | craig | 1017 | // #9173 et. al.: move charstyle to parstyle if whole paragraph is affected |
15658 | jghali | 1018 | /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL && lastParStart >= 0) |
15386 | craig | 1019 | { |
1020 | eraseCharStyle(lastParStart, i - lastParStart, style); |
||
1021 | itText->parstyle->charStyle().applyCharStyle(style); |
||
1022 | lastParStart = i + 1; |
||
15658 | jghali | 1023 | }*/ |
10366 | jghali | 1024 | itText->applyCharStyle(style); |
5721 | avox | 1025 | } |
15658 | jghali | 1026 | // Does not work well, do not reenable before checking #9337, #9376 and #9428 |
1027 | /*if (pos + signed(len) == length() && lastParStart >= 0) |
||
15386 | craig | 1028 | { |
1029 | eraseCharStyle(lastParStart, length() - lastParStart, style); |
||
1030 | d->trailingStyle.charStyle().applyCharStyle(style); |
||
15658 | jghali | 1031 | }*/ |
15386 | craig | 1032 | |
4669 | avox | 1033 | invalidate(pos, pos + len); |
1034 | } |
||
1035 | |||
5773 | avox | 1036 | |
1037 | |||
1038 | void StoryText::eraseCharStyle(int pos, uint len, const CharStyle& style ) |
||
1039 | { |
||
1040 | if (pos < 0) |
||
1041 | pos += length(); |
||
1042 | |||
1043 | assert(pos >= 0); |
||
1044 | assert(pos + signed(len) <= length()); |
||
1045 | |||
1046 | if (len == 0) |
||
1047 | return; |
||
1048 | |||
10366 | jghali | 1049 | ScText* itText; |
5773 | avox | 1050 | for (uint i=pos; i < pos+len; ++i) { |
10366 | jghali | 1051 | itText = d->at(i); |
13290 | jghali | 1052 | // FIXME?? see #6165 : should we really erase charstyle of paragraph style?? |
10366 | jghali | 1053 | if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL) |
1054 | itText->parstyle->charStyle().eraseCharStyle(style); |
||
1055 | itText->eraseCharStyle(style); |
||
5773 | avox | 1056 | } |
15658 | jghali | 1057 | // Does not work well, do not reenable before checking #9337, #9376 and #9428 |
1058 | /*if (pos + signed(len) == length()) |
||
15386 | craig | 1059 | { |
1060 | d->trailingStyle.charStyle().eraseCharStyle(style); |
||
15658 | jghali | 1061 | }*/ |
5773 | avox | 1062 | |
1063 | invalidate(pos, pos + len); |
||
1064 | } |
||
1065 | |||
13290 | jghali | 1066 | void StoryText::applyStyle(int pos, const ParagraphStyle& style, bool rmDirectFormatting) |
4669 | avox | 1067 | { |
1068 | if (pos < 0) |
||
1069 | pos += length(); |
||
1070 | |||
1071 | assert(pos >= 0); |
||
5721 | avox | 1072 | assert(pos <= length()); |
4669 | avox | 1073 | |
5721 | avox | 1074 | int i = pos; |
9737 | jghali | 1075 | while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) { |
5184 | avox | 1076 | ++i; |
1077 | } |
||
5721 | avox | 1078 | if (i < length()) { |
1079 | if (!d->at(i)->parstyle) { |
||
10394 | cbradney | 1080 | qDebug("PARSEP without style at pos %i", i); |
5980 | avox | 1081 | d->at(i)->parstyle = new ParagraphStyle(); |
8134 | avox | 1082 | d->at(i)->parstyle->setContext( & d->pstyleContext); |
8194 | avox | 1083 | // d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE |
1084 | // d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE |
||
8134 | avox | 1085 | // d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() ); |
5721 | avox | 1086 | } |
13085 | jghali | 1087 | // qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos); |
5721 | avox | 1088 | d->at(i)->parstyle->applyStyle(style); |
1089 | } |
||
1090 | else { |
||
1091 | // not happy about this but inserting a new PARSEP makes more trouble |
||
13085 | jghali | 1092 | // qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos); |
7232 | avox | 1093 | d->trailingStyle.applyStyle(style); |
5721 | avox | 1094 | } |
13290 | jghali | 1095 | if (rmDirectFormatting) |
1096 | { |
||
1097 | --i; |
||
1098 | while (i >= 0 && d->at(i)->ch != SpecialChars::PARSEP) |
||
1099 | { |
||
1100 | d->at(i)->eraseDirectFormatting(); |
||
1101 | --i; |
||
1102 | } |
||
1103 | } |
||
8562 | jghali | 1104 | invalidate(pos, qMin(i, length())); |
4669 | avox | 1105 | } |
1106 | |||
5773 | avox | 1107 | void StoryText::eraseStyle(int pos, const ParagraphStyle& style) |
1108 | { |
||
1109 | if (pos < 0) |
||
1110 | pos += length(); |
||
1111 | |||
1112 | assert(pos >= 0); |
||
1113 | assert(pos <= length()); |
||
1114 | |||
1115 | int i = pos; |
||
9737 | jghali | 1116 | while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) { |
5773 | avox | 1117 | ++i; |
1118 | } |
||
1119 | if (i < length()) { |
||
1120 | if (!d->at(i)->parstyle) { |
||
10394 | cbradney | 1121 | qDebug("PARSEP without style at pos %i", i); |
5980 | avox | 1122 | d->at(i)->parstyle = new ParagraphStyle(); |
8134 | avox | 1123 | d->at(i)->parstyle->setContext( & d->pstyleContext); |
8194 | avox | 1124 | // d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE |
1125 | // d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE |
||
8134 | avox | 1126 | // d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext()); |
5773 | avox | 1127 | } |
13085 | jghali | 1128 | // qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos); |
5773 | avox | 1129 | d->at(i)->parstyle->eraseStyle(style); |
1130 | } |
||
1131 | else { |
||
1132 | // not happy about this but inserting a new PARSEP makes more trouble |
||
13085 | jghali | 1133 | // qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos); |
7232 | avox | 1134 | d->trailingStyle.eraseStyle(style); |
5773 | avox | 1135 | } |
8562 | jghali | 1136 | invalidate(pos, qMin(i, length())); |
5773 | avox | 1137 | } |
1138 | |||
1139 | |||
6733 | avox | 1140 | void StoryText::setStyle(int pos, const ParagraphStyle& style) |
1141 | { |
||
1142 | eraseStyle(pos, paragraphStyle(pos)); |
||
1143 | applyStyle(pos, style); |
||
1144 | } |
||
1145 | |||
1146 | |||
1147 | void StoryText::setCharStyle(int pos, uint len, const CharStyle& style) |
||
1148 | { |
||
9067 | avox | 1149 | if (pos < 0) |
1150 | pos += length(); |
||
1151 | |||
1152 | assert(pos >= 0); |
||
9645 | avox | 1153 | assert(len <= unsigned(length())); |
9067 | avox | 1154 | assert(pos + signed(len) <= length()); |
1155 | |||
1156 | if (len == 0) |
||
1157 | return; |
||
1158 | |||
10366 | jghali | 1159 | ScText* itText; |
9067 | avox | 1160 | for (uint i=pos; i < pos+len; ++i) { |
10366 | jghali | 1161 | itText = d->at(i); |
11936 | jghali | 1162 | // #6165 : applying style on last character applies style on whole text on next open |
1163 | /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL) |
||
1164 | itText->parstyle->charStyle() = style;*/ |
||
10366 | jghali | 1165 | itText->setStyle(style); |
9067 | avox | 1166 | } |
1167 | |||
1168 | invalidate(pos, pos + len); |
||
6733 | avox | 1169 | } |
1170 | |||
1171 | |||
8455 | avox | 1172 | |
1173 | void StoryText::getNamedResources(ResourceCollection& lists) const |
||
1174 | { |
||
8938 | avox | 1175 | d->defaultStyle.getNamedResources(lists); |
1176 | d->trailingStyle.getNamedResources(lists); |
||
1177 | |||
8455 | avox | 1178 | for (int i=0; i < length(); ++i) |
1179 | { |
||
1180 | if (text(i) == SpecialChars::PARSEP) |
||
1181 | paragraphStyle(i).getNamedResources(lists); |
||
16600 | jghali | 1182 | else if (hasObject(i)) |
8455 | avox | 1183 | object(i)->getNamedResources(lists); |
1184 | else |
||
1185 | charStyle(i).getNamedResources(lists); |
||
1186 | } |
||
1187 | } |
||
1188 | |||
1189 | |||
7442 | avox | 1190 | void StoryText::replaceStyles(QMap<QString,QString> newNameForOld) |
1191 | { |
||
8455 | avox | 1192 | ResourceCollection newnames; |
8775 | cbradney | 1193 | newnames.mapStyles(newNameForOld); |
8455 | avox | 1194 | replaceNamedResources(newnames); |
1195 | } |
||
1196 | |||
1197 | void StoryText::replaceNamedResources(ResourceCollection& newNames) |
||
1198 | { |
||
7442 | avox | 1199 | int len = length(); |
8455 | avox | 1200 | |
1201 | d->defaultStyle.replaceNamedResources(newNames); |
||
1202 | d->trailingStyle.replaceNamedResources(newNames); |
||
1203 | |||
7442 | avox | 1204 | if (len == 0) |
1205 | return; |
||
1206 | |||
10366 | jghali | 1207 | ScText* itText; |
7501 | avox | 1208 | for (int i=0; i < len; ++i) { |
10366 | jghali | 1209 | itText = d->at(i); |
1210 | if (itText->parstyle) |
||
1211 | itText->parstyle->replaceNamedResources(newNames); |
||
8455 | avox | 1212 | else |
10366 | jghali | 1213 | itText->replaceNamedResources(newNames); |
7442 | avox | 1214 | } |
1215 | |||
1216 | invalidate(0, len); |
||
1217 | } |
||
1218 | |||
8455 | avox | 1219 | |
7442 | avox | 1220 | void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld) |
1221 | { |
||
8455 | avox | 1222 | ResourceCollection newnames; |
8775 | cbradney | 1223 | newnames.mapCharStyles(newNameForOld); |
8455 | avox | 1224 | replaceNamedResources(newnames); |
7442 | avox | 1225 | } |
1226 | |||
16681 | jghali | 1227 | void StoryText::fixLegacyFormatting() |
1228 | { |
||
1229 | if (length() == 0) |
||
1230 | return; |
||
1231 | |||
1232 | for (int i = 0; i < length(); ++ i) |
||
1233 | { |
||
1234 | if (d->at(i)->ch == SpecialChars::PARSEP) |
||
1235 | fixLegacyFormatting(i); |
||
1236 | } |
||
1237 | fixLegacyFormatting( length() ); |
||
1238 | } |
||
1239 | |||
1240 | void StoryText::fixLegacyFormatting(int pos) |
||
1241 | { |
||
1242 | if (length() == 0) |
||
1243 | return; |
||
1244 | |||
1245 | if (pos < 0) |
||
1246 | pos += length(); |
||
1247 | |||
1248 | assert(pos >= 0); |
||
1249 | assert(pos <= length()); |
||
1250 | |||
1251 | int i = pos; |
||
1252 | while (i > 0 && d->at(i - 1)->ch != SpecialChars::PARSEP) { |
||
1253 | --i; |
||
1254 | } |
||
1255 | |||
1256 | const ParagraphStyle& parStyle = this->paragraphStyle(pos); |
||
1257 | parStyle.validate(); |
||
1258 | |||
1259 | if (parStyle.hasParent()) |
||
1260 | { |
||
1261 | int start = i; |
||
1262 | while ((i < length()) && (d->at(i)->ch != SpecialChars::PARSEP)) |
||
1263 | { |
||
1264 | d->at(i)->validate(); |
||
1265 | d->at(i)->eraseCharStyle( parStyle.charStyle() ); |
||
1266 | ++i; |
||
1267 | } |
||
1268 | invalidate(start, qMin(i + 1, length())); |
||
1269 | } |
||
1270 | } |
||
1271 | |||
16626 | jghali | 1272 | uint StoryText::nrOfParagraph() const |
1273 | { |
||
1274 | return nrOfParagraph(d->cursorPosition); |
||
1275 | } |
||
8455 | avox | 1276 | |
16069 | craig | 1277 | uint StoryText::nrOfParagraph(int pos) const |
10623 | avox | 1278 | { |
16069 | craig | 1279 | uint result = 0; |
10623 | avox | 1280 | StoryText* that = const_cast<StoryText *>(this); |
1281 | bool lastWasPARSEP = true; |
||
16069 | craig | 1282 | pos = qMin(pos, that->length()); |
1283 | for (int i=0; i < pos; ++i) |
||
1284 | { |
||
10623 | avox | 1285 | lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP; |
1286 | if (lastWasPARSEP) |
||
1287 | ++result; |
||
1288 | } |
||
11380 | jghali | 1289 | return result; |
10623 | avox | 1290 | } |
1291 | |||
4669 | avox | 1292 | uint StoryText::nrOfParagraphs() const |
1293 | { |
||
1294 | uint result = 0; |
||
5721 | avox | 1295 | StoryText* that = const_cast<StoryText *>(this); |
7059 | avox | 1296 | bool lastWasPARSEP = true; |
16069 | craig | 1297 | for (int i=0; i < length(); ++i) |
1298 | { |
||
10366 | jghali | 1299 | lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP; |
6887 | avox | 1300 | if (lastWasPARSEP) |
4669 | avox | 1301 | ++result; |
5721 | avox | 1302 | } |
11359 | jghali | 1303 | return lastWasPARSEP ? result : result + 1; |
4669 | avox | 1304 | } |
1305 | |||
16626 | jghali | 1306 | int StoryText::startOfParagraph() const |
1307 | { |
||
1308 | return startOfParagraph(nrOfParagraph()); |
||
1309 | } |
||
1310 | |||
4669 | avox | 1311 | int StoryText::startOfParagraph(uint index) const |
1312 | { |
||
1313 | if (index == 0) |
||
1314 | return 0; |
||
1315 | |||
5184 | avox | 1316 | StoryText* that = const_cast<StoryText *>(this); |
16069 | craig | 1317 | for (int i=0; i < length(); ++i) |
1318 | { |
||
10366 | jghali | 1319 | if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index) |
4669 | avox | 1320 | return i + 1; |
5721 | avox | 1321 | } |
6887 | avox | 1322 | return length(); |
4669 | avox | 1323 | } |
1324 | |||
16626 | jghali | 1325 | int StoryText::endOfParagraph() const |
1326 | { |
||
1327 | return endOfParagraph(nrOfParagraph()); |
||
1328 | } |
||
1329 | |||
4669 | avox | 1330 | int StoryText::endOfParagraph(uint index) const |
1331 | { |
||
1332 | ++index; |
||
5184 | avox | 1333 | StoryText* that = const_cast<StoryText *>(this); |
16069 | craig | 1334 | for (int i=0; i < length(); ++i) |
1335 | { |
||
10366 | jghali | 1336 | if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index) |
5721 | avox | 1337 | return i; |
1338 | } |
||
4669 | avox | 1339 | return length(); |
1340 | } |
||
1341 | |||
1342 | uint StoryText::nrOfRuns() const |
||
1343 | { |
||
5464 | avox | 1344 | return length(); |
4669 | avox | 1345 | } |
1346 | |||
1347 | int StoryText::startOfRun(uint index) const |
||
1348 | { |
||
5464 | avox | 1349 | return index; |
4669 | avox | 1350 | } |
1351 | |||
1352 | int StoryText::endOfRun(uint index) const |
||
1353 | { |
||
5464 | avox | 1354 | return index + 1; |
4669 | avox | 1355 | } |
1356 | |||
7501 | avox | 1357 | // positioning. all positioning methods return char positions |
1358 | // FIXME: make that methods use correct semantic boundaries |
||
4669 | avox | 1359 | |
15805 | craig | 1360 | static QString wordBoundaries(" .,:;\"'!?\n\t"); |
1361 | static QString sentenceBoundaries(".:!?\n\t"); |
||
7501 | avox | 1362 | |
1363 | int StoryText::nextChar(int pos) |
||
1364 | { |
||
1365 | if (pos < length()) |
||
1366 | return pos+1; |
||
1367 | else |
||
1368 | return length(); |
||
1369 | } |
||
17878 | jghali | 1370 | |
7501 | avox | 1371 | int StoryText::prevChar(int pos) |
1372 | { |
||
1373 | if (pos > 0) |
||
1374 | return pos - 1; |
||
1375 | else |
||
1376 | return 0; |
||
1377 | } |
||
17878 | jghali | 1378 | |
1379 | int StoryText::firstWord() |
||
1380 | { |
||
1381 | int len = length(); |
||
1382 | int pos = 0; |
||
1383 | |||
1384 | while (pos < len) |
||
1385 | { |
||
1386 | if (text(pos).isLetter()) |
||
1387 | break; |
||
1388 | ++pos; |
||
1389 | } |
||
1390 | return pos; |
||
1391 | } |
||
1392 | |||
7501 | avox | 1393 | int StoryText::nextWord(int pos) |
1394 | { |
||
1395 | int len = length(); |
||
17501 | craig | 1396 | if (text(pos).isLetter()) |
1397 | pos = qMin(len, pos+1); |
||
1398 | else |
||
1399 | pos = qMin(len, pos); |
||
1400 | |||
1401 | // while (pos < len && wordBoundaries.indexOf(text(pos)) < 0) |
||
1402 | // ++pos; |
||
1403 | |||
1404 | while (pos < len) |
||
1405 | { |
||
1406 | if(text(pos).isLetter()) |
||
1407 | ++pos; |
||
1408 | else |
||
1409 | break; |
||
1410 | } |
||
7501 | avox | 1411 | return pos < len ? pos + 1 : pos; |
1412 | } |
||
17878 | jghali | 1413 | |
7501 | avox | 1414 | int StoryText::prevWord(int pos) |
1415 | { |
||
8562 | jghali | 1416 | pos = qMax(0, pos-1); |
10394 | cbradney | 1417 | while (pos > 0 && wordBoundaries.indexOf(text(pos)) < 0) |
7501 | avox | 1418 | --pos; |
10394 | cbradney | 1419 | return wordBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos; |
7501 | avox | 1420 | } |
17501 | craig | 1421 | |
1422 | int StoryText::endOfWord(int pos) const |
||
1423 | { |
||
1424 | int len = length(); |
||
1425 | while (pos < len) |
||
1426 | { |
||
1427 | if(text(pos).isLetter()) |
||
1428 | ++pos; |
||
1429 | else |
||
1430 | break; |
||
1431 | } |
||
1432 | return pos; |
||
1433 | } |
||
1434 | |||
7501 | avox | 1435 | int StoryText::nextSentence(int pos) |
1436 | { |
||
1437 | int len = length(); |
||
8562 | jghali | 1438 | pos = qMin(len, pos+1); |
10394 | cbradney | 1439 | while (pos < len && sentenceBoundaries.indexOf(text(pos)) < 0) |
7501 | avox | 1440 | ++pos; |
1441 | return pos < len ? pos + 1 : pos; |
||
1442 | } |
||
1443 | int StoryText::prevSentence(int pos) |
||
1444 | { |
||
8562 | jghali | 1445 | pos = qMax(0, pos-1); |
10394 | cbradney | 1446 | while (pos > 0 && sentenceBoundaries.indexOf(text(pos)) < 0) |
7501 | avox | 1447 | --pos; |
10394 | cbradney | 1448 | return sentenceBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos; |
7501 | avox | 1449 | } |
1450 | int StoryText::nextParagraph(int pos) |
||
1451 | { |
||
1452 | int len = length(); |
||
8562 | jghali | 1453 | pos = qMin(len, pos+1); |
7501 | avox | 1454 | while (pos < len && text(pos) != SpecialChars::PARSEP) |
1455 | ++pos; |
||
7637 | avox | 1456 | return pos; |
7501 | avox | 1457 | } |
1458 | int StoryText::prevParagraph(int pos) |
||
1459 | { |
||
8562 | jghali | 1460 | pos = qMax(0, pos-1); |
7501 | avox | 1461 | while (pos > 0 && text(pos) != SpecialChars::PARSEP) |
1462 | --pos; |
||
7637 | avox | 1463 | return pos; |
7501 | avox | 1464 | } |
1465 | |||
15805 | craig | 1466 | QString StoryText::wordAt(int pos) const |
1467 | { |
||
1468 | if (pos < 0) |
||
1469 | pos += length(); |
||
1470 | assert(pos >= 0); |
||
15808 | craig | 1471 | assert(pos <= length()); |
15805 | craig | 1472 | |
1473 | int len = length(); |
||
1474 | //Find the previous word position |
||
1475 | int tmpPosStart = qMax(0, pos-1); |
||
1476 | int startWordPos=0; |
||
1477 | if (tmpPosStart!=0) |
||
1478 | { |
||
1479 | while (tmpPosStart > 0 && wordBoundaries.indexOf(text(tmpPosStart)) < 0) |
||
1480 | --tmpPosStart; |
||
1481 | startWordPos= wordBoundaries.indexOf(text(tmpPosStart)) < 0 ? tmpPosStart + 1 : tmpPosStart; |
||
1482 | ++startWordPos; |
||
1483 | } |
||
1484 | qDebug ()<<"Start Word Pos:"<<startWordPos; |
||
1485 | //Find the next word position |
||
1486 | int tmpPosEnd = qMin(len, pos+1); |
||
1487 | while (tmpPosEnd < len && wordBoundaries.indexOf(text(tmpPosEnd)) < 0) |
||
1488 | { |
||
1489 | qDebug()<<tmpPosEnd<<text(tmpPosEnd)<<wordBoundaries.indexOf(text(tmpPosEnd)); |
||
1490 | ++tmpPosEnd; |
||
1491 | } |
||
1492 | int endWordPos=tmpPosEnd < len ? tmpPosEnd + 1 : tmpPosEnd; |
||
1493 | if (endWordPos>0) |
||
1494 | --endWordPos; |
||
1495 | qDebug ()<<"End Word Pos:"<<endWordPos; |
||
1496 | QString result; |
||
1497 | StoryText* that(const_cast<StoryText*>(this)); |
||
1498 | for (int i = startWordPos; i < endWordPos; ++i) |
||
1499 | { |
||
1500 | result += that->d->at(i)->ch; |
||
1501 | } |
||
1502 | return result; |
||
1503 | } |
||
1504 | |||
7501 | avox | 1505 | // these need valid layout: |
1506 | |||
1507 | int StoryText::startOfLine(int pos) |
||
1508 | { |
||
8546 | cbradney | 1509 | for (int i=0; i < m_lines.count(); ++i) { |
7501 | avox | 1510 | const LineSpec & ls(m_lines.at(i)); |
1511 | if (ls.firstItem <= pos && pos <= ls.lastItem) |
||
1512 | return ls.firstItem; |
||
1513 | } |
||
1514 | return 0; |
||
1515 | } |
||
1516 | int StoryText::endOfLine(int pos) |
||
1517 | { |
||
8546 | cbradney | 1518 | for (int i=0; i < m_lines.count(); ++i) { |
7501 | avox | 1519 | const LineSpec & ls(m_lines.at(i)); |
1520 | if (ls.firstItem <= pos && pos <= ls.lastItem) |
||
7657 | avox | 1521 | return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem : |
1522 | text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1; |
||
7501 | avox | 1523 | } |
1524 | return length(); |
||
1525 | } |
||
1526 | int StoryText::prevLine(int pos) |
||
1527 | { |
||
8546 | cbradney | 1528 | for (int i=0; i < m_lines.count(); ++i) |
7501 | avox | 1529 | { |
1530 | // find line for pos |
||
1531 | const LineSpec & ls(m_lines.at(i)); |
||
1532 | if (ls.firstItem <= pos && pos <= ls.lastItem) |
||
1533 | { |
||
7522 | avox | 1534 | if (i == 0) |
1535 | return startOfLine(pos); |
||
7501 | avox | 1536 | // find current xpos |
13161 | malex | 1537 | qreal xpos = 0.0; |
7501 | avox | 1538 | for (int j = ls.firstItem; j < pos; ++j) |
1539 | xpos += item(j)->glyph.wide(); |
||
1540 | if (pos != m_lastMagicPos || xpos > m_magicX) |
||
1541 | m_magicX = xpos; |
||
1542 | const LineSpec & ls2(m_lines.at(i-1)); |
||
1543 | // find new cpos |
||
1544 | xpos = 0.0; |
||
1545 | for (int j = ls2.firstItem; j <= ls2.lastItem; ++j) |
||
1546 | { |
||
1547 | xpos += item(j)->glyph.wide(); |
||
1548 | if (xpos > m_magicX) { |
||
1549 | m_lastMagicPos = j; |
||
1550 | return j; |
||
1551 | } |
||
1552 | } |
||
1553 | m_lastMagicPos = ls2.lastItem; |
||
1554 | return ls2.lastItem; |
||
1555 | } |
||
1556 | } |
||
18330 | jghali | 1557 | return m_firstFrameItem; |
7501 | avox | 1558 | } |
1559 | |||
1560 | int StoryText::nextLine(int pos) |
||
1561 | { |
||
8546 | cbradney | 1562 | for (int i=0; i < m_lines.count(); ++i) |
7501 | avox | 1563 | { |
1564 | // find line for pos |
||
1565 | const LineSpec & ls(m_lines.at(i)); |
||
1566 | if (ls.firstItem <= pos && pos <= ls.lastItem) |
||
1567 | { |
||
7522 | avox | 1568 | if (i+1 == m_lines.count()) |
1569 | return endOfLine(pos); |
||
7501 | avox | 1570 | // find current xpos |
13161 | malex | 1571 | qreal xpos = 0.0; |
7501 | avox | 1572 | for (int j = ls.firstItem; j < pos; ++j) |
1573 | xpos += item(j)->glyph.wide(); |
||
1574 | if (pos != m_lastMagicPos || xpos > m_magicX) |
||
1575 | m_magicX = xpos; |
||
1576 | const LineSpec & ls2(m_lines.at(i+1)); |
||
1577 | // find new cpos |
||
1578 | xpos = 0.0; |
||
1579 | for (int j = ls2.firstItem; j <= ls2.lastItem; ++j) |
||
1580 | { |
||
1581 | xpos += item(j)->glyph.wide(); |
||
1582 | if (xpos > m_magicX) { |
||
1583 | m_lastMagicPos = j; |
||
1584 | return j; |
||
1585 | } |
||
1586 | } |
||
1587 | m_lastMagicPos = ls2.lastItem + 1; |
||
1588 | return ls2.lastItem + 1; |
||
1589 | } |
||
1590 | } |
||
18330 | jghali | 1591 | return m_lastFrameItem; |
7501 | avox | 1592 | } |
1593 | |||
1594 | int StoryText::startOfFrame(int pos) |
||
1595 | { |
||
18330 | jghali | 1596 | return m_firstFrameItem; |
7501 | avox | 1597 | } |
16626 | jghali | 1598 | |
7501 | avox | 1599 | int StoryText::endOfFrame(int pos) |
1600 | { |
||
18330 | jghali | 1601 | return m_lastFrameItem + 1; |
7501 | avox | 1602 | } |
1603 | |||
4669 | avox | 1604 | // selection |
1605 | |||
1606 | int StoryText::startOfSelection() const |
||
1607 | { |
||
18330 | jghali | 1608 | return m_selFirst <= m_selLast? m_selFirst : 0; |
4669 | avox | 1609 | } |
1610 | |||
1611 | int StoryText::endOfSelection() const |
||
1612 | { |
||
18330 | jghali | 1613 | return m_selFirst <= m_selLast? m_selLast + 1 : -1; |
4669 | avox | 1614 | } |
1615 | |||
1616 | int StoryText::lengthOfSelection() const |
||
1617 | { |
||
17826 | craig | 1618 | //FIX ME - sometimes I saw values equal or greater than length of text |
18330 | jghali | 1619 | int last = m_selLast; |
1620 | if (m_selFirst >= length()) |
||
17826 | craig | 1621 | return 0; |
18330 | jghali | 1622 | if (m_selLast >= length()) |
17826 | craig | 1623 | last = length() -1; |
18330 | jghali | 1624 | return m_selFirst <= last? last - m_selFirst + 1 : 0; |
4669 | avox | 1625 | } |
1626 | |||
1627 | |||
1628 | bool StoryText::selected(int pos) const |
||
1629 | { |
||
18330 | jghali | 1630 | return (m_selFirst <= pos && pos <= m_selLast) |
5721 | avox | 1631 | // || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect) |
1632 | ; |
||
4669 | avox | 1633 | } |
1634 | |||
13054 | pierre | 1635 | |
1636 | int StoryText::selectWord(int pos) |
||
1637 | { |
||
1638 | //Double click in a frame to select a word |
||
1639 | |||
1640 | int a = pos; |
||
1641 | while(a > 0) |
||
1642 | { |
||
1643 | if (text(a-1).isLetterOrNumber()) |
||
1644 | --a; |
||
1645 | else |
||
1646 | break; |
||
1647 | } |
||
1648 | int b = pos; |
||
1649 | while(b < length()) |
||
1650 | { |
||
1651 | if (text(b).isLetterOrNumber()) |
||
1652 | ++b; |
||
1653 | else |
||
1654 | break; |
||
1655 | } |
||
1656 | select(a, b - a); |
||
1657 | return a; |
||
1658 | } |
||
1659 | |||
1660 | |||
4669 | avox | 1661 | void StoryText::select(int pos, uint len, bool on) |
1662 | { |
||
1663 | if (pos < 0) |
||
1664 | pos += length(); |
||
1665 | |||
1666 | assert( pos >= 0 ); |
||
1667 | assert( pos + signed(len) <= length() ); |
||
1668 | |||
18330 | jghali | 1669 | // qDebug("old selection: %d - %d", m_selFirst, m_selLast); |
5751 | avox | 1670 | |
5721 | avox | 1671 | // StoryText* that = const_cast<StoryText *>(this); |
1672 | // for (int i=pos; i < pos+signed(len); ++i) |
||
1673 | // that->at(i)->cselect = on; |
||
4669 | avox | 1674 | |
1675 | if (on) { |
||
1676 | // extend if possible |
||
1677 | if (selected(pos - 1)) |
||
18330 | jghali | 1678 | m_selLast = qMax(m_selLast, pos + static_cast<int>(len) - 1); |
4669 | avox | 1679 | else if (selected(pos + len)) |
18330 | jghali | 1680 | m_selFirst = qMin(m_selFirst, pos); |
4669 | avox | 1681 | else { |
18330 | jghali | 1682 | m_selFirst = pos; |
1683 | m_selLast = pos + len - 1; |
||
4669 | avox | 1684 | } |
1685 | } |
||
1686 | else { |
||
18330 | jghali | 1687 | if (pos <= m_selFirst && m_selLast < pos + signed(len)) |
4669 | avox | 1688 | deselectAll(); |
1689 | // shrink |
||
1690 | else if (!selected(pos - 1) && selected(pos + len - 1)) |
||
18330 | jghali | 1691 | m_selFirst = pos + len; |
4669 | avox | 1692 | else if (selected(pos) && !selected(pos + len)) |
18330 | jghali | 1693 | m_selLast = pos - 1; |
4669 | avox | 1694 | else if (selected(pos) || selected(pos + len - 1)) |
1695 | // Grr, deselection splits selection |
||
18330 | jghali | 1696 | m_selLast = pos - 1; |
4669 | avox | 1697 | } |
5751 | avox | 1698 | |
18330 | jghali | 1699 | // qDebug("new selection: %d - %d", m_selFirst, m_selLast); |
4669 | avox | 1700 | } |
1701 | |||
13054 | pierre | 1702 | void StoryText::extendSelection(int oldPos, int newPos) |
1703 | { |
||
18330 | jghali | 1704 | if (m_selFirst <= m_selLast) |
13054 | pierre | 1705 | { |
1706 | // have selection |
||
18330 | jghali | 1707 | if (m_selLast == oldPos - 1) |
13054 | pierre | 1708 | { |
18330 | jghali | 1709 | m_selLast = newPos - 1; |
13054 | pierre | 1710 | return; |
1711 | } |
||
18330 | jghali | 1712 | else if (m_selFirst == oldPos) |
13054 | pierre | 1713 | { |
18330 | jghali | 1714 | m_selFirst = newPos; |
13054 | pierre | 1715 | return; |
1716 | } |
||
1717 | // can't extend, fall through |
||
1718 | } |
||
1719 | // no previous selection |
||
1720 | if (newPos > oldPos) |
||
1721 | { |
||
18330 | jghali | 1722 | m_selFirst = oldPos; |
1723 | m_selLast = newPos - 1; |
||
13054 | pierre | 1724 | } |
1725 | else |
||
1726 | { |
||
18330 | jghali | 1727 | m_selFirst = newPos; |
1728 | m_selLast = oldPos - 1; |
||
13054 | pierre | 1729 | } |
1730 | } |
||
1731 | |||
4669 | avox | 1732 | void StoryText::selectAll() |
1733 | { |
||
5721 | avox | 1734 | /* StoryText* that = const_cast<StoryText *>(this); |
1735 | that->at(0); |
||
1736 | for (int i=0; i < length(); ++i) { |
||
1737 | that->current()->cselect = true; |
||
1738 | that->next(); |
||
1739 | } |
||
1740 | */ |
||
18330 | jghali | 1741 | m_selFirst = 0; |
1742 | m_selLast = length() - 1; |
||
4669 | avox | 1743 | } |
1744 | |||
1745 | void StoryText::deselectAll() |
||
1746 | { |
||
5721 | avox | 1747 | /* StoryText* that = const_cast<StoryText *>(this); |
1748 | that->at(0); |
||
1749 | for (int i=0; i < length(); ++i) { |
||
1750 | that->current()->cselect = false; |
||
1751 | that->next(); |
||
1752 | } |
||
1753 | */ |
||
18330 | jghali | 1754 | m_selFirst = 0; |
1755 | m_selLast = -1; |
||
4669 | avox | 1756 | } |
1757 | |||
1758 | void StoryText::removeSelection() |
||
1759 | { |
||
18330 | jghali | 1760 | // qDebug("removeSelection: %d - %d", m_selFirst, m_selLast); |
1761 | if (m_selFirst > m_selLast) |
||
4669 | avox | 1762 | return; |
1763 | |||
18330 | jghali | 1764 | assert( m_selFirst >= 0 ); |
1765 | assert( m_selLast < length() ); |
||
4669 | avox | 1766 | |
18330 | jghali | 1767 | removeChars(m_selFirst, m_selLast - m_selFirst+1); |
4669 | avox | 1768 | deselectAll(); |
1769 | } |
||
1770 | |||
1771 | |||
1772 | |||
1773 | void StoryText::invalidateObject(const PageItem * embedded) |
||
1774 | { |
||
1775 | } |
||
1776 | |||
1777 | void StoryText::invalidateLayout() |
||
1778 | { |
||
1779 | } |
||
1780 | |||
1781 | void StoryText::invalidateAll() |
||
1782 | { |
||
8134 | avox | 1783 | d->pstyleContext.invalidate(); |
18524 | avox | 1784 | invalidate(0, length()); |
4669 | avox | 1785 | } |
1786 | |||
7958 | avox | 1787 | void StoryText::invalidate(int firstItem, int endItem) |
4669 | avox | 1788 | { |
7958 | avox | 1789 | for (int i=firstItem; i < endItem; ++i) { |
1790 | ParagraphStyle* par = item(i)->parstyle; |
||
1791 | if (par) |
||
8134 | avox | 1792 | par->charStyleContext()->invalidate(); |
7958 | avox | 1793 | } |
17826 | craig | 1794 | if (!signalsBlocked()) |
1795 | emit changed(); |
||
13148 | pierre | 1796 | emit changed(); |
4669 | avox | 1797 | } |
1798 | |||
1799 | |||
1800 | // physical view |
||
1801 | |||
1802 | /* |
||
1803 | void StoryText::validate() |
||
1804 | { |
||
1805 | static bool withinValidate = false; |
||
1806 | |||
1807 | assert( !withinValidate ); |
||
1808 | withinValidate = true; |
||
1809 | |||
1810 | withinValidate = false; |
||
1811 | } |
||
1812 | */ |
||
1813 | |||
1814 | int StoryText::screenToPosition(FPoint coord) const |
||
1815 | { |
||
13161 | malex | 1816 | qreal maxx = coord.x() - 1.0; |
6144 | avox | 1817 | for (unsigned int i=0; i < lines(); ++i) |
1818 | { |
||
1819 | LineSpec ls = line(i); |
||
13085 | jghali | 1820 | // qDebug() << QString("screenToPosition: (%1,%2) -> y %3 - %4 + %5").arg(coord.x()).arg(coord.y()).arg(ls.y).arg(ls.ascent).arg(ls.descent); |
6144 | avox | 1821 | if (ls.y + ls.descent < coord.y()) |
1822 | continue; |
||
13161 | malex | 1823 | qreal xpos = ls.x; |
17422 | fschmid | 1824 | for (int j = ls.firstItem; j <= ls.lastItem; ++j) |
1825 | { |
||
13085 | jghali | 1826 | // qDebug() << QString("screenToPosition: (%1,%2) -> x %3 + %4").arg(coord.x()).arg(coord.y()).arg(xpos).arg(item(j)->glyph.wide()); |
13161 | malex | 1827 | qreal width = item(j)->glyph.wide(); |
7501 | avox | 1828 | xpos += width; |
1829 | if (xpos >= coord.x()) |
||
17422 | fschmid | 1830 | { |
1831 | if (hasObject(j)) |
||
1832 | return j; |
||
1833 | else |
||
1834 | return xpos - width/2 > coord.x() ? j : j+1; |
||
1835 | } |
||
6144 | avox | 1836 | } |
7501 | avox | 1837 | if (xpos > maxx) |
1838 | maxx = xpos; |
||
1839 | if (xpos + 1.0 > coord.x()) // allow 1pt after end of line |
||
1840 | return ls.lastItem + 1; |
||
13054 | pierre | 1841 | else if (coord.x() <= ls.x + ls.width) // last line of paragraph? |
18330 | jghali | 1842 | return ((ls.lastItem == m_lastFrameItem) ? (ls.lastItem + 1) : ls.lastItem); |
7501 | avox | 1843 | else if (xpos < ls.x + 0.01 && maxx >= coord.x()) // check for empty line |
1844 | return ls.firstItem; |
||
6144 | avox | 1845 | } |
18330 | jghali | 1846 | return qMax(m_lastFrameItem+1, m_firstFrameItem); |
4669 | avox | 1847 | } |
1848 | |||
1849 | |||
6144 | avox | 1850 | FRect StoryText::boundingBox(int pos, uint len) const |
4669 | avox | 1851 | { |
6144 | avox | 1852 | FRect result; |
8922 | avox | 1853 | LineSpec ls; |
6144 | avox | 1854 | for (uint i=0; i < lines(); ++i) |
1855 | { |
||
8922 | avox | 1856 | ls = line(i); |
6144 | avox | 1857 | if (ls.lastItem < pos) |
1858 | continue; |
||
1859 | if (ls.firstItem <= pos) { |
||
9165 | avox | 1860 | /* |
18524 | avox | 1861 | if (ls.lastItem == pos && (item(pos)->effects() & ScLayout_SuppressSpace) ) |
8922 | avox | 1862 | { |
1863 | if (i+1 < lines()) |
||
1864 | { |
||
1865 | ls = line(i+1); |
||
1866 | result.setRect(ls.x, ls.y - ls.ascent, 1, ls.ascent + ls.descent); |
||
1867 | } |
||
1868 | else |
||
1869 | { |
||
1870 | ls = line(lines()-1); |
||
8931 | cbradney | 1871 | const ParagraphStyle& pstyle(paragraphStyle(pos)); |
1872 | result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent); |
||
8922 | avox | 1873 | } |
1874 | } |
||
9165 | avox | 1875 | else */ |
8922 | avox | 1876 | { |
13161 | malex | 1877 | qreal xpos = ls.x; |
8922 | avox | 1878 | for (int j = ls.firstItem; j < pos; ++j) |
12309 | fschmid | 1879 | { |
16600 | jghali | 1880 | if (hasObject(j)) |
17422 | fschmid | 1881 | xpos += (object(j)->width() + object(j)->lineWidth()) * item(j)->glyph.scaleH; |
12309 | fschmid | 1882 | else |
1883 | xpos += item(j)->glyph.wide(); |
||
1884 | } |
||
13161 | malex | 1885 | qreal finalw = 1; |
16600 | jghali | 1886 | if (hasObject(pos)) |
17422 | fschmid | 1887 | finalw = (object(pos)->width() + object(pos)->lineWidth()) * item(pos)->glyph.scaleH; |
12309 | fschmid | 1888 | else |
1889 | finalw = item(pos)->glyph.wide(); |
||
13061 | pierre | 1890 | const CharStyle& cs(charStyle(pos)); |
13161 | malex | 1891 | qreal desc = -cs.font().descent(cs.fontSize() / 10.0); |
1892 | qreal asce = cs.font().ascent(cs.fontSize() / 10.0); |
||
13061 | pierre | 1893 | result.setRect(xpos, ls.y - asce, pos < length()? finalw : 1, desc+asce); |
8922 | avox | 1894 | } |
9165 | avox | 1895 | return result; |
6144 | avox | 1896 | } |
1897 | } |
||
12796 | pierre | 1898 | const ParagraphStyle& pstyle(paragraphStyle(qMin(pos, length()))); // rather the trailing style than a segfault. |
9165 | avox | 1899 | if (lines() > 0) |
1900 | { |
||
1901 | ls = line(lines()-1); |
||
1902 | result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent); |
||
1903 | } |
||
1904 | else |
||
1905 | { |
||
1906 | result.setRect(1, 1, 1, pstyle.lineSpacing()); |
||
1907 | } |
||
6144 | avox | 1908 | return result; |
4669 | avox | 1909 | } |
1910 | |||
1911 | |||
1912 | ScText* StoryText::item(uint itm) |
||
1913 | { |
||
1914 | assert( static_cast<int>(itm) < length() ); |
||
5721 | avox | 1915 | return const_cast<StoryText *>(this)->d->at(itm); |
4669 | avox | 1916 | } |
1917 | |||
6144 | avox | 1918 | |
1919 | const ScText* StoryText::item(uint itm) const |
||
1920 | { |
||
1921 | assert( static_cast<int>(itm) < length() ); |
||
1922 | return const_cast<StoryText *>(this)->d->at(itm); |
||
1923 | } |
||
1924 | |||
7442 | avox | 1925 | |
18524 | avox | 1926 | //const QString StoryText::itemText(uint itm) const |
1927 | //{ |
||
4669 | avox | 1928 | |
18524 | avox | 1929 | // assert( static_cast<int>(itm) < length() ); |
4669 | avox | 1930 | |
18524 | avox | 1931 | // return text(itm, 1); |
1932 | //} |
||
4669 | avox | 1933 | |
1934 | |||
18524 | avox | 1935 | //const CharStyle StoryText::itemStyle(uint itm) const |
1936 | //{ |
||
1937 | // assert( static_cast<int>(itm) < length() ); |
||
4669 | avox | 1938 | |
18524 | avox | 1939 | // return charStyle(itm); |
1940 | //} |
||
4669 | avox | 1941 | |
1942 | |||
18524 | avox | 1943 | //int StoryText::startOfItem(uint itm) const |
1944 | //{ |
||
1945 | // assert( static_cast<int>(itm) < length() ); |
||
4669 | avox | 1946 | |
18524 | avox | 1947 | // return itm; |
1948 | //} |
||
4669 | avox | 1949 | |
18524 | avox | 1950 | //int StoryText::endOfItem(uint itm) const |
1951 | //{ |
||
1952 | // assert( static_cast<int>(itm) < length() ); |
||
4669 | avox | 1953 | |
18524 | avox | 1954 | // return itm + 1; |
1955 | //} |
||
4669 | avox | 1956 | |
8194 | avox | 1957 | |
8266 | avox | 1958 | using namespace desaxe; |
8194 | avox | 1959 | |
8800 | jghali | 1960 | void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const |
8194 | avox | 1961 | { |
1962 | Xml_attr empty; |
||
1963 | Xml_attr pageno; |
||
1964 | pageno.insert("name", "pgno"); |
||
11713 | fschmid | 1965 | Xml_attr pageco; |
1966 | pageco.insert("name", "pgco"); |
||
8194 | avox | 1967 | |
8266 | avox | 1968 | handler.begin(elemtag, empty); |
1969 | defaultStyle().saxx(handler, "defaultstyle"); |
||
8194 | avox | 1970 | |
8398 | avox | 1971 | CharStyle lastStyle(charStyle(0)); |
8194 | avox | 1972 | bool lastWasPar = true; |
1973 | int lastPos = 0; |
||
8398 | avox | 1974 | handler.begin("p", empty); |
1975 | paragraphStyle(0).saxx(handler); |
||
1976 | handler.begin("span", empty); |
||
1977 | lastStyle.saxx(handler); |
||
8194 | avox | 1978 | for (int i=0; i < length(); ++i) |
1979 | { |
||
1980 | const QChar curr(text(i)); |
||
1981 | const CharStyle& style(charStyle(i)); |
||
1982 | |||
1983 | if (curr == SpecialChars::OBJECT || |
||
1984 | curr == SpecialChars::TAB || |
||
1985 | curr == SpecialChars::PARSEP || |
||
1986 | curr == SpecialChars::LINEBREAK || |
||
1987 | curr == SpecialChars::COLBREAK || |
||
1988 | curr == SpecialChars::FRAMEBREAK || |
||
1989 | curr == SpecialChars::PAGENUMBER || |
||
11713 | fschmid | 1990 | curr == SpecialChars::PAGECOUNT || |
8194 | avox | 1991 | curr.unicode() < 32 || |
1992 | (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) || |
||
1993 | curr.unicode() == 0xfffe || curr.unicode() == 0xffff || |
||
1994 | style != lastStyle) |
||
1995 | { |
||
1996 | // something new, write pending chars |
||
1997 | if (i - lastPos > 0) |
||
1998 | { |
||
15009 | jghali | 1999 | handler.chars(textWithSoftHyphens(lastPos, i-lastPos)); |
8194 | avox | 2000 | } |
2001 | lastPos = i; |
||
2002 | } |
||
2003 | |||
2004 | lastWasPar = (curr == SpecialChars::PARSEP); |
||
2005 | if (lastWasPar) |
||
2006 | { |
||
8398 | avox | 2007 | handler.end("span"); |
2008 | handler.end("p"); |
||
2009 | handler.begin("p", empty); |
||
8913 | avox | 2010 | paragraphStyle(i+1).saxx(handler); |
8398 | avox | 2011 | handler.begin("span", empty); |
12962 | jghali | 2012 | lastStyle.erase(); |
8194 | avox | 2013 | } |
16600 | jghali | 2014 | else if (this->hasObject(i)) |
8194 | avox | 2015 | { |
2016 | object(i)->saxx(handler); |
||
2017 | } |
||
17826 | craig | 2018 | else if (hasMark(i)) |
2019 | { |
||
2020 | Mark* mrk = mark(i); |
||
18330 | jghali | 2021 | if ((m_doc->m_Selection->itemAt(0)->isNoteFrame() && mrk->isType(MARKNoteMasterType)) |
2022 | || (!m_doc->m_Selection->itemAt(0)->isTextFrame() && mrk->isType(MARKNoteFrameType)) |
||
18047 | craig | 2023 | || mrk->isType(MARKBullNumType)) |
17826 | craig | 2024 | continue; //do not insert notes marks into text frames nad vice versa |
2025 | Xml_attr mark_attr; |
||
2026 | mark_attr.insert("label", mrk->label); |
||
2027 | mark_attr.insert("typ", QString::number((int )mrk->getType())); |
||
18047 | craig | 2028 | // if (!mrk->isType(MARKBullNumType)) |
2029 | // { |
||
2030 | // mark_attr.insert("strtxt", mrk->getString()); |
||
2031 | // ParagraphStyle pstyle = this->paragraphStyle(i); |
||
2032 | // mark_attr.insert("style_peoffset", QString::number(pstyle.parEffectOffset(),'f',2)); |
||
2033 | // mark_attr.insert("style_peindent", QString::number(pstyle.parEffectIndent(),'f',2)); |
||
2034 | // mark_attr.insert("style_pecharstyle", pstyle.peCharStyleName()); |
||
2035 | // mark_attr.insert("style_hasbul", pstyle.hasBullet() ? "1" : "0"); |
||
2036 | // mark_attr.insert("style_bulletstr", pstyle.bulletStr()); |
||
2037 | // mark_attr.insert("style_hasnum", pstyle.hasNum() ? "1" : "0"); |
||
2038 | // mark_attr.insert("style_numname", pstyle.numName()); |
||
2039 | // mark_attr.insert("style_numformat", QString::number( (int)pstyle.numFormat())); |
||
2040 | // mark_attr.insert("style_numprefix", pstyle.numPrefix()); |
||
2041 | // mark_attr.insert("style_numsuffix", pstyle.numSuffix()); |
||
2042 | // mark_attr.insert("style_numlevel", QString::number(pstyle.numLevel())); |
||
2043 | // mark_attr.insert("style_numstart", QString::number(pstyle.numStart())); |
||
2044 | // mark_attr.insert("style_numrestart", QString::number((int) pstyle.numRestart())); |
||
2045 | // mark_attr.insert("style_numother", pstyle.numOther() ? "1" : "0"); |
||
2046 | // mark_attr.insert("style_numhigher", pstyle.numHigher() ? "1" : "0"); |
||
2047 | // } |
||
17826 | craig | 2048 | if (mrk->isType(MARK2ItemType) && (mrk->getItemPtr() != NULL)) |
2049 | mark_attr.insert("item", mrk->getItemPtr()->itemName()); |
||
2050 | else if (mrk->isType(MARK2MarkType)) |
||
2051 | { |
||
2052 | QString l; |
||
2053 | MarkType t; |
||
2054 | mrk->getMark(l, t); |
||
18330 | jghali | 2055 | if (m_doc->getMarkDefinied(l,t) != NULL) |
17826 | craig | 2056 | { |
2057 | mark_attr.insert("mark_l", l); |
||
2058 | mark_attr.insert("mark_t", QString::number((int) t)); |
||
2059 | } |
||
2060 | } |
||
2061 | else if (mrk->isType(MARKNoteMasterType)) |
||
2062 | { |
||
18044 | jghali | 2063 | TextNote * note = mrk->getNotePtr(); |
2064 | assert(note != NULL); |
||
2065 | mark_attr.insert("nStyle", note->notesStyle()->name()); |
||
2066 | mark_attr.insert("note",note->saxedText()); |
||
17826 | craig | 2067 | //store noteframe name for inserting into note if it is non-auto-removable |
18044 | jghali | 2068 | if (note->noteMark() && note->noteMark()->getItemPtr() && !note->noteMark()->getItemPtr()->isAutoNoteFrame()) |
2069 | mark_attr.insert("noteframe", note->noteMark()->getItemPtr()->getUName()); |
||
17826 | craig | 2070 | } |
2071 | handler.beginEnd("mark", mark_attr); |
||
2072 | } |
||
8194 | avox | 2073 | else if (curr == SpecialChars::TAB) |
2074 | { |
||
8266 | avox | 2075 | handler.beginEnd("tab", empty); |
8194 | avox | 2076 | } |
2077 | else if (curr == SpecialChars::LINEBREAK) |
||
2078 | { |
||
8266 | avox | 2079 | handler.beginEnd("breakline", empty); |
8194 | avox | 2080 | } |
2081 | else if (curr == SpecialChars::COLBREAK) |
||
2082 | { |
||
8266 | avox | 2083 | handler.beginEnd("breakcol", empty); |
8194 | avox | 2084 | } |
2085 | else if (curr == SpecialChars::FRAMEBREAK) |
||
2086 | { |
||
8266 | avox | 2087 | handler.beginEnd("breakframe", empty); |
8194 | avox | 2088 | } |
2089 | else if (curr == SpecialChars::PAGENUMBER) |
||
2090 | { |
||
8266 | avox | 2091 | handler.beginEnd("var", pageno); |
8194 | avox | 2092 | } |
11713 | fschmid | 2093 | else if (curr == SpecialChars::PAGECOUNT) |
2094 | { |
||
2095 | handler.beginEnd("var", pageco); |
||
2096 | } |
||
8194 | avox | 2097 | else if (curr.unicode() < 32 || |
2098 | (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) || |
||
2099 | curr.unicode() == 0xfffe || curr.unicode() == 0xffff) |
||
2100 | { |
||
2101 | Xml_attr unic; |
||
8266 | avox | 2102 | unic.insert("code", toXMLString(curr.unicode())); |
2103 | handler.beginEnd("unicode", unic); |
||
8194 | avox | 2104 | } |
2105 | else if (lastStyle != style) |
||
2106 | { |
||
8398 | avox | 2107 | handler.end("span"); |
2108 | handler.begin("span", empty); |
||
8194 | avox | 2109 | style.saxx(handler); |
2110 | lastStyle = style; |
||
2111 | continue; |
||
2112 | } |
||
2113 | else |
||
2114 | continue; |
||
2115 | lastPos = i+1; |
||
2116 | } |
||
2117 | |||
2118 | if (length() - lastPos > 0) |
||
15009 | jghali | 2119 | handler.chars(textWithSoftHyphens(lastPos, length()-lastPos)); |
8398 | avox | 2120 | handler.end("span"); |
2121 | handler.end("p"); |
||
8194 | avox | 2122 | |
8398 | avox | 2123 | // if (!lastWasPar) |
2124 | // paragraphStyle(length()-1).saxx(handler); |
||
8194 | avox | 2125 | |
8266 | avox | 2126 | handler.end(elemtag); |
8194 | avox | 2127 | |
2128 | } |
||
2129 | |||
8266 | avox | 2130 | |
2131 | class AppendText_body : public Action_body |
||
2132 | { |
||
2133 | public: |
||
8834 | cbradney | 2134 | void chars(const Xml_string& txt) |
8266 | avox | 2135 | { |
12093 | jghali | 2136 | QChar chr; |
2137 | int lastPos = 0, len; |
||
8266 | avox | 2138 | StoryText* obj = this->dig->top<StoryText>(); |
12093 | jghali | 2139 | for (int i = 0; i < txt.length(); ++i) |
2140 | { |
||
2141 | chr = txt.at(i); |
||
2142 | if (chr == SpecialChars::SHYPHEN && i > 0) |
||
2143 | { |
||
2144 | int toInsert = i - lastPos; |
||
2145 | if (toInsert > 0) |
||
2146 | obj->insertChars(obj->length(), txt.mid(lastPos, toInsert)); |
||
2147 | len = obj->length(); |
||
13161 | malex | 2148 | // qreal SHY means user provided SHY, single SHY is automatic one |
18524 | avox | 2149 | if (obj->hasFlag(len-1, ScLayout_HyphenationPossible)) |
12093 | jghali | 2150 | { |
18524 | avox | 2151 | obj->clearFlag(len-1, ScLayout_HyphenationPossible); |
12093 | jghali | 2152 | obj->insertChars(len, QString(chr)); |
2153 | } |
||
2154 | else |
||
2155 | { |
||
18524 | avox | 2156 | obj->setFlag(len-1, ScLayout_HyphenationPossible); |
12093 | jghali | 2157 | } |
2158 | lastPos = i + 1; |
||
2159 | } |
||
2160 | } |
||
2161 | if (lastPos < txt.length()) |
||
2162 | { |
||
2163 | QString ins = (lastPos == 0) ? txt : txt.right(txt.length() - lastPos); |
||
2164 | len = obj->length(); |
||
2165 | obj->insertChars(len, ins); |
||
2166 | } |
||
8266 | avox | 2167 | } |
2168 | }; |
||
2169 | |||
2170 | struct AppendText : public MakeAction<AppendText_body> |
||
2171 | {}; |
||
2172 | |||
2173 | |||
2174 | class AppendSpecial_body : public Action_body |
||
2175 | { |
||
2176 | public: |
||
2177 | AppendSpecial_body(QChar sp) : chr(sp) {} |
||
2178 | |||
8834 | cbradney | 2179 | void begin(const Xml_string& tag, Xml_attr attr) |
8266 | avox | 2180 | { |
2181 | StoryText* obj = this->dig->top<StoryText>(); |
||
2182 | Xml_attr::iterator code = attr.find("code"); |
||
11713 | fschmid | 2183 | Xml_attr::iterator name = attr.find("name"); |
8266 | avox | 2184 | if (tag == "unicode" && code != attr.end()) |
2185 | obj->insertChars(-1, QChar(parseUInt(Xml_data(code)))); |
||
11713 | fschmid | 2186 | else if (tag == "var" && name != attr.end()) |
2187 | { |
||
2188 | if (Xml_data(name) == "pgno") |
||
2189 | obj->insertChars(-1, SpecialChars::PAGENUMBER); |
||
2190 | else |
||
2191 | obj->insertChars(-1, SpecialChars::PAGECOUNT); |
||
2192 | } |
||
8266 | avox | 2193 | else |
2194 | obj->insertChars(-1, chr); |
||
2195 | } |
||
2196 | private: |
||
2197 | QChar chr; |
||
2198 | }; |
||
2199 | |||
2200 | struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar> |
||
2201 | { |
||
2202 | AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {} |
||
2203 | AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {} |
||
2204 | }; |
||
2205 | |||
2206 | |||
2207 | class AppendInlineFrame_body : public Action_body |
||
2208 | { |
||
2209 | public: |
||
8834 | cbradney | 2210 | void end(const Xml_string& tag) // this could be a setter if we had StoryText::appendObject() ... |
8266 | avox | 2211 | { |
2212 | StoryText* story = this->dig->top<StoryText>(1); |
||
2213 | PageItem* obj = this->dig->top<PageItem>(0); |
||
14086 | jghali | 2214 | // FIXME : currently inline objects are added to doc items when parsing |
2215 | // We have to remove them from object list and add them to the FrameItems list |
||
2216 | // before inserting them in story object |
||
2217 | QList<PageItem*> *items = obj->doc()->Items; |
||
2218 | if (items->count() > 0) |
||
2219 | { |
||
2220 | if (items->last() == obj) // try a fast path first |
||
2221 | items->removeLast(); |
||
2222 | else |
||
2223 | items->removeAll(obj); |
||
2224 | } |
||
17407 | fschmid | 2225 | story->insertObject(-1, obj->doc()->addToInlineFrames(obj)); |
8266 | avox | 2226 | } |
2227 | }; |
||
2228 | |||
2229 | struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body> |
||
2230 | {}; |
||
2231 | |||
17826 | craig | 2232 | //marks support |
2233 | class AppendMark_body : public Action_body |
||
2234 | { |
||
2235 | public: |
||
2236 | void begin(const Xml_string& tag, Xml_attr attr) |
||
2237 | { |
||
2238 | StoryText* story = this->dig->top<StoryText>(); |
||
2239 | QString l = ""; |
||
2240 | MarkType t = MARKNoType; |
||
2241 | |||
2242 | Mark* mrk = NULL; |
||
2243 | |||
2244 | if (tag == "mark") |
||
2245 | { |
||
2246 | Xml_attr::iterator lIt = attr.find("label"); |
||
2247 | Xml_attr::iterator tIt = attr.find("typ"); |
||
18047 | craig | 2248 | // Xml_attr::iterator nf_It = attr.find("noteframe"); |
17826 | craig | 2249 | if (lIt != attr.end()) |
2250 | l = Xml_data(lIt); |
||
2251 | if (tIt != attr.end()) |
||
2252 | t = (MarkType) parseInt(Xml_data(tIt)); |
||
18047 | craig | 2253 | if (t != MARKBullNumType) |
17826 | craig | 2254 | { |
18047 | craig | 2255 | ScribusDoc* doc = this->dig->lookup<ScribusDoc>("<scribusdoc>"); |
2256 | // ParagraphStyle* pstyle = NULL; |
||
2257 | if (t == MARKVariableTextType) |
||
2258 | mrk = doc->getMarkDefinied(l,t); |
||
2259 | // else if (t == MARKBullNumType) |
||
2260 | // { |
||
2261 | // mrk = (Mark*) new BulNumMark(); |
||
2262 | // Xml_attr::iterator str_It = attr.find("strtxt"); |
||
2263 | // mrk->setString(Xml_data(str_It)); |
||
2264 | // pstyle = new ParagraphStyle(); |
||
2265 | // Xml_attr::iterator ite; |
||
2266 | // ite = attr.find("style_peoffset"); |
||
2267 | // if (ite != attr.end()) |
||
2268 | // pstyle->setParEffectOffset(parseDouble(Xml_data(ite))); |
||
2269 | // ite = attr.find("style_peindent"); |
||
2270 | // if (ite != attr.end()) |
||
2271 | // pstyle->setParEffectIndent(parseDouble(Xml_data(ite))); |
||
2272 | // ite = attr.find("style_pecharstyle"); |
||
2273 | // if (ite != attr.end()) |
||
2274 | // pstyle->setPeCharStyleName(Xml_data(ite)); |
||
2275 | // ite = attr.find("style_hasbul"); |
||
2276 | // if (ite != attr.end()) |
||
2277 | // pstyle->setHasBullet(parseInt(Xml_data(ite)) == 1); |
||
2278 | // ite = attr.find("style_bulletstr"); |
||
2279 | // if (ite != attr.end()) |
||
2280 | // pstyle->setBulletStr(Xml_data(ite)); |
||
2281 | // ite = attr.find("style_hasnum"); |
||
2282 | // if (ite != attr.end()) |
||
2283 | // pstyle->setHasNum(parseInt(Xml_data(ite)) == 1); |
||
2284 | // ite = attr.find("style_numname"); |
||
2285 | // if (ite != attr.end()) |
||
2286 | // pstyle->setNumName(Xml_data(ite)); |
||
2287 | // ite = attr.find("style_numformat"); |
||
2288 | // if (ite != attr.end()) |
||
2289 | // pstyle->setNumFormat(parseInt(Xml_data(ite))); |
||
2290 | // ite = attr.find("style_numprefix"); |
||
2291 | // if (ite != attr.end()) |
||
2292 | // pstyle->setNumPrefix(Xml_data(ite)); |
||
2293 | // ite = attr.find("style_numsuffix"); |
||
2294 | // if (ite != attr.end()) |
||
2295 | // pstyle->setNumSuffix(Xml_data(ite)); |
||
2296 | // ite = attr.find("style_numlevel"); |
||
2297 | // if (ite != attr.end()) |
||
2298 | // pstyle->setNumLevel(parseInt(Xml_data(ite))); |
||
2299 | // ite = attr.find("style_numstart"); |
||
2300 | // if (ite != attr.end()) |
||
2301 | // pstyle->setNumStart(parseInt(Xml_data(ite))); |
||
2302 | // ite = attr.find("style_numrestart"); |
||
2303 | // if (ite != attr.end()) |
||
2304 | // pstyle->setNumRestart(parseInt(Xml_data(ite))); |
||
2305 | // ite = attr.find("style_numother"); |
||
2306 | // if (ite != attr.end()) |
||
2307 | // pstyle->setNumOther(parseInt(Xml_data(ite)) == 1); |
||
2308 | // ite = attr.find("style_numhigher"); |
||
2309 | // if (ite != attr.end()) |
||
2310 | // pstyle->setNumHigher(parseInt(Xml_data(ite)) == 1); |
||
2311 | // } |
||
2312 | else |
||
17826 | craig | 2313 | { |
18047 | craig | 2314 | mrk = doc->newMark(); |
2315 | getUniqueName(l,doc->marksLabelsList(t), "_"); |
||
2316 | mrk->label = l; |
||
2317 | mrk->OwnPage = doc->currentPage()->pageNr(); |
||
2318 | mrk->setType(t); |
||
2319 | Xml_attr::iterator iIt = attr.find("item"); |
||
2320 | Xml_attr::iterator m_lIt = attr.find("mark_l"); |
||
2321 | Xml_attr::iterator m_tIt = attr.find("mark_t"); |
||
2322 | if (mrk->isType(MARK2ItemType) && (iIt != attr.end())) |
||
2323 | { |
||
2324 | PageItem* item = doc->getItemFromName(Xml_data(iIt)); |
||
2325 | mrk->setItemPtr(item); |
||
2326 | if (item == NULL) |
||
2327 | mrk->setString("0"); |
||
2328 | else |
||
2329 | mrk->setString(QString::number(item->OwnPage)); |
||
2330 | mrk->setItemName(Xml_data(iIt)); |
||
233 |