Subversion Repositories Scribus

Rev

Rev 12337 | 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
 
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
 
339
        const StyleContext* cStyleContext = paragraphStyle(pos).charStyleContext();
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;
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
        {
480
                if (this->charStyle(i).effects() & ScStyle_HyphenationPossible
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
 
12337 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");
5732 avox 517
                return defaultStyle().charStyle();
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)
524
                return paragraphStyle(pos).charStyle();
5732 avox 525
 
4669 avox 526
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 527
        return dynamic_cast<const CharStyle &> (*that->d->at(pos));
4669 avox 528
}
529
 
12337 pierre 530
const QString & StoryText::paragraphStyle(int pos) const
4669 avox 531
{
532
        if (pos < 0)
533
                pos += length();
534
 
535
        assert(pos >= 0);
5721 avox 536
        assert(pos <= length());
4669 avox 537
 
6733 avox 538
        assert(d);
539
 
5192 avox 540
        StoryText * that = const_cast<StoryText *> (this);
5721 avox 541
//      assert( that->at(pos)->cab >= 0 );
542
//      assert( that->at(pos)->cab < doc->docParagraphStyles.count() );
543
//      return doc->docParagraphStyles[that->at(pos)->cab];
544
 
10366 jghali 545
        while (pos < length() && that->d->at(pos)->ch != SpecialChars::PARSEP) {
5721 avox 546
                ++pos;
547
        }
6909 avox 548
        if (pos >= length()) {
7232 avox 549
                return that->d->trailingStyle;
5778 avox 550
        }
10366 jghali 551
        else if ( !that->d->at(pos)->parstyle ) {
552
                ScText* current = that->d->at(pos);
10394 cbradney 553
                qDebug("inserting default parstyle at %i", pos);
10366 jghali 554
                current->parstyle = new ParagraphStyle();
555
                current->parstyle->setContext( & d->pstyleContext);
556
//              current->parstyle->setName( "para(paragraphStyle)" ); // DONT TRANSLATE
557
//              current->parstyle->charStyle().setName( "cpara(paragraphStyle)" ); // DONT TRANSLATE
558
//              current->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5721 avox 559
        }
5778 avox 560
        else {
561
//              qDebug(QString("using parstyle at %1").arg(pos));
562
        }
10366 jghali 563
        assert (that->d->at(pos)->parstyle);
564
        return *that->d->at(pos)->parstyle;
4669 avox 565
}
566
 
12337 pierre 567
const QString& StoryText::defaultStyle() const
5559 avox 568
{
6733 avox 569
        assert(d);
5721 avox 570
        return d->defaultStyle;
5559 avox 571
}
572
 
5569 avox 573
 
12337 pierre 574
void StoryText::setDefaultStyle(const QString& style)
5569 avox 575
{
8134 avox 576
        const StyleContext * oldPContext = d->defaultStyle.context();
8289 avox 577
//      const StyleContext * oldCContext = d->defaultStyle.charStyle().context();
5721 avox 578
        d->defaultStyle = style;
8134 avox 579
        d->defaultStyle.setContext( oldPContext );
8194 avox 580
//      d->defaultStyle.setName( "storydefault" ); // DONT TRANSLATE
581
//      d->defaultStyle.charStyle().setName( "cstorydefault" ); // DONT TRANSLATE
8134 avox 582
//      qDebug(QString("defstyle %1 context %2 defcstyle %3 ccontext %4 newcontext %5")
583
//                 .arg((uint)&d->defaultStyle,16).arg((uint)oldPContext,16)
584
//                 .arg((uint)&d->defaultStyle.charStyle(),16).arg((uint)oldCContext,16)
585
//                 .arg((uint)d->defaultStyle.charStyle().context(),16));
586
//      d->defaultStyle.charStyle().setContext( oldCContext );
7958 avox 587
        invalidateAll();
5569 avox 588
}
589
 
590
 
12337 pierre 591
void StoryText::applyCharStyle(int pos, uint len, const QString& style )
4669 avox 592
{
593
        if (pos < 0)
594
                pos += length();
595
 
11968 fschmid 596
/* Why do we use an assert here instead of a gracefully return doing nothing ????? */
597
//      assert(pos >= 0);
598
//      assert(pos + signed(len) <= length());
599
        if (pos < 0)
600
                return;
601
        if (pos + signed(len) > length())
602
                return;
4669 avox 603
 
604
        if (len == 0)
605
                return;
606
 
10366 jghali 607
        ScText* itText;
5721 avox 608
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 609
                itText = d->at(i);
11936 jghali 610
                // #6165 : applying style on last character applies style on whole text on next open 
611
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
612
                        itText->parstyle->charStyle().applyCharStyle(style);*/
10366 jghali 613
                itText->applyCharStyle(style);
5721 avox 614
        }
4669 avox 615
 
616
        invalidate(pos, pos + len);
617
}
618
 
5773 avox 619
 
620
 
12340 pierre 621
void StoryText::eraseCharStyle(int pos, uint len, const QString& style )
5773 avox 622
{
623
        if (pos < 0)
624
                pos += length();
625
 
626
        assert(pos >= 0);
627
        assert(pos + signed(len) <= length());
628
 
629
        if (len == 0)
630
                return;
631
 
10366 jghali 632
        ScText* itText;
5773 avox 633
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 634
                itText = d->at(i);
635
                if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
636
                        itText->parstyle->charStyle().eraseCharStyle(style);
637
                itText->eraseCharStyle(style);
5773 avox 638
        }
639
 
640
        invalidate(pos, pos + len);
641
}
642
 
643
 
12337 pierre 644
void StoryText::applyStyle(int pos, const QString& style)
4669 avox 645
{
646
        if (pos < 0)
647
                pos += length();
648
 
649
        assert(pos >= 0);
5721 avox 650
        assert(pos <= length());
4669 avox 651
 
5721 avox 652
        int i = pos;
9737 jghali 653
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5184 avox 654
                ++i;
655
        }
5721 avox 656
        if (i < length()) {
657
                if (!d->at(i)->parstyle) {
10394 cbradney 658
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 659
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 660
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
8194 avox 661
//                      d->at(i)->parstyle->setName( "para(applyStyle)" ); // DONT TRANSLATE
662
//                      d->at(i)->parstyle->charStyle().setName( "cpara(applyStyle)" ); // DONT TRANSLATE
8134 avox 663
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext() );
5721 avox 664
                }
665
//              qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
666
                d->at(i)->parstyle->applyStyle(style);
667
        }
668
        else {
669
                // not happy about this but inserting a new PARSEP makes more trouble
670
//              qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
7232 avox 671
                d->trailingStyle.applyStyle(style);
5721 avox 672
        }
8562 jghali 673
        invalidate(pos, qMin(i, length()));
4669 avox 674
}
675
 
12340 pierre 676
void StoryText::eraseStyle(int pos, const QString& style)
5773 avox 677
{
678
        if (pos < 0)
679
                pos += length();
680
 
681
        assert(pos >= 0);
682
        assert(pos <= length());
683
 
684
        int i = pos;
9737 jghali 685
        while (i < length() && d->at(i)->ch != SpecialChars::PARSEP) {
5773 avox 686
                ++i;
687
        }
688
        if (i < length()) {
689
                if (!d->at(i)->parstyle) {
10394 cbradney 690
                        qDebug("PARSEP without style at pos %i", i);
5980 avox 691
                        d->at(i)->parstyle = new ParagraphStyle();
8134 avox 692
                        d->at(i)->parstyle->setContext( & d->pstyleContext);
8194 avox 693
//                      d->at(i)->parstyle->setName( "para(eraseStyle)" ); // DONT TRANSLATE
694
//                      d->at(i)->parstyle->charStyle().setName( "cpara(eraseStyle)" ); // DONT TRANSLATE
8134 avox 695
//                      d->at(i)->parstyle->charStyle().setContext( d->defaultStyle.charStyleContext());
5773 avox 696
                }
697
                //              qDebug(QString("applying parstyle %2 at %1 for %3").arg(i).arg(paragraphStyle(pos).name()).arg(pos));
698
                d->at(i)->parstyle->eraseStyle(style);
699
        }
700
        else {
701
                // not happy about this but inserting a new PARSEP makes more trouble
702
                //              qDebug(QString("applying parstyle %1 as defaultstyle for %2").arg(paragraphStyle(pos).name()).arg(pos));
7232 avox 703
                d->trailingStyle.eraseStyle(style);
5773 avox 704
        }
8562 jghali 705
        invalidate(pos, qMin(i, length()));
5773 avox 706
}
707
 
708
 
12337 pierre 709
void StoryText::setStyle(int pos, const QString& style)
6733 avox 710
{
711
        eraseStyle(pos, paragraphStyle(pos));
712
        applyStyle(pos, style);
713
}
714
 
715
 
716
void StoryText::setCharStyle(int pos, uint len, const CharStyle& style)
717
{
9067 avox 718
        if (pos < 0)
719
                pos += length();
720
 
721
        assert(pos >= 0);
9645 avox 722
        assert(len <= unsigned(length()));
9067 avox 723
        assert(pos + signed(len) <= length());
724
 
725
        if (len == 0)
726
                return;
727
 
10366 jghali 728
        ScText* itText;
9067 avox 729
        for (uint i=pos; i < pos+len; ++i) {
10366 jghali 730
                itText = d->at(i);
11936 jghali 731
                // #6165 : applying style on last character applies style on whole text on next open 
732
                /*if (itText->ch == SpecialChars::PARSEP && itText->parstyle != NULL)
733
                        itText->parstyle->charStyle() = style;*/
10366 jghali 734
                itText->setStyle(style);
9067 avox 735
        }
736
 
737
        invalidate(pos, pos + len);
6733 avox 738
}
739
 
740
 
8455 avox 741
 
742
void StoryText::getNamedResources(ResourceCollection& lists) const
743
{
8938 avox 744
        d->defaultStyle.getNamedResources(lists);
745
        d->trailingStyle.getNamedResources(lists);
746
 
8455 avox 747
        for (int i=0; i < length(); ++i)
748
        {
749
                if (text(i) == SpecialChars::PARSEP)
750
                        paragraphStyle(i).getNamedResources(lists);
751
                else if (text(i) == SpecialChars::OBJECT)
752
                        object(i)->getNamedResources(lists);
753
                else
754
                        charStyle(i).getNamedResources(lists);
755
        }
756
}
757
 
758
 
7442 avox 759
void StoryText::replaceStyles(QMap<QString,QString> newNameForOld)
760
{
8455 avox 761
        ResourceCollection newnames;
8775 cbradney 762
        newnames.mapStyles(newNameForOld);
8455 avox 763
        replaceNamedResources(newnames);
764
}
765
 
766
void StoryText::replaceNamedResources(ResourceCollection& newNames)
767
{
7442 avox 768
        int len = length();
8455 avox 769
 
770
        d->defaultStyle.replaceNamedResources(newNames);
771
        d->trailingStyle.replaceNamedResources(newNames);
772
 
7442 avox 773
        if (len == 0)
774
                return;
775
 
10366 jghali 776
        ScText* itText;
7501 avox 777
        for (int i=0; i < len; ++i) {
10366 jghali 778
                itText = d->at(i);
779
                if (itText->parstyle)
780
                        itText->parstyle->replaceNamedResources(newNames);
8455 avox 781
                else
10366 jghali 782
                        itText->replaceNamedResources(newNames);
7442 avox 783
        }
784
 
785
        invalidate(0, len);    
786
}
787
 
8455 avox 788
 
7442 avox 789
void StoryText::replaceCharStyles(QMap<QString,QString> newNameForOld)
790
{
8455 avox 791
        ResourceCollection newnames;
8775 cbradney 792
        newnames.mapCharStyles(newNameForOld);
8455 avox 793
        replaceNamedResources(newnames);
7442 avox 794
}
795
 
8455 avox 796
 
10623 avox 797
int StoryText::nrOfParagraph(uint index) const
798
{
10643 fschmid 799
        int result = 0;
10623 avox 800
        StoryText* that = const_cast<StoryText *>(this);
801
        bool lastWasPARSEP = true;
11359 jghali 802
        index = qMin(index, (uint) that->length());
11229 fschmid 803
        for (uint i=0; i < index; ++i) {
10623 avox 804
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
805
                if (lastWasPARSEP)
806
                        ++result;
807
        }
11380 jghali 808
        return result;
10623 avox 809
}
810
 
4669 avox 811
uint StoryText::nrOfParagraphs() const
812
{
813
        uint result = 0;
5721 avox 814
        StoryText* that = const_cast<StoryText *>(this);
7059 avox 815
        bool lastWasPARSEP = true;
5721 avox 816
        for (int i=0; i < length(); ++i) {
10366 jghali 817
                lastWasPARSEP = that->d->at(i)->ch == SpecialChars::PARSEP;
6887 avox 818
                if (lastWasPARSEP)
4669 avox 819
                        ++result;
5721 avox 820
        }
11359 jghali 821
        return lastWasPARSEP ? result : result + 1;
4669 avox 822
}
823
 
824
int StoryText::startOfParagraph(uint index) const
825
{
826
        if (index == 0)
827
                return 0;
828
 
5184 avox 829
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 830
        for (int i=0; i < length(); ++i) {
10366 jghali 831
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
4669 avox 832
                        return i + 1;
5721 avox 833
        }
6887 avox 834
        return length();
4669 avox 835
}
836
 
837
int StoryText::endOfParagraph(uint index) const
838
{
839
        ++index;
5184 avox 840
        StoryText* that = const_cast<StoryText *>(this);
5721 avox 841
        for (int i=0; i < length(); ++i) {
10366 jghali 842
                if (that->d->at(i)->ch == SpecialChars::PARSEP && ! --index)
5721 avox 843
                        return i;
844
        }
4669 avox 845
        return length();
846
}
847
 
848
uint StoryText::nrOfRuns() const
849
{
5464 avox 850
        return length();
4669 avox 851
}
852
 
853
int StoryText::startOfRun(uint index) const
854
{
5464 avox 855
        return index;
4669 avox 856
}
857
 
858
int StoryText::endOfRun(uint index) const
859
{
5464 avox 860
        return index + 1;
4669 avox 861
}
862
 
7501 avox 863
// positioning. all positioning methods return char positions
864
// FIXME: make that methods use correct semantic boundaries
4669 avox 865
 
7501 avox 866
static QString wordBoundaries(" .,:;\"'!?\n");
867
static QString sentenceBoundaries(".:!?\n");
868
 
869
int StoryText::nextChar(int pos)
870
{
871
        if (pos < length())
872
                return pos+1;
873
        else
874
                return length();
875
}
876
int StoryText::prevChar(int pos)
877
{
878
        if (pos > 0)
879
                return pos - 1;
880
        else
881
                return 0;
882
}
883
int StoryText::nextWord(int pos)
884
{
885
        int len = length();
8562 jghali 886
        pos = qMin(len, pos+1);
10394 cbradney 887
        while (pos < len  && wordBoundaries.indexOf(text(pos)) < 0)
7501 avox 888
                ++pos;
889
        return pos < len ? pos + 1 : pos;
890
}
891
int StoryText::prevWord(int pos)
892
{
8562 jghali 893
        pos = qMax(0, pos-1);
10394 cbradney 894
        while (pos > 0 && wordBoundaries.indexOf(text(pos)) < 0)
7501 avox 895
                --pos;
10394 cbradney 896
        return wordBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 897
}
898
int StoryText::nextSentence(int pos)
899
{
900
        int len = length();
8562 jghali 901
        pos = qMin(len, pos+1);
10394 cbradney 902
        while (pos < len && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 903
                ++pos;
904
        return pos < len ? pos + 1 : pos;
905
}
906
int StoryText::prevSentence(int pos)
907
{
8562 jghali 908
        pos = qMax(0, pos-1);
10394 cbradney 909
        while (pos > 0 && sentenceBoundaries.indexOf(text(pos)) < 0)
7501 avox 910
                --pos;
10394 cbradney 911
        return sentenceBoundaries.indexOf(text(pos)) < 0 ? pos + 1 : pos;
7501 avox 912
}
913
int StoryText::nextParagraph(int pos)
914
{
915
        int len = length();
8562 jghali 916
        pos = qMin(len, pos+1);
7501 avox 917
        while (pos < len && text(pos) != SpecialChars::PARSEP)
918
                ++pos;
7637 avox 919
        return pos;
7501 avox 920
}
921
int StoryText::prevParagraph(int pos)
922
{
8562 jghali 923
        pos = qMax(0, pos-1);
7501 avox 924
        while (pos > 0 && text(pos) != SpecialChars::PARSEP)
925
                --pos;
7637 avox 926
        return pos;
7501 avox 927
}
928
 
929
// these need valid layout:
930
 
931
int StoryText::startOfLine(int pos)
932
{
8546 cbradney 933
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 934
                const LineSpec & ls(m_lines.at(i));
935
                if (ls.firstItem <= pos && pos <= ls.lastItem)
936
                        return ls.firstItem;
937
        }
938
        return 0;
939
}
940
int StoryText::endOfLine(int pos)
941
{
8546 cbradney 942
        for (int i=0; i < m_lines.count(); ++i) {
7501 avox 943
                const LineSpec & ls(m_lines.at(i));
944
                if (ls.firstItem <= pos && pos <= ls.lastItem)
7657 avox 945
                        return text(ls.lastItem) == SpecialChars::PARSEP ? ls.lastItem :
946
                                text(ls.lastItem) == ' ' ? ls.lastItem : ls.lastItem + 1;
7501 avox 947
        }
948
        return length();
949
}
950
int StoryText::prevLine(int pos)
951
{
8546 cbradney 952
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 953
        {
954
                // find line for pos
955
                const LineSpec & ls(m_lines.at(i));
956
                if (ls.firstItem <= pos && pos <= ls.lastItem)
957
                {
7522 avox 958
                        if (i == 0)
959
                                return startOfLine(pos);
7501 avox 960
                        // find current xpos
961
                        double xpos = 0.0;
962
                        for (int j = ls.firstItem; j < pos; ++j)
963
                                xpos += item(j)->glyph.wide();
964
                        if (pos != m_lastMagicPos || xpos > m_magicX)
965
                                m_magicX = xpos;
966
                        const LineSpec & ls2(m_lines.at(i-1));
967
                        // find new cpos
968
                        xpos = 0.0;
969
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
970
                        {
971
                                xpos += item(j)->glyph.wide();
972
                                if (xpos > m_magicX) {
973
                                        m_lastMagicPos = j;
974
                                        return j;
975
                                }
976
                        }
977
                        m_lastMagicPos = ls2.lastItem;
978
                        return ls2.lastItem;
979
                }
980
        }
981
        return firstFrameItem;
982
}
983
 
984
int StoryText::nextLine(int pos)
985
{
8546 cbradney 986
        for (int i=0; i < m_lines.count(); ++i)
7501 avox 987
        {
988
                // find line for pos
989
                const LineSpec & ls(m_lines.at(i));
990
                if (ls.firstItem <= pos && pos <= ls.lastItem)
991
                {
7522 avox 992
                        if (i+1 == m_lines.count())
993
                                return endOfLine(pos);
7501 avox 994
                        // find current xpos
995
                        double xpos = 0.0;
996
                        for (int j = ls.firstItem; j < pos; ++j)
997
                                xpos += item(j)->glyph.wide();
998
                        if (pos != m_lastMagicPos || xpos > m_magicX)
999
                                m_magicX = xpos;
1000
                        const LineSpec & ls2(m_lines.at(i+1));
1001
                        // find new cpos
1002
                        xpos = 0.0;
1003
                        for (int j = ls2.firstItem; j <= ls2.lastItem; ++j)
1004
                        {
1005
                                xpos += item(j)->glyph.wide();
1006
                                if (xpos > m_magicX) {
1007
                                        m_lastMagicPos = j;
1008
                                        return j;
1009
                                }
1010
                        }
1011
                        m_lastMagicPos = ls2.lastItem + 1;
1012
                        return ls2.lastItem + 1;
1013
                }
1014
        }
1015
        return lastFrameItem;
1016
}
1017
 
1018
int StoryText::startOfFrame(int pos)
1019
{
1020
        return firstFrameItem;
1021
}
1022
int StoryText::endOfFrame(int pos)
1023
{
7637 avox 1024
        return lastFrameItem + 1;
7501 avox 1025
}
1026
 
4669 avox 1027
// selection
1028
 
1029
int StoryText::startOfSelection() const
1030
{
1031
        return selFirst <= selLast? selFirst : 0;
1032
}
1033
 
1034
int StoryText::endOfSelection() const
1035
{
1036
        return selFirst <= selLast? selLast + 1 : -1;
1037
}
1038
 
1039
int StoryText::lengthOfSelection() const
1040
{
1041
        return selFirst <= selLast? selLast - selFirst + 1 : 0;
1042
}
1043
 
1044
 
1045
bool StoryText::selected(int pos) const
1046
{
4749 avox 1047
        return (selFirst <= pos && pos <= selLast)
5721 avox 1048
//             || (pos >= 0 && pos < length() && const_cast<StoryText*>(this)->d->at(pos)->cselect)
1049
        ;
4669 avox 1050
}
1051
 
1052
void StoryText::select(int pos, uint len, bool on)
1053
{
1054
        if (pos < 0)
1055
                pos += length();
1056
 
1057
        assert( pos >= 0 );
1058
        assert( pos + signed(len) <= length() );
1059
 
5751 avox 1060
//      qDebug("old selection: %d - %d", selFirst, selLast);
1061
 
5721 avox 1062
//      StoryText* that = const_cast<StoryText *>(this);
1063
//      for (int i=pos; i < pos+signed(len); ++i)
1064
//              that->at(i)->cselect = on;
4669 avox 1065
 
1066
        if (on) {
1067
                // extend if possible
1068
                if (selected(pos - 1))
8562 jghali 1069
                        selLast = qMax(selLast, pos + static_cast<int>(len) - 1);
4669 avox 1070
                else if (selected(pos + len))
8562 jghali 1071
                        selFirst = qMin(selFirst, pos);
4669 avox 1072
                else {
1073
                        selFirst = pos;
1074
                        selLast = pos + len - 1;
1075
                }
1076
        }
1077
        else {
1078
                if (pos <= selFirst && selLast < pos + signed(len))
1079
                        deselectAll();
1080
                // shrink
1081
                else if (!selected(pos - 1) && selected(pos + len - 1))
1082
                        selFirst = pos + len;
1083
                else if (selected(pos) && !selected(pos + len))
1084
                        selLast = pos - 1;
1085
                else if (selected(pos) || selected(pos + len - 1))
1086
                        // Grr, deselection splits selection
1087
                        selLast = pos - 1;
1088
        }
5751 avox 1089
 
1090
//      qDebug("new selection: %d - %d", selFirst, selLast);
4669 avox 1091
}
1092
 
1093
void StoryText::selectAll()
1094
{
5721 avox 1095
/*      StoryText* that = const_cast<StoryText *>(this);
1096
        that->at(0);
1097
        for (int i=0; i < length(); ++i) {
1098
                that->current()->cselect = true;
1099
                that->next();
1100
        }
1101
*/
4669 avox 1102
        selFirst = 0;
1103
        selLast = length() - 1;
1104
}
1105
 
1106
void StoryText::deselectAll()
1107
{
5721 avox 1108
/*      StoryText* that = const_cast<StoryText *>(this);
1109
        that->at(0);
1110
        for (int i=0; i < length(); ++i) {
1111
                that->current()->cselect = false;
1112
                that->next();
1113
        }
1114
*/     
4669 avox 1115
        selFirst = 0;
1116
        selLast = -1;
1117
}
1118
 
1119
void StoryText::removeSelection()
1120
{
5751 avox 1121
//      qDebug("removeSelection: %d - %d", selFirst, selLast);
4669 avox 1122
        if (selFirst > selLast)
1123
                return;
1124
 
1125
        assert( selFirst >= 0 );
1126
        assert( selLast < length() );
1127
 
1128
        removeChars(selFirst, selLast - selFirst+1);
1129
        deselectAll();
1130
}
1131
 
1132
 
1133
 
1134
void StoryText::invalidateObject(const PageItem * embedded)
1135
{
1136
}
1137
 
1138
void StoryText::invalidateLayout()
1139
{
1140
}
1141
 
1142
void StoryText::invalidateAll()
1143
{
8134 avox 1144
        d->pstyleContext.invalidate();
7958 avox 1145
        invalidate(0, nrOfItems());
4669 avox 1146
}
1147
 
7958 avox 1148
void StoryText::invalidate(int firstItem, int endItem)
4669 avox 1149
{
7958 avox 1150
        for (int i=firstItem; i < endItem; ++i) {
1151
                ParagraphStyle* par = item(i)->parstyle;
1152
                if (par)
8134 avox 1153
                        par->charStyleContext()->invalidate();
7958 avox 1154
        }
4669 avox 1155
}
1156
 
1157
 
1158
// physical view
1159
 
1160
/*
1161
void StoryText::validate()
1162
{
1163
        static bool withinValidate = false;
1164
 
1165
        assert( !withinValidate );
1166
        withinValidate = true;
1167
 
1168
        withinValidate = false;
1169
}
1170
*/
1171
 
1172
int StoryText::screenToPosition(FPoint coord) const
1173
{
7501 avox 1174
        double maxx = coord.x() - 1.0;
6144 avox 1175
        for (unsigned int i=0; i < lines(); ++i)
1176
        {
1177
                LineSpec ls = line(i);
6334 avox 1178
//              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 1179
                if (ls.y + ls.descent < coord.y())
1180
                        continue;
7501 avox 1181
                double xpos = ls.x;
1182
                for (int j = ls.firstItem; j <= ls.lastItem; ++j) {
6334 avox 1183
//                              qDebug(QString("screenToPosition: (%1,%2) -> x %3 + %4").arg(coord.x()).arg(coord.y()).arg(xpos).arg(item(j)->glyph.wide()));
7501 avox 1184
                        double width = item(j)->glyph.wide();
1185
                        xpos += width;
1186
                        if (xpos >= coord.x())
1187
                                return xpos - width/2 > coord.x() ? j : j+1;
6144 avox 1188
                }
7501 avox 1189
                if (xpos > maxx)
1190
                        maxx = xpos;
1191
                if (xpos + 1.0 > coord.x()) // allow 1pt after end of line
1192
                        return ls.lastItem + 1;
1193
                else if (xpos < ls.x + 0.01 && maxx >= coord.x()) // check for empty line
1194
                        return ls.firstItem;
6144 avox 1195
        }
8562 jghali 1196
        return qMax(lastFrameItem+1, firstFrameItem);
4669 avox 1197
}
1198
 
1199
 
6144 avox 1200
FRect StoryText::boundingBox(int pos, uint len) const
4669 avox 1201
{
6144 avox 1202
        FRect result;
8922 avox 1203
        LineSpec ls;
6144 avox 1204
        for (uint i=0; i < lines(); ++i)
1205
        {
8922 avox 1206
                ls = line(i);
6144 avox 1207
                if (ls.lastItem < pos)
1208
                        continue;
1209
                if (ls.firstItem <= pos) {
9165 avox 1210
                        /*
1211
                        if (ls.lastItem == pos && (item(pos)->effects() & ScStyle_SuppressSpace)  )
8922 avox 1212
                        {
1213
                                if (i+1 < lines())
1214
                                {
1215
                                        ls = line(i+1);
1216
                                        result.setRect(ls.x, ls.y - ls.ascent, 1, ls.ascent + ls.descent);
1217
                                }
1218
                                else
1219
                                {
1220
                                        ls = line(lines()-1);
8931 cbradney 1221
                                        const ParagraphStyle& pstyle(paragraphStyle(pos));
1222
                                        result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
8922 avox 1223
                                }
1224
                        }
9165 avox 1225
                        else */
8922 avox 1226
                        {
1227
                                double xpos = ls.x;
1228
                                for (int j = ls.firstItem; j < pos; ++j)
12315 pierre 1229
                                {
1230
                                        if (item(j)->ch == SpecialChars::OBJECT)
1231
                                                xpos += (object(j)->gWidth + object(j)->lineWidth());
1232
                                        else
1233
                                                xpos += item(j)->glyph.wide();
1234
                                }
1235
                                double finalw = 1;
1236
                                if (item(pos)->ch == SpecialChars::OBJECT)
1237
                                        finalw = (object(pos)->gWidth + object(pos)->lineWidth());
1238
                                else
1239
                                        finalw = item(pos)->glyph.wide();
1240
                                result.setRect(xpos, ls.y - ls.ascent, pos < length()? finalw : 1, ls.ascent + ls.descent);
8922 avox 1241
                        }
9165 avox 1242
                        return result;
6144 avox 1243
                }
1244
        }
8922 avox 1245
 
9165 avox 1246
        const ParagraphStyle& pstyle(paragraphStyle(pos));
1247
        if (lines() > 0)
1248
        {
1249
                ls = line(lines()-1);          
1250
                result.setRect(ls.x, ls.y + pstyle.lineSpacing() - ls.ascent, 1, ls.ascent + ls.descent);
1251
        }
1252
        else
1253
        {
1254
                result.setRect(1, 1, 1, pstyle.lineSpacing());
1255
        }      
6144 avox 1256
        return result;
4669 avox 1257
}
1258
 
1259
int StoryText::layout(int startItem)
1260
{
1261
        //FIXME:NLS
1262
        return -1;
1263
}
1264
 
1265
 
1266
uint StoryText::nrOfItems() const
1267
{
1268
        return length();
1269
}
1270
 
1271
 
1272
ScText*  StoryText::item(uint itm)
1273
{
1274
        assert( static_cast<int>(itm) < length() );
5721 avox 1275
        return const_cast<StoryText *>(this)->d->at(itm);
4669 avox 1276
}
1277
 
6144 avox 1278
 
1279
const ScText*  StoryText::item(uint itm) const
1280
{
1281
        assert( static_cast<int>(itm) < length() );
1282
        return const_cast<StoryText *>(this)->d->at(itm);
1283
}
1284
 
7442 avox 1285
 
4669 avox 1286
const QString StoryText::itemText(uint itm) const
1287
{
1288
 
1289
        assert( static_cast<int>(itm) < length() );
1290
 
1291
        return text(itm, 1);
1292
}
1293
 
1294
 
1295
const CharStyle StoryText::itemStyle(uint itm) const
1296
{
1297
        assert( static_cast<int>(itm) < length() );
1298
 
1299
        return charStyle(itm);
1300
}
1301
 
1302
 
1303
int StoryText::startOfItem(uint itm) const
1304
{
1305
        assert( static_cast<int>(itm) < length() );
1306
 
1307
        return itm;
1308
}
1309
 
1310
int StoryText::endOfItem(uint itm) const
1311
{
1312
        assert( static_cast<int>(itm) < length() );
1313
 
1314
        return itm + 1;
1315
}
1316
 
8194 avox 1317
 
8266 avox 1318
using namespace desaxe;
8194 avox 1319
 
8800 jghali 1320
void StoryText::saxx(SaxHandler& handler, const Xml_string& elemtag) const
8194 avox 1321
{
1322
        Xml_attr empty;
1323
        Xml_attr pageno;
1324
        pageno.insert("name", "pgno");
11713 fschmid 1325
        Xml_attr pageco;
1326
        pageco.insert("name", "pgco");
8194 avox 1327
 
8266 avox 1328
        handler.begin(elemtag, empty);
1329
        defaultStyle().saxx(handler, "defaultstyle");
8194 avox 1330
 
8398 avox 1331
        CharStyle lastStyle(charStyle(0));
8194 avox 1332
        bool lastWasPar = true;
1333
        int lastPos = 0;
8398 avox 1334
        handler.begin("p", empty);
1335
        paragraphStyle(0).saxx(handler);
1336
        handler.begin("span", empty);
1337
        lastStyle.saxx(handler);
8194 avox 1338
        for (int i=0; i < length(); ++i)
1339
        {
1340
                const QChar curr(text(i));
1341
                const CharStyle& style(charStyle(i));
1342
 
1343
                if (curr == SpecialChars::OBJECT ||
1344
                        curr == SpecialChars::TAB ||
1345
                        curr == SpecialChars::PARSEP ||
1346
                        curr == SpecialChars::LINEBREAK ||
1347
                        curr == SpecialChars::COLBREAK ||
1348
                        curr == SpecialChars::FRAMEBREAK ||
1349
                        curr == SpecialChars::PAGENUMBER ||
11713 fschmid 1350
                        curr == SpecialChars::PAGECOUNT ||
8194 avox 1351
                        curr.unicode() < 32 ||
1352
                        (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1353
                        curr.unicode() == 0xfffe || curr.unicode() == 0xffff ||
1354
                        style != lastStyle)
1355
                {
1356
                        // something new, write pending chars
1357
                        if  (i - lastPos > 0)
1358
                        {
12093 jghali 1359
                                handler.chars(textWithSmartHyphens(lastPos, i-lastPos));
8194 avox 1360
                        }
1361
                        lastPos = i;
1362
                }
1363
 
1364
                lastWasPar = (curr == SpecialChars::PARSEP);
1365
                if (lastWasPar)
1366
                {
8398 avox 1367
                        handler.end("span");
1368
                        handler.end("p");
1369
                        handler.begin("p", empty);
8913 avox 1370
                        paragraphStyle(i+1).saxx(handler);
8398 avox 1371
                        handler.begin("span", empty);
8194 avox 1372
                }
1373
                else if (curr == SpecialChars::OBJECT && object(i) != NULL)
1374
                {
1375
                        object(i)->saxx(handler);
1376
                }
1377
                else if (curr == SpecialChars::TAB)
1378
                {
8266 avox 1379
                        handler.beginEnd("tab", empty);
8194 avox 1380
                }
1381
                else if (curr == SpecialChars::LINEBREAK)
1382
                {
8266 avox 1383
                        handler.beginEnd("breakline", empty);
8194 avox 1384
                }
1385
                else if (curr == SpecialChars::COLBREAK)
1386
                {
8266 avox 1387
                        handler.beginEnd("breakcol", empty);
8194 avox 1388
                }
1389
                else if (curr == SpecialChars::FRAMEBREAK)
1390
                {
8266 avox 1391
                        handler.beginEnd("breakframe", empty);
8194 avox 1392
                }
1393
                else if (curr == SpecialChars::PAGENUMBER)
1394
                {
8266 avox 1395
                        handler.beginEnd("var", pageno);
8194 avox 1396
                }
11713 fschmid 1397
                else if (curr == SpecialChars::PAGECOUNT)
1398
                {
1399
                        handler.beginEnd("var", pageco);
1400
                }
8194 avox 1401
                else if (curr.unicode() < 32 ||
1402
                                 (0xd800 <= curr.unicode() && curr.unicode() < 0xe000) ||
1403
                                 curr.unicode() == 0xfffe || curr.unicode() == 0xffff)
1404
                {
1405
                        Xml_attr unic;
8266 avox 1406
                        unic.insert("code", toXMLString(curr.unicode()));
1407
                        handler.beginEnd("unicode", unic);
8194 avox 1408
                }
1409
                else if (lastStyle != style)
1410
                {
8398 avox 1411
                        handler.end("span");
1412
                        handler.begin("span", empty);
8194 avox 1413
                        style.saxx(handler);
1414
                        lastStyle = style;
1415
                        continue;
1416
                }
1417
                else
1418
                        continue;
1419
                lastPos = i+1;
1420
        }
1421
 
1422
        if  (length() - lastPos > 0)
12093 jghali 1423
                handler.chars(textWithSmartHyphens(lastPos, length()-lastPos));
8398 avox 1424
        handler.end("span");
1425
        handler.end("p");
8194 avox 1426
 
8398 avox 1427
//      if (!lastWasPar)
1428
//              paragraphStyle(length()-1).saxx(handler);
8194 avox 1429
 
8266 avox 1430
        handler.end(elemtag);
8194 avox 1431
 
1432
}
1433
 
8266 avox 1434
 
1435
class AppendText_body : public Action_body
1436
{
1437
public:
8834 cbradney 1438
        void chars(const Xml_string& txt)
8266 avox 1439
        {
12093 jghali 1440
                QChar chr;
1441
                int   lastPos = 0, len;
8266 avox 1442
                StoryText* obj = this->dig->top<StoryText>();
12093 jghali 1443
                for (int i = 0; i < txt.length(); ++i)
1444
                {
1445
                        chr = txt.at(i);
1446
                        if (chr == SpecialChars::SHYPHEN && i > 0)
1447
                        {
1448
                                int toInsert = i - lastPos;
1449
                                if (toInsert > 0)
1450
                                        obj->insertChars(obj->length(), txt.mid(lastPos, toInsert));
1451
                                len = obj->length();
1452
                                ScText* lastItem = obj->item(len-1);
1453
                                // double SHY means user provided SHY, single SHY is automatic one
1454
                                if (lastItem->effects() & ScStyle_HyphenationPossible)
1455
                                {
1456
                                        lastItem->setEffects(lastItem->effects() & ~ScStyle_HyphenationPossible);
1457
                                        obj->insertChars(len, QString(chr));
1458
                                }
1459
                                else
1460
                                {
1461
                                        lastItem->setEffects(lastItem->effects() | ScStyle_HyphenationPossible);
1462
                                }
1463
                                lastPos = i + 1;
1464
                        }
1465
                }
1466
                if (lastPos < txt.length())
1467
                {
1468
                        QString ins = (lastPos == 0) ? txt : txt.right(txt.length() - lastPos);
1469
                        len = obj->length();
1470
                        obj->insertChars(len, ins);
1471
                }
8266 avox 1472
        }
1473
};
1474
 
1475
struct  AppendText : public MakeAction<AppendText_body>
1476
{};
1477
 
1478
 
1479
class AppendSpecial_body : public Action_body
1480
{
1481
public:
1482
        AppendSpecial_body(QChar sp) : chr(sp) {}
1483
 
8834 cbradney 1484
        void begin(const Xml_string& tag, Xml_attr attr)
8266 avox 1485
        {
1486
                StoryText* obj = this->dig->top<StoryText>();
1487
                Xml_attr::iterator code = attr.find("code");
11713 fschmid 1488
                Xml_attr::iterator name = attr.find("name");
8266 avox 1489
                if (tag == "unicode" && code != attr.end())
1490
                        obj->insertChars(-1, QChar(parseUInt(Xml_data(code))));
11713 fschmid 1491
                else if (tag == "var" && name != attr.end())
1492
                {
1493
                        if (Xml_data(name) == "pgno")
1494
                                obj->insertChars(-1, SpecialChars::PAGENUMBER);
1495
                        else
1496
                                obj->insertChars(-1, SpecialChars::PAGECOUNT);
1497
                }
8266 avox 1498
                else
1499
                        obj->insertChars(-1, chr);
1500
        }
1501
private:
1502
        QChar chr;
1503
};
1504
 
1505
struct AppendSpecial : public MakeAction<AppendSpecial_body, QChar>
1506
{
1507
        AppendSpecial(QChar sp) : MakeAction<AppendSpecial_body, QChar>(sp) {}
1508
        AppendSpecial() : MakeAction<AppendSpecial_body, QChar>(SpecialChars::BLANK) {}
1509
};
1510
 
1511
 
1512
class AppendInlineFrame_body : public Action_body
1513
{
1514
public:
8834 cbradney 1515
        void end(const Xml_string& tag) // this could be a setter if we had StoryText::appendObject() ...
8266 avox 1516
        {
1517
                StoryText* story = this->dig->top<StoryText>(1);
1518
                PageItem* obj = this->dig->top<PageItem>(0);
1519
                story->insertObject(-1, obj);
1520
        }
1521
};
1522
 
1523
struct AppendInlineFrame : public MakeAction<AppendInlineFrame_body>
1524
{};
1525
 
8398 avox 1526
/*
8266 avox 1527
class ApplyStyle_body : public Action_body
1528
{
1529
public:
8834 cbradney 1530
        void end(const Xml_string& tag)
8266 avox 1531
        {
1532
                qDebug("storytext desaxe: apply style");
1533
                StoryText* story = this->dig->top<StoryText>(1);
1534
                ParagraphStyle* obj = this->dig->top<ParagraphStyle>(0);
1535
                story->applyStyle(-1, *obj);
1536
        }
1537
};
1538
 
1539
struct ApplyStyle : public MakeAction<ApplyStyle_body>
1540
{};
1541
 
1542
 
8289 avox 1543
class ApplyCharStyle_body : public Action_body
1544
{
1545
public:
1546
        ApplyCharStyle_body() : storyTag(StoryText::saxxDefaultElem), lastPos(0), lastStyle()
1547
        {}
1548
        ApplyCharStyle_body(const Xml_string& tag) : storyTag(tag), lastPos(0), lastStyle()
1549
        {}
1550
 
8834 cbradney 1551
        void end(const Xml_string& tag)
8289 avox 1552
        {
1553
                qDebug("storytext desaxe: apply charstyle");
1554
                if (tag == CharStyle::saxxDefaultElem)
1555
                {
1556
                        StoryText* story = this->dig->top<StoryText>(1);
1557
                        CharStyle* obj = this->dig->top<CharStyle>(0);
1558
                        int len = story->length();
1559
                        if (len > lastPos && lastStyle != *obj)
1560
                        {
1561
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1562
                                lastPos = len;
1563
                                lastStyle = *obj;
1564
                        }
1565
                }
1566
                else if (tag == StoryText::saxxDefaultElem)
1567
                {
1568
                        StoryText* story = this->dig->top<StoryText>();
1569
                        int len = story->length();
1570
                        if (len > lastPos)
1571
                        {
1572
                                story->applyCharStyle(lastPos, len - lastPos, lastStyle);
1573
                        }
1574
                }
1575
        }
1576
private:
1577
        Xml_string storyTag;
1578
        int lastPos;
1579
        CharStyle lastStyle;
1580
};
1581
 
1582
struct ApplyCharStyle : public MakeAction<ApplyCharStyle_body, const Xml_string&>
1583
{
1584
        ApplyCharStyle() : MakeAction<ApplyCharStyle_body, const Xml_string&>() {}
1585
        ApplyCharStyle(const Xml_string& tag) : MakeAction<ApplyCharStyle_body, const Xml_string&>(tag) {}
1586
};
1587
 
8398 avox 1588
*/
8289 avox 1589
 
8398 avox 1590
class Paragraph_body : public Action_body
1591
{
1592
public:
1593
        Paragraph_body() : lastPos(0), lastStyle(NULL)
1594
        {}
1595
 
1596
        ~Paragraph_body()
1597
        {
1598
                if (lastStyle)
1599
                        delete lastStyle;
1600
        }
1601
 
8834 cbradney 1602
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 1603
        {
1604
                if (tag == "p")
1605
                {
1606
                        StoryText* story = this->dig->top<StoryText>();
9454 avox 1607
//                      qDebug(QString("startpar: %1->%2 %3->NULL").arg(lastPos).arg(story->length()).arg((ulong)lastStyle));
8398 avox 1608
                        lastPos = story->length();
8455 avox 1609
                        if (lastPos > 0) {
8398 avox 1610
                                story->insertChars(-1, SpecialChars::PARSEP);
8455 avox 1611
                                ++lastPos;
1612
                        }
8398 avox 1613
                        if (lastStyle)
1614
                                delete lastStyle;
1615
                        lastStyle = NULL;
1616
                }
1617
        }
1618
 
8834 cbradney 1619
        void end(const Xml_string& tag)
8398 avox 1620
        {
1621
                if (tag == ParagraphStyle::saxxDefaultElem)
1622
                {
1623
                        if (lastStyle)
1624
                                delete lastStyle;
1625
                        lastStyle = this->dig->top<ParagraphStyle>(0);
9454 avox 1626
//                      qDebug(QString("endstyle: %1 %2 %3").arg("?").arg(lastPos).arg((ulong)lastStyle));
8398 avox 1627
                }
1628
                else if (tag == "p")
1629
                {
1630
                        StoryText* story = this->dig->top<StoryText>();
1631
                        int len = story->length();
9454 avox 1632
//                      qDebug(QString("endpar: %1 %2 %3 %4").arg(len).arg(lastPos).arg((ulong)lastStyle).arg(lastStyle? lastStyle->parent() : QString()));
8398 avox 1633
                        if (len > lastPos && lastStyle)
1634
                        {
1635
                                story->applyStyle(lastPos, *lastStyle);
1636
                        }
1637
                }
1638
        }
1639
private:
1640
        int lastPos;
1641
        ParagraphStyle* lastStyle;
1642
};
1643
 
1644
struct Paragraph : public MakeAction<Paragraph_body>
1645
{
1646
        Paragraph() : MakeAction<Paragraph_body>() {}
1647
};
1648
 
1649
 
1650
class SpanAction_body : public Action_body
1651
{
1652
public:
1653
        SpanAction_body() : lastPos(0), lastStyle(NULL)
1654
        {}
1655
 
1656
        ~SpanAction_body()
1657
        {
1658
                if (lastStyle)
1659
                        delete lastStyle;
1660
        }
1661
 
8834 cbradney 1662
        void begin(const Xml_string& tag, Xml_attr attr)
8398 avox 1663
        {
8413 avox 1664
//              qDebug(QString("spanaction: begin %1").arg(tag));
8398 avox 1665
                if (tag == "span")
1666
                {
1667
                        StoryText* story = this->dig->top<StoryText>();
1668
                        lastPos = story->length();
1669
                        if (lastStyle)
1670
                                delete lastStyle;
1671
                        lastStyle = NULL;
1672
                }
1673
        }
1674
 
8834 cbradney 1675
        void end(const Xml_string& tag)
8398 avox 1676
        {
1677
                if (tag == CharStyle::saxxDefaultElem)
8413 avox 1678
//                      qDebug(QString("spanaction: end %1").arg(tag));
8398 avox 1679
                {
1680
                        if (lastStyle)
1681
                                delete lastStyle;
1682
                        lastStyle = this->dig->top<CharStyle>(0);
1683
                }
8413 avox 1684
                else if (tag == "span")
8398 avox 1685
                {
1686
                        StoryText* story = this->dig->top<StoryText>();
1687
                        int len = story->length();
1688
                        if (len > lastPos && lastStyle)
1689
                        {
1690
                                story->applyCharStyle(lastPos, len - lastPos, *lastStyle);
1691
                        }
1692
                }
1693
        }
1694
private:
1695
        int lastPos;
1696
        CharStyle* lastStyle;
1697
};
1698
 
1699
struct SpanAction : public MakeAction<SpanAction_body>
1700
{
1701
        SpanAction() : MakeAction<SpanAction_body>() {}
1702
};
1703
 
1704
 
8266 avox 1705
const Xml_string StoryText::saxxDefaultElem("story");
1706
 
8834 cbradney 1707
void StoryText::desaxeRules(const Xml_string& prefixPattern, Digester& ruleset, Xml_string elemtag)
8266 avox 1708
{
1709
        Xml_string storyPrefix(Digester::concat(prefixPattern, elemtag));
1710
        ruleset.addRule(storyPrefix, Factory<StoryText>());
1711
 
1712
        ParagraphStyle::desaxeRules(storyPrefix, ruleset, "defaultstyle");
1713
        ruleset.addRule(Digester::concat(storyPrefix, "defaultstyle"), SetterWithConversion<StoryText, const ParagraphStyle&, ParagraphStyle>( & StoryText::setDefaultStyle ));
1714
 
8398 avox 1715
        Paragraph paraAction;
1716
        Xml_string paraPrefix(Digester::concat(storyPrefix, "p"));
11713 fschmid 1717
        ruleset.addRule(paraPrefix, paraAction );
8398 avox 1718
        ParagraphStyle::desaxeRules(paraPrefix, ruleset, ParagraphStyle::saxxDefaultElem);
11713 fschmid 1719
        ruleset.addRule(Digester::concat(paraPrefix, ParagraphStyle::saxxDefaultElem), paraAction );
8266 avox 1720
 
8398 avox 1721
        SpanAction spanAction;
1722
        Xml_string spanPrefix(Digester::concat(paraPrefix, "span"));
1723
        ruleset.addRule(spanPrefix, spanAction );
1724
        CharStyle::desaxeRules(spanPrefix, ruleset, CharStyle::saxxDefaultElem);
1725
        ruleset.addRule(Digester::concat(spanPrefix, CharStyle::saxxDefaultElem), spanAction );
1726
 
1727
        ruleset.addRule(spanPrefix, AppendText());
8266 avox 1728
 
8398 avox 1729
        ruleset.addRule(Digester::concat(spanPrefix, "breakline"), AppendSpecial(SpecialChars::LINEBREAK) );
1730
        ruleset.addRule(Digester::concat(spanPrefix, "breakcol"), AppendSpecial(SpecialChars::COLBREAK) );
1731
        ruleset.addRule(Digester::concat(spanPrefix, "breakframe"), AppendSpecial(SpecialChars::FRAMEBREAK) );
11713 fschmid 1732
        ruleset.addRule(Digester::concat(spanPrefix, "tab"), AppendSpecial(SpecialChars::TAB) );
1733
        ruleset.addRule(Digester::concat(spanPrefix, "unicode"), AppendSpecial() );
1734
        ruleset.addRule(Digester::concat(spanPrefix, "var"), AppendSpecial());
8266 avox 1735
 
1736
        //PageItem::desaxeRules(storyPrefix, ruleset); argh, that would be recursive!
11713 fschmid 1737
        ruleset.addRule(Digester::concat(spanPrefix, "item"), AppendInlineFrame() );
8266 avox 1738
 
1739
}