summaryrefslogtreecommitdiff
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/opie-write/qrichtext.cpp72
1 files changed, 23 insertions, 49 deletions
diff --git a/noncore/apps/opie-write/qrichtext.cpp b/noncore/apps/opie-write/qrichtext.cpp
index 768da44..b457cd6 100644
--- a/noncore/apps/opie-write/qrichtext.cpp
+++ b/noncore/apps/opie-write/qrichtext.cpp
@@ -1,628 +1,619 @@
/****************************************************************************
** $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"
/* OPIE */
#include <opie2/odebug.h>
using namespace Opie::Core;
/* QT */
#include "qdragobject.h"
#include "qpaintdevicemetrics.h"
#include "qdrawutil.h"
#include "qcleanuphandler.h"
/* STD */
#include <stdlib.h>
using namespace Qt3;
static QTextCursor* richTextExportStart = 0;
static QTextCursor* richTextExportEnd = 0;
static QTextFormatCollection *qFormatCollection = 0;
const int border_tolerance = 2;
#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 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( 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 )
{
QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
- if ( !s && doc ) {
+ if ( !s ) {
owarn << "can't locate parag at " << id << ", last parag: " << doc->lastParagraph()->paragId() << "" << oendl;
return 0;
- } else if ( !doc ) {
- owarn << "No valid doc" << oendl;
- return 0;
}
cursor.setParagraph( s );
cursor.setIndex( index );
int len = text.size();
if ( c )
*c = cursor;
if ( doc ) {
doc->setSelectionStart( QTextDocument::Temp, cursor );
for ( int i = 0; i < len; ++i )
cursor.gotoNextLetter();
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 )
{
QTextParagraph *s = doc ? doc->paragAt( id ) : parag;
- if ( !s && doc ) {
+ if ( !s ) {
owarn << "can't locate parag at " << id << ", last parag: " << doc->lastParagraph()->paragId() << "" << oendl;
return 0;
- } else if ( !doc ) {
- owarn << "No valid doc" << oendl;
- return 0;
}
cursor.setParagraph( s );
cursor.setIndex( index );
QString str = QTextString::toString( text );
cursor.insert( str, TRUE, &text );
cursor.setParagraph( s );
cursor.setIndex( index );
if ( c ) {
c->setParagraph( s );
c->setIndex( index );
for ( int i = 0; i < (int)text.size(); ++i )
c->gotoNextLetter();
- } else {
- owarn << "No valid cursor" << oendl;
- return 0;
}
if ( !styleInformation.isEmpty() ) {
QDataStream styleStream( styleInformation, IO_ReadOnly );
int num;
styleStream >> num;
QTextParagraph *p = s;
while ( num-- && p ) {
p->readStyleInformation( styleStream );
p = p->next();
}
}
s = cursor.paragraph();
while ( s ) {
s->format();
s->setChanged( TRUE );
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 )
{
QTextParagraph *sp = doc->paragAt( startId );
QTextParagraph *ep = doc->paragAt( endId );
if ( !sp || !ep )
return c;
QTextCursor start( doc );
start.setParagraph( sp );
start.setIndex( startIndex );
QTextCursor end( doc );
end.setParagraph( ep );
end.setIndex( endIndex );
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 )
{
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.setParagraph( ep );
end.setIndex( endIndex );
if ( endIndex == ep->length() )
end.gotoLeft();
*c = end;
return c;
}
QTextStyleCommand::QTextStyleCommand( QTextDocument *d, int fParag, int lParag, const QByteArray& beforeChange )
: QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), before( beforeChange )
{
after = readStyleInformation( d, fParag, lParag );
}
QByteArray QTextStyleCommand::readStyleInformation( QTextDocument* doc, int fParag, int lParag )
{
QByteArray style;
QTextParagraph *p = doc->paragAt( fParag );
if ( !p )
return style;
QDataStream styleStream( style, IO_WriteOnly );
int num = lParag - fParag + 1;
styleStream << num;
while ( num -- && p ) {
p->writeStyleInformation( styleStream );
p = p->next();
}
return style;
}
void QTextStyleCommand::writeStyleInformation( QTextDocument* doc, int fParag, const QByteArray& style )
{
QTextParagraph *p = doc->paragAt( fParag );
if ( !p )
return;
QDataStream styleStream( style, IO_ReadOnly );
int num;
styleStream >> num;
while ( num-- && p ) {
p->readStyleInformation( styleStream );
p = p->next();
}
}
QTextCursor *QTextStyleCommand::execute( QTextCursor *c )
{
writeStyleInformation( doc, firstParag, after );
return c;
}
QTextCursor *QTextStyleCommand::unexecute( QTextCursor *c )
{
writeStyleInformation( doc, firstParag, before );
return c;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
QTextCursor::QTextCursor( QTextDocument *d )
: idx( 0 ), tmpIndex( -1 ), ox( 0 ), oy( 0 ),
valid( TRUE )
{
para = d ? d->firstParagraph() : 0;
}
QTextCursor::QTextCursor( const QTextCursor &c )
{
ox = c.ox;
oy = c.oy;
idx = c.idx;
para = c.para;
tmpIndex = c.tmpIndex;
indices = c.indices;
paras = c.paras;
xOffsets = c.xOffsets;
yOffsets = c.yOffsets;
valid = c.valid;
}
QTextCursor &QTextCursor::operator=( const QTextCursor &c )
{
ox = c.ox;
oy = c.oy;
idx = c.idx;
para = c.para;
tmpIndex = c.tmpIndex;
indices = c.indices;
paras = c.paras;
xOffsets = c.xOffsets;
yOffsets = c.yOffsets;
valid = c.valid;
return *this;
}
bool QTextCursor::operator==( const QTextCursor &c ) const
{
return para == c.para && idx == c.idx;
}
int QTextCursor::totalOffsetX() const
{
int xoff = ox;
for ( QValueStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit )
xoff += *xit;
return xoff;
}
int QTextCursor::totalOffsetY() const
{
int yoff = oy;
for ( QValueStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit )
yoff += *yit;
return yoff;
}
void QTextCursor::gotoIntoNested( const QPoint &globalPos )
{
if ( !para )
return;
push();
ox = 0;
int bl, y;
para->lineHeightOfChar( idx, &bl, &y );
oy = y + para->rect().y();
QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() );
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()
{
QValueStack<QTextParagraph*>::Iterator it = paras.begin();
QValueStack<int>::Iterator it2 = indices.begin();
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 ) {
int i = 0;
while ( ( i = s.find( '\r', i ) ) != -1 )
s.remove( i ,1 );
}
#endif
if ( checkNewLine )
justInsert = s.find( '\n' ) == -1;
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();
para->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
}
}
}
idx += s.length();
} else { // we split at new lines
int start = -1;
int end;
int y = para->rect().y() + para->rect().height();
int lastIndex = 0;
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
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();
para->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
}
}
lastIndex += len;
}
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++;
}
} 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 = para->rect().height();
para->format( -1, TRUE );
if ( h != para->rect().height() )
invalidateNested();
else if ( para->document() && para->document()->parent() )
para->document()->nextDoubleBuffered = TRUE;
}
void QTextCursor::gotoLeft()
{
if ( para->string()->isRightToLeft() )
gotoNextLetter();
else
gotoPreviousLetter();
}
void QTextCursor::gotoPreviousLetter()
{
tmpIndex = -1;
if ( idx > 0 ) {
idx--;
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 ( para->prev() ) {
para = para->prev();
idx = para->length() - 1;
}
}
}
}
void QTextCursor::push()
{
indices.push( idx );
paras.push( para );
xOffsets.push( ox );
yOffsets.push( oy );
}
void QTextCursor::pop()
{
if ( indices.isEmpty() )
return;
idx = indices.pop();
para = paras.pop();
ox = xOffsets.pop();
@@ -1020,1287 +1011,1273 @@ void QTextCursor::gotoNextWord()
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 ( para->next() ) {
QTextParagraph *p = para->next();
while ( p && !p->isVisible() )
p = p->next();
if ( s ) {
para = p;
idx = 0;
}
} else {
gotoLineEnd();
}
}
bool QTextCursor::atParagStart()
{
return idx == 0;
}
bool QTextCursor::atParagEnd()
{
return idx == para->length() - 1;
}
void QTextCursor::splitAndInsertEmptyParagraph( bool ind, bool updateIds )
{
if ( !para->document() )
return;
tmpIndex = -1;
QTextFormat *f = 0;
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 = para->document()->formatCollection()->format( f->font(), f->color() );
}
}
if ( atParagEnd() ) {
QTextParagraph *n = para->next();
QTextParagraph *s = para->document()->createParagraph( para->document(), para, n, updateIds );
if ( f )
s->setFormat( 0, 1, f, TRUE );
s->copyParagData( para );
if ( ind ) {
int oi, ni;
s->indent( &oi, &ni );
para = s;
idx = ni;
} else {
para = s;
idx = 0;
}
} else if ( atParagStart() ) {
QTextParagraph *p = para->prev();
QTextParagraph *s = para->document()->createParagraph( para->document(), p, para, updateIds );
if ( f )
s->setFormat( 0, 1, f, TRUE );
s->copyParagData( para );
if ( ind ) {
s->indent();
s->format();
indent();
para->format();
}
} else {
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 ) {
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 );
tsc->loseCustomItem();
}
if ( tsc->isAnchor() )
s->at( i )->setAnchor( tsc->anchorName(),
tsc->anchorHref() );
}
para->truncate( idx );
if ( ind ) {
int oi, ni;
s->indent( &oi, &ni );
para = s;
idx = ni;
} else {
para = s;
idx = 0;
}
}
invalidateNested();
}
bool QTextCursor::remove()
{
tmpIndex = -1;
if ( !atParagEnd() ) {
para->remove( idx, 1 );
int h = para->rect().height();
para->format( -1, TRUE );
if ( h != para->rect().height() )
invalidateNested();
else if ( para->document() && para->document()->parent() )
para->document()->nextDoubleBuffered = TRUE;
return FALSE;
} else if ( para->next() ) {
para->join( para->next() );
invalidateNested();
return TRUE;
}
return FALSE;
}
void QTextCursor::indent()
{
int oi = 0, ni = 0;
para->indent( &oi, &ni );
if ( oi == ni )
return;
if ( idx >= oi )
idx += ni - oi;
else
idx = ni;
}
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
QTextDocument::QTextDocument( QTextDocument *p )
: par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
{
fCollection = new QTextFormatCollection;
init();
}
QTextDocument::QTextDocument( QTextDocument *p, QTextFormatCollection *f )
: par( p ), parentPar( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
{
fCollection = f;
init();
}
void QTextDocument::init()
{
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;
setStyleSheet( QStyleSheet::defaultSheet() );
factory_ = QMimeSourceFactory::defaultFactory();
contxt = QString::null;
underlLinks = par ? par->underlLinks : TRUE;
backBrush = 0;
buf_pixmap = 0;
nextDoubleBuffered = FALSE;
if ( par )
withoutDoubleBuffer = par->withoutDoubleBuffer;
else
withoutDoubleBuffer = FALSE;
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 ) {
QTextParagraph *p = fParag->next();
delete fParag;
fParag = p;
}
fParag = lParag = 0;
if ( createEmptyParag )
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 );
}
QTextParagraph *QTextDocument::createParagraph( QTextDocument *d, QTextParagraph *pr, QTextParagraph *nx, bool updateIds )
{
return new QTextParagraph( d, pr, nx, updateIds );
}
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 = 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 = 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 = 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) { \
- if ( !curpar ) { \
- owarn << "no current paragraph" << oendl; \
- return; \
- } \
- 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 ); \
- if ( !curpar ) { \
- owarn << "failed in creating a new paragraph" << oendl; \
- return; \
- } \
- vec = 0; \
- } \
- hasNewPar = TRUE; \
- 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; \
- 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 ); \
- } while ( 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->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; \
+ 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 ); \
+ }while(FALSE)
+
void QTextDocument::setRichText( const QString &text, const QString &context )
{
if ( !context.isEmpty() )
setContext( context );
clear();
fParag = lParag = createParagraph( this );
oTextValid = TRUE;
oText = text;
setRichTextInternal( text );
fParag->rtext = TRUE;
}
void QTextDocument::setRichTextInternal( const QString &text, QTextCursor* cursor )
{
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 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
const QStyleSheetItem* nstyle = sheet_->item(tagname);
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 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 tags, some have a nstyle, some not
if ( wellKnownTags.find( tagname ) != -1 ) {
if ( tagname == "br" ) {
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 = space = TRUE;
custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
NEWPAR;
} else if ( tagname == "table" ) {
emptyTag = space = TRUE;
QTextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor );
curpar->setAlignment( curtag.alignment );
custom = parseTable( attr, format, doc, length, pos, curpar );
} else if ( tagname == "qt" || tagname == "body" ) {
if ( attr.contains( "bgcolor" ) ) {
QBrush *b = new QBrush( QColor( attr["bgcolor"] ) );
setPaper( b );
}
if ( attr.contains( "background" ) ) {
QImage img;
QString bg = attr["background"];
const QMimeSource* m = factory_->data( bg, contxt );
if ( !m ) {
owarn << "QRichText: no mimesource for " << bg.latin1() << "" << oendl;
} else {
if ( !QImageDrag::decode( m, img ) ) {
owarn << "QTextImage: cannot decode " << bg.latin1() << "" << oendl;
}
}
if ( !img.isNull() ) {
QPixmap pm;
pm.convertFromImage( img );
QBrush *b = new QBrush( QColor(), pm );
setPaper( b );
}
}
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 );
}
}
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();
}
}
}
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 = 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 ) {
/* 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;
}
curtag.name = tagname;
curtag.style = nstyle;
curtag.name = tagname;
curtag.style = nstyle;
if ( int(nstyle->whiteSpaceMode()) != QStyleSheetItem::Undefined )
curtag.wsm = nstyle->whiteSpaceMode();
/* 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 ( (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;
}
}
}
/* 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" ) ) {
QString dir = attr["dir"];
if ( dir == "rtl" )
curtag.direction = QChar::DirR;
else if ( dir == "ltr" )
curtag.direction = QChar::DirL;
}
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 );
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
|| curtag.style->displayMode() == QStyleSheetItem::DisplayListItem;
// 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 ( textEditMode && tagname == "p" ) // preserve empty paragraphs
hasNewPar = FALSE;
NEWPAR;
}
}
} else {
// normal contents
QString s;
QChar c;
while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){
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' )
break; // break on newlines, pre delievers a QChar_linesep
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 = 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 ( 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)[curStyle->size()-1];
- if ( !mainStyle )
- return;
-
- if ( mainStyle->displayMode() == QStyleSheetItem::DisplayListItem )
+ 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() const
{
QString buffer;
QString s;
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;
}
static QString align_to_string( int a )
{
if ( a & Qt::AlignRight )
return " align=\"right\"";
if ( a & Qt::AlignHCenter )
return " align=\"center\"";
if ( a & Qt3::AlignJustify )
return " align=\"justify\"";
return QString::null;
}
static QString direction_to_string( int d )
{
if ( d != QChar::DirON )
return ( d == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" );
return QString::null;
}
static QString list_value_to_string( int v )
{
if ( v != -1 )
return " listvalue=\"" + QString::number( v ) + "\"";
return QString::null;
}
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;
}
}
static inline bool list_is_ordered( int v )
{
return v == QStyleSheetItem::ListDecimal ||
v == QStyleSheetItem::ListLowerAlpha ||
v == QStyleSheetItem::ListUpperAlpha;
}
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;
}
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 +="\">";
}
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 ) {
owarn << "QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, ul, ol, or li)" << oendl;
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 += '\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 ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
return richText();
return plainText();
}
QString QTextDocument::text( int parag ) const
{
QTextParagraph *p = paragAt( parag );
if ( !p )
return QString::null;
if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
return p->richText();
else
return p->string()->toString();
}
void QTextDocument::invalidate()
{
QTextParagraph *s = fParag;
while ( s ) {
s->invalidate( 0 );
s = s->next();
}
}
void QTextDocument::selectionStart( int id, int &paragId, int &index )
{
QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
if ( it == selections.end() )
return;
QTextDocumentSelection &sel = *it;
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;
}
@@ -2689,777 +2666,774 @@ void QTextDocument::setFormat( int id, QTextFormat *f, int flags )
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.paragraph() == c2.paragraph() ) {
c1.paragraph()->remove( c1.index(), c2.index() - c1.index() );
return;
}
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.paragraph() != fParag ) {
cursor->gotoPreviousLetter();
if ( cursor->isValid() )
didGoLeft = TRUE;
}
c1.paragraph()->remove( c1.index(), c1.paragraph()->length() - 1 - c1.index() );
QTextParagraph *p = c1.paragraph()->next();
int dy = 0;
QTextParagraph *tmp;
while ( p && p != c2.paragraph() ) {
tmp = p->next();
dy -= p->rect().height();
delete p;
p = tmp;
}
c2.paragraph()->remove( 0, c2.index() );
while ( p ) {
p->move( dy );
p->invalidate( 0 );
p->setEndState( -1 );
p = p->next();
}
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;
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();
}
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( QTextCursor& cursor, const QString &e, bool cs, bool wo, bool forward )
{
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;
}
}
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 );
int end = res + expr.length();
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( forward ? res : end );
setSelectionEnd( Standard, cursor );
return TRUE;
}
start = res + (forward ? 1 : -1);
}
}
if ( forward ) {
if ( cursor.paragraph() == lastParagraph() && cursor.atParagEnd () )
break;
cursor.gotoNextLetter();
} else {
if ( cursor.paragraph() == firstParagraph() && cursor.atParagStart() )
break;
cursor.gotoPreviousLetter();
}
}
return FALSE;
}
void QTextDocument::setTextFormat( Qt::TextFormat f )
{
txtFormat = f;
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;
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.paragraph()->paragId() < sel.startCursor.paragraph()->paragId() ) {
endParag = sel.startCursor.paragraph();
startParag = sel.endCursor.paragraph();
}
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;
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 )
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 ( !firstParagraph() )
return;
if ( paper ) {
p->setBrushOrigin( 0, 0 );
p->fillRect( rect, *paper );
}
if ( formatCollection()->defaultFormat()->color() != cg.text() )
setDefaultFormat( formatCollection()->defaultFormat()->font(), cg.text() );
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::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 ) )
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() ),
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;
}
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 ( !firstParagraph() )
return 0;
if ( cx < 0 && cy < 0 ) {
cx = 0;
cy = 0;
cw = width();
ch = height();
}
QTextParagraph *lastFormatted = 0;
QTextParagraph *parag = firstParagraph();
QPixmap *doubleBuffer = 0;
QPainter painter;
- bool fullWidthSelection = FALSE;
while ( parag ) {
lastFormatted = parag;
if ( !parag->isValid() )
parag->format();
QRect pr = parag->rect();
- if ( fullWidthSelection )
- pr.setWidth( parag->document()->width() );
if ( pr.y() > cy + ch )
goto floating;
if ( !pr.intersects( QRect( cx, cy, cw, ch ) ) || ( onlyChanged && !parag->hasChanged() ) ) {
parag = parag->next();
continue;
}
drawParagraph( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
parag = parag->next();
}
parag = lastParagraph();
floating:
if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
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 );
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;
}
return lastFormatted;
}
/*
#### this function only sets the default font size in the format collection
*/
void QTextDocument::setDefaultFormat( const QFont &font, const QColor &color )
{
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();
}
}
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, 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 ) {
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 {
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;
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() + len );
if ( index < os ) {
memmove( data.data() + index + len, data.data() + index,
sizeof( QTextStringChar ) * ( os - index ) );
}
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 = unicode[i];
data[ (int)index + i ].setFormat( f );
}
bidiDirty = TRUE;
}
QTextString::~QTextString()
{
clear();
}
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;
@@ -4983,769 +4957,769 @@ QTextLineStart *QTextFormatter::bidiReorderLine( QTextParagraph * /*parag*/, QTe
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( ' ' );
}
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;
QTextLineStart *ls = new QTextLineStart( control->context, control->status );
delete control;
delete runs;
return ls;
}
#endif
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( 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, 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, QTextParagraph* parag )
{
int oldHeight = parag->rect().height();
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 ) {
QTextLineStart * ls = it.data();
ls->y = h;
QTextStringChar *c = &parag->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() )
m = 0;
else
m = QMAX(m, parag->next()->topMargin() ) / 2;
h += m;
parag->setHeight( h );
return h - oldHeight;
}
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
QTextFormatterBreakInWords::QTextFormatterBreakInWords()
{
}
#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() : 0;
int x = left + ( doc ? parag->firstLineMargin() : 0 );
int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 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 = &parag->string()->at( 0 );
int i = start;
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 = &parag->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 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() ) ) {
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, 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() )
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, 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 = 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 linespacing = doc ? parag->lineSpacing() : 0;
bool wrapEnabled = isWrapEnabled( parag );
start = 0;
if ( start == 0 )
c = &parag->string()->at( 0 );
int i = start;
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 ( c && painter )
+ 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 );
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, 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();
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() + ( 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 );
}
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 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, 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 ( !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 += 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 { // ... otherwise if we had a breakable char, break there
DO_FLOW( lineStart );
i = lastBreak;
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 ( !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 += 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 ) ) {
if ( len <= 2 || i < len - 1 ) {
tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
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()- 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.
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 || lastChr == QChar_linesep )
align = Qt3::AlignAuto;
DO_FLOW( lineStart );
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() )
m = 0;
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 )
{
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 ) {
lastFormat = f;
lastFormat->addRef();
return lastFormat;
}
if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) {
lastFormat->addRef();
return lastFormat;
}
QTextFormat *fm = cKey.find( f->key() );
if ( fm ) {
lastFormat = fm;
lastFormat->addRef();
return lastFormat;
}
if ( f->key() == defFormat->key() )
return defFormat;
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 ) {
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 ) {
cres->collection = this;
cKey.insert( cres->key(), cres );
} else {
delete cres;
cres = fm;
cres->addRef();
}
return cres;
}
QTextFormat *QTextFormatCollection::format( const QFont &f, const QColor &c )
{
if ( cachedFormat && cfont == f && ccol == c ) {
cachedFormat->addRef();
return cachedFormat;
}
QString key = QTextFormat::getKey( f, c, FALSE, QTextFormat::AlignNormal );
cachedFormat = cKey.find( key );
cfont = f;
ccol = c;
if ( cachedFormat ) {
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 )
owarn << "ASSERT: keys for format not identical: '" << cachedFormat->key().latin1() << " '" << key.latin1() << "'" << oendl;
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() );
}
#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::updateDefaultFormat( const QFont &font, const QColor &color, QStyleSheet *sheet )
{
QDictIterator<QTextFormat> it( cKey );
QTextFormat *fm;