Subversion Repositories Scribus

Rev

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