"Fossies" - the Fresh Open Source Software Archive 
Member "scribus-1.5.6.1/scribus/text/storytext.cpp" (14 Nov 2020, 69514 Bytes) of package /linux/misc/scribus-1.5.6.1.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "storytext.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.5.6_vs_1.5.6.1.
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
13 ***************************************************************************/
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
24 //#include <QDebug>
25 #include <unicode/brkiter.h>
26
27 //FIXME: this include must go to sctextstruct.h !
28 #include <QList>
29 #include <cassert> //added to make Fedora-5 happy
30 #include "fpoint.h"
31 #include "notesstyles.h"
32 #include "scfonts.h"
33 #include "scribusdoc.h"
34 #include "sctext_shared.h"
35 #include "selection.h"
36 #include "storytext.h"
37 //
38 #include "util.h"
39 #include "resourcecollection.h"
40 #include "desaxe/saxiohelper.h"
41 #include "desaxe/digester.h"
42 #include "desaxe/simple_actions.h"
43 #include "shapedtextcache.h"
44
45 using namespace icu;
46
47 StoryText::StoryText(ScribusDoc * doc_) : m_doc(doc_)
48 {
49 if (doc_)
50 {
51 d = new ScText_Shared(&doc_->paragraphStyles());
52 m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
53 m_doc->charStyles().connect(this, SLOT(invalidateAll()));
54 }
55 else
56 d = new ScText_Shared(nullptr);
57
58 d->selFirst = 0;
59 d->selLast = -1;
60
61 m_shapedTextCache = new ShapedTextCache();
62
63 d->len = 0;
64 invalidateAll();
65 }
66
67 StoryText::StoryText() : m_doc(nullptr)
68 {
69 d = new ScText_Shared(nullptr);
70
71 d->selFirst = 0;
72 d->selLast = -1;
73 m_shapedTextCache = nullptr;
74 }
75
76 StoryText::StoryText(const StoryText & other) : m_doc(other.m_doc)
77 {
78 d = other.d;
79 d->refs++;
80
81 if (m_doc)
82 {
83 m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
84 m_doc->charStyles().connect(this, SLOT(invalidateAll()));
85 }
86
87 d->selFirst = 0;
88 d->selLast = -1;
89 m_shapedTextCache = nullptr;
90
91 invalidateLayout();
92 }
93
94 StoryText::~StoryText()
95 {
96 /* Code below is not needed imho, as all connections will be disconnected automatically
97 by the QObject destructor. At least removing that code fixes a crash when closing
98 documents */
99 /* if (doc)
100 {
101 doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
102 doc->charStyles().disconnect(this, SLOT(invalidateAll()));
103 } */
104 d->refs--;
105 if (d->refs == 0) {
106 d->clear();
107 d->len = 0;
108 delete d;
109 }
110 }
111
112 bool StoryText::hasBulletOrNum() const
113 {
114 if (!d)
115 return false;
116
117 bool lastWasPARSEP = true;
118 for (int i = 0; i < length(); ++i)
119 {
120 lastWasPARSEP = (d->at(i)->ch == SpecialChars::PARSEP);
121 if (!lastWasPARSEP)
122 continue;
123 const ParagraphStyle& paraStyle = paragraphStyle(i);
124 if (paraStyle.hasBullet() || paraStyle.hasNum())
125 return true;
126 }
127
128 const ParagraphStyle& trailingStyle = d->trailingStyle;
129 return (trailingStyle.hasBullet() || trailingStyle.hasNum());
130 }
131
132 bool StoryText::hasTextMarks() const
133 {
134 if (d)
135 return (d->marksCount > 0);
136 return false;
137 }
138
139 bool StoryText::marksCountChanged() const
140 {
141 if (d)
142 return d->marksCountChanged;
143 return false;
144 }
145
146 void StoryText::resetMarksCountChanged()
147 {
148 d->marksCountChanged = false;
149 }
150
151 void StoryText::setDoc(ScribusDoc *docin)
152 {
153 if (m_doc)
154 {
155 m_doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
156 m_doc->charStyles().disconnect(this, SLOT(invalidateAll()));
157 }
158
159 m_doc = docin;
160
161 if (m_doc)
162 {
163 m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
164 m_doc->charStyles().connect(this, SLOT(invalidateAll()));
165 }
166 }
167
168 StoryText StoryText::copy() const
169 {
170 StoryText result(m_doc);
171 *(result.d) = *d;
172 return result;
173 }
174
175 StoryText& StoryText::operator= (const StoryText & other)
176 {
177 other.d->refs++;
178
179 d->refs--;
180 if (d->refs == 0)
181 {
182 clear();
183 delete d;
184 }
185
186 if (m_doc)
187 {
188 m_doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
189 m_doc->charStyles().disconnect(this, SLOT(invalidateAll()));
190 }
191
192 m_doc = other.m_doc;
193 d = other.d;
194
195 if (m_doc)
196 {
197 m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
198 m_doc->charStyles().connect(this, SLOT(invalidateAll()));
199 }
200
201 d->selFirst = 0;
202 d->selLast = -1;
203
204 invalidateLayout();
205 return *this;
206 }
207
208 int StoryText::cursorPosition() const
209 {
210 return d->cursorPosition;
211 }
212
213 void StoryText::setCursorPosition(int pos, bool relative)
214 {
215 if (relative)
216 {
217 bool forward = (pos >= 0);
218 pos += d->cursorPosition;
219 if (isLowSurrogate(pos) && isHighSurrogate(pos - 1))
220 {
221 if (forward)
222 pos += 1;
223 else
224 pos -= 1;
225 }
226 }
227 d->cursorPosition = qMin((uint) qMax(pos, 0), d->len);
228 }
229
230 void StoryText::normalizeCursorPosition()
231 {
232 d->cursorPosition = qMax((uint) 0, qMin(d->cursorPosition, d->len));
233 }
234
235 int StoryText::normalizedCursorPosition()
236 {
237 return (int) qMax((uint) 0, qMin(d->cursorPosition, d->len));
238 }
239
240 void StoryText::moveCursorForward()
241 {
242 BreakIterator* it = getGraphemeIterator();
243 if (!it)
244 return;
245
246 it->setText((const UChar*) plainText().utf16());
247 int pos = it->following(cursorPosition());
248 if (pos != BreakIterator::DONE)
249 setCursorPosition(pos);
250 }
251
252 void StoryText::moveCursorBackward()
253 {
254 BreakIterator* it = getGraphemeIterator();
255 if (!it)
256 return;
257
258 it->setText((const UChar*) plainText().utf16());
259 int pos = it->preceding(cursorPosition());
260 if (pos != BreakIterator::DONE)
261 setCursorPosition(pos);
262 }
263
264 void StoryText::moveCursorLeft()
265 {
266 if (paragraphStyle().direction() == ParagraphStyle::RTL)
267 moveCursorForward();
268 else
269 moveCursorBackward();
270 }
271
272 void StoryText::moveCursorRight()
273 {
274 if (paragraphStyle().direction() == ParagraphStyle::RTL)
275 moveCursorBackward();
276 else
277 moveCursorForward();
278 }
279
280 void StoryText::moveCursorWordLeft()
281 {
282 BreakIterator* it = getWordIterator();
283 if (!it)
284 return;
285 it->setText((const UChar*) plainText().utf16());
286 int pos = cursorPosition();
287 if (paragraphStyle().direction() == ParagraphStyle::RTL)
288 {
289 pos = it->following(pos);
290 if (pos != BreakIterator::DONE)
291 {
292 while (pos < length() && text(pos).isSpace())
293 pos += 1;
294 }
295 }
296 else
297 {
298 pos = cursorPosition();
299 while (pos > 0 && text(pos - 1).isSpace())
300 pos -= 1;
301 pos = it->preceding(pos);
302 }
303
304 if (pos != BreakIterator::DONE)
305 setCursorPosition(pos);
306 }
307
308 void StoryText::moveCursorWordRight()
309 {
310 BreakIterator* it = getWordIterator();
311 if (!it)
312 return;
313
314 it->setText((const UChar*) plainText().utf16());
315 int pos = cursorPosition();
316 if (paragraphStyle().direction() == ParagraphStyle::RTL)
317 {
318 pos = cursorPosition();
319 while (pos > 0 && text(pos - 1).isSpace())
320 pos -= 1;
321 pos = it->preceding(pos);
322 }
323 else
324 {
325 pos = it->following(pos);
326 if (pos != BreakIterator::DONE)
327 {
328 while (pos < length() && text(pos).isSpace())
329 pos += 1;
330 }
331 }
332
333 if (pos != BreakIterator::DONE)
334 setCursorPosition(pos);
335 }
336
337 void StoryText::clear()
338 {
339 d->selFirst = 0;
340 d->selLast = -1;
341
342 d->defaultStyle.erase();
343 d->trailingStyle.erase();
344
345 d->clear();
346 d->len = 0;
347 invalidateAll();
348 }
349
350 int StoryText::indexOf(const QString &str, int from, Qt::CaseSensitivity cs, int* pLen) const
351 {
352 int foundIndex = -1;
353 if (pLen)
354 *pLen = 0;
355
356 if (str.isEmpty() || (from < 0))
357 return -1;
358
359 QString qStr = str;
360 if (cs == Qt::CaseInsensitive)
361 qStr = qStr.toLower();
362 QChar ch = qStr.at(0);
363
364 int strLen = qStr.length();
365 int storyLen = length();
366 if (cs == Qt::CaseSensitive)
367 {
368 int i = indexOf(ch, from, cs);
369 while (i >= 0 && i < (int) d->len)
370 {
371 int index = 0;
372 while ((index < strLen) && ((index + i) < storyLen))
373 {
374 if (qStr.at(index) != d->at(index + i)->ch)
375 break;
376 ++index;
377 }
378 if (index == strLen)
379 {
380 foundIndex = i;
381 if (pLen)
382 *pLen = strLen;
383 break;
384 }
385 i = indexOf(ch, i + 1, cs);
386 }
387 }
388 else
389 {
390 bool qCharIsDiacritic, curCharIsDiacritic;
391 int i = indexOf(ch, from, cs);
392 while (i >= 0 && i < (int) d->len)
393 {
394 int index = 0;
395 int diacriticsCounter = 0; //counter for diacritics
396 while ((index < strLen) && ((index + i + diacriticsCounter) < storyLen))
397 {
398 const QChar &qChar = qStr.at(index);
399 const QChar &curChar = d->at(index + diacriticsCounter + i)->ch;
400 qCharIsDiacritic = SpecialChars::isArabicModifierLetter(qChar.unicode()) | (qChar.category() == QChar::Mark_NonSpacing);
401 curCharIsDiacritic = SpecialChars::isArabicModifierLetter(curChar.unicode()) | (curChar.category() == QChar::Mark_NonSpacing);
402 if (qCharIsDiacritic || curCharIsDiacritic)
403 {
404 if (qCharIsDiacritic)
405 {
406 ++index;
407 --diacriticsCounter;
408 }
409 if (curCharIsDiacritic)
410 ++diacriticsCounter;
411 continue;
412 }
413 if (qChar != curChar.toLower())
414 break;
415 ++index;
416 }
417 if (index == strLen)
418 {
419 foundIndex = i;
420 while ((index + i + diacriticsCounter) < storyLen)
421 {
422 const QChar &curChar = d->at(index + diacriticsCounter + i)->ch;
423 if (!SpecialChars::isArabicModifierLetter(curChar.unicode()) && (curChar.category() != QChar::Mark_NonSpacing))
424 break;
425 ++diacriticsCounter;
426 }
427
428 if (pLen)
429 *pLen = strLen + diacriticsCounter;
430 break;
431 }
432 i = indexOf(ch, i + 1, cs);
433 }
434 }
435 return foundIndex;
436 }
437
438 int StoryText::indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
439 {
440 int foundIndex = -1;
441 int textLength = length();
442
443 if (cs == Qt::CaseSensitive)
444 {
445 for (int i = from; i < textLength; ++i)
446 {
447 if (d->at(i)->ch == ch)
448 {
449 foundIndex = i;
450 break;
451 }
452 }
453 }
454 else
455 {
456 for (int i = from; i < textLength; ++i)
457 {
458 if (d->at(i)->ch.toLower() == ch)
459 {
460 foundIndex = i;
461 break;
462 }
463 }
464 }
465 return foundIndex;
466 }
467
468 void StoryText::insert(const StoryText& other, bool onlySelection)
469 {
470 insert(d->cursorPosition, other, onlySelection);
471 }
472
473 void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
474 {
475 if (pos < 0)
476 pos += length() + 1;
477
478 CharStyle cstyle(charStyle(pos));
479 ParagraphStyle pstyle(paragraphStyle(pos));
480
481 // this style represents all differences between this and other's defaultstyles
482 ParagraphStyle otherDefault(other.defaultStyle());
483 otherDefault.eraseStyle(defaultStyle());
484
485 int oldPos = pos;
486 int oldLen = length();
487 int otherStart = onlySelection? other.startOfSelection() : 0;
488 int otherEnd = onlySelection? other.endOfSelection() : other.length();
489 int cstyleStart = otherStart;
490 for (int i = otherStart; i < otherEnd; ++i)
491 {
492 if (other.charStyle(i) == cstyle
493 && other.text(i) != SpecialChars::OBJECT
494 && other.text(i) != SpecialChars::PARSEP)
495 continue;
496 int len = i - cstyleStart;
497 if (len > 0)
498 {
499 insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
500 applyCharStyle(pos, len, otherDefault.charStyle());
501 applyCharStyle(pos, len, cstyle);
502 pos += len;
503 }
504 if (other.text(i) == SpecialChars::PARSEP)
505 {
506 insertChars(pos, SpecialChars::PARSEP);
507 //#5845 : disable for now as it has nasty side effects when linking frames
508 //applyStyle(pos, otherDefault);
509 applyStyle(pos, other.paragraphStyle(i));
510 cstyleStart = i+1;
511 pos += 1;
512 }
513 else if (other.text(i) == SpecialChars::OBJECT)
514 {
515 insertChars(pos, SpecialChars::OBJECT);
516 item(pos)->embedded = other.item(i)->embedded;
517 item(pos)->mark = other.item(i)->mark;
518 if (item(pos)->mark)
519 {
520 d->marksCount++;
521 d->marksCountChanged = true;
522 }
523 applyCharStyle(pos, 1, other.charStyle(i));
524 cstyleStart = i+1;
525 pos += 1;
526 }
527 else
528 {
529 cstyle = other.charStyle(i);
530 cstyleStart = i;
531 }
532 }
533 int len = otherEnd - cstyleStart;
534 if (len > 0)
535 {
536 insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
537 applyCharStyle(pos, len, otherDefault.charStyle());
538 applyCharStyle(pos, len, cstyle);
539 pos += len;
540 if (other.text(otherEnd-1) != SpecialChars::PARSEP)
541 {
542 //#5845 : disable for now as it has nasty side effects when linking frames
543 //applyStyle(pos, otherDefault);
544 applyStyle(pos, other.paragraphStyle(otherEnd-1));
545 }
546 }
547 if ((d->selLast >= d->selFirst) && (d->selFirst <= oldPos) && (oldPos <= d->selLast))
548 d->selLast += (length() - oldLen);
549 invalidate(pos, length());
550 }
551
552
553 /**
554 Make sure that the paragraph CharStyle's point to the new ParagraphStyle
555 */
556 void StoryText::insertParSep(int pos)
557 {
558 ScText* it = item(pos);
559 if (!it->parstyle)
560 {
561 it->parstyle = new ParagraphStyle(paragraphStyle(pos+1));
562 it->parstyle->setContext( & d->pstyleContext);
563 // #7432 : when inserting a paragraph separator, apply/erase the trailing Style
564 if (pos >= signed(d->len - 1))
565 {
566 applyStyle(pos, d->trailingStyle);
567 d->trailingStyle.erase();
568 }
569 // it->parstyle->setName("para"); // DON'T TRANSLATE
570 // it->parstyle->charStyle().setName("cpara"); // DON'T TRANSLATE
571 // it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
572 }
573 d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
574 }
575 /**
576 need to remove the ParagraphStyle structure and replace all pointers
577 to it...
578 */
579 void StoryText::removeParSep(int pos)
580 {
581 ScText* it = item(pos);
582 if (it->parstyle) {
583 // const CharStyle* oldP = & it->parstyle->charStyle();
584 // const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
585 // d->replaceParentStyle(pos, oldP, newP);
586 delete it->parstyle;
587 it->parstyle = nullptr;
588 }
589 // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
590 // doesn't choke:
591 it->ch = 0;
592 d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());
593 }
594
595 void StoryText::removeChars(int pos, uint len)
596 {
597 if (pos < 0)
598 pos += length();
599
600 assert( len > 0 );
601 assert( pos >= 0 );
602 // This case can be handled more gracefully
603 // assert( pos + static_cast<int>(len) <= length() );
604 if (pos >= length())
605 return;
606 if (pos + static_cast<int>(len) > length())
607 len = length() - pos;
608
609 uint oldMarksCount = d->marksCount;
610
611 if ((pos == 0) && (len > 0) && (static_cast<int>(len) == length()))
612 {
613 int lastChar = length() - 1;
614 while (lastChar > 0 && text(lastChar) == SpecialChars::PARSEP)
615 --lastChar;
616 d->orphanedCharStyle = charStyle(lastChar);
617 }
618 for (int i = pos + static_cast<int>(len) - 1; i >= pos; --i)
619 {
620 ScText *it = d->at(i);
621 if (it->ch == SpecialChars::PARSEP)
622 removeParSep(i);
623 if ((it->ch == SpecialChars::OBJECT) && (it->mark != nullptr))
624 d->marksCount--;
625 d->takeAt(i);
626 d->len--;
627 delete it;
628 // #9592 : adjust d->selFirst and d->selLast, those values have to be
629 // consistent in functions such as select()
630 if (i <= d->selLast)
631 --d->selLast;
632 if (i < d->selFirst)
633 --d->selFirst;
634 if (static_cast<uint>(i + 1) <= d->cursorPosition && d->cursorPosition > 0)
635 d->cursorPosition -= 1;
636 }
637
638 if (oldMarksCount != d->marksCount)
639 d->marksCountChanged = true;
640
641 d->len = d->count();
642 d->cursorPosition = qMin(d->cursorPosition, d->len);
643 if (d->selFirst > d->selLast)
644 {
645 d->selFirst = 0;
646 d->selLast = -1;
647 }
648 invalidate(pos, length());
649 }
650
651 void StoryText::trim()
652 {
653 if (length() < 2)
654 return;
655 int posCount = 0;
656 int pos = static_cast<int>(length()) - 1;
657 for ( int i = static_cast<int>(length()) - 1; i >= 0; --i )
658 {
659 ScText *it = d->at(i);
660 if ((it->ch == SpecialChars::PARSEP) || (it->ch.isSpace()))
661 {
662 pos--;
663 posCount++;
664 }
665 else
666 break;
667 }
668 if (posCount > 0)
669 {
670 ParagraphStyle pst = paragraphStyle(pos);
671 if (posCount > 1)
672 removeChars(pos+1, posCount-1);
673 applyStyle(pos, pst);
674 }
675 }
676
677 void StoryText::insertChars(const QString& txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
678 {
679 insertChars(d->cursorPosition, txt, applyNeighbourStyle);
680 }
681
682 void StoryText::insertChars(int pos, const QString& txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
683 {
684 if (pos < 0)
685 pos += length() + 1;
686
687 assert(pos >= 0);
688 assert(pos <= length());
689
690 if (txt.length() == 0)
691 return;
692
693 const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
694
695 ScText clone;
696 if (applyNeighbourStyle)
697 {
698 int referenceChar = qMax(0, qMin(pos, length()-1));
699 clone.applyCharStyle(charStyle(referenceChar));
700 clone.setEffects(ScStyle_Default);
701 }
702
703 for (int i = 0; i < txt.length(); ++i)
704 {
705 ScText * item = new ScText(clone);
706 item->ch= txt.at(i);
707 item->setContext(cStyleContext);
708 d->insert(pos + i, item);
709 d->len++;
710 if (item->ch == SpecialChars::PARSEP)
711 {
712 // qDebug() << QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name());
713 insertParSep(pos + i);
714 }
715 if (d->cursorPosition >= static_cast<uint>(pos + i))
716 d->cursorPosition += 1;
717 }
718
719 d->len = d->count();
720 if ((d->selLast >= d->selFirst) && (d->selFirst <= pos) && (pos <= d->selLast))
721 d->selLast += txt.length();
722 invalidate(pos, pos + txt.length());
723 }
724
725 void StoryText::insertCharsWithSoftHyphens(int pos, const QString& txt, bool applyNeighbourStyle)
726 {
727 if (pos < 0)
728 pos += length() + 1;
729
730 assert(pos >= 0);
731 assert(pos <= length());
732
733 if (txt.length() == 0)
734 return;
735
736 const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
737
738 ScText clone;
739 if (applyNeighbourStyle)
740 {
741 int referenceChar = qMax(0, qMin(pos, length() - 1));
742 clone.applyCharStyle(charStyle(referenceChar));
743 clone.setEffects(ScStyle_Default);
744 }
745
746 int inserted = 0;
747 for (int i = 0; i < txt.length(); ++i)
748 {
749 QChar ch = txt.at(i);
750 int index = pos + inserted;
751 bool insert = true;
752 if (ch == SpecialChars::SHYPHEN && index > 0)
753 {
754 ScText* lastItem = this->item(index - 1);
755 // qreal SHY means user provided SHY, single SHY is automatic one
756 if (lastItem->effects() & ScStyle_HyphenationPossible)
757 lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
758 else
759 {
760 lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
761 insert = false;
762 }
763 }
764 if (insert)
765 {
766 ScText * item = new ScText(clone);
767 item->ch = ch;
768 item->setContext(cStyleContext);
769 d->insert(index, item);
770 d->len++;
771 if (item->ch == SpecialChars::PARSEP)
772 insertParSep(index);
773 if (d->cursorPosition >= static_cast<uint>(index))
774 d->cursorPosition += 1;
775 ++inserted;
776 }
777 }
778
779 d->len = d->count();
780 if ((d->selLast >= d->selFirst) && (d->selFirst <= pos) && (pos <= d->selLast))
781 d->selLast += inserted;
782 invalidate(pos, pos + inserted);
783 }
784
785 void StoryText::replaceChar(int pos, QChar ch)
786 {
787 if (pos < 0)
788 pos += length();
789
790 assert(pos >= 0);
791 assert(pos < length());
792
793 ScText* item = d->at(pos);
794 if (item->ch == ch)
795 return;
796
797 uint oldMarksCount = d->marksCount;
798
799 if (item->ch == SpecialChars::PARSEP)
800 removeParSep(pos);
801 if ((item->ch == SpecialChars::OBJECT) && (item->mark != nullptr))
802 d->marksCount--;
803 item->ch = ch;
804 if (d->at(pos)->ch == SpecialChars::PARSEP)
805 insertParSep(pos);
806
807 if (oldMarksCount != d->marksCount)
808 d->marksCountChanged = true;
809
810 invalidate(pos, pos + 1);
811 }
812
813 void StoryText::replaceSelection(const QString& newText)
814 {
815 if (selectionLength() <= 0)
816 return;
817
818 int selStart = startOfSelection();
819 int selLength = selectionLength();
820
821 int lengthDiff = newText.length() - selLength;
822 if (lengthDiff == 0)
823 {
824 for (int i = 0; i < selLength; ++i)
825 replaceChar(selStart + i, newText[i]);
826 }
827 else if (lengthDiff > 0)
828 {
829 for (int i = 0; i < selLength; ++i)
830 replaceChar(selStart + i, newText[i]);
831 for (int i = selLength; i < newText.length(); ++i)
832 insertChars(selStart + i, newText.mid(i, 1), true);
833 }
834 else
835 {
836 for (int i = 0; i < newText.length(); ++i)
837 replaceChar(selStart + i, newText[i]);
838 removeChars(selStart + newText.length(), -lengthDiff);
839 }
840
841 deselectAll();
842 if (newText.length() > 0)
843 select(selStart, newText.length());
844 setCursorPosition(selStart + newText.length());
845 }
846
847 int StoryText::replaceWord(int pos, QString newWord)
848 {
849 QString word = this->word(pos);
850 int lengthDiff = newWord.length() - word.length();
851 if (lengthDiff == 0)
852 {
853 for (int j = 0; j < word.length(); ++j)
854 replaceChar(pos + j, newWord[j]);
855 }
856 else if (lengthDiff > 0)
857 {
858 for (int j = 0; j < word.length(); ++j)
859 replaceChar(pos + j, newWord[j]);
860 for (int j = word.length(); j < newWord.length(); ++j)
861 insertChars(pos + j, newWord.mid(j, 1), true);
862 }
863 else
864 {
865 for (int j = 0; j < newWord.length(); ++j)
866 replaceChar(pos + j, newWord[j]);
867 removeChars(pos + newWord.length(), -lengthDiff);
868 }
869 return lengthDiff;
870 }
871
872 void StoryText::hyphenateWord(int pos, uint len, const char* hyphens)
873 {
874 assert(pos >= 0);
875 assert(pos + signed(len) <= length());
876
877 // QString dump("");
878 for (int i=pos; i < pos+signed(len); ++i)
879 {
880 // dump += d->at(i)->ch;
881 if (hyphens && hyphens[i-pos] & 1)
882 {
883 d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
884 // dump += "-";
885 }
886 else {
887 d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
888 }
889 }
890 // qDebug() << QString("st: %1").arg(dump);
891 invalidate(pos, pos + len);
892 }
893
894 void StoryText::insertObject(int ob)
895 {
896 insertObject(d->cursorPosition, ob);
897 }
898
899 void StoryText::insertObject(int pos, int ob)
900 {
901 if (pos < 0)
902 pos += length()+1;
903
904 insertChars(pos, SpecialChars::OBJECT);
905 const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
906 m_doc->FrameItems[ob]->isEmbedded = true; // this might not be enough...
907 m_doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object
908 }
909
910 void StoryText::insertMark(Mark* mark, int pos)
911 {
912 if (mark == nullptr)
913 return;
914 if (pos < 0)
915 pos = d->cursorPosition;
916
917 insertChars(pos, SpecialChars::OBJECT, false);
918 d->at(pos)->mark = mark;
919 if (mark)
920 {
921 d->marksCount++;
922 d->marksCountChanged = true;
923 }
924 }
925
926 void StoryText::replaceObject(int pos, int ob)
927 {
928 if (pos < 0)
929 pos += length()+1;
930
931 replaceChar(pos, SpecialChars::OBJECT);
932 const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
933 m_doc->FrameItems[ob]->isEmbedded = true; // this might not be enough...
934 m_doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object
935 }
936
937
938 int StoryText::length() const
939 {
940 return d->len;
941 }
942
943 QString StoryText::plainText() const
944 {
945 if (length() <= 0)
946 return QString();
947
948 QChar ch;
949 QString result;
950
951 int len = length();
952 result.reserve(len);
953
954 StoryText* that(const_cast<StoryText*>(this));
955 for (int i = 0; i < len; ++i) {
956 ch = that->d->at(i)->ch;
957 if (ch == SpecialChars::PARSEP)
958 ch = QLatin1Char('\n');
959 result += ch;
960 }
961
962 return result;
963 }
964 #if 0
965 QChar StoryText::text() const
966 {
967 return text(d->cursorPosition);
968 }
969 #endif
970
971 QChar StoryText::text(int pos) const
972 {
973 if (pos < 0)
974 pos += length();
975
976 assert(pos >= 0);
977 assert(pos < length());
978
979 return const_cast<StoryText *>(this)->d->at(pos)->ch;
980 }
981
982 QString StoryText::text(int pos, uint len) const
983 {
984 if (pos < 0)
985 pos += length();
986
987 assert(pos >= 0);
988 assert(pos + signed(len) <= length());
989
990 QString result;
991 StoryText* that(const_cast<StoryText*>(this));
992 for (int i = pos; i < pos+signed(len); ++i)
993 result += that->d->at(i)->ch;
994
995 return result;
996 }
997
998
999 bool StoryText::isBlockStart(int pos) const
1000 {
1001 return pos == 0 || text(pos-1) == SpecialChars::PARSEP;
1002 }
1003
1004
1005 int StoryText::nextBlockStart(int pos) const
1006 {
1007 int result = pos + 1;
1008 while (result < length() && !isBlockStart(result))
1009 ++result;
1010
1011 // lump empty (or small) paragraphs together
1012 while (result+1 < length() && isBlockStart(result+1))
1013 ++result;
1014
1015 return result;
1016 }
1017
1018
1019 InlineFrame StoryText::object(int pos) const
1020 {
1021 if (pos < 0)
1022 pos += length();
1023
1024 assert(pos >= 0);
1025 assert(pos < length());
1026
1027 StoryText* that = const_cast<StoryText *>(this);
1028 return InlineFrame(that->d->at(pos)->embedded);
1029 }
1030
1031
1032 bool StoryText::hasExpansionPoint(int pos) const
1033 {
1034 return text(pos) == SpecialChars::PAGENUMBER || text(pos) == SpecialChars::PAGECOUNT || hasMark(pos);
1035 }
1036
1037
1038 ExpansionPoint StoryText::expansionPoint(int pos) const
1039 {
1040 if (text(pos) == SpecialChars::PAGENUMBER)
1041 return ExpansionPoint(ExpansionPoint::PageNumber);
1042 if( text(pos) == SpecialChars::PAGECOUNT)
1043 return ExpansionPoint(ExpansionPoint::PageCount);
1044 if (hasMark(pos))
1045 return ExpansionPoint(mark(pos));
1046 return ExpansionPoint(ExpansionPoint::Invalid);
1047 }
1048
1049
1050 QString StoryText::sentence(int pos, int &posn)
1051 {
1052 int sentencePos = qMax(0, prevSentence(pos));
1053 posn = sentencePos;
1054 int nextSentencePos = qMin(length(), endOfSentence(pos));
1055 return text(sentencePos, nextSentencePos - sentencePos);
1056 }
1057
1058 QString StoryText::word(int pos)
1059 {
1060 BreakIterator* it = getWordIterator();
1061 if (it)
1062 {
1063 it->setText((const UChar*) plainText().utf16());
1064 int end = it->following(pos);
1065 return text(pos, end - pos);
1066 }
1067
1068 // Fallback to old code
1069 int eoWord = pos;
1070 while (eoWord < length())
1071 {
1072 if (text(eoWord).isLetterOrNumber() || SpecialChars::isIgnorableCodePoint(text(eoWord).unicode()))
1073 ++eoWord;
1074 else
1075 break;
1076 }
1077 return text(pos, eoWord - pos);
1078 }
1079
1080 QString StoryText::textWithSoftHyphens(int pos, uint len) const
1081 {
1082 QString result;
1083 int lastPos = pos;
1084
1085 len = qMin((uint) (length() - pos), len);
1086 for (int i = pos; i < pos+signed(len); ++i)
1087 {
1088 if (this->charStyle(i).effects() & ScStyle_HyphenationPossible
1089 // duplicate SHYPHEN if already present to indicate a user provided SHYPHEN:
1090 || this->text(i) == SpecialChars::SHYPHEN)
1091 {
1092 result += text(lastPos, i + 1 - lastPos);
1093 result += SpecialChars::SHYPHEN;
1094 lastPos = i+1;
1095 }
1096 }
1097 if (lastPos < pos+signed(len))
1098 result += text(lastPos, pos+signed(len) - lastPos);
1099 return result;
1100 }
1101
1102 bool StoryText::hasObject(int pos) const
1103 {
1104 if (pos < 0)
1105 pos += length();
1106
1107 assert(pos >= 0);
1108 assert(pos < length());
1109
1110 StoryText* that = const_cast<StoryText *>(this);
1111 if (that->d->at(pos)->ch == SpecialChars::OBJECT)
1112 return that->d->at(pos)->hasObject(m_doc);
1113 return false;
1114 }
1115
1116
1117 PageItem* StoryText::getItem(int pos) const
1118 {
1119 if (pos < 0)
1120 pos += length();
1121
1122 assert(pos >= 0);
1123 assert(pos < length());
1124
1125 StoryText* that = const_cast<StoryText *>(this);
1126 return that->d->at(pos)->getItem(m_doc);
1127 }
1128
1129 int StoryText::findMark(const Mark* mrk, int startPos) const
1130 {
1131 assert(startPos >= 0);
1132
1133 int len = d->len;
1134 for (int i = startPos; i < len; ++i)
1135 {
1136 if (d->at(i)->ch != SpecialChars::OBJECT)
1137 continue;
1138 if (d->at(i)->hasMark(mrk))
1139 return i;
1140 }
1141
1142 return -1;
1143 }
1144
1145 bool StoryText::hasMark(int pos, const Mark* mrk) const
1146 {
1147 if (pos < 0)
1148 pos += length();
1149
1150 assert(pos >= 0);
1151 assert(pos < length());
1152
1153 StoryText* that = const_cast<StoryText *>(this);
1154 if (that->d->at(pos)->ch == SpecialChars::OBJECT)
1155 return that->d->at(pos)->hasMark(mrk);
1156 return false;
1157 }
1158
1159 Mark* StoryText::mark(int pos) const
1160 {
1161 if (pos < 0)
1162 pos += length();
1163
1164 assert(pos >= 0);
1165 assert(pos < length());
1166
1167 StoryText* that = const_cast<StoryText *>(this);
1168 return that->d->at(pos)->mark;
1169 }
1170
1171
1172 void StoryText::applyMarkCharstyle(Mark* mrk, CharStyle& currStyle) const
1173 {
1174 TextNote* note = mrk->getNotePtr();
1175 if (note == nullptr)
1176 return;
1177
1178 NotesStyle* nStyle = note->notesStyle();
1179 Q_ASSERT(nStyle != nullptr);
1180
1181 QString chsName = nStyle->marksChStyle();
1182 if (!chsName.isEmpty())
1183 {
1184 CharStyle marksStyle(m_doc->charStyle(chsName));
1185 if (!currStyle.equiv(marksStyle))
1186 currStyle.setParent(chsName);
1187 }
1188
1189 StyleFlag s(currStyle.effects());
1190 if (mrk->isType(MARKNoteMasterType))
1191 {
1192 if (nStyle->isSuperscriptInMaster())
1193 s |= ScStyle_Superscript;
1194 else
1195 s &= ~ScStyle_Superscript;
1196 }
1197 else
1198 {
1199 if (nStyle->isSuperscriptInNote())
1200 s |= ScStyle_Superscript;
1201 else
1202 s &= ~ScStyle_Superscript;
1203 }
1204 if (s != currStyle.effects())
1205 currStyle.setFeatures(s.featureList());
1206 }
1207
1208
1209 void StoryText::replaceMark(int pos, Mark* mrk)
1210 {
1211 if (pos < 0)
1212 pos += length();
1213
1214 assert(pos >= 0);
1215 assert(pos < length());
1216
1217 ScText* textItem = this->d->at(pos);
1218 if (textItem->mark)
1219 this->d->marksCount--;
1220 this->d->at(pos)->mark = mrk;
1221 if (textItem->mark)
1222 this->d->marksCount++;
1223
1224 // Set marksCountChanged unconditionnaly to force text relayout
1225 d->marksCountChanged = true;
1226 }
1227
1228
1229 bool StoryText::isHighSurrogate(int pos) const
1230 {
1231 return pos >= 0 && pos < length() && text(pos).isHighSurrogate();
1232 }
1233
1234 bool StoryText::isLowSurrogate(int pos) const
1235 {
1236 return pos >= 0 && pos < length() && text(pos).isLowSurrogate();
1237 }
1238
1239
1240 LayoutFlags StoryText::flags(int pos) const
1241 {
1242 if (pos < 0)
1243 pos += length();
1244
1245 assert(pos >= 0);
1246 assert(pos < length());
1247
1248 StoryText* that = const_cast<StoryText *>(this);
1249 return static_cast<LayoutFlags>((*that->d->at(pos)).effects().value & ScStyle_NonUserStyles);
1250 }
1251
1252 bool StoryText::hasFlag(int pos, LayoutFlags flags) const
1253 {
1254 if (pos < 0)
1255 pos += length();
1256
1257 assert(pos >= 0);
1258 assert(pos < length());
1259 assert((flags & ScStyle_UserStyles) == ScStyle_None);
1260
1261 return (flags & d->at(pos)->effects().value) == flags;
1262 }
1263
1264 void StoryText::setFlag(int pos, LayoutFlags flags)
1265 {
1266 if (pos < 0)
1267 pos += length();
1268
1269 assert(pos >= 0);
1270 assert(pos < length());
1271 assert((flags & ScStyle_UserStyles) == ScStyle_None);
1272
1273 d->at(pos)->setEffects(flags | d->at(pos)->effects().value);
1274 }
1275
1276 void StoryText::clearFlag(int pos, LayoutFlags flags)
1277 {
1278 if (pos < 0)
1279 pos += length();
1280
1281 assert(pos >= 0);
1282 assert(pos < length());
1283
1284 d->at(pos)->setEffects(~(flags & ScStyle_NonUserStyles) & d->at(pos)->effects().value);
1285 }
1286
1287
1288 const CharStyle & StoryText::charStyle() const
1289 {
1290 return charStyle(d->cursorPosition);
1291 }
1292
1293 const CharStyle & StoryText::charStyle(int pos) const
1294 {
1295 if (pos < 0)
1296 pos += length();
1297
1298 assert(pos >= 0);
1299 assert(pos <= length());
1300
1301 if (length() == 0)
1302 {
1303 // qDebug() << "storytext::charstyle: default";
1304 return d->orphanedCharStyle;
1305 }
1306 if (pos == length())
1307 {
1308 //qDebug() << "storytext::charstyle: access at end of text %i" << pos;
1309 --pos;
1310 }
1311
1312 if (text(pos) == SpecialChars::PARSEP)
1313 return paragraphStyle(pos).charStyle();
1314
1315 StoryText* that = const_cast<StoryText *>(this);
1316
1317 if (hasMark(pos))
1318 {
1319 Mark* mrk = mark(pos);
1320 applyMarkCharstyle(mrk, *that->d->at(pos)); // hack to keep note charstyles current
1321 }
1322
1323 return dynamic_cast<const CharStyle &> (*that->d->at(pos));
1324 }
1325
1326 const ParagraphStyle & StoryText::paragraphStyle() const
1327 {
1328 return paragraphStyle(d->cursorPosition);
1329 }
1330
1331 const ParagraphStyle & StoryText::paragraphStyle(int pos) const
1332 {
1333 if (pos < 0)
1334 pos += length();
1335
1336 assert(pos >= 0);
1337 assert(pos <= length());
1338
1339 assert(d);
1340
1341 StoryText * that = const_cast<StoryText *> (this);
1342 // assert( that->at(pos)->cab >= 0 );
1343 // assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
1344 // return doc->docParagraphStyles[that->at(pos)->cab];
1345
1346 while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP)
1347 ++pos;
1348
1349 if (pos >= length())
1350 return that->d->trailingStyle;
1351 if ( !that->d->at(pos)->parstyle )
1352 {
1353 ScText* current = that->d->at(pos);
1354 qDebug("inserting default parstyle at %i", pos);
1355 current->parstyle = new ParagraphStyle();
1356 current->parstyle->setContext( & d->pstyleContext);
1357 // current->parstyle->setName( "para(paragraphStyle)" ); // DON'T TRANSLATE
1358 // current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DON'T TRANSLATE
1359 // current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
1360 }
1361 else {
1362 // qDebug() << QString("using parstyle at %1").arg(pos);
1363 }
1364 assert (that->d->at(pos)->parstyle);
1365 return *that->d->at(pos)->parstyle;
1366 }
1367
1368 const ParagraphStyle& StoryText::defaultStyle() const
1369 {
1370 assert(d);
1371 return d->defaultStyle;
1372 }
1373
1374
1375 void StoryText::setDefaultStyle(const ParagraphStyle& style)
1376 {
1377 const StyleContext * oldPContext = d->defaultStyle.context();
1378 // const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
1379 d->defaultStyle = style;
1380 d->defaultStyle.setContext( oldPContext );
1381 // d->defaultStyle.setName( "storydefault" ); // DON'T TRANSLATE
1382 // d->defaultStyle.charStyle().setName( "cstorydefault" ); // DON'T TRANSLATE
1383 // qDebug() << QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
1384 // .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
1385 // .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
1386 // .arg((uint)d->defaultStyle.charStyle().context(),16);
1387 // d->defaultStyle.charStyle().setContext( oldCContext );
1388 invalidateAll();
1389 }
1390
1391
1392 void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style )
1393 {
1394 if (pos < 0)
1395 pos += length();
1396
1397 /* Why do we use an assert here instead of a gracefully return doing nothing ????? */
1398 // assert(pos >= 0);
1399 // assert(pos + signed(len) <= length());
1400 if (pos < 0)
1401 return;
1402 if (pos + signed(len) > length())
1403 return;
1404 if (len == 0)
1405 return;
1406
1407 // int lastParStart = pos == 0? 0 : -1;
1408 ScText* itText;
1409 for (uint i=pos; i < pos+len; ++i)
1410 {
1411 itText = d->at(i);
1412 // #6165 : applying style on last character applies style on whole text on next open
1413 /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
1414 itText->parstyle->charStyle().applyCharStyle(style);*/
1415
1416 // Does not work well, do not reenable before checking #9337, #9376 and #9428
1417 // #9173 et. al.: move charstyle to parstyle if whole paragraph is affected
1418 /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr && lastParStart >= 0)
1419 {
1420 eraseCharStyle(lastParStart, i - lastParStart, style);
1421 itText->parstyle->charStyle().applyCharStyle(style);
1422 lastParStart = i + 1;
1423 }*/
1424 itText->applyCharStyle(style);
1425 }
1426 // Does not work well, do not reenable before checking #9337, #9376 and #9428
1427 /*if (pos + signed(len) == length() && lastParStart >= 0)
1428 {
1429 eraseCharStyle(lastParStart, length() - lastParStart, style);
1430 d->trailingStyle.charStyle().applyCharStyle(style);
1431 }*/
1432
1433 invalidate(pos, pos + len);
1434 }
1435
1436
1437
1438 void StoryText::eraseCharStyle(int pos, uint len, const CharStyle& style )
1439 {
1440 if (pos < 0)
1441 pos += length();
1442
1443 assert(pos >= 0);
1444 assert(pos + signed(len) <= length());
1445
1446 if (len == 0)
1447 return;
1448
1449 ScText* itText;
1450 for (uint i=pos; i < pos+len; ++i) {
1451 itText = d->at(i);
1452 // FIXME?? see #6165 : should we really erase charstyle of paragraph style??
1453 if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
1454 itText->parstyle->charStyle().eraseCharStyle(style);
1455 itText->eraseCharStyle(style);
1456 }
1457 // Does not work well, do not reenable before checking #9337, #9376 and #9428
1458 /*if (pos + signed(len) == length())
1459 {
1460 d->trailingStyle.charStyle().eraseCharStyle(style);
1461 }*/
1462
1463 invalidate(pos, pos + len);
1464 }
1465
1466 void StoryText::applyStyle(int pos, const ParagraphStyle& style, bool rmDirectFormatting)
1467 {
1468 if (pos < 0)
1469 pos += length();
1470
1471 assert(pos >= 0);
1472 assert(pos <= length());
1473
1474 int i = pos;
1475 while (i < length() && d->at(i)->ch != SpecialChars::PARSEP)
1476 ++i;
1477
1478 if (i < length())
1479 {
1480 if (!d->at(i)->parstyle) {
1481 qDebug("PARSEP without style at pos %i", i);
1482 d->at(i)->parstyle = new ParagraphStyle();
1483 d->at(i)->parstyle->setContext( & d->pstyleContext);
1484 // d->at(i)->parstyle->setName( "para(applyStyle)" ); // DON'T TRANSLATE
1485 // d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DON'T TRANSLATE
1486 // d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
1487 }
1488 // qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
1489 d->at(i)->parstyle->applyStyle(style);
1490 }
1491 else {
1492 // not happy about this but inserting a new PARSEP makes more trouble
1493 // qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
1494 d->trailingStyle.applyStyle(style);
1495 }
1496 if (rmDirectFormatting)
1497 {
1498 --i;
1499 while (i >= 0 && d->at(i)->ch != SpecialChars::PARSEP)
1500 {
1501 d->at(i)->eraseDirectFormatting();
1502 --i;
1503 }
1504 }
1505 invalidate(pos, qMin(i, length()));
1506 }
1507
1508 void StoryText::eraseStyle(int pos, const ParagraphStyle& style)
1509 {
1510 if (pos < 0)
1511 pos += length();
1512
1513 assert(pos >= 0);
1514 assert(pos <= length());
1515
1516 int i = pos;
1517 while (i < length() && d->at(i)->ch != SpecialChars::PARSEP)
1518 ++i;
1519
1520 if (i < length())
1521 {
1522 if (!d->at(i)->parstyle) {
1523 qDebug("PARSEP without style at pos %i", i);
1524 d->at(i)->parstyle = new ParagraphStyle();
1525 d->at(i)->parstyle->setContext( & d->pstyleContext);
1526 // d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DON'T TRANSLATE
1527 // d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DON'T TRANSLATE
1528 // d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
1529 }
1530 // qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
1531 d->at(i)->parstyle->eraseStyle(style);
1532 }
1533 else {
1534 // not happy about this but inserting a new PARSEP makes more trouble
1535 // qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
1536 d->trailingStyle.eraseStyle(style);
1537 }
1538 invalidate(pos, qMin(i, length()));
1539 }
1540
1541
1542 void StoryText::setStyle(int pos, const ParagraphStyle& style)
1543 {
1544 eraseStyle(pos, paragraphStyle(pos));
1545 applyStyle(pos, style);
1546 }
1547
1548
1549 void StoryText::setCharStyle(int pos, uint len, const CharStyle& style)
1550 {
1551 if (pos < 0)
1552 pos += length();
1553
1554 assert(pos >= 0);
1555 assert(len <= unsigned(length()));
1556 assert(pos + signed(len) <= length());
1557
1558 if (len == 0)
1559 return;
1560
1561 ScText* itText;
1562 for (uint i=pos; i < pos+len; ++i)
1563 {
1564 itText = d->at(i);
1565 // #6165 : applying style on last character applies style on whole text on next open
1566 /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
1567 itText->parstyle->charStyle() = style;*/
1568 itText->setStyle(style);
1569 }
1570
1571 invalidate(pos, pos + len);
1572 }
1573
1574
1575
1576 void StoryText::getNamedResources(ResourceCollection& lists) const
1577 {
1578 d->defaultStyle.getNamedResources(lists);
1579 d->trailingStyle.getNamedResources(lists);
1580
1581 for (int i=0; i < length(); ++i)
1582 {
1583 if (text(i) == SpecialChars::PARSEP)
1584 paragraphStyle(i).getNamedResources(lists);
1585 else if (hasObject(i))
1586 getItem(i)->getNamedResources(lists);
1587 else
1588 charStyle(i).getNamedResources(lists);
1589 }
1590 }
1591
1592
1593 void StoryText::replaceStyles(const QMap<QString,QString>& newNameForOld)
1594 {
1595 ResourceCollection newnames;
1596 newnames.mapStyles(newNameForOld);
1597 replaceNamedResources(newnames);
1598 }
1599
1600 void StoryText::replaceNamedResources(ResourceCollection& newNames)
1601 {
1602 int len = length();
1603
1604 d->defaultStyle.replaceNamedResources(newNames);
1605 d->trailingStyle.replaceNamedResources(newNames);
1606
1607 if (len == 0)
1608 return;
1609
1610 ScText* itText;
1611 for (int i=0; i < len; ++i)
1612 {
1613 itText = d->at(i);
1614 if (itText->parstyle)
1615 itText->parstyle->replaceNamedResources(newNames);
1616 else
1617 itText->replaceNamedResources(newNames);
1618 }
1619
1620 invalidate(0, len);
1621 }
1622
1623
1624 void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
1625 {
1626 ResourceCollection newnames;
1627 newnames.mapCharStyles(newNameForOld);
1628 replaceNamedResources(newnames);
1629 }
1630
1631 void StoryText::fixLegacyFormatting()
1632 {
1633 if (length() == 0)
1634 return;
1635
1636 for (int i = 0; i < length(); ++ i)
1637 {
1638 if (d->at(i)->ch == SpecialChars::PARSEP)
1639 fixLegacyFormatting(i);
1640 }
1641 fixLegacyFormatting( length() );
1642 }
1643
1644 void StoryText::fixLegacyFormatting(int pos)
1645 {
1646 if (length() == 0)
1647 return;
1648
1649 if (pos < 0)
1650 pos += length();
1651
1652 assert(pos >= 0);
1653 assert(pos <= length());
1654
1655 int i = pos;
1656 while (i > 0 && d->at(i - 1)->ch != SpecialChars::PARSEP)
1657 --i;
1658
1659 const ParagraphStyle& parStyle = this->paragraphStyle(pos);
1660 parStyle.validate();
1661
1662 if (parStyle.hasParent())
1663 {
1664 int start = i;
1665 while ((i < length()) && (d->at(i)->ch != SpecialChars::PARSEP))
1666 {
1667 d->at(i)->validate();
1668 d->at(i)->eraseCharStyle( parStyle.charStyle() );
1669 ++i;
1670 }
1671 invalidate(start, qMin(i + 1, length()));
1672 }
1673 }
1674
1675 uint StoryText::nrOfParagraph() const
1676 {
1677 return nrOfParagraph(d->cursorPosition);
1678 }
1679
1680 uint StoryText::nrOfParagraph(int pos) const
1681 {
1682 uint result = 0;
1683 StoryText* that = const_cast<StoryText *>(this);
1684 bool lastWasPARSEP = true;
1685 pos = qMin(pos, that->length());
1686 for (int i=0; i < pos; ++i)
1687 {
1688 lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
1689 if (lastWasPARSEP)
1690 ++result;
1691 }
1692 return result;
1693 }
1694
1695 uint StoryText::nrOfParagraphs() const
1696 {
1697 uint result = 0;
1698 StoryText* that = const_cast<StoryText *>(this);
1699 bool lastWasPARSEP = true;
1700 for (int i=0; i < length(); ++i)
1701 {
1702 lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
1703 if (lastWasPARSEP)
1704 ++result;
1705 }
1706 return lastWasPARSEP ? result : result + 1;
1707 }
1708
1709 int StoryText::startOfParagraph() const
1710 {
1711 return startOfParagraph(nrOfParagraph());
1712 }
1713
1714 int StoryText::startOfParagraph(uint index) const
1715 {
1716 if (index == 0)
1717 return 0;
1718
1719 StoryText* that = const_cast<StoryText *>(this);
1720 for (int i=0; i < length(); ++i)
1721 {
1722 if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
1723 return i + 1;
1724 }
1725 return length();
1726 }
1727
1728 int StoryText::endOfParagraph() const
1729 {
1730 return endOfParagraph(nrOfParagraph());
1731 }
1732
1733 int StoryText::endOfParagraph(uint index) const
1734 {
1735 ++index;
1736 StoryText* that = const_cast<StoryText *>(this);
1737 for (int i=0; i < length(); ++i)
1738 {
1739 if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
1740 return i;
1741 }
1742 return length();
1743 }
1744
1745 uint StoryText::nrOfRuns() const
1746 {
1747 return length();
1748 }
1749
1750 int StoryText::startOfRun(uint index) const
1751 {
1752 return index;
1753 }
1754
1755 int StoryText::endOfRun(uint index) const
1756 {
1757 return index + 1;
1758 }
1759
1760 // positioning. all positioning methods return char positions
1761
1762 int StoryText::nextChar(int pos)
1763 {
1764 if (pos < length())
1765 return pos + 1;
1766 return length();
1767 }
1768
1769 int StoryText::prevChar(int pos)
1770 {
1771 if (pos > 0)
1772 return pos - 1;
1773 return 0;
1774 }
1775
1776 int StoryText::firstWord()
1777 {
1778 int len = length();
1779 int pos = 0;
1780
1781 while (pos < len)
1782 {
1783 if (text(pos).isLetter())
1784 break;
1785 ++pos;
1786 }
1787 return pos;
1788 }
1789
1790 int StoryText::nextWord(int pos)
1791 {
1792 BreakIterator* it = getWordIterator();
1793 if (!it)
1794 return pos;
1795
1796 it->setText((const UChar*) plainText().utf16());
1797 pos = it->following(pos);
1798
1799 int len = length();
1800 while ((pos < len) && (text(pos).isSpace() || text(pos).isPunct()))
1801 ++pos;
1802 return pos;
1803 }
1804
1805 int StoryText::prevWord(int pos)
1806 {
1807 BreakIterator* it = getWordIterator();
1808 if (!it)
1809 return pos;
1810
1811 it->setText((const UChar*) plainText().utf16());
1812 pos = it->preceding(pos);
1813 return pos;
1814 }
1815
1816 int StoryText::endOfWord(int pos) const
1817 {
1818 BreakIterator* it = getWordIterator();
1819 if (!it)
1820 return pos;
1821
1822 it->setText((const UChar*) plainText().utf16());
1823 pos = it->following(pos);
1824 return pos;
1825 }
1826
1827 int StoryText::endOfSentence(int pos) const
1828 {
1829 BreakIterator* it = getSentenceIterator();
1830 if (!it)
1831 return pos;
1832
1833 it->setText((const UChar*) plainText().utf16());
1834 int end = it->following(pos);
1835 return end;
1836 }
1837
1838 int StoryText::nextSentence(int pos)
1839 {
1840 BreakIterator* it = getSentenceIterator();
1841 if (!it)
1842 return pos;
1843
1844 it->setText((const UChar*) plainText().utf16());
1845 pos = it->following(pos);
1846 pos = it->next();
1847 return pos;
1848 }
1849
1850 int StoryText::prevSentence(int pos)
1851 {
1852 //we cannot go before the first position so just return it.
1853 if (pos == 0)
1854 return 0;
1855 BreakIterator* it = getSentenceIterator();
1856 if (!it)
1857 return pos;
1858
1859 it->setText((const UChar*) plainText().utf16());
1860 pos = it->preceding(pos);
1861 return pos;
1862 }
1863 int StoryText::nextParagraph(int pos)
1864 {
1865 int len = length();
1866 pos = qMin(len, pos + 1);
1867 while (pos < len && text(pos) != SpecialChars::PARSEP)
1868 ++pos;
1869 return pos;
1870 }
1871 int StoryText::prevParagraph(int pos)
1872 {
1873 pos = qMax(0, pos - 1);
1874 while (pos > 0 && text(pos) != SpecialChars::PARSEP)
1875 --pos;
1876 return pos;
1877 }
1878
1879 // selection
1880 int StoryText::startOfSelection() const
1881 {
1882 return d->selFirst <= d->selLast? d->selFirst : 0;
1883 }
1884
1885 int StoryText::endOfSelection() const
1886 {
1887 return d->selFirst <= d->selLast? d->selLast + 1 : -1;
1888 }
1889
1890 int StoryText::selectionLength() const
1891 {
1892 //FIX ME - sometimes I saw values equal or greater than length of text
1893 int last = d->selLast;
1894 if (d->selFirst >= length())
1895 return 0;
1896 if (d->selLast >= length())
1897 last = length() -1;
1898 return d->selFirst <= last? last - d->selFirst + 1 : 0;
1899 }
1900
1901 bool StoryText::hasSelection() const
1902 {
1903 return selectionLength() > 0;
1904 }
1905
1906
1907 bool StoryText::selected(int pos) const
1908 {
1909 return (d->selFirst <= pos && pos <= d->selLast)
1910 // || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
1911 ;
1912 }
1913
1914 QString StoryText::selectedText() const
1915 {
1916 int selLen = selectionLength();
1917 if (selLen <= 0)
1918 return QString();
1919 return text(startOfSelection(), selLen);
1920 }
1921
1922 int StoryText::selectWord(int pos)
1923 {
1924 //Double click in a frame to select a word
1925 BreakIterator* it = getWordIterator();
1926 if (!it)
1927 return pos;
1928 it->setText((const UChar*) plainText().utf16());
1929 int start = it->preceding(pos + 1);
1930 int end = it->next();
1931 int wordLength = end - start;
1932 if (wordLength > 0)
1933 {
1934 select(start, wordLength);
1935 setCursorPosition(endOfSelection());
1936 }
1937 else
1938 {
1939 deselectAll();
1940 setCursorPosition(start);
1941 }
1942 return start;
1943 }
1944
1945
1946 void StoryText::select(int pos, int len, bool on)
1947 {
1948 if (pos < 0)
1949 pos += length();
1950 if (len < 0)
1951 len = 0;
1952
1953 assert( pos >= 0 );
1954 assert( pos + signed(len) <= length() );
1955
1956 // qDebug("old selection: %d - %d", d->selFirst, d->selLast);
1957
1958 // StoryText* that = const_cast<StoryText *>(this);
1959 // for (int i=pos; i < pos+signed(len); ++i)
1960 // that->at(i)->cselect = on;
1961
1962 if (on) {
1963 // extend if possible
1964 if (selected(pos - 1))
1965 d->selLast = qMax(d->selLast, pos + len - 1);
1966 else if (selected(pos + len))
1967 d->selFirst = qMin(d->selFirst, pos);
1968 else {
1969 d->selFirst = pos;
1970 d->selLast = pos + len - 1;
1971 }
1972 }
1973 else {
1974 if (pos <= d->selFirst && d->selLast < pos + signed(len))
1975 deselectAll();
1976 // shrink
1977 else if (!selected(pos - 1) && selected(pos + len - 1))
1978 d->selFirst = pos + len;
1979 else if (selected(pos) && !selected(pos + len))
1980 d->selLast = pos - 1;
1981 else if (selected(pos) || selected(pos + len - 1))
1982 // Grr, deselection splits selection
1983 d->selLast = pos - 1;
1984 }
1985
1986 fixSurrogateSelection();
1987
1988 // qDebug("new selection: %d - %d", d->selFirst, d->selLast);
1989 }
1990
1991 void StoryText::extendSelection(int oldPos, int newPos)
1992 {
1993 if (d->selFirst <= d->selLast)
1994 {
1995 // have selection
1996 if (d->selLast == oldPos - 1)
1997 {
1998 d->selLast = newPos - 1;
1999 return;
2000 }
2001 if (d->selFirst == oldPos)
2002 {
2003 d->selFirst = newPos;
2004 return;
2005 }
2006 // can't extend, fall through
2007 }
2008 // no previous selection
2009 if (newPos > oldPos)
2010 {
2011 d->selFirst = oldPos;
2012 d->selLast = newPos - 1;
2013 }
2014 else
2015 {
2016 d->selFirst = newPos;
2017 d->selLast = oldPos - 1;
2018 }
2019
2020 fixSurrogateSelection();
2021 }
2022
2023
2024 void StoryText::fixSurrogateSelection()
2025 {
2026 if (isLowSurrogate(d->selFirst) && isHighSurrogate(d->selFirst - 1))
2027 d->selFirst -= 1;
2028 if (isHighSurrogate(d->selLast) && isLowSurrogate(d->selLast + 1))
2029 d->selLast += 1;
2030 }
2031
2032 BreakIterator* StoryText::m_graphemeIterator = nullptr;
2033
2034 BreakIterator* StoryText::getGraphemeIterator()
2035 {
2036 UErrorCode status = U_ZERO_ERROR;
2037 if (m_graphemeIterator == nullptr)
2038 m_graphemeIterator = BreakIterator::createCharacterInstance(Locale(), status);
2039
2040 if (U_FAILURE(status))
2041 {
2042 delete m_graphemeIterator;
2043 m_graphemeIterator = nullptr;
2044 }
2045
2046 return m_graphemeIterator;
2047 }
2048
2049 BreakIterator* StoryText::m_wordIterator = nullptr;
2050
2051 BreakIterator* StoryText::getWordIterator()
2052 {
2053 UErrorCode status = U_ZERO_ERROR;
2054 if (m_wordIterator == nullptr)
2055 m_wordIterator = BreakIterator::createWordInstance(Locale(), status);
2056
2057 if (U_FAILURE(status))
2058 {
2059 delete m_wordIterator;
2060 m_wordIterator = nullptr;
2061 }
2062 return m_wordIterator;
2063 }
2064
2065 BreakIterator* StoryText::m_sentenceIterator = nullptr;
2066
2067 BreakIterator* StoryText::getSentenceIterator()
2068 {
2069 UErrorCode status = U_ZERO_ERROR;
2070 if (m_sentenceIterator == nullptr)
2071 m_sentenceIterator = BreakIterator::createSentenceInstance(Locale(), status);
2072
2073 if (U_FAILURE(status))
2074 {
2075 delete m_sentenceIterator;
2076 m_sentenceIterator = nullptr;
2077 }
2078
2079 return m_sentenceIterator;
2080 }
2081
2082 BreakIterator* StoryText::m_lineIterator = nullptr;
2083
2084 BreakIterator* StoryText::getLineIterator()
2085 {
2086 UErrorCode status = U_ZERO_ERROR;
2087 if (m_lineIterator == nullptr)
2088 m_lineIterator = BreakIterator::createLineInstance(Locale(), status);
2089
2090 if (U_FAILURE(status))
2091 {
2092 delete m_lineIterator;
2093 m_lineIterator = nullptr;
2094 }
2095
2096 return m_lineIterator;
2097 }
2098
2099 void StoryText::selectAll()
2100 {
2101 /* StoryText* that = const_cast<StoryText *>(this);
2102 that->at(0);
2103 for (int i=0; i < length(); ++i) {
2104 that->current()->cselect = true;
2105 that->next();
2106 }
2107 */
2108 d->selFirst = 0;
2109 d->selLast = length() - 1;
2110 }
2111
2112 void StoryText::deselectAll()
2113 {
2114 /* StoryText* that = const_cast<StoryText *>(this);
2115 that->at(0);
2116 for (int i=0; i < length(); ++i) {
2117 that->current()->cselect = false;
2118 that->next();
2119 }
2120 */
2121 d->selFirst = 0;
2122 d->selLast = -1;
2123 }
2124
2125 void StoryText::removeSelection()
2126 {
2127 // qDebug("removeSelection: %d - %d", d->selFirst, d->selLast);
2128 if (d->selFirst > d->selLast)
2129 return;
2130
2131 assert( d->selFirst >= 0 );
2132 assert( d->selLast < length() );
2133
2134 removeChars(d->selFirst, d->selLast - d->selFirst+1);
2135 deselectAll();
2136 }
2137
2138
2139
2140 void StoryText::invalidateObject(const PageItem * embedded)
2141 {
2142 }
2143
2144 void StoryText::invalidateLayout()
2145 {
2146 }
2147
2148 void StoryText::invalidateAll()
2149 {
2150 d->pstyleContext.invalidate();
2151 invalidate(0, length());
2152 }
2153
2154 void StoryText::invalidate(int firstItem, int endItem)
2155 {
2156 for (int i = firstItem; i < endItem; ++i)
2157 {
2158 ParagraphStyle* par = item(i)->parstyle;
2159 if (par)
2160 par->charStyleContext()->invalidate();
2161 }
2162 if (!signalsBlocked())
2163 emit changed(firstItem, endItem);
2164 }
2165
2166 // physical view
2167
2168 /*
2169 void StoryText::validate()
2170 {
2171 static bool withinValidate = false;
2172
2173 assert( !withinValidate );
2174 withinValidate = true;
2175
2176 withinValidate = false;
2177 }
2178 */
2179
2180 ScText* StoryText::item(int index)
2181 {
2182 assert( index < length() );
2183 return const_cast<StoryText *>(this)->d->at(index);
2184 }
2185
2186 const ScText* StoryText::item(int index) const
2187 {
2188 assert( index < length() );
2189 return const_cast<StoryText *>(this)->d->at(index);
2190 }
2191
2192
2193 using namespace desaxe;
2194
2195 void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
2196 {
2197 Xml_attr empty;
2198 Xml_attr pageno;
2199 pageno.insert("name", "pgno");
2200 Xml_attr pageco;
2201 pageco.insert("name", "pgco");
2202
2203 handler.begin(elemtag, empty);
2204 defaultStyle().saxx(handler, "defaultstyle");
2205
2206 CharStyle lastStyle(charStyle(0));
2207 bool lastWasPar = true;
2208 int lastPos = 0;
2209 handler.begin("p", empty);
2210 paragraphStyle(0).saxx(handler);
2211 handler.begin("span", empty);
2212 lastStyle.saxx(handler);
2213 for (int i=0; i < length(); ++i)
2214 {
2215 const QChar curr(text(i));
2216 const CharStyle& style(charStyle(i));
2217
2218 if (curr == SpecialChars::OBJECT ||
2219 curr == SpecialChars::TAB ||
2220 curr == SpecialChars::PARSEP ||
2221 curr == SpecialChars::LINEBREAK ||
2222 curr == SpecialChars::COLBREAK ||
2223 curr == SpecialChars::FRAMEBREAK ||
2224 curr == SpecialChars::PAGENUMBER ||
2225 curr == SpecialChars::PAGECOUNT ||
2226 curr.unicode() < 32 ||
2227 (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
2228 curr.unicode() == 0xfffe || curr.unicode() == 0xffff ||
2229 style != lastStyle)
2230 {
2231 // something new, write pending chars
2232 if (i - lastPos > 0)
2233 {
2234 handler.chars(textWithSoftHyphens(lastPos, i-lastPos));
2235 }
2236 lastPos = i;
2237 }
2238
2239 lastWasPar = (curr == SpecialChars::PARSEP);
2240 if (lastWasPar)
2241 {
2242 handler.end("span");
2243 handler.end("p");
2244 handler.begin("p", empty);
2245 paragraphStyle(i+1).saxx(handler);
2246 handler.begin("span", empty);
2247 lastStyle.erase();
2248 }
2249 else if (this->hasObject(i))
2250 {
2251 getItem(i)->saxx(handler);
2252 }
2253 else if (hasMark(i))
2254 {
2255 Mark* mrk = mark(i);
2256 if ((m_doc->m_Selection->itemAt(0)->isNoteFrame() && mrk->isType(MARKNoteMasterType))
2257 || (!m_doc->m_Selection->itemAt(0)->isTextFrame() && mrk->isType(MARKNoteFrameType))
2258 || mrk->isType(MARKBullNumType))
2259 continue; //do not insert notes marks into text frames nad vice versa
2260 Xml_attr mark_attr;
2261 mark_attr.insert("label", mrk->label);
2262 mark_attr.insert("typ", QString::number((int )mrk->getType()));
2263 // if (!mrk->isType(MARKBullNumType))
2264 // {
2265 // mark_attr.insert("strtxt", mrk->getString());
2266 // ParagraphStyle pstyle = this->paragraphStyle(i);
2267 // mark_attr.insert("style_peoffset", QString::number(pstyle.parEffectOffset(),'f',2));
2268 // mark_attr.insert("style_peindent", QString::number(pstyle.parEffectIndent(),'f',2));
2269 // mark_attr.insert("style_pecharstyle", pstyle.peCharStyleName());
2270 // mark_attr.insert("style_hasbul", pstyle.hasBullet() ? "1" : "0");
2271 // mark_attr.insert("style_bulletstr", pstyle.bulletStr());
2272 // mark_attr.insert("style_hasnum", pstyle.hasNum() ? "1" : "0");
2273 // mark_attr.insert("style_numname", pstyle.numName());
2274 // mark_attr.insert("style_numformat", QString::number( (int)pstyle.numFormat()));
2275 // mark_attr.insert("style_numprefix", pstyle.numPrefix());
2276 // mark_attr.insert("style_numsuffix", pstyle.numSuffix());
2277 // mark_attr.insert("style_numlevel", QString::number(pstyle.numLevel()));
2278 // mark_attr.insert("style_numstart", QString::number(pstyle.numStart()));
2279 // mark_attr.insert("style_numrestart", QString::number((int) pstyle.numRestart()));
2280 // mark_attr.insert("style_numother", pstyle.numOther() ? "1" : "0");
2281 // mark_attr.insert("style_numhigher", pstyle.numHigher() ? "1" : "0");
2282 // }
2283 if (mrk->isType(MARK2ItemType) && (mrk->getItemPtr() != nullptr))
2284 mark_attr.insert("item", mrk->getItemPtr()->itemName());
2285 else if (mrk->isType(MARK2MarkType))
2286 {
2287 QString l;
2288 MarkType t;
2289 mrk->getMark(l, t);
2290 if (m_doc->getMark(l,t) != nullptr)
2291 {
2292 mark_attr.insert("mark_l", l);
2293 mark_attr.insert("mark_t", QString::number((int) t));
2294 }
2295 }
2296 else if (mrk->isType(MARKNoteMasterType))
2297 {
2298 TextNote * note = mrk->getNotePtr();
2299 assert(note != nullptr);
2300 mark_attr.insert("nStyle", note->notesStyle()->name());
2301 mark_attr.insert("note",note->saxedText());
2302 //store noteframe name for inserting into note if it is non-auto-removable
2303 if (note->noteMark() && note->noteMark()->getItemPtr() && !note->noteMark()->getItemPtr()->isAutoNoteFrame())
2304 mark_attr.insert("noteframe", note->noteMark()->getItemPtr()->getUName());
2305 }
2306 handler.beginEnd("mark", mark_attr);
2307 }
2308 else if (curr == SpecialChars::TAB)
2309 {
2310 handler.beginEnd("tab", empty);
2311 }
2312 else if (curr == SpecialChars::LINEBREAK)
2313 {
2314 handler.beginEnd("breakline", empty);
2315 }
2316 else if (curr == SpecialChars::COLBREAK)
2317 {
2318 handler.beginEnd("breakcol", empty);
2319 }
2320 else if (curr == SpecialChars::FRAMEBREAK)
2321 {
2322 handler.beginEnd("breakframe", empty);
2323 }
2324 else if (curr == SpecialChars::PAGENUMBER)
2325 {
2326 handler.beginEnd("var", pageno);
2327 }
2328 else if (curr == SpecialChars::PAGECOUNT)
2329 {
2330 handler.beginEnd("var", pageco);
2331 }
2332 else if (curr.unicode() < 32 ||
2333 (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
2334 curr.unicode() == 0xfffe || curr.unicode() == 0xffff)
2335 {
2336 Xml_attr unic;
2337 unic.insert("code", toXMLString(curr.unicode()));
2338 handler.beginEnd("unicode", unic);
2339 }
2340 else if (lastStyle != style)
2341 {
2342 handler.end("span");
2343 handler.begin("span", empty);
2344 style.saxx(handler);
2345 lastStyle = style;
2346 continue;
2347 }
2348 else
2349 continue;
2350 lastPos = i+1;
2351 }
2352
2353 if (length() - lastPos > 0)
2354 handler.chars(textWithSoftHyphens(lastPos, length()-lastPos));
2355 handler.end("span");
2356 handler.end("p");
2357
2358 // if (!lastWasPar)
2359 // paragraphStyle(length()-1).saxx(handler);
2360
2361 handler.end(elemtag);
2362
2363 }
2364
2365
2366 class AppendText_body : public Action_body
2367 {
2368 public:
2369 void chars(const Xml_string& txt)
2370 {
2371 QChar chr;
2372 int lastPos = 0, len;
2373 StoryText* obj = this->dig->top<StoryText>();
2374 for (int i = 0; i < txt.length(); ++i)
2375 {
2376 chr = txt.at(i);
2377 if (chr == SpecialChars::SHYPHEN && i > 0)
2378 {
2379 int toInsert = i - lastPos;
2380 if (toInsert > 0)
2381 obj->insertChars(obj->length(), txt.mid(lastPos, toInsert));
2382 len = obj->length();
2383 // qreal SHY means user provided SHY, single SHY is automatic one
2384 if (obj->hasFlag(len-1, ScLayout_HyphenationPossible))
2385 {
2386 obj->clearFlag(len-1, ScLayout_HyphenationPossible);
2387 obj->insertChars(len, QString(chr));
2388 }
2389 else
2390 {
2391 obj->setFlag(len-1, ScLayout_HyphenationPossible);
2392 }
2393 lastPos = i + 1;
2394 }
2395 }
2396 if (lastPos < txt.length())
2397 {
2398 QString ins = (lastPos == 0) ? txt : txt.right(txt.length() - lastPos);
2399 len = obj->length();
2400 obj->insertChars(len, ins);
2401 }
2402 }
2403 };
2404
2405 struct AppendText : public MakeAction<AppendText_body>
2406 {};
2407
2408
2409 class AppendSpecial_body : public Action_body
2410 {
2411 public:
2412 AppendSpecial_body(QChar sp) : chr(sp) {}
2413
2414 void begin(const Xml_string& tag, Xml_attr attr)
2415 {
2416 StoryText* obj = this->dig->top<StoryText>();
2417 Xml_attr::iterator code = attr.find("code");
2418 Xml_attr::iterator name = attr.find("name");
2419 if (tag == "unicode" && code != attr.end())
2420 obj->insertChars(-1, QChar(parseUInt(Xml_data(code))));
2421 else if (tag == "var" && name != attr.end())
2422 {
2423 if (Xml_data(name) == "pgno")
2424 obj->insertChars(-1, SpecialChars::PAGENUMBER);
2425 else
2426 obj->insertChars(-1, SpecialChars::PAGECOUNT);
2427 }
2428 else
2429 obj->insertChars(-1, chr);
2430 }
2431 private:
2432 QChar chr;
2433 };
2434
2435 struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar>
2436 {
2437 AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {}
2438 AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {}
2439 };
2440
2441
2442 class AppendInlineFrame_body : public Action_body
2443 {
2444 public:
2445 void end(const Xml_string& tag) // this could be a setter if we had StoryText::appendObject() ...
2446 {
2447 StoryText* story = this->dig->top<StoryText>(1);
2448 PageItem* obj = this->dig->top<PageItem>(0);
2449 // FIXME : currently inline objects are added to doc items when parsing
2450 // We have to remove them from object list and add them to the FrameItems list
2451 // before inserting them in story object
2452 QList<PageItem*> *items = obj->doc()->Items;
2453 if (items->count() > 0)
2454 {
2455 if (items->last() == obj) // try a fast path first
2456 items->removeLast();
2457 else
2458 items->removeAll(obj);
2459 }
2460 story->insertObject(-1, obj->doc()->addToInlineFrames(obj));
2461 }
2462 };
2463
2464 struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body>
2465 {};
2466
2467 //marks support
2468 class AppendMark_body : public Action_body
2469 {
2470 public:
2471 void begin(const Xml_string& tag, Xml_attr attr)
2472 {
2473 StoryText* story = this->dig->top<StoryText>();
2474 QString l = "";
2475 MarkType t = MARKNoType;
2476
2477 Mark* mrk = nullptr;
2478
2479 if (tag == "mark")
2480 {
2481 Xml_attr::iterator lIt = attr.find("label");
2482 Xml_attr::iterator tIt = attr.find("typ");
2483 // Xml_attr::iterator nf_It = attr.find("noteframe");
2484 if (lIt != attr.end())
2485 l = Xml_data(lIt);
2486 if (tIt != attr.end())
2487 t = (MarkType) parseInt(Xml_data(tIt));
2488 if (t != MARKBullNumType)
2489 {
2490 ScribusDoc* doc = this->dig->lookup<ScribusDoc>("<scribusdoc>");
2491 // ParagraphStyle* pstyle = nullptr;
2492 if (t == MARKVariableTextType)
2493 mrk = doc->getMark(l,t);
2494 // else if (t == MARKBullNumType)
2495 // {
2496 // mrk = (Mark*) new BulNumMark();
2497 // Xml_attr::iterator str_It = attr.find("strtxt");
2498 // mrk->setString(Xml_data(str_It));
2499 // pstyle = new ParagraphStyle();
2500 // Xml_attr::iterator ite;
2501 // ite = attr.find("style_peoffset");
2502 // if (ite != attr.end())
2503 // pstyle->setParEffectOffset(parseDouble(Xml_data(ite)));
2504 // ite = attr.find("style_peindent");
2505 // if (ite != attr.end())
2506 // pstyle->setParEffectIndent(parseDouble(Xml_data(ite)));
2507 // ite = attr.find("style_pecharstyle");
2508 // if (ite != attr.end())
2509 // pstyle->setPeCharStyleName(Xml_data(ite));
2510 // ite = attr.find("style_hasbul");
2511 // if (ite != attr.end())
2512 // pstyle->setHasBullet(parseInt(Xml_data(ite)) == 1);
2513 // ite = attr.find("style_bulletstr");
2514 // if (ite != attr.end())
2515 // pstyle->setBulletStr(Xml_data(ite));
2516 // ite = attr.find("style_hasnum");
2517 // if (ite != attr.end())
2518 // pstyle->setHasNum(parseInt(Xml_data(ite)) == 1);
2519 // ite = attr.find("style_numname");
2520 // if (ite != attr.end())
2521 // pstyle->setNumName(Xml_data(ite));
2522 // ite = attr.find("style_numformat");
2523 // if (ite != attr.end())
2524 // pstyle->setNumFormat(parseInt(Xml_data(ite)));
2525 // ite = attr.find("style_numprefix");
2526 // if (ite != attr.end())
2527 // pstyle->setNumPrefix(Xml_data(ite));
2528 // ite = attr.find("style_numsuffix");
2529 // if (ite != attr.end())
2530 // pstyle->setNumSuffix(Xml_data(ite));
2531 // ite = attr.find("style_numlevel");
2532 // if (ite != attr.end())
2533 // pstyle->setNumLevel(parseInt(Xml_data(ite)));
2534 // ite = attr.find("style_numstart");
2535 // if (ite != attr.end())
2536 // pstyle->setNumStart(parseInt(Xml_data(ite)));
2537 // ite = attr.find("style_numrestart");
2538 // if (ite != attr.end())
2539 // pstyle->setNumRestart(parseInt(Xml_data(ite)));
2540 // ite = attr.find("style_numother");
2541 // if (ite != attr.end())
2542 // pstyle->setNumOther(parseInt(Xml_data(ite)) == 1);
2543 // ite = attr.find("style_numhigher");
2544 // if (ite != attr.end())
2545 // pstyle->setNumHigher(parseInt(Xml_data(ite)) == 1);
2546 // }
2547 else
2548 {
2549 mrk = doc->newMark();
2550 getUniqueName(l,doc->marksLabelsList(t), "_");
2551 mrk->label = l;
2552 mrk->OwnPage = doc->currentPage()->pageNr();
2553 mrk->setType(t);
2554 Xml_attr::iterator iIt = attr.find("item");
2555 Xml_attr::iterator m_lIt = attr.find("mark_l");
2556 Xml_attr::iterator m_tIt = attr.find("mark_t");
2557 if (mrk->isType(MARK2ItemType) && (iIt != attr.end()))
2558 {
2559 PageItem* item = doc->getItemFromName(Xml_data(iIt));
2560 mrk->setItemPtr(item);
2561 if (item == nullptr)
2562 mrk->setString("0");
2563 else
2564 mrk->setString(doc->getSectionPageNumberForPageIndex(item->OwnPage));
2565 mrk->setItemName(Xml_data(iIt));
2566 }
2567 if (mrk->isType(MARK2MarkType) && (m_lIt != attr.end()) && (m_tIt != attr.end()))
2568 {
2569 Mark* targetMark = doc->getMark(Xml_data(m_lIt), (MarkType) parseInt(Xml_data(m_tIt)));
2570 mrk->setMark(targetMark);
2571 if (targetMark == nullptr)
2572 mrk->setString("0");
2573 else
2574 mrk->setString(doc->getSectionPageNumberForPageIndex(targetMark->OwnPage));
2575 mrk->setItemName(Xml_data(m_lIt));
2576 }
2577 if (mrk->isType(MARKNoteMasterType))
2578 {
2579 Xml_attr::iterator nIt = attr.find("note");
2580 Xml_attr::iterator nsIt = attr.find("nStyle");
2581 NotesStyle* NS;
2582 if (nsIt == attr.end())
2583 NS = doc->m_docNotesStylesList.at(0);
2584 else
2585 NS = doc->getNotesStyle(Xml_data(nsIt));
2586 TextNote* note = doc->newNote(NS);
2587 note->setMasterMark(mrk);
2588 if (nIt != attr.end())
2589 note->setSaxedText(Xml_data(nIt));
2590 // if (!NS->isAutoRemoveEmptyNotesFrames() && (nf_It != attr.end()))
2591 // {
2592 // PageItem_NoteFrame* nF = (PageItem_NoteFrame*) doc->getItemFromName(Xml_data(nf_It));
2593 // if (nF != nullptr)
2594 // doc->m_Selection->itemAt(0)->asTextFrame()->setNoteFrame(nF);
2595 // }
2596 mrk->setNotePtr(note);
2597 doc->setNotesChanged(true);
2598 }
2599 doc->newMark(mrk);
2600 }
2601 story->insertMark(mrk);
2602 // if (pstyle != nullptr)
2603 // {
2604 // int i = story->cursorPosition() -1;
2605 // if (story->item(i)->parstyle == nullptr) {
2606 // story->item(i)->parstyle = new ParagraphStyle(*pstyle);
2607 // story->item(i)->parstyle->setContext( &doc->paragraphStyles());
2608 // }
2609 // else
2610 // story->item(i)->parstyle->applyStyle(*pstyle);
2611 // }
2612 }
2613 }
2614 }
2615 };
2616
2617 struct AppendMark : public MakeAction<AppendMark_body>
2618 {};
2619
2620 /*
2621 class ApplyStyle_body : public Action_body
2622 {
2623 public:
2624 void end(const Xml_string& tag)
2625 {
2626 qDebug() << "storytext desaxe: apply style";
2627 StoryText* story = this->dig->top<StoryText>(1);
2628 ParagraphStyle* obj = this->dig->top<ParagraphStyle>(0);
2629 story->applyStyle(-1, *obj);
2630 }
2631 };
2632
2633 struct ApplyStyle : public MakeAction<ApplyStyle_body>
2634 {};
2635
2636
2637 class ApplyCharStyle_body : public Action_body
2638 {
2639 public:
2640 ApplyCharStyle_body() : storyTag(StoryText::saxxDefaultElem), lastPos(0), lastStyle()
2641 {}
2642 ApplyCharStyle_body(const Xml_string& tag) : storyTag(tag), lastPos(0), lastStyle()
2643 {}
2644
2645 void end(const Xml_string& tag)
2646 {
2647 qDebug() << "storytext desaxe: apply charstyle";
2648 if (tag == CharStyle::saxxDefaultElem)
2649 {
2650 StoryText* story = this->dig->top<StoryText>(1);
2651 CharStyle* obj = this->dig->top<CharStyle>(0);
2652 int len = story->length();
2653 if (len > lastPos && lastStyle != *obj)
2654 {
2655 story->applyCharStyle(lastPos, len - lastPos, lastStyle);
2656 lastPos = len;
2657 lastStyle = *obj;
2658 }
2659 }
2660 else if (tag == StoryText::saxxDefaultElem)
2661 {
2662 StoryText* story = this->dig->top<StoryText>();
2663 int len = story->length();
2664 if (len > lastPos)
2665 {
2666 story->applyCharStyle(lastPos, len - lastPos, lastStyle);
2667 }
2668 }
2669 }
2670 private:
2671 Xml_string storyTag;
2672 int lastPos;
2673 CharStyle lastStyle;
2674 };
2675
2676 struct ApplyCharStyle : public MakeAction<ApplyCharStyle_body, const Xml_string&>
2677 {
2678 ApplyCharStyle() : MakeAction<ApplyCharStyle_body, const Xml_string&>() {}
2679 ApplyCharStyle(const Xml_string& tag) : MakeAction<ApplyCharStyle_body, const Xml_string&>(tag) {}
2680 };
2681
2682 */
2683
2684 class Paragraph_body : public Action_body
2685 {
2686 public:
2687 Paragraph_body() : lastPos(0), numPara(0), lastStyle(nullptr)
2688 {}
2689
2690 ~Paragraph_body()
2691 {
2692 delete lastStyle;
2693 }
2694
2695 virtual void reset()
2696 {
2697 numPara = 0;
2698 }
2699
2700 void begin(const Xml_string& tag, Xml_attr attr)
2701 {
2702 if (tag == "text-content")
2703 {
2704 numPara = 0;
2705 }
2706 else if (tag == "p")
2707 {
2708 StoryText* story = this->dig->top<StoryText>();
2709 // qDebug() << QString("startpar: %1->%2 %3->nullptr").arg(lastPos).arg(story->length()).arg((ulong)lastStyle);
2710 lastPos = story->length();
2711 if (numPara > 0) {
2712 story->insertChars(-1, SpecialChars::PARSEP);
2713 ++lastPos;
2714 }
2715 delete lastStyle;
2716 lastStyle = nullptr;
2717 }
2718 }
2719
2720 void end(const Xml_string& tag)
2721 {
2722 if (tag == ParagraphStyle::saxxDefaultElem)
2723 {
2724 delete lastStyle;
2725 lastStyle = this->dig->top<ParagraphStyle>(0);
2726 // qDebug() << QString("endstyle: %1 %2 %3").arg("?").arg(lastPos).arg((ulong)lastStyle);
2727 }
2728 else if (tag == "p")
2729 {
2730 StoryText* story = this->dig->top<StoryText>();
2731 int len = story->length();
2732 // qDebug() << QString("endpar: %1 %2 %3 %4").arg(len).arg(lastPos).arg((ulong)lastStyle).arg(lastStyle? lastStyle->parent() : QString());
2733 if (len >= lastPos && lastStyle)
2734 {
2735 story->applyStyle(lastPos, *lastStyle);
2736 }
2737 ++numPara;
2738 }
2739 }
2740 private:
2741 int lastPos;
2742 int numPara;
2743 ParagraphStyle* lastStyle;
2744 };
2745
2746 struct Paragraph : public MakeAction<Paragraph_body>
2747 {
2748 Paragraph() {}
2749 };
2750
2751
2752 class SpanAction_body : public Action_body
2753 {
2754 public:
2755 SpanAction_body() : lastPos(0), lastStyle(nullptr)
2756 {}
2757
2758 ~SpanAction_body()
2759 {
2760 delete lastStyle;
2761 }
2762
2763 void begin(const Xml_string& tag, Xml_attr attr)
2764 {
2765 // qDebug() << QString("spanaction: begin %1").arg(tag);
2766 if (tag == "span")
2767 {
2768 StoryText* story = this->dig->top<StoryText>();
2769 lastPos = story->length();
2770 delete lastStyle;
2771 lastStyle = nullptr;
2772 }
2773 }
2774
2775 void end(const Xml_string& tag)
2776 {
2777 if (tag == CharStyle::saxxDefaultElem)
2778 // qDebug() << QString("spanaction: end %1").arg(tag);
2779 {
2780 delete lastStyle;
2781 lastStyle = this->dig->top<CharStyle>(0);
2782 }
2783 else if (tag == "span")
2784 {
2785 StoryText* story = this->dig->top<StoryText>();
2786 int len = story->length();
2787 if (len > lastPos && lastStyle)
2788 {
2789 story->applyCharStyle(lastPos, len - lastPos, *lastStyle);
2790 }
2791 }
2792 }
2793 private:
2794 int lastPos;
2795 CharStyle* lastStyle;
2796 };
2797
2798 struct SpanAction : public MakeAction<SpanAction_body>
2799 {
2800 SpanAction() {}
2801 };
2802
2803
2804 const Xml_string StoryText::saxxDefaultElem("story");
2805
2806 void StoryText::desaxeRules(const Xml_string& prefixPattern, Digester& ruleset, Xml_string elemtag)
2807 {
2808 Xml_string storyPrefix(Digester::concat(prefixPattern, elemtag));
2809 ruleset.addRule(storyPrefix, Factory<StoryText>());
2810
2811 ParagraphStyle::desaxeRules(storyPrefix, ruleset, "defaultstyle");
2812 ruleset.addRule(Digester::concat(storyPrefix, "defaultstyle"), SetterWithConversion<StoryText, const ParagraphStyle&, ParagraphStyle>( & StoryText::setDefaultStyle ));
2813
2814 Paragraph paraAction;
2815 Xml_string paraPrefix(Digester::concat(storyPrefix, "p"));
2816 ruleset.addRule(storyPrefix, paraAction ); // needed to initialize some variables
2817 ruleset.addRule(paraPrefix, paraAction );
2818 ParagraphStyle::desaxeRules(paraPrefix, ruleset, ParagraphStyle::saxxDefaultElem);
2819 ruleset.addRule(Digester::concat(paraPrefix, ParagraphStyle::saxxDefaultElem), paraAction );
2820
2821 SpanAction spanAction;
2822 Xml_string spanPrefix(Digester::concat(paraPrefix, "span"));
2823 ruleset.addRule(spanPrefix, spanAction );
2824 CharStyle::desaxeRules(spanPrefix, ruleset, CharStyle::saxxDefaultElem);
2825 ruleset.addRule(Digester::concat(spanPrefix, CharStyle::saxxDefaultElem), spanAction );
2826
2827 ruleset.addRule(spanPrefix, AppendText());
2828
2829 ruleset.addRule(Digester::concat(spanPrefix, "breakline"), AppendSpecial(SpecialChars::LINEBREAK) );
2830 ruleset.addRule(Digester::concat(spanPrefix, "breakcol"), AppendSpecial(SpecialChars::COLBREAK) );
2831 ruleset.addRule(Digester::concat(spanPrefix, "breakframe"), AppendSpecial(SpecialChars::FRAMEBREAK) );
2832 ruleset.addRule(Digester::concat(spanPrefix, "tab"), AppendSpecial(SpecialChars::TAB) );
2833 ruleset.addRule(Digester::concat(spanPrefix, "unicode"), AppendSpecial() );
2834 ruleset.addRule(Digester::concat(spanPrefix, "var"), AppendSpecial());
2835
2836 //PageItem::desaxeRules(storyPrefix, ruleset); argh, that would be recursive!
2837 ruleset.addRule(Digester::concat(spanPrefix, "item"), AppendInlineFrame() );
2838 ruleset.addRule(Digester::concat(spanPrefix, "mark"), AppendMark() );
2839
2840 }