Subversion Repositories Scribus

Rev

Rev 12340 | 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
 
24
 
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"
8520 cbradney 33
//#include "storytext.moc"
4669 avox 34
#include "scribus.h"
5184 avox 35
#include "util.h"
8455 avox 36
#include "resourcecollection.h"
8266 avox 37
#include "desaxe/saxiohelper.h"
38
#include "desaxe/digester.h"
39
#include "desaxe/simple_actions.h"
4669 avox 40
 
41
 
7970 cbradney 42
StoryText::StoryText(ScribusDoc * doc_) : doc(doc_)
5753 jghali 43
{
12340 pierre 44
 
45
        d = new ScText_Shared();
46
 
7002 avox 47
        selFirst = 0;
48
        selLast = -1;
49
 
50
        firstFrameItem = 0;
51
        lastFrameItem = -1;
7501 avox 52
        m_magicX = 0.0;
53
        m_lastMagicPos = -1;
7002 avox 54
 
55
        d->len = 0;
56
        invalidateAll();
4669 avox 57
}
58
 
7970 cbradney 59
StoryText::StoryText() : doc(NULL)
6144 avox 60
{
12340 pierre 61
        d = new ScText_Shared();
7501 avox 62
 
63
        selFirst = 0;
64
        selLast = -1;
65
 
66
        firstFrameItem = 0;
67
        lastFrameItem = -1;
68
        m_magicX = 0.0;
69
        m_lastMagicPos = -1;
6144 avox 70
}
71
 
8337 avox 72
StoryText::StoryText(const StoryText & other) : QObject(), SaxIO(), doc(other.doc)
4669 avox 73
{
5721 avox 74
        d = other.d;
75
        d->refs++;
76
 
4706 avox 77
        selFirst = 0;
78
        selLast = -1;
79
 
80
        firstFrameItem = 0;
81
        lastFrameItem = -1;
7501 avox 82
        m_magicX = 0.0;
83
        m_lastMagicPos = -1;
5721 avox 84
 
4669 avox 85
        invalidateLayout();
86
}
87
 
88
StoryText::~StoryText()
89
{
9476 fschmid 90
/* Code below is not needed imho, as all connections will be disconnected automatically
91
        by the QObject destructor. At least removing that code fixes a crash when closing
92
        documents */
93
/*      if (doc)
7958 avox 94
        {
95
                doc->paragraphStyles().disconnect(this, SLOT(invalidateAll()));
96
                doc->charStyles().disconnect(this, SLOT(invalidateAll()));
9476 fschmid 97
        } */
5721 avox 98
        d->refs--;
99
        if (d->refs == 0) {
5773 avox 100
                d->clear();
101
                d->len = 0;
5721 avox 102
                delete d;
103
        }      
4669 avox 104
}
105
 
6144 avox 106
StoryText StoryText::copy() const
107
{
108
        StoryText result(doc);
109
        *(result.d) = *d;
7786 avox 110
        return result;
6735 avox 111
//      qDebug(QString("StoryText::copy:"));
10366 jghali 112
        QListIterator<ScText*> it( *(result.d) );
6144 avox 113
        ScText* elem;
10366 jghali 114
        while ( it.hasNext() ) {
115
                elem = it.next();
6735 avox 116
//              qDebug(QString("\tchar '%1' size %2 (orig %3)").arg(elem->ch).arg(elem->fontSize()).arg(charStyle(i++).fontSize()));
6144 avox 117
        }
118
 
119
        return result;
120
}
121
 
122
 
4669 avox 123
StoryText& StoryText::operator= (const StoryText & other)
124
{
7786 avox 125
        other.d->refs++;
126
 
5721 avox 127
        d->refs--;
128
        if (d->refs == 0) {
129
                clear();
130
                delete d;
131
        }
132
 
7958 avox 133
 
5721 avox 134
        doc = other.doc;
135
        d = other.d;
136
 
7958 avox 137
 
4706 avox 138
        selFirst = 0;
139
        selLast = -1;
140
 
141
        firstFrameItem = 0;
142
        lastFrameItem = -1;
143
 
4669 avox 144
        invalidateLayout();
145
        return *this;
146
}
147
 
148
 
149
void StoryText::clear()
150
{
151
        selFirst = 0;
152
        selLast = -1;
153
 
154
        firstFrameItem = 0;
155
        lastFrameItem = -1;
156
 
12340 pierre 157
//      d->defaultStyle.erase();
158
//      d->trailingStyle.erase();
5569 avox 159
 
5721 avox 160
        d->clear();
5722 avox 161
        d->len = 0;
4669 avox 162
        invalidateAll();
163
}
164
 
165
 
6887 avox 166
void StoryText::insert(int pos, const StoryText& other, bool onlySelection)
5732 avox 167
{
8049 avox 168
        if (pos < 0)
169
                pos += length()+1;
170
 
12337 pierre 171
        QString cstyle(charStyle(pos));
172
        QString pstyle(paragraphStyle(pos));
7242 avox 173
 
174
        // this style represents all differences between this and other's defaultstyles
12337 pierre 175
        QString otherDefault(other.defaultStyle());
12340 pierre 176
        StylesLibrary::BaseStyle( otherDefault )->eraseStyle( defaultStyle() );
7242 avox 177
 
6887 avox 178
        int otherStart  = onlySelection? other.startOfSelection() : 0;
179
        int otherEnd    = onlySelection? other.endOfSelection() : other.length();
180
        int cstyleStart = otherStart;
181
        for (int i=otherStart; i < otherEnd; ++i) {
182
                if (other.charStyle(i) == cstyle
183
                        && other.text(i) != SpecialChars::OBJECT
184
                        && other.text(i) != SpecialChars::PARSEP)
5732 avox 185
                        continue;
186
                int len = i - cstyleStart;
187
                if (len > 0) {
12093 jghali 188
                        insertCharsWithSmartHyphens(pos, other.textWithSmartHyphens(cstyleStart, len));
12340 pierre 189
                        applyCharStyle(pos, len, otherDefault);
5732 avox 190
                        applyCharStyle(pos, len, cstyle);
6887 avox 191
                        pos += len;
5732 avox 192
                }
193
                if (other.text(i) == SpecialChars::PARSEP) {
6887 avox 194
                        insertChars(pos, SpecialChars::PARSEP);
12056 jghali 195
                        //#5845 : disable for now as it has nasty side effects when linking frames
196
                        //applyStyle(pos, otherDefault);
6887 avox 197
                        applyStyle(pos, other.paragraphStyle(i));
5732 avox 198
                        cstyleStart = i+1;
6887 avox 199
                        pos += 1;
5732 avox 200
                }
6887 avox 201
                else if (other.text(i) == SpecialChars::OBJECT) {
8174 avox 202
                        insertChars(pos, SpecialChars::OBJECT);
203
                        item(pos)->embedded = other.item(i)->embedded;
6887 avox 204
                        cstyleStart = i+1;
205
                        pos += 1;
206
                }
5732 avox 207
                else {
208
                        cstyle = other.charStyle(i);
209
                        cstyleStart = i;
210
                }
211
        }
6887 avox 212
        int len = otherEnd - cstyleStart;
5732 avox 213
        if (len > 0) {
12093 jghali 214
                insertCharsWithSmartHyphens(pos, other.textWithSmartHyphens(cstyleStart, len));
12340 pierre 215
                applyCharStyle(pos, len, otherDefault);
5732 avox 216
                applyCharStyle(pos, len, cstyle);
7232 avox 217
                pos += len;
7256 avox 218
                if (other.text(otherEnd-1) != SpecialChars::PARSEP) {
12056 jghali 219
                        //#5845 : disable for now as it has nasty side effects when linking frames
220
                        //applyStyle(pos, otherDefault);
7256 avox 221
                        applyStyle(pos, other.paragraphStyle(otherEnd-1));
222
                }
5732 avox 223
        }
8049 avox 224
        invalidate(pos, length());
5732 avox 225
}
226
 
227
 
5721 avox 228
/**
229
    Make sure that the paragraph CharStyle's point to the new ParagraphStyle
230
 */
8174 avox 231
void StoryText::insertParSep(int pos)
5721 avox 232
{
8174 avox 233
        ScText* it = item_p(pos);
12340 pierre 234
//      if(!it->parstyle) {
235
                it->parstyle = paragraphStyle(pos+1);
236
//              it->parstyle->setContext( & d->pstyleContext);
8194 avox 237
//              it->parstyle->setName("para"); // DONT TRANSLATE
238
//              it->parstyle->charStyle().setName("cpara"); // DONT TRANSLATE
8134 avox 239
//              it->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
12340 pierre 240
//      }
241
//      d->replaceCharStyleContextInParagraph(pos, it->parstyle->charStyleContext());
5721 avox 242
}
243
/**
244
     need to remove the ParagraphStyle structure and replace all pointers
245
     to it...
246
 */
8174 avox 247
void StoryText::removeParSep(int pos)
5721 avox 248
{
8174 avox 249
        ScText* it = item_p(pos);
12340 pierre 250
//      if (it->parstyle) {
6733 avox 251
//              const CharStyle* oldP = & it->parstyle->charStyle();
252
//              const CharStyle* newP = & that->paragraphStyle(pos+1).charStyle();
253
//              d->replaceParentStyle(pos, oldP, newP);
12340 pierre 254
//              delete it->parstyle;
255
                it->parstyle = "";
256
//      }
8134 avox 257
        // demote this parsep so the assert code in replaceCharStyleContextInParagraph()
6904 avox 258
        // doesnt choke:
9737 jghali 259
        it->ch = 0;
12340 pierre 260
//      d->replaceCharStyleContextInParagraph(pos, paragraphStyle(pos+1).charStyleContext());   
5721 avox 261
}
262
 
4669 avox 263
void StoryText::removeChars(int pos, uint len)
264
{
265
        if (pos < 0)
266
                pos += length();
8049 avox 267
 
4669 avox 268
        assert( len > 0 );
269
        assert( pos >= 0 );
11991 jghali 270
        // This case can be handled more gracefully
271
        // assert( pos + static_cast<int>(len) <= length() );
272
        if (pos >= length())
273
                return;
274
        if (pos + static_cast<int>(len) > length())
275
                len = length() - pos;
4749 avox 276
 
5751 avox 277
        for ( int i=pos + static_cast<int>(len) - 1; i >= pos; --i )
4749 avox 278
        {
5721 avox 279
                ScText *it = d->at(i);
9737 jghali 280
                if ((it->ch == SpecialChars::PARSEP)) {
8174 avox 281
                        removeParSep(i);
4749 avox 282
                }
9737 jghali 283
//              qDebug("remove char %d at %d", (int) it->ch.unicode(), i);
10366 jghali 284
                d->takeAt(i);
5722 avox 285
                d->len--;
4749 avox 286
                delete it;
287
        }
5722 avox 288
 
289
        d->len = d->count();
8049 avox 290
        invalidate(pos, length());
4669 avox 291
}
292
 
9645 avox 293
void StoryText::insertChars(int pos, QString txt, bool applyNeighbourStyle) //, const CharStyle & charstyle)
4669 avox 294
{
295
        if (pos < 0)
8049 avox 296
                pos += length()+1;
4669 avox 297
 
298
        assert(pos >= 0);
299
        assert(pos <= length());
4749 avox 300
 
5365 avox 301
        if (txt.length() == 0)
302
                return;
303
 
9426 avox 304
 
9645 avox 305
        ScText clone;
306
        if (applyNeighbourStyle)
307
        {
10394 cbradney 308
                int referenceChar = qMax(0, qMin(pos, length()-1));
9645 avox 309
                clone.applyCharStyle(charStyle(referenceChar));
310
                clone.setEffects(ScStyle_Default);
311
        }
9607 avox 312
 
8546 cbradney 313
        for (int i = 0; i < txt.length(); ++i) {
9426 avox 314
                ScText * item = new ScText(clone);
9737 jghali 315
                item->ch= txt.at(i);
5721 avox 316
                d->insert(pos + i, item);
5722 avox 317
                d->len++;
9737 jghali 318
                if (item->ch == SpecialChars::PARSEP) {
6735 avox 319
//                      qDebug(QString("new PARSEP %2 at %1").arg(pos).arg(paragraphStyle(pos).name()));
8174 avox 320
                        insertParSep(pos + i);
5721 avox 321
                }
4749 avox 322
        }
323
 
5722 avox 324
        d->len = d->count();
4669 avox 325
        invalidate(pos, pos + txt.length());
326
}
327
 
12093 jghali 328
void StoryText::insertCharsWithSmartHyphens(int pos, QString txt, bool applyNeighbourStyle)
329
{
330
        if (pos < 0)
331
                pos += length()+1;
332
 
333
        assert(pos >= 0);
334
        assert(pos <= length());
335
 
336
        if (txt.length() == 0)
337
                return;
338
 
12343 pierre 339
//      const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
12093 jghali 340
 
341
        ScText clone;
342
        if (applyNeighbourStyle)
343
        {
344
                int referenceChar = qMax(0, qMin(pos, length()-1));
345
                clone.applyCharStyle(charStyle(referenceChar));
346
                clone.setEffects(ScStyle_Default);
347
        }
348
 
349
        int inserted = 0;
350
        for (int i = 0; i < txt.length(); ++i)
351
        {
352
                QChar ch = txt.at(i);
12112 jghali 353
                int  index  = pos + inserted;
12093 jghali 354
                bool insert = true;
12097 jghali 355
                if (ch == SpecialChars::SHYPHEN && index > 0) {
356
                        ScText* lastItem = this->item(index - 1);
12093 jghali 357
                        // double SHY means user provided SHY, single SHY is automatic one
358
                        if (lastItem->effects() & ScStyle_HyphenationPossible)
359
                                lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
360
                        else
361
                        {
362
                                lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
363
                                insert = false;
364
                        }
365
                }
366
                if (insert)
367
                {
368
                        ScText * item = new ScText(clone);
369
                        item->ch = ch;
12343 pierre 370
//                      item->setContext(cStyleContext);
12097 jghali 371
                        d->insert(index, item);
12093 jghali 372
                        d->len++;
373
                        if (item->ch == SpecialChars::PARSEP) {
12097 jghali 374
                                insertParSep(index);
12093 jghali 375
                        }
376
                        ++inserted;
377
                }
378
        }
379
 
380
        d->len = d->count();
381
        invalidate(pos, pos + inserted);
382
}
383
 
4669 avox 384
void StoryText::replaceChar(int pos, QChar ch)
385
{
386
        if (pos < 0)
387
                pos += length();
388
 
389
        assert(pos >= 0);
390
        assert(pos < length());
391
 
5721 avox 392
        ScText* item = d->at(pos);
9737 jghali 393
        if (item->ch == ch)
5721 avox 394
                return;
395
 
9737 jghali 396
        if (d->at(pos)->ch == SpecialChars::PARSEP) {
8174 avox 397
                removeParSep(pos);
5721 avox 398
        }
399
        item->ch = ch;
9737 jghali 400
        if (d->at(pos)->ch == SpecialChars::PARSEP) {
8174 avox 401
                insertParSep(pos);
5721 avox 402
        }
403
 
4669 avox 404
        invalidate(pos, pos + 1);
405
}
406
 
5559 avox 407
void StoryText::hyphenateWord(int pos, uint len, char* hyphens)
408
{
409
        assert(pos >= 0);
5691 avox 410
        assert(pos + signed(len) <= length());
5559 avox 411
 
6940 avox 412
//      QString dump("");
5691 avox 413
        for (int i=pos; i < pos+signed(len); ++i)
6937 avox 414
        {
6940 avox 415
//              dump += d->at(i)->ch;
6937 avox 416
                if(hyphens && hyphens[i-pos] & 1) {
5721 avox 417
                        d->at(i)->setEffects(d->at(i)->effects() | ScStyle_HyphenationPossible);
6940 avox 418
//                      dump += "-";
6937 avox 419
                }
420
                else {
5721 avox 421
                        d->at(i)->setEffects(d->at(i)->effects() & ~ScStyle_HyphenationPossible);
6937 avox 422
                }
423
        }
6940 avox 424
//      qDebug(QString("st: %1").arg(dump));
5559 avox 425
        invalidate(pos, pos + len);
426
}
427
 
4669 avox 428
void StoryText::insertObject(int pos, PageItem* ob)
429
{
430
        if (pos < 0)
8049 avox 431
                pos += length()+1;
4669 avox 432
 
433
        insertChars(pos, SpecialChars::OBJECT);
8174 avox 434
        const_cast<StoryText *>(this)->d->at(pos)->embedded = InlineFrame(ob);
8289 avox 435
        ob->isEmbedded = true;   // this might not be enough...
4669 avox 436
}
437
 
438
 
439
int StoryText::length() const
440
{
5722 avox 441
        return d->len;
4669 avox 442
}
443
 
444
QChar StoryText::text(int pos) const
445
{
446
        if (pos < 0)
447
                pos += length();
448
 
449
        assert(pos >= 0);
450
        assert(pos < length());
451
 
9737 jghali 452
        return const_cast<StoryText *>(this)->d->at(pos)->ch;
4669 avox 453
}
454
 
455
QString StoryText::text(int pos, uint len) const
456
{
457
        if (pos < 0)
458
                pos += length();
459
 
460
        assert(pos >= 0);
461
        assert(pos + signed(len) <= length());
462
 
463
        QString result;
5721 avox 464
        StoryText* that(const_cast<StoryText*>(this));
465
        for (int i = pos; i < pos+signed(len); ++i) {
10366 jghali 466
                result += that->d->at(i)->ch;
5721 avox 467
        }
4669 avox 468
 
469
        return result;
470
}
471
 
12093 jghali 472
QString StoryText::textWithSmartHyphens(int pos, uint len) const
473
{
474
        QString result("");
475
        int lastPos = pos;
476
 
477
        len = qMin((uint) (length() - pos), len);
478
        for (int i = pos; i < pos+signed(len); ++i)
479
        {
12343 pierre 480
                if (StylesLibrary::Char( this->charStyle(i) )->effects() & ScStyle_HyphenationPossible
12093 jghali 481
                        // duplicate SHYPHEN if already present to indicate a user provided SHYPHEN:
482
                        || this->text(i) == SpecialChars::SHYPHEN)
483
                {
484
                        result += text(lastPos, i + 1 - lastPos);
485
                        result += SpecialChars::SHYPHEN;
486
                        lastPos = i+1;
487
                }
488
        }
489
        if (lastPos < pos+signed(len))
490
                result += text(lastPos, pos+signed(len) - lastPos);
491
        return result;
492
}
493
 
4669 avox 494
PageItem* StoryText::object(int pos) const
495
{
496
        if (pos < 0)
497
                pos += length();
498
 
499
        assert(pos >= 0);
500
        assert(pos < length());
501
 
8174 avox 502
        StoryText* that = const_cast<StoryText *>(this);
503
        return that->d->at(pos)->embedded.getItem();
4669 avox 504
}
505
 
506
 
12343 pierre 507
const QString StoryText::charStyle(int pos) const
4669 avox 508
{
509
        if (pos < 0)
510
                pos += length();
511
 
512
        assert(pos >= 0);
5732 avox 513
        assert(pos <= length());
4669 avox 514
 
6144 avox 515
        if (length() == 0) {
6735 avox 516
//              qDebug("storytext::charstyle: default");
12343 pierre 517
                return defaultStyle();
6144 avox 518
        }
519
        else if (pos == length()) {
10394 cbradney 520
                qDebug("storytext::charstyle: access at end of text %i", pos);
5732 avox 521
                --pos;
6144 avox 522
        }
8956 avox 523
        if (text(pos) == SpecialChars::PARSEP)
12343 pierre 524
        {
525
                QString r(StylesLibrary::Paragraph( paragraphStyle(pos) )->charStyle());
526
                return r;
527
        }
5732 avox 528
 
4669 avox 529
        StoryText* that = const_cast<StoryText *>(this);
12343 pierre 530
        that->d->at(pos)->uuid();
4669 avox 531
}
532
 
12343 pierre 533
const QString StoryText::paragraphStyle(int pos) const
4669 avox 534
{
535
        if (pos < 0)
536
                pos += length();
537
 
538
        assert(pos >= 0);
5721 avox 539
        assert(pos <= length());
4669 avox 540
 
6733 avox 541
        assert(d);
542
 
5192 avox 543
        StoryText * that = const_cast<StoryText *> (this);
5721 avox 544
//      assert( that->at(pos)->cab >= 0 );
545
//      assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
546
//      return doc->docParagraphStyles[that->at(pos)->cab];
547
 
10366 jghali 548
        while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP) {
5721 avox 549
                ++pos;
550
        }
6909 avox 551
        if (pos >= length()) {
7232 avox 552
                return that->d->trailingStyle;
5778 avox 553
        }
12343 pierre 554
        else if ( StylesLibrary::Paragraph( that->d->at(pos)->parstyle ) ) {
10366 jghali 555
                ScText* current = that->d->at(pos);
10394 cbradney 556
                qDebug("inserting default parstyle at %i", pos);
10366 jghali 557
//              current->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE
558
//              current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE
559
//              current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5721 avox 560
        }
5778 avox 561
        else {
562
//              qDebug(QString("using parstyle at %1").arg(pos));
563
        }
12343 pierre 564
//      assert (that->d->at(pos)->parstyle);
565
        return that->d->at(pos)->parstyle;
4669 avox 566
}
567
 
12343 pierre 568
const QString StoryText::defaultStyle() const
5559 avox 569
{
6733 avox 570
        assert(d);
5721 avox 571
        return d->defaultStyle;
5559 avox 572
}
573
 
5569 avox 574
 
12337 pierre 575
void StoryText::setDefaultStyle(const QString& style)
5569 avox 576
{
12343 pierre 577
//      const StyleContext * oldPContext = d->defaultStyle.context();
8289 avox 578
//      const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
5721 avox 579
        d->defaultStyle = style;
12343 pierre 580
//      d->defaultStyle.setContext( oldPContext );
8194 avox 581
//      d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE
582
//      d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE
8134 avox 583
//      qDebug(QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
584
//                 .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
585
//                 .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
586
//                 .arg((uint)d->defaultStyle.charStyle().context(),16));
587
//      d->defaultStyle.charStyle().setContext( oldCContext );
7958 avox 588
        invalidateAll();
5569 avox 589
}
590
 
591
 
12337 pierre 592
void StoryText::applyCharStyle(int pos, uint len, const QString& style )
4669 avox 593
{
594
        if (pos < 0)
595
                pos += length();
596
 
11968 fschmid 597
/* Why do we use an assert here instead of a gracefully return doing nothing ????? */
598
//      assert(pos >= 0);
599
//      assert(pos + signed(len) <= length());
600
        if (pos < 0)
601
                return;
602
        if (pos + signed(len) > length())
603
                return;
4669 avox 604
 
605
        if (len == 0)
606
                return;
607
 
10366 jghali 608
        ScText* itText;
5721 avox 609
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 610
                itText = d->at(i);
11936 jghali 611
                // #6165 : applying style on last character applies style on whole text on next open 
612
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
613
                        itText->parstyle->charStyle().applyCharStyle(style);*/
10366 jghali 614
                itText->applyCharStyle(style);
5721 avox 615
        }
4669 avox 616
 
617
        invalidate(pos, pos + len);
618
}
619
 
5773 avox 620
 
621
 
12340 pierre 622
void StoryText::eraseCharStyle(int pos, uint len, const QString& style )
5773 avox 623
{
624
        if (pos < 0)
625
                pos += length();
626
 
627
        assert(pos >= 0);
628
        assert(pos + signed(len) <= length());
629
 
630
        if (len == 0)
631
                return;
632
 
10366 jghali 633
        ScText* itText;
5773 avox 634
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 635
                itText = d->at(i);
636
                if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
12343 pierre 637
                {
638
                        QString cs = StylesLibrary::Paragraph(itText->parstyle)->charStyle();
639
                        StylesLibrary::Char(cs)->eraseCharStyle(style);
640
                }
10366 jghali 641
                itText->eraseCharStyle(style);
5773 avox 642
        }
643
 
644
        invalidate(pos, pos + len);
645
}
646
 
647
 
12337 pierre 648
void StoryText::applyStyle(int pos, const QString& style)
4669 avox 649
{
650
        if (pos < 0)
651
                pos += length();
652
 
653
        assert(pos >= 0);
5721 avox 654
        assert(pos <= length());
4669 avox 655
 
5721 avox 656
        int i = pos;
9737 jghali 657
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5184 avox 658
                ++i;
659
        }
5721 avox 660
        if (i < length()) {
12343 pierre 661
//              if (!d->at(i)->parstyle) {
662
//                      qDebug("PARSEP without style at pos %i", i);
663
//                      d->at(i)->parstyle = new ParagraphStyle();
664
//                      d->at(i)->parstyle->setContext( & d->pstyleContext);
665
// //                   d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE
666
// //                   d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE
667
// //                   d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
668
//              }
5721 avox 669
//              qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
12343 pierre 670
                StylesLibrary::Paragraph(d->at(i)->parstyle)->applyStyle(style);
5721 avox 671
        }
672
        else {
673
                // not happy about this but inserting a new PARSEP makes more trouble
674
//              qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
12343 pierre 675
                StylesLibrary::Paragraph(d->trailingStyle)->applyStyle(style);
5721 avox 676
        }
8562 jghali 677
        invalidate(pos, qMin(i, length()));
4669 avox 678
}
679
 
12340 pierre 680
void StoryText::eraseStyle(int pos, const QString& style)
5773 avox 681
{
682
        if (pos < 0)
683
                pos += length();
684
 
685
        assert(pos >= 0);
686
        assert(pos <= length());
687
 
688
        int i = pos;
9737 jghali 689
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5773 avox 690
                ++i;
691
        }
692
        if (i < length()) {
12343 pierre 693
//              if (!d->at(i)->parstyle) {
694
//                      qDebug("PARSEP without style at pos %i", i);
695
//                      d->at(i)->parstyle = new ParagraphStyle();
696
//                      d->at(i)->parstyle->setContext( & d->pstyleContext);
697
// //                   d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE
698
// //                   d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE
699
// //                   d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
700
//              }
5773 avox 701
                //              qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
12343 pierre 702
                StylesLibrary::Paragraph(d->at(i)->parstyle)->eraseStyle(style);
5773 avox 703
        }
704
        else {
705
                // not happy about this but inserting a new PARSEP makes more trouble
706
                //              qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
12343 pierre 707
                StylesLibrary::Paragraph(d->trailingStyle)->eraseStyle(style);
5773 avox 708
        }
8562 jghali 709
        invalidate(pos, qMin(i, length()));
5773 avox 710
}
711
 
712
 
12337 pierre 713
void StoryText::setStyle(int pos, const QString& style)
6733 avox 714
{
715
        eraseStyle(pos, paragraphStyle(pos));
716
        applyStyle(pos, style);
717
}
718
 
719
 
12343 pierre 720
void StoryText::setCharStyle(int pos, uint len, const QString& style)
6733 avox 721
{
9067 avox 722
        if (pos < 0)
723
                pos += length();
724
 
725
        assert(pos >= 0);
9645 avox 726
        assert(len <= unsigned(length()));
9067 avox 727
        assert(pos + signed(len) <= length());
728
 
729
        if (len == 0)
730
                return;
731
 
10366 jghali 732
        ScText* itText;
9067 avox 733
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 734
                itText = d->at(i);
11936 jghali 735
                // #6165 : applying style on last character applies style on whole text on next open 
736
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
737
                        itText->parstyle->charStyle() = style;*/
10366 jghali 738
                itText->setStyle(style);
9067 avox 739
        }
740
 
741
        invalidate(pos, pos + len);
6733 avox 742
}
743
 
744
 
8455 avox 745
 
746
void StoryText::getNamedResources(ResourceCollection& lists) const
747
{
12343 pierre 748
        StylesLibrary::Paragraph(d->defaultStyle)->getNamedResources(lists);
749
        StylesLibrary::Paragraph(d->trailingStyle)->getNamedResources(lists);
8938 avox 750
 
8455 avox 751
        for (int i=0; i < length(); ++i)
752
        {
753
                if (text(i) == SpecialChars::PARSEP)
12343 pierre 754
                        StylesLibrary::Paragraph(paragraphStyle(i))->getNamedResources(lists);
8455 avox 755
                else if (text(i) == SpecialChars::OBJECT)
756
                        object(i)->getNamedResources(lists);
757
                else
12343 pierre 758
                        StylesLibrary::Char(charStyle(i))->getNamedResources(lists);
8455 avox 759
        }
760
}
761
 
762
 
7442 avox 763
void StoryText::replaceStyles(QMap<QString,QString> newNameForOld)
764
{
8455 avox 765
        ResourceCollection newnames;
8775 cbradney 766
        newnames.mapStyles(newNameForOld);
12343 pierre 767
//      replaceNamedResources(newnames);
8455 avox 768
}
769
 
12343 pierre 770
// // // void StoryText::replaceNamedResources(ResourceCollection& newNames)
771
// // // {
772
// // //        int len = length();
773
// // // 
774
// // //        d->defaultStyle.replaceNamedResources(newNames);
775
// // //        d->trailingStyle.replaceNamedResources(newNames);
776
// // //        
777
// // //        if (len == 0)
778
// // //                return;
779
// // //        
780
// // //        ScText* itText;
781
// // //        for (int i=0; i < len; ++i) {
782
// // //                itText = d->at(i);
783
// // //                if (itText->parstyle)
784
// // //                        itText->parstyle->replaceNamedResources(newNames);
785
// // //                else
786
// // //                        itText->replaceNamedResources(newNames);
787
// // //        }
788
// // //        
789
// // //        invalidate(0, len);     
790
// // // }
8455 avox 791
 
7442 avox 792
 
793
void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
794
{
8455 avox 795
        ResourceCollection newnames;
8775 cbradney 796
        newnames.mapCharStyles(newNameForOld);
12343 pierre 797
//      replaceNamedResources(newnames);
7442 avox 798
}
799
 
8455 avox 800
 
10623 avox 801
int StoryText::nrOfParagraph(uint index) const
802
{
10643 fschmid 803
        int result = 0;
10623 avox 804
        StoryText* that = const_cast<StoryText *>(this);
805
        bool lastWasPARSEP = true;
11359 jghali 806
        index = qMin(index, (uint) that->length());
11229 fschmid 807
        for (uint i=0; i < index; ++i) {
10623 avox 808
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
809
                if (lastWasPARSEP)
810
                        ++result;
811
        }
11380 jghali 812
        return result;
10623 avox 813
}
814
 
4669 avox 815
uint StoryText::nrOfParagraphs() const
816
{
817
        uint result = 0;
5721 avox 818
        StoryText* that = const_cast<StoryText *>(this);
7059 avox 819
        bool lastWasPARSEP = true;
5721 avox 820
        for (int i=0; i < length(); ++i) {
10366 jghali 821
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
6887 avox 822
                if (lastWasPARSEP)
4669 avox 823
                        ++result;
5721 avox 824
        }
11359 jghali 825
        return lastWasPARSEP ? result : result + 1;
4669 avox 826
}
827
 
828
int StoryText::startOfParagraph(uint index) const
829
{
830
        if (index == 0)
831
                return 0;
832
 
5184 avox 833
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 834
        for (int i=0; i < length(); ++i) {
10366 jghali 835
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
4669 avox 836
                        return i + 1;
5721 avox 837
        }
6887 avox 838
        return length();
4669 avox 839
}
840
 
841
int StoryText::endOfParagraph(uint index) const
842
{
843
        ++index;
5184 avox 844
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 845
        for (int i=0; i < length(); ++i) {
10366 jghali 846
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
5721 avox 847
                        return i;
848
        }
4669 avox 849
        return length();
850
}
851
 
852
uint StoryText::nrOfRuns() const
853
{
5464 avox 854
        return length();
4669 avox 855
}
856
 
857
int StoryText::startOfRun(uint index) const
858
{
5464 avox 859
        return index;
4669 avox 860
}
861
 
862
int StoryText::endOfRun(uint index) const
863
{
5464 avox 864
        return index + 1;
4669 avox 865
}
866
 
7501 avox 867
// positioning. all positioning methods return char positions
868
// FIXME: make that methods use correct semantic boundaries
4669 avox 869
 
7501 avox 870
static QString wordBoundaries(" .,:;\"'!?\n");
871
static QString sentenceBoundaries(".:!?\n");
872
 
873
int StoryText::nextChar(int pos)
874
{
875
        if (pos < length())
876
                return pos+1;
877
        else
878
                return length();
879
}
880
int StoryText::prevChar(int pos)
881
{
882
        if (pos > 0)
883
                return pos - 1;
884
        else
885
                return 0;
886
}
887
int StoryText::nextWord(int pos)
888
{
889
        int len = length();
8562 jghali 890
        pos = qMin(len, pos+1);
10394 cbradney 891
        while (pos < len  && wordBoundaries.indexOf(text(pos)) < 0)
7501 avox 892
                ++pos;
893
        return pos < len ? pos + 1 : pos;
894
}
895
int StoryText::prevWord(int pos)
896
{
8562 jghali 897
        pos = qMax(0, pos-1);
10394 cbradney 898
        while (pos > 0 && wordBoundaries.indexOf(text(pos)) < 0)
7501 avox 899
                --pos;
10394 cbradney 900
        return wordBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 901
}
902
int StoryText::nextSentence(int pos)
903
{
904
        int len = length();
8562 jghali 905
        pos = qMin(len, pos+1);
10394 cbradney 906
        while (pos < len && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 907
                ++pos;
908
        return pos < len ? pos + 1 : pos;
909
}
910
int StoryText::prevSentence(int pos)
911
{
8562 jghali 912
        pos = qMax(0, pos-1);
10394 cbradney 913
        while (pos > 0 && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 914
                --pos;
10394 cbradney 915
        return sentenceBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 916
}
917
int StoryText::nextParagraph(int pos)
918
{
919
        int len = length();
8562 jghali 920
        pos = qMin(len, pos+1);
7501 avox 921
        while (pos < len && text(pos) != SpecialChars::PARSEP)
922
                ++pos;
7637 avox 923
        return pos;
7501 avox 924
}
925
int StoryText::prevParagraph(int pos)
926
{
8562 jghali 927
        pos = qMax(0, pos-1);
7501 avox 928
        while (pos > 0 && text(pos) != SpecialChars::PARSEP)
929
                --pos;
7637 avox 930
        return pos;
7501 avox 931
}
932
 
933
// these need valid layout:
934
 
935
int StoryText::startOfLine(int pos)
936
{
8546 cbradney 937
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 938
                const LineSpec & ls(m_lines.at(i));
939
                if (ls.firstItem <= pos && pos <= ls.lastItem)
940
                        return ls.firstItem;
941
        }
942
        return 0;
943
}
944
int StoryText::endOfLine(int pos)
945
{
8546 cbradney 946
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 947
                const LineSpec & ls(m_lines.at(i));
948
                if (ls.firstItem <= pos && pos <= ls.lastItem)
7657 avox 949
                        return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem :
950
                                text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1;
7501 avox 951
        }
952
        return length();
953
}
954
int StoryText::prevLine(int pos)
955
{
8546 cbradney 956
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 957
        {
958
                // find line for pos
959
                const LineSpec & ls(m_lines.at(i));
960
                if (ls.firstItem <= pos && pos <= ls.lastItem)
961
                {
7522 avox 962
                        if (i == 0)
963
                                return startOfLine(pos);
7501 avox 964
                        // find current xpos
965
                        double xpos = 0.0;
966
                        for (int j = ls.firstItem; j < pos; ++j)
967
                                xpos += item(j)->glyph.wide();
968
                        if (pos != m_lastMagicPos || xpos > m_magicX)
969
                                m_magicX = xpos;
970
                        const LineSpec & ls2(m_lines.at(i-1));
971
                        // find new cpos
972
                        xpos = 0.0;
973
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
974
                        {
975
                                xpos += item(j)->glyph.wide();
976
                                if (xpos > m_magicX) {
977
                                        m_lastMagicPos = j;
978
                                        return j;
979
                                }
980
                        }
981
                        m_lastMagicPos = ls2.lastItem;
982
                        return ls2.lastItem;
983
                }
984
        }
985
        return firstFrameItem;
986
}
987
 
988
int StoryText::nextLine(int pos)
989
{
8546 cbradney 990
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 991
        {
992
                // find line for pos
993
                const LineSpec & ls(m_lines.at(i));
994
                if (ls.firstItem <= pos && pos <= ls.lastItem)
995
                {
7522 avox 996
                        if (i+1 == m_lines.count())
997
                                return endOfLine(pos);
7501 avox 998
                        // find current xpos
999
                        double xpos = 0.0;
1000
                        for (int j = ls.firstItem; j < pos; ++j)
1001
                                xpos += item(j)->glyph.wide();
1002
                        if (pos != m_lastMagicPos || xpos > m_magicX)
1003
                                m_magicX = xpos;
1004
                        const LineSpec & ls2(m_lines.at(i+1));
1005
                        // find new cpos
1006
                        xpos = 0.0;
1007
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
1008
                        {
1009
                                xpos += item(j)->glyph.wide();
1010
                                if (xpos > m_magicX) {
1011
                                        m_lastMagicPos = j;
1012
                                        return j;
1013
                                }
1014
                        }
1015
                        m_lastMagicPos = ls2.lastItem + 1;
1016
                        return ls2.lastItem + 1;
1017
                }
1018
        }
1019
        return lastFrameItem;
1020
}
1021
 
1022
int StoryText::startOfFrame(int pos)
1023
{
1024
        return firstFrameItem;
1025
}
1026
int StoryText::endOfFrame(int pos)
1027
{
7637 avox 1028
        return lastFrameItem + 1;
7501 avox 1029
}
1030
 
4669 avox 1031
// selection
1032
 
1033
int StoryText::startOfSelection() const
1034
{
1035
        return selFirst <= selLast? selFirst : 0;
1036
}
1037
 
1038
int StoryText::endOfSelection() const
1039
{
1040
        return selFirst <= selLast? selLast + 1 : -1;
1041
}
1042
 
1043
int StoryText::lengthOfSelection() const
1044
{
1045
        return selFirst <= selLast? selLast - selFirst + 1 : 0;
1046
}
1047
 
1048
 
1049
bool StoryText::selected(int pos) const
1050
{
4749 avox 1051
        return (selFirst <= pos && pos <= selLast)
5721 avox 1052
//             || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
1053
        ;
4669 avox 1054
}
1055
 
1056
void StoryText::select(int pos, uint len, bool on)
1057
{
1058
        if (pos < 0)
1059
                pos += length();
1060
 
1061
        assert( pos >= 0 );
1062
        assert( pos + signed(len) <= length() );
1063
 
5751 avox 1064
//      qDebug("old selection: %d - %d", selFirst, selLast);
1065
 
5721 avox 1066
//      StoryText* that = const_cast<StoryText *>(this);
1067
//      for (int i=pos; i < pos+signed(len); ++i)
1068
//              that->at(i)->cselect = on;
4669 avox 1069
 
1070
        if (on) {
1071
                // extend if possible
1072
                if (selected(pos - 1))
8562 jghali 1073
                        selLast = qMax(selLast, pos + static_cast<int>(len) - 1);
4669 avox 1074
                else if (selected(pos + len))
8562 jghali 1075
                        selFirst = qMin(selFirst, pos);
4669 avox 1076
                else {
1077
                        selFirst = pos;
1078
                        selLast = pos + len - 1;
1079
                }
1080
        }
1081
        else {
1082
                if (pos <= selFirst && selLast < pos + signed(len))
1083
                        deselectAll();
1084
                // shrink
1085
                else if (!selected(pos - 1) && selected(pos + len - 1))
1086
                        selFirst = pos + len;
1087
                else if (selected(pos) && !selected(pos + len))
1088
                        selLast = pos - 1;
1089
                else if (selected(pos) || selected(pos + len - 1))
1090
                        // Grr, deselection splits selection
1091
                        selLast = pos - 1;
1092
        }
5751 avox 1093
 
1094
//      qDebug("new selection: %d - %d", selFirst, selLast);
4669 avox 1095
}
1096
 
1097
void StoryText::selectAll()
1098
{
5721 avox 1099
/*      StoryText* that = const_cast<StoryText *>(this);
1100
        that->at(0);
1101
        for (int i=0; i < length(); ++i) {
1102
                that->current()->cselect = true;
1103
                that->next();
1104
        }
1105
*/
4669 avox 1106
        selFirst = 0;
1107
        selLast = length() - 1;
1108
}
1109
 
1110
void StoryText::deselectAll()
1111
{
5721 avox 1112
/*      StoryText* that = const_cast<StoryText *>(this);
1113
        that->at(0);
1114
        for (int i=0; i < length(); ++i) {
1115
                that->current()->cselect = false;
1116
                that->next();
1117
        }
1118
*/     
4669 avox 1119
        selFirst = 0;
1120
        selLast = -1;
1121
}
1122
 
1123
void StoryText::removeSelection()
1124
{
5751 avox 1125
//      qDebug("removeSelection: %d - %d", selFirst, selLast);
4669 avox 1126
        if (selFirst > selLast)
1127
                return;
1128
 
1129
        assert( selFirst >= 0 );
1130
        assert( selLast < length() );
1131
 
1132
        removeChars(selFirst, selLast - selFirst+1);
1133
        deselectAll();
1134
}
1135
 
1136
 
1137
 
1138
void StoryText::invalidateObject(const PageItem * embedded)
1139
{
1140
}
1141
 
1142
void StoryText::invalidateLayout()
1143
{
1144
}
1145
 
1146
void StoryText::invalidateAll()
1147
{
12343 pierre 1148
//      d->pstyleContext.invalidate();
7958 avox 1149
        invalidate(0, nrOfItems());
4669 avox 1150
}
1151
 
7958 avox 1152
void StoryText::invalidate(int firstItem, int endItem)
4669 avox 1153
{
12343 pierre 1154
//      for (int i=firstItem; i < endItem; ++i) {
1155
//              ParagraphStyle* par = item(i)->parstyle;
1156
//              if (par)
1157
//                      par->charStyleContext()->invalidate();
1158
//      }
4669 avox 1159
}
1160
 
1161
 
1162
// physical view
1163
 
1164
/*
1165
void StoryText::validate()
1166
{
1167
        static bool withinValidate = false;
1168
 
1169
        assert( !withinValidate );
1170
        withinValidate = true;
1171
 
1172
        withinValidate = false;
1173
}
1174
*/
1175
 
1176
int StoryText::screenToPosition(FPoint coord) const
1177
{
7501 avox 1178
        double maxx = coord.x() - 1.0;
6144 avox 1179
        for (unsigned int i=0; i < lines(); ++i)
1180
        {
1181
                LineSpec ls = line(i);
6334 avox 1182
//              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 1183
                if (ls.y + ls.descent < coord.y())
1184
                        continue;
7501 avox 1185
                double xpos = ls.x;
1186
                for (int j = ls.firstItem; j <= ls.lastItem; ++j) {
6334 avox 1187
//                              qDebug(QString("screenToPosition: (%1,%2) -> x %3 + %4").arg(coord.x()).arg(coord.y()).arg(xpos).arg(item(j)->glyph.wide()));
7501 avox 1188
                        double width = item(j)->glyph.wide();
1189
                        xpos += width;
1190
                        if (xpos >= coord.x())
1191
                                return xpos - width/2 > coord.x() ? j : j+1;
6144 avox 1192
                }
7501 avox 1193
                if (xpos > maxx)
1194
                        maxx = xpos;
1195
                if (xpos + 1.0 > coord.x()) // allow 1pt after end of line
1196
                        return ls.lastItem + 1;
1197
                else if (xpos < ls.x + 0.01 && maxx >= coord.x()) // check for empty line
1198
                        return ls.firstItem;
6144 avox 1199
        }
8562 jghali 1200
        return qMax(lastFrameItem+1, firstFrameItem);
4669 avox 1201
}
1202
 
1203
 
6144 avox 1204
FRect StoryText::boundingBox(int pos, uint len) const
4669 avox 1205
{
6144 avox 1206
        FRect result;
8922 avox 1207
        LineSpec ls;
6144 avox 1208
        for (uint i=0; i < lines(); ++i)
1209
        {
8922 avox 1210
                ls = line(i);
6144 avox 1211
                if (ls.lastItem < pos)
1212
                        continue;
1213
                if (ls.firstItem <= pos) {
9165 avox 1214
                        /*
1215
                        if (ls.lastItem == pos && (item(pos)->effects() & ScStyle_SuppressSpace)  )
8922 avox 1216
                        {
1217
                                if (i+1 < lines())
1218
                                {
1219
                                        ls = line(i+1);
1220
                                        result.setRect(ls.x, ls.y - ls.ascent, 1, ls.ascent + ls.descent);
1221
                                }
1222
                                else
1223
                                {
1224
                                        ls = line(lines()-1);
8931 cbradney 1225
                                        const ParagraphStyle& pstyle(paragraphStyle(pos));
1226
                                        result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
8922 avox 1227
                                }
1228
                        }
9165 avox 1229
                        else */
8922 avox 1230
                        {
1231
                                double xpos = ls.x;
1232
                                for (int j = ls.firstItem; j < pos; ++j)
12315 pierre 1233
                                {
1234
                                        if (item(j)->ch == SpecialChars::OBJECT)
1235
                                                xpos += (object(j)->gWidth + object(j)->lineWidth());
1236
                                        else
1237
                                                xpos += item(j)->glyph.wide();
1238
                                }
1239
                                double finalw = 1;
1240
                                if (item(pos)->ch == SpecialChars::OBJECT)
1241
                                        finalw = (object(pos)->gWidth + object(pos)->lineWidth());
1242
                                else
1243
                                        finalw = item(pos)->glyph.wide();
1244
                                result.setRect(xpos, ls.y - ls.ascent, pos < length()? finalw : 1, ls.ascent + ls.descent);
8922 avox 1245
                        }
9165 avox 1246
                        return result;
6144 avox 1247
                }
1248
        }
8922 avox 1249
 
12343 pierre 1250
        const ParagraphStyle* pstyle =  StylesLibrary::Paragraph(paragraphStyle(pos));
9165 avox 1251
        if (lines() > 0)
1252
        {
1253
                ls = line(lines()-1);          
12343 pierre 1254
                result.setRect(ls.x, ls.y + pstyle->lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
9165 avox 1255
        }
1256
        else
1257
        {
12343 pierre 1258
                result.setRect(1, 1, 1, pstyle->lineSpacing());
9165 avox 1259
        }      
6144 avox 1260
        return result;
4669 avox 1261
}
1262
 
1263
int StoryText::layout(int startItem)
1264
{
1265
        //FIXME:NLS
1266
        return -1;
1267
}
1268
 
1269
 
1270
uint StoryText::nrOfItems() const
1271
{
1272
        return length();
1273
}
1274
 
1275
 
1276
ScText*  StoryText::item(uint itm)
1277
{
1278
        assert( static_cast<int>(itm) < length() );
5721 avox 1279
        return const_cast<StoryText *>(this)->d->at(itm);
4669 avox 1280
}
1281
 
6144 avox 1282
 
1283
const ScText*  StoryText::item(uint itm) const
1284
{
1285
        assert( static_cast<int>(itm) < length() );
1286
        return const_cast<StoryText *>(this)->d->at(itm);
1287
}
1288
 
7442 avox 1289
 
4669 avox 1290
const QString StoryText::itemText(uint itm) const
1291
{
1292
 
1293
        assert( static_cast<int>(itm) < length() );
1294
 
1295
        return text(itm, 1);
1296
}
1297
 
1298
 
12343 pierre 1299
QString StoryText::itemStyle(uint itm) const
4669 avox 1300
{
1301
        assert( static_cast<int>(itm) < length() );
1302
 
1303
        return charStyle(itm);
1304
}
1305
 
1306
 
1307
int StoryText::startOfItem(uint itm) const
1308
{
1309
        assert( static_cast<int>(itm) < length() );
1310
 
1311
        return itm;
1312
}
1313
 
1314
int StoryText::endOfItem(uint itm) const
1315
{
1316
        assert( static_cast<int>(itm) < length() );
1317
 
1318
        return itm + 1;
1319
}
1320
 
8194 avox 1321
 
8266 avox 1322
using namespace desaxe;
8194 avox 1323
 
8800 jghali 1324
void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
8194 avox 1325
{
1326
        Xml_attr empty;
1327
        Xml_attr pageno;
1328
        pageno.insert("name", "pgno");
11713 fschmid 1329
        Xml_attr pageco;
1330
        pageco.insert("name", "pgco");
8194 avox 1331
 
8266 avox 1332
        handler.begin(elemtag, empty);
12343 pierre 1333
        StylesLibrary::Char( defaultStyle() )->saxx(handler, "defaultstyle");
8194 avox 1334
 
12343 pierre 1335
        QString lastStyle(charStyle(0));
8194 avox 1336
        bool lastWasPar = true;
1337
        int lastPos = 0;
8398 avox 1338
        handler.begin("p", empty);
12343 pierre 1339
        StylesLibrary::Paragraph(paragraphStyle(0))->saxx(handler);
8398 avox 1340
        handler.begin("span", empty);
12343 pierre 1341
        StylesLibrary::Char(lastStyle)->saxx(handler);
8194 avox 1342
        for (int i=0; i < length(); ++i)
1343
        {
1344
                const QChar curr(text(i));
12343 pierre 1345
                const QString style(charStyle(i));
8194 avox 1346
 
1347
                if (curr == SpecialChars::OBJECT ||
1348
                        curr == SpecialChars::TAB ||
1349
                        curr == SpecialChars::PARSEP ||
1350
                        curr == SpecialChars::LINEBREAK ||
1351
                        curr == SpecialChars::COLBREAK ||
1352
                        curr == SpecialChars::FRAMEBREAK ||
1353
                        curr == SpecialChars::PAGENUMBER ||
11713 fschmid 1354
                        curr == SpecialChars::PAGECOUNT ||
8194 avox 1355
                        curr.unicode() < 32 ||
1356
                        (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1357
                        curr.unicode() == 0xfffe || curr.unicode() == 0xffff ||
1358
                        style != lastStyle)
1359
                {
1360
                        // something new, write pending chars
1361
                        if  (i - lastPos > 0)
1362
                        {
12093 jghali 1363
                                handler.chars(textWithSmartHyphens(lastPos, i-lastPos));
8194 avox 1364
                        }
1365
                        lastPos = i;
1366
                }
1367
 
1368
                lastWasPar = (curr == SpecialChars::PARSEP);
1369
                if (lastWasPar)
1370
                {
8398 avox 1371
                        handler.end("span");
1372
                        handler.end("p");
1373
                        handler.begin("p", empty);
12343 pierre 1374
                        StylesLibrary::Paragraph(paragraphStyle(i+1))->saxx(handler);
8398 avox 1375
                        handler.begin("span", empty);
8194 avox 1376
                }
1377
                else if (curr == SpecialChars::OBJECT && object(i) != NULL)
1378
                {
1379
                        object(i)->saxx(handler);
1380
                }
1381
                else if (curr == SpecialChars::TAB)
1382
                {
8266 avox 1383
                        handler.beginEnd("tab", empty);
8194 avox 1384
                }
1385
                else if (curr == SpecialChars::LINEBREAK)
1386
                {
8266 avox 1387
                        handler.beginEnd("breakline", empty);
8194 avox 1388
                }
1389
                else if (curr == SpecialChars::COLBREAK)
1390
                {
8266 avox 1391
                        handler.beginEnd("breakcol", empty);
8194 avox 1392
                }
1393
                else if (curr == SpecialChars::FRAMEBREAK)
1394
                {
8266 avox 1395
                        handler.beginEnd("breakframe", empty);
8194 avox 1396
                }
1397
                else if (curr == SpecialChars::PAGENUMBER)
1398
                {
8266 avox 1399
                        handler.beginEnd("var", pageno);
8194 avox 1400
                }
11713 fschmid 1401
                else if (curr == SpecialChars::PAGECOUNT)
1402
                {
1403
                        handler.beginEnd("var", pageco);
1404
                }
8194 avox 1405
                else if (curr.unicode() < 32 ||
1406
                                 (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1407
                                 curr.unicode() == 0xfffe || curr.unicode() == 0xffff)
1408
                {
1409
                        Xml_attr unic;
8266 avox 1410
                        unic.insert("code", toXMLString(curr.unicode()));
1411
                        handler.beginEnd("unicode", unic);
8194 avox 1412
                }
1413
                else if (lastStyle != style)
1414
                {
8398 avox 1415
                        handler.end("span");
1416
                        handler.begin("span", empty);
12343 pierre 1417
                        StylesLibrary::Char(style)->saxx(handler);
8194 avox 1418
                        lastStyle = style;
1419
                        continue;
1420
                }
1421
                else
1422
                        continue;
1423
                lastPos = i+1;
1424
        }
1425
 
1426
        if  (length() - lastPos > 0)
12093 jghali 1427
                handler.chars(textWithSmartHyphens(lastPos, length()-lastPos));
8398 avox 1428
        handler.end("span");
1429
        handler.end("p");
8194 avox 1430
 
8398 avox 1431
//      if (!lastWasPar)
1432
//              paragraphStyle(length()-1).saxx(handler);
8194 avox 1433
 
8266 avox 1434
        handler.end(elemtag);
8194 avox 1435
 
1436
}
1437
 
8266 avox 1438
 
1439
class AppendText_body : public Action_body
1440
{
1441
public:
8834 cbradney 1442
        void chars(const Xml_string& txt)
8266 avox 1443
        {
12093 jghali 1444
                QChar chr;
1445
                int   lastPos = 0, len;
8266 avox 1446
                StoryText* obj = this->dig->top<StoryText>();
12093 jghali 1447
                for (int i = 0; i < txt.length(); ++i)
1448
                {
1449
                        chr = txt.at(i);
1450
                        if (chr == SpecialChars::SHYPHEN && i > 0)
1451
                        {
1452
                                int toInsert = i - lastPos;
1453
                                if (toInsert > 0)
1454
                                        obj->insertChars(obj->length(), txt.mid(lastPos, toInsert));
1455
                                len = obj->length();
1456
                                ScText* lastItem = obj->item(len-1);
1457
                                // double SHY means user provided SHY, single SHY is automatic one
1458
                                if (lastItem->effects() & ScStyle_HyphenationPossible)
1459
                                {
1460
                                        lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
1461
                                        obj->insertChars(len, QString(chr));
1462
                                }
1463
                                else
1464
                                {
1465
                                        lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
1466
                                }
1467
                                lastPos = i + 1;
1468
                        }
1469
                }
1470
                if (lastPos < txt.length())
1471
                {
1472
                        QString ins = (lastPos == 0) ? txt : txt.right(txt.length() - lastPos);
1473
                        len = obj->length();
1474
                        obj->insertChars(len, ins);
1475
                }
8266 avox 1476
        }
1477
};
1478
 
1479
struct  AppendText : public MakeAction<AppendText_body>
1480
{};
1481
 
1482
 
1483
class AppendSpecial_body : public Action_body
1484
{
1485
public:
1486
        AppendSpecial_body(QChar sp) : chr(sp) {}
1487
 
8834 cbradney 1488
        void begin(const Xml_string& tag, Xml_attr attr)
8266 avox 1489
        {
1490
                StoryText* obj = this->dig->top<StoryText>();
1491
                Xml_attr::iterator code = attr.find("code");
11713 fschmid 1492
                Xml_attr::iterator name = attr.find("name");
8266 avox 1493
                if (tag == "unicode" && code != attr.end())
1494
                        obj->insertChars(-1, QChar(parseUInt(Xml_data(code))));
11713 fschmid 1495
                else if (tag == "var" && name != attr.end())
1496
                {
1497
                        if (Xml_data(name) == "pgno")
1498
                                obj->insertChars(-1, SpecialChars::PAGENUMBER);
1499
                        else
1500
                                obj->insertChars(-1, SpecialChars::PAGECOUNT);
1501
                }
8266 avox 1502
                else
1503
                        obj->insertChars(-1, chr);
1504
        }
1505
private:
1506
        QChar chr;
1507
};
1508
 
1509
struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar>
1510
{
1511
        AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {}
1512
        AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {}
1513
};
1514
 
1515
 
1516
class AppendInlineFrame_body : public Action_body
1517
{
1518
public:
8834 cbradney 1519
        void end(const Xml_string& tag) // this could be a setter if we had StoryText::appendObject() ...
8266 avox 1520
        {
1521
                StoryText* story = this->dig->top<StoryText>(1);
1522
                PageItem* obj = this->dig->top<PageItem>(0);
1523
                story->insertObject(-1, obj);
1524
        }
1525
};
1526
 
1527
struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body>
1528
{};
1529
 
8398 avox 1530
/*
8266 avox 1531
class ApplyStyle_body : public Action_body
1532
{
1533
public:
8834 cbradney 1534
        void end(const Xml_string& tag)
8266 avox 1535
        {
1536
                qDebug("storytext desaxe: apply style");
1537
                StoryText* story = this->dig->top<StoryText>(1);
1538
                ParagraphStyle* obj = this->dig->top<ParagraphStyle>(0);
1539
                story->applyStyle(-1, *obj);
1540
        }
1541
};
1542
 
1543
struct ApplyStyle : public MakeAction<ApplyStyle_body>
1544
{};
1545
 
1546
 
8289 avox 1547
class ApplyCharStyle_body : public Action_body
1548
{
1549
public:
1550
        ApplyCharStyle_body() : storyTag(StoryText::saxxDefaultElem), lastPos(0), lastStyle()
1551
        {}
1552
        ApplyCharStyle_body(const Xml_string& tag) : storyTag(tag), lastPos(0), lastStyle()
1553
        {}
1554
 
8834 cbradney 1555
        void end(const Xml_string& tag)
8289 avox 1556
        {
1557
                qDebug("storytext desaxe: apply charstyle");
1558
                if (tag == CharStyle::saxxDefaultElem)
1559
                {
1560
                        StoryText* story = this->dig->top<StoryText>(1);
1561
                        CharStyle* obj = this->dig->top<CharStyle>(0);
1562
                        int len = story->length();
1563
                        if (len > lastPos && lastStyle != *obj)
1564
                        {
1565
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1566
                                lastPos = len;
1567
                                lastStyle = *obj;
1568
                        }
1569
                }
1570
                else if (tag == StoryText::saxxDefaultElem)
1571
                {
1572
                        StoryText* story = this->dig->top<StoryText>();
1573
                        int len = story->length();
1574
                        if (len > lastPos)
1575
                        {
1576
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1577
                        }
1578
                }
1579
        }
1580
private:
1581
        Xml_string storyTag;
1582
        int lastPos;
1583
        CharStyle lastStyle;
1584
};
1585
 
1586
struct ApplyCharStyle : public MakeAction<ApplyCharStyle_body, const Xml_string&>
1587
{
1588
        ApplyCharStyle() : MakeAction<ApplyCharStyle_body, const Xml_string&>() {}
1589
        ApplyCharStyle(const Xml_string& tag) : MakeAction<ApplyCharStyle_body, const Xml_string&>(tag) {}
1590
};
1591
 
8398 avox 1592
*/
8289 avox 1593
 
8398 avox 1594
class Paragraph_body : public Action_body
1595
{
1596
public:
1597
        Paragraph_body() : lastPos(0), lastStyle(NULL)
1598
        {}
1599
 
1600
        ~Paragraph_body()
1601
        {
1602
                if (lastStyle)
1603
                        delete lastStyle;
1604
        }
1605
 
8834 cbradney 1606
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 1607
        {
1608
                if (tag == "p")
1609
                {
1610
                        StoryText* story = this->dig->top<StoryText>();
9454 avox 1611
//                      qDebug(QString("startpar: %1->%2 %3->NULL").arg(lastPos).arg(story->length()).arg((ulong)lastStyle));
8398 avox 1612
                        lastPos = story->length();
8455 avox 1613
                        if (lastPos > 0) {
8398 avox 1614
                                story->insertChars(-1, SpecialChars::PARSEP);
8455 avox 1615
                                ++lastPos;
1616
                        }
8398 avox 1617
                        if (lastStyle)
1618
                                delete lastStyle;
1619
                        lastStyle = NULL;
1620
                }
1621
        }
1622
 
8834 cbradney 1623
        void end(const Xml_string& tag)
8398 avox 1624
        {
1625
                if (tag == ParagraphStyle::saxxDefaultElem)
1626
                {
1627
                        if (lastStyle)
1628
                                delete lastStyle;
1629
                        lastStyle = this->dig->top<ParagraphStyle>(0);
9454 avox 1630
//                      qDebug(QString("endstyle: %1 %2 %3").arg("?").arg(lastPos).arg((ulong)lastStyle));
8398 avox 1631
                }
1632
                else if (tag == "p")
1633
                {
1634
                        StoryText* story = this->dig->top<StoryText>();
1635
                        int len = story->length();
9454 avox 1636
//                      qDebug(QString("endpar: %1 %2 %3 %4").arg(len).arg(lastPos).arg((ulong)lastStyle).arg(lastStyle? lastStyle->parent() : QString()));
8398 avox 1637
                        if (len > lastPos && lastStyle)
1638
                        {
1639
                                story->applyStyle(lastPos, *lastStyle);
1640
                        }
1641
                }
1642
        }
1643
private:
1644
        int lastPos;
1645
        ParagraphStyle* lastStyle;
1646
};
1647
 
1648
struct Paragraph : public MakeAction<Paragraph_body>
1649
{
1650
        Paragraph() : MakeAction<Paragraph_body>() {}
1651
};
1652
 
1653
 
1654
class SpanAction_body : public Action_body
1655
{
1656
public:
1657
        SpanAction_body() : lastPos(0), lastStyle(NULL)
1658
        {}
1659
 
1660
        ~SpanAction_body()
1661
        {
1662
                if (lastStyle)
1663
                        delete lastStyle;
1664
        }
1665
 
8834 cbradney 1666
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 1667
        {
8413 avox 1668
//              qDebug(QString("spanaction: begin %1").arg(tag));
8398 avox 1669
                if (tag == "span")
1670
                {
1671
                        StoryText* story = this->dig->top<StoryText>();
1672
                        lastPos = story->length();
1673
                        if (lastStyle)
1674
                                delete lastStyle;
1675
                        lastStyle = NULL;
1676
                }
1677
        }
1678
 
8834 cbradney 1679
        void end(const Xml_string& tag)
8398 avox 1680
        {
1681
                if (tag == CharStyle::saxxDefaultElem)
8413 avox 1682
//                      qDebug(QString("spanaction: end %1").arg(tag));
8398 avox 1683
                {
1684
                        if (lastStyle)
1685
                                delete lastStyle;
1686
                        lastStyle = this->dig->top<CharStyle>(0);
1687
                }
8413 avox 1688
                else if (tag == "span")
8398 avox 1689
                {
1690
                        StoryText* story = this->dig->top<StoryText>();
1691
                        int len = story->length();
1692
                        if (len > lastPos && lastStyle)
1693
                        {
1694
                                story->applyCharStyle(lastPos, len - lastPos, *lastStyle);
1695
                        }
1696
                }
1697
        }
1698
private:
1699
        int lastPos;
1700
        CharStyle* lastStyle;
1701
};
1702
 
1703
struct SpanAction : public MakeAction<SpanAction_body>
1704
{
1705
        SpanAction() : MakeAction<SpanAction_body>() {}
1706
};
1707
 
1708
 
8266 avox 1709
const Xml_string StoryText::saxxDefaultElem("story");
1710
 
8834 cbradney 1711
void StoryText::desaxeRules(const Xml_string& prefixPattern, Digester& ruleset, Xml_string elemtag)
8266 avox 1712
{
1713
        Xml_string storyPrefix(Digester::concat(prefixPattern, elemtag));
1714
        ruleset.addRule(storyPrefix, Factory<StoryText>());
1715
 
1716
        ParagraphStyle::desaxeRules(storyPrefix, ruleset, "defaultstyle");
12343 pierre 1717
        ruleset.addRule( Digester::concat(storyPrefix, "defaultstyle"),
1718
                         SetterWithConversion<StoryText, const QString&, QString>( & StoryText::setDefaultStyle )
1719
                       );
8266 avox 1720
 
8398 avox 1721
        Paragraph paraAction;
1722
        Xml_string paraPrefix(Digester::concat(storyPrefix, "p"));
11713 fschmid 1723
        ruleset.addRule(paraPrefix, paraAction );
8398 avox 1724
        ParagraphStyle::desaxeRules(paraPrefix, ruleset, ParagraphStyle::saxxDefaultElem);
11713 fschmid 1725
        ruleset.addRule(Digester::concat(paraPrefix, ParagraphStyle::saxxDefaultElem), paraAction );
8266 avox 1726
 
8398 avox 1727
        SpanAction spanAction;
1728
        Xml_string spanPrefix(Digester::concat(paraPrefix, "span"));
1729
        ruleset.addRule(spanPrefix, spanAction );
1730
        CharStyle::desaxeRules(spanPrefix, ruleset, CharStyle::saxxDefaultElem);
1731
        ruleset.addRule(Digester::concat(spanPrefix, CharStyle::saxxDefaultElem), spanAction );
1732
 
1733
        ruleset.addRule(spanPrefix, AppendText());
8266 avox 1734
 
8398 avox 1735
        ruleset.addRule(Digester::concat(spanPrefix, "breakline"), AppendSpecial(SpecialChars::LINEBREAK) );
1736
        ruleset.addRule(Digester::concat(spanPrefix, "breakcol"), AppendSpecial(SpecialChars::COLBREAK) );
1737
        ruleset.addRule(Digester::concat(spanPrefix, "breakframe"), AppendSpecial(SpecialChars::FRAMEBREAK) );
11713 fschmid 1738
        ruleset.addRule(Digester::concat(spanPrefix, "tab"), AppendSpecial(SpecialChars::TAB) );
1739
        ruleset.addRule(Digester::concat(spanPrefix, "unicode"), AppendSpecial() );
1740
        ruleset.addRule(Digester::concat(spanPrefix, "var"), AppendSpecial());
8266 avox 1741
 
1742
        //PageItem::desaxeRules(storyPrefix, ruleset); argh, that would be recursive!
11713 fschmid 1743
        ruleset.addRule(Digester::concat(spanPrefix, "item"), AppendInlineFrame() );
8266 avox 1744
 
1745
}