source: vendor/trolltech/current/src/kernel/qrichtext.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 227.5 KB
Line 
1/****************************************************************************
2** $Id: qrichtext.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of the internal Qt classes dealing with rich text
5**
6** Created : 990101
7**
8** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
9**
10** This file is part of the kernel module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qrichtext_p.h"
39
40#ifndef QT_NO_RICHTEXT
41
42
43#include "qstringlist.h"
44#include "qfont.h"
45#include "qtextstream.h"
46#include "qfile.h"
47#include "qapplication.h"
48#include "qmap.h"
49#include "qfileinfo.h"
50#include "qstylesheet.h"
51#include "qmime.h"
52#include "qimage.h"
53#include "qdragobject.h"
54#include "qpaintdevicemetrics.h"
55#include "qpainter.h"
56#include "qdrawutil.h"
57#include "qcursor.h"
58#include "qptrstack.h"
59#include "qptrdict.h"
60#include "qstyle.h"
61#include "qcleanuphandler.h"
62#include "qtextengine_p.h"
63#include <private/qunicodetables_p.h>
64
65#include <stdlib.h>
66
67static QTextCursor* richTextExportStart = 0;
68static QTextCursor* richTextExportEnd = 0;
69
70class QTextFormatCollection;
71
72const int border_tolerance = 2;
73
74#ifdef Q_WS_WIN
75#include "qt_windows.h"
76#endif
77
78#define QChar_linesep QChar(0x2028U)
79
80static inline bool is_printer( QPainter *p )
81{
82 if ( !p || !p->device() )
83 return FALSE;
84 return p->device()->devType() == QInternal::Printer;
85}
86
87static inline int scale( int value, QPainter *painter )
88{
89 if ( is_printer( painter ) ) {
90 QPaintDeviceMetrics metrics( painter->device() );
91#if defined(Q_WS_X11)
92 value = value * metrics.logicalDpiY() /
93 QPaintDevice::x11AppDpiY( painter->device()->x11Screen() );
94#elif defined (Q_WS_WIN)
95 HDC hdc = GetDC( 0 );
96 int gdc = GetDeviceCaps( hdc, LOGPIXELSY );
97 if ( gdc )
98 value = value * metrics.logicalDpiY() / gdc;
99 ReleaseDC( 0, hdc );
100#elif defined (Q_WS_MAC)
101 value = value * metrics.logicalDpiY() / 75; // ##### FIXME
102#elif defined (Q_WS_QWS)
103 value = value * metrics.logicalDpiY() / 75;
104#endif
105 }
106 return value;
107}
108
109
110inline bool isBreakable( QTextString *string, int pos )
111{
112 if (string->at(pos).nobreak)
113 return FALSE;
114 return (pos < string->length()-1 && string->at(pos+1).softBreak);
115}
116
117// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
118
119void QTextCommandHistory::addCommand( QTextCommand *cmd )
120{
121 if ( current < (int)history.count() - 1 ) {
122 QPtrList<QTextCommand> commands;
123 commands.setAutoDelete( FALSE );
124
125 for( int i = 0; i <= current; ++i ) {
126 commands.insert( i, history.at( 0 ) );
127 history.take( 0 );
128 }
129
130 commands.append( cmd );
131 history.clear();
132 history = commands;
133 history.setAutoDelete( TRUE );
134 } else {
135 history.append( cmd );
136 }
137
138 if ( (int)history.count() > steps )
139 history.removeFirst();
140 else
141 ++current;
142}
143
144QTextCursor *QTextCommandHistory::undo( QTextCursor *c )
145{
146 if ( current > -1 ) {
147 QTextCursor *c2 = history.at( current )->unexecute( c );
148 --current;
149 return c2;
150 }
151 return 0;
152}
153
154QTextCursor *QTextCommandHistory::redo( QTextCursor *c )
155{
156 if ( current > -1 ) {
157 if ( current < (int)history.count() - 1 ) {
158 ++current;
159 return history.at( current )->execute( c );
160 }
161 } else {
162 if ( history.count() > 0 ) {
163 ++current;
164 return history.at( current )->execute( c );
165 }
166 }
167 return 0;
168}
169
170bool QTextCommandHistory::isUndoAvailable()
171{
172 return current > -1;
173}
174
175bool QTextCommandHistory::isRedoAvailable()
176{
177 return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
178}
179
180// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
181
182QTextDeleteCommand::QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str,
183 const QByteArray& oldStyleInfo )
184 : QTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), styleInformation( oldStyleInfo )
185{
186 for ( int j = 0; j < (int)text.size(); ++j ) {
187 if ( text[ j ].format() )
188 text[ j ].format()->addRef();
189 }
190}
191
192QTextDeleteCommand::QTextDeleteCommand( QTextParagraph *p, int idx, const QMemArray<QTextStringChar> &str )
193 : QTextCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
194{
195 for ( int i = 0; i < (int)text.size(); ++i ) {
196 if ( text[ i ].format() )
197 text[ i ].format()->addRef();
198 }
199}
200
201QTextDeleteCommand::~QTextDeleteCommand()
202{
203 for ( int i = 0; i < (int)text.size(); ++i ) {
204 if ( text[ i ].format() )
205 text[ i ].format()->removeRef();
206 }
207 text.resize( 0 );
208}
209
210QTextCursor *QTextDeleteCommand::execute( QTextCursor *c )
211{
212 QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
213 if ( !s ) {
214 qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() );
215 return 0;
216 }
217
218 cursor.setParagraph( s );
219 cursor.setIndex( index );
220 int len = text.size();
221 if ( c )
222 *c = cursor;
223 if ( doc ) {
224 doc->setSelectionStart( QTextDocument::Temp, cursor );
225 for ( int i = 0; i < len; ++i )
226 cursor.gotoNextLetter();
227 doc->setSelectionEnd( QTextDocument::Temp, cursor );
228 doc->removeSelectedText( QTextDocument::Temp, &cursor );
229 if ( c )
230 *c = cursor;
231 } else {
232 s->remove( index, len );
233 }
234
235 return c;
236}
237
238QTextCursor *QTextDeleteCommand::unexecute( QTextCursor *c )
239{
240 QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
241 if ( !s ) {
242 qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() );
243 return 0;
244 }
245
246 cursor.setParagraph( s );
247 cursor.setIndex( index );
248 QString str = QTextString::toString( text );
249 cursor.insert( str, TRUE, &text );
250 if ( c )
251 *c = cursor;
252 cursor.setParagraph( s );
253 cursor.setIndex( index );
254
255#ifndef QT_NO_DATASTREAM
256 if ( !styleInformation.isEmpty() ) {
257 QDataStream styleStream( styleInformation, IO_ReadOnly );
258 int num;
259 styleStream >> num;
260 QTextParagraph *p = s;
261 while ( num-- && p ) {
262 p->readStyleInformation( styleStream );
263 p = p->next();
264 }
265 }
266#endif
267 s = cursor.paragraph();
268 while ( s ) {
269 s->format();
270 s->setChanged( TRUE );
271 if ( s == c->paragraph() )
272 break;
273 s = s->next();
274 }
275
276 return &cursor;
277}
278
279QTextFormatCommand::QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx,
280 const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl )
281 : QTextCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), format( f ), oldFormats( old ), flags( fl )
282{
283 format = d->formatCollection()->format( f );
284 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
285 if ( oldFormats[ j ].format() )
286 oldFormats[ j ].format()->addRef();
287 }
288}
289
290QTextFormatCommand::~QTextFormatCommand()
291{
292 format->removeRef();
293 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
294 if ( oldFormats[ j ].format() )
295 oldFormats[ j ].format()->removeRef();
296 }
297}
298
299QTextCursor *QTextFormatCommand::execute( QTextCursor *c )
300{
301 QTextParagraph *sp = doc->paragAt( startId );
302 QTextParagraph *ep = doc->paragAt( endId );
303 if ( !sp || !ep )
304 return c;
305
306 QTextCursor start( doc );
307 start.setParagraph( sp );
308 start.setIndex( startIndex );
309 QTextCursor end( doc );
310 end.setParagraph( ep );
311 end.setIndex( endIndex );
312
313 doc->setSelectionStart( QTextDocument::Temp, start );
314 doc->setSelectionEnd( QTextDocument::Temp, end );
315 doc->setFormat( QTextDocument::Temp, format, flags );
316 doc->removeSelection( QTextDocument::Temp );
317 if ( endIndex == ep->length() )
318 end.gotoLeft();
319 *c = end;
320 return c;
321}
322
323QTextCursor *QTextFormatCommand::unexecute( QTextCursor *c )
324{
325 QTextParagraph *sp = doc->paragAt( startId );
326 QTextParagraph *ep = doc->paragAt( endId );
327 if ( !sp || !ep )
328 return 0;
329
330 int idx = startIndex;
331 int fIndex = 0;
332 for ( ;; ) {
333 if ( oldFormats.at( fIndex ).c == '\n' ) {
334 if ( idx > 0 ) {
335 if ( idx < sp->length() && fIndex > 0 )
336 sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
337 if ( sp == ep )
338 break;
339 sp = sp->next();
340 idx = 0;
341 }
342 fIndex++;
343 }
344 if ( oldFormats.at( fIndex ).format() )
345 sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
346 idx++;
347 fIndex++;
348 if ( fIndex >= (int)oldFormats.size() )
349 break;
350 if ( idx >= sp->length() ) {
351 if ( sp == ep )
352 break;
353 sp = sp->next();
354 idx = 0;
355 }
356 }
357
358 QTextCursor end( doc );
359 end.setParagraph( ep );
360 end.setIndex( endIndex );
361 if ( endIndex == ep->length() )
362 end.gotoLeft();
363 *c = end;
364 return c;
365}
366
367QTextStyleCommand::QTextStyleCommand( QTextDocument *d, int fParag, int lParag, const QByteArray& beforeChange )
368 : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), before( beforeChange )
369{
370 after = readStyleInformation( d, fParag, lParag );
371}
372
373
374QByteArray QTextStyleCommand::readStyleInformation( QTextDocument* doc, int fParag, int lParag )
375{
376 QByteArray style;
377#ifndef QT_NO_DATASTREAM
378 QTextParagraph *p = doc->paragAt( fParag );
379 if ( !p )
380 return style;
381 QDataStream styleStream( style, IO_WriteOnly );
382 int num = lParag - fParag + 1;
383 styleStream << num;
384 while ( num -- && p ) {
385 p->writeStyleInformation( styleStream );
386 p = p->next();
387 }
388#endif
389 return style;
390}
391
392void QTextStyleCommand::writeStyleInformation( QTextDocument* doc, int fParag, const QByteArray& style )
393{
394#ifndef QT_NO_DATASTREAM
395 QTextParagraph *p = doc->paragAt( fParag );
396 if ( !p )
397 return;
398 QDataStream styleStream( style, IO_ReadOnly );
399 int num;
400 styleStream >> num;
401 while ( num-- && p ) {
402 p->readStyleInformation( styleStream );
403 p = p->next();
404 }
405#endif
406}
407
408QTextCursor *QTextStyleCommand::execute( QTextCursor *c )
409{
410 writeStyleInformation( doc, firstParag, after );
411 return c;
412}
413
414QTextCursor *QTextStyleCommand::unexecute( QTextCursor *c )
415{
416 writeStyleInformation( doc, firstParag, before );
417 return c;
418}
419
420// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
421
422QTextCursor::QTextCursor( QTextDocument *d )
423 : idx( 0 ), tmpX( -1 ), ox( 0 ), oy( 0 ),
424 valid( TRUE )
425{
426 para = d ? d->firstParagraph() : 0;
427}
428
429QTextCursor::QTextCursor( const QTextCursor &c )
430{
431 ox = c.ox;
432 oy = c.oy;
433 idx = c.idx;
434 para = c.para;
435 tmpX = c.tmpX;
436 indices = c.indices;
437 paras = c.paras;
438 xOffsets = c.xOffsets;
439 yOffsets = c.yOffsets;
440 valid = c.valid;
441}
442
443QTextCursor &QTextCursor::operator=( const QTextCursor &c )
444{
445 ox = c.ox;
446 oy = c.oy;
447 idx = c.idx;
448 para = c.para;
449 tmpX = c.tmpX;
450 indices = c.indices;
451 paras = c.paras;
452 xOffsets = c.xOffsets;
453 yOffsets = c.yOffsets;
454 valid = c.valid;
455
456 return *this;
457}
458
459bool QTextCursor::operator==( const QTextCursor &c ) const
460{
461 return para == c.para && idx == c.idx;
462}
463
464int QTextCursor::totalOffsetX() const
465{
466 int xoff = ox;
467 for ( QValueStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit )
468 xoff += *xit;
469 return xoff;
470}
471
472int QTextCursor::totalOffsetY() const
473{
474 int yoff = oy;
475 for ( QValueStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit )
476 yoff += *yit;
477 return yoff;
478}
479
480#ifndef QT_NO_TEXTCUSTOMITEM
481void QTextCursor::gotoIntoNested( const QPoint &globalPos )
482{
483 if ( !para )
484 return;
485 Q_ASSERT( para->at( idx )->isCustom() );
486 push();
487 ox = 0;
488 int bl, y;
489 para->lineHeightOfChar( idx, &bl, &y );
490 oy = y + para->rect().y();
491 ox = para->at( idx )->x;
492 QTextDocument* doc = document();
493 para->at( idx )->customItem()->enterAt( this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy) );
494}
495#endif
496
497void QTextCursor::invalidateNested()
498{
499 if ( nestedDepth() ) {
500 QValueStack<QTextParagraph*>::Iterator it = paras.begin();
501 QValueStack<int>::Iterator it2 = indices.begin();
502 for ( ; it != paras.end(); ++it, ++it2 ) {
503 if ( *it == para )
504 continue;
505 (*it)->invalidate( 0 );
506#ifndef QT_NO_TEXTCUSTOMITEM
507 if ( (*it)->at( *it2 )->isCustom() )
508 (*it)->at( *it2 )->customItem()->invalidate();
509#endif
510 }
511 }
512}
513
514void QTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<QTextStringChar> *formatting )
515{
516 tmpX = -1;
517 bool justInsert = TRUE;
518 QString s( str );
519#if defined(Q_WS_WIN)
520 if ( checkNewLine ) {
521 int i = 0;
522 while ( ( i = s.find( '\r', i ) ) != -1 )
523 s.remove( i ,1 );
524 }
525#endif
526 if ( checkNewLine )
527 justInsert = s.find( '\n' ) == -1;
528 if ( justInsert ) { // we ignore new lines and insert all in the current para at the current index
529 para->insert( idx, s.unicode(), s.length() );
530 if ( formatting ) {
531 for ( int i = 0; i < (int)s.length(); ++i ) {
532 if ( formatting->at( i ).format() ) {
533 formatting->at( i ).format()->addRef();
534 para->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
535 }
536 }
537 }
538 idx += s.length();
539 } else { // we split at new lines
540 int start = -1;
541 int end;
542 int y = para->rect().y() + para->rect().height();
543 int lastIndex = 0;
544 do {
545 end = s.find( '\n', start + 1 ); // find line break
546 if ( end == -1 ) // didn't find one, so end of line is end of string
547 end = s.length();
548 int len = (start == -1 ? end : end - start - 1);
549 if ( len > 0 ) // insert the line
550 para->insert( idx, s.unicode() + start + 1, len );
551 else
552 para->invalidate( 0 );
553 if ( formatting ) { // set formats to the chars of the line
554 for ( int i = 0; i < len; ++i ) {
555 if ( formatting->at( i + lastIndex ).format() ) {
556 formatting->at( i + lastIndex ).format()->addRef();
557 para->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
558 }
559 }
560 lastIndex += len;
561 }
562 start = end; // next start is at the end of this line
563 idx += len; // increase the index of the cursor to the end of the inserted text
564 if ( s[end] == '\n' ) { // if at the end was a line break, break the line
565 splitAndInsertEmptyParagraph( FALSE, TRUE );
566 para->setEndState( -1 );
567 para->prev()->format( -1, FALSE );
568 lastIndex++;
569 }
570
571 } while ( end < (int)s.length() );
572
573 para->format( -1, FALSE );
574 int dy = para->rect().y() + para->rect().height() - y;
575 QTextParagraph *p = para;
576 p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 );
577 p = p->next();
578 while ( p ) {
579 p->setParagId( p->prev()->paragId() + 1 );
580 p->move( dy );
581 p->invalidate( 0 );
582 p->setEndState( -1 );
583 p = p->next();
584 }
585 }
586
587 int h = para->rect().height();
588 para->format( -1, TRUE );
589 if ( h != para->rect().height() )
590 invalidateNested();
591 else if ( para->document() && para->document()->parent() )
592 para->document()->nextDoubleBuffered = TRUE;
593
594 fixCursorPosition();
595}
596
597void QTextCursor::gotoLeft()
598{
599 if ( para->string()->isRightToLeft() )
600 gotoNextLetter();
601 else
602 gotoPreviousLetter();
603}
604
605void QTextCursor::gotoPreviousLetter()
606{
607 tmpX = -1;
608
609 if ( idx > 0 ) {
610 idx = para->string()->previousCursorPosition( idx );
611#ifndef QT_NO_TEXTCUSTOMITEM
612 const QTextStringChar *tsc = para->at( idx );
613 if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() )
614 processNesting( EnterEnd );
615#endif
616 } else if ( para->prev() ) {
617 para = para->prev();
618 while ( !para->isVisible() && para->prev() )
619 para = para->prev();
620 idx = para->length() - 1;
621 } else if ( nestedDepth() ) {
622 pop();
623 processNesting( Prev );
624 if ( idx == -1 ) {
625 pop();
626 if ( idx > 0 ) {
627 idx = para->string()->previousCursorPosition( idx );
628 } else if ( para->prev() ) {
629 para = para->prev();
630 idx = para->length() - 1;
631 }
632 }
633 }
634}
635
636void QTextCursor::push()
637{
638 indices.push( idx );
639 paras.push( para );
640 xOffsets.push( ox );
641 yOffsets.push( oy );
642}
643
644void QTextCursor::pop()
645{
646 if ( indices.isEmpty() )
647 return;
648 idx = indices.pop();
649 para = paras.pop();
650 ox = xOffsets.pop();
651 oy = yOffsets.pop();
652}
653
654void QTextCursor::restoreState()
655{
656 while ( !indices.isEmpty() )
657 pop();
658}
659
660bool QTextCursor::place( const QPoint &p, QTextParagraph *s, bool link )
661{
662 QPoint pos( p );
663 QRect r;
664 QTextParagraph *str = s;
665 if ( pos.y() < s->rect().y() ) {
666 pos.setY( s->rect().y() );
667#ifdef Q_WS_MACX
668 pos.setX( s->rect().x() );
669#endif
670 }
671 while ( s ) {
672 r = s->rect();
673 r.setWidth( document() ? document()->width() : QWIDGETSIZE_MAX );
674 if ( s->isVisible() )
675 str = s;
676 if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() )
677 break;
678 if ( !s->next() ) {
679#ifdef Q_WS_MACX
680 pos.setX( s->rect().x() + s->rect().width() );
681#endif
682 break;
683 }
684 s = s->next();
685 }
686
687 if ( !s || !str )
688 return FALSE;
689
690 s = str;
691
692 setParagraph( s );
693 int y = s->rect().y();
694 int lines = s->lines();
695 QTextStringChar *chr = 0;
696 int index = 0;
697 int i = 0;
698 int cy = 0;
699 int ch = 0;
700 for ( ; i < lines; ++i ) {
701 chr = s->lineStartOfLine( i, &index );
702 cy = s->lineY( i );
703 ch = s->lineHeight( i );
704 if ( !chr )
705 return FALSE;
706 if ( pos.y() <= y + cy + ch )
707 break;
708 }
709 int nextLine;
710 if ( i < lines - 1 )
711 s->lineStartOfLine( i+1, &nextLine );
712 else
713 nextLine = s->length();
714 i = index;
715 int x = s->rect().x();
716 if ( pos.x() < x )
717 pos.setX( x + 1 );
718 int cw;
719 int curpos = s->length()-1;
720 int dist = 10000000;
721 bool inCustom = FALSE;
722 while ( i < nextLine ) {
723 chr = s->at(i);
724 int cpos = x + chr->x;
725 cw = s->string()->width( i );
726#ifndef QT_NO_TEXTCUSTOMITEM
727 if ( chr->isCustom() && chr->customItem()->isNested() ) {
728 if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
729 pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
730 inCustom = TRUE;
731 curpos = i;
732 break;
733 }
734 } else
735#endif
736 {
737 if( chr->rightToLeft )
738 cpos += cw;
739 int d = cpos - pos.x();
740 bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
741 if ( (QABS( d ) < dist || (dist == d && dm == TRUE )) && para->string()->validCursorPosition( i ) ) {
742 dist = QABS( d );
743 if ( !link || pos.x() >= x + chr->x )
744 curpos = i;
745 }
746 }
747 i++;
748 }
749 setIndex( curpos );
750
751#ifndef QT_NO_TEXTCUSTOMITEM
752 if ( inCustom && para->document() && para->at( curpos )->isCustom() && para->at( curpos )->customItem()->isNested() ) {
753 QTextDocument *oldDoc = para->document();
754 gotoIntoNested( pos );
755 if ( oldDoc == para->document() )
756 return TRUE;
757 QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
758 if ( !place( p, document()->firstParagraph(), link ) )
759 pop();
760 }
761#endif
762 return TRUE;
763}
764
765bool QTextCursor::processNesting( Operation op )
766{
767 if ( !para->document() )
768 return FALSE;
769 QTextDocument* doc = para->document();
770 push();
771 ox = para->at( idx )->x;
772 int bl, y;
773 para->lineHeightOfChar( idx, &bl, &y );
774 oy = y + para->rect().y();
775 bool ok = FALSE;
776
777#ifndef QT_NO_TEXTCUSTOMITEM
778 switch ( op ) {
779 case EnterBegin:
780 ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy );
781 break;
782 case EnterEnd:
783 ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy, TRUE );
784 break;
785 case Next:
786 ok = para->at( idx )->customItem()->next( this, doc, para, idx, ox, oy );
787 break;
788 case Prev:
789 ok = para->at( idx )->customItem()->prev( this, doc, para, idx, ox, oy );
790 break;
791 case Down:
792 ok = para->at( idx )->customItem()->down( this, doc, para, idx, ox, oy );
793 break;
794 case Up:
795 ok = para->at( idx )->customItem()->up( this, doc, para, idx, ox, oy );
796 break;
797 }
798 if ( !ok )
799#endif
800 pop();
801 return ok;
802}
803
804void QTextCursor::gotoRight()
805{
806 if ( para->string()->isRightToLeft() )
807 gotoPreviousLetter();
808 else
809 gotoNextLetter();
810}
811
812void QTextCursor::gotoNextLetter()
813{
814 tmpX = -1;
815
816#ifndef QT_NO_TEXTCUSTOMITEM
817 const QTextStringChar *tsc = para->at( idx );
818 if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
819 if ( processNesting( EnterBegin ) )
820 return;
821 }
822#endif
823
824 if ( idx < para->length() - 1 ) {
825 idx = para->string()->nextCursorPosition( idx );
826 } else if ( para->next() ) {
827 para = para->next();
828 while ( !para->isVisible() && para->next() )
829 para = para->next();
830 idx = 0;
831 } else if ( nestedDepth() ) {
832 pop();
833 processNesting( Next );
834 if ( idx == -1 ) {
835 pop();
836 if ( idx < para->length() - 1 ) {
837 idx = para->string()->nextCursorPosition( idx );
838 } else if ( para->next() ) {
839 para = para->next();
840 idx = 0;
841 }
842 }
843 }
844}
845
846void QTextCursor::gotoUp()
847{
848 int indexOfLineStart;
849 int line;
850 QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
851 if ( !c )
852 return;
853
854 if (tmpX < 0)
855 tmpX = x();
856
857 if ( indexOfLineStart == 0 ) {
858 if ( !para->prev() ) {
859 if ( !nestedDepth() )
860 return;
861 pop();
862 processNesting( Up );
863 if ( idx == -1 ) {
864 pop();
865 if ( !para->prev() )
866 return;
867 idx = tmpX = 0;
868 } else {
869 tmpX = -1;
870 return;
871 }
872 }
873 QTextParagraph *p = para->prev();
874 while ( p && !p->isVisible() )
875 p = p->prev();
876 if ( p )
877 para = p;
878 int lastLine = para->lines() - 1;
879 if ( !para->lineStartOfLine( lastLine, &indexOfLineStart ) )
880 return;
881 idx = indexOfLineStart;
882 while (idx < para->length()-1 && para->at(idx)->x < tmpX)
883 ++idx;
884 if (idx > indexOfLineStart &&
885 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
886 --idx;
887 } else {
888 --line;
889 int oldIndexOfLineStart = indexOfLineStart;
890 if ( !para->lineStartOfLine( line, &indexOfLineStart ) )
891 return;
892 idx = indexOfLineStart;
893 while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX)
894 ++idx;
895 if (idx > indexOfLineStart &&
896 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
897 --idx;
898 }
899 fixCursorPosition();
900}
901
902void QTextCursor::gotoDown()
903{
904 int indexOfLineStart;
905 int line;
906 QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
907 if ( !c )
908 return;
909
910 if (tmpX < 0)
911 tmpX = x();
912 if ( line == para->lines() - 1 ) {
913 if ( !para->next() ) {
914 if ( !nestedDepth() )
915 return;
916 pop();
917 processNesting( Down );
918 if ( idx == -1 ) {
919 pop();
920 if ( !para->next() )
921 return;
922 idx = tmpX = 0;
923 } else {
924 tmpX = -1;
925 return;
926 }
927 }
928 QTextParagraph *s = para->next();
929 while ( s && !s->isVisible() )
930 s = s->next();
931 if ( s )
932 para = s;
933 if ( !para->lineStartOfLine( 0, &indexOfLineStart ) )
934 return;
935 int end;
936 if ( para->lines() == 1 )
937 end = para->length();
938 else
939 para->lineStartOfLine( 1, &end );
940
941 idx = indexOfLineStart;
942 while (idx < end-1 && para->at(idx)->x < tmpX)
943 ++idx;
944 if (idx > indexOfLineStart &&
945 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
946 --idx;
947 } else {
948 ++line;
949 int end;
950 if ( line == para->lines() - 1 )
951 end = para->length();
952 else
953 para->lineStartOfLine( line + 1, &end );
954 if ( !para->lineStartOfLine( line, &indexOfLineStart ) )
955 return;
956 idx = indexOfLineStart;
957 while (idx < end-1 && para->at(idx)->x < tmpX)
958 ++idx;
959 if (idx > indexOfLineStart &&
960 para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
961 --idx;
962 }
963 fixCursorPosition();
964}
965
966void QTextCursor::gotoLineEnd()
967{
968 tmpX = -1;
969 int indexOfLineStart;
970 int line;
971 QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
972 if ( !c )
973 return;
974
975 if ( line == para->lines() - 1 ) {
976 idx = para->length() - 1;
977 } else {
978 c = para->lineStartOfLine( ++line, &indexOfLineStart );
979 indexOfLineStart--;
980 idx = indexOfLineStart;
981 }
982}
983
984void QTextCursor::gotoLineStart()
985{
986 tmpX = -1;
987 int indexOfLineStart;
988 int line;
989 QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line );
990 if ( !c )
991 return;
992
993 idx = indexOfLineStart;
994}
995
996void QTextCursor::gotoHome()
997{
998 if ( topParagraph()->document() )
999 gotoPosition( topParagraph()->document()->firstParagraph() );
1000 else
1001 gotoLineStart();
1002}
1003
1004void QTextCursor::gotoEnd()
1005{
1006 if ( topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid() )
1007 gotoPosition( topParagraph()->document()->lastParagraph(),
1008 topParagraph()->document()->lastParagraph()->length() - 1);
1009 else
1010 gotoLineEnd();
1011}
1012
1013void QTextCursor::gotoPageUp( int visibleHeight )
1014{
1015 int targetY = globalY() - visibleHeight;
1016 QTextParagraph* old; int index;
1017 do {
1018 old = para; index = idx;
1019 gotoUp();
1020 } while ( (old != para || index != idx) && globalY() > targetY );
1021}
1022
1023void QTextCursor::gotoPageDown( int visibleHeight )
1024{
1025 int targetY = globalY() + visibleHeight;
1026 QTextParagraph* old; int index;
1027 do {
1028 old = para; index = idx;
1029 gotoDown();
1030 } while ( (old != para || index != idx) && globalY() < targetY );
1031}
1032
1033void QTextCursor::gotoWordRight()
1034{
1035 if ( para->string()->isRightToLeft() )
1036 gotoPreviousWord();
1037 else
1038 gotoNextWord();
1039}
1040
1041void QTextCursor::gotoWordLeft()
1042{
1043 if ( para->string()->isRightToLeft() )
1044 gotoNextWord();
1045 else
1046 gotoPreviousWord();
1047}
1048
1049static bool is_seperator( const QChar &c, bool onlySpace )
1050{
1051 if ( onlySpace )
1052 return c.isSpace();
1053 return c.isSpace() ||
1054 c == '\t' ||
1055 c == '.' ||
1056 c == ',' ||
1057 c == ':' ||
1058 c == ';' ||
1059 c == '-' ||
1060 c == '<' ||
1061 c == '>' ||
1062 c == '[' ||
1063 c == ']' ||
1064 c == '(' ||
1065 c == ')' ||
1066 c == '{' ||
1067 c == '}';
1068}
1069
1070void QTextCursor::gotoPreviousWord( bool onlySpace )
1071{
1072 gotoPreviousLetter();
1073 tmpX = -1;
1074 QTextString *s = para->string();
1075 bool allowSame = FALSE;
1076 if ( idx == ((int)s->length()-1) )
1077 return;
1078 for ( int i = idx; i >= 0; --i ) {
1079 if ( is_seperator( s->at( i ).c, onlySpace ) ) {
1080 if ( !allowSame )
1081 continue;
1082 idx = i + 1;
1083 return;
1084 }
1085 if ( !allowSame && !is_seperator( s->at( i ).c, onlySpace ) )
1086 allowSame = TRUE;
1087 }
1088 idx = 0;
1089}
1090
1091void QTextCursor::gotoNextWord( bool onlySpace )
1092{
1093 tmpX = -1;
1094 QTextString *s = para->string();
1095 bool allowSame = FALSE;
1096 for ( int i = idx; i < (int)s->length(); ++i ) {
1097 if ( !is_seperator( s->at( i ).c, onlySpace ) ) {
1098 if ( !allowSame )
1099 continue;
1100 idx = i;
1101 return;
1102 }
1103 if ( !allowSame && is_seperator( s->at( i ).c, onlySpace ) )
1104 allowSame = TRUE;
1105
1106 }
1107
1108 if ( idx < ((int)s->length()-1) ) {
1109 gotoLineEnd();
1110 } else if ( para->next() ) {
1111 QTextParagraph *p = para->next();
1112 while ( p && !p->isVisible() )
1113 p = p->next();
1114 if ( s ) {
1115 para = p;
1116 idx = 0;
1117 }
1118 } else {
1119 gotoLineEnd();
1120 }
1121}
1122
1123bool QTextCursor::atParagStart()
1124{
1125 return idx == 0;
1126}
1127
1128bool QTextCursor::atParagEnd()
1129{
1130 return idx == para->length() - 1;
1131}
1132
1133void QTextCursor::splitAndInsertEmptyParagraph( bool ind, bool updateIds )
1134{
1135 if ( !para->document() )
1136 return;
1137 tmpX = -1;
1138 QTextFormat *f = 0;
1139 if ( para->document()->useFormatCollection() ) {
1140 f = para->at( idx )->format();
1141 if ( idx == para->length() - 1 && idx > 0 )
1142 f = para->at( idx - 1 )->format();
1143 if ( f->isMisspelled() ) {
1144 f->removeRef();
1145 f = para->document()->formatCollection()->format( f->font(), f->color() );
1146 }
1147 }
1148
1149 if ( atParagEnd() ) {
1150 QTextParagraph *n = para->next();
1151 QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds );
1152 if ( f )
1153 s->setFormat( 0, 1, f, TRUE );
1154 s->copyParagData( para );
1155 if ( ind ) {
1156 int oi, ni;
1157 s->indent( &oi, &ni );
1158 para = s;
1159 idx = ni;
1160 } else {
1161 para = s;
1162 idx = 0;
1163 }
1164 } else if ( atParagStart() ) {
1165 QTextParagraph *p = para->prev();
1166 QTextParagraph *s = para->document()->createParagraph( para->document(), p, para, updateIds );
1167 if ( f )
1168 s->setFormat( 0, 1, f, TRUE );
1169 s->copyParagData( para );
1170 if ( ind ) {
1171 s->indent();
1172 s->format();
1173 indent();
1174 para->format();
1175 }
1176 } else {
1177 QString str = para->string()->toString().mid( idx, 0xFFFFFF );
1178 QTextParagraph *n = para->next();
1179 QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds );
1180 s->copyParagData( para );
1181 s->remove( 0, 1 );
1182 s->append( str, TRUE );
1183 for ( uint i = 0; i < str.length(); ++i ) {
1184 QTextStringChar* tsc = para->at( idx + i );
1185 s->setFormat( i, 1, tsc->format(), TRUE );
1186#ifndef QT_NO_TEXTCUSTOMITEM
1187 if ( tsc->isCustom() ) {
1188 QTextCustomItem * item = tsc->customItem();
1189 s->at( i )->setCustomItem( item );
1190 tsc->loseCustomItem();
1191 }
1192#endif
1193 if ( tsc->isAnchor() )
1194 s->at( i )->setAnchor( tsc->anchorName(),
1195 tsc->anchorHref() );
1196 }
1197 para->truncate( idx );
1198 if ( ind ) {
1199 int oi, ni;
1200 s->indent( &oi, &ni );
1201 para = s;
1202 idx = ni;
1203 } else {
1204 para = s;
1205 idx = 0;
1206 }
1207 }
1208
1209 invalidateNested();
1210}
1211
1212bool QTextCursor::remove()
1213{
1214 tmpX = -1;
1215 if ( !atParagEnd() ) {
1216 int next = para->string()->nextCursorPosition( idx );
1217 para->remove( idx, next-idx );
1218 int h = para->rect().height();
1219 para->format( -1, TRUE );
1220 if ( h != para->rect().height() )
1221 invalidateNested();
1222 else if ( para->document() && para->document()->parent() )
1223 para->document()->nextDoubleBuffered = TRUE;
1224 return FALSE;
1225 } else if ( para->next() ) {
1226 para->join( para->next() );
1227 invalidateNested();
1228 return TRUE;
1229 }
1230 return FALSE;
1231}
1232
1233/* needed to implement backspace the correct way */
1234bool QTextCursor::removePreviousChar()
1235{
1236 tmpX = -1;
1237 if ( !atParagStart() ) {
1238 para->remove( idx-1, 1 );
1239 int h = para->rect().height();
1240 idx--;
1241 // shouldn't be needed, just to make sure.
1242 fixCursorPosition();
1243 para->format( -1, TRUE );
1244 if ( h != para->rect().height() )
1245 invalidateNested();
1246 else if ( para->document() && para->document()->parent() )
1247 para->document()->nextDoubleBuffered = TRUE;
1248 return FALSE;
1249 } else if ( para->prev() ) {
1250 para = para->prev();
1251 para->join( para->next() );
1252 invalidateNested();
1253 return TRUE;
1254 }
1255 return FALSE;
1256}
1257
1258void QTextCursor::indent()
1259{
1260 int oi = 0, ni = 0;
1261 para->indent( &oi, &ni );
1262 if ( oi == ni )
1263 return;
1264
1265 if ( idx >= oi )
1266 idx += ni - oi;
1267 else
1268 idx = ni;
1269}
1270
1271void QTextCursor::fixCursorPosition()
1272{
1273 // searches for the closest valid cursor position
1274 if ( para->string()->validCursorPosition( idx ) )
1275 return;
1276
1277 int lineIdx;
1278 QTextStringChar *start = para->lineStartOfChar( idx, &lineIdx, 0 );
1279 int x = para->string()->at( idx ).x;
1280 int diff = QABS(start->x - x);
1281 int best = lineIdx;
1282
1283 QTextStringChar *c = start;
1284 ++c;
1285
1286 QTextStringChar *end = &para->string()->at( para->length()-1 );
1287 while ( c <= end && !c->lineStart ) {
1288 int xp = c->x;
1289 if ( c->rightToLeft )
1290 xp += para->string()->width( lineIdx + (c-start) );
1291 int ndiff = QABS(xp - x);
1292 if ( ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start)) ) {
1293 diff = ndiff;
1294 best = lineIdx + (c-start);
1295 }
1296 ++c;
1297 }
1298 idx = best;
1299}
1300
1301
1302// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1303
1304QTextDocument::QTextDocument( QTextDocument *p )
1305 : par( p ), parentPar( 0 )
1306#ifndef QT_NO_TEXTCUSTOMITEM
1307 , tc( 0 )
1308#endif
1309 , tArray( 0 ), tStopWidth( 0 )
1310{
1311 fCollection = new QTextFormatCollection;
1312 init();
1313}
1314
1315QTextDocument::QTextDocument( QTextDocument *p, QTextFormatCollection *f )
1316 : par( p ), parentPar( 0 )
1317#ifndef QT_NO_TEXTCUSTOMITEM
1318 , tc( 0 )
1319#endif
1320 , tArray( 0 ), tStopWidth( 0 )
1321{
1322 fCollection = f;
1323 init();
1324}
1325
1326void QTextDocument::init()
1327{
1328 oTextValid = TRUE;
1329 mightHaveCustomItems = FALSE;
1330 if ( par )
1331 par->insertChild( this );
1332 pProcessor = 0;
1333 useFC = TRUE;
1334 pFormatter = 0;
1335 indenter = 0;
1336 fParag = 0;
1337 txtFormat = Qt::AutoText;
1338 preferRichText = FALSE;
1339 pages = FALSE;
1340 focusIndicator.parag = 0;
1341 minw = 0;
1342 wused = 0;
1343 minwParag = curParag = 0;
1344 align = AlignAuto;
1345 nSelections = 1;
1346
1347 setStyleSheet( QStyleSheet::defaultSheet() );
1348#ifndef QT_NO_MIME
1349 factory_ = QMimeSourceFactory::defaultFactory();
1350#endif
1351 contxt = QString::null;
1352
1353 underlLinks = par ? par->underlLinks : TRUE;
1354 backBrush = 0;
1355 buf_pixmap = 0;
1356 nextDoubleBuffered = FALSE;
1357
1358 if ( par )
1359 withoutDoubleBuffer = par->withoutDoubleBuffer;
1360 else
1361 withoutDoubleBuffer = FALSE;
1362
1363 lParag = fParag = createParagraph( this, 0, 0 );
1364
1365 cx = 0;
1366 cy = 2;
1367 if ( par )
1368 cx = cy = 0;
1369 cw = 600;
1370 vw = 0;
1371 flow_ = new QTextFlow;
1372 flow_->setWidth( cw );
1373
1374 leftmargin = rightmargin = 4;
1375 scaleFontsFactor = 1;
1376
1377
1378 selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
1379 selectionText[ Standard ] = TRUE;
1380 selectionText[ IMSelectionText ] = TRUE;
1381 selectionText[ IMCompositionText ] = FALSE;
1382 commandHistory = new QTextCommandHistory( 100 );
1383 tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
1384}
1385
1386QTextDocument::~QTextDocument()
1387{
1388 delete commandHistory;
1389 if ( par )
1390 par->removeChild( this );
1391 clear();
1392 delete flow_;
1393 if ( !par )
1394 delete pFormatter;
1395 delete fCollection;
1396 delete pProcessor;
1397 delete buf_pixmap;
1398 delete indenter;
1399 delete backBrush;
1400 delete [] tArray;
1401}
1402
1403void QTextDocument::clear( bool createEmptyParag )
1404{
1405 while ( fParag ) {
1406 QTextParagraph *p = fParag->next();
1407 delete fParag;
1408 fParag = p;
1409 }
1410 if ( flow_ )
1411 flow_->clear();
1412 fParag = lParag = 0;
1413 if ( createEmptyParag )
1414 fParag = lParag = createParagraph( this );
1415 selections.clear();
1416 oText = QString::null;
1417 oTextValid = FALSE;
1418}
1419
1420int QTextDocument::widthUsed() const
1421{
1422 return wused + 2*border_tolerance;
1423}
1424
1425int QTextDocument::height() const
1426{
1427 int h = 0;
1428 if ( lParag )
1429 h = lParag->rect().top() + lParag->rect().height() + 1;
1430 int fh = flow_->boundingRect().bottom();
1431 return QMAX( h, fh );
1432}
1433
1434
1435
1436QTextParagraph *QTextDocument::createParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds )
1437{
1438 return new QTextParagraph( d, pr, nx, updateIds );
1439}
1440
1441bool QTextDocument::setMinimumWidth( int needed, int used, QTextParagraph *p )
1442{
1443 if ( needed == -1 ) {
1444 minw = 0;
1445 wused = 0;
1446 p = 0;
1447 }
1448 if ( p == minwParag ) {
1449 if (minw > needed) {
1450 QTextParagraph *tp = fParag;
1451 while (tp) {
1452 if (tp != p && tp->minwidth > needed) {
1453 needed = tp->minwidth;
1454 minwParag = tp;
1455 }
1456 tp = tp->n;
1457 }
1458 }
1459 minw = needed;
1460 emit minimumWidthChanged( minw );
1461 } else if ( needed > minw ) {
1462 minw = needed;
1463 minwParag = p;
1464 emit minimumWidthChanged( minw );
1465 }
1466 wused = QMAX( wused, used );
1467 wused = QMAX( wused, minw );
1468 cw = QMAX( minw, cw );
1469 return TRUE;
1470}
1471
1472void QTextDocument::setPlainText( const QString &text )
1473{
1474 preferRichText = FALSE;
1475 clear();
1476 oTextValid = TRUE;
1477 oText = text;
1478
1479 int lastNl = 0;
1480 int nl = text.find( '\n' );
1481 if ( nl == -1 ) {
1482 lParag = createParagraph( this, lParag, 0 );
1483 if ( !fParag )
1484 fParag = lParag;
1485 QString s = text;
1486 if ( !s.isEmpty() ) {
1487 if ( s[ (int)s.length() - 1 ] == '\r' )
1488 s.remove( s.length() - 1, 1 );
1489 lParag->append( s );
1490 }
1491 } else {
1492 for (;;) {
1493 lParag = createParagraph( this, lParag, 0 );
1494 if ( !fParag )
1495 fParag = lParag;
1496 int l = nl - lastNl;
1497 if ( l > 0 ) {
1498 if (text.unicode()[nl-1] == '\r')
1499 l--;
1500 QConstString cs(text.unicode()+lastNl, l);
1501 lParag->append( cs.string() );
1502 }
1503 if ( nl == (int)text.length() )
1504 break;
1505 lastNl = nl + 1;
1506 nl = text.find( '\n', nl + 1 );
1507 if ( nl == -1 )
1508 nl = text.length();
1509 }
1510 }
1511 if ( !lParag )
1512 lParag = fParag = createParagraph( this, 0, 0 );
1513}
1514
1515struct Q_EXPORT QTextDocumentTag {
1516 QTextDocumentTag(){}
1517 QTextDocumentTag( const QString&n, const QStyleSheetItem* s, const QTextFormat& f )
1518 :name(n),style(s), format(f), alignment(Qt::AlignAuto), direction(QChar::DirON),liststyle(QStyleSheetItem::ListDisc) {
1519 wsm = QStyleSheetItem::WhiteSpaceNormal;
1520 }
1521 QString name;
1522 const QStyleSheetItem* style;
1523 QString anchorHref;
1524 QStyleSheetItem::WhiteSpaceMode wsm;
1525 QTextFormat format;
1526 int alignment : 16;
1527 int direction : 5;
1528 QStyleSheetItem::ListStyle liststyle;
1529
1530 QTextDocumentTag( const QTextDocumentTag& t ) {
1531 name = t.name;
1532 style = t.style;
1533 anchorHref = t.anchorHref;
1534 wsm = t.wsm;
1535 format = t.format;
1536 alignment = t.alignment;
1537 direction = t.direction;
1538 liststyle = t.liststyle;
1539 }
1540 QTextDocumentTag& operator=(const QTextDocumentTag& t) {
1541 name = t.name;
1542 style = t.style;
1543 anchorHref = t.anchorHref;
1544 wsm = t.wsm;
1545 format = t.format;
1546 alignment = t.alignment;
1547 direction = t.direction;
1548 liststyle = t.liststyle;
1549 return *this;
1550 }
1551
1552 Q_DUMMY_COMPARISON_OPERATOR(QTextDocumentTag)
1553};
1554
1555
1556#define NEWPAR do{ if ( !hasNewPar) { \
1557 if ( !textEditMode && curpar && curpar->length()>1 && curpar->at( curpar->length()-2)->c == QChar_linesep ) \
1558 curpar->remove( curpar->length()-2, 1 ); \
1559 curpar = createParagraph( this, curpar, curpar->next() ); styles.append( vec ); vec = 0;} \
1560 hasNewPar = TRUE; \
1561 curpar->rtext = TRUE; \
1562 curpar->align = curtag.alignment; \
1563 curpar->lstyle = curtag.liststyle; \
1564 curpar->litem = ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ); \
1565 curpar->str->setDirection( (QChar::Direction)curtag.direction ); \
1566 space = TRUE; \
1567 tabExpansionColumn = 0; \
1568 delete vec; vec = new QPtrVector<QStyleSheetItem>( (uint)tags.count() + 1); \
1569 int i = 0; \
1570 for ( QValueStack<QTextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it ) \
1571 vec->insert( i++, (*it).style ); \
1572 vec->insert( i, curtag.style ); \
1573 }while(FALSE);
1574
1575
1576void QTextDocument::setRichText( const QString &text, const QString &context )
1577{
1578 preferRichText = TRUE;
1579 if ( !context.isEmpty() )
1580 setContext( context );
1581 clear();
1582 fParag = lParag = createParagraph( this );
1583 oTextValid = TRUE;
1584 oText = text;
1585 setRichTextInternal( text );
1586 fParag->rtext = TRUE;
1587}
1588
1589void QTextDocument::setRichTextInternal( const QString &text, QTextCursor* cursor )
1590{
1591 QTextParagraph* curpar = lParag;
1592 int pos = 0;
1593 QValueStack<QTextDocumentTag> tags;
1594 QTextDocumentTag initag( "", sheet_->item(""), *formatCollection()->defaultFormat() );
1595 if ( bodyText.isValid() )
1596 initag.format.setColor( bodyText );
1597 QTextDocumentTag curtag = initag;
1598 bool space = TRUE;
1599 bool canMergeLi = FALSE;
1600
1601 bool textEditMode = FALSE;
1602 int tabExpansionColumn = 0;
1603
1604 const QChar* doc = text.unicode();
1605 int length = text.length();
1606 bool hasNewPar = curpar->length() <= 1;
1607 QString anchorName;
1608
1609 // style sheet handling for margin and line spacing calculation below
1610 QTextParagraph* stylesPar = curpar;
1611 QPtrVector<QStyleSheetItem>* vec = 0;
1612 QPtrList< QPtrVector<QStyleSheetItem> > styles;
1613 styles.setAutoDelete( TRUE );
1614
1615 if ( cursor ) {
1616 cursor->splitAndInsertEmptyParagraph();
1617 QTextCursor tmp = *cursor;
1618 tmp.gotoPreviousLetter();
1619 stylesPar = curpar = tmp.paragraph();
1620 hasNewPar = TRUE;
1621 textEditMode = TRUE;
1622 } else {
1623 NEWPAR;
1624 }
1625
1626 // set rtext spacing to FALSE for the initial paragraph.
1627 curpar->rtext = FALSE;
1628
1629 QString wellKnownTags = "br hr wsp table qt body meta title";
1630
1631 while ( pos < length ) {
1632 if ( hasPrefix(doc, length, pos, '<' ) ){
1633 if ( !hasPrefix( doc, length, pos+1, QChar('/') ) ) {
1634 // open tag
1635 QMap<QString, QString> attr;
1636 bool emptyTag = FALSE;
1637 QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
1638 if ( tagname.isEmpty() )
1639 continue; // nothing we could do with this, probably parse error
1640
1641 const QStyleSheetItem* nstyle = sheet_->item(tagname);
1642
1643 if ( nstyle ) {
1644 // we might have to close some 'forgotten' tags
1645 while ( !nstyle->allowedInContext( curtag.style ) ) {
1646 QString msg;
1647 msg.sprintf( "QText Warning: Document not valid ( '%s' not allowed in '%s' #%d)",
1648 tagname.ascii(), curtag.style->name().ascii(), pos);
1649 sheet_->error( msg );
1650 if ( tags.isEmpty() )
1651 break;
1652 curtag = tags.pop();
1653 }
1654
1655 /* special handling for p and li for HTML
1656 compatibility. We do not want to embed blocks in
1657 p, and we do not want new blocks inside non-empty
1658 lis. Plus we want to merge empty lis sometimes. */
1659 if( nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) {
1660 canMergeLi = TRUE;
1661 } else if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) {
1662 while ( curtag.style->name() == "p" ) {
1663 if ( tags.isEmpty() )
1664 break;
1665 curtag = tags.pop();
1666 }
1667
1668 if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
1669 // we are in a li and a new block comes along
1670 if ( nstyle->name() == "ul" || nstyle->name() == "ol" )
1671 hasNewPar = FALSE; // we want an empty li (like most browsers)
1672 if ( !hasNewPar ) {
1673 /* do not add new blocks inside
1674 non-empty lis */
1675 while ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
1676 if ( tags.isEmpty() )
1677 break;
1678 curtag = tags.pop();
1679 }
1680 } else if ( canMergeLi ) {
1681 /* we have an empty li and a block
1682 comes along, merge them */
1683 nstyle = curtag.style;
1684 }
1685 canMergeLi = FALSE;
1686 }
1687 }
1688 }
1689
1690#ifndef QT_NO_TEXTCUSTOMITEM
1691 QTextCustomItem* custom = 0;
1692#else
1693 bool custom = FALSE;
1694#endif
1695
1696 // some well-known tags, some have a nstyle, some not
1697 if ( wellKnownTags.find( tagname ) != -1 ) {
1698 if ( tagname == "br" ) {
1699 emptyTag = space = TRUE;
1700 int index = QMAX( curpar->length(),1) - 1;
1701 QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
1702 curpar->append( QChar_linesep );
1703 curpar->setFormat( index, 1, &format );
1704 } else if ( tagname == "hr" ) {
1705 emptyTag = space = TRUE;
1706#ifndef QT_NO_TEXTCUSTOMITEM
1707 custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
1708#endif
1709 } else if ( tagname == "table" ) {
1710 emptyTag = space = TRUE;
1711#ifndef QT_NO_TEXTCUSTOMITEM
1712 QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
1713 curpar->setAlignment( curtag.alignment );
1714 custom = parseTable( attr, format, doc, length, pos, curpar );
1715#endif
1716 } else if ( tagname == "qt" || tagname == "body" ) {
1717 if ( attr.contains( "bgcolor" ) ) {
1718 QBrush *b = new QBrush( QColor( attr["bgcolor"] ) );
1719 setPaper( b );
1720 }
1721 if ( attr.contains( "background" ) ) {
1722#ifndef QT_NO_MIME
1723 QImage img;
1724 QString bg = attr["background"];
1725 const QMimeSource* m = factory_->data( bg, contxt );
1726 if ( !m ) {
1727 qWarning("QRichText: no mimesource for %s", bg.latin1() );
1728 } else {
1729 if ( !QImageDrag::decode( m, img ) ) {
1730 qWarning("QTextImage: cannot decode %s", bg.latin1() );
1731 }
1732 }
1733 if ( !img.isNull() ) {
1734 QBrush *b = new QBrush( QColor(), QPixmap( img ) );
1735 setPaper( b );
1736 }
1737#endif
1738 }
1739 if ( attr.contains( "text" ) ) {
1740 QColor c( attr["text"] );
1741 initag.format.setColor( c );
1742 curtag.format.setColor( c );
1743 bodyText = c;
1744 }
1745 if ( attr.contains( "link" ) )
1746 linkColor = QColor( attr["link"] );
1747 if ( attr.contains( "title" ) )
1748 attribs.replace( "title", attr["title"] );
1749
1750 if ( textEditMode ) {
1751 if ( attr.contains("style" ) ) {
1752 QString a = attr["style"];
1753 for ( int s = 0; s < a.contains(';')+1; s++ ) {
1754 QString style = a.section( ';', s, s );
1755 if ( style.startsWith("font-size:" ) && style.endsWith("pt") ) {
1756 scaleFontsFactor = double( formatCollection()->defaultFormat()->fn.pointSize() ) /
1757 style.mid( 10, style.length() - 12 ).toInt();
1758 }
1759 }
1760 }
1761 nstyle = 0; // ignore body in textEditMode
1762 }
1763 // end qt- and body-tag handling
1764 } else if ( tagname == "meta" ) {
1765 if ( attr["name"] == "qrichtext" && attr["content"] == "1" )
1766 textEditMode = TRUE;
1767 } else if ( tagname == "title" ) {
1768 QString title;
1769 while ( pos < length ) {
1770 if ( hasPrefix( doc, length, pos, QChar('<') ) && hasPrefix( doc, length, pos+1, QChar('/') ) &&
1771 parseCloseTag( doc, length, pos ) == "title" )
1772 break;
1773 title += doc[ pos ];
1774 ++pos;
1775 }
1776 attribs.replace( "title", title );
1777 }
1778 } // end of well-known tag handling
1779
1780#ifndef QT_NO_TEXTCUSTOMITEM
1781 if ( !custom ) // try generic custom item
1782 custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
1783#endif
1784 if ( !nstyle && !custom ) // we have no clue what this tag could be, ignore it
1785 continue;
1786
1787 if ( custom ) {
1788#ifndef QT_NO_TEXTCUSTOMITEM
1789 int index = QMAX( curpar->length(),1) - 1;
1790 QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
1791 curpar->append( QChar('*') );
1792 QTextFormat* f = formatCollection()->format( &format );
1793 curpar->setFormat( index, 1, f );
1794 curpar->at( index )->setCustomItem( custom );
1795 if ( !curtag.anchorHref.isEmpty() )
1796 curpar->at(index)->setAnchor( QString::null, curtag.anchorHref );
1797 if ( !anchorName.isEmpty() ) {
1798 curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() );
1799 anchorName = QString::null;
1800 }
1801 registerCustomItem( custom, curpar );
1802 hasNewPar = FALSE;
1803#endif
1804 } else if ( !emptyTag ) {
1805 /* if we do nesting, push curtag on the stack,
1806 otherwise reinint curag. */
1807 if ( curtag.style->name() != tagname || nstyle->selfNesting() ) {
1808 tags.push( curtag );
1809 } else {
1810 if ( !tags.isEmpty() )
1811 curtag = tags.top();
1812 else
1813 curtag = initag;
1814 }
1815
1816 curtag.name = tagname;
1817 curtag.style = nstyle;
1818 curtag.name = tagname;
1819 curtag.style = nstyle;
1820 if ( nstyle->whiteSpaceMode() != QStyleSheetItem::WhiteSpaceModeUndefined )
1821 curtag.wsm = nstyle->whiteSpaceMode();
1822
1823 /* netscape compatibility: eat a newline and only a newline if a pre block starts */
1824 if ( curtag.wsm == QStyleSheetItem::WhiteSpacePre &&
1825 nstyle->displayMode() == QStyleSheetItem::DisplayBlock )
1826 eat( doc, length, pos, '\n' );
1827
1828 /* ignore whitespace for inline elements if there
1829 was already one*/
1830 if ( !textEditMode &&
1831 (curtag.wsm == QStyleSheetItem::WhiteSpaceNormal
1832 || curtag.wsm == QStyleSheetItem::WhiteSpaceNoWrap)
1833 && ( space || nstyle->displayMode() != QStyleSheetItem::DisplayInline ) )
1834 eatSpace( doc, length, pos );
1835
1836 curtag.format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
1837 if ( nstyle->isAnchor() ) {
1838 if ( !anchorName.isEmpty() )
1839 anchorName += "#" + attr["name"];
1840 else
1841 anchorName = attr["name"];
1842 curtag.anchorHref = attr["href"];
1843 }
1844
1845 if ( nstyle->alignment() != QStyleSheetItem::Undefined )
1846 curtag.alignment = nstyle->alignment();
1847
1848 if ( nstyle->listStyle() != QStyleSheetItem::ListStyleUndefined )
1849 curtag.liststyle = nstyle->listStyle();
1850
1851 if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock
1852 || nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) {
1853
1854 if ( nstyle->name() == "ol" || nstyle->name() == "ul" || nstyle->name() == "li") {
1855 QString type = attr["type"];
1856 if ( !type.isEmpty() ) {
1857 if ( type == "1" ) {
1858 curtag.liststyle = QStyleSheetItem::ListDecimal;
1859 } else if ( type == "a" ) {
1860 curtag.liststyle = QStyleSheetItem::ListLowerAlpha;
1861 } else if ( type == "A" ) {
1862 curtag.liststyle = QStyleSheetItem::ListUpperAlpha;
1863 } else {
1864 type = type.lower();
1865 if ( type == "square" )
1866 curtag.liststyle = QStyleSheetItem::ListSquare;
1867 else if ( type == "disc" )
1868 curtag.liststyle = QStyleSheetItem::ListDisc;
1869 else if ( type == "circle" )
1870 curtag.liststyle = QStyleSheetItem::ListCircle;
1871 }
1872 }
1873 }
1874
1875
1876 /* Internally we treat ordered and bullet
1877 lists the same for margin calculations. In
1878 order to have fast pointer compares in the
1879 xMargin() functions we restrict ourselves to
1880 <ol>. Once we calculate the margins in the
1881 parser rathern than later, the unelegance of
1882 this approach goes awy
1883 */
1884 if ( nstyle->name() == "ul" )
1885 curtag.style = sheet_->item( "ol" );
1886
1887 if ( attr.contains( "align" ) ) {
1888 QString align = attr["align"].lower();
1889 if ( align == "center" )
1890 curtag.alignment = Qt::AlignCenter;
1891 else if ( align == "right" )
1892 curtag.alignment = Qt::AlignRight;
1893 else if ( align == "justify" )
1894 curtag.alignment = Qt::AlignJustify;
1895 }
1896 if ( attr.contains( "dir" ) ) {
1897 QString dir = attr["dir"];
1898 if ( dir == "rtl" )
1899 curtag.direction = QChar::DirR;
1900 else if ( dir == "ltr" )
1901 curtag.direction = QChar::DirL;
1902 }
1903
1904 NEWPAR;
1905
1906 if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
1907 if ( attr.contains( "value " ) )
1908 curpar->setListValue( attr["value"].toInt() );
1909 }
1910
1911 if ( attr.contains( "style" ) ) {
1912 QString a = attr["style"];
1913 bool ok = TRUE;
1914 for ( int s = 0; ok && s < a.contains(';')+1; s++ ) {
1915 QString style = a.section( ';', s, s );
1916 if ( style.startsWith("margin-top:" ) && style.endsWith("px") )
1917 curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
1918 else if ( style.startsWith("margin-bottom:" ) && style.endsWith("px") )
1919 curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
1920 else if ( style.startsWith("margin-left:" ) && style.endsWith("px") )
1921 curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
1922 else if ( style.startsWith("margin-right:" ) && style.endsWith("px") )
1923 curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
1924 else if ( style.startsWith("text-indent:" ) && style.endsWith("px") )
1925 curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
1926 }
1927 if ( !ok ) // be pressmistic
1928 curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
1929 }
1930 }
1931 }
1932 } else {
1933 QString tagname = parseCloseTag( doc, length, pos );
1934 if ( tagname.isEmpty() )
1935 continue; // nothing we could do with this, probably parse error
1936 if ( !sheet_->item( tagname ) ) // ignore unknown tags
1937 continue;
1938 if ( tagname == "li" )
1939 continue;
1940
1941 // we close a block item. Since the text may continue, we need to have a new paragraph
1942 bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock
1943 || curtag.style->displayMode() == QStyleSheetItem::DisplayListItem;
1944
1945
1946 // html slopiness: handle unbalanched tag closing
1947 while ( curtag.name != tagname ) {
1948 QString msg;
1949 msg.sprintf( "QText Warning: Document not valid ( '%s' not closed before '%s' #%d)",
1950 curtag.name.ascii(), tagname.ascii(), pos);
1951 sheet_->error( msg );
1952 if ( tags.isEmpty() )
1953 break;
1954 curtag = tags.pop();
1955 }
1956
1957
1958 // close the tag
1959 if ( !tags.isEmpty() )
1960 curtag = tags.pop();
1961 else
1962 curtag = initag;
1963
1964 if ( needNewPar ) {
1965 if ( textEditMode && (tagname == "p" || tagname == "div" ) ) // preserve empty paragraphs
1966 hasNewPar = FALSE;
1967 NEWPAR;
1968 }
1969 }
1970 } else {
1971 // normal contents
1972 QString s;
1973 QChar c;
1974 while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){
1975 if ( textEditMode ) {
1976 // text edit mode: we handle all white space but ignore newlines
1977 c = parseChar( doc, length, pos, QStyleSheetItem::WhiteSpacePre );
1978 if ( c == QChar_linesep )
1979 break;
1980 } else {
1981 int l = pos;
1982 c = parseChar( doc, length, pos, curtag.wsm );
1983
1984 // in white space pre mode: treat any space as non breakable
1985 // and expand tabs to eight character wide columns.
1986 if ( curtag.wsm == QStyleSheetItem::WhiteSpacePre ) {
1987 if ( c == '\t' ) {
1988 c = ' ';
1989 while( (++tabExpansionColumn)%8 )
1990 s += c;
1991 }
1992 if ( c == QChar_linesep )
1993 tabExpansionColumn = 0;
1994 else
1995 tabExpansionColumn++;
1996
1997 }
1998 if ( c == ' ' || c == QChar_linesep ) {
1999 /* avoid overlong paragraphs by forcing a new
2000 paragraph after 4096 characters. This case can
2001 occur when loading undiscovered plain text
2002 documents in rich text mode. Instead of hanging
2003 forever, we do the trick.
2004 */
2005 if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && s.length() > 4096 ) do {
2006 if ( doc[l] == '\n' ) {
2007 hasNewPar = FALSE; // for a new paragraph ...
2008 NEWPAR;
2009 hasNewPar = FALSE; // ... and make it non-reusable
2010 c = '\n'; // make sure we break below
2011 break;
2012 }
2013 } while ( ++l < pos );
2014 }
2015 }
2016
2017 if ( c == '\n' )
2018 break; // break on newlines, pre delievers a QChar_linesep
2019
2020 bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
2021
2022 if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && c_isSpace && space )
2023 continue;
2024 if ( c == '\r' )
2025 continue;
2026 space = c_isSpace;
2027 s += c;
2028 }
2029 if ( !s.isEmpty() && curtag.style->displayMode() != QStyleSheetItem::DisplayNone ) {
2030 hasNewPar = FALSE;
2031 int index = QMAX( curpar->length(),1) - 1;
2032 curpar->append( s );
2033 if (curtag.wsm != QStyleSheetItem::WhiteSpaceNormal) {
2034 QTextString *str = curpar->string();
2035 for (uint i = index; i < index + s.length(); ++i)
2036 str->at(i).nobreak = TRUE;
2037 }
2038
2039 QTextFormat* f = formatCollection()->format( &curtag.format );
2040 curpar->setFormat( index, s.length(), f, FALSE ); // do not use collection because we have done that already
2041 f->ref += s.length() -1; // that what friends are for...
2042 if ( !curtag.anchorHref.isEmpty() ) {
2043 for ( int i = 0; i < int(s.length()); i++ )
2044 curpar->at(index + i)->setAnchor( QString::null, curtag.anchorHref );
2045 }
2046 if ( !anchorName.isEmpty() ) {
2047 for ( int i = 0; i < int(s.length()); i++ )
2048 curpar->at(index + i)->setAnchor( anchorName, curpar->at(index + i)->anchorHref() );
2049 anchorName = QString::null;
2050 }
2051 }
2052 }
2053 }
2054
2055 if ( hasNewPar && curpar != fParag && !cursor && stylesPar != curpar ) {
2056 // cleanup unused last paragraphs
2057 curpar = curpar->p;
2058 delete curpar->n;
2059 }
2060
2061 if ( !anchorName.isEmpty() ) {
2062 curpar->at(curpar->length() - 1)->setAnchor( anchorName, curpar->at( curpar->length() - 1 )->anchorHref() );
2063 anchorName = QString::null;
2064 }
2065
2066
2067 setRichTextMarginsInternal( styles, stylesPar );
2068
2069 if ( cursor ) {
2070 cursor->gotoPreviousLetter();
2071 cursor->remove();
2072 }
2073 delete vec;
2074}
2075
2076void QTextDocument::setRichTextMarginsInternal( QPtrList< QPtrVector<QStyleSheetItem> >& styles, QTextParagraph* stylesPar )
2077{
2078 // margin and line spacing calculation
2079 QPtrVector<QStyleSheetItem>* prevStyle = 0;
2080 QPtrVector<QStyleSheetItem>* curStyle = styles.first();
2081 QPtrVector<QStyleSheetItem>* nextStyle = styles.next();
2082 while ( stylesPar ) {
2083 if ( !curStyle ) {
2084 stylesPar = stylesPar->next();
2085 prevStyle = curStyle;
2086 curStyle = nextStyle;
2087 nextStyle = styles.next();
2088 continue;
2089 }
2090
2091 int i, mar;
2092 QStyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
2093 if ( mainStyle && mainStyle->displayMode() == QStyleSheetItem::DisplayListItem )
2094 stylesPar->setListItem( TRUE );
2095 int numLists = 0;
2096 for ( i = 0; i < (int)curStyle->size(); ++i ) {
2097 if ( (*curStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock
2098 && (*curStyle)[ i ]->listStyle() != QStyleSheetItem::ListStyleUndefined )
2099 numLists++;
2100 }
2101 stylesPar->ldepth = numLists;
2102 if ( stylesPar->next() && nextStyle ) {
2103 // also set the depth of the next paragraph, required for the margin calculation
2104 numLists = 0;
2105 for ( i = 0; i < (int)nextStyle->size(); ++i ) {
2106 if ( (*nextStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock
2107 && (*nextStyle)[ i ]->listStyle() != QStyleSheetItem::ListStyleUndefined )
2108 numLists++;
2109 }
2110 stylesPar->next()->ldepth = numLists;
2111 }
2112
2113 // do the top margin
2114 QStyleSheetItem* item = mainStyle;
2115 int m;
2116 if (stylesPar->utm > 0 ) {
2117 m = stylesPar->utm-1;
2118 stylesPar->utm = 0;
2119 } else {
2120 m = QMAX(0, item->margin( QStyleSheetItem::MarginTop ) );
2121 if ( stylesPar->ldepth )
2122 if ( item->displayMode() == QStyleSheetItem::DisplayListItem )
2123 m /= stylesPar->ldepth * stylesPar->ldepth;
2124 else
2125 m = 0;
2126 }
2127 for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
2128 item = (*curStyle)[ i ];
2129 if ( prevStyle && i < (int) prevStyle->size() &&
2130 ( item->displayMode() == QStyleSheetItem::DisplayBlock &&
2131 (*prevStyle)[ i ] == item ) )
2132 break;
2133 // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
2134 if ( item->listStyle() != QStyleSheetItem::ListStyleUndefined &&
2135 ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) )
2136 continue;
2137 mar = QMAX( 0, item->margin( QStyleSheetItem::MarginTop ) );
2138 m = QMAX( m, mar );
2139 }
2140 stylesPar->utm = m - stylesPar->topMargin();
2141
2142 // do the bottom margin
2143 item = mainStyle;
2144 if (stylesPar->ubm > 0 ) {
2145 m = stylesPar->ubm-1;
2146 stylesPar->ubm = 0;
2147 } else {
2148 m = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) );
2149 if ( stylesPar->ldepth )
2150 if ( item->displayMode() == QStyleSheetItem::DisplayListItem )
2151 m /= stylesPar->ldepth * stylesPar->ldepth;
2152 else
2153 m = 0;
2154 }
2155 for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
2156 item = (*curStyle)[ i ];
2157 if ( nextStyle && i < (int) nextStyle->size() &&
2158 ( item->displayMode() == QStyleSheetItem::DisplayBlock &&
2159 (*nextStyle)[ i ] == item ) )
2160 break;
2161 // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
2162 if ( item->listStyle() != QStyleSheetItem::ListStyleUndefined &&
2163 ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) )
2164 continue;
2165 mar = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) );
2166 m = QMAX( m, mar );
2167 }
2168 stylesPar->ubm = m - stylesPar->bottomMargin();
2169
2170 // do the left margin, simplyfied
2171 item = mainStyle;
2172 if (stylesPar->ulm > 0 ) {
2173 m = stylesPar->ulm-1;
2174 stylesPar->ulm = 0;
2175 } else {
2176 m = QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) );
2177 }
2178 for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
2179 item = (*curStyle)[ i ];
2180 m += QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) );
2181 }
2182 stylesPar->ulm = m - stylesPar->leftMargin();
2183
2184 // do the right margin, simplyfied
2185 item = mainStyle;
2186 if (stylesPar->urm > 0 ) {
2187 m = stylesPar->urm-1;
2188 stylesPar->urm = 0;
2189 } else {
2190 m = QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) );
2191 }
2192 for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
2193 item = (*curStyle)[ i ];
2194 m += QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) );
2195 }
2196 stylesPar->urm = m - stylesPar->rightMargin();
2197
2198 // do the first line margin, which really should be called text-indent
2199 item = mainStyle;
2200 if (stylesPar->uflm > 0 ) {
2201 m = stylesPar->uflm-1;
2202 stylesPar->uflm = 0;
2203 } else {
2204 m = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) );
2205 }
2206 for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) {
2207 item = (*curStyle)[ i ];
2208 mar = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) );
2209 m = QMAX( m, mar );
2210 }
2211 stylesPar->uflm =m - stylesPar->firstLineMargin();
2212
2213 // do the bogus line "spacing", which really is just an extra margin
2214 item = mainStyle;
2215 for ( i = (int)curStyle->size() - 1 ; i >= 0; --i ) {
2216 item = (*curStyle)[ i ];
2217 if ( item->lineSpacing() != QStyleSheetItem::Undefined ) {
2218 stylesPar->ulinespacing = item->lineSpacing();
2219 if ( formatCollection() &&
2220 stylesPar->ulinespacing < formatCollection()->defaultFormat()->height() )
2221 stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
2222 break;
2223 }
2224 }
2225
2226 stylesPar = stylesPar->next();
2227 prevStyle = curStyle;
2228 curStyle = nextStyle;
2229 nextStyle = styles.next();
2230 }
2231}
2232
2233void QTextDocument::setText( const QString &text, const QString &context )
2234{
2235 focusIndicator.parag = 0;
2236 selections.clear();
2237 if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
2238 txtFormat == Qt::RichText )
2239 setRichText( text, context );
2240 else
2241 setPlainText( text );
2242}
2243
2244QString QTextDocument::plainText() const
2245{
2246 QString buffer;
2247 QString s;
2248 QTextParagraph *p = fParag;
2249 while ( p ) {
2250 if ( !p->mightHaveCustomItems ) {
2251 const QTextString *ts = p->string(); // workaround VC++ and Borland
2252 s = ts->toString(); // with FALSE we don't fix spaces (nbsp)
2253 } else {
2254 for ( int i = 0; i < p->length() - 1; ++i ) {
2255#ifndef QT_NO_TEXTCUSTOMITEM
2256 if ( p->at( i )->isCustom() ) {
2257 if ( p->at( i )->customItem()->isNested() ) {
2258 s += "\n";
2259 QTextTable *t = (QTextTable*)p->at( i )->customItem();
2260 QPtrList<QTextTableCell> cells = t->tableCells();
2261 for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
2262 s += c->richText()->plainText() + "\n";
2263 s += "\n";
2264 }
2265 } else
2266#endif
2267 {
2268 s += p->at( i )->c;
2269 }
2270 }
2271 }
2272 s.remove( s.length() - 1, 1 );
2273 if ( p->next() )
2274 s += "\n";
2275 buffer += s;
2276 p = p->next();
2277 }
2278 return buffer;
2279}
2280
2281static QString align_to_string( int a )
2282{
2283 if ( a & Qt::AlignRight )
2284 return " align=\"right\"";
2285 if ( a & Qt::AlignHCenter )
2286 return " align=\"center\"";
2287 if ( a & Qt::AlignJustify )
2288 return " align=\"justify\"";
2289 return QString::null;
2290}
2291
2292static QString direction_to_string( int d )
2293{
2294 if ( d != QChar::DirON )
2295 return ( d == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" );
2296 return QString::null;
2297}
2298
2299static QString list_value_to_string( int v )
2300{
2301 if ( v != -1 )
2302 return " listvalue=\"" + QString::number( v ) + "\"";
2303 return QString::null;
2304}
2305
2306static QString list_style_to_string( int v )
2307{
2308 switch( v ) {
2309 case QStyleSheetItem::ListDecimal: return "\"1\"";
2310 case QStyleSheetItem::ListLowerAlpha: return "\"a\"";
2311 case QStyleSheetItem::ListUpperAlpha: return "\"A\"";
2312 case QStyleSheetItem::ListDisc: return "\"disc\"";
2313 case QStyleSheetItem::ListSquare: return "\"square\"";
2314 case QStyleSheetItem::ListCircle: return "\"circle\"";
2315 default:
2316 return QString::null;
2317 }
2318}
2319
2320static inline bool list_is_ordered( int v )
2321{
2322 return v == QStyleSheetItem::ListDecimal ||
2323 v == QStyleSheetItem::ListLowerAlpha ||
2324 v == QStyleSheetItem::ListUpperAlpha;
2325}
2326
2327
2328static QString margin_to_string( QStyleSheetItem* style, int t, int b, int l, int r, int fl )
2329{
2330 QString s;
2331 if ( l > 0 )
2332 s += QString(!!s?";":"") + "margin-left:" + QString::number(l+QMAX(0,style->margin(QStyleSheetItem::MarginLeft))) + "px";
2333 if ( r > 0 )
2334 s += QString(!!s?";":"") + "margin-right:" + QString::number(r+QMAX(0,style->margin(QStyleSheetItem::MarginRight))) + "px";
2335 if ( t > 0 )
2336 s += QString(!!s?";":"") + "margin-top:" + QString::number(t+QMAX(0,style->margin(QStyleSheetItem::MarginTop))) + "px";
2337 if ( b > 0 )
2338 s += QString(!!s?";":"") + "margin-bottom:" + QString::number(b+QMAX(0,style->margin(QStyleSheetItem::MarginBottom))) + "px";
2339 if ( fl > 0 )
2340 s += QString(!!s?";":"") + "text-indent:" + QString::number(fl+QMAX(0,style->margin(QStyleSheetItem::MarginFirstLine))) + "px";
2341 if ( !!s )
2342 return " style=\"" + s + "\"";
2343 return QString::null;
2344}
2345
2346QString QTextDocument::richText() const
2347{
2348 QString s = "";
2349 if ( !par ) {
2350 s += "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:" ;
2351 s += QString::number( formatCollection()->defaultFormat()->font().pointSize() );
2352 s += "pt;font-family:";
2353 s += formatCollection()->defaultFormat()->font().family();
2354 s +="\">";
2355 }
2356 QTextParagraph* p = fParag;
2357
2358 QStyleSheetItem* item_p = styleSheet()->item("p");
2359 QStyleSheetItem* item_div = styleSheet()->item("div");
2360 QStyleSheetItem* item_ul = styleSheet()->item("ul");
2361 QStyleSheetItem* item_ol = styleSheet()->item("ol");
2362 QStyleSheetItem* item_li = styleSheet()->item("li");
2363 if ( !item_p || !item_div || !item_ul || !item_ol || !item_li ) {
2364 qWarning( "QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)" );
2365 return QString::null;
2366 }
2367 int pastListDepth = 0;
2368 int listDepth = 0;
2369#if 0
2370 int futureListDepth = 0;
2371#endif
2372 QMemArray<int> listStyles(10);
2373
2374 while ( p ) {
2375 listDepth = p->listDepth();
2376 if ( listDepth < pastListDepth ) {
2377 for ( int i = pastListDepth; i > listDepth; i-- )
2378 s += list_is_ordered( listStyles[i] ) ? "</ol>" : "</ul>";
2379 s += '\n';
2380 } else if ( listDepth > pastListDepth ) {
2381 s += '\n';
2382 listStyles.resize( QMAX( (int)listStyles.size(), listDepth+1 ) );
2383 QString list_type;
2384 listStyles[listDepth] = p->listStyle();
2385 if ( !list_is_ordered( p->listStyle() ) || item_ol->listStyle() != p->listStyle() )
2386 list_type = " type=" + list_style_to_string( p->listStyle() );
2387 for ( int i = pastListDepth; i < listDepth; i++ ) {
2388 s += list_is_ordered( p->listStyle() ) ? "<ol" : "<ul" ;
2389 s += list_type + ">";
2390 }
2391 } else {
2392 s += '\n';
2393 }
2394
2395 QString ps = p->richText();
2396
2397#if 0
2398 // for the bottom margin we need to know whether we are at the end of a list
2399 futureListDepth = 0;
2400 if ( listDepth > 0 && p->next() )
2401 futureListDepth = p->next()->listDepth();
2402#endif
2403
2404 if ( richTextExportStart && richTextExportStart->paragraph() ==p &&
2405 richTextExportStart->index() == 0 )
2406 s += "<!--StartFragment-->";
2407
2408 if ( p->isListItem() ) {
2409 s += "<li";
2410 if ( p->listStyle() != listStyles[listDepth] )
2411 s += " type=" + list_style_to_string( p->listStyle() );
2412 s +=align_to_string( p->alignment() );
2413 s += margin_to_string( item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm );
2414 s += list_value_to_string( p->listValue() );
2415 s += direction_to_string( p->direction() );
2416 s +=">";
2417 s += ps;
2418 s += "</li>";
2419 } else if ( p->listDepth() ) {
2420 s += "<div";
2421 s += align_to_string( p->alignment() );
2422 s += margin_to_string( item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm );
2423 s +=direction_to_string( p->direction() );
2424 s += ">";
2425 s += ps;
2426 s += "</div>";
2427 } else {
2428 // normal paragraph item
2429 s += "<p";
2430 s += align_to_string( p->alignment() );
2431 s += margin_to_string( item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm );
2432 s +=direction_to_string( p->direction() );
2433 s += ">";
2434 s += ps;
2435 s += "</p>";
2436 }
2437 pastListDepth = listDepth;
2438 p = p->next();
2439 }
2440 while ( listDepth > 0 ) {
2441 s += list_is_ordered( listStyles[listDepth] ) ? "</ol>" : "</ul>";
2442 listDepth--;
2443 }
2444
2445 if ( !par )
2446 s += "\n</body></html>\n";
2447
2448 return s;
2449}
2450
2451QString QTextDocument::text() const
2452{
2453 if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
2454 return richText();
2455 return plainText();
2456}
2457
2458QString QTextDocument::text( int parag ) const
2459{
2460 QTextParagraph *p = paragAt( parag );
2461 if ( !p )
2462 return QString::null;
2463
2464 if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
2465 return p->richText();
2466 else
2467 return p->string()->toString();
2468}
2469
2470void QTextDocument::invalidate()
2471{
2472 QTextParagraph *s = fParag;
2473 while ( s ) {
2474 s->invalidate( 0 );
2475 s = s->next();
2476 }
2477}
2478
2479void QTextDocument::selectionStart( int id, int &paragId, int &index )
2480{
2481 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2482 if ( it == selections.end() )
2483 return;
2484 QTextDocumentSelection &sel = *it;
2485 paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
2486 index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
2487}
2488
2489QTextCursor QTextDocument::selectionStartCursor( int id)
2490{
2491 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2492 if ( it == selections.end() )
2493 return QTextCursor( this );
2494 QTextDocumentSelection &sel = *it;
2495 if ( sel.swapped )
2496 return sel.endCursor;
2497 return sel.startCursor;
2498}
2499
2500QTextCursor QTextDocument::selectionEndCursor( int id)
2501{
2502 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2503 if ( it == selections.end() )
2504 return QTextCursor( this );
2505 QTextDocumentSelection &sel = *it;
2506 if ( !sel.swapped )
2507 return sel.endCursor;
2508 return sel.startCursor;
2509}
2510
2511void QTextDocument::selectionEnd( int id, int &paragId, int &index )
2512{
2513 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2514 if ( it == selections.end() )
2515 return;
2516 QTextDocumentSelection &sel = *it;
2517 paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
2518 index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
2519}
2520
2521void QTextDocument::addSelection( int id )
2522{
2523 nSelections = QMAX( nSelections, id + 1 );
2524}
2525
2526static void setSelectionEndHelper( int id, QTextDocumentSelection &sel, QTextCursor &start, QTextCursor &end )
2527{
2528 QTextCursor c1 = start;
2529 QTextCursor c2 = end;
2530 if ( sel.swapped ) {
2531 c1 = end;
2532 c2 = start;
2533 }
2534
2535 c1.paragraph()->removeSelection( id );
2536 c2.paragraph()->removeSelection( id );
2537 if ( c1.paragraph() != c2.paragraph() ) {
2538 c1.paragraph()->setSelection( id, c1.index(), c1.paragraph()->length() - 1 );
2539 c2.paragraph()->setSelection( id, 0, c2.index() );
2540 } else {
2541 c1.paragraph()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
2542 }
2543
2544 sel.startCursor = start;
2545 sel.endCursor = end;
2546 if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() )
2547 sel.swapped = sel.startCursor.index() > sel.endCursor.index();
2548}
2549
2550bool QTextDocument::setSelectionEnd( int id, const QTextCursor &cursor )
2551{
2552 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2553 if ( it == selections.end() )
2554 return FALSE;
2555 QTextDocumentSelection &sel = *it;
2556
2557 QTextCursor start = sel.startCursor;
2558 QTextCursor end = cursor;
2559
2560 if ( start == end ) {
2561 removeSelection( id );
2562 setSelectionStart( id, cursor );
2563 return TRUE;
2564 }
2565
2566 if ( sel.endCursor.paragraph() == end.paragraph() ) {
2567 setSelectionEndHelper( id, sel, start, end );
2568 return TRUE;
2569 }
2570
2571 bool inSelection = FALSE;
2572 QTextCursor c( this );
2573 QTextCursor tmp = sel.startCursor;
2574 if ( sel.swapped )
2575 tmp = sel.endCursor;
2576 tmp.restoreState();
2577 QTextCursor tmp2 = cursor;
2578 tmp2.restoreState();
2579 c.setParagraph( tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph() );
2580 bool hadStart = FALSE;
2581 bool hadEnd = FALSE;
2582 bool hadStartParag = FALSE;
2583 bool hadEndParag = FALSE;
2584 bool hadOldStart = FALSE;
2585 bool hadOldEnd = FALSE;
2586 bool leftSelection = FALSE;
2587 sel.swapped = FALSE;
2588 for ( ;; ) {
2589 if ( c == start )
2590 hadStart = TRUE;
2591 if ( c == end )
2592 hadEnd = TRUE;
2593 if ( c.paragraph() == start.paragraph() )
2594 hadStartParag = TRUE;
2595 if ( c.paragraph() == end.paragraph() )
2596 hadEndParag = TRUE;
2597 if ( c == sel.startCursor )
2598 hadOldStart = TRUE;
2599 if ( c == sel.endCursor )
2600 hadOldEnd = TRUE;
2601
2602 if ( !sel.swapped &&
2603 ( hadEnd && !hadStart ||
2604 hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index() ) )
2605 sel.swapped = TRUE;
2606
2607 if ( c == end && hadStartParag ||
2608 c == start && hadEndParag ) {
2609 QTextCursor tmp = c;
2610 tmp.restoreState();
2611 if ( tmp.paragraph() != c.paragraph() ) {
2612 int sstart = tmp.paragraph()->selectionStart( id );
2613 tmp.paragraph()->removeSelection( id );
2614 tmp.paragraph()->setSelection( id, sstart, tmp.index() );
2615 }
2616 }
2617
2618 if ( inSelection &&
2619 ( c == end && hadStart || c == start && hadEnd ) )
2620 leftSelection = TRUE;
2621 else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
2622 inSelection = TRUE;
2623
2624 bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection( id ) && c.atParagEnd();
2625 c.paragraph()->removeSelection( id );
2626 if ( inSelection ) {
2627 if ( c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph() ) {
2628 c.paragraph()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) );
2629 } else if ( c.paragraph() == start.paragraph() && !hadEndParag ) {
2630 c.paragraph()->setSelection( id, start.index(), c.paragraph()->length() - 1 );
2631 } else if ( c.paragraph() == end.paragraph() && !hadStartParag ) {
2632 c.paragraph()->setSelection( id, end.index(), c.paragraph()->length() - 1 );
2633 } else if ( c.paragraph() == end.paragraph() && hadEndParag ) {
2634 c.paragraph()->setSelection( id, 0, end.index() );
2635 } else if ( c.paragraph() == start.paragraph() && hadStartParag ) {
2636 c.paragraph()->setSelection( id, 0, start.index() );
2637 } else {
2638 c.paragraph()->setSelection( id, 0, c.paragraph()->length() - 1 );
2639 }
2640 }
2641
2642 if ( leftSelection )
2643 inSelection = FALSE;
2644
2645 if ( noSelectionAnymore )
2646 break;
2647 // *ugle*hack optimization
2648 QTextParagraph *p = c.paragraph();
2649 if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph() ) {
2650 c.gotoNextLetter();
2651 if ( p == lastParagraph() && c.atParagEnd() )
2652 break;
2653 } else {
2654 if ( p->document()->parent() )
2655 do {
2656 c.gotoNextLetter();
2657 } while ( c.paragraph() == p );
2658 else
2659 c.setParagraph( p->next() );
2660 }
2661 }
2662
2663 if ( !sel.swapped )
2664 sel.startCursor.paragraph()->setSelection( id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1 );
2665
2666 sel.startCursor = start;
2667 sel.endCursor = end;
2668 if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() )
2669 sel.swapped = sel.startCursor.index() > sel.endCursor.index();
2670
2671 setSelectionEndHelper( id, sel, start, end );
2672
2673 return TRUE;
2674}
2675
2676void QTextDocument::selectAll( int id )
2677{
2678 removeSelection( id );
2679
2680 QTextDocumentSelection sel;
2681 sel.swapped = FALSE;
2682 QTextCursor c( this );
2683
2684 c.setParagraph( fParag );
2685 c.setIndex( 0 );
2686 sel.startCursor = c;
2687
2688 c.setParagraph( lParag );
2689 c.setIndex( lParag->length() - 1 );
2690 sel.endCursor = c;
2691
2692 selections.insert( id, sel );
2693
2694 QTextParagraph *p = fParag;
2695 while ( p ) {
2696 p->setSelection( id, 0, p->length() - 1 );
2697 p = p->next();
2698 }
2699
2700 for ( QTextDocument *d = childList.first(); d; d = childList.next() )
2701 d->selectAll( id );
2702}
2703
2704bool QTextDocument::removeSelection( int id )
2705{
2706 if ( !selections.contains( id ) )
2707 return FALSE;
2708
2709 QTextDocumentSelection &sel = selections[ id ];
2710
2711 QTextCursor start = sel.swapped ? sel.endCursor : sel.startCursor;
2712 QTextCursor end = sel.swapped ? sel.startCursor : sel.endCursor;
2713 QTextParagraph* p = 0;
2714 while ( start != end ) {
2715 if ( p != start.paragraph() ) {
2716 p = start.paragraph();
2717 p->removeSelection( id );
2718 //### avoid endless loop by all means necessary, did somebody mention refactoring?
2719 if ( !parent() && p == lParag )
2720 break;
2721 }
2722 start.gotoNextLetter();
2723 }
2724 p = start.paragraph();
2725 p->removeSelection( id );
2726 selections.remove( id );
2727 return TRUE;
2728}
2729
2730QString QTextDocument::selectedText( int id, bool asRichText ) const
2731{
2732 QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
2733 if ( it == selections.end() )
2734 return QString::null;
2735
2736 QTextDocumentSelection sel = *it;
2737
2738
2739 QTextCursor c1 = sel.startCursor;
2740 QTextCursor c2 = sel.endCursor;
2741 if ( sel.swapped ) {
2742 c2 = sel.startCursor;
2743 c1 = sel.endCursor;
2744 }
2745
2746 /* 3.0.3 improvement: Make it possible to get a reasonable
2747 selection inside a table. This approach is very conservative:
2748 make sure that both cursors have the same depth level and point
2749 to paragraphs within the same text document.
2750
2751 Meaning if you select text in two table cells, you will get the
2752 entire table. This is still far better than the 3.0.2, where
2753 you always got the entire table.
2754
2755 ### Fix this properly when refactoring
2756 */
2757 while ( c2.nestedDepth() > c1.nestedDepth() )
2758 c2.oneUp();
2759 while ( c1.nestedDepth() > c2.nestedDepth() )
2760 c1.oneUp();
2761 while ( c1.nestedDepth() && c2.nestedDepth() &&
2762 c1.paragraph()->document() != c2.paragraph()->document() ) {
2763 c1.oneUp();
2764 c2.oneUp();
2765 }
2766 // do not trust sel_swapped with tables. Fix this properly when refactoring as well
2767 if ( c1.paragraph()->paragId() > c2.paragraph()->paragId() ||
2768 (c1.paragraph() == c2.paragraph() && c1.index() > c2.index() ) ) {
2769 QTextCursor tmp = c1;
2770 c2 = c1;
2771 c1 = tmp;
2772 }
2773
2774 // end selection 3.0.3 improvement
2775
2776 if ( asRichText && !parent() ) {
2777 richTextExportStart = &c1;
2778 richTextExportEnd = &c2;
2779
2780 QString sel = richText();
2781 int from = sel.find( "<!--StartFragment-->" );
2782 if ( from >= 0 ) {
2783 from += 20;
2784 // find the previous span and move it into the start fragment before we clip it
2785 QString prevspan;
2786 int pspan = sel.findRev( "<span", from-21 );
2787 if ( pspan > sel.findRev( "</span", from-21 ) ) {
2788 int spanend = sel.find( '>', pspan );
2789 prevspan = sel.mid( pspan, spanend - pspan + 1 );
2790 }
2791 int to = sel.findRev( "<!--EndFragment-->" );
2792 if ( from <= to )
2793 sel = "<!--StartFragment-->" + prevspan + sel.mid( from, to - from );
2794 }
2795 richTextExportStart = richTextExportEnd = 0;
2796 return sel;
2797 }
2798
2799 QString s;
2800 if ( c1.paragraph() == c2.paragraph() ) {
2801 QTextParagraph *p = c1.paragraph();
2802 int end = c2.index();
2803 if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
2804 ++end;
2805 if ( !p->mightHaveCustomItems ) {
2806 s += p->string()->toString().mid( c1.index(), end - c1.index() );
2807 } else {
2808 for ( int i = c1.index(); i < end; ++i ) {
2809#ifndef QT_NO_TEXTCUSTOMITEM
2810 if ( p->at( i )->isCustom() ) {
2811 if ( p->at( i )->customItem()->isNested() ) {
2812 s += "\n";
2813 QTextTable *t = (QTextTable*)p->at( i )->customItem();
2814 QPtrList<QTextTableCell> cells = t->tableCells();
2815 for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
2816 s += c->richText()->plainText() + "\n";
2817 s += "\n";
2818 }
2819 } else
2820#endif
2821 {
2822 s += p->at( i )->c;
2823 }
2824 }
2825 }
2826 } else {
2827 QTextParagraph *p = c1.paragraph();
2828 int start = c1.index();
2829 while ( p ) {
2830 int end = p == c2.paragraph() ? c2.index() : p->length() - 1;
2831 if ( p == c2.paragraph() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
2832 ++end;
2833 if ( !p->mightHaveCustomItems ) {
2834 s += p->string()->toString().mid( start, end - start );
2835 if ( p != c2.paragraph() )
2836 s += "\n";
2837 } else {
2838 for ( int i = start; i < end; ++i ) {
2839#ifndef QT_NO_TEXTCUSTOMITEM
2840 if ( p->at( i )->isCustom() ) {
2841 if ( p->at( i )->customItem()->isNested() ) {
2842 s += "\n";
2843 QTextTable *t = (QTextTable*)p->at( i )->customItem();
2844 QPtrList<QTextTableCell> cells = t->tableCells();
2845 for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
2846 s += c->richText()->plainText() + "\n";
2847 s += "\n";
2848 }
2849 } else
2850#endif
2851 {
2852 s += p->at( i )->c;
2853 }
2854 }
2855 }
2856 start = 0;
2857 if ( p == c2.paragraph() )
2858 break;
2859 p = p->next();
2860 }
2861 }
2862 // ### workaround for plain text export until we get proper
2863 // mime types: turn unicode line seperators into the more
2864 // widely understood \n. Makes copy and pasting code snipplets
2865 // from within Assistent possible
2866 QChar* uc = (QChar*) s.unicode();
2867 for ( uint ii = 0; ii < s.length(); ii++ )
2868 if ( uc[(int)ii] == QChar_linesep )
2869 uc[(int)ii] = QChar('\n');
2870 return s;
2871}
2872
2873void QTextDocument::setFormat( int id, QTextFormat *f, int flags )
2874{
2875 QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
2876 if ( it == selections.end() )
2877 return;
2878
2879 QTextDocumentSelection sel = *it;
2880
2881 QTextCursor c1 = sel.startCursor;
2882 QTextCursor c2 = sel.endCursor;
2883 if ( sel.swapped ) {
2884 c2 = sel.startCursor;
2885 c1 = sel.endCursor;
2886 }
2887
2888 c2.restoreState();
2889 c1.restoreState();
2890
2891 if ( c1.paragraph() == c2.paragraph() ) {
2892 c1.paragraph()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
2893 return;
2894 }
2895
2896 c1.paragraph()->setFormat( c1.index(), c1.paragraph()->length() - c1.index(), f, TRUE, flags );
2897 QTextParagraph *p = c1.paragraph()->next();
2898 while ( p && p != c2.paragraph() ) {
2899 p->setFormat( 0, p->length(), f, TRUE, flags );
2900 p = p->next();
2901 }
2902 c2.paragraph()->setFormat( 0, c2.index(), f, TRUE, flags );
2903}
2904
2905void QTextDocument::removeSelectedText( int id, QTextCursor *cursor )
2906{
2907 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2908 if ( it == selections.end() )
2909 return;
2910
2911 QTextDocumentSelection sel = *it;
2912 QTextCursor c1 = sel.startCursor;
2913 QTextCursor c2 = sel.endCursor;
2914 if ( sel.swapped ) {
2915 c2 = sel.startCursor;
2916 c1 = sel.endCursor;
2917 }
2918
2919 // ### no support for editing tables yet
2920 if ( c1.nestedDepth() || c2.nestedDepth() )
2921 return;
2922
2923 c2.restoreState();
2924 c1.restoreState();
2925
2926 *cursor = c1;
2927 removeSelection( id );
2928
2929 if ( c1.paragraph() == c2.paragraph() ) {
2930 c1.paragraph()->remove( c1.index(), c2.index() - c1.index() );
2931 return;
2932 }
2933
2934 if ( c1.paragraph() == fParag && c1.index() == 0 &&
2935 c2.paragraph() == lParag && c2.index() == lParag->length() - 1 )
2936 cursor->setValid( FALSE );
2937
2938 bool didGoLeft = FALSE;
2939 if ( c1.index() == 0 && c1.paragraph() != fParag ) {
2940 cursor->gotoPreviousLetter();
2941 didGoLeft = cursor->isValid();
2942 }
2943
2944 c1.paragraph()->remove( c1.index(), c1.paragraph()->length() - 1 - c1.index() );
2945 QTextParagraph *p = c1.paragraph()->next();
2946 int dy = 0;
2947 QTextParagraph *tmp;
2948 while ( p && p != c2.paragraph() ) {
2949 tmp = p->next();
2950 dy -= p->rect().height();
2951 delete p;
2952 p = tmp;
2953 }
2954 c2.paragraph()->remove( 0, c2.index() );
2955 while ( p ) {
2956 p->move( dy );
2957 p->invalidate( 0 );
2958 p->setEndState( -1 );
2959 p = p->next();
2960 }
2961
2962
2963 c1.paragraph()->join( c2.paragraph() );
2964
2965 if ( didGoLeft )
2966 cursor->gotoNextLetter();
2967}
2968
2969void QTextDocument::indentSelection( int id )
2970{
2971 QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
2972 if ( it == selections.end() )
2973 return;
2974
2975 QTextDocumentSelection sel = *it;
2976 QTextParagraph *startParag = sel.startCursor.paragraph();
2977 QTextParagraph *endParag = sel.endCursor.paragraph();
2978 if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) {
2979 endParag = sel.startCursor.paragraph();
2980 startParag = sel.endCursor.paragraph();
2981 }
2982
2983 QTextParagraph *p = startParag;
2984 while ( p && p != endParag ) {
2985 p->indent();
2986 p = p->next();
2987 }
2988}
2989
2990void QTextDocument::addCommand( QTextCommand *cmd )
2991{
2992 commandHistory->addCommand( cmd );
2993}
2994
2995QTextCursor *QTextDocument::undo( QTextCursor *c )
2996{
2997 return commandHistory->undo( c );
2998}
2999
3000QTextCursor *QTextDocument::redo( QTextCursor *c )
3001{
3002 return commandHistory->redo( c );
3003}
3004
3005bool QTextDocument::find( QTextCursor& cursor, const QString &expr, bool cs, bool wo, bool forward )
3006{
3007 removeSelection( Standard );
3008 QTextParagraph *p = 0;
3009 if ( expr.isEmpty() )
3010 return FALSE;
3011 for (;;) {
3012 if ( p != cursor.paragraph() ) {
3013 p = cursor.paragraph();
3014 QString s = cursor.paragraph()->string()->toString();
3015 int start = cursor.index();
3016 for ( ;; ) {
3017 int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
3018 int end = res + expr.length();
3019 if ( res == -1 || ( !forward && start <= res ) )
3020 break;
3021 if ( !wo || ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
3022 ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) ) ) {
3023 removeSelection( Standard );
3024 cursor.setIndex( forward ? end : res );
3025 setSelectionStart( Standard, cursor );
3026 cursor.setIndex( forward ? res : end );
3027 setSelectionEnd( Standard, cursor );
3028 if ( !forward )
3029 cursor.setIndex( res );
3030 return TRUE;
3031 }
3032 start = res + (forward ? 1 : -1);
3033 }
3034 }
3035 if ( forward ) {
3036 if ( cursor.paragraph() == lastParagraph() && cursor.atParagEnd() )
3037 break;
3038 cursor.gotoNextLetter();
3039 } else {
3040 if ( cursor.paragraph() == firstParagraph() && cursor.atParagStart() )
3041 break;
3042 cursor.gotoPreviousLetter();
3043 }
3044 }
3045 return FALSE;
3046}
3047
3048void QTextDocument::setTextFormat( Qt::TextFormat f )
3049{
3050 txtFormat = f;
3051 if ( fParag == lParag && fParag->length() <= 1 )
3052 fParag->rtext = ( f == Qt::RichText );
3053}
3054
3055Qt::TextFormat QTextDocument::textFormat() const
3056{
3057 return txtFormat;
3058}
3059
3060bool QTextDocument::inSelection( int selId, const QPoint &pos ) const
3061{
3062 QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( selId );
3063 if ( it == selections.end() )
3064 return FALSE;
3065
3066 QTextDocumentSelection sel = *it;
3067 QTextParagraph *startParag = sel.startCursor.paragraph();
3068 QTextParagraph *endParag = sel.endCursor.paragraph();
3069 if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() &&
3070 sel.startCursor.paragraph()->selectionStart( selId ) == sel.endCursor.paragraph()->selectionEnd( selId ) )
3071 return FALSE;
3072 if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) {
3073 endParag = sel.startCursor.paragraph();
3074 startParag = sel.endCursor.paragraph();
3075 }
3076
3077 QTextParagraph *p = startParag;
3078 while ( p ) {
3079 if ( p->rect().contains( pos ) ) {
3080 bool inSel = FALSE;
3081 int selStart = p->selectionStart( selId );
3082 int selEnd = p->selectionEnd( selId );
3083 int y = 0;
3084 int h = 0;
3085 for ( int i = 0; i < p->length(); ++i ) {
3086 if ( i == selStart )
3087 inSel = TRUE;
3088 if ( i == selEnd )
3089 break;
3090 if ( p->at( i )->lineStart ) {
3091 y = (*p->lineStarts.find( i ))->y;
3092 h = (*p->lineStarts.find( i ))->h;
3093 }
3094 if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
3095 if ( inSel && pos.x() >= p->at( i )->x &&
3096 pos.x() <= p->at( i )->x + p->at( i )->format()->width( p->at( i )->c ) )
3097 return TRUE;
3098 }
3099 }
3100 }
3101 if ( pos.y() < p->rect().y() )
3102 break;
3103 if ( p == endParag )
3104 break;
3105 p = p->next();
3106 }
3107
3108 return FALSE;
3109}
3110
3111void QTextDocument::doLayout( QPainter *p, int w )
3112{
3113 minw = wused = 0;
3114 if ( !is_printer( p ) )
3115 p = 0;
3116 withoutDoubleBuffer = ( p != 0 );
3117 QPainter * oldPainter = QTextFormat::painter();
3118 QTextFormat::setPainter( p );
3119 flow_->setWidth( w );
3120 cw = w;
3121 vw = w;
3122 QTextParagraph *parag = fParag;
3123 while ( parag ) {
3124 parag->invalidate( 0 );
3125 if ( p )
3126 parag->adjustToPainter( p );
3127 parag->format();
3128 parag = parag->next();
3129 }
3130 QTextFormat::setPainter( oldPainter );
3131}
3132
3133QPixmap *QTextDocument::bufferPixmap( const QSize &s )
3134{
3135 if ( !buf_pixmap )
3136 buf_pixmap = new QPixmap( s.expandedTo( QSize(1,1) ) );
3137 else if ( buf_pixmap->size() != s )
3138 buf_pixmap->resize( s.expandedTo( buf_pixmap->size() ) );
3139 return buf_pixmap;
3140}
3141
3142void QTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
3143{
3144 if ( !firstParagraph() )
3145 return;
3146
3147 if ( paper ) {
3148 p->setBrushOrigin( -int( p->translationX() ),
3149 -int( p->translationY() ) );
3150
3151 p->fillRect( rect, *paper );
3152 }
3153
3154 QPainter * oldPainter = QTextFormat::painter();
3155 QTextFormat::setPainter( p );
3156
3157 if ( formatCollection()->defaultFormat()->color() != cg.text() )
3158 setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() );
3159
3160 QTextParagraph *parag = firstParagraph();
3161 while ( parag ) {
3162 if ( !parag->isValid() )
3163 parag->format();
3164 int y = parag->rect().y();
3165 QRect pr( parag->rect() );
3166 pr.setX( 0 );
3167 pr.setWidth( QWIDGETSIZE_MAX );
3168 if ( !rect.isNull() && !rect.intersects( pr ) ) {
3169 parag = parag->next();
3170 continue;
3171 }
3172 p->translate( 0, y );
3173 if ( rect.isValid() )
3174 parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
3175 else
3176 parag->paint( *p, cg, 0, FALSE );
3177 p->translate( 0, -y );
3178 parag = parag->next();
3179 if ( !flow()->isEmpty() )
3180 flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE );
3181 }
3182 QTextFormat::setPainter(oldPainter);
3183}
3184
3185void QTextDocument::drawParagraph( QPainter *p, QTextParagraph *parag, int cx, int cy, int cw, int ch,
3186 QPixmap *&doubleBuffer, const QColorGroup &cg,
3187 bool drawCursor, QTextCursor *cursor, bool resetChanged )
3188{
3189 QPainter *painter = 0;
3190 if ( resetChanged )
3191 parag->setChanged( FALSE );
3192 QRect ir( parag->rect() );
3193#ifndef QT_NO_TEXTCUSTOMITEM
3194 if (!parag->tableCell())
3195#endif
3196 ir.setWidth(width());
3197
3198 bool uDoubleBuffer = useDoubleBuffer( parag, p );
3199
3200 if ( uDoubleBuffer ) {
3201 painter = new QPainter;
3202 if ( cx >= 0 && cy >= 0 )
3203 ir = ir.intersect( QRect( cx, cy, cw, ch ) );
3204 if ( !doubleBuffer ||
3205 ir.width() > doubleBuffer->width() ||
3206 ir.height() > doubleBuffer->height() ) {
3207 doubleBuffer = bufferPixmap( ir.size() );
3208 painter->begin( doubleBuffer );
3209 } else {
3210 painter->begin( doubleBuffer );
3211 }
3212 } else {
3213 painter = p;
3214 painter->translate( ir.x(), ir.y() );
3215 }
3216
3217 painter->setBrushOrigin( -ir.x(), -ir.y() );
3218
3219 if ( uDoubleBuffer || is_printer( painter ) )
3220 painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), parag->backgroundBrush( cg ) );
3221 else if ( cursor && cursor->paragraph() == parag )
3222 painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
3223 parag->backgroundBrush( cg ) );
3224
3225 painter->translate( -( ir.x() - parag->rect().x() ),
3226 -( ir.y() - parag->rect().y() ) );
3227 parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch );
3228
3229 if ( uDoubleBuffer ) {
3230 delete painter;
3231 painter = 0;
3232 p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
3233 } else {
3234 painter->translate( -ir.x(), -ir.y() );
3235 }
3236
3237 parag->document()->nextDoubleBuffered = FALSE;
3238}
3239
3240QTextParagraph *QTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
3241 bool onlyChanged, bool drawCursor, QTextCursor *cursor, bool resetChanged )
3242{
3243 if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
3244 withoutDoubleBuffer = TRUE;
3245 QRect r;
3246 draw( p, r, cg );
3247 return 0;
3248 }
3249 withoutDoubleBuffer = FALSE;
3250
3251 if ( !firstParagraph() )
3252 return 0;
3253
3254 QPainter * oldPainter = QTextFormat::painter();
3255 QTextFormat::setPainter( p );
3256 if ( formatCollection()->defaultFormat()->color() != cg.text() )
3257 setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() );
3258
3259 if ( cx < 0 && cy < 0 ) {
3260 cx = 0;
3261 cy = 0;
3262 cw = width();
3263 ch = height();
3264 }
3265
3266 QTextParagraph *lastFormatted = 0;
3267 QTextParagraph *parag = firstParagraph();
3268
3269 QPixmap *doubleBuffer = 0;
3270
3271 while ( parag ) {
3272 lastFormatted = parag;
3273 if ( !parag->isValid() )
3274 parag->format();
3275
3276 QRect pr = parag->rect();
3277 pr.setWidth( parag->document()->width() );
3278 if ( pr.y() > cy + ch )
3279 goto floating;
3280 QRect clipr( cx, cy, cw, ch );
3281 if ( !pr.intersects( clipr ) || ( onlyChanged && !parag->hasChanged() ) ) {
3282 pr.setWidth( parag->document()->width() );
3283 parag = parag->next();
3284 continue;
3285 }
3286
3287 drawParagraph( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
3288 parag = parag->next();
3289 }
3290
3291 parag = lastParagraph();
3292
3293 floating:
3294 if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
3295 if ( !parag->document()->parent() ) {
3296 QRect fillRect = QRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
3297 parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) );
3298 if ( QRect( cx, cy, cw, ch ).intersects( fillRect ) )
3299 p->fillRect( fillRect, cg.brush( QColorGroup::Base ) );
3300 }
3301 if ( !flow()->isEmpty() ) {
3302 QRect cr( cx, cy, cw, ch );
3303 flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
3304 }
3305 }
3306
3307 if ( buf_pixmap && buf_pixmap->height() > 300 ) {
3308 delete buf_pixmap;
3309 buf_pixmap = 0;
3310 }
3311
3312 QTextFormat::setPainter(oldPainter);
3313 return lastFormatted;
3314}
3315
3316/*
3317 #### this function only sets the default font size in the format collection
3318 */
3319void QTextDocument::setDefaultFormat( const QFont &font, const QColor &color )
3320{
3321 bool reformat = font != fCollection->defaultFormat()->font();
3322 for ( QTextDocument *d = childList.first(); d; d = childList.next() )
3323 d->setDefaultFormat( font, color );
3324 fCollection->updateDefaultFormat( font, color, sheet_ );
3325
3326 if ( !reformat )
3327 return;
3328 tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
3329
3330 // invalidate paragraphs and custom items
3331 QTextParagraph *p = fParag;
3332 while ( p ) {
3333 p->invalidate( 0 );
3334#ifndef QT_NO_TEXTCUSTOMITEM
3335 for ( int i = 0; i < p->length() - 1; ++i )
3336 if ( p->at( i )->isCustom() )
3337 p->at( i )->customItem()->invalidate();
3338#endif
3339 p = p->next();
3340 }
3341}
3342
3343#ifndef QT_NO_TEXTCUSTOMITEM
3344void QTextDocument::registerCustomItem( QTextCustomItem *i, QTextParagraph *p )
3345{
3346 if ( i && i->placement() != QTextCustomItem::PlaceInline ) {
3347 flow_->registerFloatingItem( i );
3348 p->registerFloatingItem( i );
3349 i->setParagraph( p );
3350 }
3351 p->mightHaveCustomItems = mightHaveCustomItems = TRUE;
3352}
3353
3354void QTextDocument::unregisterCustomItem( QTextCustomItem *i, QTextParagraph *p )
3355{
3356 p->unregisterFloatingItem( i );
3357 i->setParagraph( 0 );
3358 flow_->unregisterFloatingItem( i );
3359}
3360#endif
3361
3362bool QTextDocument::hasFocusParagraph() const
3363{
3364 return !!focusIndicator.parag;
3365}
3366
3367QString QTextDocument::focusHref() const
3368{
3369 return focusIndicator.href;
3370}
3371
3372QString QTextDocument::focusName() const
3373{
3374 return focusIndicator.name;
3375}
3376
3377bool QTextDocument::focusNextPrevChild( bool next )
3378{
3379 if ( !focusIndicator.parag ) {
3380 if ( next ) {
3381 focusIndicator.parag = fParag;
3382 focusIndicator.start = 0;
3383 focusIndicator.len = 0;
3384 } else {
3385 focusIndicator.parag = lParag;
3386 focusIndicator.start = lParag->length();
3387 focusIndicator.len = 0;
3388 }
3389 } else {
3390 focusIndicator.parag->setChanged( TRUE );
3391 }
3392 focusIndicator.href = QString::null;
3393 focusIndicator.name = QString::null;
3394
3395 if ( next ) {
3396 QTextParagraph *p = focusIndicator.parag;
3397 int index = focusIndicator.start + focusIndicator.len;
3398 while ( p ) {
3399 for ( int i = index; i < p->length(); ++i ) {
3400 if ( p->at( i )->isAnchor() ) {
3401 p->setChanged( TRUE );
3402 focusIndicator.parag = p;
3403 focusIndicator.start = i;
3404 focusIndicator.len = 0;
3405 focusIndicator.href = p->at( i )->anchorHref();
3406 focusIndicator.name = p->at( i )->anchorName();
3407 while ( i < p->length() ) {
3408 if ( !p->at( i )->isAnchor() )
3409 return TRUE;
3410 focusIndicator.len++;
3411 i++;
3412 }
3413#ifndef QT_NO_TEXTCUSTOMITEM
3414 } else if ( p->at( i )->isCustom() ) {
3415 if ( p->at( i )->customItem()->isNested() ) {
3416 QTextTable *t = (QTextTable*)p->at( i )->customItem();
3417 QPtrList<QTextTableCell> cells = t->tableCells();
3418 // first try to continue
3419 QTextTableCell *c;
3420 bool resetCells = TRUE;
3421 for ( c = cells.first(); c; c = cells.next() ) {
3422 if ( c->richText()->hasFocusParagraph() ) {
3423 if ( c->richText()->focusNextPrevChild( next ) ) {
3424 p->setChanged( TRUE );
3425 focusIndicator.parag = p;
3426 focusIndicator.start = i;
3427 focusIndicator.len = 0;
3428 focusIndicator.href = c->richText()->focusHref();
3429 focusIndicator.name = c->richText()->focusName();
3430 return TRUE;
3431 } else {
3432 resetCells = FALSE;
3433 c = cells.next();
3434 break;
3435 }
3436 }
3437 }
3438 // now really try
3439 if ( resetCells )
3440 c = cells.first();
3441 for ( ; c; c = cells.next() ) {
3442 if ( c->richText()->focusNextPrevChild( next ) ) {
3443 p->setChanged( TRUE );
3444 focusIndicator.parag = p;
3445 focusIndicator.start = i;
3446 focusIndicator.len = 0;
3447 focusIndicator.href = c->richText()->focusHref();
3448 focusIndicator.name = c->richText()->focusName();
3449 return TRUE;
3450 }
3451 }
3452 }
3453#endif
3454 }
3455 }
3456 index = 0;
3457 p = p->next();
3458 }
3459 } else {
3460 QTextParagraph *p = focusIndicator.parag;
3461 int index = focusIndicator.start - 1;
3462 if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
3463 index++;
3464 while ( p ) {
3465 for ( int i = index; i >= 0; --i ) {
3466 if ( p->at( i )->isAnchor() ) {
3467 p->setChanged( TRUE );
3468 focusIndicator.parag = p;
3469 focusIndicator.start = i;
3470 focusIndicator.len = 0;
3471 focusIndicator.href = p->at( i )->anchorHref();
3472 focusIndicator.name = p->at( i )->anchorName();
3473 while ( i >= -1 ) {
3474 if ( i < 0 || !p->at( i )->isAnchor() ) {
3475 focusIndicator.start++;
3476 return TRUE;
3477 }
3478 if ( i < 0 )
3479 break;
3480 focusIndicator.len++;
3481 focusIndicator.start--;
3482 i--;
3483 }
3484#ifndef QT_NO_TEXTCUSTOMITEM
3485 } else if ( p->at( i )->isCustom() ) {
3486 if ( p->at( i )->customItem()->isNested() ) {
3487 QTextTable *t = (QTextTable*)p->at( i )->customItem();
3488 QPtrList<QTextTableCell> cells = t->tableCells();
3489 // first try to continue
3490 QTextTableCell *c;
3491 bool resetCells = TRUE;
3492 for ( c = cells.last(); c; c = cells.prev() ) {
3493 if ( c->richText()->hasFocusParagraph() ) {
3494 if ( c->richText()->focusNextPrevChild( next ) ) {
3495 p->setChanged( TRUE );
3496 focusIndicator.parag = p;
3497 focusIndicator.start = i;
3498 focusIndicator.len = 0;
3499 focusIndicator.href = c->richText()->focusHref();
3500 focusIndicator.name = c->richText()->focusName();
3501 return TRUE;
3502 } else {
3503 resetCells = FALSE;
3504 c = cells.prev();
3505 break;
3506 }
3507 }
3508 if ( cells.at() == 0 )
3509 break;
3510 }
3511 // now really try
3512 if ( resetCells )
3513 c = cells.last();
3514 for ( ; c; c = cells.prev() ) {
3515 if ( c->richText()->focusNextPrevChild( next ) ) {
3516 p->setChanged( TRUE );
3517 focusIndicator.parag = p;
3518 focusIndicator.start = i;
3519 focusIndicator.len = 0;
3520 focusIndicator.href = c->richText()->focusHref();
3521 focusIndicator.name = c->richText()->focusName();
3522 return TRUE;
3523 }
3524 if ( cells.at() == 0 )
3525 break;
3526 }
3527 }
3528#endif
3529 }
3530 }
3531 p = p->prev();
3532 if ( p )
3533 index = p->length() - 1;
3534 }
3535 }
3536
3537 focusIndicator.parag = 0;
3538
3539 return FALSE;
3540}
3541
3542int QTextDocument::length() const
3543{
3544 int l = -1;
3545 QTextParagraph *p = fParag;
3546 while ( p ) {
3547 l += p->length();
3548 p = p->next();
3549 }
3550 return QMAX(0,l);
3551}
3552
3553// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3554
3555int QTextFormat::width( const QChar &c ) const
3556{
3557 if ( c.unicode() == 0xad ) // soft hyphen
3558 return 0;
3559 if ( !pntr || !pntr->isActive() ) {
3560 if ( c == '\t' )
3561 return fm.width( ' ' );
3562 if ( ha == AlignNormal ) {
3563 int w;
3564 if ( c.row() )
3565 w = fm.width( c );
3566 else
3567 w = widths[ c.unicode() ];
3568 if ( w == 0 && !c.row() ) {
3569 w = fm.width( c );
3570 ( (QTextFormat*)this )->widths[ c.unicode() ] = w;
3571 }
3572 return w;
3573 } else {
3574 QFont f( fn );
3575 if ( usePixelSizes )
3576 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
3577 else
3578 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
3579 QFontMetrics fm_( f );
3580 return fm_.width( c );
3581 }
3582 }
3583
3584 QFont f( fn );
3585 if ( ha != AlignNormal ) {
3586 if ( usePixelSizes )
3587 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
3588 else
3589 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
3590 }
3591 applyFont( f );
3592
3593 return pntr_fm->width( c );
3594}
3595
3596int QTextFormat::width( const QString &str, int pos ) const
3597{
3598 int w = 0;
3599 if ( str.unicode()[ pos ].unicode() == 0xad )
3600 return w;
3601 if ( !pntr || !pntr->isActive() ) {
3602 if ( ha == AlignNormal ) {
3603 w = fm.charWidth( str, pos );
3604 } else {
3605 QFont f( fn );
3606 if ( usePixelSizes )
3607 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
3608 else
3609 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
3610 QFontMetrics fm_( f );
3611 w = fm_.charWidth( str, pos );
3612 }
3613 } else {
3614 QFont f( fn );
3615 if ( ha != AlignNormal ) {
3616 if ( usePixelSizes )
3617 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
3618 else
3619 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
3620 }
3621 applyFont( f );
3622 w = pntr_fm->charWidth( str, pos );
3623 }
3624 return w;
3625}
3626
3627// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3628
3629QTextString::QTextString()
3630{
3631 bidiDirty = TRUE;
3632 bidi = FALSE;
3633 rightToLeft = FALSE;
3634 dir = QChar::DirON;
3635}
3636
3637QTextString::QTextString( const QTextString &s )
3638{
3639 bidiDirty = TRUE;
3640 bidi = s.bidi;
3641 rightToLeft = s.rightToLeft;
3642 dir = s.dir;
3643 data = s.data;
3644 data.detach();
3645 for ( int i = 0; i < (int)data.size(); ++i ) {
3646 QTextFormat *f = data[i].format();
3647 if ( f )
3648 f->addRef();
3649 }
3650}
3651
3652void QTextString::insert( int index, const QString &s, QTextFormat *f )
3653{
3654 insert( index, s.unicode(), s.length(), f );
3655}
3656
3657void QTextString::insert( int index, const QChar *unicode, int len, QTextFormat *f )
3658{
3659 int os = data.size();
3660 data.resize( data.size() + len, QGArray::SpeedOptim );
3661 if ( index < os ) {
3662 memmove( data.data() + index + len, data.data() + index,
3663 sizeof( QTextStringChar ) * ( os - index ) );
3664 }
3665 QTextStringChar *ch = data.data() + index;
3666 for ( int i = 0; i < len; ++i ) {
3667 ch->x = 0;
3668 ch->lineStart = 0;
3669 ch->d.format = 0;
3670 ch->nobreak = FALSE;
3671 ch->type = QTextStringChar::Regular;
3672 ch->d.format = f;
3673 ch->rightToLeft = 0;
3674 ch->c = unicode[i];
3675 ++ch;
3676 }
3677 bidiDirty = TRUE;
3678}
3679
3680QTextString::~QTextString()
3681{
3682 clear();
3683}
3684
3685void QTextString::insert( int index, QTextStringChar *c, bool doAddRefFormat )
3686{
3687 int os = data.size();
3688 data.resize( data.size() + 1, QGArray::SpeedOptim );
3689 if ( index < os ) {
3690 memmove( data.data() + index + 1, data.data() + index,
3691 sizeof( QTextStringChar ) * ( os - index ) );
3692 }
3693 QTextStringChar &ch = data[ (int)index ];
3694 ch.c = c->c;
3695 ch.x = 0;
3696 ch.lineStart = 0;
3697 ch.rightToLeft = 0;
3698 ch.d.format = 0;
3699 ch.type = QTextStringChar::Regular;
3700 ch.nobreak = FALSE;
3701 if ( doAddRefFormat && c->format() )
3702 c->format()->addRef();
3703 ch.setFormat( c->format() );
3704 bidiDirty = TRUE;
3705}
3706
3707void QTextString::truncate( int index )
3708{
3709 index = QMAX( index, 0 );
3710 index = QMIN( index, (int)data.size() - 1 );
3711 if ( index < (int)data.size() ) {
3712 for ( int i = index + 1; i < (int)data.size(); ++i ) {
3713 QTextStringChar &ch = data[ i ];
3714#ifndef QT_NO_TEXTCUSTOMITEM
3715 if ( !(ch.type == QTextStringChar::Regular) ) {
3716 delete ch.customItem();
3717 if ( ch.d.custom->format )
3718 ch.d.custom->format->removeRef();
3719 delete ch.d.custom;
3720 ch.d.custom = 0;
3721 } else
3722#endif
3723 if ( ch.format() ) {
3724 ch.format()->removeRef();
3725 }
3726 }
3727 }
3728 data.truncate( index );
3729 bidiDirty = TRUE;
3730}
3731
3732void QTextString::remove( int index, int len )
3733{
3734 for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
3735 QTextStringChar &ch = data[ i ];
3736#ifndef QT_NO_TEXTCUSTOMITEM
3737 if ( !(ch.type == QTextStringChar::Regular) ) {
3738 delete ch.customItem();
3739 if ( ch.d.custom->format )
3740 ch.d.custom->format->removeRef();
3741 delete ch.d.custom;
3742 ch.d.custom = 0;
3743 } else
3744#endif
3745 if ( ch.format() ) {
3746 ch.format()->removeRef();
3747 }
3748 }
3749 memmove( data.data() + index, data.data() + index + len,
3750 sizeof( QTextStringChar ) * ( data.size() - index - len ) );
3751 data.resize( data.size() - len, QGArray::SpeedOptim );
3752 bidiDirty = TRUE;
3753}
3754
3755void QTextString::clear()
3756{
3757 for ( int i = 0; i < (int)data.count(); ++i ) {
3758 QTextStringChar &ch = data[ i ];
3759#ifndef QT_NO_TEXTCUSTOMITEM
3760 if ( !(ch.type == QTextStringChar::Regular) ) {
3761 if ( ch.customItem() && ch.customItem()->placement() == QTextCustomItem::PlaceInline )
3762 delete ch.customItem();
3763 if ( ch.d.custom->format )
3764 ch.d.custom->format->removeRef();
3765 delete ch.d.custom;
3766 ch.d.custom = 0;
3767 } else
3768#endif
3769 if ( ch.format() ) {
3770 ch.format()->removeRef();
3771 }
3772 }
3773 data.resize( 0 );
3774 bidiDirty = TRUE;
3775}
3776
3777void QTextString::setFormat( int index, QTextFormat *f, bool useCollection )
3778{
3779 QTextStringChar &ch = data[ index ];
3780 if ( useCollection && ch.format() )
3781 ch.format()->removeRef();
3782 ch.setFormat( f );
3783}
3784
3785void QTextString::checkBidi() const
3786{
3787 QTextString *that = (QTextString *)this;
3788 that->bidiDirty = FALSE;
3789 int length = data.size();
3790 if ( !length ) {
3791 that->bidi = FALSE;
3792 that->rightToLeft = dir == QChar::DirR;
3793 return;
3794 }
3795 const QTextStringChar *start = data.data();
3796 const QTextStringChar *end = start + length;
3797
3798 // determines the properties we need for layouting
3799 QTextEngine textEngine( toString(), 0 );
3800 textEngine.itemize(QTextEngine::SingleLine);
3801 const QCharAttributes *ca = textEngine.attributes() + length-1;
3802 QTextStringChar *ch = (QTextStringChar *)end - 1;
3803 QScriptItem *item = &textEngine.items[textEngine.items.size()-1];
3804 unsigned char bidiLevel = item->analysis.bidiLevel;
3805 if ( bidiLevel )
3806 that->bidi = TRUE;
3807 int pos = length-1;
3808 while ( ch >= start ) {
3809 if ( item->position > pos ) {
3810 --item;
3811 Q_ASSERT( item >= &textEngine.items[0] );
3812 Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
3813 bidiLevel = item->analysis.bidiLevel;
3814 if ( bidiLevel )
3815 that->bidi = TRUE;
3816 }
3817 ch->softBreak = ca->softBreak;
3818 ch->whiteSpace = ca->whiteSpace;
3819 ch->charStop = ca->charStop;
3820 ch->wordStop = ca->wordStop;
3821 ch->bidiLevel = bidiLevel;
3822 ch->rightToLeft = (bidiLevel%2);
3823 --ch;
3824 --ca;
3825 --pos;
3826 }
3827
3828 if ( dir == QChar::DirR ) {
3829 that->bidi = TRUE;
3830 that->rightToLeft = TRUE;
3831 } else if ( dir == QChar::DirL ) {
3832 that->rightToLeft = FALSE;
3833 } else {
3834 that->rightToLeft = (textEngine.items[0].analysis.bidiLevel % 2);
3835 }
3836}
3837
3838void QTextDocument::setStyleSheet( QStyleSheet *s )
3839{
3840 if ( !s )
3841 return;
3842 sheet_ = s;
3843 list_tm = list_bm = par_tm = par_bm = 12;
3844 list_lm = 40;
3845 li_tm = li_bm = 0;
3846 QStyleSheetItem* item = s->item( "ol" );
3847 if ( item ) {
3848 list_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
3849 list_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
3850 list_lm = QMAX(0,item->margin( QStyleSheetItem::MarginLeft ));
3851 }
3852 if ( (item = s->item( "li" ) ) ) {
3853 li_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
3854 li_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
3855 }
3856 if ( (item = s->item( "p" ) ) ) {
3857 par_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop ));
3858 par_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom ));
3859 }
3860}
3861
3862void QTextDocument::setUnderlineLinks( bool b ) {
3863 underlLinks = b;
3864 for ( QTextDocument *d = childList.first(); d; d = childList.next() )
3865 d->setUnderlineLinks( b );
3866}
3867
3868void QTextStringChar::setFormat( QTextFormat *f )
3869{
3870 if ( type == Regular ) {
3871 d.format = f;
3872 } else {
3873#ifndef QT_NO_TEXTCUSTOMITEM
3874 if ( !d.custom ) {
3875 d.custom = new CustomData;
3876 d.custom->custom = 0;
3877 }
3878 d.custom->format = f;
3879#endif
3880 }
3881}
3882
3883#ifndef QT_NO_TEXTCUSTOMITEM
3884void QTextStringChar::setCustomItem( QTextCustomItem *i )
3885{
3886 if ( type == Regular ) {
3887 QTextFormat *f = format();
3888 d.custom = new CustomData;
3889 d.custom->format = f;
3890 } else {
3891 delete d.custom->custom;
3892 }
3893 d.custom->custom = i;
3894 type = (type == Anchor ? CustomAnchor : Custom);
3895}
3896
3897void QTextStringChar::loseCustomItem()
3898{
3899 if ( type == Custom ) {
3900 QTextFormat *f = d.custom->format;
3901 d.custom->custom = 0;
3902 delete d.custom;
3903 type = Regular;
3904 d.format = f;
3905 } else if ( type == CustomAnchor ) {
3906 d.custom->custom = 0;
3907 type = Anchor;
3908 }
3909}
3910
3911#endif
3912
3913QString QTextStringChar::anchorName() const
3914{
3915 if ( type == Regular )
3916 return QString::null;
3917 else
3918 return d.custom->anchorName;
3919}
3920
3921QString QTextStringChar::anchorHref() const
3922{
3923 if ( type == Regular )
3924 return QString::null;
3925 else
3926 return d.custom->anchorHref;
3927}
3928
3929void QTextStringChar::setAnchor( const QString& name, const QString& href )
3930{
3931 if ( type == Regular ) {
3932 QTextFormat *f = format();
3933 d.custom = new CustomData;
3934#ifndef QT_NO_TEXTCUSTOMITEM
3935 d.custom->custom = 0;
3936#endif
3937 d.custom->format = f;
3938 type = Anchor;
3939 } else if ( type == Custom ) {
3940 type = CustomAnchor;
3941 }
3942 d.custom->anchorName = name;
3943 d.custom->anchorHref = href;
3944}
3945
3946
3947int QTextString::width( int idx ) const
3948{
3949 int w = 0;
3950 QTextStringChar *c = &at( idx );
3951 if ( !c->charStop || c->c.unicode() == 0xad || c->c.unicode() == 0x2028 )
3952 return 0;
3953#ifndef QT_NO_TEXTCUSTOMITEM
3954 if( c->isCustom() ) {
3955 if( c->customItem()->placement() == QTextCustomItem::PlaceInline )
3956 w = c->customItem()->width;
3957 } else
3958#endif
3959 {
3960 int r = c->c.row();
3961 if(r < 0x06 || (r > 0x1f && !(r > 0xd7 && r < 0xe0))) {
3962 w = c->format()->width( c->c );
3963 } else {
3964 // complex text. We need some hacks to get the right metric here
3965 QString str;
3966 int pos = 0;
3967 if( idx > 8 )
3968 pos = idx - 8;
3969 int off = idx - pos;
3970 int end = QMIN( length(), idx + 8 );
3971 str.setLength( end-pos );
3972 QChar *uc = (QChar *)str.unicode();
3973 while ( pos < end ) {
3974 *(uc++) = at(pos).c;
3975 pos++;
3976 }
3977 w = c->format()->width( str, off );
3978 }
3979 }
3980 return w;
3981}
3982
3983// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3984
3985QTextParagraph::QTextParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds )
3986 : p( pr ), n( nx ), docOrPseudo( d ),
3987 changed(FALSE), firstFormat(TRUE), firstPProcess(TRUE), needPreProcess(FALSE), fullWidth(TRUE),
3988 lastInFrame(FALSE), visible(TRUE), breakable(TRUE), movedDown(FALSE),
3989 mightHaveCustomItems(FALSE), hasdoc( d != 0 ), litem(FALSE), rtext(FALSE),
3990 align( 0 ), lstyle( QStyleSheetItem::ListDisc ), invalid( 0 ), mSelections( 0 ),
3991#ifndef QT_NO_TEXTCUSTOMITEM
3992 mFloatingItems( 0 ),
3993#endif
3994 utm( 0 ), ubm( 0 ), ulm( 0 ), urm( 0 ), uflm( 0 ), ulinespacing( 0 ),
3995 tabStopWidth(0), minwidth(0), tArray(0), eData( 0 ), ldepth( 0 )
3996{
3997 lstyle = QStyleSheetItem::ListDisc;
3998 if ( !hasdoc )
3999 docOrPseudo = new QTextParagraphPseudoDocument;
4000 bgcol = 0;
4001 list_val = -1;
4002 paintdevice = 0;
4003 QTextFormat* defFormat = formatCollection()->defaultFormat();
4004 if ( !hasdoc ) {
4005 tabStopWidth = defFormat->width( 'x' ) * 8;
4006 pseudoDocument()->commandHistory = new QTextCommandHistory( 100 );
4007 }
4008
4009 if ( p )
4010 p->n = this;
4011 if ( n )
4012 n->p = this;
4013
4014 if ( !p && hasdoc )
4015 document()->setFirstParagraph( this );
4016 if ( !n && hasdoc )
4017 document()->setLastParagraph( this );
4018
4019 state = -1;
4020
4021 if ( p )
4022 id = p->id + 1;
4023 else
4024 id = 0;
4025 if ( n && updateIds ) {
4026 QTextParagraph *s = n;
4027 while ( s ) {
4028 s->id = s->p->id + 1;
4029 s->invalidateStyleCache();
4030 s = s->n;
4031 }
4032 }
4033
4034 str = new QTextString();
4035 QChar ch(' ');
4036 str->insert( 0, &ch, 1, formatCollection()->defaultFormat() );
4037}
4038
4039QTextParagraph::~QTextParagraph()
4040{
4041 delete str;
4042 if ( hasdoc ) {
4043 register QTextDocument *doc = document();
4044 if ( this == doc->minwParag ) {
4045 doc->minwParag = 0;
4046 doc->minw = 0;
4047 }
4048 if ( this == doc->curParag )
4049 doc->curParag = 0;
4050 } else {
4051 delete pseudoDocument();
4052 }
4053 delete [] tArray;
4054 delete eData;
4055 QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
4056 for ( ; it != lineStarts.end(); ++it )
4057 delete *it;
4058 if ( mSelections )
4059 delete mSelections;
4060#ifndef QT_NO_TEXTCUSTOMITEM
4061 if ( mFloatingItems )
4062 delete mFloatingItems;
4063#endif
4064 if ( p )
4065 p->setNext( n );
4066 if ( n )
4067 n->setPrev( p );
4068}
4069
4070void QTextParagraph::setNext( QTextParagraph *s )
4071{
4072 n = s;
4073 if ( !n && hasdoc )
4074 document()->setLastParagraph( this );
4075}
4076
4077void QTextParagraph::setPrev( QTextParagraph *s )
4078{
4079 p = s;
4080 if ( !p && hasdoc )
4081 document()->setFirstParagraph( this );
4082}
4083
4084void QTextParagraph::invalidate( int chr )
4085{
4086 if ( invalid < 0 )
4087 invalid = chr;
4088 else
4089 invalid = QMIN( invalid, chr );
4090#ifndef QT_NO_TEXTCUSTOMITEM
4091 if ( mFloatingItems ) {
4092 for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
4093 i->ypos = -1;
4094 }
4095#endif
4096 invalidateStyleCache();
4097}
4098
4099void QTextParagraph::invalidateStyleCache()
4100{
4101 if ( list_val < 0 )
4102 list_val = -1;
4103}
4104
4105
4106void QTextParagraph::insert( int index, const QString &s )
4107{
4108 insert( index, s.unicode(), s.length() );
4109}
4110
4111void QTextParagraph::insert( int index, const QChar *unicode, int len )
4112{
4113 if ( hasdoc && !document()->useFormatCollection() && document()->preProcessor() )
4114 str->insert( index, unicode, len,
4115 document()->preProcessor()->format( QTextPreProcessor::Standard ) );
4116 else
4117 str->insert( index, unicode, len, formatCollection()->defaultFormat() );
4118 invalidate( index );
4119 needPreProcess = TRUE;
4120}
4121
4122void QTextParagraph::truncate( int index )
4123{
4124 str->truncate( index );
4125 insert( length(), " " );
4126 needPreProcess = TRUE;
4127}
4128
4129void QTextParagraph::remove( int index, int len )
4130{
4131 if ( index + len - str->length() > 0 )
4132 return;
4133#ifndef QT_NO_TEXTCUSTOMITEM
4134 for ( int i = index; i < index + len; ++i ) {
4135 QTextStringChar *c = at( i );
4136 if ( hasdoc && c->isCustom() ) {
4137 document()->unregisterCustomItem( c->customItem(), this );
4138 }
4139 }
4140#endif
4141 str->remove( index, len );
4142 invalidate( 0 );
4143 needPreProcess = TRUE;
4144}
4145
4146void QTextParagraph::join( QTextParagraph *s )
4147{
4148 int oh = r.height() + s->r.height();
4149 n = s->n;
4150 if ( n )
4151 n->p = this;
4152 else if ( hasdoc )
4153 document()->setLastParagraph( this );
4154
4155 int start = str->length();
4156 if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
4157 remove( length() - 1, 1 );
4158 --start;
4159 }
4160 append( s->str->toString(), TRUE );
4161
4162 for ( int i = 0; i < s->length(); ++i ) {
4163 if ( !hasdoc || document()->useFormatCollection() ) {
4164 s->str->at( i ).format()->addRef();
4165 str->setFormat( i + start, s->str->at( i ).format(), TRUE );
4166 }
4167#ifndef QT_NO_TEXTCUSTOMITEM
4168 if ( s->str->at( i ).isCustom() ) {
4169 QTextCustomItem * item = s->str->at( i ).customItem();
4170 str->at( i + start ).setCustomItem( item );
4171 s->str->at( i ).loseCustomItem();
4172 if ( hasdoc ) {
4173 document()->unregisterCustomItem( item, s );
4174 document()->registerCustomItem( item, this );
4175 }
4176 }
4177 if ( s->str->at( i ).isAnchor() ) {
4178 str->at( i + start ).setAnchor( s->str->at( i ).anchorName(),
4179 s->str->at( i ).anchorHref() );
4180 }
4181#endif
4182 }
4183
4184 if ( !extraData() && s->extraData() ) {
4185 setExtraData( s->extraData() );
4186 s->setExtraData( 0 );
4187 } else if ( extraData() && s->extraData() ) {
4188 extraData()->join( s->extraData() );
4189 }
4190 delete s;
4191 invalidate( 0 );
4192 r.setHeight( oh );
4193 needPreProcess = TRUE;
4194 if ( n ) {
4195 QTextParagraph *s = n;
4196 s->invalidate( 0 );
4197 while ( s ) {
4198 s->id = s->p->id + 1;
4199 s->state = -1;
4200 s->needPreProcess = TRUE;
4201 s->changed = TRUE;
4202 s->invalidateStyleCache();
4203 s = s->n;
4204 }
4205 }
4206 format();
4207 state = -1;
4208}
4209
4210void QTextParagraph::move( int &dy )
4211{
4212 if ( dy == 0 )
4213 return;
4214 changed = TRUE;
4215 r.moveBy( 0, dy );
4216#ifndef QT_NO_TEXTCUSTOMITEM
4217 if ( mFloatingItems ) {
4218 for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
4219 i->ypos += dy;
4220 }
4221#endif
4222 if ( p )
4223 p->lastInFrame = TRUE;
4224
4225 // do page breaks if required
4226 if ( hasdoc && document()->isPageBreakEnabled() ) {
4227 int shift;
4228 if ( ( shift = document()->formatter()->formatVertically( document(), this ) ) ) {
4229 if ( p )
4230 p->setChanged( TRUE );
4231 dy += shift;
4232 }
4233 }
4234}
4235
4236void QTextParagraph::format( int start, bool doMove )
4237{
4238 if ( !str || str->length() == 0 || !formatter() )
4239 return;
4240
4241 if ( hasdoc &&
4242 document()->preProcessor() &&
4243 ( needPreProcess || state == -1 ) )
4244 document()->preProcessor()->process( document(), this, invalid <= 0 ? 0 : invalid );
4245 needPreProcess = FALSE;
4246
4247 if ( invalid == -1 )
4248 return;
4249
4250 r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
4251 if ( p )
4252 p->lastInFrame = FALSE;
4253
4254 movedDown = FALSE;
4255 bool formattedAgain = FALSE;
4256
4257 formatAgain:
4258
4259 r.setWidth( documentWidth() );
4260#ifndef QT_NO_TEXTCUSTOMITEM
4261 if ( hasdoc && mFloatingItems ) {
4262 for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
4263 i->ypos = r.y();
4264 if ( i->placement() == QTextCustomItem::PlaceRight ) {
4265 i->xpos = r.x() + r.width() - i->width;
4266 }
4267 }
4268 }
4269#endif
4270 QMap<int, QTextLineStart*> oldLineStarts = lineStarts;
4271 lineStarts.clear();
4272 int y = formatter()->format( document(), this, start, oldLineStarts );
4273
4274
4275 r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
4276
4277
4278 QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin();
4279
4280 for ( ; it != oldLineStarts.end(); ++it )
4281 delete *it;
4282
4283 if ( !hasdoc ) { // qt_format_text bounding rect handling
4284 it = lineStarts.begin();
4285 int usedw = 0;
4286 for ( ; it != lineStarts.end(); ++it )
4287 usedw = QMAX( usedw, (*it)->w );
4288 if ( r.width() <= 0 ) {
4289 // if the user specifies an invalid rect, this means that the
4290 // bounding box should grow to the width that the text actually
4291 // needs
4292 r.setWidth( usedw );
4293 } else {
4294 r.setWidth( QMIN( usedw, r.width() ) );
4295 }
4296 }
4297
4298 if ( y != r.height() )
4299 r.setHeight( y );
4300
4301 if ( !visible ) {
4302 r.setHeight( 0 );
4303 } else {
4304 int minw = minwidth = formatter()->minimumWidth();
4305 int wused = formatter()->widthUsed();
4306 wused = QMAX( minw, wused );
4307 if ( hasdoc ) {
4308 document()->setMinimumWidth( minw, wused, this );
4309 } else {
4310 pseudoDocument()->minw = QMAX( pseudoDocument()->minw, minw );
4311 pseudoDocument()->wused = QMAX( pseudoDocument()->wused, wused );
4312 }
4313 }
4314
4315 // do page breaks if required
4316 if ( hasdoc && document()->isPageBreakEnabled() ) {
4317 int shift = document()->formatter()->formatVertically( document(), this );
4318 if ( shift && !formattedAgain ) {
4319 formattedAgain = TRUE;
4320 goto formatAgain;
4321 }
4322 }
4323
4324 if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) {
4325 int dy = ( r.y() + r.height() ) - n->r.y();
4326 QTextParagraph *s = n;
4327 bool makeInvalid = p && p->lastInFrame;
4328 while ( s && dy ) {
4329 if ( !s->isFullWidth() )
4330 makeInvalid = TRUE;
4331 if ( makeInvalid )
4332 s->invalidate( 0 );
4333 s->move( dy );
4334 if ( s->lastInFrame )
4335 makeInvalid = TRUE;
4336 s = s->n;
4337 }
4338 }
4339
4340 firstFormat = FALSE;
4341 changed = TRUE;
4342 invalid = -1;
4343 //##### string()->setTextChanged( FALSE );
4344}
4345
4346int QTextParagraph::lineHeightOfChar( int i, int *bl, int *y ) const
4347{
4348 if ( !isValid() )
4349 ( (QTextParagraph*)this )->format();
4350
4351 QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
4352 --it;
4353 for ( ;; ) {
4354 if ( i >= it.key() ) {
4355 if ( bl )
4356 *bl = ( *it )->baseLine;
4357 if ( y )
4358 *y = ( *it )->y;
4359 return ( *it )->h;
4360 }
4361 if ( it == lineStarts.begin() )
4362 break;
4363 --it;
4364 }
4365
4366 qWarning( "QTextParagraph::lineHeightOfChar: couldn't find lh for %d", i );
4367 return 15;
4368}
4369
4370QTextStringChar *QTextParagraph::lineStartOfChar( int i, int *index, int *line ) const
4371{
4372 if ( !isValid() )
4373 ( (QTextParagraph*)this )->format();
4374
4375 int l = (int)lineStarts.count() - 1;
4376 QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
4377 --it;
4378 for ( ;; ) {
4379 if ( i >= it.key() ) {
4380 if ( index )
4381 *index = it.key();
4382 if ( line )
4383 *line = l;
4384 return &str->at( it.key() );
4385 }
4386 if ( it == lineStarts.begin() )
4387 break;
4388 --it;
4389 --l;
4390 }
4391
4392 qWarning( "QTextParagraph::lineStartOfChar: couldn't find %d", i );
4393 return 0;
4394}
4395
4396int QTextParagraph::lines() const
4397{
4398 if ( !isValid() )
4399 ( (QTextParagraph*)this )->format();
4400
4401 return (int)lineStarts.count();
4402}
4403
4404QTextStringChar *QTextParagraph::lineStartOfLine( int line, int *index ) const
4405{
4406 if ( !isValid() )
4407 ( (QTextParagraph*)this )->format();
4408
4409 if ( line >= 0 && line < (int)lineStarts.count() ) {
4410 QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
4411 while ( line-- > 0 )
4412 ++it;
4413 int i = it.key();
4414 if ( index )
4415 *index = i;
4416 return &str->at( i );
4417 }
4418
4419 qWarning( "QTextParagraph::lineStartOfLine: couldn't find %d", line );
4420 return 0;
4421}
4422
4423int QTextParagraph::leftGap() const
4424{
4425 if ( !isValid() )
4426 ( (QTextParagraph*)this )->format();
4427
4428 if ( str->length() == 0)
4429 return 0;
4430
4431 int line = 0;
4432 int x = str->length() ? str->at(0).x : 0; /* set x to x of first char */
4433 if ( str->isBidi() ) {
4434 for ( int i = 1; i < str->length()-1; ++i )
4435 x = QMIN(x, str->at(i).x);
4436 return x;
4437 }
4438
4439 QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
4440 while (line < (int)lineStarts.count()) {
4441 int i = it.key(); /* char index */
4442 x = QMIN(x, str->at(i).x);
4443 ++it;
4444 ++line;
4445 }
4446 return x;
4447}
4448
4449void QTextParagraph::setFormat( int index, int len, QTextFormat *f, bool useCollection, int flags )
4450{
4451 if ( !f )
4452 return;
4453 if ( index < 0 )
4454 index = 0;
4455 if ( index > str->length() - 1 )
4456 index = str->length() - 1;
4457 if ( index + len >= str->length() )
4458 len = str->length() - index;
4459
4460 QTextFormatCollection *fc = 0;
4461 if ( useCollection )
4462 fc = formatCollection();
4463 QTextFormat *of;
4464 for ( int i = 0; i < len; ++i ) {
4465 of = str->at( i + index ).format();
4466 if ( !changed && ( !of || f->key() != of->key() ) )
4467 changed = TRUE;
4468 if ( invalid == -1 &&
4469 ( f->font().family() != of->font().family() ||
4470 f->font().pointSize() != of->font().pointSize() ||
4471 f->font().weight() != of->font().weight() ||
4472 f->font().italic() != of->font().italic() ||
4473 f->vAlign() != of->vAlign() ) ) {
4474 invalidate( 0 );
4475 }
4476 if ( flags == -1 || flags == QTextFormat::Format || !fc ) {
4477 if ( fc )
4478 f = fc->format( f );
4479 str->setFormat( i + index, f, useCollection );
4480 } else {
4481 QTextFormat *fm = fc->format( of, f, flags );
4482 str->setFormat( i + index, fm, useCollection );
4483 }
4484 }
4485}
4486
4487void QTextParagraph::indent( int *oldIndent, int *newIndent )
4488{
4489 if ( !hasdoc || !document()->indent() || isListItem() ) {
4490 if ( oldIndent )
4491 *oldIndent = 0;
4492 if ( newIndent )
4493 *newIndent = 0;
4494 if ( oldIndent && newIndent )
4495 *newIndent = *oldIndent;
4496 return;
4497 }
4498 document()->indent()->indent( document(), this, oldIndent, newIndent );
4499}
4500
4501void QTextParagraph::paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor, bool drawSelections,
4502 int clipx, int clipy, int clipw, int cliph )
4503{
4504 if ( !visible )
4505 return;
4506 int i, y, h, baseLine, xstart, xend = 0;
4507 i = y =h = baseLine = 0;
4508 QRect cursorRect;
4509 drawSelections &= ( mSelections != 0 );
4510 // macintosh full-width selection style
4511 bool fullWidthStyle = QApplication::style().styleHint(QStyle::SH_RichText_FullWidthSelection);
4512 int fullSelectionWidth = 0;
4513 if ( drawSelections && fullWidthStyle )
4514 fullSelectionWidth = (hasdoc ? document()->width() : r.width());
4515
4516 QString qstr = str->toString();
4517 // ### workaround so that \n are not drawn, actually this should
4518 // be fixed in QFont somewhere (under Windows you get ugly boxes
4519 // otherwise)
4520 QChar* uc = (QChar*) qstr.unicode();
4521 for ( uint ii = 0; ii < qstr.length(); ii++ )
4522 if ( uc[(int)ii]== '\n' || uc[(int)ii] == '\t' )
4523 uc[(int)ii] = 0x20;
4524
4525 int line = -1;
4526 int paintStart = 0;
4527 QTextStringChar *chr = 0;
4528 QTextStringChar *nextchr = at( 0 );
4529 for ( i = 0; i < length(); i++ ) {
4530 chr = nextchr;
4531 if ( i < length()-1 )
4532 nextchr = at( i+1 );
4533
4534 // we flush at end of document
4535 bool flush = (i == length()-1);
4536 bool ignoreSoftHyphen = FALSE;
4537 if ( !flush ) {
4538 // we flush at end of line
4539 flush |= nextchr->lineStart;
4540 // we flush on format changes
4541 flush |= ( nextchr->format() != chr->format() );
4542 // we flush on link changes
4543 flush |= ( nextchr->isLink() != chr->isLink() );
4544 // we flush on start of run
4545 flush |= ( nextchr->bidiLevel != chr->bidiLevel );
4546 // we flush on bidi changes
4547 flush |= ( nextchr->rightToLeft != chr->rightToLeft );
4548 // we flush before and after tabs
4549 flush |= ( chr->c == '\t' || nextchr->c == '\t' );
4550 // we flush on soft hypens
4551 if (chr->c.unicode() == 0xad) {
4552 flush = TRUE;
4553 if (!nextchr->lineStart)
4554 ignoreSoftHyphen = TRUE;
4555 }
4556 // we flush on custom items
4557 flush |= chr->isCustom();
4558 // we flush before custom items
4559 flush |= nextchr->isCustom();
4560 // when painting justified, we flush on spaces
4561 if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify )
4562 flush |= chr->whiteSpace;
4563 }
4564
4565 // init a new line
4566 if ( chr->lineStart ) {
4567 ++line;
4568 paintStart = i;
4569 lineInfo( line, y, h, baseLine );
4570 if ( clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph ) { // outside clip area, leave
4571 break;
4572 }
4573
4574 // if this is the first line and we are a list item, draw the the bullet label
4575 if ( line == 0 && isListItem() ) {
4576 int x = chr->x;
4577 if (str->isBidi()) {
4578 if (str->isRightToLeft()) {
4579 x = chr->x + str->width(0);
4580 for (int k = 1; k < length(); ++k) {
4581 if (str->at(k).lineStart)
4582 break;
4583 x = QMAX(x, str->at(k).x + str->width(k));
4584 }
4585 } else {
4586 x = chr->x;
4587 for (int k = 1; k < length(); ++k) {
4588 if (str->at(k).lineStart)
4589 break;
4590 x = QMIN(x, str->at(k).x);
4591 }
4592 }
4593 }
4594 drawLabel( &painter, x, y, 0, 0, baseLine, cg );
4595 }
4596 }
4597
4598 // check for cursor mark
4599 if ( cursor && this == cursor->paragraph() && i == cursor->index() ) {
4600 QTextStringChar *c = i == 0 ? chr : chr - 1;
4601 cursorRect.setRect( cursor->x() , y + baseLine - c->format()->ascent(),
4602 1, c->format()->height() );
4603 }
4604
4605 if ( flush ) { // something changed, draw what we have so far
4606 if ( chr->rightToLeft ) {
4607 xstart = chr->x;
4608 xend = at( paintStart )->x + str->width( paintStart );
4609 } else {
4610 xstart = at( paintStart )->x;
4611 xend = chr->x;
4612 if ( i < length() - 1 ) {
4613 if ( !str->at( i + 1 ).lineStart &&
4614 str->at( i + 1 ).rightToLeft == chr->rightToLeft )
4615 xend = str->at( i + 1 ).x;
4616 else
4617 xend += str->width( i );
4618 }
4619 }
4620
4621 if ( (clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) &&
4622 ( clipy == -1 || clipy < y+r.y()+h ) ) {
4623 if ( !chr->isCustom() )
4624 drawString( painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y,
4625 baseLine, xend-xstart, h, drawSelections, fullSelectionWidth,
4626 chr, cg, chr->rightToLeft );
4627#ifndef QT_NO_TEXTCUSTOMITEM
4628 else if ( chr->customItem()->placement() == QTextCustomItem::PlaceInline ) {
4629 bool inSelection = FALSE;
4630 if (drawSelections) {
4631 QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->find( QTextDocument::Standard );
4632 inSelection = (it != mSelections->end() && (*it).start <= i && (*it).end > i);
4633 }
4634 chr->customItem()->draw( &painter, chr->x, y,
4635 clipx == -1 ? clipx : (clipx - r.x()),
4636 clipy == -1 ? clipy : (clipy - r.y()),
4637 clipw, cliph, cg, inSelection );
4638 }
4639#endif
4640 }
4641 paintStart = i+1;
4642 }
4643
4644 }
4645
4646 // time to draw the cursor
4647 const int cursor_extent = 4;
4648 if ( !cursorRect.isNull() && cursor &&
4649 ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw)) ) {
4650 painter.fillRect( cursorRect, cg.color( QColorGroup::Text ) );
4651 painter.save();
4652 if ( string()->isBidi() ) {
4653 if ( at( cursor->index() )->rightToLeft ) {
4654 painter.setPen( Qt::black );
4655 painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
4656 painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
4657 } else {
4658 painter.setPen( Qt::black );
4659 painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
4660 painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 );
4661 }
4662 }
4663 painter.restore();
4664 }
4665}
4666
4667//#define BIDI_DEBUG
4668
4669void QTextParagraph::setColorForSelection( QColor &color, QPainter &painter,
4670 const QColorGroup& cg, int selection )
4671{
4672 if (selection < 0)
4673 return;
4674 color = ( hasdoc && selection != QTextDocument::Standard ) ?
4675 document()->selectionColor( selection ) :
4676 cg.color( QColorGroup::Highlight );
4677 if ( selection == QTextDocument::IMCompositionText ) {
4678 int h1, s1, v1, h2, s2, v2;
4679 cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 );
4680 cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 );
4681 color.setHsv( h1, s1, ( v1 + v2 ) / 2 );
4682 painter.setPen( cg.color( QColorGroup::Text ) );
4683 } else if ( selection == QTextDocument::IMSelectionText ) {
4684 color = cg.color( QColorGroup::Dark );
4685 painter.setPen( cg.color( QColorGroup::BrightText ) );
4686 } else if ( !hasdoc || document()->invertSelectionText( selection ) ) {
4687 painter.setPen( cg.color( QColorGroup::HighlightedText ) );
4688 }
4689}
4690
4691void QTextParagraph::drawString( QPainter &painter, const QString &str, int start, int len, int xstart,
4692 int y, int baseLine, int w, int h, bool drawSelections, int fullSelectionWidth,
4693 QTextStringChar *formatChar, const QColorGroup& cg,
4694 bool rightToLeft )
4695{
4696 bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : FALSE;
4697 QTextFormat* format = formatChar->format();
4698
4699 if ( !plainText || hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color() )
4700 painter.setPen( QPen( format->color() ) );
4701 else
4702 painter.setPen( cg.text() );
4703 painter.setFont( format->font() );
4704
4705 if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() ) {
4706 if ( format->useLinkColor() )
4707 painter.setPen(document()->linkColor.isValid() ? document()->linkColor : cg.link());
4708 if ( document()->underlineLinks() ) {
4709 QFont fn = format->font();
4710 fn.setUnderline( TRUE );
4711 painter.setFont( fn );
4712 }
4713 }
4714
4715 QPainter::TextDirection dir = rightToLeft ? QPainter::RTL : QPainter::LTR;
4716
4717 int real_length = len;
4718 if (len && dir != QPainter::RTL && start + len == length() ) // don't draw the last character (trailing space)
4719 len--;
4720 if (len && str.unicode()[start+len-1] == QChar_linesep)
4721 len--;
4722
4723
4724 QTextFormat::VerticalAlignment vAlign = format->vAlign();
4725 if ( vAlign != QTextFormat::AlignNormal ) {
4726 // sub or superscript
4727 QFont f( painter.font() );
4728 if ( format->fontSizesInPixels() )
4729 f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
4730 else
4731 f.setPointSize( ( f.pointSize() * 2 ) / 3 );
4732 painter.setFont( f );
4733 int h = painter.fontMetrics().height();
4734 baseLine += (vAlign == QTextFormat::AlignSubScript) ? h/6 : -h/2;
4735 }
4736
4737 bool allSelected = FALSE;
4738 if (drawSelections) {
4739 QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->find( QTextDocument::Standard );
4740 allSelected = (it != mSelections->end() && (*it).start <= start && (*it).end >= start+len);
4741 }
4742 if (!allSelected)
4743 painter.drawText(xstart, y + baseLine, str, start, len, dir);
4744
4745#ifdef BIDI_DEBUG
4746 painter.save();
4747 painter.setPen ( Qt::red );
4748 painter.drawLine( xstart, y, xstart, y + baseLine );
4749 painter.drawLine( xstart, y + baseLine/2, xstart + 10, y + baseLine/2 );
4750 int w = 0;
4751 int i = 0;
4752 while( i < len )
4753 w += painter.fontMetrics().charWidth( str, start + i++ );
4754 painter.setPen ( Qt::blue );
4755 painter.drawLine( xstart + w - 1, y, xstart + w - 1, y + baseLine );
4756 painter.drawLine( xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2 );
4757 painter.restore();
4758#endif
4759
4760 // check if we are in a selection and draw it
4761 if (drawSelections) {
4762 QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->end();
4763 while ( it != mSelections->begin() ) {
4764 --it;
4765 int selStart = (*it).start;
4766 int selEnd = (*it).end;
4767 int tmpw = w;
4768
4769 selStart = QMAX(selStart, start);
4770 int real_selEnd = QMIN(selEnd, start+real_length);
4771 selEnd = QMIN(selEnd, start+len);
4772 bool extendRight = FALSE;
4773 bool extendLeft = FALSE;
4774 bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key()));
4775 if (selWrap || this->str->at(real_selEnd).lineStart) {
4776 extendRight = (fullSelectionWidth != 0);
4777 if (!extendRight && !rightToLeft)
4778 tmpw += painter.fontMetrics().width(' ');
4779 }
4780 if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) {
4781 extendLeft = TRUE;
4782 }
4783 if (this->str->isRightToLeft() != rightToLeft)
4784 extendLeft = extendRight = FALSE;
4785
4786 if (this->str->isRightToLeft()) {
4787 bool tmp = extendLeft;
4788 extendLeft = extendRight;
4789 extendRight = tmp;
4790 }
4791
4792 if (selStart < real_selEnd ||
4793 selWrap && fullSelectionWidth && extendRight &&
4794 // don't draw the standard selection on a printer=
4795 (it.key() != QTextDocument::Standard || !is_printer( &painter))) {
4796 int selection = it.key();
4797 QColor color;
4798 setColorForSelection( color, painter, cg, selection );
4799 if (selStart != start || selEnd != start + len || selWrap) {
4800 // have to clip
4801 painter.save();
4802 int cs, ce;
4803 if (rightToLeft) {
4804 cs = (selEnd != start + len) ?
4805 this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart;
4806 ce = (selStart != start) ?
4807 this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw;
4808 } else {
4809 cs = (selStart != start) ? this->str->at(selStart).x : xstart;
4810 ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw;
4811 }
4812 QRect r(cs, y, ce-cs, h);
4813 if (extendLeft)
4814 r.setLeft(0);
4815 if (extendRight)
4816 r.setRight(fullSelectionWidth);
4817 QRegion reg(r);
4818 if ( painter.hasClipping() )
4819 reg &= painter.clipRegion(QPainter::CoordPainter);
4820 painter.setClipRegion(reg, QPainter::CoordPainter);
4821 }
4822 int xleft = xstart;
4823 if ( extendLeft ) {
4824 tmpw += xstart;
4825 xleft = 0;
4826 }
4827 if ( extendRight )
4828 tmpw = fullSelectionWidth - xleft;
4829 painter.fillRect( xleft, y, tmpw, h, color );
4830 painter.drawText( xstart, y + baseLine, str, start, len, dir );
4831 if (selStart != start || selEnd != start + len || selWrap)
4832 painter.restore();
4833 }
4834 }
4835 }
4836
4837 if ( format->isMisspelled() ) {
4838 painter.save();
4839 painter.setPen( QPen( Qt::red, 1, Qt::DotLine ) );
4840 painter.drawLine( xstart, y + baseLine + 1, xstart + w, y + baseLine + 1 );
4841 painter.restore();
4842 }
4843
4844 if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
4845 document()->focusIndicator.parag == this &&
4846 ( document()->focusIndicator.start >= start &&
4847 document()->focusIndicator.start + document()->focusIndicator.len <= start + len ||
4848 document()->focusIndicator.start <= start &&
4849 document()->focusIndicator.start + document()->focusIndicator.len >= start + len ) )
4850 painter.drawWinFocusRect( QRect( xstart, y, w, h ) );
4851}
4852
4853void QTextParagraph::drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg )
4854{
4855 QRect r ( x, y, w, h );
4856 QStyleSheetItem::ListStyle s = listStyle();
4857
4858 p->save();
4859 QTextFormat *format = at( 0 )->format();
4860 if ( format ) {
4861 p->setPen( format->color() );
4862 p->setFont( format->font() );
4863 }
4864 QFontMetrics fm( p->fontMetrics() );
4865 int size = fm.lineSpacing() / 3;
4866
4867 bool rtl = str->isRightToLeft();
4868
4869 switch ( s ) {
4870 case QStyleSheetItem::ListDecimal:
4871 case QStyleSheetItem::ListLowerAlpha:
4872 case QStyleSheetItem::ListUpperAlpha:
4873 {
4874 if ( list_val == -1 ) { // uninitialised list value, calcluate the right one
4875 int depth = listDepth();
4876 list_val--;
4877 // ### evil, square and expensive. This needs to be done when formatting, not when painting
4878 QTextParagraph* s = prev();
4879 int depth_s;
4880 while ( s && (depth_s = s->listDepth()) >= depth ) {
4881 if ( depth_s == depth && s->isListItem() )
4882 list_val--;
4883 s = s->prev();
4884 }
4885 }
4886
4887 int n = list_val;
4888 if ( n < -1 )
4889 n = -n - 1;
4890 QString l;
4891 switch ( s ) {
4892 case QStyleSheetItem::ListLowerAlpha:
4893 if ( n < 27 ) {
4894 l = QChar( ('a' + (char) (n-1)));
4895 break;
4896 }
4897 case QStyleSheetItem::ListUpperAlpha:
4898 if ( n < 27 ) {
4899 l = QChar( ('A' + (char) (n-1)));
4900 break;
4901 }
4902 break;
4903 default: //QStyleSheetItem::ListDecimal:
4904 l.setNum( n );
4905 break;
4906 }
4907 if (rtl)
4908 l.prepend(" .");
4909 else
4910 l += QString::fromLatin1(". ");
4911 int x = ( rtl ? r.left() : r.right() - fm.width(l));
4912 p->drawText( x, r.top() + base, l );
4913 }
4914 break;
4915 case QStyleSheetItem::ListSquare:
4916 {
4917 int x = rtl ? r.left() + size : r.right() - size*2;
4918 QRect er( x, r.top() + fm.height() / 2 - size / 2, size, size );
4919 p->fillRect( er , cg.brush( QColorGroup::Text ) );
4920 }
4921 break;
4922 case QStyleSheetItem::ListCircle:
4923 {
4924 int x = rtl ? r.left() + size : r.right() - size*2;
4925 QRect er( x, r.top() + fm.height() / 2 - size / 2, size, size);
4926 p->drawEllipse( er );
4927 }
4928 break;
4929 case QStyleSheetItem::ListDisc:
4930 default:
4931 {
4932 p->setBrush( cg.brush( QColorGroup::Text ));
4933 int x = rtl ? r.left() + size : r.right() - size*2;
4934 QRect er( x, r.top() + fm.height() / 2 - size / 2, size, size);
4935 p->drawEllipse( er );
4936 p->setBrush( Qt::NoBrush );
4937 }
4938 break;
4939 }
4940
4941 p->restore();
4942}
4943
4944#ifndef QT_NO_DATASTREAM
4945void QTextParagraph::readStyleInformation( QDataStream& stream )
4946{
4947 int int_align, int_lstyle;
4948 uchar uchar_litem, uchar_rtext, uchar_dir;
4949 stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm
4950 >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir;
4951 align = int_align; lstyle = (QStyleSheetItem::ListStyle) int_lstyle;
4952 litem = uchar_litem; rtext = uchar_rtext; str->setDirection( (QChar::Direction)uchar_dir );
4953 QTextParagraph* s = prev() ? prev() : this;
4954 while ( s ) {
4955 s->invalidate( 0 );
4956 s = s->next();
4957 }
4958}
4959
4960void QTextParagraph::writeStyleInformation( QDataStream& stream ) const
4961{
4962 stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction();
4963}
4964#endif
4965
4966
4967void QTextParagraph::setListItem( bool li )
4968{
4969 if ( (bool)litem == li )
4970 return;
4971 litem = li;
4972 changed = TRUE;
4973 QTextParagraph* s = prev() ? prev() : this;
4974 while ( s ) {
4975 s->invalidate( 0 );
4976 s = s->next();
4977 }
4978}
4979
4980void QTextParagraph::setListDepth( int depth ) {
4981 if ( !hasdoc || depth == ldepth )
4982 return;
4983 ldepth = depth;
4984 QTextParagraph* s = prev() ? prev() : this;
4985 while ( s ) {
4986 s->invalidate( 0 );
4987 s = s->next();
4988 }
4989}
4990
4991int *QTextParagraph::tabArray() const
4992{
4993 int *ta = tArray;
4994 if ( !ta && hasdoc )
4995 ta = document()->tabArray();
4996 return ta;
4997}
4998
4999int QTextParagraph::nextTab( int, int x )
5000{
5001 int *ta = tArray;
5002 if ( hasdoc ) {
5003 if ( !ta )
5004 ta = document()->tabArray();
5005 tabStopWidth = document()->tabStopWidth();
5006 }
5007 if ( ta ) {
5008 int i = 0;
5009 while ( ta[ i ] ) {
5010 if ( ta[ i ] >= x )
5011 return tArray[ i ];
5012 ++i;
5013 }
5014 return tArray[ 0 ];
5015 } else {
5016 int d;
5017 if ( tabStopWidth != 0 )
5018 d = x / tabStopWidth;
5019 else
5020 return x;
5021 return tabStopWidth * ( d + 1 );
5022 }
5023}
5024
5025void QTextParagraph::adjustToPainter( QPainter *p )
5026{
5027#ifndef QT_NO_TEXTCUSTOMITEM
5028 for ( int i = 0; i < length(); ++i ) {
5029 if ( at( i )->isCustom() )
5030 at( i )->customItem()->adjustToPainter( p );
5031 }
5032#endif
5033}
5034
5035QTextFormatCollection *QTextParagraph::formatCollection() const
5036{
5037 if ( hasdoc )
5038 return document()->formatCollection();
5039 QTextFormatCollection* fc = &pseudoDocument()->collection;
5040 if ( paintdevice != fc->paintDevice() )
5041 fc->setPaintDevice( paintdevice );
5042 return fc;
5043}
5044
5045QString QTextParagraph::richText() const
5046{
5047 QString s;
5048 QTextStringChar *formatChar = 0;
5049 QString spaces;
5050 bool doStart = richTextExportStart && richTextExportStart->paragraph() == this;
5051 bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this;
5052 int i;
5053 for ( i = 0; i < length()-1; ++i ) {
5054 if ( doStart && i && richTextExportStart->index() == i )
5055 s += "<!--StartFragment-->";
5056 if ( doEnd && richTextExportEnd->index() == i )
5057 s += "<!--EndFragment-->";
5058 QTextStringChar *c = &str->at( i );
5059 if ( c->isAnchor() && !c->anchorName().isEmpty() ) {
5060 if ( c->anchorName().contains( '#' ) ) {
5061 QStringList l = QStringList::split( '#', c->anchorName() );
5062 for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it )
5063 s += "<a name=\"" + *it + "\"></a>";
5064 } else {
5065 s += "<a name=\"" + c->anchorName() + "\"></a>";
5066 }
5067 }
5068 if ( !formatChar ) {
5069 s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(),
5070 0, QString::null, c->anchorHref() );
5071 formatChar = c;
5072 } else if ( ( formatChar->format()->key() != c->format()->key() ) ||
5073 (c->anchorHref() != formatChar->anchorHref() ) ) {
5074 s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(),
5075 formatChar->format() , formatChar->anchorHref(), c->anchorHref() );
5076 formatChar = c;
5077 }
5078 if ( c->c == '<' )
5079 s += "&lt;";
5080 else if ( c->c == '>' )
5081 s += "&gt;";
5082 else if ( c->c =='&' )
5083 s += "&amp;";
5084 else if ( c->c =='\"' )
5085 s += "&quot;";
5086#ifndef QT_NO_TEXTCUSTOMITEM
5087 else if ( c->isCustom() )
5088 s += c->customItem()->richText();
5089#endif
5090 else if ( c->c == '\n' || c->c == QChar_linesep )
5091 s += "<br />"; // space on purpose for compatibility with Netscape, Lynx & Co.
5092 else
5093 s += c->c;
5094 }
5095 if ( doEnd && richTextExportEnd->index() == i )
5096 s += "<!--EndFragment-->";
5097 if ( formatChar )
5098 s += formatChar->format()->makeFormatEndTags( formatCollection()->defaultFormat(), formatChar->anchorHref() );
5099 return s;
5100}
5101
5102void QTextParagraph::addCommand( QTextCommand *cmd )
5103{
5104 if ( !hasdoc )
5105 pseudoDocument()->commandHistory->addCommand( cmd );
5106 else
5107 document()->commands()->addCommand( cmd );
5108}
5109
5110QTextCursor *QTextParagraph::undo( QTextCursor *c )
5111{
5112 if ( !hasdoc )
5113 return pseudoDocument()->commandHistory->undo( c );
5114 return document()->commands()->undo( c );
5115}
5116
5117QTextCursor *QTextParagraph::redo( QTextCursor *c )
5118{
5119 if ( !hasdoc )
5120 return pseudoDocument()->commandHistory->redo( c );
5121 return document()->commands()->redo( c );
5122}
5123
5124int QTextParagraph::topMargin() const
5125{
5126 int m = 0;
5127 if ( rtext ) {
5128 m = isListItem() ? (document()->li_tm/QMAX(1,listDepth()*listDepth())) :
5129 ( listDepth() ? 0 : document()->par_tm );
5130 if ( listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth() ) )
5131 m = QMAX( m, document()->list_tm );
5132 }
5133 m += utm;
5134 return scale( m, QTextFormat::painter() );
5135}
5136
5137int QTextParagraph::bottomMargin() const
5138{
5139 int m = 0;
5140 if ( rtext ) {
5141 m = isListItem() ? (document()->li_bm/QMAX(1,listDepth()*listDepth())) :
5142 ( listDepth() ? 0 : document()->par_bm );
5143 if ( listDepth() == 1 &&( !next() || next()->listDepth() < listDepth() ) )
5144 m = QMAX( m, document()->list_bm );
5145 }
5146 m += ubm;
5147 return scale( m, QTextFormat::painter() );
5148}
5149
5150int QTextParagraph::leftMargin() const
5151{
5152 int m = ulm;
5153 if ( listDepth() && !string()->isRightToLeft() )
5154 m += listDepth() * document()->list_lm;
5155 return scale( m, QTextFormat::painter() );
5156}
5157
5158int QTextParagraph::firstLineMargin() const
5159{
5160 int m = uflm;
5161 return scale( m, QTextFormat::painter() );
5162}
5163
5164int QTextParagraph::rightMargin() const
5165{
5166 int m = urm;
5167 if ( listDepth() && string()->isRightToLeft() )
5168 m += listDepth() * document()->list_lm;
5169 return scale( m, QTextFormat::painter() );
5170}
5171
5172int QTextParagraph::lineSpacing() const
5173{
5174 int l = ulinespacing;
5175 l = scale( l, QTextFormat::painter() );
5176 return l;
5177}
5178
5179void QTextParagraph::copyParagData( QTextParagraph *parag )
5180{
5181 rtext = parag->rtext;
5182 lstyle = parag->lstyle;
5183 ldepth = parag->ldepth;
5184 litem = parag->litem;
5185 align = parag->align;
5186 utm = parag->utm;
5187 ubm = parag->ubm;
5188 urm = parag->urm;
5189 ulm = parag->ulm;
5190 uflm = parag->uflm;
5191 ulinespacing = parag->ulinespacing;
5192 QColor *c = parag->backgroundColor();
5193 if ( c )
5194 setBackgroundColor( *c );
5195 str->setDirection( parag->str->direction() );
5196}
5197
5198void QTextParagraph::show()
5199{
5200 if ( visible || !hasdoc )
5201 return;
5202 visible = TRUE;
5203}
5204
5205void QTextParagraph::hide()
5206{
5207 if ( !visible || !hasdoc )
5208 return;
5209 visible = FALSE;
5210}
5211
5212void QTextParagraph::setDirection( QChar::Direction d )
5213{
5214 if ( str && str->direction() != d ) {
5215 str->setDirection( d );
5216 invalidate( 0 );
5217 }
5218}
5219
5220QChar::Direction QTextParagraph::direction() const
5221{
5222 return (str ? str->direction() : QChar::DirON );
5223}
5224
5225void QTextParagraph::setChanged( bool b, bool recursive )
5226{
5227 changed = b;
5228 if ( recursive ) {
5229 if ( document() && document()->parentParagraph() )
5230 document()->parentParagraph()->setChanged( b, recursive );
5231 }
5232}
5233
5234// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5235
5236
5237QTextPreProcessor::QTextPreProcessor()
5238{
5239}
5240
5241// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5242
5243QTextFormatter::QTextFormatter()
5244 : thisminw(0), thiswused(0), wrapEnabled( TRUE ), wrapColumn( -1 ), biw( FALSE )
5245{
5246}
5247
5248QTextLineStart *QTextFormatter::formatLine( QTextParagraph *parag, QTextString *string, QTextLineStart *line,
5249 QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
5250{
5251 if ( lastChar < startChar )
5252 return new QTextLineStart;
5253#ifndef QT_NO_COMPLEXTEXT
5254 if( string->isBidi() )
5255 return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
5256#endif
5257 int start = (startChar - &string->at(0));
5258 int last = (lastChar - &string->at(0) );
5259
5260 // ignore white space at the end of the line.
5261 QTextStringChar *ch = lastChar;
5262 while ( ch > startChar && ch->whiteSpace ) {
5263 space += ch->format()->width( ' ' );
5264 --ch;
5265 }
5266
5267 if (space < 0)
5268 space = 0;
5269
5270 // do alignment Auto == Left in this case
5271 if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
5272 if ( align & Qt::AlignHCenter )
5273 space /= 2;
5274 for ( int j = start; j <= last; ++j )
5275 string->at( j ).x += space;
5276 } else if ( align & Qt::AlignJustify ) {
5277 int numSpaces = 0;
5278 // End at "last-1", the last space ends up with a width of 0
5279 for ( int j = last-1; j >= start; --j ) {
5280 // Start at last tab, if any.
5281 QTextStringChar &ch = string->at( j );
5282 if ( ch.c == '\t' ) {
5283 start = j+1;
5284 break;
5285 }
5286 if(ch.whiteSpace)
5287 numSpaces++;
5288 }
5289 int toAdd = 0;
5290 for ( int k = start + 1; k <= last; ++k ) {
5291 QTextStringChar &ch = string->at( k );
5292 if( numSpaces && ch.whiteSpace ) {
5293 int s = space / numSpaces;
5294 toAdd += s;
5295 space -= s;
5296 numSpaces--;
5297 }
5298 string->at( k ).x += toAdd;
5299 }
5300 }
5301
5302 if ( last >= 0 && last < string->length() )
5303 line->w = string->at( last ).x + string->width( last );
5304 else
5305 line->w = 0;
5306
5307 return new QTextLineStart;
5308}
5309
5310#ifndef QT_NO_COMPLEXTEXT
5311
5312#ifdef BIDI_DEBUG
5313#include <iostream>
5314#endif
5315
5316// collects one line of the paragraph and transforms it to visual order
5317QTextLineStart *QTextFormatter::bidiReorderLine( QTextParagraph * /*parag*/, QTextString *text, QTextLineStart *line,
5318 QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
5319{
5320 // ignore white space at the end of the line.
5321 int endSpaces = 0;
5322 while ( lastChar > startChar && lastChar->whiteSpace ) {
5323 space += lastChar->format()->width( ' ' );
5324 --lastChar;
5325 ++endSpaces;
5326 }
5327
5328 int start = (startChar - &text->at(0));
5329 int last = (lastChar - &text->at(0) );
5330
5331 int length = lastChar - startChar + 1;
5332
5333
5334 int x = startChar->x;
5335
5336 unsigned char _levels[256];
5337 int _visual[256];
5338
5339 unsigned char *levels = _levels;
5340 int *visual = _visual;
5341
5342 if ( length > 255 ) {
5343 levels = (unsigned char *)malloc( length*sizeof( unsigned char ) );
5344 visual = (int *)malloc( length*sizeof( int ) );
5345 }
5346
5347 //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last );
5348
5349 QTextStringChar *ch = startChar;
5350 unsigned char *l = levels;
5351 while ( ch <= lastChar ) {
5352 //qDebug( " level: %d", ch->bidiLevel );
5353 *(l++) = (ch++)->bidiLevel;
5354 }
5355
5356 QTextEngine::bidiReorder( length, levels, visual );
5357
5358 // now construct the reordered string out of the runs...
5359
5360 int numSpaces = 0;
5361 // set the correct alignment. This is a bit messy....
5362 if( align == Qt::AlignAuto ) {
5363 // align according to directionality of the paragraph...
5364 if ( text->isRightToLeft() )
5365 align = Qt::AlignRight;
5366 }
5367
5368 // This is not really correct, but as we can't make the scrollbar move to the left of the origin,
5369 // this ensures all text can be scrolled to and read.
5370 if (space < 0)
5371 space = 0;
5372
5373 if ( align & Qt::AlignHCenter )
5374 x += space/2;
5375 else if ( align & Qt::AlignRight )
5376 x += space;
5377 else if ( align & Qt::AlignJustify ) {
5378 // End at "last-1", the last space ends up with a width of 0
5379 for ( int j = last-1; j >= start; --j ) {
5380 // Start at last tab, if any.
5381 QTextStringChar &ch = text->at( j );
5382 if ( ch.c == '\t' ) {
5383 start = j+1;
5384 break;
5385 }
5386 if(ch.whiteSpace)
5387 numSpaces++;
5388 }
5389 }
5390
5391 int toAdd = 0;
5392 int xorig = x;
5393 QTextStringChar *lc = startChar + visual[0];
5394 for ( int i = 0; i < length; i++ ) {
5395 QTextStringChar *ch = startChar + visual[i];
5396 if (numSpaces && ch->whiteSpace) {
5397 int s = space / numSpaces;
5398 toAdd += s;
5399 space -= s;
5400 numSpaces--;
5401 }
5402
5403 if (lc->format() != ch->format() && !ch->c.isSpace()
5404 && lc->format()->font().italic() && !ch->format()->font().italic()) {
5405 int rb = lc->format()->fontMetrics().rightBearing(lc->c);
5406 if (rb < 0)
5407 x -= rb;
5408 }
5409
5410 ch->x = x + toAdd;
5411 //qDebug("visual: %d (%p) placed at %d rightToLeft=%d", visual[i], ch, x +toAdd, ch->rightToLeft );
5412 int ww = 0;
5413 if ( ch->c.unicode() >= 32 || ch->c == '\t' || ch->c == '\n' || ch->isCustom() ) {
5414 ww = text->width( start+visual[i] );
5415 } else {
5416 ww = ch->format()->width( ' ' );
5417 }
5418 x += ww;
5419 lc = ch;
5420 }
5421 x += toAdd;
5422
5423 while ( endSpaces-- ) {
5424 ++lastChar;
5425 int sw = lastChar->format()->width( ' ' );
5426 if ( lastChar->rightToLeft ) {
5427 xorig -= sw;
5428 lastChar->x = xorig;
5429 } else {
5430 lastChar->x = x;
5431 x += sw;
5432 }
5433 }
5434
5435 line->w = x;
5436
5437 if ( length > 255 ) {
5438 free( levels );
5439 free( visual );
5440 }
5441
5442 return new QTextLineStart;
5443}
5444#endif
5445
5446
5447void QTextFormatter::insertLineStart( QTextParagraph *parag, int index, QTextLineStart *ls )
5448{
5449 QMap<int, QTextLineStart*>::Iterator it;
5450 if ( ( it = parag->lineStartList().find( index ) ) == parag->lineStartList().end() ) {
5451 parag->lineStartList().insert( index, ls );
5452 } else {
5453 delete *it;
5454 parag->lineStartList().remove( it );
5455 parag->lineStartList().insert( index, ls );
5456 }
5457}
5458
5459
5460/* Standard pagebreak algorithm using QTextFlow::adjustFlow. Returns
5461 the shift of the paragraphs bottom line.
5462 */
5463int QTextFormatter::formatVertically( QTextDocument* doc, QTextParagraph* parag )
5464{
5465 int oldHeight = parag->rect().height();
5466 QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList();
5467 QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
5468 int h = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin() ) / 2: 0;
5469 for ( ; it != lineStarts.end() ; ++it ) {
5470 QTextLineStart * ls = it.data();
5471 ls->y = h;
5472 QTextStringChar *c = &parag->string()->at(it.key());
5473#ifndef QT_NO_TEXTCUSTOMITEM
5474 if ( c && c->customItem() && c->customItem()->ownLine() ) {
5475 int h = c->customItem()->height;
5476 c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
5477 int delta = c->customItem()->height - h;
5478 ls->h += delta;
5479 if ( delta )
5480 parag->setMovedDown( TRUE );
5481 } else
5482#endif
5483 {
5484
5485 int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
5486 ls->y += shift;
5487 if ( shift )
5488 parag->setMovedDown( TRUE );
5489 }
5490 h = ls->y + ls->h;
5491 }
5492 int m = parag->bottomMargin();
5493 if ( !parag->next() )
5494 m = 0;
5495 else
5496 m = QMAX(m, parag->next()->topMargin() ) / 2;
5497 h += m;
5498 parag->setHeight( h );
5499 return h - oldHeight;
5500}
5501
5502// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5503
5504QTextFormatterBreakInWords::QTextFormatterBreakInWords()
5505{
5506}
5507
5508#define SPACE(s) s
5509
5510int QTextFormatterBreakInWords::format( QTextDocument *doc,QTextParagraph *parag,
5511 int start, const QMap<int, QTextLineStart*> & )
5512{
5513 // make sure bidi information is correct.
5514 (void )parag->string()->isBidi();
5515
5516 QTextStringChar *c = 0;
5517 QTextStringChar *firstChar = 0;
5518 int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
5519 int x = left + ( doc ? parag->firstLineMargin() : 0 );
5520 int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 0 );
5521 int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
5522 int h = y;
5523 int len = parag->length();
5524 if ( doc )
5525 x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 4 );
5526 int rm = parag->rightMargin();
5527 int w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
5528 bool fullWidth = TRUE;
5529 int minw = 0;
5530 int wused = 0;
5531 bool wrapEnabled = isWrapEnabled( parag );
5532
5533 start = 0; //######### what is the point with start?! (Matthias)
5534 if ( start == 0 )
5535 c = &parag->string()->at( 0 );
5536
5537 int i = start;
5538 QTextLineStart *lineStart = new QTextLineStart( y, y, 0 );
5539 insertLineStart( parag, 0, lineStart );
5540
5541 QPainter *painter = QTextFormat::painter();
5542
5543 int col = 0;
5544 int ww = 0;
5545 QChar lastChr;
5546 for ( ; i < len; ++i, ++col ) {
5547 if ( c )
5548 lastChr = c->c;
5549 c = &parag->string()->at( i );
5550 // ### the lines below should not be needed
5551 if ( painter )
5552 c->format()->setPainter( painter );
5553 if ( i > 0 ) {
5554 c->lineStart = 0;
5555 } else {
5556 c->lineStart = 1;
5557 firstChar = c;
5558 }
5559 if ( c->c.unicode() >= 32 || c->isCustom() ) {
5560 ww = parag->string()->width( i );
5561 } else if ( c->c == '\t' ) {
5562 int nx = parag->nextTab( i, x - left ) + left;
5563 if ( nx < x )
5564 ww = w - x;
5565 else
5566 ww = nx - x;
5567 } else {
5568 ww = c->format()->width( ' ' );
5569 }
5570
5571#ifndef QT_NO_TEXTCUSTOMITEM
5572 if ( c->isCustom() && c->customItem()->ownLine() ) {
5573 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
5574 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
5575 c->customItem()->resize( w - x );
5576 w = dw;
5577 y += h;
5578 h = c->height();
5579 lineStart = new QTextLineStart( y, h, h );
5580 insertLineStart( parag, i, lineStart );
5581 c->lineStart = 1;
5582 firstChar = c;
5583 x = 0xffffff;
5584 continue;
5585 }
5586#endif
5587
5588 if ( wrapEnabled &&
5589 ( wrapAtColumn() == -1 && x + ww > w ||
5590 wrapAtColumn() != -1 && col >= wrapAtColumn() ) ) {
5591 x = doc ? parag->document()->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
5592 w = dw;
5593 y += h;
5594 h = c->height();
5595 lineStart = formatLine( parag, parag->string(), lineStart, firstChar, c-1 );
5596 lineStart->y = y;
5597 insertLineStart( parag, i, lineStart );
5598 lineStart->baseLine = c->ascent();
5599 lineStart->h = c->height();
5600 c->lineStart = 1;
5601 firstChar = c;
5602 col = 0;
5603 if ( wrapAtColumn() != -1 )
5604 minw = QMAX( minw, w );
5605 } else if ( lineStart ) {
5606 lineStart->baseLine = QMAX( lineStart->baseLine, c->ascent() );
5607 h = QMAX( h, c->height() );
5608 lineStart->h = h;
5609 }
5610
5611 c->x = x;
5612 x += ww;
5613 wused = QMAX( wused, x );
5614 }
5615
5616 int m = parag->bottomMargin();
5617 if ( !parag->next() )
5618 m = 0;
5619 else
5620 m = QMAX(m, parag->next()->topMargin() ) / 2;
5621 parag->setFullWidth( fullWidth );
5622 y += h + m;
5623 if ( doc )
5624 minw += doc->rightMargin();
5625 if ( !wrapEnabled )
5626 minw = QMAX(minw, wused);
5627
5628 thisminw = minw;
5629 thiswused = wused;
5630 return y;
5631}
5632
5633// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5634
5635QTextFormatterBreakWords::QTextFormatterBreakWords()
5636{
5637}
5638
5639#define DO_FLOW( lineStart ) do{ if ( doc && doc->isPageBreakEnabled() ) { \
5640 int yflow = lineStart->y + parag->rect().y();\
5641 int shift = doc->flow()->adjustFlow( yflow, dw, lineStart->h ); \
5642 lineStart->y += shift;\
5643 y += shift;\
5644 }}while(FALSE)
5645
5646int QTextFormatterBreakWords::format( QTextDocument *doc, QTextParagraph *parag,
5647 int start, const QMap<int, QTextLineStart*> & )
5648{
5649 // make sure bidi information is correct.
5650 (void )parag->string()->isBidi();
5651
5652 QTextStringChar *c = 0;
5653 QTextStringChar *firstChar = 0;
5654 QTextString *string = parag->string();
5655 int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
5656 int x = left + ( doc ? parag->firstLineMargin() : 0 );
5657 int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
5658 int h = y;
5659 int len = parag->length();
5660 if ( doc )
5661 x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 0 );
5662 int dw = parag->documentVisibleWidth() - ( doc ? ( left != x ? 0 : doc->rightMargin() ) : 0 );
5663
5664 int curLeft = x;
5665 int rm = parag->rightMargin();
5666 int rdiff = doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 0 ) : 0;
5667 int w = dw - rdiff;
5668 bool fullWidth = TRUE;
5669 int marg = left + rdiff;
5670 int minw = 0;
5671 int wused = 0;
5672 int tminw = marg;
5673 int linespacing = doc ? parag->lineSpacing() : 0;
5674 bool wrapEnabled = isWrapEnabled( parag );
5675
5676 start = 0;
5677
5678 int i = start;
5679 QTextLineStart *lineStart = new QTextLineStart( y, y, 0 );
5680 insertLineStart( parag, 0, lineStart );
5681 int lastBreak = -1;
5682 int tmpBaseLine = 0, tmph = 0;
5683 bool lastWasNonInlineCustom = FALSE;
5684
5685 int align = parag->alignment();
5686 if ( align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto )
5687 align = doc->alignment();
5688
5689 align &= Qt::AlignHorizontal_Mask;
5690
5691 // ### hack. The last char in the paragraph is always invisible,
5692 // ### and somehow sometimes has a wrong format. It changes
5693 // ### between // layouting and printing. This corrects some
5694 // ### layouting errors in BiDi mode due to this.
5695 if ( len > 1 ) {
5696 c = &parag->string()->at(len - 1);
5697 if (!c->isAnchor()) {
5698 c->format()->removeRef();
5699 c->setFormat( string->at( len - 2 ).format() );
5700 c->format()->addRef();
5701 }
5702 }
5703
5704 c = &parag->string()->at( 0 );
5705
5706 QPainter *painter = QTextFormat::painter();
5707 int col = 0;
5708 int ww = 0;
5709 QChar lastChr = c->c;
5710 QTextFormat *lastFormat = c->format();
5711 for ( ; i < len; ++i, ++col ) {
5712 if ( i ) {
5713 c = &parag->string()->at(i-1);
5714 lastChr = c->c;
5715 lastFormat = c->format();
5716 }
5717 bool lastWasOwnLineCustomItem = lastBreak == -2;
5718 bool hadBreakableChar = lastBreak != -1;
5719 bool lastWasHardBreak = lastChr == QChar_linesep;
5720
5721 // ### next line should not be needed
5722 if ( painter )
5723 c->format()->setPainter( painter );
5724 c = &string->at( i );
5725
5726 if (lastFormat != c->format() && !c->c.isSpace()
5727 && lastFormat->font().italic() && !c->format()->font().italic()) {
5728 int rb = lastFormat->fontMetrics().rightBearing(lastChr);
5729 if (rb < 0)
5730 x -= rb;
5731 }
5732
5733 if ( i > 0 && (x > curLeft || ww == 0) || lastWasNonInlineCustom ) {
5734 c->lineStart = 0;
5735 } else {
5736 c->lineStart = 1;
5737 firstChar = c;
5738 }
5739
5740#ifndef QT_NO_TEXTCUSTOMITEM
5741 lastWasNonInlineCustom = ( c->isCustom() && c->customItem()->placement() != QTextCustomItem::PlaceInline );
5742#endif
5743
5744 if ( c->c.unicode() >= 32 || c->isCustom() ) {
5745 ww = string->width( i );
5746 } else if ( c->c == '\t' ) {
5747 if ( align == Qt::AlignRight || align == Qt::AlignCenter ) {
5748 // we can not (yet) do tabs
5749 ww = c->format()->width(' ' );
5750 } else {
5751 int tabx = lastWasHardBreak ? (left + ( doc ? parag->firstLineMargin() : 0 )) : x;
5752 int nx = parag->nextTab( i, tabx - left ) + left;
5753 if ( nx < tabx ) // strrrange...
5754 ww = 0;
5755 else
5756 ww = nx - tabx;
5757 }
5758 } else {
5759 ww = c->format()->width( ' ' );
5760 }
5761
5762#ifndef QT_NO_TEXTCUSTOMITEM
5763 QTextCustomItem* ci = c->customItem();
5764 if ( c->isCustom() && ci->ownLine() ) {
5765 QTextLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww) );
5766 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
5767 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
5768 ci->resize(w - x);
5769 if ( ci->width < w - x ) {
5770 if ( align & Qt::AlignHCenter )
5771 x = ( w - ci->width ) / 2;
5772 else if ( align & Qt::AlignRight ) {
5773 x = w - ci->width;
5774 }
5775 }
5776 c->x = x;
5777 curLeft = x;
5778 if ( i == 0 || !isBreakable(string, i-1) ||
5779 string->at( i - 1 ).lineStart == 0 ) {
5780 y += QMAX( h, QMAX( tmph, linespacing ) );
5781 tmph = c->height();
5782 h = tmph;
5783 lineStart = lineStart2;
5784 lineStart->y = y;
5785 insertLineStart( parag, i, lineStart );
5786 c->lineStart = 1;
5787 firstChar = c;
5788 } else {
5789 tmph = c->height();
5790 h = tmph;
5791 delete lineStart2;
5792 }
5793 lineStart->h = h;
5794 lineStart->baseLine = h;
5795 tmpBaseLine = lineStart->baseLine;
5796 lastBreak = -2;
5797 x = w;
5798 minw = QMAX( minw, tminw );
5799
5800 int tw = ci->minimumWidth() + ( doc ? doc->leftMargin() : 0 );
5801 if ( tw < QWIDGETSIZE_MAX )
5802 tminw = tw;
5803 else
5804 tminw = marg;
5805 wused = QMAX( wused, ci->width );
5806 continue;
5807 } else if ( c->isCustom() && ci->placement() != QTextCustomItem::PlaceInline ) {
5808 int tw = ci->minimumWidth();
5809 if ( tw < QWIDGETSIZE_MAX )
5810 minw = QMAX( minw, tw );
5811 }
5812#endif
5813 // we break if
5814 // 1. the last character was a hard break (QChar_linesep) or
5815 // 2. the last charater was a own-line custom item (eg. table or ruler) or
5816 // 3. wrapping was enabled, it was not a space and following
5817 // condition is true: We either had a breakable character
5818 // previously or we ar allowed to break in words and - either
5819 // we break at w pixels and the current char would exceed that
5820 // or - we break at a column and the current character would
5821 // exceed that.
5822 if ( lastWasHardBreak || lastWasOwnLineCustomItem ||
5823 ( wrapEnabled &&
5824 ( (!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) &&
5825 ( (wrapAtColumn() == -1 && x + ww > w) ||
5826 (wrapAtColumn() != -1 && col >= wrapAtColumn()) ) ) )
5827 )
5828 ) {
5829 if ( wrapAtColumn() != -1 )
5830 minw = QMAX( minw, x + ww );
5831 // if a break was forced (no breakable char, hard break or own line custom item), break immediately....
5832 if ( !hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem ) {
5833 if ( lineStart ) {
5834 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
5835 h = QMAX( h, tmph );
5836 lineStart->h = h;
5837 DO_FLOW( lineStart );
5838 }
5839 lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) );
5840 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
5841 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
5842 if ( !doc && c->c == '\t' ) { // qt_format_text tab handling
5843 int nx = parag->nextTab( i, x - left ) + left;
5844 if ( nx < x )
5845 ww = w - x;
5846 else
5847 ww = nx - x;
5848 }
5849 curLeft = x;
5850 y += QMAX( h, linespacing );
5851 tmph = c->height();
5852 h = 0;
5853 lineStart->y = y;
5854 insertLineStart( parag, i, lineStart );
5855 lineStart->baseLine = c->ascent();
5856 lineStart->h = c->height();
5857 c->lineStart = 1;
5858 firstChar = c;
5859 tmpBaseLine = lineStart->baseLine;
5860 lastBreak = -1;
5861 col = 0;
5862 if ( allowBreakInWords() || lastWasHardBreak ) {
5863 minw = QMAX(minw, tminw);
5864 tminw = marg;
5865 }
5866 } else { // ... otherwise if we had a breakable char, break there
5867 DO_FLOW( lineStart );
5868 c->x = x;
5869 i = lastBreak;
5870 lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ),align, SPACE(w - string->at( i+1 ).x) );
5871 x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
5872 w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
5873 if ( !doc && c->c == '\t' ) { // qt_format_text tab handling
5874 int nx = parag->nextTab( i, x - left ) + left;
5875 if ( nx < x )
5876 ww = w - x;
5877 else
5878 ww = nx - x;
5879 }
5880 curLeft = x;
5881 y += QMAX( h, linespacing );
5882 tmph = c->height();
5883 h = tmph;
5884 lineStart->y = y;
5885 insertLineStart( parag, i + 1, lineStart );
5886 lineStart->baseLine = c->ascent();
5887 lineStart->h = c->height();
5888 c->lineStart = 1;
5889 firstChar = c;
5890 tmpBaseLine = lineStart->baseLine;
5891 lastBreak = -1;
5892 col = 0;
5893 minw = QMAX(minw, tminw);
5894 tminw = marg;
5895 continue;
5896 }
5897 } else if (lineStart && isBreakable(string, i)) {
5898 if ( len <= 2 || i < len - 1 ) {
5899 tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
5900 tmph = QMAX( tmph, c->height() );
5901 }
5902 minw = QMAX( minw, tminw );
5903
5904 tminw = marg + ww;
5905 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
5906 h = QMAX( h, tmph );
5907 lineStart->h = h;
5908 if ( i < len - 2 || c->c != ' ' )
5909 lastBreak = i;
5910 } else {
5911 tminw += ww;
5912 int cascent = c->ascent();
5913 int cheight = c->height();
5914 int belowBaseLine = QMAX( tmph - tmpBaseLine, cheight-cascent );
5915 tmpBaseLine = QMAX( tmpBaseLine, cascent );
5916 tmph = tmpBaseLine + belowBaseLine;
5917 }
5918
5919 c->x = x;
5920 x += ww;
5921 wused = QMAX( wused, x );
5922 }
5923
5924 if ( lineStart ) {
5925 lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
5926 h = QMAX( h, tmph );
5927 lineStart->h = h;
5928 // last line in a paragraph is not justified
5929 if ( align == Qt::AlignJustify )
5930 align = Qt::AlignAuto;
5931 DO_FLOW( lineStart );
5932 lineStart = formatLine( parag, string, lineStart, firstChar, c, align, SPACE(w - x) );
5933 delete lineStart;
5934 }
5935
5936 minw = QMAX( minw, tminw );
5937 if ( doc )
5938 minw += doc->rightMargin();
5939
5940 int m = parag->bottomMargin();
5941 if ( !parag->next() )
5942 m = 0;
5943 else
5944 m = QMAX(m, parag->next()->topMargin() ) / 2;
5945 parag->setFullWidth( fullWidth );
5946 y += QMAX( h, linespacing ) + m;
5947
5948 wused += rm;
5949 if ( !wrapEnabled || wrapAtColumn() != -1 )
5950 minw = QMAX(minw, wused);
5951
5952 // This is the case where we are breaking wherever we darn well please
5953 // in cases like that, the minw should not be the length of the entire
5954 // word, because we necessarily want to show the word on the whole line.
5955 // example: word wrap in iconview
5956 if ( allowBreakInWords() && minw > wused )
5957 minw = wused;
5958
5959 thisminw = minw;
5960 thiswused = wused;
5961 return y;
5962}
5963
5964// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5965
5966QTextIndent::QTextIndent()
5967{
5968}
5969
5970// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5971
5972QTextFormatCollection::QTextFormatCollection()
5973 : cKey( 307 ), paintdevice( 0 )
5974{
5975 defFormat = new QTextFormat( QApplication::font(),
5976 QApplication::palette().color( QPalette::Active, QColorGroup::Text ) );
5977 lastFormat = cres = 0;
5978 cflags = -1;
5979 cKey.setAutoDelete( TRUE );
5980 cachedFormat = 0;
5981}
5982
5983QTextFormatCollection::~QTextFormatCollection()
5984{
5985 delete defFormat;
5986}
5987
5988void QTextFormatCollection::setPaintDevice( QPaintDevice *pd )
5989{
5990 paintdevice = pd;
5991
5992#if defined(Q_WS_X11)
5993 int scr = ( paintdevice ) ? paintdevice->x11Screen() : QPaintDevice::x11AppScreen();
5994
5995 defFormat->fn.x11SetScreen( scr );
5996 defFormat->update();
5997
5998 QDictIterator<QTextFormat> it( cKey );
5999 QTextFormat *format;
6000 while ( ( format = it.current() ) != 0 ) {
6001 ++it;
6002 format->fn.x11SetScreen( scr );
6003 format->update();
6004 }
6005#endif // Q_WS_X11
6006}
6007
6008QTextFormat *QTextFormatCollection::format( QTextFormat *f )
6009{
6010 if ( f->parent() == this || f == defFormat ) {
6011 lastFormat = f;
6012 lastFormat->addRef();
6013 return lastFormat;
6014 }
6015
6016 if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) {
6017 lastFormat->addRef();
6018 return lastFormat;
6019 }
6020
6021 QTextFormat *fm = cKey.find( f->key() );
6022 if ( fm ) {
6023 lastFormat = fm;
6024 lastFormat->addRef();
6025 return lastFormat;
6026 }
6027
6028 if ( f->key() == defFormat->key() )
6029 return defFormat;
6030
6031 lastFormat = createFormat( *f );
6032 lastFormat->collection = this;
6033 cKey.insert( lastFormat->key(), lastFormat );
6034 return lastFormat;
6035}
6036
6037QTextFormat *QTextFormatCollection::format( QTextFormat *of, QTextFormat *nf, int flags )
6038{
6039 if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) {
6040 cres->addRef();
6041 return cres;
6042 }
6043
6044 cres = createFormat( *of );
6045 kof = of->key();
6046 knf = nf->key();
6047 cflags = flags;
6048 if ( flags & QTextFormat::Bold )
6049 cres->fn.setBold( nf->fn.bold() );
6050 if ( flags & QTextFormat::Italic )
6051 cres->fn.setItalic( nf->fn.italic() );
6052 if ( flags & QTextFormat::Underline )
6053 cres->fn.setUnderline( nf->fn.underline() );
6054 if ( flags & QTextFormat::StrikeOut )
6055 cres->fn.setStrikeOut( nf->fn.strikeOut() );
6056 if ( flags & QTextFormat::Family )
6057 cres->fn.setFamily( nf->fn.family() );
6058 if ( flags & QTextFormat::Size ) {
6059 if ( of->usePixelSizes )
6060 cres->fn.setPixelSize( nf->fn.pixelSize() );
6061 else
6062 cres->fn.setPointSize( nf->fn.pointSize() );
6063 }
6064 if ( flags & QTextFormat::Color )
6065 cres->col = nf->col;
6066 if ( flags & QTextFormat::Misspelled )
6067 cres->missp = nf->missp;
6068 if ( flags & QTextFormat::VAlign )
6069 cres->ha = nf->ha;
6070 cres->update();
6071
6072 QTextFormat *fm = cKey.find( cres->key() );
6073 if ( !fm ) {
6074 cres->collection = this;
6075 cKey.insert( cres->key(), cres );
6076 } else {
6077 delete cres;
6078 cres = fm;
6079 cres->addRef();
6080 }
6081
6082 return cres;
6083}
6084
6085QTextFormat *QTextFormatCollection::format( const QFont &f, const QColor &c )
6086{
6087 if ( cachedFormat && cfont == f && ccol == c ) {
6088 cachedFormat->addRef();
6089 return cachedFormat;
6090 }
6091
6092 QString key = QTextFormat::getKey( f, c, FALSE, QTextFormat::AlignNormal );
6093 cachedFormat = cKey.find( key );
6094 cfont = f;
6095 ccol = c;
6096
6097 if ( cachedFormat ) {
6098 cachedFormat->addRef();
6099 return cachedFormat;
6100 }
6101
6102 if ( key == defFormat->key() )
6103 return defFormat;
6104
6105 cachedFormat = createFormat( f, c );
6106 cachedFormat->collection = this;
6107 cKey.insert( cachedFormat->key(), cachedFormat );
6108 if ( cachedFormat->key() != key )
6109 qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1() );
6110 return cachedFormat;
6111}
6112
6113void QTextFormatCollection::remove( QTextFormat *f )
6114{
6115 if ( lastFormat == f )
6116 lastFormat = 0;
6117 if ( cres == f )
6118 cres = 0;
6119 if ( cachedFormat == f )
6120 cachedFormat = 0;
6121 if (cKey.find(f->key()) == f)
6122 cKey.remove( f->key() );
6123}
6124
6125#define UPDATE( up, lo, rest ) \
6126 if ( font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest() ) \
6127 fm->fn.set##up##rest( font.lo##rest() )
6128
6129void QTextFormatCollection::updateDefaultFormat( const QFont &font, const QColor &color, QStyleSheet *sheet )
6130{
6131 QDictIterator<QTextFormat> it( cKey );
6132 QTextFormat *fm;
6133 bool usePixels = font.pointSize() == -1;
6134 bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() :
6135 font.pointSize() != defFormat->fn.pointSize();
6136 int base = usePixels ? font.pixelSize() : font.pointSize();
6137 while ( ( fm = it.current() ) ) {
6138 ++it;
6139 UPDATE( F, f, amily );
6140 UPDATE( W, w, eight );
6141 UPDATE( B, b, old );
6142 UPDATE( I, i, talic );
6143 UPDATE( U, u, nderline );
6144 if ( changeSize ) {
6145 fm->stdSize = base;
6146 fm->usePixelSizes = usePixels;
6147 if ( usePixels )
6148 fm->fn.setPixelSize( fm->stdSize );
6149 else
6150 fm->fn.setPointSize( fm->stdSize );
6151 sheet->scaleFont( fm->fn, fm->logicalFontSize );
6152 }
6153 if ( color.isValid() && color != defFormat->col && fm->col == defFormat->col )
6154 fm->col = color;
6155 fm->update();
6156 }
6157
6158 defFormat->fn = font;
6159 defFormat->col = color;
6160 defFormat->update();
6161 defFormat->stdSize = base;
6162 defFormat->usePixelSizes = usePixels;
6163
6164 updateKeys();
6165}
6166
6167// the keys in cKey have changed, rebuild the hashtable
6168void QTextFormatCollection::updateKeys()
6169{
6170 if ( cKey.isEmpty() )
6171 return;
6172 cKey.setAutoDelete( FALSE );
6173 QTextFormat** formats = new QTextFormat*[ cKey.count() + 1 ];
6174 QTextFormat **f = formats;
6175 QDictIterator<QTextFormat> it( cKey );
6176 while ( ( *f = it.current() ) ) {
6177 ++it;
6178 ++f;
6179 }
6180 cKey.clear();
6181 for ( f = formats; *f; f++ )
6182 cKey.insert( (*f)->key(), *f );
6183 cKey.setAutoDelete( TRUE );
6184 delete [] formats;
6185}
6186
6187
6188
6189// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6190
6191void QTextFormat::setBold( bool b )
6192{
6193 if ( b == fn.bold() )
6194 return;
6195 fn.setBold( b );
6196 update();
6197}
6198
6199void QTextFormat::setMisspelled( bool b )
6200{
6201 if ( b == (bool)missp )
6202 return;
6203 missp = b;
6204 update();
6205}
6206
6207void QTextFormat::setVAlign( VerticalAlignment a )
6208{
6209 if ( a == ha )
6210 return;
6211 ha = a;
6212 update();
6213}
6214
6215void QTextFormat::setItalic( bool b )
6216{
6217 if ( b == fn.italic() )
6218 return;
6219 fn.setItalic( b );
6220 update();
6221}
6222
6223void QTextFormat::setUnderline( bool b )
6224{
6225 if ( b == fn.underline() )
6226 return;
6227 fn.setUnderline( b );
6228 update();
6229}
6230
6231void QTextFormat::setStrikeOut( bool b )
6232{
6233 if ( b == fn.strikeOut() )
6234 return;
6235 fn.setStrikeOut( b );
6236 update();
6237}
6238
6239void QTextFormat::setFamily( const QString &f )
6240{
6241 if ( f == fn.family() )
6242 return;
6243 fn.setFamily( f );
6244 update();
6245}
6246
6247void QTextFormat::setPointSize( int s )
6248{
6249 if ( s == fn.pointSize() )
6250 return;
6251 fn.setPointSize( s );
6252 usePixelSizes = FALSE;
6253 update();
6254}
6255
6256void QTextFormat::setFont( const QFont &f )
6257{
6258 if ( f == fn && !k.isEmpty() )
6259 return;
6260 fn = f;
6261 update();
6262}
6263
6264void QTextFormat::setColor( const QColor &c )
6265{
6266 if ( c == col )
6267 return;
6268 col = c;
6269 update();
6270}
6271
6272QString QTextFormat::makeFormatChangeTags( QTextFormat* defaultFormat, QTextFormat *f,
6273 const QString& oldAnchorHref, const QString& anchorHref ) const
6274{
6275 QString tag;
6276 if ( f )
6277 tag += f->makeFormatEndTags( defaultFormat, oldAnchorHref );
6278
6279 if ( !anchorHref.isEmpty() )
6280 tag += "<a href=\"" + anchorHref + "\">";
6281
6282 if ( font() != defaultFormat->font()
6283 || vAlign() != defaultFormat->vAlign()
6284 || color().rgb() != defaultFormat->color().rgb() ) {
6285 QString s;
6286 if ( font().family() != defaultFormat->font().family() )
6287 s += QString(!!s?";":"") + "font-family:" + fn.family();
6288 if ( font().italic() && font().italic() != defaultFormat->font().italic() )
6289 s += QString(!!s?";":"") + "font-style:" + (font().italic() ? "italic" : "normal");
6290 if ( font().pointSize() != defaultFormat->font().pointSize() )
6291 s += QString(!!s?";":"") + "font-size:" + QString::number( fn.pointSize() ) + "pt";
6292 if ( font().weight() != defaultFormat->font().weight() )
6293 s += QString(!!s?";":"") + "font-weight:" + QString::number( fn.weight() * 8 );
6294 if ( font().underline() != defaultFormat->font().underline() )
6295 s += QString(!!s?";":"") + "text-decoration:" + ( font().underline() ? "underline" : "none");
6296 if ( vAlign() != defaultFormat->vAlign() ) {
6297 s += QString(!!s?";":"") + "vertical-align:";
6298 if ( vAlign() == QTextFormat::AlignSuperScript )
6299 s += "super";
6300 else if ( vAlign() == QTextFormat::AlignSubScript )
6301 s += "sub";
6302 else
6303 s += "normal";
6304 }
6305 if ( color().rgb() != defaultFormat->color().rgb() )
6306 s += QString(!!s?";":"") + "color:" + col.name();
6307 if ( !s.isEmpty() )
6308 tag += "<span style=\"" + s + "\">";
6309 }
6310
6311 return tag;
6312}
6313
6314QString QTextFormat::makeFormatEndTags( QTextFormat* defaultFormat, const QString& anchorHref ) const
6315{
6316 QString tag;
6317 if ( font().family() != defaultFormat->font().family()
6318 || font().pointSize() != defaultFormat->font().pointSize()
6319 || font().weight() != defaultFormat->font().weight()
6320 || font().italic() != defaultFormat->font().italic()
6321 || font().underline() != defaultFormat->font().underline()
6322 || font().strikeOut() != defaultFormat->font().strikeOut()
6323 || vAlign() != defaultFormat->vAlign()
6324 || color().rgb() != defaultFormat->color().rgb() )
6325 tag += "</span>";
6326 if ( !anchorHref.isEmpty() )
6327 tag += "</a>";
6328 return tag;
6329}
6330
6331QTextFormat QTextFormat::makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor ) const
6332{
6333 QTextFormat format(*this);
6334 if (!style )
6335 return format;
6336
6337 if ( !style->isAnchor() && style->color().isValid() ) {
6338 // the style is not an anchor and defines a color.
6339 // It might be used inside an anchor and it should
6340 // override the link color.
6341 format.linkColor = FALSE;
6342 }
6343 switch ( style->verticalAlignment() ) {
6344 case QStyleSheetItem::VAlignBaseline:
6345 format.setVAlign( QTextFormat::AlignNormal );
6346 break;
6347 case QStyleSheetItem::VAlignSuper:
6348 format.setVAlign( QTextFormat::AlignSuperScript );
6349 break;
6350 case QStyleSheetItem::VAlignSub:
6351 format.setVAlign( QTextFormat::AlignSubScript );
6352 break;
6353 }
6354
6355 if ( style->fontWeight() != QStyleSheetItem::Undefined )
6356 format.fn.setWeight( style->fontWeight() );
6357 if ( style->fontSize() != QStyleSheetItem::Undefined ) {
6358 format.fn.setPointSize( style->fontSize() );
6359 } else if ( style->logicalFontSize() != QStyleSheetItem::Undefined ) {
6360 format.logicalFontSize = style->logicalFontSize();
6361 if ( format.usePixelSizes )
6362 format.fn.setPixelSize( format.stdSize );
6363 else
6364 format.fn.setPointSize( format.stdSize );
6365 style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
6366 } else if ( style->logicalFontSizeStep() ) {
6367 format.logicalFontSize += style->logicalFontSizeStep();
6368 if ( format.usePixelSizes )
6369 format.fn.setPixelSize( format.stdSize );
6370 else
6371 format.fn.setPointSize( format.stdSize );
6372 style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
6373 }
6374 if ( !style->fontFamily().isEmpty() )
6375 format.fn.setFamily( style->fontFamily() );
6376 if ( style->color().isValid() )
6377 format.col = style->color();
6378 if ( style->definesFontItalic() )
6379 format.fn.setItalic( style->fontItalic() );
6380 if ( style->definesFontUnderline() )
6381 format.fn.setUnderline( style->fontUnderline() );
6382 if ( style->definesFontStrikeOut() )
6383 format.fn.setStrikeOut( style->fontStrikeOut() );
6384
6385
6386 if ( style->name() == "font") {
6387 if ( attr.contains("color") ) {
6388 QString s = attr["color"];
6389 if ( !s.isEmpty() ) {
6390 format.col.setNamedColor( s );
6391 format.linkColor = FALSE;
6392 }
6393 }
6394 if ( attr.contains("face") ) {
6395 QString a = attr["face"];
6396 QString family = a.section( ',', 0, 0 );
6397 if ( !!family )
6398 format.fn.setFamily( family );
6399 }
6400 if ( attr.contains("size") ) {
6401 QString a = attr["size"];
6402 int n = a.toInt();
6403 if ( a[0] == '+' || a[0] == '-' )
6404 n += 3;
6405 format.logicalFontSize = n;
6406 if ( format.usePixelSizes )
6407 format.fn.setPixelSize( format.stdSize );
6408 else
6409 format.fn.setPointSize( format.stdSize );
6410 style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
6411 }
6412 }
6413 if ( attr.contains("style" ) ) {
6414 QString a = attr["style"];
6415 for ( int s = 0; s < a.contains(';')+1; s++ ) {
6416 QString style = a.section( ';', s, s );
6417 if ( style.startsWith("font-size:" ) && style.endsWith("pt") ) {
6418 format.logicalFontSize = 0;
6419 int size = int( scaleFontsFactor * style.mid( 10, style.length() - 12 ).toDouble() );
6420 format.setPointSize( size );
6421 } if ( style.startsWith("font-style:" ) ) {
6422 QString s = style.mid( 11 ).stripWhiteSpace();
6423 if ( s == "normal" )
6424 format.fn.setItalic( FALSE );
6425 else if ( s == "italic" || s == "oblique" )
6426 format.fn.setItalic( TRUE );
6427 } else if ( style.startsWith("font-weight:" ) ) {
6428 QString s = style.mid( 12 );
6429 bool ok = TRUE;
6430 int n = s.toInt( &ok );
6431 if ( ok )
6432 format.fn.setWeight( n/8 );
6433 } else if ( style.startsWith("font-family:" ) ) {
6434 QString family = style.mid(12).section(',',0,0);
6435 family.replace( '\"', ' ' );
6436 family.replace( '\'', ' ' );
6437 family = family.stripWhiteSpace();
6438 format.fn.setFamily( family );
6439 } else if ( style.startsWith("text-decoration:" ) ) {
6440 QString s = style.mid( 16 ).stripWhiteSpace();
6441 format.fn.setUnderline( s == "underline" );
6442 } else if ( style.startsWith("vertical-align:" ) ) {
6443 QString s = style.mid( 15 ).stripWhiteSpace();
6444 if ( s == "sub" )
6445 format.setVAlign( QTextFormat::AlignSubScript );
6446 else if ( s == "super" )
6447 format.setVAlign( QTextFormat::AlignSuperScript );
6448 else
6449 format.setVAlign( QTextFormat::AlignNormal );
6450 } else if ( style.startsWith("color:" ) ) {
6451 format.col.setNamedColor( style.mid(6) );
6452 format.linkColor = FALSE;
6453 }
6454 }
6455 }
6456
6457 format.update();
6458 return format;
6459}
6460
6461#ifndef QT_NO_TEXTCUSTOMITEM
6462
6463struct QPixmapInt
6464{
6465 QPixmapInt() : ref( 0 ) {}
6466 QPixmap pm;
6467 int ref;
6468 Q_DUMMY_COMPARISON_OPERATOR(QPixmapInt)
6469};
6470
6471static QMap<QString, QPixmapInt> *pixmap_map = 0;
6472
6473QTextImage::QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
6474 QMimeSourceFactory &factory )
6475 : QTextCustomItem( p )
6476{
6477 width = height = 0;
6478 if ( attr.contains("width") )
6479 width = attr["width"].toInt();
6480 if ( attr.contains("height") )
6481 height = attr["height"].toInt();
6482
6483 reg = 0;
6484 QString imageName = attr["src"];
6485
6486 if (!imageName)
6487 imageName = attr["source"];
6488
6489 if ( !imageName.isEmpty() ) {
6490 imgId = QString( "%1,%2,%3,%4" ).arg( imageName ).arg( width ).arg( height ).arg( (ulong)&factory );
6491 if ( !pixmap_map )
6492 pixmap_map = new QMap<QString, QPixmapInt>;
6493 if ( pixmap_map->contains( imgId ) ) {
6494 QPixmapInt& pmi = pixmap_map->operator[](imgId);
6495 pm = pmi.pm;
6496 pmi.ref++;
6497 width = pm.width();
6498 height = pm.height();
6499 } else {
6500 QImage img;
6501 const QMimeSource* m =
6502 factory.data( imageName, context );
6503 if ( !m ) {
6504 qWarning("QTextImage: no mimesource for %s", imageName.latin1() );
6505 }
6506 else {
6507 if ( !QImageDrag::decode( m, img ) ) {
6508 qWarning("QTextImage: cannot decode %s", imageName.latin1() );
6509 }
6510 }
6511
6512 if ( !img.isNull() ) {
6513 if ( width == 0 ) {
6514 width = img.width();
6515 if ( height != 0 ) {
6516 width = img.width() * height / img.height();
6517 }
6518 }
6519 if ( height == 0 ) {
6520 height = img.height();
6521 if ( width != img.width() ) {
6522 height = img.height() * width / img.width();
6523 }
6524 }
6525 if ( img.width() != width || img.height() != height ){
6526#ifndef QT_NO_IMAGE_SMOOTHSCALE
6527 img = img.smoothScale(width, height);
6528#endif
6529 width = img.width();
6530 height = img.height();
6531 }
6532 pm.convertFromImage( img );
6533 }
6534 if ( !pm.isNull() ) {
6535 QPixmapInt& pmi = pixmap_map->operator[](imgId);
6536 pmi.pm = pm;
6537 pmi.ref++;
6538 }
6539 }
6540 if ( pm.mask() ) {
6541 QRegion mask( *pm.mask() );
6542 QRegion all( 0, 0, pm.width(), pm.height() );
6543 reg = new QRegion( all.subtract( mask ) );
6544 }
6545 }
6546
6547 if ( pm.isNull() && (width*height)==0 )
6548 width = height = 50;
6549
6550 place = PlaceInline;
6551 if ( attr["align"] == "left" )
6552 place = PlaceLeft;
6553 else if ( attr["align"] == "right" )
6554 place = PlaceRight;
6555
6556 tmpwidth = width;
6557 tmpheight = height;
6558
6559 attributes = attr;
6560}
6561
6562QTextImage::~QTextImage()
6563{
6564 if ( pixmap_map && pixmap_map->contains( imgId ) ) {
6565 QPixmapInt& pmi = pixmap_map->operator[](imgId);
6566 pmi.ref--;
6567 if ( !pmi.ref ) {
6568 pixmap_map->remove( imgId );
6569 if ( pixmap_map->isEmpty() ) {
6570 delete pixmap_map;
6571 pixmap_map = 0;
6572 }
6573 }
6574 }
6575 delete reg;
6576}
6577
6578QString QTextImage::richText() const
6579{
6580 QString s;
6581 s += "<img ";
6582 QMap<QString, QString>::ConstIterator it = attributes.begin();
6583 for ( ; it != attributes.end(); ++it ) {
6584 s += it.key() + "=";
6585 if ( (*it).find( ' ' ) != -1 )
6586 s += "\"" + *it + "\"" + " ";
6587 else
6588 s += *it + " ";
6589 }
6590 s += ">";
6591 return s;
6592}
6593
6594void QTextImage::adjustToPainter( QPainter* p )
6595{
6596 width = scale( tmpwidth, p );
6597 height = scale( tmpheight, p );
6598}
6599
6600#if !defined(Q_WS_X11)
6601#include <qbitmap.h>
6602#include <qcleanuphandler.h>
6603static QPixmap *qrt_selection = 0;
6604static QSingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
6605static void qrt_createSelectionPixmap( const QColorGroup &cg )
6606{
6607 qrt_selection = new QPixmap( 2, 2 );
6608 qrt_cleanup_pixmap.set( &qrt_selection );
6609 qrt_selection->fill( Qt::color0 );
6610 QBitmap m( 2, 2 );
6611 m.fill( Qt::color1 );
6612 QPainter p( &m );
6613 p.setPen( Qt::color0 );
6614 for ( int j = 0; j < 2; ++j ) {
6615 p.drawPoint( j % 2, j );
6616 }
6617 p.end();
6618 qrt_selection->setMask( m );
6619 qrt_selection->fill( cg.highlight() );
6620}
6621#endif
6622
6623void QTextImage::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
6624{
6625 if ( placement() != PlaceInline ) {
6626 x = xpos;
6627 y = ypos;
6628 }
6629
6630 if ( pm.isNull() ) {
6631 p->fillRect( x , y, width, height, cg.dark() );
6632 return;
6633 }
6634
6635 if ( is_printer( p ) ) {
6636 p->drawPixmap( QRect( x, y, width, height ), pm );
6637 return;
6638 }
6639
6640 if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) )
6641 return;
6642
6643 if ( placement() == PlaceInline )
6644 p->drawPixmap( x , y, pm );
6645 else
6646 p->drawPixmap( cx , cy, pm, cx - x, cy - y, cw, ch );
6647
6648 if ( selected && placement() == PlaceInline && is_printer( p ) ) {
6649#if defined(Q_WS_X11)
6650 p->fillRect( QRect( QPoint( x, y ), pm.size() ), QBrush( cg.highlight(), QBrush::Dense4Pattern) );
6651#else // in WIN32 Dense4Pattern doesn't work correctly (transparency problem), so work around it
6652 if ( !qrt_selection )
6653 qrt_createSelectionPixmap( cg );
6654 p->drawTiledPixmap( x, y, pm.width(), pm.height(), *qrt_selection );
6655#endif
6656 }
6657}
6658
6659void QTextHorizontalLine::adjustToPainter( QPainter* p )
6660{
6661 height = scale( tmpheight, p );
6662}
6663
6664
6665QTextHorizontalLine::QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr,
6666 const QString &,
6667 QMimeSourceFactory & )
6668 : QTextCustomItem( p )
6669{
6670 height = tmpheight = 8;
6671 if ( attr.find( "color" ) != attr.end() )
6672 color = QColor( *attr.find( "color" ) );
6673 shade = attr.find( "noshade" ) == attr.end();
6674}
6675
6676QTextHorizontalLine::~QTextHorizontalLine()
6677{
6678}
6679
6680QString QTextHorizontalLine::richText() const
6681{
6682 return "<hr>";
6683}
6684
6685void QTextHorizontalLine::draw( QPainter* p, int x, int y, int , int , int , int , const QColorGroup& cg, bool selected )
6686{
6687 QRect r( x, y, width, height);
6688 if ( is_printer( p ) || !shade ) {
6689 QPen oldPen = p->pen();
6690 if ( !color.isValid() )
6691 p->setPen( QPen( cg.text(), is_printer( p ) ? height/8 : QMAX( 2, height/4 ) ) );
6692 else
6693 p->setPen( QPen( color, is_printer( p ) ? height/8 : QMAX( 2, height/4 ) ) );
6694 p->drawLine( r.left()-1, y + height / 2, r.right() + 1, y + height / 2 );
6695 p->setPen( oldPen );
6696 } else {
6697 QColorGroup g( cg );
6698 if ( color.isValid() )
6699 g.setColor( QColorGroup::Dark, color );
6700 if ( selected )
6701 p->fillRect( r, g.highlight() );
6702 qDrawShadeLine( p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, g, TRUE, height / 8 );
6703 }
6704}
6705#endif //QT_NO_TEXTCUSTOMITEM
6706
6707/*****************************************************************/
6708// Small set of utility functions to make the parser a bit simpler
6709//
6710
6711bool QTextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
6712{
6713 if ( pos + 1 > length )
6714 return FALSE;
6715 return doc[ pos ].lower() == c.lower();
6716}
6717
6718bool QTextDocument::hasPrefix( const QChar* doc, int length, int pos, const QString& s )
6719{
6720 if ( pos + (int) s.length() > length )
6721 return FALSE;
6722 for ( int i = 0; i < (int)s.length(); i++ ) {
6723 if ( doc[ pos + i ].lower() != s[ i ].lower() )
6724 return FALSE;
6725 }
6726 return TRUE;
6727}
6728
6729#ifndef QT_NO_TEXTCUSTOMITEM
6730static bool qt_is_cell_in_use( QPtrList<QTextTableCell>& cells, int row, int col )
6731{
6732 for ( QTextTableCell* c = cells.first(); c; c = cells.next() ) {
6733 if ( row >= c->row() && row < c->row() + c->rowspan()
6734 && col >= c->column() && col < c->column() + c->colspan() )
6735 return TRUE;
6736 }
6737 return FALSE;
6738}
6739
6740QTextCustomItem* QTextDocument::parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt,
6741 const QChar* doc, int length, int& pos, QTextParagraph *curpar )
6742{
6743
6744 QTextTable* table = new QTextTable( this, attr );
6745 int row = -1;
6746 int col = -1;
6747
6748 QString rowbgcolor;
6749 QString rowalign;
6750 QString tablebgcolor = attr["bgcolor"];
6751
6752 QPtrList<QTextTableCell> multicells;
6753
6754 QString tagname;
6755 (void) eatSpace(doc, length, pos);
6756 while ( pos < length) {
6757 if (hasPrefix(doc, length, pos, QChar('<')) ){
6758 if (hasPrefix(doc, length, pos+1, QChar('/'))) {
6759 tagname = parseCloseTag( doc, length, pos );
6760 if ( tagname == "table" ) {
6761 return table;
6762 }
6763 } else {
6764 QMap<QString, QString> attr2;
6765 bool emptyTag = FALSE;
6766 tagname = parseOpenTag( doc, length, pos, attr2, emptyTag );
6767 if ( tagname == "tr" ) {
6768 rowbgcolor = attr2["bgcolor"];
6769 rowalign = attr2["align"];
6770 row++;
6771 col = -1;
6772 }
6773 else if ( tagname == "td" || tagname == "th" ) {
6774 col++;
6775 while ( qt_is_cell_in_use( multicells, row, col ) ) {
6776 col++;
6777 }
6778
6779 if ( row >= 0 && col >= 0 ) {
6780 const QStyleSheetItem* s = sheet_->item(tagname);
6781 if ( !attr2.contains("bgcolor") ) {
6782 if (!rowbgcolor.isEmpty() )
6783 attr2["bgcolor"] = rowbgcolor;
6784 else if (!tablebgcolor.isEmpty() )
6785 attr2["bgcolor"] = tablebgcolor;
6786 }
6787 if ( !attr2.contains("align") ) {
6788 if (!rowalign.isEmpty() )
6789 attr2["align"] = rowalign;
6790 }
6791
6792 // extract the cell contents
6793 int end = pos;
6794 while ( end < length
6795 && !hasPrefix( doc, length, end, "</td")
6796 && !hasPrefix( doc, length, end, "<td")
6797 && !hasPrefix( doc, length, end, "</th")
6798 && !hasPrefix( doc, length, end, "<th")
6799 && !hasPrefix( doc, length, end, "<td")
6800 && !hasPrefix( doc, length, end, "</tr")
6801 && !hasPrefix( doc, length, end, "<tr")
6802 && !hasPrefix( doc, length, end, "</table") ) {
6803 if ( hasPrefix( doc, length, end, "<table" ) ) { // nested table
6804 int nested = 1;
6805 ++end;
6806 while ( end < length && nested != 0 ) {
6807 if ( hasPrefix( doc, length, end, "</table" ) )
6808 nested--;
6809 if ( hasPrefix( doc, length, end, "<table" ) )
6810 nested++;
6811 end++;
6812 }
6813 }
6814 end++;
6815 }
6816 QTextTableCell* cell = new QTextTableCell( table, row, col,
6817 attr2, s, fmt.makeTextFormat( s, attr2, scaleFontsFactor ),
6818 contxt, *factory_, sheet_,
6819 QConstString( doc + pos, end - pos ).string() );
6820 cell->richText()->parentPar = curpar;
6821 if ( cell->colspan() > 1 || cell->rowspan() > 1 )
6822 multicells.append( cell );
6823 col += cell->colspan()-1;
6824 pos = end;
6825 }
6826 }
6827 }
6828
6829 } else {
6830 ++pos;
6831 }
6832 }
6833 return table;
6834}
6835#endif // QT_NO_TEXTCUSTOMITEM
6836
6837bool QTextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp )
6838{
6839 int old_pos = pos;
6840 while (pos < length && doc[pos].isSpace() && ( includeNbsp || (doc[pos] != QChar::nbsp ) ) )
6841 pos++;
6842 return old_pos < pos;
6843}
6844
6845bool QTextDocument::eat(const QChar* doc, int length, int& pos, QChar c)
6846{
6847 bool ok = pos < length && doc[pos] == c;
6848 if ( ok )
6849 pos++;
6850 return ok;
6851}
6852/*****************************************************************/
6853
6854struct Entity {
6855 const char * name;
6856 Q_UINT16 code;
6857};
6858
6859static const Entity entitylist [] = {
6860 { "AElig", 0x00c6 },
6861 { "Aacute", 0x00c1 },
6862 { "Acirc", 0x00c2 },
6863 { "Agrave", 0x00c0 },
6864 { "Alpha", 0x0391 },
6865 { "AMP", 38 },
6866 { "Aring", 0x00c5 },
6867 { "Atilde", 0x00c3 },
6868 { "Auml", 0x00c4 },
6869 { "Beta", 0x0392 },
6870 { "Ccedil", 0x00c7 },
6871 { "Chi", 0x03a7 },
6872 { "Dagger", 0x2021 },
6873 { "Delta", 0x0394 },
6874 { "ETH", 0x00d0 },
6875 { "Eacute", 0x00c9 },
6876 { "Ecirc", 0x00ca },
6877 { "Egrave", 0x00c8 },
6878 { "Epsilon", 0x0395 },
6879 { "Eta", 0x0397 },
6880 { "Euml", 0x00cb },
6881 { "Gamma", 0x0393 },
6882 { "GT", 62 },
6883 { "Iacute", 0x00cd },
6884 { "Icirc", 0x00ce },
6885 { "Igrave", 0x00cc },
6886 { "Iota", 0x0399 },
6887 { "Iuml", 0x00cf },
6888 { "Kappa", 0x039a },
6889 { "Lambda", 0x039b },
6890 { "LT", 60 },
6891 { "Mu", 0x039c },
6892 { "Ntilde", 0x00d1 },
6893 { "Nu", 0x039d },
6894 { "OElig", 0x0152 },
6895 { "Oacute", 0x00d3 },
6896 { "Ocirc", 0x00d4 },
6897 { "Ograve", 0x00d2 },
6898 { "Omega", 0x03a9 },
6899 { "Omicron", 0x039f },
6900 { "Oslash", 0x00d8 },
6901 { "Otilde", 0x00d5 },
6902 { "Ouml", 0x00d6 },
6903 { "Phi", 0x03a6 },
6904 { "Pi", 0x03a0 },
6905 { "Prime", 0x2033 },
6906 { "Psi", 0x03a8 },
6907 { "QUOT", 34 },
6908 { "Rho", 0x03a1 },
6909 { "Scaron", 0x0160 },
6910 { "Sigma", 0x03a3 },
6911 { "THORN", 0x00de },
6912 { "Tau", 0x03a4 },
6913 { "Theta", 0x0398 },
6914 { "Uacute", 0x00da },
6915 { "Ucirc", 0x00db },
6916 { "Ugrave", 0x00d9 },
6917 { "Upsilon", 0x03a5 },
6918 { "Uuml", 0x00dc },
6919 { "Xi", 0x039e },
6920 { "Yacute", 0x00dd },
6921 { "Yuml", 0x0178 },
6922 { "Zeta", 0x0396 },
6923 { "aacute", 0x00e1 },
6924 { "acirc", 0x00e2 },
6925 { "acute", 0x00b4 },
6926 { "aelig", 0x00e6 },
6927 { "agrave", 0x00e0 },
6928 { "alefsym", 0x2135 },
6929 { "alpha", 0x03b1 },
6930 { "amp", 38 },
6931 { "and", 0x22a5 },
6932 { "ang", 0x2220 },
6933 { "apos", 0x0027 },
6934 { "aring", 0x00e5 },
6935 { "asymp", 0x2248 },
6936 { "atilde", 0x00e3 },
6937 { "auml", 0x00e4 },
6938 { "bdquo", 0x201e },
6939 { "beta", 0x03b2 },
6940 { "brvbar", 0x00a6 },
6941 { "bull", 0x2022 },
6942 { "cap", 0x2229 },
6943 { "ccedil", 0x00e7 },
6944 { "cedil", 0x00b8 },
6945 { "cent", 0x00a2 },
6946 { "chi", 0x03c7 },
6947 { "circ", 0x02c6 },
6948 { "clubs", 0x2663 },
6949 { "cong", 0x2245 },
6950 { "copy", 0x00a9 },
6951 { "crarr", 0x21b5 },
6952 { "cup", 0x222a },
6953 { "curren", 0x00a4 },
6954 { "dArr", 0x21d3 },
6955 { "dagger", 0x2020 },
6956 { "darr", 0x2193 },
6957 { "deg", 0x00b0 },
6958 { "delta", 0x03b4 },
6959 { "diams", 0x2666 },
6960 { "divide", 0x00f7 },
6961 { "eacute", 0x00e9 },
6962 { "ecirc", 0x00ea },
6963 { "egrave", 0x00e8 },
6964 { "empty", 0x2205 },
6965 { "emsp", 0x2003 },
6966 { "ensp", 0x2002 },
6967 { "epsilon", 0x03b5 },
6968 { "equiv", 0x2261 },
6969 { "eta", 0x03b7 },
6970 { "eth", 0x00f0 },
6971 { "euml", 0x00eb },
6972 { "euro", 0x20ac },
6973 { "exist", 0x2203 },
6974 { "fnof", 0x0192 },
6975 { "forall", 0x2200 },
6976 { "frac12", 0x00bd },
6977 { "frac14", 0x00bc },
6978 { "frac34", 0x00be },
6979 { "frasl", 0x2044 },
6980 { "gamma", 0x03b3 },
6981 { "ge", 0x2265 },
6982 { "gt", 62 },
6983 { "hArr", 0x21d4 },
6984 { "harr", 0x2194 },
6985 { "hearts", 0x2665 },
6986 { "hellip", 0x2026 },
6987 { "iacute", 0x00ed },
6988 { "icirc", 0x00ee },
6989 { "iexcl", 0x00a1 },
6990 { "igrave", 0x00ec },
6991 { "image", 0x2111 },
6992 { "infin", 0x221e },
6993 { "int", 0x222b },
6994 { "iota", 0x03b9 },
6995 { "iquest", 0x00bf },
6996 { "isin", 0x2208 },
6997 { "iuml", 0x00ef },
6998 { "kappa", 0x03ba },
6999 { "lArr", 0x21d0 },
7000 { "lambda", 0x03bb },
7001 { "lang", 0x2329 },
7002 { "laquo", 0x00ab },
7003 { "larr", 0x2190 },
7004 { "lceil", 0x2308 },
7005 { "ldquo", 0x201c },
7006 { "le", 0x2264 },
7007 { "lfloor", 0x230a },
7008 { "lowast", 0x2217 },
7009 { "loz", 0x25ca },
7010 { "lrm", 0x200e },
7011 { "lsaquo", 0x2039 },
7012 { "lsquo", 0x2018 },
7013 { "lt", 60 },
7014 { "macr", 0x00af },
7015 { "mdash", 0x2014 },
7016 { "micro", 0x00b5 },
7017 { "middot", 0x00b7 },
7018 { "minus", 0x2212 },
7019 { "mu", 0x03bc },
7020 { "nabla", 0x2207 },
7021 { "nbsp", 0x00a0 },
7022 { "ndash", 0x2013 },
7023 { "ne", 0x2260 },
7024 { "ni", 0x220b },
7025 { "not", 0x00ac },
7026 { "notin", 0x2209 },
7027 { "nsub", 0x2284 },
7028 { "ntilde", 0x00f1 },
7029 { "nu", 0x03bd },
7030 { "oacute", 0x00f3 },
7031 { "ocirc", 0x00f4 },
7032 { "oelig", 0x0153 },
7033 { "ograve", 0x00f2 },
7034 { "oline", 0x203e },
7035 { "omega", 0x03c9 },
7036 { "omicron", 0x03bf },
7037 { "oplus", 0x2295 },
7038 { "or", 0x22a6 },
7039 { "ordf", 0x00aa },
7040 { "ordm", 0x00ba },
7041 { "oslash", 0x00f8 },
7042 { "otilde", 0x00f5 },
7043 { "otimes", 0x2297 },
7044 { "ouml", 0x00f6 },
7045 { "para", 0x00b6 },
7046 { "part", 0x2202 },
7047 { "percnt", 0x0025 },
7048 { "permil", 0x2030 },
7049 { "perp", 0x22a5 },
7050 { "phi", 0x03c6 },
7051 { "pi", 0x03c0 },
7052 { "piv", 0x03d6 },
7053 { "plusmn", 0x00b1 },
7054 { "pound", 0x00a3 },
7055 { "prime", 0x2032 },
7056 { "prod", 0x220f },
7057 { "prop", 0x221d },
7058 { "psi", 0x03c8 },
7059 { "quot", 34 },
7060 { "rArr", 0x21d2 },
7061 { "radic", 0x221a },
7062 { "rang", 0x232a },
7063 { "raquo", 0x00bb },
7064 { "rarr", 0x2192 },
7065 { "rceil", 0x2309 },
7066 { "rdquo", 0x201d },
7067 { "real", 0x211c },
7068 { "reg", 0x00ae },
7069 { "rfloor", 0x230b },
7070 { "rho", 0x03c1 },
7071 { "rlm", 0x200f },
7072 { "rsaquo", 0x203a },
7073 { "rsquo", 0x2019 },
7074 { "sbquo", 0x201a },
7075 { "scaron", 0x0161 },
7076 { "sdot", 0x22c5 },
7077 { "sect", 0x00a7 },
7078 { "shy", 0x00ad },
7079 { "sigma", 0x03c3 },
7080 { "sigmaf", 0x03c2 },
7081 { "sim", 0x223c },
7082 { "spades", 0x2660 },
7083 { "sub", 0x2282 },
7084 { "sube", 0x2286 },
7085 { "sum", 0x2211 },
7086 { "sup1", 0x00b9 },
7087 { "sup2", 0x00b2 },
7088 { "sup3", 0x00b3 },
7089 { "sup", 0x2283 },
7090 { "supe", 0x2287 },
7091 { "szlig", 0x00df },
7092 { "tau", 0x03c4 },
7093 { "there4", 0x2234 },
7094 { "theta", 0x03b8 },
7095 { "thetasym", 0x03d1 },
7096 { "thinsp", 0x2009 },
7097 { "thorn", 0x00fe },
7098 { "tilde", 0x02dc },
7099 { "times", 0x00d7 },
7100 { "trade", 0x2122 },
7101 { "uArr", 0x21d1 },
7102 { "uacute", 0x00fa },
7103 { "uarr", 0x2191 },
7104 { "ucirc", 0x00fb },
7105 { "ugrave", 0x00f9 },
7106 { "uml", 0x00a8 },
7107 { "upsih", 0x03d2 },
7108 { "upsilon", 0x03c5 },
7109 { "uuml", 0x00fc },
7110 { "weierp", 0x2118 },
7111 { "xi", 0x03be },
7112 { "yacute", 0x00fd },
7113 { "yen", 0x00a5 },
7114 { "yuml", 0x00ff },
7115 { "zeta", 0x03b6 },
7116 { "zwj", 0x200d },
7117 { "zwnj", 0x200c },
7118 { "", 0x0000 }
7119};
7120
7121
7122
7123
7124
7125static QMap<QCString, QChar> *html_map = 0;
7126static void qt_cleanup_html_map()
7127{
7128 delete html_map;
7129 html_map = 0;
7130}
7131
7132static QMap<QCString, QChar> *htmlMap()
7133{
7134 if ( !html_map ) {
7135 html_map = new QMap<QCString, QChar>;
7136 qAddPostRoutine( qt_cleanup_html_map );
7137
7138 const Entity *ent = entitylist;
7139 while( ent->code ) {
7140 html_map->insert( ent->name, QChar(ent->code) );
7141 ent++;
7142 }
7143 }
7144 return html_map;
7145}
7146
7147QChar QTextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
7148{
7149 QCString s;
7150 pos++;
7151 int recoverpos = pos;
7152 while ( pos < length && doc[pos] != ';' && !doc[pos].isSpace() && pos < recoverpos + 8 ) {
7153 s += doc[pos];
7154 pos++;
7155 }
7156 if (doc[pos] != ';' && !doc[pos].isSpace() ) {
7157 pos = recoverpos;
7158 return '&';
7159 }
7160 pos++;
7161
7162 if ( s.length() > 1 && s[0] == '#') {
7163 int num = s.mid(1).toInt();
7164 if ( num == 151 ) // ### hack for designer manual
7165 return '-';
7166 return num;
7167 }
7168
7169 QMap<QCString, QChar>::Iterator it = htmlMap()->find(s);
7170 if ( it != htmlMap()->end() ) {
7171 return *it;
7172 }
7173
7174 pos = recoverpos;
7175 return '&';
7176}
7177
7178QString QTextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
7179{
7180 QString s;
7181
7182 if (doc[pos] == '"') {
7183 pos++;
7184 while ( pos < length && doc[pos] != '"' ) {
7185 if ( doc[pos] == '&' ) {
7186 s += parseHTMLSpecialChar( doc, length, pos );
7187 } else {
7188 s += doc[pos];
7189 pos++;
7190 }
7191 }
7192 eat(doc, length, pos, '"');
7193 } else if (doc[pos] == '\'') {
7194 pos++;
7195 while ( pos < length && doc[pos] != '\'' ) {
7196 s += doc[pos];
7197 pos++;
7198 }
7199 eat(doc, length, pos, '\'');
7200 } else {
7201 static QString term = QString::fromLatin1("/>");
7202 while ( pos < length
7203 && doc[pos] != '>'
7204 && !hasPrefix(doc, length, pos, term)
7205 && doc[pos] != '<'
7206 && doc[pos] != '='
7207 && !doc[pos].isSpace() )
7208 {
7209 if ( doc[pos] == '&' ) {
7210 s += parseHTMLSpecialChar( doc, length, pos );
7211 } else {
7212 s += doc[pos];
7213 pos++;
7214 }
7215 }
7216 if (lower)
7217 s = s.lower();
7218 }
7219 return s;
7220}
7221
7222QChar QTextDocument::parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm )
7223{
7224 if ( pos >= length )
7225 return QChar::null;
7226
7227 QChar c = doc[pos++];
7228
7229 if (c == '<' )
7230 return QChar::null;
7231
7232 if ( c.isSpace() && c != QChar::nbsp ) {
7233 if ( wsm == QStyleSheetItem::WhiteSpacePre ) {
7234 if ( c == '\n' )
7235 return QChar_linesep;
7236 else
7237 return c;
7238 } else { // non-pre mode: collapse whitespace except nbsp
7239 while ( pos< length &&
7240 doc[pos].isSpace() && doc[pos] != QChar::nbsp )
7241 pos++;
7242 return ' ';
7243 }
7244 }
7245 else if ( c == '&' )
7246 return parseHTMLSpecialChar( doc, length, --pos );
7247 else
7248 return c;
7249}
7250
7251QString QTextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
7252 QMap<QString, QString> &attr, bool& emptyTag)
7253{
7254 emptyTag = FALSE;
7255 pos++;
7256 if ( hasPrefix(doc, length, pos, '!') ) {
7257 if ( hasPrefix( doc, length, pos+1, "--")) {
7258 pos += 3;
7259 // eat comments
7260 QString pref = QString::fromLatin1("-->");
7261 while ( !hasPrefix(doc, length, pos, pref ) && pos < length )
7262 pos++;
7263 if ( hasPrefix(doc, length, pos, pref ) ) {
7264 pos += 3;
7265 eatSpace(doc, length, pos, TRUE);
7266 }
7267 emptyTag = TRUE;
7268 return QString::null;
7269 }
7270 else {
7271 // eat strange internal tags
7272 while ( !hasPrefix(doc, length, pos, '>') && pos < length )
7273 pos++;
7274 if ( hasPrefix(doc, length, pos, '>') ) {
7275 pos++;
7276 eatSpace(doc, length, pos, TRUE);
7277 }
7278 return QString::null;
7279 }
7280 }
7281
7282 QString tag = parseWord(doc, length, pos );
7283 eatSpace(doc, length, pos, TRUE);
7284 static QString term = QString::fromLatin1("/>");
7285 static QString s_TRUE = QString::fromLatin1("TRUE");
7286
7287 while (doc[pos] != '>' && ! (emptyTag = hasPrefix(doc, length, pos, term) )) {
7288 QString key = parseWord(doc, length, pos );
7289 eatSpace(doc, length, pos, TRUE);
7290 if ( key.isEmpty()) {
7291 // error recovery
7292 while ( pos < length && doc[pos] != '>' )
7293 pos++;
7294 break;
7295 }
7296 QString value;
7297 if (hasPrefix(doc, length, pos, '=') ){
7298 pos++;
7299 eatSpace(doc, length, pos);
7300 value = parseWord(doc, length, pos, FALSE);
7301 }
7302 else
7303 value = s_TRUE;
7304 attr.insert(key.lower(), value );
7305 eatSpace(doc, length, pos, TRUE);
7306 }
7307
7308 if (emptyTag) {
7309 eat(doc, length, pos, '/');
7310 eat(doc, length, pos, '>');
7311 }
7312 else
7313 eat(doc, length, pos, '>');
7314
7315 return tag;
7316}
7317
7318QString QTextDocument::parseCloseTag( const QChar* doc, int length, int& pos )
7319{
7320 pos++;
7321 pos++;
7322 QString tag = parseWord(doc, length, pos );
7323 eatSpace(doc, length, pos, TRUE);
7324 eat(doc, length, pos, '>');
7325 return tag;
7326}
7327
7328QTextFlow::QTextFlow()
7329{
7330 w = pagesize = 0;
7331}
7332
7333QTextFlow::~QTextFlow()
7334{
7335 clear();
7336}
7337
7338void QTextFlow::clear()
7339{
7340#ifndef QT_NO_TEXTCUSTOMITEM
7341 leftItems.setAutoDelete( TRUE );
7342 rightItems.setAutoDelete( TRUE );
7343 leftItems.clear();
7344 rightItems.clear();
7345 leftItems.setAutoDelete( FALSE );
7346 rightItems.setAutoDelete( FALSE );
7347#endif
7348}
7349
7350void QTextFlow::setWidth( int width )
7351{
7352 w = width;
7353}
7354
7355int QTextFlow::adjustLMargin( int yp, int, int margin, int space )
7356{
7357#ifndef QT_NO_TEXTCUSTOMITEM
7358 for ( QTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
7359 if ( item->ypos == -1 )
7360 continue;
7361 if ( yp >= item->ypos && yp < item->ypos + item->height )
7362 margin = QMAX( margin, item->xpos + item->width + space );
7363 }
7364#endif
7365 return margin;
7366}
7367
7368int QTextFlow::adjustRMargin( int yp, int, int margin, int space )
7369{
7370#ifndef QT_NO_TEXTCUSTOMITEM
7371 for ( QTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
7372 if ( item->ypos == -1 )
7373 continue;
7374 if ( yp >= item->ypos && yp < item->ypos + item->height )
7375 margin = QMAX( margin, w - item->xpos - space );
7376 }
7377#endif
7378 return margin;
7379}
7380
7381
7382int QTextFlow::adjustFlow( int y, int /*w*/, int h )
7383{
7384 if ( pagesize > 0 ) { // check pages
7385 int yinpage = y % pagesize;
7386 if ( yinpage <= border_tolerance )
7387 return border_tolerance - yinpage;
7388 else
7389 if ( yinpage + h > pagesize - border_tolerance )
7390 return ( pagesize - yinpage ) + border_tolerance;
7391 }
7392 return 0;
7393}
7394
7395#ifndef QT_NO_TEXTCUSTOMITEM
7396void QTextFlow::unregisterFloatingItem( QTextCustomItem* item )
7397{
7398 leftItems.removeRef( item );
7399 rightItems.removeRef( item );
7400}
7401
7402void QTextFlow::registerFloatingItem( QTextCustomItem* item )
7403{
7404 if ( item->placement() == QTextCustomItem::PlaceRight ) {
7405 if ( !rightItems.contains( item ) )
7406 rightItems.append( item );
7407 } else if ( item->placement() == QTextCustomItem::PlaceLeft &&
7408 !leftItems.contains( item ) ) {
7409 leftItems.append( item );
7410 }
7411}
7412#endif // QT_NO_TEXTCUSTOMITEM
7413
7414QRect QTextFlow::boundingRect() const
7415{
7416 QRect br;
7417#ifndef QT_NO_TEXTCUSTOMITEM
7418 QPtrListIterator<QTextCustomItem> l( leftItems );
7419 while( l.current() ) {
7420 br = br.unite( l.current()->geometry() );
7421 ++l;
7422 }
7423 QPtrListIterator<QTextCustomItem> r( rightItems );
7424 while( r.current() ) {
7425 br = br.unite( r.current()->geometry() );
7426 ++r;
7427 }
7428#endif
7429 return br;
7430}
7431
7432
7433void QTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
7434{
7435#ifndef QT_NO_TEXTCUSTOMITEM
7436 QTextCustomItem *item;
7437 for ( item = leftItems.first(); item; item = leftItems.next() ) {
7438 if ( item->xpos == -1 || item->ypos == -1 )
7439 continue;
7440 item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
7441 }
7442
7443 for ( item = rightItems.first(); item; item = rightItems.next() ) {
7444 if ( item->xpos == -1 || item->ypos == -1 )
7445 continue;
7446 item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
7447 }
7448#endif
7449}
7450
7451// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7452
7453#ifndef QT_NO_TEXTCUSTOMITEM
7454void QTextCustomItem::pageBreak( int /*y*/ , QTextFlow* /*flow*/ )
7455{
7456}
7457#endif
7458
7459#ifndef QT_NO_TEXTCUSTOMITEM
7460QTextTable::QTextTable( QTextDocument *p, const QMap<QString, QString> & attr )
7461 : QTextCustomItem( p )
7462{
7463 cells.setAutoDelete( FALSE );
7464 cellspacing = 2;
7465 if ( attr.contains("cellspacing") )
7466 cellspacing = attr["cellspacing"].toInt();
7467 cellpadding = 1;
7468 if ( attr.contains("cellpadding") )
7469 cellpadding = attr["cellpadding"].toInt();
7470 border = innerborder = 0;
7471 if ( attr.contains("border" ) ) {
7472 QString s( attr["border"] );
7473 if ( s == "TRUE" )
7474 border = 1;
7475 else
7476 border = attr["border"].toInt();
7477 }
7478 us_b = border;
7479
7480 innerborder = us_ib = border ? 1 : 0;
7481
7482 if ( border )
7483 cellspacing += 2;
7484
7485 us_ib = innerborder;
7486 us_cs = cellspacing;
7487 us_cp = cellpadding;
7488 outerborder = cellspacing + border;
7489 us_ob = outerborder;
7490 layout = new QGridLayout( 1, 1, cellspacing );
7491
7492 fixwidth = 0;
7493 stretch = 0;
7494 if ( attr.contains("width") ) {
7495 bool b;
7496 QString s( attr["width"] );
7497 int w = s.toInt( &b );
7498 if ( b ) {
7499 fixwidth = w;
7500 } else {
7501 s = s.stripWhiteSpace();
7502 if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
7503 stretch = s.left( s.length()-1).toInt();
7504 }
7505 }
7506
7507 place = PlaceInline;
7508 if ( attr["align"] == "left" )
7509 place = PlaceLeft;
7510 else if ( attr["align"] == "right" )
7511 place = PlaceRight;
7512 cachewidth = 0;
7513 attributes = attr;
7514 pageBreakFor = -1;
7515}
7516
7517QTextTable::~QTextTable()
7518{
7519 delete layout;
7520}
7521
7522QString QTextTable::richText() const
7523{
7524 QString s;
7525 s = "<table ";
7526 QMap<QString, QString>::ConstIterator it = attributes.begin();
7527 for ( ; it != attributes.end(); ++it )
7528 s += it.key() + "=" + *it + " ";
7529 s += ">\n";
7530
7531 int lastRow = -1;
7532 bool needEnd = FALSE;
7533 QPtrListIterator<QTextTableCell> it2( cells );
7534 while ( it2.current() ) {
7535 QTextTableCell *cell = it2.current();
7536 ++it2;
7537 if ( lastRow != cell->row() ) {
7538 if ( lastRow != -1 )
7539 s += "</tr>\n";
7540 s += "<tr>";
7541 lastRow = cell->row();
7542 needEnd = TRUE;
7543 }
7544 s += "<td";
7545 it = cell->attributes.begin();
7546 for ( ; it != cell->attributes.end(); ++it )
7547 s += " " + it.key() + "=" + *it;
7548 s += ">";
7549 s += cell->richText()->richText();
7550 s += "</td>";
7551 }
7552 if ( needEnd )
7553 s += "</tr>\n";
7554 s += "</table>\n";
7555 return s;
7556}
7557
7558void QTextTable::adjustToPainter( QPainter* p )
7559{
7560 cellspacing = scale( us_cs, p );
7561 cellpadding = scale( us_cp, p );
7562 border = scale( us_b , p );
7563 innerborder = scale( us_ib, p );
7564 outerborder = scale( us_ob ,p );
7565 width = 0;
7566 cachewidth = 0;
7567 for ( QTextTableCell* cell = cells.first(); cell; cell = cells.next() )
7568 cell->adjustToPainter( p );
7569}
7570
7571void QTextTable::adjustCells( int y , int shift )
7572{
7573 QPtrListIterator<QTextTableCell> it( cells );
7574 QTextTableCell* cell;
7575 bool enlarge = FALSE;
7576 while ( ( cell = it.current() ) ) {
7577 ++it;
7578 QRect r = cell->geometry();
7579 if ( y <= r.top() ) {
7580 r.moveBy(0, shift );
7581 cell->setGeometry( r );
7582 enlarge = TRUE;
7583 } else if ( y <= r.bottom() ) {
7584 r.rBottom() += shift;
7585 cell->setGeometry( r );
7586 enlarge = TRUE;
7587 }
7588 }
7589 if ( enlarge )
7590 height += shift;
7591}
7592
7593void QTextTable::pageBreak( int yt, QTextFlow* flow )
7594{
7595 if ( flow->pageSize() <= 0 )
7596 return;
7597 if ( layout && pageBreakFor > 0 && pageBreakFor != yt ) {
7598 layout->invalidate();
7599 int h = layout->heightForWidth( width-2*outerborder );
7600 layout->setGeometry( QRect(0, 0, width-2*outerborder, h) );
7601 height = layout->geometry().height()+2*outerborder;
7602 }
7603 pageBreakFor = yt;
7604 QPtrListIterator<QTextTableCell> it( cells );
7605 QTextTableCell* cell;
7606 while ( ( cell = it.current() ) ) {
7607 ++it;
7608 int y = yt + outerborder + cell->geometry().y();
7609 int shift = flow->adjustFlow( y - cellspacing, width, cell->richText()->height() + 2*cellspacing );
7610 adjustCells( y - outerborder - yt, shift );
7611 }
7612}
7613
7614
7615void QTextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
7616{
7617 if ( placement() != PlaceInline ) {
7618 x = xpos;
7619 y = ypos;
7620 }
7621
7622 for (QTextTableCell* cell = cells.first(); cell; cell = cells.next() ) {
7623 if ( cx < 0 && cy < 0 ||
7624 QRect( cx, cy, cw, ch ).intersects( QRect( x + outerborder + cell->geometry().x(),
7625 y + outerborder + cell->geometry().y(),
7626 cell->geometry().width(), cell->geometry().height() ) ) ) {
7627 cell->draw( p, x+outerborder, y+outerborder, cx, cy, cw, ch, cg, selected );
7628 if ( border ) {
7629 QRect r( x+outerborder+cell->geometry().x() - innerborder,
7630 y+outerborder+cell->geometry().y() - innerborder,
7631 cell->geometry().width() + 2 * innerborder,
7632 cell->geometry().height() + 2 * innerborder );
7633 if ( is_printer( p ) ) {
7634 QPen oldPen = p->pen();
7635 QRect r2 = r;
7636 r2.addCoords( innerborder/2, innerborder/2, -innerborder/2, -innerborder/2 );
7637 p->setPen( QPen( cg.text(), innerborder ) );
7638 p->drawRect( r2 );
7639 p->setPen( oldPen );
7640 } else {
7641 int s = QMAX( cellspacing-2*innerborder, 0);
7642 if ( s ) {
7643 p->fillRect( r.left()-s, r.top(), s+1, r.height(), cg.button() );
7644 p->fillRect( r.right(), r.top(), s+1, r.height(), cg.button() );
7645 p->fillRect( r.left()-s, r.top()-s, r.width()+2*s, s, cg.button() );
7646 p->fillRect( r.left()-s, r.bottom(), r.width()+2*s, s, cg.button() );
7647 }
7648 qDrawShadePanel( p, r, cg, TRUE, innerborder );
7649 }
7650 }
7651 }
7652 }
7653 if ( border ) {
7654 QRect r ( x, y, width, height );
7655 if ( is_printer( p ) ) {
7656 QRect r2 = r;
7657 r2.addCoords( border/2, border/2, -border/2, -border/2 );
7658 QPen oldPen = p->pen();
7659 p->setPen( QPen( cg.text(), border ) );
7660 p->drawRect( r2 );
7661 p->setPen( oldPen );
7662 } else {
7663 int s = border+QMAX( cellspacing-2*innerborder, 0);
7664 if ( s ) {
7665 p->fillRect( r.left(), r.top(), s, r.height(), cg.button() );
7666 p->fillRect( r.right()-s, r.top(), s, r.height(), cg.button() );
7667 p->fillRect( r.left(), r.top(), r.width(), s, cg.button() );
7668 p->fillRect( r.left(), r.bottom()-s, r.width(), s, cg.button() );
7669 }
7670 qDrawShadePanel( p, r, cg, FALSE, border );
7671 }
7672 }
7673
7674}
7675
7676int QTextTable::minimumWidth() const
7677{
7678 return fixwidth ? fixwidth : ((layout ? layout->minimumSize().width() : 0) + 2 * outerborder);
7679}
7680
7681void QTextTable::resize( int nwidth )
7682{
7683 if ( fixwidth && cachewidth != 0 )
7684 return;
7685 if ( nwidth == cachewidth )
7686 return;
7687
7688
7689 cachewidth = nwidth;
7690 int w = nwidth;
7691
7692 format( w );
7693
7694 if ( stretch )
7695 nwidth = nwidth * stretch / 100;
7696
7697 width = nwidth;
7698 layout->invalidate();
7699 int shw = layout->sizeHint().width() + 2*outerborder;
7700 int mw = layout->minimumSize().width() + 2*outerborder;
7701 if ( stretch )
7702 width = QMAX( mw, nwidth );
7703 else
7704 width = QMAX( mw, QMIN( nwidth, shw ) );
7705
7706 if ( fixwidth )
7707 width = fixwidth;
7708
7709 layout->invalidate();
7710 mw = layout->minimumSize().width() + 2*outerborder;
7711 width = QMAX( width, mw );
7712
7713 int h = layout->heightForWidth( width-2*outerborder );
7714 layout->setGeometry( QRect(0, 0, width-2*outerborder, h) );
7715 height = layout->geometry().height()+2*outerborder;
7716}
7717
7718void QTextTable::format( int w )
7719{
7720 for ( int i = 0; i < (int)cells.count(); ++i ) {
7721 QTextTableCell *cell = cells.at( i );
7722 QRect r = cell->geometry();
7723 r.setWidth( w - 2*outerborder );
7724 cell->setGeometry( r );
7725 }
7726}
7727
7728void QTextTable::addCell( QTextTableCell* cell )
7729{
7730 cells.append( cell );
7731 layout->addMultiCell( cell, cell->row(), cell->row() + cell->rowspan()-1,
7732 cell->column(), cell->column() + cell->colspan()-1 );
7733}
7734
7735bool QTextTable::enter( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd )
7736{
7737 currCell.remove( c );
7738 if ( !atEnd )
7739 return next( c, doc, parag, idx, ox, oy );
7740 currCell.insert( c, cells.count() );
7741 return prev( c, doc, parag, idx, ox, oy );
7742}
7743
7744bool QTextTable::enterAt( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos )
7745{
7746 currCell.remove( c );
7747 int lastCell = -1;
7748 int lastY = -1;
7749 int i;
7750 for ( i = 0; i < (int)cells.count(); ++i ) {
7751 QTextTableCell *cell = cells.at( i );
7752 if ( !cell )
7753 continue;
7754 QRect r( cell->geometry().x(),
7755 cell->geometry().y(),
7756 cell->geometry().width() + 2 * innerborder + 2 * outerborder,
7757 cell->geometry().height() + 2 * innerborder + 2 * outerborder );
7758
7759 if ( r.left() <= pos.x() && r.right() >= pos.x() ) {
7760 if ( cell->geometry().y() > lastY ) {
7761 lastCell = i;
7762 lastY = cell->geometry().y();
7763 }
7764 if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) {
7765 currCell.insert( c, i );
7766 break;
7767 }
7768 }
7769 }
7770 if ( i == (int) cells.count() )
7771 return FALSE; // no cell found
7772
7773 if ( currCell.find( c ) == currCell.end() ) {
7774 if ( lastY != -1 )
7775 currCell.insert( c, lastCell );
7776 else
7777 return FALSE;
7778 }
7779
7780 QTextTableCell *cell = cells.at( *currCell.find( c ) );
7781 if ( !cell )
7782 return FALSE;
7783 doc = cell->richText();
7784 parag = doc->firstParagraph();
7785 idx = 0;
7786 ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
7787 oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
7788 return TRUE;
7789}
7790
7791bool QTextTable::next( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
7792{
7793 int cc = -1;
7794 if ( currCell.find( c ) != currCell.end() )
7795 cc = *currCell.find( c );
7796 if ( cc > (int)cells.count() - 1 || cc < 0 )
7797 cc = -1;
7798 currCell.remove( c );
7799 currCell.insert( c, ++cc );
7800 if ( cc >= (int)cells.count() ) {
7801 currCell.insert( c, 0 );
7802 QTextCustomItem::next( c, doc, parag, idx, ox, oy );
7803 QTextTableCell *cell = cells.first();
7804 if ( !cell )
7805 return FALSE;
7806 doc = cell->richText();
7807 idx = -1;
7808 return TRUE;
7809 }
7810
7811 if ( currCell.find( c ) == currCell.end() )
7812 return FALSE;
7813 QTextTableCell *cell = cells.at( *currCell.find( c ) );
7814 if ( !cell )
7815 return FALSE;
7816 doc = cell->richText();
7817 parag = doc->firstParagraph();
7818 idx = 0;
7819 ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
7820 oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
7821 return TRUE;
7822}
7823
7824bool QTextTable::prev( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
7825{
7826 int cc = -1;
7827 if ( currCell.find( c ) != currCell.end() )
7828 cc = *currCell.find( c );
7829 if ( cc > (int)cells.count() - 1 || cc < 0 )
7830 cc = cells.count();
7831 currCell.remove( c );
7832 currCell.insert( c, --cc );
7833 if ( cc < 0 ) {
7834 currCell.insert( c, 0 );
7835 QTextCustomItem::prev( c, doc, parag, idx, ox, oy );
7836 QTextTableCell *cell = cells.first();
7837 if ( !cell )
7838 return FALSE;
7839 doc = cell->richText();
7840 idx = -1;
7841 return TRUE;
7842 }
7843
7844 if ( currCell.find( c ) == currCell.end() )
7845 return FALSE;
7846 QTextTableCell *cell = cells.at( *currCell.find( c ) );
7847 if ( !cell )
7848 return FALSE;
7849 doc = cell->richText();
7850 parag = doc->lastParagraph();
7851 idx = parag->length() - 1;
7852 ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
7853 oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
7854 return TRUE;
7855}
7856
7857bool QTextTable::down( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
7858{
7859 if ( currCell.find( c ) == currCell.end() )
7860 return FALSE;
7861 QTextTableCell *cell = cells.at( *currCell.find( c ) );
7862 if ( cell->row_ == layout->numRows() - 1 ) {
7863 currCell.insert( c, 0 );
7864 QTextCustomItem::down( c, doc, parag, idx, ox, oy );
7865 QTextTableCell *cell = cells.first();
7866 if ( !cell )
7867 return FALSE;
7868 doc = cell->richText();
7869 idx = -1;
7870 return TRUE;
7871 }
7872
7873 int oldRow = cell->row_;
7874 int oldCol = cell->col_;
7875 if ( currCell.find( c ) == currCell.end() )
7876 return FALSE;
7877 int cc = *currCell.find( c );
7878 for ( int i = cc; i < (int)cells.count(); ++i ) {
7879 cell = cells.at( i );
7880 if ( cell->row_ > oldRow && cell->col_ == oldCol ) {
7881 currCell.insert( c, i );
7882 break;
7883 }
7884 }
7885 doc = cell->richText();
7886 if ( !cell )
7887 return FALSE;
7888 parag = doc->firstParagraph();
7889 idx = 0;
7890 ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
7891 oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
7892 return TRUE;
7893}
7894
7895bool QTextTable::up( QTextCursor *c, QTextDocument *&doc, QTextParagraph *&parag, int &idx, int &ox, int &oy )
7896{
7897 if ( currCell.find( c ) == currCell.end() )
7898 return FALSE;
7899 QTextTableCell *cell = cells.at( *currCell.find( c ) );
7900 if ( cell->row_ == 0 ) {
7901 currCell.insert( c, 0 );
7902 QTextCustomItem::up( c, doc, parag, idx, ox, oy );
7903 QTextTableCell *cell = cells.first();
7904 if ( !cell )
7905 return FALSE;
7906 doc = cell->richText();
7907 idx = -1;
7908 return TRUE;
7909 }
7910
7911 int oldRow = cell->row_;
7912 int oldCol = cell->col_;
7913 if ( currCell.find( c ) == currCell.end() )
7914 return FALSE;
7915 int cc = *currCell.find( c );
7916 for ( int i = cc; i >= 0; --i ) {
7917 cell = cells.at( i );
7918 if ( cell->row_ < oldRow && cell->col_ == oldCol ) {
7919 currCell.insert( c, i );
7920 break;
7921 }
7922 }
7923 doc = cell->richText();
7924 if ( !cell )
7925 return FALSE;
7926 parag = doc->lastParagraph();
7927 idx = parag->length() - 1;
7928 ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
7929 oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
7930 return TRUE;
7931}
7932
7933QTextTableCell::QTextTableCell( QTextTable* table,
7934 int row, int column,
7935 const QMap<QString, QString> &attr,
7936 const QStyleSheetItem* /*style*/, // ### use them
7937 const QTextFormat& fmt, const QString& context,
7938 QMimeSourceFactory &factory, QStyleSheet *sheet,
7939 const QString& doc)
7940{
7941 cached_width = -1;
7942 cached_sizehint = -1;
7943
7944 maxw = QWIDGETSIZE_MAX;
7945 minw = 0;
7946
7947 parent = table;
7948 row_ = row;
7949 col_ = column;
7950 stretch_ = 0;
7951 richtext = new QTextDocument( table->parent );
7952 richtext->formatCollection()->setPaintDevice( table->parent->formatCollection()->paintDevice() );
7953 richtext->bodyText = fmt.color();
7954 richtext->setTableCell( this );
7955 QString a = *attr.find( "align" );
7956 if ( !a.isEmpty() ) {
7957 a = a.lower();
7958 if ( a == "left" )
7959 richtext->setAlignment( Qt::AlignLeft );
7960 else if ( a == "center" )
7961 richtext->setAlignment( Qt::AlignHCenter );
7962 else if ( a == "right" )
7963 richtext->setAlignment( Qt::AlignRight );
7964 }
7965 align = 0;
7966 QString va = *attr.find( "valign" );
7967 if ( !va.isEmpty() ) {
7968 va = va.lower();
7969 if ( va == "center" )
7970 align |= Qt::AlignVCenter;
7971 else if ( va == "bottom" )
7972 align |= Qt::AlignBottom;
7973 }
7974 richtext->setFormatter( table->parent->formatter() );
7975 richtext->setUseFormatCollection( table->parent->useFormatCollection() );
7976 richtext->setMimeSourceFactory( &factory );
7977 richtext->setStyleSheet( sheet );
7978 richtext->setDefaultFormat( fmt.font(), fmt.color() );
7979 richtext->setRichText( doc, context );
7980 rowspan_ = 1;
7981 colspan_ = 1;
7982 if ( attr.contains("colspan") )
7983 colspan_ = attr["colspan"].toInt();
7984 if ( attr.contains("rowspan") )
7985 rowspan_ = attr["rowspan"].toInt();
7986
7987 background = 0;
7988 if ( attr.contains("bgcolor") ) {
7989 background = new QBrush(QColor( attr["bgcolor"] ));
7990 }
7991
7992
7993 hasFixedWidth = FALSE;
7994 if ( attr.contains("width") ) {
7995 bool b;
7996 QString s( attr["width"] );
7997 int w = s.toInt( &b );
7998 if ( b ) {
7999 maxw = w;
8000 minw = maxw;
8001 hasFixedWidth = TRUE;
8002 } else {
8003 s = s.stripWhiteSpace();
8004 if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
8005 stretch_ = s.left( s.length()-1).toInt();
8006 }
8007 }
8008
8009 attributes = attr;
8010
8011 parent->addCell( this );
8012}
8013
8014QTextTableCell::~QTextTableCell()
8015{
8016 delete background;
8017 background = 0;
8018 delete richtext;
8019 richtext = 0;
8020}
8021
8022QSize QTextTableCell::sizeHint() const
8023{
8024 int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
8025 int used = richtext->widthUsed() + extra;
8026
8027 if (stretch_ ) {
8028 int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding;
8029 return QSize( QMIN( w, maxw ), 0 ).expandedTo( minimumSize() );
8030 }
8031
8032 return QSize( used, 0 ).expandedTo( minimumSize() );
8033}
8034
8035QSize QTextTableCell::minimumSize() const
8036{
8037 int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
8038 return QSize( QMAX( richtext->minimumWidth() + extra, minw), 0 );
8039}
8040
8041QSize QTextTableCell::maximumSize() const
8042{
8043 return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
8044}
8045
8046QSizePolicy::ExpandData QTextTableCell::expanding() const
8047{
8048 return QSizePolicy::BothDirections;
8049}
8050
8051bool QTextTableCell::isEmpty() const
8052{
8053 return FALSE;
8054}
8055void QTextTableCell::setGeometry( const QRect& r )
8056{
8057 int extra = 2 * ( parent->innerborder + parent->cellpadding );
8058 if ( r.width() != cached_width )
8059 richtext->doLayout( QTextFormat::painter(), r.width() - extra );
8060 cached_width = r.width();
8061 geom = r;
8062}
8063
8064QRect QTextTableCell::geometry() const
8065{
8066 return geom;
8067}
8068
8069bool QTextTableCell::hasHeightForWidth() const
8070{
8071 return TRUE;
8072}
8073
8074int QTextTableCell::heightForWidth( int w ) const
8075{
8076 int extra = 2 * ( parent->innerborder + parent->cellpadding );
8077 w = QMAX( minw, w );
8078
8079 if ( cached_width != w ) {
8080 QTextTableCell* that = (QTextTableCell*) this;
8081 that->richtext->doLayout( QTextFormat::painter(), w - extra );
8082 that->cached_width = w;
8083 }
8084 return richtext->height() + extra;
8085}
8086
8087void QTextTableCell::adjustToPainter( QPainter* p )
8088{
8089 QTextParagraph *parag = richtext->firstParagraph();
8090 while ( parag ) {
8091 parag->adjustToPainter( p );
8092 parag = parag->next();
8093 }
8094}
8095
8096int QTextTableCell::horizontalAlignmentOffset() const
8097{
8098 return parent->cellpadding;
8099}
8100
8101int QTextTableCell::verticalAlignmentOffset() const
8102{
8103 if ( (align & Qt::AlignVCenter ) == Qt::AlignVCenter )
8104 return ( geom.height() - richtext->height() ) / 2;
8105 else if ( ( align & Qt::AlignBottom ) == Qt::AlignBottom )
8106 return geom.height() - parent->cellpadding - richtext->height() ;
8107 return parent->cellpadding;
8108}
8109
8110void QTextTableCell::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool )
8111{
8112 if ( cached_width != geom.width() ) {
8113 int extra = 2 * ( parent->innerborder + parent->cellpadding );
8114 richtext->doLayout( p, geom.width() - extra );
8115 cached_width = geom.width();
8116 }
8117 QColorGroup g( cg );
8118 if ( background )
8119 g.setBrush( QColorGroup::Base, *background );
8120 else if ( richtext->paper() )
8121 g.setBrush( QColorGroup::Base, *richtext->paper() );
8122
8123 p->save();
8124 p->translate( x + geom.x(), y + geom.y() );
8125 if ( background )
8126 p->fillRect( 0, 0, geom.width(), geom.height(), *background );
8127 else if ( richtext->paper() )
8128 p->fillRect( 0, 0, geom.width(), geom.height(), *richtext->paper() );
8129
8130 p->translate( horizontalAlignmentOffset(), verticalAlignmentOffset() );
8131
8132 QRegion r;
8133 if ( cx >= 0 && cy >= 0 )
8134 richtext->draw( p, cx - ( x + horizontalAlignmentOffset() + geom.x() ),
8135 cy - ( y + geom.y() + verticalAlignmentOffset() ),
8136 cw, ch, g, FALSE, FALSE, 0 );
8137 else
8138 richtext->draw( p, -1, -1, -1, -1, g, FALSE, FALSE, 0 );
8139
8140 p->restore();
8141}
8142#endif
8143
8144#endif //QT_NO_RICHTEXT
Note: See TracBrowser for help on using the repository browser.