1
/****************************************************************************
2
** Implementation of the internal Qt classes dealing with rich text
6
** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
8
** This file is part of the kernel module of the Qt GUI Toolkit.
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.
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.
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.
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.
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.
31
** Contact info@trolltech.com if any conditions of this licensing are
34
**********************************************************************/
36
#include "korichtext.h"
37
#include "kotextformat.h"
39
#include <qpaintdevicemetrics.h>
40
#include "qdrawutil.h" // for KoTextHorizontalLine
43
#include "koparagcounter.h"
44
#include "kotextdocument.h"
46
#include <kdeversion.h>
47
#if ! KDE_IS_VERSION(3,1,90)
48
#include <kdebugclasses.h>
53
//#define PARSER_DEBUG
54
//#define DEBUG_COLLECTION
55
//#define DEBUG_TABLE_RENDERING
57
//static KoTextFormatCollection *qFormatCollection = 0;
59
#if defined(PARSER_DEBUG)
60
static QString debug_indent;
63
static bool is_printer( QPainter *p )
65
return p && p->device() && p->device()->devType() == QInternal::Printer;
68
static inline int scale( int value, QPainter *painter )
70
if ( is_printer( painter ) ) {
71
QPaintDeviceMetrics metrics( painter->device() );
73
value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
74
#elif defined (Q_WS_WIN)
75
int gdc = GetDeviceCaps( GetDC( 0 ), LOGPIXELSY );
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;
87
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
89
void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
91
if ( current < (int)history.count() - 1 ) {
92
QPtrList<KoTextDocCommand> commands;
93
commands.setAutoDelete( FALSE );
95
for( int i = 0; i <= current; ++i ) {
96
commands.insert( i, history.at( 0 ) );
100
commands.append( cmd );
103
history.setAutoDelete( TRUE );
105
history.append( cmd );
108
if ( (int)history.count() > steps )
109
history.removeFirst();
114
KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
116
if ( current > -1 ) {
117
KoTextCursor *c2 = history.at( current )->unexecute( c );
124
KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
126
if ( current > -1 ) {
127
if ( current < (int)history.count() - 1 ) {
129
return history.at( current )->execute( c );
132
if ( history.count() > 0 ) {
134
return history.at( current )->execute( c );
140
bool KoTextDocCommandHistory::isUndoAvailable()
145
bool KoTextDocCommandHistory::isRedoAvailable()
147
return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
150
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
152
KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const QMemArray<KoTextStringChar> &str )
153
: KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
155
for ( int j = 0; j < (int)text.size(); ++j ) {
156
if ( text[ j ].format() )
157
text[ j ].format()->addRef();
161
/*KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextParag *p, int idx, const QMemArray<KoTextStringChar> &str )
162
: KoTextDocCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
164
for ( int i = 0; i < (int)text.size(); ++i ) {
165
if ( text[ i ].format() )
166
text[ i ].format()->addRef();
170
KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
172
for ( int i = 0; i < (int)text.size(); ++i ) {
173
if ( text[ i ].format() )
174
text[ i ].format()->removeRef();
179
KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
181
KoTextParag *s = doc ? doc->paragAt( id ) : parag;
183
kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
187
cursor.setParag( s );
188
cursor.setIndex( index );
189
int len = text.size();
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 );
201
s->remove( index, len );
207
KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
209
KoTextParag *s = doc ? doc->paragAt( id ) : parag;
211
kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
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 );
223
c->setIndex( index );
224
for ( int i = 0; i < (int)text.size(); ++i )
231
s->setChanged( TRUE );
232
if ( s == c->parag() )
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 )
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();
251
KoTextDocFormatCommand::~KoTextDocFormatCommand()
254
for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
255
if ( oldFormats[ j ].format() )
256
oldFormats[ j ].format()->removeRef();
260
KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
262
KoTextParag *sp = doc->paragAt( startId );
263
KoTextParag *ep = doc->paragAt( endId );
267
KoTextCursor start( doc );
268
start.setParag( sp );
269
start.setIndex( startIndex );
270
KoTextCursor end( doc );
272
end.setIndex( endIndex );
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
284
KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
286
KoTextParag *sp = doc->paragAt( startId );
287
KoTextParag *ep = doc->paragAt( endId );
291
int idx = startIndex;
293
if( !oldFormats.isEmpty()) // ## not in QRT. Not sure how it can happen.
296
if ( oldFormats.at( fIndex ).c == '\n' ) {
298
if ( idx < sp->length() && fIndex > 0 )
299
sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
307
if ( oldFormats.at( fIndex ).format() )
308
sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
311
if ( fIndex >= (int)oldFormats.size() )
313
if ( idx >= sp->length() ) {
321
KoTextCursor end( doc );
323
end.setIndex( endIndex );
324
if ( endIndex == ep->length() )
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 )
335
KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
337
KoTextParag *p = doc->paragAt( firstParag );
341
p->setAlignment( newAlign );
342
if ( p->paragId() == lastParag )
349
KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
351
KoTextParag *p = doc->paragAt( firstParag );
356
if ( i < (int)oldAligns.size() )
357
p->setAlignment( oldAligns.at( i ) );
358
if ( p->paragId() == lastParag )
367
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
369
KoTextCursor::KoTextCursor( KoTextDocument *d )
370
: doc( d ), ox( 0 ), oy( 0 )
374
string = doc ? doc->firstParag() : 0;
378
KoTextCursor::KoTextCursor()
382
KoTextCursor::KoTextCursor( const KoTextCursor &c )
390
tmpIndex = c.tmpIndex;
393
xOffsets = c.xOffsets;
394
yOffsets = c.yOffsets;
397
KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
405
tmpIndex = c.tmpIndex;
408
xOffsets = c.xOffsets;
409
yOffsets = c.yOffsets;
414
bool KoTextCursor::operator==( const KoTextCursor &c ) const
416
return doc == c.doc && string == c.string && idx == c.idx;
419
int KoTextCursor::totalOffsetX() const
423
QValueStack<int>::ConstIterator xit = xOffsets.begin();
425
for ( ; xit != xOffsets.end(); ++xit )
430
int KoTextCursor::totalOffsetY() const
434
QValueStack<int>::ConstIterator yit = yOffsets.begin();
436
for ( ; yit != yOffsets.end(); ++yit )
441
void KoTextCursor::gotoIntoNested( const QPoint &globalPos )
448
string->lineHeightOfChar( idx, &bl, &y );
449
oy = y + string->rect().y();
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 );
457
void KoTextCursor::invalidateNested()
460
QValueStack<KoTextParag*>::Iterator it = parags.begin();
461
QValueStack<int>::Iterator it2 = indices.begin();
462
for ( ; it != parags.end(); ++it, ++it2 ) {
465
(*it)->invalidate( 0 );
466
if ( (*it)->at( *it2 )->isCustom() )
467
(*it)->at( *it2 )->customItem()->invalidate();
472
void KoTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<KoTextStringChar> *formatting )
474
string->invalidate( idx );
476
bool justInsert = TRUE;
478
#if defined(Q_WS_WIN)
480
s = s.replace( QRegExp( "\\r" ), "" );
483
justInsert = s.find( '\n' ) == -1;
485
string->insert( idx, s );
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 );
496
QStringList lst = QStringList::split( '\n', s, TRUE );
497
QStringList::Iterator it = lst.begin();
498
//int y = string->rect().y() + string->rect().height();
500
KoTextFormat *lastFormat = 0;
501
for ( ; it != lst.end(); ) {
502
if ( it != lst.begin() ) {
503
splitAndInsertEmptyParag( FALSE, TRUE );
504
//string->setEndState( -1 );
506
string->prev()->format( -1, FALSE );
508
if ( lastFormat && formatting && string->prev() ) {
509
lastFormat->addRef();
510
string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
517
string->insert( idx, s );
519
string->invalidate( 0 );
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 );
529
if ( it != lst.end() )
530
lastFormat = formatting->at( len + lastIndex ).format();
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;
541
KoTextParag *p = string;
542
p->setParagId( p->prev()->paragId() + 1 );
545
p->setParagId( p->prev()->paragId() + 1 );
552
#if 0 //// useless and slow
553
int h = string->rect().height();
554
string->format( -1, TRUE );
555
if ( h != string->rect().height() )
557
else if ( doc && doc->parent() )
558
doc->nextDoubleBuffered = TRUE;
562
void KoTextCursor::gotoLeft()
564
if ( string->string()->isRightToLeft() )
567
gotoPreviousLetter();
570
void KoTextCursor::gotoPreviousLetter()
576
} else if ( string->prev() ) {
577
string = string->prev();
578
while ( !string->isVisible() )
579
string = string->prev();
580
idx = string->length() - 1;
584
processNesting( Prev );
589
} else if ( string->prev() ) {
590
string = string->prev();
591
idx = string->length() - 1;
597
const KoTextStringChar *tsc = string->at( idx );
598
if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
599
processNesting( EnterEnd );
603
void KoTextCursor::push()
606
parags.push( string );
609
nestedStack.push( nested );
612
void KoTextCursor::pop()
617
string = parags.pop();
620
//if ( doc->parent() )
621
//doc = doc->parent();
622
nested = nestedStack.pop();
625
void KoTextCursor::restoreState()
627
while ( !indices.isEmpty() )
631
bool KoTextCursor::place( const QPoint &p, KoTextParag *s, bool link, int *customItemIndex )
633
if ( customItemIndex )
634
*customItemIndex = -1;
637
if ( pos.y() < s->rect().y() )
638
pos.setY( s->rect().y() );
641
r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
642
if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
650
setParag( s, FALSE );
651
int y = s->rect().y();
652
int lines = s->lines();
653
KoTextStringChar *chr = 0;
658
for ( ; i < lines; ++i ) {
659
chr = s->lineStartOfLine( i, &index );
661
//ch = s->lineHeight( i );
664
if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
669
s->lineStartOfLine( i+1, &nextLine );
671
nextLine = s->length();
673
int x = s->rect().x();
677
int curpos = s->length()-1;
679
bool inCustom = FALSE;
680
while ( i < nextLine ) {
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() )
697
if( chr->rightToLeft )
699
int d = cpos - pos.x();
700
bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
701
if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) {
703
if ( !link || pos.x() >= x + chr->x ) {
709
setIndex( curpos, FALSE );
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 );
717
QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
718
if ( !place( p, document()->firstParag() ) )
724
void KoTextCursor::processNesting( Operation op )
729
ox = string->at( idx )->x;
731
string->lineHeightOfChar( idx, &bl, &y );
732
oy = y + string->rect().y();
738
ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy );
741
ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy, TRUE );
744
ok = string->at( idx )->customItem()->next( this, doc, string, idx, ox, oy );
747
ok = string->at( idx )->customItem()->prev( this, doc, string, idx, ox, oy );
750
ok = string->at( idx )->customItem()->down( this, doc, string, idx, ox, oy );
753
ok = string->at( idx )->customItem()->up( this, doc, string, idx, ox, oy );
760
void KoTextCursor::gotoRight()
762
if ( string->string()->isRightToLeft() )
763
gotoPreviousLetter();
768
void KoTextCursor::gotoNextLetter()
772
const KoTextStringChar *tsc = string->at( idx );
773
if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
774
processNesting( EnterBegin );
778
if ( idx < string->length() - 1 ) {
780
} else if ( string->next() ) {
781
string = string->next();
782
while ( !string->isVisible() )
783
string = string->next();
788
processNesting( Next );
791
if ( idx < string->length() - 1 ) {
793
} else if ( string->next() ) {
794
string = string->next();
802
void KoTextCursor::gotoUp()
804
int indexOfLineStart;
806
KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
810
tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
811
if ( indexOfLineStart == 0 ) {
812
if ( !string->prev() ) {
816
processNesting( Up );
819
if ( !string->prev() )
827
string = string->prev();
828
while ( !string->isVisible() )
829
string = string->prev();
830
int lastLine = string->lines() - 1;
831
if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
833
if ( indexOfLineStart + tmpIndex < string->length() )
834
idx = indexOfLineStart + tmpIndex;
836
idx = string->length() - 1;
839
int oldIndexOfLineStart = indexOfLineStart;
840
if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
842
if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
843
idx = indexOfLineStart + tmpIndex;
845
idx = oldIndexOfLineStart - 1;
849
void KoTextCursor::gotoDown()
851
int indexOfLineStart;
853
KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
857
tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
858
if ( line == string->lines() - 1 ) {
859
if ( !string->next() ) {
863
processNesting( Down );
866
if ( !string->next() )
874
string = string->next();
875
while ( !string->isVisible() )
876
string = string->next();
877
if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
880
if ( string->lines() == 1 )
881
end = string->length();
883
string->lineStartOfLine( 1, &end );
884
if ( indexOfLineStart + tmpIndex < end )
885
idx = indexOfLineStart + tmpIndex;
891
if ( line == string->lines() - 1 )
892
end = string->length();
894
string->lineStartOfLine( line + 1, &end );
895
if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
897
if ( indexOfLineStart + tmpIndex < end )
898
idx = indexOfLineStart + tmpIndex;
904
void KoTextCursor::gotoLineEnd()
907
int indexOfLineStart;
909
KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
913
if ( line == string->lines() - 1 ) {
914
idx = string->length() - 1;
916
c = string->lineStartOfLine( ++line, &indexOfLineStart );
918
idx = indexOfLineStart;
922
void KoTextCursor::gotoLineStart()
925
int indexOfLineStart;
927
KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
931
idx = indexOfLineStart;
934
void KoTextCursor::gotoHome()
938
string = doc->firstParag();
942
void KoTextCursor::gotoEnd()
944
if ( doc && !doc->lastParag()->isValid() )
946
kdDebug(32500) << "Last parag, " << doc->lastParag()->paragId() << ", is invalid - aborting gotoEnd() !" << endl;
952
string = doc->lastParag();
953
idx = string->length() - 1;
956
void KoTextCursor::gotoPageUp( int visibleHeight )
959
KoTextParag *s = string;
960
int h = visibleHeight;
961
int y = s->rect().y();
963
if ( y - s->rect().y() >= h )
969
s = doc->firstParag();
975
void KoTextCursor::gotoPageDown( int visibleHeight )
978
KoTextParag *s = string;
979
int h = visibleHeight;
980
int y = s->rect().y();
982
if ( s->rect().y() - y >= h )
988
s = doc->lastParag();
990
idx = string->length() - 1;
1001
void KoTextCursor::gotoWordRight()
1003
if ( string->string()->isRightToLeft() )
1009
void KoTextCursor::gotoWordLeft()
1011
if ( string->string()->isRightToLeft() )
1017
void KoTextCursor::gotoPreviousWord()
1019
gotoPreviousLetter();
1021
KoTextString *s = string->string();
1022
bool allowSame = FALSE;
1023
if ( idx == ( (int)s->length()-1 ) )
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 == ';' ) {
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 == ';' ) )
1040
void KoTextCursor::gotoNextWord()
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 == ';' ) ) {
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 == ';' ) )
1058
if ( idx < ((int)s->length()-1) ) {
1060
} else if ( string->next() ) {
1061
string = string->next();
1062
while ( !string->isVisible() )
1063
string = string->next();
1070
bool KoTextCursor::atParagStart() const
1075
bool KoTextCursor::atParagEnd() const
1077
return idx == string->length() - 1;
1080
void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
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 );
1097
if ( atParagEnd() ) {
1098
KoTextParag *n = string->next();
1099
KoTextParag *s = doc->createParag( doc, string, n, updateIds );
1101
s->setFormat( 0, 1, f, TRUE );
1102
s->copyParagData( string );
1105
s->indent( &oi, &ni );
1112
} else if ( atParagStart() ) {
1113
KoTextParag *p = string->prev();
1114
KoTextParag *s = doc->createParag( doc, p, string, updateIds );
1116
s->setFormat( 0, 1, f, TRUE );
1117
s->copyParagData( string );
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 );
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();
1140
string->removeCustomItem();
1142
doc->unregisterCustomItem( item, string );
1143
doc->registerCustomItem( item, s );
1146
string->truncate( idx );
1149
s->indent( &oi, &ni );
1161
bool KoTextCursor::remove()
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() )
1170
//else if ( doc && doc->parent() )
1171
// doc->nextDoubleBuffered = TRUE;
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();
1181
string->invalidate( 0 );
1183
string->invalidateCounters();
1185
KoTextParag *s = string;
1187
s->id = s->p ? s->p->id + 1 : 0;
1189
//s->needPreProcess = TRUE;
1195
string->join( string->next() );
1203
void KoTextCursor::killLine()
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() )
1212
//else if ( doc && doc->parent() )
1213
//doc->nextDoubleBuffered = TRUE;
1216
void KoTextCursor::indent()
1219
string->indent( &oi, &ni );
1229
void KoTextCursor::setDocument( KoTextDocument *d )
1232
string = d->firstParag();
1240
int KoTextCursor::x() const
1242
KoTextStringChar *c = string->at( idx );
1244
if ( c->rightToLeft )
1245
curx += c->width; //string->string()->width( idx );
1249
int KoTextCursor::y() const
1252
string->lineStartOfChar( idx, &dummy, &line );
1253
return string->lineY( line );
1256
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1258
// TODO: move to kotextdocument.cpp
1260
void KoTextDocument::init()
1262
#if defined(PARSER_DEBUG)
1263
kdDebug(32500) << debug_indent + "new KoTextDocument (%p)", this << endl;
1265
//oTextValid = TRUE;
1267
// par->insertChild( this );
1273
m_pageBreakEnabled = false;
1275
align = Qt::AlignAuto;
1280
preferRichText = FALSE;
1281
txtFormat = Qt::AutoText;
1282
focusIndicator.parag = 0;
1284
sheet_ = QStyleSheet::defaultSheet();
1285
factory_ = QMimeSourceFactory::defaultFactory();
1286
contxt = QString::null;
1287
fCollection->setStyleSheet( sheet_ );
1293
//nextDoubleBuffered = FALSE;
1296
// withoutDoubleBuffer = par->withoutDoubleBuffer;
1298
withoutDoubleBuffer = FALSE;
1300
lParag = fParag = createParag( this, 0, 0 );
1309
flow_ = new KoTextFlow;
1310
//flow_->setWidth( cw );
1312
leftmargin = 0; // 4 in QRT
1313
rightmargin = 0; // 4 in QRT
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;
1321
KoTextDocument::~KoTextDocument()
1324
// par->removeChild( this );
1326
m_bDestroying = true;
1329
delete commandHistory;
1334
//delete pProcessor;
1342
void KoTextDocument::clear( bool createEmptyParag )
1347
KoTextParag *p = fParag->next();
1351
fParag = lParag = 0;
1352
if ( createEmptyParag )
1353
fParag = lParag = createParag( this );
1359
int KoTextDocument::widthUsed() const
1361
KoTextParag *p = fParag;
1364
int a = p->alignment();
1365
p->setAlignment( Qt::AlignLeft );
1368
w = QMAX( w, p->rect().width() );
1369
p->setAlignment( a );
1377
int KoTextDocument::height() const
1381
h = lParag->rect().top() + lParag->rect().height() + 1;
1382
//int fh = flow_->boundingRect().height();
1383
//return QMAX( h, fh );
1389
KoTextParag *KoTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
1391
return new KoTextParag( d, pr, nx, updateIds );
1395
bool KoTextDocument::setMinimumWidth( int w, KoTextParag *p )
1401
if ( p == minwParag ) {
1403
emit minimumWidthChanged( minw );
1404
} else if ( w > minw ) {
1407
emit minimumWidthChanged( minw );
1409
cw = QMAX( minw, cw );
1414
void KoTextDocument::setPlainText( const QString &text )
1417
//preferRichText = FALSE;
1418
//oTextValid = TRUE;
1422
int nl = text.find( '\n' );
1424
lParag = createParag( this, lParag, 0 );
1428
if ( !s.isEmpty() ) {
1429
if ( s[ (int)s.length() - 1 ] == '\r' )
1430
s.remove( s.length() - 1, 1 );
1431
lParag->append( s );
1435
lParag = createParag( this, lParag, 0 );
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 );
1444
if ( nl == 0xffffff )
1447
nl = text.find( '\n', nl + 1 );
1453
lParag = fParag = createParag( this, 0, 0 );
1456
void KoTextDocument::setText( const QString &text, const QString & /*context*/ )
1458
//focusIndicator.parag = 0;
1461
if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
1462
txtFormat == Qt::RichText )
1463
setRichText( text, context );
1466
setPlainText( text );
1469
QString KoTextDocument::plainText( KoTextParag *p ) const
1474
KoTextParag *p = fParag;
1476
s = p->string()->toString();
1477
s.remove( s.length() - 1, 1 );
1485
return p->string()->toString();
1489
QString KoTextDocument::richText( KoTextParag * ) const
1492
// TODO update from QRT if this code is needed
1496
QString KoTextDocument::text() const
1498
if ( plainText().simplifyWhiteSpace().isEmpty() )
1500
//if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
1501
// return richText();
1502
return plainText( 0 );
1505
QString KoTextDocument::text( int parag ) const
1507
KoTextParag *p = paragAt( parag );
1509
return QString::null;
1511
//if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
1512
// return richText( p );
1514
return plainText( p );
1517
void KoTextDocument::invalidate()
1519
KoTextParag *s = fParag;
1526
void KoTextDocument::informParagraphDeleted( KoTextParag* parag )
1528
QMap<int, KoTextDocumentSelection>::Iterator it = selections.begin();
1529
for ( ; it != selections.end(); ++it )
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 );
1537
(*it).startCursor.setParag( parag->next() ); // sets index to 0
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 );
1545
(*it).endCursor.setParag( parag->next() ); // sets index to 0
1548
emit paragraphDeleted( parag );
1551
void KoTextDocument::selectionStart( int id, int ¶gId, int &index )
1553
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1554
if ( it == selections.end() )
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();
1561
KoTextCursor KoTextDocument::selectionStartCursor( int id)
1563
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1564
if ( it == selections.end() )
1565
return KoTextCursor( this );
1566
KoTextDocumentSelection &sel = *it;
1568
return sel.endCursor;
1569
return sel.startCursor;
1572
KoTextCursor KoTextDocument::selectionEndCursor( int id)
1574
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1575
if ( it == selections.end() )
1576
return KoTextCursor( this );
1577
KoTextDocumentSelection &sel = *it;
1579
return sel.endCursor;
1580
return sel.startCursor;
1583
void KoTextDocument::selectionEnd( int id, int ¶gId, int &index )
1585
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1586
if ( it == selections.end() )
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();
1593
bool KoTextDocument::isSelectionSwapped( int id )
1595
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1596
if ( it == selections.end() )
1598
KoTextDocumentSelection &sel = *it;
1602
KoTextParag *KoTextDocument::selectionStart( int id )
1604
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1605
if ( it == selections.end() )
1607
KoTextDocumentSelection &sel = *it;
1608
if ( sel.startCursor.parag()->paragId() < sel.endCursor.parag()->paragId() )
1609
return sel.startCursor.parag();
1610
return sel.endCursor.parag();
1613
KoTextParag *KoTextDocument::selectionEnd( int id )
1615
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1616
if ( it == selections.end() )
1618
KoTextDocumentSelection &sel = *it;
1619
if ( sel.startCursor.parag()->paragId() > sel.endCursor.parag()->paragId() )
1620
return sel.startCursor.parag();
1621
return sel.endCursor.parag();
1624
void KoTextDocument::addSelection( int id )
1626
nSelections = QMAX( nSelections, id + 1 );
1629
static void setSelectionEndHelper( int id, KoTextDocumentSelection &sel, KoTextCursor &start, KoTextCursor &end )
1631
KoTextCursor c1 = start;
1632
KoTextCursor c2 = end;
1633
if ( sel.swapped ) {
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() );
1644
c1.parag()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
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();
1653
bool KoTextDocument::setSelectionEnd( int id, KoTextCursor *cursor )
1655
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1656
if ( it == selections.end() )
1658
KoTextDocumentSelection &sel = *it;
1660
KoTextCursor start = sel.startCursor;
1661
KoTextCursor end = *cursor;
1663
if ( start == end ) {
1664
removeSelection( id );
1665
setSelectionStart( id, cursor );
1669
if ( sel.endCursor.parag() == end.parag() ) {
1670
setSelectionEndHelper( id, sel, start, end );
1674
bool inSelection = FALSE;
1675
KoTextCursor c( this );
1676
KoTextCursor tmp = sel.startCursor;
1678
tmp = sel.endCursor;
1680
KoTextCursor tmp2 = *cursor;
1681
tmp2.restoreState();
1682
c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
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;
1697
if ( c.parag() == start.parag() )
1698
hadStartParag = TRUE;
1699
if ( c.parag() == end.parag() )
1701
if ( c == sel.startCursor )
1703
if ( c == sel.endCursor )
1706
if ( !sel.swapped &&
1707
( hadEnd && !hadStart ||
1708
hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
1711
if ( c == end && hadStartParag ||
1712
c == start && hadEndParag ) {
1713
KoTextCursor tmp = c;
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() );
1723
( c == end && hadStart || c == start && hadEnd ) )
1724
leftSelection = TRUE;
1725
else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
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() );
1742
c.parag()->setSelection( id, 0, c.parag()->length() - 1 );
1746
if ( leftSelection )
1747
inSelection = FALSE;
1751
if ( old == c || noSelectionAnymore )
1756
sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
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();
1763
setSelectionEndHelper( id, sel, start, end );
1768
void KoTextDocument::selectAll( int id )
1770
removeSelection( id );
1772
KoTextDocumentSelection sel;
1773
sel.swapped = FALSE;
1774
KoTextCursor c( this );
1776
c.setParag( fParag );
1778
sel.startCursor = c;
1780
c.setParag( lParag );
1781
c.setIndex( lParag->length() - 1 );
1784
KoTextParag *p = fParag;
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 );
1800
selections.insert( id, sel );
1803
bool KoTextDocument::removeSelection( int id )
1805
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1806
if ( it == selections.end() )
1809
KoTextDocumentSelection &sel = *it;
1811
KoTextCursor c( this );
1812
KoTextCursor tmp = sel.startCursor;
1814
tmp = sel.endCursor;
1816
c.setParag( tmp.parag() );
1818
bool hadStart = FALSE;
1819
bool hadEnd = FALSE;
1820
KoTextParag *lastParag = 0;
1821
bool leftSelection = FALSE;
1822
bool inSelection = FALSE;
1823
sel.swapped = FALSE;
1825
if ( !hadStart && c.parag() == sel.startCursor.parag() )
1827
if ( !hadEnd && c.parag() == sel.endCursor.parag() )
1830
if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
1834
( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) {
1835
leftSelection = TRUE;
1836
inSelection = FALSE;
1839
bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
1841
if ( lastParag != c.parag() )
1842
c.parag()->removeSelection( id );
1845
lastParag = c.parag();
1847
if ( old == c || noSelectionAnymore )
1851
selections.remove( id );
1855
QString KoTextDocument::selectedText( int id, bool withCustom ) const
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;
1862
KoTextDocumentSelection sel = *it;
1865
KoTextCursor c1 = sel.startCursor;
1866
KoTextCursor c2 = sel.endCursor;
1867
if ( sel.swapped ) {
1868
c2 = sel.startCursor;
1875
if ( c1.parag() == c2.parag() ) {
1877
KoTextParag *p = c1.parag();
1878
int end = c2.index();
1879
if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
1881
if ( !withCustom || !p->customItems() ) {
1882
s += p->string()->toString().mid( c1.index(), end - c1.index() );
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() ) {
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";
1906
KoTextParag *p = c1.parag();
1907
int start = c1.index();
1909
int end = p == c2.parag() ? c2.index() : p->length() - 1;
1910
if ( p == c2.parag() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
1912
if ( !withCustom || !p->customItems() ) {
1913
s += p->string()->toString().mid( start, end - start );
1914
if ( p != c2.parag() )
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() ) {
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";
1936
if ( p == c2.parag() )
1943
void KoTextDocument::setFormat( int id, const KoTextFormat *f, int flags )
1945
QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
1946
if ( it == selections.end() )
1949
KoTextDocumentSelection sel = *it;
1951
KoTextCursor c1 = sel.startCursor;
1952
KoTextCursor c2 = sel.endCursor;
1953
if ( sel.swapped ) {
1954
c2 = sel.startCursor;
1961
if ( c1.parag() == c2.parag() ) {
1962
c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
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 );
1972
c2.parag()->setFormat( 0, c2.index(), f, TRUE, flags );
1975
/*void KoTextDocument::copySelectedText( int id )
1977
#ifndef QT_NO_CLIPBOARD
1978
if ( !hasSelection( id ) )
1981
QApplication::clipboard()->setText( selectedText( id ) );
1985
void KoTextDocument::removeSelectedText( int id, KoTextCursor *cursor )
1987
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
1988
if ( it == selections.end() )
1991
KoTextDocumentSelection sel = *it;
1993
KoTextCursor c1 = sel.startCursor;
1994
KoTextCursor c2 = sel.endCursor;
1995
if ( sel.swapped ) {
1996
c2 = sel.startCursor;
2000
// ### no support for editing tables yet
2001
if ( c1.nestedDepth() || c2.nestedDepth() )
2008
removeSelection( id );
2010
if ( c1.parag() == c2.parag() ) {
2011
c1.parag()->remove( c1.index(), c2.index() - c1.index() );
2015
// ## Qt has a strange setValid/isValid on QTextCursor, only used in the few lines below !?!?
2017
if ( c1.parag() == fParag && c1.index() == 0 &&
2018
c2.parag() == lParag && c2.index() == lParag->length() - 1 )
2021
bool didGoLeft = FALSE;
2022
if ( c1.index() == 0 && c1.parag() != fParag ) {
2023
cursor->gotoPreviousLetter();
2028
c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
2029
KoTextParag *p = c1.parag()->next();
2032
while ( p && p != c2.parag() ) {
2034
dy -= p->rect().height();
2038
c2.parag()->remove( 0, c2.index() );
2042
if ( p->paragLayout().counter )
2043
p->paragLayout().counter->invalidate();
2046
//p->setEndState( -1 );
2050
c1.parag()->join( c2.parag() );
2053
cursor->gotoNextLetter();
2056
void KoTextDocument::indentSelection( int id )
2058
QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
2059
if ( it == selections.end() )
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();
2070
KoTextParag *p = startParag;
2071
while ( p && p != endParag ) {
2077
void KoTextDocument::addCommand( KoTextDocCommand *cmd )
2079
commandHistory->addCommand( cmd );
2082
KoTextCursor *KoTextDocument::undo( KoTextCursor *c )
2084
return commandHistory->undo( c );
2087
KoTextCursor *KoTextDocument::redo( KoTextCursor *c )
2089
return commandHistory->redo( c );
2092
bool KoTextDocument::find( const QString &expr, bool cs, bool wo, bool forward,
2093
int *parag, int *index, KoTextCursor *cursor )
2095
KoTextParag *p = forward ? fParag : lParag;
2097
p = paragAt( *parag );
2099
p = cursor->parag();
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 )
2109
start = cursor->index();
2110
if ( !forward && first ) {
2111
start -= expr.length() + 1;
2121
int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
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() ) )
2135
cursor->setParag( p );
2136
cursor->setIndex( res );
2137
setSelectionStart( Standard, cursor );
2138
cursor->setIndex( res + expr.length() );
2139
setSelectionEnd( Standard, cursor );
2141
*parag = p->paragId();
2154
p = forward ? p->next() : p->prev();
2161
void KoTextDocument::setTextFormat( Qt::TextFormat f )
2166
Qt::TextFormat KoTextDocument::textFormat() const
2172
bool KoTextDocument::inSelection( int selId, const QPoint &pos ) const
2174
QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( selId );
2175
if ( it == selections.end() )
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 ) )
2184
if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
2185
endParag = sel.startCursor.parag();
2186
startParag = sel.endCursor.parag();
2189
KoTextParag *p = startParag;
2191
if ( p->rect().contains( pos ) ) {
2193
int selStart = p->selectionStart( selId );
2194
int selEnd = p->selectionEnd( selId );
2197
for ( int i = 0; i < p->length(); ++i ) {
2198
if ( i == selStart )
2202
if ( p->at( i )->lineStart ) {
2203
y = (*p->lineStarts.find( i ))->y;
2204
h = (*p->lineStarts.find( i ))->h;
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 )*/ )
2213
if ( pos.y() < p->rect().y() )
2215
if ( p == endParag )
2224
void KoTextDocument::doLayout( QPainter *p, int w )
2226
if ( !is_printer( p ) )
2228
withoutDoubleBuffer = ( p != 0 );
2229
flow_->setWidth( w );
2232
fCollection->setPainter( p );
2233
KoTextParag *parag = fParag;
2235
parag->invalidate( 0 );
2236
parag->setPainter( p, TRUE );
2238
parag = parag->next();
2241
fCollection->setPainter( 0 );
2244
parag->setPainter( 0, FALSE );
2245
parag = parag->next();
2250
QPixmap *KoTextDocument::bufferPixmap( const QSize &s )
2252
if ( !buf_pixmap ) {
2253
int w = QABS( s.width() );
2254
int h = QABS( s.height() );
2255
buf_pixmap = new QPixmap( w, h );
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() ) );
2268
void KoTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
2270
if ( !firstParag() )
2273
QBrush bgBrush = paper ? *paper : cg.brush( QColorGroup::Base ); // ## QRT doesn't use cg.brush(Base)
2275
p->setBrushOrigin( -int( p->translationX() ),
2276
-int( p->translationY() ) );
2277
p->fillRect( rect, bgBrush );
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() ) {
2289
it.current()->setColor( cg.text() );
2292
formatCollection()->defaultFormat()->setColor( cg.text() );
2296
KoTextParag *parag = firstParag();
2298
if ( !parag->isValid() )
2300
int y = parag->rect().y();
2301
QRect pr( parag->rect() );
2303
pr.setWidth( QWIDGETSIZE_MAX );
2304
if ( !rect.isNull() && !rect.intersects( pr ) ) {
2305
parag = parag->next();
2308
p->translate( 0, y );
2309
if ( rect.isValid() )
2310
parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
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 );
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 )
2324
QPainter *painter = 0;
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;
2335
if ( useDoubleBuffer ) {
2336
if ( cx >= 0 && cy >= 0 )
2338
ir = ir.intersect( QRect( cx, cy, cw, ch ) );
2340
useDoubleBuffer = FALSE;
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 );
2352
painter->begin( doubleBuffer );
2356
painter->translate( ir.x(), ir.y() );
2359
painter->setBrushOrigin( -ir.x(), -ir.y() );
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 ) );
2366
painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
2367
*parag->backgroundColor() );
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 ) );
2374
painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
2375
*parag->backgroundColor() );
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() );
2390
if ( useDoubleBuffer ) {
2393
p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
2395
painter->translate( -ir.x(), -ir.y() );
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 ) );
2407
//parag->document()->nextDoubleBuffered = FALSE;
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 )
2413
if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
2414
withoutDoubleBuffer = TRUE;
2415
QRect crect( cx, cy, cw, ch );
2416
draw( p, crect, cg );
2419
withoutDoubleBuffer = FALSE;
2421
if ( !firstParag() )
2424
if ( drawCursor && cursor )
2426
if ( cx < 0 && cy < 0 ) {
2433
KoTextParag *lastFormatted = 0;
2434
KoTextParag *parag = firstParag();
2436
QPixmap *doubleBuffer = 0;
2438
QRect crect( cx, cy, cw, ch );
2440
// Space above first parag
2441
if ( isPageBreakEnabled() && parag && cy <= parag->rect().y() && parag->rect().y() > 0 ) {
2443
parag->document()->x() + parag->document()->width(),
2444
parag->rect().y() );
2447
p->fillRect( r, cg.brush( QColorGroup::Base ) );
2451
lastFormatted = parag;
2452
if ( !parag->isValid() )
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() ) );
2463
p->fillRect( r, cg.brush( QColorGroup::Base ) );
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 ) {
2472
if ( buf_pixmap && buf_pixmap->height() > 300 ) {
2476
return lastFormatted;
2478
parag = parag->next();
2482
if ( !parag->hasChanged() && onlyChanged ) {
2483
parag = parag->next();
2487
drawParag( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
2488
parag = parag->next();
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 ) );
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 );
2506
if ( buf_pixmap && buf_pixmap->height() > 300 ) {
2512
return lastFormatted;
2517
void KoTextDocument::setDefaultFont( const QFont &f )
2519
updateFontSizes( f.pointSize() );
2523
void KoTextDocument::registerCustomItem( KoTextCustomItem *i, KoTextParag *p )
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 );
2533
void KoTextDocument::unregisterCustomItem( KoTextCustomItem *i, KoTextParag *p )
2535
flow_->unregisterFloatingItem( i );
2536
p->unregisterFloatingItem( i );
2537
i->setParagraph( 0 );
2538
customItems.removeRef( i );
2541
// unused in kotext, and needs KoTextStringChar::isAnchor
2543
bool KoTextDocument::hasFocusParagraph() const
2545
return !!focusIndicator.parag;
2548
QString KoTextDocument::focusHref() const
2550
return focusIndicator.href;
2553
bool KoTextDocument::focusNextPrevChild( bool next )
2555
if ( !focusIndicator.parag ) {
2557
focusIndicator.parag = fParag;
2558
focusIndicator.start = 0;
2559
focusIndicator.len = 0;
2561
focusIndicator.parag = lParag;
2562
focusIndicator.start = lParag->length();
2563
focusIndicator.len = 0;
2566
focusIndicator.parag->setChanged( TRUE );
2568
focusIndicator.href = QString::null;
2571
KoTextParag *p = focusIndicator.parag;
2572
int index = focusIndicator.start + focusIndicator.len;
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() )
2584
focusIndicator.len++;
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
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();
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();
2632
KoTextParag *p = focusIndicator.parag;
2633
int index = focusIndicator.start - 1;
2634
if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
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();
2645
if ( i < 0 || !p->at( i )->format()->isAnchor() ) {
2646
focusIndicator.start++;
2651
focusIndicator.len++;
2652
focusIndicator.start--;
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
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();
2678
if ( cells.at() == 0 )
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();
2693
if ( cells.at() == 0 )
2702
index = p->length() - 1;
2706
focusIndicator.parag = 0;
2712
int KoTextDocument::length() const
2715
KoTextParag *p = fParag;
2717
l += p->length() - 1; // don't count trailing space
2723
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2725
KoTextString::KoTextString()
2728
bNeedsSpellCheck = true;
2730
rightToLeft = FALSE;
2734
KoTextString::KoTextString( const KoTextString &s )
2736
bidiDirty = s.bidiDirty;
2737
bNeedsSpellCheck = s.bNeedsSpellCheck;
2739
rightToLeft = s.rightToLeft;
2743
for ( int i = 0; i < (int)data.size(); ++i ) {
2744
KoTextFormat *f = data[i].format();
2750
void KoTextString::insert( int index, const QString &s, KoTextFormat *f )
2752
int os = data.size();
2753
data.resize( data.size() + s.length() );
2755
memmove( data.data() + index + s.length(), data.data() + index,
2756
sizeof( KoTextStringChar ) * ( os - index ) );
2758
for ( int i = 0; i < (int)s.length(); ++i ) {
2759
KoTextStringChar &ch = data[ (int)index + i ];
2766
ch.type = KoTextStringChar::Regular;
2770
#ifdef DEBUG_COLLECTION
2771
kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
2776
bNeedsSpellCheck = true;
2779
KoTextString::~KoTextString()
2784
void KoTextString::insert( int index, KoTextStringChar *c )
2786
int os = data.size();
2787
data.resize( data.size() + 1 );
2789
memmove( data.data() + index + 1, data.data() + index,
2790
sizeof( KoTextStringChar ) * ( os - index ) );
2792
KoTextStringChar &ch = data[ (int)index ];
2801
ch.type = KoTextStringChar::Regular;
2802
ch.setFormat( c->format() );
2804
bNeedsSpellCheck = true;
2807
void KoTextString::truncate( int index )
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();
2820
} else if ( ch.format() ) {
2821
ch.format()->removeRef();
2825
data.truncate( index );
2827
bNeedsSpellCheck = true;
2830
void KoTextString::remove( int index, int len )
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();
2840
} else if ( ch.format() ) {
2841
ch.format()->removeRef();
2844
memmove( data.data() + index, data.data() + index + len,
2845
sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
2846
data.resize( data.size() - len, QGArray::SpeedOptim );
2848
bNeedsSpellCheck = true;
2851
void KoTextString::clear()
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();
2861
} else if ( ch.format() ) {
2862
ch.format()->removeRef();
2868
void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection )
2870
KoTextStringChar &ch = data[ index ];
2871
// kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
2872
if ( useCollection && ch.format() )
2874
//kdDebug(32500) << "KoTextString::setFormat removing ref on old format " << ch.format() << endl;
2875
ch.format()->removeRef();
2880
void KoTextString::checkBidi() const
2882
bool rtlKnown = FALSE;
2883
if ( dir == QChar::DirR ) {
2884
((KoTextString *)this)->bidi = TRUE;
2885
((KoTextString *)this)->rightToLeft = TRUE;
2886
((KoTextString *)this)->bidiDirty = FALSE;
2888
} else if ( dir == QChar::DirL ) {
2889
((KoTextString *)this)->rightToLeft = FALSE;
2892
((KoTextString *)this)->rightToLeft = FALSE;
2895
int len = data.size();
2896
const KoTextStringChar *c = data.data();
2897
((KoTextString *)this)->bidi = FALSE;
2900
switch( c->c.direction() )
2905
((KoTextString *)this)->rightToLeft = FALSE;
2912
((KoTextString *)this)->rightToLeft = TRUE;
2919
uchar row = c->c.row();
2920
if( (row > 0x04 && row < 0x09) || ( row > 0xfa && row < 0xff ) ) {
2921
((KoTextString *)this)->bidi = TRUE;
2928
((KoTextString *)this)->bidiDirty = FALSE;
2931
QMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
2933
if ( len == 0xFFFFFF )
2935
QMemArray<KoTextStringChar> a;
2937
for ( int i = 0; i < len; ++i ) {
2938
KoTextStringChar *c = &data[ i + start ];
2941
a[ i ].pixelxadj = 0;
2942
a[ i ].pixelwidth = 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() );
2950
c->format()->addRef();
2955
QString KoTextString::mid( int start, int len ) const
2957
if ( len == 0xFFFFFF )
2960
res.setLength( len );
2961
for ( int i = 0; i < len; ++i ) {
2962
KoTextStringChar *c = &data[ i + start ];
2968
QString KoTextString::toString( const QMemArray<KoTextStringChar> &data )
2971
int l = data.size();
2972
s.setUnicode( 0, l );
2973
KoTextStringChar *c = data.data();
2974
QChar *uc = (QChar *)s.unicode();
2984
QString KoTextString::toReverseString() const
2989
KoTextStringChar *c = data.data() + (l-1);
2990
QChar *uc = (QChar *)s.unicode();
3001
void KoTextStringChar::setFormat( KoTextFormat *f )
3003
if ( type == Regular ) {
3007
d.custom = new CustomData;
3008
d.custom->custom = 0;
3010
d.custom->format = f;
3011
if ( d.custom->custom )
3012
d.custom->custom->setFormat( f );
3016
void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
3018
if ( type == Regular ) {
3019
KoTextFormat *f = format();
3020
d.custom = new CustomData;
3021
d.custom->format = f;
3024
delete d.custom->custom;
3026
d.custom->custom = i;
3029
void KoTextStringChar::loseCustomItem() // setRegular() might be a better name
3032
KoTextFormat *f = d.custom->format;
3033
d.custom->custom = 0;
3040
KoTextStringChar::~KoTextStringChar()
3043
format()->removeRef();
3046
delete d.custom; break;
3052
KoTextStringChar *KoTextStringChar::clone() const
3054
KoTextStringChar *chr = new KoTextStringChar;
3058
chr->pixelwidth = 0;
3061
chr->rightToLeft = 0;
3063
chr->type = KoTextStringChar::Regular;
3064
chr->setFormat( format() );
3065
if ( chr->format() )
3066
chr->format()->addRef();
3070
int KoTextStringChar::height() const
3072
return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
3075
int KoTextStringChar::ascent() const
3077
return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
3080
int KoTextStringChar::descent() const
3082
return !isCustom() ? format()->descent() : 0;
3085
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3087
KoTextParag::KoTextParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
3088
: invalid( 0 ), p( pr ), n( nx ), doc( d ),
3091
newLinesAllowed( TRUE ), // default in kotext
3092
visible( TRUE ), breakable( TRUE ), movedDown( FALSE ),
3094
m_lineChanged( -1 ),
3097
mFloatingItems( 0 ),
3100
defFormat = formatCollection()->defaultFormat();
3102
tabStopWidth = defFormat->width( 'x' ) * 8;
3103
commandHistory = new KoTextDocCommandHistory( 100 );
3105
#if defined(PARSER_DEBUG)
3106
kdDebug(32500) << debug_indent + "new KoTextParag" << endl;
3111
#ifdef QTEXTTABLE_AVAILABLE
3118
#ifdef QTEXTTABLE_AVAILABLE
3124
#ifdef QTEXTTABLE_AVAILABLE
3125
if ( !tc && d && d->tableCell() )
3126
tc = d->tableCell();
3130
doc->setFirstParag( this );
3132
doc->setLastParag( this );
3134
//firstFormat = TRUE; //// unused
3135
//firstPProcess = TRUE;
3137
//needPreProcess = FALSE;
3143
if ( n && updateIds ) {
3146
s->id = s->p->id + 1;
3147
//s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1;
3152
str = new KoTextString();
3153
str->insert( 0, " ", formatCollection()->defaultFormat() );
3156
KoTextParag::~KoTextParag()
3158
//kdDebug(32500) << "KoTextParag::~KoTextParag " << this << " id=" << paragId() << endl;
3160
// if ( doc && p == doc->minwParag ) {
3161
// doc->minwParag = 0;
3165
//delete pFormatter;
3166
//delete commandHistory;
3170
QMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
3171
for ( ; it != lineStarts.end(); ++it )
3173
if ( mSelections ) delete mSelections;
3174
if ( mFloatingItems ) delete mFloatingItems;
3182
if ( doc && !doc->isDestroying() )
3184
doc->informParagraphDeleted( this );
3186
//kdDebug(32500) << "KoTextParag::~KoTextParag " << this << endl;
3190
void KoTextParag::setNext( KoTextParag *s )
3194
doc->setLastParag( this );
3197
void KoTextParag::setPrev( KoTextParag *s )
3201
doc->setFirstParag( this );
3204
void KoTextParag::invalidate( int chr )
3209
invalid = QMIN( invalid, chr );
3210
#if 0 /// strange code!
3211
if ( mFloatingItems ) {
3212
for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
3216
//lm = rm = bm = tm = flm = -1;
3219
void KoTextParag::setChanged( bool b, bool /*recursive*/ )
3222
m_lineChanged = -1; // all
3223
//if ( recursive ) {
3224
// if ( doc && doc->parentParag() )
3225
// doc->parentParag()->setChanged( b, recursive );
3229
void KoTextParag::setLineChanged( short int line )
3231
if ( m_lineChanged == -1 ) {
3232
if ( !changed ) // only if the whole parag wasn't "changed" already
3233
m_lineChanged = line;
3236
m_lineChanged = QMIN( m_lineChanged, line ); // also works if line=-1
3238
//kdDebug(32500) << "KoTextParag::setLineChanged line=" << line << " -> m_lineChanged=" << m_lineChanged << endl;
3241
void KoTextParag::insert( int index, const QString &s )
3244
if ( doc && !doc->useFormatCollection() && doc->preProcessor() )
3245
str->insert( index, s,
3246
doc->preProcessor()->format( KoTextPreProcessor::Standard ) );
3249
str->insert( index, s, formatCollection()->defaultFormat() );
3250
invalidate( index );
3251
//needPreProcess = TRUE;
3254
void KoTextParag::truncate( int index )
3256
str->truncate( index );
3257
insert( length(), " " );
3258
//needPreProcess = TRUE;
3261
void KoTextParag::remove( int index, int len )
3263
if ( index + len - str->length() > 0 )
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();
3272
str->remove( index, len );
3274
//needPreProcess = TRUE;
3277
void KoTextParag::join( KoTextParag *s )
3279
//kdDebug(32500) << "KoTextParag::join this=" << paragId() << " (length " << length() << ") with " << s->paragId() << " (length " << s->length() << ")" << endl;
3280
int oh = r.height() + s->r.height();
3285
doc->setLastParag( this );
3287
int start = str->length();
3288
if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
3289
remove( length() - 1, 1 );
3292
append( s->str->toString(), TRUE );
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 );
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 );
3307
Q_ASSERT(str->at(str->length()-1).c == ' ');
3309
/*if ( !extraData() && s->extraData() ) {
3310
setExtraData( s->extraData() );
3311
s->setExtraData( 0 );
3312
} else if ( extraData() && s->extraData() ) {
3313
extraData()->join( s->extraData() );
3318
invalidateCounters();
3321
//needPreProcess = TRUE;
3325
s->id = s->p->id + 1;
3327
//s->needPreProcess = TRUE;
3336
void KoTextParag::move( int &dy )
3338
//kdDebug(32500) << "KoTextParag::move paragId=" << paragId() << " dy=" << dy << endl;
3343
if ( mFloatingItems ) {
3344
for ( KoTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
3349
// p->lastInFrame = TRUE; // Qt does this, but the loop at the end of format() calls move a lot!
3353
// do page breaks if required
3354
if ( doc && doc->isPageBreakEnabled() ) {
3356
if ( ( shift = doc->formatter()->formatVertically( doc, this ) ) ) {
3358
p->setChanged( TRUE );
3364
void KoTextParag::format( int start, bool doMove )
3366
if ( !str || str->length() == 0 || !formatter() )
3371
doc->preProcessor() &&
3372
( needPreProcess || state == -1 ) )
3373
doc->preProcessor()->process( doc, this, invalid <= 0 ? 0 : invalid );
3374
needPreProcess = FALSE;
3377
if ( invalid == -1 )
3380
//kdDebug(32500) << "KoTextParag::format " << this << " id:" << paragId() << endl;
3382
r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
3384
// p->lastInFrame = FALSE;
3387
bool formattedAgain = FALSE;
3390
r.setWidth( documentWidth() );
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() );
3398
i->move( i->x(), r.y() );
3401
QMap<int, KoTextParagLineStart*> oldLineStarts = lineStarts;
3404
bool formatterWorked = formatter()->format( doc, this, start, oldLineStarts, y, m_wused );
3406
// It can't happen that width < minimumWidth -- hopefully.
3407
//r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
3408
//m_minw = formatter()->minimumWidth();
3410
QMap<int, KoTextParagLineStart*>::Iterator it = oldLineStarts.begin();
3412
for ( ; it != oldLineStarts.end(); ++it )
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...
3419
setWidth( textDocument()->width() - 1 );
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 );
3429
r.setWidth( lineStarts[0]->w );
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 );
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
3442
r.setWidth( usedw );
3444
r.setWidth( QMIN( usedw, r.width() ) );
3449
if ( y != r.height() )
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;
3466
doc->formatter()->postFormat( this );
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();
3472
bool makeInvalid = false; //p && p->lastInFrame;
3473
//kdDebug(32500) << "might move of dy=" << dy << ". previous's lastInFrame (=makeInvalid): " << makeInvalid << endl;
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...)
3479
if ( !s->isFullWidth() )
3484
//if ( s->lastInFrame )
3485
// makeInvalid = TRUE;
3490
//#define DEBUG_CI_PLACEMENT
3491
if ( mFloatingItems ) {
3492
#ifdef DEBUG_CI_PLACEMENT
3493
kdDebug(32500) << lineStarts.count() << " lines" << endl;
3495
// Place custom items - after the formatting is finished
3498
int lineY = 0; // the one called "cy" in other algos
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 ) {
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;
3513
if ( chr->isCustom() ) {
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;
3527
//firstFormat = FALSE; //// unused
3528
if ( formatterWorked > 0 ) // only if it worked, i.e. we had some width to format it
3533
//#### string()->setTextChanged( FALSE );
3536
int KoTextParag::lineHeightOfChar( int i, int *bl, int *y ) const
3539
( (KoTextParag*)this )->format();
3541
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
3544
if ( i >= it.key() ) {
3546
*bl = ( *it )->baseLine;
3551
if ( it == lineStarts.begin() )
3556
kdWarning(32500) << "KoTextParag::lineHeightOfChar: couldn't find lh for " << i << endl;
3560
KoTextStringChar *KoTextParag::lineStartOfChar( int i, int *index, int *line ) const
3563
( (KoTextParag*)this )->format();
3565
int l = (int)lineStarts.count() - 1;
3566
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.end();
3569
if ( i >= it.key() ) {
3574
return &str->at( it.key() );
3576
if ( it == lineStarts.begin() )
3582
kdWarning(32500) << "KoTextParag::lineStartOfChar: couldn't find " << i << endl;
3586
int KoTextParag::lines() const
3589
( (KoTextParag*)this )->format();
3591
return (int)lineStarts.count();
3594
KoTextStringChar *KoTextParag::lineStartOfLine( int line, int *index ) const
3597
( (KoTextParag*)this )->format();
3599
if ( line >= 0 && line < (int)lineStarts.count() ) {
3600
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
3601
while ( line-- > 0 )
3606
return &str->at( i );
3609
kdWarning(32500) << "KoTextParag::lineStartOfLine: couldn't find " << line << endl;
3613
int KoTextParag::leftGap() const
3616
( (KoTextParag*)this )->format();
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);
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);
3636
void KoTextParag::setFormat( int index, int len, const KoTextFormat *_f, bool useCollection, int flags )
3638
Q_ASSERT( useCollection ); // just for info
3641
if ( index > str->length() - 1 )
3642
index = str->length() - 1;
3643
if ( index + len >= str->length() )
3644
len = str->length() - index;
3646
KoTextFormatCollection *fc = 0;
3647
if ( useCollection )
3648
fc = formatCollection();
3650
for ( int i = 0; i < len; ++i ) {
3651
of = str->at( i + index ).format();
3652
if ( !changed && _f->key() != of->key() )
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()
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;
3678
KoTextFormat* f = fc ? fc->format( _f ) : const_cast<KoTextFormat *>( _f );
3679
str->setFormat( i + index, f, useCollection );
3681
#ifdef DEBUG_COLLECTION
3682
kdDebug(32500) << " KoTextParag::setFormat, will use format(of,f,flags) of=" << of << " " << of->key() << ", f=" << _f << " " << _f->key() << endl;
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;
3688
str->setFormat( i + index, fm, useCollection );
3693
void KoTextParag::indent( int *oldIndent, int *newIndent )
3695
if ( !doc || !doc->indent() /*|| isListItem() TODO*/ ) {
3700
if ( oldIndent && newIndent )
3701
*newIndent = *oldIndent;
3704
doc->indent()->indent( doc, this, oldIndent, newIndent );
3707
void KoTextParag::drawCursorDefault( QPainter &painter, KoTextCursor *cursor, int curx, int cury, int curh, const QColorGroup &cg )
3709
painter.fillRect( QRect( curx, cury, 1, curh ), cg.color( QColorGroup::Text ) );
3711
if ( string()->isBidi() ) {
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 );
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 );
3726
int *KoTextParag::tabArray() const
3730
ta = doc->tabArray();
3734
int KoTextParag::nextTabDefault( int, int x )
3739
ta = doc->tabArray();
3740
int tabStopWidth = doc->tabStopWidth();
3742
if ( tabStopWidth != 0 )
3743
return tabStopWidth*(x/tabStopWidth+1);
3748
/*void KoTextParag::setPainter( QPainter *p, bool adjust )
3751
for ( int i = 0; i < length(); ++i ) {
3752
if ( at( i )->isCustom() )
3753
at( i )->customItem()->setPainter( p, adjust );
3757
KoTextFormatCollection *KoTextParag::formatCollection() const
3760
return doc->formatCollection();
3761
//if ( !qFormatCollection )
3762
// qFormatCollection = new KoTextFormatCollection;
3763
//return qFormatCollection;
3767
QString KoTextParag::richText() const
3771
KoTextStringChar *formatChar = 0;
3773
for ( int i = 0; i < length()-1; ++i ) {
3774
KoTextStringChar *c = &str->at( i );
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>";
3783
s += "<a name=\"" + c->anchorName() + "\"></a>";
3788
if ( !formatChar ) {
3789
s += c->format()->makeFormatChangeTags( 0, QString::null, QString::null /*c->anchorHref()*/ );
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()*/ );
3799
if ( c->c == ' ' || c->c == '\t' ) {
3802
} else if ( !spaces.isEmpty() ) {
3803
if ( spaces.length() > 1 || spaces[0] == '\t' )
3804
s += "<wsp>" + spaces + "</wsp>";
3807
spaces = QString::null;
3810
if ( c->c == '<' ) {
3812
} else if ( c->c == '>' ) {
3814
} else if ( c->isCustom() ) {
3815
s += c->customItem()->richText();
3820
if ( !spaces.isEmpty() ) {
3821
if ( spaces.length() > 1 || spaces[0] == '\t' )
3822
s += "<wsp>" + spaces + "</wsp>";
3828
s += formatChar->format()->makeFormatEndTags( QString::null /*formatChar->anchorHref()*/ );
3833
/*void KoTextParag::addCommand( KoTextDocCommand *cmd )
3836
commandHistory->addCommand( cmd );
3838
doc->commands()->addCommand( cmd );
3841
KoTextCursor *KoTextParag::undo( KoTextCursor *c )
3844
return commandHistory->undo( c );
3845
return doc->commands()->undo( c );
3848
KoTextCursor *KoTextParag::redo( KoTextCursor *c )
3851
return commandHistory->redo( c );
3852
return doc->commands()->redo( c );
3855
void KoTextParag::show()
3857
if ( visible || !doc )
3862
void KoTextParag::hide()
3864
if ( !visible || !doc )
3869
void KoTextParag::setDirection( QChar::Direction d )
3871
if ( str && str->direction() != d ) {
3872
str->setDirection( d );
3875
m_layout.direction = d;
3880
QChar::Direction KoTextParag::direction() const
3882
return (str ? str->direction() : QChar::DirON );
3885
void KoTextParag::setSelection( int id, int start, int end )
3887
QMap<int, KoTextParagSelection>::ConstIterator it = selections().find( id );
3888
if ( it != mSelections->end() ) {
3889
if ( start == ( *it ).start && end == ( *it ).end )
3893
KoTextParagSelection sel;
3896
(*mSelections)[ id ] = sel;
3897
setChanged( TRUE, TRUE );
3900
void KoTextParag::removeSelection( int id )
3902
if ( !hasSelection( id ) )
3905
mSelections->remove( id );
3906
setChanged( TRUE, TRUE );
3909
int KoTextParag::selectionStart( int id ) const
3913
QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
3914
if ( it == mSelections->end() )
3916
return ( *it ).start;
3919
int KoTextParag::selectionEnd( int id ) const
3923
QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
3924
if ( it == mSelections->end() )
3929
bool KoTextParag::hasSelection( int id ) const
3933
QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
3934
if ( it == mSelections->end() )
3936
return ( *it ).start != ( *it ).end || length() == 1;
3939
bool KoTextParag::fullSelected( int id ) const
3943
QMap<int, KoTextParagSelection>::ConstIterator it = mSelections->find( id );
3944
if ( it == mSelections->end() )
3946
return ( *it ).start == 0 && ( *it ).end == str->length() - 1;
3949
int KoTextParag::lineY( int l ) const
3951
if ( l > (int)lineStarts.count() - 1 ) {
3952
kdWarning(32500) << "KoTextParag::lineY: line " << l << " out of range!" << endl;
3957
( (KoTextParag*)this )->format();
3959
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
3965
int KoTextParag::lineBaseLine( int l ) const
3967
if ( l > (int)lineStarts.count() - 1 ) {
3968
kdWarning(32500) << "KoTextParag::lineBaseLine: line " << l << " out of range!" << endl;
3973
( (KoTextParag*)this )->format();
3975
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
3978
return ( *it )->baseLine;
3981
int KoTextParag::lineHeight( int l ) const
3983
if ( l > (int)lineStarts.count() - 1 ) {
3984
kdWarning(32500) << "KoTextParag::lineHeight: line " << l << " out of range!" << endl;
3989
( (KoTextParag*)this )->format();
3991
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
3997
void KoTextParag::lineInfo( int l, int &y, int &h, int &bl ) const
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;
4009
( (KoTextParag*)this )->format();
4011
QMap<int, KoTextParagLineStart*>::ConstIterator it = lineStarts.begin();
4016
bl = ( *it )->baseLine;
4019
uint KoTextParag::alignment() const
4024
void KoTextParag::setFormat( KoTextFormat *fm )
4027
bool doUpdate = FALSE;
4028
if (defFormat && (defFormat != formatCollection()->defaultFormat()))
4031
defFormat = formatCollection()->format( fm );
4035
for ( int i = 0; i < length(); ++i ) {
4036
if ( at( i )->format()->styleName() == defFormat->styleName() )
4037
at( i )->format()->updateStyle();
4042
KoTextFormatterBase *KoTextParag::formatter() const
4045
return doc->formatter();
4047
// return pFormatter;
4048
//return ( ( (KoTextParag*)this )->pFormatter = new KoTextFormatterBaseBreakWords );
4052
/*void KoTextParag::setFormatter( KoTextFormatterBase *f )
4055
if ( pFormatter ) delete pFormatter;
4059
/*int KoTextParag::minimumWidth() const
4061
//return doc ? doc->minimumWidth() : 0;
4065
int KoTextParag::widthUsed() const
4070
void KoTextParag::setTabArray( int *a )
4076
void KoTextParag::setTabStops( int tw )
4079
doc->setTabStops( tw );
4081
// tabStopWidth = tw;
4084
QMap<int, KoTextParagSelection> &KoTextParag::selections() const
4087
((KoTextParag *)this)->mSelections = new QMap<int, KoTextParagSelection>;
4088
return *mSelections;
4091
QPtrList<KoTextCustomItem> &KoTextParag::floatingItems() const
4093
if ( !mFloatingItems )
4094
((KoTextParag *)this)->mFloatingItems = new QPtrList<KoTextCustomItem>;
4095
return *mFloatingItems;
4098
void KoTextCursor::setIndex( int i, bool restore )
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;
4112
i = i < 0 ? 0 : string->length() - 1;
4119
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4121
KoTextFormatterBase::KoTextFormatterBase()
4122
: wrapColumn( -1 ), wrapEnabled( TRUE ),
4123
m_bViewFormattingChars( false ),
4124
biw( true /*default in kotext*/ )
4128
// See KoTextFormatter
4130
KoTextParagLineStart *KoTextFormatterBase::formatLine( KoTextParag *parag, KoTextString *string, KoTextParagLineStart *line,
4131
KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
4133
#ifndef QT_NO_COMPLEXTEXT
4134
if( string->isBidi() )
4135
return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
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 )
4144
for ( int j = start; j <= last; ++j )
4145
string->at( j ).x += space;
4146
} else if ( align & AlignJustify ) {
4148
for ( int j = start; j < last; ++j ) {
4149
if( isBreakable( string, j ) ) {
4154
for ( int k = start + 1; k <= last; ++k ) {
4155
if( isBreakable( string, k ) && numSpaces ) {
4156
int s = space / numSpaces;
4161
string->at( k ).x += toAdd;
4165
if ( last >= 0 && last < string->length() )
4166
line->w = string->at( last ).x + string->width( last );
4170
return new KoTextParagLineStart();
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 )
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;
4186
KoBidiControl *control = new KoBidiControl( line->context(), line->status );
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 ) {
4197
int x = startChar->x;
4199
QPtrList<KoTextRun> *runs;
4200
runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
4201
(text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
4203
// now construct the reordered string out of the runs...
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;
4213
if ( align & Qt::AlignHCenter )
4215
else if ( align & Qt::AlignRight )
4217
else if ( align & Qt::AlignJustify ) {
4218
for ( int j = start; j < last; ++j ) {
4219
if( isBreakable( text, j ) ) {
4226
KoTextRun *r = runs->first();
4227
int xmax = -0xffffff;
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;
4239
} else if ( first ) {
4242
x -= c->format()->width( ' ' );
4245
c->rightToLeft = TRUE;
4246
c->startOfRun = FALSE;
4248
if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
4251
ww = c->format()->width( ' ' );
4253
if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
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;
4266
} else if ( first ) {
4269
x -= c->format()->width( ' ' );
4272
c->rightToLeft = FALSE;
4273
c->startOfRun = FALSE;
4275
if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
4278
ww = c->format()->width( ' ' );
4280
//kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
4281
if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
4286
text->at( r->start + start ).startOfRun = TRUE;
4290
line->w = xmax + 10;
4291
KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
4297
bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
4299
if ( string->at( pos ).c == QChar(160) ) //non-breaking space
4301
return isBreakable( string, pos );
4304
bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
4306
const QChar &c = string->at( pos ).c;
4307
char ch = c.latin1();
4308
if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U )
4310
if ( c == '-' || c.unicode() == 0xad ) // hyphen or soft hyphen
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
4320
if( string != cachedString ) {
4321
// build up string of thai chars
4322
QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
4324
thaiCache = new QCString;
4326
thaiIt = ThBreakIterator::createWordInstance();
4327
*thaiCache = thaiCodec->fromUnicode( s->string() );
4329
thaiIt->setText(thaiCache->data());
4330
for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) {
4338
// if we don't have a thai line breaking lib, allow
4339
// breaks everywhere except directly before punctuation.
4345
if ( row < 0x11 ) // no asian font
4347
if ( row > 0x2d && row < 0xfb || row == 0x11 )
4348
// asian line breaking. Everywhere allowed except directly
4349
// in front of a punctuation character.
4355
void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
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...)
4362
QMap<int, KoTextParagLineStart*>::Iterator it;
4363
if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
4364
lineStarts.insert( index, ls );
4366
kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
4368
lineStarts.remove( it );
4369
lineStarts.insert( index, ls );
4371
#else // non-debug code, take the fast route
4372
lineStarts.insert( index, ls );
4377
/* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
4378
the shift of the paragraphs bottom line.
4380
int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
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();
4389
KoTextStringChar *c = ¶g->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;
4396
parag->setMovedDown( TRUE );
4398
int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
4401
parag->setMovedDown( TRUE );
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() )
4411
parag->setHeight( h );
4412
return h - oldHeight;
4415
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4417
KoTextIndent::KoTextIndent()
4421
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4423
KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
4424
: width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
4426
m_deleted = false; // added for kotext
4429
KoTextCustomItem::~KoTextCustomItem()
4433
KoTextFlow::KoTextFlow()
4436
leftItems.setAutoDelete( FALSE );
4437
rightItems.setAutoDelete( FALSE );
4440
KoTextFlow::~KoTextFlow()
4444
void KoTextFlow::clear()
4450
// Called by KoTextDocument::setWidth
4451
void KoTextFlow::setWidth( int width )
4456
void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
4462
int KoTextFlow::adjustLMargin( int yp, int, int margin, int space, KoTextParag* )
4464
for ( KoTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
4465
if ( item->y() == -1 )
4467
if ( yp >= item->y() && yp < item->y() + item->height )
4468
margin = QMAX( margin, item->x() + item->width + space );
4473
int KoTextFlow::adjustRMargin( int yp, int, int margin, int space, KoTextParag* )
4475
for ( KoTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
4476
if ( item->y() == -1 )
4478
if ( yp >= item->y() && yp < item->y() + item->height )
4479
margin = QMAX( margin, w - item->x() - space );
4485
int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
4488
if ( pagesize > 0 ) { // check pages
4489
int yinpage = y % pagesize;
4493
if ( yinpage + h > pagesize - 2 )
4494
return ( pagesize - yinpage ) + 2;
4500
void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
4502
leftItems.removeRef( item );
4503
rightItems.removeRef( item );
4506
void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
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 );
4518
QRect KoTextFlow::boundingRect() const
4521
QPtrListIterator<KoTextCustomItem> l( leftItems );
4522
while( l.current() ) {
4523
br = br.unite( l.current()->geometry() );
4526
QPtrListIterator<KoTextCustomItem> r( rightItems );
4527
while( r.current() ) {
4528
br = br.unite( r.current()->geometry() );
4535
int KoTextFlow::availableHeight() const
4537
return -1; // no limit
4540
void KoTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
4542
KoTextCustomItem *item;
4543
for ( item = leftItems.first(); item; item = leftItems.next() ) {
4544
if ( item->x() == -1 || item->y() == -1 )
4546
item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
4549
for ( item = rightItems.first(); item; item = rightItems.next() ) {
4550
if ( item->x() == -1 || item->y() == -1 )
4552
item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
4556
//void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
4557
bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }