Subversion Repositories Scribus

Rev

Rev 21733 | Rev 22130 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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