~ubuntu-branches/ubuntu/breezy/koffice/breezy

« back to all changes in this revision

Viewing changes to lib/kotext/korichtext.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Ben Burton
  • Date: 2004-05-09 11:33:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040509113300-vfrdadqsvjfuhn3b
Tags: 1:1.3.1-1
* New upstream bugfix release.
* Built against newer imagemagick (closes: #246623).
* Made koffice-libs/kformula recommend/depend on latex-xft-fonts, which
  provides mathematical fonts that the formula editor can use.  Also
  patched the kformula part to make these fonts the default.
* Changed kword menu hint from "WordProcessors" to "Word processors"
  (closes: #246209).
* Spellchecker configuration is now fixed (closes: #221256, #227568).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
** Implementation of the internal Qt classes dealing with rich text
 
3
**
 
4
** Created : 990101
 
5
**
 
6
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
 
7
**
 
8
** This file is part of the kernel module of the Qt GUI Toolkit.
 
9
**
 
10
** This file may be distributed under the terms of the Q Public License
 
11
** as defined by Trolltech AS of Norway and appearing in the file
 
12
** LICENSE.QPL included in the packaging of this file.
 
13
**
 
14
** This file may be distributed and/or modified under the terms of the
 
15
** GNU General Public License version 2 as published by the Free Software
 
16
** Foundation and appearing in the file LICENSE.GPL included in the
 
17
** packaging of this file.
 
18
**
 
19
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
 
20
** licenses may use this file in accordance with the Qt Commercial License
 
21
** Agreement provided with the Software.
 
22
**
 
23
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
24
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
25
**
 
26
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
27
**   information about Qt Commercial License Agreements.
 
28
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
29
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
30
**
 
31
** Contact info@trolltech.com if any conditions of this licensing are
 
32
** not clear to you.
 
33
**
 
34
**********************************************************************/
 
35
 
 
36
#include "korichtext.h"
 
37
#include "kotextformat.h"
 
38
 
 
39
#include <qpaintdevicemetrics.h>
 
40
#include "qdrawutil.h" // for KoTextHorizontalLine
 
41
 
 
42
#include <stdlib.h>
 
43
#include "koparagcounter.h"
 
44
#include "kotextdocument.h"
 
45
#include <kdebug.h>
 
46
#include <kdeversion.h>
 
47
#if ! KDE_IS_VERSION(3,1,90)
 
48
#include <kdebugclasses.h>
 
49
#endif
 
50
#include <kglobal.h>
 
51
#include <klocale.h>
 
52
 
 
53
//#define PARSER_DEBUG
 
54
//#define DEBUG_COLLECTION
 
55
//#define DEBUG_TABLE_RENDERING
 
56
 
 
57
//static KoTextFormatCollection *qFormatCollection = 0;
 
58
 
 
59
#if defined(PARSER_DEBUG)
 
60
static QString debug_indent;
 
61
#endif
 
62
 
 
63
static bool is_printer( QPainter *p )
 
64
{
 
65
    return p && p->device() && p->device()->devType() == QInternal::Printer;
 
66
}
 
67
 
 
68
static inline int scale( int value, QPainter *painter )
 
69
{
 
70
    if ( is_printer( painter ) ) {
 
71
        QPaintDeviceMetrics metrics( painter->device() );
 
72
#if defined(Q_WS_X11)
 
73
        value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
 
74
#elif defined (Q_WS_WIN)
 
75
        int gdc = GetDeviceCaps( GetDC( 0 ), LOGPIXELSY );
 
76
        if ( gdc )
 
77
            value = value * metrics.logicalDpiY() / gdc;
 
78
#elif defined (Q_WS_MAC)
 
79
        value = value * metrics.logicalDpiY() / 75; // ##### FIXME
 
80
#elif defined (Q_WS_QWS)
 
81
        value = value * metrics.logicalDpiY() / 75;
 
82
#endif
 
83
    }
 
84
    return value;
 
85
}
 
86
 
 
87
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
88
 
 
89
void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
 
90
{
 
91
    if ( current < (int)history.count() - 1 ) {
 
92
        QPtrList<KoTextDocCommand> commands;
 
93
        commands.setAutoDelete( FALSE );
 
94
 
 
95
        for( int i = 0; i <= current; ++i ) {
 
96
            commands.insert( i, history.at( 0 ) );
 
97
            history.take( 0 );
 
98
        }
 
99
 
 
100
        commands.append( cmd );
 
101
        history.clear();
 
102
        history = commands;
 
103
        history.setAutoDelete( TRUE );
 
104
    } else {
 
105
        history.append( cmd );
 
106
    }
 
107
 
 
108
    if ( (int)history.count() > steps )
 
109
        history.removeFirst();
 
110
    else
 
111
        ++current;
 
112
}
 
113
 
 
114
KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
 
115
{
 
116
    if ( current > -1 ) {
 
117
        KoTextCursor *c2 = history.at( current )->unexecute( c );
 
118
        --current;
 
119
        return c2;
 
120
    }
 
121
    return 0;
 
122
}
 
123
 
 
124
KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
 
125
{
 
126
    if ( current > -1 ) {
 
127
        if ( current < (int)history.count() - 1 ) {
 
128
            ++current;
 
129
            return history.at( current )->execute( c );
 
130
        }
 
131
    } else {
 
132
        if ( history.count() > 0 ) {
 
133
            ++current;
 
134
            return history.at( current )->execute( c );
 
135
        }
 
136
    }
 
137
    return 0;
 
138
}
 
139
 
 
140
bool KoTextDocCommandHistory::isUndoAvailable()
 
141
{
 
142
    return current > -1;
 
143
}
 
144
 
 
145
bool KoTextDocCommandHistory::isRedoAvailable()
 
146
{
 
147
   return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
 
148
}
 
149
 
 
150
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
151
 
 
152
KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const QMemArray<KoTextStringChar> &str )
 
153
    : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
 
154
{
 
155
    for ( int j = 0; j < (int)text.size(); ++j ) {
 
156
        if ( text[ j ].format() )
 
157
            text[ j ].format()->addRef();
 
158
    }
 
159
}
 
160
 
 
161
/*KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextParag *p, int idx, const QMemArray<KoTextStringChar> &str )
 
162
    : KoTextDocCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
 
163
{
 
164
    for ( int i = 0; i < (int)text.size(); ++i ) {
 
165
        if ( text[ i ].format() )
 
166
            text[ i ].format()->addRef();
 
167
    }
 
168
}*/
 
169
 
 
170
KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
 
171
{
 
172
    for ( int i = 0; i < (int)text.size(); ++i ) {
 
173
        if ( text[ i ].format() )
 
174
            text[ i ].format()->removeRef();
 
175
    }
 
176
    text.resize( 0 );
 
177
}
 
178
 
 
179
KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
 
180
{
 
181
    KoTextParag *s = doc ? doc->paragAt( id ) : parag;
 
182
    if ( !s ) {
 
183
        kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
 
184
        return 0;
 
185
    }
 
186
 
 
187
    cursor.setParag( s );
 
188
    cursor.setIndex( index );
 
189
    int len = text.size();
 
190
    if ( c )
 
191
        *c = cursor;
 
192
    if ( doc ) {
 
193
        doc->setSelectionStart( KoTextDocument::Temp, &cursor );
 
194
        for ( int i = 0; i < len; ++i )
 
195
            cursor.gotoNextLetter();
 
196
        doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
 
197
        doc->removeSelectedText( KoTextDocument::Temp, &cursor );
 
198
        if ( c )
 
199
            *c = cursor;
 
200
    } else {
 
201
        s->remove( index, len );
 
202
    }
 
203
 
 
204
    return c;
 
205
}
 
206
 
 
207
KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
 
208
{
 
209
    KoTextParag *s = doc ? doc->paragAt( id ) : parag;
 
210
    if ( !s ) {
 
211
        kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
 
212
        return 0;
 
213
    }
 
214
 
 
215
    cursor.setParag( s );
 
216
    cursor.setIndex( index );
 
217
    QString str = KoTextString::toString( text );
 
218
    cursor.insert( str, TRUE, &text );
 
219
    cursor.setParag( s );
 
220
    cursor.setIndex( index );
 
221
    if ( c ) {
 
222
        c->setParag( s );
 
223
        c->setIndex( index );
 
224
        for ( int i = 0; i < (int)text.size(); ++i )
 
225
            c->gotoNextLetter();
 
226
    }
 
227
 
 
228
    s = cursor.parag();
 
229
    while ( s ) {
 
230
        s->format();
 
231
        s->setChanged( TRUE );
 
232
        if ( s == c->parag() )
 
233
            break;
 
234
        s = s->next();
 
235
    }
 
236
 
 
237
    return &cursor;
 
238
}
 
239
 
 
240
KoTextDocFormatCommand::KoTextDocFormatCommand( KoTextDocument *d, int sid, int sidx, int eid, int eidx,
 
241
                                        const QMemArray<KoTextStringChar> &old, const KoTextFormat *f, int fl )
 
242
    : KoTextDocCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), oldFormats( old ), flags( fl )
 
243
{
 
244
    format = d->formatCollection()->format( f );
 
245
    for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
 
246
        if ( oldFormats[ j ].format() )
 
247
            oldFormats[ j ].format()->addRef();
 
248
    }
 
249
}
 
250
 
 
251
KoTextDocFormatCommand::~KoTextDocFormatCommand()
 
252
{
 
253
    format->removeRef();
 
254
    for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
 
255
        if ( oldFormats[ j ].format() )
 
256
            oldFormats[ j ].format()->removeRef();
 
257
    }
 
258
}
 
259
 
 
260
KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
 
261
{
 
262
    KoTextParag *sp = doc->paragAt( startId );
 
263
    KoTextParag *ep = doc->paragAt( endId );
 
264
    if ( !sp || !ep )
 
265
        return c;
 
266
 
 
267
    KoTextCursor start( doc );
 
268
    start.setParag( sp );
 
269
    start.setIndex( startIndex );
 
270
    KoTextCursor end( doc );
 
271
    end.setParag( ep );
 
272
    end.setIndex( endIndex );
 
273
 
 
274
    doc->setSelectionStart( KoTextDocument::Temp, &start );
 
275
    doc->setSelectionEnd( KoTextDocument::Temp, &end );
 
276
    doc->setFormat( KoTextDocument::Temp, format, flags );
 
277
    doc->removeSelection( KoTextDocument::Temp );
 
278
    if ( endIndex == ep->length() ) // ### Not in QRT - report sent. Description at http://bugs.kde.org/db/34/34556.html
 
279
        end.gotoLeft();
 
280
    *c = end;
 
281
    return c;
 
282
}
 
283
 
 
284
KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
 
285
{
 
286
    KoTextParag *sp = doc->paragAt( startId );
 
287
    KoTextParag *ep = doc->paragAt( endId );
 
288
    if ( !sp || !ep )
 
289
        return 0;
 
290
 
 
291
    int idx = startIndex;
 
292
    int fIndex = 0;
 
293
    if( !oldFormats.isEmpty()) // ## not in QRT. Not sure how it can happen.
 
294
    {
 
295
    for ( ;; ) {
 
296
        if ( oldFormats.at( fIndex ).c == '\n' ) {
 
297
            if ( idx > 0 ) {
 
298
                if ( idx < sp->length() && fIndex > 0 )
 
299
                    sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
 
300
                if ( sp == ep )
 
301
                    break;
 
302
                sp = sp->next();
 
303
                idx = 0;
 
304
            }
 
305
            fIndex++;
 
306
        }
 
307
        if ( oldFormats.at( fIndex ).format() )
 
308
            sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
 
309
        idx++;
 
310
        fIndex++;
 
311
        if ( fIndex >= (int)oldFormats.size() )
 
312
            break;
 
313
        if ( idx >= sp->length() ) {
 
314
            if ( sp == ep )
 
315
                break;
 
316
            sp = sp->next();
 
317
            idx = 0;
 
318
        }
 
319
    }
 
320
    }
 
321
    KoTextCursor end( doc );
 
322
    end.setParag( ep );
 
323
    end.setIndex( endIndex );
 
324
    if ( endIndex == ep->length() )
 
325
        end.gotoLeft();
 
326
    *c = end;
 
327
    return c;
 
328
}
 
329
 
 
330
KoTextAlignmentCommand::KoTextAlignmentCommand( KoTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa )
 
331
    : KoTextDocCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
 
332
{
 
333
}
 
334
 
 
335
KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
 
336
{
 
337
    KoTextParag *p = doc->paragAt( firstParag );
 
338
    if ( !p )
 
339
        return c;
 
340
    while ( p ) {
 
341
        p->setAlignment( newAlign );
 
342
        if ( p->paragId() == lastParag )
 
343
            break;
 
344
        p = p->next();
 
345
    }
 
346
    return c;
 
347
}
 
348
 
 
349
KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
 
350
{
 
351
    KoTextParag *p = doc->paragAt( firstParag );
 
352
    if ( !p )
 
353
        return c;
 
354
    int i = 0;
 
355
    while ( p ) {
 
356
        if ( i < (int)oldAligns.size() )
 
357
            p->setAlignment( oldAligns.at( i ) );
 
358
        if ( p->paragId() == lastParag )
 
359
            break;
 
360
        p = p->next();
 
361
        ++i;
 
362
    }
 
363
    return c;
 
364
}
 
365
 
 
366
 
 
367
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
368
 
 
369
KoTextCursor::KoTextCursor( KoTextDocument *d )
 
370
    : doc( d ), ox( 0 ), oy( 0 )
 
371
{
 
372
    nested = FALSE;
 
373
    idx = 0;
 
374
    string = doc ? doc->firstParag() : 0;
 
375
    tmpIndex = -1;
 
376
}
 
377
 
 
378
KoTextCursor::KoTextCursor()
 
379
{
 
380
}
 
381
 
 
382
KoTextCursor::KoTextCursor( const KoTextCursor &c )
 
383
{
 
384
    doc = c.doc;
 
385
    ox = c.ox;
 
386
    oy = c.oy;
 
387
    nested = c.nested;
 
388
    idx = c.idx;
 
389
    string = c.string;
 
390
    tmpIndex = c.tmpIndex;
 
391
    indices = c.indices;
 
392
    parags = c.parags;
 
393
    xOffsets = c.xOffsets;
 
394
    yOffsets = c.yOffsets;
 
395
}
 
396
 
 
397
KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
 
398
{
 
399
    doc = c.doc;
 
400
    ox = c.ox;
 
401
    oy = c.oy;
 
402
    nested = c.nested;
 
403
    idx = c.idx;
 
404
    string = c.string;
 
405
    tmpIndex = c.tmpIndex;
 
406
    indices = c.indices;
 
407
    parags = c.parags;
 
408
    xOffsets = c.xOffsets;
 
409
    yOffsets = c.yOffsets;
 
410
 
 
411
    return *this;
 
412
}
 
413
 
 
414
bool KoTextCursor::operator==( const KoTextCursor &c ) const
 
415
{
 
416
    return doc == c.doc && string == c.string && idx == c.idx;
 
417
}
 
418
 
 
419
int KoTextCursor::totalOffsetX() const
 
420
{
 
421
    if ( !nested )
 
422
        return 0;
 
423
    QValueStack<int>::ConstIterator xit = xOffsets.begin();
 
424
    int xoff = ox;
 
425
    for ( ; xit != xOffsets.end(); ++xit )
 
426
        xoff += *xit;
 
427
    return xoff;
 
428
}
 
429
 
 
430
int KoTextCursor::totalOffsetY() const
 
431
{
 
432
    if ( !nested )
 
433
        return 0;
 
434
    QValueStack<int>::ConstIterator yit = yOffsets.begin();
 
435
    int yoff = oy;
 
436
    for ( ; yit != yOffsets.end(); ++yit )
 
437
        yoff += *yit;
 
438
    return yoff;
 
439
}
 
440
 
 
441
void KoTextCursor::gotoIntoNested( const QPoint &globalPos )
 
442
{
 
443
    if ( !doc )
 
444
        return;
 
445
    push();
 
446
    ox = 0;
 
447
    int bl, y;
 
448
    string->lineHeightOfChar( idx, &bl, &y );
 
449
    oy = y + string->rect().y();
 
450
    nested = TRUE;
 
451
    QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() );
 
452
    Q_ASSERT( string->at( idx )->isCustom() );
 
453
    ox = string->at( idx )->x;
 
454
    string->at( idx )->customItem()->enterAt( this, doc, string, idx, ox, oy, p );
 
455
}
 
456
 
 
457
void KoTextCursor::invalidateNested()
 
458
{
 
459
    if ( nested ) {
 
460
        QValueStack<KoTextParag*>::Iterator it = parags.begin();
 
461
        QValueStack<int>::Iterator it2 = indices.begin();
 
462
        for ( ; it != parags.end(); ++it, ++it2 ) {
 
463
            if ( *it == string )
 
464
                continue;
 
465
            (*it)->invalidate( 0 );
 
466
            if ( (*it)->at( *it2 )->isCustom() )
 
467
                (*it)->at( *it2 )->customItem()->invalidate();
 
468
        }
 
469
    }
 
470
}
 
471
 
 
472
void KoTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<KoTextStringChar> *formatting )
 
473
{
 
474
    string->invalidate( idx );
 
475
    tmpIndex = -1;
 
476
    bool justInsert = TRUE;
 
477
    QString s( str );
 
478
#if defined(Q_WS_WIN)
 
479
    if ( checkNewLine )
 
480
        s = s.replace( QRegExp( "\\r" ), "" );
 
481
#endif
 
482
    if ( checkNewLine )
 
483
        justInsert = s.find( '\n' ) == -1;
 
484
    if ( justInsert ) {
 
485
        string->insert( idx, s );
 
486
        if ( formatting ) {
 
487
            for ( int i = 0; i < (int)s.length(); ++i ) {
 
488
                if ( formatting->at( i ).format() ) {
 
489
                    formatting->at( i ).format()->addRef();
 
490
                    string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
 
491
                }
 
492
            }
 
493
        }
 
494
        idx += s.length();
 
495
    } else {
 
496
        QStringList lst = QStringList::split( '\n', s, TRUE );
 
497
        QStringList::Iterator it = lst.begin();
 
498
        //int y = string->rect().y() + string->rect().height();
 
499
        int lastIndex = 0;
 
500
        KoTextFormat *lastFormat = 0;
 
501
        for ( ; it != lst.end(); ) {
 
502
            if ( it != lst.begin() ) {
 
503
                splitAndInsertEmptyParag( FALSE, TRUE );
 
504
                //string->setEndState( -1 );
 
505
#if 0 // no!
 
506
                string->prev()->format( -1, FALSE );
 
507
#endif
 
508
                if ( lastFormat && formatting && string->prev() ) {
 
509
                    lastFormat->addRef();
 
510
                    string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
 
511
                }
 
512
            }
 
513
            lastFormat = 0;
 
514
            QString s = *it;
 
515
            ++it;
 
516
            if ( !s.isEmpty() )
 
517
                string->insert( idx, s );
 
518
            else
 
519
                string->invalidate( 0 );
 
520
 
 
521
            if ( formatting ) {
 
522
                int len = s.length();
 
523
                for ( int i = 0; i < len; ++i ) {
 
524
                    if ( formatting->at( i + lastIndex ).format() ) {
 
525
                        formatting->at( i + lastIndex ).format()->addRef();
 
526
                        string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
 
527
                    }
 
528
                }
 
529
                if ( it != lst.end() )
 
530
                    lastFormat = formatting->at( len + lastIndex ).format();
 
531
                ++len;
 
532
                lastIndex += len;
 
533
            }
 
534
 
 
535
            idx += s.length();
 
536
        }
 
537
#if 0  //// useless and wrong. We'll format things and move them down correctly in KoTextObject::insert().
 
538
        string->format( -1, FALSE );
 
539
        int dy = string->rect().y() + string->rect().height() - y;
 
540
#endif
 
541
        KoTextParag *p = string;
 
542
        p->setParagId( p->prev()->paragId() + 1 );
 
543
        p = p->next();
 
544
        while ( p ) {
 
545
            p->setParagId( p->prev()->paragId() + 1 );
 
546
            //p->move( dy );
 
547
            p->invalidate( 0 );
 
548
            p = p->next();
 
549
        }
 
550
    }
 
551
 
 
552
#if 0  //// useless and slow
 
553
    int h = string->rect().height();
 
554
    string->format( -1, TRUE );
 
555
    if ( h != string->rect().height() )
 
556
        invalidateNested();
 
557
    else if ( doc && doc->parent() )
 
558
        doc->nextDoubleBuffered = TRUE;
 
559
#endif
 
560
}
 
561
 
 
562
void KoTextCursor::gotoLeft()
 
563
{
 
564
    if ( string->string()->isRightToLeft() )
 
565
        gotoNextLetter();
 
566
    else
 
567
        gotoPreviousLetter();
 
568
}
 
569
 
 
570
void KoTextCursor::gotoPreviousLetter()
 
571
{
 
572
    tmpIndex = -1;
 
573
 
 
574
    if ( idx > 0 ) {
 
575
        idx--;
 
576
    } else if ( string->prev() ) {
 
577
        string = string->prev();
 
578
        while ( !string->isVisible() )
 
579
            string = string->prev();
 
580
        idx = string->length() - 1;
 
581
    } else {
 
582
        if ( nested ) {
 
583
            pop();
 
584
            processNesting( Prev );
 
585
            if ( idx == -1 ) {
 
586
                pop();
 
587
                if ( idx > 0 ) {
 
588
                    idx--;
 
589
                } else if ( string->prev() ) {
 
590
                    string = string->prev();
 
591
                    idx = string->length() - 1;
 
592
                }
 
593
            }
 
594
        }
 
595
    }
 
596
 
 
597
    const KoTextStringChar *tsc = string->at( idx );
 
598
    if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
 
599
        processNesting( EnterEnd );
 
600
    }
 
601
}
 
602
 
 
603
void KoTextCursor::push()
 
604
{
 
605
    indices.push( idx );
 
606
    parags.push( string );
 
607
    xOffsets.push( ox );
 
608
    yOffsets.push( oy );
 
609
    nestedStack.push( nested );
 
610
}
 
611
 
 
612
void KoTextCursor::pop()
 
613
{
 
614
    if ( !doc )
 
615
        return;
 
616
    idx = indices.pop();
 
617
    string = parags.pop();
 
618
    ox = xOffsets.pop();
 
619
    oy = yOffsets.pop();
 
620
    //if ( doc->parent() )
 
621
    //doc = doc->parent();
 
622
    nested = nestedStack.pop();
 
623
}
 
624
 
 
625
void KoTextCursor::restoreState()
 
626
{
 
627
    while ( !indices.isEmpty() )
 
628
        pop();
 
629
}
 
630
 
 
631
bool KoTextCursor::place( const QPoint &p, KoTextParag *s, bool link, int *customItemIndex )
 
632
{
 
633
    if ( customItemIndex )
 
634
        *customItemIndex = -1;
 
635
    QPoint pos( p );
 
636
    QRect r;
 
637
    if ( pos.y() < s->rect().y() )
 
638
        pos.setY( s->rect().y() );
 
639
    while ( s ) {
 
640
        r = s->rect();
 
641
        r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
 
642
        if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
 
643
            break;
 
644
        s = s->next();
 
645
    }
 
646
 
 
647
    if ( !s )
 
648
        return FALSE;
 
649
 
 
650
    setParag( s, FALSE );
 
651
    int y = s->rect().y();
 
652
    int lines = s->lines();
 
653
    KoTextStringChar *chr = 0;
 
654
    int index = 0;
 
655
    int i = 0;
 
656
    int cy = 0;
 
657
    //int ch = 0;
 
658
    for ( ; i < lines; ++i ) {
 
659
        chr = s->lineStartOfLine( i, &index );
 
660
        cy = s->lineY( i );
 
661
        //ch = s->lineHeight( i );
 
662
        if ( !chr )
 
663
            return FALSE;
 
664
        if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
 
665
            break;
 
666
    }
 
667
    int nextLine;
 
668
    if ( i < lines - 1 )
 
669
        s->lineStartOfLine( i+1, &nextLine );
 
670
    else
 
671
        nextLine = s->length();
 
672
    i = index;
 
673
    int x = s->rect().x();
 
674
    if ( pos.x() < x )
 
675
        pos.setX( x + 1 );
 
676
    int cw;
 
677
    int curpos = s->length()-1;
 
678
    int dist = 10000000;
 
679
    bool inCustom = FALSE;
 
680
    while ( i < nextLine ) {
 
681
        chr = s->at(i);
 
682
        int cpos = x + chr->x;
 
683
        cw = chr->width; //s->string()->width( i );
 
684
        if ( chr->isCustom() ) {
 
685
             if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
 
686
                  pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
 
687
                if ( customItemIndex )
 
688
                    *customItemIndex = i;
 
689
                if ( chr->customItem()->isNested() )
 
690
                {
 
691
                    curpos = i;
 
692
                    inCustom = TRUE;
 
693
                    break;
 
694
                }
 
695
            }
 
696
        }
 
697
        if( chr->rightToLeft )
 
698
            cpos += cw;
 
699
        int d = cpos - pos.x();
 
700
        bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
 
701
        if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) {
 
702
            dist = QABS( d );
 
703
            if ( !link || pos.x() >= x + chr->x ) {
 
704
                curpos = i;
 
705
            }
 
706
        }
 
707
        i++;
 
708
    }
 
709
    setIndex( curpos, FALSE );
 
710
 
 
711
    if ( inCustom && doc && parag()->at( curpos )->isCustom() && parag()->at( curpos )->customItem()->isNested() ) {
 
712
        KoTextDocument *oldDoc = doc;
 
713
        pos.setX( pos.x() - parag()->at( curpos )->x );
 
714
        gotoIntoNested( pos );
 
715
        if ( oldDoc == doc )
 
716
            return TRUE;
 
717
        QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
 
718
        if ( !place( p, document()->firstParag() ) )
 
719
            pop();
 
720
    }
 
721
    return TRUE;
 
722
}
 
723
 
 
724
void KoTextCursor::processNesting( Operation op )
 
725
{
 
726
    if ( !doc )
 
727
        return;
 
728
    push();
 
729
    ox = string->at( idx )->x;
 
730
    int bl, y;
 
731
    string->lineHeightOfChar( idx, &bl, &y );
 
732
    oy = y + string->rect().y();
 
733
    nested = TRUE;
 
734
    bool ok = FALSE;
 
735
 
 
736
    switch ( op ) {
 
737
    case EnterBegin:
 
738
        ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy );
 
739
        break;
 
740
    case EnterEnd:
 
741
        ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy, TRUE );
 
742
        break;
 
743
    case Next:
 
744
        ok = string->at( idx )->customItem()->next( this, doc, string, idx, ox, oy );
 
745
        break;
 
746
    case Prev:
 
747
        ok = string->at( idx )->customItem()->prev( this, doc, string, idx, ox, oy );
 
748
        break;
 
749
    case Down:
 
750
        ok = string->at( idx )->customItem()->down( this, doc, string, idx, ox, oy );
 
751
        break;
 
752
    case Up:
 
753
        ok = string->at( idx )->customItem()->up( this, doc, string, idx, ox, oy );
 
754
        break;
 
755
    }
 
756
    if ( !ok )
 
757
        pop();
 
758
}
 
759
 
 
760
void KoTextCursor::gotoRight()
 
761
{
 
762
    if ( string->string()->isRightToLeft() )
 
763
        gotoPreviousLetter();
 
764
    else
 
765
        gotoNextLetter();
 
766
}
 
767
 
 
768
void KoTextCursor::gotoNextLetter()
 
769
{
 
770
    tmpIndex = -1;
 
771
 
 
772
    const KoTextStringChar *tsc = string->at( idx );
 
773
    if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
 
774
        processNesting( EnterBegin );
 
775
        return;
 
776
    }
 
777
 
 
778
    if ( idx < string->length() - 1 ) {
 
779
        idx++;
 
780
    } else if ( string->next() ) {
 
781
        string = string->next();
 
782
        while ( !string->isVisible() )
 
783
            string = string->next();
 
784
        idx = 0;
 
785
    } else {
 
786
        if ( nested ) {
 
787
            pop();
 
788
            processNesting( Next );
 
789
            if ( idx == -1 ) {
 
790
                pop();
 
791
                if ( idx < string->length() - 1 ) {
 
792
                    idx++;
 
793
                } else if ( string->next() ) {
 
794
                    string = string->next();
 
795
                    idx = 0;
 
796
                }
 
797
            }
 
798
        }
 
799
    }
 
800
}
 
801
 
 
802
void KoTextCursor::gotoUp()
 
803
{
 
804
    int indexOfLineStart;
 
805
    int line;
 
806
    KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
 
807
    if ( !c )
 
808
        return;
 
809
 
 
810
    tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
 
811
    if ( indexOfLineStart == 0 ) {
 
812
        if ( !string->prev() ) {
 
813
            if ( !nested )
 
814
                return;
 
815
            pop();
 
816
            processNesting( Up );
 
817
            if ( idx == -1 ) {
 
818
                pop();
 
819
                if ( !string->prev() )
 
820
                    return;
 
821
                idx = tmpIndex = 0;
 
822
            } else {
 
823
                tmpIndex = -1;
 
824
                return;
 
825
            }
 
826
        }
 
827
        string = string->prev();
 
828
        while ( !string->isVisible() )
 
829
            string = string->prev();
 
830
        int lastLine = string->lines() - 1;
 
831
        if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
 
832
            return;
 
833
        if ( indexOfLineStart + tmpIndex < string->length() )
 
834
            idx = indexOfLineStart + tmpIndex;
 
835
        else
 
836
            idx = string->length() - 1;
 
837
    } else {
 
838
        --line;
 
839
        int oldIndexOfLineStart = indexOfLineStart;
 
840
        if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
 
841
            return;
 
842
        if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
 
843
            idx = indexOfLineStart + tmpIndex;
 
844
        else
 
845
            idx = oldIndexOfLineStart - 1;
 
846
    }
 
847
}
 
848
 
 
849
void KoTextCursor::gotoDown()
 
850
{
 
851
    int indexOfLineStart;
 
852
    int line;
 
853
    KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
 
854
    if ( !c )
 
855
        return;
 
856
 
 
857
    tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
 
858
    if ( line == string->lines() - 1 ) {
 
859
        if ( !string->next() ) {
 
860
            if ( !nested )
 
861
                return;
 
862
            pop();
 
863
            processNesting( Down );
 
864
            if ( idx == -1 ) {
 
865
                pop();
 
866
                if ( !string->next() )
 
867
                    return;
 
868
                idx = tmpIndex = 0;
 
869
            } else {
 
870
                tmpIndex = -1;
 
871
                return;
 
872
            }
 
873
        }
 
874
        string = string->next();
 
875
        while ( !string->isVisible() )
 
876
            string = string->next();
 
877
        if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
 
878
            return;
 
879
        int end;
 
880
        if ( string->lines() == 1 )
 
881
            end = string->length();
 
882
        else
 
883
            string->lineStartOfLine( 1, &end );
 
884
        if ( indexOfLineStart + tmpIndex < end )
 
885
            idx = indexOfLineStart + tmpIndex;
 
886
        else
 
887
            idx = end - 1;
 
888
    } else {
 
889
        ++line;
 
890
        int end;
 
891
        if ( line == string->lines() - 1 )
 
892
            end = string->length();
 
893
        else
 
894
            string->lineStartOfLine( line + 1, &end );
 
895
        if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
 
896
            return;
 
897
        if ( indexOfLineStart + tmpIndex < end )
 
898
            idx = indexOfLineStart + tmpIndex;
 
899
        else
 
900
            idx = end - 1;
 
901
    }
 
902
}
 
903
 
 
904
void KoTextCursor::gotoLineEnd()
 
905
{
 
906
    tmpIndex = -1;
 
907
    int indexOfLineStart;
 
908
    int line;
 
909
    KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
 
910
    if ( !c )
 
911
        return;
 
912
 
 
913
    if ( line == string->lines() - 1 ) {
 
914
        idx = string->length() - 1;
 
915
    } else {
 
916
        c = string->lineStartOfLine( ++line, &indexOfLineStart );
 
917
        indexOfLineStart--;
 
918
        idx = indexOfLineStart;
 
919
    }
 
920
}
 
921
 
 
922
void KoTextCursor::gotoLineStart()
 
923
{
 
924
    tmpIndex = -1;
 
925
    int indexOfLineStart;
 
926
    int line;
 
927
    KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
 
928
    if ( !c )
 
929
        return;
 
930
 
 
931
    idx = indexOfLineStart;
 
932
}
 
933
 
 
934
void KoTextCursor::gotoHome()
 
935
{
 
936
    tmpIndex = -1;
 
937
    if ( doc )
 
938
        string = doc->firstParag();
 
939
    idx = 0;
 
940
}
 
941
 
 
942
void KoTextCursor::gotoEnd()
 
943
{
 
944
    if ( doc && !doc->lastParag()->isValid() )
 
945
    {
 
946
        kdDebug(32500) << "Last parag, " << doc->lastParag()->paragId() << ", is invalid - aborting gotoEnd() !" << endl;
 
947
        return;
 
948
    }
 
949
 
 
950
    tmpIndex = -1;
 
951
    if ( doc )
 
952
        string = doc->lastParag();
 
953
    idx = string->length() - 1;
 
954
}
 
955
 
 
956
void KoTextCursor::gotoPageUp( int visibleHeight )
 
957
{
 
958
    tmpIndex = -1;
 
959
    KoTextParag *s = string;
 
960
    int h = visibleHeight;
 
961
    int y = s->rect().y();
 
962
    while ( s ) {
 
963
        if ( y - s->rect().y() >= h )
 
964
            break;
 
965
        s = s->prev();
 
966
    }
 
967
 
 
968
    if ( !s && doc )
 
969
        s = doc->firstParag();
 
970
 
 
971
    string = s;
 
972
    idx = 0;
 
973
}
 
974
 
 
975
void KoTextCursor::gotoPageDown( int visibleHeight )
 
976
{
 
977
    tmpIndex = -1;
 
978
    KoTextParag *s = string;
 
979
    int h = visibleHeight;
 
980
    int y = s->rect().y();
 
981
    while ( s ) {
 
982
        if ( s->rect().y() - y >= h )
 
983
            break;
 
984
        s = s->next();
 
985
    }
 
986
 
 
987
    if ( !s && doc ) {
 
988
        s = doc->lastParag();
 
989
        string = s;
 
990
        idx = string->length() - 1;
 
991
        return;
 
992
    }
 
993
 
 
994
    if ( !s->isValid() )
 
995
        return;
 
996
 
 
997
    string = s;
 
998
    idx = 0;
 
999
}
 
1000
 
 
1001
void KoTextCursor::gotoWordRight()
 
1002
{
 
1003
    if ( string->string()->isRightToLeft() )
 
1004
        gotoPreviousWord();
 
1005
    else
 
1006
        gotoNextWord();
 
1007
}
 
1008
 
 
1009
void KoTextCursor::gotoWordLeft()
 
1010
{
 
1011
    if ( string->string()->isRightToLeft() )
 
1012
        gotoNextWord();
 
1013
    else
 
1014
        gotoPreviousWord();
 
1015
}
 
1016
 
 
1017
void KoTextCursor::gotoPreviousWord()
 
1018
{
 
1019
    gotoPreviousLetter();
 
1020
    tmpIndex = -1;
 
1021
    KoTextString *s = string->string();
 
1022
    bool allowSame = FALSE;
 
1023
    if ( idx == ( (int)s->length()-1 ) )
 
1024
        return;
 
1025
    for ( int i = idx; i >= 0; --i ) {
 
1026
        if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
 
1027
             s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
 
1028
            if ( !allowSame )
 
1029
                continue;
 
1030
            idx = i + 1;
 
1031
            return;
 
1032
        }
 
1033
        if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
 
1034
                              s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
 
1035
            allowSame = TRUE;
 
1036
    }
 
1037
    idx = 0;
 
1038
}
 
1039
 
 
1040
void KoTextCursor::gotoNextWord()
 
1041
{
 
1042
    tmpIndex = -1;
 
1043
    KoTextString *s = string->string();
 
1044
    bool allowSame = FALSE;
 
1045
    for ( int i = idx; i < (int)s->length(); ++i ) {
 
1046
        if ( ! ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
 
1047
             s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) {
 
1048
            if ( !allowSame )
 
1049
                continue;
 
1050
            idx = i;
 
1051
            return;
 
1052
        }
 
1053
        if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
 
1054
                              s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';'  ) )
 
1055
            allowSame = TRUE;
 
1056
    }
 
1057
 
 
1058
    if ( idx < ((int)s->length()-1) ) {
 
1059
        gotoLineEnd();
 
1060
    } else if ( string->next() ) {
 
1061
        string = string->next();
 
1062
        while ( !string->isVisible() )
 
1063
            string = string->next();
 
1064
        idx = 0;
 
1065
    } else {
 
1066
        gotoLineEnd();
 
1067
    }
 
1068
}
 
1069
 
 
1070
bool KoTextCursor::atParagStart() const
 
1071
{
 
1072
    return idx == 0;
 
1073
}
 
1074
 
 
1075
bool KoTextCursor::atParagEnd() const
 
1076
{
 
1077
    return idx == string->length() - 1;
 
1078
}
 
1079
 
 
1080
void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
 
1081
{
 
1082
    if ( !doc )
 
1083
        return;
 
1084
    tmpIndex = -1;
 
1085
    KoTextFormat *f = 0;
 
1086
    if ( doc->useFormatCollection() ) {
 
1087
        f = string->at( idx )->format();
 
1088
        if ( idx == string->length() - 1 && idx > 0 )
 
1089
            f = string->at( idx - 1 )->format();
 
1090
        if ( f->isMisspelled() ) {
 
1091
            KoTextFormat fNoMisspelled( *f );
 
1092
            fNoMisspelled.setMisspelled( false );
 
1093
            f = doc->formatCollection()->format( &fNoMisspelled );
 
1094
        }
 
1095
    }
 
1096
 
 
1097
    if ( atParagEnd() ) {
 
1098
        KoTextParag *n = string->next();
 
1099
        KoTextParag *s = doc->createParag( doc, string, n, updateIds );
 
1100
        if ( f )
 
1101
            s->setFormat( 0, 1, f, TRUE );
 
1102
        s->copyParagData( string );
 
1103
        if ( ind ) {
 
1104
            int oi, ni;
 
1105
            s->indent( &oi, &ni );
 
1106
            string = s;
 
1107
            idx = ni;
 
1108
        } else {
 
1109
            string = s;
 
1110
            idx = 0;
 
1111
        }
 
1112
    } else if ( atParagStart() ) {
 
1113
        KoTextParag *p = string->prev();
 
1114
        KoTextParag *s = doc->createParag( doc, p, string, updateIds );
 
1115
        if ( f )
 
1116
            s->setFormat( 0, 1, f, TRUE );
 
1117
        s->copyParagData( string );
 
1118
        if ( ind ) {
 
1119
            s->indent();
 
1120
            s->format();
 
1121
            indent();
 
1122
            string->format();
 
1123
        }
 
1124
    } else {
 
1125
        QString str = string->string()->toString().mid( idx, 0xFFFFFF );
 
1126
        KoTextParag *n = string->next();
 
1127
        KoTextParag *s = doc->createParag( doc, string, n, updateIds );
 
1128
        s->copyParagData( string );
 
1129
        s->remove( 0, 1 );
 
1130
        s->append( str, TRUE );
 
1131
        for ( uint i = 0; i < str.length(); ++i ) {
 
1132
            KoTextStringChar* tsc = string->at( idx + i );
 
1133
            s->setFormat( i, 1, tsc->format(), TRUE );
 
1134
            if ( tsc->isCustom() ) {
 
1135
                KoTextCustomItem * item = tsc->customItem();
 
1136
                s->at( i )->setCustomItem( item );
 
1137
                tsc->loseCustomItem();
 
1138
#if 0
 
1139
                s->addCustomItem();
 
1140
                string->removeCustomItem();
 
1141
#endif
 
1142
                doc->unregisterCustomItem( item, string );
 
1143
                doc->registerCustomItem( item, s );
 
1144
            }
 
1145
        }
 
1146
        string->truncate( idx );
 
1147
        if ( ind ) {
 
1148
            int oi, ni;
 
1149
            s->indent( &oi, &ni );
 
1150
            string = s;
 
1151
            idx = ni;
 
1152
        } else {
 
1153
            string = s;
 
1154
            idx = 0;
 
1155
        }
 
1156
    }
 
1157
 
 
1158
    invalidateNested();
 
1159
}
 
1160
 
 
1161
bool KoTextCursor::remove()
 
1162
{
 
1163
    tmpIndex = -1;
 
1164
    if ( !atParagEnd() ) {
 
1165
        string->remove( idx, 1 );
 
1166
        int h = string->rect().height();
 
1167
        string->format( -1, TRUE );
 
1168
        if ( h != string->rect().height() )
 
1169
            invalidateNested();
 
1170
        //else if ( doc && doc->parent() )
 
1171
        //    doc->nextDoubleBuffered = TRUE;
 
1172
        return FALSE;
 
1173
    } else if ( string->next() ) {
 
1174
        if ( string->length() == 1 ) {
 
1175
            string->next()->setPrev( string->prev() );
 
1176
            if ( string->prev() )
 
1177
                string->prev()->setNext( string->next() );
 
1178
            KoTextParag *p = string->next();
 
1179
            delete string;
 
1180
            string = p;
 
1181
            string->invalidate( 0 );
 
1182
            //// kotext
 
1183
            string->invalidateCounters();
 
1184
            ////
 
1185
            KoTextParag *s = string;
 
1186
            while ( s ) {
 
1187
                s->id = s->p ? s->p->id + 1 : 0;
 
1188
                //s->state = -1;
 
1189
                //s->needPreProcess = TRUE;
 
1190
                s->changed = TRUE;
 
1191
                s = s->n;
 
1192
            }
 
1193
            string->format();
 
1194
        } else {
 
1195
            string->join( string->next() );
 
1196
        }
 
1197
        invalidateNested();
 
1198
        return TRUE;
 
1199
    }
 
1200
    return FALSE;
 
1201
}
 
1202
 
 
1203
void KoTextCursor::killLine()
 
1204
{
 
1205
    if ( atParagEnd() )
 
1206
        return;
 
1207
    string->remove( idx, string->length() - idx - 1 );
 
1208
    int h = string->rect().height();
 
1209
    string->format( -1, TRUE );
 
1210
    if ( h != string->rect().height() )
 
1211
        invalidateNested();
 
1212
    //else if ( doc && doc->parent() )
 
1213
    //doc->nextDoubleBuffered = TRUE;
 
1214
}
 
1215
 
 
1216
void KoTextCursor::indent()
 
1217
{
 
1218
    int oi = 0, ni = 0;
 
1219
    string->indent( &oi, &ni );
 
1220
    if ( oi == ni )
 
1221
        return;
 
1222
 
 
1223
    if ( idx >= oi )
 
1224
        idx += ni - oi;
 
1225
    else
 
1226
        idx = ni;
 
1227
}
 
1228
 
 
1229
void KoTextCursor::setDocument( KoTextDocument *d )
 
1230
{
 
1231
    doc = d;
 
1232
    string = d->firstParag();
 
1233
    idx = 0;
 
1234
    nested = FALSE;
 
1235
    restoreState();
 
1236
    tmpIndex = -1;
 
1237
}
 
1238
 
 
1239
 
 
1240
int KoTextCursor::x() const
 
1241
{
 
1242
    KoTextStringChar *c = string->at( idx );
 
1243
    int curx = c->x;
 
1244
    if ( c->rightToLeft )
 
1245
        curx += c->width; //string->string()->width( idx );
 
1246
    return curx;
 
1247
}
 
1248
 
 
1249
int KoTextCursor::y() const
 
1250
{
 
1251
    int dummy, line;
 
1252
    string->lineStartOfChar( idx, &dummy, &line );
 
1253
    return string->lineY( line );
 
1254
}
 
1255
 
 
1256
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
1257
 
 
1258
// TODO: move to kotextdocument.cpp
 
1259
 
 
1260
void KoTextDocument::init()
 
1261
{
 
1262
#if defined(PARSER_DEBUG)
 
1263
    kdDebug(32500) << debug_indent + "new KoTextDocument (%p)", this << endl;
 
1264
#endif
 
1265
    //oTextValid = TRUE;
 
1266
    //if ( par )
 
1267
//      par->insertChild( this );
 
1268
    //pProcessor = 0;
 
1269
    useFC = TRUE;
 
1270
    pFormatter = 0;
 
1271
    indenter = 0;
 
1272
    fParag = 0;
 
1273
    m_pageBreakEnabled = false;
 
1274
    //minw = 0;
 
1275
    align = Qt::AlignAuto;
 
1276
    nSelections = 1;
 
1277
    addMargs = FALSE;
 
1278
 
 
1279
#if 0
 
1280
    preferRichText = FALSE;
 
1281
    txtFormat = Qt::AutoText;
 
1282
    focusIndicator.parag = 0;
 
1283
    minwParag = 0;
 
1284
    sheet_ = QStyleSheet::defaultSheet();
 
1285
    factory_ = QMimeSourceFactory::defaultFactory();
 
1286
    contxt = QString::null;
 
1287
    fCollection->setStyleSheet( sheet_ );
 
1288
#endif
 
1289
 
 
1290
    underlLinks = TRUE;
 
1291
    backBrush = 0;
 
1292
    buf_pixmap = 0;
 
1293
    //nextDoubleBuffered = FALSE;
 
1294
 
 
1295
    //if ( par )
 
1296
//      withoutDoubleBuffer = par->withoutDoubleBuffer;
 
1297
//    else
 
1298
        withoutDoubleBuffer = FALSE;
 
1299
 
 
1300
    lParag = fParag = createParag( this, 0, 0 );
 
1301
    tmpCursor = 0;
 
1302
 
 
1303
    //cx = 0;
 
1304
    //cy = 2;
 
1305
    //if ( par )
 
1306
        cx = cy = 0;
 
1307
    //cw = 600; // huh?
 
1308
    //vw = 0;
 
1309
    flow_ = new KoTextFlow;
 
1310
    //flow_->setWidth( cw );
 
1311
 
 
1312
    leftmargin = 0; // 4 in QRT
 
1313
    rightmargin = 0; // 4 in QRT
 
1314
 
 
1315
    selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
 
1316
    selectionText[ Standard ] = TRUE;
 
1317
    commandHistory = new KoTextDocCommandHistory( 100 );
 
1318
    tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
 
1319
}
 
1320
 
 
1321
KoTextDocument::~KoTextDocument()
 
1322
{
 
1323
    //if ( par )
 
1324
//      par->removeChild( this );
 
1325
    //// kotext
 
1326
    m_bDestroying = true;
 
1327
    clear( false );
 
1328
    ////
 
1329
    delete commandHistory;
 
1330
    delete flow_;
 
1331
    //if ( !par )
 
1332
        delete pFormatter;
 
1333
    delete fCollection;
 
1334
    //delete pProcessor;
 
1335
    delete buf_pixmap;
 
1336
    delete indenter;
 
1337
    delete backBrush;
 
1338
    if ( tArray )
 
1339
        delete [] tArray;
 
1340
}
 
1341
 
 
1342
void KoTextDocument::clear( bool createEmptyParag )
 
1343
{
 
1344
    if ( flow_ )
 
1345
        flow_->clear();
 
1346
    while ( fParag ) {
 
1347
        KoTextParag *p = fParag->next();
 
1348
        delete fParag;
 
1349
        fParag = p;
 
1350
    }
 
1351
    fParag = lParag = 0;
 
1352
    if ( createEmptyParag )
 
1353
        fParag = lParag = createParag( this );
 
1354
    selections.clear();
 
1355
}
 
1356
 
 
1357
/*
 
1358
   // Looks slow!
 
1359
int KoTextDocument::widthUsed() const
 
1360
{
 
1361
    KoTextParag *p = fParag;
 
1362
    int w = 0;
 
1363
    while ( p ) {
 
1364
        int a = p->alignment();
 
1365
        p->setAlignment( Qt::AlignLeft );
 
1366
        p->invalidate( 0 );
 
1367
        p->format();
 
1368
        w = QMAX( w, p->rect().width() );
 
1369
        p->setAlignment( a );
 
1370
        p->invalidate( 0 );
 
1371
        p = p->next();
 
1372
    }
 
1373
    return w;
 
1374
}
 
1375
*/
 
1376
 
 
1377
int KoTextDocument::height() const
 
1378
{
 
1379
    int h = 0;
 
1380
    if ( lParag )
 
1381
        h = lParag->rect().top() + lParag->rect().height() + 1;
 
1382
    //int fh = flow_->boundingRect().height();
 
1383
    //return QMAX( h, fh );
 
1384
    return h;
 
1385
}
 
1386
 
 
1387
 
 
1388
 
 
1389
KoTextParag *KoTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
 
1390
{
 
1391
    return new KoTextParag( d, pr, nx, updateIds );
 
1392
}
 
1393
 
 
1394
#if 0
 
1395
bool KoTextDocument::setMinimumWidth( int w, KoTextParag *p )
 
1396
{
 
1397
    if ( w == -1 ) {
 
1398
        minw = 0;
 
1399
        p = 0;
 
1400
    }
 
1401
    if ( p == minwParag ) {
 
1402
        minw = w;
 
1403
        emit minimumWidthChanged( minw );
 
1404
    } else if ( w > minw ) {
 
1405
        minw = w;
 
1406
        minwParag = p;
 
1407
        emit minimumWidthChanged( minw );
 
1408
    }
 
1409
    cw = QMAX( minw, cw );
 
1410
    return TRUE;
 
1411
}
 
1412
#endif
 
1413
 
 
1414
void KoTextDocument::setPlainText( const QString &text )
 
1415
{
 
1416
    clear();
 
1417
    //preferRichText = FALSE;
 
1418
    //oTextValid = TRUE;
 
1419
    //oText = text;
 
1420
 
 
1421
    int lastNl = 0;
 
1422
    int nl = text.find( '\n' );
 
1423
    if ( nl == -1 ) {
 
1424
        lParag = createParag( this, lParag, 0 );
 
1425
        if ( !fParag )
 
1426
            fParag = lParag;
 
1427
        QString s = text;
 
1428
        if ( !s.isEmpty() ) {
 
1429
            if ( s[ (int)s.length() - 1 ] == '\r' )
 
1430
                s.remove( s.length() - 1, 1 );
 
1431
            lParag->append( s );
 
1432
        }
 
1433
    } else {
 
1434
        for (;;) {
 
1435
            lParag = createParag( this, lParag, 0 );
 
1436
            if ( !fParag )
 
1437
                fParag = lParag;
 
1438
            QString s = text.mid( lastNl, nl - lastNl );
 
1439
            if ( !s.isEmpty() ) {
 
1440
                if ( s[ (int)s.length() - 1 ] == '\r' )
 
1441
                    s.remove( s.length() - 1, 1 );
 
1442
                lParag->append( s );
 
1443
            }
 
1444
            if ( nl == 0xffffff )
 
1445
                break;
 
1446
            lastNl = nl + 1;
 
1447
            nl = text.find( '\n', nl + 1 );
 
1448
            if ( nl == -1 )
 
1449
                nl = 0xffffff;
 
1450
        }
 
1451
    }
 
1452
    if ( !lParag )
 
1453
        lParag = fParag = createParag( this, 0, 0 );
 
1454
}
 
1455
 
 
1456
void KoTextDocument::setText( const QString &text, const QString & /*context*/ )
 
1457
{
 
1458
    //focusIndicator.parag = 0;
 
1459
    selections.clear();
 
1460
#if 0
 
1461
    if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
 
1462
         txtFormat == Qt::RichText )
 
1463
        setRichText( text, context );
 
1464
    else
 
1465
#endif
 
1466
        setPlainText( text );
 
1467
}
 
1468
 
 
1469
QString KoTextDocument::plainText( KoTextParag *p ) const
 
1470
{
 
1471
    if ( !p ) {
 
1472
        QString buffer;
 
1473
        QString s;
 
1474
        KoTextParag *p = fParag;
 
1475
        while ( p ) {
 
1476
            s = p->string()->toString();
 
1477
            s.remove( s.length() - 1, 1 );
 
1478
            if ( p->next() )
 
1479
                s += "\n";
 
1480
            buffer += s;
 
1481
            p = p->next();
 
1482
        }
 
1483
        return buffer;
 
1484
    } else {
 
1485
        return p->string()->toString();
 
1486
    }
 
1487
}
 
1488
 
 
1489
QString KoTextDocument::richText( KoTextParag * ) const
 
1490
{
 
1491
    QString s;
 
1492
    // TODO update from QRT if this code is needed
 
1493
    return s;
 
1494
}
 
1495
 
 
1496
QString KoTextDocument::text() const
 
1497
{
 
1498
    if ( plainText().simplifyWhiteSpace().isEmpty() )
 
1499
        return QString("");
 
1500
    //if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
 
1501
    //    return richText();
 
1502
    return plainText( 0 );
 
1503
}
 
1504
 
 
1505
QString KoTextDocument::text( int parag ) const
 
1506
{
 
1507
    KoTextParag *p = paragAt( parag );
 
1508
    if ( !p )
 
1509
        return QString::null;
 
1510
 
 
1511
    //if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
 
1512
    //    return richText( p );
 
1513
    //else
 
1514
        return plainText( p );
 
1515
}
 
1516
 
 
1517
void KoTextDocument::invalidate()
 
1518
{
 
1519
    KoTextParag *s = fParag;
 
1520
    while ( s ) {
 
1521
        s->invalidate( 0 );
 
1522
        s = s->next();
 
1523
    }
 
1524
}
 
1525
 
 
1526
void KoTextDocument::informParagraphDeleted( KoTextParag* parag )
 
1527
{
 
1528
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.begin();
 
1529
    for ( ; it != selections.end(); ++it )
 
1530
    {
 
1531
        if ( (*it).startCursor.parag() == parag ) {
 
1532
            if ( parag->prev() ) {
 
1533
                KoTextParag* prevP = parag->prev();
 
1534
                (*it).startCursor.setParag( prevP );
 
1535
                (*it).startCursor.setIndex( prevP->length()-1 );
 
1536
            } else
 
1537
                (*it).startCursor.setParag( parag->next() ); // sets index to 0
 
1538
        }
 
1539
        if ( (*it).endCursor.parag() == parag ) {
 
1540
            if ( parag->prev() ) {
 
1541
                KoTextParag* prevP = parag->prev();
 
1542
                (*it).endCursor.setParag( prevP );
 
1543
                (*it).endCursor.setIndex( prevP->length()-1 );
 
1544
            } else
 
1545
                (*it).endCursor.setParag( parag->next() ); // sets index to 0
 
1546
        }
 
1547
    }
 
1548
    emit paragraphDeleted( parag );
 
1549
}
 
1550
 
 
1551
void KoTextDocument::selectionStart( int id, int &paragId, int &index )
 
1552
{
 
1553
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1554
    if ( it == selections.end() )
 
1555
        return;
 
1556
    KoTextDocumentSelection &sel = *it;
 
1557
    paragId = !sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
 
1558
    index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
 
1559
}
 
1560
 
 
1561
KoTextCursor KoTextDocument::selectionStartCursor( int id)
 
1562
{
 
1563
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1564
    if ( it == selections.end() )
 
1565
        return KoTextCursor( this );
 
1566
    KoTextDocumentSelection &sel = *it;
 
1567
    if ( sel.swapped )
 
1568
        return sel.endCursor;
 
1569
    return sel.startCursor;
 
1570
}
 
1571
 
 
1572
KoTextCursor KoTextDocument::selectionEndCursor( int id)
 
1573
{
 
1574
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1575
    if ( it == selections.end() )
 
1576
        return KoTextCursor( this );
 
1577
    KoTextDocumentSelection &sel = *it;
 
1578
    if ( !sel.swapped )
 
1579
        return sel.endCursor;
 
1580
    return sel.startCursor;
 
1581
}
 
1582
 
 
1583
void KoTextDocument::selectionEnd( int id, int &paragId, int &index )
 
1584
{
 
1585
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1586
    if ( it == selections.end() )
 
1587
        return;
 
1588
    KoTextDocumentSelection &sel = *it;
 
1589
    paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
 
1590
    index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
 
1591
}
 
1592
 
 
1593
bool KoTextDocument::isSelectionSwapped( int id )
 
1594
{
 
1595
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1596
    if ( it == selections.end() )
 
1597
        return false;
 
1598
    KoTextDocumentSelection &sel = *it;
 
1599
    return sel.swapped;
 
1600
}
 
1601
 
 
1602
KoTextParag *KoTextDocument::selectionStart( int id )
 
1603
{
 
1604
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1605
    if ( it == selections.end() )
 
1606
        return 0;
 
1607
    KoTextDocumentSelection &sel = *it;
 
1608
    if ( sel.startCursor.parag()->paragId() <  sel.endCursor.parag()->paragId() )
 
1609
        return sel.startCursor.parag();
 
1610
    return sel.endCursor.parag();
 
1611
}
 
1612
 
 
1613
KoTextParag *KoTextDocument::selectionEnd( int id )
 
1614
{
 
1615
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1616
    if ( it == selections.end() )
 
1617
        return 0;
 
1618
    KoTextDocumentSelection &sel = *it;
 
1619
    if ( sel.startCursor.parag()->paragId() >  sel.endCursor.parag()->paragId() )
 
1620
        return sel.startCursor.parag();
 
1621
    return sel.endCursor.parag();
 
1622
}
 
1623
 
 
1624
void KoTextDocument::addSelection( int id )
 
1625
{
 
1626
    nSelections = QMAX( nSelections, id + 1 );
 
1627
}
 
1628
 
 
1629
static void setSelectionEndHelper( int id, KoTextDocumentSelection &sel, KoTextCursor &start, KoTextCursor &end )
 
1630
{
 
1631
    KoTextCursor c1 = start;
 
1632
    KoTextCursor c2 = end;
 
1633
    if ( sel.swapped ) {
 
1634
        c1 = end;
 
1635
        c2 = start;
 
1636
    }
 
1637
 
 
1638
    c1.parag()->removeSelection( id );
 
1639
    c2.parag()->removeSelection( id );
 
1640
    if ( c1.parag() != c2.parag() ) {
 
1641
        c1.parag()->setSelection( id, c1.index(), c1.parag()->length() - 1 );
 
1642
        c2.parag()->setSelection( id, 0, c2.index() );
 
1643
    } else {
 
1644
        c1.parag()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
 
1645
    }
 
1646
 
 
1647
    sel.startCursor = start;
 
1648
    sel.endCursor = end;
 
1649
    if ( sel.startCursor.parag() == sel.endCursor.parag() )
 
1650
        sel.swapped = sel.startCursor.index() > sel.endCursor.index();
 
1651
}
 
1652
 
 
1653
bool KoTextDocument::setSelectionEnd( int id, KoTextCursor *cursor )
 
1654
{
 
1655
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1656
    if ( it == selections.end() )
 
1657
        return FALSE;
 
1658
    KoTextDocumentSelection &sel = *it;
 
1659
 
 
1660
    KoTextCursor start = sel.startCursor;
 
1661
    KoTextCursor end = *cursor;
 
1662
 
 
1663
    if ( start == end ) {
 
1664
        removeSelection( id );
 
1665
        setSelectionStart( id, cursor );
 
1666
        return TRUE;
 
1667
    }
 
1668
 
 
1669
    if ( sel.endCursor.parag() == end.parag() ) {
 
1670
        setSelectionEndHelper( id, sel, start, end );
 
1671
        return TRUE;
 
1672
    }
 
1673
 
 
1674
    bool inSelection = FALSE;
 
1675
    KoTextCursor c( this );
 
1676
    KoTextCursor tmp = sel.startCursor;
 
1677
    if ( sel.swapped )
 
1678
        tmp = sel.endCursor;
 
1679
    tmp.restoreState();
 
1680
    KoTextCursor tmp2 = *cursor;
 
1681
    tmp2.restoreState();
 
1682
    c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
 
1683
    KoTextCursor old;
 
1684
    bool hadStart = FALSE;
 
1685
    bool hadEnd = FALSE;
 
1686
    bool hadStartParag = FALSE;
 
1687
    bool hadEndParag = FALSE;
 
1688
    bool hadOldStart = FALSE;
 
1689
    bool hadOldEnd = FALSE;
 
1690
    bool leftSelection = FALSE;
 
1691
    sel.swapped = FALSE;
 
1692
    for ( ;; ) {
 
1693
        if ( c == start )
 
1694
            hadStart = TRUE;
 
1695
        if ( c == end )
 
1696
            hadEnd = TRUE;
 
1697
        if ( c.parag() == start.parag() )
 
1698
            hadStartParag = TRUE;
 
1699
        if ( c.parag() == end.parag() )
 
1700
            hadEndParag = TRUE;
 
1701
        if ( c == sel.startCursor )
 
1702
            hadOldStart = TRUE;
 
1703
        if ( c == sel.endCursor )
 
1704
            hadOldEnd = TRUE;
 
1705
 
 
1706
        if ( !sel.swapped &&
 
1707
             ( hadEnd && !hadStart ||
 
1708
               hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
 
1709
            sel.swapped = TRUE;
 
1710
 
 
1711
        if ( c == end && hadStartParag ||
 
1712
             c == start && hadEndParag ) {
 
1713
            KoTextCursor tmp = c;
 
1714
            tmp.restoreState();
 
1715
            if ( tmp.parag() != c.parag() ) {
 
1716
                int sstart = tmp.parag()->selectionStart( id );
 
1717
                tmp.parag()->removeSelection( id );
 
1718
                tmp.parag()->setSelection( id, sstart, tmp.index() );
 
1719
            }
 
1720
        }
 
1721
 
 
1722
        if ( inSelection &&
 
1723
             ( c == end && hadStart || c == start && hadEnd ) )
 
1724
             leftSelection = TRUE;
 
1725
        else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
 
1726
            inSelection = TRUE;
 
1727
 
 
1728
        bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
 
1729
        c.parag()->removeSelection( id );
 
1730
        if ( inSelection ) {
 
1731
            if ( c.parag() == start.parag() && start.parag() == end.parag() ) {
 
1732
                c.parag()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) );
 
1733
            } else if ( c.parag() == start.parag() && !hadEndParag ) {
 
1734
                c.parag()->setSelection( id, start.index(), c.parag()->length() - 1 );
 
1735
            } else if ( c.parag() == end.parag() && !hadStartParag ) {
 
1736
                c.parag()->setSelection( id, end.index(), c.parag()->length() - 1 );
 
1737
            } else if ( c.parag() == end.parag() && hadEndParag ) {
 
1738
                c.parag()->setSelection( id, 0, end.index() );
 
1739
            } else if ( c.parag() == start.parag() && hadStartParag ) {
 
1740
                c.parag()->setSelection( id, 0, start.index() );
 
1741
            } else {
 
1742
                c.parag()->setSelection( id, 0, c.parag()->length() - 1 );
 
1743
            }
 
1744
        }
 
1745
 
 
1746
        if ( leftSelection )
 
1747
            inSelection = FALSE;
 
1748
 
 
1749
        old = c;
 
1750
        c.gotoNextLetter();
 
1751
        if ( old == c || noSelectionAnymore )
 
1752
            break;
 
1753
    }
 
1754
 
 
1755
    if ( !sel.swapped )
 
1756
        sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
 
1757
 
 
1758
    sel.startCursor = start;
 
1759
    sel.endCursor = end;
 
1760
    if ( sel.startCursor.parag() == sel.endCursor.parag() )
 
1761
        sel.swapped = sel.startCursor.index() > sel.endCursor.index();
 
1762
 
 
1763
    setSelectionEndHelper( id, sel, start, end );
 
1764
 
 
1765
    return TRUE;
 
1766
}
 
1767
 
 
1768
void KoTextDocument::selectAll( int id )
 
1769
{
 
1770
    removeSelection( id );
 
1771
 
 
1772
    KoTextDocumentSelection sel;
 
1773
    sel.swapped = FALSE;
 
1774
    KoTextCursor c( this );
 
1775
 
 
1776
    c.setParag( fParag );
 
1777
    c.setIndex( 0 );
 
1778
    sel.startCursor = c;
 
1779
 
 
1780
    c.setParag( lParag );
 
1781
    c.setIndex( lParag->length() - 1 );
 
1782
    sel.endCursor = c;
 
1783
 
 
1784
    KoTextParag *p = fParag;
 
1785
    while ( p ) {
 
1786
        p->setSelection( id, 0, p->length() - 1 );
 
1787
#ifdef QTEXTTABLE_AVAILABLE
 
1788
        for ( int i = 0; i < (int)p->length(); ++i ) {
 
1789
            if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) {
 
1790
                KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
 
1791
                QPtrList<KoTextTableCell> tableCells = t->tableCells();
 
1792
                for ( KoTextTableCell *c = tableCells.first(); c; c = tableCells.next() )
 
1793
                    c->richText()->selectAll( id );
 
1794
            }
 
1795
        }
 
1796
#endif
 
1797
        p = p->next();
 
1798
    }
 
1799
 
 
1800
    selections.insert( id, sel );
 
1801
}
 
1802
 
 
1803
bool KoTextDocument::removeSelection( int id )
 
1804
{
 
1805
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1806
    if ( it == selections.end() )
 
1807
        return FALSE;
 
1808
 
 
1809
    KoTextDocumentSelection &sel = *it;
 
1810
 
 
1811
    KoTextCursor c( this );
 
1812
    KoTextCursor tmp = sel.startCursor;
 
1813
    if ( sel.swapped )
 
1814
        tmp = sel.endCursor;
 
1815
    tmp.restoreState();
 
1816
    c.setParag( tmp.parag() );
 
1817
    KoTextCursor old;
 
1818
    bool hadStart = FALSE;
 
1819
    bool hadEnd = FALSE;
 
1820
    KoTextParag *lastParag = 0;
 
1821
    bool leftSelection = FALSE;
 
1822
    bool inSelection = FALSE;
 
1823
    sel.swapped = FALSE;
 
1824
    for ( ;; ) {
 
1825
        if ( !hadStart && c.parag() == sel.startCursor.parag() )
 
1826
            hadStart = TRUE;
 
1827
        if ( !hadEnd && c.parag() == sel.endCursor.parag() )
 
1828
            hadEnd = TRUE;
 
1829
 
 
1830
        if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
 
1831
            inSelection = TRUE;
 
1832
 
 
1833
        if ( inSelection &&
 
1834
             ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) {
 
1835
             leftSelection = TRUE;
 
1836
             inSelection = FALSE;
 
1837
        }
 
1838
 
 
1839
        bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
 
1840
 
 
1841
        if ( lastParag != c.parag() )
 
1842
            c.parag()->removeSelection( id );
 
1843
 
 
1844
        old = c;
 
1845
        lastParag = c.parag();
 
1846
        c.gotoNextLetter();
 
1847
        if ( old == c || noSelectionAnymore )
 
1848
            break;
 
1849
    }
 
1850
 
 
1851
    selections.remove( id );
 
1852
    return TRUE;
 
1853
}
 
1854
 
 
1855
QString KoTextDocument::selectedText( int id, bool withCustom ) const
 
1856
{
 
1857
    // ######## TODO: look at textFormat() and return rich text or plain text (like the text() method!)
 
1858
    QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
 
1859
    if ( it == selections.end() )
 
1860
        return QString::null;
 
1861
 
 
1862
    KoTextDocumentSelection sel = *it;
 
1863
 
 
1864
 
 
1865
    KoTextCursor c1 = sel.startCursor;
 
1866
    KoTextCursor c2 = sel.endCursor;
 
1867
    if ( sel.swapped ) {
 
1868
        c2 = sel.startCursor;
 
1869
        c1 = sel.endCursor;
 
1870
    }
 
1871
 
 
1872
    c2.restoreState();
 
1873
    c1.restoreState();
 
1874
 
 
1875
    if ( c1.parag() == c2.parag() ) {
 
1876
        QString s;
 
1877
        KoTextParag *p = c1.parag();
 
1878
        int end = c2.index();
 
1879
        if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
 
1880
            ++end;
 
1881
        if ( !withCustom || !p->customItems() ) {
 
1882
            s += p->string()->toString().mid( c1.index(), end - c1.index() );
 
1883
        } else {
 
1884
            for ( int i = c1.index(); i < end; ++i ) {
 
1885
                if ( p->at( i )->isCustom() ) {
 
1886
#ifdef QTEXTTABLE_AVAILABLE
 
1887
                    if ( p->at( i )->customItem()->isNested() ) {
 
1888
                        s += "\n";
 
1889
                        KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
 
1890
                        QPtrList<KoTextTableCell> cells = t->tableCells();
 
1891
                        for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
 
1892
                            s += c->richText()->plainText() + "\n";
 
1893
                        s += "\n";
 
1894
                    }
 
1895
#endif
 
1896
                } else {
 
1897
                    s += p->at( i )->c;
 
1898
                }
 
1899
                s += "\n";
 
1900
            }
 
1901
        }
 
1902
        return s;
 
1903
    }
 
1904
 
 
1905
    QString s;
 
1906
    KoTextParag *p = c1.parag();
 
1907
    int start = c1.index();
 
1908
    while ( p ) {
 
1909
        int end = p == c2.parag() ? c2.index() : p->length() - 1;
 
1910
        if ( p == c2.parag() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
 
1911
            ++end;
 
1912
        if ( !withCustom || !p->customItems() ) {
 
1913
            s += p->string()->toString().mid( start, end - start );
 
1914
            if ( p != c2.parag() )
 
1915
                s += "\n";
 
1916
        } else {
 
1917
            for ( int i = start; i < end; ++i ) {
 
1918
                if ( p->at( i )->isCustom() ) {
 
1919
#ifdef QTEXTTABLE_AVAILABLE
 
1920
                    if ( p->at( i )->customItem()->isNested() ) {
 
1921
                        s += "\n";
 
1922
                        KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
 
1923
                        QPtrList<KoTextTableCell> cells = t->tableCells();
 
1924
                        for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
 
1925
                            s += c->richText()->plainText() + "\n";
 
1926
                        s += "\n";
 
1927
                    }
 
1928
#endif
 
1929
                } else {
 
1930
                    s += p->at( i )->c;
 
1931
                }
 
1932
                s += "\n";
 
1933
            }
 
1934
        }
 
1935
        start = 0;
 
1936
        if ( p == c2.parag() )
 
1937
            break;
 
1938
        p = p->next();
 
1939
    }
 
1940
    return s;
 
1941
}
 
1942
 
 
1943
void KoTextDocument::setFormat( int id, const KoTextFormat *f, int flags )
 
1944
{
 
1945
    QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
 
1946
    if ( it == selections.end() )
 
1947
        return;
 
1948
 
 
1949
    KoTextDocumentSelection sel = *it;
 
1950
 
 
1951
    KoTextCursor c1 = sel.startCursor;
 
1952
    KoTextCursor c2 = sel.endCursor;
 
1953
    if ( sel.swapped ) {
 
1954
        c2 = sel.startCursor;
 
1955
        c1 = sel.endCursor;
 
1956
    }
 
1957
 
 
1958
    c2.restoreState();
 
1959
    c1.restoreState();
 
1960
 
 
1961
    if ( c1.parag() == c2.parag() ) {
 
1962
        c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
 
1963
        return;
 
1964
    }
 
1965
 
 
1966
    c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f, TRUE, flags );
 
1967
    KoTextParag *p = c1.parag()->next();
 
1968
    while ( p && p != c2.parag() ) {
 
1969
        p->setFormat( 0, p->length(), f, TRUE, flags );
 
1970
        p = p->next();
 
1971
    }
 
1972
    c2.parag()->setFormat( 0, c2.index(), f, TRUE, flags );
 
1973
}
 
1974
 
 
1975
/*void KoTextDocument::copySelectedText( int id )
 
1976
{
 
1977
#ifndef QT_NO_CLIPBOARD
 
1978
    if ( !hasSelection( id ) )
 
1979
        return;
 
1980
 
 
1981
    QApplication::clipboard()->setText( selectedText( id ) );
 
1982
#endif
 
1983
}*/
 
1984
 
 
1985
void KoTextDocument::removeSelectedText( int id, KoTextCursor *cursor )
 
1986
{
 
1987
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
1988
    if ( it == selections.end() )
 
1989
        return;
 
1990
 
 
1991
    KoTextDocumentSelection sel = *it;
 
1992
 
 
1993
    KoTextCursor c1 = sel.startCursor;
 
1994
    KoTextCursor c2 = sel.endCursor;
 
1995
    if ( sel.swapped ) {
 
1996
        c2 = sel.startCursor;
 
1997
        c1 = sel.endCursor;
 
1998
    }
 
1999
 
 
2000
    // ### no support for editing tables yet
 
2001
    if ( c1.nestedDepth() || c2.nestedDepth() )
 
2002
        return;
 
2003
 
 
2004
    c2.restoreState();
 
2005
    c1.restoreState();
 
2006
 
 
2007
    *cursor = c1;
 
2008
    removeSelection( id );
 
2009
 
 
2010
    if ( c1.parag() == c2.parag() ) {
 
2011
        c1.parag()->remove( c1.index(), c2.index() - c1.index() );
 
2012
        return;
 
2013
    }
 
2014
 
 
2015
    // ## Qt has a strange setValid/isValid on QTextCursor, only used in the few lines below !?!?
 
2016
    bool valid = true;
 
2017
    if ( c1.parag() == fParag && c1.index() == 0 &&
 
2018
         c2.parag() == lParag && c2.index() == lParag->length() - 1 )
 
2019
        valid = FALSE;
 
2020
 
 
2021
    bool didGoLeft = FALSE;
 
2022
    if (  c1.index() == 0 && c1.parag() != fParag ) {
 
2023
        cursor->gotoPreviousLetter();
 
2024
        if ( valid )
 
2025
            didGoLeft = TRUE;
 
2026
    }
 
2027
 
 
2028
    c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
 
2029
    KoTextParag *p = c1.parag()->next();
 
2030
    int dy = 0;
 
2031
    KoTextParag *tmp;
 
2032
    while ( p && p != c2.parag() ) {
 
2033
        tmp = p->next();
 
2034
        dy -= p->rect().height();
 
2035
        delete p;
 
2036
        p = tmp;
 
2037
    }
 
2038
    c2.parag()->remove( 0, c2.index() );
 
2039
    while ( p ) {
 
2040
        p->move( dy );
 
2041
        //// kotext
 
2042
        if ( p->paragLayout().counter )
 
2043
            p->paragLayout().counter->invalidate();
 
2044
        ////
 
2045
        p->invalidate( 0 );
 
2046
        //p->setEndState( -1 );
 
2047
        p = p->next();
 
2048
    }
 
2049
 
 
2050
    c1.parag()->join( c2.parag() );
 
2051
 
 
2052
    if ( didGoLeft )
 
2053
        cursor->gotoNextLetter();
 
2054
}
 
2055
 
 
2056
void KoTextDocument::indentSelection( int id )
 
2057
{
 
2058
    QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
 
2059
    if ( it == selections.end() )
 
2060
        return;
 
2061
 
 
2062
    KoTextDocumentSelection sel = *it;
 
2063
    KoTextParag *startParag = sel.startCursor.parag();
 
2064
    KoTextParag *endParag = sel.endCursor.parag();
 
2065
    if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
 
2066
        endParag = sel.startCursor.parag();
 
2067
        startParag = sel.endCursor.parag();
 
2068
    }
 
2069
 
 
2070
    KoTextParag *p = startParag;
 
2071
    while ( p && p != endParag ) {
 
2072
        p->indent();
 
2073
        p = p->next();
 
2074
    }
 
2075
}
 
2076
 
 
2077
void KoTextDocument::addCommand( KoTextDocCommand *cmd )
 
2078
{
 
2079
    commandHistory->addCommand( cmd );
 
2080
}
 
2081
 
 
2082
KoTextCursor *KoTextDocument::undo( KoTextCursor *c )
 
2083
{
 
2084
    return commandHistory->undo( c );
 
2085
}
 
2086
 
 
2087
KoTextCursor *KoTextDocument::redo( KoTextCursor *c )
 
2088
{
 
2089
    return commandHistory->redo( c );
 
2090
}
 
2091
 
 
2092
bool KoTextDocument::find( const QString &expr, bool cs, bool wo, bool forward,
 
2093
                          int *parag, int *index, KoTextCursor *cursor )
 
2094
{
 
2095
    KoTextParag *p = forward ? fParag : lParag;
 
2096
    if ( parag )
 
2097
        p = paragAt( *parag );
 
2098
    else if ( cursor )
 
2099
        p = cursor->parag();
 
2100
    bool first = TRUE;
 
2101
 
 
2102
    while ( p ) {
 
2103
        QString s = p->string()->toString();
 
2104
        s.remove( s.length() - 1, 1 ); // get rid of trailing space
 
2105
        int start = forward ? 0 : s.length() - 1;
 
2106
        if ( first && index )
 
2107
            start = *index;
 
2108
        else if ( first )
 
2109
            start = cursor->index();
 
2110
        if ( !forward && first ) {
 
2111
            start -= expr.length() + 1;
 
2112
            if ( start < 0 ) {
 
2113
                first = FALSE;
 
2114
                p = p->prev();
 
2115
                continue;
 
2116
            }
 
2117
        }
 
2118
        first = FALSE;
 
2119
 
 
2120
        for ( ;; ) {
 
2121
            int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
 
2122
            if ( res == -1 )
 
2123
                break;
 
2124
 
 
2125
            bool ok = TRUE;
 
2126
            if ( wo ) {
 
2127
                int end = res + expr.length();
 
2128
                if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
 
2129
                     ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) )
 
2130
                    ok = TRUE;
 
2131
                else
 
2132
                    ok = FALSE;
 
2133
            }
 
2134
            if ( ok ) {
 
2135
                cursor->setParag( p );
 
2136
                cursor->setIndex( res );
 
2137
                setSelectionStart( Standard, cursor );
 
2138
                cursor->setIndex( res + expr.length() );
 
2139
                setSelectionEnd( Standard, cursor );
 
2140
                if ( parag )
 
2141
                    *parag = p->paragId();
 
2142
                if ( index )
 
2143
                    *index = res;
 
2144
                return TRUE;
 
2145
            }
 
2146
            if ( forward ) {
 
2147
                start = res + 1;
 
2148
            } else {
 
2149
                if ( res == 0 )
 
2150
                    break;
 
2151
                start = res - 1;
 
2152
            }
 
2153
        }
 
2154
        p = forward ? p->next() : p->prev();
 
2155
    }
 
2156
 
 
2157
    return FALSE;
 
2158
}
 
2159
 
 
2160
#if 0
 
2161
void KoTextDocument::setTextFormat( Qt::TextFormat f )
 
2162
{
 
2163
    txtFormat = f;
 
2164
}
 
2165
 
 
2166
Qt::TextFormat KoTextDocument::textFormat() const
 
2167
{
 
2168
    return txtFormat;
 
2169
}
 
2170
#endif
 
2171
 
 
2172
bool KoTextDocument::inSelection( int selId, const QPoint &pos ) const
 
2173
{
 
2174
    QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( selId );
 
2175
    if ( it == selections.end() )
 
2176
        return FALSE;
 
2177
 
 
2178
    KoTextDocumentSelection sel = *it;
 
2179
    KoTextParag *startParag = sel.startCursor.parag();
 
2180
    KoTextParag *endParag = sel.endCursor.parag();
 
2181
    if ( sel.startCursor.parag() == sel.endCursor.parag() &&
 
2182
         sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) )
 
2183
        return FALSE;
 
2184
    if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
 
2185
        endParag = sel.startCursor.parag();
 
2186
        startParag = sel.endCursor.parag();
 
2187
    }
 
2188
 
 
2189
    KoTextParag *p = startParag;
 
2190
    while ( p ) {
 
2191
        if ( p->rect().contains( pos ) ) {
 
2192
            bool inSel = FALSE;
 
2193
            int selStart = p->selectionStart( selId );
 
2194
            int selEnd = p->selectionEnd( selId );
 
2195
            int y = 0;
 
2196
            int h = 0;
 
2197
            for ( int i = 0; i < p->length(); ++i ) {
 
2198
                if ( i == selStart )
 
2199
                    inSel = TRUE;
 
2200
                if ( i == selEnd )
 
2201
                    break;
 
2202
                if ( p->at( i )->lineStart ) {
 
2203
                    y = (*p->lineStarts.find( i ))->y;
 
2204
                    h = (*p->lineStarts.find( i ))->h;
 
2205
                }
 
2206
                if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
 
2207
                    if ( inSel && pos.x() >= p->at( i )->x &&
 
2208
                         pos.x() <= p->at( i )->x + p->at( i )->width /*p->at( i )->format()->width( p->at( i )->c )*/ )
 
2209
                        return TRUE;
 
2210
                }
 
2211
            }
 
2212
        }
 
2213
        if ( pos.y() < p->rect().y() )
 
2214
            break;
 
2215
        if ( p == endParag )
 
2216
            break;
 
2217
        p = p->next();
 
2218
    }
 
2219
 
 
2220
    return FALSE;
 
2221
}
 
2222
 
 
2223
#if 0
 
2224
void KoTextDocument::doLayout( QPainter *p, int w )
 
2225
{
 
2226
    if ( !is_printer( p ) )
 
2227
        p = 0;
 
2228
    withoutDoubleBuffer = ( p != 0 );
 
2229
    flow_->setWidth( w );
 
2230
    cw = w;
 
2231
    vw = w;
 
2232
    fCollection->setPainter( p );
 
2233
    KoTextParag *parag = fParag;
 
2234
    while ( parag ) {
 
2235
        parag->invalidate( 0 );
 
2236
        parag->setPainter( p, TRUE );
 
2237
        parag->format();
 
2238
        parag = parag->next();
 
2239
    }
 
2240
 
 
2241
    fCollection->setPainter( 0 );
 
2242
    parag = fParag;
 
2243
    while ( parag ) {
 
2244
        parag->setPainter( 0, FALSE );
 
2245
        parag = parag->next();
 
2246
    }
 
2247
}
 
2248
#endif
 
2249
 
 
2250
QPixmap *KoTextDocument::bufferPixmap( const QSize &s )
 
2251
{
 
2252
    if ( !buf_pixmap ) {
 
2253
        int w = QABS( s.width() );
 
2254
        int h = QABS( s.height() );
 
2255
        buf_pixmap = new QPixmap( w, h );
 
2256
    } else {
 
2257
        if ( buf_pixmap->width() < s.width() ||
 
2258
             buf_pixmap->height() < s.height() ) {
 
2259
            buf_pixmap->resize( QMAX( s.width(), buf_pixmap->width() ),
 
2260
                                QMAX( s.height(), buf_pixmap->height() ) );
 
2261
        }
 
2262
    }
 
2263
 
 
2264
    return buf_pixmap;
 
2265
}
 
2266
 
 
2267
#if 0
 
2268
void KoTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
 
2269
{
 
2270
    if ( !firstParag() )
 
2271
        return;
 
2272
 
 
2273
    QBrush bgBrush = paper ? *paper : cg.brush( QColorGroup::Base ); // ## QRT doesn't use cg.brush(Base)
 
2274
    {
 
2275
        p->setBrushOrigin( -int( p->translationX() ),
 
2276
                           -int( p->translationY() ) );
 
2277
        p->fillRect( rect, bgBrush );
 
2278
    }
 
2279
 
 
2280
#if 0 // strange code found in QRT - I don't want all my colors to go away !
 
2281
    if ( formatCollection()->defaultFormat()->color() != cg.text() ) {
 
2282
        QDict<KoTextFormat> formats = formatCollection()->dict();
 
2283
        QDictIterator<KoTextFormat> it( formats );
 
2284
        while ( it.current() ) {
 
2285
            if ( it.current() == formatCollection()->defaultFormat() ) {
 
2286
                ++it;
 
2287
                continue;
 
2288
            }
 
2289
            it.current()->setColor( cg.text() );
 
2290
            ++it;
 
2291
        }
 
2292
        formatCollection()->defaultFormat()->setColor( cg.text() );
 
2293
    }
 
2294
#endif
 
2295
 
 
2296
    KoTextParag *parag = firstParag();
 
2297
    while ( parag ) {
 
2298
        if ( !parag->isValid() )
 
2299
            parag->format();
 
2300
        int y = parag->rect().y();
 
2301
        QRect pr( parag->rect() );
 
2302
        pr.setX( 0 );
 
2303
        pr.setWidth( QWIDGETSIZE_MAX );
 
2304
        if ( !rect.isNull() && !rect.intersects( pr ) ) {
 
2305
            parag = parag->next();
 
2306
            continue;
 
2307
        }
 
2308
        p->translate( 0, y );
 
2309
        if ( rect.isValid() )
 
2310
            parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
 
2311
        else
 
2312
            parag->paint( *p, cg, 0, FALSE );
 
2313
        p->translate( 0, -y );
 
2314
        parag = parag->next();
 
2315
        if ( !flow()->isEmpty() )
 
2316
            flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE );
 
2317
    }
 
2318
}
 
2319
 
 
2320
void KoTextDocument::drawParag( QPainter *p, KoTextParag *parag, int cx, int cy, int cw, int ch,
 
2321
                               QPixmap *&doubleBuffer, const QColorGroup &cg,
 
2322
                               bool drawCursor, KoTextCursor *cursor, bool resetChanged )
 
2323
{
 
2324
    QPainter *painter = 0;
 
2325
    if ( resetChanged )
 
2326
        parag->setChanged( FALSE );
 
2327
    QRect ir( parag->rect() );
 
2328
    bool useDoubleBuffer = true;
 
2329
    //bool useDoubleBuffer = !parag->document()->parent();
 
2330
    //if ( !useDoubleBuffer && parag->document()->nextDoubleBuffered )
 
2331
    //useDoubleBuffer = TRUE;
 
2332
    if ( p->device()->devType() == QInternal::Printer )
 
2333
        useDoubleBuffer = FALSE;
 
2334
 
 
2335
    if ( useDoubleBuffer  ) {
 
2336
        if ( cx >= 0 && cy >= 0 )
 
2337
        {
 
2338
            ir = ir.intersect( QRect( cx, cy, cw, ch ) );
 
2339
            if (ir.isEmpty())
 
2340
                useDoubleBuffer = FALSE;
 
2341
        }
 
2342
    }
 
2343
 
 
2344
    if ( useDoubleBuffer  ) {
 
2345
        painter = new QPainter;
 
2346
        if ( !doubleBuffer ||
 
2347
             ir.width() > doubleBuffer->width() ||
 
2348
             ir.height() > doubleBuffer->height() ) {
 
2349
            doubleBuffer = bufferPixmap( ir.size() );
 
2350
            painter->begin( doubleBuffer );
 
2351
        } else {
 
2352
            painter->begin( doubleBuffer );
 
2353
        }
 
2354
    } else {
 
2355
        painter = p;
 
2356
        painter->translate( ir.x(), ir.y() );
 
2357
    }
 
2358
 
 
2359
    painter->setBrushOrigin( -ir.x(), -ir.y() );
 
2360
 
 
2361
    if ( useDoubleBuffer || is_printer( painter ) ) {
 
2362
        if ( !parag->backgroundColor() )
 
2363
            painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
 
2364
                               cg.brush( QColorGroup::Base ) );
 
2365
        else
 
2366
            painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
 
2367
                               *parag->backgroundColor() );
 
2368
    } else {
 
2369
        if ( cursor && cursor->parag() == parag ) {
 
2370
            if ( !parag->backgroundColor() )
 
2371
                painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
 
2372
                                   cg.brush( QColorGroup::Base ) );
 
2373
            else
 
2374
                painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
 
2375
                                   *parag->backgroundColor() );
 
2376
        }
 
2377
    }
 
2378
 
 
2379
    painter->translate( -( ir.x() - parag->rect().x() ),
 
2380
                       -( ir.y() - parag->rect().y() ) );
 
2381
    parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch );
 
2382
    if ( !flow()->isEmpty() ) {
 
2383
        painter->translate( 0, -parag->rect().y() );
 
2384
        QRect cr( cx, cy, cw, ch );
 
2385
        cr = cr.intersect( QRect( 0, parag->rect().y(), parag->rect().width(), parag->rect().height() ) );
 
2386
        flow()->drawFloatingItems( painter, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
 
2387
        painter->translate( 0, +parag->rect().y() );
 
2388
    }
 
2389
 
 
2390
    if ( useDoubleBuffer ) {
 
2391
        delete painter;
 
2392
        painter = 0;
 
2393
        p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
 
2394
    } else {
 
2395
        painter->translate( -ir.x(), -ir.y() );
 
2396
    }
 
2397
 
 
2398
    if ( useDoubleBuffer ) {
 
2399
        QRect rect = parag->rect();
 
2400
        if ( rect.x() + rect.width() < parag->document()->x() + parag->document()->width() ) {
 
2401
            p->fillRect( rect.x() + rect.width(), rect.y(),
 
2402
                         ( parag->document()->x() + parag->document()->width() ) -
 
2403
                         ( rect.x() + rect.width() ),
 
2404
                         rect.height(), cg.brush( QColorGroup::Base ) );
 
2405
    }
 
2406
 
 
2407
    //parag->document()->nextDoubleBuffered = FALSE;
 
2408
}
 
2409
 
 
2410
KoTextParag *KoTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
 
2411
                                 bool onlyChanged, bool drawCursor, KoTextCursor *cursor, bool resetChanged )
 
2412
{
 
2413
    if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
 
2414
        withoutDoubleBuffer = TRUE;
 
2415
        QRect crect( cx, cy, cw, ch );
 
2416
        draw( p, crect, cg );
 
2417
        return 0;
 
2418
    }
 
2419
    withoutDoubleBuffer = FALSE;
 
2420
 
 
2421
    if ( !firstParag() )
 
2422
        return 0;
 
2423
 
 
2424
    if ( drawCursor && cursor )
 
2425
        tmpCursor = cursor;
 
2426
    if ( cx < 0 && cy < 0 ) {
 
2427
        cx = 0;
 
2428
        cy = 0;
 
2429
        cw = width();
 
2430
        ch = height();
 
2431
    }
 
2432
 
 
2433
    KoTextParag *lastFormatted = 0;
 
2434
    KoTextParag *parag = firstParag();
 
2435
 
 
2436
    QPixmap *doubleBuffer = 0;
 
2437
    QPainter painter;
 
2438
    QRect crect( cx, cy, cw, ch );
 
2439
 
 
2440
    // Space above first parag
 
2441
    if ( isPageBreakEnabled() && parag && cy <= parag->rect().y() && parag->rect().y() > 0 ) {
 
2442
        QRect r( 0, 0,
 
2443
                 parag->document()->x() + parag->document()->width(),
 
2444
                 parag->rect().y() );
 
2445
        r &= crect;
 
2446
        if ( !r.isEmpty() )
 
2447
            p->fillRect( r, cg.brush( QColorGroup::Base ) );
 
2448
    }
 
2449
 
 
2450
    while ( parag ) {
 
2451
        lastFormatted = parag;
 
2452
        if ( !parag->isValid() )
 
2453
            parag->format();
 
2454
 
 
2455
        QRect ir = parag->rect();
 
2456
        if ( isPageBreakEnabled() && parag->next() )
 
2457
            if ( ir.y() + ir.height() < parag->next()->rect().y() ) {
 
2458
                QRect r( 0, ir.y() + ir.height(),
 
2459
                         parag->document()->x() + parag->document()->width(),
 
2460
                         parag->next()->rect().y() - ( ir.y() + ir.height() ) );
 
2461
                r &= crect;
 
2462
                if ( !r.isEmpty() )
 
2463
                    p->fillRect( r, cg.brush( QColorGroup::Base ) );
 
2464
            }
 
2465
 
 
2466
        if ( !ir.intersects( crect ) ) {
 
2467
            ir.setWidth( parag->document()->width() );
 
2468
            if ( ir.intersects( crect ) )
 
2469
                p->fillRect( ir.intersect( crect ), cg.brush( QColorGroup::Base ) );
 
2470
            if ( ir.y() > cy + ch ) {
 
2471
                tmpCursor = 0;
 
2472
                if ( buf_pixmap && buf_pixmap->height() > 300 ) {
 
2473
                    delete buf_pixmap;
 
2474
                    buf_pixmap = 0;
 
2475
                }
 
2476
                return lastFormatted;
 
2477
            }
 
2478
            parag = parag->next();
 
2479
            continue;
 
2480
        }
 
2481
 
 
2482
        if ( !parag->hasChanged() && onlyChanged ) {
 
2483
            parag = parag->next();
 
2484
            continue;
 
2485
        }
 
2486
 
 
2487
        drawParag( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
 
2488
        parag = parag->next();
 
2489
    }
 
2490
 
 
2491
    parag = lastParag();
 
2492
    if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
 
2493
        if ( !parag->document()->parent() ) { // !useDoubleBuffer
 
2494
            p->fillRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
 
2495
                         parag->document()->height() - ( parag->rect().y() + parag->rect().height() ),
 
2496
                         cg.brush( QColorGroup::Base ) );
 
2497
        }
 
2498
        if ( !flow()->isEmpty() ) {
 
2499
            QRect cr( cx, cy, cw, ch );
 
2500
            cr = cr.intersect( QRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
 
2501
                                      parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) ) );
 
2502
            flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
 
2503
        }
 
2504
    }
 
2505
 
 
2506
    if ( buf_pixmap && buf_pixmap->height() > 300 ) {
 
2507
        delete buf_pixmap;
 
2508
        buf_pixmap = 0;
 
2509
    }
 
2510
 
 
2511
    tmpCursor = 0;
 
2512
    return lastFormatted;
 
2513
}
 
2514
#endif
 
2515
 
 
2516
#if 0
 
2517
void KoTextDocument::setDefaultFont( const QFont &f )
 
2518
{
 
2519
    updateFontSizes( f.pointSize() );
 
2520
}
 
2521
#endif
 
2522
 
 
2523
void KoTextDocument::registerCustomItem( KoTextCustomItem *i, KoTextParag *p )
 
2524
{
 
2525
    if ( i && i->placement() != KoTextCustomItem::PlaceInline )
 
2526
        flow_->registerFloatingItem( i );
 
2527
    p->registerFloatingItem( i );
 
2528
    i->setParagraph( p );
 
2529
    //kdDebug(32500) << "KoTextDocument::registerCustomItem " << (void*)i << endl;
 
2530
    customItems.append( i );
 
2531
}
 
2532
 
 
2533
void KoTextDocument::unregisterCustomItem( KoTextCustomItem *i, KoTextParag *p )
 
2534
{
 
2535
    flow_->unregisterFloatingItem( i );
 
2536
    p->unregisterFloatingItem( i );
 
2537
    i->setParagraph( 0 );
 
2538
    customItems.removeRef( i );
 
2539
}
 
2540
 
 
2541
// unused in kotext, and needs KoTextStringChar::isAnchor
 
2542
#if 0
 
2543
bool KoTextDocument::hasFocusParagraph() const
 
2544
{
 
2545
    return !!focusIndicator.parag;
 
2546
}
 
2547
 
 
2548
QString KoTextDocument::focusHref() const
 
2549
{
 
2550
    return focusIndicator.href;
 
2551
}
 
2552
 
 
2553
bool KoTextDocument::focusNextPrevChild( bool next )
 
2554
{
 
2555
    if ( !focusIndicator.parag ) {
 
2556
        if ( next ) {
 
2557
            focusIndicator.parag = fParag;
 
2558
            focusIndicator.start = 0;
 
2559
            focusIndicator.len = 0;
 
2560
        } else {
 
2561
            focusIndicator.parag = lParag;
 
2562
            focusIndicator.start = lParag->length();
 
2563
            focusIndicator.len = 0;
 
2564
        }
 
2565
    } else {
 
2566
        focusIndicator.parag->setChanged( TRUE );
 
2567
    }
 
2568
    focusIndicator.href = QString::null;
 
2569
 
 
2570
    if ( next ) {
 
2571
        KoTextParag *p = focusIndicator.parag;
 
2572
        int index = focusIndicator.start + focusIndicator.len;
 
2573
        while ( p ) {
 
2574
            for ( int i = index; i < p->length(); ++i ) {
 
2575
                if ( p->at( i )->isAnchor() ) {
 
2576
                    p->setChanged( TRUE );
 
2577
                    focusIndicator.parag = p;
 
2578
                    focusIndicator.start = i;
 
2579
                    focusIndicator.len = 0;
 
2580
                    focusIndicator.href = p->at( i )->format()->anchorHref();
 
2581
                    while ( i < p->length() ) {
 
2582
                        if ( !p->at( i )->format()->isAnchor() )
 
2583
                            return TRUE;
 
2584
                        focusIndicator.len++;
 
2585
                        i++;
 
2586
                    }
 
2587
                } else if ( p->at( i )->isCustom() ) {
 
2588
#ifdef QTEXTTABLE_AVAILABLE
 
2589
                    if ( p->at( i )->customItem()->isNested() ) {
 
2590
                        KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
 
2591
                        QPtrList<KoTextTableCell> cells = t->tableCells();
 
2592
                        // first try to continue
 
2593
                        KoTextTableCell *c;
 
2594
                        bool resetCells = TRUE;
 
2595
                        for ( c = cells.first(); c; c = cells.next() ) {
 
2596
                            if ( c->richText()->hasFocusParagraph() ) {
 
2597
                                if ( c->richText()->focusNextPrevChild( next ) ) {
 
2598
                                    p->setChanged( TRUE );
 
2599
                                    focusIndicator.parag = p;
 
2600
                                    focusIndicator.start = i;
 
2601
                                    focusIndicator.len = 0;
 
2602
                                    focusIndicator.href = c->richText()->focusHref();
 
2603
                                    return TRUE;
 
2604
                                } else {
 
2605
                                    resetCells = FALSE;
 
2606
                                    c = cells.next();
 
2607
                                    break;
 
2608
                                }
 
2609
                            }
 
2610
                        }
 
2611
                        // now really try
 
2612
                        if ( resetCells )
 
2613
                            c = cells.first();
 
2614
                        for ( ; c; c = cells.next() ) {
 
2615
                            if ( c->richText()->focusNextPrevChild( next ) ) {
 
2616
                                p->setChanged( TRUE );
 
2617
                                focusIndicator.parag = p;
 
2618
                                focusIndicator.start = i;
 
2619
                                focusIndicator.len = 0;
 
2620
                                focusIndicator.href = c->richText()->focusHref();
 
2621
                                return TRUE;
 
2622
                            }
 
2623
                        }
 
2624
                    }
 
2625
#endif
 
2626
                }
 
2627
            }
 
2628
            index = 0;
 
2629
            p = p->next();
 
2630
        }
 
2631
    } else {
 
2632
        KoTextParag *p = focusIndicator.parag;
 
2633
        int index = focusIndicator.start - 1;
 
2634
        if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
 
2635
            index++;
 
2636
        while ( p ) {
 
2637
            for ( int i = index; i >= 0; --i ) {
 
2638
                if ( p->at( i )->format()->isAnchor() ) {
 
2639
                    p->setChanged( TRUE );
 
2640
                    focusIndicator.parag = p;
 
2641
                    focusIndicator.start = i;
 
2642
                    focusIndicator.len = 0;
 
2643
                    focusIndicator.href = p->at( i )->format()->anchorHref();
 
2644
                    while ( i >= -1 ) {
 
2645
                        if ( i < 0 || !p->at( i )->format()->isAnchor() ) {
 
2646
                            focusIndicator.start++;
 
2647
                            return TRUE;
 
2648
                        }
 
2649
                        if ( i < 0 )
 
2650
                            break;
 
2651
                        focusIndicator.len++;
 
2652
                        focusIndicator.start--;
 
2653
                        i--;
 
2654
                    }
 
2655
                } else if ( p->at( i )->isCustom() ) {
 
2656
#ifdef QTEXTTABLE_AVAILABLE
 
2657
                    if ( p->at( i )->customItem()->isNested() ) {
 
2658
                        KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
 
2659
                        QPtrList<KoTextTableCell> cells = t->tableCells();
 
2660
                        // first try to continue
 
2661
                        KoTextTableCell *c;
 
2662
                        bool resetCells = TRUE;
 
2663
                        for ( c = cells.last(); c; c = cells.prev() ) {
 
2664
                            if ( c->richText()->hasFocusParagraph() ) {
 
2665
                                if ( c->richText()->focusNextPrevChild( next ) ) {
 
2666
                                    p->setChanged( TRUE );
 
2667
                                    focusIndicator.parag = p;
 
2668
                                    focusIndicator.start = i;
 
2669
                                    focusIndicator.len = 0;
 
2670
                                    focusIndicator.href = c->richText()->focusHref();
 
2671
                                    return TRUE;
 
2672
                                } else {
 
2673
                                    resetCells = FALSE;
 
2674
                                    c = cells.prev();
 
2675
                                    break;
 
2676
                                }
 
2677
                            }
 
2678
                            if ( cells.at() == 0 )
 
2679
                                break;
 
2680
                        }
 
2681
                        // now really try
 
2682
                        if ( resetCells )
 
2683
                            c = cells.last();
 
2684
                        for ( ; c; c = cells.prev() ) {
 
2685
                            if ( c->richText()->focusNextPrevChild( next ) ) {
 
2686
                                p->setChanged( TRUE );
 
2687
                                focusIndicator.parag = p;
 
2688
                                focusIndicator.start = i;
 
2689
                                focusIndicator.len = 0;
 
2690
                                focusIndicator.href = c->richText()->focusHref();
 
2691
                                return TRUE;
 
2692
                            }
 
2693
                            if ( cells.at() == 0 )
 
2694
                                break;
 
2695
                        }
 
2696
                    }
 
2697
#endif
 
2698
                }
 
2699
            }
 
2700
            p = p->prev();
 
2701
            if ( p )
 
2702
                index = p->length() - 1;
 
2703
        }
 
2704
    }
 
2705
 
 
2706
    focusIndicator.parag = 0;
 
2707
 
 
2708
    return FALSE;
 
2709
}
 
2710
#endif
 
2711
 
 
2712
int KoTextDocument::length() const
 
2713
{
 
2714
    int l = 0;
 
2715
    KoTextParag *p = fParag;
 
2716
    while ( p ) {
 
2717
        l += p->length() - 1; // don't count trailing space
 
2718
        p = p->next();
 
2719
    }
 
2720
    return l;
 
2721
}
 
2722
 
 
2723
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
2724
 
 
2725
KoTextString::KoTextString()
 
2726
{
 
2727
    bidiDirty = TRUE;
 
2728
    bNeedsSpellCheck = true;
 
2729
    bidi = FALSE;
 
2730
    rightToLeft = FALSE;
 
2731
    dir = QChar::DirON;
 
2732
}
 
2733
 
 
2734
KoTextString::KoTextString( const KoTextString &s )
 
2735
{
 
2736
    bidiDirty = s.bidiDirty;
 
2737
    bNeedsSpellCheck = s.bNeedsSpellCheck;
 
2738
    bidi = s.bidi;
 
2739
    rightToLeft = s.rightToLeft;
 
2740
    dir = s.dir;
 
2741
    data = s.data;
 
2742
    data.detach();
 
2743
    for ( int i = 0; i < (int)data.size(); ++i ) {
 
2744
        KoTextFormat *f = data[i].format();
 
2745
        if ( f )
 
2746
            f->addRef();
 
2747
    }
 
2748
}
 
2749
 
 
2750
void KoTextString::insert( int index, const QString &s, KoTextFormat *f )
 
2751
{
 
2752
    int os = data.size();
 
2753
    data.resize( data.size() + s.length() );
 
2754
    if ( index < os ) {
 
2755
        memmove( data.data() + index + s.length(), data.data() + index,
 
2756
                 sizeof( KoTextStringChar ) * ( os - index ) );
 
2757
    }
 
2758
    for ( int i = 0; i < (int)s.length(); ++i ) {
 
2759
        KoTextStringChar &ch = data[ (int)index + i ];
 
2760
        ch.x = 0;
 
2761
        ch.pixelxadj = 0;
 
2762
        ch.pixelwidth = 0;
 
2763
        ch.width = 0;
 
2764
        ch.lineStart = 0;
 
2765
        ch.d.format = 0;
 
2766
        ch.type = KoTextStringChar::Regular;
 
2767
        ch.rightToLeft = 0;
 
2768
        ch.startOfRun = 0;
 
2769
        ch.c = s[ i ];
 
2770
#ifdef DEBUG_COLLECTION
 
2771
        kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
 
2772
#endif
 
2773
        ch.setFormat( f );
 
2774
    }
 
2775
    bidiDirty = TRUE;
 
2776
    bNeedsSpellCheck = true;
 
2777
}
 
2778
 
 
2779
KoTextString::~KoTextString()
 
2780
{
 
2781
    clear();
 
2782
}
 
2783
 
 
2784
void KoTextString::insert( int index, KoTextStringChar *c )
 
2785
{
 
2786
    int os = data.size();
 
2787
    data.resize( data.size() + 1 );
 
2788
    if ( index < os ) {
 
2789
        memmove( data.data() + index + 1, data.data() + index,
 
2790
                 sizeof( KoTextStringChar ) * ( os - index ) );
 
2791
    }
 
2792
    KoTextStringChar &ch = data[ (int)index ];
 
2793
    ch.c = c->c;
 
2794
    ch.x = 0;
 
2795
    ch.pixelxadj = 0;
 
2796
    ch.pixelwidth = 0;
 
2797
    ch.width = 0;
 
2798
    ch.lineStart = 0;
 
2799
    ch.rightToLeft = 0;
 
2800
    ch.d.format = 0;
 
2801
    ch.type = KoTextStringChar::Regular;
 
2802
    ch.setFormat( c->format() );
 
2803
    bidiDirty = TRUE;
 
2804
    bNeedsSpellCheck = true;
 
2805
}
 
2806
 
 
2807
void KoTextString::truncate( int index )
 
2808
{
 
2809
    index = QMAX( index, 0 );
 
2810
    index = QMIN( index, (int)data.size() - 1 );
 
2811
    if ( index < (int)data.size() ) {
 
2812
        for ( int i = index + 1; i < (int)data.size(); ++i ) {
 
2813
            KoTextStringChar &ch = data[ i ];
 
2814
            if ( ch.isCustom() ) {
 
2815
                delete ch.customItem();
 
2816
                if ( ch.d.custom->format )
 
2817
                    ch.d.custom->format->removeRef();
 
2818
                delete ch.d.custom;
 
2819
                ch.d.custom = 0;
 
2820
            } else if ( ch.format() ) {
 
2821
                ch.format()->removeRef();
 
2822
            }
 
2823
        }
 
2824
    }
 
2825
    data.truncate( index );
 
2826
    bidiDirty = TRUE;
 
2827
    bNeedsSpellCheck = true;
 
2828
}
 
2829
 
 
2830
void KoTextString::remove( int index, int len )
 
2831
{
 
2832
    for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
 
2833
        KoTextStringChar &ch = data[ i ];
 
2834
        if ( ch.isCustom() ) {
 
2835
            delete ch.customItem();
 
2836
            if ( ch.d.custom->format )
 
2837
                ch.d.custom->format->removeRef();
 
2838
            delete ch.d.custom;
 
2839
            ch.d.custom = 0;
 
2840
        } else if ( ch.format() ) {
 
2841
            ch.format()->removeRef();
 
2842
        }
 
2843
    }
 
2844
    memmove( data.data() + index, data.data() + index + len,
 
2845
             sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
 
2846
    data.resize( data.size() - len, QGArray::SpeedOptim );
 
2847
    bidiDirty = TRUE;
 
2848
    bNeedsSpellCheck = true;
 
2849
}
 
2850
 
 
2851
void KoTextString::clear()
 
2852
{
 
2853
    for ( int i = 0; i < (int)data.count(); ++i ) {
 
2854
        KoTextStringChar &ch = data[ i ];
 
2855
        if ( ch.isCustom() ) {
 
2856
            delete ch.customItem();
 
2857
            if ( ch.d.custom->format )
 
2858
                ch.d.custom->format->removeRef();
 
2859
            delete ch.d.custom;
 
2860
            ch.d.custom = 0;
 
2861
        } else if ( ch.format() ) {
 
2862
            ch.format()->removeRef();
 
2863
        }
 
2864
    }
 
2865
    data.resize( 0 );
 
2866
}
 
2867
 
 
2868
void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection )
 
2869
{
 
2870
    KoTextStringChar &ch = data[ index ];
 
2871
//    kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
 
2872
    if ( useCollection && ch.format() )
 
2873
    {
 
2874
        //kdDebug(32500) << "KoTextString::setFormat removing ref on old format " << ch.format() << endl;
 
2875
        ch.format()->removeRef();
 
2876
    }
 
2877
    ch.setFormat( f );
 
2878
}
 
2879
 
 
2880
void KoTextString::checkBidi() const
 
2881
{
 
2882
    bool rtlKnown = FALSE;
 
2883
    if ( dir == QChar::DirR ) {
 
2884
        ((KoTextString *)this)->bidi = TRUE;
 
2885
        ((KoTextString *)this)->rightToLeft = TRUE;
 
2886
        ((KoTextString *)this)->bidiDirty = FALSE;
 
2887
        return;
 
2888
    } else if ( dir == QChar::DirL ) {
 
2889
        ((KoTextString *)this)->rightToLeft = FALSE;
 
2890
        rtlKnown = TRUE;
 
2891
    } else {
 
2892
        ((KoTextString *)this)->rightToLeft = FALSE;
 
2893
    }
 
2894
 
 
2895
    int len = data.size();
 
2896
    const KoTextStringChar *c = data.data();
 
2897
    ((KoTextString *)this)->bidi = FALSE;
 
2898
    while( len ) {
 
2899
        if ( !rtlKnown ) {
 
2900
            switch( c->c.direction() )
 
2901
            {
 
2902
                case QChar::DirL:
 
2903
                case QChar::DirLRO:
 
2904
                case QChar::DirLRE:
 
2905
                    ((KoTextString *)this)->rightToLeft = FALSE;
 
2906
                    rtlKnown = TRUE;
 
2907
                    break;
 
2908
                case QChar::DirR:
 
2909
                case QChar::DirAL:
 
2910
                case QChar::DirRLO:
 
2911
                case QChar::DirRLE:
 
2912
                    ((KoTextString *)this)->rightToLeft = TRUE;
 
2913
                    rtlKnown = TRUE;
 
2914
                    break;
 
2915
                default:
 
2916
                    break;
 
2917
            }
 
2918
        }
 
2919
        uchar row = c->c.row();
 
2920
        if( (row > 0x04 && row < 0x09) || ( row > 0xfa && row < 0xff ) ) {
 
2921
            ((KoTextString *)this)->bidi = TRUE;
 
2922
            if ( rtlKnown )
 
2923
                break;
 
2924
        }
 
2925
        len--;
 
2926
        ++c;
 
2927
    }
 
2928
    ((KoTextString *)this)->bidiDirty = FALSE;
 
2929
}
 
2930
 
 
2931
QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
 
2932
{
 
2933
    if ( len == 0xFFFFFF )
 
2934
        len = data.size();
 
2935
    QMemArray<KoTextStringChar> a;
 
2936
    a.resize( len );
 
2937
    for ( int i = 0; i < len; ++i ) {
 
2938
        KoTextStringChar *c = &data[ i + start ];
 
2939
        a[ i ].c = c->c;
 
2940
        a[ i ].x = 0;
 
2941
        a[ i ].pixelxadj = 0;
 
2942
        a[ i ].pixelwidth = 0;
 
2943
        a[ i ].width = 0;
 
2944
        a[ i ].lineStart = 0;
 
2945
        a[ i ].rightToLeft = 0;
 
2946
        a[ i ].d.format = 0;
 
2947
        a[ i ].type = KoTextStringChar::Regular;
 
2948
        a[ i ].setFormat( c->format() );
 
2949
        if ( c->format() )
 
2950
            c->format()->addRef();
 
2951
    }
 
2952
    return a;
 
2953
}
 
2954
 
 
2955
QString KoTextString::mid( int start, int len ) const
 
2956
{
 
2957
    if ( len == 0xFFFFFF )
 
2958
        len = data.size();
 
2959
    QString res;
 
2960
    res.setLength( len );
 
2961
    for ( int i = 0; i < len; ++i ) {
 
2962
        KoTextStringChar *c = &data[ i + start ];
 
2963
        res[ i ] = c->c;
 
2964
    }
 
2965
    return res;
 
2966
}
 
2967
 
 
2968
QString KoTextString::toString( const QMemArray<KoTextStringChar> &data )
 
2969
{
 
2970
    QString s;
 
2971
    int l = data.size();
 
2972
    s.setUnicode( 0, l );
 
2973
    KoTextStringChar *c = data.data();
 
2974
    QChar *uc = (QChar *)s.unicode();
 
2975
    while ( l-- ) {
 
2976
        *uc = c->c;
 
2977
        uc++;
 
2978
        c++;
 
2979
    }
 
2980
 
 
2981
    return s;
 
2982
}
 
2983
 
 
2984
QString KoTextString::toReverseString() const
 
2985
{
 
2986
    QString s;
 
2987
    int l = length();
 
2988
    s.setUnicode(0, l);
 
2989
    KoTextStringChar *c = data.data() + (l-1);
 
2990
    QChar *uc = (QChar *)s.unicode();
 
2991
    while ( l-- ) {
 
2992
        *uc = c->c;
 
2993
        uc++;
 
2994
        c--;
 
2995
    }
 
2996
 
 
2997
    return s;
 
2998
}
 
2999
 
 
3000
 
 
3001
void KoTextStringChar::setFormat( KoTextFormat *f )
 
3002
{
 
3003
    if ( type == Regular ) {
 
3004
        d.format = f;
 
3005
    } else {
 
3006
        if ( !d.custom ) {
 
3007
            d.custom = new CustomData;
 
3008
            d.custom->custom = 0;
 
3009
        }
 
3010
        d.custom->format = f;
 
3011
        if ( d.custom->custom )
 
3012
            d.custom->custom->setFormat( f );
 
3013
    }
 
3014
}
 
3015
 
 
3016
void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
 
3017
{
 
3018
    if ( type == Regular ) {
 
3019
        KoTextFormat *f = format();
 
3020
        d.custom = new CustomData;
 
3021
        d.custom->format = f;
 
3022
        type = Custom;
 
3023
    } else {
 
3024
        delete d.custom->custom;
 
3025
    }
 
3026
    d.custom->custom = i;
 
3027
}
 
3028
 
 
3029
void KoTextStringChar::loseCustomItem() // setRegular() might be a better name
 
3030
{
 
3031
    if ( isCustom() ) {
 
3032
        KoTextFormat *f = d.custom->format;
 
3033
        d.custom->custom = 0;
 
3034
        delete d.custom;
 
3035
        type = Regular;
 
3036
        d.format = f;
 
3037
    }
 
3038
}
 
3039
 
 
3040
KoTextStringChar::~KoTextStringChar()
 
3041
{
 
3042
    if ( format() )
 
3043
        format()->removeRef();
 
3044
    switch ( type ) {
 
3045
        case Custom:
 
3046
            delete d.custom; break;
 
3047
        default:
 
3048
            break;
 
3049
    }
 
3050
}
 
3051
 
 
3052
KoTextStringChar *KoTextStringChar::clone() const
 
3053
{
 
3054
    KoTextStringChar *chr = new KoTextStringChar;
 
3055
    chr->c = c;
 
3056
    chr->x = 0;
 
3057
    chr->pixelxadj = 0;
 
3058
    chr->pixelwidth = 0;
 
3059
    chr->width = 0;
 
3060
    chr->lineStart = 0;
 
3061
    chr->rightToLeft = 0;
 
3062
    chr->d.format = 0;
 
3063
    chr->type = KoTextStringChar::Regular;
 
3064
    chr->setFormat( format() );
 
3065
    if ( chr->format() )
 
3066
        chr->format()->addRef();
 
3067
    return chr;
 
3068
}
 
3069
 
 
3070
int KoTextStringChar::height() const
 
3071
{
 
3072
    return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
 
3073
}
 
3074
 
 
3075
int KoTextStringChar::ascent() const
 
3076
{
 
3077
    return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
 
3078
}
 
3079
 
 
3080
int KoTextStringChar::descent() const
 
3081
{
 
3082
    return !isCustom() ? format()->descent() : 0;
 
3083
}
 
3084
 
 
3085
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
3086
 
 
3087
KoTextParag::KoTextParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
 
3088
    : invalid( 0 ), p( pr ), n( nx ), doc( d ),
 
3089
      changed( FALSE ),
 
3090
      fullWidth( TRUE ),
 
3091
      newLinesAllowed( TRUE ), // default in kotext
 
3092
      visible( TRUE ), breakable( TRUE ), movedDown( FALSE ),
 
3093
      align( 0 ),
 
3094
      m_lineChanged( -1 ),
 
3095
      m_wused( 0 ),
 
3096
      mSelections( 0 ),
 
3097
      mFloatingItems( 0 ),
 
3098
      tArray( 0 )
 
3099
{
 
3100
    defFormat = formatCollection()->defaultFormat();
 
3101
    /*if ( !doc ) {
 
3102
        tabStopWidth = defFormat->width( 'x' ) * 8;
 
3103
        commandHistory = new KoTextDocCommandHistory( 100 );
 
3104
    }*/
 
3105
#if defined(PARSER_DEBUG)
 
3106
    kdDebug(32500) << debug_indent + "new KoTextParag" << endl;
 
3107
#endif
 
3108
 
 
3109
    if ( p ) {
 
3110
        p->n = this;
 
3111
#ifdef QTEXTTABLE_AVAILABLE
 
3112
        if ( p->tc )
 
3113
            tc = p->tc;
 
3114
#endif
 
3115
    }
 
3116
    if ( n ) {
 
3117
        n->p = this;
 
3118
#ifdef QTEXTTABLE_AVAILABLE
 
3119
        if ( n->tc )
 
3120
            tc = n->tc;
 
3121
#endif
 
3122
    }
 
3123
 
 
3124
#ifdef QTEXTTABLE_AVAILABLE
 
3125
    if ( !tc && d && d->tableCell() )
 
3126
        tc = d->tableCell();
 
3127
#endif
 
3128
 
 
3129
    if ( !p && doc )
 
3130
        doc->setFirstParag( this );
 
3131
    if ( !n && doc )
 
3132
        doc->setLastParag( this );
 
3133
 
 
3134
    //firstFormat = TRUE; //// unused
 
3135
    //firstPProcess = TRUE;
 
3136
    //state = -1;
 
3137
    //needPreProcess = FALSE;
 
3138
 
 
3139
    if ( p )
 
3140
        id = p->id + 1;
 
3141
    else
 
3142
        id = 0;
 
3143
    if ( n && updateIds ) {
 
3144
        KoTextParag *s = n;
 
3145
        while ( s ) {
 
3146
            s->id = s->p->id + 1;
 
3147
            //s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1;
 
3148
            s = s->n;
 
3149
        }
 
3150
    }
 
3151
 
 
3152
    str = new KoTextString();
 
3153
    str->insert( 0, " ", formatCollection()->defaultFormat() );
 
3154
}
 
3155
 
 
3156
KoTextParag::~KoTextParag()
 
3157
{
 
3158
    //kdDebug(32500) << "KoTextParag::~KoTextParag " << this << " id=" << paragId() << endl;
 
3159
    delete str;
 
3160
//    if ( doc && p == doc->minwParag ) {
 
3161
//      doc->minwParag = 0;
 
3162
//      doc->minw = 0;
 
3163
//    }
 
3164
    if ( !doc ) {
 
3165
        //delete pFormatter;
 
3166
        //delete commandHistory;
 
3167
    }
 
3168
    delete [] tArray;
 
3169
    //delete eData;
 
3170
    QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
 
3171
    for ( ; it != lineStarts.end(); ++it )
 
3172
        delete *it;
 
3173
    if ( mSelections ) delete mSelections;
 
3174
    if ( mFloatingItems ) delete mFloatingItems;
 
3175
 
 
3176
    if (p)
 
3177
       p->setNext(n);
 
3178
    if (n)
 
3179
       n->setPrev(p);
 
3180
 
 
3181
    //// kotext
 
3182
    if ( doc && !doc->isDestroying() )
 
3183
    {
 
3184
        doc->informParagraphDeleted( this );
 
3185
    }
 
3186
    //kdDebug(32500) << "KoTextParag::~KoTextParag " << this << endl;
 
3187
    ////
 
3188
}
 
3189
 
 
3190
void KoTextParag::setNext( KoTextParag *s )
 
3191
{
 
3192
    n = s;
 
3193
    if ( !n && doc )
 
3194
        doc->setLastParag( this );
 
3195
}
 
3196
 
 
3197
void KoTextParag::setPrev( KoTextParag *s )
 
3198
{
 
3199
    p = s;
 
3200
    if ( !p && doc )
 
3201
        doc->setFirstParag( this );
 
3202
}
 
3203
 
 
3204
void KoTextParag::invalidate( int chr )
 
3205
{
 
3206
    if ( invalid < 0 )
 
3207
        invalid = chr;
 
3208
    else
 
3209
        invalid = QMIN( invalid, chr );
 
3210
#if 0 /// strange code!
 
3211
    if ( mFloatingItems ) {
 
3212
        for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
 
3213
            i->move( 0, -1 );
 
3214
    }
 
3215
#endif
 
3216
    //lm = rm = bm = tm = flm = -1;
 
3217
}
 
3218
 
 
3219
void KoTextParag::setChanged( bool b, bool /*recursive*/ )
 
3220
{
 
3221
    changed = b;
 
3222
    m_lineChanged = -1; // all
 
3223
    //if ( recursive ) {
 
3224
//      if ( doc && doc->parentParag() )
 
3225
//          doc->parentParag()->setChanged( b, recursive );
 
3226
//    }
 
3227
}
 
3228
 
 
3229
void KoTextParag::setLineChanged( short int line )
 
3230
{
 
3231
    if ( m_lineChanged == -1 ) {
 
3232
        if ( !changed ) // only if the whole parag wasn't "changed" already
 
3233
            m_lineChanged = line;
 
3234
    }
 
3235
    else
 
3236
        m_lineChanged = QMIN( m_lineChanged, line ); // also works if line=-1
 
3237
    changed = true;
 
3238
    //kdDebug(32500) << "KoTextParag::setLineChanged line=" << line << " -> m_lineChanged=" << m_lineChanged << endl;
 
3239
}
 
3240
 
 
3241
void KoTextParag::insert( int index, const QString &s )
 
3242
{
 
3243
#if 0
 
3244
    if ( doc && !doc->useFormatCollection() && doc->preProcessor() )
 
3245
        str->insert( index, s,
 
3246
                     doc->preProcessor()->format( KoTextPreProcessor::Standard ) );
 
3247
    else
 
3248
#endif
 
3249
        str->insert( index, s, formatCollection()->defaultFormat() );
 
3250
    invalidate( index );
 
3251
    //needPreProcess = TRUE;
 
3252
}
 
3253
 
 
3254
void KoTextParag::truncate( int index )
 
3255
{
 
3256
    str->truncate( index );
 
3257
    insert( length(), " " );
 
3258
    //needPreProcess = TRUE;
 
3259
}
 
3260
 
 
3261
void KoTextParag::remove( int index, int len )
 
3262
{
 
3263
    if ( index + len - str->length() > 0 )
 
3264
        return;
 
3265
    for ( int i = index; i < index + len; ++i ) {
 
3266
        KoTextStringChar *c = at( i );
 
3267
        if ( doc && c->isCustom() ) {
 
3268
            doc->unregisterCustomItem( c->customItem(), this );
 
3269
            //removeCustomItem();
 
3270
        }
 
3271
    }
 
3272
    str->remove( index, len );
 
3273
    invalidate( 0 );
 
3274
    //needPreProcess = TRUE;
 
3275
}
 
3276
 
 
3277
void KoTextParag::join( KoTextParag *s )
 
3278
{
 
3279
    //kdDebug(32500) << "KoTextParag::join this=" << paragId() << " (length " << length() << ") with " << s->paragId() << " (length " << s->length() << ")" << endl;
 
3280
    int oh = r.height() + s->r.height();
 
3281
    n = s->n;
 
3282
    if ( n )
 
3283
        n->p = this;
 
3284
    else if ( doc )
 
3285
        doc->setLastParag( this );
 
3286
 
 
3287
    int start = str->length();
 
3288
    if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
 
3289
        remove( length() - 1, 1 );
 
3290
        --start;
 
3291
    }
 
3292
    append( s->str->toString(), TRUE );
 
3293
 
 
3294
    for ( int i = 0; i < s->length(); ++i ) {
 
3295
        if ( !doc || doc->useFormatCollection() ) {
 
3296
            s->str->at( i ).format()->addRef();
 
3297
            str->setFormat( i + start, s->str->at( i ).format(), TRUE );
 
3298
        }
 
3299
        if ( s->str->at( i ).isCustom() ) {
 
3300
            KoTextCustomItem * item = s->str->at( i ).customItem();
 
3301
            str->at( i + start ).setCustomItem( item );
 
3302
            s->str->at( i ).loseCustomItem();
 
3303
            doc->unregisterCustomItem( item, s ); // ### missing in QRT
 
3304
            doc->registerCustomItem( item, this );
 
3305
        }
 
3306
    }
 
3307
    Q_ASSERT(str->at(str->length()-1).c == ' ');
 
3308
 
 
3309
    /*if ( !extraData() && s->extraData() ) {
 
3310
        setExtraData( s->extraData() );
 
3311
        s->setExtraData( 0 );
 
3312
    } else if ( extraData() && s->extraData() ) {
 
3313
        extraData()->join( s->extraData() );
 
3314
        }*/
 
3315
    delete s;
 
3316
    invalidate( 0 );
 
3317
    //// kotext
 
3318
    invalidateCounters();
 
3319
    ////
 
3320
    r.setHeight( oh );
 
3321
    //needPreProcess = TRUE;
 
3322
    if ( n ) {
 
3323
        KoTextParag *s = n;
 
3324
        while ( s ) {
 
3325
            s->id = s->p->id + 1;
 
3326
            //s->state = -1;
 
3327
            //s->needPreProcess = TRUE;
 
3328
            s->changed = TRUE;
 
3329
            s = s->n;
 
3330
        }
 
3331
    }
 
3332
    format();
 
3333
    //state = -1;
 
3334
}
 
3335
 
 
3336
void KoTextParag::move( int &dy )
 
3337
{
 
3338
    //kdDebug(32500) << "KoTextParag::move paragId=" << paragId() << " dy=" << dy << endl;
 
3339
    if ( dy == 0 )
 
3340
        return;
 
3341
    changed = TRUE;
 
3342
    r.moveBy( 0, dy );
 
3343
    if ( mFloatingItems ) {
 
3344
        for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
 
3345
                i->finalize();
 
3346
        }
 
3347
    }
 
3348
    //if ( p )
 
3349
    //    p->lastInFrame = TRUE; // Qt does this, but the loop at the end of format() calls move a lot!
 
3350
 
 
3351
    movedDown = FALSE;
 
3352
 
 
3353
    // do page breaks if required
 
3354
    if ( doc && doc->isPageBreakEnabled() ) {
 
3355
        int shift;
 
3356
        if ( ( shift = doc->formatter()->formatVertically(  doc, this ) ) ) {
 
3357
            if ( p )
 
3358
                p->setChanged( TRUE );
 
3359
            dy += shift;
 
3360
        }
 
3361
    }
 
3362
}
 
3363
 
 
3364
void KoTextParag::format( int start, bool doMove )
 
3365
{
 
3366
    if ( !str || str->length() == 0 || !formatter() )
 
3367
        return;
 
3368
 
 
3369
#if 0
 
3370
    if ( doc &&
 
3371
         doc->preProcessor() &&
 
3372
         ( needPreProcess || state == -1 ) )
 
3373
        doc->preProcessor()->process( doc, this, invalid <= 0 ? 0 : invalid );
 
3374
    needPreProcess = FALSE;
 
3375
#endif
 
3376
 
 
3377
    if ( invalid == -1 )
 
3378
        return;
 
3379
 
 
3380
    //kdDebug(32500) << "KoTextParag::format " << this << " id:" << paragId() << endl;
 
3381
 
 
3382
    r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
 
3383
    //if ( p )
 
3384
    //    p->lastInFrame = FALSE;
 
3385
 
 
3386
    movedDown = FALSE;
 
3387
    bool formattedAgain = FALSE;
 
3388
 
 
3389
 formatAgain:
 
3390
    r.setWidth( documentWidth() );
 
3391
 
 
3392
    // Not really useful....
 
3393
    if ( doc && mFloatingItems ) {
 
3394
        for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
 
3395
            if ( i->placement() == KoTextCustomItem::PlaceRight )
 
3396
                i->move( r.x() + r.width() - i->width, r.y() );
 
3397
            else
 
3398
                i->move( i->x(), r.y() );
 
3399
        }
 
3400
    }
 
3401
    QMap<int, KoTextParagLineStart*> oldLineStarts = lineStarts;
 
3402
    lineStarts.clear();
 
3403
    int y;
 
3404
    bool formatterWorked = formatter()->format( doc, this, start, oldLineStarts, y, m_wused );
 
3405
 
 
3406
    // It can't happen that width < minimumWidth -- hopefully.
 
3407
    //r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
 
3408
    //m_minw = formatter()->minimumWidth();
 
3409
 
 
3410
    QMap<int, KoTextParagLineStart*>::Iterator it = oldLineStarts.begin();
 
3411
 
 
3412
    for ( ; it != oldLineStarts.end(); ++it )
 
3413
        delete *it;
 
3414
 
 
3415
/*    if ( hasBorder() || string()->isRightToLeft() )
 
3416
        ////kotext: border extends to doc width
 
3417
        ////        and, bidi parags might have a counter, which will be right-aligned...
 
3418
    {
 
3419
        setWidth( textDocument()->width() - 1 );
 
3420
    }
 
3421
    else*/
 
3422
    {
 
3423
        if ( lineStarts.count() == 1 ) { //&& ( !doc || doc->flow()->isEmpty() ) ) {
 
3424
// kotext: for proper parag borders, we want all parags to be as wide as linestart->w
 
3425
/*            if ( !string()->isBidi() ) {
 
3426
                KoTextStringChar *c = &str->at( str->length() - 1 );
 
3427
                r.setWidth( c->x + c->width );
 
3428
            } else*/ {
 
3429
                r.setWidth( lineStarts[0]->w );
 
3430
            }
 
3431
        }
 
3432
        if ( newLinesAllowed ) {
 
3433
            it = lineStarts.begin();
 
3434
            int usedw = 0; int lineid = 0;
 
3435
            for ( ; it != lineStarts.end(); ++it, ++lineid ) {
 
3436
                usedw = QMAX( usedw, (*it)->w );
 
3437
            }
 
3438
            if ( r.width() <= 0 ) {
 
3439
                // if the user specifies an invalid rect, this means that the
 
3440
                // bounding box should grow to the width that the text actually
 
3441
                // needs
 
3442
                r.setWidth( usedw );
 
3443
            } else {
 
3444
                r.setWidth( QMIN( usedw, r.width() ) );
 
3445
            }
 
3446
        }
 
3447
    }
 
3448
 
 
3449
    if ( y != r.height() )
 
3450
        r.setHeight( y );
 
3451
 
 
3452
    if ( !visible )
 
3453
        r.setHeight( 0 );
 
3454
 
 
3455
    // do page breaks if required
 
3456
    if ( doc && doc->isPageBreakEnabled() ) {
 
3457
        int shift = doc->formatter()->formatVertically( doc, this );
 
3458
        //kdDebug(32500) << "formatVertically returned shift=" << shift << endl;
 
3459
        if ( shift && !formattedAgain ) {
 
3460
            formattedAgain = TRUE;
 
3461
            goto formatAgain;
 
3462
        }
 
3463
    }
 
3464
 
 
3465
    if ( doc )
 
3466
        doc->formatter()->postFormat( this );
 
3467
 
 
3468
    if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) {
 
3469
        //kdDebug(32500) << "r=" << r << " n->r=" << n->r << endl;
 
3470
        int dy = ( r.y() + r.height() ) - n->r.y();
 
3471
        KoTextParag *s = n;
 
3472
        bool makeInvalid = false; //p && p->lastInFrame;
 
3473
        //kdDebug(32500) << "might move of dy=" << dy << ". previous's lastInFrame (=makeInvalid): " << makeInvalid << endl;
 
3474
        while ( s && dy ) {
 
3475
            if ( s->movedDown ) { // (not in QRT) : moved down -> invalidate and stop moving down
 
3476
                s->invalidate( 0 ); // (there is no point in moving down a parag that has a frame break...)
 
3477
                break;
 
3478
            }
 
3479
            if ( !s->isFullWidth() )
 
3480
                makeInvalid = TRUE;
 
3481
            if ( makeInvalid )
 
3482
                s->invalidate( 0 );
 
3483
            s->move( dy );
 
3484
            //if ( s->lastInFrame )
 
3485
            //    makeInvalid = TRUE;
 
3486
            s = s->n;
 
3487
        }
 
3488
    }
 
3489
 
 
3490
//#define DEBUG_CI_PLACEMENT
 
3491
    if ( mFloatingItems ) {
 
3492
#ifdef DEBUG_CI_PLACEMENT
 
3493
        kdDebug(32500) << lineStarts.count() << " lines" << endl;
 
3494
#endif
 
3495
        // Place custom items - after the formatting is finished
 
3496
        int len = length();
 
3497
        int line = -1;
 
3498
        int lineY = 0; // the one called "cy" in other algos
 
3499
        int baseLine = 0;
 
3500
        QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
 
3501
        for ( int i = 0 ; i < len; ++i ) {
 
3502
            KoTextStringChar *chr = &str->at( i );
 
3503
            if ( chr->lineStart ) {
 
3504
                ++line;
 
3505
                if ( line > 0 )
 
3506
                    ++it;
 
3507
                lineY = (*it)->y;
 
3508
                baseLine = (*it)->baseLine;
 
3509
#ifdef DEBUG_CI_PLACEMENT
 
3510
                kdDebug(32500) << "New line (" << line << "): lineStart=" << (*it) << " lineY=" << lineY << " baseLine=" << baseLine << " height=" << (*it)->h << endl;
 
3511
#endif
 
3512
            }
 
3513
            if ( chr->isCustom() ) {
 
3514
                int x = chr->x;
 
3515
                KoTextCustomItem* item = chr->customItem();
 
3516
                Q_ASSERT( baseLine >= item->ascent() ); // something went wrong in KoTextFormatter if this isn't the case
 
3517
                int y = lineY + baseLine - item->ascent();
 
3518
#ifdef DEBUG_CI_PLACEMENT
 
3519
                kdDebug(32500) << "Custom item: i=" << i << " x=" << x << " lineY=" << lineY << " baseLine=" << baseLine << " ascent=" << item->ascent() << " -> y=" << y << endl;
 
3520
#endif
 
3521
                item->move( x, y );
 
3522
                item->finalize();
 
3523
            }
 
3524
        }
 
3525
    }
 
3526
 
 
3527
    //firstFormat = FALSE; //// unused
 
3528
    if ( formatterWorked > 0 ) // only if it worked, i.e. we had some width to format it
 
3529
    {
 
3530
        invalid = -1;
 
3531
    }
 
3532
    changed = TRUE;
 
3533
    //####   string()->setTextChanged( FALSE );
 
3534
}
 
3535
 
 
3536
int KoTextParag::lineHeightOfChar( int i, int *bl, int *y ) const
 
3537
{
 
3538
    if ( !isValid() )
 
3539
        ( (KoTextParag*)this )->format();
 
3540
 
 
3541
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
 
3542
    --it;
 
3543
    for ( ;; ) {
 
3544
        if ( i >= it.key() ) {
 
3545
            if ( bl )
 
3546
                *bl = ( *it )->baseLine;
 
3547
            if ( y )
 
3548
                *y = ( *it )->y;
 
3549
            return ( *it )->h;
 
3550
        }
 
3551
        if ( it == lineStarts.begin() )
 
3552
            break;
 
3553
        --it;
 
3554
    }
 
3555
 
 
3556
    kdWarning(32500) << "KoTextParag::lineHeightOfChar: couldn't find lh for " << i << endl;
 
3557
    return 15;
 
3558
}
 
3559
 
 
3560
KoTextStringChar *KoTextParag::lineStartOfChar( int i, int *index, int *line ) const
 
3561
{
 
3562
    if ( !isValid() )
 
3563
        ( (KoTextParag*)this )->format();
 
3564
 
 
3565
    int l = (int)lineStarts.count() - 1;
 
3566
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
 
3567
    --it;
 
3568
    for ( ;; ) {
 
3569
        if ( i >= it.key() ) {
 
3570
            if ( index )
 
3571
                *index = it.key();
 
3572
            if ( line )
 
3573
                *line = l;
 
3574
            return &str->at( it.key() );
 
3575
        }
 
3576
        if ( it == lineStarts.begin() )
 
3577
            break;
 
3578
        --it;
 
3579
        --l;
 
3580
    }
 
3581
 
 
3582
    kdWarning(32500) << "KoTextParag::lineStartOfChar: couldn't find " << i << endl;
 
3583
    return 0;
 
3584
}
 
3585
 
 
3586
int KoTextParag::lines() const
 
3587
{
 
3588
    if ( !isValid() )
 
3589
        ( (KoTextParag*)this )->format();
 
3590
 
 
3591
    return (int)lineStarts.count();
 
3592
}
 
3593
 
 
3594
KoTextStringChar *KoTextParag::lineStartOfLine( int line, int *index ) const
 
3595
{
 
3596
    if ( !isValid() )
 
3597
        ( (KoTextParag*)this )->format();
 
3598
 
 
3599
    if ( line >= 0 && line < (int)lineStarts.count() ) {
 
3600
        QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
3601
        while ( line-- > 0 )
 
3602
            ++it;
 
3603
        int i = it.key();
 
3604
        if ( index )
 
3605
            *index = i;
 
3606
        return &str->at( i );
 
3607
    }
 
3608
 
 
3609
    kdWarning(32500) << "KoTextParag::lineStartOfLine: couldn't find " << line << endl;
 
3610
    return 0;
 
3611
}
 
3612
 
 
3613
int KoTextParag::leftGap() const
 
3614
{
 
3615
    if ( !isValid() )
 
3616
        ( (KoTextParag*)this )->format();
 
3617
 
 
3618
    int line = 0;
 
3619
    int x = str->at(0).x;  /* set x to x of first char */
 
3620
    if ( str->isBidi() ) {
 
3621
        for ( int i = 1; i < str->length(); ++i )
 
3622
            x = QMIN(x, str->at(i).x);
 
3623
        return x;
 
3624
    }
 
3625
 
 
3626
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
3627
    while (line < (int)lineStarts.count()) {
 
3628
        int i = it.key(); /* char index */
 
3629
        x = QMIN(x, str->at(i).x);
 
3630
        ++it;
 
3631
        ++line;
 
3632
    }
 
3633
    return x;
 
3634
}
 
3635
 
 
3636
void KoTextParag::setFormat( int index, int len, const KoTextFormat *_f, bool useCollection, int flags )
 
3637
{
 
3638
    Q_ASSERT( useCollection ); // just for info
 
3639
    if ( index < 0 )
 
3640
        index = 0;
 
3641
    if ( index > str->length() - 1 )
 
3642
        index = str->length() - 1;
 
3643
    if ( index + len >= str->length() )
 
3644
        len = str->length() - index;
 
3645
 
 
3646
    KoTextFormatCollection *fc = 0;
 
3647
    if ( useCollection )
 
3648
        fc = formatCollection();
 
3649
    KoTextFormat *of;
 
3650
    for ( int i = 0; i < len; ++i ) {
 
3651
        of = str->at( i + index ).format();
 
3652
        if ( !changed && _f->key() != of->key() )
 
3653
            changed = TRUE;
 
3654
        // Check things that need the textformatter to run
 
3655
        // (e.g. not color changes)
 
3656
        // ######## Is this test exhaustive?
 
3657
        if ( invalid == -1 &&
 
3658
             ( _f->font().family() != of->font().family() ||
 
3659
               _f->pointSize() != of->pointSize() ||
 
3660
               _f->font().weight() != of->font().weight() ||
 
3661
               _f->font().italic() != of->font().italic() ||
 
3662
               _f->vAlign() != of->vAlign() ||
 
3663
               _f->relativeTextSize() != of->relativeTextSize() ||
 
3664
               _f->offsetFromBaseLine() != of->offsetFromBaseLine() ||
 
3665
               _f->wordByWord() != of->wordByWord()  ||
 
3666
               _f->attributeFont() != of->attributeFont() ||
 
3667
               _f->language() != of->language() ||
 
3668
               _f->hyphenation() != of->hyphenation() ||
 
3669
               _f->shadowDistanceX() != of->shadowDistanceX() ||
 
3670
               _f->shadowDistanceY() != of->shadowDistanceY()
 
3671
                 ) ) {
 
3672
            invalidate( 0 );
 
3673
        }
 
3674
        if ( flags == -1 || flags == KoTextFormat::Format || !fc ) {
 
3675
#ifdef DEBUG_COLLECTION
 
3676
            kdDebug(32500) << " KoTextParag::setFormat, will use format(f) " << f << " " << _f->key() << endl;
 
3677
#endif
 
3678
            KoTextFormat* f = fc ? fc->format( _f ) : const_cast<KoTextFormat *>( _f );
 
3679
            str->setFormat( i + index, f, useCollection );
 
3680
        } else {
 
3681
#ifdef DEBUG_COLLECTION
 
3682
            kdDebug(32500) << " KoTextParag::setFormat, will use format(of,f,flags) of=" << of << " " << of->key() << ", f=" << _f << " " << _f->key() << endl;
 
3683
#endif
 
3684
            KoTextFormat *fm = fc->format( of, _f, flags );
 
3685
#ifdef DEBUG_COLLECTION
 
3686
            kdDebug(32500) << " KoTextParag::setFormat, format(of,f,flags) returned " << fm << " " << fm->key() << " " << endl;
 
3687
#endif
 
3688
            str->setFormat( i + index, fm, useCollection );
 
3689
        }
 
3690
    }
 
3691
}
 
3692
 
 
3693
void KoTextParag::indent( int *oldIndent, int *newIndent )
 
3694
{
 
3695
    if ( !doc || !doc->indent() /*|| isListItem() TODO*/ ) {
 
3696
        if ( oldIndent )
 
3697
            *oldIndent = 0;
 
3698
        if ( newIndent )
 
3699
            *newIndent = 0;
 
3700
        if ( oldIndent && newIndent )
 
3701
            *newIndent = *oldIndent;
 
3702
        return;
 
3703
    }
 
3704
    doc->indent()->indent( doc, this, oldIndent, newIndent );
 
3705
}
 
3706
 
 
3707
void KoTextParag::drawCursorDefault( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
 
3708
{
 
3709
    painter.fillRect( QRect( curx, cury, 1, curh ), cg.color( QColorGroup::Text ) );
 
3710
    painter.save();
 
3711
    if ( string()->isBidi() ) {
 
3712
        const int d = 4;
 
3713
        if ( at( cursor->index() )->rightToLeft ) {
 
3714
            painter.setPen( Qt::black );
 
3715
            painter.drawLine( curx, cury, curx - d / 2, cury + d / 2 );
 
3716
            painter.drawLine( curx, cury + d, curx - d / 2, cury + d / 2 );
 
3717
        } else {
 
3718
            painter.setPen( Qt::black );
 
3719
            painter.drawLine( curx, cury, curx + d / 2, cury + d / 2 );
 
3720
            painter.drawLine( curx, cury + d, curx + d / 2, cury + d / 2 );
 
3721
        }
 
3722
    }
 
3723
    painter.restore();
 
3724
}
 
3725
 
 
3726
int *KoTextParag::tabArray() const
 
3727
{
 
3728
    int *ta = tArray;
 
3729
    if ( !ta && doc )
 
3730
        ta = doc->tabArray();
 
3731
    return ta;
 
3732
}
 
3733
 
 
3734
int KoTextParag::nextTabDefault( int, int x )
 
3735
{
 
3736
    int *ta = tArray;
 
3737
    //if ( doc ) {
 
3738
        if ( !ta )
 
3739
            ta = doc->tabArray();
 
3740
        int tabStopWidth = doc->tabStopWidth();
 
3741
    //}
 
3742
    if ( tabStopWidth != 0 )
 
3743
        return tabStopWidth*(x/tabStopWidth+1);
 
3744
    else
 
3745
        return x;
 
3746
}
 
3747
 
 
3748
/*void KoTextParag::setPainter( QPainter *p, bool adjust  )
 
3749
{
 
3750
    pntr = p;
 
3751
    for ( int i = 0; i < length(); ++i ) {
 
3752
        if ( at( i )->isCustom() )
 
3753
            at( i )->customItem()->setPainter( p, adjust  );
 
3754
    }
 
3755
}*/
 
3756
 
 
3757
KoTextFormatCollection *KoTextParag::formatCollection() const
 
3758
{
 
3759
    if ( doc )
 
3760
        return doc->formatCollection();
 
3761
    //if ( !qFormatCollection )
 
3762
    //    qFormatCollection = new KoTextFormatCollection;
 
3763
    //return qFormatCollection;
 
3764
    return 0L;
 
3765
}
 
3766
 
 
3767
QString KoTextParag::richText() const
 
3768
{
 
3769
    QString s;
 
3770
#if 0
 
3771
    KoTextStringChar *formatChar = 0;
 
3772
    QString spaces;
 
3773
    for ( int i = 0; i < length()-1; ++i ) {
 
3774
        KoTextStringChar *c = &str->at( i );
 
3775
#endif
 
3776
#if 0
 
3777
        if ( c->isAnchor() && !c->anchorName().isEmpty() ) {
 
3778
            if ( c->anchorName().contains( '#' ) ) {
 
3779
                QStringList l = QStringList::split( '#', c->anchorName() );
 
3780
                for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it )
 
3781
                    s += "<a name=\"" + *it + "\"></a>";
 
3782
            } else {
 
3783
                s += "<a name=\"" + c->anchorName() + "\"></a>";
 
3784
            }
 
3785
        }
 
3786
#endif
 
3787
#if 0
 
3788
        if ( !formatChar ) {
 
3789
            s += c->format()->makeFormatChangeTags( 0, QString::null, QString::null /*c->anchorHref()*/ );
 
3790
            formatChar = c;
 
3791
        } else if ( ( formatChar->format()->key() != c->format()->key() && c->c != ' ' ) /* ||
 
3792
                  (formatChar->isAnchor() != c->isAnchor() &&
 
3793
                  (!c->anchorHref().isEmpty() || !formatChar->anchorHref().isEmpty() ) ) */ )  {// lisp was here
 
3794
            s += c->format()->makeFormatChangeTags( formatChar->format(), QString::null /*formatChar->anchorHref()*/,
 
3795
                                                    QString::null /*c->anchorHref()*/ );
 
3796
            formatChar = c;
 
3797
        }
 
3798
 
 
3799
        if ( c->c == ' ' || c->c == '\t' ) {
 
3800
            spaces += c->c;
 
3801
            continue;
 
3802
        } else if ( !spaces.isEmpty() ) {
 
3803
            if ( spaces.length() > 1 || spaces[0] == '\t' )
 
3804
                s += "<wsp>" + spaces + "</wsp>";
 
3805
            else
 
3806
                s += spaces;
 
3807
            spaces = QString::null;
 
3808
        }
 
3809
 
 
3810
        if ( c->c == '<' ) {
 
3811
            s += "&lt;";
 
3812
        } else if ( c->c == '>' ) {
 
3813
            s += "&gt;";
 
3814
        } else if ( c->isCustom() ) {
 
3815
            s += c->customItem()->richText();
 
3816
        } else {
 
3817
            s += c->c;
 
3818
        }
 
3819
    }
 
3820
    if ( !spaces.isEmpty() ) {
 
3821
        if ( spaces.length() > 1 || spaces[0] == '\t' )
 
3822
                s += "<wsp>" + spaces + "</wsp>";
 
3823
        else
 
3824
            s += spaces;
 
3825
    }
 
3826
 
 
3827
    if ( formatChar )
 
3828
        s += formatChar->format()->makeFormatEndTags( QString::null /*formatChar->anchorHref()*/ );
 
3829
#endif
 
3830
    return s;
 
3831
}
 
3832
 
 
3833
/*void KoTextParag::addCommand( KoTextDocCommand *cmd )
 
3834
{
 
3835
    if ( !doc )
 
3836
        commandHistory->addCommand( cmd );
 
3837
    else
 
3838
        doc->commands()->addCommand( cmd );
 
3839
}
 
3840
 
 
3841
KoTextCursor *KoTextParag::undo( KoTextCursor *c )
 
3842
{
 
3843
    if ( !doc )
 
3844
        return commandHistory->undo( c );
 
3845
    return doc->commands()->undo( c );
 
3846
}
 
3847
 
 
3848
KoTextCursor *KoTextParag::redo( KoTextCursor *c )
 
3849
{
 
3850
    if ( !doc )
 
3851
        return commandHistory->redo( c );
 
3852
    return doc->commands()->redo( c );
 
3853
}*/
 
3854
 
 
3855
void KoTextParag::show()
 
3856
{
 
3857
    if ( visible || !doc )
 
3858
        return;
 
3859
    visible = TRUE;
 
3860
}
 
3861
 
 
3862
void KoTextParag::hide()
 
3863
{
 
3864
    if ( !visible || !doc )
 
3865
        return;
 
3866
    visible = FALSE;
 
3867
}
 
3868
 
 
3869
void KoTextParag::setDirection( QChar::Direction d )
 
3870
{
 
3871
    if ( str && str->direction() != d ) {
 
3872
        str->setDirection( d );
 
3873
        invalidate( 0 );
 
3874
        //// kotext
 
3875
        m_layout.direction = d;
 
3876
        ////
 
3877
    }
 
3878
}
 
3879
 
 
3880
QChar::Direction KoTextParag::direction() const
 
3881
{
 
3882
    return (str ? str->direction() : QChar::DirON );
 
3883
}
 
3884
 
 
3885
void KoTextParag::setSelection( int id, int start, int end )
 
3886
{
 
3887
    QMap<int, KoTextParagSelection>::ConstIterator it = selections().find( id );
 
3888
    if ( it != mSelections->end() ) {
 
3889
        if ( start == ( *it ).start && end == ( *it ).end )
 
3890
            return;
 
3891
    }
 
3892
 
 
3893
    KoTextParagSelection sel;
 
3894
    sel.start = start;
 
3895
    sel.end = end;
 
3896
    (*mSelections)[ id ] = sel;
 
3897
    setChanged( TRUE, TRUE );
 
3898
}
 
3899
 
 
3900
void KoTextParag::removeSelection( int id )
 
3901
{
 
3902
    if ( !hasSelection( id ) )
 
3903
        return;
 
3904
    if ( mSelections )
 
3905
        mSelections->remove( id );
 
3906
    setChanged( TRUE, TRUE );
 
3907
}
 
3908
 
 
3909
int KoTextParag::selectionStart( int id ) const
 
3910
{
 
3911
    if ( !mSelections )
 
3912
        return -1;
 
3913
    QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
 
3914
    if ( it == mSelections->end() )
 
3915
        return -1;
 
3916
    return ( *it ).start;
 
3917
}
 
3918
 
 
3919
int KoTextParag::selectionEnd( int id ) const
 
3920
{
 
3921
    if ( !mSelections )
 
3922
        return -1;
 
3923
    QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
 
3924
    if ( it == mSelections->end() )
 
3925
        return -1;
 
3926
    return ( *it ).end;
 
3927
}
 
3928
 
 
3929
bool KoTextParag::hasSelection( int id ) const
 
3930
{
 
3931
    if ( !mSelections )
 
3932
        return FALSE;
 
3933
    QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
 
3934
    if ( it == mSelections->end() )
 
3935
        return FALSE;
 
3936
    return ( *it ).start != ( *it ).end || length() == 1;
 
3937
}
 
3938
 
 
3939
bool KoTextParag::fullSelected( int id ) const
 
3940
{
 
3941
    if ( !mSelections )
 
3942
        return FALSE;
 
3943
    QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
 
3944
    if ( it == mSelections->end() )
 
3945
        return FALSE;
 
3946
    return ( *it ).start == 0 && ( *it ).end == str->length() - 1;
 
3947
}
 
3948
 
 
3949
int KoTextParag::lineY( int l ) const
 
3950
{
 
3951
    if ( l > (int)lineStarts.count() - 1 ) {
 
3952
        kdWarning(32500) << "KoTextParag::lineY: line " << l << " out of range!" << endl;
 
3953
        return 0;
 
3954
    }
 
3955
 
 
3956
    if ( !isValid() )
 
3957
        ( (KoTextParag*)this )->format();
 
3958
 
 
3959
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
3960
    while ( l-- > 0 )
 
3961
        ++it;
 
3962
    return ( *it )->y;
 
3963
}
 
3964
 
 
3965
int KoTextParag::lineBaseLine( int l ) const
 
3966
{
 
3967
    if ( l > (int)lineStarts.count() - 1 ) {
 
3968
        kdWarning(32500) << "KoTextParag::lineBaseLine: line " << l << " out of range!" << endl;
 
3969
        return 10;
 
3970
    }
 
3971
 
 
3972
    if ( !isValid() )
 
3973
        ( (KoTextParag*)this )->format();
 
3974
 
 
3975
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
3976
    while ( l-- > 0 )
 
3977
        ++it;
 
3978
    return ( *it )->baseLine;
 
3979
}
 
3980
 
 
3981
int KoTextParag::lineHeight( int l ) const
 
3982
{
 
3983
    if ( l > (int)lineStarts.count() - 1 ) {
 
3984
        kdWarning(32500) << "KoTextParag::lineHeight: line " << l << " out of range!" << endl;
 
3985
        return 15;
 
3986
    }
 
3987
 
 
3988
    if ( !isValid() )
 
3989
        ( (KoTextParag*)this )->format();
 
3990
 
 
3991
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
3992
    while ( l-- > 0 )
 
3993
        ++it;
 
3994
    return ( *it )->h;
 
3995
}
 
3996
 
 
3997
void KoTextParag::lineInfo( int l, int &y, int &h, int &bl ) const
 
3998
{
 
3999
    if ( l > (int)lineStarts.count() - 1 ) {
 
4000
        kdWarning(32500) << "KoTextParag::lineInfo: line " << l << " out of range!" << endl;
 
4001
        kdDebug(32500) << (int)lineStarts.count() - 1 << " " << l << endl;
 
4002
        y = 0;
 
4003
        h = 15;
 
4004
        bl = 10;
 
4005
        return;
 
4006
    }
 
4007
 
 
4008
    if ( !isValid() )
 
4009
        ( (KoTextParag*)this )->format();
 
4010
 
 
4011
    QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
 
4012
    while ( l-- > 0 )
 
4013
        ++it;
 
4014
    y = ( *it )->y;
 
4015
    h = ( *it )->h;
 
4016
    bl = ( *it )->baseLine;
 
4017
}
 
4018
 
 
4019
uint KoTextParag::alignment() const
 
4020
{
 
4021
    return align;
 
4022
}
 
4023
 
 
4024
void KoTextParag::setFormat( KoTextFormat *fm )
 
4025
{
 
4026
#if 0
 
4027
    bool doUpdate = FALSE;
 
4028
    if (defFormat && (defFormat != formatCollection()->defaultFormat()))
 
4029
       doUpdate = TRUE;
 
4030
#endif
 
4031
    defFormat = formatCollection()->format( fm );
 
4032
#if 0
 
4033
    if ( !doUpdate )
 
4034
        return;
 
4035
    for ( int i = 0; i < length(); ++i ) {
 
4036
        if ( at( i )->format()->styleName() == defFormat->styleName() )
 
4037
            at( i )->format()->updateStyle();
 
4038
    }
 
4039
#endif
 
4040
}
 
4041
 
 
4042
KoTextFormatterBase *KoTextParag::formatter() const
 
4043
{
 
4044
    if ( doc )
 
4045
        return doc->formatter();
 
4046
    //if ( pFormatter )
 
4047
    //    return pFormatter;
 
4048
    //return ( ( (KoTextParag*)this )->pFormatter = new KoTextFormatterBaseBreakWords );
 
4049
    return 0L;
 
4050
}
 
4051
 
 
4052
/*void KoTextParag::setFormatter( KoTextFormatterBase *f )
 
4053
{
 
4054
    if ( doc ) return;
 
4055
    if ( pFormatter ) delete pFormatter;
 
4056
    pFormatter = f;
 
4057
}*/
 
4058
 
 
4059
/*int KoTextParag::minimumWidth() const
 
4060
{
 
4061
    //return doc ? doc->minimumWidth() : 0;
 
4062
    return m_minw;
 
4063
}*/
 
4064
 
 
4065
int KoTextParag::widthUsed() const
 
4066
{
 
4067
    return m_wused;
 
4068
}
 
4069
 
 
4070
void KoTextParag::setTabArray( int *a )
 
4071
{
 
4072
    delete [] tArray;
 
4073
    tArray = a;
 
4074
}
 
4075
 
 
4076
void KoTextParag::setTabStops( int tw )
 
4077
{
 
4078
    if ( doc )
 
4079
        doc->setTabStops( tw );
 
4080
    //else
 
4081
    //    tabStopWidth = tw;
 
4082
}
 
4083
 
 
4084
QMap<int, KoTextParagSelection> &KoTextParag::selections() const
 
4085
{
 
4086
    if ( !mSelections )
 
4087
        ((KoTextParag *)this)->mSelections = new QMap<int, KoTextParagSelection>;
 
4088
    return *mSelections;
 
4089
}
 
4090
 
 
4091
QPtrList<KoTextCustomItem> &KoTextParag::floatingItems() const
 
4092
{
 
4093
    if ( !mFloatingItems )
 
4094
        ((KoTextParag *)this)->mFloatingItems = new QPtrList<KoTextCustomItem>;
 
4095
    return *mFloatingItems;
 
4096
}
 
4097
 
 
4098
void KoTextCursor::setIndex( int i, bool restore )
 
4099
{
 
4100
    if ( restore )
 
4101
        restoreState();
 
4102
// Note: QRT doesn't allow to position the cursor at string->length
 
4103
// However we need it, when applying a style to a paragraph, so that
 
4104
// the trailing space gets the style change applied as well.
 
4105
// Obviously "right of the trailing space" isn't a good place for a real
 
4106
// cursor, but this needs to be checked somewhere else.
 
4107
    if ( i < 0 || i > string->length() ) {
 
4108
#if defined(QT_CHECK_RANGE)
 
4109
        kdWarning(32500) << "KoTextCursor::setIndex: " << i << " out of range" << endl;
 
4110
        //abort();
 
4111
#endif
 
4112
        i = i < 0 ? 0 : string->length() - 1;
 
4113
    }
 
4114
 
 
4115
    tmpIndex = -1;
 
4116
    idx = i;
 
4117
}
 
4118
 
 
4119
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
4120
 
 
4121
KoTextFormatterBase::KoTextFormatterBase()
 
4122
    : wrapColumn( -1 ), wrapEnabled( TRUE ),
 
4123
      m_bViewFormattingChars( false ),
 
4124
      biw( true /*default in kotext*/ )
 
4125
{
 
4126
}
 
4127
 
 
4128
// See KoTextFormatter
 
4129
#if 0
 
4130
KoTextParagLineStart *KoTextFormatterBase::formatLine( KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
 
4131
                                                   KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
 
4132
{
 
4133
#ifndef QT_NO_COMPLEXTEXT
 
4134
    if( string->isBidi() )
 
4135
        return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
 
4136
#endif
 
4137
    space = QMAX( space, 0 ); // #### with nested tables this gets negative because of a bug I didn't find yet, so workaround for now. This also means non-left aligned nested tables do not work at the moment
 
4138
    int start = (startChar - &string->at(0));
 
4139
    int last = (lastChar - &string->at(0) );
 
4140
    // do alignment Auto == Left in this case
 
4141
    if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
 
4142
        if ( align & Qt::AlignHCenter )
 
4143
            space /= 2;
 
4144
        for ( int j = start; j <= last; ++j )
 
4145
            string->at( j ).x += space;
 
4146
    } else if ( align & AlignJustify ) {
 
4147
        int numSpaces = 0;
 
4148
        for ( int j = start; j < last; ++j ) {
 
4149
            if( isBreakable( string, j ) ) {
 
4150
                numSpaces++;
 
4151
            }
 
4152
        }
 
4153
        int toAdd = 0;
 
4154
        for ( int k = start + 1; k <= last; ++k ) {
 
4155
            if( isBreakable( string, k ) && numSpaces ) {
 
4156
                int s = space / numSpaces;
 
4157
                toAdd += s;
 
4158
                space -= s;
 
4159
                numSpaces--;
 
4160
            }
 
4161
            string->at( k ).x += toAdd;
 
4162
        }
 
4163
    }
 
4164
 
 
4165
    if ( last >= 0 && last < string->length() )
 
4166
        line->w = string->at( last ).x + string->width( last );
 
4167
    else
 
4168
        line->w = 0;
 
4169
 
 
4170
    return new KoTextParagLineStart();
 
4171
}
 
4172
#endif
 
4173
 
 
4174
#ifdef BIDI_DEBUG
 
4175
#include <iostream>
 
4176
#endif
 
4177
 
 
4178
// collects one line of the paragraph and transforms it to visual order
 
4179
KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
 
4180
                                                        KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
 
4181
{
 
4182
    int start = (startChar - &text->at(0));
 
4183
    int last = (lastChar - &text->at(0) );
 
4184
    //kdDebug(32500) << "doing BiDi reordering from " << start << " to " << last << "!" << endl;
 
4185
 
 
4186
    KoBidiControl *control = new KoBidiControl( line->context(), line->status );
 
4187
    QString str;
 
4188
    str.setUnicode( 0, last - start + 1 );
 
4189
    // fill string with logically ordered chars.
 
4190
    KoTextStringChar *ch = startChar;
 
4191
    QChar *qch = (QChar *)str.unicode();
 
4192
    while ( ch <= lastChar ) {
 
4193
        *qch = ch->c;
 
4194
        qch++;
 
4195
        ch++;
 
4196
    }
 
4197
    int x = startChar->x;
 
4198
 
 
4199
    QPtrList<KoTextRun> *runs;
 
4200
    runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
 
4201
                                         (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
 
4202
 
 
4203
    // now construct the reordered string out of the runs...
 
4204
 
 
4205
    int numSpaces = 0;
 
4206
    // set the correct alignment. This is a bit messy....
 
4207
    if( align == Qt::AlignAuto ) {
 
4208
        // align according to directionality of the paragraph...
 
4209
        if ( text->isRightToLeft() )
 
4210
            align = Qt::AlignRight;
 
4211
    }
 
4212
 
 
4213
    if ( align & Qt::AlignHCenter )
 
4214
        x += space/2;
 
4215
    else if ( align & Qt::AlignRight )
 
4216
        x += space;
 
4217
    else if ( align & Qt::AlignJustify ) {
 
4218
        for ( int j = start; j < last; ++j ) {
 
4219
            if( isBreakable( text, j ) ) {
 
4220
                numSpaces++;
 
4221
            }
 
4222
        }
 
4223
    }
 
4224
    int toAdd = 0;
 
4225
    bool first = TRUE;
 
4226
    KoTextRun *r = runs->first();
 
4227
    int xmax = -0xffffff;
 
4228
    while ( r ) {
 
4229
        if(r->level %2) {
 
4230
            // odd level, need to reverse the string
 
4231
            int pos = r->stop + start;
 
4232
            while(pos >= r->start + start) {
 
4233
                KoTextStringChar *c = &text->at(pos);
 
4234
                if( numSpaces && !first && isBreakable( text, pos ) ) {
 
4235
                    int s = space / numSpaces;
 
4236
                    toAdd += s;
 
4237
                    space -= s;
 
4238
                    numSpaces--;
 
4239
                } else if ( first ) {
 
4240
                    first = FALSE;
 
4241
                    if ( c->c == ' ' )
 
4242
                        x -= c->format()->width( ' ' );
 
4243
                }
 
4244
                c->x = x + toAdd;
 
4245
                c->rightToLeft = TRUE;
 
4246
                c->startOfRun = FALSE;
 
4247
                int ww = 0;
 
4248
                if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
 
4249
                    ww = c->width;
 
4250
                } else {
 
4251
                    ww = c->format()->width( ' ' );
 
4252
                }
 
4253
                if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
 
4254
                x += ww;
 
4255
                pos--;
 
4256
            }
 
4257
        } else {
 
4258
            int pos = r->start + start;
 
4259
            while(pos <= r->stop + start) {
 
4260
                KoTextStringChar* c = &text->at(pos);
 
4261
                if( numSpaces && !first && isBreakable( text, pos ) ) {
 
4262
                    int s = space / numSpaces;
 
4263
                    toAdd += s;
 
4264
                    space -= s;
 
4265
                    numSpaces--;
 
4266
                } else if ( first ) {
 
4267
                    first = FALSE;
 
4268
                    if ( c->c == ' ' )
 
4269
                        x -= c->format()->width( ' ' );
 
4270
                }
 
4271
                c->x = x + toAdd;
 
4272
                c->rightToLeft = FALSE;
 
4273
                c->startOfRun = FALSE;
 
4274
                int ww = 0;
 
4275
                if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
 
4276
                    ww = c->width;
 
4277
                } else {
 
4278
                    ww = c->format()->width( ' ' );
 
4279
                }
 
4280
                //kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
 
4281
                if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
 
4282
                x += ww;
 
4283
                pos++;
 
4284
            }
 
4285
        }
 
4286
        text->at( r->start + start ).startOfRun = TRUE;
 
4287
        r = runs->next();
 
4288
    }
 
4289
 
 
4290
    line->w = xmax + 10;
 
4291
    KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
 
4292
    delete control;
 
4293
    delete runs;
 
4294
    return ls;
 
4295
}
 
4296
 
 
4297
bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
 
4298
{
 
4299
    if ( string->at( pos ).c == QChar(160) ) //non-breaking space
 
4300
        return true;
 
4301
    return isBreakable( string, pos );
 
4302
}
 
4303
 
 
4304
bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
 
4305
{
 
4306
    const QChar &c = string->at( pos ).c;
 
4307
    char ch = c.latin1();
 
4308
    if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U )
 
4309
        return TRUE;
 
4310
    if ( c == '-' || c.unicode() == 0xad ) // hyphen or soft hyphen
 
4311
        return TRUE;
 
4312
    if ( !ch ) {
 
4313
        // not latin1, need to do more sophisticated checks for other scripts
 
4314
        uchar row = c.row();
 
4315
        if ( row == 0x0e ) {
 
4316
            // 0e00 - 0e7f == Thai
 
4317
            if ( c.cell() < 0x80 ) {
 
4318
#ifdef HAVE_THAI_BREAKS
 
4319
                // check for thai
 
4320
                if( string != cachedString ) {
 
4321
                    // build up string of thai chars
 
4322
                    QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
 
4323
                    if ( !thaiCache )
 
4324
                        thaiCache = new QCString;
 
4325
                    if ( !thaiIt )
 
4326
                        thaiIt = ThBreakIterator::createWordInstance();
 
4327
                    *thaiCache = thaiCodec->fromUnicode( s->string() );
 
4328
                }
 
4329
                thaiIt->setText(thaiCache->data());
 
4330
                for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) {
 
4331
                    if( i == pos )
 
4332
                        return TRUE;
 
4333
                    if( i > pos )
 
4334
                        return FALSE;
 
4335
                }
 
4336
                return FALSE;
 
4337
#else
 
4338
                // if we don't have a thai line breaking lib, allow
 
4339
                // breaks everywhere except directly before punctuation.
 
4340
                return TRUE;
 
4341
#endif
 
4342
            } else
 
4343
                return FALSE;
 
4344
        }
 
4345
        if ( row < 0x11 ) // no asian font
 
4346
            return FALSE;
 
4347
        if ( row > 0x2d && row < 0xfb || row == 0x11 )
 
4348
            // asian line breaking. Everywhere allowed except directly
 
4349
            // in front of a punctuation character.
 
4350
            return TRUE;
 
4351
    }
 
4352
    return FALSE;
 
4353
}
 
4354
 
 
4355
void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
 
4356
{
 
4357
    // This tests if we break at the same character in more than one line,
 
4358
    // i.e. there no space even for _one_ char in a given line.
 
4359
    // However this shouldn't happen, KoTextFormatter prevents it, otherwise
 
4360
    // we could loop forever (e.g. if one char is wider than the page...)
 
4361
#ifndef NDEBUG
 
4362
    QMap<int, KoTextParagLineStart*>::Iterator it;
 
4363
    if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
 
4364
        lineStarts.insert( index, ls );
 
4365
    } else {
 
4366
        kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
 
4367
        delete *it;
 
4368
        lineStarts.remove( it );
 
4369
        lineStarts.insert( index, ls );
 
4370
    }
 
4371
#else // non-debug code, take the fast route
 
4372
    lineStarts.insert( index, ls );
 
4373
#endif
 
4374
}
 
4375
 
 
4376
 
 
4377
/* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
 
4378
 the shift of the paragraphs bottom line.
 
4379
 */
 
4380
int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
 
4381
{
 
4382
    int oldHeight = parag->rect().height();
 
4383
    QMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
 
4384
    QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
 
4385
    int h = doc->addMargins() ? parag->topMargin() : 0;
 
4386
    for ( ; it != lineStarts.end() ; ++it  ) {
 
4387
        KoTextParagLineStart * ls = it.data();
 
4388
        ls->y = h;
 
4389
        KoTextStringChar *c = &parag->string()->at(it.key());
 
4390
        if ( c && c->customItem() && c->customItem()->ownLine() ) {
 
4391
            int h = c->customItem()->height;
 
4392
            c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
 
4393
            int delta = c->customItem()->height - h;
 
4394
            ls->h += delta;
 
4395
            if ( delta )
 
4396
                parag->setMovedDown( TRUE );
 
4397
        } else {
 
4398
            int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
 
4399
            ls->y += shift;
 
4400
            if ( shift )
 
4401
                parag->setMovedDown( TRUE );
 
4402
        }
 
4403
        h = ls->y + ls->h;
 
4404
    }
 
4405
    int m = parag->bottomMargin();
 
4406
    if ( parag->next() && doc && !doc->addMargins() )
 
4407
        m = QMAX( m, parag->next()->topMargin() );
 
4408
    //if ( parag->next() && parag->next()->isLineBreak() )
 
4409
    //  m = 0;
 
4410
    h += m;
 
4411
    parag->setHeight( h );
 
4412
    return h - oldHeight;
 
4413
}
 
4414
 
 
4415
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
4416
 
 
4417
KoTextIndent::KoTextIndent()
 
4418
{
 
4419
}
 
4420
 
 
4421
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
4422
 
 
4423
KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
 
4424
      :  width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
 
4425
{
 
4426
    m_deleted = false; // added for kotext
 
4427
}
 
4428
 
 
4429
KoTextCustomItem::~KoTextCustomItem()
 
4430
{
 
4431
}
 
4432
 
 
4433
KoTextFlow::KoTextFlow()
 
4434
{
 
4435
    w = 0;
 
4436
    leftItems.setAutoDelete( FALSE );
 
4437
    rightItems.setAutoDelete( FALSE );
 
4438
}
 
4439
 
 
4440
KoTextFlow::~KoTextFlow()
 
4441
{
 
4442
}
 
4443
 
 
4444
void KoTextFlow::clear()
 
4445
{
 
4446
    leftItems.clear();
 
4447
    rightItems.clear();
 
4448
}
 
4449
 
 
4450
// Called by KoTextDocument::setWidth
 
4451
void KoTextFlow::setWidth( int width )
 
4452
{
 
4453
    w = width;
 
4454
}
 
4455
 
 
4456
void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
 
4457
{
 
4458
    pageWidth = w;
 
4459
}
 
4460
 
 
4461
#if 0
 
4462
int KoTextFlow::adjustLMargin( int yp, int, int margin, int space, KoTextParag* )
 
4463
{
 
4464
    for ( KoTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
 
4465
        if ( item->y() == -1 )
 
4466
            continue;
 
4467
        if ( yp >= item->y() && yp < item->y() + item->height )
 
4468
            margin = QMAX( margin, item->x() + item->width + space );
 
4469
    }
 
4470
    return margin;
 
4471
}
 
4472
 
 
4473
int KoTextFlow::adjustRMargin( int yp, int, int margin, int space, KoTextParag* )
 
4474
{
 
4475
    for ( KoTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
 
4476
        if ( item->y() == -1 )
 
4477
            continue;
 
4478
        if ( yp >= item->y() && yp < item->y() + item->height )
 
4479
            margin = QMAX( margin, w - item->x() - space );
 
4480
    }
 
4481
    return margin;
 
4482
}
 
4483
#endif
 
4484
 
 
4485
int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
 
4486
{
 
4487
#if 0
 
4488
    if ( pagesize > 0 ) { // check pages
 
4489
        int yinpage = y % pagesize;
 
4490
        if ( yinpage <= 2 )
 
4491
            return 2 - yinpage;
 
4492
        else
 
4493
            if ( yinpage + h > pagesize - 2 )
 
4494
                return ( pagesize - yinpage ) + 2;
 
4495
    }
 
4496
#endif
 
4497
    return 0;
 
4498
}
 
4499
 
 
4500
void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
 
4501
{
 
4502
    leftItems.removeRef( item );
 
4503
    rightItems.removeRef( item );
 
4504
}
 
4505
 
 
4506
void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
 
4507
{
 
4508
    if ( item->placement() == KoTextCustomItem::PlaceRight ) {
 
4509
        if ( !rightItems.contains( item ) )
 
4510
            rightItems.append( item );
 
4511
    } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
 
4512
                !leftItems.contains( item ) ) {
 
4513
        leftItems.append( item );
 
4514
    }
 
4515
}
 
4516
 
 
4517
#if 0
 
4518
QRect KoTextFlow::boundingRect() const
 
4519
{
 
4520
    QRect br;
 
4521
    QPtrListIterator<KoTextCustomItem> l( leftItems );
 
4522
    while( l.current() ) {
 
4523
        br = br.unite( l.current()->geometry() );
 
4524
        ++l;
 
4525
    }
 
4526
    QPtrListIterator<KoTextCustomItem> r( rightItems );
 
4527
    while( r.current() ) {
 
4528
        br = br.unite( r.current()->geometry() );
 
4529
        ++r;
 
4530
    }
 
4531
    return br;
 
4532
}
 
4533
#endif
 
4534
 
 
4535
int KoTextFlow::availableHeight() const
 
4536
{
 
4537
    return -1; // no limit
 
4538
}
 
4539
 
 
4540
void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
 
4541
{
 
4542
    KoTextCustomItem *item;
 
4543
    for ( item = leftItems.first(); item; item = leftItems.next() ) {
 
4544
        if ( item->x() == -1 || item->y() == -1 )
 
4545
            continue;
 
4546
        item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
 
4547
    }
 
4548
 
 
4549
    for ( item = rightItems.first(); item; item = rightItems.next() ) {
 
4550
        if ( item->x() == -1 || item->y() == -1 )
 
4551
            continue;
 
4552
        item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
 
4553
    }
 
4554
}
 
4555
 
 
4556
//void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
 
4557
bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
 
4558