Subversion Repositories Scribus

Rev

Rev 24236 | 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>
23660 craig 25
#include <unicode/brkiter.h>
26
 
4777 avox 27
//FIXME: this include must go to sctextstruct.h !
9803 fschmid 28
#include <QList>
5105 mrdocs 29
#include <cassert>  //added to make Fedora-5 happy
4669 avox 30
#include "fpoint.h"
17826 craig 31
#include "notesstyles.h"
5980 avox 32
#include "scfonts.h"
7970 cbradney 33
#include "scribusdoc.h"
34
#include "sctext_shared.h"
17826 craig 35
#include "selection.h"
4669 avox 36
#include "storytext.h"
19080 craig 37
//
5184 avox 38
#include "util.h"
8455 avox 39
#include "resourcecollection.h"
8266 avox 40
#include "desaxe/saxiohelper.h"
41
#include "desaxe/digester.h"
42
#include "desaxe/simple_actions.h"
21563 jghali 43
#include "shapedtextcache.h"
4669 avox 44
 
23660 craig 45
using namespace icu;
4669 avox 46
 
18330 jghali 47
StoryText::StoryText(ScribusDoc * doc_) : m_doc(doc_)
5753 jghali 48
{
22295 craig 49
        if (doc_)
50
        {
7442 avox 51
                d = new ScText_Shared(&doc_->paragraphStyles());
18330 jghali 52
                m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
53
                m_doc->charStyles().connect(this, SLOT(invalidateAll()));
6733 avox 54
        }
22295 craig 55
        else
22516 craig 56
                d = new ScText_Shared(nullptr);
22295 craig 57
 
23626 jghali 58
        d->selFirst = 0;
59
        d->selLast = -1;
7002 avox 60
 
21563 jghali 61
        m_shapedTextCache = new ShapedTextCache();
62
 
7002 avox 63
        d->len = 0;
64
        invalidateAll();
4669 avox 65
}
66
 
22516 craig 67
StoryText::StoryText() : m_doc(nullptr)
6144 avox 68
{
22516 craig 69
        d = new ScText_Shared(nullptr);
7501 avox 70
 
23626 jghali 71
        d->selFirst = 0;
72
        d->selLast = -1;
22516 craig 73
        m_shapedTextCache = nullptr;
6144 avox 74
}
75
 
22639 craig 76
StoryText::StoryText(const StoryText & other) : m_doc(other.m_doc)
4669 avox 77
{
5721 avox 78
        d = other.d;
79
        d->refs++;
80
 
22295 craig 81
        if (m_doc)
82
        {
18330 jghali 83
                m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
84
                m_doc->charStyles().connect(this, SLOT(invalidateAll()));
7958 avox 85
        }
86
 
23626 jghali 87
        d->selFirst = 0;
88
        d->selLast = -1;
22516 craig 89
        m_shapedTextCache = nullptr;
5721 avox 90
 
4669 avox 91
        invalidateLayout();
92
}
93
 
94
StoryText::~StoryText()
95
{
9476 fschmid 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)
7958 avox 100
        {
101
                doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
102
                doc->charStyles().disconnect(this, SLOT(invalidateAll()));
9476 fschmid 103
        } */
5721 avox 104
        d->refs--;
105
        if (d->refs == 0) {
5773 avox 106
                d->clear();
107
                d->len = 0;
5721 avox 108
                delete d;
109
        }      
4669 avox 110
}
111
 
23456 jghali 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;
23821 jghali 129
        return (trailingStyle.hasBullet() || trailingStyle.hasNum());
23456 jghali 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
 
17408 fschmid 151
void StoryText::setDoc(ScribusDoc *docin)
152
{
22295 craig 153
        if (m_doc)
154
        {
21681 jghali 155
                m_doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
156
                m_doc->charStyles().disconnect(this, SLOT(invalidateAll()));
157
        }
158
 
18330 jghali 159
        m_doc = docin;
21681 jghali 160
 
22295 craig 161
        if (m_doc)
162
        {
21681 jghali 163
                m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
164
                m_doc->charStyles().connect(this, SLOT(invalidateAll()));
165
        }
17408 fschmid 166
}
167
 
6144 avox 168
StoryText StoryText::copy() const
169
{
18330 jghali 170
        StoryText result(m_doc);
6144 avox 171
        *(result.d) = *d;
7786 avox 172
        return result;
6144 avox 173
}
174
 
4669 avox 175
StoryText& StoryText::operator= (const StoryText & other)
176
{
7786 avox 177
        other.d->refs++;
178
 
5721 avox 179
        d->refs--;
22295 craig 180
        if (d->refs == 0)
181
        {
5721 avox 182
                clear();
183
                delete d;
184
        }
185
 
22295 craig 186
        if (m_doc)
187
        {
18330 jghali 188
                m_doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
189
                m_doc->charStyles().disconnect(this, SLOT(invalidateAll()));
7958 avox 190
        }
191
 
18330 jghali 192
        m_doc = other.m_doc;
5721 avox 193
        d = other.d;
194
 
22295 craig 195
        if (m_doc)
196
        {
18330 jghali 197
                m_doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
198
                m_doc->charStyles().connect(this, SLOT(invalidateAll()));
7958 avox 199
        }
200
 
23626 jghali 201
        d->selFirst = 0;
202
        d->selLast = -1;
4706 avox 203
 
4669 avox 204
        invalidateLayout();
205
        return *this;
206
}
207
 
16639 jghali 208
int StoryText::cursorPosition() const
209
{
210
        return d->cursorPosition;
211
}
4669 avox 212
 
16639 jghali 213
void StoryText::setCursorPosition(int pos, bool relative)
214
{
215
        if (relative)
21563 jghali 216
        {
217
                bool forward = (pos >= 0);
16639 jghali 218
                pos += d->cursorPosition;
21563 jghali 219
                if (isLowSurrogate(pos) && isHighSurrogate(pos - 1))
220
                {
221
                        if (forward)
222
                                pos += 1;
223
                        else
224
                                pos -= 1;
225
                }
226
        }
16639 jghali 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
 
21563 jghali 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);
22468 jghali 290
                if (pos != BreakIterator::DONE)
291
                {
292
                        while (pos < length() && text(pos).isSpace())
293
                                pos += 1;
294
                }
21563 jghali 295
        }
296
        else
297
        {
298
                pos = cursorPosition();
22468 jghali 299
                while (pos > 0 && text(pos - 1).isSpace())
21563 jghali 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();
22468 jghali 319
                while (pos > 0 && text(pos - 1).isSpace())
21563 jghali 320
                        pos -= 1;
321
                pos = it->preceding(pos);
322
        }
323
        else
324
        {
325
                pos = it->following(pos);
22468 jghali 326
                if (pos != BreakIterator::DONE)
327
                {
328
                        while (pos < length() && text(pos).isSpace())
329
                                pos += 1;
330
                }
21563 jghali 331
        }
332
 
333
        if (pos != BreakIterator::DONE)
334
                setCursorPosition(pos);
335
}
336
 
4669 avox 337
void StoryText::clear()
338
{
23626 jghali 339
        d->selFirst = 0;
340
        d->selLast = -1;
4669 avox 341
 
6733 avox 342
        d->defaultStyle.erase();
7232 avox 343
        d->trailingStyle.erase();
17231 fschmid 344
 
5721 avox 345
        d->clear();
5722 avox 346
        d->len = 0;
4669 avox 347
        invalidateAll();
348
}
349
 
22127 jghali 350
int StoryText::indexOf(const QString &str, int from, Qt::CaseSensitivity cs, int* pLen) const
17541 jghali 351
{
352
        int foundIndex = -1;
22127 jghali 353
        if (pLen)
354
                *pLen = 0;
17541 jghali 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
 
22127 jghali 364
        int strLen   = qStr.length();
365
        int storyLen = length();
17541 jghali 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;
22127 jghali 381
                                if (pLen)
382
                                        *pLen = strLen;
17541 jghali 383
                                break;
384
                        }
385
                        i = indexOf(ch, i + 1, cs);
386
                }
387
        }
388
        else
389
        {
22127 jghali 390
                bool qCharIsDiacritic, curCharIsDiacritic;
17541 jghali 391
                int i = indexOf(ch, from, cs);
392
                while (i >= 0 && i < (int) d->len)
393
                {
394
                        int index = 0;
22127 jghali 395
                        int diacriticsCounter = 0; //counter for diacritics
396
                        while ((index < strLen) && ((index + i + diacriticsCounter) < storyLen))
17541 jghali 397
                        {
22127 jghali 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())
17541 jghali 414
                                        break;
415
                                ++index;
416
                        }
417
                        if (index == strLen)
418
                        {
419
                                foundIndex = i;
22130 jghali 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
 
22127 jghali 428
                                if (pLen)
429
                                        *pLen = strLen + diacriticsCounter;
17541 jghali 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
 
16626 jghali 468
void StoryText::insert(const StoryText& other, bool onlySelection)
469
{
470
        insert(d->cursorPosition, other, onlySelection);
471
}
4669 avox 472
 
6887 avox 473
void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
5732 avox 474
{
8049 avox 475
        if (pos < 0)
23984 jghali 476
                pos += length() + 1;
8049 avox 477
 
6977 avox 478
        CharStyle cstyle(charStyle(pos));
479
        ParagraphStyle pstyle(paragraphStyle(pos));
7242 avox 480
 
481
        // this style represents all differences between this and other's defaultstyles
482
        ParagraphStyle otherDefault(other.defaultStyle());
483
        otherDefault.eraseStyle(defaultStyle());
484
 
23984 jghali 485
        int oldPos = pos;
486
        int oldLen = length();
6887 avox 487
        int otherStart  = onlySelection? other.startOfSelection() : 0;
488
        int otherEnd    = onlySelection? other.endOfSelection() : other.length();
489
        int cstyleStart = otherStart;
22295 craig 490
        for (int i = otherStart; i < otherEnd; ++i)
491
        {
6887 avox 492
                if (other.charStyle(i) == cstyle
493
                        && other.text(i) != SpecialChars::OBJECT
494
                        && other.text(i) != SpecialChars::PARSEP)
5732 avox 495
                        continue;
496
                int len = i - cstyleStart;
22295 craig 497
                if (len > 0)
498
                {
15009 jghali 499
                        insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
7242 avox 500
                        applyCharStyle(pos, len, otherDefault.charStyle());
5732 avox 501
                        applyCharStyle(pos, len, cstyle);
6887 avox 502
                        pos += len;
5732 avox 503
                }
22295 craig 504
                if (other.text(i) == SpecialChars::PARSEP)
505
                {
6887 avox 506
                        insertChars(pos, SpecialChars::PARSEP);
12056 jghali 507
                        //#5845 : disable for now as it has nasty side effects when linking frames
508
                        //applyStyle(pos, otherDefault);
6887 avox 509
                        applyStyle(pos, other.paragraphStyle(i));
5732 avox 510
                        cstyleStart = i+1;
6887 avox 511
                        pos += 1;
5732 avox 512
                }
22295 craig 513
                else if (other.text(i) == SpecialChars::OBJECT)
514
                {
8174 avox 515
                        insertChars(pos, SpecialChars::OBJECT);
516
                        item(pos)->embedded = other.item(i)->embedded;
17826 craig 517
                        item(pos)->mark = other.item(i)->mark;
23456 jghali 518
                        if (item(pos)->mark)
519
                        {
520
                                d->marksCount++;
521
                                d->marksCountChanged = true;
522
                        }
16600 jghali 523
                        applyCharStyle(pos, 1, other.charStyle(i));
6887 avox 524
                        cstyleStart = i+1;
525
                        pos += 1;
526
                }
22295 craig 527
                else
528
                {
5732 avox 529
                        cstyle = other.charStyle(i);
530
                        cstyleStart = i;
531
                }
532
        }
6887 avox 533
        int len = otherEnd - cstyleStart;
22295 craig 534
        if (len > 0)
535
        {
15009 jghali 536
                insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
7242 avox 537
                applyCharStyle(pos, len, otherDefault.charStyle());
5732 avox 538
                applyCharStyle(pos, len, cstyle);
7232 avox 539
                pos += len;
22295 craig 540
                if (other.text(otherEnd-1) != SpecialChars::PARSEP)
541
                {
12056 jghali 542
                        //#5845 : disable for now as it has nasty side effects when linking frames
543
                        //applyStyle(pos, otherDefault);
7256 avox 544
                        applyStyle(pos, other.paragraphStyle(otherEnd-1));
545
                }
5732 avox 546
        }
23984 jghali 547
        if ((d->selLast >= d->selFirst) && (d->selFirst <= oldPos) && (oldPos <= d->selLast))
548
                d->selLast += (length() - oldLen);
8049 avox 549
        invalidate(pos, length());
5732 avox 550
}
551
 
552
 
5721 avox 553
/**
554
    Make sure that the paragraph CharStyle's point to the new ParagraphStyle
555
 */
8174 avox 556
void StoryText::insertParSep(int pos)
5721 avox 557
{
18987 avox 558
        ScText* it = item(pos);
22295 craig 559
        if (!it->parstyle)
560
        {
8174 avox 561
                it->parstyle = new ParagraphStyle(paragraphStyle(pos+1));
8134 avox 562
                it->parstyle->setContext( & d->pstyleContext);
12689 jghali 563
                // #7432 : when inserting a paragraph separator, apply/erase the trailing Style
12796 pierre 564
                if (pos >= signed(d->len - 1))
12689 jghali 565
                {
566
                        applyStyle(pos, d->trailingStyle);
567
                        d->trailingStyle.erase();
568
                }
20681 craig 569
//              it->parstyle->setName("para"); // DON'T TRANSLATE
570
//              it->parstyle->charStyle().setName("cpara"); // DON'T TRANSLATE
8134 avox 571
//              it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
6733 avox 572
        }
8134 avox 573
        d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
5721 avox 574
}
575
/**
576
     need to remove the ParagraphStyle structure and replace all pointers
577
     to it...
578
 */
8174 avox 579
void StoryText::removeParSep(int pos)
5721 avox 580
{
18987 avox 581
        ScText* it = item(pos);
5721 avox 582
        if (it->parstyle) {
6733 avox 583
//              const CharStyle* oldP = & it->parstyle->charStyle();
584
//              const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
585
//              d->replaceParentStyle(pos, oldP, newP);
5778 avox 586
                delete it->parstyle;
22631 craig 587
                it->parstyle = nullptr;
5721 avox 588
        }
8134 avox 589
        // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
20681 craig 590
        // doesn't choke:
9737 jghali 591
        it->ch = 0;
18843 fschmid 592
        d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());
5721 avox 593
}
594
 
4669 avox 595
void StoryText::removeChars(int pos, uint len)
596
{
597
        if (pos < 0)
598
                pos += length();
8049 avox 599
 
4669 avox 600
        assert( len > 0 );
601
        assert( pos >= 0 );
11991 jghali 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;
4749 avox 608
 
23456 jghali 609
        uint oldMarksCount = d->marksCount;
610
 
23184 jghali 611
        if ((pos == 0) && (len > 0) && (static_cast<int>(len) == length()))
22993 jghali 612
        {
613
                int lastChar = length() - 1;
614
                while (lastChar > 0 && text(lastChar) == SpecialChars::PARSEP)
615
                        --lastChar;
23002 jghali 616
                d->orphanedCharStyle = charStyle(lastChar);
22993 jghali 617
        }
21733 jghali 618
        for (int i = pos + static_cast<int>(len) - 1; i >= pos; --i)
4749 avox 619
        {
5721 avox 620
                ScText *it = d->at(i);
23456 jghali 621
                if (it->ch == SpecialChars::PARSEP)
8174 avox 622
                        removeParSep(i);
23456 jghali 623
                if ((it->ch == SpecialChars::OBJECT) && (it->mark != nullptr))
624
                        d->marksCount--;
10366 jghali 625
                d->takeAt(i);
5722 avox 626
                d->len--;
4749 avox 627
                delete it;
23626 jghali 628
                // #9592 : adjust d->selFirst and d->selLast, those values have to be
16104 jghali 629
                // consistent in functions such as select()
23626 jghali 630
                if (i <= d->selLast)
631
                        --d->selLast;
632
                if (i < d->selFirst)
633
                        --d->selFirst;
23184 jghali 634
                if (static_cast<uint>(i + 1) <= d->cursorPosition && d->cursorPosition > 0)
20620 fschmid 635
                        d->cursorPosition -= 1;
4749 avox 636
        }
5722 avox 637
 
23456 jghali 638
        if (oldMarksCount != d->marksCount)
639
                d->marksCountChanged = true;
640
 
5722 avox 641
        d->len = d->count();
16626 jghali 642
        d->cursorPosition = qMin(d->cursorPosition, d->len);
23626 jghali 643
        if (d->selFirst > d->selLast)
16104 jghali 644
        {
23626 jghali 645
                d->selFirst =  0;
646
                d->selLast  = -1;
16104 jghali 647
        }
8049 avox 648
        invalidate(pos, length());
4669 avox 649
}
650
 
18843 fschmid 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);
18850 fschmid 660
                if ((it->ch == SpecialChars::PARSEP) || (it->ch.isSpace()))
18843 fschmid 661
                {
662
                        pos--;
663
                        posCount++;
664
                }
18850 fschmid 665
                else
666
                        break;
18843 fschmid 667
        }
668
        if (posCount > 0)
669
        {
670
                ParagraphStyle pst = paragraphStyle(pos);
19230 jghali 671
                if (posCount > 1)
672
                        removeChars(pos+1, posCount-1);
18843 fschmid 673
                applyStyle(pos, pst);
674
        }
675
}
676
 
22635 craig 677
void StoryText::insertChars(const QString& txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
16626 jghali 678
{
679
        insertChars(d->cursorPosition, txt, applyNeighbourStyle);
680
}
681
 
22295 craig 682
void StoryText::insertChars(int pos, const QString& txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
4669 avox 683
{
684
        if (pos < 0)
23984 jghali 685
                pos += length() + 1;
4669 avox 686
 
687
        assert(pos >= 0);
688
        assert(pos <= length());
4749 avox 689
 
5365 avox 690
        if (txt.length() == 0)
691
                return;
692
 
8134 avox 693
        const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
9426 avox 694
 
9645 avox 695
        ScText clone;
696
        if (applyNeighbourStyle)
697
        {
10394 cbradney 698
                int referenceChar = qMax(0, qMin(pos, length()-1));
9645 avox 699
                clone.applyCharStyle(charStyle(referenceChar));
700
                clone.setEffects(ScStyle_Default);
701
        }
9607 avox 702
 
22295 craig 703
        for (int i = 0; i < txt.length(); ++i)
704
        {
9426 avox 705
                ScText * item = new ScText(clone);
21733 jghali 706
                item->ch= txt.at(i);
8134 avox 707
                item->setContext(cStyleContext);
5721 avox 708
                d->insert(pos + i, item);
5722 avox 709
                d->len++;
22295 craig 710
                if (item->ch == SpecialChars::PARSEP)
711
                {
13085 jghali 712
//                      qDebug() << QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name());
8174 avox 713
                        insertParSep(pos + i);
5721 avox 714
                }
22295 craig 715
                if (d->cursorPosition >= static_cast<uint>(pos + i))
16626 jghali 716
                        d->cursorPosition += 1;
4749 avox 717
        }
718
 
5722 avox 719
        d->len = d->count();
23984 jghali 720
        if ((d->selLast >= d->selFirst) && (d->selFirst <= pos) && (pos <= d->selLast))
721
                d->selLast += txt.length();
4669 avox 722
        invalidate(pos, pos + txt.length());
723
}
724
 
22635 craig 725
void StoryText::insertCharsWithSoftHyphens(int pos, const QString& txt, bool applyNeighbourStyle)
12093 jghali 726
{
727
        if (pos < 0)
23984 jghali 728
                pos += length() + 1;
12093 jghali 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
        {
23984 jghali 741
                int referenceChar = qMax(0, qMin(pos, length() - 1));
12093 jghali 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);
12112 jghali 750
                int  index  = pos + inserted;
12093 jghali 751
                bool insert = true;
22295 craig 752
                if (ch == SpecialChars::SHYPHEN && index > 0)
753
                {
12097 jghali 754
                        ScText* lastItem = this->item(index - 1);
13161 malex 755
                        // qreal SHY means user provided SHY, single SHY is automatic one
12093 jghali 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);
12097 jghali 769
                        d->insert(index, item);
12093 jghali 770
                        d->len++;
17407 fschmid 771
                        if (item->ch == SpecialChars::PARSEP)
12097 jghali 772
                                insertParSep(index);
17407 fschmid 773
                        if (d->cursorPosition >= static_cast<uint>(index))
16626 jghali 774
                                d->cursorPosition += 1;
12093 jghali 775
                        ++inserted;
776
                }
777
        }
778
 
779
        d->len = d->count();
23984 jghali 780
        if ((d->selLast >= d->selFirst) && (d->selFirst <= pos) && (pos <= d->selLast))
781
                d->selLast += inserted;
12093 jghali 782
        invalidate(pos, pos + inserted);
783
}
784
 
4669 avox 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
 
5721 avox 793
        ScText* item = d->at(pos);
9737 jghali 794
        if (item->ch == ch)
5721 avox 795
                return;
23456 jghali 796
 
797
        uint oldMarksCount = d->marksCount;
5721 avox 798
 
23456 jghali 799
        if (item->ch == SpecialChars::PARSEP)
8174 avox 800
                removeParSep(pos);
23456 jghali 801
        if ((item->ch == SpecialChars::OBJECT) && (item->mark != nullptr))
802
                d->marksCount--;
5721 avox 803
        item->ch = ch;
22295 craig 804
        if (d->at(pos)->ch == SpecialChars::PARSEP)
8174 avox 805
                insertParSep(pos);
23456 jghali 806
 
807
        if (oldMarksCount != d->marksCount)
808
                d->marksCountChanged = true;
5721 avox 809
 
4669 avox 810
        invalidate(pos, pos + 1);
811
}
812
 
23396 jghali 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
 
17501 craig 847
int StoryText::replaceWord(int pos, QString newWord)
848
{
24041 jghali 849
        QString word = this->word(pos);
21563 jghali 850
        int lengthDiff = newWord.length() - word.length();
851
        if (lengthDiff == 0)
17501 craig 852
        {
853
                for (int j = 0; j < word.length(); ++j)
23396 jghali 854
                        replaceChar(pos + j, newWord[j]);
17501 craig 855
        }
23396 jghali 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
        }
17501 craig 863
        else
864
        {
23396 jghali 865
                for (int j = 0; j < newWord.length(); ++j)
866
                        replaceChar(pos + j, newWord[j]);
867
                removeChars(pos + newWord.length(), -lengthDiff);
17501 craig 868
        }
869
        return lengthDiff;
870
}
871
 
22639 craig 872
void StoryText::hyphenateWord(int pos, uint len, const char* hyphens)
5559 avox 873
{
874
        assert(pos >= 0);
5691 avox 875
        assert(pos + signed(len) <= length());
5559 avox 876
 
6940 avox 877
//      QString dump("");
5691 avox 878
        for (int i=pos; i < pos+signed(len); ++i)
6937 avox 879
        {
6940 avox 880
//              dump += d->at(i)->ch;
22295 craig 881
                if (hyphens && hyphens[i-pos] & 1)
882
                {
5721 avox 883
                        d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
6940 avox 884
//                      dump += "-";
6937 avox 885
                }
886
                else {
5721 avox 887
                        d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
6937 avox 888
                }
889
        }
13085 jghali 890
//      qDebug() << QString("st: %1").arg(dump);
5559 avox 891
        invalidate(pos, pos + len);
892
}
893
 
17407 fschmid 894
void StoryText::insertObject(int ob)
16626 jghali 895
{
896
        insertObject(d->cursorPosition, ob);
897
}
898
 
17407 fschmid 899
void StoryText::insertObject(int pos, int ob)
4669 avox 900
{
901
        if (pos < 0)
8049 avox 902
                pos += length()+1;
4669 avox 903
 
904
        insertChars(pos, SpecialChars::OBJECT);
17405 fschmid 905
        const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
18330 jghali 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
4669 avox 908
}
909
 
23456 jghali 910
void StoryText::insertMark(Mark* mark, int pos)
17826 craig 911
{
23456 jghali 912
        if (mark == nullptr)
17826 craig 913
                return;
914
        if (pos < 0)
915
                pos = d->cursorPosition;
916
 
917
        insertChars(pos, SpecialChars::OBJECT, false);
23456 jghali 918
        d->at(pos)->mark = mark;
919
        if (mark)
920
        {
921
                d->marksCount++;
922
                d->marksCountChanged = true;
923
        }
17826 craig 924
}
925
 
17407 fschmid 926
void StoryText::replaceObject(int pos, int ob)
17231 fschmid 927
{
928
        if (pos < 0)
929
                pos += length()+1;
4669 avox 930
 
17231 fschmid 931
        replaceChar(pos, SpecialChars::OBJECT);
17405 fschmid 932
        const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
18330 jghali 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
17231 fschmid 935
}
936
 
937
 
4669 avox 938
int StoryText::length() const
939
{
5722 avox 940
        return d->len;
4669 avox 941
}
942
 
17674 jghali 943
QString StoryText::plainText() const
944
{
21733 jghali 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;
17674 jghali 963
}
21563 jghali 964
#if 0
16626 jghali 965
QChar StoryText::text() const
966
{
967
        return text(d->cursorPosition);
968
}
21563 jghali 969
#endif
16626 jghali 970
 
4669 avox 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
 
9737 jghali 979
        return const_cast<StoryText *>(this)->d->at(pos)->ch;
4669 avox 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;
5721 avox 991
        StoryText* that(const_cast<StoryText*>(this));
22295 craig 992
        for (int i = pos; i < pos+signed(len); ++i)
10366 jghali 993
                result += that->d->at(i)->ch;
4669 avox 994
 
995
        return result;
996
}
997
 
21563 jghali 998
 
999
bool StoryText::isBlockStart(int pos) const
1000
{
23659 jghali 1001
        return pos == 0 || text(pos-1) == SpecialChars::PARSEP;
21563 jghali 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
{
22297 jghali 1021
        if (pos < 0)
22295 craig 1022
                pos += length();
21563 jghali 1023
 
22295 craig 1024
        assert(pos >= 0);
1025
        assert(pos < length());
21563 jghali 1026
 
22295 craig 1027
        StoryText* that = const_cast<StoryText *>(this);
1028
        return InlineFrame(that->d->at(pos)->embedded);
21563 jghali 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);
22638 craig 1042
        if( text(pos) == SpecialChars::PAGECOUNT)
21563 jghali 1043
                return ExpansionPoint(ExpansionPoint::PageCount);
22638 craig 1044
        if (hasMark(pos))
21563 jghali 1045
                return ExpansionPoint(mark(pos));
22638 craig 1046
        return ExpansionPoint(ExpansionPoint::Invalid);
21563 jghali 1047
}
1048
 
1049
 
17501 craig 1050
QString StoryText::sentence(int pos, int &posn)
1051
{
23456 jghali 1052
        int sentencePos = qMax(0, prevSentence(pos));
24041 jghali 1053
        posn = sentencePos;
23456 jghali 1054
        int nextSentencePos = qMin(length(), endOfSentence(pos));
24041 jghali 1055
        return text(sentencePos, nextSentencePos - sentencePos);
17501 craig 1056
}
1057
 
24041 jghali 1058
QString StoryText::word(int pos)
1059
{
24119 jghali 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
24041 jghali 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
 
15009 jghali 1080
QString StoryText::textWithSoftHyphens(int pos, uint len) const
12093 jghali 1081
{
22295 craig 1082
        QString result;
12093 jghali 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
 
16600 jghali 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)
18330 jghali 1112
                return that->d->at(pos)->hasObject(m_doc);
16600 jghali 1113
        return false;
1114
}
1115
 
21563 jghali 1116
 
1117
PageItem* StoryText::getItem(int pos) const
4669 avox 1118
{
1119
        if (pos < 0)
1120
                pos += length();
1121
 
1122
        assert(pos >= 0);
1123
        assert(pos < length());
1124
 
8174 avox 1125
        StoryText* that = const_cast<StoryText *>(this);
18330 jghali 1126
        return that->d->at(pos)->getItem(m_doc);
4669 avox 1127
}
1128
 
23625 jghali 1129
int StoryText::findMark(const Mark* mrk, int startPos) const
1130
{
1131
        assert(startPos >= 0);
21563 jghali 1132
 
23625 jghali 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
 
23440 jghali 1145
bool StoryText::hasMark(int pos, const Mark* mrk) const
17826 craig 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)
22993 jghali 1155
                return that->d->at(pos)->hasMark(mrk);
17826 craig 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
 
18524 avox 1171
 
21563 jghali 1172
void StoryText::applyMarkCharstyle(Mark* mrk, CharStyle& currStyle) const
1173
{
1174
        TextNote* note = mrk->getNotePtr();
22516 craig 1175
        if (note == nullptr)
21563 jghali 1176
                return;
1177
 
1178
        NotesStyle* nStyle = note->notesStyle();
22516 craig 1179
        Q_ASSERT(nStyle != nullptr);
21563 jghali 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
 
18524 avox 1209
void StoryText::replaceMark(int pos, Mark* mrk)
1210
{
22295 craig 1211
        if (pos < 0)
1212
                pos += length();
18524 avox 1213
 
22295 craig 1214
        assert(pos >= 0);
1215
        assert(pos < length());
18524 avox 1216
 
23456 jghali 1217
        ScText* textItem = this->d->at(pos);
1218
        if (textItem->mark)
1219
                this->d->marksCount--;
22295 craig 1220
        this->d->at(pos)->mark = mrk;
23456 jghali 1221
        if (textItem->mark)
1222
                this->d->marksCount++;
1223
 
1224
        // Set marksCountChanged unconditionnaly to force text relayout
1225
        d->marksCountChanged = true;
18524 avox 1226
}
1227
 
1228
 
21563 jghali 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
 
18524 avox 1240
LayoutFlags StoryText::flags(int pos) const
1241
{
22295 craig 1242
        if (pos < 0)
1243
                pos += length();
18524 avox 1244
 
18987 avox 1245
        assert(pos >= 0);
22295 craig 1246
        assert(pos < length());
18524 avox 1247
 
22295 craig 1248
        StoryText* that = const_cast<StoryText *>(this);
18524 avox 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
{
22295 craig 1254
        if (pos < 0)
1255
                pos += length();
18524 avox 1256
 
22295 craig 1257
        assert(pos >= 0);
1258
        assert(pos < length());
1259
        assert((flags & ScStyle_UserStyles) == ScStyle_None);
18524 avox 1260
 
1261
        return (flags & d->at(pos)->effects().value) == flags;
1262
}
1263
 
1264
void StoryText::setFlag(int pos, LayoutFlags flags)
1265
{
22295 craig 1266
        if (pos < 0)
1267
                pos += length();
18524 avox 1268
 
22295 craig 1269
        assert(pos >= 0);
1270
        assert(pos < length());
1271
        assert((flags & ScStyle_UserStyles) == ScStyle_None);
18524 avox 1272
 
1273
        d->at(pos)->setEffects(flags | d->at(pos)->effects().value);
1274
}
1275
 
1276
void StoryText::clearFlag(int pos, LayoutFlags flags)
1277
{
22295 craig 1278
        if (pos < 0)
1279
                pos += length();
18524 avox 1280
 
22295 craig 1281
        assert(pos >= 0);
1282
        assert(pos < length());
18524 avox 1283
 
1284
        d->at(pos)->setEffects(~(flags & ScStyle_NonUserStyles) & d->at(pos)->effects().value);
1285
}
1286
 
1287
 
16626 jghali 1288
const CharStyle & StoryText::charStyle() const
1289
{
1290
        return charStyle(d->cursorPosition);
1291
}
4669 avox 1292
 
1293
const CharStyle & StoryText::charStyle(int pos) const
1294
{
1295
        if (pos < 0)
1296
                pos += length();
1297
 
1298
        assert(pos >= 0);
5732 avox 1299
        assert(pos <= length());
4669 avox 1300
 
22295 craig 1301
        if (length() == 0)
1302
        {
13085 jghali 1303
//              qDebug() << "storytext::charstyle: default";
23002 jghali 1304
                return d->orphanedCharStyle;
6144 avox 1305
        }
22638 craig 1306
        if (pos == length())
22295 craig 1307
        {
24246 craig 1308
                //qDebug() << "storytext::charstyle: access at end of text %i" << pos;
5732 avox 1309
                --pos;
6144 avox 1310
        }
21563 jghali 1311
 
8956 avox 1312
        if (text(pos) == SpecialChars::PARSEP)
1313
                return paragraphStyle(pos).charStyle();
21563 jghali 1314
 
4669 avox 1315
        StoryText* that = const_cast<StoryText *>(this);
21563 jghali 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
 
5721 avox 1323
        return dynamic_cast<const CharStyle &> (*that->d->at(pos));
4669 avox 1324
}
1325
 
16639 jghali 1326
const ParagraphStyle & StoryText::paragraphStyle() const
1327
{
1328
        return paragraphStyle(d->cursorPosition);
1329
}
1330
 
4669 avox 1331
const ParagraphStyle & StoryText::paragraphStyle(int pos) const
1332
{
1333
        if (pos < 0)
1334
                pos += length();
1335
 
1336
        assert(pos >= 0);
5721 avox 1337
        assert(pos <= length());
4669 avox 1338
 
6733 avox 1339
        assert(d);
1340
 
5192 avox 1341
        StoryText * that = const_cast<StoryText *> (this);
5721 avox 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
 
22295 craig 1346
        while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP)
5721 avox 1347
                ++pos;
22295 craig 1348
 
1349
        if (pos >= length())
7232 avox 1350
                return that->d->trailingStyle;
22638 craig 1351
        if ( !that->d->at(pos)->parstyle )
22295 craig 1352
        {
10366 jghali 1353
                ScText* current = that->d->at(pos);
10394 cbradney 1354
                qDebug("inserting default parstyle at %i", pos);
10366 jghali 1355
                current->parstyle = new ParagraphStyle();
1356
                current->parstyle->setContext( & d->pstyleContext);
20681 craig 1357
//              current->parstyle->setName( "para(paragraphStyle)" ); // DON'T TRANSLATE
1358
//              current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DON'T TRANSLATE
10366 jghali 1359
//              current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5721 avox 1360
        }
5778 avox 1361
        else {
13085 jghali 1362
//              qDebug() << QString("using parstyle at %1").arg(pos);
5778 avox 1363
        }
10366 jghali 1364
        assert (that->d->at(pos)->parstyle);
1365
        return *that->d->at(pos)->parstyle;
4669 avox 1366
}
1367
 
5559 avox 1368
const ParagraphStyle& StoryText::defaultStyle() const
1369
{
6733 avox 1370
        assert(d);
5721 avox 1371
        return d->defaultStyle;
5559 avox 1372
}
1373
 
5569 avox 1374
 
1375
void StoryText::setDefaultStyle(const ParagraphStyle& style)
1376
{
8134 avox 1377
        const StyleContext * oldPContext = d->defaultStyle.context();
8289 avox 1378
//      const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
5721 avox 1379
        d->defaultStyle = style;
8134 avox 1380
        d->defaultStyle.setContext( oldPContext );
20681 craig 1381
//      d->defaultStyle.setName( "storydefault" ); // DON'T TRANSLATE
1382
//      d->defaultStyle.charStyle().setName( "cstorydefault" ); // DON'T TRANSLATE
13085 jghali 1383
//      qDebug() << QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
8134 avox 1384
//                 .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
1385
//                 .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
13085 jghali 1386
//                 .arg((uint)d->defaultStyle.charStyle().context(),16);
8134 avox 1387
//      d->defaultStyle.charStyle().setContext( oldCContext );
7958 avox 1388
        invalidateAll();
5569 avox 1389
}
1390
 
1391
 
5721 avox 1392
void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style )
4669 avox 1393
{
1394
        if (pos < 0)
1395
                pos += length();
1396
 
11968 fschmid 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;
4669 avox 1404
        if (len == 0)
1405
                return;
1406
 
17407 fschmid 1407
//      int lastParStart = pos == 0? 0 : -1;
10366 jghali 1408
        ScText* itText;
22295 craig 1409
        for (uint i=pos; i < pos+len; ++i)
1410
        {
10366 jghali 1411
                itText = d->at(i);
15658 jghali 1412
                // #6165 : applying style on last character applies style on whole text on next open 
22516 craig 1413
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
15658 jghali 1414
                        itText->parstyle->charStyle().applyCharStyle(style);*/
1415
 
1416
                // Does not work well, do not reenable before checking #9337, #9376 and #9428
15386 craig 1417
                // #9173 et. al.: move charstyle to parstyle if whole paragraph is affected
22516 craig 1418
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr && lastParStart >= 0)
15386 craig 1419
                {
1420
                        eraseCharStyle(lastParStart, i - lastParStart, style);
1421
                        itText->parstyle->charStyle().applyCharStyle(style);
1422
                        lastParStart = i + 1;
15658 jghali 1423
                }*/
10366 jghali 1424
                itText->applyCharStyle(style);
5721 avox 1425
        }
15658 jghali 1426
        // Does not work well, do not reenable before checking #9337, #9376 and #9428
1427
        /*if (pos + signed(len) == length() && lastParStart >= 0)
15386 craig 1428
        {
1429
                eraseCharStyle(lastParStart, length() - lastParStart, style);
1430
                d->trailingStyle.charStyle().applyCharStyle(style);
15658 jghali 1431
        }*/
15386 craig 1432
 
4669 avox 1433
        invalidate(pos, pos + len);
1434
}
1435
 
5773 avox 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
 
10366 jghali 1449
        ScText* itText;
5773 avox 1450
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 1451
                itText = d->at(i);
13290 jghali 1452
                // FIXME?? see #6165 : should we really erase charstyle of paragraph style??
22516 craig 1453
                if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
10366 jghali 1454
                        itText->parstyle->charStyle().eraseCharStyle(style);
1455
                itText->eraseCharStyle(style);
5773 avox 1456
        }
15658 jghali 1457
        // Does not work well, do not reenable before checking #9337, #9376 and #9428
1458
        /*if (pos + signed(len) == length())
15386 craig 1459
        {
1460
                d->trailingStyle.charStyle().eraseCharStyle(style);
15658 jghali 1461
        }*/
5773 avox 1462
 
1463
        invalidate(pos, pos + len);
1464
}
1465
 
13290 jghali 1466
void StoryText::applyStyle(int pos, const ParagraphStyle& style, bool rmDirectFormatting)
4669 avox 1467
{
1468
        if (pos < 0)
1469
                pos += length();
1470
 
1471
        assert(pos >= 0);
5721 avox 1472
        assert(pos <= length());
4669 avox 1473
 
5721 avox 1474
        int i = pos;
22295 craig 1475
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP)
5184 avox 1476
                ++i;
22295 craig 1477
 
1478
        if (i < length())
1479
        {
5721 avox 1480
                if (!d->at(i)->parstyle) {
10394 cbradney 1481
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 1482
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 1483
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
20681 craig 1484
//                      d->at(i)->parstyle->setName( "para(applyStyle)" ); // DON'T TRANSLATE
1485
//                      d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DON'T TRANSLATE
8134 avox 1486
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
5721 avox 1487
                }
13085 jghali 1488
//              qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
5721 avox 1489
                d->at(i)->parstyle->applyStyle(style);
1490
        }
1491
        else {
1492
                // not happy about this but inserting a new PARSEP makes more trouble
13085 jghali 1493
//              qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
7232 avox 1494
                d->trailingStyle.applyStyle(style);
5721 avox 1495
        }
13290 jghali 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
        }
8562 jghali 1505
        invalidate(pos, qMin(i, length()));
4669 avox 1506
}
1507
 
5773 avox 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;
22295 craig 1517
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP)
5773 avox 1518
                ++i;
22295 craig 1519
 
1520
        if (i < length())
1521
        {
5773 avox 1522
                if (!d->at(i)->parstyle) {
10394 cbradney 1523
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 1524
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 1525
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
20681 craig 1526
//                      d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DON'T TRANSLATE
1527
//                      d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DON'T TRANSLATE
8134 avox 1528
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5773 avox 1529
                }
13085 jghali 1530
                //              qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
5773 avox 1531
                d->at(i)->parstyle->eraseStyle(style);
1532
        }
1533
        else {
1534
                // not happy about this but inserting a new PARSEP makes more trouble
13085 jghali 1535
                //              qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
7232 avox 1536
                d->trailingStyle.eraseStyle(style);
5773 avox 1537
        }
8562 jghali 1538
        invalidate(pos, qMin(i, length()));
5773 avox 1539
}
1540
 
1541
 
6733 avox 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
{
9067 avox 1551
        if (pos < 0)
1552
                pos += length();
1553
 
1554
        assert(pos >= 0);
9645 avox 1555
        assert(len <= unsigned(length()));
9067 avox 1556
        assert(pos + signed(len) <= length());
1557
 
1558
        if (len == 0)
1559
                return;
1560
 
10366 jghali 1561
        ScText* itText;
22295 craig 1562
        for (uint i=pos; i < pos+len; ++i)
1563
        {
10366 jghali 1564
                itText = d->at(i);
11936 jghali 1565
                // #6165 : applying style on last character applies style on whole text on next open 
22516 craig 1566
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != nullptr)
11936 jghali 1567
                        itText->parstyle->charStyle() = style;*/
10366 jghali 1568
                itText->setStyle(style);
9067 avox 1569
        }
1570
 
1571
        invalidate(pos, pos + len);
6733 avox 1572
}
1573
 
1574
 
8455 avox 1575
 
1576
void StoryText::getNamedResources(ResourceCollection& lists) const
1577
{
8938 avox 1578
        d->defaultStyle.getNamedResources(lists);
1579
        d->trailingStyle.getNamedResources(lists);
1580
 
8455 avox 1581
        for (int i=0; i < length(); ++i)
1582
        {
1583
                if (text(i) == SpecialChars::PARSEP)
1584
                        paragraphStyle(i).getNamedResources(lists);
16600 jghali 1585
                else if (hasObject(i))
21563 jghali 1586
                        getItem(i)->getNamedResources(lists);
8455 avox 1587
                else
1588
                        charStyle(i).getNamedResources(lists);
1589
        }
1590
}
1591
 
1592
 
22631 craig 1593
void StoryText::replaceStyles(const QMap<QString,QString>& newNameForOld)
7442 avox 1594
{
8455 avox 1595
        ResourceCollection newnames;
8775 cbradney 1596
        newnames.mapStyles(newNameForOld);
8455 avox 1597
        replaceNamedResources(newnames);
1598
}
1599
 
1600
void StoryText::replaceNamedResources(ResourceCollection& newNames)
1601
{
7442 avox 1602
        int len = length();
8455 avox 1603
 
1604
        d->defaultStyle.replaceNamedResources(newNames);
1605
        d->trailingStyle.replaceNamedResources(newNames);
1606
 
7442 avox 1607
        if (len == 0)
1608
                return;
1609
 
10366 jghali 1610
        ScText* itText;
22295 craig 1611
        for (int i=0; i < len; ++i)
1612
        {
10366 jghali 1613
                itText = d->at(i);
1614
                if (itText->parstyle)
1615
                        itText->parstyle->replaceNamedResources(newNames);
8455 avox 1616
                else
10366 jghali 1617
                        itText->replaceNamedResources(newNames);
7442 avox 1618
        }
1619
 
1620
        invalidate(0, len);    
1621
}
1622
 
8455 avox 1623
 
7442 avox 1624
void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
1625
{
8455 avox 1626
        ResourceCollection newnames;
8775 cbradney 1627
        newnames.mapCharStyles(newNameForOld);
8455 avox 1628
        replaceNamedResources(newnames);
7442 avox 1629
}
1630
 
16681 jghali 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;
22295 craig 1656
        while (i > 0 && d->at(i - 1)->ch != SpecialChars::PARSEP)
16681 jghali 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
 
16626 jghali 1675
uint StoryText::nrOfParagraph() const
1676
{
1677
        return nrOfParagraph(d->cursorPosition);
1678
}
8455 avox 1679
 
16069 craig 1680
uint StoryText::nrOfParagraph(int pos) const
10623 avox 1681
{
16069 craig 1682
        uint result = 0;
10623 avox 1683
        StoryText* that = const_cast<StoryText *>(this);
1684
        bool lastWasPARSEP = true;
16069 craig 1685
        pos = qMin(pos, that->length());
1686
        for (int i=0; i < pos; ++i)
1687
        {
10623 avox 1688
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
1689
                if (lastWasPARSEP)
1690
                        ++result;
1691
        }
11380 jghali 1692
        return result;
10623 avox 1693
}
1694
 
4669 avox 1695
uint StoryText::nrOfParagraphs() const
1696
{
1697
        uint result = 0;
5721 avox 1698
        StoryText* that = const_cast<StoryText *>(this);
7059 avox 1699
        bool lastWasPARSEP = true;
16069 craig 1700
        for (int i=0; i < length(); ++i)
1701
        {
10366 jghali 1702
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
6887 avox 1703
                if (lastWasPARSEP)
4669 avox 1704
                        ++result;
5721 avox 1705
        }
11359 jghali 1706
        return lastWasPARSEP ? result : result + 1;
4669 avox 1707
}
1708
 
16626 jghali 1709
int StoryText::startOfParagraph() const
1710
{
1711
        return startOfParagraph(nrOfParagraph());
1712
}
1713
 
4669 avox 1714
int StoryText::startOfParagraph(uint index) const
1715
{
1716
        if (index == 0)
1717
                return 0;
1718
 
5184 avox 1719
        StoryText* that = const_cast<StoryText *>(this);
16069 craig 1720
        for (int i=0; i < length(); ++i)
1721
        {
10366 jghali 1722
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
4669 avox 1723
                        return i + 1;
5721 avox 1724
        }
6887 avox 1725
        return length();
4669 avox 1726
}
1727
 
16626 jghali 1728
int StoryText::endOfParagraph() const
1729
{
1730
        return endOfParagraph(nrOfParagraph());
1731
}
1732
 
4669 avox 1733
int StoryText::endOfParagraph(uint index) const
1734
{
1735
        ++index;
5184 avox 1736
        StoryText* that = const_cast<StoryText *>(this);
16069 craig 1737
        for (int i=0; i < length(); ++i)
1738
        {
10366 jghali 1739
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
5721 avox 1740
                        return i;
1741
        }
4669 avox 1742
        return length();
1743
}
1744
 
1745
uint StoryText::nrOfRuns() const
1746
{
5464 avox 1747
        return length();
4669 avox 1748
}
1749
 
1750
int StoryText::startOfRun(uint index) const
1751
{
5464 avox 1752
        return index;
4669 avox 1753
}
1754
 
1755
int StoryText::endOfRun(uint index) const
1756
{
5464 avox 1757
        return index + 1;
4669 avox 1758
}
1759
 
7501 avox 1760
// positioning. all positioning methods return char positions
4669 avox 1761
 
7501 avox 1762
int StoryText::nextChar(int pos)
1763
{
1764
        if (pos < length())
23529 jghali 1765
                return pos + 1;
22631 craig 1766
        return length();
7501 avox 1767
}
17878 jghali 1768
 
7501 avox 1769
int StoryText::prevChar(int pos)
1770
{
1771
        if (pos > 0)
1772
                return pos - 1;
22631 craig 1773
        return 0;
7501 avox 1774
}
17878 jghali 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
 
7501 avox 1790
int StoryText::nextWord(int pos)
1791
{
21563 jghali 1792
        BreakIterator* it = getWordIterator();
1793
        if (!it)
20558 craig 1794
                return pos;
21563 jghali 1795
 
1796
        it->setText((const UChar*) plainText().utf16());
1797
        pos = it->following(pos);
23101 jghali 1798
 
1799
        int len = length();
1800
        while ((pos < len) && (text(pos).isSpace() || text(pos).isPunct()))
1801
                ++pos;
21563 jghali 1802
        return pos;
7501 avox 1803
}
17878 jghali 1804
 
7501 avox 1805
int StoryText::prevWord(int pos)
1806
{
21563 jghali 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;
7501 avox 1814
}
17501 craig 1815
 
1816
int StoryText::endOfWord(int pos) const
1817
{
21563 jghali 1818
        BreakIterator* it = getWordIterator();
1819
        if (!it)
1820
                return pos;
1821
 
1822
        it->setText((const UChar*) plainText().utf16());
1823
        pos = it->following(pos);
17501 craig 1824
        return pos;
1825
}
1826
 
20558 craig 1827
int StoryText::endOfSentence(int pos) const
7501 avox 1828
{
21563 jghali 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;
7501 avox 1836
}
20558 craig 1837
 
1838
int StoryText::nextSentence(int pos)
1839
{
21563 jghali 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();
20558 craig 1847
        return pos;
1848
}
1849
 
7501 avox 1850
int StoryText::prevSentence(int pos)
1851
{
20558 craig 1852
        //we cannot go before the first position so just return it.
1853
        if (pos == 0)
1854
                return 0;
21563 jghali 1855
        BreakIterator* it = getSentenceIterator();
1856
        if (!it)
1857
                return pos;
1858
 
1859
        it->setText((const UChar*) plainText().utf16());
1860
        pos = it->preceding(pos);
20558 craig 1861
        return pos;
7501 avox 1862
}
1863
int StoryText::nextParagraph(int pos)
1864
{
1865
        int len = length();
23529 jghali 1866
        pos = qMin(len, pos + 1);
7501 avox 1867
        while (pos < len && text(pos) != SpecialChars::PARSEP)
1868
                ++pos;
7637 avox 1869
        return pos;
7501 avox 1870
}
1871
int StoryText::prevParagraph(int pos)
1872
{
23529 jghali 1873
        pos = qMax(0, pos - 1);
7501 avox 1874
        while (pos > 0 && text(pos) != SpecialChars::PARSEP)
1875
                --pos;
7637 avox 1876
        return pos;
7501 avox 1877
}
1878
 
4669 avox 1879
// selection
1880
int StoryText::startOfSelection() const
1881
{
23626 jghali 1882
        return d->selFirst <= d->selLast? d->selFirst : 0;
4669 avox 1883
}
1884
 
1885
int StoryText::endOfSelection() const
1886
{
23626 jghali 1887
        return d->selFirst <= d->selLast? d->selLast + 1 : -1;
4669 avox 1888
}
1889
 
22832 craig 1890
int StoryText::selectionLength() const
4669 avox 1891
{
17826 craig 1892
        //FIX ME - sometimes I saw values equal or greater than length of text
23626 jghali 1893
        int last = d->selLast;
1894
        if (d->selFirst >= length())
17826 craig 1895
                return 0;
23626 jghali 1896
        if (d->selLast >= length())
17826 craig 1897
                last = length() -1;
23626 jghali 1898
        return d->selFirst <= last? last - d->selFirst + 1 : 0;
4669 avox 1899
}
1900
 
23940 jghali 1901
bool StoryText::hasSelection() const
23386 craig 1902
{
1903
        return selectionLength() > 0;
1904
}
4669 avox 1905
 
23386 craig 1906
 
4669 avox 1907
bool StoryText::selected(int pos) const
1908
{
23626 jghali 1909
        return (d->selFirst <= pos && pos <= d->selLast)
5721 avox 1910
//             || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
1911
        ;
4669 avox 1912
}
1913
 
23396 jghali 1914
QString StoryText::selectedText() const
1915
{
1916
        int selLen = selectionLength();
1917
        if (selLen <= 0)
1918
                return QString();
1919
        return text(startOfSelection(), selLen);
1920
}
13054 pierre 1921
 
1922
int StoryText::selectWord(int pos)
1923
{
1924
        //Double click in a frame to select a word
21563 jghali 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();
22277 jghali 1931
        int wordLength = end - start;
22279 jghali 1932
        if (wordLength > 0)
23396 jghali 1933
        {
22279 jghali 1934
                select(start, wordLength);
23396 jghali 1935
                setCursorPosition(endOfSelection());
1936
        }
22279 jghali 1937
        else
1938
        {
1939
                deselectAll();
1940
                setCursorPosition(start);
1941
        }
21563 jghali 1942
        return start;
13054 pierre 1943
}
1944
 
1945
 
22271 craig 1946
void StoryText::select(int pos, int len, bool on)
4669 avox 1947
{
1948
        if (pos < 0)
1949
                pos += length();
22279 jghali 1950
        if (len < 0)
1951
                len = 0;
4669 avox 1952
 
1953
        assert( pos >= 0 );
1954
        assert( pos + signed(len) <= length() );
1955
 
23626 jghali 1956
//      qDebug("old selection: %d - %d", d->selFirst, d->selLast);
5751 avox 1957
 
5721 avox 1958
//      StoryText* that = const_cast<StoryText *>(this);
1959
//      for (int i=pos; i < pos+signed(len); ++i)
1960
//              that->at(i)->cselect = on;
4669 avox 1961
 
1962
        if (on) {
1963
                // extend if possible
1964
                if (selected(pos - 1))
23626 jghali 1965
                        d->selLast = qMax(d->selLast, pos + len - 1);
4669 avox 1966
                else if (selected(pos + len))
23626 jghali 1967
                        d->selFirst = qMin(d->selFirst, pos);
4669 avox 1968
                else {
23626 jghali 1969
                        d->selFirst = pos;
1970
                        d->selLast = pos + len - 1;
4669 avox 1971
                }
1972
        }
1973
        else {
23626 jghali 1974
                if (pos <= d->selFirst && d->selLast < pos + signed(len))
4669 avox 1975
                        deselectAll();
1976
                // shrink
1977
                else if (!selected(pos - 1) && selected(pos + len - 1))
23626 jghali 1978
                        d->selFirst = pos + len;
4669 avox 1979
                else if (selected(pos) && !selected(pos + len))
23626 jghali 1980
                        d->selLast = pos - 1;
4669 avox 1981
                else if (selected(pos) || selected(pos + len - 1))
1982
                        // Grr, deselection splits selection
23626 jghali 1983
                        d->selLast = pos - 1;
4669 avox 1984
        }
21563 jghali 1985
 
1986
        fixSurrogateSelection();
5751 avox 1987
 
23626 jghali 1988
//      qDebug("new selection: %d - %d", d->selFirst, d->selLast);
4669 avox 1989
}
1990
 
13054 pierre 1991
void StoryText::extendSelection(int oldPos, int newPos)
1992
{
23626 jghali 1993
        if (d->selFirst <= d->selLast)
13054 pierre 1994
        {
1995
                // have selection
23626 jghali 1996
                if (d->selLast == oldPos - 1)
13054 pierre 1997
                {
23626 jghali 1998
                        d->selLast = newPos - 1;
13054 pierre 1999
                        return;
2000
                }
23626 jghali 2001
                if (d->selFirst == oldPos)
13054 pierre 2002
                {
23626 jghali 2003
                        d->selFirst = newPos;
13054 pierre 2004
                        return;
2005
                }
2006
                // can't extend, fall through
2007
        }
2008
        // no previous selection
2009
        if (newPos > oldPos)
2010
        {
23626 jghali 2011
                d->selFirst = oldPos;
2012
                d->selLast = newPos - 1;
13054 pierre 2013
        }
2014
        else
2015
        {
23626 jghali 2016
                d->selFirst = newPos;
2017
                d->selLast = oldPos - 1;
13054 pierre 2018
        }
21563 jghali 2019
 
2020
        fixSurrogateSelection();
13054 pierre 2021
}
2022
 
21563 jghali 2023
 
2024
void StoryText::fixSurrogateSelection()
2025
{
23626 jghali 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;
21563 jghali 2030
}
2031
 
22516 craig 2032
BreakIterator* StoryText::m_graphemeIterator = nullptr;
21563 jghali 2033
 
2034
BreakIterator* StoryText::getGraphemeIterator()
2035
{
2036
        UErrorCode status = U_ZERO_ERROR;
22516 craig 2037
        if (m_graphemeIterator == nullptr)
21563 jghali 2038
                m_graphemeIterator = BreakIterator::createCharacterInstance(Locale(), status);
2039
 
2040
        if (U_FAILURE(status))
2041
        {
2042
                delete m_graphemeIterator;
22516 craig 2043
                m_graphemeIterator = nullptr;
21563 jghali 2044
        }
2045
 
2046
        return m_graphemeIterator;
2047
}
2048
 
22516 craig 2049
BreakIterator* StoryText::m_wordIterator = nullptr;
21563 jghali 2050
 
2051
BreakIterator* StoryText::getWordIterator()
2052
{
2053
        UErrorCode status = U_ZERO_ERROR;
22516 craig 2054
        if (m_wordIterator == nullptr)
21563 jghali 2055
                m_wordIterator = BreakIterator::createWordInstance(Locale(), status);
2056
 
2057
        if (U_FAILURE(status))
2058
        {
2059
                delete m_wordIterator;
22516 craig 2060
                m_wordIterator = nullptr;
21563 jghali 2061
        }
2062
        return m_wordIterator;
2063
}
2064
 
22516 craig 2065
BreakIterator* StoryText::m_sentenceIterator = nullptr;
21563 jghali 2066
 
2067
BreakIterator* StoryText::getSentenceIterator()
2068
{
2069
        UErrorCode status = U_ZERO_ERROR;
22516 craig 2070
        if (m_sentenceIterator == nullptr)
21563 jghali 2071
                m_sentenceIterator = BreakIterator::createSentenceInstance(Locale(), status);
2072
 
2073
        if (U_FAILURE(status))
2074
        {
2075
                delete m_sentenceIterator;
22516 craig 2076
                m_sentenceIterator = nullptr;
21563 jghali 2077
        }
2078
 
2079
        return m_sentenceIterator;
2080
}
2081
 
22516 craig 2082
BreakIterator* StoryText::m_lineIterator = nullptr;
21563 jghali 2083
 
2084
BreakIterator* StoryText::getLineIterator()
2085
{
2086
        UErrorCode status = U_ZERO_ERROR;
22516 craig 2087
        if (m_lineIterator == nullptr)
21563 jghali 2088
                m_lineIterator = BreakIterator::createLineInstance(Locale(), status);
2089
 
2090
        if (U_FAILURE(status))
2091
        {
2092
                delete m_lineIterator;
22516 craig 2093
                m_lineIterator = nullptr;
21563 jghali 2094
        }
2095
 
2096
        return m_lineIterator;
2097
}
2098
 
4669 avox 2099
void StoryText::selectAll()
2100
{
5721 avox 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
*/
23626 jghali 2108
        d->selFirst = 0;
2109
        d->selLast = length() - 1;
4669 avox 2110
}
2111
 
2112
void StoryText::deselectAll()
2113
{
5721 avox 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
*/     
23626 jghali 2121
        d->selFirst = 0;
2122
        d->selLast = -1;
4669 avox 2123
}
2124
 
2125
void StoryText::removeSelection()
2126
{
23626 jghali 2127
//      qDebug("removeSelection: %d - %d", d->selFirst, d->selLast);
2128
        if (d->selFirst > d->selLast)
4669 avox 2129
                return;
2130
 
23626 jghali 2131
        assert( d->selFirst >= 0 );
2132
        assert( d->selLast < length() );
4669 avox 2133
 
23626 jghali 2134
        removeChars(d->selFirst, d->selLast - d->selFirst+1);
4669 avox 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
{
8134 avox 2150
        d->pstyleContext.invalidate();
22993 jghali 2151
        invalidate(0, length());
4669 avox 2152
}
2153
 
7958 avox 2154
void StoryText::invalidate(int firstItem, int endItem)
4669 avox 2155
{
23534 jghali 2156
        for (int i = firstItem; i < endItem; ++i)
22295 craig 2157
        {
7958 avox 2158
                ParagraphStyle* par = item(i)->parstyle;
2159
                if (par)
8134 avox 2160
                        par->charStyleContext()->invalidate();
7958 avox 2161
        }
17826 craig 2162
        if (!signalsBlocked())
21515 jghali 2163
                emit changed(firstItem, endItem);
4669 avox 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
 
23820 craig 2180
ScText*  StoryText::item(int index)
4669 avox 2181
{
23820 craig 2182
        assert( index < length() );
2183
        return const_cast<StoryText *>(this)->d->at(index);
4669 avox 2184
}
2185
 
23820 craig 2186
const ScText*  StoryText::item(int index) const
6144 avox 2187
{
23820 craig 2188
        assert( index < length() );
2189
        return const_cast<StoryText *>(this)->d->at(index);
6144 avox 2190
}
2191
 
7442 avox 2192
 
8266 avox 2193
using namespace desaxe;
8194 avox 2194
 
8800 jghali 2195
void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
8194 avox 2196
{
2197
        Xml_attr empty;
2198
        Xml_attr pageno;
2199
        pageno.insert("name", "pgno");
11713 fschmid 2200
        Xml_attr pageco;
2201
        pageco.insert("name", "pgco");
8194 avox 2202
 
8266 avox 2203
        handler.begin(elemtag, empty);
2204
        defaultStyle().saxx(handler, "defaultstyle");
8194 avox 2205
 
8398 avox 2206
        CharStyle lastStyle(charStyle(0));
8194 avox 2207
        bool lastWasPar = true;
2208
        int lastPos = 0;
8398 avox 2209
        handler.begin("p", empty);
2210
        paragraphStyle(0).saxx(handler);
2211
        handler.begin("span", empty);
2212
        lastStyle.saxx(handler);
8194 avox 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 ||
11713 fschmid 2225
                        curr == SpecialChars::PAGECOUNT ||
8194 avox 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
                        {
15009 jghali 2234
                                handler.chars(textWithSoftHyphens(lastPos, i-lastPos));
8194 avox 2235
                        }
2236
                        lastPos = i;
2237
                }
2238
 
2239
                lastWasPar = (curr == SpecialChars::PARSEP);
2240
                if (lastWasPar)
2241
                {
8398 avox 2242
                        handler.end("span");
2243
                        handler.end("p");
2244
                        handler.begin("p", empty);
8913 avox 2245
                        paragraphStyle(i+1).saxx(handler);
8398 avox 2246
                        handler.begin("span", empty);
12962 jghali 2247
                        lastStyle.erase();
8194 avox 2248
                }
16600 jghali 2249
                else if (this->hasObject(i))
8194 avox 2250
                {
21563 jghali 2251
                        getItem(i)->saxx(handler);
8194 avox 2252
                }
17826 craig 2253
                else if (hasMark(i))
2254
                {
2255
                        Mark* mrk = mark(i);
18330 jghali 2256
                        if ((m_doc->m_Selection->itemAt(0)->isNoteFrame() && mrk->isType(MARKNoteMasterType))
2257
                                || (!m_doc->m_Selection->itemAt(0)->isTextFrame() && mrk->isType(MARKNoteFrameType))
18047 craig 2258
                            || mrk->isType(MARKBullNumType))
17826 craig 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()));
18047 craig 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
//                      }
22516 craig 2283
                        if (mrk->isType(MARK2ItemType) && (mrk->getItemPtr() != nullptr))
17826 craig 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);
22516 craig 2290
                                if (m_doc->getMark(l,t) != nullptr)
17826 craig 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
                        {
18044 jghali 2298
                                TextNote * note = mrk->getNotePtr();
22516 craig 2299
                                assert(note != nullptr);
18044 jghali 2300
                                mark_attr.insert("nStyle", note->notesStyle()->name());
2301
                                mark_attr.insert("note",note->saxedText());
17826 craig 2302
                                //store noteframe name for inserting into note if it is non-auto-removable
18044 jghali 2303
                                if (note->noteMark() && note->noteMark()->getItemPtr() && !note->noteMark()