Subversion Repositories Scribus

Rev

Rev 17422 | Rev 17541 | 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"
5980 avox 29
#include "scfonts.h"
7970 cbradney 30
#include "scribusdoc.h"
31
#include "sctext_shared.h"
4669 avox 32
#include "storytext.h"
33
#include "scribus.h"
5184 avox 34
#include "util.h"
8455 avox 35
#include "resourcecollection.h"
8266 avox 36
#include "desaxe/saxiohelper.h"
37
#include "desaxe/digester.h"
38
#include "desaxe/simple_actions.h"
4669 avox 39
 
40
 
7970 cbradney 41
StoryText::StoryText(ScribusDoc * doc_) : doc(doc_)
5753 jghali 42
{
6733 avox 43
        if (doc_) {
7442 avox 44
                d = new ScText_Shared(&doc_->paragraphStyles());
7958 avox 45
                doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
46
                doc->charStyles().connect(this, SLOT(invalidateAll()));
6733 avox 47
        }
48
        else {
7442 avox 49
                d = new ScText_Shared(NULL);
6733 avox 50
        }
7002 avox 51
        selFirst = 0;
52
        selLast = -1;
53
 
54
        firstFrameItem = 0;
55
        lastFrameItem = -1;
7501 avox 56
        m_magicX = 0.0;
57
        m_lastMagicPos = -1;
7002 avox 58
 
59
        d->len = 0;
60
        invalidateAll();
4669 avox 61
}
62
 
7970 cbradney 63
StoryText::StoryText() : doc(NULL)
6144 avox 64
{
7442 avox 65
        d = new ScText_Shared(NULL);
7501 avox 66
 
67
        selFirst = 0;
68
        selLast = -1;
69
 
70
        firstFrameItem = 0;
71
        lastFrameItem = -1;
72
        m_magicX = 0.0;
73
        m_lastMagicPos = -1;
6144 avox 74
}
75
 
8337 avox 76
StoryText::StoryText(const StoryText & other) : QObject(), SaxIO(), doc(other.doc)
4669 avox 77
{
5721 avox 78
        d = other.d;
79
        d->refs++;
80
 
7958 avox 81
        if(doc) {
82
                doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
83
                doc->charStyles().connect(this, SLOT(invalidateAll()));
84
        }
85
 
4706 avox 86
        selFirst = 0;
87
        selLast = -1;
88
 
89
        firstFrameItem = 0;
90
        lastFrameItem = -1;
7501 avox 91
        m_magicX = 0.0;
92
        m_lastMagicPos = -1;
5721 avox 93
 
4669 avox 94
        invalidateLayout();
95
}
96
 
97
StoryText::~StoryText()
98
{
9476 fschmid 99
/* Code below is not needed imho, as all connections will be disconnected automatically
100
        by the QObject destructor. At least removing that code fixes a crash when closing
101
        documents */
102
/*      if (doc)
7958 avox 103
        {
104
                doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
105
                doc->charStyles().disconnect(this, SLOT(invalidateAll()));
9476 fschmid 106
        } */
5721 avox 107
        d->refs--;
108
        if (d->refs == 0) {
5773 avox 109
                d->clear();
110
                d->len = 0;
5721 avox 111
                delete d;
112
        }      
4669 avox 113
}
114
 
17408 fschmid 115
void StoryText::setDoc(ScribusDoc *docin)
116
{
117
        doc = docin;
118
}
119
 
6144 avox 120
StoryText StoryText::copy() const
121
{
122
        StoryText result(doc);
123
        *(result.d) = *d;
7786 avox 124
        return result;
13085 jghali 125
//      qDebug() << QString("StoryText::copy:");
10366 jghali 126
        QListIterator<ScText*> it( *(result.d) );
6144 avox 127
        ScText* elem;
10366 jghali 128
        while ( it.hasNext() ) {
129
                elem = it.next();
13085 jghali 130
//              qDebug() << QString("\tchar '%1' size %2 (orig %3)").arg(elem->ch).arg(elem->fontSize()).arg(charStyle(i++).fontSize());
6144 avox 131
        }
132
 
133
        return result;
134
}
135
 
136
 
4669 avox 137
StoryText& StoryText::operator= (const StoryText & other)
138
{
7786 avox 139
        other.d->refs++;
140
 
5721 avox 141
        d->refs--;
142
        if (d->refs == 0) {
143
                clear();
144
                delete d;
145
        }
146
 
7958 avox 147
        if(doc) {
148
                doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
149
                doc->charStyles().disconnect(this, SLOT(invalidateAll()));
150
        }
151
 
5721 avox 152
        doc = other.doc;
153
        d = other.d;
154
 
7958 avox 155
        if (doc) {
156
                doc->paragraphStyles().connect(this, SLOT(invalidateAll()));
157
                doc->charStyles().connect(this, SLOT(invalidateAll()));
158
        }
159
 
4706 avox 160
        selFirst = 0;
161
        selLast = -1;
162
 
163
        firstFrameItem = 0;
164
        lastFrameItem = -1;
165
 
4669 avox 166
        invalidateLayout();
167
        return *this;
168
}
169
 
16639 jghali 170
int StoryText::cursorPosition() const
171
{
172
        return d->cursorPosition;
173
}
4669 avox 174
 
16639 jghali 175
void StoryText::setCursorPosition(int pos, bool relative)
176
{
177
        if (relative)
178
                pos += d->cursorPosition;
179
        d->cursorPosition = qMin((uint) qMax(pos, 0), d->len);
180
}
181
 
182
void StoryText::normalizeCursorPosition()
183
{
184
        d->cursorPosition = qMax((uint) 0, qMin(d->cursorPosition, d->len));
185
}
186
 
187
int StoryText::normalizedCursorPosition()
188
{
189
        return (int) qMax((uint) 0, qMin(d->cursorPosition, d->len));
190
}
191
 
4669 avox 192
void StoryText::clear()
193
{
194
        selFirst = 0;
195
        selLast = -1;
196
 
197
        firstFrameItem = 0;
198
        lastFrameItem = -1;
199
 
6733 avox 200
        d->defaultStyle.erase();
7232 avox 201
        d->trailingStyle.erase();
17231 fschmid 202
 
5721 avox 203
        d->clear();
5722 avox 204
        d->len = 0;
4669 avox 205
        invalidateAll();
206
}
207
 
16626 jghali 208
void StoryText::insert(const StoryText& other, bool onlySelection)
209
{
210
        insert(d->cursorPosition, other, onlySelection);
211
}
4669 avox 212
 
6887 avox 213
void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
5732 avox 214
{
8049 avox 215
        if (pos < 0)
216
                pos += length()+1;
217
 
6977 avox 218
        CharStyle cstyle(charStyle(pos));
219
        ParagraphStyle pstyle(paragraphStyle(pos));
7242 avox 220
 
221
        // this style represents all differences between this and other's defaultstyles
222
        ParagraphStyle otherDefault(other.defaultStyle());
223
        otherDefault.eraseStyle(defaultStyle());
224
 
6887 avox 225
        int otherStart  = onlySelection? other.startOfSelection() : 0;
226
        int otherEnd    = onlySelection? other.endOfSelection() : other.length();
227
        int cstyleStart = otherStart;
228
        for (int i=otherStart; i < otherEnd; ++i) {
229
                if (other.charStyle(i) == cstyle
230
                        && other.text(i) != SpecialChars::OBJECT
231
                        && other.text(i) != SpecialChars::PARSEP)
5732 avox 232
                        continue;
233
                int len = i - cstyleStart;
234
                if (len > 0) {
15009 jghali 235
                        insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
7242 avox 236
                        applyCharStyle(pos, len, otherDefault.charStyle());
5732 avox 237
                        applyCharStyle(pos, len, cstyle);
6887 avox 238
                        pos += len;
5732 avox 239
                }
240
                if (other.text(i) == SpecialChars::PARSEP) {
6887 avox 241
                        insertChars(pos, SpecialChars::PARSEP);
12056 jghali 242
                        //#5845 : disable for now as it has nasty side effects when linking frames
243
                        //applyStyle(pos, otherDefault);
6887 avox 244
                        applyStyle(pos, other.paragraphStyle(i));
5732 avox 245
                        cstyleStart = i+1;
6887 avox 246
                        pos += 1;
5732 avox 247
                }
6887 avox 248
                else if (other.text(i) == SpecialChars::OBJECT) {
8174 avox 249
                        insertChars(pos, SpecialChars::OBJECT);
250
                        item(pos)->embedded = other.item(i)->embedded;
16600 jghali 251
                        applyCharStyle(pos, 1, other.charStyle(i));
6887 avox 252
                        cstyleStart = i+1;
253
                        pos += 1;
254
                }
5732 avox 255
                else {
256
                        cstyle = other.charStyle(i);
257
                        cstyleStart = i;
258
                }
259
        }
6887 avox 260
        int len = otherEnd - cstyleStart;
5732 avox 261
        if (len > 0) {
15009 jghali 262
                insertCharsWithSoftHyphens(pos, other.textWithSoftHyphens(cstyleStart, len));
7242 avox 263
                applyCharStyle(pos, len, otherDefault.charStyle());
5732 avox 264
                applyCharStyle(pos, len, cstyle);
7232 avox 265
                pos += len;
7256 avox 266
                if (other.text(otherEnd-1) != SpecialChars::PARSEP) {
12056 jghali 267
                        //#5845 : disable for now as it has nasty side effects when linking frames
268
                        //applyStyle(pos, otherDefault);
7256 avox 269
                        applyStyle(pos, other.paragraphStyle(otherEnd-1));
270
                }
5732 avox 271
        }
8049 avox 272
        invalidate(pos, length());
5732 avox 273
}
274
 
275
 
5721 avox 276
/**
277
    Make sure that the paragraph CharStyle's point to the new ParagraphStyle
278
 */
8174 avox 279
void StoryText::insertParSep(int pos)
5721 avox 280
{
8174 avox 281
        ScText* it = item_p(pos);
6733 avox 282
        if(!it->parstyle) {
8174 avox 283
                it->parstyle = new ParagraphStyle(paragraphStyle(pos+1));
8134 avox 284
                it->parstyle->setContext( & d->pstyleContext);
12689 jghali 285
                // #7432 : when inserting a paragraph separator, apply/erase the trailing Style
12796 pierre 286
                if (pos >= signed(d->len - 1))
12689 jghali 287
                {
288
                        applyStyle(pos, d->trailingStyle);
289
                        d->trailingStyle.erase();
290
                }
8194 avox 291
//              it->parstyle->setName("para"); // DONT TRANSLATE
292
//              it->parstyle->charStyle().setName("cpara"); // DONT TRANSLATE
8134 avox 293
//              it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
6733 avox 294
        }
8134 avox 295
        d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
5721 avox 296
}
297
/**
298
     need to remove the ParagraphStyle structure and replace all pointers
299
     to it...
300
 */
8174 avox 301
void StoryText::removeParSep(int pos)
5721 avox 302
{
8174 avox 303
        ScText* it = item_p(pos);
5721 avox 304
        if (it->parstyle) {
6733 avox 305
//              const CharStyle* oldP = & it->parstyle->charStyle();
306
//              const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
307
//              d->replaceParentStyle(pos, oldP, newP);
5778 avox 308
                delete it->parstyle;
5980 avox 309
                it->parstyle = 0;
5721 avox 310
        }
8134 avox 311
        // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
6904 avox 312
        // doesnt choke:
9737 jghali 313
        it->ch = 0;
8174 avox 314
        d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());  
5721 avox 315
}
316
 
4669 avox 317
void StoryText::removeChars(int pos, uint len)
318
{
319
        if (pos < 0)
320
                pos += length();
8049 avox 321
 
4669 avox 322
        assert( len > 0 );
323
        assert( pos >= 0 );
11991 jghali 324
        // This case can be handled more gracefully
325
        // assert( pos + static_cast<int>(len) <= length() );
326
        if (pos >= length())
327
                return;
328
        if (pos + static_cast<int>(len) > length())
329
                len = length() - pos;
4749 avox 330
 
5751 avox 331
        for ( int i=pos + static_cast<int>(len) - 1; i >= pos; --i )
4749 avox 332
        {
5721 avox 333
                ScText *it = d->at(i);
9737 jghali 334
                if ((it->ch == SpecialChars::PARSEP)) {
8174 avox 335
                        removeParSep(i);
4749 avox 336
                }
9737 jghali 337
//              qDebug("remove char %d at %d", (int) it->ch.unicode(), i);
10366 jghali 338
                d->takeAt(i);
5722 avox 339
                d->len--;
4749 avox 340
                delete it;
16104 jghali 341
                // #9592 : adjust selFirst and selLast, those values have to be
342
                // consistent in functions such as select()
343
                if (i <= selLast) --selLast;
344
                if (i < selFirst) --selFirst;
16639 jghali 345
                if ((i + 1 ) <= d->cursorPosition && d->cursorPosition > 0) d->cursorPosition -= 1;
4749 avox 346
        }
5722 avox 347
 
348
        d->len = d->count();
16626 jghali 349
        d->cursorPosition = qMin(d->cursorPosition, d->len);
16104 jghali 350
        if (selFirst > selLast)
351
        {
352
                selFirst =  0;
353
                selLast  = -1;
354
        }
8049 avox 355
        invalidate(pos, length());
4669 avox 356
}
357
 
16626 jghali 358
void StoryText::insertChars(QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
359
{
360
        insertChars(d->cursorPosition, txt, applyNeighbourStyle);
361
}
362
 
9645 avox 363
void StoryText::insertChars(int pos, QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
4669 avox 364
{
365
        if (pos < 0)
8049 avox 366
                pos += length()+1;
4669 avox 367
 
368
        assert(pos >= 0);
369
        assert(pos <= length());
4749 avox 370
 
5365 avox 371
        if (txt.length() == 0)
372
                return;
373
 
8134 avox 374
        const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
9426 avox 375
 
9645 avox 376
        ScText clone;
377
        if (applyNeighbourStyle)
378
        {
10394 cbradney 379
                int referenceChar = qMax(0, qMin(pos, length()-1));
9645 avox 380
                clone.applyCharStyle(charStyle(referenceChar));
381
                clone.setEffects(ScStyle_Default);
382
        }
9607 avox 383
 
8546 cbradney 384
        for (int i = 0; i < txt.length(); ++i) {
9426 avox 385
                ScText * item = new ScText(clone);
9737 jghali 386
                item->ch= txt.at(i);
8134 avox 387
                item->setContext(cStyleContext);
5721 avox 388
                d->insert(pos + i, item);
5722 avox 389
                d->len++;
9737 jghali 390
                if (item->ch == SpecialChars::PARSEP) {
13085 jghali 391
//                      qDebug() << QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name());
8174 avox 392
                        insertParSep(pos + i);
5721 avox 393
                }
16626 jghali 394
                if (d->cursorPosition >= (pos + i)) {
395
                        d->cursorPosition += 1;
396
                }
4749 avox 397
        }
398
 
5722 avox 399
        d->len = d->count();
4669 avox 400
        invalidate(pos, pos + txt.length());
401
}
402
 
15009 jghali 403
void StoryText::insertCharsWithSoftHyphens(int pos, QString txt, bool applyNeighbourStyle)
12093 jghali 404
{
405
        if (pos < 0)
406
                pos += length()+1;
407
 
408
        assert(pos >= 0);
409
        assert(pos <= length());
410
 
411
        if (txt.length() == 0)
412
                return;
413
 
414
        const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
415
 
416
        ScText clone;
417
        if (applyNeighbourStyle)
418
        {
419
                int referenceChar = qMax(0, qMin(pos, length()-1));
420
                clone.applyCharStyle(charStyle(referenceChar));
421
                clone.setEffects(ScStyle_Default);
422
        }
423
 
424
        int inserted = 0;
425
        for (int i = 0; i < txt.length(); ++i)
426
        {
427
                QChar ch = txt.at(i);
12112 jghali 428
                int  index  = pos + inserted;
12093 jghali 429
                bool insert = true;
12097 jghali 430
                if (ch == SpecialChars::SHYPHEN && index > 0) {
431
                        ScText* lastItem = this->item(index - 1);
13161 malex 432
                        // qreal SHY means user provided SHY, single SHY is automatic one
12093 jghali 433
                        if (lastItem->effects() & ScStyle_HyphenationPossible)
434
                                lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
435
                        else
436
                        {
437
                                lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
438
                                insert = false;
439
                        }
440
                }
441
                if (insert)
442
                {
443
                        ScText * item = new ScText(clone);
444
                        item->ch = ch;
445
                        item->setContext(cStyleContext);
12097 jghali 446
                        d->insert(index, item);
12093 jghali 447
                        d->len++;
17407 fschmid 448
                        if (item->ch == SpecialChars::PARSEP)
12097 jghali 449
                                insertParSep(index);
17407 fschmid 450
                        if (d->cursorPosition >= static_cast<uint>(index))
16626 jghali 451
                                d->cursorPosition += 1;
12093 jghali 452
                        ++inserted;
453
                }
454
        }
455
 
456
        d->len = d->count();
457
        invalidate(pos, pos + inserted);
458
}
459
 
4669 avox 460
void StoryText::replaceChar(int pos, QChar ch)
461
{
462
        if (pos < 0)
463
                pos += length();
464
 
465
        assert(pos >= 0);
466
        assert(pos < length());
467
 
5721 avox 468
        ScText* item = d->at(pos);
9737 jghali 469
        if (item->ch == ch)
5721 avox 470
                return;
471
 
9737 jghali 472
        if (d->at(pos)->ch == SpecialChars::PARSEP) {
8174 avox 473
                removeParSep(pos);
5721 avox 474
        }
475
        item->ch = ch;
9737 jghali 476
        if (d->at(pos)->ch == SpecialChars::PARSEP) {
8174 avox 477
                insertParSep(pos);
5721 avox 478
        }
479
 
4669 avox 480
        invalidate(pos, pos + 1);
481
}
482
 
17501 craig 483
int StoryText::replaceWord(int pos, QString newWord)
484
{
485
        int eoWord=pos;
486
        while(eoWord < length())
487
        {
488
                if (text(eoWord).isLetterOrNumber())
489
                        ++eoWord;
490
                else
491
                        break;
492
        }
493
        QString word=text(pos,eoWord-pos);
494
        int lengthDiff=newWord.length()-word.length();
495
        if (lengthDiff==0)
496
        {
497
                for (int j = 0; j < word.length(); ++j)
498
                        replaceChar(pos+j, newWord[j]);
499
        }
500
        else
501
        {
502
                if (lengthDiff>0)
503
                {
504
                        for (int j = 0; j < word.length(); ++j)
505
                                replaceChar(pos+j, newWord[j]);
506
                        for (int j = word.length(); j < newWord.length(); ++j)
507
                                insertChars(pos+j, newWord.mid(j,1), true);
508
                }
509
                else
510
                {
511
                        for (int j = 0; j < newWord.length(); ++j)
512
                                replaceChar(pos+j, newWord[j]);
513
                        removeChars(pos+newWord.length(), -lengthDiff);
514
                }
515
        }
516
        return lengthDiff;
517
}
518
 
5559 avox 519
void StoryText::hyphenateWord(int pos, uint len, char* hyphens)
520
{
521
        assert(pos >= 0);
5691 avox 522
        assert(pos + signed(len) <= length());
5559 avox 523
 
6940 avox 524
//      QString dump("");
5691 avox 525
        for (int i=pos; i < pos+signed(len); ++i)
6937 avox 526
        {
6940 avox 527
//              dump += d->at(i)->ch;
6937 avox 528
                if(hyphens && hyphens[i-pos] & 1) {
5721 avox 529
                        d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
6940 avox 530
//                      dump += "-";
6937 avox 531
                }
532
                else {
5721 avox 533
                        d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
6937 avox 534
                }
535
        }
13085 jghali 536
//      qDebug() << QString("st: %1").arg(dump);
5559 avox 537
        invalidate(pos, pos + len);
538
}
539
 
17407 fschmid 540
void StoryText::insertObject(int ob)
16626 jghali 541
{
542
        insertObject(d->cursorPosition, ob);
543
}
544
 
17407 fschmid 545
void StoryText::insertObject(int pos, int ob)
4669 avox 546
{
547
        if (pos < 0)
8049 avox 548
                pos += length()+1;
4669 avox 549
 
550
        insertChars(pos, SpecialChars::OBJECT);
17405 fschmid 551
        const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
17407 fschmid 552
        doc->FrameItems[ob]->isEmbedded = true;   // this might not be enough...
553
        doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object
4669 avox 554
}
555
 
17407 fschmid 556
void StoryText::replaceObject(int pos, int ob)
17231 fschmid 557
{
558
        if (pos < 0)
559
                pos += length()+1;
4669 avox 560
 
17231 fschmid 561
        replaceChar(pos, SpecialChars::OBJECT);
17405 fschmid 562
        const_cast<StoryText *>(this)->d->at(pos)->embedded = ob;
17407 fschmid 563
        doc->FrameItems[ob]->isEmbedded = true;   // this might not be enough...
564
        doc->FrameItems[ob]->OwnPage = -1; // #10379: OwnPage is not meaningful for inline object
17231 fschmid 565
}
566
 
567
 
4669 avox 568
int StoryText::length() const
569
{
5722 avox 570
        return d->len;
4669 avox 571
}
572
 
16626 jghali 573
QChar StoryText::text() const
574
{
575
        return text(d->cursorPosition);
576
}
577
 
4669 avox 578
QChar StoryText::text(int pos) const
579
{
580
        if (pos < 0)
581
                pos += length();
582
 
583
        assert(pos >= 0);
584
        assert(pos < length());
585
 
9737 jghali 586
        return const_cast<StoryText *>(this)->d->at(pos)->ch;
4669 avox 587
}
588
 
589
QString StoryText::text(int pos, uint len) const
590
{
591
        if (pos < 0)
592
                pos += length();
593
 
594
        assert(pos >= 0);
595
        assert(pos + signed(len) <= length());
596
 
597
        QString result;
5721 avox 598
        StoryText* that(const_cast<StoryText*>(this));
599
        for (int i = pos; i < pos+signed(len); ++i) {
10366 jghali 600
                result += that->d->at(i)->ch;
5721 avox 601
        }
4669 avox 602
 
603
        return result;
604
}
605
 
17501 craig 606
QString StoryText::sentence(int pos, int &posn)
607
{
608
        int sentencePos=qMax(0, prevSentence(pos));
609
        sentencePos=qMax(sentencePos, nextWord(sentencePos));
610
        posn=sentencePos;
611
        int nextSentencePos=qMin(length(), nextSentence(pos+1));
612
        return text(sentencePos, nextSentencePos-sentencePos);
613
}
614
 
15009 jghali 615
QString StoryText::textWithSoftHyphens(int pos, uint len) const
12093 jghali 616
{
617
        QString result("");
618
        int lastPos = pos;
619
 
620
        len = qMin((uint) (length() - pos), len);
621
        for (int i = pos; i < pos+signed(len); ++i)
622
        {
623
                if (this->charStyle(i).effects() & ScStyle_HyphenationPossible
624
                        // duplicate SHYPHEN if already present to indicate a user provided SHYPHEN:
625
                        || this->text(i) == SpecialChars::SHYPHEN)
626
                {
627
                        result += text(lastPos, i + 1 - lastPos);
628
                        result += SpecialChars::SHYPHEN;
629
                        lastPos = i+1;
630
                }
631
        }
632
        if (lastPos < pos+signed(len))
633
                result += text(lastPos, pos+signed(len) - lastPos);
634
        return result;
635
}
636
 
16600 jghali 637
bool StoryText::hasObject(int pos) const
638
{
639
        if (pos < 0)
640
                pos += length();
641
 
642
        assert(pos >= 0);
643
        assert(pos < length());
644
 
645
        StoryText* that = const_cast<StoryText *>(this);
646
        if (that->d->at(pos)->ch == SpecialChars::OBJECT)
17407 fschmid 647
                return that->d->at(pos)->hasObject(doc);
16600 jghali 648
        return false;
649
}
650
 
4669 avox 651
PageItem* StoryText::object(int pos) const
652
{
653
        if (pos < 0)
654
                pos += length();
655
 
656
        assert(pos >= 0);
657
        assert(pos < length());
658
 
8174 avox 659
        StoryText* that = const_cast<StoryText *>(this);
17407 fschmid 660
        return that->d->at(pos)->getItem(doc);
4669 avox 661
}
662
 
16626 jghali 663
const CharStyle & StoryText::charStyle() const
664
{
665
        return charStyle(d->cursorPosition);
666
}
4669 avox 667
 
668
const CharStyle & StoryText::charStyle(int pos) const
669
{
670
        if (pos < 0)
671
                pos += length();
672
 
673
        assert(pos >= 0);
5732 avox 674
        assert(pos <= length());
4669 avox 675
 
6144 avox 676
        if (length() == 0) {
13085 jghali 677
//              qDebug() << "storytext::charstyle: default";
5732 avox 678
                return defaultStyle().charStyle();
6144 avox 679
        }
680
        else if (pos == length()) {
13144 fschmid 681
                qDebug() << "storytext::charstyle: access at end of text %i" << pos;
5732 avox 682
                --pos;
6144 avox 683
        }
8956 avox 684
        if (text(pos) == SpecialChars::PARSEP)
685
                return paragraphStyle(pos).charStyle();
5732 avox 686
 
4669 avox 687
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 688
        return dynamic_cast<const CharStyle &> (*that->d->at(pos));
4669 avox 689
}
690
 
16639 jghali 691
const ParagraphStyle & StoryText::paragraphStyle() const
692
{
693
        return paragraphStyle(d->cursorPosition);
694
}
695
 
4669 avox 696
const ParagraphStyle & StoryText::paragraphStyle(int pos) const
697
{
698
        if (pos < 0)
699
                pos += length();
700
 
701
        assert(pos >= 0);
5721 avox 702
        assert(pos <= length());
4669 avox 703
 
6733 avox 704
        assert(d);
705
 
5192 avox 706
        StoryText * that = const_cast<StoryText *> (this);
5721 avox 707
//      assert( that->at(pos)->cab >= 0 );
708
//      assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
709
//      return doc->docParagraphStyles[that->at(pos)->cab];
710
 
10366 jghali 711
        while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP) {
5721 avox 712
                ++pos;
713
        }
6909 avox 714
        if (pos >= length()) {
7232 avox 715
                return that->d->trailingStyle;
5778 avox 716
        }
10366 jghali 717
        else if ( !that->d->at(pos)->parstyle ) {
718
                ScText* current = that->d->at(pos);
10394 cbradney 719
                qDebug("inserting default parstyle at %i", pos);
10366 jghali 720
                current->parstyle = new ParagraphStyle();
721
                current->parstyle->setContext( & d->pstyleContext);
722
//              current->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE
723
//              current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE
724
//              current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5721 avox 725
        }
5778 avox 726
        else {
13085 jghali 727
//              qDebug() << QString("using parstyle at %1").arg(pos);
5778 avox 728
        }
10366 jghali 729
        assert (that->d->at(pos)->parstyle);
730
        return *that->d->at(pos)->parstyle;
4669 avox 731
}
732
 
5559 avox 733
const ParagraphStyle& StoryText::defaultStyle() const
734
{
6733 avox 735
        assert(d);
5721 avox 736
        return d->defaultStyle;
5559 avox 737
}
738
 
5569 avox 739
 
740
void StoryText::setDefaultStyle(const ParagraphStyle& style)
741
{
8134 avox 742
        const StyleContext * oldPContext = d->defaultStyle.context();
8289 avox 743
//      const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
5721 avox 744
        d->defaultStyle = style;
8134 avox 745
        d->defaultStyle.setContext( oldPContext );
8194 avox 746
//      d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE
747
//      d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE
13085 jghali 748
//      qDebug() << QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
8134 avox 749
//                 .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
750
//                 .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
13085 jghali 751
//                 .arg((uint)d->defaultStyle.charStyle().context(),16);
8134 avox 752
//      d->defaultStyle.charStyle().setContext( oldCContext );
7958 avox 753
        invalidateAll();
5569 avox 754
}
755
 
756
 
5721 avox 757
void StoryText::applyCharStyle(int pos, uint len, const CharStyle& style )
4669 avox 758
{
759
        if (pos < 0)
760
                pos += length();
761
 
11968 fschmid 762
/* Why do we use an assert here instead of a gracefully return doing nothing ????? */
763
//      assert(pos >= 0);
764
//      assert(pos + signed(len) <= length());
765
        if (pos < 0)
766
                return;
767
        if (pos + signed(len) > length())
768
                return;
4669 avox 769
 
770
        if (len == 0)
771
                return;
772
 
17407 fschmid 773
//      int lastParStart = pos == 0? 0 : -1;
10366 jghali 774
        ScText* itText;
5721 avox 775
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 776
                itText = d->at(i);
15658 jghali 777
                // #6165 : applying style on last character applies style on whole text on next open 
778
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
779
                        itText->parstyle->charStyle().applyCharStyle(style);*/
780
 
781
                // Does not work well, do not reenable before checking #9337, #9376 and #9428
15386 craig 782
                // #9173 et. al.: move charstyle to parstyle if whole paragraph is affected
15658 jghali 783
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL && lastParStart >= 0)
15386 craig 784
                {
785
                        eraseCharStyle(lastParStart, i - lastParStart, style);
786
                        itText->parstyle->charStyle().applyCharStyle(style);
787
                        lastParStart = i + 1;
15658 jghali 788
                }*/
10366 jghali 789
                itText->applyCharStyle(style);
5721 avox 790
        }
15658 jghali 791
        // Does not work well, do not reenable before checking #9337, #9376 and #9428
792
        /*if (pos + signed(len) == length() && lastParStart >= 0)
15386 craig 793
        {
794
                eraseCharStyle(lastParStart, length() - lastParStart, style);
795
                d->trailingStyle.charStyle().applyCharStyle(style);
15658 jghali 796
        }*/
15386 craig 797
 
4669 avox 798
        invalidate(pos, pos + len);
799
}
800
 
5773 avox 801
 
802
 
803
void StoryText::eraseCharStyle(int pos, uint len, const CharStyle& style )
804
{
805
        if (pos < 0)
806
                pos += length();
807
 
808
        assert(pos >= 0);
809
        assert(pos + signed(len) <= length());
810
 
811
        if (len == 0)
812
                return;
813
 
10366 jghali 814
        ScText* itText;
5773 avox 815
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 816
                itText = d->at(i);
13290 jghali 817
                // FIXME?? see #6165 : should we really erase charstyle of paragraph style??
10366 jghali 818
                if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
819
                        itText->parstyle->charStyle().eraseCharStyle(style);
820
                itText->eraseCharStyle(style);
5773 avox 821
        }
15658 jghali 822
        // Does not work well, do not reenable before checking #9337, #9376 and #9428
823
        /*if (pos + signed(len) == length())
15386 craig 824
        {
825
                d->trailingStyle.charStyle().eraseCharStyle(style);
15658 jghali 826
        }*/
5773 avox 827
 
828
        invalidate(pos, pos + len);
829
}
830
 
13290 jghali 831
void StoryText::applyStyle(int pos, const ParagraphStyle& style, bool rmDirectFormatting)
4669 avox 832
{
833
        if (pos < 0)
834
                pos += length();
835
 
836
        assert(pos >= 0);
5721 avox 837
        assert(pos <= length());
4669 avox 838
 
5721 avox 839
        int i = pos;
9737 jghali 840
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5184 avox 841
                ++i;
842
        }
5721 avox 843
        if (i < length()) {
844
                if (!d->at(i)->parstyle) {
10394 cbradney 845
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 846
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 847
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
8194 avox 848
//                      d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE
849
//                      d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE
8134 avox 850
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
5721 avox 851
                }
13085 jghali 852
//              qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
5721 avox 853
                d->at(i)->parstyle->applyStyle(style);
854
        }
855
        else {
856
                // not happy about this but inserting a new PARSEP makes more trouble
13085 jghali 857
//              qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
7232 avox 858
                d->trailingStyle.applyStyle(style);
5721 avox 859
        }
13290 jghali 860
        if (rmDirectFormatting)
861
        {
862
                --i;
863
                while (i >= 0 && d->at(i)->ch != SpecialChars::PARSEP)
864
                {
865
                        d->at(i)->eraseDirectFormatting();
866
                        --i;
867
                }
868
        }
8562 jghali 869
        invalidate(pos, qMin(i, length()));
4669 avox 870
}
871
 
5773 avox 872
void StoryText::eraseStyle(int pos, const ParagraphStyle& style)
873
{
874
        if (pos < 0)
875
                pos += length();
876
 
877
        assert(pos >= 0);
878
        assert(pos <= length());
879
 
880
        int i = pos;
9737 jghali 881
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5773 avox 882
                ++i;
883
        }
884
        if (i < length()) {
885
                if (!d->at(i)->parstyle) {
10394 cbradney 886
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 887
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 888
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
8194 avox 889
//                      d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE
890
//                      d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE
8134 avox 891
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5773 avox 892
                }
13085 jghali 893
                //              qDebug() << QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos);
5773 avox 894
                d->at(i)->parstyle->eraseStyle(style);
895
        }
896
        else {
897
                // not happy about this but inserting a new PARSEP makes more trouble
13085 jghali 898
                //              qDebug() << QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos);
7232 avox 899
                d->trailingStyle.eraseStyle(style);
5773 avox 900
        }
8562 jghali 901
        invalidate(pos, qMin(i, length()));
5773 avox 902
}
903
 
904
 
6733 avox 905
void StoryText::setStyle(int pos, const ParagraphStyle& style)
906
{
907
        eraseStyle(pos, paragraphStyle(pos));
908
        applyStyle(pos, style);
909
}
910
 
911
 
912
void StoryText::setCharStyle(int pos, uint len, const CharStyle& style)
913
{
9067 avox 914
        if (pos < 0)
915
                pos += length();
916
 
917
        assert(pos >= 0);
9645 avox 918
        assert(len <= unsigned(length()));
9067 avox 919
        assert(pos + signed(len) <= length());
920
 
921
        if (len == 0)
922
                return;
923
 
10366 jghali 924
        ScText* itText;
9067 avox 925
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 926
                itText = d->at(i);
11936 jghali 927
                // #6165 : applying style on last character applies style on whole text on next open 
928
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
929
                        itText->parstyle->charStyle() = style;*/
10366 jghali 930
                itText->setStyle(style);
9067 avox 931
        }
932
 
933
        invalidate(pos, pos + len);
6733 avox 934
}
935
 
936
 
8455 avox 937
 
938
void StoryText::getNamedResources(ResourceCollection& lists) const
939
{
8938 avox 940
        d->defaultStyle.getNamedResources(lists);
941
        d->trailingStyle.getNamedResources(lists);
942
 
8455 avox 943
        for (int i=0; i < length(); ++i)
944
        {
945
                if (text(i) == SpecialChars::PARSEP)
946
                        paragraphStyle(i).getNamedResources(lists);
16600 jghali 947
                else if (hasObject(i))
8455 avox 948
                        object(i)->getNamedResources(lists);
949
                else
950
                        charStyle(i).getNamedResources(lists);
951
        }
952
}
953
 
954
 
7442 avox 955
void StoryText::replaceStyles(QMap<QString,QString> newNameForOld)
956
{
8455 avox 957
        ResourceCollection newnames;
8775 cbradney 958
        newnames.mapStyles(newNameForOld);
8455 avox 959
        replaceNamedResources(newnames);
960
}
961
 
962
void StoryText::replaceNamedResources(ResourceCollection& newNames)
963
{
7442 avox 964
        int len = length();
8455 avox 965
 
966
        d->defaultStyle.replaceNamedResources(newNames);
967
        d->trailingStyle.replaceNamedResources(newNames);
968
 
7442 avox 969
        if (len == 0)
970
                return;
971
 
10366 jghali 972
        ScText* itText;
7501 avox 973
        for (int i=0; i < len; ++i) {
10366 jghali 974
                itText = d->at(i);
975
                if (itText->parstyle)
976
                        itText->parstyle->replaceNamedResources(newNames);
8455 avox 977
                else
10366 jghali 978
                        itText->replaceNamedResources(newNames);
7442 avox 979
        }
980
 
981
        invalidate(0, len);    
982
}
983
 
8455 avox 984
 
7442 avox 985
void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
986
{
8455 avox 987
        ResourceCollection newnames;
8775 cbradney 988
        newnames.mapCharStyles(newNameForOld);
8455 avox 989
        replaceNamedResources(newnames);
7442 avox 990
}
991
 
16681 jghali 992
void StoryText::fixLegacyFormatting()
993
{
994
        if (length() == 0)
995
                return;
996
 
997
        for (int i = 0; i < length(); ++ i)
998
        {
999
                if (d->at(i)->ch == SpecialChars::PARSEP)
1000
                        fixLegacyFormatting(i);
1001
        }
1002
        fixLegacyFormatting( length() );
1003
}
1004
 
1005
void StoryText::fixLegacyFormatting(int pos)
1006
{
1007
        if (length() == 0)
1008
                return;
1009
 
1010
        if (pos < 0)
1011
                pos += length();
1012
 
1013
        assert(pos >= 0);
1014
        assert(pos <= length());
1015
 
1016
        int i = pos;
1017
        while (i > 0 && d->at(i - 1)->ch != SpecialChars::PARSEP) {
1018
                --i;
1019
        }
1020
 
1021
        const ParagraphStyle& parStyle = this->paragraphStyle(pos);
1022
        parStyle.validate();
1023
 
1024
        if (parStyle.hasParent())
1025
        {
1026
                int start = i;
1027
                while ((i < length()) && (d->at(i)->ch != SpecialChars::PARSEP))
1028
                {
1029
                        d->at(i)->validate();
1030
                        d->at(i)->eraseCharStyle( parStyle.charStyle() );
1031
                        ++i;
1032
                }
1033
                invalidate(start, qMin(i + 1, length()));
1034
        }
1035
}
1036
 
16626 jghali 1037
uint StoryText::nrOfParagraph() const
1038
{
1039
        return nrOfParagraph(d->cursorPosition);
1040
}
8455 avox 1041
 
16069 craig 1042
uint StoryText::nrOfParagraph(int pos) const
10623 avox 1043
{
16069 craig 1044
        uint result = 0;
10623 avox 1045
        StoryText* that = const_cast<StoryText *>(this);
1046
        bool lastWasPARSEP = true;
16069 craig 1047
        pos = qMin(pos, that->length());
1048
        for (int i=0; i < pos; ++i)
1049
        {
10623 avox 1050
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
1051
                if (lastWasPARSEP)
1052
                        ++result;
1053
        }
11380 jghali 1054
        return result;
10623 avox 1055
}
1056
 
4669 avox 1057
uint StoryText::nrOfParagraphs() const
1058
{
1059
        uint result = 0;
5721 avox 1060
        StoryText* that = const_cast<StoryText *>(this);
7059 avox 1061
        bool lastWasPARSEP = true;
16069 craig 1062
        for (int i=0; i < length(); ++i)
1063
        {
10366 jghali 1064
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
6887 avox 1065
                if (lastWasPARSEP)
4669 avox 1066
                        ++result;
5721 avox 1067
        }
11359 jghali 1068
        return lastWasPARSEP ? result : result + 1;
4669 avox 1069
}
1070
 
16626 jghali 1071
int StoryText::startOfParagraph() const
1072
{
1073
        return startOfParagraph(nrOfParagraph());
1074
}
1075
 
4669 avox 1076
int StoryText::startOfParagraph(uint index) const
1077
{
1078
        if (index == 0)
1079
                return 0;
1080
 
5184 avox 1081
        StoryText* that = const_cast<StoryText *>(this);
16069 craig 1082
        for (int i=0; i < length(); ++i)
1083
        {
10366 jghali 1084
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
4669 avox 1085
                        return i + 1;
5721 avox 1086
        }
6887 avox 1087
        return length();
4669 avox 1088
}
1089
 
16626 jghali 1090
int StoryText::endOfParagraph() const
1091
{
1092
        return endOfParagraph(nrOfParagraph());
1093
}
1094
 
4669 avox 1095
int StoryText::endOfParagraph(uint index) const
1096
{
1097
        ++index;
5184 avox 1098
        StoryText* that = const_cast<StoryText *>(this);
16069 craig 1099
        for (int i=0; i < length(); ++i)
1100
        {
10366 jghali 1101
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
5721 avox 1102
                        return i;
1103
        }
4669 avox 1104
        return length();
1105
}
1106
 
1107
uint StoryText::nrOfRuns() const
1108
{
5464 avox 1109
        return length();
4669 avox 1110
}
1111
 
1112
int StoryText::startOfRun(uint index) const
1113
{
5464 avox 1114
        return index;
4669 avox 1115
}
1116
 
1117
int StoryText::endOfRun(uint index) const
1118
{
5464 avox 1119
        return index + 1;
4669 avox 1120
}
1121
 
7501 avox 1122
// positioning. all positioning methods return char positions
1123
// FIXME: make that methods use correct semantic boundaries
4669 avox 1124
 
15805 craig 1125
static QString wordBoundaries(" .,:;\"'!?\n\t");
1126
static QString sentenceBoundaries(".:!?\n\t");
7501 avox 1127
 
1128
int StoryText::nextChar(int pos)
1129
{
1130
        if (pos < length())
1131
                return pos+1;
1132
        else
1133
                return length();
1134
}
1135
int StoryText::prevChar(int pos)
1136
{
1137
        if (pos > 0)
1138
                return pos - 1;
1139
        else
1140
                return 0;
1141
}
1142
int StoryText::nextWord(int pos)
1143
{
1144
        int len = length();
17501 craig 1145
        if (text(pos).isLetter())
1146
                pos = qMin(len, pos+1);
1147
        else
1148
                pos = qMin(len, pos);
1149
 
1150
        //      while (pos < len  && wordBoundaries.indexOf(text(pos)) < 0)
1151
        //              ++pos;
1152
 
1153
        while (pos < len)
1154
        {
1155
                if(text(pos).isLetter())
1156
                        ++pos;
1157
                else
1158
                        break;
1159
        }
7501 avox 1160
        return pos < len ? pos + 1 : pos;
1161
}
1162
int StoryText::prevWord(int pos)
1163
{
8562 jghali 1164
        pos = qMax(0, pos-1);
10394 cbradney 1165
        while (pos > 0 && wordBoundaries.indexOf(text(pos)) < 0)
7501 avox 1166
                --pos;
10394 cbradney 1167
        return wordBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 1168
}
17501 craig 1169
 
1170
int StoryText::endOfWord(int pos) const
1171
{
1172
        int len = length();
1173
        while (pos < len)
1174
        {
1175
                if(text(pos).isLetter())
1176
                        ++pos;
1177
                else
1178
                        break;
1179
        }
1180
        return pos;
1181
}
1182
 
7501 avox 1183
int StoryText::nextSentence(int pos)
1184
{
1185
        int len = length();
8562 jghali 1186
        pos = qMin(len, pos+1);
10394 cbradney 1187
        while (pos < len && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 1188
                ++pos;
1189
        return pos < len ? pos + 1 : pos;
1190
}
1191
int StoryText::prevSentence(int pos)
1192
{
8562 jghali 1193
        pos = qMax(0, pos-1);
10394 cbradney 1194
        while (pos > 0 && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 1195
                --pos;
10394 cbradney 1196
        return sentenceBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 1197
}
1198
int StoryText::nextParagraph(int pos)
1199
{
1200
        int len = length();
8562 jghali 1201
        pos = qMin(len, pos+1);
7501 avox 1202
        while (pos < len && text(pos) != SpecialChars::PARSEP)
1203
                ++pos;
7637 avox 1204
        return pos;
7501 avox 1205
}
1206
int StoryText::prevParagraph(int pos)
1207
{
8562 jghali 1208
        pos = qMax(0, pos-1);
7501 avox 1209
        while (pos > 0 && text(pos) != SpecialChars::PARSEP)
1210
                --pos;
7637 avox 1211
        return pos;
7501 avox 1212
}
1213
 
15805 craig 1214
QString StoryText::wordAt(int pos) const
1215
{
1216
        if (pos < 0)
1217
                pos += length();
1218
        assert(pos >= 0);
15808 craig 1219
        assert(pos <= length());
15805 craig 1220
 
1221
        int len = length();
1222
        //Find the previous word position
1223
        int tmpPosStart = qMax(0, pos-1);
1224
        int startWordPos=0;
1225
        if (tmpPosStart!=0)
1226
        {
1227
                while (tmpPosStart > 0 && wordBoundaries.indexOf(text(tmpPosStart)) < 0)
1228
                        --tmpPosStart;
1229
                startWordPos= wordBoundaries.indexOf(text(tmpPosStart)) < 0 ? tmpPosStart + 1 : tmpPosStart;
1230
                ++startWordPos;
1231
        }
1232
        qDebug ()<<"Start Word Pos:"<<startWordPos;
1233
        //Find the next word position
1234
        int tmpPosEnd = qMin(len, pos+1);
1235
        while (tmpPosEnd < len  && wordBoundaries.indexOf(text(tmpPosEnd)) < 0)
1236
        {
1237
                qDebug()<<tmpPosEnd<<text(tmpPosEnd)<<wordBoundaries.indexOf(text(tmpPosEnd));
1238
                ++tmpPosEnd;
1239
        }
1240
        int endWordPos=tmpPosEnd < len ? tmpPosEnd + 1 : tmpPosEnd;
1241
        if (endWordPos>0)
1242
                --endWordPos;
1243
        qDebug ()<<"End Word Pos:"<<endWordPos;
1244
        QString result;
1245
        StoryText* that(const_cast<StoryText*>(this));
1246
        for (int i = startWordPos; i < endWordPos; ++i)
1247
        {
1248
                result += that->d->at(i)->ch;
1249
        }
1250
        return result;
1251
}
1252
 
7501 avox 1253
// these need valid layout:
1254
 
1255
int StoryText::startOfLine(int pos)
1256
{
8546 cbradney 1257
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 1258
                const LineSpec & ls(m_lines.at(i));
1259
                if (ls.firstItem <= pos && pos <= ls.lastItem)
1260
                        return ls.firstItem;
1261
        }
1262
        return 0;
1263
}
1264
int StoryText::endOfLine(int pos)
1265
{
8546 cbradney 1266
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 1267
                const LineSpec & ls(m_lines.at(i));
1268
                if (ls.firstItem <= pos && pos <= ls.lastItem)
7657 avox 1269
                        return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem :
1270
                                text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1;
7501 avox 1271
        }
1272
        return length();
1273
}
1274
int StoryText::prevLine(int pos)
1275
{
8546 cbradney 1276
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 1277
        {
1278
                // find line for pos
1279
                const LineSpec & ls(m_lines.at(i));
1280
                if (ls.firstItem <= pos && pos <= ls.lastItem)
1281
                {
7522 avox 1282
                        if (i == 0)
1283
                                return startOfLine(pos);
7501 avox 1284
                        // find current xpos
13161 malex 1285
                        qreal xpos = 0.0;
7501 avox 1286
                        for (int j = ls.firstItem; j < pos; ++j)
1287
                                xpos += item(j)->glyph.wide();
1288
                        if (pos != m_lastMagicPos || xpos > m_magicX)
1289
                                m_magicX = xpos;
1290
                        const LineSpec & ls2(m_lines.at(i-1));
1291
                        // find new cpos
1292
                        xpos = 0.0;
1293
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
1294
                        {
1295
                                xpos += item(j)->glyph.wide();
1296
                                if (xpos > m_magicX) {
1297
                                        m_lastMagicPos = j;
1298
                                        return j;
1299
                                }
1300
                        }
1301
                        m_lastMagicPos = ls2.lastItem;
1302
                        return ls2.lastItem;
1303
                }
1304
        }
1305
        return firstFrameItem;
1306
}
1307
 
1308
int StoryText::nextLine(int pos)
1309
{
8546 cbradney 1310
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 1311
        {
1312
                // find line for pos
1313
                const LineSpec & ls(m_lines.at(i));
1314
                if (ls.firstItem <= pos && pos <= ls.lastItem)
1315
                {
7522 avox 1316
                        if (i+1 == m_lines.count())
1317
                                return endOfLine(pos);
7501 avox 1318
                        // find current xpos
13161 malex 1319
                        qreal xpos = 0.0;
7501 avox 1320
                        for (int j = ls.firstItem; j < pos; ++j)
1321
                                xpos += item(j)->glyph.wide();
1322
                        if (pos != m_lastMagicPos || xpos > m_magicX)
1323
                                m_magicX = xpos;
1324
                        const LineSpec & ls2(m_lines.at(i+1));
1325
                        // find new cpos
1326
                        xpos = 0.0;
1327
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
1328
                        {
1329
                                xpos += item(j)->glyph.wide();
1330
                                if (xpos > m_magicX) {
1331
                                        m_lastMagicPos = j;
1332
                                        return j;
1333
                                }
1334
                        }
1335
                        m_lastMagicPos = ls2.lastItem + 1;
1336
                        return ls2.lastItem + 1;
1337
                }
1338
        }
1339
        return lastFrameItem;
1340
}
1341
 
1342
int StoryText::startOfFrame(int pos)
1343
{
1344
        return firstFrameItem;
1345
}
16626 jghali 1346
 
7501 avox 1347
int StoryText::endOfFrame(int pos)
1348
{
7637 avox 1349
        return lastFrameItem + 1;
7501 avox 1350
}
1351
 
4669 avox 1352
// selection
1353
 
1354
int StoryText::startOfSelection() const
1355
{
1356
        return selFirst <= selLast? selFirst : 0;
1357
}
1358
 
1359
int StoryText::endOfSelection() const
1360
{
1361
        return selFirst <= selLast? selLast + 1 : -1;
1362
}
1363
 
1364
int StoryText::lengthOfSelection() const
1365
{
1366
        return selFirst <= selLast? selLast - selFirst + 1 : 0;
1367
}
1368
 
1369
 
1370
bool StoryText::selected(int pos) const
1371
{
4749 avox 1372
        return (selFirst <= pos && pos <= selLast)
5721 avox 1373
//             || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
1374
        ;
4669 avox 1375
}
1376
 
13054 pierre 1377
 
1378
int StoryText::selectWord(int pos)
1379
{
1380
        //Double click in a frame to select a word
1381
 
1382
        int a = pos;
1383
        while(a > 0)
1384
        {
1385
                if (text(a-1).isLetterOrNumber())
1386
                        --a;
1387
                else
1388
                        break;
1389
        }
1390
        int b = pos;
1391
        while(b < length())
1392
        {
1393
                if (text(b).isLetterOrNumber())
1394
                        ++b;
1395
                else
1396
                        break;
1397
        }
1398
        select(a, b - a);
1399
        return a;
1400
}
1401
 
1402
 
4669 avox 1403
void StoryText::select(int pos, uint len, bool on)
1404
{
1405
        if (pos < 0)
1406
                pos += length();
1407
 
1408
        assert( pos >= 0 );
1409
        assert( pos + signed(len) <= length() );
1410
 
5751 avox 1411
//      qDebug("old selection: %d - %d", selFirst, selLast);
1412
 
5721 avox 1413
//      StoryText* that = const_cast<StoryText *>(this);
1414
//      for (int i=pos; i < pos+signed(len); ++i)
1415
//              that->at(i)->cselect = on;
4669 avox 1416
 
1417
        if (on) {
1418
                // extend if possible
1419
                if (selected(pos - 1))
8562 jghali 1420
                        selLast = qMax(selLast, pos + static_cast<int>(len) - 1);
4669 avox 1421
                else if (selected(pos + len))
8562 jghali 1422
                        selFirst = qMin(selFirst, pos);
4669 avox 1423
                else {
1424
                        selFirst = pos;
1425
                        selLast = pos + len - 1;
1426
                }
1427
        }
1428
        else {
1429
                if (pos <= selFirst && selLast < pos + signed(len))
1430
                        deselectAll();
1431
                // shrink
1432
                else if (!selected(pos - 1) && selected(pos + len - 1))
1433
                        selFirst = pos + len;
1434
                else if (selected(pos) && !selected(pos + len))
1435
                        selLast = pos - 1;
1436
                else if (selected(pos) || selected(pos + len - 1))
1437
                        // Grr, deselection splits selection
1438
                        selLast = pos - 1;
1439
        }
5751 avox 1440
 
1441
//      qDebug("new selection: %d - %d", selFirst, selLast);
4669 avox 1442
}
1443
 
13054 pierre 1444
void StoryText::extendSelection(int oldPos, int newPos)
1445
{
1446
        if (selFirst <= selLast)
1447
        {
1448
                // have selection
1449
                if (selLast == oldPos - 1)
1450
                {
1451
                        selLast = newPos - 1;
1452
                        return;
1453
                }
1454
                else if (selFirst == oldPos)
1455
                {
1456
                        selFirst = newPos;
1457
                        return;
1458
                }
1459
                // can't extend, fall through
1460
        }
1461
        // no previous selection
1462
        if (newPos > oldPos)
1463
        {
1464
                selFirst = oldPos;
1465
                selLast = newPos - 1;
1466
        }
1467
        else
1468
        {
1469
                selFirst = newPos;
1470
                selLast = oldPos - 1;
1471
        }
1472
}
1473
 
4669 avox 1474
void StoryText::selectAll()
1475
{
5721 avox 1476
/*      StoryText* that = const_cast<StoryText *>(this);
1477
        that->at(0);
1478
        for (int i=0; i < length(); ++i) {
1479
                that->current()->cselect = true;
1480
                that->next();
1481
        }
1482
*/
4669 avox 1483
        selFirst = 0;
1484
        selLast = length() - 1;
1485
}
1486
 
1487
void StoryText::deselectAll()
1488
{
5721 avox 1489
/*      StoryText* that = const_cast<StoryText *>(this);
1490
        that->at(0);
1491
        for (int i=0; i < length(); ++i) {
1492
                that->current()->cselect = false;
1493
                that->next();
1494
        }
1495
*/     
4669 avox 1496
        selFirst = 0;
1497
        selLast = -1;
1498
}
1499
 
1500
void StoryText::removeSelection()
1501
{
5751 avox 1502
//      qDebug("removeSelection: %d - %d", selFirst, selLast);
4669 avox 1503
        if (selFirst > selLast)
1504
                return;
1505
 
1506
        assert( selFirst >= 0 );
1507
        assert( selLast < length() );
1508
 
1509
        removeChars(selFirst, selLast - selFirst+1);
1510
        deselectAll();
1511
}
1512
 
1513
 
1514
 
1515
void StoryText::invalidateObject(const PageItem * embedded)
1516
{
1517
}
1518
 
1519
void StoryText::invalidateLayout()
1520
{
1521
}
1522
 
1523
void StoryText::invalidateAll()
1524
{
8134 avox 1525
        d->pstyleContext.invalidate();
7958 avox 1526
        invalidate(0, nrOfItems());
4669 avox 1527
}
1528
 
7958 avox 1529
void StoryText::invalidate(int firstItem, int endItem)
4669 avox 1530
{
7958 avox 1531
        for (int i=firstItem; i < endItem; ++i) {
1532
                ParagraphStyle* par = item(i)->parstyle;
1533
                if (par)
8134 avox 1534
                        par->charStyleContext()->invalidate();
7958 avox 1535
        }
13148 pierre 1536
        emit changed();
4669 avox 1537
}
1538
 
1539
 
1540
// physical view
1541
 
1542
/*
1543
void StoryText::validate()
1544
{
1545
        static bool withinValidate = false;
1546
 
1547
        assert( !withinValidate );
1548
        withinValidate = true;
1549
 
1550
        withinValidate = false;
1551
}
1552
*/
1553
 
1554
int StoryText::screenToPosition(FPoint coord) const
1555
{
13161 malex 1556
        qreal maxx = coord.x() - 1.0;
6144 avox 1557
        for (unsigned int i=0; i < lines(); ++i)
1558
        {
1559
                LineSpec ls = line(i);
13085 jghali 1560
//              qDebug() << QString("screenToPosition: (%1,%2) -> y %3 - %4 + %5").arg(coord.x()).arg(coord.y()).arg(ls.y).arg(ls.ascent).arg(ls.descent);
6144 avox 1561
                if (ls.y + ls.descent < coord.y())
1562
                        continue;
13161 malex 1563
                qreal xpos = ls.x;
17422 fschmid 1564
                for (int j = ls.firstItem; j <= ls.lastItem; ++j)
1565
                {
13085 jghali 1566
//                              qDebug() << QString("screenToPosition: (%1,%2) -> x %3 + %4").arg(coord.x()).arg(coord.y()).arg(xpos).arg(item(j)->glyph.wide());
13161 malex 1567
                        qreal width = item(j)->glyph.wide();
7501 avox 1568
                        xpos += width;
1569
                        if (xpos >= coord.x())
17422 fschmid 1570
                        {
1571
                                if (hasObject(j))
1572
                                        return j;
1573
                                else
1574
                                        return xpos - width/2 > coord.x() ? j : j+1;
1575
                        }
6144 avox 1576
                }
7501 avox 1577
                if (xpos > maxx)
1578
                        maxx = xpos;
1579
                if (xpos + 1.0 > coord.x()) // allow 1pt after end of line
1580
                        return ls.lastItem + 1;
13054 pierre 1581
                else if (coord.x() <= ls.x + ls.width) // last line of paragraph?
15406 jghali 1582
                        return ((ls.lastItem == lastFrameItem) ? (ls.lastItem + 1) : ls.lastItem);
7501 avox 1583
                else if (xpos < ls.x + 0.01 && maxx >= coord.x()) // check for empty line
1584
                        return ls.firstItem;
6144 avox 1585
        }
8562 jghali 1586
        return qMax(lastFrameItem+1, firstFrameItem);
4669 avox 1587
}
1588
 
1589
 
6144 avox 1590
FRect StoryText::boundingBox(int pos, uint len) const
4669 avox 1591
{
6144 avox 1592
        FRect result;
8922 avox 1593
        LineSpec ls;
6144 avox 1594
        for (uint i=0; i < lines(); ++i)
1595
        {
8922 avox 1596
                ls = line(i);
6144 avox 1597
                if (ls.lastItem < pos)
1598
                        continue;
1599
                if (ls.firstItem <= pos) {
9165 avox 1600
                        /*
1601
                        if (ls.lastItem == pos && (item(pos)->effects() & ScStyle_SuppressSpace)  )
8922 avox 1602
                        {
1603
                                if (i+1 < lines())
1604
                                {
1605
                                        ls = line(i+1);
1606
                                        result.setRect(ls.x, ls.y - ls.ascent, 1, ls.ascent + ls.descent);
1607
                                }
1608
                                else
1609
                                {
1610
                                        ls = line(lines()-1);
8931 cbradney 1611
                                        const ParagraphStyle& pstyle(paragraphStyle(pos));
1612
                                        result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
8922 avox 1613
                                }
1614
                        }
9165 avox 1615
                        else */
8922 avox 1616
                        {
13161 malex 1617
                                qreal xpos = ls.x;
8922 avox 1618
                                for (int j = ls.firstItem; j < pos; ++j)
12309 fschmid 1619
                                {
16600 jghali 1620
                                        if (hasObject(j))
17422 fschmid 1621
                                                xpos += (object(j)->width() + object(j)->lineWidth()) * item(j)->glyph.scaleH;
12309 fschmid 1622
                                        else
1623
                                                xpos += item(j)->glyph.wide();
1624
                                }
13161 malex 1625
                                qreal finalw = 1;
16600 jghali 1626
                                if (hasObject(pos))
17422 fschmid 1627
                                        finalw = (object(pos)->width() + object(pos)->lineWidth()) * item(pos)->glyph.scaleH;
12309 fschmid 1628
                                else
1629
                                        finalw = item(pos)->glyph.wide();
13061 pierre 1630
                                const CharStyle& cs(charStyle(pos));
13161 malex 1631
                                qreal desc = -cs.font().descent(cs.fontSize() / 10.0);
1632
                                qreal asce = cs.font().ascent(cs.fontSize() / 10.0);
13061 pierre 1633
                                result.setRect(xpos, ls.y - asce, pos < length()? finalw : 1, desc+asce);
8922 avox 1634
                        }
9165 avox 1635
                        return result;
6144 avox 1636
                }
1637
        }
12796 pierre 1638
        const ParagraphStyle& pstyle(paragraphStyle(qMin(pos, length()))); // rather the trailing style than a segfault.
9165 avox 1639
        if (lines() > 0)
1640
        {
1641
                ls = line(lines()-1);          
1642
                result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
1643
        }
1644
        else
1645
        {
1646
                result.setRect(1, 1, 1, pstyle.lineSpacing());
1647
        }      
6144 avox 1648
        return result;
4669 avox 1649
}
1650
 
1651
int StoryText::layout(int startItem)
1652
{
1653
        //FIXME:NLS
1654
        return -1;
1655
}
1656
 
1657
 
1658
uint StoryText::nrOfItems() const
1659
{
1660
        return length();
1661
}
1662
 
1663
 
1664
ScText*  StoryText::item(uint itm)
1665
{
1666
        assert( static_cast<int>(itm) < length() );
5721 avox 1667
        return const_cast<StoryText *>(this)->d->at(itm);
4669 avox 1668
}
1669
 
6144 avox 1670
 
1671
const ScText*  StoryText::item(uint itm) const
1672
{
1673
        assert( static_cast<int>(itm) < length() );
1674
        return const_cast<StoryText *>(this)->d->at(itm);
1675
}
1676
 
7442 avox 1677
 
4669 avox 1678
const QString StoryText::itemText(uint itm) const
1679
{
1680
 
1681
        assert( static_cast<int>(itm) < length() );
1682
 
1683
        return text(itm, 1);
1684
}
1685
 
1686
 
1687
const CharStyle StoryText::itemStyle(uint itm) const
1688
{
1689
        assert( static_cast<int>(itm) < length() );
1690
 
1691
        return charStyle(itm);
1692
}
1693
 
1694
 
1695
int StoryText::startOfItem(uint itm) const
1696
{
1697
        assert( static_cast<int>(itm) < length() );
1698
 
1699
        return itm;
1700
}
1701
 
1702
int StoryText::endOfItem(uint itm) const
1703
{
1704
        assert( static_cast<int>(itm) < length() );
1705
 
1706
        return itm + 1;
1707
}
1708
 
8194 avox 1709
 
8266 avox 1710
using namespace desaxe;
8194 avox 1711
 
8800 jghali 1712
void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
8194 avox 1713
{
1714
        Xml_attr empty;
1715
        Xml_attr pageno;
1716
        pageno.insert("name", "pgno");
11713 fschmid 1717
        Xml_attr pageco;
1718
        pageco.insert("name", "pgco");
8194 avox 1719
 
8266 avox 1720
        handler.begin(elemtag, empty);
1721
        defaultStyle().saxx(handler, "defaultstyle");
8194 avox 1722
 
8398 avox 1723
        CharStyle lastStyle(charStyle(0));
8194 avox 1724
        bool lastWasPar = true;
1725
        int lastPos = 0;
8398 avox 1726
        handler.begin("p", empty);
1727
        paragraphStyle(0).saxx(handler);
1728
        handler.begin("span", empty);
1729
        lastStyle.saxx(handler);
8194 avox 1730
        for (int i=0; i < length(); ++i)
1731
        {
1732
                const QChar curr(text(i));
1733
                const CharStyle& style(charStyle(i));
1734
 
1735
                if (curr == SpecialChars::OBJECT ||
1736
                        curr == SpecialChars::TAB ||
1737
                        curr == SpecialChars::PARSEP ||
1738
                        curr == SpecialChars::LINEBREAK ||
1739
                        curr == SpecialChars::COLBREAK ||
1740
                        curr == SpecialChars::FRAMEBREAK ||
1741
                        curr == SpecialChars::PAGENUMBER ||
11713 fschmid 1742
                        curr == SpecialChars::PAGECOUNT ||
8194 avox 1743
                        curr.unicode() < 32 ||
1744
                        (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1745
                        curr.unicode() == 0xfffe || curr.unicode() == 0xffff ||
1746
                        style != lastStyle)
1747
                {
1748
                        // something new, write pending chars
1749
                        if  (i - lastPos > 0)
1750
                        {
15009 jghali 1751
                                handler.chars(textWithSoftHyphens(lastPos, i-lastPos));
8194 avox 1752
                        }
1753
                        lastPos = i;
1754
                }
1755
 
1756
                lastWasPar = (curr == SpecialChars::PARSEP);
1757
                if (lastWasPar)
1758
                {
8398 avox 1759
                        handler.end("span");
1760
                        handler.end("p");
1761
                        handler.begin("p", empty);
8913 avox 1762
                        paragraphStyle(i+1).saxx(handler);
8398 avox 1763
                        handler.begin("span", empty);
12962 jghali 1764
                        lastStyle.erase();
8194 avox 1765
                }
16600 jghali 1766
                else if (this->hasObject(i))
8194 avox 1767
                {
1768
                        object(i)->saxx(handler);
1769
                }
1770
                else if (curr == SpecialChars::TAB)
1771
                {
8266 avox 1772
                        handler.beginEnd("tab", empty);
8194 avox 1773
                }
1774
                else if (curr == SpecialChars::LINEBREAK)
1775
                {
8266 avox 1776
                        handler.beginEnd("breakline", empty);
8194 avox 1777
                }
1778
                else if (curr == SpecialChars::COLBREAK)
1779
                {
8266 avox 1780
                        handler.beginEnd("breakcol", empty);
8194 avox 1781
                }
1782
                else if (curr == SpecialChars::FRAMEBREAK)
1783
                {
8266 avox 1784
                        handler.beginEnd("breakframe", empty);
8194 avox 1785
                }
1786
                else if (curr == SpecialChars::PAGENUMBER)
1787
                {
8266 avox 1788
                        handler.beginEnd("var", pageno);
8194 avox 1789
                }
11713 fschmid 1790
                else if (curr == SpecialChars::PAGECOUNT)
1791
                {
1792
                        handler.beginEnd("var", pageco);
1793
                }
8194 avox 1794
                else if (curr.unicode() < 32 ||
1795
                                 (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1796
                                 curr.unicode() == 0xfffe || curr.unicode() == 0xffff)
1797
                {
1798
                        Xml_attr unic;
8266 avox 1799
                        unic.insert("code", toXMLString(curr.unicode()));
1800
                        handler.beginEnd("unicode", unic);
8194 avox 1801
                }
1802
                else if (lastStyle != style)
1803
                {
8398 avox 1804
                        handler.end("span");
1805
                        handler.begin("span", empty);
8194 avox 1806
                        style.saxx(handler);
1807
                        lastStyle = style;
1808
                        continue;
1809
                }
1810
                else
1811
                        continue;
1812
                lastPos = i+1;
1813
        }
1814
 
1815
        if  (length() - lastPos > 0)
15009 jghali 1816
                handler.chars(textWithSoftHyphens(lastPos, length()-lastPos));
8398 avox 1817
        handler.end("span");
1818
        handler.end("p");
8194 avox 1819
 
8398 avox 1820
//      if (!lastWasPar)
1821
//              paragraphStyle(length()-1).saxx(handler);
8194 avox 1822
 
8266 avox 1823
        handler.end(elemtag);
8194 avox 1824
 
1825
}
1826
 
8266 avox 1827
 
1828
class AppendText_body : public Action_body
1829
{
1830
public:
8834 cbradney 1831
        void chars(const Xml_string& txt)
8266 avox 1832
        {
12093 jghali 1833
                QChar chr;
1834
                int   lastPos = 0, len;
8266 avox 1835
                StoryText* obj = this->dig->top<StoryText>();
12093 jghali 1836
                for (int i = 0; i < txt.length(); ++i)
1837
                {
1838
                        chr = txt.at(i);
1839
                        if (chr == SpecialChars::SHYPHEN && i > 0)
1840
                        {
1841
                                int toInsert = i - lastPos;
1842
                                if (toInsert > 0)
1843
                                        obj->insertChars(obj->length(), txt.mid(lastPos, toInsert));
1844
                                len = obj->length();
1845
                                ScText* lastItem = obj->item(len-1);
13161 malex 1846
                                // qreal SHY means user provided SHY, single SHY is automatic one
12093 jghali 1847
                                if (lastItem->effects() & ScStyle_HyphenationPossible)
1848
                                {
1849
                                        lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
1850
                                        obj->insertChars(len, QString(chr));
1851
                                }
1852
                                else
1853
                                {
1854
                                        lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
1855
                                }
1856
                                lastPos = i + 1;
1857
                        }
1858
                }
1859
                if (lastPos < txt.length())
1860
                {
1861
                        QString ins = (lastPos == 0) ? txt : txt.right(txt.length() - lastPos);
1862
                        len = obj->length();
1863
                        obj->insertChars(len, ins);
1864
                }
8266 avox 1865
        }
1866
};
1867
 
1868
struct  AppendText : public MakeAction<AppendText_body>
1869
{};
1870
 
1871
 
1872
class AppendSpecial_body : public Action_body
1873
{
1874
public:
1875
        AppendSpecial_body(QChar sp) : chr(sp) {}
1876
 
8834 cbradney 1877
        void begin(const Xml_string& tag, Xml_attr attr)
8266 avox 1878
        {
1879
                StoryText* obj = this->dig->top<StoryText>();
1880
                Xml_attr::iterator code = attr.find("code");
11713 fschmid 1881
                Xml_attr::iterator name = attr.find("name");
8266 avox 1882
                if (tag == "unicode" && code != attr.end())
1883
                        obj->insertChars(-1, QChar(parseUInt(Xml_data(code))));
11713 fschmid 1884
                else if (tag == "var" && name != attr.end())
1885
                {
1886
                        if (Xml_data(name) == "pgno")
1887
                                obj->insertChars(-1, SpecialChars::PAGENUMBER);
1888
                        else
1889
                                obj->insertChars(-1, SpecialChars::PAGECOUNT);
1890
                }
8266 avox 1891
                else
1892
                        obj->insertChars(-1, chr);
1893
        }
1894
private:
1895
        QChar chr;
1896
};
1897
 
1898
struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar>
1899
{
1900
        AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {}
1901
        AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {}
1902
};
1903
 
1904
 
1905
class AppendInlineFrame_body : public Action_body
1906
{
1907
public:
8834 cbradney 1908
        void end(const Xml_string& tag) // this could be a setter if we had StoryText::appendObject() ...
8266 avox 1909
        {
1910
                StoryText* story = this->dig->top<StoryText>(1);
1911
                PageItem* obj = this->dig->top<PageItem>(0);
14086 jghali 1912
                // FIXME : currently inline objects are added to doc items when parsing
1913
                // We have to remove them from object list and add them to the FrameItems list
1914
                // before inserting them in story object
1915
                QList<PageItem*> *items = obj->doc()->Items;
1916
                if (items->count() > 0)
1917
                {
1918
                        if (items->last() == obj) // try a fast path first
1919
                                items->removeLast();
1920
                        else
1921
                                items->removeAll(obj);
1922
                }
17407 fschmid 1923
                story->insertObject(-1, obj->doc()->addToInlineFrames(obj));
8266 avox 1924
        }
1925
};
1926
 
1927
struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body>
1928
{};
1929
 
8398 avox 1930
/*
8266 avox 1931
class ApplyStyle_body : public Action_body
1932
{
1933
public:
8834 cbradney 1934
        void end(const Xml_string& tag)
8266 avox 1935
        {
13085 jghali 1936
                qDebug() << "storytext desaxe: apply style";
8266 avox 1937
                StoryText* story = this->dig->top<StoryText>(1);
1938
                ParagraphStyle* obj = this->dig->top<ParagraphStyle>(0);
1939
                story->applyStyle(-1, *obj);
1940
        }
1941
};
1942
 
1943
struct ApplyStyle : public MakeAction<ApplyStyle_body>
1944
{};
1945
 
1946
 
8289 avox 1947
class ApplyCharStyle_body : public Action_body
1948
{
1949
public:
1950
        ApplyCharStyle_body() : storyTag(StoryText::saxxDefaultElem), lastPos(0), lastStyle()
1951
        {}
1952
        ApplyCharStyle_body(const Xml_string& tag) : storyTag(tag), lastPos(0), lastStyle()
1953
        {}
1954
 
8834 cbradney 1955
        void end(const Xml_string& tag)
8289 avox 1956
        {
13085 jghali 1957
                qDebug() << "storytext desaxe: apply charstyle";
8289 avox 1958
                if (tag == CharStyle::saxxDefaultElem)
1959
                {
1960
                        StoryText* story = this->dig->top<StoryText>(1);
1961
                        CharStyle* obj = this->dig->top<CharStyle>(0);
1962
                        int len = story->length();
1963
                        if (len > lastPos && lastStyle != *obj)
1964
                        {
1965
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1966
                                lastPos = len;
1967
                                lastStyle = *obj;
1968
                        }
1969
                }
1970
                else if (tag == StoryText::saxxDefaultElem)
1971
                {
1972
                        StoryText* story = this->dig->top<StoryText>();
1973
                        int len = story->length();
1974
                        if (len > lastPos)
1975
                        {
1976
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1977
                        }
1978
                }
1979
        }
1980
private:
1981
        Xml_string storyTag;
1982
        int lastPos;
1983
        CharStyle lastStyle;
1984
};
1985
 
1986
struct ApplyCharStyle : public MakeAction<ApplyCharStyle_body, const Xml_string&>
1987
{
1988
        ApplyCharStyle() : MakeAction<ApplyCharStyle_body, const Xml_string&>() {}
1989
        ApplyCharStyle(const Xml_string& tag) : MakeAction<ApplyCharStyle_body, const Xml_string&>(tag) {}
1990
};
1991
 
8398 avox 1992
*/
8289 avox 1993
 
8398 avox 1994
class Paragraph_body : public Action_body
1995
{
1996
public:
13442 jghali 1997
        Paragraph_body() : lastPos(0), numPara(0), lastStyle(NULL)
8398 avox 1998
        {}
1999
 
2000
        ~Paragraph_body()
2001
        {
2002
                if (lastStyle)
2003
                        delete lastStyle;
2004
        }
2005
 
8834 cbradney 2006
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 2007
        {
13671 jghali 2008
                if (tag == "text-content")
8398 avox 2009
                {
13671 jghali 2010
                        numPara = 0;
2011
                }
2012
                else if (tag == "p")
2013
                {
8398 avox 2014
                        StoryText* story = this->dig->top<StoryText>();
13085 jghali 2015
//                      qDebug() << QString("startpar: %1->%2 %3->NULL").arg(lastPos).arg(story->length()).arg((ulong)lastStyle);
13445 jghali 2016
                        lastPos = story->length();
13442 jghali 2017
                        if (numPara > 0) {
8398 avox 2018
                                story->insertChars(-1, SpecialChars::PARSEP);
8455 avox 2019
                                ++lastPos;
2020
                        }
8398 avox 2021
                        if (lastStyle)
2022
                                delete lastStyle;
2023
                        lastStyle = NULL;
2024
                }
2025
        }
2026
 
8834 cbradney 2027
        void end(const Xml_string& tag)
8398 avox 2028
        {
2029
                if (tag == ParagraphStyle::saxxDefaultElem)
2030
                {
2031
                        if (lastStyle)
2032
                                delete lastStyle;
2033
                        lastStyle = this->dig->top<ParagraphStyle>(0);
13085 jghali 2034
//                      qDebug() << QString("endstyle: %1 %2 %3").arg("?").arg(lastPos).arg((ulong)lastStyle);
8398 avox 2035
                }
2036
                else if (tag == "p")
2037
                {
2038
                        StoryText* story = this->dig->top<StoryText>();
2039
                        int len = story->length();
13085 jghali 2040
//                      qDebug() << QString("endpar: %1 %2 %3 %4").arg(len).arg(lastPos).arg((ulong)lastStyle).arg(lastStyle? lastStyle->parent() : QString());
13445 jghali 2041
                        if (len >= lastPos && lastStyle)
8398 avox 2042
                        {
2043
                                story->applyStyle(lastPos, *lastStyle);
2044
                        }
13442 jghali 2045
                        ++numPara;
8398 avox 2046
                }
2047
        }
2048
private:
2049
        int lastPos;
13442 jghali 2050
        int numPara;
8398 avox 2051
        ParagraphStyle* lastStyle;
2052
};
2053
 
2054
struct Paragraph : public MakeAction<Paragraph_body>
2055
{
2056
        Paragraph() : MakeAction<Paragraph_body>() {}
2057
};
2058
 
2059
 
2060
class SpanAction_body : public Action_body
2061
{
2062
public:
2063
        SpanAction_body() : lastPos(0), lastStyle(NULL)
2064
        {}
2065
 
2066
        ~SpanAction_body()
2067
        {
2068
                if (lastStyle)
2069
                        delete lastStyle;
2070
        }
2071
 
8834 cbradney 2072
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 2073
        {
13085 jghali 2074
//              qDebug() << QString("spanaction: begin %1").arg(tag);
8398 avox 2075
                if (tag == "span")
2076
                {
2077
                        StoryText* story = this->dig->top<StoryText>();
2078
                        lastPos = story->length();
2079
                        if (lastStyle)
2080
                                delete lastStyle;
2081
                        lastStyle = NULL;
2082
                }
2083
        }
2084
 
8834 cbradney 2085
        void end(const Xml_string& tag)
8398 avox 2086
        {
2087
                if (tag == CharStyle::saxxDefaultElem)
13085 jghali 2088
//                      qDebug() << QString("spanaction: end %1").arg(tag);
8398 avox 2089
                {
2090
                        if (lastStyle)
2091
                                delete lastStyle;
2092
                        lastStyle = this->dig->top<CharStyle>(0);
2093
                }
8413 avox 2094
                else if (tag == "span")
8398 avox 2095
                {
2096
                        StoryText* story = this->dig->top<StoryText>();
2097
                        int len = story->length();
2098
                        if (len > lastPos && lastStyle)
2099
                        {
2100
                                story->applyCharStyle(lastPos, len - lastPos, *lastStyle);
2101
                        }
2102
                }
2103
        }
2104
private:
2105
        int lastPos;
2106
        CharStyle* lastStyle;
2107
};
2108
 
2109
struct SpanAction : public MakeAction<SpanAction_body>
2110
{
2111
        SpanAction() : MakeAction<SpanAction_body>() {}
2112
};
2113
 
2114
 
8266 avox 2115
const Xml_string StoryText::saxxDefaultElem("story");
2116
 
8834 cbradney 2117
void StoryText::desaxeRules(const Xml_string& prefixPattern, Digester& ruleset, Xml_string elemtag)
8266 avox 2118
{
2119
        Xml_string storyPrefix(Digester::concat(prefixPattern, elemtag));
2120
        ruleset.addRule(storyPrefix, Factory<StoryText>());
2121
 
2122
        ParagraphStyle::desaxeRules(storyPrefix, ruleset, "defaultstyle");
2123
        ruleset.addRule(Digester::concat(storyPrefix, "defaultstyle"), SetterWithConversion<StoryText, const ParagraphStyle&, ParagraphStyle>( & StoryText::setDefaultStyle ));
2124
 
8398 avox 2125
        Paragraph paraAction;
2126
        Xml_string paraPrefix(Digester::concat(storyPrefix, "p"));
13671 jghali 2127
        ruleset.addRule(storyPrefix, paraAction ); // needed to initialize some variables
11713 fschmid 2128
        ruleset.addRule(paraPrefix, paraAction );
8398 avox 2129
        ParagraphStyle::desaxeRules(paraPrefix, ruleset, ParagraphStyle::saxxDefaultElem);
11713 fschmid 2130
        ruleset.addRule(Digester::concat(paraPrefix, ParagraphStyle::saxxDefaultElem), paraAction );
8266 avox 2131
 
8398 avox 2132
        SpanAction spanAction;
2133
        Xml_string spanPrefix(Digester::concat(paraPrefix, "span"));
2134
        ruleset.addRule(spanPrefix, spanAction );
2135
        CharStyle::desaxeRules(spanPrefix, ruleset, CharStyle::saxxDefaultElem);
2136
        ruleset.addRule(Digester::concat(spanPrefix, CharStyle::saxxDefaultElem), spanAction );
2137
 
2138
        ruleset.addRule(spanPrefix, AppendText());
8266 avox 2139
 
8398 avox 2140
        ruleset.addRule(Digester::concat(spanPrefix, "breakline"), AppendSpecial(SpecialChars::LINEBREAK) );
2141
        ruleset.addRule(Digester::concat(spanPrefix, "breakcol"), AppendSpecial(SpecialChars::COLBREAK) );
2142
        ruleset.addRule(Digester::concat(spanPrefix, "breakframe"), AppendSpecial(SpecialChars::FRAMEBREAK) );
11713 fschmid 2143
        ruleset.addRule(Digester::concat(spanPrefix, "tab"), AppendSpecial(SpecialChars::TAB) );
2144
        ruleset.addRule(Digester::concat(spanPrefix, "unicode"), AppendSpecial() );
2145
        ruleset.addRule(Digester::concat(spanPrefix, "var"), AppendSpecial());
8266 avox 2146
 
2147
        //PageItem::desaxeRules(storyPrefix, ruleset); argh, that would be recursive!
11713 fschmid 2148
        ruleset.addRule(Digester::concat(spanPrefix, "item"), AppendInlineFrame() );
8266 avox 2149
 
2150
}