/****************************************************************************
** $Id$
**
** Implementation of the QSWTextView 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 "qswtextview.h"
#ifndef QT_NO_TEXTVIEW
#include "qrichtext_p.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef stricmp
/*!
\class QSWTextView qtextview.h
\brief A sophisticated single-page rich text viewer.
\ingroup basic
\ingroup helpsystem
Unlike QSimpleRichText, which merely draws small pieces of rich
text, a QSWTextView is a real widget, with scrollbars when necessary,
for showing large text documents.
The rendering style and available tags are defined by a
styleSheet(). Currently, a small XML/CSS1 subset including embedded
images and tables is supported. See QStyleSheet for
details. Possible images within the text document are resolved by
using a QMimeSourceFactory. See setMimeSourceFactory() for details.
Using QSWTextView is quite similar to QLabel. It's mainly a call to
setText() to set the contents. Setting the background color is
slightly different from other widgets, since a text view is a
scrollable widget that naturally provides a scrolling background. You
can specify the colorgroup of the displayed text with
setPaperColorGroup() or directly define the paper background with
setPaper(). QSWTextView supports both plain color and complex pixmap
backgrounds.
Note that we do not intend to add a full-featured web browser widget
to Qt (since that would easily double Qt's size and only few
applications would benefit from it). In particular, the rich text
support in Qt is supposed to provide a fast, portable and sufficient
way to add reasonable online help facilities to applications. We
will, however, extend it to some degree in future versions of Qt.
For even more, like hypertext capabilities, see QTextBrowser.
*/
class QSWTextViewData
{
public:
QStyleSheet* sheet_;
QRichText* doc_;
QMimeSourceFactory* factory_;
QString original_txt;
QString txt;
QString contxt;
QColorGroup mypapcolgrp;
QColorGroup papcolgrp;
QColor mylinkcol;
QColor paplinkcol;
bool linkunderline;
QTimer* resizeTimer;
#ifndef QT_NO_DRAGANDDROP
QTimer* dragTimer;
#endif
QTimer* scrollTimer;
Qt::TextFormat textformat;
QRichTextFormatter* fcresize;
QPoint cursor;
QtTriple selorigin;
QtTriple selstart;
QtTriple selend;
uint selection :1;
uint dirty :1;
uint dragselection :1;
uint ownpalette : 1;
};
/*!
Constructs an empty QSWTextView
with the standard \a parent and \a name optional arguments.
*/
QSWTextView::QSWTextView(QWidget *parent, const char *name)
: QScrollView( parent, name, WRepaintNoErase ),
{
init();
}
/*!
Constructs a QSWTextView displaying the contents \a text with context
\a context, with the standard \a parent and \a name optional
arguments.
*/
QSWTextView::QSWTextView( const QString& text, const QString& context,
QWidget *parent, const char *name)
: QScrollView( parent, name, WRepaintNoErase ),
{
init();
setText( text, context );
}
void QSWTextView::init()
{
d = new QSWTextViewData;
d->mypapcolgrp = palette().active();
d->papcolgrp = d->mypapcolgrp;
d->mylinkcol = blue;
d->paplinkcol = d->mylinkcol;
d->linkunderline = TRUE;
d->fcresize = 0;
setKeyCompression( TRUE );
setVScrollBarMode( QScrollView::Auto );
setHScrollBarMode( QScrollView::Auto );
d->doc_ = 0;
d->sheet_ = 0;
d->factory_ = 0;
d->txt = QString::fromLatin1("");
d->textformat = AutoText;
d->dirty = TRUE;
d->selection = FALSE;
d->dragselection = FALSE;
d->ownpalette = FALSE;
viewport()->setBackgroundMode( PaletteBase );
viewport()->setFocusProxy( this );
viewport()->setFocusPolicy( WheelFocus );
d->resizeTimer = new QTimer( this, "qt_resizetimer" );
connect( d->resizeTimer, SIGNAL( timeout() ), this, SLOT( doResize() ));
d->scrollTimer = new QTimer( this );
connect( d->scrollTimer, SIGNAL( timeout() ), this, SLOT( doAutoScroll() ));
}
/*!
Destructs the view.
*/
QSWTextView::~QSWTextView()
{
delete d->fcresize;
QTextFormatCollection* formats = d->doc_?d->doc_->formats:0;
delete d->doc_;
delete formats; //#### fix inheritance structure in rich text
delete d;
}
/*!
Changes the contents of the view to the string \a text and the
context to \a context.
\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 view autodetects the format from \a text.
The optional \a context is used to resolve references within the
text document, for example image sources. It is passed directly to
the mimeSourceFactory() when quering data.
\sa text(), setTextFormat()
*/
void QSWTextView::setText( const QString& text, const QString& context)
{
QTextFormatCollection* formats = d->doc_?d->doc_->formats:0;
delete d->doc_;
delete formats; //#### fix inheritance structure in rich text
d->doc_ = 0;
d->selection = FALSE;
d->original_txt = text;
d->contxt = context;
if ( text.isEmpty() )
d->txt = QString::fromLatin1("");
else if ( d->textformat == AutoText ) {
if ( QStyleSheet::mightBeRichText( text ) )
d->txt = text;
else
d->txt = QStyleSheet::convertFromPlainText( text );
}
else if ( d->textformat == PlainText )
d->txt = QStyleSheet::convertFromPlainText( text );
else // rich text
d->txt = text;
setContentsPos( 0, 0 );
richText().invalidateLayout();
richText().flow()->initialize( visibleWidth() );
updateLayout();
viewport()->update();
}
/*!\overload
Changes the contents of the view to the string \a text.
\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 view autodetects the format from \a text.
This function calls setText( text, QString::null ), i.e. it sets a
text without any context.
\sa text(), setTextFormat()
*/
void QSWTextView::setText( const QString& text )
{
setText( text, QString::null );
}
/*!
Appends \a text to the current text.
Useful for log viewers.
\warning This function has known problems (incorrect painting and
layouting). If this problem occures to you, use setText( text() +
theNewText ) instead. The new richtext engine, which is part of Qt
3.0, is able to handle append(), insert(), etc. properly.
*/
void QSWTextView::append( const QString& text )
{
richText().append( text, mimeSourceFactory(), styleSheet() );
if ( isVisible() ) {
int y = contentsHeight();
int h = richText().lastChild()->bottomMargin();
if ( d->fcresize ) {
d->fcresize->updateLayout();
doResize();
} else
updateLayout();
updateContents( contentsX(), y-h, visibleWidth(), h );
}
d->original_txt += text;
}
/*!
Returns the contents of the view.
\sa context(), setText()
*/
QString QSWTextView::text() const
{
return d->original_txt;
}
/*!
Returns the context of the view.
\sa text(), setText()
*/
QString QSWTextView::context() const
{
return d->contxt;
}
void QSWTextView::createRichText()
{
if ( d->mypapcolgrp != d->papcolgrp )
viewport()->setBackgroundColor( d->mypapcolgrp.base() );
d->papcolgrp = d->mypapcolgrp;
d->paplinkcol = d->mylinkcol;
d->doc_ = new QRichText( d->txt, viewport()->font(), d->contxt,
8, mimeSourceFactory(), styleSheet() );
if (d->doc_->attributes().contains("bgcolor")){
QColor col ( d->doc_->attributes()["bgcolor"].latin1() );
if ( col.isValid() ) {
d->papcolgrp.setColor( QColorGroup::Base, col );
viewport()->setBackgroundColor( col );
}
}
if (d->doc_->attributes().contains("link")){
QColor col ( d->doc_->attributes()["link"].latin1() );
if ( col.isValid() )
d->paplinkcol = col;
}
if (d->doc_->attributes().contains("text")){
QColor col ( d->doc_->attributes()["text"].latin1() );
if ( col.isValid() )
d->papcolgrp.setColor( QColorGroup::Text, col );
}
if (d->doc_->attributes().contains("background")){
QString imageName = d->doc_->attributes()["background"];
QPixmap pm;
const QMimeSource* m =
context().isNull()
? mimeSourceFactory()->data( imageName )
: mimeSourceFactory()->data( imageName, context() );
if ( m ) {
if ( !QImageDrag::decode( m, pm ) ) {
qWarning("QTextImage: cannot load %s", imageName.latin1() );
}
}
if (!pm.isNull())
d->papcolgrp.setBrush( QColorGroup::Base, QBrush(d->papcolgrp.base(), pm) );
}
d->cursor = QPoint(0,0);
}
/*!
Returns the current style sheet of the view.
\sa setStyleSheet()
*/
QStyleSheet* QSWTextView::styleSheet() const
{
if (!d->sheet_)
return QStyleSheet::defaultSheet();
else
return d->sheet_;
}
/*!
Sets the style sheet of the view.
\sa styleSheet()
*/
void QSWTextView::setStyleSheet( QStyleSheet* styleSheet )
{
d->sheet_ = styleSheet;
viewport()->update();
}
/*!
Returns the current mime source factory for the view.
\sa setMimeSourceFactory()
*/
QMimeSourceFactory* QSWTextView::mimeSourceFactory() const
{
if (!d->factory_)
return QMimeSourceFactory::defaultFactory();
else
return d->factory_;
}
/*!
Sets the mime source factory for the view. The factory is used to
resolve named references within rich text documents. If no factory
has been specified, the text view uses the default factory
QMimeSourceFactory::defaultFactory().
Ownership of \a factory is \e not transferred to make it possible
for several text view widgets to share the same mime source.
\sa mimeSourceFactory()
*/
void QSWTextView::setMimeSourceFactory( QMimeSourceFactory* factory )
{
d->factory_ = factory;
viewport()->update();
}
/*!
Sets the brush to use as the background to \a pap.
This may be a nice parchment or marble pixmap or simply another
plain color.
Technically, setPaper() is just a convenience function to set the
base brush of the paperColorGroup().
\sa paper()
*/
void QSWTextView::setPaper( const QBrush& pap)
{
d->mypapcolgrp.setBrush( QColorGroup::Base, pap );
d->papcolgrp.setBrush( QColorGroup::Base, pap );
d->ownpalette = TRUE;
viewport()->setBackgroundColor( pap.color() );
viewport()->update();
}
/*!
Sets the full colorgroup of the paper to \a colgrp. If not specified
otherwise in the document itself, any text will use
QColorGroup::text(). The background will be painted with
QColorGroup::brush(QColorGroup::Base).
\sa paperColorGroup(), setPaper()
*/
void QSWTextView::setPaperColorGroup( const QColorGroup& colgrp)
{
d->mypapcolgrp = colgrp;
d->papcolgrp = colgrp;
d->ownpalette = TRUE;
viewport()->setBackgroundColor( colgrp.base() );
viewport()->update();
}
/*!
Returns the colorgroup of the paper.
\sa setPaperColorGroup(), setPaper()
*/
const QColorGroup& QSWTextView::paperColorGroup() const
{
return d->papcolgrp;
}
/*!
Sets the color used to display links in the document to \c col.
\sa linkColor()
*/
void QSWTextView::setLinkColor( const QColor& col )
{
d->mylinkcol = col;
d->paplinkcol = col;
}
/*!
Returns the current link color.
The color may either have been set with setLinkColor() or stem from
the document's body tag.
\sa setLinkColor()
*/
const QColor& QSWTextView::linkColor() const
{
return d->paplinkcol;
}
/*!
Defines whether or not links should be displayed underlined.
*/
void QSWTextView::setLinkUnderline( bool u)
{
d->linkunderline = u;
}
/*!
Returns whether or not links should be displayed underlined.
*/
bool QSWTextView::linkUnderline() const
{
return d->linkunderline;
}
/*!
Returns the document title parsed from the content.
*/
QString QSWTextView::documentTitle() const
{
return richText().attributes()["title"];
}
/*!
Returns the height of the view given a width of \a w.
*/
int QSWTextView::heightForWidth( int w ) const
{
QRichText doc( d->txt, viewport()->font(), d->contxt,
8, mimeSourceFactory(), styleSheet() );
doc.doLayout( 0, w );
return doc.height;
}
/*!
Returns the document defining the view as drawable and queryable rich
text object. This is not currently useful for applications.
*/
QRichText& QSWTextView::richText() const
{
if (!d->doc_){
QSWTextView* that = (QSWTextView*) this;
that->createRichText();
}
return *d->doc_;
}
/*!
Returns the brush used to paint the background.
*/
const QBrush& QSWTextView::paper()
{
return d->papcolgrp.brush( QColorGroup::Base );
}
/*!
Returns the brush used to paint the background.
*/
const QBrush& QSWTextView::paper() const
{
return d->papcolgrp.brush( QColorGroup::Base );
}
/*!
\reimp
*/
void QSWTextView::drawContentsOffset(QPainter* p, int ox, int oy,
int cx, int cy, int cw, int ch)
{
if ( !d->ownpalette && d->mypapcolgrp == d->papcolgrp ) {
d->mypapcolgrp = colorGroup();
d->papcolgrp = d->mypapcolgrp;
}
QTextOptions to(&paper(), d->paplinkcol, d->linkunderline );
to.offsetx = ox;
to.offsety = oy;
if ( d->selection ) {
to.selstart = d->selstart;
to.selend = d->selend;
}
QRegion r(cx-ox, cy-oy, cw, ch);
QRichTextFormatter tc( richText() );
tc.gotoParagraph( p, richText().getParBefore( cy ) );
QTextParagraph* b = tc.paragraph;
QFontMetrics fm( p->fontMetrics() );
while ( b && tc.y() <= cy + ch ) {
if ( b && b->dirty ) //ensure the paragraph is laid out
tc.updateLayout( p, cy + ch );
tc.gotoParagraph( p, b );
if ( tc.y() + tc.paragraph->height > cy ) {
do {
tc.makeLineLayout( p );
QRect geom( tc.lineGeometry() );
if ( geom.bottom() > cy && geom.top() < cy+ch )
tc.drawLine( p, ox, oy, cx, cy, cw, ch, r, paperColorGroup(), to );
}
while ( tc.gotoNextLine( p ) );
}
b = b->nextInDocument();
}
to.selstart = QtTriple();
to.selstart = to.selend;
richText().flow()->drawFloatingItems( p, ox, oy, cx, cy, cw, ch, r, paperColorGroup(), to );
p->setClipRegion(r);
if ( paper().pixmap() )
p->drawTiledPixmap(0, 0, visibleWidth(), visibleHeight(),
*paper().pixmap(), ox, oy);
else
p->fillRect(0, 0, visibleWidth(), visibleHeight(), paper() );
p->setClipping( FALSE );
#if 0
int pagesize = richText().flow()->pagesize;
if ( pagesize > 0 ) {
p->setPen( DotLine );
for (int page = cy / pagesize; page <= (cy+ch) / pagesize; ++page ) {
p->drawLine( cx-ox, page * pagesize - oy, cx-ox+cw, page*
pagesize - oy );
}
}
#endif
}
/*!
\reimp
*/
void QSWTextView::viewportResizeEvent(QResizeEvent* )
{
}
void QSWTextView::doResize()
{
if ( !d->fcresize->updateLayout( 0, d->fcresize->y() + d->fcresize->paragraph->height + 1000 ) )
d->resizeTimer->start( 0, TRUE );
QTextFlow* flow = richText().flow();
resizeContents( QMAX( flow->widthUsed-1, visibleWidth() ), flow->height );
}
/*!
\reimp
*/
void QSWTextView::resizeEvent( QResizeEvent* e )
{
setUpdatesEnabled( FALSE ); // to hinder qscrollview from showing/hiding scrollbars. Safe since we call resizeContents later!
QScrollView::resizeEvent( e );
setUpdatesEnabled( TRUE);
richText().flow()->initialize( visibleWidth() );
updateLayout();
}
/*!
\reimp
*/
void QSWTextView::viewportMousePressEvent( QMouseEvent* e )
{
if ( e->button() != LeftButton )
return;
d->cursor = e->pos() + QPoint( contentsX(), contentsY() );
QRichTextIterator it( richText() );
bool within = it.goTo( d->cursor );
bool sel = d->selection && it.position() >= d->selstart && it.position() < d->selend;
if ( !sel || !within ) {
clearSelection();
d->selorigin = it.position();
d->selstart = d->selorigin;
d->selend = d->selstart;
d->dragselection = TRUE;
}
}
/*!
\reimp
*/
void QSWTextView::viewportMouseReleaseEvent( QMouseEvent* e )
{
if ( e->button() == LeftButton ) {
d->scrollTimer->stop();
#ifndef QT_NO_CLIPBOARD
if ( d->dragselection ) {
#if defined(_WS_X11_)
if ( style() == MotifStyle )
copy();
#endif
d->dragselection = FALSE;
} else
#endif
{
clearSelection();
}
}
}
/*! Returns TRUE if there is any text selected, FALSE otherwise.
\sa selectedText()
*/
bool QSWTextView::hasSelectedText() const
{
return d->selection;
}
/*! Returns a copy of the selected text in plain text format.
\sa hasSelectedText()
*/
QString QSWTextView::selectedText() const
{
if ( !d->selection )
return QString::null;
QRichTextIterator it( richText() );
it.goTo( d->selstart );
if ( d->selstart.a == d->selend.a && d->selstart.b == d->selend.b )
return it.text().mid( d->selstart.c, d->selend.c - d->selstart.c );
int column = 0;
QString txt;
QString s = it.text().mid( d->selstart.c );
while ( it.position() < d->selend ) {
if ( !s.isEmpty() ) {
if ( column + s.length() > 79 &&
it.outmostParagraph()->style->whiteSpaceMode() == QStyleSheetItem::WhiteSpaceNormal ) {
txt += '\n';
column = 0;
}
if ( s[(int)s.length()-1]== '\n' )
column = 0;
txt += s;
column += s.length();
}
int oldpar = it.position().a;
if ( !it.right( FALSE ) )
break;
if ( it.position().a != oldpar ) {
txt += '\n';
column = 0;
}
s = it.text();
if ( it.position().a == d->selend.a && it.position().b == d->selend.b )
s = s.left( d->selend.c );
}
return txt;
}
static int logicalFontSize( QStyleSheet* style, QFont base, int pt )
{
for (int i=0; i<10; i++) {
QFont b = base;
style->scaleFont(b,i);
if ( b.pointSize() >= pt )
return i;
}
return 1; // else what?
}
static QString formatDiff(const QSWTextView* view, QTextCharFormat* pfmt, QTextCharFormat* nfmt)
{
QString txt;
QFont basefont = view->font();
if ( pfmt != nfmt ) {
QString t,pre,post;
if ( pfmt->color() != nfmt->color() ) {
QString c;
t += c.sprintf("color=#%06x ", nfmt->color().rgb());
}
if ( pfmt->font() != nfmt->font() ) {
int plsz = logicalFontSize( view->styleSheet(), basefont, pfmt->font().pointSize() );
int nlsz = logicalFontSize( view->styleSheet(), basefont, nfmt->font().pointSize() );
if ( nlsz != plsz ) {
QString f;
t += f.sprintf("size=%d ",nlsz-plsz);
}
if ( pfmt->font().family() != nfmt->font().family() ) {
t += "face=";
t += nfmt->font().family();
t += " ";
}
if ( pfmt->font().italic() != nfmt->font().italic() ) {
bool on = nfmt->font().italic();
if ( on )
post = post + "";
else
pre = "" + pre;
}
if ( pfmt->font().weight() != nfmt->font().weight() ) {
bool on = nfmt->font().weight() > 50;
if ( on )
post = post + "";
else
pre = "" + pre;
}
}
txt += pre;
if ( !t.isEmpty() ) {
t.truncate(t.length()-1); // chop space
txt += "";
}
txt += post;
}
return txt;
}
/*! Returns a copy of the selected text in rich text format (XML).
\sa hasSelectedText()
*/
QString QSWTextView::selectedRichTextInternal() const
{
if ( !d->selection )
return QString::null;
QRichTextIterator it( richText() );
it.goTo( d->selstart );
QString txt;
QString s = it.text().mid( d->selstart.c );
QTextCharFormat ifmt;
QTextCharFormat* pfmt = &ifmt;
while ( it.position() < d->selend ) {
QTextCharFormat* nfmt = it.format();
txt += formatDiff(this,pfmt,nfmt);
pfmt = nfmt;
txt += s;
int oldpar = it.position().a;
if ( !it.right( FALSE ) )
break;
if ( it.position().a != oldpar )
txt += "
";
s = it.text();
if ( it.position().a == d->selend.a && it.position().b == d->selend.b )
s = s.left( d->selend.c );
}
txt += formatDiff(this,pfmt,&ifmt);
return txt;
}
#ifndef QT_NO_CLIPBOARD
/*!
Copies the marked text to the clipboard.
*/
void QSWTextView::copy()
{
#if defined(_WS_X11_)
disconnect( QApplication::clipboard(), SIGNAL(dataChanged()), this, 0);
#endif
QString t = selectedText();
QRegExp nbsp(QChar(0x00a0U));
t.replace( nbsp, " " );
#if defined(_OS_WIN32_)
// Need to convert NL to CRLF
QRegExp nl("\\n");
t.replace( nl, "\r\n" );
#endif
QApplication::clipboard()->setText( t );
#if defined(_WS_X11_)
connect( QApplication::clipboard(), SIGNAL(dataChanged()),
this, SLOT(clipboardChanged()) );
#endif
}
#endif
/*!
Selects all text.
*/
void QSWTextView::selectAll()
{
QRichTextIterator it( richText() );
d->selstart = it.position();
while ( it.right( FALSE ) ) { }
d->selend = it.position();
viewport()->update();
d->selection = TRUE;
#if defined(_WS_X11_)
copy();
#endif
}
/*!
\reimp
*/
void QSWTextView::viewportMouseMoveEvent( QMouseEvent* e)
{
if (e->state() & LeftButton ) {
if (d->dragselection ) {
doSelection( e->pos() );
ensureVisible( d->cursor.x(), d->cursor.y() );
}
}
}
/*!
Provides scrolling and paging.
*/
void QSWTextView::keyPressEvent( QKeyEvent * e)
{
int unknown = 0;
switch (e->key()) {
case Key_Right:
scrollBy( 10, 0 );
break;
case Key_Left:
scrollBy( -10, 0 );
break;
case Key_Up:
scrollBy( 0, -10 );
break;
case Key_Down:
scrollBy( 0, 10 );
break;
case Key_Home:
setContentsPos(0,0);
break;
case Key_End:
setContentsPos(0,contentsHeight()-visibleHeight());
break;
case Key_PageUp:
scrollBy( 0, -visibleHeight() );
break;
case Key_PageDown:
scrollBy( 0, visibleHeight() );
break;
#ifndef QT_NO_CLIPBOARD
case Key_F16: // Copy key on Sun keyboards
copy();
break;
#if defined (_WS_WIN_)
case Key_Insert:
#endif
case Key_C:
if ( e->state() & ControlButton )
copy();
break;
#endif
default:
unknown++;
}
if ( unknown ) // unknown key
e->ignore();
}
/*!
\reimp
*/
void QSWTextView::paletteChange( const QPalette & p )
{
QScrollView::paletteChange( p );
if ( !d->ownpalette ) {
d->mypapcolgrp = palette().active();
d->papcolgrp = d->mypapcolgrp;
}
}
/*!
Returns the current text format.
\sa setTextFormat()
*/
Qt::TextFormat QSWTextView::textFormat() const
{
return d->textformat;
}
/*!
Sets the text format to \a format. Possible choices are
- \c PlainText - all characters are displayed verbatim,
including all blanks and linebreaks.
- \c RichText - rich text rendering. The available
styles are defined in the default stylesheet
QStyleSheet::defaultSheet().
- \c AutoText - this is also the default. The label
autodetects which rendering style suits best, \c PlainText
or \c RichText. Technically, this is done by using the
QStyleSheet::mightBeRichText() heuristic.
*/
void QSWTextView::setTextFormat( Qt::TextFormat format )
{
d->textformat = format;
setText( d->original_txt, d->contxt ); // trigger update
}
/*!\internal
*/
void QSWTextView::updateLayout()
{
if ( !isVisible() ) {
d->dirty = TRUE;
return;
}
QSize cs( viewportSize( contentsWidth(), contentsHeight() ) );
int ymax = contentsY() + cs.height() + 1;
delete d->fcresize;
d->fcresize = new QRichTextFormatter( richText() );
d->fcresize->initParagraph( 0, &richText() );
d->fcresize->updateLayout( 0, ymax );
QTextFlow* flow = richText().flow();
QSize vs( viewportSize( flow->widthUsed, flow->height ) );
if ( vs.width() != visibleWidth() ) {
flow->initialize( vs.width() );
richText().invalidateLayout();
d->fcresize->gotoParagraph( 0, &richText() );
d->fcresize->updateLayout( 0, ymax );
}
resizeContents( QMAX( flow->widthUsed-1, vs.width() ), flow->height );
d->resizeTimer->start( 0, TRUE );
d->dirty = FALSE;
}
/*!\reimp
*/
void QSWTextView::showEvent( QShowEvent* )
{
if ( d->dirty )
updateLayout();
}
void QSWTextView::clearSelection()
{
#ifndef QT_NO_DRAGANDDROP
d->dragTimer->stop();
#endif
if ( !d->selection )
return; // nothing to do
d->selection = FALSE;
QRichTextIterator it( richText() );
it.goTo( d->selend );
int y = it.lineGeometry().bottom();
it.goTo( d->selstart );
if ( y - it.lineGeometry().top() >= visibleHeight() )
viewport()->update();
else {
QRect r = it.lineGeometry();
while ( it.position() < d->selend && it.right() ) {
r = r.unite( it.lineGeometry() );
}
updateContents( r );
}
}
void QSWTextView::doAutoScroll()
{
QPoint pos = viewport()->mapFromGlobal( QCursor::pos() );
if ( pos.y() < 0 )
scrollBy( 0, -32 );
else if (pos.y() > visibleHeight() )
scrollBy( 0, 32 );
doSelection( pos );
}
void QSWTextView::doSelection( const QPoint& pos )
{
QPoint to( pos + QPoint( contentsX(), contentsY() ) );
if ( to != d->cursor ) {
QRichTextIterator it( richText() );
it.goTo( to );
d->selection = TRUE;
if ( (it.position() != d->selstart) && (it.position() != d->selend) ) {
if ( it.position() < d->selorigin ) {
d->selstart = it.position();
d->selend = d->selorigin;
} else {
d->selstart = d->selorigin;
d->selend = it.position();
}
QRichTextIterator it2( richText() );
it2.goTo( d->cursor );
QRect r = it2.lineGeometry();
r = r.unite( it.lineGeometry() );
while ( it.position() < it2.position() && it.right( FALSE ) )
r = r.unite( it.lineGeometry() );
while ( it2.position() < it.position() && it2.right( FALSE ) )
r = r.unite( it2.lineGeometry() );
d->cursor = to;
repaintContents( r, FALSE );
}
}
if ( pos.y() < 0 || pos.y() > visibleHeight() )
d->scrollTimer->start( 100, FALSE );
else
d->scrollTimer->stop();
}
void QSWTextView::clipboardChanged()
{
#if defined(_WS_X11_)
disconnect( QApplication::clipboard(), SIGNAL(dataChanged()),
this, SLOT(clipboardChanged()) );
clearSelection();
#endif
}
/*!\reimp
*/
void QSWTextView::focusInEvent( QFocusEvent * )
{
setMicroFocusHint(width()/2, 0, 1, height(), FALSE);
}
/*!\reimp
*/
void QSWTextView::focusOutEvent( QFocusEvent * )
{
}
#endif // QT_NO_TEXTVIEW