Subversion Repositories Scribus

Rev

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