source: trunk/src/kernel/qrichtext.cpp@ 154

Last change on this file since 154 was 8, checked in by dmik, 20 years ago

Transferred Qt for OS/2 version 3.3.1-rc5 sources from the CVS

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