Subversion Repositories Scribus

Rev

Rev 18330 | Rev 18525 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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