-rw-r--r-- | noncore/apps/opie-write/qcleanuphandler.h | 12 | ||||
-rw-r--r-- | noncore/apps/opie-write/qcomplextext.cpp | 3 | ||||
-rw-r--r-- | noncore/apps/opie-write/qrichtext.cpp | 3945 | ||||
-rw-r--r-- | noncore/apps/opie-write/qrichtext_p.cpp | 301 | ||||
-rw-r--r-- | noncore/apps/opie-write/qrichtext_p.h | 576 | ||||
-rw-r--r-- | noncore/apps/opie-write/qstylesheet.cpp | 750 | ||||
-rw-r--r-- | noncore/apps/opie-write/qstylesheet.h | 12 | ||||
-rw-r--r-- | noncore/apps/opie-write/qt3namespace.h | 1 | ||||
-rw-r--r-- | noncore/apps/opie-write/qtextedit.cpp | 1375 | ||||
-rw-r--r-- | noncore/apps/opie-write/qtextedit.h | 30 |
10 files changed, 3347 insertions, 3658 deletions
diff --git a/noncore/apps/opie-write/qcleanuphandler.h b/noncore/apps/opie-write/qcleanuphandler.h index 5c5bf16..2d6eb7c 100644 --- a/noncore/apps/opie-write/qcleanuphandler.h +++ b/noncore/apps/opie-write/qcleanuphandler.h @@ -1,139 +1,127 @@ /**************************************************************************** ** $Id$ ** ** ... ** ** Copyright (C) 2001-2002 Trolltech AS. All rights reserved. ** ** This file is part of the tools module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QCLEANUPHANDLER_H #define QCLEANUPHANDLER_H #ifndef QT_H #include <qlist.h> #endif // QT_H template<class Type> -#ifdef Q_NO_TEMPLATE_EXPORT class QCleanupHandler -#else -class Q_EXPORT QCleanupHandler -#endif { public: QCleanupHandler() : cleanupObjects( 0 ) {} ~QCleanupHandler() { clear(); } Type* add( Type **object ) { if ( !cleanupObjects ) cleanupObjects = new QPtrList<Type*>; cleanupObjects->insert( 0, object ); return *object; } void remove( Type **object ) { if ( !cleanupObjects ) return; if ( cleanupObjects->findRef( object ) >= 0 ) (void) cleanupObjects->take(); } bool isEmpty() const { return cleanupObjects ? cleanupObjects->isEmpty() : TRUE; } void clear() { if ( !cleanupObjects ) return; QPtrListIterator<Type*> it( *cleanupObjects ); Type **object; while ( ( object = it.current() ) ) { delete *object; *object = 0; cleanupObjects->remove( object ); } delete cleanupObjects; cleanupObjects = 0; } private: QPtrList<Type*> *cleanupObjects; }; template<class Type> -#ifdef Q_NO_TEMPLATE_EXPORT class QSingleCleanupHandler -#else -class Q_EXPORT QSingleCleanupHandler -#endif { public: QSingleCleanupHandler() : object( 0 ) {} ~QSingleCleanupHandler() { if ( object ) { delete *object; *object = 0; } } Type* set( Type **o ) { object = o; return *object; } void reset() { object = 0; } private: Type **object; }; template<class Type> -#ifdef Q_NO_TEMPLATE_EXPORT class QSharedCleanupHandler -#else -class Q_EXPORT QSharedCleanupHandler -#endif { public: QSharedCleanupHandler() : object( 0 ) {} ~QSharedCleanupHandler() { if ( object ) { if ( (*object)->deref() ) delete *object; *object = 0; } } Type* set( Type **o ) { object = o; return *object; } void reset() { object = 0; } private: Type **object; }; #endif //QCLEANUPHANDLER_H diff --git a/noncore/apps/opie-write/qcomplextext.cpp b/noncore/apps/opie-write/qcomplextext.cpp index 0fa6c2e..e8b94da 100644 --- a/noncore/apps/opie-write/qcomplextext.cpp +++ b/noncore/apps/opie-write/qcomplextext.cpp @@ -1,152 +1,149 @@ /**************************************************************************** ** $Id$ ** ** Implementation of some internal classes ** ** Created : ** ** Copyright (C) 2001 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "qcomplextext_p.h" #include "qrichtext_p.h" #include "qfontmetrics.h" #include "qrect.h" #include <stdlib.h> using namespace Qt3; // ----------------------------------------------------- /* a small helper class used internally to resolve Bidi embedding levels. Each line of text caches the embedding level at the start of the line for faster relayouting */ QBidiContext::QBidiContext( uchar l, QChar::Direction e, QBidiContext *p, bool o ) : level(l) , override(o), dir(e) { if ( p ) p->ref(); parent = p; count = 0; } QBidiContext::~QBidiContext() { if( parent && parent->deref() ) delete parent; } -static QChar *shapeBuffer = 0; -static int shapeBufSize = 0; - /* Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on arabic). Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent). transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks. Right join-causing: dual + center Left join-causing: dual + right + center Rules are as follows (for a string already in visual order, as we have it here): R1 Transparent characters do not affect joining behaviour. R2 A right joining character, that has a right join-causing char on the right will get form XRight (R3 A left joining character, that has a left join-causing char on the left will get form XLeft) Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on the right will get form XMedial R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left will get form XRight R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right will get form XLeft R7 Otherwise the character will get form XIsolated Additionally we have to do the minimal ligature support for lam-alef ligatures: L1 Transparent characters do not affect ligature behaviour. L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft) L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated) The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with previous and left with next in the above rules ;-) */ /* Two small helper functions for arabic shaping. They get the next shape causing character on either side of the char in question. Implements rule R1. leftChar() returns true if the char to the left is a left join-causing char rightChar() returns true if the char to the right is a right join-causing char */ static inline const QChar *prevChar( const QString &str, int pos ) { //qDebug("leftChar: pos=%d", pos); pos--; const QChar *ch = str.unicode() + pos; while( pos > -1 ) { if( !ch->isMark() ) return ch; pos--; ch--; } return &QChar::replacement; } static inline const QChar *nextChar( const QString &str, int pos) { pos++; int len = str.length(); const QChar *ch = str.unicode() + pos; while( pos < len ) { //qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining()); if( !ch->isMark() ) return ch; // assume it's a transparent char, this might not be 100% correct pos++; ch++; } return &QChar::replacement; } static inline bool prevVisualCharJoins( const QString &str, int pos) { return ( prevChar( str, pos )->joining() != QChar::OtherJoining ); } static inline bool nextVisualCharJoins( const QString &str, int pos) { QChar::Joining join = nextChar( str, pos )->joining(); return ( join == QChar::Dual || join == QChar::Center ); } diff --git a/noncore/apps/opie-write/qrichtext.cpp b/noncore/apps/opie-write/qrichtext.cpp index 7901000..3b044c3 100644 --- a/noncore/apps/opie-write/qrichtext.cpp +++ b/noncore/apps/opie-write/qrichtext.cpp @@ -1,6841 +1,6518 @@ /**************************************************************************** ** $Id$ ** ** Implementation of the internal Qt classes dealing with rich text ** ** Created : 990101 ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "qrichtext_p.h" #include "qstringlist.h" #include "qfont.h" #include "qtextstream.h" #include "qfile.h" -#include "qregexp.h" #include "qapplication.h" -#include "qclipboard.h" #include "qmap.h" #include "qfileinfo.h" #include "qstylesheet.h" #include "qmime.h" -#include "qregexp.h" #include "qimage.h" #include "qdragobject.h" #include "qpaintdevicemetrics.h" #include "qpainter.h" #include "qdrawutil.h" #include "qcursor.h" #include "qstack.h" #include "qstyle.h" #include "qcomplextext_p.h" #include "qcleanuphandler.h" #include <stdlib.h> using namespace Qt3; -//#define PARSER_DEBUG -//#define DEBUG_COLLECTION// ---> also in qrichtext_p.h -//#define DEBUG_TABLE_RENDERING +static QTextCursor* richTextExportStart = 0; +static QTextCursor* richTextExportEnd = 0; static QTextFormatCollection *qFormatCollection = 0; -const int QStyleSheetItem_WhiteSpaceNoCompression = 3; // ### belongs in QStyleSheetItem, fix 3.1 -const int QStyleSheetItem_WhiteSpaceNormalWithNewlines = 4; // ### belongs in QStyleSheetItem, fix 3.1 - const int border_tolerance = 2; -#if defined(PARSER_DEBUG) -static QString debug_indent; -#endif - #ifdef Q_WS_WIN #include "qt_windows.h" #endif +#define QChar_linesep QChar(0x2028U) + static inline bool is_printer( QPainter *p ) { if ( !p || !p->device() ) return FALSE; return p->device()->devType() == QInternal::Printer; } static inline int scale( int value, QPainter *painter ) { if ( is_printer( painter ) ) { QPaintDeviceMetrics metrics( painter->device() ); #if defined(Q_WS_X11) value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY(); #elif defined (Q_WS_WIN) HDC hdc = GetDC( 0 ); int gdc = GetDeviceCaps( hdc, LOGPIXELSY ); if ( gdc ) value = value * metrics.logicalDpiY() / gdc; ReleaseDC( 0, hdc ); #elif defined (Q_WS_MAC) value = value * metrics.logicalDpiY() / 75; // ##### FIXME #elif defined (Q_WS_QWS) value = value * metrics.logicalDpiY() / 75; #endif } return value; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void QTextCommandHistory::addCommand( QTextCommand *cmd ) { if ( current < (int)history.count() - 1 ) { QPtrList<QTextCommand> commands; commands.setAutoDelete( FALSE ); for( int i = 0; i <= current; ++i ) { commands.insert( i, history.at( 0 ) ); history.take( 0 ); } commands.append( cmd ); history.clear(); history = commands; history.setAutoDelete( TRUE ); } else { history.append( cmd ); } if ( (int)history.count() > steps ) history.removeFirst(); else ++current; } QTextCursor *QTextCommandHistory::undo( QTextCursor *c ) { if ( current > -1 ) { QTextCursor *c2 = history.at( current )->unexecute( c ); --current; return c2; } return 0; } QTextCursor *QTextCommandHistory::redo( QTextCursor *c ) { if ( current > -1 ) { if ( current < (int)history.count() - 1 ) { ++current; return history.at( current )->execute( c ); } } else { if ( history.count() > 0 ) { ++current; return history.at( current )->execute( c ); } } return 0; } bool QTextCommandHistory::isUndoAvailable() { return current > -1; } bool QTextCommandHistory::isRedoAvailable() { return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextDeleteCommand::QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str, - const QValueList< QPtrVector<QStyleSheetItem> > &os, - const QValueList<QStyleSheetItem::ListStyle> &ols, - const QMemArray<int> &oas) - : QTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), oldStyles( os ), oldListStyles( ols ), oldAligns( oas ) + const QByteArray& oldStyleInfo ) + : QTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), styleInformation( oldStyleInfo ) { for ( int j = 0; j < (int)text.size(); ++j ) { if ( text[ j ].format() ) text[ j ].format()->addRef(); } } -QTextDeleteCommand::QTextDeleteCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str ) +QTextDeleteCommand::QTextDeleteCommand( QTextParagraph *p, int idx, const QMemArray<QTextStringChar> &str ) : QTextCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str ) { for ( int i = 0; i < (int)text.size(); ++i ) { if ( text[ i ].format() ) text[ i ].format()->addRef(); } } QTextDeleteCommand::~QTextDeleteCommand() { for ( int i = 0; i < (int)text.size(); ++i ) { if ( text[ i ].format() ) text[ i ].format()->removeRef(); } text.resize( 0 ); } QTextCursor *QTextDeleteCommand::execute( QTextCursor *c ) { - QTextParag *s = doc ? doc->paragAt( id ) : parag; + QTextParagraph *s = doc ? doc->paragAt( id ) : parag; if ( !s ) { - qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParag()->paragId() ); + qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() ); return 0; } - cursor.setParag( s ); + cursor.setParagraph( s ); cursor.setIndex( index ); int len = text.size(); if ( c ) *c = cursor; if ( doc ) { - doc->setSelectionStart( QTextDocument::Temp, &cursor ); + doc->setSelectionStart( QTextDocument::Temp, cursor ); for ( int i = 0; i < len; ++i ) cursor.gotoNextLetter(); - doc->setSelectionEnd( QTextDocument::Temp, &cursor ); + doc->setSelectionEnd( QTextDocument::Temp, cursor ); doc->removeSelectedText( QTextDocument::Temp, &cursor ); if ( c ) *c = cursor; } else { s->remove( index, len ); } return c; } QTextCursor *QTextDeleteCommand::unexecute( QTextCursor *c ) { - QTextParag *s = doc ? doc->paragAt( id ) : parag; + QTextParagraph *s = doc ? doc->paragAt( id ) : parag; if ( !s ) { - qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParag()->paragId() ); + qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId() ); return 0; } - cursor.setParag( s ); + cursor.setParagraph( s ); cursor.setIndex( index ); QString str = QTextString::toString( text ); cursor.insert( str, TRUE, &text ); - cursor.setParag( s ); + cursor.setParagraph( s ); cursor.setIndex( index ); if ( c ) { - c->setParag( s ); + c->setParagraph( s ); c->setIndex( index ); for ( int i = 0; i < (int)text.size(); ++i ) c->gotoNextLetter(); } - QValueList< QPtrVector<QStyleSheetItem> >::Iterator it = oldStyles.begin(); - QValueList<QStyleSheetItem::ListStyle>::Iterator lit = oldListStyles.begin(); - int i = 0; - QTextParag *p = s; - bool end = FALSE; - while ( p ) { - if ( it != oldStyles.end() ) - p->setStyleSheetItems( *it ); - else - end = TRUE; - if ( lit != oldListStyles.end() ) - p->setListStyle( *lit ); - else - end = TRUE; - if ( i < (int)oldAligns.size() ) - p->setAlignment( oldAligns.at( i ) ); - else - end = TRUE; - if ( end ) - break; + if ( !styleInformation.isEmpty() ) { + QDataStream styleStream( styleInformation, IO_ReadOnly ); + int num; + styleStream >> num; + QTextParagraph *p = s; + while ( num-- && p ) { + p->readStyleInformation( styleStream ); p = p->next(); - ++it; - ++lit; - ++i; } - - s = cursor.parag(); + } + s = cursor.paragraph(); while ( s ) { s->format(); s->setChanged( TRUE ); - if ( s == c->parag() ) + if ( s == c->paragraph() ) break; s = s->next(); } return &cursor; } QTextFormatCommand::QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx, const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl ) : QTextCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), format( f ), oldFormats( old ), flags( fl ) { format = d->formatCollection()->format( f ); for ( int j = 0; j < (int)oldFormats.size(); ++j ) { if ( oldFormats[ j ].format() ) oldFormats[ j ].format()->addRef(); } } QTextFormatCommand::~QTextFormatCommand() { format->removeRef(); for ( int j = 0; j < (int)oldFormats.size(); ++j ) { if ( oldFormats[ j ].format() ) oldFormats[ j ].format()->removeRef(); } } QTextCursor *QTextFormatCommand::execute( QTextCursor *c ) { - QTextParag *sp = doc->paragAt( startId ); - QTextParag *ep = doc->paragAt( endId ); + QTextParagraph *sp = doc->paragAt( startId ); + QTextParagraph *ep = doc->paragAt( endId ); if ( !sp || !ep ) return c; QTextCursor start( doc ); - start.setParag( sp ); + start.setParagraph( sp ); start.setIndex( startIndex ); QTextCursor end( doc ); - end.setParag( ep ); + end.setParagraph( ep ); end.setIndex( endIndex ); - doc->setSelectionStart( QTextDocument::Temp, &start ); - doc->setSelectionEnd( QTextDocument::Temp, &end ); + doc->setSelectionStart( QTextDocument::Temp, start ); + doc->setSelectionEnd( QTextDocument::Temp, end ); doc->setFormat( QTextDocument::Temp, format, flags ); doc->removeSelection( QTextDocument::Temp ); if ( endIndex == ep->length() ) end.gotoLeft(); *c = end; return c; } QTextCursor *QTextFormatCommand::unexecute( QTextCursor *c ) { - QTextParag *sp = doc->paragAt( startId ); - QTextParag *ep = doc->paragAt( endId ); + QTextParagraph *sp = doc->paragAt( startId ); + QTextParagraph *ep = doc->paragAt( endId ); if ( !sp || !ep ) return 0; int idx = startIndex; int fIndex = 0; for ( ;; ) { if ( oldFormats.at( fIndex ).c == '\n' ) { if ( idx > 0 ) { if ( idx < sp->length() && fIndex > 0 ) sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() ); if ( sp == ep ) break; sp = sp->next(); idx = 0; } fIndex++; } if ( oldFormats.at( fIndex ).format() ) sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() ); idx++; fIndex++; if ( fIndex >= (int)oldFormats.size() ) break; if ( idx >= sp->length() ) { if ( sp == ep ) break; sp = sp->next(); idx = 0; } } QTextCursor end( doc ); - end.setParag( ep ); + end.setParagraph( ep ); end.setIndex( endIndex ); if ( endIndex == ep->length() ) end.gotoLeft(); *c = end; return c; } -QTextAlignmentCommand::QTextAlignmentCommand( QTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa ) - : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa ) +QTextStyleCommand::QTextStyleCommand( QTextDocument *d, int fParag, int lParag, const QByteArray& beforeChange ) + : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), before( beforeChange ) { + after = readStyleInformation( d, fParag, lParag ); } -QTextCursor *QTextAlignmentCommand::execute( QTextCursor *c ) + +QByteArray QTextStyleCommand::readStyleInformation( QTextDocument* doc, int fParag, int lParag ) { - QTextParag *p = doc->paragAt( firstParag ); + QByteArray style; + QTextParagraph *p = doc->paragAt( fParag ); if ( !p ) - return c; - while ( p ) { - p->setAlignment( newAlign ); - if ( p->paragId() == lastParag ) - break; + return style; + QDataStream styleStream( style, IO_WriteOnly ); + int num = lParag - fParag + 1; + styleStream << num; + while ( num -- && p ) { + p->writeStyleInformation( styleStream ); p = p->next(); } - return c; + return style; } -QTextCursor *QTextAlignmentCommand::unexecute( QTextCursor *c ) +void QTextStyleCommand::writeStyleInformation( QTextDocument* doc, int fParag, const QByteArray& style ) { - QTextParag *p = doc->paragAt( firstParag ); + QTextParagraph *p = doc->paragAt( fParag ); if ( !p ) - return c; - int i = 0; - while ( p ) { - if ( i < (int)oldAligns.size() ) - p->setAlignment( oldAligns.at( i ) ); - if ( p->paragId() == lastParag ) - break; + return; + QDataStream styleStream( style, IO_ReadOnly ); + int num; + styleStream >> num; + while ( num-- && p ) { + p->readStyleInformation( styleStream ); p = p->next(); - ++i; } - return c; } -QTextParagTypeCommand::QTextParagTypeCommand( QTextDocument *d, int fParag, int lParag, bool l, - QStyleSheetItem::ListStyle s, const QValueList< QPtrVector<QStyleSheetItem> > &os, - const QValueList<QStyleSheetItem::ListStyle> &ols ) - : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), list( l ), listStyle( s ), oldStyles( os ), oldListStyles( ols ) +QTextCursor *QTextStyleCommand::execute( QTextCursor *c ) { -} - -QTextCursor *QTextParagTypeCommand::execute( QTextCursor *c ) -{ - QTextParag *p = doc->paragAt( firstParag ); - if ( !p ) - return c; - while ( p ) { - p->setList( list, (int)listStyle ); - if ( p->paragId() == lastParag ) - break; - p = p->next(); - } + writeStyleInformation( doc, firstParag, after ); return c; } -QTextCursor *QTextParagTypeCommand::unexecute( QTextCursor *c ) +QTextCursor *QTextStyleCommand::unexecute( QTextCursor *c ) { - QTextParag *p = doc->paragAt( firstParag ); - if ( !p ) - return c; - QValueList< QPtrVector<QStyleSheetItem> >::Iterator it = oldStyles.begin(); - QValueList<QStyleSheetItem::ListStyle>::Iterator lit = oldListStyles.begin(); - while ( p ) { - if ( it != oldStyles.end() ) - p->setStyleSheetItems( *it ); - if ( lit != oldListStyles.end() ) - p->setListStyle( *lit ); - if ( p->paragId() == lastParag ) - break; - p = p->next(); - ++it; - ++lit; - } + writeStyleInformation( doc, firstParag, before ); return c; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextCursor::QTextCursor( QTextDocument *d ) - : doc( d ), ox( 0 ), oy( 0 ) -{ - nested = FALSE; - idx = 0; - string = doc ? doc->firstParag() : 0; - tmpIndex = -1; - valid = TRUE; -} - -QTextCursor::QTextCursor() + : idx( 0 ), tmpIndex( -1 ), ox( 0 ), oy( 0 ), + valid( TRUE ) { + para = d ? d->firstParagraph() : 0; } QTextCursor::QTextCursor( const QTextCursor &c ) { - doc = c.doc; ox = c.ox; oy = c.oy; - nested = c.nested; idx = c.idx; - string = c.string; + para = c.para; tmpIndex = c.tmpIndex; indices = c.indices; - parags = c.parags; + paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; } QTextCursor &QTextCursor::operator=( const QTextCursor &c ) { - doc = c.doc; ox = c.ox; oy = c.oy; - nested = c.nested; idx = c.idx; - string = c.string; + para = c.para; tmpIndex = c.tmpIndex; indices = c.indices; - parags = c.parags; + paras = c.paras; xOffsets = c.xOffsets; yOffsets = c.yOffsets; valid = c.valid; return *this; } bool QTextCursor::operator==( const QTextCursor &c ) const { - return doc == c.doc && string == c.string && idx == c.idx; + return para == c.para && idx == c.idx; } int QTextCursor::totalOffsetX() const { - if ( !nested ) - return 0; - QValueStack<int>::ConstIterator xit = xOffsets.begin(); int xoff = ox; - for ( ; xit != xOffsets.end(); ++xit ) + for ( QValueStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit ) xoff += *xit; return xoff; } int QTextCursor::totalOffsetY() const { - if ( !nested ) - return 0; - QValueStack<int>::ConstIterator yit = yOffsets.begin(); int yoff = oy; - for ( ; yit != yOffsets.end(); ++yit ) + for ( QValueStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit ) yoff += *yit; return yoff; } void QTextCursor::gotoIntoNested( const QPoint &globalPos ) { - if ( !doc ) + if ( !para ) return; push(); ox = 0; int bl, y; - string->lineHeightOfChar( idx, &bl, &y ); - oy = y + string->rect().y(); - nested = TRUE; + para->lineHeightOfChar( idx, &bl, &y ); + oy = y + para->rect().y(); QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() ); - Q_ASSERT( string->at( idx )->isCustom() ); - ox = string->at( idx )->x; - string->at( idx )->customItem()->enterAt( this, doc, string, idx, ox, oy, p ); + Q_ASSERT( para->at( idx )->isCustom() ); + ox = para->at( idx )->x; + + QTextDocument* doc = document(); + para->at( idx )->customItem()->enterAt( this, doc, para, idx, ox, oy, p ); } void QTextCursor::invalidateNested() { - if ( nested ) { - QValueStack<QTextParag*>::Iterator it = parags.begin(); + QValueStack<QTextParagraph*>::Iterator it = paras.begin(); QValueStack<int>::Iterator it2 = indices.begin(); - for ( ; it != parags.end(); ++it, ++it2 ) { - if ( *it == string ) + for ( ; it != paras.end(); ++it, ++it2 ) { + if ( *it == para ) continue; (*it)->invalidate( 0 ); if ( (*it)->at( *it2 )->isCustom() ) (*it)->at( *it2 )->customItem()->invalidate(); } } -} void QTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<QTextStringChar> *formatting ) { tmpIndex = -1; bool justInsert = TRUE; QString s( str ); #if defined(Q_WS_WIN) - if ( checkNewLine ) - s = s.replace( QRegExp( "\\r" ), "" ); + if ( checkNewLine ) { + int i = 0; + while ( ( i = s.find( '\r', i ) ) != -1 ) + s.remove( i ,1 ); + } #endif if ( checkNewLine ) justInsert = s.find( '\n' ) == -1; - if ( justInsert ) { - string->insert( idx, s ); + if ( justInsert ) { // we ignore new lines and insert all in the current para at the current index + para->insert( idx, s.unicode(), s.length() ); if ( formatting ) { for ( int i = 0; i < (int)s.length(); ++i ) { if ( formatting->at( i ).format() ) { formatting->at( i ).format()->addRef(); - string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE ); + para->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE ); } } } idx += s.length(); - } else { - QStringList lst = QStringList::split( '\n', s, TRUE ); - QStringList::Iterator it = lst.begin(); - int y = string->rect().y() + string->rect().height(); + } else { // we split at new lines + int start = -1; + int end; + int y = para->rect().y() + para->rect().height(); int lastIndex = 0; - QTextFormat *lastFormat = 0; - for ( ; it != lst.end(); ) { - if ( it != lst.begin() ) { - splitAndInsertEmptyParag( FALSE, TRUE ); - string->setEndState( -1 ); - string->prev()->format( -1, FALSE ); - if ( lastFormat && formatting && string->prev() ) { - lastFormat->addRef(); - string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE ); - } - } - lastFormat = 0; - QString s = *it; - ++it; - if ( !s.isEmpty() ) - string->insert( idx, s ); + do { + end = s.find( '\n', start + 1 ); // find line break + if ( end == -1 ) // didn't find one, so end of line is end of string + end = s.length(); + int len = (start == -1 ? end : end - start - 1); + if ( len > 0 ) // insert the line + para->insert( idx, s.unicode() + start + 1, len ); else - string->invalidate( 0 ); - if ( formatting ) { - int len = s.length(); + para->invalidate( 0 ); + if ( formatting ) { // set formats to the chars of the line for ( int i = 0; i < len; ++i ) { if ( formatting->at( i + lastIndex ).format() ) { formatting->at( i + lastIndex ).format()->addRef(); - string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE ); + para->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE ); } } - if ( it != lst.end() ) - lastFormat = formatting->at( len + lastIndex ).format(); - ++len; lastIndex += len; } - - idx += s.length(); + start = end; // next start is at the end of this line + idx += len; // increase the index of the cursor to the end of the inserted text + if ( s[end] == '\n' ) { // if at the end was a line break, break the line + splitAndInsertEmptyParagraph( FALSE, TRUE ); + para->setEndState( -1 ); + para->prev()->format( -1, FALSE ); + lastIndex++; } - string->format( -1, FALSE ); - int dy = string->rect().y() + string->rect().height() - y; - QTextParag *p = string; - p->setParagId( p->prev()->paragId() + 1 ); + + } while ( end < (int)s.length() ); + + para->format( -1, FALSE ); + int dy = para->rect().y() + para->rect().height() - y; + QTextParagraph *p = para; + p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 ); p = p->next(); while ( p ) { p->setParagId( p->prev()->paragId() + 1 ); p->move( dy ); p->invalidate( 0 ); p->setEndState( -1 ); p = p->next(); } } - int h = string->rect().height(); - string->format( -1, TRUE ); - if ( h != string->rect().height() ) + int h = para->rect().height(); + para->format( -1, TRUE ); + if ( h != para->rect().height() ) invalidateNested(); - else if ( doc && doc->parent() ) - doc->nextDoubleBuffered = TRUE; + else if ( para->document() && para->document()->parent() ) + para->document()->nextDoubleBuffered = TRUE; } void QTextCursor::gotoLeft() { - if ( string->string()->isRightToLeft() ) + if ( para->string()->isRightToLeft() ) gotoNextLetter(); else gotoPreviousLetter(); } void QTextCursor::gotoPreviousLetter() { tmpIndex = -1; if ( idx > 0 ) { idx--; - } else if ( string->prev() ) { - QTextParag *s = string->prev(); - while ( s && !s->isVisible() ) - s = s->prev(); - if ( s ) { - string = s; - idx = string->length() - 1; - } - } else { - if ( nested ) { + const QTextStringChar *tsc = para->at( idx ); + if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) + processNesting( EnterEnd ); + } else if ( para->prev() ) { + para = para->prev(); + while ( !para->isVisible() && para->prev() ) + para = para->prev(); + idx = para->length() - 1; + } else if ( nestedDepth() ) { pop(); processNesting( Prev ); if ( idx == -1 ) { pop(); if ( idx > 0 ) { idx--; - } else if ( string->prev() ) { - string = string->prev(); - idx = string->length() - 1; + } else if ( para->prev() ) { + para = para->prev(); + idx = para->length() - 1; } } } } - const QTextStringChar *tsc = string->at( idx ); - if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) { - processNesting( EnterEnd ); - } -} - void QTextCursor::push() { indices.push( idx ); - parags.push( string ); + paras.push( para ); xOffsets.push( ox ); yOffsets.push( oy ); - nestedStack.push( nested ); } void QTextCursor::pop() { - if ( !doc ) + if ( indices.isEmpty() ) return; idx = indices.pop(); - string = parags.pop(); + para = paras.pop(); ox = xOffsets.pop(); oy = yOffsets.pop(); - if ( doc->parent() ) - doc = doc->parent(); - nested = nestedStack.pop(); } void QTextCursor::restoreState() { while ( !indices.isEmpty() ) pop(); } -bool QTextCursor::place( const QPoint &p, QTextParag *s, bool link ) +bool QTextCursor::place( const QPoint &p, QTextParagraph *s, bool link ) { QPoint pos( p ); QRect r; - QTextParag *str = s; + QTextParagraph *str = s; if ( pos.y() < s->rect().y() ) pos.setY( s->rect().y() ); while ( s ) { r = s->rect(); - r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX ); + r.setWidth( document() ? document()->width() : QWIDGETSIZE_MAX ); if ( s->isVisible() ) str = s; if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() || !s->next() ) break; s = s->next(); } if ( !s || !str ) return FALSE; s = str; - setParag( s, FALSE ); + setParagraph( s ); int y = s->rect().y(); int lines = s->lines(); QTextStringChar *chr = 0; int index = 0; int i = 0; int cy = 0; int ch = 0; for ( ; i < lines; ++i ) { chr = s->lineStartOfLine( i, &index ); cy = s->lineY( i ); ch = s->lineHeight( i ); if ( !chr ) return FALSE; - if ( pos.y() >= y + cy && pos.y() <= y + cy + ch ) + if ( pos.y() <= y + cy + ch ) break; } int nextLine; if ( i < lines - 1 ) s->lineStartOfLine( i+1, &nextLine ); else nextLine = s->length(); i = index; int x = s->rect().x(); if ( pos.x() < x ) pos.setX( x + 1 ); int cw; int curpos = s->length()-1; int dist = 10000000; bool inCustom = FALSE; while ( i < nextLine ) { chr = s->at(i); int cpos = x + chr->x; cw = s->string()->width( i ); if ( chr->isCustom() && chr->customItem()->isNested() ) { if ( pos.x() >= cpos && pos.x() <= cpos + cw && pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) { inCustom = TRUE; curpos = i; break; } } else { if( chr->rightToLeft ) cpos += cw; int d = cpos - pos.x(); bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft; if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) { dist = QABS( d ); if ( !link || pos.x() >= x + chr->x ) curpos = i; } } i++; } - setIndex( curpos, FALSE ); + setIndex( curpos ); - if ( inCustom && doc && parag()->at( curpos )->isCustom() && parag()->at( curpos )->customItem()->isNested() ) { - QTextDocument *oldDoc = doc; + if ( inCustom && para->document() && para->at( curpos )->isCustom() && para->at( curpos )->customItem()->isNested() ) { + QTextDocument *oldDoc = para->document(); gotoIntoNested( pos ); - if ( oldDoc == doc ) + if ( oldDoc == para->document() ) return TRUE; QPoint p( pos.x() - offsetX(), pos.y() - offsetY() ); - if ( !place( p, document()->firstParag(), link ) ) + if ( !place( p, document()->firstParagraph(), link ) ) pop(); } return TRUE; } void QTextCursor::processNesting( Operation op ) { - if ( !doc ) + if ( !para->document() ) return; + QTextDocument* doc = para->document(); push(); - ox = string->at( idx )->x; + ox = para->at( idx )->x; int bl, y; - string->lineHeightOfChar( idx, &bl, &y ); - oy = y + string->rect().y(); - nested = TRUE; + para->lineHeightOfChar( idx, &bl, &y ); + oy = y + para->rect().y(); bool ok = FALSE; switch ( op ) { case EnterBegin: - ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy ); + ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy ); break; case EnterEnd: - ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy, TRUE ); + ok = para->at( idx )->customItem()->enter( this, doc, para, idx, ox, oy, TRUE ); break; case Next: - ok = string->at( idx )->customItem()->next( this, doc, string, idx, ox, oy ); + ok = para->at( idx )->customItem()->next( this, doc, para, idx, ox, oy ); break; case Prev: - ok = string->at( idx )->customItem()->prev( this, doc, string, idx, ox, oy ); + ok = para->at( idx )->customItem()->prev( this, doc, para, idx, ox, oy ); break; case Down: - ok = string->at( idx )->customItem()->down( this, doc, string, idx, ox, oy ); + ok = para->at( idx )->customItem()->down( this, doc, para, idx, ox, oy ); break; case Up: - ok = string->at( idx )->customItem()->up( this, doc, string, idx, ox, oy ); + ok = para->at( idx )->customItem()->up( this, doc, para, idx, ox, oy ); break; } if ( !ok ) pop(); } void QTextCursor::gotoRight() { - if ( string->string()->isRightToLeft() ) + if ( para->string()->isRightToLeft() ) gotoPreviousLetter(); else gotoNextLetter(); } void QTextCursor::gotoNextLetter() { tmpIndex = -1; - const QTextStringChar *tsc = string->at( idx ); + const QTextStringChar *tsc = para->at( idx ); if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) { processNesting( EnterBegin ); return; } - if ( idx < string->length() - 1 ) { + if ( idx < para->length() - 1 ) { idx++; - } else if ( string->next() ) { - QTextParag *s = string->next(); - while ( s && !s->isVisible() ) - s = s->next(); - if ( s ) { - string = s; + } else if ( para->next() ) { + para = para->next(); + while ( !para->isVisible() && para->next() ) + para = para->next(); idx = 0; - } - } else { - if ( nested ) { + } else if ( nestedDepth() ) { pop(); processNesting( Next ); if ( idx == -1 ) { pop(); - if ( idx < string->length() - 1 ) { + if ( idx < para->length() - 1 ) { idx++; - } else if ( string->next() ) { - string = string->next(); + } else if ( para->next() ) { + para = para->next(); idx = 0; } } } } -} void QTextCursor::gotoUp() { int indexOfLineStart; int line; - QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line ); + QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart ); if ( indexOfLineStart == 0 ) { - if ( !string->prev() ) { - if ( !nested ) + if ( !para->prev() ) { + if ( !nestedDepth() ) return; pop(); processNesting( Up ); if ( idx == -1 ) { pop(); - if ( !string->prev() ) + if ( !para->prev() ) return; idx = tmpIndex = 0; } else { tmpIndex = -1; return; } } - QTextParag *s = string->prev(); - while ( s && !s->isVisible() ) - s = s->prev(); - if ( s ) - string = s; - int lastLine = string->lines() - 1; - if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) ) + QTextParagraph *p = para->prev(); + while ( p && !p->isVisible() ) + p = p->prev(); + if ( p ) + para = p; + int lastLine = para->lines() - 1; + if ( !para->lineStartOfLine( lastLine, &indexOfLineStart ) ) return; - if ( indexOfLineStart + tmpIndex < string->length() ) + if ( indexOfLineStart + tmpIndex < para->length() ) idx = indexOfLineStart + tmpIndex; else - idx = string->length() - 1; + idx = para->length() - 1; } else { --line; int oldIndexOfLineStart = indexOfLineStart; - if ( !string->lineStartOfLine( line, &indexOfLineStart ) ) + if ( !para->lineStartOfLine( line, &indexOfLineStart ) ) return; if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart ) idx = indexOfLineStart + tmpIndex; else idx = oldIndexOfLineStart - 1; } } void QTextCursor::gotoDown() { int indexOfLineStart; int line; - QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line ); + QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart ); - if ( line == string->lines() - 1 ) { - if ( !string->next() ) { - if ( !nested ) + if ( line == para->lines() - 1 ) { + if ( !para->next() ) { + if ( !nestedDepth() ) return; pop(); processNesting( Down ); if ( idx == -1 ) { pop(); - if ( !string->next() ) + if ( !para->next() ) return; idx = tmpIndex = 0; } else { tmpIndex = -1; return; } } - QTextParag *s = string->next(); + QTextParagraph *s = para->next(); while ( s && !s->isVisible() ) s = s->next(); if ( s ) - string = s; - if ( !string->lineStartOfLine( 0, &indexOfLineStart ) ) + para = s; + if ( !para->lineStartOfLine( 0, &indexOfLineStart ) ) return; int end; - if ( string->lines() == 1 ) - end = string->length(); + if ( para->lines() == 1 ) + end = para->length(); else - string->lineStartOfLine( 1, &end ); + para->lineStartOfLine( 1, &end ); if ( indexOfLineStart + tmpIndex < end ) idx = indexOfLineStart + tmpIndex; else idx = end - 1; } else { ++line; int end; - if ( line == string->lines() - 1 ) - end = string->length(); + if ( line == para->lines() - 1 ) + end = para->length(); else - string->lineStartOfLine( line + 1, &end ); - if ( !string->lineStartOfLine( line, &indexOfLineStart ) ) + para->lineStartOfLine( line + 1, &end ); + if ( !para->lineStartOfLine( line, &indexOfLineStart ) ) return; if ( indexOfLineStart + tmpIndex < end ) idx = indexOfLineStart + tmpIndex; else idx = end - 1; } } void QTextCursor::gotoLineEnd() { tmpIndex = -1; int indexOfLineStart; int line; - QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line ); + QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; - if ( line == string->lines() - 1 ) { - idx = string->length() - 1; + if ( line == para->lines() - 1 ) { + idx = para->length() - 1; } else { - c = string->lineStartOfLine( ++line, &indexOfLineStart ); + c = para->lineStartOfLine( ++line, &indexOfLineStart ); indexOfLineStart--; idx = indexOfLineStart; } } void QTextCursor::gotoLineStart() { tmpIndex = -1; int indexOfLineStart; int line; - QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line ); + QTextStringChar *c = para->lineStartOfChar( idx, &indexOfLineStart, &line ); if ( !c ) return; idx = indexOfLineStart; } void QTextCursor::gotoHome() { - tmpIndex = -1; - if ( doc ) - string = doc->firstParag(); - idx = 0; + if ( topParagraph()->document() ) + gotoPosition( topParagraph()->document()->firstParagraph() ); + else + gotoLineStart(); } void QTextCursor::gotoEnd() { - if ( doc && !doc->lastParag()->isValid() ) - return; - - tmpIndex = -1; - if ( doc ) - string = doc->lastParag(); - idx = string->length() - 1; + if ( topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid() ) + gotoPosition( topParagraph()->document()->lastParagraph(), + topParagraph()->document()->lastParagraph()->length() - 1); + else + gotoLineEnd(); } void QTextCursor::gotoPageUp( int visibleHeight ) { - tmpIndex = -1; - QTextParag *s = string; - int h = visibleHeight; - int y = s->rect().y(); - while ( s ) { - if ( y - s->rect().y() >= h ) - break; - s = s->prev(); - } - - if ( !s && doc ) - s = doc->firstParag(); - - string = s; - idx = 0; + int targetY = globalY() - visibleHeight; + QTextParagraph* old; int index; + do { + old = para; index = idx; + gotoUp(); + } while ( (old != para || index != idx) && globalY() > targetY ); } void QTextCursor::gotoPageDown( int visibleHeight ) { - tmpIndex = -1; - QTextParag *s = string; - int h = visibleHeight; - int y = s->rect().y(); - while ( s ) { - if ( s->rect().y() - y >= h ) - break; - s = s->next(); - } - - if ( !s && doc ) { - s = doc->lastParag(); - string = s; - idx = string->length() - 1; - return; - } - - if ( !s->isValid() ) - return; - - string = s; - idx = 0; + int targetY = globalY() + visibleHeight; + QTextParagraph* old; int index; + do { + old = para; index = idx; + gotoDown(); + } while ( (old != para || index != idx) && globalY() < targetY ); } void QTextCursor::gotoWordRight() { - if ( string->string()->isRightToLeft() ) + if ( para->string()->isRightToLeft() ) gotoPreviousWord(); else gotoNextWord(); } void QTextCursor::gotoWordLeft() { - if ( string->string()->isRightToLeft() ) + if ( para->string()->isRightToLeft() ) gotoNextWord(); else gotoPreviousWord(); } void QTextCursor::gotoPreviousWord() { gotoPreviousLetter(); tmpIndex = -1; - QTextString *s = string->string(); + QTextString *s = para->string(); bool allowSame = FALSE; if ( idx == ((int)s->length()-1) ) return; for ( int i = idx; i >= 0; --i ) { if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' || s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) { if ( !allowSame ) continue; idx = i + 1; return; } if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' || s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) allowSame = TRUE; } idx = 0; } void QTextCursor::gotoNextWord() { tmpIndex = -1; - QTextString *s = string->string(); + QTextString *s = para->string(); bool allowSame = FALSE; for ( int i = idx; i < (int)s->length(); ++i ) { if ( ! (s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' || s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';') ) { if ( !allowSame ) continue; idx = i; return; } if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' || s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) allowSame = TRUE; } if ( idx < ((int)s->length()-1) ) { gotoLineEnd(); - } else if ( string->next() ) { - QTextParag *s = string->next(); - while ( s && !s->isVisible() ) - s = s->next(); + } else if ( para->next() ) { + QTextParagraph *p = para->next(); + while ( p && !p->isVisible() ) + p = p->next(); if ( s ) { - string = s; + para = p; idx = 0; } } else { gotoLineEnd(); } } bool QTextCursor::atParagStart() { return idx == 0; } bool QTextCursor::atParagEnd() { - return idx == string->length() - 1; + return idx == para->length() - 1; } -void QTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds ) +void QTextCursor::splitAndInsertEmptyParagraph( bool ind, bool updateIds ) { - if ( !doc ) + if ( !para->document() ) return; tmpIndex = -1; QTextFormat *f = 0; - if ( doc->useFormatCollection() ) { - f = string->at( idx )->format(); - if ( idx == string->length() - 1 && idx > 0 ) - f = string->at( idx - 1 )->format(); + if ( para->document()->useFormatCollection() ) { + f = para->at( idx )->format(); + if ( idx == para->length() - 1 && idx > 0 ) + f = para->at( idx - 1 )->format(); if ( f->isMisspelled() ) { f->removeRef(); - f = doc->formatCollection()->format( f->font(), f->color() ); + f = para->document()->formatCollection()->format( f->font(), f->color() ); } } if ( atParagEnd() ) { - QTextParag *n = string->next(); - QTextParag *s = doc->createParag( doc, string, n, updateIds ); + QTextParagraph *n = para->next(); + QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds ); if ( f ) s->setFormat( 0, 1, f, TRUE ); - s->copyParagData( string ); + s->copyParagData( para ); if ( ind ) { int oi, ni; s->indent( &oi, &ni ); - string = s; + para = s; idx = ni; } else { - string = s; + para = s; idx = 0; } } else if ( atParagStart() ) { - QTextParag *p = string->prev(); - QTextParag *s = doc->createParag( doc, p, string, updateIds ); + QTextParagraph *p = para->prev(); + QTextParagraph *s = para->document()->createParagraph( para->document(), p, para, updateIds ); if ( f ) s->setFormat( 0, 1, f, TRUE ); - s->copyParagData( string ); + s->copyParagData( para ); if ( ind ) { s->indent(); s->format(); indent(); - string->format(); + para->format(); } } else { - QString str = string->string()->toString().mid( idx, 0xFFFFFF ); - QTextParag *n = string->next(); - QTextParag *s = doc->createParag( doc, string, n, updateIds ); - s->copyParagData( string ); + QString str = para->string()->toString().mid( idx, 0xFFFFFF ); + QTextParagraph *n = para->next(); + QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds ); + s->copyParagData( para ); s->remove( 0, 1 ); s->append( str, TRUE ); for ( uint i = 0; i < str.length(); ++i ) { - s->setFormat( i, 1, string->at( idx + i )->format(), TRUE ); - if ( string->at( idx + i )->isCustom() ) { - QTextCustomItem * item = string->at( idx + i )->customItem(); + QTextStringChar* tsc = para->at( idx + i ); + s->setFormat( i, 1, tsc->format(), TRUE ); + if ( tsc->isCustom() ) { + QTextCustomItem * item = tsc->customItem(); s->at( i )->setCustomItem( item ); - string->at( idx + i )->loseCustomItem(); + tsc->loseCustomItem(); } + if ( tsc->isAnchor() ) + s->at( i )->setAnchor( tsc->anchorName(), + tsc->anchorHref() ); } - string->truncate( idx ); + para->truncate( idx ); if ( ind ) { int oi, ni; s->indent( &oi, &ni ); - string = s; + para = s; idx = ni; } else { - string = s; + para = s; idx = 0; } } invalidateNested(); } bool QTextCursor::remove() { tmpIndex = -1; if ( !atParagEnd() ) { - string->remove( idx, 1 ); - int h = string->rect().height(); - string->format( -1, TRUE ); - if ( h != string->rect().height() ) + para->remove( idx, 1 ); + int h = para->rect().height(); + para->format( -1, TRUE ); + if ( h != para->rect().height() ) invalidateNested(); - else if ( doc && doc->parent() ) - doc->nextDoubleBuffered = TRUE; + else if ( para->document() && para->document()->parent() ) + para->document()->nextDoubleBuffered = TRUE; return FALSE; - } else if ( string->next() ) { - if ( string->length() == 1 ) { - string->next()->setPrev( string->prev() ); - if ( string->prev() ) - string->prev()->setNext( string->next() ); - QTextParag *p = string->next(); - delete string; - string = p; - string->invalidate( 0 ); - QTextParag *s = string; - while ( s ) { - s->id = s->p ? s->p->id + 1 : 0; - s->state = -1; - s->needPreProcess = TRUE; - s->changed = TRUE; - s = s->n; - } - string->format(); - } else { - string->join( string->next() ); - } + } else if ( para->next() ) { + para->join( para->next() ); invalidateNested(); return TRUE; } return FALSE; } -void QTextCursor::killLine() -{ - if ( atParagEnd() ) - return; - string->remove( idx, string->length() - idx - 1 ); - int h = string->rect().height(); - string->format( -1, TRUE ); - if ( h != string->rect().height() ) - invalidateNested(); - else if ( doc && doc->parent() ) - doc->nextDoubleBuffered = TRUE; -} - void QTextCursor::indent() { int oi = 0, ni = 0; - string->indent( &oi, &ni ); + para->indent( &oi, &ni ); if ( oi == ni ) return; if ( idx >= oi ) idx += ni - oi; else idx = ni; } -void QTextCursor::setDocument( QTextDocument *d ) -{ - doc = d; - string = d->firstParag(); - idx = 0; - nested = FALSE; - restoreState(); - tmpIndex = -1; -} - // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextDocument::QTextDocument( QTextDocument *p ) - : par( p ), parParag( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 ) + : par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 ) { fCollection = new QTextFormatCollection; init(); } QTextDocument::QTextDocument( QTextDocument *p, QTextFormatCollection *f ) - : par( p ), parParag( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 ) + : par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 ) { fCollection = f; init(); } void QTextDocument::init() { -#if defined(PARSER_DEBUG) - qDebug( debug_indent + "new QTextDocument (%p)", this ); -#endif oTextValid = TRUE; mightHaveCustomItems = FALSE; if ( par ) par->insertChild( this ); pProcessor = 0; useFC = TRUE; pFormatter = 0; indenter = 0; fParag = 0; txtFormat = Qt::AutoText; preferRichText = FALSE; pages = FALSE; focusIndicator.parag = 0; minw = 0; wused = 0; minwParag = curParag = 0; align = AlignAuto; nSelections = 1; - addMargs = FALSE; - sheet_ = QStyleSheet::defaultSheet(); + setStyleSheet( QStyleSheet::defaultSheet() ); factory_ = QMimeSourceFactory::defaultFactory(); contxt = QString::null; - fCollection->setStyleSheet( sheet_ ); underlLinks = par ? par->underlLinks : TRUE; backBrush = 0; buf_pixmap = 0; nextDoubleBuffered = FALSE; if ( par ) withoutDoubleBuffer = par->withoutDoubleBuffer; else withoutDoubleBuffer = FALSE; - lParag = fParag = createParag( this, 0, 0 ); - tmpCursor = 0; + lParag = fParag = createParagraph( this, 0, 0 ); cx = 0; cy = 2; if ( par ) cx = cy = 0; cw = 600; vw = 0; flow_ = new QTextFlow; flow_->setWidth( cw ); leftmargin = rightmargin = 4; + scaleFontsFactor = 1; + selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight ); selectionText[ Standard ] = TRUE; commandHistory = new QTextCommandHistory( 100 ); tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8; } QTextDocument::~QTextDocument() { if ( par ) par->removeChild( this ); clear(); delete commandHistory; delete flow_; if ( !par ) delete pFormatter; delete fCollection; delete pProcessor; delete buf_pixmap; delete indenter; delete backBrush; if ( tArray ) delete [] tArray; } void QTextDocument::clear( bool createEmptyParag ) { if ( flow_ ) flow_->clear(); while ( fParag ) { - QTextParag *p = fParag->next(); + QTextParagraph *p = fParag->next(); delete fParag; fParag = p; } fParag = lParag = 0; if ( createEmptyParag ) - fParag = lParag = createParag( this ); + fParag = lParag = createParagraph( this ); selections.clear(); + oText = QString::null; + oTextValid = TRUE; } int QTextDocument::widthUsed() const { return wused + border_tolerance; } int QTextDocument::height() const { int h = 0; if ( lParag ) h = lParag->rect().top() + lParag->rect().height() + 1; int fh = flow_->boundingRect().bottom(); return QMAX( h, fh ); } -QTextParag *QTextDocument::createParag( QTextDocument *d, QTextParag *pr, QTextParag *nx, bool updateIds ) +QTextParagraph *QTextDocument::createParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds ) { - return new QTextParag( d, pr, nx, updateIds ); + return new QTextParagraph( d, pr, nx, updateIds ); } -bool QTextDocument::setMinimumWidth( int needed, int used, QTextParag *p ) +bool QTextDocument::setMinimumWidth( int needed, int used, QTextParagraph *p ) { if ( needed == -1 ) { minw = 0; wused = 0; p = 0; } if ( p == minwParag ) { minw = needed; emit minimumWidthChanged( minw ); } else if ( needed > minw ) { minw = needed; minwParag = p; emit minimumWidthChanged( minw ); } wused = QMAX( wused, used ); wused = QMAX( wused, minw ); cw = QMAX( minw, cw ); return TRUE; } void QTextDocument::setPlainText( const QString &text ) { clear(); preferRichText = FALSE; oTextValid = TRUE; oText = text; int lastNl = 0; int nl = text.find( '\n' ); if ( nl == -1 ) { - lParag = createParag( this, lParag, 0 ); + lParag = createParagraph( this, lParag, 0 ); if ( !fParag ) fParag = lParag; QString s = text; if ( !s.isEmpty() ) { if ( s[ (int)s.length() - 1 ] == '\r' ) s.remove( s.length() - 1, 1 ); lParag->append( s ); } } else { for (;;) { - lParag = createParag( this, lParag, 0 ); + lParag = createParagraph( this, lParag, 0 ); if ( !fParag ) fParag = lParag; QString s = text.mid( lastNl, nl - lastNl ); if ( !s.isEmpty() ) { if ( s[ (int)s.length() - 1 ] == '\r' ) s.remove( s.length() - 1, 1 ); lParag->append( s ); } if ( nl == 0xffffff ) break; lastNl = nl + 1; nl = text.find( '\n', nl + 1 ); if ( nl == -1 ) nl = 0xffffff; } } if ( !lParag ) - lParag = fParag = createParag( this, 0, 0 ); + lParag = fParag = createParagraph( this, 0, 0 ); } struct Q_EXPORT QTextDocumentTag { QTextDocumentTag(){} QTextDocumentTag( const QString&n, const QStyleSheetItem* s, const QTextFormat& f ) :name(n),style(s), format(f), alignment(Qt3::AlignAuto), direction(QChar::DirON),liststyle(QStyleSheetItem::ListDisc) { wsm = QStyleSheetItem::WhiteSpaceNormal; } QString name; const QStyleSheetItem* style; QString anchorHref; QStyleSheetItem::WhiteSpaceMode wsm; QTextFormat format; int alignment : 16; int direction : 5; QStyleSheetItem::ListStyle liststyle; QTextDocumentTag( const QTextDocumentTag& t ) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; alignment = t.alignment; direction = t.direction; liststyle = t.liststyle; } QTextDocumentTag& operator=(const QTextDocumentTag& t) { name = t.name; style = t.style; anchorHref = t.anchorHref; wsm = t.wsm; format = t.format; alignment = t.alignment; direction = t.direction; liststyle = t.liststyle; return *this; } #if defined(Q_FULL_TEMPLATE_INSTANTIATION) bool operator==( const QTextDocumentTag& ) const { return FALSE; } #endif }; -#define NEWPAR do{ if ( !hasNewPar ) curpar = createParag( this, curpar ); \ - if ( curpar->isBr ) curpar->isBr = FALSE; \ + +#define NEWPAR do{ if ( !hasNewPar) { \ + if ( !textEditMode && curpar && curpar->length()>1 && curpar->at( curpar->length()-2)->c == QChar_linesep ) \ + curpar->remove( curpar->length()-2, 1 ); \ + curpar = createParagraph( this, curpar, curpar->next() ); styles.append( vec ); vec = 0;} \ hasNewPar = TRUE; \ - curpar->setAlignment( curtag.alignment ); \ - curpar->setDirection( (QChar::Direction)curtag.direction ); \ + curpar->rtext = TRUE; \ + curpar->align = curtag.alignment; \ + curpar->lstyle = curtag.liststyle; \ + curpar->litem = ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ); \ + curpar->str->setDirection( (QChar::Direction)curtag.direction ); \ space = TRUE; \ - QPtrVector<QStyleSheetItem> vec( (uint)tags.count() + 1); \ + delete vec; vec = new QPtrVector<QStyleSheetItem>( (uint)tags.count() + 1); \ int i = 0; \ for ( QValueStack<QTextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it ) \ - vec.insert( i++, (*it).style ); \ - vec.insert( i, curtag.style ); \ - curpar->setStyleSheetItems( vec ); }while(FALSE) + vec->insert( i++, (*it).style ); \ + vec->insert( i, curtag.style ); \ + }while(FALSE) void QTextDocument::setRichText( const QString &text, const QString &context ) { - setTextFormat( Qt::RichText ); if ( !context.isEmpty() ) setContext( context ); clear(); - fParag = lParag = createParag( this ); + fParag = lParag = createParagraph( this ); + oTextValid = TRUE; + oText = text; setRichTextInternal( text ); + fParag->rtext = TRUE; } -static QStyleSheetItem::ListStyle chooseListStyle( const QStyleSheetItem *nstyle, - const QMap<QString, QString> &attr, - QStyleSheetItem::ListStyle curListStyle ) -{ - if ( nstyle->name() == "ol" || nstyle->name() == "ul" ) { - curListStyle = nstyle->listStyle(); - QMap<QString, QString>::ConstIterator it = attr.find( "type" ); - if ( it != attr.end() ) { - QString sl = *it; - if ( sl == "1" ) { - curListStyle = QStyleSheetItem::ListDecimal; - } else if ( sl == "a" ) { - curListStyle = QStyleSheetItem::ListLowerAlpha; - } else if ( sl == "A" ) { - curListStyle = QStyleSheetItem::ListUpperAlpha; - } else { - sl = sl.lower(); - if ( sl == "square" ) - curListStyle = QStyleSheetItem::ListSquare; - else if ( sl == "disc" ) - curListStyle = QStyleSheetItem::ListDisc; - else if ( sl == "circle" ) - curListStyle = QStyleSheetItem::ListCircle; - } - } - } - return curListStyle; -} - -void QTextDocument::setRichTextInternal( const QString &text ) +void QTextDocument::setRichTextInternal( const QString &text, QTextCursor* cursor ) { - oTextValid = TRUE; - oText = text; - QTextParag* curpar = lParag; + QTextParagraph* curpar = lParag; int pos = 0; QValueStack<QTextDocumentTag> tags; QTextDocumentTag initag( "", sheet_->item(""), *formatCollection()->defaultFormat() ); QTextDocumentTag curtag = initag; bool space = TRUE; + bool canMergeLi = FALSE; + + bool textEditMode = FALSE; const QChar* doc = text.unicode(); int length = text.length(); bool hasNewPar = curpar->length() <= 1; - QString lastClose; QString anchorName; + + // style sheet handling for margin and line spacing calculation below + QTextParagraph* stylesPar = curpar; + QPtrVector<QStyleSheetItem>* vec = 0; + QPtrList< QPtrVector<QStyleSheetItem> > styles; + styles.setAutoDelete( TRUE ); + + if ( cursor ) { + cursor->splitAndInsertEmptyParagraph(); + QTextCursor tmp = *cursor; + tmp.gotoPreviousLetter(); + stylesPar = curpar = tmp.paragraph(); + hasNewPar = TRUE; + textEditMode = TRUE; + } else { + NEWPAR; + } + + // set rtext spacing to FALSE for the initial paragraph. + curpar->rtext = FALSE; + + QString wellKnownTags = "br hr wsp table qt body meta title"; + while ( pos < length ) { if ( hasPrefix(doc, length, pos, '<' ) ){ if ( !hasPrefix( doc, length, pos+1, QChar('/') ) ) { // open tag QMap<QString, QString> attr; bool emptyTag = FALSE; QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag); if ( tagname.isEmpty() ) continue; // nothing we could do with this, probably parse error - if ( tagname == "title" ) { - QString title; - while ( pos < length ) { - if ( hasPrefix( doc, length, pos, QChar('<') ) && hasPrefix( doc, length, pos+1, QChar('/') ) && - parseCloseTag( doc, length, pos ) == "title" ) - break; - title += doc[ pos ]; - ++pos; - } - attribs.replace( "title", title ); - } - const QStyleSheetItem* nstyle = sheet_->item(tagname); - if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { -// if ( tagname == "br" ) { -// // our standard br emty-tag handling breaks -// // inside list items, we would get another -// // list item in this case. As workaround, fake -// // a new paragraph instead -// tagname = "p"; -// nstyle = sheet_->item( tagname ); -// } - if ( nstyle ) - hasNewPar = FALSE; // we want empty paragraphs in this case - } - if ( nstyle ) { // we might have to close some 'forgotten' tags while ( !nstyle->allowedInContext( curtag.style ) ) { QString msg; msg.sprintf( "QText Warning: Document not valid ( '%s' not allowed in '%s' #%d)", tagname.ascii(), curtag.style->name().ascii(), pos); sheet_->error( msg ); if ( tags.isEmpty() ) break; curtag = tags.pop(); } - // special handling for p. We do not want to nest there for HTML compatibility - if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) { + /* special handling for p and li for HTML + compatibility. We do not want to embed blocks in + p, and we do not want new blocks inside non-empty + lis. Plus we want to merge empty lis sometimes. */ + if( nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) { + canMergeLi = TRUE; + } else if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) { while ( curtag.style->name() == "p" ) { if ( tags.isEmpty() ) break; curtag = tags.pop(); } - } + if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { + // we are in a li and a new block comes along + if ( nstyle->name() == "ul" || nstyle->name() == "ol" ) + hasNewPar = FALSE; // we want an empty li (like most browsers) + if ( !hasNewPar ) { + /* do not add new blocks inside + non-empty lis */ + while ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { + if ( tags.isEmpty() ) + break; + curtag = tags.pop(); + } + } else if ( canMergeLi ) { + /* we have an empty li and a block + comes along, merge them */ + nstyle = curtag.style; + } + canMergeLi = FALSE; + } + } } QTextCustomItem* custom = 0; - // some well-known empty tags + + // some well-known tags, some have a nstyle, some not + if ( wellKnownTags.find( tagname ) != -1 ) { if ( tagname == "br" ) { - emptyTag = TRUE; - hasNewPar = FALSE; - if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { - // when linebreaking a list item, we do not - // actually want a new list item but just a - // new line. Fake this by pushing a paragraph - // onto the stack - tags.push( curtag ); - curtag.name = tagname; - curtag.style = nstyle; - } - NEWPAR; - curpar->isBr = TRUE; - curpar->setAlignment( curtag.alignment ); + emptyTag = space = TRUE; + int index = QMAX( curpar->length(),1) - 1; + QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); + curpar->append( QChar_linesep ); + curpar->setFormat( index, 1, &format ); } else if ( tagname == "hr" ) { - emptyTag = TRUE; + emptyTag = space = TRUE; custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this ); NEWPAR; } else if ( tagname == "table" ) { - QTextFormat format = curtag.format.makeTextFormat( nstyle, attr ); + emptyTag = space = TRUE; + QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); curpar->setAlignment( curtag.alignment ); custom = parseTable( attr, format, doc, length, pos, curpar ); - (void)eatSpace( doc, length, pos ); - emptyTag = TRUE; - } else if ( tagname == "qt" ) { - for ( QMap<QString, QString>::Iterator it = attr.begin(); it != attr.end(); ++it ) { - if ( it.key() == "bgcolor" ) { - QBrush *b = new QBrush( QColor( *it ) ); + } else if ( tagname == "qt" || tagname == "body" ) { + if ( attr.contains( "bgcolor" ) ) { + QBrush *b = new QBrush( QColor( attr["bgcolor"] ) ); setPaper( b ); - } else if ( it.key() == "background" ) { + } + if ( attr.contains( "background" ) ) { QImage img; - const QMimeSource* m = factory_->data( *it, contxt ); + QString bg = attr["background"]; + const QMimeSource* m = factory_->data( bg, contxt ); if ( !m ) { - qWarning("QRichText: no mimesource for %s", (*it).latin1() ); + qWarning("QRichText: no mimesource for %s", bg.latin1() ); } else { if ( !QImageDrag::decode( m, img ) ) { - qWarning("QTextImage: cannot decode %s", (*it).latin1() ); + qWarning("QTextImage: cannot decode %s", bg.latin1() ); } } if ( !img.isNull() ) { QPixmap pm; pm.convertFromImage( img ); QBrush *b = new QBrush( QColor(), pm ); setPaper( b ); } - } else if ( it.key() == "text" ) { - QColor c( *it ); + } + if ( attr.contains( "text" ) ) { + QColor c( attr["text"] ); if ( formatCollection()->defaultFormat()->color() != c ) { QDict<QTextFormat> formats = formatCollection()->dict(); QDictIterator<QTextFormat> it( formats ); while ( it.current() ) { if ( it.current() == formatCollection()->defaultFormat() ) { ++it; continue; } it.current()->setColor( c ); ++it; } formatCollection()->defaultFormat()->setColor( c ); curtag.format.setColor( c ); } - } else if ( it.key() == "link" ) { - linkColor = QColor( *it ); - } else if ( it.key() == "title" ) { - attribs.replace( it.key(), *it ); } + if ( attr.contains( "link" ) ) + linkColor = QColor( attr["link"] ); + if ( attr.contains( "title" ) ) + attribs.replace( "title", attr["title"] ); + + if ( textEditMode ) { + if ( attr.contains("style" ) ) { + QString a = attr["style"]; + for ( int s = 0; s < a.contains(';')+1; s++ ) { + QString style = QTextDocument::section( a, ";", s, s ); + if ( style.startsWith("font-size:" ) && QTextDocument::endsWith(style, "pt") ) { + scaleFontsFactor = double( formatCollection()->defaultFormat()->fn.pointSize() ) / + style.mid( 10, style.length() - 12 ).toInt(); } - } else { - custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this ); } + } + nstyle = 0; // ignore body in textEditMode + } + // end qt- and body-tag handling + } else if ( tagname == "meta" ) { + if ( attr["name"] == "qrichtext" && attr["content"] == "1" ) + textEditMode = TRUE; + } else if ( tagname == "title" ) { + QString title; + while ( pos < length ) { + if ( hasPrefix( doc, length, pos, QChar('<') ) && hasPrefix( doc, length, pos+1, QChar('/') ) && + parseCloseTag( doc, length, pos ) == "title" ) + break; + title += doc[ pos ]; + ++pos; + } + attribs.replace( "title", title ); + } + } // end of well-known tag handling + + if ( !custom ) // try generic custom item + custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this ); if ( !nstyle && !custom ) // we have no clue what this tag could be, ignore it continue; if ( custom ) { - int index = curpar->length() - 1; - if ( index < 0 ) - index = 0; - QTextFormat format = curtag.format.makeTextFormat( nstyle, attr ); + int index = QMAX( curpar->length(),1) - 1; + QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); curpar->append( QChar('*') ); curpar->setFormat( index, 1, &format ); curpar->at( index )->setCustomItem( custom ); if ( !curtag.anchorHref.isEmpty() ) curpar->at(index)->setAnchor( QString::null, curtag.anchorHref ); if ( !anchorName.isEmpty() ) { curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() ); anchorName = QString::null; } registerCustomItem( custom, curpar ); hasNewPar = FALSE; } else if ( !emptyTag ) { - // ignore whitespace for inline elements if there was already one - if ( nstyle->whiteSpaceMode() == QStyleSheetItem::WhiteSpaceNormal - && ( space || nstyle->displayMode() != QStyleSheetItem::DisplayInline ) ) - eatSpace( doc, length, pos ); - - // if we do nesting, push curtag on the stack, - // otherwise reinint curag. - if ( nstyle != curtag.style || nstyle->selfNesting() ) { + /* if we do nesting, push curtag on the stack, + otherwise reinint curag. */ + if ( curtag.style->name() != tagname || nstyle->selfNesting() ) { tags.push( curtag ); } else { if ( !tags.isEmpty() ) curtag = tags.top(); else curtag = initag; } - const QStyleSheetItem* ostyle = curtag.style; - curtag.name = tagname; curtag.style = nstyle; curtag.name = tagname; curtag.style = nstyle; - if ( nstyle->whiteSpaceMode() != QStyleSheetItem::WhiteSpaceNormal ) + if ( int(nstyle->whiteSpaceMode()) != QStyleSheetItem::Undefined ) curtag.wsm = nstyle->whiteSpaceMode(); - curtag.liststyle = chooseListStyle( nstyle, attr, curtag.liststyle ); - curtag.format = curtag.format.makeTextFormat( nstyle, attr ); + + /* ignore whitespace for inline elements if there + was already one*/ + if ( !textEditMode && curtag.wsm == QStyleSheetItem::WhiteSpaceNormal + && ( space || nstyle->displayMode() != QStyleSheetItem::DisplayInline ) ) + eatSpace( doc, length, pos ); + + curtag.format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor ); if ( nstyle->isAnchor() ) { if ( !anchorName.isEmpty() ) anchorName += "#" + attr["name"]; else anchorName = attr["name"]; curtag.anchorHref = attr["href"]; } if ( nstyle->alignment() != QStyleSheetItem::Undefined ) curtag.alignment = nstyle->alignment(); - if ( ostyle->displayMode() == QStyleSheetItem::DisplayListItem && - curpar->length() <= 1 - && nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) { - // do not do anything, we reuse the paragraph we have - } else if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline && nstyle->displayMode() != QStyleSheetItem::DisplayNone ) { - NEWPAR; + if ( (int) nstyle->listStyle() != QStyleSheetItem::Undefined ) + curtag.liststyle = nstyle->listStyle(); + + if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock + || nstyle->displayMode() == QStyleSheetItem::DisplayListItem ) { + + if ( nstyle->name() == "ol" || nstyle->name() == "ul" || nstyle->name() == "li") { + QString type = attr["type"]; + if ( !type.isEmpty() ) { + if ( type == "1" ) { + curtag.liststyle = QStyleSheetItem::ListDecimal; + } else if ( type == "a" ) { + curtag.liststyle = QStyleSheetItem::ListLowerAlpha; + } else if ( type == "A" ) { + curtag.liststyle = QStyleSheetItem::ListUpperAlpha; + } else { + type = type.lower(); + if ( type == "square" ) + curtag.liststyle = QStyleSheetItem::ListSquare; + else if ( type == "disc" ) + curtag.liststyle = QStyleSheetItem::ListDisc; + else if ( type == "circle" ) + curtag.liststyle = QStyleSheetItem::ListCircle; + } } - - if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { - curpar->setListStyle( curtag.liststyle ); - if ( attr.find( "value" ) != attr.end() ) - curpar->setListValue( (*attr.find( "value" )).toInt() ); } - if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline ) - curpar->setFormat( &curtag.format ); - if ( attr.contains( "align" ) && - ( curtag.name == "p" || - curtag.name == "div" || - curtag.name == "li" || - curtag.name[ 0 ] == 'h' ) ) { + /* Internally we treat ordered and bullet + lists the same for margin calculations. In + order to have fast pointer compares in the + xMargin() functions we restrict ourselves to + <ol>. Once we calculate the margins in the + parser rathern than later, the unelegance of + this approach goes awy + */ + if ( nstyle->name() == "ul" ) + curtag.style = sheet_->item( "ol" ); + + if ( attr.contains( "align" ) ) { QString align = attr["align"]; if ( align == "center" ) curtag.alignment = Qt::AlignCenter; else if ( align == "right" ) curtag.alignment = Qt::AlignRight; else if ( align == "justify" ) curtag.alignment = Qt3::AlignJustify; } - if ( attr.contains( "dir" ) && - ( curtag.name == "p" || - curtag.name == "div" || - curtag.name == "li" || - curtag.name[ 0 ] == 'h' ) ) { + if ( attr.contains( "dir" ) ) { QString dir = attr["dir"]; if ( dir == "rtl" ) curtag.direction = QChar::DirR; else if ( dir == "ltr" ) curtag.direction = QChar::DirL; } - if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline ) { - curpar->setAlignment( curtag.alignment ); - curpar->setDirection( (QChar::Direction)curtag.direction ); + + NEWPAR; + + if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { + if ( attr.contains( "value " ) ) + curpar->setListValue( attr["value"].toInt() ); + } + + if ( attr.contains( "style" ) ) { + QString a = attr["style"]; + bool ok = TRUE; + for ( int s = 0; ok && s < a.contains(';')+1; s++ ) { + QString style = QTextDocument::section( a, ";", s, s ); + if ( style.startsWith("margin-top:" ) && QTextDocument::endsWith(style, "px") ) + curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok); + else if ( style.startsWith("margin-bottom:" ) && QTextDocument::endsWith(style, "px") ) + curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok); + else if ( style.startsWith("margin-left:" ) && QTextDocument::endsWith(style, "px") ) + curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok); + else if ( style.startsWith("margin-right:" ) && QTextDocument::endsWith(style, "px") ) + curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok); + else if ( style.startsWith("text-indent:" ) && QTextDocument::endsWith(style, "px") ) + curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok); + } + if ( !ok ) // be pressmistic + curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0; + } } } } else { QString tagname = parseCloseTag( doc, length, pos ); - lastClose = tagname; if ( tagname.isEmpty() ) continue; // nothing we could do with this, probably parse error if ( !sheet_->item( tagname ) ) // ignore unknown tags continue; - // we close a block item. Since the text may continue, we need to have a new paragraph - bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock; + bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock + || curtag.style->displayMode() == QStyleSheetItem::DisplayListItem; - if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { - needNewPar = TRUE; - hasNewPar = FALSE; // we want empty paragraphs in this case - } // html slopiness: handle unbalanched tag closing while ( curtag.name != tagname ) { QString msg; msg.sprintf( "QText Warning: Document not valid ( '%s' not closed before '%s' #%d)", curtag.name.ascii(), tagname.ascii(), pos); sheet_->error( msg ); if ( tags.isEmpty() ) break; curtag = tags.pop(); } // close the tag if ( !tags.isEmpty() ) curtag = tags.pop(); else curtag = initag; if ( needNewPar ) { - if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) { - tags.push( curtag ); - curtag.name = "p"; - curtag.style = sheet_->item( curtag.name ); // a list item continues, use p for that - } + if ( textEditMode && tagname == "p" ) // preserve empty paragraphs + hasNewPar = FALSE; NEWPAR; } } } else { // normal contents QString s; QChar c; while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){ - QStyleSheetItem::WhiteSpaceMode wsm = curtag.wsm; - if ( s.length() > 4096 ) - wsm = (QStyleSheetItem::WhiteSpaceMode)QStyleSheetItem_WhiteSpaceNormalWithNewlines; - - c = parseChar( doc, length, pos, wsm ); + if ( textEditMode ) { + // text edit mode: we handle all white space but ignore newlines + c = parseChar( doc, length, pos, QStyleSheetItem::WhiteSpacePre ); + if ( c == QChar_linesep ) + break; + } else { + int l = pos; + c = parseChar( doc, length, pos, curtag.wsm ); + + // in white space pre mode: treat any space as non breakable + if ( c == ' ' && curtag.wsm == QStyleSheetItem::WhiteSpacePre ) + c = QChar::nbsp; + + if ( c == ' ' || c == QChar_linesep ) { + /* avoid overlong paragraphs by forcing a new + paragraph after 4096 characters. This case can + occur when loading undiscovered plain text + documents in rich text mode. Instead of hanging + forever, we do the trick. + */ + if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && s.length() > 4096 ) do { + if ( doc[l] == '\n' ) { + hasNewPar = FALSE; // for a new paragraph ... + NEWPAR; + hasNewPar = FALSE; // ... and make it non-reusable + c = '\n'; // make sure we break below + break; + } + } while ( ++l < pos ); + } + } - if ( c == '\n' ) // happens only in whitespacepre-mode or with WhiteSpaceNormalWithNewlines. - break; // we want a new line in this case + if ( c == '\n' ) + break; // break on newlines, pre delievers a QChar_linesep - bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && - curtag.wsm != QStyleSheetItem_WhiteSpaceNoCompression; + bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode; if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && c_isSpace && space ) continue; if ( c == '\r' ) continue; space = c_isSpace; s += c; } if ( !s.isEmpty() && curtag.style->displayMode() != QStyleSheetItem::DisplayNone ) { hasNewPar = FALSE; - int index = curpar->length() - 1; - if ( index < 0 ) - index = 0; + int index = QMAX( curpar->length(),1) - 1; curpar->append( s ); QTextFormat* f = formatCollection()->format( &curtag.format ); curpar->setFormat( index, s.length(), f, FALSE ); // do not use collection because we have done that already f->ref += s.length() -1; // that what friends are for... if ( !curtag.anchorHref.isEmpty() ) { for ( int i = 0; i < int(s.length()); i++ ) curpar->at(index + i)->setAnchor( QString::null, curtag.anchorHref ); } if ( !anchorName.isEmpty() ) { curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() ); anchorName = QString::null; } } - if ( c == '\n' ) { // happens in WhiteSpacePre mode - hasNewPar = FALSE; - tags.push( curtag ); - NEWPAR; - curtag = tags.pop(); - } } } - - if ( hasNewPar && curpar != fParag ) { + if ( hasNewPar && curpar != fParag && !cursor ) { // cleanup unused last paragraphs curpar = curpar->p; delete curpar->n; } - if ( !anchorName.isEmpty() ) { curpar->at(curpar->length() - 1)->setAnchor( anchorName, curpar->at( curpar->length() - 1 )->anchorHref() ); anchorName = QString::null; } + + + setRichTextMarginsInternal( styles, stylesPar ); + + if ( cursor ) { + cursor->gotoPreviousLetter(); + cursor->remove(); + } + +} + +void QTextDocument::setRichTextMarginsInternal( QPtrList< QPtrVector<QStyleSheetItem> >& styles, QTextParagraph* stylesPar ) +{ + // margin and line spacing calculation + QPtrVector<QStyleSheetItem>* prevStyle = 0; + QPtrVector<QStyleSheetItem>* curStyle = styles.first(); + QPtrVector<QStyleSheetItem>* nextStyle = styles.next(); + while ( stylesPar ) { + if ( !curStyle ) { + stylesPar = stylesPar->next(); + prevStyle = curStyle; + curStyle = nextStyle; + nextStyle = styles.next(); + continue; + } + + int i, mar; + QStyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0; + if ( mainStyle && mainStyle->displayMode() == QStyleSheetItem::DisplayListItem ) + stylesPar->setListItem( TRUE ); + int numLists = 0; + for ( i = 0; i < (int)curStyle->size(); ++i ) { + if ( (*curStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock + && int((*curStyle)[ i ]->listStyle()) != QStyleSheetItem::Undefined ) + numLists++; + } + stylesPar->ldepth = numLists; + if ( stylesPar->next() && nextStyle ) { + // also set the depth of the next paragraph, required for the margin calculation + numLists = 0; + for ( i = 0; i < (int)nextStyle->size(); ++i ) { + if ( (*nextStyle)[ i ]->displayMode() == QStyleSheetItem::DisplayBlock + && int((*nextStyle)[ i ]->listStyle()) != QStyleSheetItem::Undefined ) + numLists++; + } + stylesPar->next()->ldepth = numLists; + } + + // do the top margin + QStyleSheetItem* item = mainStyle; + int m; + if (stylesPar->utm > 0 ) { + m = stylesPar->utm-1; + stylesPar->utm = 0; + } else { + m = QMAX(0, item->margin( QStyleSheetItem::MarginTop ) ); + if ( item->displayMode() == QStyleSheetItem::DisplayListItem + && stylesPar->ldepth ) + m /= stylesPar->ldepth; + } + for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + if ( prevStyle && i < (int) prevStyle->size() && + ( item->displayMode() == QStyleSheetItem::DisplayBlock && + (*prevStyle)[ i ] == item ) ) + break; + // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags + if ( int(item->listStyle()) != QStyleSheetItem::Undefined && + ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) ) + continue; + mar = QMAX( 0, item->margin( QStyleSheetItem::MarginTop ) ); + m = QMAX( m, mar ); + } + stylesPar->utm = m - stylesPar->topMargin(); + + // do the bottom margin + item = mainStyle; + if (stylesPar->ubm > 0 ) { + m = stylesPar->ubm-1; + stylesPar->ubm = 0; + } else { + m = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) ); + if ( item->displayMode() == QStyleSheetItem::DisplayListItem + && stylesPar->ldepth ) + m /= stylesPar->ldepth; + } + for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + if ( nextStyle && i < (int) nextStyle->size() && + ( item->displayMode() == QStyleSheetItem::DisplayBlock && + (*nextStyle)[ i ] == item ) ) + break; + // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags + if ( int(item->listStyle()) != QStyleSheetItem::Undefined && + ( ( i> 0 && (*curStyle)[ i-1 ] == item ) || (*curStyle)[i+1] == item ) ) + continue; + mar = QMAX(0, item->margin( QStyleSheetItem::MarginBottom ) ); + m = QMAX( m, mar ); + } + stylesPar->ubm = m - stylesPar->bottomMargin(); + + // do the left margin, simplyfied + item = mainStyle; + if (stylesPar->ulm > 0 ) { + m = stylesPar->ulm-1; + stylesPar->ulm = 0; + } else { + m = QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) ); + } + for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + m += QMAX( 0, item->margin( QStyleSheetItem::MarginLeft ) ); + } + stylesPar->ulm = m - stylesPar->leftMargin(); + + // do the right margin, simplyfied + item = mainStyle; + if (stylesPar->urm > 0 ) { + m = stylesPar->urm-1; + stylesPar->urm = 0; + } else { + m = QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) ); + } + for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + m += QMAX( 0, item->margin( QStyleSheetItem::MarginRight ) ); + } + stylesPar->urm = m - stylesPar->rightMargin(); + + // do the first line margin, which really should be called text-indent + item = mainStyle; + if (stylesPar->uflm > 0 ) { + m = stylesPar->uflm-1; + stylesPar->uflm = 0; + } else { + m = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) ); + } + for ( i = (int)curStyle->size() - 2 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + mar = QMAX( 0, item->margin( QStyleSheetItem::MarginFirstLine ) ); + m = QMAX( m, mar ); + } + stylesPar->uflm =m - stylesPar->firstLineMargin(); + + // do the bogus line "spacing", which really is just an extra margin + item = mainStyle; + for ( i = (int)curStyle->size() - 1 ; i >= 0; --i ) { + item = (*curStyle)[ i ]; + if ( item->lineSpacing() != QStyleSheetItem::Undefined ) { + stylesPar->ulinespacing = item->lineSpacing(); + if ( formatCollection() && + stylesPar->ulinespacing < formatCollection()->defaultFormat()->height() ) + stylesPar->ulinespacing += formatCollection()->defaultFormat()->height(); + break; + } + } + + stylesPar = stylesPar->next(); + prevStyle = curStyle; + curStyle = nextStyle; + nextStyle = styles.next(); + } } void QTextDocument::setText( const QString &text, const QString &context ) { focusIndicator.parag = 0; selections.clear(); if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) || txtFormat == Qt::RichText ) setRichText( text, context ); else setPlainText( text ); } -QString QTextDocument::plainText( QTextParag *p ) const +QString QTextDocument::plainText() const { - if ( !p ) { QString buffer; QString s; - QTextParag *p = fParag; + QTextParagraph *p = fParag; while ( p ) { if ( !p->mightHaveCustomItems ) { s = p->string()->toString(); } else { for ( int i = 0; i < p->length() - 1; ++i ) { if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; QTextTable *t = (QTextTable*)p->at( i )->customItem(); QPtrList<QTextTableCell> cells = t->tableCells(); for ( QTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else { s += p->at( i )->c; } } } s.remove( s.length() - 1, 1 ); if ( p->next() ) s += "\n"; buffer += s; p = p->next(); } return buffer; - } else { - return p->string()->toString(); - } } -static QString align_to_string( const QString &tag, int a ) +static QString align_to_string( int a ) { - if ( tag == "p" || tag == "li" || ( tag[0] == 'h' && tag[1].isDigit() ) ) { if ( a & Qt::AlignRight ) return " align=\"right\""; - if ( a & Qt::AlignCenter ) + if ( a & Qt::AlignHCenter ) return " align=\"center\""; if ( a & Qt3::AlignJustify ) return " align=\"justify\""; - } - return ""; + return QString::null; } -static QString direction_to_string( const QString &tag, int d ) +static QString direction_to_string( int d ) { - if ( d != QChar::DirON && - ( tag == "p" || tag == "div" || tag == "li" || ( tag[0] == 'h' && tag[1].isDigit() ) ) ) + if ( d != QChar::DirON ) return ( d == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" ); - return ""; + return QString::null; } -QString QTextDocument::richText( QTextParag *p ) const +static QString list_value_to_string( int v ) { - QString s,n; - if ( !p ) { - p = fParag; - QPtrVector<QStyleSheetItem> lastItems, items; - while ( p ) { - items = p->styleSheetItems(); - if ( items.size() ) { - QStyleSheetItem *item = items[ items.size() - 1 ]; - items.resize( items.size() - 1 ); - if ( items.size() > lastItems.size() ) { - for ( int i = lastItems.size(); i < (int)items.size(); ++i ) { - n = items[i]->name(); - if ( n.isEmpty() || n == "li" ) - continue; - s += "<" + n + align_to_string( n, p->alignment() ) + ">"; + if ( v != -1 ) + return " listvalue=\"" + QString::number( v ) + "\""; + return QString::null; } - } else { - QString end; - for ( int i = items.size(); i < (int)lastItems.size(); ++i ) { - n = lastItems[i]->name(); - if ( n.isEmpty() || n == "li" || n == "br" ) - continue; - end.prepend( "</" + lastItems[ i ]->name() + ">" ); + +static QString list_style_to_string( int v ) +{ + switch( v ) { + case QStyleSheetItem::ListDecimal: return "\"1\""; + case QStyleSheetItem::ListLowerAlpha: return "\"a\""; + case QStyleSheetItem::ListUpperAlpha: return "\"A\""; + case QStyleSheetItem::ListDisc: return "\"disc\""; + case QStyleSheetItem::ListSquare: return "\"square\""; + case QStyleSheetItem::ListCircle: return "\"circle\""; + default: + return QString::null; } - s += end; } - lastItems = items; - n = item->name(); - if ( n == "li" && p->listValue() != -1 ) { - s += "<li value=\"" + QString::number( p->listValue() ) + "\">"; - } else { - QString ps = p->richText(); - if ( ps.isEmpty() ) - s += "<br>"; // empty paragraph - else if ( !n.isEmpty() ) { - s += "<" + n + align_to_string( n, p->alignment() ) - + direction_to_string( n, p->direction() ) + ">" + ps; - if ( n != "li" && n != "br") - s += "</" + n + ">"; - } else - s += ps; + +static inline bool list_is_ordered( int v ) +{ + return v == QStyleSheetItem::ListDecimal || + v == QStyleSheetItem::ListLowerAlpha || + v == QStyleSheetItem::ListUpperAlpha; } - } else { - QString end; - for ( int i = 0; i < (int)lastItems.size(); ++i ) { - QString n = lastItems[i]->name(); - if ( n.isEmpty() || n == "li" || n == "br" ) - continue; - end.prepend( "</" + n + ">" ); + + +static QString margin_to_string( QStyleSheetItem* style, int t, int b, int l, int r, int fl ) +{ + QString s; + if ( l > 0 ) + s += QString(!!s?";":"") + "margin-left:" + QString::number(l+QMAX(0,style->margin(QStyleSheetItem::MarginLeft))) + "px"; + if ( r > 0 ) + s += QString(!!s?";":"") + "margin-right:" + QString::number(r+QMAX(0,style->margin(QStyleSheetItem::MarginRight))) + "px"; + if ( t > 0 ) + s += QString(!!s?";":"") + "margin-top:" + QString::number(t+QMAX(0,style->margin(QStyleSheetItem::MarginTop))) + "px"; + if ( b > 0 ) + s += QString(!!s?";":"") + "margin-bottom:" + QString::number(b+QMAX(0,style->margin(QStyleSheetItem::MarginBottom))) + "px"; + if ( fl > 0 ) + s += QString(!!s?";":"") + "text-indent:" + QString::number(fl+QMAX(0,style->margin(QStyleSheetItem::MarginFirstLine))) + "px"; + if ( !!s ) + return " style=\"" + s + "\""; + return QString::null; } - s += end; - QString ps = p->richText(); - if ( ps.isEmpty() ) - s += "<br>"; // empty paragraph - else - s += "<p" + align_to_string( "p", p->alignment() ) + direction_to_string( "p", p->direction() ) - + ">" + ps + "</p>"; - lastItems = items; + +QString QTextDocument::richText() const +{ + QString s = ""; + if ( !par ) { + s += "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:" ; + s += QString::number( formatCollection()->defaultFormat()->font().pointSize() ); + s += "pt;font-family:"; + s += formatCollection()->defaultFormat()->font().family(); + s +="\">"; } - if ( ( p = p->next() ) ) + QTextParagraph* p = fParag; + + QStyleSheetItem* item_p = styleSheet()->item("p"); + QStyleSheetItem* item_ul = styleSheet()->item("ul"); + QStyleSheetItem* item_ol = styleSheet()->item("ol"); + QStyleSheetItem* item_li = styleSheet()->item("li"); + if ( !item_p || !item_ul || !item_ol || !item_li ) { + qWarning( "QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, ul, ol, or li)" ); + return QString::null; + } + int pastListDepth = 0; + int listDepth = 0; + int futureListDepth = 0; + QMemArray<int> listStyles(10); + + while ( p ) { + listDepth = p->listDepth(); + if ( listDepth < pastListDepth ) { + for ( int i = listDepth+1; i <= pastListDepth; i++ ) + s += list_is_ordered( listStyles[i] ) ? "</ol>" : "</ul>"; + s += '\n'; + } else if ( listDepth > pastListDepth ) { s += '\n'; + listStyles.resize( QMAX( (int)listStyles.size(), listDepth+1 ) ); + QString list_type; + listStyles[listDepth] = p->listStyle(); + if ( !list_is_ordered( p->listStyle() ) || item_ol->listStyle() != p->listStyle() ) + list_type = " type=" + list_style_to_string( p->listStyle() ); + for ( int i = pastListDepth; i < listDepth; i++ ) { + s += list_is_ordered( p->listStyle() ) ? "<ol" : "<ul" ; + s += list_type + ">"; } } else { - s = p->richText(); + s += '\n'; } + QString ps = p->richText(); + + // for the bottom margin we need to know whether we are at the end of a list + futureListDepth = 0; + if ( listDepth > 0 && p->next() ) + futureListDepth = p->next()->listDepth(); + + if ( richTextExportStart && richTextExportStart->paragraph() ==p && + richTextExportStart->index() == 0 ) + s += "<selstart/>"; + + if ( p->isListItem() ) { + s += "<li"; + if ( p->listStyle() != listStyles[listDepth] ) + s += " type=" + list_style_to_string( p->listStyle() ); + s +=align_to_string( p->alignment() ); + s += margin_to_string( item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm ); + s += list_value_to_string( p->listValue() ); + s += direction_to_string( p->direction() ); + s +=">"; + s += ps; + s += "</li>"; + } else { + // normal paragraph item + s += "<p"; + s += align_to_string( p->alignment() ); + s += margin_to_string( item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm ); + s +=direction_to_string( p->direction() ); + s += ">"; + s += ps; + s += "</p>"; + } + pastListDepth = listDepth; + p = p->next(); + } + while ( listDepth > 0 ) { + s += list_is_ordered( listStyles[listDepth] ) ? "</ol>" : "</ul>"; + listDepth--; + } + + if ( !par ) + s += "\n</body></html>\n"; + return s; } QString QTextDocument::text() const { - if ( plainText().simplifyWhiteSpace().isEmpty() ) - return QString(""); if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText ) return richText(); - return plainText( 0 ); + return plainText(); } QString QTextDocument::text( int parag ) const { - QTextParag *p = paragAt( parag ); + QTextParagraph *p = paragAt( parag ); if ( !p ) return QString::null; if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText ) - return richText( p ); + return p->richText(); else - return plainText( p ); + return p->string()->toString(); } void QTextDocument::invalidate() { - QTextParag *s = fParag; + QTextParagraph *s = fParag; while ( s ) { s->invalidate( 0 ); s = s->next(); } } void QTextDocument::selectionStart( int id, int ¶gId, int &index ) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return; QTextDocumentSelection &sel = *it; - paragId = !sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId(); + paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } QTextCursor QTextDocument::selectionStartCursor( int id) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return QTextCursor( this ); QTextDocumentSelection &sel = *it; if ( sel.swapped ) return sel.endCursor; return sel.startCursor; } QTextCursor QTextDocument::selectionEndCursor( int id) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return QTextCursor( this ); QTextDocumentSelection &sel = *it; if ( !sel.swapped ) return sel.endCursor; return sel.startCursor; } void QTextDocument::selectionEnd( int id, int ¶gId, int &index ) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return; QTextDocumentSelection &sel = *it; - paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId(); + paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId(); index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index(); } -QTextParag *QTextDocument::selectionStart( int id ) -{ - QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); - if ( it == selections.end() ) - return 0; - QTextDocumentSelection &sel = *it; - if ( sel.startCursor.parag()->paragId() < sel.endCursor.parag()->paragId() ) - return sel.startCursor.parag(); - return sel.endCursor.parag(); -} - -QTextParag *QTextDocument::selectionEnd( int id ) -{ - QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); - if ( it == selections.end() ) - return 0; - QTextDocumentSelection &sel = *it; - if ( sel.startCursor.parag()->paragId() > sel.endCursor.parag()->paragId() ) - return sel.startCursor.parag(); - return sel.endCursor.parag(); -} - void QTextDocument::addSelection( int id ) { nSelections = QMAX( nSelections, id + 1 ); } static void setSelectionEndHelper( int id, QTextDocumentSelection &sel, QTextCursor &start, QTextCursor &end ) { QTextCursor c1 = start; QTextCursor c2 = end; if ( sel.swapped ) { c1 = end; c2 = start; } - c1.parag()->removeSelection( id ); - c2.parag()->removeSelection( id ); - if ( c1.parag() != c2.parag() ) { - c1.parag()->setSelection( id, c1.index(), c1.parag()->length() - 1 ); - c2.parag()->setSelection( id, 0, c2.index() ); + c1.paragraph()->removeSelection( id ); + c2.paragraph()->removeSelection( id ); + if ( c1.paragraph() != c2.paragraph() ) { + c1.paragraph()->setSelection( id, c1.index(), c1.paragraph()->length() - 1 ); + c2.paragraph()->setSelection( id, 0, c2.index() ); } else { - c1.parag()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) ); + c1.paragraph()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) ); } sel.startCursor = start; sel.endCursor = end; - if ( sel.startCursor.parag() == sel.endCursor.parag() ) + if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() ) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); } -bool QTextDocument::setSelectionEnd( int id, QTextCursor *cursor ) +bool QTextDocument::setSelectionEnd( int id, const QTextCursor &cursor ) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return FALSE; QTextDocumentSelection &sel = *it; QTextCursor start = sel.startCursor; - QTextCursor end = *cursor; + QTextCursor end = cursor; if ( start == end ) { removeSelection( id ); setSelectionStart( id, cursor ); return TRUE; } - if ( sel.endCursor.parag() == end.parag() ) { + if ( sel.endCursor.paragraph() == end.paragraph() ) { setSelectionEndHelper( id, sel, start, end ); return TRUE; } bool inSelection = FALSE; QTextCursor c( this ); QTextCursor tmp = sel.startCursor; if ( sel.swapped ) tmp = sel.endCursor; tmp.restoreState(); - QTextCursor tmp2 = *cursor; + QTextCursor tmp2 = cursor; tmp2.restoreState(); - c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() ); - QTextCursor old; + c.setParagraph( tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph() ); bool hadStart = FALSE; bool hadEnd = FALSE; bool hadStartParag = FALSE; bool hadEndParag = FALSE; bool hadOldStart = FALSE; bool hadOldEnd = FALSE; bool leftSelection = FALSE; sel.swapped = FALSE; for ( ;; ) { if ( c == start ) hadStart = TRUE; if ( c == end ) hadEnd = TRUE; - if ( c.parag() == start.parag() ) + if ( c.paragraph() == start.paragraph() ) hadStartParag = TRUE; - if ( c.parag() == end.parag() ) + if ( c.paragraph() == end.paragraph() ) hadEndParag = TRUE; if ( c == sel.startCursor ) hadOldStart = TRUE; if ( c == sel.endCursor ) hadOldEnd = TRUE; if ( !sel.swapped && ( hadEnd && !hadStart || - hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) ) + hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index() ) ) sel.swapped = TRUE; if ( c == end && hadStartParag || c == start && hadEndParag ) { QTextCursor tmp = c; tmp.restoreState(); - if ( tmp.parag() != c.parag() ) { - int sstart = tmp.parag()->selectionStart( id ); - tmp.parag()->removeSelection( id ); - tmp.parag()->setSelection( id, sstart, tmp.index() ); + if ( tmp.paragraph() != c.paragraph() ) { + int sstart = tmp.paragraph()->selectionStart( id ); + tmp.paragraph()->removeSelection( id ); + tmp.paragraph()->setSelection( id, sstart, tmp.index() ); } } if ( inSelection && ( c == end && hadStart || c == start && hadEnd ) ) leftSelection = TRUE; else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) ) inSelection = TRUE; - bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd(); - c.parag()->removeSelection( id ); + bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection( id ) && c.atParagEnd(); + c.paragraph()->removeSelection( id ); if ( inSelection ) { - if ( c.parag() == start.parag() && start.parag() == end.parag() ) { - c.parag()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) ); - } else if ( c.parag() == start.parag() && !hadEndParag ) { - c.parag()->setSelection( id, start.index(), c.parag()->length() - 1 ); - } else if ( c.parag() == end.parag() && !hadStartParag ) { - c.parag()->setSelection( id, end.index(), c.parag()->length() - 1 ); - } else if ( c.parag() == end.parag() && hadEndParag ) { - c.parag()->setSelection( id, 0, end.index() ); - } else if ( c.parag() == start.parag() && hadStartParag ) { - c.parag()->setSelection( id, 0, start.index() ); + if ( c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph() ) { + c.paragraph()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) ); + } else if ( c.paragraph() == start.paragraph() && !hadEndParag ) { + c.paragraph()->setSelection( id, start.index(), c.paragraph()->length() - 1 ); + } else if ( c.paragraph() == end.paragraph() && !hadStartParag ) { + c.paragraph()->setSelection( id, end.index(), c.paragraph()->length() - 1 ); + } else if ( c.paragraph() == end.paragraph() && hadEndParag ) { + c.paragraph()->setSelection( id, 0, end.index() ); + } else if ( c.paragraph() == start.paragraph() && hadStartParag ) { + c.paragraph()->setSelection( id, 0, start.index() ); } else { - c.parag()->setSelection( id, 0, c.parag()->length() - 1 ); + c.paragraph()->setSelection( id, 0, c.paragraph()->length() - 1 ); } } if ( leftSelection ) inSelection = FALSE; - old = c; + if ( noSelectionAnymore ) + break; + // *ugle*hack optimization + QTextParagraph *p = c.paragraph(); + if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph() ) { c.gotoNextLetter(); - if ( old == c || noSelectionAnymore ) + if ( p == lastParagraph() && c.atParagEnd() ) break; + } else { + if ( p->document()->parent() ) + do { + c.gotoNextLetter(); + } while ( c.paragraph() == p ); + else + c.setParagraph( p->next() ); + } } if ( !sel.swapped ) - sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 ); + sel.startCursor.paragraph()->setSelection( id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1 ); sel.startCursor = start; sel.endCursor = end; - if ( sel.startCursor.parag() == sel.endCursor.parag() ) + if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() ) sel.swapped = sel.startCursor.index() > sel.endCursor.index(); setSelectionEndHelper( id, sel, start, end ); return TRUE; } void QTextDocument::selectAll( int id ) { removeSelection( id ); QTextDocumentSelection sel; sel.swapped = FALSE; QTextCursor c( this ); - c.setParag( fParag ); + c.setParagraph( fParag ); c.setIndex( 0 ); sel.startCursor = c; - c.setParag( lParag ); + c.setParagraph( lParag ); c.setIndex( lParag->length() - 1 ); sel.endCursor = c; - QTextParag *p = fParag; + selections.insert( id, sel ); + + QTextParagraph *p = fParag; while ( p ) { p->setSelection( id, 0, p->length() - 1 ); - for ( int i = 0; i < (int)p->length(); ++i ) { - if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) { - QTextTable *t = (QTextTable*)p->at( i )->customItem(); - QPtrList<QTextTableCell> tableCells = t->tableCells(); - for ( QTextTableCell *c = tableCells.first(); c; c = tableCells.next() ) - c->richText()->selectAll( id ); - } - } p = p->next(); } - selections.insert( id, sel ); + for ( QTextDocument *d = childList.first(); d; d = childList.next() ) + d->selectAll( id ); } bool QTextDocument::removeSelection( int id ) { - QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); - if ( it == selections.end() ) + if ( !selections.contains( id ) ) return FALSE; - QTextDocumentSelection &sel = *it; - - if ( sel.startCursor == sel.endCursor ) { - selections.remove( id ); - return TRUE; - } - - if ( !mightHaveCustomItems ) { - QTextCursor start = sel.startCursor; - QTextCursor end = sel.endCursor; - if ( sel.swapped ) { - start = sel.endCursor; - end = sel.startCursor; - } + QTextDocumentSelection &sel = selections[ id ]; - for ( QTextParag *p = start.parag(); p; p = p->next() ) { + QTextCursor start = sel.swapped ? sel.endCursor : sel.startCursor; + QTextCursor end = sel.swapped ? sel.startCursor : sel.endCursor; + QTextParagraph* p = 0; + while ( start != end ) { + if ( p != start.paragraph() ) { + p = start.paragraph(); p->removeSelection( id ); - if ( p == end.parag() ) - break; } - - selections.remove( id ); - return TRUE; + start.gotoNextLetter(); } - - QTextCursor c( this ); - QTextCursor tmp = sel.startCursor; - if ( sel.swapped ) - tmp = sel.endCursor; - tmp.restoreState(); - c.setParag( tmp.parag() ); - QTextCursor old; - bool hadStart = FALSE; - bool hadEnd = FALSE; - QTextParag *lastParag = 0; - bool leftSelection = FALSE; - bool inSelection = FALSE; - sel.swapped = FALSE; - for ( ;; ) { - if ( c.parag() == sel.startCursor.parag() ) - hadStart = TRUE; - if ( c.parag() == sel.endCursor.parag() ) - hadEnd = TRUE; - - if ( inSelection && - ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) - leftSelection = TRUE; - else if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) ) - inSelection = TRUE; - - bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd(); - - if ( lastParag != c.parag() ) - c.parag()->removeSelection( id ); - - old = c; - lastParag = c.parag(); - c.gotoNextLetter(); - if ( old == c || noSelectionAnymore ) - break; - } - selections.remove( id ); return TRUE; } -QString QTextDocument::selectedText( int id, bool withCustom ) const +QString QTextDocument::selectedText( int id, bool asRichText ) const { - // ######## TODO: look at textFormat() and return rich text or plain text (like the text() method!) QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id ); if ( it == selections.end() ) return QString::null; QTextDocumentSelection sel = *it; QTextCursor c1 = sel.startCursor; QTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } /* 3.0.3 improvement: Make it possible to get a reasonable selection inside a table. This approach is very conservative: make sure that both cursors have the same depth level and point to paragraphs within the same text document. Meaning if you select text in two table cells, you will get the entire table. This is still far better than the 3.0.2, where you always got the entire table. - ### Fix this properly for 3.0.4. + ### Fix this properly when refactoring */ while ( c2.nestedDepth() > c1.nestedDepth() ) c2.oneUp(); while ( c1.nestedDepth() > c2.nestedDepth() ) c1.oneUp(); while ( c1.nestedDepth() && c2.nestedDepth() && - c1.parag()->document() != c2.parag()->document() ) { + c1.paragraph()->document() != c2.paragraph()->document() ) { c1.oneUp(); c2.oneUp(); } - // do not trust sel_swapped with tables. Fix this properly for 3.0.4 as well - if ( c1.parag()->paragId() > c2.parag()->paragId() || - (c1.parag() == c2.parag() && c1.index() > c2.index() ) ) { + // do not trust sel_swapped with tables. Fix this properly when refactoring as well + if ( c1.paragraph()->paragId() > c2.paragraph()->paragId() || + (c1.paragraph() == c2.paragraph() && c1.index() > c2.index() ) ) { QTextCursor tmp = c1; c2 = c1; c1 = tmp; } // end selection 3.0.3 improvement + if ( asRichText && !parent() ) { + richTextExportStart = &c1; + richTextExportEnd = &c2; + + QString sel = richText(); + int from = sel.find( "<selstart/>" ); + int to = sel.findRev( "<selend/>" ); + if ( from >= 0 && from <= to ) + sel = sel.mid( from, to - from ); + richTextExportStart = richTextExportEnd = 0; + return sel; + } - if ( c1.parag() == c2.parag() ) { QString s; - QTextParag *p = c1.parag(); + if ( c1.paragraph() == c2.paragraph() ) { + QTextParagraph *p = c1.paragraph(); int end = c2.index(); if ( p->at( QMAX( 0, end - 1 ) )->isCustom() ) ++end; - if ( !withCustom || !p->mightHaveCustomItems ) { + if ( !p->mightHaveCustomItems ) { s += p->string()->toString().mid( c1.index(), end - c1.index() ); } else { for ( int i = c1.index(); i < end; ++i ) { if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; QTextTable *t = (QTextTable*)p->at( i )->customItem(); QPtrList<QTextTableCell> cells = t->tableCells(); for ( QTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else { s += p->at( i )->c; } } } - return s; - } - - QString s; - QTextParag *p = c1.parag(); + } else { + QTextParagraph *p = c1.paragraph(); int start = c1.index(); while ( p ) { - int end = p == c2.parag() ? c2.index() : p->length() - 1; - if ( p == c2.parag() && p->at( QMAX( 0, end - 1 ) )->isCustom() ) + int end = p == c2.paragraph() ? c2.index() : p->length() - 1; + if ( p == c2.paragraph() && p->at( QMAX( 0, end - 1 ) )->isCustom() ) ++end; - if ( !withCustom || !p->mightHaveCustomItems ) { + if ( !p->mightHaveCustomItems ) { s += p->string()->toString().mid( start, end - start ); - if ( p != c2.parag() ) + if ( p != c2.paragraph() ) s += "\n"; } else { for ( int i = start; i < end; ++i ) { if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { s += "\n"; QTextTable *t = (QTextTable*)p->at( i )->customItem(); QPtrList<QTextTableCell> cells = t->tableCells(); for ( QTextTableCell *c = cells.first(); c; c = cells.next() ) s += c->richText()->plainText() + "\n"; s += "\n"; } } else { s += p->at( i )->c; } } } start = 0; - if ( p == c2.parag() ) + if ( p == c2.paragraph() ) break; p = p->next(); } + } + // ### workaround for plain text export until we get proper + // mime types: turn unicode line seperators into the more + // widely understood \n. Makes copy and pasting code snipplets + // from within Assistent possible + QChar* uc = (QChar*) s.unicode(); + for ( uint ii = 0; ii < s.length(); ii++ ) + if ( uc[(int)ii] == QChar_linesep ) + uc[(int)ii] = QChar('\n'); return s; } void QTextDocument::setFormat( int id, QTextFormat *f, int flags ) { QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id ); if ( it == selections.end() ) return; QTextDocumentSelection sel = *it; QTextCursor c1 = sel.startCursor; QTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } c2.restoreState(); c1.restoreState(); - if ( c1.parag() == c2.parag() ) { - c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags ); + if ( c1.paragraph() == c2.paragraph() ) { + c1.paragraph()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags ); return; } - c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f, TRUE, flags ); - QTextParag *p = c1.parag()->next(); - while ( p && p != c2.parag() ) { + c1.paragraph()->setFormat( c1.index(), c1.paragraph()->length() - c1.index(), f, TRUE, flags ); + QTextParagraph *p = c1.paragraph()->next(); + while ( p && p != c2.paragraph() ) { p->setFormat( 0, p->length(), f, TRUE, flags ); p = p->next(); } - c2.parag()->setFormat( 0, c2.index(), f, TRUE, flags ); -} - -void QTextDocument::copySelectedText( int id ) -{ -#ifndef QT_NO_CLIPBOARD - if ( !hasSelection( id ) ) - return; - - QApplication::clipboard()->setText( selectedText( id ) ); -#endif + c2.paragraph()->setFormat( 0, c2.index(), f, TRUE, flags ); } void QTextDocument::removeSelectedText( int id, QTextCursor *cursor ) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return; QTextDocumentSelection sel = *it; QTextCursor c1 = sel.startCursor; QTextCursor c2 = sel.endCursor; if ( sel.swapped ) { c2 = sel.startCursor; c1 = sel.endCursor; } // ### no support for editing tables yet if ( c1.nestedDepth() || c2.nestedDepth() ) return; c2.restoreState(); c1.restoreState(); *cursor = c1; removeSelection( id ); - if ( c1.parag() == c2.parag() ) { - c1.parag()->remove( c1.index(), c2.index() - c1.index() ); + if ( c1.paragraph() == c2.paragraph() ) { + c1.paragraph()->remove( c1.index(), c2.index() - c1.index() ); return; } - if ( c1.parag() == fParag && c1.index() == 0 && - c2.parag() == lParag && c2.index() == lParag->length() - 1 ) + if ( c1.paragraph() == fParag && c1.index() == 0 && + c2.paragraph() == lParag && c2.index() == lParag->length() - 1 ) cursor->setValid( FALSE ); bool didGoLeft = FALSE; - if ( c1.index() == 0 && c1.parag() != fParag ) { + if ( c1.index() == 0 && c1.paragraph() != fParag ) { cursor->gotoPreviousLetter(); if ( cursor->isValid() ) didGoLeft = TRUE; } - c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() ); - QTextParag *p = c1.parag()->next(); + c1.paragraph()->remove( c1.index(), c1.paragraph()->length() - 1 - c1.index() ); + QTextParagraph *p = c1.paragraph()->next(); int dy = 0; - QTextParag *tmp; - while ( p && p != c2.parag() ) { + QTextParagraph *tmp; + while ( p && p != c2.paragraph() ) { tmp = p->next(); dy -= p->rect().height(); delete p; p = tmp; } - c2.parag()->remove( 0, c2.index() ); + c2.paragraph()->remove( 0, c2.index() ); while ( p ) { p->move( dy ); p->invalidate( 0 ); p->setEndState( -1 ); p = p->next(); } - c1.parag()->join( c2.parag() ); + c1.paragraph()->join( c2.paragraph() ); if ( didGoLeft ) cursor->gotoNextLetter(); } void QTextDocument::indentSelection( int id ) { QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id ); if ( it == selections.end() ) return; QTextDocumentSelection sel = *it; - QTextParag *startParag = sel.startCursor.parag(); - QTextParag *endParag = sel.endCursor.parag(); - if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) { - endParag = sel.startCursor.parag(); - startParag = sel.endCursor.parag(); + QTextParagraph *startParag = sel.startCursor.paragraph(); + QTextParagraph *endParag = sel.endCursor.paragraph(); + if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) { + endParag = sel.startCursor.paragraph(); + startParag = sel.endCursor.paragraph(); } - QTextParag *p = startParag; + QTextParagraph *p = startParag; while ( p && p != endParag ) { p->indent(); p = p->next(); } } void QTextDocument::addCommand( QTextCommand *cmd ) { commandHistory->addCommand( cmd ); } QTextCursor *QTextDocument::undo( QTextCursor *c ) { return commandHistory->undo( c ); } QTextCursor *QTextDocument::redo( QTextCursor *c ) { return commandHistory->redo( c ); } -bool QTextDocument::find( const QString &expr, bool cs, bool wo, bool forward, - int *parag, int *index, QTextCursor *cursor ) +bool QTextDocument::find( QTextCursor& cursor, const QString &e, bool cs, bool wo, bool forward ) { - QTextParag *p = forward ? fParag : lParag; - if ( parag ) - p = paragAt( *parag ); - else if ( cursor ) - p = cursor->parag(); - bool first = TRUE; - - while ( p ) { - QString s = p->string()->toString(); - s.remove( s.length() - 1, 1 ); // get rid of trailing space - int start = forward ? 0 : s.length() - 1; - if ( first && index ) - start = *index; - else if ( first ) - start = cursor->index(); - if ( !forward && first ) { - start -= expr.length() + 1; - if ( start < 0 ) { - first = FALSE; - p = p->prev(); - continue; + removeSelection( Standard ); + QTextParagraph *p = 0; + QString expr = e; + // if we search for 'word only' than we have to be sure that + // the expression contains no space or punct character at the + // beginning or in the end. Otherwise we would run into a + // endlessloop. + if ( wo ) { + for ( ;; ) { + if ( expr[ 0 ].isSpace() || expr[ 0 ].isPunct() ) + expr = expr.right( expr.length() - 1 ); + else + break; } + for ( ;; ) { + if ( expr.at( expr.length() - 1 ).isSpace() || expr.at( expr.length() - 1 ).isPunct() ) + expr = expr.left( expr.length() - 1 ); + else + break; } - first = FALSE; - + } + for (;;) { + if ( p != cursor.paragraph() ) { + p = cursor.paragraph(); + QString s = cursor.paragraph()->string()->toString(); + int start = cursor.index(); for ( ;; ) { int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs ); - if ( res == -1 ) - break; - - bool ok = TRUE; - if ( wo ) { int end = res + expr.length(); - if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) && - ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) ) - ok = TRUE; - else - ok = FALSE; - } - if ( ok ) { - cursor->setParag( p ); - cursor->setIndex( res ); + if ( res == -1 || ( !forward && start < end ) ) + break; + if ( !wo || ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) && + ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) ) ) { + removeSelection( Standard ); + cursor.setIndex( forward ? end : res ); setSelectionStart( Standard, cursor ); - cursor->setIndex( res + expr.length() ); + cursor.setIndex( forward ? res : end ); setSelectionEnd( Standard, cursor ); - if ( parag ) - *parag = p->paragId(); - if ( index ) - *index = res; return TRUE; } + start = res + (forward ? 1 : -1); + } + } if ( forward ) { - start = res + 1; + if ( cursor.paragraph() == lastParagraph() && cursor.atParagEnd () ) + break; + cursor.gotoNextLetter(); } else { - if ( res == 0 ) + if ( cursor.paragraph() == firstParagraph() && cursor.atParagStart() ) break; - start = res - 1; - } + cursor.gotoPreviousLetter(); } - p = forward ? p->next() : p->prev(); } - return FALSE; } void QTextDocument::setTextFormat( Qt::TextFormat f ) { txtFormat = f; - if ( txtFormat == Qt::RichText && fParag && fParag == lParag && fParag->length() <= 1 ) { - QPtrVector<QStyleSheetItem> v = fParag->styleSheetItems(); - v.resize( v.size() + 1 ); - v.insert( v.size() - 1, styleSheet()->item( "p" ) ); - fParag->setStyleSheetItems( v ); - } - + if ( fParag == lParag && fParag->length() <= 1 ) + fParag->rtext = ( f == Qt::RichText ); } Qt::TextFormat QTextDocument::textFormat() const { return txtFormat; } bool QTextDocument::inSelection( int selId, const QPoint &pos ) const { QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( selId ); if ( it == selections.end() ) return FALSE; QTextDocumentSelection sel = *it; - QTextParag *startParag = sel.startCursor.parag(); - QTextParag *endParag = sel.endCursor.parag(); - if ( sel.startCursor.parag() == sel.endCursor.parag() && - sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) ) + QTextParagraph *startParag = sel.startCursor.paragraph(); + QTextParagraph *endParag = sel.endCursor.paragraph(); + if ( sel.startCursor.paragraph() == sel.endCursor.paragraph() && + sel.startCursor.paragraph()->selectionStart( selId ) == sel.endCursor.paragraph()->selectionEnd( selId ) ) return FALSE; - if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) { - endParag = sel.startCursor.parag(); - startParag = sel.endCursor.parag(); + if ( sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) { + endParag = sel.startCursor.paragraph(); + startParag = sel.endCursor.paragraph(); } - QTextParag *p = startParag; + QTextParagraph *p = startParag; while ( p ) { if ( p->rect().contains( pos ) ) { bool inSel = FALSE; int selStart = p->selectionStart( selId ); int selEnd = p->selectionEnd( selId ); int y = 0; int h = 0; for ( int i = 0; i < p->length(); ++i ) { if ( i == selStart ) inSel = TRUE; if ( i == selEnd ) break; if ( p->at( i )->lineStart ) { y = (*p->lineStarts.find( i ))->y; h = (*p->lineStarts.find( i ))->h; } if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) { if ( inSel && pos.x() >= p->at( i )->x && pos.x() <= p->at( i )->x + p->at( i )->format()->width( p->at( i )->c ) ) return TRUE; } } } if ( pos.y() < p->rect().y() ) break; if ( p == endParag ) break; p = p->next(); } return FALSE; } void QTextDocument::doLayout( QPainter *p, int w ) { minw = wused = 0; if ( !is_printer( p ) ) p = 0; withoutDoubleBuffer = ( p != 0 ); QPainter * oldPainter = QTextFormat::painter(); QTextFormat::setPainter( p ); flow_->setWidth( w ); cw = w; vw = w; - QTextParag *parag = fParag; + QTextParagraph *parag = fParag; while ( parag ) { parag->invalidate( 0 ); if ( p ) parag->adjustToPainter( p ); parag->format(); parag = parag->next(); } QTextFormat::setPainter( oldPainter ); } QPixmap *QTextDocument::bufferPixmap( const QSize &s ) { - if ( !buf_pixmap ) { - int w = QABS( s.width() ); - int h = QABS( s.height() ); - buf_pixmap = new QPixmap( w, h ); - } else { - if ( buf_pixmap->width() < s.width() || - buf_pixmap->height() < s.height() ) { - buf_pixmap->resize( QMAX( s.width(), buf_pixmap->width() ), - QMAX( s.height(), buf_pixmap->height() ) ); - } - } - + if ( !buf_pixmap ) + buf_pixmap = new QPixmap( s.expandedTo( QSize(1,1) ) ); + else if ( buf_pixmap->size() != s ) + buf_pixmap->resize( s.expandedTo( buf_pixmap->size() ) ); return buf_pixmap; } void QTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper ) { - if ( !firstParag() ) + if ( !firstParagraph() ) return; if ( paper ) { p->setBrushOrigin( 0, 0 ); p->fillRect( rect, *paper ); } - if ( formatCollection()->defaultFormat()->color() != cg.text() ) { - QDict<QTextFormat> formats = formatCollection()->dict(); - QDictIterator<QTextFormat> it( formats ); - while ( it.current() ) { - if ( it.current() == formatCollection()->defaultFormat() ) { - ++it; - continue; - } - it.current()->setColor( cg.text() ); - ++it; - } - formatCollection()->defaultFormat()->setColor( cg.text() ); - } + if ( formatCollection()->defaultFormat()->color() != cg.text() ) + setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() ); - QTextParag *parag = firstParag(); + QTextParagraph *parag = firstParagraph(); while ( parag ) { if ( !parag->isValid() ) parag->format(); int y = parag->rect().y(); QRect pr( parag->rect() ); pr.setX( 0 ); pr.setWidth( QWIDGETSIZE_MAX ); if ( !rect.isNull() && !rect.intersects( pr ) ) { parag = parag->next(); continue; } p->translate( 0, y ); if ( rect.isValid() ) parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() ); else parag->paint( *p, cg, 0, FALSE ); p->translate( 0, -y ); parag = parag->next(); if ( !flow()->isEmpty() ) flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE ); } } -void QTextDocument::drawParag( QPainter *p, QTextParag *parag, int cx, int cy, int cw, int ch, +void QTextDocument::drawParagraph( QPainter *p, QTextParagraph *parag, int cx, int cy, int cw, int ch, QPixmap *&doubleBuffer, const QColorGroup &cg, bool drawCursor, QTextCursor *cursor, bool resetChanged ) { QPainter *painter = 0; if ( resetChanged ) parag->setChanged( FALSE ); QRect ir( parag->rect() ); bool useDoubleBuffer = !parag->document()->parent(); if ( !useDoubleBuffer && parag->document()->nextDoubleBuffered ) useDoubleBuffer = TRUE; if ( is_printer( p ) ) useDoubleBuffer = FALSE; if ( useDoubleBuffer ) { painter = new QPainter; if ( cx >= 0 && cy >= 0 ) ir = ir.intersect( QRect( cx, cy, cw, ch ) ); if ( !doubleBuffer || ir.width() > doubleBuffer->width() || ir.height() > doubleBuffer->height() ) { doubleBuffer = bufferPixmap( ir.size() ); painter->begin( doubleBuffer ); } else { painter->begin( doubleBuffer ); } } else { painter = p; painter->translate( ir.x(), ir.y() ); } painter->setBrushOrigin( -ir.x(), -ir.y() ); - if ( useDoubleBuffer || is_printer( painter ) ) { - if ( !parag->backgroundColor() ) - painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), - cg.brush( QColorGroup::Base ) ); - else - painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), - *parag->backgroundColor() ); - } else { - if ( cursor && cursor->parag() == parag ) { - if ( !parag->backgroundColor() ) + if ( useDoubleBuffer || is_printer( painter ) ) + painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), parag->backgroundBrush( cg ) ); + else if ( cursor && cursor->paragraph() == parag ) painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ), - cg.brush( QColorGroup::Base ) ); - else - painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ), - *parag->backgroundColor() ); - } - } + parag->backgroundBrush( cg ) ); painter->translate( -( ir.x() - parag->rect().x() ), -( ir.y() - parag->rect().y() ) ); parag->paint( *painter, cg, drawCursor ? cursor : 0, TRUE, cx, cy, cw, ch ); if ( useDoubleBuffer ) { delete painter; painter = 0; p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) ); } else { painter->translate( -ir.x(), -ir.y() ); } + if ( useDoubleBuffer ) { if ( parag->rect().x() + parag->rect().width() < parag->document()->x() + parag->document()->width() ) { p->fillRect( parag->rect().x() + parag->rect().width(), parag->rect().y(), ( parag->document()->x() + parag->document()->width() ) - ( parag->rect().x() + parag->rect().width() ), parag->rect().height(), cg.brush( QColorGroup::Base ) ); } + } parag->document()->nextDoubleBuffered = FALSE; } -QTextParag *QTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg, +QTextParagraph *QTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool onlyChanged, bool drawCursor, QTextCursor *cursor, bool resetChanged ) { if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) { withoutDoubleBuffer = TRUE; QRect r; draw( p, r, cg ); return 0; } withoutDoubleBuffer = FALSE; - if ( !firstParag() ) + if ( !firstParagraph() ) return 0; - if ( drawCursor && cursor ) - tmpCursor = cursor; if ( cx < 0 && cy < 0 ) { cx = 0; cy = 0; cw = width(); ch = height(); } - QTextParag *lastFormatted = 0; - QTextParag *parag = firstParag(); + QTextParagraph *lastFormatted = 0; + QTextParagraph *parag = firstParagraph(); QPixmap *doubleBuffer = 0; QPainter painter; + bool fullWidthSelection = FALSE; while ( parag ) { lastFormatted = parag; if ( !parag->isValid() ) parag->format(); - if ( !parag->rect().intersects( QRect( cx, cy, cw, ch ) ) ) { - QRect pr( parag->rect() ); + QRect pr = parag->rect(); + if ( fullWidthSelection ) pr.setWidth( parag->document()->width() ); - if ( pr.intersects( QRect( cx, cy, cw, ch ) ) ) - p->fillRect( pr.intersect( QRect( cx, cy, cw, ch ) ), cg.brush( QColorGroup::Base ) ); - if ( parag->rect().y() > cy + ch ) { - tmpCursor = 0; + if ( pr.y() > cy + ch ) goto floating; - } + if ( !pr.intersects( QRect( cx, cy, cw, ch ) ) || ( onlyChanged && !parag->hasChanged() ) ) { parag = parag->next(); continue; } - if ( !parag->hasChanged() && onlyChanged ) { - parag = parag->next(); - continue; - } - - drawParag( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged ); + drawParagraph( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged ); parag = parag->next(); } - parag = lastParag(); + parag = lastParagraph(); floating: if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) { - if ( !parag->document()->parent() ) { // !useDoubleBuffer + if ( !parag->document()->parent() ) { p->fillRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(), parag->document()->height() - ( parag->rect().y() + parag->rect().height() ), cg.brush( QColorGroup::Base ) ); } if ( !flow()->isEmpty() ) { QRect cr( cx, cy, cw, ch ); -// cr = cr.intersect( QRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(), -// parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) ) ); flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE ); } } if ( buf_pixmap && buf_pixmap->height() > 300 ) { delete buf_pixmap; buf_pixmap = 0; } - tmpCursor = 0; return lastFormatted; } -void QTextDocument::setDefaultFont( const QFont &f ) +/* + #### this function only sets the default font size in the format collection + */ +void QTextDocument::setDefaultFormat( const QFont &font, const QColor &color ) { - int s = f.pointSize(); - bool usePixels = FALSE; - if ( s == -1 ) { - s = f.pixelSize(); - usePixels = TRUE; + bool reformat = font != fCollection->defaultFormat()->font(); + for ( QTextDocument *d = childList.first(); d; d = childList.next() ) + d->setDefaultFormat( font, color ); + fCollection->updateDefaultFormat( font, color, sheet_ ); + + if ( !reformat ) + return; + tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8; + + // invalidate paragraphs and custom items + QTextParagraph *p = fParag; + while ( p ) { + p->invalidate( 0 ); + for ( int i = 0; i < p->length() - 1; ++i ) + if ( p->at( i )->isCustom() ) + p->at( i )->customItem()->invalidate(); + p = p->next(); } - updateFontSizes( s, usePixels ); } -void QTextDocument::registerCustomItem( QTextCustomItem *i, QTextParag *p ) +void QTextDocument::registerCustomItem( QTextCustomItem *i, QTextParagraph *p ) { if ( i && i->placement() != QTextCustomItem::PlaceInline ) { flow_->registerFloatingItem( i ); p->registerFloatingItem( i ); i->setParagraph( p ); } p->mightHaveCustomItems = mightHaveCustomItems = TRUE; } -void QTextDocument::unregisterCustomItem( QTextCustomItem *i, QTextParag *p ) +void QTextDocument::unregisterCustomItem( QTextCustomItem *i, QTextParagraph *p ) { flow_->unregisterFloatingItem( i ); p->unregisterFloatingItem( i ); i->setParagraph( 0 ); } bool QTextDocument::hasFocusParagraph() const { return !!focusIndicator.parag; } QString QTextDocument::focusHref() const { return focusIndicator.href; } bool QTextDocument::focusNextPrevChild( bool next ) { if ( !focusIndicator.parag ) { if ( next ) { focusIndicator.parag = fParag; focusIndicator.start = 0; focusIndicator.len = 0; } else { focusIndicator.parag = lParag; focusIndicator.start = lParag->length(); focusIndicator.len = 0; } } else { focusIndicator.parag->setChanged( TRUE ); } focusIndicator.href = QString::null; if ( next ) { - QTextParag *p = focusIndicator.parag; + QTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start + focusIndicator.len; while ( p ) { for ( int i = index; i < p->length(); ++i ) { if ( p->at( i )->isAnchor() ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at( i )->anchorHref(); while ( i < p->length() ) { if ( !p->at( i )->isAnchor() ) return TRUE; focusIndicator.len++; i++; } } else if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { QTextTable *t = (QTextTable*)p->at( i )->customItem(); QPtrList<QTextTableCell> cells = t->tableCells(); // first try to continue QTextTableCell *c; bool resetCells = TRUE; for ( c = cells.first(); c; c = cells.next() ) { if ( c->richText()->hasFocusParagraph() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); return TRUE; } else { resetCells = FALSE; c = cells.next(); break; } } } // now really try if ( resetCells ) c = cells.first(); for ( ; c; c = cells.next() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); return TRUE; } } } } } index = 0; p = p->next(); } } else { - QTextParag *p = focusIndicator.parag; + QTextParagraph *p = focusIndicator.parag; int index = focusIndicator.start - 1; if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 ) index++; while ( p ) { for ( int i = index; i >= 0; --i ) { if ( p->at( i )->isAnchor() ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = p->at( i )->anchorHref(); while ( i >= -1 ) { if ( i < 0 || !p->at( i )->isAnchor() ) { focusIndicator.start++; return TRUE; } if ( i < 0 ) break; focusIndicator.len++; focusIndicator.start--; i--; } } else if ( p->at( i )->isCustom() ) { if ( p->at( i )->customItem()->isNested() ) { QTextTable *t = (QTextTable*)p->at( i )->customItem(); QPtrList<QTextTableCell> cells = t->tableCells(); // first try to continue QTextTableCell *c; bool resetCells = TRUE; for ( c = cells.last(); c; c = cells.prev() ) { if ( c->richText()->hasFocusParagraph() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); return TRUE; } else { resetCells = FALSE; c = cells.prev(); break; } } if ( cells.at() == 0 ) break; } // now really try if ( resetCells ) c = cells.last(); for ( ; c; c = cells.prev() ) { if ( c->richText()->focusNextPrevChild( next ) ) { p->setChanged( TRUE ); focusIndicator.parag = p; focusIndicator.start = i; focusIndicator.len = 0; focusIndicator.href = c->richText()->focusHref(); return TRUE; } if ( cells.at() == 0 ) break; } } } } p = p->prev(); if ( p ) index = p->length() - 1; } } focusIndicator.parag = 0; return FALSE; } int QTextDocument::length() const { int l = 0; - QTextParag *p = fParag; + QTextParagraph *p = fParag; while ( p ) { l += p->length() - 1; // don't count trailing space p = p->next(); } return l; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ int QTextFormat::width( const QChar &c ) const { if ( c.unicode() == 0xad ) // soft hyphen return 0; if ( !pntr || !pntr->isActive() ) { if ( c == '\t' ) return fm.width( 'x' ) * 8; if ( ha == AlignNormal ) { int w; if ( c.row() ) w = fm.width( c ); else w = widths[ c.unicode() ]; if ( w == 0 && !c.row() ) { w = fm.width( c ); ( (QTextFormat*)this )->widths[ c.unicode() ] = w; } return w; } else { QFont f( fn ); if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); QFontMetrics fm_( f ); return fm_.width( c ); } } QFont f( fn ); if ( ha != AlignNormal ) { if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); } pntr->setFont( f ); return pntr->fontMetrics().width( c ); } int QTextFormat::width( const QString &str, int pos ) const { int w = 0; if ( str[ pos ].unicode() == 0xad ) return w; if ( !pntr || !pntr->isActive() ) { if ( ha == AlignNormal ) { w = fm.width( str[ pos ] ); } else { QFont f( fn ); if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); QFontMetrics fm_( f ); w = fm_.width( str[ pos ] ); } } else { QFont f( fn ); if ( ha != AlignNormal ) { if ( usePixelSizes ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); } pntr->setFont( f ); w = pntr->fontMetrics().width( str[ pos ] ); } return w; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextString::QTextString() { bidiDirty = FALSE; bidi = FALSE; rightToLeft = FALSE; dir = QChar::DirON; } QTextString::QTextString( const QTextString &s ) { bidiDirty = s.bidiDirty; bidi = s.bidi; rightToLeft = s.rightToLeft; dir = s.dir; data = s.subString(); } void QTextString::insert( int index, const QString &s, QTextFormat *f ) { + insert( index, s.unicode(), s.length(), f ); +} + +void QTextString::insert( int index, const QChar *unicode, int len, QTextFormat *f ) +{ int os = data.size(); - data.resize( data.size() + s.length() ); + data.resize( data.size() + len ); if ( index < os ) { - memmove( data.data() + index + s.length(), data.data() + index, + memmove( data.data() + index + len, data.data() + index, sizeof( QTextStringChar ) * ( os - index ) ); } - for ( int i = 0; i < (int)s.length(); ++i ) { + for ( int i = 0; i < len; ++i ) { data[ (int)index + i ].x = 0; data[ (int)index + i ].lineStart = 0; data[ (int)index + i ].d.format = 0; data[ (int)index + i ].type = QTextStringChar::Regular; data[ (int)index + i ].rightToLeft = 0; data[ (int)index + i ].startOfRun = 0; - data[ (int)index + i ].c = s[ i ]; + data[ (int)index + i ].c = unicode[i]; data[ (int)index + i ].setFormat( f ); } bidiDirty = TRUE; } QTextString::~QTextString() { clear(); } -void QTextString::insert( int index, QTextStringChar *c ) +void QTextString::insert( int index, QTextStringChar *c, bool doAddRefFormat ) { int os = data.size(); data.resize( data.size() + 1 ); if ( index < os ) { memmove( data.data() + index + 1, data.data() + index, sizeof( QTextStringChar ) * ( os - index ) ); } data[ (int)index ].c = c->c; data[ (int)index ].x = 0; data[ (int)index ].lineStart = 0; data[ (int)index ].rightToLeft = 0; data[ (int)index ].d.format = 0; data[ (int)index ].type = QTextStringChar::Regular; + if ( doAddRefFormat && c->format() ) + c->format()->addRef(); data[ (int)index ].setFormat( c->format() ); bidiDirty = TRUE; } void QTextString::truncate( int index ) { index = QMAX( index, 0 ); index = QMIN( index, (int)data.size() - 1 ); if ( index < (int)data.size() ) { for ( int i = index + 1; i < (int)data.size(); ++i ) { if ( !(data[ i ].type == QTextStringChar::Regular) ) { delete data[ i ].customItem(); if ( data[ i ].d.custom->format ) data[ i ].d.custom->format->removeRef(); delete data[ i ].d.custom; data[ i ].d.custom = 0; } else if ( data[ i ].format() ) { data[ i ].format()->removeRef(); } } } data.truncate( index ); bidiDirty = TRUE; } void QTextString::remove( int index, int len ) { for ( int i = index; i < (int)data.size() && i - index < len; ++i ) { if ( !(data[ i ].type == QTextStringChar::Regular) ) { delete data[ i ].customItem(); if ( data[ i ].d.custom->format ) data[ i ].d.custom->format->removeRef(); delete data[ i ].d.custom; data[ i ].d.custom = 0; } else if ( data[ i ].format() ) { data[ i ].format()->removeRef(); } } memmove( data.data() + index, data.data() + index + len, sizeof( QTextStringChar ) * ( data.size() - index - len ) ); data.resize( data.size() - len ); bidiDirty = TRUE; } void QTextString::clear() { for ( int i = 0; i < (int)data.count(); ++i ) { if ( !(data[ i ].type == QTextStringChar::Regular) ) { delete data[ i ].customItem(); if ( data[ i ].d.custom->format ) data[ i ].d.custom->format->removeRef(); delete data[ i ].d.custom; data[ i ].d.custom = 0; } else if ( data[ i ].format() ) { data[ i ].format()->removeRef(); } } data.resize( 0 ); } void QTextString::setFormat( int index, QTextFormat *f, bool useCollection ) { if ( useCollection && data[ index ].format() ) data[ index ].format()->removeRef(); data[ index ].setFormat( f ); } void QTextString::checkBidi() const { bool rtlKnown = FALSE; if ( dir == QChar::DirR ) { ((QTextString *)this)->bidi = TRUE; ((QTextString *)this)->rightToLeft = TRUE; rtlKnown = TRUE; return; } else if ( dir == QChar::DirL ) { ((QTextString *)this)->rightToLeft = FALSE; rtlKnown = TRUE; } else { ((QTextString *)this)->rightToLeft = FALSE; } int len = data.size(); const QTextStringChar *c = data.data(); ((QTextString *)this)->bidi = FALSE; while( len ) { if ( !rtlKnown ) { switch( c->c.direction() ) { case QChar::DirL: case QChar::DirLRO: case QChar::DirLRE: ((QTextString *)this)->rightToLeft = FALSE; rtlKnown = TRUE; break; case QChar::DirR: case QChar::DirAL: case QChar::DirRLO: case QChar::DirRLE: ((QTextString *)this)->rightToLeft = TRUE; rtlKnown = TRUE; break; default: break; } } uchar row = c->c.row(); if( (row > 0x04 && row < 0x09) || (row > 0xfa && row < 0xff) ) { ((QTextString *)this)->bidi = TRUE; if ( rtlKnown ) return; } len--; ++c; } } void QTextDocument::setStyleSheet( QStyleSheet *s ) { if ( !s ) return; sheet_ = s; - fCollection->setStyleSheet( s ); - updateStyles(); + list_tm = list_bm = par_tm = par_bm = 12; + list_lm = 40; + li_tm = li_bm = 0; + QStyleSheetItem* item = s->item( "ol" ); + if ( item ) { + list_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop )); + list_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom )); + list_lm = QMAX(0,item->margin( QStyleSheetItem::MarginLeft )); } - -void QTextDocument::updateStyles() -{ - invalidate(); - if ( par ) - underlLinks = par->underlLinks; - fCollection->updateStyles(); - for ( QTextDocument *d = childList.first(); d; d = childList.next() ) - d->updateStyles(); + if ( (item = s->item( "li" ) ) ) { + li_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop )); + li_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom )); + } + if ( (item = s->item( "p" ) ) ) { + par_tm = QMAX(0,item->margin( QStyleSheetItem::MarginTop )); + par_bm = QMAX(0,item->margin( QStyleSheetItem::MarginBottom )); } - -void QTextDocument::updateFontSizes( int base, bool usePixels ) -{ - for ( QTextDocument *d = childList.first(); d; d = childList.next() ) - d->updateFontSizes( base, usePixels ); - invalidate(); - fCollection->updateFontSizes( base, usePixels ); } -void QTextDocument::updateFontAttributes( const QFont &f, const QFont &old ) -{ +void QTextDocument::setUnderlineLinks( bool b ) { + underlLinks = b; for ( QTextDocument *d = childList.first(); d; d = childList.next() ) - d->updateFontAttributes( f, old ); - invalidate(); - fCollection->updateFontAttributes( f, old ); + d->setUnderlineLinks( b ); } void QTextStringChar::setFormat( QTextFormat *f ) { if ( type == Regular ) { d.format = f; } else { if ( !d.custom ) { d.custom = new CustomData; d.custom->custom = 0; } d.custom->format = f; } } void QTextStringChar::setCustomItem( QTextCustomItem *i ) { if ( type == Regular ) { QTextFormat *f = format(); d.custom = new CustomData; d.custom->format = f; } else { delete d.custom->custom; } d.custom->custom = i; type = (type == Anchor ? CustomAnchor : Custom); } void QTextStringChar::loseCustomItem() { if ( type == Custom ) { QTextFormat *f = d.custom->format; d.custom->custom = 0; delete d.custom; type = Regular; d.format = f; } else if ( type == CustomAnchor ) { d.custom->custom = 0; type = Anchor; } } QString QTextStringChar::anchorName() const { if ( type == Regular ) return QString::null; else return d.custom->anchorName; } QString QTextStringChar::anchorHref() const { if ( type == Regular ) return QString::null; else return d.custom->anchorHref; } void QTextStringChar::setAnchor( const QString& name, const QString& href ) { if ( type == Regular ) { QTextFormat *f = format(); d.custom = new CustomData; d.custom->custom = 0; d.custom->format = f; type = Anchor; } else if ( type == Custom ) { type = CustomAnchor; } d.custom->anchorName = name; d.custom->anchorHref = href; } int QTextString::width( int idx ) const { int w = 0; QTextStringChar *c = &at( idx ); - if ( c->c.unicode() == 0xad ) + if ( c->c.unicode() == 0xad || c->c.unicode() == 0x2028 ) return 0; if( c->isCustom() ) { if( c->customItem()->placement() == QTextCustomItem::PlaceInline ) w = c->customItem()->width; } else { int r = c->c.row(); if( r < 0x06 || r > 0x1f ) w = c->format()->width( c->c ); else { // complex text. We need some hacks to get the right metric here QString str; int pos = 0; if( idx > 4 ) pos = idx - 4; int off = idx - pos; int end = QMIN( length(), idx + 4 ); while ( pos < end ) { str += at(pos).c; pos++; } w = c->format()->width( str, off ); } } return w; } QMemArray<QTextStringChar> QTextString::subString( int start, int len ) const { if ( len == 0xFFFFFF ) len = data.size(); QMemArray<QTextStringChar> a; a.resize( len ); for ( int i = 0; i < len; ++i ) { QTextStringChar *c = &data[ i + start ]; a[ i ].c = c->c; a[ i ].x = 0; a[ i ].lineStart = 0; a[ i ].rightToLeft = 0; a[ i ].d.format = 0; a[ i ].type = QTextStringChar::Regular; a[ i ].setFormat( c->format() ); if ( c->format() ) c->format()->addRef(); } return a; } -QTextStringChar *QTextStringChar::clone() const -{ - QTextStringChar *chr = new QTextStringChar; - chr->c = c; - chr->x = 0; - chr->lineStart = 0; - chr->rightToLeft = 0; - chr->d.format = 0; - chr->type = QTextStringChar::Regular; - chr->setFormat( format() ); - if ( chr->format() ) - chr->format()->addRef(); - return chr; -} - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -QTextParag::QTextParag( QTextDocument *d, QTextParag *pr, QTextParag *nx, bool updateIds ) - : invalid( 0 ), p( pr ), n( nx ), docOrPseudo( d ), align( 0 ),mSelections( 0 ), - mStyleSheetItemsVec( 0 ), mFloatingItems( 0 ), listS( QStyleSheetItem::ListDisc ), - numSubParag( -1 ), tm( -1 ), bm( -1 ), lm( -1 ), rm( -1 ), flm( -1 ), - tArray(0), tabStopWidth(0), eData( 0 ) -{ - listS = QStyleSheetItem::ListDisc; - if ( ! (hasdoc = docOrPseudo != 0 ) ) - docOrPseudo = new QTextParagPseudoDocument; +QTextParagraph::QTextParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds ) + : invalid( 0 ), p( pr ), n( nx ), docOrPseudo( d ), + changed(FALSE), firstFormat(TRUE), firstPProcess(TRUE), needPreProcess(FALSE), fullWidth(TRUE), + lastInFrame(FALSE), visible(TRUE), breakable(TRUE), movedDown(FALSE), + mightHaveCustomItems(FALSE), hasdoc( d != 0 ), litem(FALSE), rtext(FALSE), + align( 0 ),mSelections( 0 ), + mFloatingItems( 0 ), lstyle( QStyleSheetItem::ListDisc ), + utm( 0 ), ubm( 0 ), ulm( 0 ), urm( 0 ), uflm( 0 ), ulinespacing( 0 ), + tArray(0), tabStopWidth(0), eData( 0 ), ldepth( 0 ) +{ + lstyle = QStyleSheetItem::ListDisc; + if ( !hasdoc ) + docOrPseudo = new QTextParagraphPseudoDocument; bgcol = 0; - breakable = TRUE; - isBr = FALSE; - movedDown = FALSE; - mightHaveCustomItems = FALSE; - visible = TRUE; list_val = -1; - newLinesAllowed = FALSE; - lastInFrame = FALSE; - defFormat = formatCollection()->defaultFormat(); + QTextFormat* defFormat = formatCollection()->defaultFormat(); if ( !hasdoc ) { tabStopWidth = defFormat->width( 'x' ) * 8; pseudoDocument()->commandHistory = new QTextCommandHistory( 100 ); } -#if defined(PARSER_DEBUG) - qDebug( debug_indent + "new QTextParag" ); -#endif - fullWidth = TRUE; if ( p ) p->n = this; if ( n ) n->p = this; if ( !p && hasdoc ) - document()->setFirstParag( this ); + document()->setFirstParagraph( this ); if ( !n && hasdoc ) - document()->setLastParag( this ); + document()->setLastParagraph( this ); - changed = FALSE; - firstFormat = TRUE; state = -1; - needPreProcess = FALSE; if ( p ) id = p->id + 1; else id = 0; if ( n && updateIds ) { - QTextParag *s = n; + QTextParagraph *s = n; while ( s ) { s->id = s->p->id + 1; - s->numSubParag = -1; - s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1; + s->invalidateStyleCache(); s = s->n; } } - firstPProcess = TRUE; str = new QTextString(); str->insert( 0, " ", formatCollection()->defaultFormat() ); } -QTextParag::~QTextParag() +QTextParagraph::~QTextParagraph() { delete str; if ( hasdoc ) { register QTextDocument *doc = document(); if ( this == doc->minwParag ) { doc->minwParag = 0; doc->minw = 0; } if ( this == doc->curParag ) doc->curParag = 0; } else { delete pseudoDocument(); } if ( tArray ) delete [] tArray; delete eData; - QMap<int, QTextParagLineStart*>::Iterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin(); for ( ; it != lineStarts.end(); ++it ) delete *it; if ( mSelections ) delete mSelections; if ( mFloatingItems ) delete mFloatingItems; - if ( mStyleSheetItemsVec ) - delete mStyleSheetItemsVec; if ( p ) p->setNext( n ); if ( n ) n->setPrev( p ); } -void QTextParag::setNext( QTextParag *s ) +void QTextParagraph::setNext( QTextParagraph *s ) { n = s; if ( !n && hasdoc ) - document()->setLastParag( this ); + document()->setLastParagraph( this ); } -void QTextParag::setPrev( QTextParag *s ) +void QTextParagraph::setPrev( QTextParagraph *s ) { p = s; if ( !p && hasdoc ) - document()->setFirstParag( this ); + document()->setFirstParagraph( this ); } -void QTextParag::invalidate( int chr ) +void QTextParagraph::invalidate( int chr ) { if ( invalid < 0 ) invalid = chr; else invalid = QMIN( invalid, chr ); if ( mFloatingItems ) { for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) i->ypos = -1; } - lm = rm = bm = tm = flm = -1; + invalidateStyleCache(); } -void QTextParag::insert( int index, const QString &s ) +void QTextParagraph::invalidateStyleCache() +{ + if ( list_val < 0 ) + list_val = -1; +} + + +void QTextParagraph::insert( int index, const QString &s ) +{ + insert( index, s.unicode(), s.length() ); +} + +void QTextParagraph::insert( int index, const QChar *unicode, int len ) { if ( hasdoc && !document()->useFormatCollection() && document()->preProcessor() ) - str->insert( index, s, + str->insert( index, unicode, len, document()->preProcessor()->format( QTextPreProcessor::Standard ) ); else - str->insert( index, s, formatCollection()->defaultFormat() ); + str->insert( index, unicode, len, formatCollection()->defaultFormat() ); invalidate( index ); needPreProcess = TRUE; } -void QTextParag::truncate( int index ) +void QTextParagraph::truncate( int index ) { str->truncate( index ); insert( length(), " " ); needPreProcess = TRUE; } -void QTextParag::remove( int index, int len ) +void QTextParagraph::remove( int index, int len ) { if ( index + len - str->length() > 0 ) return; for ( int i = index; i < index + len; ++i ) { QTextStringChar *c = at( i ); if ( hasdoc && c->isCustom() ) { document()->unregisterCustomItem( c->customItem(), this ); } } str->remove( index, len ); invalidate( 0 ); needPreProcess = TRUE; } -void QTextParag::join( QTextParag *s ) +void QTextParagraph::join( QTextParagraph *s ) { int oh = r.height() + s->r.height(); n = s->n; if ( n ) n->p = this; else if ( hasdoc ) - document()->setLastParag( this ); + document()->setLastParagraph( this ); int start = str->length(); if ( length() > 0 && at( length() - 1 )->c == ' ' ) { remove( length() - 1, 1 ); --start; } append( s->str->toString(), TRUE ); for ( int i = 0; i < s->length(); ++i ) { if ( !hasdoc || document()->useFormatCollection() ) { s->str->at( i ).format()->addRef(); str->setFormat( i + start, s->str->at( i ).format(), TRUE ); } if ( s->str->at( i ).isCustom() ) { QTextCustomItem * item = s->str->at( i ).customItem(); str->at( i + start ).setCustomItem( item ); s->str->at( i ).loseCustomItem(); + if ( hasdoc ) { + document()->unregisterCustomItem( item, s ); + document()->registerCustomItem( item, this ); + } } if ( s->str->at( i ).isAnchor() ) { str->at( i + start ).setAnchor( s->str->at( i ).anchorName(), s->str->at( i ).anchorHref() ); } } if ( !extraData() && s->extraData() ) { setExtraData( s->extraData() ); s->setExtraData( 0 ); } else if ( extraData() && s->extraData() ) { extraData()->join( s->extraData() ); } delete s; invalidate( 0 ); r.setHeight( oh ); needPreProcess = TRUE; if ( n ) { - QTextParag *s = n; + QTextParagraph *s = n; + s->invalidate( 0 ); while ( s ) { s->id = s->p->id + 1; s->state = -1; s->needPreProcess = TRUE; s->changed = TRUE; + s->invalidateStyleCache(); s = s->n; } } format(); state = -1; } -void QTextParag::move( int &dy ) +void QTextParagraph::move( int &dy ) { if ( dy == 0 ) return; changed = TRUE; r.moveBy( 0, dy ); if ( mFloatingItems ) { for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) i->ypos += dy; } if ( p ) p->lastInFrame = TRUE; // do page breaks if required if ( hasdoc && document()->isPageBreakEnabled() ) { int shift; if ( ( shift = document()->formatter()->formatVertically( document(), this ) ) ) { if ( p ) p->setChanged( TRUE ); dy += shift; } } } -void QTextParag::format( int start, bool doMove ) +void QTextParagraph::format( int start, bool doMove ) { if ( !str || str->length() == 0 || !formatter() ) return; if ( hasdoc && document()->preProcessor() && ( needPreProcess || state == -1 ) ) document()->preProcessor()->process( document(), this, invalid <= 0 ? 0 : invalid ); needPreProcess = FALSE; if ( invalid == -1 ) return; r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) ); - r.setWidth( documentWidth() ); if ( p ) p->lastInFrame = FALSE; movedDown = FALSE; bool formattedAgain = FALSE; formatAgain: + r.setWidth( documentWidth() ); if ( hasdoc && mFloatingItems ) { for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) { i->ypos = r.y(); if ( i->placement() == QTextCustomItem::PlaceRight ) { i->xpos = r.x() + r.width() - i->width; } } } - QMap<int, QTextParagLineStart*> oldLineStarts = lineStarts; + QMap<int, QTextLineStart*> oldLineStarts = lineStarts; lineStarts.clear(); int y = formatter()->format( document(), this, start, oldLineStarts ); r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) ); - QMap<int, QTextParagLineStart*>::Iterator it = oldLineStarts.begin(); + QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin(); for ( ; it != oldLineStarts.end(); ++it ) delete *it; QTextStringChar *c = 0; // do not do this on mac, as the paragraph // with has to be the full document width on mac as the selections // always extend completely to the right. This is a bit unefficient, // as this results in a bigger double buffer than needed but ok for // now. - if ( lineStarts.count() == 1 ) { //&& ( !doc || document()->flow()->isEmpty() ) ) { + if ( lineStarts.count() == 1 ) { if ( !string()->isBidi() ) { c = &str->at( str->length() - 1 ); r.setWidth( c->x + str->width( str->length() - 1 ) ); } else { r.setWidth( lineStarts[0]->w ); } } - if ( newLinesAllowed ) { + if ( !hasdoc ) { // qt_format_text bounding rect handling it = lineStarts.begin(); int usedw = 0; for ( ; it != lineStarts.end(); ++it ) usedw = QMAX( usedw, (*it)->w ); if ( r.width() <= 0 ) { // if the user specifies an invalid rect, this means that the // bounding box should grow to the width that the text actually // needs r.setWidth( usedw ); } else { r.setWidth( QMIN( usedw, r.width() ) ); } } if ( y != r.height() ) r.setHeight( y ); if ( !visible ) { r.setHeight( 0 ); } else { int minw = formatter()->minimumWidth(); int wused = formatter()->widthUsed(); wused = QMAX( minw, wused ); if ( hasdoc ) { document()->setMinimumWidth( minw, wused, this ); } else { pseudoDocument()->minw = QMAX( pseudoDocument()->minw, minw ); pseudoDocument()->wused = QMAX( pseudoDocument()->wused, wused ); } } // do page breaks if required if ( hasdoc && document()->isPageBreakEnabled() ) { int shift = document()->formatter()->formatVertically( document(), this ); if ( shift && !formattedAgain ) { formattedAgain = TRUE; goto formatAgain; } } if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) { int dy = ( r.y() + r.height() ) - n->r.y(); - QTextParag *s = n; + QTextParagraph *s = n; bool makeInvalid = p && p->lastInFrame; while ( s && dy ) { if ( !s->isFullWidth() ) makeInvalid = TRUE; if ( makeInvalid ) s->invalidate( 0 ); s->move( dy ); if ( s->lastInFrame ) makeInvalid = TRUE; s = s->n; } } firstFormat = FALSE; changed = TRUE; invalid = -1; //##### string()->setTextChanged( FALSE ); } -int QTextParag::lineHeightOfChar( int i, int *bl, int *y ) const +int QTextParagraph::lineHeightOfChar( int i, int *bl, int *y ) const { if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.end(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end(); --it; for ( ;; ) { if ( i >= it.key() ) { if ( bl ) *bl = ( *it )->baseLine; if ( y ) *y = ( *it )->y; return ( *it )->h; } if ( it == lineStarts.begin() ) break; --it; } - qWarning( "QTextParag::lineHeightOfChar: couldn't find lh for %d", i ); + qWarning( "QTextParagraph::lineHeightOfChar: couldn't find lh for %d", i ); return 15; } -QTextStringChar *QTextParag::lineStartOfChar( int i, int *index, int *line ) const +QTextStringChar *QTextParagraph::lineStartOfChar( int i, int *index, int *line ) const { if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); int l = (int)lineStarts.count() - 1; - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.end(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end(); --it; for ( ;; ) { if ( i >= it.key() ) { if ( index ) *index = it.key(); if ( line ) *line = l; return &str->at( it.key() ); } if ( it == lineStarts.begin() ) break; --it; --l; } - qWarning( "QTextParag::lineStartOfChar: couldn't find %d", i ); + qWarning( "QTextParagraph::lineStartOfChar: couldn't find %d", i ); return 0; } -int QTextParag::lines() const +int QTextParagraph::lines() const { if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); return (int)lineStarts.count(); } -QTextStringChar *QTextParag::lineStartOfLine( int line, int *index ) const +QTextStringChar *QTextParagraph::lineStartOfLine( int line, int *index ) const { if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); if ( line >= 0 && line < (int)lineStarts.count() ) { - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while ( line-- > 0 ) ++it; int i = it.key(); if ( index ) *index = i; return &str->at( i ); } - qWarning( "QTextParag::lineStartOfLine: couldn't find %d", line ); + qWarning( "QTextParagraph::lineStartOfLine: couldn't find %d", line ); return 0; } -int QTextParag::leftGap() const +int QTextParagraph::leftGap() const { if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); int line = 0; int x = str->at(0).x; /* set x to x of first char */ if ( str->isBidi() ) { - for ( int i = 1; i < str->length(); ++i ) + for ( int i = 1; i < str->length()-1; ++i ) x = QMIN(x, str->at(i).x); return x; } - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while (line < (int)lineStarts.count()) { int i = it.key(); /* char index */ x = QMIN(x, str->at(i).x); ++it; ++line; } return x; } -void QTextParag::setFormat( int index, int len, QTextFormat *f, bool useCollection, int flags ) +void QTextParagraph::setFormat( int index, int len, QTextFormat *f, bool useCollection, int flags ) { if ( !f ) return; if ( index < 0 ) index = 0; if ( index > str->length() - 1 ) index = str->length() - 1; if ( index + len >= str->length() ) len = str->length() - index; QTextFormatCollection *fc = 0; if ( useCollection ) fc = formatCollection(); QTextFormat *of; for ( int i = 0; i < len; ++i ) { of = str->at( i + index ).format(); if ( !changed && f->key() != of->key() ) changed = TRUE; if ( invalid == -1 && ( f->font().family() != of->font().family() || f->font().pointSize() != of->font().pointSize() || f->font().weight() != of->font().weight() || f->font().italic() != of->font().italic() || f->vAlign() != of->vAlign() ) ) { invalidate( 0 ); } if ( flags == -1 || flags == QTextFormat::Format || !fc ) { if ( fc ) f = fc->format( f ); str->setFormat( i + index, f, useCollection ); } else { QTextFormat *fm = fc->format( of, f, flags ); str->setFormat( i + index, fm, useCollection ); } } } -void QTextParag::indent( int *oldIndent, int *newIndent ) +void QTextParagraph::indent( int *oldIndent, int *newIndent ) { - if ( !hasdoc || !document()->indent() || style() && style()->displayMode() != QStyleSheetItem::DisplayBlock ) { + if ( !hasdoc || !document()->indent() || isListItem() ) { if ( oldIndent ) *oldIndent = 0; if ( newIndent ) *newIndent = 0; if ( oldIndent && newIndent ) *newIndent = *oldIndent; return; } document()->indent()->indent( document(), this, oldIndent, newIndent ); } -void QTextParag::paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor, bool drawSelections, +void QTextParagraph::paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor, bool drawSelections, int clipx, int clipy, int clipw, int cliph ) { if ( !visible ) return; - QTextStringChar *chr = at( 0 ); - int i = 0; - int h = 0; - int baseLine = 0, lastBaseLine = 0; - QTextStringChar *formatChar = 0; - int lastY = -1; - int startX = 0; - int bw = 0; - int cy = 0; - int curx = -1, cury = 0, curh = 0; - bool lastDirection = chr->rightToLeft; - const int full_sel_width = (hasdoc ? document()->width() : r.width()); -#if 0 // seems we don't need that anymore - int tw = 0; -#endif + QTextStringChar *chr = 0; + int i, y, h, baseLine, xstart, xend; + i = y =h = baseLine = 0; + QRect cursorRect; + drawSelections &= ( mSelections != 0 ); + // macintosh full-width selection style + bool fullWidthStyle = FALSE; + int fullSelectionWidth = 0; + if ( drawSelections && fullWidthStyle ) + fullSelectionWidth = (hasdoc ? document()->width() : r.width()); QString qstr = str->toString(); - // ### workaround so that \n are not drawn, actually this should be - // fixed in QFont somewhere (under Windows you get ugly boxes + // ### workaround so that \n are not drawn, actually this should + // be fixed in QFont somewhere (under Windows you get ugly boxes // otherwise) QChar* uc = (QChar*) qstr.unicode(); - for ( uint ii = 0; ii < qstr.length(); ii++ ) { - if ( uc[(int)ii]== '\n' ) + for ( uint ii = 0; ii < qstr.length(); ii++ ) + if ( uc[(int)ii]== '\n' || uc[(int)ii] == QChar_linesep || uc[(int)ii] == '\t' ) uc[(int)ii] = 0x20; - } - - - const int nSels = hasdoc ? document()->numSelections() : 1; - QMemArray<int> selectionStarts( nSels ); - QMemArray<int> selectionEnds( nSels ); - if ( drawSelections ) { - bool hasASelection = FALSE; - for ( i = 0; i < nSels; ++i ) { - if ( !hasSelection( i ) ) { - selectionStarts[ i ] = -1; - selectionEnds[ i ] = -1; - } else { - hasASelection = TRUE; - selectionStarts[ i ] = selectionStart( i ); - int end = selectionEnd( i ); - if ( end == length() - 1 && n && n->hasSelection( i ) ) - end++; - selectionEnds[ i ] = end; - } - } - if ( !hasASelection ) - drawSelections = FALSE; - } int line = -1; - int cw; - bool didListLabel = FALSE; int paintStart = 0; - int paintEnd = -1; - int lasth = 0; + int selection = -1; for ( i = 0; i < length(); i++ ) { chr = at( i ); -#if 0 // seems we don't need that anymore - if ( !str->isBidi() && is_printer( &painter ) ) { // ### fix our broken ps-printer - if ( !chr->lineStart ) - chr->x = QMAX( chr->x, tw ); - else - tw = 0; + + // we flush at end of document + bool flush = i== length()-1; + bool selectionStateChanged = FALSE; + if ( !flush ) { + QTextStringChar *nextchr = at( i+1 ); + // we flush at end of line + flush |= nextchr->lineStart; + // we flush on format changes + flush |= ( nextchr->format() != chr->format() ); + // we flush on anchor changes + flush |= ( nextchr->isAnchor() != chr->isAnchor() ); + // we flush on start of run + flush |= nextchr->startOfRun; + // we flush on bidi changes + flush |= ( nextchr->rightToLeft != chr->rightToLeft ); + // we flush on tab + flush |= ( chr->c == '\t' ); + // we flush on soft hypens + flush |= ( chr->c.unicode() == 0xad ); + // we flush on custom items + flush |= chr->isCustom(); + // we flush before custom items + flush |= nextchr->isCustom(); + // when painting justified, we flush on spaces + if ((alignment() & Qt3::AlignJustify) == Qt3::AlignJustify ) + flush |= QTextFormatter::isBreakable( str, i ); + // we flush when the string is getting too long + flush |= ( i - paintStart >= 256 ); + // we flush when the selection state changes + if ( drawSelections ) { + for ( QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->begin(); + it != mSelections->end(); ++it ) + selectionStateChanged |=( (*it).start == i || (*it).start == i+1 || (*it).end == i+1 ); + flush |= selectionStateChanged; + } } -#endif - cw = string()->width( i ); - if ( chr->c == '\t' && i < length() - 1 ) - cw = at( i + 1 )->x - chr->x + 1; - if ( chr->c.unicode() == 0xad && i < length() - 1 ) - cw = 0; // init a new line if ( chr->lineStart ) { -#if 0 // seems we don't need that anymore - tw = 0; -#endif + if (fullWidthStyle && drawSelections && selection >= 0) + painter.fillRect( xend, y, fullSelectionWidth - xend, h, + (selection == QTextDocument::Standard || !hasdoc) ? + cg.color( QColorGroup::Highlight ) : + document()->selectionColor( selection ) ); ++line; - lineInfo( line, cy, h, baseLine ); - lasth = h; - if ( clipy != -1 && cy > clipy - r.y() + cliph ) // outside clip area, leave + paintStart = i; + lineInfo( line, y, h, baseLine ); + if ( clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph ) { // outside clip area, leave break; - if ( lastBaseLine == 0 ) - lastBaseLine = baseLine; } - // draw bullet list items - if ( !didListLabel && line == 0 && style() && style()->displayMode() == QStyleSheetItem::DisplayListItem ) { - didListLabel = TRUE; - drawLabel( &painter, chr->x, cy, 0, 0, baseLine, cg ); + // if this is the first line and we are a list item, draw the the bullet label + if ( line == 0 && isListItem() ) + drawLabel( &painter, chr->x, y, 0, 0, baseLine, cg ); } // check for cursor mark - if ( cursor && this == cursor->parag() && i == cursor->index() ) { - curx = cursor->x(); - QTextStringChar *c = chr; - if ( i > 0 ) - --c; - curh = c->format()->height(); - cury = cy + baseLine - c->format()->ascent(); - } - - // first time - start again... - if ( !formatChar || lastY == -1 ) { - formatChar = chr; - lastY = cy; - startX = chr->x; - if ( !chr->isCustom() && chr->c != '\n' ) - paintEnd = i; - bw = cw; - if ( !chr->isCustom() ) - continue; + if ( cursor && this == cursor->paragraph() && i == cursor->index() ) { + QTextStringChar *c = i == 0 ? chr : chr - 1; + cursorRect.setRect( cursor->x() , y + baseLine - c->format()->ascent(), + 1, c->format()->height() ); } - // check if selection state changed - bool selectionChange = FALSE; + // check if we are in a selection and store which one it is + selection = -1; if ( drawSelections ) { - for ( int j = 0; j < nSels; ++j ) { - selectionChange = selectionStarts[ j ] == i || selectionEnds[ j ] == i; - if ( selectionChange ) + for ( QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->begin(); + it != mSelections->end(); ++it ) + if ( (*it).start <= i && i < (*it).end + ( (*it).end == length()-1 && n && n->hasSelection(it.key()) ) ? 1:0 + // exclude the standard selection from printing + && (it.key() != QTextDocument::Standard || !is_printer( &painter) ) ) { + selection = it.key(); break; } } - //if something (format, etc.) changed, draw what we have so far - if ( ( ( ( alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify && at(paintEnd)->c.isSpace() ) || - lastDirection != (bool)chr->rightToLeft || - chr->startOfRun || - lastY != cy || chr->format() != formatChar->format() || chr->isAnchor() != formatChar->isAnchor() || - ( paintEnd != -1 && at( paintEnd )->c =='\t' ) || chr->c == '\t' || - ( paintEnd != -1 && at( paintEnd )->c.unicode() == 0xad ) || chr->c.unicode() == 0xad || - selectionChange || chr->isCustom() ) ) { - if ( paintStart <= paintEnd ) { - // ### temporary hack until I get the new placement/shaping stuff working - int x = startX; - if ( ( alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify && paintEnd != -1 && - paintEnd > 1 && at( paintEnd )->c.isSpace() ) { - int add = str->at(paintEnd).x - str->at(paintEnd-1).x - str->width(paintEnd-1); - bw += ( lastDirection ? 0 : add ); - } - drawParagString( painter, qstr, paintStart, paintEnd - paintStart + 1, x, lastY, - lastBaseLine, bw, lasth, drawSelections, - formatChar, i, selectionStarts, selectionEnds, cg, lastDirection ); - } -#if 0 // seems we don't need that anymore - if ( !str->isBidi() && is_printer( &painter ) ) { // ### fix our broken ps-printer - if ( !chr->lineStart ) { - // ### the next line doesn't look 100% correct for arabic - tw = startX + painter.fontMetrics().width( qstr.mid(paintStart, paintEnd - paintStart +1) ); - chr->x = QMAX( chr->x, tw ); + if ( flush ) { // something changed, draw what we have so far + if ( chr->rightToLeft ) { + xstart = chr->x; + xend = at( paintStart )->x + str->width( paintStart ); } else { - tw = 0; - } + xstart = at( paintStart )->x; + if ( !selectionStateChanged && i < length() - 1 && !str->at( i + 1 ).lineStart ) + xend = str->at( i + 1 ).x; + else + xend = chr->x + str->width( i ); } -#endif - if ( !chr->isCustom() ) { - if ( chr->c != '\n' ) { - paintStart = i; - paintEnd = i; - } else { - paintStart = i+1; - paintEnd = -1; + + if ( (clipx == -1 || clipw == -1) || (xend >= clipx && xstart <= clipx + clipw) ) { + if ( !chr->isCustom() ) + drawString( painter, qstr, paintStart, i - paintStart + 1, xstart, y, + baseLine, xend-xstart, h, selection, + chr, cg, chr->rightToLeft ); + else if ( chr->customItem()->placement() == QTextCustomItem::PlaceInline ) + chr->customItem()->draw( &painter, chr->x, y, + clipx == -1 ? clipx : (clipx - r.x()), + clipy == -1 ? clipy : (clipy - r.y()), + clipw, cliph, cg, selection >= 0 ); } - formatChar = chr; - lastY = cy; - startX = chr->x; - bw = cw; - } else { - if ( chr->customItem()->placement() == QTextCustomItem::PlaceInline ) { - chr->customItem()->draw( &painter, chr->x, cy, clipx - r.x(), clipy - r.y(), clipw, cliph, cg, - nSels && selectionStarts[ 0 ] <= i && selectionEnds[ 0 ] >= i ); - paintStart = i+1; - paintEnd = -1; - formatChar = chr; - lastY = cy; - startX = chr->x + string()->width( i ); - bw = 0; - } else { - chr->customItem()->resize( chr->customItem()->width ); paintStart = i+1; - paintEnd = -1; - formatChar = chr; - lastY = cy; - startX = chr->x + string()->width( i ); - bw = 0; - } - } - } else { - if ( chr->c != '\n' ) { - if( chr->rightToLeft ) { - startX = chr->x; } - paintEnd = i; - } - bw += cw; - } - lastBaseLine = baseLine; - lasth = h; - lastDirection = chr->rightToLeft; + } - // if we are through the parag, but still have some stuff left to draw, draw it now - if ( paintStart <= paintEnd ) { - bool selectionChange = FALSE; - if ( drawSelections ) { - for ( int j = 0; j < nSels; ++j ) { - selectionChange = selectionStarts[ j ] == i || selectionEnds[ j ] == i; - if ( selectionChange ) + if (fullWidthStyle && drawSelections && selection >= 0 && next() && next()->mSelections) + for ( QMap<int, QTextParagraphSelection>::ConstIterator it = next()->mSelections->begin(); + it != next()->mSelections->end(); ++it ) + if (((*it).start) == 0) { + painter.fillRect( xend, y, fullSelectionWidth - xend, h, + (selection == QTextDocument::Standard || !hasdoc) ? + cg.color( QColorGroup::Highlight ) : + document()->selectionColor( selection ) ); break; } - } - int x = startX; - drawParagString( painter, qstr, paintStart, paintEnd-paintStart+1, x, lastY, - lastBaseLine, bw, h, drawSelections, - formatChar, i, selectionStarts, selectionEnds, cg, lastDirection ); - } - // if we should draw a cursor, draw it now - if ( curx != -1 && cursor ) { - painter.fillRect( QRect( curx, cury, 1, curh - lineSpacing() ), cg.color( QColorGroup::Text ) ); + // time to draw the cursor + const int cursor_extent = 4; + if ( !cursorRect.isNull() && cursor && + ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw)) ) { + painter.fillRect( cursorRect, cg.color( QColorGroup::Text ) ); painter.save(); if ( string()->isBidi() ) { - const int d = 4; if ( at( cursor->index() )->rightToLeft ) { painter.setPen( Qt::black ); - painter.drawLine( curx, cury, curx - d / 2, cury + d / 2 ); - painter.drawLine( curx, cury + d, curx - d / 2, cury + d / 2 ); + painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); + painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); } else { painter.setPen( Qt::black ); - painter.drawLine( curx, cury, curx + d / 2, cury + d / 2 ); - painter.drawLine( curx, cury + d, curx + d / 2, cury + d / 2 ); + painter.drawLine( cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); + painter.drawLine( cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2 ); } } painter.restore(); } } //#define BIDI_DEBUG -void QTextParag::drawParagString( QPainter &painter, const QString &s, int start, int len, int startX, - int lastY, int baseLine, int bw, int h, bool drawSelections, - QTextStringChar *formatChar, int i, const QMemArray<int> &selectionStarts, - const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft ) +void QTextParagraph::drawString( QPainter &painter, const QString &s, int start, int len, int xstart, + int y, int baseLine, int w, int h, int selection, + QTextStringChar *formatChar, const QColorGroup& cg, + bool rightToLeft ) { + int i = start + len - 1; bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : FALSE; QTextFormat* format = formatChar->format(); QString str( s ); if ( str[ (int)str.length() - 1 ].unicode() == 0xad ) str.remove( str.length() - 1, 1 ); if ( !plainText || hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color() ) painter.setPen( QPen( format->color() ) ); else painter.setPen( cg.text() ); painter.setFont( format->font() ); if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() ) { if ( format->useLinkColor() ) { if ( document()->linkColor.isValid() ) painter.setPen( document()->linkColor ); else painter.setPen( QPen( Qt::blue ) ); } if ( document()->underlineLinks() ) { QFont fn = format->font(); fn.setUnderline( TRUE ); painter.setFont( fn ); } } - if ( drawSelections ) { - const int nSels = hasdoc ? document()->numSelections() : 1; - const int startSel = is_printer( 0 ) ? 1 : 0; - for ( int j = startSel; j < nSels; ++j ) { - if ( i > selectionStarts[ j ] && i <= selectionEnds[ j ] ) { - if ( !hasdoc || document()->invertSelectionText( j ) ) - painter.setPen( QPen( cg.color( QColorGroup::HighlightedText ) ) ); - if ( j == QTextDocument::Standard ) - painter.fillRect( startX, lastY, bw, h, cg.color( QColorGroup::Highlight ) ); - else - painter.fillRect( startX, lastY, bw, h, hasdoc ? document()->selectionColor( j ) : cg.color( QColorGroup::Highlight ) ); - } - } + if ( selection >= 0 ) { + if ( !hasdoc || document()->invertSelectionText( selection ) ) + painter.setPen( cg.color( QColorGroup::HighlightedText ) ); + painter.fillRect( xstart, y, w, h, + (selection == QTextDocument::Standard || !hasdoc) ? + cg.color( QColorGroup::Highlight ) : document()->selectionColor( selection ) ); } if ( str[ start ] != '\t' && str[ start ].unicode() != 0xad ) { if ( format->vAlign() == QTextFormat::AlignNormal ) { - painter.drawText( startX, lastY + baseLine, str.mid( start ), len ); + painter.drawText( xstart, y + baseLine, str.mid( start ), len ); #ifdef BIDI_DEBUG painter.save(); painter.setPen ( Qt::red ); - painter.drawLine( startX, lastY, startX, lastY + baseLine ); - painter.drawLine( startX, lastY + baseLine/2, startX + 10, lastY + baseLine/2 ); + painter.drawLine( xstart, y, xstart, y + baseLine ); + painter.drawLine( xstart, y + baseLine/2, xstart + 10, y + baseLine/2 ); int w = 0; int i = 0; while( i < len ) w += painter.fontMetrics().charWidth( str, start + i++ ); painter.setPen ( Qt::blue ); - painter.drawLine( startX + w - 1, lastY, startX + w - 1, lastY + baseLine ); - painter.drawLine( startX + w - 1, lastY + baseLine/2, startX + w - 1 - 10, lastY + baseLine/2 ); + painter.drawLine( xstart + w - 1, y, xstart + w - 1, y + baseLine ); + painter.drawLine( xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2 ); painter.restore(); #endif } else if ( format->vAlign() == QTextFormat::AlignSuperScript ) { QFont f( painter.font() ); if ( format->fontSizesInPixels() ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); painter.setFont( f ); - painter.drawText( startX, lastY + baseLine - ( painter.fontMetrics().height() / 2 ), + painter.drawText( xstart, y + baseLine - ( painter.fontMetrics().height() / 2 ), str.mid( start ), len ); } else if ( format->vAlign() == QTextFormat::AlignSubScript ) { QFont f( painter.font() ); if ( format->fontSizesInPixels() ) f.setPixelSize( ( f.pixelSize() * 2 ) / 3 ); else f.setPointSize( ( f.pointSize() * 2 ) / 3 ); painter.setFont( f ); - painter.drawText( startX, lastY + baseLine + painter.fontMetrics().height() / 6, str.mid( start ), len ); + painter.drawText( xstart, y + baseLine + painter.fontMetrics().height() / 6, str.mid( start ), len ); } } if ( i + 1 < length() && at( i + 1 )->lineStart && at( i )->c.unicode() == 0xad ) { - painter.drawText( startX + bw, lastY + baseLine, "\xad" ); + painter.drawText( xstart + w, y + baseLine, "\xad" ); } if ( format->isMisspelled() ) { painter.save(); painter.setPen( QPen( Qt::red, 1, Qt::DotLine ) ); - painter.drawLine( startX, lastY + baseLine + 1, startX + bw, lastY + baseLine + 1 ); + painter.drawLine( xstart, y + baseLine + 1, xstart + w, y + baseLine + 1 ); painter.restore(); } i -= len; if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() && document()->focusIndicator.parag == this && ( document()->focusIndicator.start >= i && document()->focusIndicator.start + document()->focusIndicator.len <= i + len || document()->focusIndicator.start <= i && document()->focusIndicator.start + document()->focusIndicator.len >= i + len ) ) { - painter.drawWinFocusRect( QRect( startX, lastY, bw, h ) ); + painter.drawWinFocusRect( QRect( xstart, y, w, h ) ); } } -void QTextParag::drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg ) +void QTextParagraph::drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg ) { - if ( !style() ) - return; QRect r ( x, y, w, h ); QStyleSheetItem::ListStyle s = listStyle(); p->save(); - p->setPen( defFormat->color() ); - - QFont font2( defFormat->font() ); - if ( length() > 0 ) { QTextFormat *format = at( 0 )->format(); if ( format ) { - if ( format->fontSizesInPixels() ) - font2.setPixelSize( at( 0 )->format()->font().pixelSize() ); - else - font2.setPointSize( at( 0 )->format()->font().pointSize() ); - } + p->setPen( format->color() ); + p->setFont( format->font() ); } - p->setFont( font2 ); QFontMetrics fm( p->fontMetrics() ); int size = fm.lineSpacing() / 3; switch ( s ) { case QStyleSheetItem::ListDecimal: case QStyleSheetItem::ListLowerAlpha: case QStyleSheetItem::ListUpperAlpha: { - int n = numberOfSubParagraph(); + if ( list_val == -1 ) { // uninitialised list value, calcluate the right one + int depth = listDepth(); + list_val--; + // ### evil, square and expensive. This needs to be done when formatting, not when painting + QTextParagraph* s = prev(); + int depth_s; + while ( s && (depth_s = s->listDepth()) >= depth ) { + if ( depth_s == depth && s->isListItem() ) + list_val--; + s = s->prev(); + } + } + + int n = list_val; + if ( n < -1 ) + n = -n - 1; QString l; switch ( s ) { case QStyleSheetItem::ListLowerAlpha: if ( n < 27 ) { l = QChar( ('a' + (char) (n-1))); break; } case QStyleSheetItem::ListUpperAlpha: if ( n < 27 ) { l = QChar( ('A' + (char) (n-1))); break; } break; default: //QStyleSheetItem::ListDecimal: l.setNum( n ); break; } l += QString::fromLatin1(". "); p->drawText( r.right() - fm.width( l ), r.top() + base, l ); } break; case QStyleSheetItem::ListSquare: { QRect er( r.right() - size * 2, r.top() + fm.height() / 2 - size / 2, size, size ); - p->fillRect( er , cg.brush( QColorGroup::Foreground ) ); + p->fillRect( er , cg.brush( QColorGroup::Text ) ); } break; case QStyleSheetItem::ListCircle: { QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse( er ); } break; case QStyleSheetItem::ListDisc: default: { - p->setBrush( cg.brush( QColorGroup::Foreground )); + p->setBrush( cg.brush( QColorGroup::Text )); QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size); p->drawEllipse( er ); p->setBrush( Qt::NoBrush ); } break; } p->restore(); } -void QTextParag::setStyleSheetItems( const QPtrVector<QStyleSheetItem> &vec ) -{ - styleSheetItemsVec() = vec; - invalidate( 0 ); - lm = rm = tm = bm = flm = -1; - numSubParag = -1; -} - -void QTextParag::setList( bool b, int listStyle ) +void QTextParagraph::readStyleInformation( QDataStream& stream ) { - if ( !hasdoc ) - return; - - if ( !style() ) { - styleSheetItemsVec().resize( 2 ); - mStyleSheetItemsVec->insert( 0, document()->styleSheet()->item( "html" ) ); - mStyleSheetItemsVec->insert( 1, document()->styleSheet()->item( "p" ) ); - } - - if ( b ) { - if ( style()->displayMode() != QStyleSheetItem::DisplayListItem || this->listStyle() != listStyle ) { - styleSheetItemsVec().remove( styleSheetItemsVec().size() - 1 ); - QStyleSheetItem *item = (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 1 ]; - if ( item ) - mStyleSheetItemsVec->remove( mStyleSheetItemsVec->size() - 1 ); - mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1, - listStyle == QStyleSheetItem::ListDisc || listStyle == QStyleSheetItem::ListCircle - || listStyle == QStyleSheetItem::ListSquare ? - document()->styleSheet()->item( "ul" ) : document()->styleSheet()->item( "ol" ) ); - mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1, document()->styleSheet()->item( "li" ) ); - setListStyle( (QStyleSheetItem::ListStyle)listStyle ); - } else { - return; - } - } else { - if ( style()->displayMode() != QStyleSheetItem::DisplayBlock ) { - styleSheetItemsVec().remove( styleSheetItemsVec().size() - 1 ); - if ( mStyleSheetItemsVec->size() >= 2 ) { - mStyleSheetItemsVec->remove( mStyleSheetItemsVec->size() - 2 ); - mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 2 ); - } else { - mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 1 ); - } - } else { - return; - } - } - invalidate( 0 ); - lm = rm = tm = bm = flm = -1; - numSubParag = -1; - if ( next() ) { - QTextParag *s = next(); + int int_align, int_lstyle; + uchar uchar_litem, uchar_rtext, uchar_dir; + stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm + >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir; + align = int_align; lstyle = (QStyleSheetItem::ListStyle) int_lstyle; + litem = uchar_litem; rtext = uchar_rtext; str->setDirection( (QChar::Direction)uchar_dir ); + QTextParagraph* s = prev() ? prev() : this; while ( s ) { - s->numSubParag = -1; - s->lm = s->rm = s->tm = s->bm = flm = -1; - s->numSubParag = -1; s->invalidate( 0 ); s = s->next(); } } -} -void QTextParag::incDepth() +void QTextParagraph::writeStyleInformation( QDataStream& stream ) const { - if ( !style() || !hasdoc ) - return; - if ( style()->displayMode() != QStyleSheetItem::DisplayListItem ) - return; - styleSheetItemsVec().resize( styleSheetItemsVec().size() + 1 ); - mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1, (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 2 ] ); - mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 2, - listStyle() == QStyleSheetItem::ListDisc || listStyle() == QStyleSheetItem::ListCircle || - listStyle() == QStyleSheetItem::ListSquare ? - document()->styleSheet()->item( "ul" ) : document()->styleSheet()->item( "ol" ) ); - invalidate( 0 ); - lm = -1; - flm = -1; + stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction(); } -void QTextParag::decDepth() -{ - if ( !style() || !hasdoc ) - return; - if ( style()->displayMode() != QStyleSheetItem::DisplayListItem ) - return; - int numLists = 0; - QStyleSheetItem *lastList = 0; - int lastIndex = 0; - int i; - if ( mStyleSheetItemsVec ) { - for ( i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - QStyleSheetItem *item = (*mStyleSheetItemsVec)[ i ]; - if ( item->name() == "ol" || item->name() == "ul" ) { - lastList = item; - lastIndex = i; - numLists++; - } - } - } - if ( !lastList ) - return; - styleSheetItemsVec().remove( lastIndex ); - for ( i = lastIndex; i < (int)mStyleSheetItemsVec->size() - 1; ++i ) - mStyleSheetItemsVec->insert( i, (*mStyleSheetItemsVec)[ i + 1 ] ); - mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 1 ); - if ( numLists == 1 ) - setList( FALSE, -1 ); - invalidate( 0 ); - lm = -1; - flm = -1; -} -int QTextParag::listDepth() const -{ - int numLists = 0; - int i; - if ( mStyleSheetItemsVec ) { - for ( i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - QStyleSheetItem *item = (*mStyleSheetItemsVec)[ i ]; - if ( item->name() == "ol" || item->name() == "ul" ) - numLists++; - } +void QTextParagraph::setListDepth( int depth ) { + if ( !hasdoc || depth == ldepth ) + return; + ldepth = depth; + QTextParagraph* s = prev() ? prev() : this; + while ( s ) { + s->invalidate( 0 ); + s = s->next(); } - return numLists - 1; } -int *QTextParag::tabArray() const +int *QTextParagraph::tabArray() const { int *ta = tArray; if ( !ta && hasdoc ) ta = document()->tabArray(); return ta; } -int QTextParag::nextTab( int, int x ) +int QTextParagraph::nextTab( int, int x ) { int *ta = tArray; if ( hasdoc ) { if ( !ta ) ta = document()->tabArray(); tabStopWidth = document()->tabStopWidth(); } if ( ta ) { int i = 0; while ( ta[ i ] ) { if ( ta[ i ] >= x ) return tArray[ i ]; ++i; } return tArray[ 0 ]; } else { int d; if ( tabStopWidth != 0 ) d = x / tabStopWidth; else return x; return tabStopWidth * ( d + 1 ); } } -void QTextParag::adjustToPainter( QPainter *p ) +void QTextParagraph::adjustToPainter( QPainter *p ) { for ( int i = 0; i < length(); ++i ) { if ( at( i )->isCustom() ) at( i )->customItem()->adjustToPainter( p ); } } -QTextFormatCollection *QTextParag::formatCollection() const +QTextFormatCollection *QTextParagraph::formatCollection() const { if ( hasdoc ) return document()->formatCollection(); if ( !qFormatCollection ) { qFormatCollection = new QTextFormatCollection; static QSingleCleanupHandler<QTextFormatCollection> qtfCleanup; qtfCleanup.set( &qFormatCollection ); } return qFormatCollection; } -QString QTextParag::richText() const +QString QTextParagraph::richText() const { QString s; QTextStringChar *formatChar = 0; QString spaces; - bool lastCharWasSpace = FALSE; - int firstcol = 0; - for ( int i = 0; i < length()-1; ++i ) { + bool doStart = richTextExportStart && richTextExportStart->paragraph() == this; + bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this; + int i; + for ( i = 0; i < length()-1; ++i ) { + if ( doStart && i && richTextExportStart->index() == i ) + s += "<selstart/>"; + if ( doEnd && richTextExportEnd->index() == i ) + s += "<selend/>"; QTextStringChar *c = &str->at( i ); if ( c->isAnchor() && !c->anchorName().isEmpty() ) { if ( c->anchorName().contains( '#' ) ) { QStringList l = QStringList::split( '#', c->anchorName() ); for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) s += "<a name=\"" + *it + "\"></a>"; } else { s += "<a name=\"" + c->anchorName() + "\"></a>"; } } if ( !formatChar ) { - s += c->format()->makeFormatChangeTags( 0, QString::null, c->anchorHref() ); + s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(), + 0, QString::null, c->anchorHref() ); formatChar = c; } else if ( ( formatChar->format()->key() != c->format()->key() ) || - (formatChar->isAnchor() != c->isAnchor() && - (!c->anchorHref().isEmpty() || !formatChar->anchorHref().isEmpty() ) ) ) {// lisp was here - - if ( !spaces.isEmpty() ) { - if ( spaces[0] == '\t' || lastCharWasSpace ) - s += "<wsp>" + spaces + "</wsp>"; - else if ( spaces.length() > 1 ) - s += "<wsp>" + spaces.mid(1) + "</wsp> "; - else - s += spaces; - lastCharWasSpace = TRUE; - spaces = QString::null; - } - s += c->format()->makeFormatChangeTags( formatChar->format() , formatChar->anchorHref(), c->anchorHref() ); + (c->anchorHref() != formatChar->anchorHref() ) ) { + s += c->format()->makeFormatChangeTags( formatCollection()->defaultFormat(), + formatChar->format() , formatChar->anchorHref(), c->anchorHref() ); formatChar = c; } - - if ( c->c == ' ' || c->c == '\t' ) { - spaces += c->c; - continue; - } else if ( !spaces.isEmpty() ) { - if ( spaces[0] == '\t' || lastCharWasSpace ) - s += "<wsp>" + spaces + "</wsp>"; - else if ( spaces.length() > 1 ) - s += "<wsp>" + spaces.mid(1) + "</wsp> "; - else - s += spaces; - spaces = QString::null; - if ( s.length() - firstcol > 60 ) { - s += '\n'; - firstcol = s.length(); - } - } - - lastCharWasSpace = FALSE; - if ( c->c == '<' ) { + if ( c->c == '<' ) s += "<"; - } else if ( c->c == '>' ) { + else if ( c->c == '>' ) s += ">"; - } else if ( c->isCustom() ) { + else if ( c->isCustom() ) s += c->customItem()->richText(); - } else { - s += c->c; - } - } - if ( !spaces.isEmpty() ) { - if ( spaces.length() > 1 || spaces[0] == '\t' || lastCharWasSpace ) - s += "<wsp>" + spaces + "</wsp>"; + else if ( c->c == '\n' || c->c == QChar_linesep ) + s += "<br />"; // space on purpose for compatibility with Netscape, Lynx & Co. else - s += spaces; + s += c->c; } - + if ( doEnd && richTextExportEnd->index() == i ) + s += "<selend/>"; if ( formatChar ) - s += formatChar->format()->makeFormatEndTags( formatChar->anchorHref() ); + s += formatChar->format()->makeFormatEndTags( formatCollection()->defaultFormat(), formatChar->anchorHref() ); return s; } -void QTextParag::addCommand( QTextCommand *cmd ) +void QTextParagraph::addCommand( QTextCommand *cmd ) { if ( !hasdoc ) pseudoDocument()->commandHistory->addCommand( cmd ); else document()->commands()->addCommand( cmd ); } -QTextCursor *QTextParag::undo( QTextCursor *c ) +QTextCursor *QTextParagraph::undo( QTextCursor *c ) { if ( !hasdoc ) return pseudoDocument()->commandHistory->undo( c ); return document()->commands()->undo( c ); } -QTextCursor *QTextParag::redo( QTextCursor *c ) +QTextCursor *QTextParagraph::redo( QTextCursor *c ) { if ( !hasdoc ) return pseudoDocument()->commandHistory->redo( c ); return document()->commands()->redo( c ); } -int QTextParag::topMargin() const +int QTextParagraph::topMargin() const { - if ( !p && ( !hasdoc || !document()->addMargins() ) ) - return 0; - if ( tm != -1 ) - return tm; - QStyleSheetItem *item = style(); - if ( !item ) { - ( (QTextParag*)this )->tm = 0; - return 0; - } - int m = 0; - if ( item->margin( QStyleSheetItem::MarginTop ) != QStyleSheetItem::Undefined ) - m = item->margin( QStyleSheetItem::MarginTop ); - if ( mStyleSheetItemsVec ) { - QStyleSheetItem *it = 0; - QStyleSheetItem *p = prev() ? prev()->style() : 0; - for ( int i = (int)mStyleSheetItemsVec->size() - 2 ; i >= 0; --i ) { - it = (*mStyleSheetItemsVec)[ i ]; - if ( it != p ) - break; - int mar = it->margin( QStyleSheetItem::MarginTop ); - m += (mar != QStyleSheetItem::Undefined) ? mar : 0; - if ( it->displayMode() != QStyleSheetItem::DisplayInline ) - break; - } + if ( rtext ) { + m = isListItem() ? (document()->li_tm/QMAX(1,listDepth())) : document()->par_tm; + if ( listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth() ) ) + m = QMAX( m, document()->list_tm ); } - m = scale( m, QTextFormat::painter() ); - - ( (QTextParag*)this )->tm = m; - return tm; + m += utm; + return scale( m, QTextFormat::painter() ); } -int QTextParag::bottomMargin() const +int QTextParagraph::bottomMargin() const { - if ( bm != -1 ) - return bm; - QStyleSheetItem *item = style(); - if ( !item || !next() ) { - ( (QTextParag*)this )->bm = 0; - return 0; - } - int m = 0; - if ( item->margin( QStyleSheetItem::MarginBottom ) != QStyleSheetItem::Undefined ) - m = item->margin( QStyleSheetItem::MarginBottom ); - if ( mStyleSheetItemsVec ) { - QStyleSheetItem *it = 0; - QStyleSheetItem *n = next() ? next()->style() : 0; - for ( int i =(int)mStyleSheetItemsVec->size() - 2 ; i >= 0; --i ) { - it = (*mStyleSheetItemsVec)[ i ]; - if ( it != n ) - break; - int mar = it->margin( QStyleSheetItem::MarginBottom ); - m += mar != QStyleSheetItem::Undefined ? mar : 0; - if ( it->displayMode() != QStyleSheetItem::DisplayInline ) - break; + if ( rtext ) { + m = isListItem() ? (document()->li_bm/QMAX(1,listDepth())) : document()->par_bm; + if ( listDepth() == 1 &&( !next() || next()->listDepth() < listDepth() ) ) + m = QMAX( m, document()->list_bm ); } - } - m = scale ( m, QTextFormat::painter() ); - - ( (QTextParag*)this )->bm = m; - return bm; + m += ubm; + return scale( m, QTextFormat::painter() ); } -int QTextParag::leftMargin() const +int QTextParagraph::leftMargin() const { - if ( lm != -1 ) - return lm; - QStyleSheetItem *item = style(); - if ( !item ) { - ( (QTextParag*)this )->lm = 0; - return 0; - } - int m = 0; - if ( mStyleSheetItemsVec ) { - for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - item = (*mStyleSheetItemsVec)[ i ]; - int mar = item->margin( QStyleSheetItem::MarginLeft ); - m += mar != QStyleSheetItem::Undefined ? mar : 0; - if ( item->name() == "ol" || item->name() == "ul" ) { - QPainter* oldPainter = QTextFormat::painter(); - QTextFormat::setPainter( 0 ); - m += defFormat->width( '1' ) + - defFormat->width( '2' ) + - defFormat->width( '3' ) + - defFormat->width( '.' ); - QTextFormat::setPainter( oldPainter ); - } - } + int m = ulm; + if ( listDepth() ) + m += listDepth() * document()->list_lm; + return scale( m, QTextFormat::painter() ); } - m = scale ( m, QTextFormat::painter() ); - - ( (QTextParag*)this )->lm = m; - return lm; -} - -int QTextParag::firstLineMargin() const +int QTextParagraph::firstLineMargin() const { - if ( flm != -1 ) - return lm; - QStyleSheetItem *item = style(); - if ( !item ) { - ( (QTextParag*)this )->flm = 0; - return 0; + int m = uflm; + return scale( m, QTextFormat::painter() ); } - int m = 0; - if ( mStyleSheetItemsVec ) { - for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - item = (*mStyleSheetItemsVec)[ i ]; - int mar = item->margin( QStyleSheetItem::MarginFirstLine ); - m += mar != QStyleSheetItem::Undefined ? mar : 0; - } - } - - m = scale( m, QTextFormat::painter() ); - ( (QTextParag*)this )->flm = m; - return flm; -} - -int QTextParag::rightMargin() const +int QTextParagraph::rightMargin() const { - if ( rm != -1 ) - return rm; - QStyleSheetItem *item = style(); - if ( !item ) { - ( (QTextParag*)this )->rm = 0; - return 0; - } - int m = 0; - if ( mStyleSheetItemsVec ) { - for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - item = (*mStyleSheetItemsVec)[ i ]; - int mar = item->margin( QStyleSheetItem::MarginRight ); - m += mar != QStyleSheetItem::Undefined ? mar : 0; - } - } - m = scale( m, QTextFormat::painter() ); - - ( (QTextParag*)this )->rm = m; - return rm; + int m = urm; + return scale( m, QTextFormat::painter() ); } -int QTextParag::lineSpacing() const +int QTextParagraph::lineSpacing() const { - QStyleSheetItem *item = style(); - if ( !item ) - return 0; - - int ls = item->lineSpacing(); - if ( ls == QStyleSheetItem::Undefined ) - return 0; - ls = scale( ls, QTextFormat::painter() ); - - return ls; + int l = ulinespacing; + l = scale( l, QTextFormat::painter() ); + return l; } -void QTextParag::copyParagData( QTextParag *parag ) +void QTextParagraph::copyParagData( QTextParagraph *parag ) { - setStyleSheetItems( parag->styleSheetItems() ); - setListStyle( parag->listStyle() ); - setAlignment( parag->alignment() ); + rtext = parag->rtext; + lstyle = parag->lstyle; + ldepth = parag->ldepth; + litem = parag->litem; + align = parag->align; + utm = parag->utm; + ubm = parag->ubm; + urm = parag->urm; + ulm = parag->ulm; + uflm = parag->uflm; + ulinespacing = parag->ulinespacing; QColor *c = parag->backgroundColor(); if ( c ) setBackgroundColor( *c ); + str->setDirection( parag->str->direction() ); } -void QTextParag::show() +void QTextParagraph::show() { if ( visible || !hasdoc ) return; visible = TRUE; } -void QTextParag::hide() +void QTextParagraph::hide() { if ( !visible || !hasdoc ) return; visible = FALSE; } -void QTextParag::setDirection( QChar::Direction d ) +void QTextParagraph::setDirection( QChar::Direction d ) { if ( str && str->direction() != d ) { str->setDirection( d ); invalidate( 0 ); } } -QChar::Direction QTextParag::direction() const +QChar::Direction QTextParagraph::direction() const { return (str ? str->direction() : QChar::DirON ); } -void QTextParag::setChanged( bool b, bool recursive ) +void QTextParagraph::setChanged( bool b, bool recursive ) { changed = b; if ( recursive ) { - if ( document() && document()->parentParag() ) - document()->parentParag()->setChanged( b, recursive ); + if ( document() && document()->parentParagraph() ) + document()->parentParagraph()->setChanged( b, recursive ); } } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextPreProcessor::QTextPreProcessor() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextFormatter::QTextFormatter() : thisminw(0), thiswused(0), wrapEnabled( TRUE ), wrapColumn( -1 ), biw( FALSE ) { } -/* only used for bidi or complex text reordering - */ -QTextParagLineStart *QTextFormatter::formatLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line, +QTextLineStart *QTextFormatter::formatLine( QTextParagraph *parag, QTextString *string, QTextLineStart *line, QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space ) { #ifndef QT_NO_COMPLEXTEXT if( string->isBidi() ) return bidiReorderLine( parag, string, line, startChar, lastChar, align, space ); #endif - space = QMAX( space, 0 ); // #### with nested tables this gets negative because of a bug I didn't find yet, so workaround for now. This also means non-left aligned nested tables do not work at the moment int start = (startChar - &string->at(0)); int last = (lastChar - &string->at(0) ); // do alignment Auto == Left in this case if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) { if ( align & Qt::AlignHCenter ) space /= 2; for ( int j = start; j <= last; ++j ) string->at( j ).x += space; } else if ( align & Qt3::AlignJustify ) { int numSpaces = 0; - for ( int j = start; j < last; ++j ) { + // End at "last-1", the last space ends up with a width of 0 + for ( int j = last-1; j >= start; --j ) { + // Start at last tab, if any. + if ( string->at( j ).c == '\t' ) { + start = j+1; + break; + } if( isBreakable( string, j ) ) { numSpaces++; } } int toAdd = 0; for ( int k = start + 1; k <= last; ++k ) { if( isBreakable( string, k ) && numSpaces ) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } string->at( k ).x += toAdd; } } if ( last >= 0 && last < string->length() ) line->w = string->at( last ).x + string->width( last ); else line->w = 0; - return new QTextParagLineStart(); + return new QTextLineStart(); } #ifndef QT_NO_COMPLEXTEXT #ifdef BIDI_DEBUG #include <iostream> #endif // collects one line of the paragraph and transforms it to visual order -QTextParagLineStart *QTextFormatter::bidiReorderLine( QTextParag * /*parag*/, QTextString *text, QTextParagLineStart *line, +QTextLineStart *QTextFormatter::bidiReorderLine( QTextParagraph * /*parag*/, QTextString *text, QTextLineStart *line, QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space ) { int start = (startChar - &text->at(0)); int last = (lastChar - &text->at(0) ); - //qDebug("doing BiDi reordering from %d to %d!", start, last); QBidiControl *control = new QBidiControl( line->context(), line->status ); QString str; str.setUnicode( 0, last - start + 1 ); // fill string with logically ordered chars. QTextStringChar *ch = startChar; QChar *qch = (QChar *)str.unicode(); while ( ch <= lastChar ) { *qch = ch->c; qch++; ch++; } int x = startChar->x; QPtrList<QTextRun> *runs; runs = QComplexText::bidiReorderLine(control, str, 0, last - start + 1, (text->isRightToLeft() ? QChar::DirR : QChar::DirL) ); // now construct the reordered string out of the runs... int numSpaces = 0; // set the correct alignment. This is a bit messy.... if( align == Qt3::AlignAuto ) { // align according to directionality of the paragraph... if ( text->isRightToLeft() ) align = Qt::AlignRight; } if ( align & Qt::AlignHCenter ) x += space/2; else if ( align & Qt::AlignRight ) x += space; else if ( align & Qt3::AlignJustify ) { - for ( int j = start; j < last; ++j ) { + // End at "last-1", the last space ends up with a width of 0 + for ( int j = last-1; j >= start; --j ) { + // Start at last tab, if any. + if ( text->at( j ).c == '\t' ) { + start = j+1; + break; + } if( isBreakable( text, j ) ) { numSpaces++; } } } int toAdd = 0; bool first = TRUE; QTextRun *r = runs->first(); int xmax = -0xffffff; while ( r ) { if(r->level %2) { // odd level, need to reverse the string int pos = r->stop + start; while(pos >= r->start + start) { QTextStringChar *c = &text->at(pos); if( numSpaces && !first && isBreakable( text, pos ) ) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } else if ( first ) { first = FALSE; if ( c->c == ' ' ) x -= c->format()->width( ' ' ); } c->x = x + toAdd; c->rightToLeft = TRUE; c->startOfRun = FALSE; int ww = 0; if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) { ww = text->width( pos ); } else { ww = c->format()->width( ' ' ); } if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww; x += ww; pos--; } } else { int pos = r->start + start; while(pos <= r->stop + start) { QTextStringChar* c = &text->at(pos); if( numSpaces && !first && isBreakable( text, pos ) ) { int s = space / numSpaces; toAdd += s; space -= s; numSpaces--; } else if ( first ) { first = FALSE; if ( c->c == ' ' ) x -= c->format()->width( ' ' ); } c->x = x + toAdd; c->rightToLeft = FALSE; c->startOfRun = FALSE; int ww = 0; if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) { ww = text->width( pos ); } else { ww = c->format()->width( ' ' ); } - //qDebug("setting char %d at pos %d", pos, x); if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww; x += ww; pos++; } } text->at( r->start + start ).startOfRun = TRUE; r = runs->next(); } line->w = xmax + 10; - QTextParagLineStart *ls = new QTextParagLineStart( control->context, control->status ); + QTextLineStart *ls = new QTextLineStart( control->context, control->status ); delete control; delete runs; return ls; } #endif -bool QTextFormatter::isBreakable( QTextString *string, int pos ) const +bool QTextFormatter::isBreakable( QTextString *string, int pos ) { const QChar &c = string->at( pos ).c; char ch = c.latin1(); + if ( c == QChar_linesep ) + return TRUE; if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U ) return TRUE; if ( c.unicode() == 0xad ) // soft hyphen return TRUE; if ( !ch ) { // not latin1, need to do more sophisticated checks for other scripts uchar row = c.row(); if ( row == 0x0e ) { // 0e00 - 0e7f == Thai if ( c.cell() < 0x80 ) { #ifdef HAVE_THAI_BREAKS // check for thai if( string != cachedString ) { // build up string of thai chars QTextCodec *thaiCodec = QTextCodec::codecForMib(2259); if ( !thaiCache ) thaiCache = new QCString; if ( !thaiIt ) thaiIt = ThBreakIterator::createWordInstance(); *thaiCache = thaiCodec->fromUnicode( s->string() ); } thaiIt->setText(thaiCache->data()); for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) { if( i == pos ) return TRUE; if( i > pos ) return FALSE; } return FALSE; #else // if we don't have a thai line breaking lib, allow // breaks everywhere except directly before punctuation. return TRUE; #endif } else return FALSE; } if ( row < 0x11 ) // no asian font return FALSE; if ( row > 0x2d && row < 0xfb || row == 0x11 ) // asian line breaking. Everywhere allowed except directly // in front of a punctuation character. return TRUE; } return FALSE; } -void QTextFormatter::insertLineStart( QTextParag *parag, int index, QTextParagLineStart *ls ) +void QTextFormatter::insertLineStart( QTextParagraph *parag, int index, QTextLineStart *ls ) { if ( index > 0 ) { // we can assume that only first line starts are insrted multiple times parag->lineStartList().insert( index, ls ); return; } - QMap<int, QTextParagLineStart*>::Iterator it; + QMap<int, QTextLineStart*>::Iterator it; if ( ( it = parag->lineStartList().find( index ) ) == parag->lineStartList().end() ) { parag->lineStartList().insert( index, ls ); } else { delete *it; parag->lineStartList().remove( it ); parag->lineStartList().insert( index, ls ); } } /* Standard pagebreak algorithm using QTextFlow::adjustFlow. Returns the shift of the paragraphs bottom line. */ -int QTextFormatter::formatVertically( QTextDocument* doc, QTextParag* parag ) +int QTextFormatter::formatVertically( QTextDocument* doc, QTextParagraph* parag ) { int oldHeight = parag->rect().height(); - QMap<int, QTextParagLineStart*>& lineStarts = parag->lineStartList(); - QMap<int, QTextParagLineStart*>::Iterator it = lineStarts.begin(); - int h = doc->addMargins() ? parag->topMargin() : 0; + QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList(); + QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin(); + int h = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin() ) / 2: 0; for ( ; it != lineStarts.end() ; ++it ) { - QTextParagLineStart * ls = it.data(); + QTextLineStart * ls = it.data(); ls->y = h; QTextStringChar *c = ¶g->string()->at(it.key()); if ( c && c->customItem() && c->customItem()->ownLine() ) { int h = c->customItem()->height; c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() ); int delta = c->customItem()->height - h; ls->h += delta; if ( delta ) parag->setMovedDown( TRUE ); } else { int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h ); ls->y += shift; if ( shift ) parag->setMovedDown( TRUE ); } h = ls->y + ls->h; } int m = parag->bottomMargin(); - if ( parag->next() && doc && !doc->addMargins() ) - m = QMAX( m, parag->next()->topMargin() ); - if ( parag->next() && parag->next()->isLineBreak() ) + if ( !parag->next() ) m = 0; + else + m = QMAX(m, parag->next()->topMargin() ) / 2; h += m; parag->setHeight( h ); return h - oldHeight; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextFormatterBreakInWords::QTextFormatterBreakInWords() { } -int QTextFormatterBreakInWords::format( QTextDocument *doc,QTextParag *parag, - int start, const QMap<int, QTextParagLineStart*> & ) +#define SPACE(s) doc?(s>0?s:0):s + +int QTextFormatterBreakInWords::format( QTextDocument *doc,QTextParagraph *parag, + int start, const QMap<int, QTextLineStart*> & ) { QTextStringChar *c = 0; QTextStringChar *firstChar = 0; - int left = doc ? parag->leftMargin() + doc->leftMargin() : 4; + int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + ( doc ? parag->firstLineMargin() : 0 ); int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 0 ); - int y = doc && doc->addMargins() ? parag->topMargin() : 0; + int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if ( doc ) x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 4 ); int rm = parag->rightMargin(); int w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); bool fullWidth = TRUE; int minw = 0; int wused = 0; bool wrapEnabled = isWrapEnabled( parag ); start = 0; //######### what is the point with start?! (Matthias) if ( start == 0 ) c = ¶g->string()->at( 0 ); int i = start; - QTextParagLineStart *lineStart = new QTextParagLineStart( y, y, 0 ); + QTextLineStart *lineStart = new QTextLineStart( y, y, 0 ); insertLineStart( parag, 0, lineStart ); QPainter *painter = QTextFormat::painter(); int col = 0; int ww = 0; QChar lastChr; for ( ; i < len; ++i, ++col ) { if ( c ) lastChr = c->c; c = ¶g->string()->at( i ); c->rightToLeft = FALSE; // ### the lines below should not be needed if ( painter ) c->format()->setPainter( painter ); if ( i > 0 ) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } if ( c->c.unicode() >= 32 || c->isCustom() ) { ww = parag->string()->width( i ); } else if ( c->c == '\t' ) { int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } else { ww = c->format()->width( ' ' ); } if ( c->isCustom() && c->customItem()->ownLine() ) { x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); c->customItem()->resize( w - x ); w = dw; y += h; h = c->height(); - lineStart = new QTextParagLineStart( y, h, h ); + lineStart = new QTextLineStart( y, h, h ); insertLineStart( parag, i, lineStart ); c->lineStart = 1; firstChar = c; x = 0xffffff; continue; } if ( wrapEnabled && ( wrapAtColumn() == -1 && x + ww > w || - wrapAtColumn() != -1 && col >= wrapAtColumn() ) || - parag->isNewLinesAllowed() && lastChr == '\n' ) { + wrapAtColumn() != -1 && col >= wrapAtColumn() ) ) { x = doc ? parag->document()->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw; y += h; h = c->height(); - lineStart = formatLine( parag, parag->string(), lineStart, firstChar, c-1 ); + lineStart = formatLine( parag, parag->string(), lineStart, firstChar, SPACE(c-1) ); lineStart->y = y; insertLineStart( parag, i, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; col = 0; if ( wrapAtColumn() != -1 ) minw = QMAX( minw, w ); } else if ( lineStart ) { lineStart->baseLine = QMAX( lineStart->baseLine, c->ascent() ); h = QMAX( h, c->height() ); lineStart->h = h; } c->x = x; x += ww; wused = QMAX( wused, x ); } int m = parag->bottomMargin(); - if ( parag->next() && doc && !doc->addMargins() ) - m = QMAX( m, parag->next()->topMargin() ); - parag->setFullWidth( fullWidth ); - if ( parag->next() && parag->next()->isLineBreak() ) + if ( !parag->next() ) m = 0; + else + m = QMAX(m, parag->next()->topMargin() ) / 2; + parag->setFullWidth( fullWidth ); y += h + m; + if ( doc ) + minw += doc->rightMargin(); if ( !wrapEnabled ) minw = QMAX(minw, wused); thisminw = minw; thiswused = wused; return y; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextFormatterBreakWords::QTextFormatterBreakWords() { } #define DO_FLOW( lineStart ) do{ if ( doc && doc->isPageBreakEnabled() ) { \ int yflow = lineStart->y + parag->rect().y();\ int shift = doc->flow()->adjustFlow( yflow, dw, lineStart->h ); \ lineStart->y += shift;\ y += shift;\ }}while(FALSE) -int QTextFormatterBreakWords::format( QTextDocument *doc, QTextParag *parag, - int start, const QMap<int, QTextParagLineStart*> & ) +int QTextFormatterBreakWords::format( QTextDocument *doc, QTextParagraph *parag, + int start, const QMap<int, QTextLineStart*> & ) { QTextStringChar *c = 0; QTextStringChar *firstChar = 0; QTextString *string = parag->string(); int left = doc ? parag->leftMargin() + doc->leftMargin() : 0; int x = left + ( doc ? parag->firstLineMargin() : 0 ); - int y = doc && doc->addMargins() ? parag->topMargin() : 0; + int y = parag->prev() ? QMAX(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0; int h = y; int len = parag->length(); if ( doc ) x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 0 ); int dw = parag->documentVisibleWidth() - ( doc ? ( left != x ? 0 : doc->rightMargin() ) : 0 ); int curLeft = x; int rm = parag->rightMargin(); int rdiff = doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 0 ) : 0; int w = dw - rdiff; bool fullWidth = TRUE; int marg = left + rdiff; int minw = 0; int wused = 0; int tminw = marg; - int linespace = doc ? parag->lineSpacing() : 0; + int linespacing = doc ? parag->lineSpacing() : 0; bool wrapEnabled = isWrapEnabled( parag ); start = 0; if ( start == 0 ) c = ¶g->string()->at( 0 ); int i = start; - QTextParagLineStart *lineStart = new QTextParagLineStart( y, y, 0 ); + QTextLineStart *lineStart = new QTextLineStart( y, y, 0 ); insertLineStart( parag, 0, lineStart ); int lastBreak = -1; int tmpBaseLine = 0, tmph = 0; bool lastWasNonInlineCustom = FALSE; int align = parag->alignment(); if ( align == Qt3::AlignAuto && doc && doc->alignment() != Qt3::AlignAuto ) align = doc->alignment(); align &= Qt3::AlignHorizontal_Mask; QPainter *painter = QTextFormat::painter(); int col = 0; int ww = 0; QChar lastChr; for ( ; i < len; ++i, ++col ) { if ( c ) lastChr = c->c; // ### next line should not be needed if ( painter ) c->format()->setPainter( painter ); c = &string->at( i ); c->rightToLeft = FALSE; if ( i > 0 && (x > curLeft || ww == 0) || lastWasNonInlineCustom ) { c->lineStart = 0; } else { c->lineStart = 1; firstChar = c; } if ( c->isCustom() && c->customItem()->placement() != QTextCustomItem::PlaceInline ) lastWasNonInlineCustom = TRUE; else lastWasNonInlineCustom = FALSE; if ( c->c.unicode() >= 32 || c->isCustom() ) { ww = string->width( i ); } else if ( c->c == '\t' ) { int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } else { ww = c->format()->width( ' ' ); } // last character ("invisible" space) has no width if ( i == len - 1 ) ww = 0; QTextCustomItem* ci = c->customItem(); if ( c->isCustom() && ci->ownLine() ) { x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); - QTextParagLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, w - x ); + QTextLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) ); ci->resize( w - x); if ( ci->width < w - x ) { if ( align & Qt::AlignHCenter ) x = ( w - ci->width ) / 2; else if ( align & Qt::AlignRight ) { x = w - ci->width; } } c->x = x; curLeft = x; if ( i == 0 || !isBreakable( string, i - 1 ) || string->at( i - 1 ).lineStart == 0 ) { - y += QMAX( h, tmph ); - tmph = c->height() + linespace; + y += QMAX( h, QMAX( tmph, linespacing ) ); + tmph = c->height(); h = tmph; lineStart = lineStart2; lineStart->y = y; insertLineStart( parag, i, lineStart ); c->lineStart = 1; firstChar = c; } else { - tmph = c->height() + linespace; + tmph = c->height(); h = tmph; delete lineStart2; } lineStart->h = h; lineStart->baseLine = h; tmpBaseLine = lineStart->baseLine; lastBreak = -2; x = 0xffffff; minw = QMAX( minw, tminw ); - int tw = ci->minimumWidth(); + int tw = ci->minimumWidth() + ( doc ? doc->leftMargin() : 0 ); if ( tw < QWIDGETSIZE_MAX ) tminw = tw; else tminw = marg; wused = QMAX( wused, ci->width ); continue; } else if ( c->isCustom() && ci->placement() != QTextCustomItem::PlaceInline ) { int tw = ci->minimumWidth(); if ( tw < QWIDGETSIZE_MAX ) minw = QMAX( minw, tw ); } - if ( wrapEnabled && ( !c->c.isSpace() || lastBreak == -2 ) - && ( lastBreak != -1 || allowBreakInWords() ) && - ( wrapAtColumn() == -1 && x + ww > w && lastBreak != -1 || - wrapAtColumn() == -1 && x + ww > w - 4 && lastBreak == -1 && allowBreakInWords() || - wrapAtColumn() != -1 && col >= wrapAtColumn() ) || - parag->isNewLinesAllowed() && lastChr == '\n' && firstChar < c ) { + bool lastWasOwnLineCustomItem = lastBreak == -2; + bool hadBreakableChar = lastBreak != -1; + bool lastWasHardBreak = lastChr == QChar_linesep; + + // we break if + // 1. the last character was a hard break (QChar_linesep) or + // 2. the last charater was a own-line custom item (eg. table or ruler) or + // 3. wrapping was enabled, it was not a space and following + // condition is true: We either had a breakable character + // previously or we ar allowed to break in words and - either + // we break at w pixels and the current char would exceed that + // or - we break at a column and the current character would + // exceed that. + if ( lastWasHardBreak || lastWasOwnLineCustomItem || + ( wrapEnabled && + ( (!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) && + ( (wrapAtColumn() == -1 && x + ww > w) || + (wrapAtColumn() != -1 && col >= wrapAtColumn()) ) ) ) + ) + ) { if ( wrapAtColumn() != -1 ) minw = QMAX( minw, x + ww ); - if ( lastBreak < 0 ) { + // if a break was forced (no breakable char, hard break or own line custom item), break immediately.... + if ( !hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem ) { if ( lineStart ) { lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine ); h = QMAX( h, tmph ); lineStart->h = h; DO_FLOW( lineStart ); } - lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, w - x ); + lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, SPACE(w - x) ); x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); - if ( parag->isNewLinesAllowed() && c->c == '\t' ) { + if ( !doc && c->c == '\t' ) { // qt_format_text tab handling int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } curLeft = x; - y += h; - tmph = c->height() + linespace; + y += QMAX( h, linespacing ); + tmph = c->height(); h = 0; lineStart->y = y; insertLineStart( parag, i, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; - } else { + } else { // ... otherwise if we had a breakable char, break there DO_FLOW( lineStart ); i = lastBreak; - lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ), align, w - string->at( i ).x ); + lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ),align, SPACE(w - string->at( i ).x) ); x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left; w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 ); - if ( parag->isNewLinesAllowed() && c->c == '\t' ) { + if ( !doc && c->c == '\t' ) { // qt_format_text tab handling int nx = parag->nextTab( i, x - left ) + left; if ( nx < x ) ww = w - x; else ww = nx - x; } curLeft = x; - y += h; - tmph = c->height() + linespace; + y += QMAX( h, linespacing ); + tmph = c->height(); h = tmph; lineStart->y = y; insertLineStart( parag, i + 1, lineStart ); lineStart->baseLine = c->ascent(); lineStart->h = c->height(); c->lineStart = 1; firstChar = c; tmpBaseLine = lineStart->baseLine; lastBreak = -1; col = 0; tminw = marg; continue; } - } else if ( lineStart && ( isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) { + } else if ( lineStart && isBreakable( string, i ) ) { if ( len <= 2 || i < len - 1 ) { tmpBaseLine = QMAX( tmpBaseLine, c->ascent() ); - tmph = QMAX( tmph, c->height() + linespace ); + tmph = QMAX( tmph, c->height() ); } minw = QMAX( minw, tminw ); tminw = marg + ww; lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine ); h = QMAX( h, tmph ); lineStart->h = h; if ( i < len - 2 || c->c != ' ' ) lastBreak = i; } else { tminw += ww; - int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() + linespace - c->ascent() ); + int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height()- c->ascent() ); tmpBaseLine = QMAX( tmpBaseLine, c->ascent() ); tmph = tmpBaseLine + belowBaseLine; } c->x = x; x += ww; wused = QMAX( wused, x ); } - // ### hack. The last char in the paragraph is always invisible, and somehow sometimes has a wrong format. It changes between - // layouting and printing. This corrects some layouting errors in BiDi mode due to this. + // ### hack. The last char in the paragraph is always invisible, + // ### and somehow sometimes has a wrong format. It changes + // ### between // layouting and printing. This corrects some + // ### layouting errors in BiDi mode due to this. if ( len > 1 && !c->isAnchor() ) { c->format()->removeRef(); c->setFormat( string->at( len - 2 ).format() ); c->format()->addRef(); } if ( lineStart ) { lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine ); h = QMAX( h, tmph ); lineStart->h = h; // last line in a paragraph is not justified - if ( align == Qt3::AlignJustify ) + if ( align == Qt3::AlignJustify || lastChr == QChar_linesep ) align = Qt3::AlignAuto; DO_FLOW( lineStart ); - lineStart = formatLine( parag, string, lineStart, firstChar, c, align, w - x ); + lineStart = formatLine( parag, string, lineStart, firstChar, c, align, SPACE(w - x) ); delete lineStart; } minw = QMAX( minw, tminw ); + if ( doc ) + minw += doc->rightMargin(); int m = parag->bottomMargin(); - if ( parag->next() && doc && !doc->addMargins() ) - m = QMAX( m, parag->next()->topMargin() ); - parag->setFullWidth( fullWidth ); - if ( parag->next() && parag->next()->isLineBreak() ) + if ( !parag->next() ) m = 0; - y += h + m; + else + m = QMAX(m, parag->next()->topMargin() ) / 2; + parag->setFullWidth( fullWidth ); + y += QMAX( h, linespacing ) + m; wused += rm; if ( !wrapEnabled || wrapAtColumn() != -1 ) minw = QMAX(minw, wused); + + // This is the case where we are breaking wherever we darn well please + // in cases like that, the minw should not be the length of the entire + // word, because we necessarily want to show the word on the whole line. + // example: word wrap in iconview + if ( allowBreakInWords() && minw > wused ) + minw = wused; + thisminw = minw; thiswused = wused; return y; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextIndent::QTextIndent() { } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ QTextFormatCollection::QTextFormatCollection() - : cKey( 307 ), sheet( 0 ) + : cKey( 307 ) { defFormat = new QTextFormat( QApplication::font(), QApplication::palette().color( QPalette::Active, QColorGroup::Text ) ); lastFormat = cres = 0; cflags = -1; cKey.setAutoDelete( TRUE ); cachedFormat = 0; } QTextFormatCollection::~QTextFormatCollection() { delete defFormat; } QTextFormat *QTextFormatCollection::format( QTextFormat *f ) { if ( f->parent() == this || f == defFormat ) { -#ifdef DEBUG_COLLECTION - qDebug( "need '%s', best case!", f->key().latin1() ); -#endif lastFormat = f; lastFormat->addRef(); return lastFormat; } if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) { -#ifdef DEBUG_COLLECTION - qDebug( "need '%s', good case!", f->key().latin1() ); -#endif lastFormat->addRef(); return lastFormat; } QTextFormat *fm = cKey.find( f->key() ); if ( fm ) { -#ifdef DEBUG_COLLECTION - qDebug( "need '%s', normal case!", f->key().latin1() ); -#endif lastFormat = fm; lastFormat->addRef(); return lastFormat; } if ( f->key() == defFormat->key() ) return defFormat; -#ifdef DEBUG_COLLECTION - qDebug( "need '%s', worst case!", f->key().latin1() ); -#endif lastFormat = createFormat( *f ); lastFormat->collection = this; cKey.insert( lastFormat->key(), lastFormat ); return lastFormat; } QTextFormat *QTextFormatCollection::format( QTextFormat *of, QTextFormat *nf, int flags ) { if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) { -#ifdef DEBUG_COLLECTION - qDebug( "mix of '%s' and '%s, best case!", of->key().latin1(), nf->key().latin1() ); -#endif cres->addRef(); return cres; } cres = createFormat( *of ); kof = of->key(); knf = nf->key(); cflags = flags; if ( flags & QTextFormat::Bold ) cres->fn.setBold( nf->fn.bold() ); if ( flags & QTextFormat::Italic ) cres->fn.setItalic( nf->fn.italic() ); if ( flags & QTextFormat::Underline ) cres->fn.setUnderline( nf->fn.underline() ); + if ( flags & QTextFormat::StrikeOut ) + cres->fn.setStrikeOut( nf->fn.strikeOut() ); if ( flags & QTextFormat::Family ) cres->fn.setFamily( nf->fn.family() ); if ( flags & QTextFormat::Size ) { if ( of->usePixelSizes ) cres->fn.setPixelSize( nf->fn.pixelSize() ); else cres->fn.setPointSize( nf->fn.pointSize() ); } if ( flags & QTextFormat::Color ) cres->col = nf->col; if ( flags & QTextFormat::Misspelled ) cres->missp = nf->missp; if ( flags & QTextFormat::VAlign ) cres->ha = nf->ha; cres->update(); QTextFormat *fm = cKey.find( cres->key() ); if ( !fm ) { -#ifdef DEBUG_COLLECTION - qDebug( "mix of '%s' and '%s, worst case!", of->key().latin1(), nf->key().latin1() ); -#endif cres->collection = this; cKey.insert( cres->key(), cres ); } else { -#ifdef DEBUG_COLLECTION - qDebug( "mix of '%s' and '%s, good case!", of->key().latin1(), nf->key().latin1() ); -#endif delete cres; cres = fm; cres->addRef(); } return cres; } QTextFormat *QTextFormatCollection::format( const QFont &f, const QColor &c ) { if ( cachedFormat && cfont == f && ccol == c ) { -#ifdef DEBUG_COLLECTION - qDebug( "format of font and col '%s' - best case", cachedFormat->key().latin1() ); -#endif cachedFormat->addRef(); return cachedFormat; } QString key = QTextFormat::getKey( f, c, FALSE, QTextFormat::AlignNormal ); cachedFormat = cKey.find( key ); cfont = f; ccol = c; if ( cachedFormat ) { -#ifdef DEBUG_COLLECTION - qDebug( "format of font and col '%s' - good case", cachedFormat->key().latin1() ); -#endif cachedFormat->addRef(); return cachedFormat; } if ( key == defFormat->key() ) return defFormat; cachedFormat = createFormat( f, c ); cachedFormat->collection = this; cKey.insert( cachedFormat->key(), cachedFormat ); if ( cachedFormat->key() != key ) qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1() ); -#ifdef DEBUG_COLLECTION - qDebug( "format of font and col '%s' - worst case", cachedFormat->key().latin1() ); -#endif return cachedFormat; } void QTextFormatCollection::remove( QTextFormat *f ) { if ( lastFormat == f ) lastFormat = 0; if ( cres == f ) cres = 0; if ( cachedFormat == f ) cachedFormat = 0; cKey.remove( f->key() ); } -void QTextFormatCollection::debug() -{ -#ifdef DEBUG_COLLECTION - qDebug( "------------ QTextFormatCollection: debug --------------- BEGIN" ); - QDictIterator<QTextFormat> it( cKey ); - for ( ; it.current(); ++it ) { - qDebug( "format '%s' (%p): refcount: %d", it.current()->key().latin1(), - it.current(), it.current()->ref ); - } - qDebug( "------------ QTextFormatCollection: debug --------------- END" ); -#endif -} - -void QTextFormatCollection::updateStyles() -{ - QDictIterator<QTextFormat> it( cKey ); - QTextFormat *f; - while ( ( f = it.current() ) ) { - ++it; - f->updateStyle(); - } - updateKeys(); -} +#define UPDATE( up, lo, rest ) \ + if ( font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest() ) \ + fm->fn.set##up##rest( font.lo##rest() ) -void QTextFormatCollection::updateFontSizes( int base, bool usePixels ) -{ - QDictIterator<QTextFormat> it( cKey ); - QTextFormat *f; - while ( ( f = it.current() ) ) { - ++it; - f->stdSize = base; - f->usePixelSizes = usePixels; - if ( usePixels ) - f->fn.setPixelSize( f->stdSize ); - else - f->fn.setPointSize( f->stdSize ); - styleSheet()->scaleFont( f->fn, f->logicalFontSize ); - f->update(); - } - f = defFormat; - f->stdSize = base; - f->usePixelSizes = usePixels; - if ( usePixels ) - f->fn.setPixelSize( f->stdSize ); - else - f->fn.setPointSize( f->stdSize ); - styleSheet()->scaleFont( f->fn, f->logicalFontSize ); - f->update(); - updateKeys(); -} - -void QTextFormatCollection::updateFontAttributes( const QFont &f, const QFont &old ) +void QTextFormatCollection::updateDefaultFormat( const QFont &font, const QColor &color, QStyleSheet *sheet ) { QDictIterator<QTextFormat> it( cKey ); QTextFormat *fm; + bool usePixels = font.pointSize() == -1; + bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() : + font.pointSize() != defFormat->fn.pointSize(); + int base = usePixels ? font.pixelSize() : font.pointSize(); while ( ( fm = it.current() ) ) { ++it; - if ( fm->fn.family() == old.family() && - fm->fn.weight() == old.weight() && - fm->fn.italic() == old.italic() && - fm->fn.underline() == old.underline() ) { - fm->fn.setFamily( f.family() ); - fm->fn.setWeight( f.weight() ); - fm->fn.setItalic( f.italic() ); - fm->fn.setUnderline( f.underline() ); - fm->update(); - } + UPDATE( F, f, amily ); + UPDATE( W, w, eight ); + UPDATE( B, b, old ); + UPDATE( I, i, talic ); + UPDATE( U, u, nderline ); + if ( changeSize ) { + fm->stdSize = base; + fm->usePixelSizes = usePixels; + if ( usePixels ) + fm->fn.setPixelSize( fm->stdSize ); + else + fm->fn.setPointSize( fm->stdSize ); + sheet->scaleFont( fm->fn, fm->logicalFontSize ); } - fm = defFormat; - if ( fm->fn.family() == old.family() && - fm->fn.weight() == old.weight() && - fm->fn.italic() == old.italic() && - fm->fn.underline() == old.underline() ) { - fm->fn.setFamily( f.family() ); - fm->fn.setWeight( f.weight() ); - fm->fn.setItalic( f.italic() ); - fm->fn.setUnderline( f.underline() ); + if ( color.isValid() && color != defFormat->col && fm->col == defFormat->col ) + fm->col = color; fm->update(); } + + defFormat->fn = font; + defFormat->col = color; + defFormat->update(); + defFormat->stdSize = base; + defFormat->usePixelSizes = usePixels; + updateKeys(); } - // the keys in cKey have changed, rebuild the hashtable void QTextFormatCollection::updateKeys() { if ( cKey.isEmpty() ) return; cKey.setAutoDelete( FALSE ); QTextFormat** formats = new QTextFormat*[ cKey.count() + 1]; QTextFormat **f = formats; QDictIterator<QTextFormat> it( cKey ); while ( ( *f = it.current() ) ) { ++it; ++f; } cKey.clear(); for ( f = formats; *f; f++ ) cKey.insert( (*f)->key(), *f ); cKey.setAutoDelete( TRUE ); + delete [] formats; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void QTextFormat::setBold( bool b ) { if ( b == fn.bold() ) return; fn.setBold( b ); update(); } void QTextFormat::setMisspelled( bool b ) { if ( b == (bool)missp ) return; missp = b; update(); } void QTextFormat::setVAlign( VerticalAlignment a ) { if ( a == ha ) return; ha = a; update(); } void QTextFormat::setItalic( bool b ) { if ( b == fn.italic() ) return; fn.setItalic( b ); update(); } void QTextFormat::setUnderline( bool b ) { if ( b == fn.underline() ) return; fn.setUnderline( b ); update(); } +void QTextFormat::setStrikeOut( bool b ) +{ + if ( b == fn.strikeOut() ) + return; + fn.setStrikeOut( b ); + update(); +} + void QTextFormat::setFamily( const QString &f ) { if ( f == fn.family() ) return; fn.setFamily( f ); update(); } void QTextFormat::setPointSize( int s ) { if ( s == fn.pointSize() ) return; fn.setPointSize( s ); usePixelSizes = FALSE; update(); } void QTextFormat::setFont( const QFont &f ) { if ( f == fn && !k.isEmpty() ) return; fn = f; update(); } void QTextFormat::setColor( const QColor &c ) { if ( c == col ) return; col = c; update(); } -static int makeLogicFontSize( int s ) -{ - int defSize = QApplication::font().pointSize(); - if ( s < defSize - 4 ) - return 1; - if ( s < defSize ) - return 2; - if ( s < defSize + 4 ) - return 3; - if ( s < defSize + 8 ) - return 4; - if ( s < defSize + 12 ) - return 5; - if (s < defSize + 16 ) - return 6; - return 7; -} - -static QTextFormat *defaultFormat = 0; - -QString QTextFormat::makeFormatChangeTags( QTextFormat *f, const QString& oldAnchorHref, const QString& anchorHref ) const +QString QTextFormat::makeFormatChangeTags( QTextFormat* defaultFormat, QTextFormat *f, + const QString& oldAnchorHref, const QString& anchorHref ) const { - if ( !defaultFormat ) // #### wrong, use the document's default format instead - defaultFormat = new QTextFormat( QApplication::font(), - QApplication::palette().color( QPalette::Active, QColorGroup::Text ) ); - QString tag; - if ( f ) { - if ( f->font() != defaultFormat->font() ) { - if ( f->font().family() != defaultFormat->font().family() - || f->font().pointSize() != defaultFormat->font().pointSize() - || f->color().rgb() != defaultFormat->color().rgb() ) - tag += "</font>"; - if ( f->font().underline() && f->font().underline() != defaultFormat->font().underline() ) - tag += "</u>"; - if ( f->font().italic() && f->font().italic() != defaultFormat->font().italic() ) - tag += "</i>"; - if ( f->font().bold() && f->font().bold() != defaultFormat->font().bold() ) - tag += "</b>"; - } - if ( !oldAnchorHref.isEmpty() ) - tag += "</a>"; - } + if ( f ) + tag += f->makeFormatEndTags( defaultFormat, oldAnchorHref ); if ( !anchorHref.isEmpty() ) tag += "<a href=\"" + anchorHref + "\">"; - if ( font() != defaultFormat->font() ) { - if ( font().bold() && font().bold() != defaultFormat->font().bold() ) - tag += "<b>"; - if ( font().italic() && font().italic() != defaultFormat->font().italic() ) - tag += "<i>"; - if ( font().underline() && font().underline() != defaultFormat->font().underline() ) - tag += "<u>"; - } if ( font() != defaultFormat->font() + || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb() ) { - QString f; + QString s; if ( font().family() != defaultFormat->font().family() ) - f +=" face=\"" + fn.family() + "\""; - if ( font().pointSize() != defaultFormat->font().pointSize() ) { - f +=" size=\"" + QString::number( makeLogicFontSize( fn.pointSize() ) ) + "\""; - f +=" style=\"font-size:" + QString::number( fn.pointSize() ) + "pt\""; + s += QString(!!s?";":"") + "font-family:" + fn.family(); + if ( font().italic() && font().italic() != defaultFormat->font().italic() ) + s += QString(!!s?";":"") + "font-style:" + (font().italic() ? "italic" : "normal"); + if ( font().pointSize() != defaultFormat->font().pointSize() ) + s += QString(!!s?";":"") + "font-size:" + QString::number( fn.pointSize() ) + "pt"; + if ( font().weight() != defaultFormat->font().weight() ) + s += QString(!!s?";":"") + "font-weight:" + QString::number( fn.weight() * 8 ); + if ( font().underline() != defaultFormat->font().underline() ) + s += QString(!!s?";":"") + "text-decoration:" + ( font().underline() ? "underline" : "none"); + if ( vAlign() != defaultFormat->vAlign() ) { + s += QString(!!s?";":"") + "vertical-align:"; + if ( vAlign() == QTextFormat::AlignSuperScript ) + s += "super"; + else if ( vAlign() == QTextFormat::AlignSubScript ) + s += "sub"; + else + s += "normal"; } if ( color().rgb() != defaultFormat->color().rgb() ) - f +=" color=\"" + col.name() + "\""; - if ( !f.isEmpty() ) - tag += "<font" + f + ">"; + s += QString(!!s?";":"") + "color:" + col.name(); + if ( !s.isEmpty() ) + tag += "<span style=\"" + s + "\">"; } return tag; } -QString QTextFormat::makeFormatEndTags( const QString& anchorHref ) const +QString QTextFormat::makeFormatEndTags( QTextFormat* defaultFormat, const QString& anchorHref ) const { - if ( !defaultFormat ) - defaultFormat = new QTextFormat( QApplication::font(), - QApplication::palette().color( QPalette::Active, QColorGroup::Text ) ); - QString tag; - if ( font() != defaultFormat->font() ) { if ( font().family() != defaultFormat->font().family() || font().pointSize() != defaultFormat->font().pointSize() + || font().weight() != defaultFormat->font().weight() + || font().italic() != defaultFormat->font().italic() + || font().underline() != defaultFormat->font().underline() + || font().strikeOut() != defaultFormat->font().strikeOut() + || vAlign() != defaultFormat->vAlign() || color().rgb() != defaultFormat->color().rgb() ) - tag += "</font>"; - if ( font().underline() && font().underline() != defaultFormat->font().underline() ) - tag += "</u>"; - if ( font().italic() && font().italic() != defaultFormat->font().italic() ) - tag += "</i>"; - if ( font().bold() && font().bold() != defaultFormat->font().bold() ) - tag += "</b>"; - } + tag += "</span>"; if ( !anchorHref.isEmpty() ) tag += "</a>"; return tag; } -QTextFormat QTextFormat::makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr ) const +QTextFormat QTextFormat::makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor ) const { QTextFormat format(*this); - if ( style ) { - format.style = style->name(); - if ( style->name() == "font") { - if ( attr.contains("color") ) { - QString s = attr["color"]; - if ( !s.isEmpty() ) { - format.col.setNamedColor( s ); - format.linkColor = FALSE; - } - } - if ( attr.contains("size") ) { - QString a = attr["size"]; - int n = a.toInt(); - if ( a[0] == '+' || a[0] == '-' ) - n += format.logicalFontSize; - format.logicalFontSize = n; - if ( format.usePixelSizes ) - format.fn.setPixelSize( format.stdSize ); - else - format.fn.setPointSize( format.stdSize ); - style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); - } - if ( attr.contains("style" ) ) { - QString a = attr["style"]; - if ( a.startsWith( "font-size:" ) ) { - QString s = a.mid( a.find( ':' ) + 1 ); - int n = s.left( s.length() - 2 ).toInt(); - format.logicalFontSize = 0; - if ( format.usePixelSizes ) - format.fn.setPixelSize( n ); - else - format.fn.setPointSize( n ); - } - } - if ( attr.contains("face") ) { - QString a = attr["face"]; - if ( a.contains(',') ) - a = a.left( a.find(',') ); - format.fn.setFamily( a ); - } - } else { + if (!style ) + return format; + if ( !style->isAnchor() && style->color().isValid() ) { // the style is not an anchor and defines a color. // It might be used inside an anchor and it should // override the link color. format.linkColor = FALSE; } switch ( style->verticalAlignment() ) { case QStyleSheetItem::VAlignBaseline: format.setVAlign( QTextFormat::AlignNormal ); break; case QStyleSheetItem::VAlignSuper: format.setVAlign( QTextFormat::AlignSuperScript ); break; case QStyleSheetItem::VAlignSub: format.setVAlign( QTextFormat::AlignSubScript ); break; } if ( style->fontWeight() != QStyleSheetItem::Undefined ) format.fn.setWeight( style->fontWeight() ); if ( style->fontSize() != QStyleSheetItem::Undefined ) { format.fn.setPointSize( style->fontSize() ); } else if ( style->logicalFontSize() != QStyleSheetItem::Undefined ) { format.logicalFontSize = style->logicalFontSize(); if ( format.usePixelSizes ) format.fn.setPixelSize( format.stdSize ); else format.fn.setPointSize( format.stdSize ); style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); } else if ( style->logicalFontSizeStep() ) { format.logicalFontSize += style->logicalFontSizeStep(); if ( format.usePixelSizes ) format.fn.setPixelSize( format.stdSize ); else format.fn.setPointSize( format.stdSize ); style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); } if ( !style->fontFamily().isEmpty() ) format.fn.setFamily( style->fontFamily() ); if ( style->color().isValid() ) format.col = style->color(); if ( style->definesFontItalic() ) format.fn.setItalic( style->fontItalic() ); if ( style->definesFontUnderline() ) format.fn.setUnderline( style->fontUnderline() ); + if ( style->definesFontStrikeOut() ) + format.fn.setStrikeOut( style->fontStrikeOut() ); + + + if ( style->name() == "font") { + if ( attr.contains("color") ) { + QString s = attr["color"]; + if ( !s.isEmpty() ) { + format.col.setNamedColor( s ); + format.linkColor = FALSE; + } + } + if ( attr.contains("face") ) { + QString a = attr["face"]; + QString family = QTextDocument::section( a, ",", 0, 0 ); + if ( !!family ) + format.fn.setFamily( family ); + } + if ( attr.contains("size") ) { + QString a = attr["size"]; + int n = a.toInt(); + if ( a[0] == '+' || a[0] == '-' ) + n += format.logicalFontSize; + format.logicalFontSize = n; + if ( format.usePixelSizes ) + format.fn.setPixelSize( format.stdSize ); + else + format.fn.setPointSize( format.stdSize ); + style->styleSheet()->scaleFont( format.fn, format.logicalFontSize ); + } + } + if ( attr.contains("style" ) ) { + QString a = attr["style"]; + for ( int s = 0; s < a.contains(';')+1; s++ ) { + QString style = QTextDocument::section( a, ";", s, s ); + if ( style.startsWith("font-size:" ) && QTextDocument::endsWith(style, "pt") ) { + format.logicalFontSize = 0; + format.setPointSize( int( scaleFontsFactor * style.mid( 10, style.length() - 12 ).toInt() ) ); + } if ( style.startsWith("font-style:" ) ) { + QString s = style.mid( 11 ).stripWhiteSpace(); + if ( s == "normal" ) + format.fn.setItalic( FALSE ); + else if ( s == "italic" || s == "oblique" ) + format.fn.setItalic( TRUE ); + } else if ( style.startsWith("font-weight:" ) ) { + QString s = style.mid( 12 ); + bool ok = TRUE; + int n = s.toInt( &ok ); + if ( ok ) + format.fn.setWeight( n/8 ); + } else if ( style.startsWith("font-family:" ) ) { + format.fn.setFamily( QTextDocument::section(style.mid(12),",",0,0).stripWhiteSpace() ); + } else if ( style.startsWith("text-decoration:" ) ) { + QString s = style.mid( 16 ).stripWhiteSpace(); + format.fn.setUnderline( s == "underline" ); + } else if ( style.startsWith("vertical-align:" ) ) { + QString s = style.mid( 15 ).stripWhiteSpace(); + if ( s == "sub" ) + format.setVAlign( QTextFormat::AlignSubScript ); + else if ( s == "super" ) + format.setVAlign( QTextFormat::AlignSuperScript ); + else + format.setVAlign( QTextFormat::AlignNormal ); + } else if ( style.startsWith("color:" ) ) { + format.col.setNamedColor( style.mid(6) ); + format.linkColor = FALSE; + } } } format.update(); return format; } struct QPixmapInt { QPixmapInt() : ref( 0 ) {} QPixmap pm; int ref; }; static QMap<QString, QPixmapInt> *pixmap_map = 0; QTextImage::QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context, QMimeSourceFactory &factory ) : QTextCustomItem( p ) { -#if defined(PARSER_DEBUG) - qDebug( debug_indent + "new QTextImage (pappi: %p)", p ); -#endif - width = height = 0; if ( attr.contains("width") ) width = attr["width"].toInt(); if ( attr.contains("height") ) height = attr["height"].toInt(); reg = 0; QString imageName = attr["src"]; if (!imageName) imageName = attr["source"]; -#if defined(PARSER_DEBUG) - qDebug( debug_indent + " .." + imageName ); -#endif - if ( !imageName.isEmpty() ) { imgId = QString( "%1,%2,%3,%4" ).arg( imageName ).arg( width ).arg( height ).arg( (ulong)&factory ); if ( !pixmap_map ) pixmap_map = new QMap<QString, QPixmapInt>; if ( pixmap_map->contains( imgId ) ) { QPixmapInt& pmi = pixmap_map->operator[](imgId); pm = pmi.pm; pmi.ref++; width = pm.width(); height = pm.height(); } else { QImage img; const QMimeSource* m = factory.data( imageName, context ); if ( !m ) { qWarning("QTextImage: no mimesource for %s", imageName.latin1() ); } else { if ( !QImageDrag::decode( m, img ) ) { qWarning("QTextImage: cannot decode %s", imageName.latin1() ); } } if ( !img.isNull() ) { if ( width == 0 ) { width = img.width(); if ( height != 0 ) { width = img.width() * height / img.height(); } } if ( height == 0 ) { height = img.height(); if ( width != img.width() ) { height = img.height() * width / img.width(); } } if ( img.width() != width || img.height() != height ){ #ifndef QT_NO_IMAGE_SMOOTHSCALE img = img.smoothScale(width, height); #endif width = img.width(); height = img.height(); } pm.convertFromImage( img ); } if ( !pm.isNull() ) { QPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.pm = pm; pmi.ref++; } } if ( pm.mask() ) { QRegion mask( *pm.mask() ); QRegion all( 0, 0, pm.width(), pm.height() ); reg = new QRegion( all.subtract( mask ) ); } } if ( pm.isNull() && (width*height)==0 ) width = height = 50; place = PlaceInline; if ( attr["align"] == "left" ) place = PlaceLeft; else if ( attr["align"] == "right" ) place = PlaceRight; tmpwidth = width; tmpheight = height; attributes = attr; } QTextImage::~QTextImage() { if ( pixmap_map && pixmap_map->contains( imgId ) ) { QPixmapInt& pmi = pixmap_map->operator[](imgId); pmi.ref--; if ( !pmi.ref ) { pixmap_map->remove( imgId ); if ( pixmap_map->isEmpty() ) { delete pixmap_map; pixmap_map = 0; } } } + delete reg; } QString QTextImage::richText() const { QString s; s += "<img "; QMap<QString, QString>::ConstIterator it = attributes.begin(); for ( ; it != attributes.end(); ++it ) s += it.key() + "=" + *it + " "; s += ">"; return s; } void QTextImage::adjustToPainter( QPainter* p ) { width = scale( tmpwidth, p ); height = scale( tmpheight, p ); } #if !defined(Q_WS_X11) #include <qbitmap.h> #include "qcleanuphandler.h" static QPixmap *qrt_selection = 0; static QSingleCleanupHandler<QPixmap> qrt_cleanup_pixmap; static void qrt_createSelectionPixmap( const QColorGroup &cg ) { qrt_selection = new QPixmap( 2, 2 ); qrt_cleanup_pixmap.set( &qrt_selection ); qrt_selection->fill( Qt::color0 ); QBitmap m( 2, 2 ); m.fill( Qt::color1 ); QPainter p( &m ); p.setPen( Qt::color0 ); for ( int j = 0; j < 2; ++j ) { p.drawPoint( j % 2, j ); } p.end(); qrt_selection->setMask( m ); qrt_selection->fill( cg.highlight() ); } #endif void QTextImage::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ) { if ( placement() != PlaceInline ) { x = xpos; y = ypos; } if ( pm.isNull() ) { p->fillRect( x , y, width, height, cg.dark() ); return; } if ( is_printer( p ) ) { p->drawPixmap( x, y, pm ); return; } if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) ) return; if ( placement() == PlaceInline ) p->drawPixmap( x , y, pm ); else p->drawPixmap( cx , cy, pm, cx - x, cy - y, cw, ch ); if ( selected && placement() == PlaceInline && is_printer( p ) ) { #if defined(Q_WS_X11) p->fillRect( QRect( QPoint( x, y ), pm.size() ), QBrush( cg.highlight(), QBrush::Dense4Pattern) ); #else // in WIN32 Dense4Pattern doesn't work correctly (transparency problem), so work around it if ( !qrt_selection ) qrt_createSelectionPixmap( cg ); p->drawTiledPixmap( x, y, pm.width(), pm.height(), *qrt_selection ); #endif } } void QTextHorizontalLine::adjustToPainter( QPainter* p ) { height = scale( tmpheight, p ); } QTextHorizontalLine::QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr, const QString &, QMimeSourceFactory & ) : QTextCustomItem( p ) { height = tmpheight = 8; if ( attr.find( "color" ) != attr.end() ) color = QColor( *attr.find( "color" ) ); } QTextHorizontalLine::~QTextHorizontalLine() { } QString QTextHorizontalLine::richText() const { return "<hr>"; } void QTextHorizontalLine::draw( QPainter* p, int x, int y, int , int , int , int , const QColorGroup& cg, bool selected ) { QRect r( x, y, width, height); if ( is_printer( p ) ) { QPen oldPen = p->pen(); if ( !color.isValid() ) p->setPen( QPen( cg.text(), height/8 ) ); else p->setPen( QPen( color, height/8 ) ); p->drawLine( r.left()-1, y + height / 2, r.right() + 1, y + height / 2 ); p->setPen( oldPen ); } else { QColorGroup g( cg ); if ( color.isValid() ) g.setColor( QColorGroup::Dark, color ); if ( selected ) p->fillRect( r.left(), y, r.right(), y + height, g.highlight() ); qDrawShadeLine( p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, g, TRUE, height / 8 ); } } /*****************************************************************/ // Small set of utility functions to make the parser a bit simpler // bool QTextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c) { if ( pos >= length ) return FALSE; return doc[ pos ].lower() == c.lower(); } bool QTextDocument::hasPrefix( const QChar* doc, int length, int pos, const QString& s ) { if ( pos + (int) s.length() >= length ) return FALSE; for ( int i = 0; i < (int)s.length(); i++ ) { if ( doc[ pos + i ].lower() != s[ i ].lower() ) return FALSE; } return TRUE; } static bool qt_is_cell_in_use( QPtrList<QTextTableCell>& cells, int row, int col ) { for ( QTextTableCell* c = cells.first(); c; c = cells.next() ) { if ( row >= c->row() && row < c->row() + c->rowspan() && col >= c->column() && col < c->column() + c->colspan() ) return TRUE; } return FALSE; } QTextCustomItem* QTextDocument::parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt, - const QChar* doc, int length, int& pos, QTextParag *curpar ) + const QChar* doc, int length, int& pos, QTextParagraph *curpar ) { QTextTable* table = new QTextTable( this, attr ); int row = -1; int col = -1; QString rowbgcolor; QString rowalign; QString tablebgcolor = attr["bgcolor"]; QPtrList<QTextTableCell> multicells; QString tagname; (void) eatSpace(doc, length, pos); while ( pos < length) { if (hasPrefix(doc, length, pos, QChar('<')) ){ if (hasPrefix(doc, length, pos+1, QChar('/'))) { tagname = parseCloseTag( doc, length, pos ); if ( tagname == "table" ) { -#if defined(PARSER_DEBUG) - debug_indent.remove( debug_indent.length() - 3, 2 ); -#endif return table; } } else { QMap<QString, QString> attr2; bool emptyTag = FALSE; tagname = parseOpenTag( doc, length, pos, attr2, emptyTag ); if ( tagname == "tr" ) { rowbgcolor = attr2["bgcolor"]; rowalign = attr2["align"]; row++; col = -1; } else if ( tagname == "td" || tagname == "th" ) { col++; while ( qt_is_cell_in_use( multicells, row, col ) ) { col++; } if ( row >= 0 && col >= 0 ) { const QStyleSheetItem* s = sheet_->item(tagname); if ( !attr2.contains("bgcolor") ) { if (!rowbgcolor.isEmpty() ) attr2["bgcolor"] = rowbgcolor; else if (!tablebgcolor.isEmpty() ) attr2["bgcolor"] = tablebgcolor; } if ( !attr2.contains("align") ) { if (!rowalign.isEmpty() ) attr2["align"] = rowalign; } // extract the cell contents int end = pos; while ( end < length && !hasPrefix( doc, length, end, "</td") && !hasPrefix( doc, length, end, "<td") && !hasPrefix( doc, length, end, "</th") && !hasPrefix( doc, length, end, "<th") && !hasPrefix( doc, length, end, "<td") && !hasPrefix( doc, length, end, "</tr") && !hasPrefix( doc, length, end, "<tr") && !hasPrefix( doc, length, end, "</table") ) { if ( hasPrefix( doc, length, end, "<table" ) ) { // nested table int nested = 1; ++end; while ( end < length && nested != 0 ) { if ( hasPrefix( doc, length, end, "</table" ) ) nested--; if ( hasPrefix( doc, length, end, "<table" ) ) nested++; end++; } } end++; } QTextTableCell* cell = new QTextTableCell( table, row, col, - attr2, s, fmt.makeTextFormat( s, attr2 ), + attr2, s, fmt.makeTextFormat( s, attr2, scaleFontsFactor ), contxt, *factory_, sheet_, QString( doc, length).mid( pos, end - pos ) ); - cell->richText()->parParag = curpar; + cell->richText()->parentPar = curpar; if ( cell->colspan() > 1 || cell->rowspan() > 1 ) multicells.append( cell ); col += cell->colspan()-1; pos = end; } } } } else { ++pos; } } -#if defined(PARSER_DEBUG) - debug_indent.remove( debug_indent.length() - 3, 2 ); -#endif return table; } bool QTextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp ) { int old_pos = pos; while (pos < length && doc[pos].isSpace() && ( includeNbsp || (doc[pos] != QChar::nbsp ) ) ) pos++; return old_pos < pos; } bool QTextDocument::eat(const QChar* doc, int length, int& pos, QChar c) { bool ok = pos < length && doc[pos] == c; if ( ok ) pos++; return ok; } /*****************************************************************/ struct Entity { const char * name; Q_UINT16 code; }; static const Entity entitylist [] = { { "AElig", 0x00c6 }, { "Aacute", 0x00c1 }, { "Acirc", 0x00c2 }, { "Agrave", 0x00c0 }, { "Alpha", 0x0391 }, { "AMP", 38 }, { "Aring", 0x00c5 }, { "Atilde", 0x00c3 }, { "Auml", 0x00c4 }, { "Beta", 0x0392 }, { "Ccedil", 0x00c7 }, { "Chi", 0x03a7 }, { "Dagger", 0x2021 }, { "Delta", 0x0394 }, { "ETH", 0x00d0 }, { "Eacute", 0x00c9 }, { "Ecirc", 0x00ca }, { "Egrave", 0x00c8 }, { "Epsilon", 0x0395 }, { "Eta", 0x0397 }, { "Euml", 0x00cb }, { "Gamma", 0x0393 }, { "GT", 62 }, { "Iacute", 0x00cd }, { "Icirc", 0x00ce }, { "Igrave", 0x00cc }, { "Iota", 0x0399 }, { "Iuml", 0x00cf }, { "Kappa", 0x039a }, { "Lambda", 0x039b }, { "LT", 60 }, { "Mu", 0x039c }, { "Ntilde", 0x00d1 }, { "Nu", 0x039d }, { "OElig", 0x0152 }, { "Oacute", 0x00d3 }, { "Ocirc", 0x00d4 }, { "Ograve", 0x00d2 }, { "Omega", 0x03a9 }, { "Omicron", 0x039f }, { "Oslash", 0x00d8 }, { "Otilde", 0x00d5 }, { "Ouml", 0x00d6 }, { "Phi", 0x03a6 }, { "Pi", 0x03a0 }, { "Prime", 0x2033 }, { "Psi", 0x03a8 }, { "QUOT", 34 }, { "Rho", 0x03a1 }, { "Scaron", 0x0160 }, { "Sigma", 0x03a3 }, { "THORN", 0x00de }, { "Tau", 0x03a4 }, { "Theta", 0x0398 }, { "Uacute", 0x00da }, { "Ucirc", 0x00db }, { "Ugrave", 0x00d9 }, { "Upsilon", 0x03a5 }, { "Uuml", 0x00dc }, { "Xi", 0x039e }, { "Yacute", 0x00dd }, { "Yuml", 0x0178 }, { "Zeta", 0x0396 }, { "aacute", 0x00e1 }, { "acirc", 0x00e2 }, { "acute", 0x00b4 }, { "aelig", 0x00e6 }, { "agrave", 0x00e0 }, { "alefsym", 0x2135 }, { "alpha", 0x03b1 }, @@ -7038,205 +6715,196 @@ static QMap<QCString, QChar> *html_map = 0; static void qt_cleanup_html_map() { delete html_map; html_map = 0; } static QMap<QCString, QChar> *htmlMap() { if ( !html_map ) { html_map = new QMap<QCString, QChar>; qAddPostRoutine( qt_cleanup_html_map ); const Entity *ent = entitylist; while( ent->code ) { html_map->insert( ent->name, QChar(ent->code) ); ent++; } } return html_map; } QChar QTextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos) { QCString s; pos++; int recoverpos = pos; while ( pos < length && doc[pos] != ';' && !doc[pos].isSpace() && pos < recoverpos + 6) { s += doc[pos]; pos++; } if (doc[pos] != ';' && !doc[pos].isSpace() ) { pos = recoverpos; return '&'; } pos++; if ( s.length() > 1 && s[0] == '#') { int num = s.mid(1).toInt(); if ( num == 151 ) // ### hack for designer manual return '-'; return num; } QMap<QCString, QChar>::Iterator it = htmlMap()->find(s); if ( it != htmlMap()->end() ) { return *it; } pos = recoverpos; return '&'; } QString QTextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower) { QString s; if (doc[pos] == '"') { pos++; while ( pos < length && doc[pos] != '"' ) { s += doc[pos]; pos++; } eat(doc, length, pos, '"'); } else { static QString term = QString::fromLatin1("/>"); while( pos < length && (doc[pos] != '>' && !hasPrefix( doc, length, pos, term)) && doc[pos] != '<' && doc[pos] != '=' && !doc[pos].isSpace()) { if ( doc[pos] == '&') s += parseHTMLSpecialChar( doc, length, pos ); else { s += doc[pos]; pos++; } } if (lower) s = s.lower(); } return s; } QChar QTextDocument::parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm ) { if ( pos >= length ) return QChar::null; QChar c = doc[pos++]; if (c == '<' ) return QChar::null; if ( c.isSpace() && c != QChar::nbsp ) { if ( wsm == QStyleSheetItem::WhiteSpacePre ) { - if ( c == ' ' ) - return QChar::nbsp; - else - return c; - } else if ( wsm == QStyleSheetItem_WhiteSpaceNoCompression ) { - return c; - } else if ( wsm == QStyleSheetItem_WhiteSpaceNormalWithNewlines ) { if ( c == '\n' ) + return QChar_linesep; + else return c; - while ( pos< length && - doc[pos].isSpace() && doc[pos] != QChar::nbsp && doc[pos] != '\n' ) - pos++; - return ' '; } else { // non-pre mode: collapse whitespace except nbsp while ( pos< length && doc[pos].isSpace() && doc[pos] != QChar::nbsp ) pos++; if ( wsm == QStyleSheetItem::WhiteSpaceNoWrap ) return QChar::nbsp; else return ' '; } } else if ( c == '&' ) return parseHTMLSpecialChar( doc, length, --pos ); else return c; } QString QTextDocument::parseOpenTag(const QChar* doc, int length, int& pos, QMap<QString, QString> &attr, bool& emptyTag) { emptyTag = FALSE; pos++; if ( hasPrefix(doc, length, pos, '!') ) { if ( hasPrefix( doc, length, pos+1, "--")) { pos += 3; // eat comments QString pref = QString::fromLatin1("-->"); while ( !hasPrefix(doc, length, pos, pref ) && pos < length ) pos++; if ( hasPrefix(doc, length, pos, pref ) ) { pos += 3; eatSpace(doc, length, pos, TRUE); } emptyTag = TRUE; return QString::null; } else { // eat strange internal tags while ( !hasPrefix(doc, length, pos, '>') && pos < length ) pos++; if ( hasPrefix(doc, length, pos, '>') ) { pos++; eatSpace(doc, length, pos, TRUE); } return QString::null; } } QString tag = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); static QString term = QString::fromLatin1("/>"); static QString s_TRUE = QString::fromLatin1("TRUE"); while (doc[pos] != '>' && ! (emptyTag = hasPrefix(doc, length, pos, term) )) { QString key = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); if ( key.isEmpty()) { // error recovery while ( pos < length && doc[pos] != '>' ) pos++; break; } QString value; if (hasPrefix(doc, length, pos, '=') ){ pos++; eatSpace(doc, length, pos); value = parseWord(doc, length, pos, FALSE); } else value = s_TRUE; attr.insert(key.lower(), value ); eatSpace(doc, length, pos, TRUE); } if (emptyTag) { eat(doc, length, pos, '/'); eat(doc, length, pos, '>'); } else eat(doc, length, pos, '>'); return tag; } QString QTextDocument::parseCloseTag( const QChar* doc, int length, int& pos ) { pos++; pos++; QString tag = parseWord(doc, length, pos ); eatSpace(doc, length, pos, TRUE); eat(doc, length, pos, '>'); return tag; } QTextFlow::QTextFlow() { w = pagesize = 0; @@ -7262,824 +6930,915 @@ void QTextFlow::setWidth( int width ) int QTextFlow::adjustLMargin( int yp, int, int margin, int space ) { for ( QTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) { if ( item->ypos == -1 ) continue; if ( yp >= item->ypos && yp < item->ypos + item->height ) margin = QMAX( margin, item->xpos + item->width + space ); } return margin; } int QTextFlow::adjustRMargin( int yp, int, int margin, int space ) { for ( QTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) { if ( item->ypos == -1 ) continue; if ( yp >= item->ypos && yp < item->ypos + item->height ) margin = QMAX( margin, w - item->xpos - space ); } return margin; } int QTextFlow::adjustFlow( int y, int /*w*/, int h ) { if ( pagesize > 0 ) { // check pages int yinpage = y % pagesize; if ( yinpage <= border_tolerance ) return border_tolerance - yinpage; else if ( yinpage + h > pagesize - border_tolerance ) return ( pagesize - yinpage ) + border_tolerance; } return 0; } void QTextFlow::unregisterFloatingItem( QTextCustomItem* item ) { leftItems.removeRef( item ); rightItems.removeRef( item ); } void QTextFlow::registerFloatingItem( QTextCustomItem* item ) { if ( item->placement() == QTextCustomItem::PlaceRight ) { if ( !rightItems.contains( item ) ) rightItems.append( item ); } else if ( item->placement() == QTextCustomItem::PlaceLeft && !leftItems.contains( item ) ) { leftItems.append( item ); } } QRect QTextFlow::boundingRect() const { QRect br; QPtrListIterator<QTextCustomItem> l( leftItems ); while( l.current() ) { br = br.unite( l.current()->geometry() ); ++l; } QPtrListIterator<QTextCustomItem> r( rightItems ); while( r.current() ) { br = br.unite( r.current()->geometry() ); ++r; } return br; } void QTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ) { QTextCustomItem *item; for ( item = leftItems.first(); item; item = leftItems.next() ) { if ( item->xpos == -1 || item->ypos == -1 ) continue; item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected ); } for ( item = rightItems.first(); item; item = rightItems.next() ) { if ( item->xpos == -1 || item->ypos == -1 ) continue; item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected ); } } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void QTextCustomItem::pageBreak( int /*y*/ , QTextFlow* /*flow*/ ) { } QTextTable::QTextTable( QTextDocument *p, const QMap<QString, QString> & attr ) : QTextCustomItem( p ) { cells.setAutoDelete( FALSE ); -#if defined(PARSER_DEBUG) - debug_indent += "\t"; - qDebug( debug_indent + "new QTextTable (%p)", this ); - debug_indent += "\t"; -#endif cellspacing = 2; if ( attr.contains("cellspacing") ) cellspacing = attr["cellspacing"].toInt(); cellpadding = 1; if ( attr.contains("cellpadding") ) cellpadding = attr["cellpadding"].toInt(); border = innerborder = 0; if ( attr.contains("border" ) ) { QString s( attr["border"] ); if ( s == "TRUE" ) border = 1; else border = attr["border"].toInt(); } us_b = border; innerborder = us_ib = border ? 1 : 0; if ( border ) cellspacing += 2; us_ib = innerborder; us_cs = cellspacing; us_cp = cellpadding; outerborder = cellspacing + border; us_ob = outerborder; layout = new QGridLayout( 1, 1, cellspacing ); fixwidth = 0; stretch = 0; if ( attr.contains("width") ) { bool b; QString s( attr["width"] ); int w = s.toInt( &b ); if ( b ) { fixwidth = w; } else { s = s.stripWhiteSpace(); if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' ) stretch = s.left( s.length()-1).toInt(); } } place = PlaceInline; if ( attr["align"] == "left" ) place = PlaceLeft; else if ( attr["align"] == "right" ) place = PlaceRight; cachewidth = 0; attributes = attr; pageBreakFor = -1; } QTextTable::~QTextTable() { delete layout; } QString QTextTable::richText() const { QString s; s = "<table "; QMap<QString, QString>::ConstIterator it = attributes.begin(); for ( ; it != attributes.end(); ++it ) s += it.key() + "=" + *it + " "; s += ">\n"; int lastRow = -1; bool needEnd = FALSE; QPtrListIterator<QTextTableCell> it2( cells ); while ( it2.current() ) { QTextTableCell *cell = it2.current(); ++it2; if ( lastRow != cell->row() ) { if ( lastRow != -1 ) s += "</tr>\n"; s += "<tr>"; lastRow = cell->row(); needEnd = TRUE; } s += "<td "; it = cell->attributes.begin(); for ( ; it != cell->attributes.end(); ++it ) - s += it.key() + "=" + *it + " "; + s += " " + it.key() + "=" + *it; s += ">"; s += cell->richText()->richText(); s += "</td>"; } if ( needEnd ) s += "</tr>\n"; s += "</table>\n"; return s; } void QTextTable::adjustToPainter( QPainter* p ) { cellspacing = scale( us_cs, p ); cellpadding = scale( us_cp, p ); border = scale( us_b , p ); innerborder = scale( us_ib, p ); outerborder = scale( us_ob ,p ); width = 0; cachewidth = 0; for ( QTextTableCell* cell = cells.first(); cell; cell = cells.next() ) cell->adjustToPainter( p ); } void QTextTable::adjustCells( int y , int shift ) { QPtrListIterator<QTextTableCell> it( cells ); QTextTableCell* cell; bool enlarge = FALSE; while ( ( cell = it.current() ) ) { ++it; QRect r = cell->geometry(); if ( y <= r.top() ) { r.moveBy(0, shift ); cell->setGeometry( r ); enlarge = TRUE; } else if ( y <= r.bottom() ) { r.rBottom() += shift; cell->setGeometry( r ); enlarge = TRUE; } } if ( enlarge ) height += shift; } void QTextTable::pageBreak( int yt, QTextFlow* flow ) { if ( flow->pageSize() <= 0 ) return; if ( layout && pageBreakFor > 0 && pageBreakFor != yt ) { layout->invalidate(); int h = layout->heightForWidth( width-2*outerborder ); layout->setGeometry( QRect(0, 0, width-2*outerborder, h) ); height = layout->geometry().height()+2*outerborder; } pageBreakFor = yt; QPtrListIterator<QTextTableCell> it( cells ); QTextTableCell* cell; while ( ( cell = it.current() ) ) { ++it; int y = yt + outerborder + cell->geometry().y(); int shift = flow->adjustFlow( y - cellspacing, width, cell->richText()->height() + 2*cellspacing ); adjustCells( y - outerborder - yt, shift ); } } void QTextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ) { if ( placement() != PlaceInline ) { x = xpos; y = ypos; } for (QTextTableCell* cell = cells.first(); cell; cell = cells.next() ) { if ( cx < 0 && cy < 0 || QRect( cx, cy, cw, ch ).intersects( QRect( x + outerborder + cell->geometry().x(), y + outerborder + cell->geometry().y(), cell->geometry().width(), cell->geometry().height() ) ) ) { cell->draw( p, x+outerborder, y+outerborder, cx, cy, cw, ch, cg, selected ); if ( border ) { QRect r( x+outerborder+cell->geometry().x() - innerborder, y+outerborder+cell->geometry().y() - innerborder, cell->geometry().width() + 2 * innerborder, cell->geometry().height() + 2 * innerborder ); if ( is_printer( p ) ) { QPen oldPen = p->pen(); QRect r2 = r; r2.setLeft( r2.left() + innerborder/2 ); r2.setTop( r2.top() + innerborder/2 ); r2.setRight( r2.right() - innerborder/2 ); r2.setBottom( r2.bottom() - innerborder/2 ); p->setPen( QPen( cg.text(), innerborder ) ); p->drawRect( r2 ); p->setPen( oldPen ); } else { int s = QMAX( cellspacing-2*innerborder, 0); if ( s ) { p->fillRect( r.left()-s, r.top(), s+1, r.height(), cg.button() ); p->fillRect( r.right(), r.top(), s+1, r.height(), cg.button() ); p->fillRect( r.left()-s, r.top()-s, r.width()+2*s, s, cg.button() ); p->fillRect( r.left()-s, r.bottom(), r.width()+2*s, s, cg.button() ); } qDrawShadePanel( p, r, cg, TRUE, innerborder ); } } } } if ( border ) { QRect r ( x, y, width, height ); if ( is_printer( p ) ) { QRect r2 = r; r2.setLeft( r2.left() + border/2 ); r2.setTop( r2.top() + border/2 ); r2.setRight( r2.right() - border/2 ); r2.setBottom( r2.bottom() - border/2 ); QPen oldPen = p->pen(); p->setPen( QPen( cg.text(), border ) ); p->drawRect( r2 ); p->setPen( oldPen ); } else { int s = border+QMAX( cellspacing-2*innerborder, 0); if ( s ) { p->fillRect( r.left(), r.top(), s, r.height(), cg.button() ); p->fillRect( r.right()-s, r.top(), s, r.height(), cg.button() ); p->fillRect( r.left(), r.top(), r.width(), s, cg.button() ); p->fillRect( r.left(), r.bottom()-s, r.width(), s, cg.button() ); } qDrawShadePanel( p, r, cg, FALSE, border ); } } -#if defined(DEBUG_TABLE_RENDERING) - p->save(); - p->setPen( Qt::red ); - p->drawRect( x, y, width, height ); - p->restore(); -#endif } int QTextTable::minimumWidth() const { return (layout ? layout->minimumSize().width() : 0) + 2 * outerborder; } void QTextTable::resize( int nwidth ) { if ( fixwidth && cachewidth != 0 ) return; if ( nwidth == cachewidth ) return; cachewidth = nwidth; int w = nwidth; format( w ); if ( stretch ) nwidth = nwidth * stretch / 100; width = nwidth; layout->invalidate(); int shw = layout->sizeHint().width() + 2*outerborder; int mw = layout->minimumSize().width() + 2*outerborder; if ( stretch ) width = QMAX( mw, nwidth ); else width = QMAX( mw, QMIN( nwidth, shw ) ); if ( fixwidth ) width = fixwidth; layout->invalidate(); mw = layout->minimumSize().width() + 2*outerborder; width = QMAX( width, mw ); int h = layout->heightForWidth( width-2*outerborder ); layout->setGeometry( QRect(0, 0, width-2*outerborder, h) ); height = layout->geometry().height()+2*outerborder; } void QTextTable::format( int w ) { for ( int i = 0; i < (int)cells.count(); ++i ) { QTextTableCell *cell = cells.at( i ); QRect r = cell->geometry(); r.setWidth( w - 2*outerborder ); cell->setGeometry( r ); } } void QTextTable::addCell( QTextTableCell* cell ) { cells.append( cell ); layout->addMultiCell( cell, cell->row(), cell->row() + cell->rowspan()-1, cell->column(), cell->column() + cell->colspan()-1 ); } -bool QTextTable::enter( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, bool atEnd ) +bool QTextTable::enter( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, bool atEnd ) { currCell.remove( c ); if ( !atEnd ) return next( c, doc, parag, idx, ox, oy ); currCell.insert( c, cells.count() ); return prev( c, doc, parag, idx, ox, oy ); } -bool QTextTable::enterAt( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, const QPoint &pos ) +bool QTextTable::enterAt( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, const QPoint &pos ) { currCell.remove( c ); int lastCell = -1; int lastY = -1; int i; for ( i = 0; i < (int)cells.count(); ++i ) { QTextTableCell *cell = cells.at( i ); if ( !cell ) continue; QRect r( cell->geometry().x(), cell->geometry().y(), cell->geometry().width() + 2 * innerborder + 2 * outerborder, cell->geometry().height() + 2 * innerborder + 2 * outerborder ); if ( r.left() <= pos.x() && r.right() >= pos.x() ) { if ( cell->geometry().y() > lastY ) { lastCell = i; lastY = cell->geometry().y(); } if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) { currCell.insert( c, i ); break; } } } if ( i == (int) cells.count() ) return FALSE; // no cell found if ( currCell.find( c ) == currCell.end() ) { if ( lastY != -1 ) currCell.insert( c, lastCell ); else return FALSE; } QTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); - parag = doc->firstParag(); + parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } -bool QTextTable::next( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextTable::next( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ) { int cc = -1; if ( currCell.find( c ) != currCell.end() ) cc = *currCell.find( c ); if ( cc > (int)cells.count() - 1 || cc < 0 ) cc = -1; currCell.remove( c ); currCell.insert( c, ++cc ); if ( cc >= (int)cells.count() ) { currCell.insert( c, 0 ); QTextCustomItem::next( c, doc, parag, idx, ox, oy ); QTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } if ( currCell.find( c ) == currCell.end() ) return FALSE; QTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); - parag = doc->firstParag(); + parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } -bool QTextTable::prev( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextTable::prev( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ) { int cc = -1; if ( currCell.find( c ) != currCell.end() ) cc = *currCell.find( c ); if ( cc > (int)cells.count() - 1 || cc < 0 ) cc = cells.count(); currCell.remove( c ); currCell.insert( c, --cc ); if ( cc < 0 ) { currCell.insert( c, 0 ); QTextCustomItem::prev( c, doc, parag, idx, ox, oy ); QTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } if ( currCell.find( c ) == currCell.end() ) return FALSE; QTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( !cell ) return FALSE; doc = cell->richText(); - parag = doc->firstParag(); + parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } -bool QTextTable::down( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextTable::down( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ) { if ( currCell.find( c ) == currCell.end() ) return FALSE; QTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( cell->row_ == layout->numRows() - 1 ) { currCell.insert( c, 0 ); QTextCustomItem::down( c, doc, parag, idx, ox, oy ); QTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } int oldRow = cell->row_; int oldCol = cell->col_; if ( currCell.find( c ) == currCell.end() ) return FALSE; int cc = *currCell.find( c ); for ( int i = cc; i < (int)cells.count(); ++i ) { cell = cells.at( i ); if ( cell->row_ > oldRow && cell->col_ == oldCol ) { currCell.insert( c, i ); break; } } doc = cell->richText(); if ( !cell ) return FALSE; - parag = doc->firstParag(); + parag = doc->firstParagraph(); idx = 0; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } -bool QTextTable::up( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextTable::up( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ) { if ( currCell.find( c ) == currCell.end() ) return FALSE; QTextTableCell *cell = cells.at( *currCell.find( c ) ); if ( cell->row_ == 0 ) { currCell.insert( c, 0 ); QTextCustomItem::up( c, doc, parag, idx, ox, oy ); QTextTableCell *cell = cells.first(); if ( !cell ) return FALSE; doc = cell->richText(); idx = -1; return TRUE; } int oldRow = cell->row_; int oldCol = cell->col_; if ( currCell.find( c ) == currCell.end() ) return FALSE; int cc = *currCell.find( c ); for ( int i = cc; i >= 0; --i ) { cell = cells.at( i ); if ( cell->row_ < oldRow && cell->col_ == oldCol ) { currCell.insert( c, i ); break; } } doc = cell->richText(); if ( !cell ) return FALSE; - parag = doc->lastParag(); + parag = doc->lastParagraph(); idx = parag->length() - 1; ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x(); oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder; return TRUE; } QTextTableCell::QTextTableCell( QTextTable* table, int row, int column, const QMap<QString, QString> &attr, const QStyleSheetItem* /*style*/, // ### use them const QTextFormat& /*fmt*/, const QString& context, QMimeSourceFactory &factory, QStyleSheet *sheet, const QString& doc) { -#if defined(PARSER_DEBUG) - qDebug( debug_indent + "new QTextTableCell1 (pappi: %p)", table ); - qDebug( debug_indent + doc ); -#endif cached_width = -1; cached_sizehint = -1; maxw = QWIDGETSIZE_MAX; minw = 0; parent = table; row_ = row; col_ = column; stretch_ = 0; richtext = new QTextDocument( table->parent ); richtext->setTableCell( this ); QString a = *attr.find( "align" ); if ( !a.isEmpty() ) { a = a.lower(); if ( a == "left" ) richtext->setAlignment( Qt::AlignLeft ); else if ( a == "center" ) richtext->setAlignment( Qt::AlignHCenter ); else if ( a == "right" ) richtext->setAlignment( Qt::AlignRight ); } align = 0; QString va = *attr.find( "valign" ); if ( !va.isEmpty() ) { va = va.lower(); if ( va == "center" ) align |= Qt::AlignVCenter; else if ( va == "bottom" ) align |= Qt::AlignBottom; } richtext->setFormatter( table->parent->formatter() ); richtext->setUseFormatCollection( table->parent->useFormatCollection() ); richtext->setMimeSourceFactory( &factory ); richtext->setStyleSheet( sheet ); - richtext->setDefaultFont( table->parent->formatCollection()->defaultFormat()->font() ); + richtext->setDefaultFormat( table->parent->formatCollection()->defaultFormat()->font(), + table->parent->formatCollection()->defaultFormat()->color() ); richtext->setRichText( doc, context ); rowspan_ = 1; colspan_ = 1; if ( attr.contains("colspan") ) colspan_ = attr["colspan"].toInt(); if ( attr.contains("rowspan") ) rowspan_ = attr["rowspan"].toInt(); background = 0; if ( attr.contains("bgcolor") ) { background = new QBrush(QColor( attr["bgcolor"] )); } hasFixedWidth = FALSE; if ( attr.contains("width") ) { bool b; QString s( attr["width"] ); int w = s.toInt( &b ); if ( b ) { maxw = w; minw = maxw; hasFixedWidth = TRUE; } else { s = s.stripWhiteSpace(); if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' ) stretch_ = s.left( s.length()-1).toInt(); } } attributes = attr; parent->addCell( this ); } -QTextTableCell::QTextTableCell( QTextTable* table, int row, int column ) -{ -#if defined(PARSER_DEBUG) - qDebug( debug_indent + "new QTextTableCell2( pappi: %p", table ); -#endif - maxw = QWIDGETSIZE_MAX; - minw = 0; - cached_width = -1; - cached_sizehint = -1; - - parent = table; - row_ = row; - col_ = column; - stretch_ = 0; - richtext = new QTextDocument( table->parent ); - richtext->setTableCell( this ); - richtext->setFormatter( table->parent->formatter() ); - richtext->setUseFormatCollection( table->parent->useFormatCollection() ); - richtext->setDefaultFont( table->parent->formatCollection()->defaultFormat()->font() ); - richtext->setRichText( "<html></html>", QString::null ); - rowspan_ = 1; - colspan_ = 1; - background = 0; - hasFixedWidth = FALSE; - parent->addCell( this ); -} - - QTextTableCell::~QTextTableCell() { delete background; background = 0; delete richtext; richtext = 0; } QSize QTextTableCell::sizeHint() const { int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance); int used = richtext->widthUsed() + extra; if (stretch_ ) { int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding; return QSize( QMIN( w, maxw ), 0 ).expandedTo( minimumSize() ); } return QSize( used, 0 ).expandedTo( minimumSize() ); } QSize QTextTableCell::minimumSize() const { int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance); return QSize( QMAX( richtext->minimumWidth() + extra, minw), 0 ); } QSize QTextTableCell::maximumSize() const { return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); } QSizePolicy::ExpandData QTextTableCell::expanding() const { return QSizePolicy::BothDirections; } bool QTextTableCell::isEmpty() const { return FALSE; } void QTextTableCell::setGeometry( const QRect& r ) { int extra = 2 * ( parent->innerborder + parent->cellpadding ); if ( r.width() != cached_width ) richtext->doLayout( QTextFormat::painter(), r.width() - extra ); cached_width = r.width(); geom = r; } QRect QTextTableCell::geometry() const { return geom; } bool QTextTableCell::hasHeightForWidth() const { return TRUE; } int QTextTableCell::heightForWidth( int w ) const { int extra = 2 * ( parent->innerborder + parent->cellpadding ); w = QMAX( minw, w ); if ( cached_width != w ) { QTextTableCell* that = (QTextTableCell*) this; that->richtext->doLayout( QTextFormat::painter(), w - extra ); that->cached_width = w; } return richtext->height() + extra; } void QTextTableCell::adjustToPainter( QPainter* p ) { - QTextParag *parag = richtext->firstParag(); + QTextParagraph *parag = richtext->firstParagraph(); while ( parag ) { parag->adjustToPainter( p ); parag = parag->next(); } } int QTextTableCell::horizontalAlignmentOffset() const { return parent->cellpadding; } int QTextTableCell::verticalAlignmentOffset() const { if ( (align & Qt::AlignVCenter ) == Qt::AlignVCenter ) return ( geom.height() - richtext->height() ) / 2; else if ( ( align & Qt::AlignBottom ) == Qt::AlignBottom ) return geom.height() - parent->cellpadding - richtext->height() ; return parent->cellpadding; } void QTextTableCell::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool ) { if ( cached_width != geom.width() ) { int extra = 2 * ( parent->innerborder + parent->cellpadding ); richtext->doLayout( p, geom.width() - extra ); cached_width = geom.width(); } QColorGroup g( cg ); if ( background ) g.setBrush( QColorGroup::Base, *background ); else if ( richtext->paper() ) g.setBrush( QColorGroup::Base, *richtext->paper() ); p->save(); p->translate( x + geom.x(), y + geom.y() ); if ( background ) p->fillRect( 0, 0, geom.width(), geom.height(), *background ); else if ( richtext->paper() ) p->fillRect( 0, 0, geom.width(), geom.height(), *richtext->paper() ); p->translate( horizontalAlignmentOffset(), verticalAlignmentOffset() ); QRegion r; - QTextCursor *c = 0; - if ( richtext->parent()->tmpCursor ) - c = richtext->parent()->tmpCursor; if ( cx >= 0 && cy >= 0 ) richtext->draw( p, cx - ( x + horizontalAlignmentOffset() + geom.x() ), cy - ( y + geom.y() + verticalAlignmentOffset() ), - cw, ch, g, FALSE, (c != 0), c ); + cw, ch, g, FALSE, FALSE, 0 ); else - richtext->draw( p, -1, -1, -1, -1, g, FALSE, (c != 0), c ); + richtext->draw( p, -1, -1, -1, -1, g, FALSE, FALSE, 0 ); p->restore(); } + +QString QTextDocument::section( QString str, const QString &sep, int start, int end ) +{ + const QChar *uc = str.unicode(); + if ( !uc ) + return QString(); + QString _sep = sep; + const QChar *uc_sep = _sep.unicode(); + if(!uc_sep) + return QString(); + bool match = FALSE, last_match = TRUE; + + //find start + int n = str.length(), sep_len = _sep.length(); + const QChar *begin = start < 0 ? uc + n : uc; + while(start) { + match = FALSE; + int c = 0; + for(const QChar *tmp = start < 0 ? begin - sep_len : begin; + c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) { + if( *tmp != *(uc_sep + c) ) + break; + if(c == sep_len - 1) { + match = TRUE; + break; + } + } + last_match = match; + + if(start < 0) { + if(match) { + begin -= sep_len; + if(!++start) + break; + } else { + if(start == -1 && begin == uc) + break; + begin--; + } + } else { + if(match) { + if(!--start) + break; + begin += sep_len; + } else { + if(start == 1 && begin == uc + n) + break; + begin++; + } + } + if(begin > uc + n || begin < uc) + return QString(); + } + if(match) + begin+=sep_len; + if(begin > uc + n || begin < uc) + return QString(); + + //now find last + match = FALSE; + const QChar *last = end < 0 ? uc + n : uc; + if(end == -1) { + int c = 0; + for(const QChar *tmp = end < 0 ? last - sep_len : last; + c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) { + if( *tmp != *(uc_sep + c) ) + break; + if(c == sep_len - 1) { + match = TRUE; + break; + } + } + } else { + end++; + last_match = TRUE; + while(end) { + match = FALSE; + int c = 0; + for(const QChar *tmp = end < 0 ? last - sep_len : last; + c < sep_len && tmp < uc + n && tmp >= uc; tmp++, c++) { + if( *tmp != *(uc_sep + c) ) + break; + if(c == sep_len - 1) { + match = TRUE; + break; + } + } + last_match = match; + + if(end < 0) { + if(match) { + if(!++end) + break; + last -= sep_len; + } else { + last--; + } + } else { + if(match) { + last += sep_len; + if(!--end) + break; + } else { + last++; + } + } + if(last >= uc + n) { + last = uc + n; + break; + } else if(last < uc) { + return QString(); + } + } + } + if(match) + last -= sep_len; + if(last < uc || last > uc + n || begin >= last) + return QString(); + + //done + return QString(begin, last - begin); +} + +bool QTextDocument::endsWith( QString str, const QString &s) +{ + if ( str.isNull() ) + return s.isNull(); + int pos = str.length() - s.length(); + if ( pos < 0 ) + return FALSE; + for ( uint i = 0; i < s.length(); i++ ) { + if ( str.unicode()[pos+i] != s[(int)i] ) + return FALSE; + } + return TRUE; +} diff --git a/noncore/apps/opie-write/qrichtext_p.cpp b/noncore/apps/opie-write/qrichtext_p.cpp index fb20730..6783e0b 100644 --- a/noncore/apps/opie-write/qrichtext_p.cpp +++ b/noncore/apps/opie-write/qrichtext_p.cpp @@ -1,706 +1,603 @@ /**************************************************************************** ** $Id$ ** ** Implementation of the internal Qt classes dealing with rich text ** ** Created : 990101 ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "qrichtext_p.h" using namespace Qt3; QTextCommand::~QTextCommand() {} QTextCommand::Commands QTextCommand::type() const { return Invalid; } QTextCustomItem::~QTextCustomItem() {} void QTextCustomItem::adjustToPainter( QPainter* p){ if ( p ) width = 0; } QTextCustomItem::Placement QTextCustomItem::placement() const { return PlaceInline; } bool QTextCustomItem::ownLine() const { return FALSE; } void QTextCustomItem::resize( int nwidth ){ width = nwidth; } void QTextCustomItem::invalidate() {} bool QTextCustomItem::isNested() const { return FALSE; } int QTextCustomItem::minimumWidth() const { return 0; } QString QTextCustomItem::richText() const { return QString::null; } -bool QTextCustomItem::enter( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, bool atEnd ) +bool QTextCustomItem::enter( QTextCursor *, QTextDocument*&, QTextParagraph *&, int &, int &, int &, bool ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; Q_UNUSED( atEnd ) return TRUE; - + return TRUE; } -bool QTextCustomItem::enterAt( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, const QPoint & ) +bool QTextCustomItem::enterAt( QTextCursor *, QTextDocument *&, QTextParagraph *&, int &, int &, int &, const QPoint & ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE; + return TRUE; } -bool QTextCustomItem::next( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextCustomItem::next( QTextCursor *, QTextDocument *&, QTextParagraph *&, int &, int &, int & ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE; + return TRUE; } -bool QTextCustomItem::prev( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextCustomItem::prev( QTextCursor *, QTextDocument *&, QTextParagraph *&, int &, int &, int & ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE; + return TRUE; } -bool QTextCustomItem::down( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextCustomItem::down( QTextCursor *, QTextDocument *&, QTextParagraph *&, int &, int &, int & ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE; + return TRUE; } -bool QTextCustomItem::up( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ) +bool QTextCustomItem::up( QTextCursor *, QTextDocument *&, QTextParagraph *&, int &, int &, int & ) { - doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE; + return TRUE; } void QTextFlow::setPageSize( int ps ) { pagesize = ps; } bool QTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); } void QTextTableCell::invalidate() { cached_width = -1; cached_sizehint = -1; } void QTextTable::invalidate() { cachewidth = -1; } -QTextParagData::~QTextParagData() {} -void QTextParagData::join( QTextParagData * ) {} +QTextParagraphData::~QTextParagraphData() {} +void QTextParagraphData::join( QTextParagraphData * ) {} QTextFormatter::~QTextFormatter() {} void QTextFormatter::setWrapEnabled( bool b ) { wrapEnabled = b; } void QTextFormatter::setWrapAtColumn( int c ) { wrapColumn = c; } int QTextCursor::x() const { - QTextStringChar *c = string->at( idx ); + QTextStringChar *c = para->at( idx ); int curx = c->x; if ( !c->rightToLeft && c->c.isSpace() && idx > 0 && - ( string->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify ) - curx = string->at( idx - 1 )->x + string->string()->width( idx - 1 ); + !c->lineStart && + ( para->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify ) + curx = para->at( idx - 1 )->x + para->string()->width( idx - 1 ); if ( c->rightToLeft ) - curx += string->string()->width( idx ); + curx += para->string()->width( idx ); return curx; } int QTextCursor::y() const { int dummy, line; - string->lineStartOfChar( idx, &dummy, &line ); - return string->lineY( line ); + para->lineStartOfChar( idx, &dummy, &line ); + return para->lineY( line ); +} + +int QTextCursor::globalX() const { return totalOffsetX() + para->rect().x() + x(); } +int QTextCursor::globalY() const { return totalOffsetY() + para->rect().y() + y(); } + +QTextDocument *QTextCursor::document() const +{ + return para ? para->document() : 0; +} + +void QTextCursor::gotoPosition( QTextParagraph* p, int index ) +{ + if ( para && p != para ) { + while ( para->document() != p->document() && !indices.isEmpty() ) + pop(); + Q_ASSERT( indices.isEmpty() || para->document() == p->document() ); + } + para = p; + if ( index < 0 || index >= para->length() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QTextCursor::gotoParagraph Index: %d out of range", index ); +#endif + index = index < 0 ? 0 : para->length() - 1; + } + + tmpIndex = -1; + idx = index; } bool QTextDocument::hasSelection( int id, bool visible ) const { return ( selections.find( id ) != selections.end() && ( !visible || ( (QTextDocument*)this )->selectionStartCursor( id ) != ( (QTextDocument*)this )->selectionEndCursor( id ) ) ); } -void QTextDocument::setSelectionStart( int id, QTextCursor *cursor ) +void QTextDocument::setSelectionStart( int id, const QTextCursor &cursor ) { QTextDocumentSelection sel; - sel.startCursor = *cursor; - sel.endCursor = *cursor; + sel.startCursor = cursor; + sel.endCursor = cursor; sel.swapped = FALSE; selections[ id ] = sel; } -QTextParag *QTextDocument::paragAt( int i ) const +QTextParagraph *QTextDocument::paragAt( int i ) const { - QTextParag* p = curParag; + QTextParagraph* p = curParag; if ( !p || p->paragId() > i ) p = fParag; while ( p && p->paragId() != i ) p = p->next(); ((QTextDocument*)this)->curParag = p; return p; } QTextFormat::~QTextFormat() { } QTextFormat::QTextFormat() - : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ), - different( NoFlags ) + : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ) { ref = 0; usePixelSizes = FALSE; if ( stdSize == -1 ) { stdSize = qApp->font().pixelSize(); usePixelSizes = TRUE; } missp = FALSE; ha = AlignNormal; collection = 0; } QTextFormat::QTextFormat( const QStyleSheetItem *style ) - : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ), - different( NoFlags ) + : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ) { ref = 0; usePixelSizes = FALSE; if ( stdSize == -1 ) { stdSize = qApp->font().pixelSize(); usePixelSizes = TRUE; } - this->style = style->name(); missp = FALSE; ha = AlignNormal; collection = 0; fn = QFont( style->fontFamily(), style->fontSize(), style->fontWeight(), style->fontItalic() ); fn.setUnderline( style->fontUnderline() ); + fn.setStrikeOut( style->fontStrikeOut() ); col = style->color(); fm = QFontMetrics( fn ); leftBearing = fm.minLeftBearing(); rightBearing = fm.minRightBearing(); hei = fm.lineSpacing(); asc = fm.ascent() + (fm.leading()+1)/2; dsc = fm.descent(); missp = FALSE; ha = AlignNormal; memset( widths, 0, 256 ); generateKey(); addRef(); - updateStyleFlags(); } QTextFormat::QTextFormat( const QFont &f, const QColor &c, QTextFormatCollection *parent ) : fn( f ), col( c ), fm( QFontMetrics( f ) ), linkColor( TRUE ), - logicalFontSize( 3 ), stdSize( f.pointSize() ), different( NoFlags ) + logicalFontSize( 3 ), stdSize( f.pointSize() ) { ref = 0; usePixelSizes = FALSE; if ( stdSize == -1 ) { stdSize = f.pixelSize(); usePixelSizes = TRUE; } collection = parent; leftBearing = fm.minLeftBearing(); rightBearing = fm.minRightBearing(); hei = fm.lineSpacing(); asc = fm.ascent() + (fm.leading()+1)/2; dsc = fm.descent(); missp = FALSE; ha = AlignNormal; memset( widths, 0, 256 ); generateKey(); addRef(); - updateStyleFlags(); } QTextFormat::QTextFormat( const QTextFormat &f ) : fm( f.fm ) { ref = 0; collection = 0; fn = f.fn; col = f.col; leftBearing = f.leftBearing; rightBearing = f.rightBearing; memset( widths, 0, 256 ); hei = f.hei; asc = f.asc; dsc = f.dsc; stdSize = f.stdSize; usePixelSizes = f.usePixelSizes; logicalFontSize = f.logicalFontSize; missp = f.missp; ha = f.ha; k = f.k; linkColor = f.linkColor; - style = f.style; - different = f.different; addRef(); } QTextFormat& QTextFormat::operator=( const QTextFormat &f ) { ref = 0; collection = f.collection; fn = f.fn; col = f.col; fm = f.fm; leftBearing = f.leftBearing; rightBearing = f.rightBearing; memset( widths, 0, 256 ); hei = f.hei; asc = f.asc; dsc = f.dsc; stdSize = f.stdSize; usePixelSizes = f.usePixelSizes; logicalFontSize = f.logicalFontSize; missp = f.missp; ha = f.ha; k = f.k; linkColor = f.linkColor; - style = f.style; - different = f.different; addRef(); return *this; } void QTextFormat::update() { fm = QFontMetrics( fn ); leftBearing = fm.minLeftBearing(); rightBearing = fm.minRightBearing(); hei = fm.lineSpacing(); asc = fm.ascent() + (fm.leading()+1)/2; dsc = fm.descent(); memset( widths, 0, 256 ); generateKey(); - updateStyleFlags(); } QPainter* QTextFormat::pntr = 0; void QTextFormat::setPainter( QPainter *p ) { pntr = p; } QPainter* QTextFormat::painter() { return pntr; } int QTextFormat::minLeftBearing() const { if ( !pntr || !pntr->isActive() ) return leftBearing; pntr->setFont( fn ); return pntr->fontMetrics().minLeftBearing(); } int QTextFormat::minRightBearing() const { if ( !pntr || !pntr->isActive() ) return rightBearing; pntr->setFont( fn ); return pntr->fontMetrics().minRightBearing(); } int QTextFormat::height() const { if ( !pntr || !pntr->isActive() ) return hei; pntr->setFont( fn ); return pntr->fontMetrics().lineSpacing(); } int QTextFormat::ascent() const { if ( !pntr || !pntr->isActive() ) return asc; pntr->setFont( fn ); return pntr->fontMetrics().ascent() + (pntr->fontMetrics().leading()+1)/2; } int QTextFormat::descent() const { if ( !pntr || !pntr->isActive() ) return dsc; pntr->setFont( fn ); return pntr->fontMetrics().descent(); } int QTextFormat::leading() const { if ( !pntr || !pntr->isActive() ) return fm.leading(); pntr->setFont( fn ); return pntr->fontMetrics().leading(); } void QTextFormat::generateKey() { k = getKey( fn, col, isMisspelled(), vAlign() ); } QString QTextFormat::getKey( const QFont &fn, const QColor &col, bool misspelled, VerticalAlignment a ) { QString k = fn.key(); k += '/'; k += QString::number( (uint)col.rgb() ); k += '/'; k += QString::number( (int)misspelled ); k += '/'; k += QString::number( (int)a ); return k; } -void QTextFormat::updateStyle() -{ - if ( !collection || !collection->styleSheet() ) - return; - QStyleSheetItem *item = collection->styleSheet()->item( style ); - if ( !item ) - return; - if ( !( different & Color ) && item->color().isValid() ) - col = item->color(); - if ( !( different & Size ) && item->fontSize() != -1 ) - fn.setPointSize( item->fontSize() ); - if ( !( different & Family ) && !item->fontFamily().isEmpty() ) - fn.setFamily( item->fontFamily() ); - if ( !( different & Bold ) && item->fontWeight() != -1 ) - fn.setWeight( item->fontWeight() ); - if ( !( different & Italic ) && item->definesFontItalic() ) - fn.setItalic( item->fontItalic() ); - if ( !( different & Underline ) && item->definesFontUnderline() ) - fn.setUnderline( item->fontUnderline() ); - generateKey(); - update(); - -} - -void QTextFormat::updateStyleFlags() -{ - different = NoFlags; - if ( !collection || !collection->styleSheet() ) - return; - QStyleSheetItem *item = collection->styleSheet()->item( style ); - if ( !item ) - return; - if ( item->color() != col ) - different |= Color; - if ( item->fontSize() != fn.pointSize() ) - different |= Size; - if ( item->fontFamily() != fn.family() ) - different |= Family; - if ( item->fontItalic() != fn.italic() ) - different |= Italic; - if ( item->fontUnderline() != fn.underline() ) - different |= Underline; - if ( item->fontWeight() != fn.weight() ) - different |= Bold; -} - QString QTextString::toString( const QMemArray<QTextStringChar> &data ) { QString s; int l = data.size(); s.setUnicode( 0, l ); QTextStringChar *c = data.data(); QChar *uc = (QChar *)s.unicode(); while ( l-- ) { *uc = c->c; // ### workaround so that non-breaking whitespaces are drawn // properly, actually this should be fixed in QFont somewhere if ( *uc == (char)0xa0 ) *uc = 0x20; uc++; c++; } return s; } QString QTextString::toString() const { return toString( data ); } -void QTextParag::setSelection( int id, int start, int end ) +void QTextParagraph::setSelection( int id, int start, int end ) { - QMap<int, QTextParagSelection>::ConstIterator it = selections().find( id ); + QMap<int, QTextParagraphSelection>::ConstIterator it = selections().find( id ); if ( it != mSelections->end() ) { if ( start == ( *it ).start && end == ( *it ).end ) return; } - QTextParagSelection sel; + QTextParagraphSelection sel; sel.start = start; sel.end = end; (*mSelections)[ id ] = sel; setChanged( TRUE, TRUE ); } -void QTextParag::removeSelection( int id ) +void QTextParagraph::removeSelection( int id ) { if ( !hasSelection( id ) ) return; if ( mSelections ) mSelections->remove( id ); setChanged( TRUE, TRUE ); } -int QTextParag::selectionStart( int id ) const +int QTextParagraph::selectionStart( int id ) const { if ( !mSelections ) return -1; - QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id ); + QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->find( id ); if ( it == mSelections->end() ) return -1; return ( *it ).start; } -int QTextParag::selectionEnd( int id ) const +int QTextParagraph::selectionEnd( int id ) const { if ( !mSelections ) return -1; - QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id ); + QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->find( id ); if ( it == mSelections->end() ) return -1; return ( *it ).end; } -bool QTextParag::hasSelection( int id ) const +bool QTextParagraph::hasSelection( int id ) const { - if ( !mSelections ) - return FALSE; - QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id ); - if ( it == mSelections->end() ) - return FALSE; - return ( *it ).start != ( *it ).end || length() == 1; + return mSelections ? mSelections->contains( id ) : FALSE; } -bool QTextParag::fullSelected( int id ) const +bool QTextParagraph::fullSelected( int id ) const { if ( !mSelections ) return FALSE; - QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id ); + QMap<int, QTextParagraphSelection>::ConstIterator it = mSelections->find( id ); if ( it == mSelections->end() ) return FALSE; return ( *it ).start == 0 && ( *it ).end == str->length() - 1; } -int QTextParag::lineY( int l ) const +int QTextParagraph::lineY( int l ) const { if ( l > (int)lineStarts.count() - 1 ) { - qWarning( "QTextParag::lineY: line %d out of range!", l ); + qWarning( "QTextParagraph::lineY: line %d out of range!", l ); return 0; } if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while ( l-- > 0 ) ++it; return ( *it )->y; } -int QTextParag::lineBaseLine( int l ) const +int QTextParagraph::lineBaseLine( int l ) const { if ( l > (int)lineStarts.count() - 1 ) { - qWarning( "QTextParag::lineBaseLine: line %d out of range!", l ); + qWarning( "QTextParagraph::lineBaseLine: line %d out of range!", l ); return 10; } if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while ( l-- > 0 ) ++it; return ( *it )->baseLine; } -int QTextParag::lineHeight( int l ) const +int QTextParagraph::lineHeight( int l ) const { if ( l > (int)lineStarts.count() - 1 ) { - qWarning( "QTextParag::lineHeight: line %d out of range!", l ); + qWarning( "QTextParagraph::lineHeight: line %d out of range!", l ); return 15; } if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while ( l-- > 0 ) ++it; return ( *it )->h; } -void QTextParag::lineInfo( int l, int &y, int &h, int &bl ) const +void QTextParagraph::lineInfo( int l, int &y, int &h, int &bl ) const { if ( l > (int)lineStarts.count() - 1 ) { - qWarning( "QTextParag::lineInfo: line %d out of range!", l ); + qWarning( "QTextParagraph::lineInfo: line %d out of range!", l ); qDebug( "%d %d", (int)lineStarts.count() - 1, l ); y = 0; h = 15; bl = 10; return; } if ( !isValid() ) - ( (QTextParag*)this )->format(); + ( (QTextParagraph*)this )->format(); - QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin(); + QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin(); while ( l-- > 0 ) ++it; y = ( *it )->y; h = ( *it )->h; bl = ( *it )->baseLine; } -int QTextParag::alignment() const -{ - if ( align != -1 ) - return align; - QStyleSheetItem *item = style(); - if ( !item ) - return Qt3::AlignAuto; - if ( mStyleSheetItemsVec ) { - for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) { - item = (*mStyleSheetItemsVec)[ i ]; - if ( item->alignment() != QStyleSheetItem::Undefined ) - return item->alignment(); - } - } - return Qt3::AlignAuto; -} - -QPtrVector<QStyleSheetItem> QTextParag::styleSheetItems() const -{ - QPtrVector<QStyleSheetItem> vec; - if ( mStyleSheetItemsVec ) { - vec.resize( mStyleSheetItemsVec->size() ); - for ( int i = 0; i < (int)vec.size(); ++i ) - vec.insert( i, (*mStyleSheetItemsVec)[ i ] ); - } - return vec; -} -QStyleSheetItem *QTextParag::style() const +void QTextParagraph::setAlignment( int a ) { - if ( !mStyleSheetItemsVec || mStyleSheetItemsVec->size() == 0 ) - return 0; - return (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 1 ]; -} - -int QTextParag::numberOfSubParagraph() const -{ - if ( list_val != -1 ) - return list_val; - if ( numSubParag != -1 ) - return numSubParag; - int n = 0; - QTextParag *p = (QTextParag*)this; - while ( p && ( styleSheetItemsVec().size() >= p->styleSheetItemsVec().size() && - styleSheetItemsVec()[ (int)p->styleSheetItemsVec().size() - 1 ] == p->style() || - p->styleSheetItemsVec().size() >= styleSheetItemsVec().size() && - p->styleSheetItemsVec()[ (int)styleSheetItemsVec().size() - 1 ] == style() ) ) { - if ( p->style() == style() && listStyle() != p->listStyle() - && p->styleSheetItemsVec().size() == styleSheetItemsVec().size() ) - break; - if ( p->style()->displayMode() == QStyleSheetItem::DisplayListItem - && p->style() != style() || styleSheetItemsVec().size() == p->styleSheetItemsVec().size() ) - ++n; - p = p->prev(); - } - ( (QTextParag*)this )->numSubParag = n; - return n; -} - -void QTextParag::setFormat( QTextFormat *fm ) -{ - bool doUpdate = FALSE; - if (defFormat && (defFormat != formatCollection()->defaultFormat())) - doUpdate = TRUE; - defFormat = formatCollection()->format( fm ); - if ( !doUpdate ) + if ( a == (int)align ) return; - for ( int i = 0; i < length(); ++i ) { - if ( at( i )->format()->styleName() == defFormat->styleName() ) - at( i )->format()->updateStyle(); - } + align = a; + invalidate( 0 ); } -QTextFormatter *QTextParag::formatter() const +QTextFormatter *QTextParagraph::formatter() const { if ( hasdoc ) return document()->formatter(); if ( pseudoDocument()->pFormatter ) return pseudoDocument()->pFormatter; - return ( ( (QTextParag*)this )->pseudoDocument()->pFormatter = new QTextFormatterBreakWords ); + return ( ( (QTextParagraph*)this )->pseudoDocument()->pFormatter = new QTextFormatterBreakWords ); } -void QTextParag::setTabArray( int *a ) +void QTextParagraph::setTabArray( int *a ) { delete [] tArray; tArray = a; } -void QTextParag::setTabStops( int tw ) +void QTextParagraph::setTabStops( int tw ) { if ( hasdoc ) document()->setTabStops( tw ); else tabStopWidth = tw; } -QMap<int, QTextParagSelection> &QTextParag::selections() const +QMap<int, QTextParagraphSelection> &QTextParagraph::selections() const { if ( !mSelections ) - ((QTextParag *)this)->mSelections = new QMap<int, QTextParagSelection>; + ((QTextParagraph *)this)->mSelections = new QMap<int, QTextParagraphSelection>; return *mSelections; } -QPtrVector<QStyleSheetItem> &QTextParag::styleSheetItemsVec() const -{ - if ( !mStyleSheetItemsVec ) - ((QTextParag *)this)->mStyleSheetItemsVec = new QPtrVector<QStyleSheetItem>; - return *mStyleSheetItemsVec; -} -QPtrList<QTextCustomItem> &QTextParag::floatingItems() const +QPtrList<QTextCustomItem> &QTextParagraph::floatingItems() const { if ( !mFloatingItems ) - ((QTextParag *)this)->mFloatingItems = new QPtrList<QTextCustomItem>; + ((QTextParagraph *)this)->mFloatingItems = new QPtrList<QTextCustomItem>; return *mFloatingItems; } QTextStringChar::~QTextStringChar() { if ( format() ) format()->removeRef(); if ( type ) // not Regular delete d.custom; } -QTextParagPseudoDocument::QTextParagPseudoDocument():pFormatter(0),commandHistory(0), minw(0),wused(0){} -QTextParagPseudoDocument::~QTextParagPseudoDocument(){ delete pFormatter; delete commandHistory; } +QTextParagraphPseudoDocument::QTextParagraphPseudoDocument():pFormatter(0),commandHistory(0), minw(0),wused(0){} +QTextParagraphPseudoDocument::~QTextParagraphPseudoDocument(){ delete pFormatter; delete commandHistory; } diff --git a/noncore/apps/opie-write/qrichtext_p.h b/noncore/apps/opie-write/qrichtext_p.h index 94ce913..e368edb 100644 --- a/noncore/apps/opie-write/qrichtext_p.h +++ b/noncore/apps/opie-write/qrichtext_p.h @@ -1,2158 +1,2028 @@ /**************************************************************************** ** $Id$ ** ** Definition of internal rich text classes ** ** Created : 990124 ** ** Copyright (C) 1999-2000 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QRICHTEXT_P_H #define QRICHTEXT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience // of a number of Qt sources files. This header file may change from // version to version without notice, or even be removed. // // We mean it. // // #ifndef QT_H -#include "qt3namespace.h" #include "qstring.h" #include "qlist.h" #include "qrect.h" #include "qfontmetrics.h" #include "qintdict.h" #include "qmap.h" #include "qstringlist.h" #include "qfont.h" #include "qcolor.h" #include "qsize.h" #include "qvaluelist.h" #include "qvaluestack.h" #include "qobject.h" #include "qdict.h" #include "qtextstream.h" #include "qpixmap.h" #include "qstylesheet.h" #include "qvector.h" #include "qpainter.h" #include "qlayout.h" #include "qobject.h" #include "qcomplextext_p.h" #include "qapplication.h" #include <limits.h> #endif // QT_H -//#define DEBUG_COLLECTION - namespace Qt3 { class QTextDocument; class QTextString; class QTextPreProcessor; class QTextFormat; class QTextCursor; -class QTextParag; +class QTextParagraph; class QTextFormatter; class QTextIndent; class QTextFormatCollection; class QStyleSheetItem; class QTextCustomItem; class QTextFlow; struct QBidiContext; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextStringChar { friend class QTextString; public: // this is never called, initialize variables in QTextString::insert()!!! QTextStringChar() : lineStart( 0 ), type( Regular ), startOfRun( 0 ) {d.format=0;} ~QTextStringChar(); QChar c; enum Type { Regular=0, Custom=1, Anchor=2, CustomAnchor=3 }; uint lineStart : 1; uint rightToLeft : 1; uint hasCursor : 1; uint canBreak : 1; Type type : 2; uint startOfRun : 1; int x; int height() const; int ascent() const; int descent() const; bool isCustom() const { return (type & Custom) != 0; } QTextFormat *format() const; QTextCustomItem *customItem() const; void setFormat( QTextFormat *f ); void setCustomItem( QTextCustomItem *i ); - QTextStringChar *clone() const; struct CustomData { QTextFormat *format; QTextCustomItem *custom; QString anchorName; QString anchorHref; }; void loseCustomItem(); union { QTextFormat* format; CustomData* custom; } d; bool isAnchor() const { return ( type & Anchor) != 0; } QString anchorName() const; QString anchorHref() const; void setAnchor( const QString& name, const QString& href ); private: QTextStringChar &operator=( const QTextStringChar & ) { //abort(); return *this; } friend class QComplexText; - friend class QTextParag; + friend class QTextParagraph; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QMemArray<QTextStringChar>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMemArray<QTextStringChar>; // MOC_SKIP_END #endif class Q_EXPORT QTextString { public: QTextString(); QTextString( const QTextString &s ); virtual ~QTextString(); static QString toString( const QMemArray<QTextStringChar> &data ); QString toString() const; QTextStringChar &at( int i ) const; int length() const; int width( int idx ) const; void insert( int index, const QString &s, QTextFormat *f ); - void insert( int index, QTextStringChar *c ); + void insert( int index, const QChar *unicode, int len, QTextFormat *f ); + void insert( int index, QTextStringChar *c, bool doAddRefFormat = FALSE ); void truncate( int index ); void remove( int index, int len ); void clear(); void setFormat( int index, QTextFormat *f, bool useCollection ); void setBidi( bool b ) { bidi = b; } bool isBidi() const; bool isRightToLeft() const; QChar::Direction direction() const; void setDirection( QChar::Direction d ) { dir = d; bidiDirty = TRUE; } QMemArray<QTextStringChar> subString( int start = 0, int len = 0xFFFFFF ) const; QMemArray<QTextStringChar> rawData() const { return data; } void operator=( const QString &s ) { clear(); insert( 0, s, 0 ); } - void operator+=( const QString &s ); + void operator+=( const QString &s ) {insert( length(), s, 0 ); } void prepend( const QString &s ) { insert( 0, s, 0 ); } private: void checkBidi() const; QMemArray<QTextStringChar> data; uint bidiDirty : 1; uint bidi : 1; // true when the paragraph has right to left characters uint rightToLeft : 1; uint dir : 5; }; inline bool QTextString::isBidi() const { if ( bidiDirty ) checkBidi(); return bidi; } inline bool QTextString::isRightToLeft() const { if ( bidiDirty ) checkBidi(); return rightToLeft; } inline QChar::Direction QTextString::direction() const { return (QChar::Direction) dir; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QValueStack<int>; -template class Q_EXPORT QValueStack<QTextParag*>; -template class Q_EXPORT QValueStack<bool>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QValueStack<int>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QValueStack<QTextParagraph*>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QValueStack<bool>; // MOC_SKIP_END #endif class Q_EXPORT QTextCursor { public: - QTextCursor( QTextDocument *d ); - QTextCursor(); + QTextCursor( QTextDocument *d = 0 ); QTextCursor( const QTextCursor &c ); QTextCursor &operator=( const QTextCursor &c ); virtual ~QTextCursor() {} bool operator==( const QTextCursor &c ) const; bool operator!=( const QTextCursor &c ) const { return !(*this == c); } - QTextDocument *document() const { return doc; } - void setDocument( QTextDocument *d ); - - QTextParag *parag() const; + QTextParagraph *paragraph() const; + void setParagraph( QTextParagraph*p ) { gotoPosition(p, 0 ); } + QTextDocument *document() const; int index() const; - void setParag( QTextParag *s, bool restore = TRUE ); + void setIndex( int index ) { gotoPosition(paragraph(), index ); } + void gotoPosition( QTextParagraph* p, int index = 0); void gotoLeft(); void gotoRight(); void gotoNextLetter(); void gotoPreviousLetter(); void gotoUp(); void gotoDown(); void gotoLineEnd(); void gotoLineStart(); void gotoHome(); void gotoEnd(); void gotoPageUp( int visibleHeight ); void gotoPageDown( int visibleHeight ); void gotoNextWord(); void gotoPreviousWord(); void gotoWordLeft(); void gotoWordRight(); void insert( const QString &s, bool checkNewLine, QMemArray<QTextStringChar> *formatting = 0 ); - void splitAndInsertEmptyParag( bool ind = TRUE, bool updateIds = TRUE ); + void splitAndInsertEmptyParagraph( bool ind = TRUE, bool updateIds = TRUE ); bool remove(); - void killLine(); void indent(); bool atParagStart(); bool atParagEnd(); - void setIndex( int i, bool restore = TRUE ); - - void checkIndex(); + int x() const; // x in current paragraph + int y() const; // y in current paragraph - int offsetX() const { return ox; } - int offsetY() const { return oy; } + int globalX() const; + int globalY() const; - QTextParag *topParag() const { return parags.isEmpty() ? string : parags.first(); } - int totalOffsetX() const; - int totalOffsetY() const; + QTextParagraph *topParagraph() const { return paras.isEmpty() ? para : paras.first(); } + int offsetX() const { return ox; } // inner document offset + int offsetY() const { return oy; } // inner document offset + int totalOffsetX() const; // total document offset + int totalOffsetY() const; // total document offset - bool place( const QPoint &pos, QTextParag *s ) { return place( pos, s, FALSE ); } - bool place( const QPoint &pos, QTextParag *s, bool link ); + bool place( const QPoint &pos, QTextParagraph *s ) { return place( pos, s, FALSE ); } + bool place( const QPoint &pos, QTextParagraph *s, bool link ); void restoreState(); - int x() const; - int y() const; int nestedDepth() const { return (int)indices.count(); } //### size_t/int cast void oneUp() { if ( !indices.isEmpty() ) pop(); } void setValid( bool b ) { valid = b; } bool isValid() const { return valid; } private: enum Operation { EnterBegin, EnterEnd, Next, Prev, Up, Down }; void push(); void pop(); void processNesting( Operation op ); void invalidateNested(); void gotoIntoNested( const QPoint &globalPos ); - QTextParag *string; - QTextDocument *doc; + QTextParagraph *para; int idx, tmpIndex; int ox, oy; QValueStack<int> indices; - QValueStack<QTextParag*> parags; + QValueStack<QTextParagraph*> paras; QValueStack<int> xOffsets; QValueStack<int> yOffsets; - QValueStack<bool> nestedStack; - uint nested : 1; uint valid : 1; }; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextCommand { public: - enum Commands { Invalid, Insert, Delete, Format, Alignment, ParagType }; + enum Commands { Invalid, Insert, Delete, Format, Style }; QTextCommand( QTextDocument *d ) : doc( d ), cursor( d ) {} virtual ~QTextCommand(); virtual Commands type() const; virtual QTextCursor *execute( QTextCursor *c ) = 0; virtual QTextCursor *unexecute( QTextCursor *c ) = 0; protected: QTextDocument *doc; QTextCursor cursor; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QPtrList<QTextCommand>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QPtrList<QTextCommand>; // MOC_SKIP_END #endif class Q_EXPORT QTextCommandHistory { public: QTextCommandHistory( int s ) : current( -1 ), steps( s ) { history.setAutoDelete( TRUE ); } virtual ~QTextCommandHistory(); void clear() { history.clear(); current = -1; } void addCommand( QTextCommand *cmd ); QTextCursor *undo( QTextCursor *c ); QTextCursor *redo( QTextCursor *c ); bool isUndoAvailable(); bool isRedoAvailable(); void setUndoDepth( int d ) { steps = d; } int undoDepth() const { return steps; } int historySize() const { return history.count(); } int currentPosition() const { return current; } private: QPtrList<QTextCommand> history; int current, steps; }; inline QTextCommandHistory::~QTextCommandHistory() { clear(); } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextCustomItem { public: QTextCustomItem( QTextDocument *p ) : xpos(0), ypos(-1), width(-1), height(0), parent( p ) {} virtual ~QTextCustomItem(); virtual void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ) = 0; virtual void adjustToPainter( QPainter* ); enum Placement { PlaceInline = 0, PlaceLeft, PlaceRight }; virtual Placement placement() const; bool placeInline() { return placement() == PlaceInline; } virtual bool ownLine() const; virtual void resize( int nwidth ); virtual void invalidate(); virtual int ascent() const { return height; } virtual bool isNested() const; virtual int minimumWidth() const; virtual QString richText() const; int xpos; // used for floating items int ypos; // used for floating items int width; int height; QRect geometry() const { return QRect( xpos, ypos, width, height ); } - virtual bool enter( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, bool atEnd = FALSE ); - virtual bool enterAt( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, const QPoint & ); - virtual bool next( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool prev( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool down( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool up( QTextCursor *, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); + virtual bool enter( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, bool atEnd = FALSE ); + virtual bool enterAt( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, const QPoint & ); + virtual bool next( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool prev( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool down( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool up( QTextCursor *, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); - void setParagraph( QTextParag *p ) { parag = p; } - QTextParag *paragrapth() const { return parag; } + void setParagraph( QTextParagraph *p ) { parag = p; } + QTextParagraph *paragraph() const { return parag; } QTextDocument *parent; - QTextParag *parag; + QTextParagraph *parag; virtual void pageBreak( int y, QTextFlow* flow ); }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QMap<QString, QString>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<QString, QString>; // MOC_SKIP_END #endif class Q_EXPORT QTextImage : public QTextCustomItem { public: QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context, QMimeSourceFactory &factory ); virtual ~QTextImage(); Placement placement() const { return place; } void adjustToPainter( QPainter* ); int minimumWidth() const { return width; } QString richText() const; void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ); private: QRegion* reg; QPixmap pm; Placement place; int tmpwidth, tmpheight; QMap<QString, QString> attributes; QString imgId; }; class Q_EXPORT QTextHorizontalLine : public QTextCustomItem { public: QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context, QMimeSourceFactory &factory ); virtual ~QTextHorizontalLine(); void adjustToPainter( QPainter* ); void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ); QString richText() const; bool ownLine() const { return TRUE; } private: int tmpheight; QColor color; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QPtrList<QTextCustomItem>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QPtrList<QTextCustomItem>; // MOC_SKIP_END #endif class Q_EXPORT QTextFlow { friend class QTextDocument; friend class QTextTableCell; public: QTextFlow(); virtual ~QTextFlow(); virtual void setWidth( int width ); int width() const; virtual void setPageSize( int ps ); int pageSize() const { return pagesize; } virtual int adjustLMargin( int yp, int h, int margin, int space ); virtual int adjustRMargin( int yp, int h, int margin, int space ); virtual void registerFloatingItem( QTextCustomItem* item ); virtual void unregisterFloatingItem( QTextCustomItem* item ); virtual QRect boundingRect() const; virtual void drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ); virtual int adjustFlow( int y, int w, int h ); // adjusts y according to the defined pagesize. Returns the shift. virtual bool isEmpty(); void clear(); private: int w; int pagesize; QPtrList<QTextCustomItem> leftItems; QPtrList<QTextCustomItem> rightItems; }; inline int QTextFlow::width() const { return w; } class QTextTable; class Q_EXPORT QTextTableCell : public QLayoutItem { friend class QTextTable; public: QTextTableCell( QTextTable* table, int row, int column, const QMap<QString, QString> &attr, const QStyleSheetItem* style, const QTextFormat& fmt, const QString& context, QMimeSourceFactory &factory, QStyleSheet *sheet, const QString& doc ); - QTextTableCell( QTextTable* table, int row, int column ); virtual ~QTextTableCell(); QSize sizeHint() const ; QSize minimumSize() const ; QSize maximumSize() const ; QSizePolicy::ExpandData expanding() const; bool isEmpty() const; void setGeometry( const QRect& ) ; QRect geometry() const; bool hasHeightForWidth() const; int heightForWidth( int ) const; void adjustToPainter( QPainter* ); int row() const { return row_; } int column() const { return col_; } int rowspan() const { return rowspan_; } int colspan() const { return colspan_; } int stretch() const { return stretch_; } QTextDocument* richText() const { return richtext; } QTextTable* table() const { return parent; } void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ); QBrush *backGround() const { return background; } virtual void invalidate(); int verticalAlignmentOffset() const; int horizontalAlignmentOffset() const; private: QRect geom; QTextTable* parent; QTextDocument* richtext; int row_; int col_; int rowspan_; int colspan_; int stretch_; int maxw; int minw; bool hasFixedWidth; QBrush *background; int cached_width; int cached_sizehint; QMap<QString, QString> attributes; int align; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QPtrList<QTextTableCell>; -template class Q_EXPORT QMap<QTextCursor*, int>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QPtrList<QTextTableCell>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<QTextCursor*, int>; // MOC_SKIP_END #endif class Q_EXPORT QTextTable: public QTextCustomItem { friend class QTextTableCell; public: QTextTable( QTextDocument *p, const QMap<QString, QString> &attr ); virtual ~QTextTable(); void adjustToPainter( QPainter *p ); void pageBreak( int y, QTextFlow* flow ); void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ); bool noErase() const { return TRUE; } bool ownLine() const { return TRUE; } Placement placement() const { return place; } bool isNested() const { return TRUE; } void resize( int nwidth ); virtual void invalidate(); - virtual bool enter( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, bool atEnd = FALSE ); - virtual bool enterAt( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy, const QPoint &pos ); - virtual bool next( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool prev( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool down( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); - virtual bool up( QTextCursor *c, QTextDocument *&doc, QTextParag *¶g, int &idx, int &ox, int &oy ); + virtual bool enter( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, bool atEnd = FALSE ); + virtual bool enterAt( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy, const QPoint &pos ); + virtual bool next( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool prev( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool down( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); + virtual bool up( QTextCursor *c, QTextDocument *&doc, QTextParagraph *¶g, int &idx, int &ox, int &oy ); QString richText() const; int minimumWidth() const; QPtrList<QTextTableCell> tableCells() const { return cells; } bool isStretching() const { return stretch; } private: void format( int w ); void addCell( QTextTableCell* cell ); private: QGridLayout* layout; QPtrList<QTextTableCell> cells; int cachewidth; int fixwidth; int cellpadding; int cellspacing; int border; int outerborder; int stretch; int innerborder; int us_cp, us_ib, us_b, us_ob, us_cs; QMap<QString, QString> attributes; QMap<QTextCursor*, int> currCell; Placement place; void adjustCells( int y , int shift ); int pageBreakFor; }; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class QTextTableCell; -class QTextParag; +class QTextParagraph; struct Q_EXPORT QTextDocumentSelection { QTextCursor startCursor, endCursor; bool swapped; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QMap<int, QColor>; -template class Q_EXPORT QMap<int, bool>; -template class Q_EXPORT QMap<int, QTextDocumentSelection>; -template class Q_EXPORT QPtrList<QTextDocument>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<int, QColor>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<int, bool>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<int, QTextDocumentSelection>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QPtrList<QTextDocument>; // MOC_SKIP_END #endif class Q_EXPORT QTextDocument : public QObject { Q_OBJECT friend class QTextTableCell; friend class QTextCursor; friend class QTextEdit; - friend class QTextParag; + friend class QTextParagraph; public: enum SelectionIds { Standard = 0, Temp = 32000 // This selection must not be drawn, it's used e.g. by undo/redo to // remove multiple lines with removeSelectedText() }; QTextDocument( QTextDocument *p ); QTextDocument( QTextDocument *d, QTextFormatCollection *f ); virtual ~QTextDocument(); QTextDocument *parent() const { return par; } - QTextParag *parentParag() const { return parParag; } + QTextParagraph *parentParagraph() const { return parentPar; } void setText( const QString &text, const QString &context ); QMap<QString, QString> attributes() const { return attribs; } void setAttributes( const QMap<QString, QString> &attr ) { attribs = attr; } QString text() const; QString text( int parag ) const; QString originalText() const; int x() const; int y() const; int width() const; int widthUsed() const; int visibleWidth() const; int height() const; void setWidth( int w ); int minimumWidth() const; - bool setMinimumWidth( int needed, int used = -1, QTextParag *parag = 0 ); + bool setMinimumWidth( int needed, int used = -1, QTextParagraph *parag = 0 ); void setY( int y ); int leftMargin() const; void setLeftMargin( int lm ); int rightMargin() const; void setRightMargin( int rm ); - QTextParag *firstParag() const; - QTextParag *lastParag() const; - void setFirstParag( QTextParag *p ); - void setLastParag( QTextParag *p ); + QTextParagraph *firstParagraph() const; + QTextParagraph *lastParagraph() const; + void setFirstParagraph( QTextParagraph *p ); + void setLastParagraph( QTextParagraph *p ); void invalidate(); void setPreProcessor( QTextPreProcessor *sh ); QTextPreProcessor *preProcessor() const; void setFormatter( QTextFormatter *f ); QTextFormatter *formatter() const; void setIndent( QTextIndent *i ); QTextIndent *indent() const; QColor selectionColor( int id ) const; bool invertSelectionText( int id ) const; void setSelectionColor( int id, const QColor &c ); void setInvertSelectionText( int id, bool b ); bool hasSelection( int id, bool visible = FALSE ) const; - void setSelectionStart( int id, QTextCursor *cursor ); - bool setSelectionEnd( int id, QTextCursor *cursor ); + void setSelectionStart( int id, const QTextCursor &cursor ); + bool setSelectionEnd( int id, const QTextCursor &cursor ); void selectAll( int id ); bool removeSelection( int id ); void selectionStart( int id, int ¶gId, int &index ); QTextCursor selectionStartCursor( int id ); QTextCursor selectionEndCursor( int id ); void selectionEnd( int id, int ¶gId, int &index ); void setFormat( int id, QTextFormat *f, int flags ); - QTextParag *selectionStart( int id ); - QTextParag *selectionEnd( int id ); int numSelections() const { return nSelections; } void addSelection( int id ); - QString selectedText( int id, bool withCustom = TRUE ) const; - void copySelectedText( int id ); + QString selectedText( int id, bool asRichText = FALSE ) const; void removeSelectedText( int id, QTextCursor *cursor ); void indentSelection( int id ); - QTextParag *paragAt( int i ) const; + QTextParagraph *paragAt( int i ) const; void addCommand( QTextCommand *cmd ); QTextCursor *undo( QTextCursor *c = 0 ); QTextCursor *redo( QTextCursor *c = 0 ); QTextCommandHistory *commands() const { return commandHistory; } QTextFormatCollection *formatCollection() const; - bool find( const QString &expr, bool cs, bool wo, bool forward, int *parag, int *index, QTextCursor *cursor ); + bool find( QTextCursor &cursor, const QString &expr, bool cs, bool wo, bool forward); void setTextFormat( Qt::TextFormat f ); Qt::TextFormat textFormat() const; bool inSelection( int selId, const QPoint &pos ) const; QStyleSheet *styleSheet() const { return sheet_; } QMimeSourceFactory *mimeSourceFactory() const { return factory_; } QString context() const { return contxt; } void setStyleSheet( QStyleSheet *s ); - void updateStyles(); - void updateFontSizes( int base, bool usePixels ); - void updateFontAttributes( const QFont &f, const QFont &old ); + void setDefaultFormat( const QFont &font, const QColor &color ); void setMimeSourceFactory( QMimeSourceFactory *f ) { if ( f ) factory_ = f; } void setContext( const QString &c ) { if ( !c.isEmpty() ) contxt = c; } - void setUnderlineLinks( bool b ) { underlLinks = b; } + void setUnderlineLinks( bool b ); bool underlineLinks() const { return underlLinks; } void setPaper( QBrush *brush ) { if ( backBrush ) delete backBrush; backBrush = brush; } QBrush *paper() const { return backBrush; } void doLayout( QPainter *p, int w ); void draw( QPainter *p, const QRect& rect, const QColorGroup &cg, const QBrush *paper = 0 ); - void drawParag( QPainter *p, QTextParag *parag, int cx, int cy, int cw, int ch, + void drawParagraph( QPainter *p, QTextParagraph *parag, int cx, int cy, int cw, int ch, QPixmap *&doubleBuffer, const QColorGroup &cg, bool drawCursor, QTextCursor *cursor, bool resetChanged = TRUE ); - QTextParag *draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg, + QTextParagraph *draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg, bool onlyChanged = FALSE, bool drawCursor = FALSE, QTextCursor *cursor = 0, bool resetChanged = TRUE ); - void setDefaultFont( const QFont &f ); - - void registerCustomItem( QTextCustomItem *i, QTextParag *p ); - void unregisterCustomItem( QTextCustomItem *i, QTextParag *p ); + void registerCustomItem( QTextCustomItem *i, QTextParagraph *p ); + void unregisterCustomItem( QTextCustomItem *i, QTextParagraph *p ); void setFlow( QTextFlow *f ); void takeFlow(); QTextFlow *flow() const { return flow_; } bool isPageBreakEnabled() const { return pages; } void setPageBreakEnabled( bool b ) { pages = b; } void setUseFormatCollection( bool b ) { useFC = b; } bool useFormatCollection() const { return useFC; } QTextTableCell *tableCell() const { return tc; } void setTableCell( QTextTableCell *c ) { tc = c; } void setPlainText( const QString &text ); void setRichText( const QString &text, const QString &context ); - QString richText( QTextParag *p = 0 ) const; - QString plainText( QTextParag *p = 0 ) const; + QString richText() const; + QString plainText() const; bool focusNextPrevChild( bool next ); int alignment() const; void setAlignment( int a ); int *tabArray() const; int tabStopWidth() const; void setTabArray( int *a ); void setTabStops( int tw ); void setUndoDepth( int d ) { commandHistory->setUndoDepth( d ); } int undoDepth() const { return commandHistory->undoDepth(); } int length() const; void clear( bool createEmptyParag = FALSE ); - virtual QTextParag *createParag( QTextDocument *d, QTextParag *pr = 0, QTextParag *nx = 0, bool updateIds = TRUE ); + virtual QTextParagraph *createParagraph( QTextDocument *d, QTextParagraph *pr = 0, QTextParagraph *nx = 0, bool updateIds = TRUE ); void insertChild( QObject *o ) { QObject::insertChild( o ); } void removeChild( QObject *o ) { QObject::removeChild( o ); } void insertChild( QTextDocument *d ) { childList.append( d ); } void removeChild( QTextDocument *d ) { childList.removeRef( d ); } QPtrList<QTextDocument> children() const { return childList; } - void setAddMargins( bool b ) { addMargs = b; } - int addMargins() const { return addMargs; } - bool hasFocusParagraph() const; QString focusHref() const; void invalidateOriginalText() { oTextValid = FALSE; oText = ""; } + static QString section( QString str, const QString &sep, int start, int end = 0xffffffff ); + static bool endsWith( QString str, const QString &s); + signals: void minimumWidthChanged( int ); private: void init(); QPixmap *bufferPixmap( const QSize &s ); // HTML parser bool hasPrefix(const QChar* doc, int length, int pos, QChar c); bool hasPrefix(const QChar* doc, int length, int pos, const QString& s); QTextCustomItem* parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt, - const QChar* doc, int length, int& pos, QTextParag *curpar ); + const QChar* doc, int length, int& pos, QTextParagraph *curpar ); bool eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp = FALSE ); bool eat(const QChar* doc, int length, int& pos, QChar c); QString parseOpenTag(const QChar* doc, int length, int& pos, QMap<QString, QString> &attr, bool& emptyTag); QString parseCloseTag( const QChar* doc, int length, int& pos ); QChar parseHTMLSpecialChar(const QChar* doc, int length, int& pos); QString parseWord(const QChar* doc, int length, int& pos, bool lower = TRUE); QChar parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm ); - void setRichTextInternal( const QString &text ); + void setRichTextInternal( const QString &text, QTextCursor* cursor = 0 ); + void setRichTextMarginsInternal( QPtrList< QPtrVector<QStyleSheetItem> >& styles, QTextParagraph* stylesPar ); private: struct Q_EXPORT Focus { - QTextParag *parag; + QTextParagraph *parag; int start, len; QString href; }; int cx, cy, cw, vw; - QTextParag *fParag, *lParag; + QTextParagraph *fParag, *lParag; QTextPreProcessor *pProcessor; QMap<int, QColor> selectionColors; QMap<int, QTextDocumentSelection> selections; QMap<int, bool> selectionText; QTextCommandHistory *commandHistory; QTextFormatter *pFormatter; QTextIndent *indenter; QTextFormatCollection *fCollection; Qt::TextFormat txtFormat; uint preferRichText : 1; uint pages : 1; uint useFC : 1; uint withoutDoubleBuffer : 1; uint underlLinks : 1; uint nextDoubleBuffered : 1; - uint addMargs : 1; uint oTextValid : 1; uint mightHaveCustomItems : 1; int align; int nSelections; QTextFlow *flow_; QTextDocument *par; - QTextParag *parParag; + QTextParagraph *parentPar; QTextTableCell *tc; - QTextCursor *tmpCursor; QBrush *backBrush; QPixmap *buf_pixmap; Focus focusIndicator; int minw; int wused; int leftmargin; int rightmargin; - QTextParag *minwParag, *curParag; + QTextParagraph *minwParag, *curParag; QStyleSheet* sheet_; QMimeSourceFactory* factory_; QString contxt; QMap<QString, QString> attribs; int *tArray; int tStopWidth; int uDepth; QString oText; QPtrList<QTextDocument> childList; QColor linkColor; + double scaleFontsFactor; + short list_tm,list_bm, list_lm, li_tm, li_bm, par_tm, par_bm; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextDeleteCommand : public QTextCommand { public: QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str, - const QValueList< QPtrVector<QStyleSheetItem> > &os, - const QValueList<QStyleSheetItem::ListStyle> &ols, - const QMemArray<int> &oas ); - QTextDeleteCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str ); + const QByteArray& oldStyle ); + QTextDeleteCommand( QTextParagraph *p, int idx, const QMemArray<QTextStringChar> &str ); virtual ~QTextDeleteCommand(); Commands type() const { return Delete; } QTextCursor *execute( QTextCursor *c ); QTextCursor *unexecute( QTextCursor *c ); protected: int id, index; - QTextParag *parag; + QTextParagraph *parag; QMemArray<QTextStringChar> text; - QValueList< QPtrVector<QStyleSheetItem> > oldStyles; - QValueList<QStyleSheetItem::ListStyle> oldListStyles; - QMemArray<int> oldAligns; + QByteArray styleInformation; }; class Q_EXPORT QTextInsertCommand : public QTextDeleteCommand { public: QTextInsertCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str, - const QValueList< QPtrVector<QStyleSheetItem> > &os, - const QValueList<QStyleSheetItem::ListStyle> &ols, - const QMemArray<int> &oas ) - : QTextDeleteCommand( d, i, idx, str, os, ols, oas ) {} - QTextInsertCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str ) + const QByteArray& oldStyleInfo ) + : QTextDeleteCommand( d, i, idx, str, oldStyleInfo ) {} + QTextInsertCommand( QTextParagraph *p, int idx, const QMemArray<QTextStringChar> &str ) : QTextDeleteCommand( p, idx, str ) {} virtual ~QTextInsertCommand() {} Commands type() const { return Insert; } QTextCursor *execute( QTextCursor *c ) { return QTextDeleteCommand::unexecute( c ); } QTextCursor *unexecute( QTextCursor *c ) { return QTextDeleteCommand::execute( c ); } }; class Q_EXPORT QTextFormatCommand : public QTextCommand { public: QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx, const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl ); virtual ~QTextFormatCommand(); Commands type() const { return Format; } QTextCursor *execute( QTextCursor *c ); QTextCursor *unexecute( QTextCursor *c ); protected: int startId, startIndex, endId, endIndex; QTextFormat *format; QMemArray<QTextStringChar> oldFormats; int flags; }; -class Q_EXPORT QTextAlignmentCommand : public QTextCommand +class Q_EXPORT QTextStyleCommand : public QTextCommand { public: - QTextAlignmentCommand( QTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa ); - virtual ~QTextAlignmentCommand() {} + QTextStyleCommand( QTextDocument *d, int fParag, int lParag, const QByteArray& beforeChange ); + virtual ~QTextStyleCommand() {} - Commands type() const { return Alignment; } + Commands type() const { return Style; } QTextCursor *execute( QTextCursor *c ); QTextCursor *unexecute( QTextCursor *c ); -private: - int firstParag, lastParag; - int newAlign; - QMemArray<int> oldAligns; - -}; - -class Q_EXPORT QTextParagTypeCommand : public QTextCommand -{ -public: - QTextParagTypeCommand( QTextDocument *d, int fParag, int lParag, bool l, - QStyleSheetItem::ListStyle s, const QValueList< QPtrVector<QStyleSheetItem> > &os, - const QValueList<QStyleSheetItem::ListStyle> &ols ); - virtual ~QTextParagTypeCommand() {} - - Commands type() const { return ParagType; } - QTextCursor *execute( QTextCursor *c ); - QTextCursor *unexecute( QTextCursor *c ); + static QByteArray readStyleInformation( QTextDocument* d, int fParag, int lParag ); + static void writeStyleInformation( QTextDocument* d, int fParag, const QByteArray& style ); private: int firstParag, lastParag; - bool list; - QStyleSheetItem::ListStyle listStyle; - QValueList< QPtrVector<QStyleSheetItem> > oldStyles; - QValueList<QStyleSheetItem::ListStyle> oldListStyles; - + QByteArray before; + QByteArray after; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -struct Q_EXPORT QTextParagSelection +struct Q_EXPORT QTextParagraphSelection { int start, end; }; -struct Q_EXPORT QTextParagLineStart +struct Q_EXPORT QTextLineStart { - QTextParagLineStart() : y( 0 ), baseLine( 0 ), h( 0 ) + QTextLineStart() : y( 0 ), baseLine( 0 ), h( 0 ) #ifndef QT_NO_COMPLEXTEXT , bidicontext( 0 ) #endif { } - QTextParagLineStart( ushort y_, ushort bl, ushort h_ ) : y( y_ ), baseLine( bl ), h( h_ ), + QTextLineStart( ushort y_, ushort bl, ushort h_ ) : y( y_ ), baseLine( bl ), h( h_ ), w( 0 ) #ifndef QT_NO_COMPLEXTEXT , bidicontext( 0 ) #endif { } #ifndef QT_NO_COMPLEXTEXT - QTextParagLineStart( QBidiContext *c, QBidiStatus s ) : y(0), baseLine(0), h(0), + QTextLineStart( QBidiContext *c, QBidiStatus s ) : y(0), baseLine(0), h(0), status( s ), bidicontext( c ) { if ( bidicontext ) bidicontext->ref(); } #endif - virtual ~QTextParagLineStart() + virtual ~QTextLineStart() { #ifndef QT_NO_COMPLEXTEXT if ( bidicontext && bidicontext->deref() ) delete bidicontext; #endif } #ifndef QT_NO_COMPLEXTEXT void setContext( QBidiContext *c ) { if ( c == bidicontext ) return; if ( bidicontext && bidicontext->deref() ) delete bidicontext; bidicontext = c; if ( bidicontext ) bidicontext->ref(); } QBidiContext *context() const { return bidicontext; } #endif public: ushort y, baseLine, h; #ifndef QT_NO_COMPLEXTEXT QBidiStatus status; #endif int w; private: #ifndef QT_NO_COMPLEXTEXT QBidiContext *bidicontext; #endif }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QMap<int, QTextParagSelection>; -template class Q_EXPORT QMap<int, QTextParagLineStart*>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<int, QTextParagraphSelection>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QMap<int, QTextLineStart*>; // MOC_SKIP_END #endif -class Q_EXPORT QTextParagData +class Q_EXPORT QTextParagraphData { public: - QTextParagData() {} - virtual ~QTextParagData(); - virtual void join( QTextParagData * ); + QTextParagraphData() {} + virtual ~QTextParagraphData(); + virtual void join( QTextParagraphData * ); }; -class Q_EXPORT QTextParagPseudoDocument +class Q_EXPORT QTextParagraphPseudoDocument { public: - QTextParagPseudoDocument(); - ~QTextParagPseudoDocument(); + QTextParagraphPseudoDocument(); + ~QTextParagraphPseudoDocument(); QRect docRect; QTextFormatter *pFormatter; QTextCommandHistory *commandHistory; int minw; int wused; }; //nase -class Q_EXPORT QTextParag +class Q_EXPORT QTextParagraph { friend class QTextDocument; friend class QTextCursor; public: - QTextParag( QTextDocument *d, QTextParag *pr = 0, QTextParag *nx = 0, bool updateIds = TRUE ); - virtual ~QTextParag(); + QTextParagraph( QTextDocument *d, QTextParagraph *pr = 0, QTextParagraph *nx = 0, bool updateIds = TRUE ); + virtual ~QTextParagraph(); QTextString *string() const; QTextStringChar *at( int i ) const; // maybe remove later int leftGap() const; int length() const; // maybe remove later - void setListStyle( QStyleSheetItem::ListStyle ls ); - QStyleSheetItem::ListStyle listStyle() const; + void setListStyle( QStyleSheetItem::ListStyle ls ) { lstyle = ls; changed = TRUE; } + QStyleSheetItem::ListStyle listStyle() const { return lstyle; } + void setListItem( bool li ) { litem = li; changed = TRUE; } + bool isListItem() const { return litem; } void setListValue( int v ) { list_val = v; } - int listValue() const { return list_val; } + int listValue() const { return list_val > 0 ? list_val : -1; } - void setList( bool b, int listStyle ); - void incDepth(); - void decDepth(); - int listDepth() const; + void setListDepth( int depth ); + int listDepth() const { return ldepth; } - void setFormat( QTextFormat *fm ); - QTextFormat *paragFormat() const; +// void setFormat( QTextFormat *fm ); +// QTextFormat *paragFormat() const; QTextDocument *document() const; - QTextParagPseudoDocument *pseudoDocument() const; + QTextParagraphPseudoDocument *pseudoDocument() const; QRect rect() const; void setHeight( int h ) { r.setHeight( h ); } void show(); void hide(); bool isVisible() const { return visible; } - QTextParag *prev() const; - QTextParag *next() const; - void setPrev( QTextParag *s ); - void setNext( QTextParag *s ); + QTextParagraph *prev() const; + QTextParagraph *next() const; + void setPrev( QTextParagraph *s ); + void setNext( QTextParagraph *s ); void insert( int index, const QString &s ); + void insert( int index, const QChar *unicode, int len ); void append( const QString &s, bool reallyAtEnd = FALSE ); void truncate( int index ); void remove( int index, int len ); - void join( QTextParag *s ); + void join( QTextParagraph *s ); void invalidate( int chr ); void move( int &dy ); void format( int start = -1, bool doMove = TRUE ); bool isValid() const; bool hasChanged() const; void setChanged( bool b, bool recursive = FALSE ); int lineHeightOfChar( int i, int *bl = 0, int *y = 0 ) const; QTextStringChar *lineStartOfChar( int i, int *index = 0, int *line = 0 ) const; int lines() const; QTextStringChar *lineStartOfLine( int line, int *index = 0 ) const; int lineY( int l ) const; int lineBaseLine( int l ) const; int lineHeight( int l ) const; void lineInfo( int l, int &y, int &h, int &bl ) const; void setSelection( int id, int start, int end ); void removeSelection( int id ); int selectionStart( int id ) const; int selectionEnd( int id ) const; bool hasSelection( int id ) const; bool hasAnySelection() const; bool fullSelected( int id ) const; void setEndState( int s ); int endState() const; void setParagId( int i ); int paragId() const; bool firstPreProcess() const; void setFirstPreProcess( bool b ); void indent( int *oldIndent = 0, int *newIndent = 0 ); - void setExtraData( QTextParagData *data ); - QTextParagData *extraData() const; + void setExtraData( QTextParagraphData *data ); + QTextParagraphData *extraData() const; - QMap<int, QTextParagLineStart*> &lineStartList(); + QMap<int, QTextLineStart*> &lineStartList(); void setFormat( int index, int len, QTextFormat *f, bool useCollection = TRUE, int flags = -1 ); void setAlignment( int a ); int alignment() const; virtual void paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor = 0, bool drawSelections = FALSE, int clipx = -1, int clipy = -1, int clipw = -1, int cliph = -1 ); - void setStyleSheetItems( const QPtrVector<QStyleSheetItem> &vec ); - QPtrVector<QStyleSheetItem> styleSheetItems() const; - QStyleSheetItem *style() const; - virtual int topMargin() const; virtual int bottomMargin() const; virtual int leftMargin() const; virtual int firstLineMargin() const; virtual int rightMargin() const; virtual int lineSpacing() const; - int numberOfSubParagraph() const; void registerFloatingItem( QTextCustomItem *i ); void unregisterFloatingItem( QTextCustomItem *i ); void setFullWidth( bool b ) { fullWidth = b; } bool isFullWidth() const { return fullWidth; } QTextTableCell *tableCell() const; QBrush *background() const; int documentWidth() const; int documentVisibleWidth() const; int documentX() const; int documentY() const; QTextFormatCollection *formatCollection() const; QTextFormatter *formatter() const; virtual int nextTab( int i, int x ); int *tabArray() const; void setTabArray( int *a ); void setTabStops( int tw ); void adjustToPainter( QPainter *p ); void setNewLinesAllowed( bool b ); bool isNewLinesAllowed() const; QString richText() const; void addCommand( QTextCommand *cmd ); QTextCursor *undo( QTextCursor *c = 0 ); QTextCursor *redo( QTextCursor *c = 0 ); QTextCommandHistory *commands() const; - virtual void copyParagData( QTextParag *parag ); + virtual void copyParagData( QTextParagraph *parag ); void setBreakable( bool b ) { breakable = b; } bool isBreakable() const { return breakable; } void setBackgroundColor( const QColor &c ); QColor *backgroundColor() const { return bgcol; } void clearBackgroundColor(); - bool isLineBreak() const { return isBr; } - void setMovedDown( bool b ) { movedDown = b; } bool wasMovedDown() const { return movedDown; } void setDirection( QChar::Direction d ); QChar::Direction direction() const; + void readStyleInformation( QDataStream& stream ); + void writeStyleInformation( QDataStream& stream ) const; + protected: virtual void drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg ); - virtual void drawParagString( QPainter &painter, const QString &str, int start, int len, int startX, - int lastY, int baseLine, int bw, int h, bool drawSelections, - QTextStringChar *formatChar, int i, const QMemArray<int> &selectionStarts, - const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft ); + virtual void drawString( QPainter &painter, const QString &str, int start, int len, int xstart, + int y, int baseLine, int w, int h, int selection, + QTextStringChar *formatChar, const QColorGroup& cg, + bool rightToLeft ); private: - QMap<int, QTextParagSelection> &selections() const; - QPtrVector<QStyleSheetItem> &styleSheetItemsVec() const; + QMap<int, QTextParagraphSelection> &selections() const; QPtrList<QTextCustomItem> &floatingItems() const; + QBrush backgroundBrush( const QColorGroup&cg ) { if ( bgcol ) return *bgcol; return cg.brush( QColorGroup::Base ); } + void invalidateStyleCache(); - QMap<int, QTextParagLineStart*> lineStarts; + QMap<int, QTextLineStart*> lineStarts; int invalid; QRect r; - QTextParag *p, *n; + QTextParagraph *p, *n; void *docOrPseudo; uint changed : 1; uint firstFormat : 1; uint firstPProcess : 1; uint needPreProcess : 1; uint fullWidth : 1; - uint newLinesAllowed : 1; uint lastInFrame : 1; uint visible : 1; uint breakable : 1; - uint isBr : 1; uint movedDown : 1; uint mightHaveCustomItems : 1; uint hasdoc : 1; + uint litem : 1; // whether the paragraph is a list item + uint rtext : 1; // whether the paragraph needs rich text margin int align : 4; int state, id; QTextString *str; - QMap<int, QTextParagSelection> *mSelections; - QPtrVector<QStyleSheetItem> *mStyleSheetItemsVec; + QMap<int, QTextParagraphSelection> *mSelections; QPtrList<QTextCustomItem> *mFloatingItems; - QStyleSheetItem::ListStyle listS; - int numSubParag; - int tm, bm, lm, rm, flm; - QTextFormat *defFormat; + QStyleSheetItem::ListStyle lstyle; + short utm, ubm, ulm, urm, uflm, ulinespacing; int *tArray; - int tabStopWidth; - QTextParagData *eData; - int list_val; + short tabStopWidth; + QTextParagraphData *eData; + short list_val; QColor *bgcol; + ushort ldepth; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextFormatter { public: QTextFormatter(); virtual ~QTextFormatter(); - virtual int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts ) = 0; - virtual int formatVertically( QTextDocument* doc, QTextParag* parag ); + virtual int format( QTextDocument *doc, QTextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts ) = 0; + virtual int formatVertically( QTextDocument* doc, QTextParagraph* parag ); - bool isWrapEnabled( QTextParag *p ) const { if ( !wrapEnabled ) return FALSE; if ( p && !p->isBreakable() ) return FALSE; return TRUE;} + bool isWrapEnabled( QTextParagraph *p ) const { if ( !wrapEnabled ) return FALSE; if ( p && !p->isBreakable() ) return FALSE; return TRUE;} int wrapAtColumn() const { return wrapColumn;} virtual void setWrapEnabled( bool b ); virtual void setWrapAtColumn( int c ); virtual void setAllowBreakInWords( bool b ) { biw = b; } bool allowBreakInWords() const { return biw; } int minimumWidth() const { return thisminw; } int widthUsed() const { return thiswused; } + static bool isBreakable( QTextString *string, int pos ); + protected: - virtual QTextParagLineStart *formatLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line, QTextStringChar *start, + virtual QTextLineStart *formatLine( QTextParagraph *parag, QTextString *string, QTextLineStart *line, QTextStringChar *start, QTextStringChar *last, int align = Qt3::AlignAuto, int space = 0 ); #ifndef QT_NO_COMPLEXTEXT - virtual QTextParagLineStart *bidiReorderLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line, QTextStringChar *start, + virtual QTextLineStart *bidiReorderLine( QTextParagraph *parag, QTextString *string, QTextLineStart *line, QTextStringChar *start, QTextStringChar *last, int align, int space ); #endif - virtual bool isBreakable( QTextString *string, int pos ) const; - void insertLineStart( QTextParag *parag, int index, QTextParagLineStart *ls ); + void insertLineStart( QTextParagraph *parag, int index, QTextLineStart *ls ); int thisminw; int thiswused; private: bool wrapEnabled; int wrapColumn; bool biw; #ifdef HAVE_THAI_BREAKS static QCString *thaiCache; static QTextString *cachedString; static ThBreakIterator *thaiIt; #endif }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextFormatterBreakInWords : public QTextFormatter { public: QTextFormatterBreakInWords(); virtual ~QTextFormatterBreakInWords() {} - int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts ); + int format( QTextDocument *doc, QTextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts ); }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextFormatterBreakWords : public QTextFormatter { public: QTextFormatterBreakWords(); virtual ~QTextFormatterBreakWords() {} - int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts ); + int format( QTextDocument *doc, QTextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts ); }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextIndent { public: QTextIndent(); virtual ~QTextIndent() {} - virtual void indent( QTextDocument *doc, QTextParag *parag, int *oldIndent = 0, int *newIndent = 0 ) = 0; + virtual void indent( QTextDocument *doc, QTextParagraph *parag, int *oldIndent = 0, int *newIndent = 0 ) = 0; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextPreProcessor { public: enum Ids { Standard = 0 }; QTextPreProcessor(); virtual ~QTextPreProcessor() {} - virtual void process( QTextDocument *doc, QTextParag *, int, bool = TRUE ) = 0; + virtual void process( QTextDocument *doc, QTextParagraph *, int, bool = TRUE ) = 0; virtual QTextFormat *format( int id ) = 0; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class Q_EXPORT QTextFormat { friend class QTextFormatCollection; friend class QTextDocument; public: enum Flags { NoFlags, Bold = 1, Italic = 2, Underline = 4, Family = 8, Size = 16, Color = 32, Misspelled = 64, VAlign = 128, - Font = Bold | Italic | Underline | Family | Size, + StrikeOut= 256, + Font = Bold | Italic | Underline | Family | Size | StrikeOut, Format = Font | Color | Misspelled | VAlign }; enum VerticalAlignment { AlignNormal, AlignSuperScript, AlignSubScript }; QTextFormat(); virtual ~QTextFormat(); QTextFormat( const QStyleSheetItem *s ); QTextFormat( const QFont &f, const QColor &c, QTextFormatCollection *parent = 0 ); QTextFormat( const QTextFormat &fm ); - QTextFormat makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr ) const; + QTextFormat makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor ) const; QTextFormat& operator=( const QTextFormat &fm ); QColor color() const; QFont font() const; bool isMisspelled() const; VerticalAlignment vAlign() const; int minLeftBearing() const; int minRightBearing() const; int width( const QChar &c ) const; int width( const QString &str, int pos ) const; int height() const; int ascent() const; int descent() const; int leading() const; bool useLinkColor() const; void setBold( bool b ); void setItalic( bool b ); void setUnderline( bool b ); + void setStrikeOut( bool b ); void setFamily( const QString &f ); void setPointSize( int s ); void setFont( const QFont &f ); void setColor( const QColor &c ); void setMisspelled( bool b ); void setVAlign( VerticalAlignment a ); bool operator==( const QTextFormat &f ) const; QTextFormatCollection *parent() const; QString key() const; static QString getKey( const QFont &f, const QColor &c, bool misspelled, VerticalAlignment vAlign ); void addRef(); void removeRef(); - QString makeFormatChangeTags( QTextFormat *f, const QString& oldAnchorHref, const QString& anchorHref ) const; - QString makeFormatEndTags( const QString& anchorHref ) const; + QString makeFormatChangeTags( QTextFormat* defaultFormat, QTextFormat *f, const QString& oldAnchorHref, const QString& anchorHref ) const; + QString makeFormatEndTags( QTextFormat* defaultFormat, const QString& anchorHref ) const; static void setPainter( QPainter *p ); static QPainter* painter(); - void updateStyle(); - void updateStyleFlags(); - void setStyle( const QString &s ); - QString styleName() const { return style; } - int changed() const { return different; } bool fontSizesInPixels() { return usePixelSizes; } protected: virtual void generateKey(); private: void update(); private: QFont fn; QColor col; QFontMetrics fm; uint missp : 1; uint linkColor : 1; uint usePixelSizes : 1; int leftBearing, rightBearing; VerticalAlignment ha; uchar widths[ 256 ]; int hei, asc, dsc; QTextFormatCollection *collection; int ref; QString k; int logicalFontSize; int stdSize; static QPainter *pntr; - QString style; - int different; }; // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QDict<QTextFormat>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QDict<QTextFormat>; // MOC_SKIP_END #endif class Q_EXPORT QTextFormatCollection { friend class QTextDocument; friend class QTextFormat; public: QTextFormatCollection(); virtual ~QTextFormatCollection(); void setDefaultFormat( QTextFormat *f ); QTextFormat *defaultFormat() const; virtual QTextFormat *format( QTextFormat *f ); virtual QTextFormat *format( QTextFormat *of, QTextFormat *nf, int flags ); virtual QTextFormat *format( const QFont &f, const QColor &c ); virtual void remove( QTextFormat *f ); virtual QTextFormat *createFormat( const QTextFormat &f ) { return new QTextFormat( f ); } virtual QTextFormat *createFormat( const QFont &f, const QColor &c ) { return new QTextFormat( f, c, this ); } - void debug(); - QStyleSheet *styleSheet() const { return sheet; } - void setStyleSheet( QStyleSheet *s ) { sheet = s; } - void updateStyles(); - void updateFontSizes( int base, bool usePixels ); - void updateFontAttributes( const QFont &f, const QFont &old ); + void updateDefaultFormat( const QFont &font, const QColor &c, QStyleSheet *sheet ); QDict<QTextFormat> dict() const { return cKey; } private: void updateKeys(); private: QTextFormat *defFormat, *lastFormat, *cachedFormat; QDict<QTextFormat> cKey; QTextFormat *cres; QFont cfont; QColor ccol; QString kof, knf; int cflags; - QStyleSheet *sheet; - }; // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline int QTextString::length() const { return data.size(); } -inline void QTextString::operator+=( const QString &s ) -{ - insert( length(), s, 0 ); -} - -inline int QTextParag::length() const +inline int QTextParagraph::length() const { return str->length(); } -inline QRect QTextParag::rect() const +inline QRect QTextParagraph::rect() const { return r; } -inline QTextParag *QTextCursor::parag() const +inline QTextParagraph *QTextCursor::paragraph() const { - return string; + return para; } inline int QTextCursor::index() const { return idx; } -inline void QTextCursor::setIndex( int i, bool restore ) -{ - if ( restore ) - restoreState(); - if ( i < 0 || i >= string->length() ) { -#if defined(QT_CHECK_RANGE) - qWarning( "QTextCursor::setIndex: %d out of range", i ); -#endif - i = i < 0 ? 0 : string->length() - 1; - } - - tmpIndex = -1; - idx = i; -} - -inline void QTextCursor::setParag( QTextParag *s, bool restore ) -{ - if ( restore ) - restoreState(); - idx = 0; - string = s; - tmpIndex = -1; -} - -inline void QTextCursor::checkIndex() -{ - if ( idx >= string->length() ) - idx = string->length() - 1; -} // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline int QTextDocument::x() const { return cx; } inline int QTextDocument::y() const { return cy; } inline int QTextDocument::width() const { return QMAX( cw, flow_->width() ); } inline int QTextDocument::visibleWidth() const { return vw; } -inline QTextParag *QTextDocument::firstParag() const +inline QTextParagraph *QTextDocument::firstParagraph() const { return fParag; } -inline QTextParag *QTextDocument::lastParag() const +inline QTextParagraph *QTextDocument::lastParagraph() const { return lParag; } -inline void QTextDocument::setFirstParag( QTextParag *p ) +inline void QTextDocument::setFirstParagraph( QTextParagraph *p ) { fParag = p; } -inline void QTextDocument::setLastParag( QTextParag *p ) +inline void QTextDocument::setLastParagraph( QTextParagraph *p ) { lParag = p; } inline void QTextDocument::setWidth( int w ) { cw = QMAX( w, minw ); flow_->setWidth( cw ); vw = w; } inline int QTextDocument::minimumWidth() const { return minw; } inline void QTextDocument::setY( int y ) { cy = y; } inline int QTextDocument::leftMargin() const { return leftmargin; } inline void QTextDocument::setLeftMargin( int lm ) { leftmargin = lm; } inline int QTextDocument::rightMargin() const { return rightmargin; } inline void QTextDocument::setRightMargin( int rm ) { rightmargin = rm; } inline QTextPreProcessor *QTextDocument::preProcessor() const { return pProcessor; } inline void QTextDocument::setPreProcessor( QTextPreProcessor * sh ) { pProcessor = sh; } inline void QTextDocument::setFormatter( QTextFormatter *f ) { delete pFormatter; pFormatter = f; } inline QTextFormatter *QTextDocument::formatter() const { return pFormatter; } inline void QTextDocument::setIndent( QTextIndent *i ) { indenter = i; } inline QTextIndent *QTextDocument::indent() const { return indenter; } inline QColor QTextDocument::selectionColor( int id ) const { return selectionColors[ id ]; } inline bool QTextDocument::invertSelectionText( int id ) const { return selectionText[ id ]; } inline void QTextDocument::setSelectionColor( int id, const QColor &c ) { selectionColors[ id ] = c; } inline void QTextDocument::setInvertSelectionText( int id, bool b ) { selectionText[ id ] = b; } inline QTextFormatCollection *QTextDocument::formatCollection() const { return fCollection; } inline int QTextDocument::alignment() const { return align; } inline void QTextDocument::setAlignment( int a ) { align = a; } inline int *QTextDocument::tabArray() const { return tArray; } inline int QTextDocument::tabStopWidth() const { return tStopWidth; } inline void QTextDocument::setTabArray( int *a ) { tArray = a; } inline void QTextDocument::setTabStops( int tw ) { tStopWidth = tw; } inline QString QTextDocument::originalText() const { if ( oTextValid ) return oText; return text(); } inline void QTextDocument::setFlow( QTextFlow *f ) { if ( flow_ ) delete flow_; flow_ = f; } inline void QTextDocument::takeFlow() { flow_ = 0; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline QColor QTextFormat::color() const { return col; } inline QFont QTextFormat::font() const { return fn; } inline bool QTextFormat::isMisspelled() const { return missp; } inline QTextFormat::VerticalAlignment QTextFormat::vAlign() const { return ha; } inline bool QTextFormat::operator==( const QTextFormat &f ) const { return k == f.k; } inline QTextFormatCollection *QTextFormat::parent() const { return collection; } inline void QTextFormat::addRef() { ref++; -#ifdef DEBUG_COLLECTION - qDebug( "add ref of '%s' to %d (%p)", k.latin1(), ref, this ); -#endif } inline void QTextFormat::removeRef() { ref--; if ( !collection ) return; if ( this == collection->defFormat ) return; -#ifdef DEBUG_COLLECTION - qDebug( "remove ref of '%s' to %d (%p)", k.latin1(), ref, this ); -#endif if ( ref == 0 ) collection->remove( this ); } inline QString QTextFormat::key() const { return k; } inline bool QTextFormat::useLinkColor() const { return linkColor; } -inline void QTextFormat::setStyle( const QString &s ) -{ - style = s; - updateStyleFlags(); -} // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline QTextStringChar &QTextString::at( int i ) const { return data[ i ]; } // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -inline QTextStringChar *QTextParag::at( int i ) const +inline QTextStringChar *QTextParagraph::at( int i ) const { return &str->at( i ); } -inline bool QTextParag::isValid() const +inline bool QTextParagraph::isValid() const { return invalid == -1; } -inline bool QTextParag::hasChanged() const +inline bool QTextParagraph::hasChanged() const { return changed; } -inline void QTextParag::setBackgroundColor( const QColor & c ) +inline void QTextParagraph::setBackgroundColor( const QColor & c ) { delete bgcol; bgcol = new QColor( c ); setChanged( TRUE ); } -inline void QTextParag::clearBackgroundColor() +inline void QTextParagraph::clearBackgroundColor() { delete bgcol; bgcol = 0; setChanged( TRUE ); } -inline void QTextParag::append( const QString &s, bool reallyAtEnd ) +inline void QTextParagraph::append( const QString &s, bool reallyAtEnd ) { if ( reallyAtEnd ) insert( str->length(), s ); else insert( QMAX( str->length() - 1, 0 ), s ); } -inline QTextParag *QTextParag::prev() const +inline QTextParagraph *QTextParagraph::prev() const { return p; } -inline QTextParag *QTextParag::next() const +inline QTextParagraph *QTextParagraph::next() const { return n; } -inline bool QTextParag::hasAnySelection() const +inline bool QTextParagraph::hasAnySelection() const { return mSelections ? !selections().isEmpty() : FALSE; } -inline void QTextParag::setEndState( int s ) +inline void QTextParagraph::setEndState( int s ) { if ( s == state ) return; state = s; } -inline int QTextParag::endState() const +inline int QTextParagraph::endState() const { return state; } -inline void QTextParag::setParagId( int i ) +inline void QTextParagraph::setParagId( int i ) { id = i; } -inline int QTextParag::paragId() const +inline int QTextParagraph::paragId() const { if ( id == -1 ) qWarning( "invalid parag id!!!!!!!! (%p)", (void*)this ); return id; } -inline bool QTextParag::firstPreProcess() const +inline bool QTextParagraph::firstPreProcess() const { return firstPProcess; } -inline void QTextParag::setFirstPreProcess( bool b ) +inline void QTextParagraph::setFirstPreProcess( bool b ) { firstPProcess = b; } -inline QMap<int, QTextParagLineStart*> &QTextParag::lineStartList() +inline QMap<int, QTextLineStart*> &QTextParagraph::lineStartList() { return lineStarts; } -inline QTextString *QTextParag::string() const +inline QTextString *QTextParagraph::string() const { return str; } -inline QTextDocument *QTextParag::document() const +inline QTextDocument *QTextParagraph::document() const { if ( hasdoc ) return (QTextDocument*) docOrPseudo; return 0; } -inline QTextParagPseudoDocument *QTextParag::pseudoDocument() const +inline QTextParagraphPseudoDocument *QTextParagraph::pseudoDocument() const { if ( hasdoc ) return 0; - return (QTextParagPseudoDocument*) docOrPseudo; + return (QTextParagraphPseudoDocument*) docOrPseudo; } -inline QTextTableCell *QTextParag::tableCell() const +inline QTextTableCell *QTextParagraph::tableCell() const { return hasdoc ? document()->tableCell () : 0; } -inline QTextCommandHistory *QTextParag::commands() const +inline QTextCommandHistory *QTextParagraph::commands() const { return hasdoc ? document()->commands() : pseudoDocument()->commandHistory; } -inline void QTextParag::setAlignment( int a ) -{ - if ( a == (int)align ) - return; - align = a; - invalidate( 0 ); -} - -inline void QTextParag::setListStyle( QStyleSheetItem::ListStyle ls ) -{ - listS = ls; - invalidate( 0 ); -} - -inline QStyleSheetItem::ListStyle QTextParag::listStyle() const -{ - return listS; -} - -inline QTextFormat *QTextParag::paragFormat() const +inline int QTextParagraph::alignment() const { - return defFormat; + return align; } -inline void QTextParag::registerFloatingItem( QTextCustomItem *i ) +inline void QTextParagraph::registerFloatingItem( QTextCustomItem *i ) { floatingItems().append( i ); } -inline void QTextParag::unregisterFloatingItem( QTextCustomItem *i ) +inline void QTextParagraph::unregisterFloatingItem( QTextCustomItem *i ) { floatingItems().removeRef( i ); } -inline QBrush *QTextParag::background() const +inline QBrush *QTextParagraph::background() const { return tableCell() ? tableCell()->backGround() : 0; } -inline int QTextParag::documentWidth() const +inline int QTextParagraph::documentWidth() const { return hasdoc ? document()->width() : pseudoDocument()->docRect.width(); } -inline int QTextParag::documentVisibleWidth() const +inline int QTextParagraph::documentVisibleWidth() const { return hasdoc ? document()->visibleWidth() : pseudoDocument()->docRect.width(); } -inline int QTextParag::documentX() const +inline int QTextParagraph::documentX() const { return hasdoc ? document()->x() : pseudoDocument()->docRect.x(); } -inline int QTextParag::documentY() const +inline int QTextParagraph::documentY() const { return hasdoc ? document()->y() : pseudoDocument()->docRect.y(); } -inline void QTextParag::setExtraData( QTextParagData *data ) +inline void QTextParagraph::setExtraData( QTextParagraphData *data ) { eData = data; } -inline QTextParagData *QTextParag::extraData() const +inline QTextParagraphData *QTextParagraph::extraData() const { return eData; } -inline void QTextParag::setNewLinesAllowed( bool b ) -{ - newLinesAllowed = b; -} - -inline bool QTextParag::isNewLinesAllowed() const -{ - return newLinesAllowed; -} - // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline void QTextFormatCollection::setDefaultFormat( QTextFormat *f ) { defFormat = f; } inline QTextFormat *QTextFormatCollection::defaultFormat() const { return defFormat; } // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ inline QTextFormat *QTextStringChar::format() const { return (type == Regular) ? d.format : d.custom->format; } inline QTextCustomItem *QTextStringChar::customItem() const { return isCustom() ? d.custom->custom : 0; } inline int QTextStringChar::height() const { return !isCustom() ? format()->height() : ( customItem()->placement() == QTextCustomItem::PlaceInline ? customItem()->height : 0 ); } inline int QTextStringChar::ascent() const { return !isCustom() ? format()->ascent() : ( customItem()->placement() == QTextCustomItem::PlaceInline ? customItem()->ascent() : 0 ); } inline int QTextStringChar::descent() const { return !isCustom() ? format()->descent() : 0; } } // namespace Qt3 #endif diff --git a/noncore/apps/opie-write/qstylesheet.cpp b/noncore/apps/opie-write/qstylesheet.cpp index 7ab9ec6..67cd828 100644 --- a/noncore/apps/opie-write/qstylesheet.cpp +++ b/noncore/apps/opie-write/qstylesheet.cpp @@ -1,1484 +1,1572 @@ /**************************************************************************** ** $Id$ ** ** Implementation of the QStyleSheet class ** ** Created : 990101 ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** ** This file is part of the kernel module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "qstylesheet.h" #include "qrichtext_p.h" #include "qlayout.h" #include "qpainter.h" #include "qcleanuphandler.h" #include <stdio.h> using namespace Qt3; namespace Qt3 { class QStyleSheetItemData { public: QStyleSheetItem::DisplayMode disp; int fontitalic; int fontunderline; + int fontstrikeout; int fontweight; int fontsize; int fontsizelog; int fontsizestep; int lineSpacing; QString fontfamily; QStyleSheetItem *parentstyle; QString stylename; int ncolumns; QColor col; bool anchor; int align; QStyleSheetItem::VerticalAlignment valign; int margin[5]; QStyleSheetItem::ListStyle list; QStyleSheetItem::WhiteSpaceMode whitespacemode; QString contxt; bool selfnest; QStyleSheet* sheet; }; } /*! \class QStyleSheetItem qstylesheet.h - \ingroup text \brief The QStyleSheetItem class provides an encapsulation of a set of text styles. + \ingroup text + A style sheet item consists of a name and a set of attributes that specifiy its font, color, etc. When used in a \link QStyleSheet - style sheet\endlink (see styleSheet()), items define the name() of a - rich text tag and the display property changes associated with it. + style sheet\endlink (see styleSheet()), items define the name() of + a rich text tag and the display property changes associated with + it. The \link QStyleSheetItem::DisplayMode display mode\endlink attribute indicates whether the item is a block, an inline element - or a list element; see setDisplayMode(). The treatment of whitespace - is controlled by the \link QStyleSheetItem::WhiteSpaceMode white - space mode\endlink; see setWhiteSpaceMode(). An item's margins are - set with setMargin(), and line spacing is set with setLineSpacing(). + or a list element; see setDisplayMode(). The treatment of + whitespace is controlled by the \link + QStyleSheetItem::WhiteSpaceMode white space mode\endlink; see + setWhiteSpaceMode(). An item's margins are set with setMargin(), In the case of list items, the list style is set with setListStyle(). An item may be a hypertext link anchor; see setAnchor(). Other attributes are set with setAlignment(), setVerticalAlignment(), setFontFamily(), setFontSize(), - setFontWeight(), setFontItalic(), setFontUnderline() and setColor(). + setFontWeight(), setFontItalic(), setFontUnderline(), + setFontStrikeOut and setColor(). */ /*! \enum QStyleSheetItem::AdditionalStyleValues \internal */ -/*! \enum QStyleSheetItem::WhiteSpaceMode +/*! + \enum QStyleSheetItem::WhiteSpaceMode - This enum defines the ways in which QStyleSheet can treat whitespace. There are three values at present: + This enum defines the ways in which QStyleSheet can treat + whitespace. \value WhiteSpaceNormal any sequence of whitespace (including line-breaks) is equivalent to a single space. \value WhiteSpacePre whitespace must be output exactly as given in the input. \value WhiteSpaceNoWrap multiple spaces are collapsed as with - WhiteSpaceNormal, but no automatic line-breaks occur. To break lines manually, - use the \c{<br>} tag. + WhiteSpaceNormal, but no automatic line-breaks occur. To break + lines manually, use the \c{<br>} tag. */ -/*! \enum QStyleSheetItem::Margin +/*! + \enum QStyleSheetItem::Margin \value MarginLeft left margin \value MarginRight right margin \value MarginTop top margin \value MarginBottom bottom margin \value MarginAll all margins (left, right, top and bottom) \value MarginVertical top and bottom margins \value MarginHorizontal left and right margins - \value MarginFirstLine margin (indentation) of the first line of a paragarph (in addition to the MarginLeft of the paragraph) + \value MarginFirstLine margin (indentation) of the first line of + a paragarph (in addition to the MarginLeft of the paragraph) */ /*! - Constructs a new style named \a name for the stylesheet \a parent. + Constructs a new style called \a name for the stylesheet \a + parent. - All properties in QStyleSheetItem are initially in the "do not change" state, - except \link QStyleSheetItem::DisplayMode display mode\endlink, which defaults - to \c DisplayInline. + All properties in QStyleSheetItem are initially in the "do not + change" state, except \link QStyleSheetItem::DisplayMode display + mode\endlink, which defaults to \c DisplayInline. */ QStyleSheetItem::QStyleSheetItem( QStyleSheet* parent, const QString& name ) { d = new QStyleSheetItemData; d->stylename = name.lower(); d->sheet = parent; init(); if (parent) parent->insert( this ); } /*! - Copy constructor. Constructs a copy of \a other that is - not bound to any style sheet. + Copy constructor. Constructs a copy of \a other that is not bound + to any style sheet. */ QStyleSheetItem::QStyleSheetItem( const QStyleSheetItem & other ) { d = new QStyleSheetItemData; *d = *other.d; } /*! - Destroys the style. Note that QStyleSheetItem objects become owned - by QStyleSheet when they are created. + Destroys the style. Note that QStyleSheetItem objects become + owned by QStyleSheet when they are created. */ QStyleSheetItem::~QStyleSheetItem() { delete d; } /*! Returns the style sheet this item is in. */ QStyleSheet* QStyleSheetItem::styleSheet() { return d->sheet; } /*! \overload + Returns the style sheet this item is in. */ const QStyleSheet* QStyleSheetItem::styleSheet() const { return d->sheet; } /*! \internal Internal initialization */ void QStyleSheetItem::init() { d->disp = DisplayInline; d->fontitalic = Undefined; d->fontunderline = Undefined; + d->fontstrikeout = Undefined; d->fontweight = Undefined; d->fontsize = Undefined; d->fontsizelog = Undefined; d->fontsizestep = 0; d->ncolumns = Undefined; d->col = QColor(); // !isValid() d->anchor = FALSE; d->align = Undefined; d->valign = VAlignBaseline; d->margin[0] = Undefined; d->margin[1] = Undefined; d->margin[2] = Undefined; d->margin[3] = Undefined; d->margin[4] = Undefined; - d->list = QStyleSheetItem::ListDisc; - d->whitespacemode = QStyleSheetItem::WhiteSpaceNormal; + d->list = (ListStyle) Undefined; + d->whitespacemode = (WhiteSpaceMode) Undefined; d->selfnest = TRUE; d->lineSpacing = Undefined; } /*! Returns the name of the style item. */ QString QStyleSheetItem::name() const { return d->stylename; } /*! - Returns the \link QStyleSheetItem::DisplayMode display mode\endlink - of the style. + Returns the \link QStyleSheetItem::DisplayMode display + mode\endlink of the style. \sa setDisplayMode() */ QStyleSheetItem::DisplayMode QStyleSheetItem::displayMode() const { return d->disp; } -/*! \enum QStyleSheetItem::DisplayMode +/*! + \enum QStyleSheetItem::DisplayMode - This enum type defines the way adjacent elements are displayed. The possible values are: + This enum type defines the way adjacent elements are displayed. \value DisplayBlock elements are displayed as a rectangular block (e.g. \c{<p>...</p>}). - \value DisplayInline elements are displayed in a horizontally flowing - sequence (e.g. \c{<em>...</em>}). + \value DisplayInline elements are displayed in a horizontally + flowing sequence (e.g. \c{<em>...</em>}). - \value DisplayListItem elements are displayed in a vertical sequence - (e.g. \c{<li>...</li>}). + \value DisplayListItem elements are displayed in a vertical + sequence (e.g. \c{<li>...</li>}). \value DisplayNone elements are not displayed at all. */ /*! Sets the display mode of the style to \a m. \sa displayMode() */ void QStyleSheetItem::setDisplayMode(DisplayMode m) { d->disp=m; } /*! - Returns the alignment of this style. Possible values are AlignAuto, AlignLeft, - AlignRight, AlignCenter and AlignJustify. + Returns the alignment of this style. Possible values are \c + AlignAuto, \c AlignLeft, \c AlignRight, \c AlignCenter or \c + AlignJustify. \sa setAlignment(), Qt::AlignmentFlags */ int QStyleSheetItem::alignment() const { return d->align; } /*! - Sets the alignment to \a f. This only makes sense for styles with a - \link QStyleSheetItem::DisplayMode display mode\endlink of - DisplayBlock. Possible values are AlignAuto, AlignLeft, AlignRight, - AlignCenter and AlignJustify. + Sets the alignment to \a f. This only makes sense for styles with + a \link QStyleSheetItem::DisplayMode display mode\endlink of + DisplayBlock. Possible values are \c AlignAuto, \c AlignLeft, + \c AlignRight, \c AlignCenter or \c AlignJustify. \sa alignment(), displayMode(), Qt::AlignmentFlags */ void QStyleSheetItem::setAlignment( int f ) { d->align = f; } /*! Returns the vertical alignment of the style. Possible values are - VAlignBaseline, VAlignSub and VAlignSuper. + \c VAlignBaseline, \c VAlignSub or \c VAlignSuper. - psa setVerticalAlignment() + \sa setVerticalAlignment() */ QStyleSheetItem::VerticalAlignment QStyleSheetItem::verticalAlignment() const { return d->valign; } -/*! \enum QStyleSheetItem::VerticalAlignment +/*! + \enum QStyleSheetItem::VerticalAlignment - This enum type defines the way elements are aligned vertically. This - is supported for text elements only. The possible values are: + This enum type defines the way elements are aligned vertically. + This is only supported for text elements. \value VAlignBaseline align the baseline of the element (or the - bottom, if the element doesn't have a baseline) with the baseline of - the parent + bottom, if the element doesn't have a baseline) with the + baseline of the parent \value VAlignSub subscript the element \value VAlignSuper superscript the element */ /*! Sets the vertical alignment to \a valign. Possible values are - VAlignBaseline, VAlignSub and VAlignSuper. + \c VAlignBaseline, \c VAlignSub or \c VAlignSuper. The vertical alignment property is not inherited. \sa verticalAlignment() */ void QStyleSheetItem::setVerticalAlignment( VerticalAlignment valign ) { d->valign = valign; } /*! - Returns TRUE if the style sets an italic font; otherwise returns FALSE. + Returns TRUE if the style sets an italic font; otherwise returns + FALSE. \sa setFontItalic(), definesFontItalic() */ bool QStyleSheetItem::fontItalic() const { return d->fontitalic > 0; } /*! If \a italic is TRUE sets italic for the style; otherwise sets upright. \sa fontItalic(), definesFontItalic() */ void QStyleSheetItem::setFontItalic(bool italic) { d->fontitalic = italic?1:0; } /*! - Returns whether the style defines a font shape. A style - does not define any shape until setFontItalic() is called. + Returns TRUE if the style defines a font shape; otherwise returns + FALSE. A style does not define any shape until setFontItalic() is + called. \sa setFontItalic(), fontItalic() */ bool QStyleSheetItem::definesFontItalic() const { return d->fontitalic != Undefined; } /*! - Returns TRUE if the style sets an underlined font; otherwise returns FALSE. + Returns TRUE if the style sets an underlined font; otherwise + returns FALSE. \sa setFontUnderline(), definesFontUnderline() */ bool QStyleSheetItem::fontUnderline() const { return d->fontunderline > 0; } /*! - If \a underline is TRUE sets underline for the style; otherwise sets - no underline. + If \a underline is TRUE, sets underline for the style; otherwise + sets no underline. \sa fontUnderline(), definesFontUnderline() */ void QStyleSheetItem::setFontUnderline(bool underline) { d->fontunderline = underline?1:0; } /*! - Returns whether the style defines a setting for the underline - property of the font. A style does not define this until - setFontUnderline() is called. + Returns TRUE if the style defines a setting for the underline + property of the font; otherwise returns FALSE. A style does not + define this until setFontUnderline() is called. - \sa setFontUnderline(), fontUnderline() */ + \sa setFontUnderline(), fontUnderline() +*/ bool QStyleSheetItem::definesFontUnderline() const { return d->fontunderline != Undefined; } /*! + Returns TRUE if the style sets a strike out font; otherwise + returns FALSE. + + \sa setFontStrikeOut(), definesFontStrikeOut() +*/ +bool QStyleSheetItem::fontStrikeOut() const +{ + return d->fontstrikeout > 0; +} + +/*! + If \a strikeOut is TRUE, sets strike out for the style; otherwise + sets no strike out. + + \sa fontStrikeOut(), definesFontStrikeOut() +*/ +void QStyleSheetItem::setFontStrikeOut(bool strikeOut) +{ + d->fontstrikeout = strikeOut?1:0; +} + +/*! + Returns TRUE if the style defines a setting for the strikeOut + property of the font; otherwise returns FALSE. A style does not + define this until setFontStrikeOut() is called. + + \sa setFontStrikeOut(), fontStrikeOut() +*/ +bool QStyleSheetItem::definesFontStrikeOut() const +{ + return d->fontstrikeout != Undefined; +} + + +/*! Returns the font weight setting of the style. This is either a - valid QFont::Weight or the value QStyleSheetItem::Undefined. + valid \c QFont::Weight or the value \c QStyleSheetItem::Undefined. \sa setFontWeight(), QFont */ int QStyleSheetItem::fontWeight() const { return d->fontweight; } /*! - Sets the font weight setting of the style to \a w. Valid values are - those defined by QFont::Weight. + Sets the font weight setting of the style to \a w. Valid values + are those defined by \c QFont::Weight. \sa QFont, fontWeight() */ void QStyleSheetItem::setFontWeight(int w) { d->fontweight = w; } /*! - Returns the logical font size setting of the style. This is either a valid - size between 1 and 7 or QStyleSheetItem::Undefined. + Returns the logical font size setting of the style. This is either + a valid size between 1 and 7 or \c QStyleSheetItem::Undefined. \sa setLogicalFontSize(), setLogicalFontSizeStep(), QFont::pointSize(), QFont::setPointSize() */ int QStyleSheetItem::logicalFontSize() const { return d->fontsizelog; } /*! - Sets the logical font size setting of the style to \a s. - Valid logical sizes are 1 to 7. + Sets the logical font size setting of the style to \a s. Valid + logical sizes are 1 to 7. \sa logicalFontSize(), QFont::pointSize(), QFont::setPointSize() */ void QStyleSheetItem::setLogicalFontSize(int s) { d->fontsizelog = s; } /*! Returns the logical font size step of this style. - The default is 0. Tags such as \c big define \c +1; \c small defines - \c -1. + The default is 0. Tags such as \c big define \c +1; \c small + defines \c -1. \sa setLogicalFontSizeStep() */ int QStyleSheetItem::logicalFontSizeStep() const { return d->fontsizestep; } /*! Sets the logical font size step of this style to \a s. \sa logicalFontSizeStep() */ void QStyleSheetItem::setLogicalFontSizeStep( int s ) { d->fontsizestep = s; } /*! Sets the font size setting of the style to \a s points. \sa fontSize(), QFont::pointSize(), QFont::setPointSize() */ void QStyleSheetItem::setFontSize(int s) { d->fontsize = s; } /*! Returns the font size setting of the style. This is either a valid - point size or QStyleSheetItem::Undefined. + point size or \c QStyleSheetItem::Undefined. \sa setFontSize(), QFont::pointSize(), QFont::setPointSize() */ int QStyleSheetItem::fontSize() const { return d->fontsize; } /*! - Returns the font family setting of the style. This is either a valid - font family or QString::null if no family has been set. + Returns the font family setting of the style. This is either a + valid font family or QString::null if no family has been set. \sa setFontFamily(), QFont::family(), QFont::setFamily() */ QString QStyleSheetItem::fontFamily() const { return d->fontfamily; } /*! Sets the font family setting of the style to \a fam. \sa fontFamily(), QFont::family(), QFont::setFamily() */ void QStyleSheetItem::setFontFamily( const QString& fam) { d->fontfamily = fam; } /*!\obsolete Returns the number of columns for this style. \sa setNumberOfColumns(), displayMode(), setDisplayMode() */ int QStyleSheetItem::numberOfColumns() const { return d->ncolumns; } /*!\obsolete Sets the number of columns for this style. Elements in the style are divided into columns. This makes sense only if the style uses a block display mode (see QStyleSheetItem::DisplayMode). \sa numberOfColumns() */ void QStyleSheetItem::setNumberOfColumns(int ncols) { if (ncols > 0) d->ncolumns = ncols; } /*! - Returns the text color of this style or an invalid color - if no color has been set. + Returns the text color of this style or an invalid color if no + color has been set. \sa setColor() QColor::isValid() */ QColor QStyleSheetItem::color() const { return d->col; } /*! Sets the text color of this style to \a c. \sa color() */ void QStyleSheetItem::setColor( const QColor &c) { d->col = c; } /*! Returns whether this style is an anchor. \sa setAnchor() */ bool QStyleSheetItem::isAnchor() const { return d->anchor; } /*! - If \a anc is TRUE sets this style to be an anchor (hypertext link); - otherwise sets it to not be an anchor. Elements in this style have - connections to other documents or anchors. + If \a anc is TRUE, sets this style to be an anchor (hypertext + link); otherwise sets it to not be an anchor. Elements in this + style link to other documents or anchors. \sa isAnchor() */ void QStyleSheetItem::setAnchor(bool anc) { d->anchor = anc; } /*! Returns the whitespace mode. \sa setWhiteSpaceMode() WhiteSpaceMode */ QStyleSheetItem::WhiteSpaceMode QStyleSheetItem::whiteSpaceMode() const { return d->whitespacemode; } /*! Sets the whitespace mode to \a m. + \sa WhiteSpaceMode */ void QStyleSheetItem::setWhiteSpaceMode(WhiteSpaceMode m) { d->whitespacemode = m; } /*! Returns the width of margin \a m in pixels. - The margin, \a m, can be \c MarginLeft, \c MarginRight, - \c MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c + The margin, \a m, can be \c MarginLeft, \c MarginRight, \c + MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c MarginHorizontal. \sa setMargin() Margin */ int QStyleSheetItem::margin(Margin m) const { return d->margin[m]; } /*! Sets the width of margin \a m to \a v pixels. - The margin, \a m, can be \c MarginLeft, \c MarginRight, - \c MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c + The margin, \a m, can be \c MarginLeft, \c MarginRight, \c + MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c MarginHorizontal. The value \a v must be >= 0. \sa margin() */ void QStyleSheetItem::setMargin(Margin m, int v) { if (m == MarginAll ) { d->margin[0] = v; d->margin[1] = v; d->margin[2] = v; d->margin[3] = v; d->margin[4] = v; } else if (m == MarginVertical ) { d->margin[MarginTop] = v; d->margin[MarginBottom] = v; } else if (m == MarginHorizontal ) { d->margin[MarginLeft] = v; d->margin[MarginRight] = v; } else { d->margin[m] = v; } } /*! Returns the list style of the style. \sa setListStyle() ListStyle */ QStyleSheetItem::ListStyle QStyleSheetItem::listStyle() const { return d->list; } -/*! \enum QStyleSheetItem::ListStyle +/*! + \enum QStyleSheetItem::ListStyle This enum type defines how the items in a list are prefixed when - displayed. The currently defined values are: + displayed. \value ListDisc a filled circle (i.e. a bullet) \value ListCircle an unfilled circle \value ListSquare a filled square \value ListDecimal an integer in base 10: \e 1, \e 2, \e 3, ... \value ListLowerAlpha a lowercase letter: \e a, \e b, \e c, ... \value ListUpperAlpha an uppercase letter: \e A, \e B, \e C, ... */ + /*! Sets the list style of the style to \a s. - This is used by nested elements that have a display mode of - \c DisplayListItem. + This is used by nested elements that have a display mode of \c + DisplayListItem. \sa listStyle() DisplayMode ListStyle */ void QStyleSheetItem::setListStyle(ListStyle s) { d->list=s; } -/*! Returns a space-separated list of names of styles that may - contain elements of this style. If nothing has been set, contexts() +/*! + Returns a space-separated list of names of styles that may contain + elements of this style. If nothing has been set, contexts() returns an empty string, which indicates that this style can be nested everywhere. \sa setContexts() */ QString QStyleSheetItem::contexts() const { return d->contxt; } /*! Sets a space-separated list of names of styles that may contain elements of this style. If \a c is empty, the style can be nested everywhere. \sa contexts() */ void QStyleSheetItem::setContexts( const QString& c) { d->contxt = QChar(' ') + c + QChar(' '); } /*! - Returns TRUE if this style can be nested into an element - of style \a s; otherwise returns FALSE. + Returns TRUE if this style can be nested into an element of style + \a s; otherwise returns FALSE. \sa contexts(), setContexts() */ bool QStyleSheetItem::allowedInContext( const QStyleSheetItem* s) const { if ( d->contxt.isEmpty() ) return TRUE; return d->contxt.find( QChar(' ')+s->name()+QChar(' ')) != -1; } /*! Returns TRUE if this style has self-nesting enabled; otherwise returns FALSE. \sa setSelfNesting() */ bool QStyleSheetItem::selfNesting() const { return d->selfnest; } /*! Sets the self-nesting property for this style to \a nesting. - In order to support "dirty" HTML, paragraphs \c{<p>} and list items - \c{<li>} are not self-nesting. This means that starting a new - paragraph or list item automatically closes the previous one. + In order to support "dirty" HTML, paragraphs \c{<p>} and list + items \c{<li>} are not self-nesting. This means that starting a + new paragraph or list item automatically closes the previous one. \sa selfNesting() */ void QStyleSheetItem::setSelfNesting( bool nesting ) { d->selfnest = nesting; } -/*! Sets the linespacing to be \a ls pixels */ +/* + Sets the linespacing to be at least \a ls pixels. + + For compatibility with previous Qt releases, small values get + treated differently: If \a ls is smaller than the default font + line spacing in pixels at parse time, the resulting line spacing + is the sum of the default line spacing plus \a ls. We recommend + not relying on this behavior. +*/ void QStyleSheetItem::setLineSpacing( int ls ) { d->lineSpacing = ls; } -/*! Returns the linespacing */ +/*! + \obsolete + + Returns the linespacing +*/ int QStyleSheetItem::lineSpacing() const { return d->lineSpacing; } //************************************************************************ //************************************************************************ /*! \class QStyleSheet qstylesheet.h \ingroup text \brief The QStyleSheet class is a collection of styles for rich text rendering and a generator of tags. \ingroup graphics \ingroup helpsystem By creating QStyleSheetItem objects for a style sheet you build a definition of a set of tags. This definition will be used by the internal rich text rendering system to parse and display text documents to which the style sheet applies. Rich text is normally visualized in a QTextView or a QTextBrowser. However, QLabel, QWhatsThis and QMessageBox also support it, and other classes are likely to follow. With QSimpleRichText it is possible to use the rich text renderer for custom widgets as well. The default QStyleSheet object has the following style bindings, sorted by structuring bindings, anchors, character style bindings (i.e. inline styles), special elements such as horizontal lines or - images, and other tags. In addition, rich text supports simple HTML - tables. + images, and other tags. In addition, rich text supports simple + HTML tables. The structuring tags are + \table + \header \i Structuring tags \i Notes + \row \i \c{<qt>}...\c{</qt>} + \i A Qt rich text document. It understands the following + attributes: \list - \i \c{<qt>}...\c{</qt>} - - A Qt rich text document. It understands the following attributes: - \list - \i title - - The caption of the document. This attribute is easily accessible with - QTextView::documentTitle(). - \i type - - The type of the document. The default type is \c page . It - indicates that the document is displayed in a page of its - own. Another style is \c detail, which can be used to - explain certain expressions in more detail in a few - sentences. The QTextBrowser will then keep the current page - and display the new document in a small popup similar to - QWhatsThis. Note that links will not work in documents with - \c{<qt type="detail">...</qt>}. - \i bgcolor - - The background color, for example \c bgcolor="yellow" or \c - bgcolor="#0000FF". - \i background - - The background pixmap, for example \c - background="granit.xpm". The pixmap name will be resolved by - a QMimeSourceFactory(). - \i text - - The default text color, for example \c text="red". - \i link - - The link color, for example \c link="green". - \endlist - \i \c{<h1>...</h1>} - - A top-level heading. - \i \c{<h2>...</h2>} - - A sublevel heading. - \i \c{<h3>...</h3>} - - A sub-sublevel heading. - \i \c{<p>...</p>} - - A left-aligned paragraph. Adjust the alignment with - the \c align attribute. Possible values are - \c left, \c right and \c center. - \i \c{<center>...</center>} - - A centered paragraph. - \i \c{<blockquote>...</blockquote>} - - An indented paragraph that is useful for quotes. - \i \c{<ul>...</ul>} - - An unordered list. You can also pass a type argument to - define the bullet style. The default is \c type=disc; other - types are \c circle and \c square. - \i \c{<ol>...</ol>} - - An ordered list. You can also pass a type argument to define - the enumeration label style. The default is \c type="1"; other - types are \c "a" and \c "A". - \i <tt><li></tt>...<tt></li></tt> - - A list item. This tag can be used only within the context of - \c ol or \c ul. - \i \c{<pre>...</pre>} - - For larger chunks of code. Whitespaces in the contents are preserved. - For small bits of code use the inline-style \c code. + \i \c title -- The caption of the document. This attribute is + easily accessible with QTextView::documentTitle(). + \i \c type -- The type of the document. The default type is \c + page. It indicates that the document is displayed in a + page of its own. Another style is \c detail, which can be + used to explain certain expressions in more detail in a + few sentences. For \c detail, QTextBrowser will then keep + the current page and display the new document in a small + popup similar to QWhatsThis. Note that links will not work + in documents with \c{<qt type="detail">...</qt>}. + \i \c bgcolor -- The background color, for example \c + bgcolor="yellow" or \c bgcolor="#0000FF". + \i \c background -- The background pixmap, for example \c + background="granite.xpm". The pixmap name will be resolved + by a QMimeSourceFactory(). + \i \c text -- The default text color, for example \c text="red". + \i \c link -- The link color, for example \c link="green". \endlist + \row \i \c{<h1>...</h1>} + \i A top-level heading. + \row \i \c{<h2>...</h2>} + \i A sublevel heading. + \row \i \c{<h3>...</h3>} + \i A sub-sublevel heading. + \row \i \c{<p>...</p>} + \i A left-aligned paragraph. Adjust the alignment with the \c + align attribute. Possible values are \c left, \c right and + \c center. + \row \i \c{<center>...}<br>\c{</center>} + \i A centered paragraph. + \row \i \c{<blockquote>...}<br>\c{</blockquote>} + \i An indented paragraph that is useful for quotes. + \row \i \c{<ul>...</ul>} + \i An unordered list. You can also pass a type argument to + define the bullet style. The default is \c type=disc; + other types are \c circle and \c square. + \row \i \c{<ol>...</ol>} + \i An ordered list. You can also pass a type argument to + define the enumeration label style. The default is \c + type="1"; other types are \c "a" and \c "A". + \row \i \c{<li>...</li>} + \i A list item. This tag can be used only within the context + of \c{<ol>} or \c{<ul>}. + \row \i \c{<pre>...</pre>} + \i For larger chunks of code. Whitespaces in the contents are + preserved. For small bits of code use the inline-style \c + code. + \endtable Anchors and links are done with a single tag: + \table + \header \i Anchor tags \i Notes + \row \i \c{<a>...</a>} + \i An anchor or link. \list - \i \c{<a>...</a>} - - An anchor or link. The reference target is defined in the \c - href attribute of the tag as in \c{<a - href="target.qml">...</a>}. You can also specify an - additional anchor within the specified target document, for - example \c{<a href="target.qml#123">...</a>}. If \c a is - meant to be an anchor, the reference source is given in the - \c name attribute. + \i A link is created by using an \c href + attribute, for example + <br>\c{<a href="target.qml">Link Text</a>}. Links to + targets within a document are achieved in the same way + as for HTML, e.g. + <br>\c{<a href="target.qml#subtitle">Link Text</a>}. + \i A target is created by using a \c name + attribute, for example + <br>\c{<a name="subtitle"><h2>Sub Title</h2></a>}. \endlist + \endtable The default character style bindings are + \table + \header \i Style tags \i Notes + \row \i \c{<em>...</em>} + \i Emphasized. By default this is the same as \c{<i>...</i>} + (italic). + \row \i \c{<strong>...</strong>} + \i Strong. By default this is the same as \c{<b>...</b>} + (bold). + \row \i \c{<i>...</i>} + \i Italic font style. + \row \i \c{<b>...</b>} + \i Bold font style. + \row \i \c{<u>...</u>} + \i Underlined font style. + \row \i \c{<s>...</s>} + \i Strike out font style. + \row \i \c{<big>...</big>} + \i A larger font size. + \row \i \c{<small>...</small>} + \i A smaller font size. + \row \i \c{<code>...</code>} + \i Indicates code. By default this is the same as + \c{<tt>...</tt>} (typewriter). For larger junks of code + use the block-tag \c{<}\c{pre>}. + \row \i \c{<tt>...</tt>} + \i Typewriter font style. + \row \i \c{<font>...</font>} + \i Customizes the font size, family and text color. The tag + understands the following attributes: \list - \i \c{<em>...</em>} - - Emphasized. By default this is the same as - \c{<i>...</i>} (italic). - \i \c{<strong>...</strong>} - - Strong. By default this is the same as - \c{<b>...</b>} (bold). - \i \c{<i>...</i>} - - Italic font style. - \i \c{<b>...</b>} - - Bold font style. - \i \c{<u>...</u>} - - Underlined font style. - \i \c{<big>...</big>} - - A larger font size. - \i \c{<small>...</small>} - - A smaller font size. - \i \c{<code>...</code>} - - Indicates code. By default this is the same as - \c{<tt>...</tt>} (typewriter). For - larger junks of code use the block-tag \c pre. - \i \c{<tt>...</tt>} - - Typewriter font style. - \i \c{<font>...</font>} - - Customizes the font size, family and text color. The tag understands - the following attributes: - \list - \i color - - The text color, for example \c color="red" or \c color="#FF0000". - \i size - - The logical size of the font. Logical sizes 1 to 7 are supported. - The value may either be absolute (for example, - \c size=3) or relative (\c size=-2). In the latter case the sizes - are simply added. - \i face - - The family of the font, for example \c face=times. - \endlist + \i \c color -- The text color, for example \c color="red" or + \c color="#FF0000". + \i \c size -- The logical size of the font. Logical sizes 1 + to 7 are supported. The value may either be absolute + (for example, \c size=3) or relative (\c size=-2). In + the latter case the sizes are simply added. + \i \c face -- The family of the font, for example \c face=times. \endlist + \endtable Special elements are: - \list - \i \c{<img>} - - An image. The image name for the mime source - factory is given in the source attribute, for example - \c{<img src="qt.xpm">} - The image tag also understands the attributes \c width and \c - height that determine the size of the image. If the pixmap - does not fit the specified size it will be scaled - automatically (by using QImage::smoothScale()). - + \table + \header \i Special tags \i Notes + \row \i \c{<img>} + \i An image. The image name for the mime source factory is + given in the source attribute, for example + \c{<img src="qt.xpm">} The image tag also understands the + attributes \c width and \c height that determine the size + of the image. If the pixmap does not fit the specified + size it will be scaled automatically (by using + QImage::smoothScale()). + <br> The \c align attribute determines where the image is placed. By default, an image is placed inline just like a normal character. Specify \c left or \c right to place the image at the respective side. - \i \c{<hr>} - - A horizonal line. - \i \c{<br>} - - A line break. - \endlist - - Another tag not in any of the above cathegories is + \row \i \c{<hr>} + \i A horizonal line. + \row \i \c{<br>} + \i A line break. + \row \i \c{<nobr>...</nobr>} + \i No break. Prevents word wrap. + \endtable + + In addition, rich text supports simple HTML tables. A table + consists of one or more rows each of which contains one or more + cells. Cells are either data cells or header cells, depending on + their content. Cells which span rows and columns are supported. + + \table + \header \i Table tags \i Notes + \row \i \c{<table>...</table>} + \i A table. Tables support the following attributes: \list - \i \c{<nobr>...</nobr>} - - No break. Prevents word wrap. + \i \c bgcolor -- The background color. + \i \c width -- The table width. This is either an absolute + pixel width or a relative percentage of the table's + width, for example \c width=80%. + \i \c border -- The width of the table border. The default is + 0 (= no border). + \i \c cellspacing -- Additional space around the table cells. + The default is 2. + \i \c cellpadding -- Additional space around the contents of + table cells. The default is 1. \endlist - - In addition, rich text supports simple HTML tables. A table consists - of one or more rows each of which contains one or more cells. Cells - are either data cells or header cells, depending on their - content. Cells which span rows and columns are supported. - - \list - \i \c{<table>...</table>} - - A table. Tables support the following attributes: + \row \i \c{<tr>...</tr>} + \i A table row. This is only valid within a \c table. Rows + support the following attribute: \list - \i bgcolor - - The background color. - \i width - - The table width. This is either an absolute pixel width or a relative - percentage of the table's width, for example \c width=80%. - \i border - - The width of the table border. The default is 0 (= no border). - \i cellspacing - - Additional space around the table cells. The default is 2. - \i cellpadding - - Additional space around the contents of table cells. The default is 1. + \i \c bgcolor -- The background color. \endlist - \i \c{<tr>...</tr>} - - A table row. This is only valid within a \c table. Rows support - the following attribute: + \row \i \c{<th>...</th>} + \i A table header cell. Similar to \c td, but defaults to + center alignment and a bold font. + \row \i \c{<td>...</td>} + \i A table data cell. This is only valid within a \c tr. + Cells support the following attributes: \list - \i bgcolor - - The background color. - \endlist - \i \c{<th>...</th>} - - A table header cell. Similar to \c td, but defaults to center alignment - and a bold font. - \i \c{<td>...</td>} - - A table data cell. This is only valid within a \c tr. Cells - support the following attributes: - \list - \i bgcolor - - The background color. - \i width - - The cell width. This is either an absolute pixel width or a relative - percentage of table's width, for example \c width=50%. - \i colspan - - Specifies how many columns this cell spans. The default is 1. - \i rowspan - - Specifies how many rows this cell spans. The default is 1. - \i align - - Alignment; possible values are \c left, \c right, and \c center. The - default is left. - \endlist + \i \c bgcolor -- The background color. + \i \c width -- The cell width. This is either an absolute + pixel width or a relative percentage of table's width, + for example \c width=50%. + \i \c colspan -- Specifies how many columns this cell spans. + The default is 1. + \i \c rowspan -- Specifies how many rows this cell spans. The + default is 1. + \i \c align -- Alignment; possible values are \c left, \c + right, and \c center. The default is left. \endlist + \endtable */ /*! - Creates a style sheet with parent \a parent and name \a name. Like - any QObject it will be deleted when its parent is - destroyed (if the child still exists). + Creates a style sheet called \a name, with parent \a parent. Like + any QObject it will be deleted when its parent is destroyed (if + the child still exists). By default the style sheet has the tag definitions defined above. */ QStyleSheet::QStyleSheet( QObject *parent, const char *name ) : QObject( parent, name ) { init(); } /*! Destroys the style sheet. All styles inserted into the style sheet will be deleted. */ QStyleSheet::~QStyleSheet() { } /*! \internal Initialized the style sheet to the basic Qt style. */ void QStyleSheet::init() { styles.setAutoDelete( TRUE ); nullstyle = new QStyleSheetItem( this, QString::fromLatin1("") ); QStyleSheetItem* style; style = new QStyleSheetItem( this, "qml" ); // compatibility style->setDisplayMode( QStyleSheetItem::DisplayBlock ); style = new QStyleSheetItem( this, QString::fromLatin1("qt") ); style->setDisplayMode( QStyleSheetItem::DisplayBlock ); - //style->setMargin( QStyleSheetItem::MarginAll, 4 ); style = new QStyleSheetItem( this, QString::fromLatin1("a") ); style->setAnchor( TRUE ); style = new QStyleSheetItem( this, QString::fromLatin1("em") ); style->setFontItalic( TRUE ); style = new QStyleSheetItem( this, QString::fromLatin1("i") ); style->setFontItalic( TRUE ); style = new QStyleSheetItem( this, QString::fromLatin1("big") ); style->setLogicalFontSizeStep( 1 ); style = new QStyleSheetItem( this, QString::fromLatin1("large") ); // compatibility style->setLogicalFontSizeStep( 1 ); style = new QStyleSheetItem( this, QString::fromLatin1("small") ); style->setLogicalFontSizeStep( -1 ); style = new QStyleSheetItem( this, QString::fromLatin1("strong") ); style->setFontWeight( QFont::Bold); style = new QStyleSheetItem( this, QString::fromLatin1("b") ); style->setFontWeight( QFont::Bold); style = new QStyleSheetItem( this, QString::fromLatin1("h1") ); style->setFontWeight( QFont::Bold); style->setLogicalFontSize(6); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginTop, 12); - style-> setMargin(QStyleSheetItem::MarginBottom, 6); + style-> setMargin(QStyleSheetItem::MarginTop, 18); + style-> setMargin(QStyleSheetItem::MarginBottom, 12); style = new QStyleSheetItem( this, QString::fromLatin1("h2") ); style->setFontWeight( QFont::Bold); style->setLogicalFontSize(5); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginTop, 10); - style-> setMargin(QStyleSheetItem::MarginBottom, 5); + style-> setMargin(QStyleSheetItem::MarginTop, 16); + style-> setMargin(QStyleSheetItem::MarginBottom, 12); style = new QStyleSheetItem( this, QString::fromLatin1("h3") ); style->setFontWeight( QFont::Bold); style->setLogicalFontSize(4); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginTop, 8); - style-> setMargin(QStyleSheetItem::MarginBottom, 4); + style-> setMargin(QStyleSheetItem::MarginTop, 14); + style-> setMargin(QStyleSheetItem::MarginBottom, 12); style = new QStyleSheetItem( this, QString::fromLatin1("h4") ); style->setFontWeight( QFont::Bold); style->setLogicalFontSize(3); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginTop, 8); - style-> setMargin(QStyleSheetItem::MarginBottom, 4); + style-> setMargin(QStyleSheetItem::MarginVertical, 12); style = new QStyleSheetItem( this, QString::fromLatin1("h5") ); style->setFontWeight( QFont::Bold); style->setLogicalFontSize(2); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginTop, 8); + style-> setMargin(QStyleSheetItem::MarginTop, 12); style-> setMargin(QStyleSheetItem::MarginBottom, 4); style = new QStyleSheetItem( this, QString::fromLatin1("p") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginVertical, 8); + style-> setMargin(QStyleSheetItem::MarginVertical, 12); style->setSelfNesting( FALSE ); style = new QStyleSheetItem( this, QString::fromLatin1("center") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setAlignment( AlignCenter ); style = new QStyleSheetItem( this, QString::fromLatin1("twocolumn") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setNumberOfColumns( 2 ); style = new QStyleSheetItem( this, QString::fromLatin1("multicol") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); (void) new QStyleSheetItem( this, QString::fromLatin1("font") ); style = new QStyleSheetItem( this, QString::fromLatin1("ul") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); - style-> setMargin(QStyleSheetItem::MarginVertical, 4); + style->setListStyle( QStyleSheetItem::ListDisc ); + style-> setMargin(QStyleSheetItem::MarginVertical, 12); + style->setMargin( QStyleSheetItem::MarginLeft, 40 ); style = new QStyleSheetItem( this, QString::fromLatin1("ol") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setListStyle( QStyleSheetItem::ListDecimal ); - style-> setMargin(QStyleSheetItem::MarginVertical, 4); + style-> setMargin(QStyleSheetItem::MarginVertical, 12); + style->setMargin( QStyleSheetItem::MarginLeft, 40 ); style = new QStyleSheetItem( this, QString::fromLatin1("li") ); style->setDisplayMode(QStyleSheetItem::DisplayListItem); style->setSelfNesting( FALSE ); - style->setContexts(QString::fromLatin1("ol ul")); - style-> setMargin(QStyleSheetItem::MarginVertical, 4); style = new QStyleSheetItem( this, QString::fromLatin1("code") ); style->setFontFamily( QString::fromLatin1("courier") ); style = new QStyleSheetItem( this, QString::fromLatin1("tt") ); style->setFontFamily( QString::fromLatin1("courier") ); new QStyleSheetItem(this, QString::fromLatin1("img")); new QStyleSheetItem(this, QString::fromLatin1("br")); new QStyleSheetItem(this, QString::fromLatin1("hr")); + style = new QStyleSheetItem(this, QString::fromLatin1("sub")); style->setVerticalAlignment( QStyleSheetItem::VAlignSub ); style = new QStyleSheetItem(this, QString::fromLatin1("sup")); style->setVerticalAlignment( QStyleSheetItem::VAlignSuper ); style = new QStyleSheetItem( this, QString::fromLatin1("pre") ); style->setFontFamily( QString::fromLatin1("courier") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setWhiteSpaceMode(QStyleSheetItem::WhiteSpacePre); style = new QStyleSheetItem( this, QString::fromLatin1("blockquote") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setMargin(QStyleSheetItem::MarginHorizontal, 40 ); style = new QStyleSheetItem( this, QString::fromLatin1("head") ); style->setDisplayMode(QStyleSheetItem::DisplayNone); + style = new QStyleSheetItem( this, QString::fromLatin1("body") ); + style->setDisplayMode(QStyleSheetItem::DisplayBlock); style = new QStyleSheetItem( this, QString::fromLatin1("div") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock) ; + style = new QStyleSheetItem( this, QString::fromLatin1("span") ); style = new QStyleSheetItem( this, QString::fromLatin1("dl") ); + style-> setMargin(QStyleSheetItem::MarginVertical, 8); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style = new QStyleSheetItem( this, QString::fromLatin1("dt") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setContexts(QString::fromLatin1("dl") ); style = new QStyleSheetItem( this, QString::fromLatin1("dd") ); style->setDisplayMode(QStyleSheetItem::DisplayBlock); style->setMargin(QStyleSheetItem::MarginLeft, 30); style->setContexts(QString::fromLatin1("dt dl") ); style = new QStyleSheetItem( this, QString::fromLatin1("u") ); style->setFontUnderline( TRUE); + style = new QStyleSheetItem( this, QString::fromLatin1("s") ); + style->setFontStrikeOut( TRUE); style = new QStyleSheetItem( this, QString::fromLatin1("nobr") ); style->setWhiteSpaceMode( QStyleSheetItem::WhiteSpaceNoWrap ); - style = new QStyleSheetItem( this, QString::fromLatin1("wsp") ); // qt extension for QTextEdit - style->setWhiteSpaceMode( (QStyleSheetItem::WhiteSpaceMode) 3 ); // WhiteSpaceModeNoCompression + + // compatibily with some minor 3.0.x Qt versions that had an + // undocumented <wsp> tag. ### Remove 3.1 + style = new QStyleSheetItem( this, QString::fromLatin1("wsp") ); + style->setWhiteSpaceMode( QStyleSheetItem::WhiteSpacePre ); // tables style = new QStyleSheetItem( this, QString::fromLatin1("table") ); style = new QStyleSheetItem( this, QString::fromLatin1("tr") ); style->setContexts(QString::fromLatin1("table")); style = new QStyleSheetItem( this, QString::fromLatin1("td") ); style->setContexts(QString::fromLatin1("tr")); style = new QStyleSheetItem( this, QString::fromLatin1("th") ); style->setFontWeight( QFont::Bold ); style->setAlignment( Qt::AlignCenter ); style->setContexts(QString::fromLatin1("tr")); style = new QStyleSheetItem( this, QString::fromLatin1("html") ); } static QStyleSheet* defaultsheet = 0; static QSingleCleanupHandler<QStyleSheet> qt_cleanup_stylesheet; /*! - Returns the application-wide default style sheet. This style sheet is - used by rich text rendering classes such as QSimpleRichText, + Returns the application-wide default style sheet. This style sheet + is used by rich text rendering classes such as QSimpleRichText, QWhatsThis and QMessageBox to define the rendering style and - available tags within rich text documents. It serves also as initial - style sheet for the more complex render widgets QTextEdit and - QTextBrowser. + available tags within rich text documents. It also serves as the + initial style sheet for the more complex render widgets, QTextEdit + and QTextBrowser. \sa setDefaultSheet() */ QStyleSheet* QStyleSheet::defaultSheet() { if (!defaultsheet) { defaultsheet = new QStyleSheet(); qt_cleanup_stylesheet.set( &defaultsheet ); } return defaultsheet; } /*! - Sets the application-wide default style sheet to \a sheet, deleting - any style sheet previously set. The ownership is transferred to - QStyleSheet. + Sets the application-wide default style sheet to \a sheet, + deleting any style sheet previously set. The ownership is + transferred to QStyleSheet. \sa defaultSheet() */ void QStyleSheet::setDefaultSheet( QStyleSheet* sheet) { if ( defaultsheet != sheet ) { if ( defaultsheet ) qt_cleanup_stylesheet.reset(); delete defaultsheet; } defaultsheet = sheet; if ( defaultsheet ) qt_cleanup_stylesheet.set( &defaultsheet ); } /*!\internal Inserts \a style. Any tags generated after this time will be bound to this style. Note that \a style becomes owned by the style sheet and will be deleted when the style sheet is destroyed. */ void QStyleSheet::insert( QStyleSheetItem* style ) { styles.insert(style->name(), style); } /*! - Returns the style with name \a name or 0 if there is no such style. + Returns the style called \a name or 0 if there is no such style. */ QStyleSheetItem* QStyleSheet::item( const QString& name) { if ( name.isNull() ) return 0; return styles[name]; } /*! \overload - Returns the style with name \a name or 0 if there is no such style (const version) + + Returns the style called \a name or 0 if there is no such style + (const version) */ const QStyleSheetItem* QStyleSheet::item( const QString& name) const { if ( name.isNull() ) return 0; return styles[name]; } /*! \preliminary + Generates an internal object for the tag called \a name, given the - attributes \a attr, and using additional information provided - by the mime source factory \a factory. + attributes \a attr, and using additional information provided by + the mime source factory \a factory. - \a context is the optional context of the document, i.e. the path to - look for relative links. This becomes important if the text contains - relative references, for example within image tags. QSimpleRichText - always uses the default mime source factory (see - \l{QMimeSourceFactory::defaultFactory()}) to resolve these references. - The context will then be used to calculate the absolute path. See - QMimeSourceFactory::makeAbsolute() for details. + \a context is the optional context of the document, i.e. the path + to look for relative links. This becomes important if the text + contains relative references, for example within image tags. + QSimpleRichText always uses the default mime source factory (see + \l{QMimeSourceFactory::defaultFactory()}) to resolve these + references. The context will then be used to calculate the + absolute path. See QMimeSourceFactory::makeAbsolute() for details. \a emptyTag and \a doc are for internal use only. - This function should not (yet) be used in application code. + This function should not be used in application code. */ QTextCustomItem* QStyleSheet::tag( const QString& name, const QMap<QString, QString> &attr, const QString& context, const QMimeSourceFactory& factory, bool /*emptyTag */, QTextDocument *doc ) const { - static QString s_img = QString::fromLatin1("img"); - static QString s_hr = QString::fromLatin1("hr"); - const QStyleSheetItem* style = item( name ); // first some known tags if ( !style ) return 0; - if ( style->name() == s_img ) + if ( style->name() == "img" ) return new QTextImage( doc, attr, context, (QMimeSourceFactory&)factory ); - if ( style->name() == s_hr ) + if ( style->name() == "hr" ) return new QTextHorizontalLine( doc, attr, context, (QMimeSourceFactory&)factory ); return 0; } -/*! - Auxiliary function. Converts the plain text string \a plain to a - rich text formatted paragraph while preserving its look. +/*! Auxiliary function. Converts the plain text string \a plain to a + rich text formatted paragraph while preserving most of its look. \a mode defines the whitespace mode. Possible values are \c QStyleSheetItem::WhiteSpacePre (no wrapping, all whitespaces preserved) and \c QStyleSheetItem::WhiteSpaceNormal (wrapping, simplified whitespaces). \sa escape() */ QString QStyleSheet::convertFromPlainText( const QString& plain, QStyleSheetItem::WhiteSpaceMode mode ) { int col = 0; QString rich; rich += "<p>"; for ( int i = 0; i < int(plain.length()); ++i ) { if ( plain[i] == '\n' ){ - if ( col == 1 ) - rich += "<p></p>"; - else - rich += "<br>"; - col = 0; + int c = 1; + while ( i+1 < int(plain.length()) && plain[i+1] == '\n' ) { + i++; + c++; + } + if ( c == 1) + rich += "<br>\n"; + else { + rich += "</p>\n"; + while ( --c > 1 ) + rich += "<br>\n"; + rich += "<p>"; } - else if ( mode == QStyleSheetItem::WhiteSpacePre && plain[i] == '\t' ){ + col = 0; + } else { + if ( mode == QStyleSheetItem::WhiteSpacePre && plain[i] == '\t' ){ rich += 0x00a0U; - while ( col % 4 ) { + ++col; + while ( col % 8 ) { rich += 0x00a0U; ++col; } } else if ( mode == QStyleSheetItem::WhiteSpacePre && plain[i].isSpace() ) rich += 0x00a0U; else if ( plain[i] == '<' ) rich +="<"; else if ( plain[i] == '>' ) rich +=">"; else if ( plain[i] == '&' ) rich +="&"; else rich += plain[i]; ++col; } + } + if ( col != 0 ) rich += "</p>"; return rich; } /*! Auxiliary function. Converts the plain text string \a plain to a rich text formatted string with any HTML meta-characters escaped. \sa convertFromPlainText() */ QString QStyleSheet::escape( const QString& plain) { QString rich; for ( int i = 0; i < int(plain.length()); ++i ) { if ( plain[i] == '<' ) rich +="<"; else if ( plain[i] == '>' ) rich +=">"; else if ( plain[i] == '&' ) rich +="&"; else rich += plain[i]; } return rich; } // Must doc this enum somewhere, and it is logically related to QStyleSheet /*! \enum Qt::TextFormat This enum is used in widgets that can display both plain text and rich text, e.g. QLabel. It is used for deciding whether a text - string should be interpreted as one or the other. This is - normally done by passing one of the enum values to a setTextFormat() + string should be interpreted as one or the other. This is normally + done by passing one of the enum values to a setTextFormat() function. - \value PlainText The text string is interpreted as a plain text string. + \value PlainText The text string is interpreted as a plain text + string. - \value RichText The text string is interpreted as a rich text string - using the current QStyleSheet::defaultSheet(). + \value RichText The text string is interpreted as a rich text + string using the current QStyleSheet::defaultSheet(). - \value AutoText The text string is interpreted as for \c RichText if - QStyleSheet::mightBeRichText() returns TRUE, otherwise as for \c - PlainText. + \value AutoText The text string is interpreted as for \c RichText + if QStyleSheet::mightBeRichText() returns TRUE, otherwise as + \c PlainText. */ /*! Returns TRUE if the string \a text is likely to be rich text; otherwise returns FALSE. - Note: The function uses a fast and therefore simple heuristic. It + This function uses a fast and therefore simple heuristic. It mainly checks whether there is something that looks like a tag - before the first line break. Although the result may be correct for - most common cases, there is no guarantee. + before the first line break. Although the result may be correct + for common cases, there is no guarantee. */ bool QStyleSheet::mightBeRichText( const QString& text) { if ( text.isEmpty() ) return FALSE; if ( text.left(5).lower() == "<!doc" ) return TRUE; int open = 0; while ( open < int(text.length()) && text[open] != '<' && text[open] != '\n' && text[open] != '&') ++open; if ( text[open] == '&' ) { if ( text.mid(open+1,3) == "lt;" ) return TRUE; // support desperate attempt of user to see <...> } else if ( text[open] == '<' ) { int close = text.find('>', open); if ( close > -1 ) { QString tag; for (int i = open+1; i < close; ++i) { if ( text[i].isDigit() || text[i].isLetter() ) tag += text[i]; else if ( !tag.isEmpty() && text[i].isSpace() ) break; else if ( !text[i].isSpace() && (!tag.isEmpty() || text[i] != '!' ) ) return FALSE; // that's not a tag } return defaultSheet()->item( tag.lower() ) != 0; } } return FALSE; } -/*! \fn void QStyleSheet::error( const QString& msg) const +/*! + \fn void QStyleSheet::error( const QString& msg) const This virtual function is called when an error occurs when - processing rich text. Reimplement it if you need to catch - error messages. + processing rich text. Reimplement it if you need to catch error + messages. Errors might occur if some rich text strings contain tags that are - not understood by the stylesheet, if some tags are nested incorrectly, or - if tags are not closed properly. + not understood by the stylesheet, if some tags are nested + incorrectly, or if tags are not closed properly. \a msg is the error message. */ void QStyleSheet::error( const QString& ) const { } /*! Scales the font \a font to the appropriate physical point size corresponding to the logical font size \a logicalSize. - When calling this function, \a font has a point size corresponding to - the logical font size 3. + When calling this function, \a font has a point size corresponding + to the logical font size 3. Logical font sizes range from 1 to 7, with 1 being the smallest. - \sa QStyleSheetItem::logicalFontSize(), - QStyleSheetItem::logicalFontSizeStep(), QFont::setPointSize() + \sa QStyleSheetItem::logicalFontSize(), QStyleSheetItem::logicalFontSizeStep(), QFont::setPointSize() */ void QStyleSheet::scaleFont( QFont& font, int logicalSize ) const { if ( logicalSize < 1 ) logicalSize = 1; if ( logicalSize > 7 ) logicalSize = 7; int baseSize = font.pointSize(); bool pixel = FALSE; if ( baseSize == -1 ) { baseSize = font.pixelSize(); pixel = TRUE; } int s; switch ( logicalSize ) { case 1: s = baseSize/2; break; case 2: s = (8 * baseSize) / 10; break; case 4: s = (12 * baseSize) / 10; break; case 5: s = (15 * baseSize) / 10; break; case 6: s = 2 * baseSize; break; case 7: s = (24 * baseSize) / 10; break; default: s = baseSize; } if ( pixel ) font.setPixelSize( s ); else font.setPointSize( s ); } diff --git a/noncore/apps/opie-write/qstylesheet.h b/noncore/apps/opie-write/qstylesheet.h index bb209fa..cb786f5 100644 --- a/noncore/apps/opie-write/qstylesheet.h +++ b/noncore/apps/opie-write/qstylesheet.h @@ -27,195 +27,199 @@ ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QSTYLESHEET_H #define QSTYLESHEET_H #ifndef QT_H #include "qt3namespace.h" #include "qstring.h" #include "qvaluelist.h" #include "qvector.h" #include "qdict.h" #include "qobject.h" #endif // QT_H template<class Key, class T> class QMap; namespace Qt3 { class QStyleSheet; class QTextDocument; class QStyleSheetItemData; class Q_EXPORT QStyleSheetItem : public Qt { public: QStyleSheetItem( QStyleSheet* parent, const QString& name ); QStyleSheetItem( const QStyleSheetItem & ); ~QStyleSheetItem(); QString name() const; QStyleSheet* styleSheet(); const QStyleSheet* styleSheet() const; enum AdditionalStyleValues { Undefined = - 1}; enum DisplayMode { DisplayBlock, DisplayInline, DisplayListItem, DisplayNone }; DisplayMode displayMode() const; void setDisplayMode(DisplayMode m); int alignment() const; void setAlignment( int f); enum VerticalAlignment { VAlignBaseline, VAlignSub, VAlignSuper }; VerticalAlignment verticalAlignment() const; void setVerticalAlignment( VerticalAlignment valign ); int fontWeight() const; void setFontWeight(int w); int logicalFontSize() const; void setLogicalFontSize(int s); int logicalFontSizeStep() const; void setLogicalFontSizeStep( int s ); int fontSize() const; void setFontSize(int s); QString fontFamily() const; void setFontFamily( const QString& ); int numberOfColumns() const; void setNumberOfColumns(int ncols); QColor color() const; void setColor( const QColor &); bool fontItalic() const; void setFontItalic( bool ); bool definesFontItalic() const; bool fontUnderline() const; void setFontUnderline( bool ); bool definesFontUnderline() const; + bool fontStrikeOut() const; + void setFontStrikeOut( bool ); + bool definesFontStrikeOut() const; + bool isAnchor() const; void setAnchor(bool anc); enum WhiteSpaceMode { WhiteSpaceNormal, WhiteSpacePre, WhiteSpaceNoWrap }; WhiteSpaceMode whiteSpaceMode() const; void setWhiteSpaceMode(WhiteSpaceMode m); enum Margin { MarginLeft, MarginRight, MarginTop, MarginBottom, MarginFirstLine, MarginAll, MarginVertical, MarginHorizontal }; int margin( Margin m) const; void setMargin( Margin, int); enum ListStyle { ListDisc, ListCircle, ListSquare, ListDecimal, ListLowerAlpha, ListUpperAlpha }; ListStyle listStyle() const; void setListStyle( ListStyle ); QString contexts() const; void setContexts( const QString& ); bool allowedInContext( const QStyleSheetItem* ) const; bool selfNesting() const; void setSelfNesting( bool ); void setLineSpacing( int ls ); int lineSpacing() const; private: void init(); QStyleSheetItemData* d; }; #if defined(Q_TEMPLATEDLL) // MOC_SKIP_BEGIN -template class Q_EXPORT QDict<QStyleSheetItem>; -template class Q_EXPORT QValueList< QPtrVector<QStyleSheetItem> >; -template class Q_EXPORT QPtrVector<QStyleSheetItem>; -template class Q_EXPORT QValueList<QStyleSheetItem::ListStyle>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QDict<QStyleSheetItem>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QValueList< QPtrVector<QStyleSheetItem> >; +Q_TEMPLATE_EXTERN template class Q_EXPORT QPtrVector<QStyleSheetItem>; +Q_TEMPLATE_EXTERN template class Q_EXPORT QValueList<QStyleSheetItem::ListStyle>; // MOC_SKIP_END #endif class QTextCustomItem; class Q_EXPORT QStyleSheet : public QObject { Q_OBJECT public: QStyleSheet( QObject *parent=0, const char *name=0 ); virtual ~QStyleSheet(); static QStyleSheet* defaultSheet(); static void setDefaultSheet( QStyleSheet* ); QStyleSheetItem* item( const QString& name); const QStyleSheetItem* item( const QString& name) const; void insert( QStyleSheetItem* item); virtual QTextCustomItem* tag( const QString& name, const QMap<QString, QString> &attr, const QString& context, const QMimeSourceFactory& factory, bool emptyTag, QTextDocument *doc ) const; static QString escape( const QString& ); static QString convertFromPlainText( const QString&, QStyleSheetItem::WhiteSpaceMode mode = QStyleSheetItem::WhiteSpacePre ); static bool mightBeRichText( const QString& ); virtual void scaleFont( QFont& font, int logicalSize ) const; virtual void error( const QString& ) const; private: void init(); QDict<QStyleSheetItem> styles; QStyleSheetItem* nullstyle; }; } // namespace Qt3 #endif // QSTYLESHEET_H diff --git a/noncore/apps/opie-write/qt3namespace.h b/noncore/apps/opie-write/qt3namespace.h index 81c5020..557131a 100644 --- a/noncore/apps/opie-write/qt3namespace.h +++ b/noncore/apps/opie-write/qt3namespace.h @@ -1,28 +1,29 @@ #ifndef QT3NAMESPACE_H #define QT3NAMESPACE_H #include <qnamespace.h> +#include <qstring.h> #define Q_ASSERT ASSERT #define Q_WS_QWS #define QMemArray QArray #define QPtrList QList #define QPtrListIterator QListIterator #define QPtrVector QVector namespace Qt3 { enum NewAlignmentFlags { AlignAuto = 0x0000, AlignJustify = 0x0080, AlignHorizontal_Mask = Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | AlignJustify }; enum NewWidgetFlags { WStaticContents = Qt::WNorthWestGravity }; } #endif // QT3NAMESPACE_H diff --git a/noncore/apps/opie-write/qtextedit.cpp b/noncore/apps/opie-write/qtextedit.cpp index 9c5ea79..82401c6 100644 --- a/noncore/apps/opie-write/qtextedit.cpp +++ b/noncore/apps/opie-write/qtextedit.cpp @@ -1,430 +1,501 @@ /**************************************************************************** ** $Id$ ** ** Implementation of the QTextEdit class ** ** Created : 990101 ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** ** This file is part of the widgets module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "qtextedit.h" #include "qrichtext_p.h" #include "qpainter.h" #include "qpen.h" #include "qbrush.h" #include "qpixmap.h" #include "qfont.h" #include "qcolor.h" #include "qstyle.h" #include "qsize.h" #include "qevent.h" #include "qtimer.h" #include "qapplication.h" #include "qlistbox.h" #include "qvbox.h" #include "qapplication.h" #include "qclipboard.h" #include "qcolordialog.h" #include "qfontdialog.h" #include "qstylesheet.h" #include "qdragobject.h" #include "qurl.h" #include "qcursor.h" #include "qregexp.h" #include "qpopupmenu.h" #define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k) using namespace Qt3; +static bool qt_enable_richtext_copy = FALSE; + struct QUndoRedoInfoPrivate { QTextString text; }; namespace Qt3 { class QTextEditPrivate { public: QTextEditPrivate() - :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE) {} + :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE) + { + for ( int i=0; i<7; i++ ) + id[i] = 0; + } int id[ 7 ]; int preeditStart; int preeditLength; bool ensureCursorVisibleInShowEvent; + QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized }; } static bool block_set_alignment = FALSE; /*! \class QTextEdit qtextedit.h - \brief The QTextEdit widget provides a sophisticated single-page rich text editor. + \brief The QTextEdit widget provides a powerful single-page rich text editor. \ingroup basic \ingroup text \mainclass - QTextEdit is an advanced WYSIWYG editor supporting rich text - formatting. It is optimized to handle large documents and to - respond quickly to user input. + \tableofcontents + + \section1 Introduction and Concepts + + QTextEdit is an advanced WYSIWYG viewer/editor supporting rich + text formatting using HTML-style tags. It is optimized to handle + large documents and to respond quickly to user input. + + QTextEdit has three modes of operation: + \table + \header \i Mode \i Command \i Notes + \row \i Plain Text Editor \i setTextFormat(PlainText) + \i Set text with setText(); text() returns plain text. Text + attributes (e.g. colors) can be set, but plain text is always + returned.<sup>1.</sup> + \row \i Rich Text Editor \i setTextFormat(RichText) + \i Set text with setText(); text() returns rich text. Rich + text editing is fairly limited. You can't set margins or + insert images for example (although you can read and + correctly display files that have margins set and that + include images). This mode is mostly useful for editing small + amounts of rich text. <sup>2.</sup> + \row \i Text Viewer<sup>3.</sup> \i setReadOnly(TRUE) + \i Set text with setText() or append() (which has no undo + history so is faster and uses less memory); text() returns + plain or rich text depending on the textFormat(). This mode + can correctly display a large subset of HTML tags. + \endtable + + <sup>1.</sup><small>We do \e not recommend using QTextEdit to + create syntax highlighting editors because the current API is + insufficient for this purpose. We hope to release a more complete + API that will support syntax highlighting in a later + release.</small> + + <sup>2.</sup><small>A more complete API that supports setting + margins, images, etc., is planned for a later Qt release.</small> + + <sup>3.</sup><small>Qt 3.1 will provide a Log Viewer mode which is + optimised for the fast and memory efficient display of large + amounts of read only text.</small> + + We recommend that you always call setTextFormat() to set the mode + you want to use. If you use \c AutoText then setText() and + append() will try to determine whether the text they are given is + plain text or rich text. If you use \c RichText then setText() and + append() will assume that the text they are given is rich text. + insert() simply inserts the text it is given. QTextEdit works on paragraphs and characters. A paragraph is a formatted string which is word-wrapped to fit into the width of - the widget. A document consists of zero or more paragraphs, - indexed from 0. Characters are indexed on a per-paragraph basis, - also indexed from 0. The words in the paragraph are aligned in - accordance with the paragraph's alignment(). Paragraphs are - separated by hard line breaks. Each character within a paragraph - has its own attributes, for example, font and color. + the widget. By default when reading plain text, two newlines + signify a paragraph. A document consists of zero or more + paragraphs, indexed from 0. Characters are indexed on a + per-paragraph basis, also indexed from 0. The words in the + paragraph are aligned in accordance with the paragraph's + alignment(). Paragraphs are separated by hard line breaks. Each + character within a paragraph has its own attributes, for example, + font and color. + + The text edit documentation uses the following concepts: + \list + \i \e{current format} -- + this is the format at the current cursor position, \e and it + is the format of the selected text if any. + \i \e{current paragraph} -- the paragraph which contains the + cursor. + \endlist QTextEdit can display images (using QMimeSourceFactory), lists and tables. If the text is too large to view within the text edit's viewport, scrollbars will appear. The text edit can load both plain text and HTML files (a subset of HTML 3.2 and 4). The rendering style and the set of valid tags are defined by a - styleSheet(). Change the style sheet with \l{setStyleSheet()}; see + styleSheet(). Custom tags can be created and placed in a custom + style sheet. Change the style sheet with \l{setStyleSheet()}; see QStyleSheet for details. The images identified by image tags are displayed if they can be interpreted using the text edit's \l{QMimeSourceFactory}; see setMimeSourceFactory(). If you want a text browser with more navigation use QTextBrowser. If you just need to display a small piece of rich text use QLabel or QSimpleRichText. If you create a new QTextEdit, and want to allow the user to edit rich text, call setTextFormat(Qt::RichText) to ensure that the text is treated as rich text. (Rich text uses HTML tags to set text formatting attributes. See QStyleSheet for information on the HTML tags that are supported.). If you don't call setTextFormat() explicitly the text edit will guess from the text itself whether it is rich text or plain text. This means that if the text looks like HTML or XML it will probably be interpreted as rich text, so you should call setTextFormat(Qt::PlainText) to preserve such text. - The text edit documentation uses the following concepts: - \list - \i <i>current format</i> -- - this is the format at the current cursor position, \e and it - is the format of the selected text if any. - \i <i>current paragraph</i> -- the paragraph which contains the - cursor. - \endlist + Note that we do not intend to add a full-featured web browser + widget to Qt (because that would easily double Qt's size and only + a few applications would benefit from it). The rich + text support in Qt is designed to provide a fast, portable and + efficient way to add reasonable online help facilities to + applications, and to provide a basis for rich text editors. + \section1 Using QTextEdit as a Display Widget + + QTextEdit can display a large HTML subset, including tables and + images. The text is set or replaced using setText() which deletes any existing text and replaces it with the text passed in the - setText() call. Text can be inserted with insert(), paste() and - pasteSubType(). Text can also be cut(). The entire text is deleted - with clear() and the selected text is deleted with - removeSelectedText(). Selected (marked) text can also be deleted - with del() (which will delete the character to the right of the - cursor if no text is selected). - - The current format's attributes are set with setItalic(), - setBold(), setUnderline(), setFamily() (font family), - setPointSize(), setColor() and setCurrentFont(). The current - paragraph's style is set with setParagType() and its alignment is - set with setAlignment(). - - Use setSelection() to select text. The setSelectionAttributes() - function is used to indicate how selected text should be - displayed. Use hasSelectedText() to find out if any text is - selected. The currently selected text's position is available - using getSelection() and the selected text itself is returned by - selectedText(). The selection can be copied to the clipboard with - copy(), or cut to the clipboard with cut(). It can be deleted with - removeSelectedText(). The entire text can be selected (or - unselected) using selectAll(). QTextEdit supports multiple - selections. Most of the selection functions operate on the default - selection, selection 0. If the user presses a non-selecting key, - e.g. a cursor key without also holding down Shift, all selections - are cleared. - - Set and get the position of the cursor with setCursorPosition() - and getCursorPosition() respectively. When the cursor is moved, - the signals currentFontChanged(), currentColorChanged() and - currentAlignmentChanged() are emitted to reflect the font, color - and alignment at the new cursor position. - - If the text changes, the textChanged() signal is emitted, and if - the user inserts a new line by pressing Return or Enter, - returnPressed() is emitted. The isModified() function will return - TRUE if the text has been modified. - - QTextEdit provides command-based undo and redo. To set the depth - of the command history use setUndoDepth() which defaults to 100 - steps. To undo or redo the last operation call undo() or redo(). - The signals undoAvailable() and redoAvailable() indicate whether - the undo and redo operations can be executed. - - The indent() function is used to reindent a paragraph. It is - useful for code editors, for example in <em>Qt Designer</em>'s - code editor \e{Ctrl+I} invokes the indent() function. + setText() call. If you call setText() with legacy HTML (with + setTextFormat(RichText) in force), and then call text(), the text + that is returned may have different markup, but will render the + same. Text can be inserted with insert(), paste(), pasteSubType() + and append(). Text that is appended does not go into the undo + history; this makes append() faster and consumes less memory. Text + can also be cut(). The entire text is deleted with clear() and the + selected text is deleted with removeSelectedText(). Selected + (marked) text can also be deleted with del() (which will delete + the character to the right of the cursor if no text is selected). Loading and saving text is achieved using setText() and text(), for example: \code QFile file( fileName ); // Read the text from a file if ( file.open( IO_ReadOnly ) ) { QTextStream ts( &file ); textEdit->setText( ts.read() ); } \endcode \code QFile file( fileName ); // Write the text to a file if ( file.open( IO_WriteOnly ) ) { QTextStream ts( &file ); ts << textEdit->text(); textEdit->setModified( FALSE ); } \endcode By default the text edit wraps words at whitespace to fit within the text edit widget. The setWordWrap() function is used to specify the kind of word wrap you want, or \c NoWrap if you don't want any wrapping. Call setWordWrap() to set a fixed pixel width \c FixedPixelWidth, or character column (e.g. 80 column) \c FixedColumnWidth with the pixels or columns specified with setWrapColumnOrWidth(). If you use word wrap to the widget's width \c WidgetWidth, you can specify whether to break on whitespace or anywhere with setWrapPolicy(). The background color is set differently than other widgets, using setPaper(). You specify a brush style which could be a plain color or a complex pixmap. Hypertext links are automatically underlined; this can be changed with setLinkUnderline(). The tab stop width is set with setTabStopWidth(). The zoomIn() and zoomOut() functions can be used to resize the text by increasing (decreasing for zoomOut()) the point size used. Images are not affected by the zoom functions. The lines() function returns the number of lines in the text and paragraphs() returns the number of paragraphs. The number of lines within a particular paragraph is returned by linesOfParagraph(). The length of the entire text in characters is returned by length(). You can scroll to an anchor in the text, e.g. \c{<a name="anchor">} with scrollToAnchor(). The find() function can be used to find and select a given string within the text. + A read-only QTextEdit provides the same functionality as the + (obsolete) QTextView. (QTextView is still supplied for + compatibility with old code.) + + \section2 Read-only key bindings + + When QTextEdit is used read-only the key-bindings are limited to + navigation, and text may only be selected with the mouse: + \table + \header \i Keypresses \i Action + \row \i \e{UpArrow} \i Move one line up + \row \i \e{DownArrow} \i Move one line down + \row \i \e{LeftArrow} \i Move one character left + \row \i \e{RightArrow} \i Move one character right + \row \i \e{PageUp} \i Move one (viewport) page up + \row \i \e{PageDown} \i Move one (viewport) page down + \row \i \e{Home} \i Move to the beginning of the text + \row \i \e{End} \i Move to the end of the text + \row \i \e{Shift+Wheel} \i Scroll the page horizontally (the Wheel is the mouse wheel) + \row \i \e{Ctrl+Wheel} \i Zoom the text + \endtable + + The text edit may be able to provide some meta-information. For + example, the documentTitle() function will return the text from + within HTML \c{<title>} tags. + + The text displayed in a text edit has a \e context. The context is + a path which the text edit's QMimeSourceFactory uses to resolve + the locations of files and images. It is passed to the + mimeSourceFactory() when quering data. (See QTextEdit() and + \l{context()}.) + + \section1 Using QTextEdit as an Editor + + All the information about using QTextEdit as a display widget also + applies here. + + The current format's attributes are set with setItalic(), + setBold(), setUnderline(), setFamily() (font family), + setPointSize(), setColor() and setCurrentFont(). The current + paragraph's alignment is set with setAlignment(). + + Use setSelection() to select text. The setSelectionAttributes() + function is used to indicate how selected text should be + displayed. Use hasSelectedText() to find out if any text is + selected. The currently selected text's position is available + using getSelection() and the selected text itself is returned by + selectedText(). The selection can be copied to the clipboard with + copy(), or cut to the clipboard with cut(). It can be deleted with + removeSelectedText(). The entire text can be selected (or + unselected) using selectAll(). QTextEdit supports multiple + selections. Most of the selection functions operate on the default + selection, selection 0. If the user presses a non-selecting key, + e.g. a cursor key without also holding down Shift, all selections + are cleared. + + Set and get the position of the cursor with setCursorPosition() + and getCursorPosition() respectively. When the cursor is moved, + the signals currentFontChanged(), currentColorChanged() and + currentAlignmentChanged() are emitted to reflect the font, color + and alignment at the new cursor position. + + If the text changes, the textChanged() signal is emitted, and if + the user inserts a new line by pressing Return or Enter, + returnPressed() is emitted. The isModified() function will return + TRUE if the text has been modified. + + QTextEdit provides command-based undo and redo. To set the depth + of the command history use setUndoDepth() which defaults to 100 + steps. To undo or redo the last operation call undo() or redo(). + The signals undoAvailable() and redoAvailable() indicate whether + the undo and redo operations can be executed. + + The indent() function is used to reindent a paragraph. It is + useful for code editors, for example in <em>Qt Designer</em>'s + code editor \e{Ctrl+I} invokes the indent() function. + + \section2 Editing key bindings + The list of key-bindings which are implemented for editing: \table \header \i Keypresses \i Action \row \i \e{Backspace} \i Delete the character to the left of the cursor \row \i \e{Delete} \i Delete the character to the right of the cursor \row \i \e{Ctrl+A} \i Move the cursor to the beginning of the line \row \i \e{Ctrl+B} \i Move the cursor one character left \row \i \e{Ctrl+C} \i Copy the marked text to the clipboard (also \e{Ctrl+Insert} under Windows) \row \i \e{Ctrl+D} \i Delete the character to the right of the cursor \row \i \e{Ctrl+E} \i Move the cursor to the end of the line \row \i \e{Ctrl+F} \i Move the cursor one character right \row \i \e{Ctrl+H} \i Delete the character to the left of the cursor \row \i \e{Ctrl+K} \i Delete to end of line \row \i \e{Ctrl+N} \i Move the cursor one line down \row \i \e{Ctrl+P} \i Move the cursor one line up \row \i \e{Ctrl+V} \i Paste the clipboard text into line edit (also \e{Shift+Insert} under Windows) \row \i \e{Ctrl+X} \i Cut the marked text, copy to clipboard (also \e{Shift+Delete} under Windows) \row \i \e{Ctrl+Z} \i Undo the last operation \row \i \e{Ctrl+Y} \i Redo the last operation \row \i \e{LeftArrow} \i Move the cursor one character left \row \i \e{Ctrl+LeftArrow} \i Move the cursor one word left \row \i \e{RightArrow} \i Move the cursor one character right \row \i \e{Ctrl+RightArrow} \i Move the cursor one word right \row \i \e{UpArrow} \i Move the cursor one line up \row \i \e{Ctrl+UpArrow} \i Move the cursor one word up \row \i \e{DownArrow} \i Move the cursor one line down \row \i \e{Ctrl+Down Arrow} \i Move the cursor one word down \row \i \e{PageUp} \i Move the cursor one page up \row \i \e{PageDown} \i Move the cursor one page down \row \i \e{Home} \i Move the cursor to the beginning of the line \row \i \e{Ctrl+Home} \i Move the cursor to the beginning of the text \row \i \e{End} \i Move the cursor to the end of the line \row \i \e{Ctrl+End} \i Move the cursor to the end of the text \row \i \e{Shift+Wheel} \i Scroll the page horizontally (the Wheel is the mouse wheel) \row \i \e{Ctrl+Wheel} \i Zoom the text \endtable To select (mark) text hold down the Shift key whilst pressing one of the movement keystrokes, for example, <i>Shift+Right Arrow</i> will select the character to the right, and <i>Shift+Ctrl+Right Arrow</i> will select the word to the right, etc. By default the text edit widget operates in insert mode so all text that the user enters is inserted into the text edit and any text to the right of the cursor is moved out of the way. The mode can be changed to overwrite, where new text overwrites any text to the right of the cursor, using setOverwriteMode(). - QTextEdit can also be used as read-only text viewer. Call - setReadOnly( TRUE ) to disable editing. A read-only QTextEdit - provides the same functionality as the (obsolete) QTextView. - (QTextView is still supplied for compatibility with old code.) - - When QTextEdit is used read-only the key-bindings are limited to - navigation, and text may only be selected with the mouse: - \table - \header \i Keypresses \i Action - \row \i \e{UpArrow} \i Move one line up - \row \i \e{DownArrow} \i Move one line down - \row \i \e{LeftArrow} \i Move one character left - \row \i \e{RightArrow} \i Move one character right - \row \i \e{PageUp} \i Move one (viewport) page up - \row \i \e{PageDown} \i Move one (viewport) page down - \row \i \e{Home} \i Move to the beginning of the text - \row \i \e{End} \i Move to the end of the text - \row \i \e{Shift+Wheel} \i Scroll the page horizontally (the Wheel is the mouse wheel) - \row \i \e{Ctrl+Wheel} \i Zoom the text - \endtable - - The text edit may be able to provide some meta-information. For - example, the documentTitle() function will return the text from - within HTML \c{<title>} tags. - - The text displayed in a text edit has a \e context. The context is - a path which the text edit's QMimeSourceFactory uses to resolve - the locations of files and images. It is passed to the - mimeSourceFactory() when quering data. (See QTextEdit() and - \l{context()}.) - - Note that we do not intend to add a full-featured web browser - widget to Qt (because that would easily double Qt's size and only - a few applications would benefit from it). The rich - text support in Qt is designed to provide a fast, portable and - efficient way to add reasonable online help facilities to - applications, and to provide a basis for rich text editors. */ /*! \enum QTextEdit::KeyboardAction This enum is used by doKeyboardAction() to specify which action should be executed: \value ActionBackspace Delete the character to the left of the cursor. \value ActionDelete Delete the character to the right of the cursor. \value ActionReturn Split the paragraph at the cursor position. \value ActionKill If the cursor is not at the end of the paragraph, delete the text from the cursor position until the end of the paragraph. If the cursor is at the end of the paragraph, delete the hard line break at the end of the paragraph - this will cause this paragraph to be joined with the following paragraph. */ /*! \enum QTextEdit::VerticalAlignment This enum is used to set the vertical alignment of the text. \value AlignNormal Normal alignment \value AlignSuperScript Superscript \value AlignSubScript Subscript */ /*! \fn void QTextEdit::copyAvailable (bool yes) This signal is emitted when text is selected or de-selected in the text edit. When text is selected this signal will be emitted with \a yes set to TRUE. If no text has been selected or if the selected text is de-selected this signal is emitted with \a yes set to FALSE. If \a yes is TRUE then copy() can be used to copy the selection to the clipboard. If \a yes is FALSE then copy() does nothing. \sa selectionChanged() */ /*! \fn void QTextEdit::textChanged() This signal is emitted whenever the text in the text edit changes. \sa setText() append() */ /*! \fn void QTextEdit::selectionChanged() This signal is emitted whenever the selection changes. \sa setSelection() copyAvailable() */ /*! \fn QTextDocument *QTextEdit::document() const \internal This function returns the QTextDocument which is used by the text edit. */ /*! \fn void QTextEdit::setDocument( QTextDocument *doc ) \internal This function sets the QTextDocument which should be used by the text edit to \a doc. This can be used, for example, if you want to display a document using multiple views. You would create a QTextDocument and set it to the text edits which should display it. You would need to connect to the textChanged() and selectionChanged() signals of all the text edits and update them all accordingly (preferably with a slight delay for efficiency reasons). */ /*! \enum QTextEdit::CursorAction This enum is used by moveCursor() to specify in which direction the cursor should be moved: \value MoveBackward Moves the cursor one character backward \value MoveWordBackward Moves the cursor one word backward \value MoveForward Moves the cursor one character forward \value MoveWordForward Moves the cursor one word forward \value MoveUp Moves the cursor up one line @@ -547,3970 +618,3978 @@ static bool block_set_alignment = FALSE; /*! \overload void QTextEdit::cursorPositionChanged( int para, int pos ) This signal is emitted if the position of the cursor changed. \a para contains the paragraph index and \a pos contains the character position within the paragraph. \sa setCursorPosition() */ /*! \fn void QTextEdit::returnPressed() This signal is emitted if the user pressed the Return or the Enter key. */ /*! \fn QTextCursor *QTextEdit::textCursor() const Returns the text edit's text cursor. \warning QTextCursor is not in the public API, but in special circumstances you might wish to use it. */ /*! Constructs an empty QTextEdit with parent \a parent and name \a name. */ QTextEdit::QTextEdit( QWidget *parent, const char *name ) : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ), doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) { init(); } /*! Constructs a QTextEdit with parent \a parent and name \a name. The text edit will display the text \a text using context \a context. The \a context is a path which the text edit's QMimeSourceFactory uses to resolve the locations of files and images. It is passed to the mimeSourceFactory() when quering data. For example if the text contains an image tag, \c{<img src="image.png">}, and the context is "path/to/look/in", the QMimeSourceFactory will try to load the image from "path/to/look/in/image.png". If the tag was \c{<img src="/image.png">}, the context will not be used (because QMimeSourceFactory recognizes that we have used an absolute path) and will try to load "/image.png". The context is applied in exactly the same way to \e hrefs, for example, \c{<a href="target.html">Target</a>}, would resolve to "path/to/look/in/target.html". */ QTextEdit::QTextEdit( const QString& text, const QString& context, QWidget *parent, const char *name) : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ), doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) { init(); setText( text, context ); } /*! \reimp */ QTextEdit::~QTextEdit() { delete undoRedoInfo.d; undoRedoInfo.d = 0; delete cursor; delete doc; delete d; } void QTextEdit::init() { setFrameStyle( Sunken ); undoEnabled = TRUE; readonly = TRUE; setReadOnly( FALSE ); d = new QTextEditPrivate; connect( doc, SIGNAL( minimumWidthChanged( int ) ), this, SLOT( documentWidthChanged( int ) ) ); mousePressed = FALSE; inDoubleClick = FALSE; modified = FALSE; onLink = QString::null; overWrite = FALSE; wrapMode = WidgetWidth; wrapWidth = -1; wPolicy = AtWhiteSpace; inDnD = FALSE; doc->setFormatter( new QTextFormatterBreakWords ); + doc->formatCollection()->defaultFormat()->setFont( QScrollView::font() ); + doc->formatCollection()->defaultFormat()->setColor( colorGroup().color( QColorGroup::Text ) ); currentFormat = doc->formatCollection()->defaultFormat(); currentAlignment = Qt3::AlignAuto; viewport()->setBackgroundMode( PaletteBase ); viewport()->setAcceptDrops( TRUE ); - resizeContents( 0, doc->lastParag() ? - ( doc->lastParag()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 ); + resizeContents( 0, doc->lastParagraph() ? + ( doc->lastParagraph()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 ); setKeyCompression( TRUE ); viewport()->setMouseTracking( TRUE ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif cursor = new QTextCursor( doc ); formatTimer = new QTimer( this ); connect( formatTimer, SIGNAL( timeout() ), this, SLOT( formatMore() ) ); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); scrollTimer = new QTimer( this ); connect( scrollTimer, SIGNAL( timeout() ), this, SLOT( autoScrollTimerDone() ) ); interval = 0; changeIntervalTimer = new QTimer( this ); connect( changeIntervalTimer, SIGNAL( timeout() ), this, SLOT( doChangeInterval() ) ); cursorVisible = TRUE; blinkTimer = new QTimer( this ); connect( blinkTimer, SIGNAL( timeout() ), this, SLOT( blinkCursor() ) ); #ifndef QT_NO_DRAGANDDROP dragStartTimer = new QTimer( this ); connect( dragStartTimer, SIGNAL( timeout() ), this, SLOT( startDrag() ) ); #endif formatMore(); blinkCursorVisible = FALSE; viewport()->setFocusProxy( this ); viewport()->setFocusPolicy( WheelFocus ); viewport()->installEventFilter( this ); installEventFilter( this ); } void QTextEdit::paintDocument( bool drawAll, QPainter *p, int cx, int cy, int cw, int ch ) { bool drawCur = hasFocus() || viewport()->hasFocus(); if ( hasSelectedText() || isReadOnly() || !cursorVisible ) drawCur = FALSE; QColorGroup g = colorGroup(); if ( doc->paper() ) g.setBrush( QColorGroup::Base, *doc->paper() ); if ( contentsY() < doc->y() ) { p->fillRect( contentsX(), contentsY(), visibleWidth(), doc->y(), g.brush( QColorGroup::Base ) ); } if ( drawAll && doc->width() - contentsX() < cx + cw ) { p->fillRect( doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch, g.brush( QColorGroup::Base ) ); } p->setBrushOrigin( -contentsX(), -contentsY() ); lastFormatted = doc->draw( p, cx, cy, cw, ch, g, !drawAll, drawCur, cursor ); - if ( lastFormatted == doc->lastParag() ) + if ( lastFormatted == doc->lastParagraph() ) resizeContents( contentsWidth(), doc->height() ); - if ( contentsHeight() < visibleHeight() && ( !doc->lastParag() || doc->lastParag()->isValid() ) && drawAll ) + if ( contentsHeight() < visibleHeight() && ( !doc->lastParagraph() || doc->lastParagraph()->isValid() ) && drawAll ) p->fillRect( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), g.brush( QColorGroup::Base ) ); } /*! \reimp */ void QTextEdit::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) { paintDocument( TRUE, p, cx, cy, cw, ch ); int v; p->setPen( foregroundColor() ); if ( document()->isPageBreakEnabled() && ( v = document()->flow()->pageSize() ) > 0 ) { int l = int(cy / v) * v; while ( l < cy + ch ) { p->drawLine( cx, l, cx + cw - 1, l ); l += v; } } } /*! \reimp */ void QTextEdit::drawContents( QPainter * ) { } /*! \reimp */ bool QTextEdit::event( QEvent *e ) { if ( e->type() == QEvent::AccelOverride && !isReadOnly() ) { QKeyEvent* ke = (QKeyEvent*) e; if ( ke->state() == NoButton || ke->state() == Keypad ) { if ( ke->key() < Key_Escape ) { ke->accept(); } else { switch ( ke->key() ) { case Key_Return: case Key_Enter: case Key_Delete: case Key_Home: case Key_End: case Key_Backspace: ke->accept(); default: break; } } } else if ( ke->state() & ControlButton ) { switch ( ke->key() ) { // Those are too frequently used for application functionality /* case Key_A: case Key_B: case Key_D: case Key_E: case Key_F: case Key_H: case Key_I: case Key_K: case Key_N: case Key_P: case Key_T: */ case Key_C: case Key_V: case Key_X: case Key_Y: case Key_Z: case Key_Left: case Key_Right: case Key_Up: case Key_Down: case Key_Home: case Key_End: case Key_Tab: #if defined (Q_WS_WIN) case Key_Insert: case Key_Delete: #endif ke->accept(); default: break; } } else { switch ( ke->key() ) { #if defined (Q_WS_WIN) case Key_Insert: ke->accept(); #endif default: break; } } } - if ( e->type() == QEvent::Show && d->ensureCursorVisibleInShowEvent ) { + if ( e->type() == QEvent::Show ) { + if ( d->ensureCursorVisibleInShowEvent ) { sync(); ensureCursorVisible(); d->ensureCursorVisibleInShowEvent = FALSE; } + if ( !d->scrollToAnchor.isEmpty() ) { + scrollToAnchor( d->scrollToAnchor ); + d->scrollToAnchor = QString::null; + } + } return QWidget::event( e ); } /*! Processes the key event, \a e. By default key events are used to provide keyboard navigation and text editing. */ void QTextEdit::keyPressEvent( QKeyEvent *e ) { changeIntervalTimer->stop(); interval = 10; bool unknown = FALSE; if ( isReadOnly() ) { if ( !handleReadOnlyKeyEvent( e ) ) QScrollView::keyPressEvent( e ); changeIntervalTimer->start( 100, TRUE ); return; } bool selChanged = FALSE; for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection selChanged = doc->removeSelection( i ) || selChanged; if ( selChanged ) { - cursor->parag()->document()->nextDoubleBuffered = TRUE; + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; repaintChanged(); } bool clearUndoRedoInfo = TRUE; switch ( e->key() ) { case Key_Left: case Key_Right: { // a bit hacky, but can't change this without introducing new enum values for move and keeping the // correct semantics and movement for BiDi and non BiDi text. CursorAction a; - if ( cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) ) + if ( cursor->paragraph()->string()->isRightToLeft() == (e->key() == Key_Right) ) a = e->state() & ControlButton ? MoveWordBackward : MoveBackward; else a = e->state() & ControlButton ? MoveWordForward : MoveForward; moveCursor( a, e->state() & ShiftButton ); break; } case Key_Up: moveCursor( e->state() & ControlButton ? MovePgUp : MoveUp, e->state() & ShiftButton ); break; case Key_Down: moveCursor( e->state() & ControlButton ? MovePgDown : MoveDown, e->state() & ShiftButton ); break; case Key_Home: moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton ); break; case Key_End: moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton ); break; case Key_Prior: moveCursor( MovePgUp, e->state() & ShiftButton ); break; case Key_Next: moveCursor( MovePgDown, e->state() & ShiftButton ); break; case Key_Return: case Key_Enter: if ( doc->hasSelection( QTextDocument::Standard, FALSE ) ) removeSelectedText(); + if ( textFormat() == Qt::RichText && ( e->state() & ControlButton ) ) { + // Ctrl-Enter inserts a line break in rich text mode + insert( QString( QChar( 0x2028) ), TRUE, FALSE, TRUE ); + } else { #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif clearUndoRedoInfo = FALSE; doKeyboardAction( ActionReturn ); emit returnPressed(); + } break; case Key_Delete: #if defined (Q_WS_WIN) if ( e->state() & ShiftButton ) { cut(); break; } else #endif if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { removeSelectedText(); break; } doKeyboardAction( ActionDelete ); clearUndoRedoInfo = FALSE; break; case Key_Insert: if ( e->state() & ShiftButton ) paste(); +#if defined (Q_WS_WIN) + else if ( e->state() & ControlButton ) + copy(); +#endif break; case Key_Backspace: if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { removeSelectedText(); break; } - if ( !cursor->parag()->prev() && - cursor->atParagStart() ) - break; - doKeyboardAction( ActionBackspace ); clearUndoRedoInfo = FALSE; break; case Key_F16: // Copy key on Sun keyboards copy(); break; case Key_F18: // Paste key on Sun keyboards paste(); break; case Key_F20: // Cut key on Sun keyboards cut(); break; default: { if ( e->text().length() && ( !( e->state() & ControlButton ) && !( e->state() & AltButton ) || ( ( e->state() & ControlButton | AltButton ) == (ControlButton|AltButton) ) ) && ( !e->ascii() || e->ascii() >= 32 || e->text() == "\t" ) ) { clearUndoRedoInfo = FALSE; if ( e->key() == Key_Tab ) { - if ( textFormat() == Qt::RichText && - cursor->index() == 0 && cursor->parag()->style() && - cursor->parag()->style()->displayMode() == - QStyleSheetItem::DisplayListItem ) { - cursor->parag()->incDepth(); + if ( textFormat() == Qt::RichText && cursor->paragraph()->isListItem() ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + cursor->paragraph()->setListDepth( cursor->paragraph()->listDepth() +1 ); + clearUndoRedo(); drawCursor( FALSE ); repaintChanged(); drawCursor( TRUE ); break; } } - if ( textFormat() == Qt::RichText && ( !cursor->parag()->style() || - cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayBlock ) && - cursor->index() == 0 && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) { + + if ( textFormat() == Qt::RichText && !cursor->paragraph()->isListItem() ) { + if ( cursor->index() == 0 && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc ); - cursor->parag()->incDepth(); + clearUndoRedo(); drawCursor( FALSE ); repaintChanged(); drawCursor( TRUE ); - } else { + break; + } + } if ( overWrite && !cursor->atParagEnd() ) cursor->remove(); QString t = e->text(); - QTextParag *p = cursor->parag(); + QTextParagraph *p = cursor->paragraph(); if ( p && p->string() && p->string()->isRightToLeft() ) { QChar *c = (QChar *)t.unicode(); int l = t.length(); while( l-- ) { if ( c->mirrored() ) *c = c->mirroredChar(); c++; } } insert( t, TRUE, FALSE, TRUE ); - } break; } else if ( e->state() & ControlButton ) { switch ( e->key() ) { case Key_C: case Key_F16: // Copy key on Sun keyboards copy(); break; case Key_V: paste(); break; case Key_X: cut(); break; case Key_I: case Key_T: case Key_Tab: indent(); break; case Key_A: #if defined(Q_WS_X11) moveCursor( MoveLineStart, e->state() & ShiftButton ); #else selectAll( TRUE ); #endif break; case Key_B: moveCursor( MoveBackward, e->state() & ShiftButton ); break; case Key_F: moveCursor( MoveForward, e->state() & ShiftButton ); break; case Key_D: if ( doc->hasSelection( QTextDocument::Standard ) ) { removeSelectedText(); break; } doKeyboardAction( ActionDelete ); clearUndoRedoInfo = FALSE; break; case Key_H: if ( doc->hasSelection( QTextDocument::Standard ) ) { removeSelectedText(); break; } - if ( !cursor->parag()->prev() && + if ( !cursor->paragraph()->prev() && cursor->atParagStart() ) break; doKeyboardAction( ActionBackspace ); clearUndoRedoInfo = FALSE; break; case Key_E: moveCursor( MoveLineEnd, e->state() & ShiftButton ); break; case Key_N: moveCursor( MoveDown, e->state() & ShiftButton ); break; case Key_P: moveCursor( MoveUp, e->state() & ShiftButton ); break; case Key_Z: + if(e->state() & ShiftButton) + redo(); + else undo(); break; case Key_Y: redo(); break; case Key_K: doKeyboardAction( ActionKill ); break; #if defined(Q_WS_WIN) case Key_Insert: copy(); break; case Key_Delete: del(); break; #endif default: unknown = FALSE; break; } } else { unknown = TRUE; } } } emit cursorPositionChanged( cursor ); - emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); if ( clearUndoRedoInfo ) clearUndoRedo(); changeIntervalTimer->start( 100, TRUE ); if ( unknown ) e->ignore(); } /*! Executes keyboard action \a action. This is normally called by a key event handler. */ void QTextEdit::doKeyboardAction( KeyboardAction action ) { if ( isReadOnly() ) return; if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough return; - lastFormatted = cursor->parag(); + lastFormatted = cursor->paragraph(); drawCursor( FALSE ); bool doUpdateCurrentFormat = TRUE; switch ( action ) { - case ActionDelete: { + case ActionDelete: + if ( !cursor->atParagEnd() ) { checkUndoRedoInfo( UndoRedoInfo::Delete ); if ( !undoRedoInfo.valid() ) { - undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.id = cursor->paragraph()->paragId(); undoRedoInfo.index = cursor->index(); undoRedoInfo.d->text = QString::null; } - undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c; - if ( cursor->parag()->at( cursor->index() )->format() ) { - cursor->parag()->at( cursor->index() )->format()->addRef(); - undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); - } - QTextParag *old = cursor->parag(); - if ( cursor->remove() ) { - if ( old != cursor->parag() && lastFormatted == old ) - lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0; - undoRedoInfo.d->text += "\n"; + undoRedoInfo.d->text.insert( undoRedoInfo.d->text.length(), cursor->paragraph()->at( cursor->index() ), TRUE ); + cursor->remove(); + } else { + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + cursor->gotoNextLetter(); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); } - } break; + break; case ActionBackspace: - if ( textFormat() == Qt::RichText && - cursor->parag()->style() && - cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayListItem && - cursor->index() == 0 ) { - cursor->parag()->decDepth(); - lastFormatted = cursor->parag(); + if ( textFormat() == Qt::RichText && cursor->paragraph()->isListItem() && cursor->index() == 0 ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + int ldepth = cursor->paragraph()->listDepth(); + ldepth = QMAX( ldepth-1, 0 ); + cursor->paragraph()->setListDepth( ldepth ); + if ( ldepth == 0 ) + cursor->paragraph()->setListItem( FALSE ); + clearUndoRedo(); + lastFormatted = cursor->paragraph(); repaintChanged(); drawCursor( TRUE ); return; } + if ( !cursor->atParagStart() ) { checkUndoRedoInfo( UndoRedoInfo::Delete ); if ( !undoRedoInfo.valid() ) { - undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.id = cursor->paragraph()->paragId(); undoRedoInfo.index = cursor->index(); undoRedoInfo.d->text = QString::null; } cursor->gotoPreviousLetter(); - undoRedoInfo.d->text.prepend( QString( cursor->parag()->at( cursor->index() )->c ) ); - if ( cursor->parag()->at( cursor->index() )->format() ) { - cursor->parag()->at( cursor->index() )->format()->addRef(); - undoRedoInfo.d->text.at( 0 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); - } - undoRedoInfo.index = cursor->index(); - if ( cursor->remove() ) { - undoRedoInfo.d->text.remove( 0, 1 ); - undoRedoInfo.d->text.prepend( "\n" ); + undoRedoInfo.d->text.insert( 0, cursor->paragraph()->at( cursor->index() ), TRUE ); undoRedoInfo.index = cursor->index(); - undoRedoInfo.id = cursor->parag()->paragId(); + cursor->remove(); + lastFormatted = cursor->paragraph(); + } else if ( cursor->paragraph()->prev() ){ + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + cursor->gotoPreviousLetter(); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); } - lastFormatted = cursor->parag(); break; - case ActionReturn: { + case ActionReturn: checkUndoRedoInfo( UndoRedoInfo::Return ); if ( !undoRedoInfo.valid() ) { - undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.id = cursor->paragraph()->paragId(); undoRedoInfo.index = cursor->index(); undoRedoInfo.d->text = QString::null; } undoRedoInfo.d->text += "\n"; - cursor->splitAndInsertEmptyParag(); - if ( cursor->parag()->prev() ) { - lastFormatted = cursor->parag()->prev(); + cursor->splitAndInsertEmptyParagraph(); + if ( cursor->paragraph()->prev() ) { + lastFormatted = cursor->paragraph()->prev(); lastFormatted->invalidate( 0 ); } doUpdateCurrentFormat = FALSE; - } break; + break; case ActionKill: - checkUndoRedoInfo( UndoRedoInfo::Delete ); - if ( !undoRedoInfo.valid() ) { - undoRedoInfo.id = cursor->parag()->paragId(); - undoRedoInfo.index = cursor->index(); - undoRedoInfo.d->text = QString::null; - } - if ( cursor->atParagEnd() ) { - undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c; - if ( cursor->parag()->at( cursor->index() )->format() ) { - cursor->parag()->at( cursor->index() )->format()->addRef(); - undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); - } - QTextParag *old = cursor->parag(); - if ( cursor->remove() ) { - if ( old != cursor->parag() && lastFormatted == old ) - lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0; - undoRedoInfo.d->text += "\n"; - } - } else { - int oldLen = undoRedoInfo.d->text.length(); - undoRedoInfo.d->text += cursor->parag()->string()->toString().mid( cursor->index() ); - for ( int i = cursor->index(); i < cursor->parag()->length(); ++i ) { - if ( cursor->parag()->at( i )->format() ) { - cursor->parag()->at( i )->format()->addRef(); - undoRedoInfo.d->text.at( oldLen + i - cursor->index() ).setFormat( cursor->parag()->at( i )->format() ); - } - } - undoRedoInfo.d->text.remove( undoRedoInfo.d->text.length() - 1, 1 ); - cursor->killLine(); - } + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + if ( cursor->atParagEnd() ) + cursor->gotoNextLetter(); + else + cursor->setIndex( cursor->paragraph()->length() - 1 ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); break; } formatMore(); repaintChanged(); ensureCursorVisible(); drawCursor( TRUE ); - - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } - + updateMicroFocusHint(); if ( doUpdateCurrentFormat ) updateCurrentFormat(); setModified(); emit textChanged(); } -void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, int oldLen, QTextString &text, bool fillStyles ) +void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, QTextString &text, bool fillStyles ) { + QDataStream styleStream( undoRedoInfo.styleInformation, IO_WriteOnly ); c2.restoreState(); c1.restoreState(); - if ( c1.parag() == c2.parag() ) { - for ( int i = c1.index(); i < c2.index(); ++i ) { - if ( c1.parag()->at( i )->format() ) { - c1.parag()->at( i )->format()->addRef(); - text.at( oldLen + i - c1.index() ).setFormat( c1.parag()->at( i )->format() ); - } - } + int lastIndex = text.length(); + if ( c1.paragraph() == c2.paragraph() ) { + for ( int i = c1.index(); i < c2.index(); ++i ) + text.insert( lastIndex + i - c1.index(), c1.paragraph()->at( i ), TRUE ); if ( fillStyles ) { - undoRedoInfo.oldAligns[ 0 ] = c1.parag()->alignment(); - undoRedoInfo.oldStyles << c1.parag()->styleSheetItems(); - undoRedoInfo.oldListStyles << c1.parag()->listStyle(); + styleStream << (int) 1; + c1.paragraph()->writeStyleInformation( styleStream ); } } else { - int lastIndex = oldLen; int i; - for ( i = c1.index(); i < c1.parag()->length(); ++i ) { - if ( c1.parag()->at( i )->format() ) { - c1.parag()->at( i )->format()->addRef(); - text.at( lastIndex ).setFormat( c1.parag()->at( i )->format() ); - lastIndex++; - } - } - QTextParag *p = c1.parag()->next(); - while ( p && p != c2.parag() ) { - for ( int i = 0; i < p->length(); ++i ) { - if ( p->at( i )->format() ) { - p->at( i )->format()->addRef(); - text.at( i + lastIndex ).setFormat( p->at( i )->format() ); - } - } - lastIndex += p->length(); + for ( i = c1.index(); i < c1.paragraph()->length()-1; ++i ) + text.insert( lastIndex++, c1.paragraph()->at( i ), TRUE ); + int num = 2; // start and end, being different + text += "\n"; lastIndex++; + QTextParagraph *p = c1.paragraph()->next(); + while ( p && p != c2.paragraph() ) { + for ( i = 0; i < p->length()-1; ++i ) + text.insert( lastIndex++ , p->at( i ), TRUE ); + text += "\n"; num++; lastIndex++; p = p->next(); } - for ( i = 0; i < c2.index(); ++i ) { - if ( c2.parag()->at( i )->format() ) { - c2.parag()->at( i )->format()->addRef(); - text.at( i + lastIndex ).setFormat( c2.parag()->at( i )->format() ); - } - } + for ( i = 0; i < c2.index(); ++i ) + text.insert( i + lastIndex, c2.paragraph()->at( i ), TRUE ); if ( fillStyles ) { - QTextParag *p = c1.parag(); - i = 0; - while ( p ) { - if ( i < (int)undoRedoInfo.oldAligns.size() ) - undoRedoInfo.oldAligns[ i ] = p->alignment(); - undoRedoInfo.oldStyles << p->styleSheetItems(); - undoRedoInfo.oldListStyles << p->listStyle(); - if ( p == c2.parag() ) - break; - p = p->next(); - ++i; - } + styleStream << num; + for ( QTextParagraph *p = c1.paragraph(); --num >= 0; p = p->next() ) + p->writeStyleInformation( styleStream ); } } } /*! Removes the selection \a selNum (by default 0). This does not remove the selected text. \sa removeSelectedText() */ void QTextEdit::removeSelection( int selNum ) { doc->removeSelection( selNum ); repaintChanged(); } /*! Deletes the selected text (i.e. the default selection's text) of the selection \a selNum (by default, 0). If there is no selected text nothing happens. \sa selectedText removeSelection() */ void QTextEdit::removeSelectedText( int selNum ) { if ( isReadOnly() ) return; QTextCursor c1 = doc->selectionStartCursor( selNum ); + c1.restoreState(); QTextCursor c2 = doc->selectionEndCursor( selNum ); + c2.restoreState(); - // ### no support for editing tables yet + // ### no support for editing tables yet, plus security for broken selections if ( c1.nestedDepth() || c2.nestedDepth() ) return; for ( int i = 0; i < (int)doc->numSelections(); ++i ) { if ( i == selNum ) continue; doc->removeSelection( i ); } drawCursor( FALSE ); checkUndoRedoInfo( UndoRedoInfo::RemoveSelected ); if ( !undoRedoInfo.valid() ) { doc->selectionStart( selNum, undoRedoInfo.id, undoRedoInfo.index ); undoRedoInfo.d->text = QString::null; } - int oldLen = undoRedoInfo.d->text.length(); - undoRedoInfo.d->text = doc->selectedText( selNum, FALSE ); - undoRedoInfo.oldAligns.resize( undoRedoInfo.oldAligns.size() + QMAX( 0, c2.parag()->paragId() - c1.parag()->paragId() + 1 ) ); - readFormats( c1, c2, oldLen, undoRedoInfo.d->text, TRUE ); + readFormats( c1, c2, undoRedoInfo.d->text, TRUE ); + + doc->removeSelectedText( selNum, cursor ); if ( cursor->isValid() ) { ensureCursorVisible(); - lastFormatted = cursor->parag(); + lastFormatted = cursor->paragraph(); formatMore(); repaintChanged(); ensureCursorVisible(); drawCursor( TRUE ); clearUndoRedo(); #if defined(Q_WS_WIN) // there seems to be a problem with repainting or erasing the area // of the scrollview which is not the contents on windows if ( contentsHeight() < visibleHeight() ) viewport()->repaint( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), TRUE ); #endif #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); } else { - cursor->setDocument( doc ); - cursor->setParag( doc->firstParag() ); - cursor->setIndex( 0 ); + delete cursor; + cursor = new QTextCursor( doc ); drawCursor( TRUE ); viewport()->repaint( TRUE ); } setModified(); emit textChanged(); emit selectionChanged(); } /*! Moves the text cursor according to \a action. This is normally used by some key event handler. \a select specifies whether the text between the current cursor position and the new position should be selected. */ void QTextEdit::moveCursor( CursorAction action, bool select ) { drawCursor( FALSE ); if ( select ) { if ( !doc->hasSelection( QTextDocument::Standard ) ) - doc->setSelectionStart( QTextDocument::Standard, cursor ); + doc->setSelectionStart( QTextDocument::Standard, *cursor ); moveCursor( action ); - if ( doc->setSelectionEnd( QTextDocument::Standard, cursor ) ) { - cursor->parag()->document()->nextDoubleBuffered = TRUE; + if ( doc->setSelectionEnd( QTextDocument::Standard, *cursor ) ) { + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; repaintChanged(); } else { drawCursor( TRUE ); } ensureCursorVisible(); emit selectionChanged(); emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); } else { bool redraw = doc->removeSelection( QTextDocument::Standard ); moveCursor( action ); if ( !redraw ) { ensureCursorVisible(); drawCursor( TRUE ); } else { - cursor->parag()->document()->nextDoubleBuffered = TRUE; + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; repaintChanged(); ensureCursorVisible(); drawCursor( TRUE ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } if ( redraw ) { emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); emit selectionChanged(); } } drawCursor( TRUE ); updateCurrentFormat(); - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); } /*! \overload */ void QTextEdit::moveCursor( CursorAction action ) { switch ( action ) { case MoveBackward: cursor->gotoPreviousLetter(); break; case MoveWordBackward: cursor->gotoPreviousWord(); break; case MoveForward: cursor->gotoNextLetter(); break; case MoveWordForward: cursor->gotoNextWord(); break; case MoveUp: cursor->gotoUp(); break; case MovePgUp: cursor->gotoPageUp( visibleHeight() ); break; case MoveDown: cursor->gotoDown(); break; case MovePgDown: cursor->gotoPageDown( visibleHeight() ); break; case MoveLineStart: cursor->gotoLineStart(); break; case MoveHome: cursor->gotoHome(); break; case MoveLineEnd: cursor->gotoLineEnd(); break; case MoveEnd: - ensureFormatted( doc->lastParag() ); + ensureFormatted( doc->lastParagraph() ); cursor->gotoEnd(); break; } - - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); updateCurrentFormat(); } /*! \reimp */ void QTextEdit::resizeEvent( QResizeEvent *e ) { QScrollView::resizeEvent( e ); + if ( doc->visibleWidth() == 0 ) + doResize(); } /*! \reimp */ void QTextEdit::viewportResizeEvent( QResizeEvent *e ) { QScrollView::viewportResizeEvent( e ); - if ( e->oldSize().width() != e->size().width() ) + if ( e->oldSize().width() != e->size().width() ) { + bool stayAtBottom = e->oldSize().height() != e->size().height() && + contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height(); doResize(); + if ( stayAtBottom ) + scrollToBottom(); + } } - -static bool blockEnsureCursorVisible = FALSE; /*! Ensures that the cursor is visible by scrolling the text edit if necessary. \sa setCursorPosition() */ void QTextEdit::ensureCursorVisible() { - if ( blockEnsureCursorVisible ) - return; if ( !isVisible() ) { d->ensureCursorVisibleInShowEvent = TRUE; return; } - lastFormatted = cursor->parag(); + lastFormatted = cursor->paragraph(); formatMore(); - QTextStringChar *chr = cursor->parag()->at( cursor->index() ); - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - int x = cursor->parag()->rect().x() + chr->x + cursor->offsetX(); + QTextStringChar *chr = cursor->paragraph()->at( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX(); int y = 0; int dummy; - cursor->parag()->lineHeightOfChar( cursor->index(), &dummy, &y ); - y += cursor->parag()->rect().y() + cursor->offsetY(); + cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y ); + y += cursor->paragraph()->rect().y() + cursor->offsetY(); int w = 1; ensureVisible( x, y + h / 2, w, h / 2 + 2 ); } /*! \internal */ void QTextEdit::drawCursor( bool visible ) { if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() || - !cursor->parag() || - !cursor->parag()->isValid() || + !cursor->paragraph() || + !cursor->paragraph()->isValid() || !selectedText().isEmpty() || ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) || isReadOnly() ) return; QPainter p( viewport() ); - QRect r( cursor->topParag()->rect() ); - cursor->parag()->setChanged( TRUE ); + QRect r( cursor->topParagraph()->rect() ); + cursor->paragraph()->setChanged( TRUE ); p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() ); QPixmap *pix = 0; QColorGroup cg( colorGroup() ); - if ( cursor->parag()->background() ) - cg.setBrush( QColorGroup::Base, *cursor->parag()->background() ); + if ( cursor->paragraph()->background() ) + cg.setBrush( QColorGroup::Base, *cursor->paragraph()->background() ); else if ( doc->paper() ) cg.setBrush( QColorGroup::Base, *doc->paper() ); p.setBrushOrigin( -contentsX(), -contentsY() ); - cursor->parag()->document()->nextDoubleBuffered = TRUE; + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; if ( !cursor->nestedDepth() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); int dist = 5; - if ( ( cursor->parag()->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify ) + if ( ( cursor->paragraph()->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify ) dist = 50; int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist; x = QMAX( x, 0 ); p.setClipRect( QRect( x - contentsX(), r.y() - cursor->totalOffsetY() + cursor->y() - contentsY(), 2 * dist, h ) ); - doc->drawParag( &p, cursor->parag(), x, + doc->drawParagraph( &p, cursor->paragraph(), x, r.y() - cursor->totalOffsetY() + cursor->y(), 2 * dist, h, pix, cg, visible, cursor ); } else { - doc->drawParag( &p, cursor->parag(), r.x() - cursor->totalOffsetX(), + doc->drawParagraph( &p, cursor->paragraph(), r.x() - cursor->totalOffsetX(), r.y() - cursor->totalOffsetY(), r.width(), r.height(), pix, cg, visible, cursor ); } cursorVisible = visible; } enum { IdUndo = 0, IdRedo = 1, IdCut = 2, IdCopy = 3, IdPaste = 4, IdClear = 5, IdSelectAll = 6 }; /*! \reimp */ #ifndef QT_NO_WHEELEVENT void QTextEdit::contentsWheelEvent( QWheelEvent *e ) { if ( isReadOnly() ) { if ( e->state() & ControlButton ) { if ( e->delta() > 0 ) zoomOut(); else if ( e->delta() < 0 ) zoomIn(); return; } } QScrollView::contentsWheelEvent( e ); } #endif /*! \reimp */ void QTextEdit::contentsMousePressEvent( QMouseEvent *e ) { clearUndoRedo(); QTextCursor oldCursor = *cursor; QTextCursor c = *cursor; mousePos = e->pos(); mightStartDrag = FALSE; pressedLink = QString::null; if ( e->button() == LeftButton ) { mousePressed = TRUE; drawCursor( FALSE ); placeCursor( e->pos() ); ensureCursorVisible(); if ( isReadOnly() && linksEnabled() ) { QTextCursor c = *cursor; placeCursor( e->pos(), &c, TRUE ); - if ( c.parag() && c.parag()->at( c.index() ) && - c.parag()->at( c.index() )->isAnchor() ) { - pressedLink = c.parag()->at( c.index() )->anchorHref(); + if ( c.paragraph() && c.paragraph()->at( c.index() ) && + c.paragraph()->at( c.index() )->isAnchor() ) { + pressedLink = c.paragraph()->at( c.index() )->anchorHref(); } } #ifndef QT_NO_DRAGANDDROP if ( doc->inSelection( QTextDocument::Standard, e->pos() ) ) { mightStartDrag = TRUE; drawCursor( TRUE ); dragStartTimer->start( QApplication::startDragTime(), TRUE ); dragStartPos = e->pos(); return; } #endif bool redraw = FALSE; if ( doc->hasSelection( QTextDocument::Standard ) ) { if ( !( e->state() & ShiftButton ) ) { redraw = doc->removeSelection( QTextDocument::Standard ); - doc->setSelectionStart( QTextDocument::Standard, cursor ); + doc->setSelectionStart( QTextDocument::Standard, *cursor ); } else { - redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; } } else { if ( isReadOnly() || !( e->state() & ShiftButton ) ) { - doc->setSelectionStart( QTextDocument::Standard, cursor ); + doc->setSelectionStart( QTextDocument::Standard, *cursor ); } else { - doc->setSelectionStart( QTextDocument::Standard, &c ); - redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; + doc->setSelectionStart( QTextDocument::Standard, c ); + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; } } for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection redraw = doc->removeSelection( i ) || redraw; if ( !redraw ) { drawCursor( TRUE ); } else { repaintChanged(); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } } else if ( e->button() == MidButton ) { bool redraw = doc->removeSelection( QTextDocument::Standard ); if ( !redraw ) { drawCursor( TRUE ); } else { repaintChanged(); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } } if ( *cursor != oldCursor ) updateCurrentFormat(); } /*! \reimp */ void QTextEdit::contentsMouseMoveEvent( QMouseEvent *e ) { if ( mousePressed ) { #ifndef QT_NO_DRAGANDDROP if ( mightStartDrag ) { dragStartTimer->stop(); if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() ) startDrag(); #ifndef QT_NO_CURSOR if ( !isReadOnly() ) viewport()->setCursor( ibeamCursor ); #endif return; } #endif mousePos = e->pos(); handleMouseMove( mousePos ); oldMousePos = mousePos; } #ifndef QT_NO_CURSOR if ( !isReadOnly() && !mousePressed ) { if ( doc->hasSelection( QTextDocument::Standard ) && doc->inSelection( QTextDocument::Standard, e->pos() ) ) viewport()->setCursor( arrowCursor ); else viewport()->setCursor( ibeamCursor ); } #endif updateCursor( e->pos() ); } /*! \reimp */ void QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e ) { QTextCursor oldCursor = *cursor; if ( scrollTimer->isActive() ) scrollTimer->stop(); #ifndef QT_NO_DRAGANDDROP if ( dragStartTimer->isActive() ) dragStartTimer->stop(); if ( mightStartDrag ) { selectAll( FALSE ); mousePressed = FALSE; } #endif if ( mousePressed ) { mousePressed = FALSE; } emit cursorPositionChanged( cursor ); - emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); if ( oldCursor != *cursor ) updateCurrentFormat(); inDoubleClick = FALSE; #ifndef QT_NO_NETWORKPROTOCOL if ( !onLink.isEmpty() && onLink == pressedLink && linksEnabled() ) { QUrl u( doc->context(), onLink, TRUE ); emitLinkClicked( u.toString( FALSE, FALSE ) ); // emitting linkClicked() may result in that the cursor winds // up hovering over a different valid link - check this and // set the appropriate cursor shape updateCursor( e->pos() ); } #endif drawCursor( TRUE ); if ( !doc->hasSelection( QTextDocument::Standard, TRUE ) ) doc->removeSelection( QTextDocument::Standard ); emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); emit selectionChanged(); } /*! \reimp */ void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * ) { QTextCursor c1 = *cursor; QTextCursor c2 = *cursor; - if ( cursor->index() > 0 && !cursor->parag()->at( cursor->index()-1 )->c.isSpace() ) + if ( cursor->index() > 0 && !cursor->paragraph()->at( cursor->index()-1 )->c.isSpace() ) c1.gotoPreviousWord(); - if ( !cursor->parag()->at( cursor->index() )->c.isSpace() && !cursor->atParagEnd() ) + if ( !cursor->paragraph()->at( cursor->index() )->c.isSpace() && !cursor->atParagEnd() ) c2.gotoNextWord(); - doc->setSelectionStart( QTextDocument::Standard, &c1 ); - doc->setSelectionEnd( QTextDocument::Standard, &c2 ); + doc->setSelectionStart( QTextDocument::Standard, c1 ); + doc->setSelectionEnd( QTextDocument::Standard, c2 ); *cursor = c2; repaintChanged(); inDoubleClick = TRUE; mousePressed = TRUE; } #ifndef QT_NO_DRAGANDDROP /*! \reimp */ void QTextEdit::contentsDragEnterEvent( QDragEnterEvent *e ) { if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { e->ignore(); return; } e->acceptAction(); inDnD = TRUE; } /*! \reimp */ void QTextEdit::contentsDragMoveEvent( QDragMoveEvent *e ) { if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { e->ignore(); return; } drawCursor( FALSE ); placeCursor( e->pos(), cursor ); drawCursor( TRUE ); e->acceptAction(); } /*! \reimp */ void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * ) { inDnD = FALSE; } /*! \reimp */ void QTextEdit::contentsDropEvent( QDropEvent *e ) { if ( isReadOnly() ) return; inDnD = FALSE; e->acceptAction(); QString text; bool intern = FALSE; if ( QTextDrag::decode( e, text ) ) { - if ( ( e->source() == this || - e->source() == viewport() ) && - e->action() == QDropEvent::Move ) { + bool hasSel = doc->hasSelection( QTextDocument::Standard ); + bool internalDrag = e->source() == this || e->source() == viewport(); + int dropId, dropIndex; + QTextCursor insertCursor = *cursor; + dropId = cursor->paragraph()->paragId(); + dropIndex = cursor->index(); + if ( hasSel && internalDrag ) { + QTextCursor c1, c2; + int selStartId, selStartIndex; + int selEndId, selEndIndex; + c1 = doc->selectionStartCursor( QTextDocument::Standard ); + c1.restoreState(); + c2 = doc->selectionEndCursor( QTextDocument::Standard ); + c2.restoreState(); + selStartId = c1.paragraph()->paragId(); + selStartIndex = c1.index(); + selEndId = c2.paragraph()->paragId(); + selEndIndex = c2.index(); + if ( ( ( dropId > selStartId ) || + ( dropId == selStartId && dropIndex > selStartIndex ) ) && + ( ( dropId < selEndId ) || + ( dropId == selEndId && dropIndex <= selEndIndex ) ) ) + insertCursor = c1; + if ( dropId == selEndId && dropIndex > selEndIndex ) { + insertCursor = c1; + if ( selStartId == selEndId ) { + insertCursor.setIndex( dropIndex - + ( selEndIndex - selStartIndex ) ); + } else { + insertCursor.setIndex( dropIndex - selEndIndex + + selStartIndex ); + } + } + } + + if ( internalDrag && e->action() == QDropEvent::Move ) { removeSelectedText(); intern = TRUE; } else { doc->removeSelection( QTextDocument::Standard ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } drawCursor( FALSE ); - placeCursor( e->pos(), cursor ); + cursor->setParagraph( insertCursor.paragraph() ); + cursor->setIndex( insertCursor.index() ); drawCursor( TRUE ); if ( !cursor->nestedDepth() ) { insert( text, FALSE, TRUE, FALSE ); } else { if ( intern ) undo(); e->ignore(); } } } #endif void QTextEdit::autoScrollTimerDone() { if ( mousePressed ) handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) ); } void QTextEdit::handleMouseMove( const QPoint& pos ) { if ( !mousePressed ) return; if ( !scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight() ) scrollTimer->start( 100, FALSE ); else if ( scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight() ) scrollTimer->stop(); drawCursor( FALSE ); QTextCursor oldCursor = *cursor; placeCursor( pos ); if ( inDoubleClick ) { QTextCursor cl = *cursor; cl.gotoPreviousWord(); QTextCursor cr = *cursor; cr.gotoNextWord(); - int diff = QABS( oldCursor.parag()->at( oldCursor.index() )->x - mousePos.x() ); - int ldiff = QABS( cl.parag()->at( cl.index() )->x - mousePos.x() ); - int rdiff = QABS( cr.parag()->at( cr.index() )->x - mousePos.x() ); + int diff = QABS( oldCursor.paragraph()->at( oldCursor.index() )->x - mousePos.x() ); + int ldiff = QABS( cl.paragraph()->at( cl.index() )->x - mousePos.x() ); + int rdiff = QABS( cr.paragraph()->at( cr.index() )->x - mousePos.x() ); - if ( cursor->parag()->lineStartOfChar( cursor->index() ) != - oldCursor.parag()->lineStartOfChar( oldCursor.index() ) ) + if ( cursor->paragraph()->lineStartOfChar( cursor->index() ) != + oldCursor.paragraph()->lineStartOfChar( oldCursor.index() ) ) diff = 0xFFFFFF; if ( rdiff < diff && rdiff < ldiff ) *cursor = cr; else if ( ldiff < diff && ldiff < rdiff ) *cursor = cl; else *cursor = oldCursor; } ensureCursorVisible(); bool redraw = FALSE; if ( doc->hasSelection( QTextDocument::Standard ) ) { - redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; } if ( !redraw ) { drawCursor( TRUE ); } else { repaintChanged(); drawCursor( TRUE ); } - if ( currentFormat && currentFormat->key() != cursor->parag()->at( cursor->index() )->format()->key() ) { + if ( currentFormat && currentFormat->key() != cursor->paragraph()->at( cursor->index() )->format()->key() ) { currentFormat->removeRef(); - currentFormat = doc->formatCollection()->format( cursor->parag()->at( cursor->index() )->format() ); + currentFormat = doc->formatCollection()->format( cursor->paragraph()->at( cursor->index() )->format() ); if ( currentFormat->isMisspelled() ) { currentFormat->removeRef(); currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); } emit currentFontChanged( currentFormat->font() ); emit currentColorChanged( currentFormat->color() ); emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); } - if ( currentAlignment != cursor->parag()->alignment() ) { - currentAlignment = cursor->parag()->alignment(); + if ( currentAlignment != cursor->paragraph()->alignment() ) { + currentAlignment = cursor->paragraph()->alignment(); block_set_alignment = TRUE; emit currentAlignmentChanged( currentAlignment ); block_set_alignment = FALSE; } } /*! \fn void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c ) Places the cursor \a c at the character which is closest to position \a pos (in contents coordinates). If \a c is 0, the default text cursor is used. \sa setCursorPosition() */ void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c, bool link ) { if ( !c ) c = cursor; c->restoreState(); - QTextParag *s = doc->firstParag(); + QTextParagraph *s = doc->firstParagraph(); c->place( pos, s, link ); + updateMicroFocusHint(); +} + + +void QTextEdit::updateMicroFocusHint() +{ + QTextCursor c( *cursor ); + if ( d->preeditStart != -1 ) + c.setIndex( d->preeditStart ); + if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); + int h = c.paragraph()->lineHeightOfChar( cursor->index() ); if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); + QFont f = c.paragraph()->at( c.index() )->format()->font(); + setMicroFocusHint( c.x() - contentsX() + frameWidth(), + c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); } } } + + void QTextEdit::formatMore() { if ( !lastFormatted ) return; int bottom = contentsHeight(); int lastBottom = -1; - int to = !sender() ? 2 : 20; + int to = 20; bool firstVisible = FALSE; QRect cr( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); for ( int i = 0; ( i < to || firstVisible ) && lastFormatted; ++i ) { lastFormatted->format(); if ( i == 0 ) firstVisible = lastFormatted->rect().intersects( cr ); else if ( firstVisible ) firstVisible = lastFormatted->rect().intersects( cr ); bottom = QMAX( bottom, lastFormatted->rect().top() + lastFormatted->rect().height() ); lastBottom = lastFormatted->rect().top() + lastFormatted->rect().height(); lastFormatted = lastFormatted->next(); if ( lastFormatted ) lastBottom = -1; } - if ( bottom > contentsHeight() ) + if ( bottom > contentsHeight() ) { resizeContents( contentsWidth(), QMAX( doc->height(), bottom ) ); - else if ( lastBottom != -1 && lastBottom < contentsHeight() ) + } else if ( lastBottom != -1 && lastBottom < contentsHeight() ) { resizeContents( contentsWidth(), QMAX( doc->height(), lastBottom ) ); + if ( contentsHeight() < visibleHeight() ) + updateContents( 0, contentsHeight(), visibleWidth(), + visibleHeight() - contentsHeight() ); + } if ( lastFormatted ) formatTimer->start( interval, TRUE ); else interval = QMAX( 0, interval ); } void QTextEdit::doResize() { if ( wrapMode == FixedPixelWidth ) return; doc->setMinimumWidth( -1 ); resizeContents( 0, 0 ); doc->setWidth( visibleWidth() ); doc->invalidate(); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); interval = 0; formatMore(); repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE ); } /*! \internal */ void QTextEdit::doChangeInterval() { interval = 0; } /*! \reimp */ bool QTextEdit::eventFilter( QObject *o, QEvent *e ) { if ( o == this || o == viewport() ) { if ( e->type() == QEvent::FocusIn ) { blinkTimer->start( QApplication::cursorFlashTime() / 2 ); drawCursor( TRUE ); - - if ( !readonly ) { - // make sure the micro focus hint is updated... - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - - contentsY() + frameWidth(), 0, - cursor->parag()->lineHeightOfChar( cursor->index() ), - TRUE ); - } + updateMicroFocusHint(); } else if ( e->type() == QEvent::FocusOut ) { blinkTimer->stop(); drawCursor( FALSE ); } } return QScrollView::eventFilter( o, e ); } -/*! - Inserts \a text at the current cursor position. If \a indent is TRUE, - the paragraph is re-indented. If \a checkNewLine is TRUE, newline - characters in \a text result in hard line breaks (i.e. new - paragraphs). If \a checkNewLine is FALSE the behaviour of the editor - is undefined if the \a text contains newlines. If \a removeSelected is - TRUE, any selected text (in selection 0) is removed before the text is - inserted. +/*! Inserts \a text at the current cursor position. If \a indent is + TRUE, the paragraph is re-indented. If \a checkNewLine is TRUE, + newline characters in \a text result in hard line breaks (i.e. new + paragraphs). If \a checkNewLine is FALSE and there are newlines in + \a text, the behavior is undefined. If \a checkNewLine is FALSE the + behaviour of the editor is undefined if the \a text contains + newlines. If \a removeSelected is TRUE, any selected text (in + selection 0) is removed before the text is inserted. \sa paste() pasteSubType() */ void QTextEdit::insert( const QString &text, bool indent, bool checkNewLine, bool removeSelected ) { if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough return; QString txt( text ); drawCursor( FALSE ); if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected ) removeSelectedText(); QTextCursor c2 = *cursor; int oldLen = 0; if ( undoEnabled && !isReadOnly() ) { checkUndoRedoInfo( UndoRedoInfo::Insert ); if ( !undoRedoInfo.valid() ) { - undoRedoInfo.id = cursor->parag()->paragId(); + undoRedoInfo.id = cursor->paragraph()->paragId(); undoRedoInfo.index = cursor->index(); undoRedoInfo.d->text = QString::null; } oldLen = undoRedoInfo.d->text.length(); } - lastFormatted = checkNewLine && cursor->parag()->prev() ? - cursor->parag()->prev() : cursor->parag(); + lastFormatted = checkNewLine && cursor->paragraph()->prev() ? + cursor->paragraph()->prev() : cursor->paragraph(); QTextCursor oldCursor = *cursor; cursor->insert( txt, checkNewLine ); if ( doc->useFormatCollection() ) { - doc->setSelectionStart( QTextDocument::Temp, &oldCursor ); - doc->setSelectionEnd( QTextDocument::Temp, cursor ); + doc->setSelectionStart( QTextDocument::Temp, oldCursor ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); doc->removeSelection( QTextDocument::Temp ); } if ( indent && ( txt == "{" || txt == "}" || txt == ":" || txt == "#" ) ) cursor->indent(); formatMore(); repaintChanged(); ensureCursorVisible(); drawCursor( TRUE ); if ( undoEnabled && !isReadOnly() ) { undoRedoInfo.d->text += txt; if ( !doc->preProcessor() ) { for ( int i = 0; i < (int)txt.length(); ++i ) { - if ( txt[ i ] != '\n' && c2.parag()->at( c2.index() )->format() ) { - c2.parag()->at( c2.index() )->format()->addRef(); - undoRedoInfo.d->text.setFormat( oldLen + i, c2.parag()->at( c2.index() )->format(), TRUE ); + if ( txt[ i ] != '\n' && c2.paragraph()->at( c2.index() )->format() ) { + c2.paragraph()->at( c2.index() )->format()->addRef(); + undoRedoInfo.d->text.setFormat( oldLen + i, c2.paragraph()->at( c2.index() )->format(), TRUE ); } c2.gotoNextLetter(); } } } - setModified(); - emit textChanged(); if ( !removeSelected ) { - doc->setSelectionStart( QTextDocument::Standard, &oldCursor ); - doc->setSelectionEnd( QTextDocument::Standard, cursor ); + doc->setSelectionStart( QTextDocument::Standard, oldCursor ); + doc->setSelectionEnd( QTextDocument::Standard, *cursor ); repaintChanged(); } - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); + setModified(); + emit textChanged(); } /*! Inserts \a text in the paragraph \a para and position \a index */ void QTextEdit::insertAt( const QString &text, int para, int index ) { - QTextParag *p = doc->paragAt( para ); + removeSelection( QTextDocument::Standard ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return; QTextCursor tmp = *cursor; - cursor->setParag( p ); + cursor->setParagraph( p ); cursor->setIndex( index ); insert( text, FALSE, TRUE, FALSE ); *cursor = tmp; removeSelection( QTextDocument::Standard ); } /*! Inserts \a text as the paragraph at position \a para. If \a para is -1, the text is appended. */ void QTextEdit::insertParagraph( const QString &text, int para ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( p ) { QTextCursor tmp( doc ); - tmp.setParag( p ); + tmp.setParagraph( p ); tmp.setIndex( 0 ); tmp.insert( text, TRUE ); - tmp.splitAndInsertEmptyParag(); + tmp.splitAndInsertEmptyParagraph(); repaintChanged(); } else { append( text ); } } /*! Removes the paragraph \a para */ void QTextEdit::removeParagraph( int para ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return; for ( int i = 0; i < doc->numSelections(); ++i ) doc->removeSelection( i ); - if ( p == doc->firstParag() && p == doc->lastParag() ) { + if ( p == doc->firstParagraph() && p == doc->lastParagraph() ) { p->remove( 0, p->length() - 1 ); repaintChanged(); return; } drawCursor( FALSE ); - bool resetCursor = cursor->parag() == p; + bool resetCursor = cursor->paragraph() == p; if ( p->prev() ) p->prev()->setNext( p->next() ); else - doc->setFirstParag( p->next() ); + doc->setFirstParagraph( p->next() ); if ( p->next() ) p->next()->setPrev( p->prev() ); else - doc->setLastParag( p->prev() ); - QTextParag *start = p->next(); + doc->setLastParagraph( p->prev() ); + QTextParagraph *start = p->next(); int h = p->rect().height(); delete p; p = start; int dy = -h; while ( p ) { p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 ); p->move( dy ); p->invalidate( 0 ); p->setEndState( -1 ); p = p->next(); } if ( resetCursor ) { - cursor->setParag( doc->firstParag() ); + cursor->setParagraph( doc->firstParagraph() ); cursor->setIndex( 0 ); } repaintChanged(); drawCursor( TRUE ); } /*! Undoes the last operation. If there is no operation to undo, e.g. there is no undo step in the undo/redo history, nothing happens. \sa undoAvailable() redo() undoDepth() */ void QTextEdit::undo() { // XXX FIXME The next line is here because there may be a command // that needs to be 'flushed'. The FIXME is because I am not // 100% certain this is the right call to do this. clearUndoRedo(); if ( isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled ) return; for ( int i = 0; i < (int)doc->numSelections(); ++i ) doc->removeSelection( i ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif clearUndoRedo(); drawCursor( FALSE ); QTextCursor *c = doc->undo( cursor ); if ( !c ) { drawCursor( TRUE ); return; } lastFormatted = 0; ensureCursorVisible(); repaintChanged(); drawCursor( TRUE ); + updateMicroFocusHint(); setModified(); emit textChanged(); - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } } /*! Redoes the last operation. If there is no operation to redo, e.g. there is no redo step in the undo/redo history, nothing happens. \sa redoAvailable() undo() undoDepth() */ void QTextEdit::redo() { if ( isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled ) return; for ( int i = 0; i < (int)doc->numSelections(); ++i ) doc->removeSelection( i ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif clearUndoRedo(); drawCursor( FALSE ); QTextCursor *c = doc->redo( cursor ); if ( !c ) { drawCursor( TRUE ); return; } lastFormatted = 0; ensureCursorVisible(); repaintChanged(); ensureCursorVisible(); drawCursor( TRUE ); + updateMicroFocusHint(); setModified(); emit textChanged(); - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } } /*! Pastes the text from the clipboard into the text edit at the current cursor position. Only plain text is pasted. If there is no text in the clipboard nothing happens. \sa pasteSubType() cut() QTextEdit::copy() */ void QTextEdit::paste() { #ifndef QT_NO_CLIPBOARD if ( isReadOnly() ) return; pasteSubType( "plain" ); - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); #endif } void QTextEdit::checkUndoRedoInfo( UndoRedoInfo::Type t ) { if ( undoRedoInfo.valid() && t != undoRedoInfo.type ) { clearUndoRedo(); } undoRedoInfo.type = t; } /*! Repaints any paragraphs that have changed. Although used extensively internally you shouldn't need to call this yourself. */ void QTextEdit::repaintChanged() { if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() ) return; QPainter p( viewport() ); p.translate( -contentsX(), -contentsY() ); paintDocument( FALSE, &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } /*! Copies the selected text (from selection 0) to the clipboard and deletes it from the text edit. If there is no selected text (in selection 0) nothing happens. \sa QTextEdit::copy() paste() pasteSubType() */ void QTextEdit::cut() { if ( isReadOnly() ) return; - if ( doc->hasSelection( QTextDocument::Standard ) ) { - doc->copySelectedText( QTextDocument::Standard ); + QString t; + if ( doc->hasSelection( QTextDocument::Standard ) && + !( t = doc->selectedText( QTextDocument::Standard, qt_enable_richtext_copy ) ).isEmpty() ) { + QApplication::clipboard()->setText( t ); removeSelectedText(); } - if ( hasFocus() || viewport()->hasFocus() ) { - int h = cursor->parag()->lineHeightOfChar( cursor->index() ); - if ( !readonly ) { - QFont f = cursor->parag()->at( cursor->index() )->format()->font(); - setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), - cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); - } - } + updateMicroFocusHint(); } /*! Copies any selected text (from selection 0) to the clipboard. \sa hasSelectedText() copyAvailable() */ void QTextEdit::copy() { - if ( !doc->selectedText( QTextDocument::Standard ).isEmpty() ) - doc->copySelectedText( QTextDocument::Standard ); + QString t = doc->selectedText( QTextDocument::Standard, qt_enable_richtext_copy ); + if ( doc->hasSelection( QTextDocument::Standard ) && + !t.isEmpty() && t.simplifyWhiteSpace() != "<selstart/>" ) + QApplication::clipboard()->setText( t ); } /*! Re-indents the current paragraph. */ void QTextEdit::indent() { if ( isReadOnly() ) return; drawCursor( FALSE ); if ( !doc->hasSelection( QTextDocument::Standard ) ) cursor->indent(); else doc->indentSelection( QTextDocument::Standard ); repaintChanged(); drawCursor( TRUE ); setModified(); emit textChanged(); } /*! Reimplemented to allow tabbing through links. If \a n is TRUE the tab moves the focus to the next child; if \a n is FALSE the tab moves the focus to the previous child. Returns TRUE if the focus was moved; otherwise returns FALSE. */ bool QTextEdit::focusNextPrevChild( bool n ) { if ( !isReadOnly() || !linksEnabled() ) return FALSE; bool b = doc->focusNextPrevChild( n ); repaintChanged(); if ( b ) //##### this does not work with tables. The focusIndicator //should really be a QTextCursor. Fix 3.1 makeParagVisible( doc->focusIndicator.parag ); return b; } /*! \internal This functions sets the current format to \a f. Only the fields of \a f which are specified by the \a flags are used. */ void QTextEdit::setFormat( QTextFormat *f, int flags ) { if ( doc->hasSelection( QTextDocument::Standard ) ) { drawCursor( FALSE ); - QString str = doc->selectedText( QTextDocument::Standard ); QTextCursor c1 = doc->selectionStartCursor( QTextDocument::Standard ); + c1.restoreState(); QTextCursor c2 = doc->selectionEndCursor( QTextDocument::Standard ); + c2.restoreState(); clearUndoRedo(); undoRedoInfo.type = UndoRedoInfo::Format; - undoRedoInfo.id = c1.parag()->paragId(); + undoRedoInfo.id = c1.paragraph()->paragId(); undoRedoInfo.index = c1.index(); - undoRedoInfo.eid = c2.parag()->paragId(); + undoRedoInfo.eid = c2.paragraph()->paragId(); undoRedoInfo.eindex = c2.index(); - undoRedoInfo.d->text = str; - readFormats( c1, c2, 0, undoRedoInfo.d->text ); + readFormats( c1, c2, undoRedoInfo.d->text ); undoRedoInfo.format = f; undoRedoInfo.flags = flags; clearUndoRedo(); doc->setFormat( QTextDocument::Standard, f, flags ); repaintChanged(); formatMore(); drawCursor( TRUE ); setModified(); emit textChanged(); } if ( currentFormat && currentFormat->key() != f->key() ) { currentFormat->removeRef(); currentFormat = doc->formatCollection()->format( f ); if ( currentFormat->isMisspelled() ) { currentFormat->removeRef(); currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); } emit currentFontChanged( currentFormat->font() ); emit currentColorChanged( currentFormat->color() ); emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); - if ( cursor->index() == cursor->parag()->length() - 1 ) { + if ( cursor->index() == cursor->paragraph()->length() - 1 ) { currentFormat->addRef(); - cursor->parag()->string()->setFormat( cursor->index(), currentFormat, TRUE ); - if ( cursor->parag()->length() == 1 ) { - cursor->parag()->invalidate( 0 ); - cursor->parag()->format(); + cursor->paragraph()->string()->setFormat( cursor->index(), currentFormat, TRUE ); + if ( cursor->paragraph()->length() == 1 ) { + cursor->paragraph()->invalidate( 0 ); + cursor->paragraph()->format(); repaintChanged(); } } } } /*! \reimp */ void QTextEdit::setPalette( const QPalette &p ) { QScrollView::setPalette( p ); if ( textFormat() == PlainText ) { QTextFormat *f = doc->formatCollection()->defaultFormat(); f->setColor( colorGroup().text() ); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } } -/*! +/*! \internal + + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + Sets the paragraph style of the current paragraph to \a dm. If \a dm is QStyleSheetItem::DisplayListItem, the type of the list item is set to \a listStyle. \sa setAlignment() */ void QTextEdit::setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle ) { if ( isReadOnly() ) return; drawCursor( FALSE ); - if ( !doc->hasSelection( QTextDocument::Standard ) ) { - clearUndoRedo(); - undoRedoInfo.type = UndoRedoInfo::ParagType; - QValueList< QPtrVector<QStyleSheetItem> > oldStyles; - undoRedoInfo.oldStyles.clear(); - undoRedoInfo.oldStyles << cursor->parag()->styleSheetItems(); - undoRedoInfo.oldListStyles.clear(); - undoRedoInfo.oldListStyles << cursor->parag()->listStyle(); - undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem; - undoRedoInfo.listStyle = listStyle; - undoRedoInfo.id = cursor->parag()->paragId(); - undoRedoInfo.eid = cursor->parag()->paragId(); - undoRedoInfo.d->text = " "; - undoRedoInfo.index = 1; - clearUndoRedo(); - cursor->parag()->setList( dm == QStyleSheetItem::DisplayListItem, listStyle ); - repaintChanged(); - } else { - QTextParag *start = doc->selectionStart( QTextDocument::Standard ); - QTextParag *end = doc->selectionEnd( QTextDocument::Standard ); - lastFormatted = start; + QTextParagraph *start = cursor->paragraph(); + QTextParagraph *end = start; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph(); + if ( end->paragId() < start->paragId() ) + return; // do not trust our selections + } + clearUndoRedo(); - undoRedoInfo.type = UndoRedoInfo::ParagType; + undoRedoInfo.type = UndoRedoInfo::Style; undoRedoInfo.id = start->paragId(); undoRedoInfo.eid = end->paragId(); - undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem; - undoRedoInfo.listStyle = listStyle; - undoRedoInfo.oldStyles.clear(); - undoRedoInfo.oldListStyles.clear(); - while ( start ) { - undoRedoInfo.oldStyles << start->styleSheetItems(); - undoRedoInfo.oldListStyles << start->listStyle(); - start->setList( dm == QStyleSheetItem::DisplayListItem, listStyle ); - if ( start == end ) - break; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + + while ( start != end->next() ) { + start->setListStyle( listStyle ); + if ( dm == QStyleSheetItem::DisplayListItem ) { + start->setListItem( TRUE ); + if( start->listDepth() == 0 ) + start->setListDepth( 1 ); + } else if ( start->isListItem() ) { + start->setListItem( FALSE ); + start->setListDepth( QMAX( start->listDepth()-1, 0 ) ); + } start = start->next(); } - undoRedoInfo.d->text = " "; - undoRedoInfo.index = 1; + clearUndoRedo(); repaintChanged(); formatMore(); - } drawCursor( TRUE ); setModified(); emit textChanged(); } /*! Sets the alignment of the current paragraph to \a a. Valid alignments are \c Qt::AlignLeft, \c Qt::AlignRight, Qt::AlignJustify and Qt::AlignCenter (which centers horizontally). - \sa setParagType() */ void QTextEdit::setAlignment( int a ) { if ( isReadOnly() || block_set_alignment ) return; drawCursor( FALSE ); - if ( !doc->hasSelection( QTextDocument::Standard ) ) { - if ( cursor->parag()->alignment() != a ) { - clearUndoRedo(); - undoRedoInfo.type = UndoRedoInfo::Alignment; - QMemArray<int> oa( 1 ); - oa[ 0 ] = cursor->parag()->alignment(); - undoRedoInfo.oldAligns = oa; - undoRedoInfo.newAlign = a; - undoRedoInfo.id = cursor->parag()->paragId(); - undoRedoInfo.eid = cursor->parag()->paragId(); - undoRedoInfo.d->text = " "; - undoRedoInfo.index = 1; - clearUndoRedo(); - cursor->parag()->setAlignment( a ); - repaintChanged(); + QTextParagraph *start = cursor->paragraph(); + QTextParagraph *end = start; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph(); + if ( end->paragId() < start->paragId() ) + return; // do not trust our selections } - } else { - QTextParag *start = doc->selectionStart( QTextDocument::Standard ); - QTextParag *end = doc->selectionEnd( QTextDocument::Standard ); - lastFormatted = start; - int len = end->paragId() - start->paragId() + 1; + clearUndoRedo(); - undoRedoInfo.type = UndoRedoInfo::Alignment; + undoRedoInfo.type = UndoRedoInfo::Style; undoRedoInfo.id = start->paragId(); undoRedoInfo.eid = end->paragId(); - QMemArray<int> oa( QMAX( 0, len ) ); - int i = 0; - while ( start ) { - if ( i < (int)oa.size() ) - oa[ i ] = start->alignment(); + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + + while ( start != end->next() ) { start->setAlignment( a ); - if ( start == end ) - break; start = start->next(); - ++i; } - undoRedoInfo.oldAligns = oa; - undoRedoInfo.newAlign = a; - undoRedoInfo.d->text = " "; - undoRedoInfo.index = 1; + clearUndoRedo(); repaintChanged(); formatMore(); - } drawCursor( TRUE ); if ( currentAlignment != a ) { currentAlignment = a; emit currentAlignmentChanged( currentAlignment ); } setModified(); emit textChanged(); } void QTextEdit::updateCurrentFormat() { int i = cursor->index(); if ( i > 0 ) --i; if ( doc->useFormatCollection() && - ( !currentFormat || currentFormat->key() != cursor->parag()->at( i )->format()->key() ) ) { + ( !currentFormat || currentFormat->key() != cursor->paragraph()->at( i )->format()->key() ) ) { if ( currentFormat ) currentFormat->removeRef(); - currentFormat = doc->formatCollection()->format( cursor->parag()->at( i )->format() ); + currentFormat = doc->formatCollection()->format( cursor->paragraph()->at( i )->format() ); if ( currentFormat->isMisspelled() ) { currentFormat->removeRef(); currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); } emit currentFontChanged( currentFormat->font() ); emit currentColorChanged( currentFormat->color() ); emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); } - if ( currentAlignment != cursor->parag()->alignment() ) { - currentAlignment = cursor->parag()->alignment(); + if ( currentAlignment != cursor->paragraph()->alignment() ) { + currentAlignment = cursor->paragraph()->alignment(); block_set_alignment = TRUE; emit currentAlignmentChanged( currentAlignment ); block_set_alignment = FALSE; } } /*! If \a b is TRUE sets the current format to italic; otherwise sets the current format to non-italic. \sa italic() */ void QTextEdit::setItalic( bool b ) { QTextFormat f( *currentFormat ); f.setItalic( b ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Italic ); } /*! If \a b is TRUE sets the current format to bold; otherwise sets the current format to non-bold. \sa bold() */ void QTextEdit::setBold( bool b ) { QTextFormat f( *currentFormat ); f.setBold( b ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Bold ); } /*! If \a b is TRUE sets the current format to underline; otherwise sets the current format to non-underline. \sa underline() */ void QTextEdit::setUnderline( bool b ) { QTextFormat f( *currentFormat ); f.setUnderline( b ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Underline ); } /*! Sets the font family of the current format to \a fontFamily. \sa family() setCurrentFont() */ void QTextEdit::setFamily( const QString &fontFamily ) { QTextFormat f( *currentFormat ); f.setFamily( fontFamily ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Family ); } /*! Sets the point size of the current format to \a s. Note that if \a s is zero or negative, the behaviour of this function is not defined. \sa pointSize() setCurrentFont() setFamily() */ void QTextEdit::setPointSize( int s ) { QTextFormat f( *currentFormat ); f.setPointSize( s ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Size ); } /*! Sets the color of the current format, i.e. of the text, to \a c. \sa color() setPaper() */ void QTextEdit::setColor( const QColor &c ) { QTextFormat f( *currentFormat ); f.setColor( c ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Color ); } /*! Sets the vertical alignment of the current format, i.e. of the text, to \a a. \sa color() setPaper() */ void QTextEdit::setVerticalAlignment( VerticalAlignment a ) { QTextFormat f( *currentFormat ); f.setVAlign( (QTextFormat::VerticalAlignment)a ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::VAlign ); } void QTextEdit::setFontInternal( const QFont &f_ ) { QTextFormat f( *currentFormat ); f.setFont( f_ ); QTextFormat *f2 = doc->formatCollection()->format( &f ); setFormat( f2, QTextFormat::Font ); } QString QTextEdit::text() const { if ( isReadOnly() ) return doc->originalText(); return doc->text(); } /*! \overload Returns the text of paragraph \a para. If textFormat() is \c RichText the text will contain HTML formatting tags. */ QString QTextEdit::text( int para ) const { return doc->text( para ); } /*! \overload Changes the text of the text edit to the string \a text and the context to \a context. Any previous text is removed. \a text may be interpreted either as plain text or as rich text, depending on the textFormat(). The default setting is \c AutoText, i.e. the text edit autodetects the format from \a text. The optional \a context is a path which the text edit's QMimeSourceFactory uses to resolve the locations of files and images. (See \l{QTextEdit::QTextEdit()}.) It is passed to the text edit's QMimeSourceFactory when quering data. Note that the undo/redo history is cleared by this function. \sa text(), setTextFormat() */ void QTextEdit::setText( const QString &text, const QString &context ) { - if ( !isModified() && this->context() == context && this->text() == text ) + if ( !isModified() && isReadOnly() && + this->context() == context && this->text() == text ) return; emit undoAvailable( FALSE ); emit redoAvailable( FALSE ); undoRedoInfo.clear(); doc->commands()->clear(); lastFormatted = 0; cursor->restoreState(); doc->setText( text, context ); if ( wrapMode == FixedPixelWidth ) { resizeContents( wrapWidth, 0 ); doc->setWidth( wrapWidth ); doc->setMinimumWidth( wrapWidth ); } else { doc->setMinimumWidth( -1 ); resizeContents( 0, 0 ); } - cursor->setDocument( doc ); - lastFormatted = doc->firstParag(); - cursor->setParag( doc->firstParag() ); - cursor->setIndex( 0 ); + lastFormatted = doc->firstParagraph(); + delete cursor; + cursor = new QTextCursor( doc ); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); if ( isModified() ) setModified( FALSE ); emit textChanged(); formatMore(); updateCurrentFormat(); + d->scrollToAnchor = QString::null; } /*! \property QTextEdit::text \brief the text edit's text There is no default text. On setting, any previous text is deleted. The text may be interpreted either as plain text or as rich text, depending on the textFormat(). The default setting is \c AutoText, i.e. the text edit autodetects the format of the text. For richtext, calling text() on an editable QTextEdit will cause the text to be regenerated from the textedit. This may mean that the QString returned may not be exactly the same as the one that was set. \sa textFormat */ /*! \property QTextEdit::readOnly \brief whether the text edit is read-only In a read-only text edit the user can only navigate through the text and select text; modifying the text is not possible. This property's default is FALSE. */ /*! Finds the next occurrence of the string, \a expr. Returns TRUE if \a expr is found; otherwise returns FALSE. If \a para and \a index are both null the search begins from the - start of the text. If \a para and \a index are both not null, the - search begins from the \e *\a index character position in the \e - *\a para paragraph. + current cursor position. If \a para and \a index are both not + null, the search begins from the \e *\a index character position + in the \e *\a para paragraph. If \a cs is TRUE the search is case sensitive, otherwise it is case insensitive. If \a wo is TRUE the search looks for whole word matches only; otherwise it searches for any matching text. If \a forward is TRUE (the default) the search works forward from the starting position to the end of the text, otherwise it works backwards to the beginning of the text. If \a expr is found the function returns TRUE. If \a index and \a para are not null, the number of the paragraph in which the first character of the match was found is put into \e *\a para, and the index position of that character within the paragraph is put into \e *\a index. If \a expr is not found the function returns FALSE. If \a index and \a para are not null and \a expr is not found, \e *\a index and \e *\a para are undefined. */ bool QTextEdit::find( const QString &expr, bool cs, bool wo, bool forward, int *para, int *index ) { drawCursor( FALSE ); - doc->removeSelection( QTextDocument::Standard ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif - bool found = doc->find( expr, cs, wo, forward, para, index, cursor ); + QTextCursor findcur = *cursor; + if ( para && index ) { + if ( doc->paragAt( *para ) ) + findcur.gotoPosition( doc->paragAt(*para), *index ); + else + findcur.gotoEnd(); + } else if ( doc->hasSelection( QTextDocument::Standard ) ){ + // maks sure we do not find the same selection again + if ( forward ) + findcur.gotoNextLetter(); + else + findcur.gotoPreviousLetter(); + } + removeSelection( QTextDocument::Standard ); + bool found = doc->find( findcur, expr, cs, wo, forward ); + if ( found ) { + if ( para ) + *para = findcur.paragraph()->paragId(); + if ( index ) + *index = findcur.index(); + *cursor = findcur; + repaintChanged(); ensureCursorVisible(); + } drawCursor( TRUE ); - repaintChanged(); return found; } void QTextEdit::blinkCursor() { if ( !cursorVisible ) return; bool cv = cursorVisible; blinkCursorVisible = !blinkCursorVisible; drawCursor( blinkCursorVisible ); cursorVisible = cv; } /*! Sets the cursor to position \a index in paragraph \a para. \sa getCursorPosition() */ void QTextEdit::setCursorPosition( int para, int index ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return; if ( index > p->length() - 1 ) index = p->length() - 1; drawCursor( FALSE ); - cursor->setParag( p ); + cursor->setParagraph( p ); cursor->setIndex( index ); ensureCursorVisible(); drawCursor( TRUE ); + updateCurrentFormat(); emit cursorPositionChanged( cursor ); - emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); } /*! This function sets the \e *\a para and \e *\a index parameters to the current cursor position. \a para and \a index must be non-null int pointers. \sa setCursorPosition() */ void QTextEdit::getCursorPosition( int *para, int *index ) const { if ( !para || !index ) return; - *para = cursor->parag()->paragId(); + *para = cursor->paragraph()->paragId(); *index = cursor->index(); } /*! Sets a selection which starts at position \a indexFrom in paragraph \a paraFrom and ends at position \a indexTo in paragraph \a paraTo. Existing selections which have a different id (selNum) are not removed, existing selections which have the same id as \a selNum are removed. Uses the selection settings of selection \a selNum. If \a selNum is 0, this is the default selection. The cursor is moved to the end of the selection if \a selNum is 0, otherwise the cursor position remains unchanged. \sa getSelection() selectedText */ void QTextEdit::setSelection( int paraFrom, int indexFrom, int paraTo, int indexTo, int selNum ) { if ( doc->hasSelection( selNum ) ) { doc->removeSelection( selNum ); repaintChanged(); } if ( selNum > doc->numSelections() - 1 ) doc->addSelection( selNum ); - QTextParag *p1 = doc->paragAt( paraFrom ); + QTextParagraph *p1 = doc->paragAt( paraFrom ); if ( !p1 ) return; - QTextParag *p2 = doc->paragAt( paraTo ); + QTextParagraph *p2 = doc->paragAt( paraTo ); if ( !p2 ) return; if ( indexFrom > p1->length() - 1 ) indexFrom = p1->length() - 1; if ( indexTo > p2->length() - 1 ) indexTo = p2->length() - 1; drawCursor( FALSE ); QTextCursor c = *cursor; QTextCursor oldCursor = *cursor; - c.setParag( p1 ); + c.setParagraph( p1 ); c.setIndex( indexFrom ); - cursor->setParag( p2 ); + cursor->setParagraph( p2 ); cursor->setIndex( indexTo ); - doc->setSelectionStart( selNum, &c ); - doc->setSelectionEnd( selNum, cursor ); + doc->setSelectionStart( selNum, c ); + doc->setSelectionEnd( selNum, *cursor ); repaintChanged(); ensureCursorVisible(); if ( selNum != QTextDocument::Standard ) *cursor = oldCursor; drawCursor( TRUE ); } /*! If there is a selection, \e *\a paraFrom is set to the number of the paragraph in which the selection begins and \e *\a paraTo is set to the number of the paragraph in which the selection ends. (They could be the same.) \e *\a indexFrom is set to the index at which the selection begins within \e *\a paraFrom, and \e *\a indexTo is set to the index at which the selection ends within \e *\a paraTo. If there is no selection, \e *\a paraFrom, \e *\a indexFrom, \e *\a paraTo and \e *\a indexTo are all set to -1. \a paraFrom, \a indexFrom, \a paraTo and \a indexTo must be non-null int pointers. The \a selNum is the number of the selection (multiple selections are supported). It defaults to 0 (the default selection). \sa setSelection() selectedText */ void QTextEdit::getSelection( int *paraFrom, int *indexFrom, int *paraTo, int *indexTo, int selNum ) const { if ( !paraFrom || !paraTo || !indexFrom || !indexTo ) return; if ( !doc->hasSelection( selNum ) ) { *paraFrom = -1; *indexFrom = -1; *paraTo = -1; *indexTo = -1; return; } doc->selectionStart( selNum, *paraFrom, *indexFrom ); doc->selectionEnd( selNum, *paraTo, *indexTo ); } /*! \property QTextEdit::textFormat \brief the text format: rich text, plain text or auto text The text format is one of the following: \list \i PlainText - all characters, except newlines, are displayed verbatim, including spaces. Whenever a newline appears in the text the text edit inserts a hard line break and begins a new paragraph. \i RichText - rich text rendering. The available styles are defined in the default stylesheet QStyleSheet::defaultSheet(). \i AutoText - this is the default. The text edit autodetects which rendering style is best, \c PlainText or \c RichText. This is done by using the QStyleSheet::mightBeRichText() function. \endlist */ void QTextEdit::setTextFormat( TextFormat format ) { doc->setTextFormat( format ); } Qt::TextFormat QTextEdit::textFormat() const { return doc->textFormat(); } /*! Returns the number of paragraphs in the text; this could be 0. */ int QTextEdit::paragraphs() const { - return doc->lastParag()->paragId() + 1; + return doc->lastParagraph()->paragId() + 1; } /*! Returns the number of lines in paragraph \a para, or -1 if there is no paragraph with index \a para. */ int QTextEdit::linesOfParagraph( int para ) const { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return -1; return p->lines(); } /*! Returns the length of the paragraph \a para (number of characters), or -1 if there is no paragraph with index \a para */ int QTextEdit::paragraphLength( int para ) const { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return -1; return p->length() - 1; } /*! Returns the number of lines in the text edit; this could be 0. \warning This function may be slow. Lines change all the time during word wrapping, so this function has to iterate over all the paragraphs and get the number of lines from each one individually. */ int QTextEdit::lines() const { - QTextParag *p = doc->firstParag(); + QTextParagraph *p = doc->firstParagraph(); int l = 0; while ( p ) { l += p->lines(); p = p->next(); } return l; } /*! Returns the line number of the line in paragraph \a para in which the character at position \a index appears. The \a index position is relative to the beginning of the paragraph. If there is no such paragraph or no such character at the \a index position (e.g. the index is out of range) -1 is returned. */ int QTextEdit::lineOfChar( int para, int index ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return -1; int idx, line; QTextStringChar *c = p->lineStartOfChar( index, &idx, &line ); if ( !c ) return -1; return line; } void QTextEdit::setModified( bool m ) { bool oldModified = modified; modified = m; if ( modified && doc->oTextValid ) doc->invalidateOriginalText(); if ( oldModified != modified ) emit modificationChanged( modified ); } /*! \property QTextEdit::modified \brief whether the document has been modified by the user */ bool QTextEdit::isModified() const { return modified; } void QTextEdit::setModified() { if ( !isModified() ) setModified( TRUE ); } /*! Returns TRUE if the current format is italic; otherwise returns FALSE. \sa setItalic() */ bool QTextEdit::italic() const { return currentFormat->font().italic(); } /*! Returns TRUE if the current format is bold; otherwise returns FALSE. \sa setBold() */ bool QTextEdit::bold() const { return currentFormat->font().bold(); } /*! Returns TRUE if the current format is underlined; otherwise returns FALSE. \sa setUnderline() */ bool QTextEdit::underline() const { return currentFormat->font().underline(); } /*! Returns the font family of the current format. \sa setFamily() setCurrentFont() setPointSize() */ QString QTextEdit::family() const { return currentFormat->font().family(); } /*! Returns the point size of the font of the current format. \sa setFamily() setCurrentFont() setPointSize() */ int QTextEdit::pointSize() const { return currentFormat->font().pointSize(); } /*! Returns the color of the current format. \sa setColor() setPaper() */ QColor QTextEdit::color() const { return currentFormat->color(); } /*! Returns the font of the current format. \sa setCurrentFont() setFamily() setPointSize() */ QFont QTextEdit::font() const { return currentFormat->font(); } /*! Returns the alignment of the current paragraph. \sa setAlignment() */ int QTextEdit::alignment() const { return currentAlignment; } void QTextEdit::startDrag() { #ifndef QT_NO_DRAGANDDROP mousePressed = FALSE; inDoubleClick = FALSE; - QDragObject *drag = new QTextDrag( doc->selectedText( QTextDocument::Standard ), viewport() ); + QDragObject *drag = new QTextDrag( doc->selectedText( QTextDocument::Standard, qt_enable_richtext_copy ), viewport() ); if ( isReadOnly() ) { drag->dragCopy(); } else { if ( drag->drag() && QDragObject::target() != this && QDragObject::target() != viewport() ) removeSelectedText(); } #endif } /*! If \a select is TRUE (the default), all the text is selected as selection 0. If \a select is FALSE any selected text is unselected, i.e., the default selection (selection 0) is cleared. \sa selectedText */ void QTextEdit::selectAll( bool select ) { if ( !select ) doc->removeSelection( QTextDocument::Standard ); else doc->selectAll( QTextDocument::Standard ); repaintChanged(); emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); emit selectionChanged(); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } void QTextEdit::UndoRedoInfo::clear() { if ( valid() ) { if ( type == Insert || type == Return ) - doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) ); + doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), styleInformation ) ); else if ( type == Format ) doc->addCommand( new QTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) ); - else if ( type == Alignment ) - doc->addCommand( new QTextAlignmentCommand( doc, id, eid, newAlign, oldAligns ) ); - else if ( type == ParagType ) - doc->addCommand( new QTextParagTypeCommand( doc, id, eid, list, listStyle, oldStyles, oldListStyles ) ); - else if ( type != Invalid ) - doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) ); + else if ( type == Style ) + doc->addCommand( new QTextStyleCommand( doc, id, eid, styleInformation ) ); + else if ( type != Invalid ) { + doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), styleInformation ) ); + } } + type = Invalid; d->text = QString::null; id = -1; index = -1; - oldStyles.clear(); - oldListStyles.clear(); - oldAligns.resize( 0 ); + styleInformation = QByteArray(); } /*! If there is some selected text (in selection 0) it is deleted. If there is no selected text (in selection 0) the character to the right of the text cursor is deleted. \sa removeSelectedText() cut() */ void QTextEdit::del() { if ( doc->hasSelection( QTextDocument::Standard ) ) { removeSelectedText(); return; } doKeyboardAction( ActionDelete ); } QTextEdit::UndoRedoInfo::UndoRedoInfo( QTextDocument *dc ) : type( Invalid ), doc( dc ) { d = new QUndoRedoInfoPrivate; d->text = QString::null; id = -1; index = -1; } QTextEdit::UndoRedoInfo::~UndoRedoInfo() { delete d; } bool QTextEdit::UndoRedoInfo::valid() const { - return d->text.length() > 0 && id >= 0 && index >= 0; + return id >= 0 && type != Invalid; } /*! \internal Resets the current format to the default format. */ void QTextEdit::resetFormat() { setAlignment( Qt3::AlignAuto ); setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc ); setFormat( doc->formatCollection()->defaultFormat(), QTextFormat::Format ); } /*! Returns the QStyleSheet which is currently used in this text edit. \sa setStyleSheet() */ QStyleSheet* QTextEdit::styleSheet() const { return doc->styleSheet(); } /*! Sets the stylesheet to use with this text edit to \a styleSheet. Changes will only take effect for new text added with setText() or append(). \sa styleSheet() */ void QTextEdit::setStyleSheet( QStyleSheet* styleSheet ) { doc->setStyleSheet( styleSheet ); } /*! \property QTextEdit::paper \brief the background (paper) brush. The brush that is currently used to draw the background of the text edit. The initial setting is an empty brush. */ void QTextEdit::setPaper( const QBrush& pap ) { doc->setPaper( new QBrush( pap ) ); viewport()->setBackgroundColor( pap.color() ); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } QBrush QTextEdit::paper() const { if ( doc->paper() ) return *doc->paper(); return QBrush(); } /*! \property QTextEdit::linkUnderline \brief whether hypertext links will be underlined If TRUE (the default) hypertext links will be displayed underlined. If FALSE links will not be displayed underlined. */ void QTextEdit::setLinkUnderline( bool b ) { - if ( b == doc->underlineLinks() ) - return; doc->setUnderlineLinks( b ); - updateStyles(); } bool QTextEdit::linkUnderline() const { return doc->underlineLinks(); } /*! Sets the text edit's mimesource factory to \a factory. See QMimeSourceFactory for further details. \sa mimeSourceFactory() */ void QTextEdit::setMimeSourceFactory( QMimeSourceFactory* factory ) { doc->setMimeSourceFactory( factory ); } /*! Returns the QMimeSourceFactory which is currently used by this text edit. \sa setMimeSourceFactory() */ QMimeSourceFactory* QTextEdit::mimeSourceFactory() const { return doc->mimeSourceFactory(); } /*! Returns how many pixels high the text edit needs to be to display all the text if the text edit is \a w pixels wide. */ int QTextEdit::heightForWidth( int w ) const { int oldw = doc->width(); doc->doLayout( 0, w ); int h = doc->height(); doc->setWidth( oldw ); doc->invalidate(); ( (QTextEdit*)this )->formatMore(); return h; } /*! Appends the text \a text to the end of the text edit. Note that the undo/redo history is cleared by this function. */ void QTextEdit::append( const QString &text ) { // flush and clear the undo/redo stack if necessary - if ( isReadOnly() && undoRedoInfo.valid() ) { undoRedoInfo.clear(); doc->commands()->clear(); - } + doc->removeSelection( QTextDocument::Standard ); TextFormat f = doc->textFormat(); if ( f == AutoText ) { if ( QStyleSheet::mightBeRichText( text ) ) f = RichText; else f = PlainText; } - if ( f == PlainText ) { + + drawCursor( FALSE ); QTextCursor oldc( *cursor ); - ensureFormatted( doc->lastParag() ); - bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight() - - ( horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0 ); - if ( !scrollToEnd ) - blockEnsureCursorVisible = TRUE; + ensureFormatted( doc->lastParagraph() ); + bool atBottom = contentsY() >= contentsHeight() - visibleHeight(); cursor->gotoEnd(); if ( cursor->index() > 0 ) - cursor->splitAndInsertEmptyParag(); + cursor->splitAndInsertEmptyParagraph(); QTextCursor oldCursor2 = *cursor; + + if ( f == Qt::PlainText ) { cursor->insert( text, TRUE ); - if ( doc->useFormatCollection() && currentFormat != cursor->parag()->at( cursor->index() )->format() ) { - doc->setSelectionStart( QTextDocument::Temp, &oldCursor2 ); - doc->setSelectionEnd( QTextDocument::Temp, cursor ); + if ( doc->useFormatCollection() && + currentFormat != cursor->paragraph()->at( cursor->index() )->format() ) { + doc->setSelectionStart( QTextDocument::Temp, oldCursor2 ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); doc->removeSelection( QTextDocument::Temp ); } + } else { + if ( cursor->paragraph()->prev() ) + cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change + doc->setRichTextInternal( text ); + } formatMore(); repaintChanged(); - ensureCursorVisible(); - drawCursor( TRUE ); + if ( atBottom ) + scrollToBottom(); *cursor = oldc; - if ( !scrollToEnd ) - blockEnsureCursorVisible = FALSE; - } else if ( f == RichText ) { - doc->setRichTextInternal( text ); - repaintChanged(); - } + if ( !isReadOnly() ) + cursorVisible = TRUE; setModified(); emit textChanged(); } /*! \property QTextEdit::hasSelectedText \brief whether some text is selected in selection 0 */ bool QTextEdit::hasSelectedText() const { return doc->hasSelection( QTextDocument::Standard ); } /*!\property QTextEdit::selectedText \brief The selected text (from selection 0) or an empty string if there is no currently selected text (in selection 0). The text is always returned as \c PlainText regardless of the text format. In a future version of Qt an HTML subset \e may be returned depending on the text format. \sa hasSelectedText */ QString QTextEdit::selectedText() const { return doc->selectedText( QTextDocument::Standard ); } bool QTextEdit::handleReadOnlyKeyEvent( QKeyEvent *e ) { switch( e->key() ) { case Key_Down: setContentsPos( contentsX(), contentsY() + 10 ); break; case Key_Up: setContentsPos( contentsX(), contentsY() - 10 ); break; case Key_Left: setContentsPos( contentsX() - 10, contentsY() ); break; case Key_Right: setContentsPos( contentsX() + 10, contentsY() ); break; case Key_PageUp: setContentsPos( contentsX(), contentsY() - visibleHeight() ); break; case Key_PageDown: setContentsPos( contentsX(), contentsY() + visibleHeight() ); break; case Key_Home: setContentsPos( contentsX(), 0 ); break; case Key_End: setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); break; case Key_F16: // Copy key on Sun keyboards copy(); break; #ifndef QT_NO_NETWORKPROTOCOL case Key_Return: case Key_Enter: case Key_Space: { if ( !doc->focusIndicator.href.isEmpty() ) { QUrl u( doc->context(), doc->focusIndicator.href, TRUE ); emitLinkClicked( u.toString( FALSE, FALSE ) ); #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif } } break; #endif default: if ( e->state() & ControlButton ) { switch ( e->key() ) { case Key_C: case Key_F16: // Copy key on Sun keyboards copy(); break; } } return FALSE; } return TRUE; } /*! Returns the context of the edit. The context is a path which the text edit's QMimeSourceFactory uses to resolve the locations of files and images. \sa text */ QString QTextEdit::context() const { return doc->context(); } /*! \property QTextEdit::documentTitle \brief the title of the document parsed from the text. For \c PlainText the title will be an empty string. For \c RichText the title will be the text between the \c{<title>} tags, if present, otherwise an empty string. */ QString QTextEdit::documentTitle() const { return doc->attributes()[ "title" ]; } -void QTextEdit::makeParagVisible( QTextParag *p ) +void QTextEdit::makeParagVisible( QTextParagraph *p ) { setContentsPos( contentsX(), QMIN( p->rect().y(), contentsHeight() - visibleHeight() ) ); } /*! Scrolls the text edit to make the text at the anchor called \a name visible, if it can be found in the document. If the anchor isn't found no scrolling will occur. An anchor is defined using the HTML anchor tag, e.g. \c{<a name="target">}. */ void QTextEdit::scrollToAnchor( const QString& name ) { + if ( !isVisible() ) { + d->scrollToAnchor = name; + return; + } if ( name.isEmpty() ) return; sync(); QTextCursor cursor( doc ); - QTextParag* last = doc->lastParag(); - do { - QTextStringChar* c = cursor.parag()->at( cursor.index() ); + QTextParagraph* last = doc->lastParagraph(); + for (;;) { + QTextStringChar* c = cursor.paragraph()->at( cursor.index() ); if( c->isAnchor() ) { QString a = c->anchorName(); if ( a == name || (a.contains( '#' ) && QStringList::split( '#', a ).contains( name ) ) ) { - setContentsPos( contentsX(), QMIN( cursor.parag()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) ); - return; + setContentsPos( contentsX(), QMIN( cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) ); + break; } } + if ( cursor.paragraph() == last && cursor.atParagEnd() ) + break; cursor.gotoNextLetter(); - } while( cursor.parag() != last || !cursor.atParagEnd() ); + } } /*! If there is an anchor at position \a pos (in contents coordinates), its name is returned, otherwise an empty string is returned. */ QString QTextEdit::anchorAt( const QPoint& pos ) { QTextCursor c( doc ); placeCursor( pos, &c ); - return c.parag()->at( c.index() )->anchorHref(); + return c.paragraph()->at( c.index() )->anchorHref(); } void QTextEdit::documentWidthChanged( int w ) { resizeContents( QMAX( visibleWidth(), w), contentsHeight() ); } -/*! - Updates all the rendering styles used to display the text. You will - probably want to call this function after calling setStyleSheet(). +/*! \internal + + This function does nothing */ void QTextEdit::updateStyles() { - doc->updateStyles(); - updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } void QTextEdit::setDocument( QTextDocument *dc ) { if ( dc == doc ) return; doc = dc; - cursor->setDocument( doc ); + delete cursor; + cursor = new QTextCursor( doc ); clearUndoRedo(); + undoRedoInfo.doc = doc; lastFormatted = 0; } #ifndef QT_NO_CLIPBOARD /*! Pastes the text with format \a subtype from the clipboard into the text edit at the current cursor position. The \a subtype can be "plain" or "html". If there is no text with format \a subtype in the clipboard nothing happens. \sa paste() cut() QTextEdit::copy() */ void QTextEdit::pasteSubType( const QCString& subtype ) { QCString st = subtype; QString t = QApplication::clipboard()->text(st); + if ( doc->hasSelection( QTextDocument::Standard ) ) + removeSelectedText(); if ( !t.isEmpty() ) { + if ( t.startsWith( "<selstart/>" ) ) { + t.remove( 0, 11 ); + QTextCursor oldC = *cursor; + lastFormatted = cursor->paragraph(); + if ( lastFormatted->prev() ) + lastFormatted = lastFormatted->prev(); + doc->setRichTextInternal( t, cursor ); + + if ( undoEnabled && !isReadOnly() ) { + doc->setSelectionStart( QTextDocument::Temp, oldC ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + + checkUndoRedoInfo( UndoRedoInfo::Insert ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = oldC.paragraph()->paragId(); + undoRedoInfo.index = oldC.index(); + undoRedoInfo.d->text = QString::null; + } + int oldLen = undoRedoInfo.d->text.length(); + if ( !doc->preProcessor() ) { + QString txt = doc->selectedText( QTextDocument::Temp ); + undoRedoInfo.d->text += txt; + for ( int i = 0; i < (int)txt.length(); ++i ) { + if ( txt[ i ] != '\n' && oldC.paragraph()->at( oldC.index() )->format() ) { + oldC.paragraph()->at( oldC.index() )->format()->addRef(); + undoRedoInfo.d->text. + setFormat( oldLen + i, oldC.paragraph()->at( oldC.index() )->format(), TRUE ); + } + oldC.gotoNextLetter(); + } + } + undoRedoInfo.clear(); + removeSelection( QTextDocument::Temp ); + } + + formatMore(); + setModified(); + emit textChanged(); + repaintChanged(); + ensureCursorVisible(); + return; + } + #if defined(Q_OS_WIN32) // Need to convert CRLF to LF int index = t.find( QString::fromLatin1("\r\n"), 0 ); while ( index != -1 ) { t.replace( index, 2, QChar('\n') ); index = t.find( "\r\n", index ); } #elif defined(Q_OS_MAC) //need to convert CR to LF for( unsigned int index = 0; index < t.length(); index++ ) if(t[index] == '\r') t[index] = '\n'; #endif for ( int i=0; (uint) i<t.length(); i++ ) { if ( t[ i ] < ' ' && t[ i ] != '\n' && t[ i ] != '\t' ) t[ i ] = ' '; } if ( !t.isEmpty() ) insert( t, FALSE, TRUE, TRUE ); } } #ifndef QT_NO_MIMECLIPBOARD /*! Prompts the user to choose a type from a list of text types available, then copies text from the clipboard (if there is any) into the text edit at the current text cursor position. Any selected text (in selection 0) is first deleted. */ void QTextEdit::pasteSpecial( const QPoint& pt ) { QCString st = pickSpecial( QApplication::clipboard()->data(), TRUE, pt ); if ( !st.isEmpty() ) pasteSubType( st ); } #endif #ifndef QT_NO_MIME QCString QTextEdit::pickSpecial( QMimeSource* ms, bool always_ask, const QPoint& pt ) { if ( ms ) { #ifndef QT_NO_POPUPMENU QPopupMenu popup( this, "qt_pickspecial_menu" ); QString fmt; int n = 0; QDict<void> done; for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { int semi = fmt.find( ";" ); if ( semi >= 0 ) fmt = fmt.left( semi ); if ( fmt.left( 5 ) == "text/" ) { fmt = fmt.mid( 5 ); if ( !done.find( fmt ) ) { done.insert( fmt,(void*)1 ); popup.insertItem( fmt, i ); n++; } } } if ( n ) { int i = n ==1 && !always_ask ? popup.idAt( 0 ) : popup.exec( pt ); if ( i >= 0 ) return popup.text(i).latin1(); } #else QString fmt; for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { int semi = fmt.find( ";" ); if ( semi >= 0 ) fmt = fmt.left( semi ); if ( fmt.left( 5 ) == "text/" ) { fmt = fmt.mid( 5 ); return fmt.latin1(); } } #endif } return QCString(); } #endif // QT_NO_MIME #endif // QT_NO_CLIPBOARD /*! \enum QTextEdit::WordWrap This enum defines the QTextEdit's word wrap modes. The following values are valid: \value NoWrap Do not wrap the text. \value WidgetWidth Wrap the text at the current width of the widget (this is the default). Wrapping is at whitespace by default; this can be changed with setWrapPolicy(). \value FixedPixelWidth Wrap the text at a fixed number of pixels from the widget's left side. The number of pixels is set with wrapColumnOrWidth(). \value FixedColumnWidth Wrap the text at a fixed number of character columns from the widget's left side. The number of characters is set with wrapColumnOrWidth(). This is useful if you need formatted text that can also be displayed gracefully on devices with monospaced fonts, for example a standard VT100 terminal, where you might set wrapColumnOrWidth() to 80. \sa setWordWrap() wordWrap() */ /*! \property QTextEdit::wordWrap \brief the word wrap mode The default mode is \c WidgetWidth which causes words to be wrapped at the right edge of the text edit. Wrapping occurs at whitespace, keeping whole words intact. If you want wrapping to occur within words use setWrapPolicy(). If you set a wrap mode of \c FixedPixelWidth or \c FixedColumnWidth you should also call setWrapColumnOrWidth() with the width you want. \sa WordWrap, wrapColumnOrWidth, wrapPolicy, */ void QTextEdit::setWordWrap( WordWrap mode ) { if ( wrapMode == mode ) return; wrapMode = mode; switch ( mode ) { case NoWrap: document()->formatter()->setWrapEnabled( FALSE ); document()->formatter()->setWrapAtColumn( -1 ); doc->setWidth( visibleWidth() ); doc->setMinimumWidth( -1 ); doc->invalidate(); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); interval = 0; formatMore(); break; case WidgetWidth: document()->formatter()->setWrapEnabled( TRUE ); document()->formatter()->setWrapAtColumn( -1 ); doResize(); break; case FixedPixelWidth: document()->formatter()->setWrapEnabled( TRUE ); document()->formatter()->setWrapAtColumn( -1 ); if ( wrapWidth < 0 ) wrapWidth = 200; setWrapColumnOrWidth( wrapWidth ); break; case FixedColumnWidth: if ( wrapWidth < 0 ) wrapWidth = 80; document()->formatter()->setWrapEnabled( TRUE ); document()->formatter()->setWrapAtColumn( wrapWidth ); setWrapColumnOrWidth( wrapWidth ); break; } } QTextEdit::WordWrap QTextEdit::wordWrap() const { return wrapMode; } /*! \property QTextEdit::wrapColumnOrWidth \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped If the wrap mode is \c FixedPixelWidth, the value is the number of pixels from the left edge of the text edit at which text should be wrapped. If the wrap mode is \c FixedColumnWidth, the value is the column number (in character columns) from the left edge of the text edit at which text should be wrapped. \sa wordWrap */ void QTextEdit::setWrapColumnOrWidth( int value ) { wrapWidth = value; if ( wrapMode == FixedColumnWidth ) { document()->formatter()->setWrapAtColumn( wrapWidth ); resizeContents( 0, 0 ); doc->setWidth( visibleWidth() ); doc->setMinimumWidth( -1 ); } else if (wrapMode == FixedPixelWidth ) { document()->formatter()->setWrapAtColumn( -1 ); resizeContents( wrapWidth, 0 ); doc->setWidth( wrapWidth ); doc->setMinimumWidth( wrapWidth ); } else { return; } doc->invalidate(); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); interval = 0; formatMore(); } int QTextEdit::wrapColumnOrWidth() const { if ( wrapMode == WidgetWidth ) return visibleWidth(); return wrapWidth; } /*! \enum QTextEdit::WrapPolicy This enum defines where text can be wrapped in word wrap mode. The following values are valid: \value AtWhiteSpace Break lines at whitespace, e.g. spaces or newlines. \value Anywhere Break anywhere, including within words. \value AtWordBoundary Don't use this deprecated value (it is a synonym for AtWhiteSpace which you should use instead). \sa setWrapPolicy() */ /*! \property QTextEdit::wrapPolicy \brief the word wrap policy, at whitespace or anywhere Defines where text can be wrapped when word wrap mode is not \c NoWrap. The choices are \c AtWhiteSpace (the default) and \c Anywhere. \sa wordWrap */ void QTextEdit::setWrapPolicy( WrapPolicy policy ) { if ( wPolicy == policy ) return; wPolicy = policy; QTextFormatter *formatter; if ( policy == AtWhiteSpace ) formatter = new QTextFormatterBreakWords; else formatter = new QTextFormatterBreakInWords; formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() ); formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) ); document()->setFormatter( formatter ); doc->invalidate(); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); interval = 0; formatMore(); } QTextEdit::WrapPolicy QTextEdit::wrapPolicy() const { return wPolicy; } /*! Deletes all the text in the text edit. \sa cut() removeSelectedText() setText() */ void QTextEdit::clear() { // make clear undoable doc->selectAll( QTextDocument::Temp ); removeSelectedText( QTextDocument::Temp ); setContentsPos( 0, 0 ); if ( cursor->isValid() ) cursor->restoreState(); doc->clear( TRUE ); - cursor->setDocument( doc ); - cursor->setParag( doc->firstParag() ); - cursor->setIndex( 0 ); + delete cursor; + cursor = new QTextCursor( doc ); lastFormatted = 0; updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); emit cursorPositionChanged( cursor ); - emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); } int QTextEdit::undoDepth() const { return document()->undoDepth(); } /*! \property QTextEdit::length \brief the number of characters in the text */ int QTextEdit::length() const { return document()->length(); } /*! \property QTextEdit::tabStopWidth \brief the tab stop width in pixels */ int QTextEdit::tabStopWidth() const { return document()->tabStopWidth(); } void QTextEdit::setUndoDepth( int d ) { document()->setUndoDepth( d ); } void QTextEdit::setTabStopWidth( int ts ) { document()->setTabStops( ts ); doc->invalidate(); - lastFormatted = doc->firstParag(); + lastFormatted = doc->firstParagraph(); interval = 0; formatMore(); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } /*! \reimp */ QSize QTextEdit::sizeHint() const { // ### calculate a reasonable one return QSize( 100, 100 ); } void QTextEdit::clearUndoRedo() { undoRedoInfo.clear(); emit undoAvailable( doc->commands()->isUndoAvailable() ); emit redoAvailable( doc->commands()->isRedoAvailable() ); } -/*! This function gets the format of the character at position \a +/*! \internal + + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + + This function gets the format of the character at position \a index in paragraph \a para. Sets \a font to the character's font, \a color to the character's color and \a verticalAlignment to the character's vertical alignment. Returns FALSE if \a para or \a index is out of range otherwise returns TRUE. */ bool QTextEdit::getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment ) { if ( !font || !color ) return FALSE; - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return FALSE; if ( index < 0 || index >= p->length() ) return FALSE; *font = p->at( index )->format()->font(); *color = p->at( index )->format()->color(); *verticalAlignment = (VerticalAlignment)p->at( index )->format()->vAlign(); return TRUE; } -/*! This function gets the format of the paragraph \a para. Sets \a +/*! \internal + + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + + This function gets the format of the paragraph \a para. Sets \a font to the paragraphs's font, \a color to the paragraph's color, \a verticalAlignment to the paragraph's vertical alignment, \a alignment to the paragraph's alignment, \a displayMode to the paragraph's display mode, \a listStyle to the paragraph's list style (if the display mode is QStyleSheetItem::DisplayListItem) and \a listDepth to the depth of the list (if the display mode is QStyleSheetItem::DisplayListItem). Returns FALSE if \a para is out of range otherwise returns TRUE. */ bool QTextEdit::getParagraphFormat( int para, QFont *font, QColor *color, VerticalAlignment *verticalAlignment, int *alignment, QStyleSheetItem::DisplayMode *displayMode, QStyleSheetItem::ListStyle *listStyle, int *listDepth ) { if ( !font || !color || !alignment || !displayMode || !listStyle ) return FALSE; - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return FALSE; - *font = p->paragFormat()->font(); - *color = p->paragFormat()->color(); - *verticalAlignment = (VerticalAlignment)p->paragFormat()->vAlign(); + *font = p->at(0)->format()->font(); + *color = p->at(0)->format()->color(); + *verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign(); *alignment = p->alignment(); - *displayMode = p->style() ? p->style()->displayMode() : QStyleSheetItem::DisplayBlock; + *displayMode = p->isListItem() ? QStyleSheetItem::DisplayListItem : QStyleSheetItem::DisplayBlock; *listStyle = p->listStyle(); *listDepth = p->listDepth(); return TRUE; } /*! This function is called to create a right mouse button popup menu at the document position \a pos. If you want to create a custom popup menu, reimplement this function and return the created popup menu. Ownership of the popup menu is transferred to the caller. */ QPopupMenu *QTextEdit::createPopupMenu( const QPoint& pos ) { #ifndef QT_NO_POPUPMENU QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" ); if ( !isReadOnly() ) { d->id[ IdUndo ] = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) ); d->id[ IdRedo ] = popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) ); popup->insertSeparator(); } #ifndef QT_NO_CLIPBOARD if ( !isReadOnly() ) d->id[ IdCut ] = popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) ); d->id[ IdCopy ] = popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) ); if ( !isReadOnly() ) d->id[ IdPaste ] = popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) ); #endif if ( !isReadOnly() ) { d->id[ IdClear ] = popup->insertItem( tr( "Clear" ) ); popup->insertSeparator(); } #if defined(Q_WS_X11) d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) ); #else d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) + ACCEL_KEY( A ) ); #endif popup->setItemEnabled( d->id[ IdUndo ], !isReadOnly() && doc->commands()->isUndoAvailable() ); popup->setItemEnabled( d->id[ IdRedo ], !isReadOnly() && doc->commands()->isRedoAvailable() ); #ifndef QT_NO_CLIPBOARD popup->setItemEnabled( d->id[ IdCut ], !isReadOnly() && doc->hasSelection( QTextDocument::Standard, TRUE ) ); popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( QTextDocument::Standard, TRUE ) ); popup->setItemEnabled( d->id[ IdPaste ], !isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); #endif popup->setItemEnabled( d->id[ IdClear ], !isReadOnly() && !text().isEmpty() ); popup->setItemEnabled( d->id[ IdSelectAll ], (bool)text().length() ); return popup; #else return 0; #endif } /*! \overload + \obsolete This function is called to create a right mouse button popup menu. If you want to create a custom popup menu, reimplement this function and return the created popup menu. Ownership of the popup menu is transferred to the caller. + + This function is only called if createPopupMenu( const QPoint & ) + returns 0. */ QPopupMenu *QTextEdit::createPopupMenu() { return 0; } /*! \reimp */ void QTextEdit::setFont( const QFont &f ) { QFont old( QScrollView::font() ); QScrollView::setFont( f ); doc->setMinimumWidth( -1 ); - - // ### that is a bit hacky - static short diff = 1; - diff *= -1; - doc->setWidth( visibleWidth() + diff ); - - int s = f.pointSize(); - bool usePixels = FALSE; - if ( s == -1 ) { - s = f.pixelSize(); - usePixels = TRUE; - } - doc->updateFontSizes( s, usePixels ); - doc->updateFontAttributes( f, old ); - lastFormatted = doc->firstParag(); + doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() ); + lastFormatted = doc->firstParagraph(); formatMore(); repaintChanged(); } /*! \fn QTextEdit::zoomIn() \overload Zooms in on the text by by making the base font size one point larger and recalculating all font sizes. This does not change the size of any images. \sa zoomOut() */ /*! \fn QTextEdit::zoomOut() \overload Zooms out on the text by by making the base font size one point smaller and recalculating all font sizes. This does not change the size of any images. \sa zoomIn() */ /*! Zooms in on the text by by making the base font size \a range points larger and recalculating all font sizes. This does not change the size of any images. \sa zoomOut() */ void QTextEdit::zoomIn( int range ) { QFont f( QScrollView::font() ); f.setPointSize( f.pointSize() + range ); setFont( f ); } /*! Zooms out on the text by making the base font size \a range points smaller and recalculating all font sizes. This does not change the size of any images. \sa zoomIn() */ void QTextEdit::zoomOut( int range ) { QFont f( QScrollView::font() ); f.setPointSize( QMAX( 1, f.pointSize() - range ) ); setFont( f ); } /*! Zooms the text by making the base font size \a size points and recalculating all font sizes. This does not change the size of any images. */ void QTextEdit::zoomTo( int size ) { QFont f( QScrollView::font() ); f.setPointSize( size ); setFont( f ); } /*! \internal QTextEdit is optimized for large amounts text. One of its optimizations is to format only the visible text, formatting the rest on demand, e.g. as the user scrolls, so you don't usually need to call this function. In some situations you may want to force the whole text to be formatted. For example, if after calling setText(), you wanted to know the height of the document (using contentsHeight()), you would call this function first. */ void QTextEdit::sync() { - QTextParag *p = lastFormatted; - while ( p ) { - p->format(); - p = p->next(); + while ( lastFormatted ) { + lastFormatted->format(); + lastFormatted = lastFormatted->next(); } resizeContents( contentsWidth(), doc->height() ); } /*! \reimp */ void QTextEdit::setEnabled( bool b ) { QScrollView::setEnabled( b ); if ( !b ) { blinkTimer->stop(); drawCursor( FALSE ); } if ( textFormat() == PlainText ) { QTextFormat *f = doc->formatCollection()->defaultFormat(); f->setColor( colorGroup().text() ); updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } if ( b ) { blinkTimer->start( QApplication::cursorFlashTime() / 2 ); drawCursor( TRUE ); } } /*! Sets the background color of selection number \a selNum to \a back and specifies whether the text of this selection should be inverted with \a invertText. This only works for \a selNum > 0. The default selection (\a selNum == 0) gets its attributes from the colorGroup() of this widget. */ void QTextEdit::setSelectionAttributes( int selNum, const QColor &back, bool invertText ) { if ( selNum < 1 ) return; if ( selNum > doc->numSelections() ) doc->addSelection( selNum ); doc->setSelectionColor( selNum, back ); doc->setInvertSelectionText( selNum, invertText ); } /*! \reimp */ void QTextEdit::windowActivationChange( bool ) { if ( !isVisible() ) return; if ( palette().active() != palette().inactive() ) updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); } void QTextEdit::setReadOnly( bool b ) { if ( readonly == b ) return; readonly = b; #ifndef QT_NO_CURSOR if ( readonly ) viewport()->setCursor( arrowCursor ); else viewport()->setCursor( ibeamCursor ); #endif } /*! Scrolls to the bottom of the document and does formatting if required */ void QTextEdit::scrollToBottom() { sync(); setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); } /*! Returns the rectangle of the paragraph \a para in contents coordinates, or an invalid rectangle if \a para is out of range. */ QRect QTextEdit::paragraphRect( int para ) const { QTextEdit *that = (QTextEdit *)this; that->sync(); - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return QRect( -1, -1, -1, -1 ); return p->rect(); } /*! Returns the paragraph which is at position \a pos (in contents coordinates), or -1 if there is no paragraph with index \a pos. */ int QTextEdit::paragraphAt( const QPoint &pos ) const { QTextCursor c( doc ); - c.place( pos, doc->firstParag() ); - if ( c.parag() ) - return c.parag()->paragId(); + c.place( pos, doc->firstParagraph() ); + if ( c.paragraph() ) + return c.paragraph()->paragId(); return -1; } /*! Returns the index of the character (relative to its paragraph) at position \a pos (in contents coordinates). If \a para is not null, \e *\a para is set to this paragraph. If there is no character at \a pos, -1 is returned. */ int QTextEdit::charAt( const QPoint &pos, int *para ) const { QTextCursor c( doc ); - c.place( pos, doc->firstParag() ); - if ( c.parag() ) { + c.place( pos, doc->firstParagraph() ); + if ( c.paragraph() ) { if ( para ) - *para = c.parag()->paragId(); + *para = c.paragraph()->paragId(); return c.index(); } return -1; } /*! Sets the background color of the paragraph \a para to \a bg */ void QTextEdit::setParagraphBackgroundColor( int para, const QColor &bg ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return; p->setBackgroundColor( bg ); repaintChanged(); } /*! Clears the background color of the paragraph \a para, so that the default color is used again. */ void QTextEdit::clearParagraphBackground( int para ) { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return; p->clearBackgroundColor(); repaintChanged(); } /*! Returns the background color of the paragraph \a para or an invalid color if \a para is out of range or the paragraph has no background set */ QColor QTextEdit::paragraphBackgroundColor( int para ) const { - QTextParag *p = doc->paragAt( para ); + QTextParagraph *p = doc->paragAt( para ); if ( !p ) return QColor(); QColor *c = p->backgroundColor(); if ( c ) return *c; return QColor(); } /*! \property QTextEdit::undoRedoEnabled \brief whether undo/redo is enabled The default is TRUE. */ void QTextEdit::setUndoRedoEnabled( bool b ) { undoEnabled = b; } bool QTextEdit::isUndoRedoEnabled() const { return undoEnabled; } /*! Returns whether undo is available */ bool QTextEdit::isUndoAvailable() const { return doc->commands()->isUndoAvailable() || undoRedoInfo.valid(); } /*! Returns whether redo is available */ bool QTextEdit::isRedoAvailable() const { return doc->commands()->isRedoAvailable(); } -void QTextEdit::ensureFormatted( QTextParag *p ) +void QTextEdit::ensureFormatted( QTextParagraph *p ) { while ( !p->isValid() ) { if ( !lastFormatted ) return; formatMore(); } } /*! \internal */ void QTextEdit::updateCursor( const QPoint & pos ) { if ( isReadOnly() && linksEnabled() ) { QTextCursor c = *cursor; placeCursor( pos, &c, TRUE ); #ifndef QT_NO_NETWORKPROTOCOL - if ( c.parag() && c.parag()->at( c.index() ) && - c.parag()->at( c.index() )->isAnchor() && - !c.parag()->at( c.index() )->anchorHref().isEmpty() ) { - if ( c.index() < c.parag()->length() - 1 ) - onLink = c.parag()->at( c.index() )->anchorHref(); + if ( c.paragraph() && c.paragraph()->at( c.index() ) && + c.paragraph()->at( c.index() )->isAnchor() && + !c.paragraph()->at( c.index() )->anchorHref().isEmpty() ) { + if ( c.index() < c.paragraph()->length() - 1 ) + onLink = c.paragraph()->at( c.index() )->anchorHref(); else onLink = QString::null; #ifndef QT_NO_CURSOR viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor ); #endif QUrl u( doc->context(), onLink, TRUE ); emitHighlighted( u.toString( FALSE, FALSE ) ); } else { #ifndef QT_NO_CURSOR viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); #endif onLink = QString::null; emitHighlighted( QString::null ); } #endif } } void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c ) { placeCursor( pos, c, FALSE ); } diff --git a/noncore/apps/opie-write/qtextedit.h b/noncore/apps/opie-write/qtextedit.h index b4e5701..64e8f45 100644 --- a/noncore/apps/opie-write/qtextedit.h +++ b/noncore/apps/opie-write/qtextedit.h @@ -1,448 +1,454 @@ /**************************************************************************** ** $Id$ ** ** Definition of the QTextEdit class ** ** Created : 990101 ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** ** This file is part of the widgets module of the Qt GUI Toolkit. ** ** This file may be distributed under the terms of the Q Public License ** as defined by Trolltech AS of Norway and appearing in the file ** LICENSE.QPL included in the packaging of this file. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition ** licenses may use this file in accordance with the Qt Commercial License ** Agreement provided with the Software. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for ** information about Qt Commercial License Agreements. ** See http://www.trolltech.com/qpl/ for QPL licensing information. ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef QTEXTEDIT_H #define QTEXTEDIT_H #ifndef QT_H #include "qscrollview.h" #include "qstylesheet.h" #include "qvector.h" #include "qvaluelist.h" #endif // QT_H class QPainter; class QKeyEvent; class QResizeEvent; class QMouseEvent; class QTimer; class QFont; class QColor; struct QUndoRedoInfoPrivate; class QPopupMenu; namespace Qt3 { class QTextString; class QTextDocument; class QTextCursor; class QTextCommand; -class QTextParag; +class QTextParagraph; class QTextFormat; +class QTextEdit; class QTextBrowser; +class QTextString; class QTextEditPrivate; class Q_EXPORT QTextEdit : public QScrollView { friend class QTextBrowser; Q_OBJECT Q_ENUMS( WordWrap WrapPolicy ) Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat ) Q_PROPERTY( QString text READ text WRITE setText ) Q_PROPERTY( QBrush paper READ paper WRITE setPaper ) Q_PROPERTY( bool linkUnderline READ linkUnderline WRITE setLinkUnderline ) Q_PROPERTY( QString documentTitle READ documentTitle ) Q_PROPERTY( int length READ length ) Q_PROPERTY( WordWrap wordWrap READ wordWrap WRITE setWordWrap ) Q_PROPERTY( int wrapColumnOrWidth READ wrapColumnOrWidth WRITE setWrapColumnOrWidth ) Q_PROPERTY( WrapPolicy wrapPolicy READ wrapPolicy WRITE setWrapPolicy ) Q_PROPERTY( bool hasSelectedText READ hasSelectedText ) Q_PROPERTY( QString selectedText READ selectedText ) Q_PROPERTY( int undoDepth READ undoDepth WRITE setUndoDepth ) Q_PROPERTY( bool overwriteMode READ isOverwriteMode WRITE setOverwriteMode ) Q_PROPERTY( bool modified READ isModified WRITE setModified DESIGNABLE false ) Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) Q_PROPERTY( bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled ) Q_PROPERTY( int tabStopWidth READ tabStopWidth WRITE setTabStopWidth ) public: enum WordWrap { NoWrap, WidgetWidth, FixedPixelWidth, FixedColumnWidth }; enum WrapPolicy { AtWordBoundary, Anywhere, AtWhiteSpace = AtWordBoundary // deprecated, don't use }; enum KeyboardAction { ActionBackspace, ActionDelete, ActionReturn, ActionKill }; enum CursorAction { MoveBackward, MoveForward, MoveWordBackward, MoveWordForward, MoveUp, MoveDown, MoveLineStart, MoveLineEnd, MoveHome, MoveEnd, MovePgUp, MovePgDown }; enum VerticalAlignment { AlignNormal, AlignSuperScript, AlignSubScript }; QTextEdit( const QString& text, const QString& context = QString::null, QWidget* parent=0, const char* name=0); QTextEdit( QWidget* parent=0, const char* name=0 ); virtual ~QTextEdit(); void setPalette( const QPalette & ); QString text() const; QString text( int para ) const; TextFormat textFormat() const; QString context() const; QString documentTitle() const; void getSelection( int *paraFrom, int *indexFrom, int *paraTo, int *indexTo, int selNum = 0 ) const; virtual bool find( const QString &expr, bool cs, bool wo, bool forward = TRUE, int *para = 0, int *index = 0 ); int paragraphs() const; int lines() const; int linesOfParagraph( int para ) const; int lineOfChar( int para, int chr ); int length() const; QRect paragraphRect( int para ) const; int paragraphAt( const QPoint &pos ) const; int charAt( const QPoint &pos, int *para ) const; int paragraphLength( int para ) const; QStyleSheet* styleSheet() const; QMimeSourceFactory* mimeSourceFactory() const; QBrush paper() const; bool linkUnderline() const; int heightForWidth( int w ) const; bool hasSelectedText() const; QString selectedText() const; bool isUndoAvailable() const; bool isRedoAvailable() const; WordWrap wordWrap() const; int wrapColumnOrWidth() const; WrapPolicy wrapPolicy() const; int tabStopWidth() const; QString anchorAt( const QPoint& pos ); QSize sizeHint() const; bool isReadOnly() const { return readonly; } void getCursorPosition( int *parag, int *index ) const; bool isModified() const; bool italic() const; bool bold() const; bool underline() const; QString family() const; int pointSize() const; QColor color() const; QFont font() const; int alignment() const; int undoDepth() const; + + // do not use, will go away virtual bool getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment ); + // do not use, will go away virtual bool getParagraphFormat( int para, QFont *font, QColor *color, VerticalAlignment *verticalAlignment, int *alignment, QStyleSheetItem::DisplayMode *displayMode, QStyleSheetItem::ListStyle *listStyle, int *listDepth ); + + bool isOverwriteMode() const { return overWrite; } QColor paragraphBackgroundColor( int para ) const; bool isUndoRedoEnabled() const; bool eventFilter( QObject *o, QEvent *e ); public slots: void setEnabled( bool ); virtual void setMimeSourceFactory( QMimeSourceFactory* factory ); virtual void setStyleSheet( QStyleSheet* styleSheet ); virtual void scrollToAnchor( const QString& name ); virtual void setPaper( const QBrush& pap ); virtual void setLinkUnderline( bool ); virtual void setWordWrap( WordWrap mode ); virtual void setWrapColumnOrWidth( int ); virtual void setWrapPolicy( WrapPolicy policy ); virtual void copy(); virtual void append( const QString& text ); void setText( const QString &txt ) { setText( txt, QString::null ); } virtual void setText( const QString &txt, const QString &context ); virtual void setTextFormat( TextFormat f ); virtual void selectAll( bool select = TRUE ); virtual void setTabStopWidth( int ts ); virtual void zoomIn( int range ); virtual void zoomIn() { zoomIn( 1 ); } virtual void zoomOut( int range ); virtual void zoomOut() { zoomOut( 1 ); } virtual void zoomTo( int size ); virtual void sync(); virtual void setReadOnly( bool b ); virtual void undo(); virtual void redo(); virtual void cut(); virtual void paste(); #ifndef QT_NO_CLIPBOARD virtual void pasteSubType( const QCString &subtype ); #endif virtual void clear(); virtual void del(); virtual void indent(); virtual void setItalic( bool b ); virtual void setBold( bool b ); virtual void setUnderline( bool b ); virtual void setFamily( const QString &f ); virtual void setPointSize( int s ); virtual void setColor( const QColor &c ); virtual void setFont( const QFont &f ); virtual void setVerticalAlignment( VerticalAlignment a ); virtual void setAlignment( int a ); + + // do not use, will go away virtual void setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle ); + virtual void setCursorPosition( int parag, int index ); virtual void setSelection( int parag_from, int index_from, int parag_to, int index_to, int selNum = 0 ); virtual void setSelectionAttributes( int selNum, const QColor &back, bool invertText ); virtual void setModified( bool m ); virtual void resetFormat(); virtual void setUndoDepth( int d ); virtual void setFormat( QTextFormat *f, int flags ); virtual void ensureCursorVisible(); virtual void placeCursor( const QPoint &pos, QTextCursor *c = 0 ); virtual void moveCursor( CursorAction action, bool select ); virtual void doKeyboardAction( KeyboardAction action ); virtual void removeSelectedText( int selNum = 0 ); virtual void removeSelection( int selNum = 0 ); virtual void setCurrentFont( const QFont &f ); virtual void setOverwriteMode( bool b ) { overWrite = b; } virtual void scrollToBottom(); virtual void insert( const QString &text, bool indent, bool checkNewLine, bool removeSelected ); virtual void insertAt( const QString &text, int para, int index ); virtual void removeParagraph( int para ); virtual void insertParagraph( const QString &text, int para ); virtual void setParagraphBackgroundColor( int para, const QColor &bg ); virtual void clearParagraphBackground( int para ); virtual void setUndoRedoEnabled( bool b ); signals: void textChanged(); void selectionChanged(); void copyAvailable( bool ); void undoAvailable( bool yes ); void redoAvailable( bool yes ); void currentFontChanged( const QFont &f ); void currentColorChanged( const QColor &c ); void currentAlignmentChanged( int a ); void currentVerticalAlignmentChanged( VerticalAlignment a ); void cursorPositionChanged( QTextCursor *c ); void cursorPositionChanged( int para, int pos ); void returnPressed(); void modificationChanged( bool m ); protected: void repaintChanged(); void updateStyles(); void drawContents( QPainter *p, int cx, int cy, int cw, int ch ); bool event( QEvent *e ); void keyPressEvent( QKeyEvent *e ); void resizeEvent( QResizeEvent *e ); void viewportResizeEvent( QResizeEvent* ); void contentsMousePressEvent( QMouseEvent *e ); void contentsMouseMoveEvent( QMouseEvent *e ); void contentsMouseReleaseEvent( QMouseEvent *e ); void contentsMouseDoubleClickEvent( QMouseEvent *e ); #ifndef QT_NO_WHEELEVENT void contentsWheelEvent( QWheelEvent *e ); #endif #ifndef QT_NO_DRAGANDDROP void contentsDragEnterEvent( QDragEnterEvent *e ); void contentsDragMoveEvent( QDragMoveEvent *e ); void contentsDragLeaveEvent( QDragLeaveEvent *e ); void contentsDropEvent( QDropEvent *e ); #endif bool focusNextPrevChild( bool next ); QTextDocument *document() const; QTextCursor *textCursor() const; void setDocument( QTextDocument *doc ); virtual QPopupMenu *createPopupMenu( const QPoint& pos ); virtual QPopupMenu *createPopupMenu(); void drawCursor( bool visible ); void windowActivationChange( bool ); protected slots: virtual void doChangeInterval(); private slots: void formatMore(); void doResize(); void autoScrollTimerDone(); void blinkCursor(); void setModified(); void startDrag(); void documentWidthChanged( int w ); private: struct Q_EXPORT UndoRedoInfo { - enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Alignment, ParagType }; + enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Style }; UndoRedoInfo( QTextDocument *dc ); ~UndoRedoInfo(); void clear(); bool valid() const; QUndoRedoInfoPrivate *d; int id; int index; int eid; int eindex; QTextFormat *format; int flags; Type type; QTextDocument *doc; - QMemArray<int> oldAligns; - int newAlign; - bool list; - QStyleSheetItem::ListStyle listStyle; - QValueList< QPtrVector<QStyleSheetItem> > oldStyles; - QValueList<QStyleSheetItem::ListStyle> oldListStyles; + QByteArray styleInformation; }; private: void updateCursor( const QPoint & pos ); void handleMouseMove( const QPoint& pos ); void drawContents( QPainter * ); virtual bool linksEnabled() const { return FALSE; } void init(); void checkUndoRedoInfo( UndoRedoInfo::Type t ); void updateCurrentFormat(); bool handleReadOnlyKeyEvent( QKeyEvent *e ); - void makeParagVisible( QTextParag *p ); + void makeParagVisible( QTextParagraph *p ); #ifndef QT_NO_MIME QCString pickSpecial(QMimeSource* ms, bool always_ask, const QPoint&); #endif #ifndef QT_NO_MIMECLIPBOARD void pasteSpecial(const QPoint&); #endif void setFontInternal( const QFont &f ); virtual void emitHighlighted( const QString & ) {} virtual void emitLinkClicked( const QString & ) {} - void readFormats( QTextCursor &c1, QTextCursor &c2, int oldLen, QTextString &text, bool fillStyles = FALSE ); + void readFormats( QTextCursor &c1, QTextCursor &c2, QTextString &text, bool fillStyles = FALSE ); void clearUndoRedo(); void paintDocument( bool drawAll, QPainter *p, int cx = -1, int cy = -1, int cw = -1, int ch = -1 ); void moveCursor( CursorAction action ); - void ensureFormatted( QTextParag *p ); + void ensureFormatted( QTextParagraph *p ); void placeCursor( const QPoint &pos, QTextCursor *c, bool link ); + void updateMicroFocusHint(); private: QTextDocument *doc; QTextCursor *cursor; QTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer; - QTextParag *lastFormatted; + QTextParagraph *lastFormatted; int interval; UndoRedoInfo undoRedoInfo; QTextFormat *currentFormat; int currentAlignment; QPoint oldMousePos, mousePos; QPoint dragStartPos; QString onLink; WordWrap wrapMode; WrapPolicy wPolicy; int wrapWidth; QString pressedLink; QTextEditPrivate *d; bool inDoubleClick : 1; bool mousePressed : 1; bool cursorVisible : 1; bool blinkCursorVisible : 1; bool readOnly : 1; bool modified : 1; bool mightStartDrag : 1; bool inDnD : 1; bool readonly : 1; bool undoEnabled : 1; bool overWrite : 1; }; inline QTextDocument *QTextEdit::document() const { return doc; } inline QTextCursor *QTextEdit::textCursor() const { return cursor; } inline void QTextEdit::setCurrentFont( const QFont &f ) { QTextEdit::setFontInternal( f ); } } #endif //QTEXTVIEW_H |