summaryrefslogtreecommitdiff
path: root/noncore/apps/opie-write
authorleseb <leseb>2002-07-14 21:21:35 (UTC)
committer leseb <leseb>2002-07-14 21:21:35 (UTC)
commit4feeec8b5b41cfd3d13274411f515524f687da09 (patch) (side-by-side diff)
tree002bbfb9997713e5d5975855d3cfbba7a71b9104 /noncore/apps/opie-write
parentbdef9cf23ced569a9bc80c1d4f25d85861273b4a (diff)
downloadopie-4feeec8b5b41cfd3d13274411f515524f687da09.zip
opie-4feeec8b5b41cfd3d13274411f515524f687da09.tar.gz
opie-4feeec8b5b41cfd3d13274411f515524f687da09.tar.bz2
opie-write first draft
Diffstat (limited to 'noncore/apps/opie-write') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/opie-write/main.cpp37
-rw-r--r--noncore/apps/opie-write/mainwindow.cpp570
-rw-r--r--noncore/apps/opie-write/mainwindow.h113
-rw-r--r--noncore/apps/opie-write/opie-write.pro25
-rw-r--r--noncore/apps/opie-write/qcleanuphandler.h139
-rw-r--r--noncore/apps/opie-write/qcomplextext.cpp152
-rw-r--r--noncore/apps/opie-write/qcomplextext_p.h133
-rw-r--r--noncore/apps/opie-write/qrichtext.cpp8085
-rw-r--r--noncore/apps/opie-write/qrichtext_p.cpp706
-rw-r--r--noncore/apps/opie-write/qrichtext_p.h2158
-rw-r--r--noncore/apps/opie-write/qstylesheet.cpp1484
-rw-r--r--noncore/apps/opie-write/qstylesheet.h221
-rw-r--r--noncore/apps/opie-write/qt3namespace.h28
-rw-r--r--noncore/apps/opie-write/qtextedit.cpp4516
-rw-r--r--noncore/apps/opie-write/qtextedit.h448
15 files changed, 18815 insertions, 0 deletions
diff --git a/noncore/apps/opie-write/main.cpp b/noncore/apps/opie-write/main.cpp
new file mode 100644
index 0000000..027af38
--- a/dev/null
+++ b/noncore/apps/opie-write/main.cpp
@@ -0,0 +1,37 @@
+/**********************************************************************
+** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer 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.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include <qpe/qpeapplication.h>
+#include <qpe/fileselector.h>
+#include "mainwindow.h"
+
+int main( int argc, char ** argv )
+{
+ QPEApplication a( argc, argv );
+
+ MainWindow e;
+ a.showMainDocumentWidget(&e);
+
+ QObject::connect( &a, SIGNAL( lastWindowClosed() ),
+ &a, SLOT( quit() ) );
+
+ a.exec();
+}
diff --git a/noncore/apps/opie-write/mainwindow.cpp b/noncore/apps/opie-write/mainwindow.cpp
new file mode 100644
index 0000000..ed95e83
--- a/dev/null
+++ b/noncore/apps/opie-write/mainwindow.cpp
@@ -0,0 +1,570 @@
+/**********************************************************************
+** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer 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.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "mainwindow.h"
+#include <qpe/fileselector.h>
+#include <qpe/applnk.h>
+#include <qpe/resource.h>
+//#include "qspellchecker.h"
+#include "qtextedit.h"
+#include <qaction.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qtabwidget.h>
+#include <qapplication.h>
+#include <qfontdatabase.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qfiledialog.h>
+#include <qprinter.h>
+#include <qpaintdevicemetrics.h>
+#include <qmenubar.h>
+#include <qpopupmenu.h>
+#include <qcolordialog.h>
+#include <qpainter.h>
+#include <qstyle.h>
+
+class ButtonMenu : public QToolButton
+{
+ Q_OBJECT
+public:
+ ButtonMenu( QWidget *parent, const char *name=0 )
+ : QToolButton( parent, name ), current(0)
+ {
+ setPopup( new QPopupMenu( this ) );
+ setPopupDelay( 1 );
+ connect( popup(), SIGNAL(activated(int)), this, SLOT(selected(int)) );
+ }
+
+ int insertItem(const QIconSet &icon, const QString &text, int id ) {
+ if ( !popup()->count() ) {
+ setIconSet( icon );
+ current = id;
+ }
+ return popup()->insertItem( icon, text, id );
+ }
+
+ void setCurrentItem( int id ) {
+ if ( id != current ) {
+ current = id;
+ setIconSet( *popup()->iconSet( id ) );
+ }
+ }
+
+ virtual QSize sizeHint() const {
+ return QToolButton::sizeHint() + QSize( 4, 0 );
+ }
+
+signals:
+ void activated( int id );
+
+protected slots:
+ void selected( int id ) {
+ current = id;
+ setIconSet( *popup()->iconSet( id ) );
+ emit activated( id );
+ }
+
+protected:
+ virtual void drawButtonLabel( QPainter *p ) {
+ p->translate( -4, 0 );
+ QToolButton::drawButtonLabel( p );
+ p->translate( 4, 0 );
+ }
+
+private:
+ int current;
+};
+
+//===========================================================================
+
+MainWindow::MainWindow( QWidget *parent, const char *name )
+ : QMainWindow( parent, name ),
+ doc( 0 )
+{
+ setRightJustification(TRUE);
+
+ editorStack = new QWidgetStack( this );
+
+ fileSelector = new FileSelector( "text/html",
+ editorStack, "fileselector" );
+
+
+ fileSelector->setCloseVisible( FALSE );
+ editorStack->addWidget( fileSelector, 0 );
+
+ editor = new Qt3::QTextEdit( editorStack );
+ editor->setTextFormat( Qt::RichText );
+ editorStack->addWidget( editor, 1 );
+
+ setupActions();
+
+ QObject::connect( fileSelector, SIGNAL(closeMe()),
+ this, SLOT(showEditTools()) );
+ QObject::connect( fileSelector, SIGNAL(fileSelected(const DocLnk &)),
+ this, SLOT(openFile(const DocLnk &)) );
+ QObject::connect( fileSelector, SIGNAL(newSelected(const DocLnk&)),
+ this, SLOT(newFile(const DocLnk&)) );
+
+ if ( fileSelector->fileCount() < 1 )
+ fileNew();
+ else {
+ fileOpen();
+ }
+ doConnections( editor );
+
+ setCentralWidget( editorStack );
+}
+
+MainWindow::~MainWindow()
+{
+ save();
+}
+
+void MainWindow::setupActions()
+{
+ setToolBarsMovable(false);
+
+ tbMenu = new QToolBar( this );
+ tbMenu->setHorizontalStretchable( TRUE );
+
+ QMenuBar *menu = new QMenuBar( tbMenu );
+
+ tbEdit = new QToolBar( this );
+
+ QPopupMenu *file = new QPopupMenu( this );
+ menu->insertItem( tr("File"), file );
+
+ QPopupMenu *edit = new QPopupMenu( this );
+ menu->insertItem( tr("Edit"), edit );
+
+ // ### perhaps these shortcut keys should have some
+ // IPaq keys defined???
+ QAction *a;
+
+ a = new QAction( tr( "New" ), Resource::loadPixmap("new"), QString::null, 0, this, 0 );
+ connect( a, SIGNAL(activated()), this, SLOT(fileNew()) );
+ a->addTo( file );
+
+ a = new QAction( tr( "Open" ), Resource::loadPixmap( "fileopen" ), QString::null, 0, this, 0 );
+ connect( a, SIGNAL(activated()), this, SLOT(fileOpen()) );
+ a->addTo( file );
+
+ a = new QAction( tr( "Undo" ), Resource::loadIconSet("opie-write/undo"),
+ QString::null, 0, this, "editUndo" );
+ connect( a, SIGNAL( activated() ), this, SLOT( editUndo() ) );
+ connect( editor, SIGNAL(undoAvailable(bool)), a, SLOT(setEnabled(bool)) );
+ a->addTo( tbEdit );
+ a->addTo( edit );
+ a = new QAction( tr( "Redo" ), Resource::loadIconSet("opie-write/redo"),
+ QString::null, 0, this, "editRedo" );
+ connect( a, SIGNAL( activated() ), this, SLOT( editRedo() ) );
+ connect( editor, SIGNAL(redoAvailable(bool)), a, SLOT(setEnabled(bool)) );
+ a->addTo( tbEdit );
+ a->addTo( edit );
+
+ edit->insertSeparator();
+
+ a = new QAction( tr( "Copy" ), Resource::loadIconSet("copy"),
+ QString::null, 0, this, "editCopy" );
+ connect( a, SIGNAL( activated() ), this, SLOT( editCopy() ) );
+ connect( editor, SIGNAL(copyAvailable(bool)), a, SLOT(setEnabled(bool)) );
+ a->addTo( tbEdit );
+ a->addTo( edit );
+ a = new QAction( tr( "Cut" ), Resource::loadIconSet("cut"),
+ QString::null, 0, this, "editCut" );
+ connect( a, SIGNAL( activated() ), this, SLOT( editCut() ) );
+ connect( editor, SIGNAL(copyAvailable(bool)), a, SLOT(setEnabled(bool)) );
+ a->addTo( tbEdit );
+ a->addTo( edit );
+ a = new QAction( tr( "Paste" ), Resource::loadPixmap("paste"),
+ QString::null, 0, this, "editPaste" );
+ connect( a, SIGNAL( activated() ), this, SLOT( editPaste() ) );
+ a->addTo( tbEdit );
+ a->addTo( edit );
+
+ tbFont = new QToolBar( this );
+ tbFont->setLabel( "Font Actions" );
+ tbFont->setHorizontalStretchable(TRUE);
+
+ comboFont = new QComboBox( FALSE, tbFont );
+ QFontDatabase db;
+ comboFont->insertStringList( db.families() );
+ connect( comboFont, SIGNAL( activated( const QString & ) ),
+ this, SLOT( textFamily( const QString & ) ) );
+ comboFont->setCurrentItem( comboFont->listBox()->index( comboFont->listBox()->findItem( QApplication::font().family() ) ) );
+
+ comboSize = new QComboBox( TRUE, tbFont );
+ QValueList<int> sizes = db.standardSizes();
+ QValueList<int>::Iterator it = sizes.begin();
+ for ( ; it != sizes.end(); ++it )
+ comboSize->insertItem( QString::number( *it ) );
+ connect( comboSize, SIGNAL( activated( const QString & ) ),
+ this, SLOT( textSize( const QString & ) ) );
+ comboSize->lineEdit()->setText( QString::number( QApplication::font().pointSize() ) );
+ comboSize->setFixedWidth( 38 );
+
+ tbStyle = new QToolBar( this );
+ tbStyle->setLabel( "Style Actions" );
+
+ actionTextBold = new QAction( tr( "Bold" ),
+ Resource::loadPixmap("bold"),
+ QString::null, CTRL + Key_B,
+ this, "textBold" );
+ connect( actionTextBold, SIGNAL( activated() ), this, SLOT( textBold() ) );
+ actionTextBold->addTo( tbStyle );
+ actionTextBold->setToggleAction( TRUE );
+ actionTextItalic = new QAction( tr( "Italic" ),
+ Resource::loadPixmap("italic"),
+ tr( "&Italic" ), CTRL + Key_I,
+ this, "textItalic" );
+ connect( actionTextItalic, SIGNAL( activated() ), this,
+ SLOT( textItalic() ) );
+ actionTextItalic->addTo( tbStyle );
+ actionTextItalic->setToggleAction( TRUE );
+ actionTextUnderline = new QAction( tr( "Underline" ),
+ Resource::loadPixmap("underline"),
+ tr( "&Underline" ), CTRL + Key_U,
+ this, "textUnderline" );
+ connect( actionTextUnderline, SIGNAL( activated() ),
+ this, SLOT( textUnderline() ) );
+ actionTextUnderline->addTo( tbStyle );
+ actionTextUnderline->setToggleAction( TRUE );
+
+ alignMenu = new ButtonMenu( tbStyle );
+ alignMenu->insertItem( Resource::loadPixmap("left"), tr("Left"), AlignLeft );
+ alignMenu->insertItem( Resource::loadPixmap("center"), tr("Center"), AlignCenter );
+ alignMenu->insertItem( Resource::loadPixmap("right"), tr("Right"), AlignRight );
+ alignMenu->insertItem( Resource::loadPixmap("opie-write/justify"), tr("Full"), Qt3::AlignJustify );
+ connect( alignMenu, SIGNAL(activated(int)), this, SLOT(textAlign(int)) );
+}
+
+Qt3::QTextEdit *MainWindow::currentEditor() const
+{
+ return editor;
+}
+
+void MainWindow::doConnections( Qt3::QTextEdit *e )
+{
+ connect( e, SIGNAL( currentFontChanged( const QFont & ) ),
+ this, SLOT( fontChanged( const QFont & ) ) );
+ connect( e, SIGNAL( currentColorChanged( const QColor & ) ),
+ this, SLOT( colorChanged( const QColor & ) ) );
+ connect( e, SIGNAL( currentAlignmentChanged( int ) ),
+ this, SLOT( alignmentChanged( int ) ) );
+}
+
+void MainWindow::updateFontSizeCombo( const QFont &f )
+{
+ comboSize->clear();
+ QFontDatabase fdb;
+ QValueList<int> sizes = fdb.pointSizes( f.family() );
+ QValueList<int>::Iterator it = sizes.begin();
+ for ( ; it != sizes.end(); ++it )
+ comboSize->insertItem( QString::number( *it ) );
+}
+
+void MainWindow::editUndo()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->undo();
+}
+
+void MainWindow::editRedo()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->redo();
+}
+
+void MainWindow::editCut()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->cut();
+}
+
+void MainWindow::editCopy()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->copy();
+}
+
+void MainWindow::editPaste()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->paste();
+}
+
+void MainWindow::textBold()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->setBold( actionTextBold->isOn() );
+}
+
+void MainWindow::textUnderline()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->setUnderline( actionTextUnderline->isOn() );
+}
+
+void MainWindow::textItalic()
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->setItalic( actionTextItalic->isOn() );
+}
+
+void MainWindow::textFamily( const QString &f )
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->setFamily( f );
+ currentEditor()->viewport()->setFocus();
+}
+
+void MainWindow::textSize( const QString &p )
+{
+ if ( !currentEditor() )
+ return;
+ currentEditor()->setPointSize( p.toInt() );
+ currentEditor()->viewport()->setFocus();
+}
+
+void MainWindow::textStyle( int i )
+{
+ if ( !currentEditor() )
+ return;
+ if ( i == 0 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayBlock,
+ Qt3::QStyleSheetItem::ListDisc );
+ else if ( i == 1 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListDisc );
+ else if ( i == 2 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListCircle );
+ else if ( i == 3 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListSquare );
+ else if ( i == 4 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListDecimal );
+ else if ( i == 5 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListLowerAlpha );
+ else if ( i == 6 )
+ currentEditor()->setParagType( Qt3::QStyleSheetItem::DisplayListItem,
+ Qt3::QStyleSheetItem::ListUpperAlpha );
+ currentEditor()->viewport()->setFocus();
+}
+
+void MainWindow::textAlign( int a )
+{
+ if ( !currentEditor() )
+ return;
+ editor->setAlignment( a );
+}
+
+void MainWindow::fontChanged( const QFont &f )
+{
+ comboFont->setCurrentItem( comboFont->listBox()->index( comboFont->listBox()->findItem( f.family() ) ) );
+ updateFontSizeCombo( f );
+ comboSize->lineEdit()->setText( QString::number( f.pointSize() ) );
+ actionTextBold->setOn( f.bold() );
+ actionTextItalic->setOn( f.italic() );
+ actionTextUnderline->setOn( f.underline() );
+}
+
+void MainWindow::colorChanged( const QColor & )
+{
+}
+
+void MainWindow::alignmentChanged( int a )
+{
+ if ( ( a == Qt3::AlignAuto ) || ( a & AlignLeft )) {
+ alignMenu->setCurrentItem(AlignLeft);
+ } else if ( ( a & AlignCenter ) ) {
+ alignMenu->setCurrentItem(AlignCenter);
+ } else if ( ( a & AlignRight ) ) {
+ alignMenu->setCurrentItem(AlignRight);
+ } else if ( ( a & Qt3::AlignJustify ) ) {
+ alignMenu->setCurrentItem(Qt3::AlignJustify);
+ }
+}
+
+void MainWindow::editorChanged( QWidget * )
+{
+ if ( !currentEditor() )
+ return;
+ fontChanged( currentEditor()->font() );
+ colorChanged( currentEditor()->color() );
+ alignmentChanged( currentEditor()->alignment() );
+}
+
+void MainWindow::fileOpen()
+{
+ save();
+ editorStack->raiseWidget( fileSelector );
+ fileSelector->reread();
+ hideEditTools();
+ fileSelector->setNewVisible( TRUE );
+ clear();
+ updateCaption();
+}
+
+void MainWindow::fileRevert()
+{
+ qDebug( "QMainWindow::fileRevert needs to be done" );
+}
+
+void MainWindow::fileNew()
+{
+ editor->setTextFormat( Qt::RichText );
+ save();
+ newFile(DocLnk());
+}
+
+void MainWindow::insertTable()
+{
+ qDebug( "MainWindow::insertTable() needs to be done" );
+}
+
+void MainWindow::newFile( const DocLnk &dl )
+{
+ DocLnk nf = dl;
+ nf.setType( "text/html" );
+ clear();
+ editorStack->raiseWidget( editor );
+ editor->viewport()->setFocus();
+ doc = new DocLnk( nf );
+ updateCaption();
+}
+
+void MainWindow::openFile( const DocLnk &dl )
+{
+ FileManager fm;
+ QString txt;
+ if ( !fm.loadFile( dl, txt ) )
+ qDebug( "couldn't open file" );
+ clear();
+ editorStack->raiseWidget( editor );
+ editor->viewport()->setFocus();
+ doc = new DocLnk( dl );
+ editor->setText( txt );
+ editor->setModified( FALSE );
+ updateCaption();
+}
+
+void MainWindow::showEditTools( void )
+{
+ tbMenu->show();
+ tbEdit->show();
+ tbFont->show();
+ tbStyle->show();
+}
+
+void MainWindow::hideEditTools( void )
+{
+ // let's reset the buttons...
+ actionTextBold->setOn( FALSE );
+ actionTextItalic->setOn( FALSE );
+ actionTextUnderline->setOn( FALSE );
+ //comboFont->setCurrentText( QApplication::font().family() );
+ comboSize->lineEdit()->setText( QString::number(QApplication::font().pointSize() ) );
+ tbMenu->hide();
+ tbEdit->hide();
+ tbFont->hide();
+ tbStyle->hide();
+}
+
+
+void MainWindow::save()
+{
+ if ( !doc )
+ return;
+ if ( !editor->isModified() )
+ return;
+
+ QString rt = editor->text();
+
+ // quick hack to get around formatting...
+ editor->setTextFormat( Qt::PlainText );
+ QString pt = editor->text();
+ editor->setTextFormat( Qt::RichText );
+
+ if ( doc->name().isEmpty() ) {
+ unsigned ispace = pt.find( ' ' );
+ unsigned ienter = pt.find( '\n' );
+ int i = (ispace < ienter) ? ispace : ienter;
+ QString docname;
+ if ( i == -1 ) {
+ if ( pt.isEmpty() )
+ docname = "Empty Text";
+ else
+ docname = pt;
+ } else {
+ docname = pt.left( i );
+ }
+ doc->setName(docname);
+ }
+ FileManager fm;
+ fm.saveFile( *doc, rt );
+}
+
+void MainWindow::clear()
+{
+ delete doc;
+ doc = 0;
+ editor->clear();
+}
+
+void MainWindow::updateCaption()
+{
+ if ( !doc )
+ setCaption( tr("Rich Text Editor") );
+ else {
+ QString s = doc->name();
+ if ( s.isEmpty() )
+ s = tr( "Unnamed" );
+ setCaption( s + " - " + tr("Rich Text Editor") );
+ }
+}
+
+void MainWindow::closeEvent( QCloseEvent *e )
+{
+ if ( editorStack->visibleWidget() == editor ) {
+ // call fileOpen instead, don't close it
+ fileOpen();
+ e->ignore();
+ } else {
+ e->accept();
+ }
+}
+
+#include "mainwindow.moc"
diff --git a/noncore/apps/opie-write/mainwindow.h b/noncore/apps/opie-write/mainwindow.h
new file mode 100644
index 0000000..565ad05
--- a/dev/null
+++ b/noncore/apps/opie-write/mainwindow.h
@@ -0,0 +1,113 @@
+/**********************************************************************
+** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the Qtopia Environment.
+**
+** Licensees holding valid Qtopia Developer license may use this
+** file in accordance with the Qtopia Developer 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.
+**
+** email sales@trolltech.com for information about Qtopia License
+** Agreements.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <qmainwindow.h>
+#include <qwidgetstack.h>
+#include <qmap.h>
+#include <qpe/filemanager.h>
+
+class QAction;
+class QComboBox;
+class FileSelectorView;
+class FileSelector;
+class QToolBar;
+class ButtonMenu;
+
+namespace Qt3 {
+
+class QTextEdit;
+
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow( QWidget *parent = 0, const char *name = 0 );
+ ~MainWindow();
+
+protected:
+ void closeEvent( QCloseEvent *e );
+
+private slots:
+ // new file functions
+ void fileOpen();
+ void fileRevert();
+ void fileNew();
+
+ void editUndo();
+ void editRedo();
+ void editCut();
+ void editCopy();
+ void editPaste();
+
+ void textBold();
+ void textUnderline();
+ void textItalic();
+ void textFamily( const QString &f );
+ void textSize( const QString &p );
+ void textStyle( int s );
+ void textAlign( int );
+
+ void fontChanged( const QFont &f );
+ void colorChanged( const QColor &c );
+ void alignmentChanged( int a );
+ void editorChanged( QWidget * );
+
+ // these are from textedit, we may need them
+ void insertTable();
+ void newFile( const DocLnk & );
+ void openFile( const DocLnk & );
+ void showEditTools();
+ void hideEditTools();
+
+private:
+ void updateFontSizeCombo( const QFont &f );
+ void setupActions();
+ Qt3::QTextEdit *currentEditor() const;
+ void doConnections( Qt3::QTextEdit *e );
+ void updateCaption();
+ void save();
+ void clear();
+
+ // added these from the textedit
+ QWidgetStack *editorStack;
+ FileSelector *fileSelector;
+ QToolBar *tbMenu;
+ QToolBar *tbEdit;
+ QToolBar *tbFont;
+ QToolBar *tbStyle;
+ QAction *actionTextBold,
+ *actionTextUnderline,
+ *actionTextItalic;
+ QComboBox *comboFont,
+ *comboSize;
+ ButtonMenu *alignMenu;
+ DocLnk *doc;
+ Qt3::QTextEdit* editor;
+};
+
+
+#endif
diff --git a/noncore/apps/opie-write/opie-write.pro b/noncore/apps/opie-write/opie-write.pro
new file mode 100644
index 0000000..e2ce3a9
--- a/dev/null
+++ b/noncore/apps/opie-write/opie-write.pro
@@ -0,0 +1,25 @@
+TEMPLATE = app
+CONFIG += qt warn_on release
+
+DESTDIR = $(OPIEDIR)/bin
+
+HEADERS = qcleanuphandler.h \
+ qcomplextext_p.h \
+ qrichtext_p.h \
+ qstylesheet.h \
+ qtextedit.h \
+ mainwindow.h
+
+SOURCES = qcomplextext.cpp \
+ qstylesheet.cpp \
+ qrichtext_p.cpp \
+ qrichtext.cpp \
+ qtextedit.cpp \
+ main.cpp \
+ mainwindow.cpp
+
+INCLUDEPATH += $(OPIEDIR)/include
+DEPENDPATH += $(OPIEDIR)/include
+LIBS += -lqpe
+
+TARGET = opie-write
diff --git a/noncore/apps/opie-write/qcleanuphandler.h b/noncore/apps/opie-write/qcleanuphandler.h
new file mode 100644
index 0000000..5c5bf16
--- a/dev/null
+++ b/noncore/apps/opie-write/qcleanuphandler.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+** $Id$
+**
+** ...
+**
+** Copyright (C) 2001-2002 Trolltech AS. All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QCLEANUPHANDLER_H
+#define QCLEANUPHANDLER_H
+
+#ifndef QT_H
+#include <qlist.h>
+#endif // QT_H
+
+template<class Type>
+#ifdef Q_NO_TEMPLATE_EXPORT
+class QCleanupHandler
+#else
+class Q_EXPORT QCleanupHandler
+#endif
+{
+public:
+ QCleanupHandler() : cleanupObjects( 0 ) {}
+ ~QCleanupHandler() { clear(); }
+
+ Type* add( Type **object ) {
+ if ( !cleanupObjects )
+ cleanupObjects = new QPtrList<Type*>;
+ cleanupObjects->insert( 0, object );
+ return *object;
+ }
+
+ void remove( Type **object ) {
+ if ( !cleanupObjects )
+ return;
+ if ( cleanupObjects->findRef( object ) >= 0 )
+ (void) cleanupObjects->take();
+ }
+
+ bool isEmpty() const {
+ return cleanupObjects ? cleanupObjects->isEmpty() : TRUE;
+ }
+
+ void clear() {
+ if ( !cleanupObjects )
+ return;
+ QPtrListIterator<Type*> it( *cleanupObjects );
+ Type **object;
+ while ( ( object = it.current() ) ) {
+ delete *object;
+ *object = 0;
+ cleanupObjects->remove( object );
+ }
+ delete cleanupObjects;
+ cleanupObjects = 0;
+ }
+
+private:
+ QPtrList<Type*> *cleanupObjects;
+};
+
+template<class Type>
+#ifdef Q_NO_TEMPLATE_EXPORT
+class QSingleCleanupHandler
+#else
+class Q_EXPORT QSingleCleanupHandler
+#endif
+{
+public:
+ QSingleCleanupHandler() : object( 0 ) {}
+ ~QSingleCleanupHandler() {
+ if ( object ) {
+ delete *object;
+ *object = 0;
+ }
+ }
+ Type* set( Type **o ) {
+ object = o;
+ return *object;
+ }
+ void reset() { object = 0; }
+private:
+ Type **object;
+};
+
+template<class Type>
+#ifdef Q_NO_TEMPLATE_EXPORT
+class QSharedCleanupHandler
+#else
+class Q_EXPORT QSharedCleanupHandler
+#endif
+{
+public:
+ QSharedCleanupHandler() : object( 0 ) {}
+ ~QSharedCleanupHandler() {
+ if ( object ) {
+ if ( (*object)->deref() )
+ delete *object;
+ *object = 0;
+ }
+ }
+ Type* set( Type **o ) {
+ object = o;
+ return *object;
+ }
+ void reset() { object = 0; }
+private:
+ Type **object;
+};
+
+#endif //QCLEANUPHANDLER_H
diff --git a/noncore/apps/opie-write/qcomplextext.cpp b/noncore/apps/opie-write/qcomplextext.cpp
new file mode 100644
index 0000000..0fa6c2e
--- a/dev/null
+++ b/noncore/apps/opie-write/qcomplextext.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of some internal classes
+**
+** Created :
+**
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qcomplextext_p.h"
+
+#include "qrichtext_p.h"
+#include "qfontmetrics.h"
+#include "qrect.h"
+
+#include <stdlib.h>
+
+using namespace Qt3;
+
+// -----------------------------------------------------
+
+/* a small helper class used internally to resolve Bidi embedding levels.
+ Each line of text caches the embedding level at the start of the line for faster
+ relayouting
+*/
+QBidiContext::QBidiContext( uchar l, QChar::Direction e, QBidiContext *p, bool o )
+ : level(l) , override(o), dir(e)
+{
+ if ( p )
+ p->ref();
+ parent = p;
+ count = 0;
+}
+
+QBidiContext::~QBidiContext()
+{
+ if( parent && parent->deref() )
+ delete parent;
+}
+
+static QChar *shapeBuffer = 0;
+static int shapeBufSize = 0;
+
+/*
+ Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
+ arabic).
+
+ Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
+ transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
+
+ Right join-causing: dual + center
+ Left join-causing: dual + right + center
+
+ Rules are as follows (for a string already in visual order, as we have it here):
+
+ R1 Transparent characters do not affect joining behaviour.
+ R2 A right joining character, that has a right join-causing char on the right will get form XRight
+ (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
+ Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
+ R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
+ the right will get form XMedial
+ R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
+ will get form XRight
+ R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
+ will get form XLeft
+ R7 Otherwise the character will get form XIsolated
+
+ Additionally we have to do the minimal ligature support for lam-alef ligatures:
+
+ L1 Transparent characters do not affect ligature behaviour.
+ L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
+ L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
+
+ The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
+ previous and left with next in the above rules ;-)
+*/
+
+/*
+ Two small helper functions for arabic shaping. They get the next shape causing character on either
+ side of the char in question. Implements rule R1.
+
+ leftChar() returns true if the char to the left is a left join-causing char
+ rightChar() returns true if the char to the right is a right join-causing char
+*/
+static inline const QChar *prevChar( const QString &str, int pos )
+{
+ //qDebug("leftChar: pos=%d", pos);
+ pos--;
+ const QChar *ch = str.unicode() + pos;
+ while( pos > -1 ) {
+ if( !ch->isMark() )
+ return ch;
+ pos--;
+ ch--;
+ }
+ return &QChar::replacement;
+}
+
+static inline const QChar *nextChar( const QString &str, int pos)
+{
+ pos++;
+ int len = str.length();
+ const QChar *ch = str.unicode() + pos;
+ while( pos < len ) {
+ //qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining());
+ if( !ch->isMark() )
+ return ch;
+ // assume it's a transparent char, this might not be 100% correct
+ pos++;
+ ch++;
+ }
+ return &QChar::replacement;
+}
+
+static inline bool prevVisualCharJoins( const QString &str, int pos)
+{
+ return ( prevChar( str, pos )->joining() != QChar::OtherJoining );
+}
+
+static inline bool nextVisualCharJoins( const QString &str, int pos)
+{
+ QChar::Joining join = nextChar( str, pos )->joining();
+ return ( join == QChar::Dual || join == QChar::Center );
+}
diff --git a/noncore/apps/opie-write/qcomplextext_p.h b/noncore/apps/opie-write/qcomplextext_p.h
new file mode 100644
index 0000000..b2d8293
--- a/dev/null
+++ b/noncore/apps/opie-write/qcomplextext_p.h
@@ -0,0 +1,133 @@
+/****************************************************************************
+** $Id$
+**
+** Internal header file.
+**
+** Created :
+**
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QCOMPLEXTEXT_H
+#define QCOMPLEXTEXT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of Qt Remote Control. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#ifndef QT_H
+#include "qt3namespace.h"
+#include <qstring.h>
+#include <qpointarray.h>
+#include <qfont.h>
+#include <qpainter.h>
+#include <qlist.h>
+#include <qshared.h>
+#endif // QT_H
+
+class QFontPrivate;
+
+namespace Qt3 {
+
+// bidi helper classes. Internal to Qt
+struct Q_EXPORT QBidiStatus {
+ QBidiStatus() {
+ eor = QChar::DirON;
+ lastStrong = QChar::DirON;
+ last = QChar:: DirON;
+ }
+ QChar::Direction eor;
+ QChar::Direction lastStrong;
+ QChar::Direction last;
+};
+
+struct Q_EXPORT QBidiContext : public QShared {
+ // ### ref and deref parent?
+ QBidiContext( uchar level, QChar::Direction embedding, QBidiContext *parent = 0, bool override = FALSE );
+ ~QBidiContext();
+
+ unsigned char level;
+ bool override : 1;
+ QChar::Direction dir : 5;
+
+ QBidiContext *parent;
+};
+
+struct Q_EXPORT QBidiControl {
+ QBidiControl() { context = 0; }
+ QBidiControl( QBidiContext *c, QBidiStatus s)
+ { context = c; if( context ) context->ref(); status = s; }
+ ~QBidiControl() { if ( context && context->deref() ) delete context; }
+ void setContext( QBidiContext *c ) { if ( context == c ) return; if ( context && context->deref() ) delete context; context = c; context->ref(); }
+ QBidiContext *context;
+ QBidiStatus status;
+};
+
+struct Q_EXPORT QTextRun {
+ QTextRun(int _start, int _stop, QBidiContext *context, QChar::Direction dir);
+
+ int start;
+ int stop;
+ // explicit + implicit levels here
+ uchar level;
+};
+
+class Q_EXPORT QComplexText {
+public:
+ enum Shape {
+ XIsolated,
+ XFinal,
+ XInitial,
+ XMedial
+ };
+ static Shape glyphVariant( const QString &str, int pos);
+ static Shape glyphVariantLogical( const QString &str, int pos);
+
+ static QChar shapedCharacter(const QString &str, int pos, const QFontMetrics *fm = 0);
+
+ // positions non spacing marks relative to the base character at position pos.
+ static QPointArray positionMarks( QFontPrivate *f, const QString &str, int pos, QRect *boundingRect = 0 );
+
+ static QPtrList<QTextRun> *bidiReorderLine( QBidiControl *control, const QString &str, int start, int len,
+ QChar::Direction basicDir = QChar::DirON );
+ static QString bidiReorderString( const QString &str, QChar::Direction basicDir = QChar::DirON );
+};
+
+} // namespace Qt3
+
+#endif
diff --git a/noncore/apps/opie-write/qrichtext.cpp b/noncore/apps/opie-write/qrichtext.cpp
new file mode 100644
index 0000000..7901000
--- a/dev/null
+++ b/noncore/apps/opie-write/qrichtext.cpp
@@ -0,0 +1,8085 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of the internal Qt classes dealing with rich text
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qrichtext_p.h"
+
+#include "qstringlist.h"
+#include "qfont.h"
+#include "qtextstream.h"
+#include "qfile.h"
+#include "qregexp.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+#include "qmap.h"
+#include "qfileinfo.h"
+#include "qstylesheet.h"
+#include "qmime.h"
+#include "qregexp.h"
+#include "qimage.h"
+#include "qdragobject.h"
+#include "qpaintdevicemetrics.h"
+#include "qpainter.h"
+#include "qdrawutil.h"
+#include "qcursor.h"
+#include "qstack.h"
+#include "qstyle.h"
+#include "qcomplextext_p.h"
+#include "qcleanuphandler.h"
+
+#include <stdlib.h>
+
+using namespace Qt3;
+
+//#define PARSER_DEBUG
+//#define DEBUG_COLLECTION// ---> also in qrichtext_p.h
+//#define DEBUG_TABLE_RENDERING
+
+static QTextFormatCollection *qFormatCollection = 0;
+
+const int QStyleSheetItem_WhiteSpaceNoCompression = 3; // ### belongs in QStyleSheetItem, fix 3.1
+const int QStyleSheetItem_WhiteSpaceNormalWithNewlines = 4; // ### belongs in QStyleSheetItem, fix 3.1
+
+const int border_tolerance = 2;
+
+#if defined(PARSER_DEBUG)
+static QString debug_indent;
+#endif
+
+#ifdef Q_WS_WIN
+#include "qt_windows.h"
+#endif
+
+static inline bool is_printer( QPainter *p )
+{
+ if ( !p || !p->device() )
+ return FALSE;
+ return p->device()->devType() == QInternal::Printer;
+}
+
+static inline int scale( int value, QPainter *painter )
+{
+ if ( is_printer( painter ) ) {
+ QPaintDeviceMetrics metrics( painter->device() );
+#if defined(Q_WS_X11)
+ value = value * metrics.logicalDpiY() / QPaintDevice::x11AppDpiY();
+#elif defined (Q_WS_WIN)
+ HDC hdc = GetDC( 0 );
+ int gdc = GetDeviceCaps( hdc, LOGPIXELSY );
+ if ( gdc )
+ value = value * metrics.logicalDpiY() / gdc;
+ ReleaseDC( 0, hdc );
+#elif defined (Q_WS_MAC)
+ value = value * metrics.logicalDpiY() / 75; // ##### FIXME
+#elif defined (Q_WS_QWS)
+ value = value * metrics.logicalDpiY() / 75;
+#endif
+ }
+ return value;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void QTextCommandHistory::addCommand( QTextCommand *cmd )
+{
+ if ( current < (int)history.count() - 1 ) {
+ QPtrList<QTextCommand> commands;
+ commands.setAutoDelete( FALSE );
+
+ for( int i = 0; i <= current; ++i ) {
+ commands.insert( i, history.at( 0 ) );
+ history.take( 0 );
+ }
+
+ commands.append( cmd );
+ history.clear();
+ history = commands;
+ history.setAutoDelete( TRUE );
+ } else {
+ history.append( cmd );
+ }
+
+ if ( (int)history.count() > steps )
+ history.removeFirst();
+ else
+ ++current;
+}
+
+QTextCursor *QTextCommandHistory::undo( QTextCursor *c )
+{
+ if ( current > -1 ) {
+ QTextCursor *c2 = history.at( current )->unexecute( c );
+ --current;
+ return c2;
+ }
+ return 0;
+}
+
+QTextCursor *QTextCommandHistory::redo( QTextCursor *c )
+{
+ if ( current > -1 ) {
+ if ( current < (int)history.count() - 1 ) {
+ ++current;
+ return history.at( current )->execute( c );
+ }
+ } else {
+ if ( history.count() > 0 ) {
+ ++current;
+ return history.at( current )->execute( c );
+ }
+ }
+ return 0;
+}
+
+bool QTextCommandHistory::isUndoAvailable()
+{
+ return current > -1;
+}
+
+bool QTextCommandHistory::isRedoAvailable()
+{
+ return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextDeleteCommand::QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str,
+ const QValueList< QPtrVector<QStyleSheetItem> > &os,
+ const QValueList<QStyleSheetItem::ListStyle> &ols,
+ const QMemArray<int> &oas)
+ : QTextCommand( d ), id( i ), index( idx ), parag( 0 ), text( str ), oldStyles( os ), oldListStyles( ols ), oldAligns( oas )
+{
+ for ( int j = 0; j < (int)text.size(); ++j ) {
+ if ( text[ j ].format() )
+ text[ j ].format()->addRef();
+ }
+}
+
+QTextDeleteCommand::QTextDeleteCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str )
+ : QTextCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
+{
+ for ( int i = 0; i < (int)text.size(); ++i ) {
+ if ( text[ i ].format() )
+ text[ i ].format()->addRef();
+ }
+}
+
+QTextDeleteCommand::~QTextDeleteCommand()
+{
+ for ( int i = 0; i < (int)text.size(); ++i ) {
+ if ( text[ i ].format() )
+ text[ i ].format()->removeRef();
+ }
+ text.resize( 0 );
+}
+
+QTextCursor *QTextDeleteCommand::execute( QTextCursor *c )
+{
+ QTextParag *s = doc ? doc->paragAt( id ) : parag;
+ if ( !s ) {
+ qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParag()->paragId() );
+ return 0;
+ }
+
+ cursor.setParag( 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 )
+{
+ QTextParag *s = doc ? doc->paragAt( id ) : parag;
+ if ( !s ) {
+ qWarning( "can't locate parag at %d, last parag: %d", id, doc->lastParag()->paragId() );
+ return 0;
+ }
+
+ cursor.setParag( s );
+ cursor.setIndex( index );
+ QString str = QTextString::toString( text );
+ cursor.insert( str, TRUE, &text );
+ cursor.setParag( s );
+ cursor.setIndex( index );
+ if ( c ) {
+ c->setParag( s );
+ c->setIndex( index );
+ for ( int i = 0; i < (int)text.size(); ++i )
+ c->gotoNextLetter();
+ }
+
+ QValueList< QPtrVector<QStyleSheetItem> >::Iterator it = oldStyles.begin();
+ QValueList<QStyleSheetItem::ListStyle>::Iterator lit = oldListStyles.begin();
+ int i = 0;
+ QTextParag *p = s;
+ bool end = FALSE;
+ while ( p ) {
+ if ( it != oldStyles.end() )
+ p->setStyleSheetItems( *it );
+ else
+ end = TRUE;
+ if ( lit != oldListStyles.end() )
+ p->setListStyle( *lit );
+ else
+ end = TRUE;
+ if ( i < (int)oldAligns.size() )
+ p->setAlignment( oldAligns.at( i ) );
+ else
+ end = TRUE;
+ if ( end )
+ break;
+ p = p->next();
+ ++it;
+ ++lit;
+ ++i;
+ }
+
+ s = cursor.parag();
+ while ( s ) {
+ s->format();
+ s->setChanged( TRUE );
+ if ( s == c->parag() )
+ break;
+ s = s->next();
+ }
+
+ return &cursor;
+}
+
+QTextFormatCommand::QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx,
+ const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl )
+ : QTextCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), format( f ), oldFormats( old ), flags( fl )
+{
+ format = d->formatCollection()->format( f );
+ for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
+ if ( oldFormats[ j ].format() )
+ oldFormats[ j ].format()->addRef();
+ }
+}
+
+QTextFormatCommand::~QTextFormatCommand()
+{
+ format->removeRef();
+ for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
+ if ( oldFormats[ j ].format() )
+ oldFormats[ j ].format()->removeRef();
+ }
+}
+
+QTextCursor *QTextFormatCommand::execute( QTextCursor *c )
+{
+ QTextParag *sp = doc->paragAt( startId );
+ QTextParag *ep = doc->paragAt( endId );
+ if ( !sp || !ep )
+ return c;
+
+ QTextCursor start( doc );
+ start.setParag( sp );
+ start.setIndex( startIndex );
+ QTextCursor end( doc );
+ end.setParag( 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 )
+{
+ QTextParag *sp = doc->paragAt( startId );
+ QTextParag *ep = doc->paragAt( endId );
+ if ( !sp || !ep )
+ return 0;
+
+ int idx = startIndex;
+ int fIndex = 0;
+ for ( ;; ) {
+ if ( oldFormats.at( fIndex ).c == '\n' ) {
+ if ( idx > 0 ) {
+ if ( idx < sp->length() && fIndex > 0 )
+ sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
+ if ( sp == ep )
+ break;
+ sp = sp->next();
+ idx = 0;
+ }
+ fIndex++;
+ }
+ if ( oldFormats.at( fIndex ).format() )
+ sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
+ idx++;
+ fIndex++;
+ if ( fIndex >= (int)oldFormats.size() )
+ break;
+ if ( idx >= sp->length() ) {
+ if ( sp == ep )
+ break;
+ sp = sp->next();
+ idx = 0;
+ }
+ }
+
+ QTextCursor end( doc );
+ end.setParag( ep );
+ end.setIndex( endIndex );
+ if ( endIndex == ep->length() )
+ end.gotoLeft();
+ *c = end;
+ return c;
+}
+
+QTextAlignmentCommand::QTextAlignmentCommand( QTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa )
+ : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
+{
+}
+
+QTextCursor *QTextAlignmentCommand::execute( QTextCursor *c )
+{
+ QTextParag *p = doc->paragAt( firstParag );
+ if ( !p )
+ return c;
+ while ( p ) {
+ p->setAlignment( newAlign );
+ if ( p->paragId() == lastParag )
+ break;
+ p = p->next();
+ }
+ return c;
+}
+
+QTextCursor *QTextAlignmentCommand::unexecute( QTextCursor *c )
+{
+ QTextParag *p = doc->paragAt( firstParag );
+ if ( !p )
+ return c;
+ int i = 0;
+ while ( p ) {
+ if ( i < (int)oldAligns.size() )
+ p->setAlignment( oldAligns.at( i ) );
+ if ( p->paragId() == lastParag )
+ break;
+ p = p->next();
+ ++i;
+ }
+ return c;
+}
+
+QTextParagTypeCommand::QTextParagTypeCommand( QTextDocument *d, int fParag, int lParag, bool l,
+ QStyleSheetItem::ListStyle s, const QValueList< QPtrVector<QStyleSheetItem> > &os,
+ const QValueList<QStyleSheetItem::ListStyle> &ols )
+ : QTextCommand( d ), firstParag( fParag ), lastParag( lParag ), list( l ), listStyle( s ), oldStyles( os ), oldListStyles( ols )
+{
+}
+
+QTextCursor *QTextParagTypeCommand::execute( QTextCursor *c )
+{
+ QTextParag *p = doc->paragAt( firstParag );
+ if ( !p )
+ return c;
+ while ( p ) {
+ p->setList( list, (int)listStyle );
+ if ( p->paragId() == lastParag )
+ break;
+ p = p->next();
+ }
+ return c;
+}
+
+QTextCursor *QTextParagTypeCommand::unexecute( QTextCursor *c )
+{
+ QTextParag *p = doc->paragAt( firstParag );
+ if ( !p )
+ return c;
+ QValueList< QPtrVector<QStyleSheetItem> >::Iterator it = oldStyles.begin();
+ QValueList<QStyleSheetItem::ListStyle>::Iterator lit = oldListStyles.begin();
+ while ( p ) {
+ if ( it != oldStyles.end() )
+ p->setStyleSheetItems( *it );
+ if ( lit != oldListStyles.end() )
+ p->setListStyle( *lit );
+ if ( p->paragId() == lastParag )
+ break;
+ p = p->next();
+ ++it;
+ ++lit;
+ }
+ return c;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextCursor::QTextCursor( QTextDocument *d )
+ : doc( d ), ox( 0 ), oy( 0 )
+{
+ nested = FALSE;
+ idx = 0;
+ string = doc ? doc->firstParag() : 0;
+ tmpIndex = -1;
+ valid = TRUE;
+}
+
+QTextCursor::QTextCursor()
+{
+}
+
+QTextCursor::QTextCursor( const QTextCursor &c )
+{
+ doc = c.doc;
+ ox = c.ox;
+ oy = c.oy;
+ nested = c.nested;
+ idx = c.idx;
+ string = c.string;
+ tmpIndex = c.tmpIndex;
+ indices = c.indices;
+ parags = c.parags;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+}
+
+QTextCursor &QTextCursor::operator=( const QTextCursor &c )
+{
+ doc = c.doc;
+ ox = c.ox;
+ oy = c.oy;
+ nested = c.nested;
+ idx = c.idx;
+ string = c.string;
+ tmpIndex = c.tmpIndex;
+ indices = c.indices;
+ parags = c.parags;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+
+ return *this;
+}
+
+bool QTextCursor::operator==( const QTextCursor &c ) const
+{
+ return doc == c.doc && string == c.string && idx == c.idx;
+}
+
+int QTextCursor::totalOffsetX() const
+{
+ if ( !nested )
+ return 0;
+ QValueStack<int>::ConstIterator xit = xOffsets.begin();
+ int xoff = ox;
+ for ( ; xit != xOffsets.end(); ++xit )
+ xoff += *xit;
+ return xoff;
+}
+
+int QTextCursor::totalOffsetY() const
+{
+ if ( !nested )
+ return 0;
+ QValueStack<int>::ConstIterator yit = yOffsets.begin();
+ int yoff = oy;
+ for ( ; yit != yOffsets.end(); ++yit )
+ yoff += *yit;
+ return yoff;
+}
+
+void QTextCursor::gotoIntoNested( const QPoint &globalPos )
+{
+ if ( !doc )
+ return;
+ push();
+ ox = 0;
+ int bl, y;
+ string->lineHeightOfChar( idx, &bl, &y );
+ oy = y + string->rect().y();
+ nested = TRUE;
+ QPoint p( globalPos.x() - offsetX(), globalPos.y() - offsetY() );
+ Q_ASSERT( string->at( idx )->isCustom() );
+ ox = string->at( idx )->x;
+ string->at( idx )->customItem()->enterAt( this, doc, string, idx, ox, oy, p );
+}
+
+void QTextCursor::invalidateNested()
+{
+ if ( nested ) {
+ QValueStack<QTextParag*>::Iterator it = parags.begin();
+ QValueStack<int>::Iterator it2 = indices.begin();
+ for ( ; it != parags.end(); ++it, ++it2 ) {
+ if ( *it == string )
+ continue;
+ (*it)->invalidate( 0 );
+ if ( (*it)->at( *it2 )->isCustom() )
+ (*it)->at( *it2 )->customItem()->invalidate();
+ }
+ }
+}
+
+void QTextCursor::insert( const QString &str, bool checkNewLine, QMemArray<QTextStringChar> *formatting )
+{
+ tmpIndex = -1;
+ bool justInsert = TRUE;
+ QString s( str );
+#if defined(Q_WS_WIN)
+ if ( checkNewLine )
+ s = s.replace( QRegExp( "\\r" ), "" );
+#endif
+ if ( checkNewLine )
+ justInsert = s.find( '\n' ) == -1;
+ if ( justInsert ) {
+ string->insert( idx, s );
+ if ( formatting ) {
+ for ( int i = 0; i < (int)s.length(); ++i ) {
+ if ( formatting->at( i ).format() ) {
+ formatting->at( i ).format()->addRef();
+ string->string()->setFormat( idx + i, formatting->at( i ).format(), TRUE );
+ }
+ }
+ }
+ idx += s.length();
+ } else {
+ QStringList lst = QStringList::split( '\n', s, TRUE );
+ QStringList::Iterator it = lst.begin();
+ int y = string->rect().y() + string->rect().height();
+ int lastIndex = 0;
+ QTextFormat *lastFormat = 0;
+ for ( ; it != lst.end(); ) {
+ if ( it != lst.begin() ) {
+ splitAndInsertEmptyParag( FALSE, TRUE );
+ string->setEndState( -1 );
+ string->prev()->format( -1, FALSE );
+ if ( lastFormat && formatting && string->prev() ) {
+ lastFormat->addRef();
+ string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, TRUE );
+ }
+ }
+ lastFormat = 0;
+ QString s = *it;
+ ++it;
+ if ( !s.isEmpty() )
+ string->insert( idx, s );
+ else
+ string->invalidate( 0 );
+ if ( formatting ) {
+ int len = s.length();
+ for ( int i = 0; i < len; ++i ) {
+ if ( formatting->at( i + lastIndex ).format() ) {
+ formatting->at( i + lastIndex ).format()->addRef();
+ string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), TRUE );
+ }
+ }
+ if ( it != lst.end() )
+ lastFormat = formatting->at( len + lastIndex ).format();
+ ++len;
+ lastIndex += len;
+ }
+
+ idx += s.length();
+ }
+ string->format( -1, FALSE );
+ int dy = string->rect().y() + string->rect().height() - y;
+ QTextParag *p = string;
+ p->setParagId( p->prev()->paragId() + 1 );
+ p = p->next();
+ while ( p ) {
+ p->setParagId( p->prev()->paragId() + 1 );
+ p->move( dy );
+ p->invalidate( 0 );
+ p->setEndState( -1 );
+ p = p->next();
+ }
+ }
+
+ int h = string->rect().height();
+ string->format( -1, TRUE );
+ if ( h != string->rect().height() )
+ invalidateNested();
+ else if ( doc && doc->parent() )
+ doc->nextDoubleBuffered = TRUE;
+}
+
+void QTextCursor::gotoLeft()
+{
+ if ( string->string()->isRightToLeft() )
+ gotoNextLetter();
+ else
+ gotoPreviousLetter();
+}
+
+void QTextCursor::gotoPreviousLetter()
+{
+ tmpIndex = -1;
+
+ if ( idx > 0 ) {
+ idx--;
+ } else if ( string->prev() ) {
+ QTextParag *s = string->prev();
+ while ( s && !s->isVisible() )
+ s = s->prev();
+ if ( s ) {
+ string = s;
+ idx = string->length() - 1;
+ }
+ } else {
+ if ( nested ) {
+ pop();
+ processNesting( Prev );
+ if ( idx == -1 ) {
+ pop();
+ if ( idx > 0 ) {
+ idx--;
+ } else if ( string->prev() ) {
+ string = string->prev();
+ idx = string->length() - 1;
+ }
+ }
+ }
+ }
+
+ const QTextStringChar *tsc = string->at( idx );
+ if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
+ processNesting( EnterEnd );
+ }
+}
+
+void QTextCursor::push()
+{
+ indices.push( idx );
+ parags.push( string );
+ xOffsets.push( ox );
+ yOffsets.push( oy );
+ nestedStack.push( nested );
+}
+
+void QTextCursor::pop()
+{
+ if ( !doc )
+ return;
+ idx = indices.pop();
+ string = parags.pop();
+ ox = xOffsets.pop();
+ oy = yOffsets.pop();
+ if ( doc->parent() )
+ doc = doc->parent();
+ nested = nestedStack.pop();
+}
+
+void QTextCursor::restoreState()
+{
+ while ( !indices.isEmpty() )
+ pop();
+}
+
+bool QTextCursor::place( const QPoint &p, QTextParag *s, bool link )
+{
+ QPoint pos( p );
+ QRect r;
+ QTextParag *str = s;
+ if ( pos.y() < s->rect().y() )
+ pos.setY( s->rect().y() );
+ while ( s ) {
+ r = s->rect();
+ r.setWidth( doc ? doc->width() : QWIDGETSIZE_MAX );
+ if ( s->isVisible() )
+ str = s;
+ if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() || !s->next() )
+ break;
+ s = s->next();
+ }
+
+ if ( !s || !str )
+ return FALSE;
+
+ s = str;
+
+ setParag( s, FALSE );
+ int y = s->rect().y();
+ int lines = s->lines();
+ QTextStringChar *chr = 0;
+ int index = 0;
+ int i = 0;
+ int cy = 0;
+ int ch = 0;
+ for ( ; i < lines; ++i ) {
+ chr = s->lineStartOfLine( i, &index );
+ cy = s->lineY( i );
+ ch = s->lineHeight( i );
+ if ( !chr )
+ return FALSE;
+ if ( pos.y() >= y + cy && pos.y() <= y + cy + ch )
+ break;
+ }
+ int nextLine;
+ if ( i < lines - 1 )
+ s->lineStartOfLine( i+1, &nextLine );
+ else
+ nextLine = s->length();
+ i = index;
+ int x = s->rect().x();
+ if ( pos.x() < x )
+ pos.setX( x + 1 );
+ int cw;
+ int curpos = s->length()-1;
+ int dist = 10000000;
+ bool inCustom = FALSE;
+ while ( i < nextLine ) {
+ chr = s->at(i);
+ int cpos = x + chr->x;
+ cw = s->string()->width( i );
+ if ( chr->isCustom() && chr->customItem()->isNested() ) {
+ if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
+ pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
+ inCustom = TRUE;
+ curpos = i;
+ break;
+ }
+ } else {
+ if( chr->rightToLeft )
+ cpos += cw;
+ int d = cpos - pos.x();
+ bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
+ if ( QABS( d ) < dist || (dist == d && dm == TRUE ) ) {
+ dist = QABS( d );
+ if ( !link || pos.x() >= x + chr->x )
+ curpos = i;
+ }
+ }
+ i++;
+ }
+ setIndex( curpos, FALSE );
+
+ if ( inCustom && doc && parag()->at( curpos )->isCustom() && parag()->at( curpos )->customItem()->isNested() ) {
+ QTextDocument *oldDoc = doc;
+ gotoIntoNested( pos );
+ if ( oldDoc == doc )
+ return TRUE;
+ QPoint p( pos.x() - offsetX(), pos.y() - offsetY() );
+ if ( !place( p, document()->firstParag(), link ) )
+ pop();
+ }
+ return TRUE;
+}
+
+void QTextCursor::processNesting( Operation op )
+{
+ if ( !doc )
+ return;
+ push();
+ ox = string->at( idx )->x;
+ int bl, y;
+ string->lineHeightOfChar( idx, &bl, &y );
+ oy = y + string->rect().y();
+ nested = TRUE;
+ bool ok = FALSE;
+
+ switch ( op ) {
+ case EnterBegin:
+ ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy );
+ break;
+ case EnterEnd:
+ ok = string->at( idx )->customItem()->enter( this, doc, string, idx, ox, oy, TRUE );
+ break;
+ case Next:
+ ok = string->at( idx )->customItem()->next( this, doc, string, idx, ox, oy );
+ break;
+ case Prev:
+ ok = string->at( idx )->customItem()->prev( this, doc, string, idx, ox, oy );
+ break;
+ case Down:
+ ok = string->at( idx )->customItem()->down( this, doc, string, idx, ox, oy );
+ break;
+ case Up:
+ ok = string->at( idx )->customItem()->up( this, doc, string, idx, ox, oy );
+ break;
+ }
+ if ( !ok )
+ pop();
+}
+
+void QTextCursor::gotoRight()
+{
+ if ( string->string()->isRightToLeft() )
+ gotoPreviousLetter();
+ else
+ gotoNextLetter();
+}
+
+void QTextCursor::gotoNextLetter()
+{
+ tmpIndex = -1;
+
+ const QTextStringChar *tsc = string->at( idx );
+ if ( tsc && tsc->isCustom() && tsc->customItem()->isNested() ) {
+ processNesting( EnterBegin );
+ return;
+ }
+
+ if ( idx < string->length() - 1 ) {
+ idx++;
+ } else if ( string->next() ) {
+ QTextParag *s = string->next();
+ while ( s && !s->isVisible() )
+ s = s->next();
+ if ( s ) {
+ string = s;
+ idx = 0;
+ }
+ } else {
+ if ( nested ) {
+ pop();
+ processNesting( Next );
+ if ( idx == -1 ) {
+ pop();
+ if ( idx < string->length() - 1 ) {
+ idx++;
+ } else if ( string->next() ) {
+ string = string->next();
+ idx = 0;
+ }
+ }
+ }
+ }
+}
+
+void QTextCursor::gotoUp()
+{
+ int indexOfLineStart;
+ int line;
+ QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
+ if ( !c )
+ return;
+
+ tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
+ if ( indexOfLineStart == 0 ) {
+ if ( !string->prev() ) {
+ if ( !nested )
+ return;
+ pop();
+ processNesting( Up );
+ if ( idx == -1 ) {
+ pop();
+ if ( !string->prev() )
+ return;
+ idx = tmpIndex = 0;
+ } else {
+ tmpIndex = -1;
+ return;
+ }
+ }
+ QTextParag *s = string->prev();
+ while ( s && !s->isVisible() )
+ s = s->prev();
+ if ( s )
+ string = s;
+ int lastLine = string->lines() - 1;
+ if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
+ return;
+ if ( indexOfLineStart + tmpIndex < string->length() )
+ idx = indexOfLineStart + tmpIndex;
+ else
+ idx = string->length() - 1;
+ } else {
+ --line;
+ int oldIndexOfLineStart = indexOfLineStart;
+ if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
+ return;
+ if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
+ idx = indexOfLineStart + tmpIndex;
+ else
+ idx = oldIndexOfLineStart - 1;
+ }
+}
+
+void QTextCursor::gotoDown()
+{
+ int indexOfLineStart;
+ int line;
+ QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
+ if ( !c )
+ return;
+
+ tmpIndex = QMAX( tmpIndex, idx - indexOfLineStart );
+ if ( line == string->lines() - 1 ) {
+ if ( !string->next() ) {
+ if ( !nested )
+ return;
+ pop();
+ processNesting( Down );
+ if ( idx == -1 ) {
+ pop();
+ if ( !string->next() )
+ return;
+ idx = tmpIndex = 0;
+ } else {
+ tmpIndex = -1;
+ return;
+ }
+ }
+ QTextParag *s = string->next();
+ while ( s && !s->isVisible() )
+ s = s->next();
+ if ( s )
+ string = s;
+ if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
+ return;
+ int end;
+ if ( string->lines() == 1 )
+ end = string->length();
+ else
+ string->lineStartOfLine( 1, &end );
+ if ( indexOfLineStart + tmpIndex < end )
+ idx = indexOfLineStart + tmpIndex;
+ else
+ idx = end - 1;
+ } else {
+ ++line;
+ int end;
+ if ( line == string->lines() - 1 )
+ end = string->length();
+ else
+ string->lineStartOfLine( line + 1, &end );
+ if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
+ return;
+ if ( indexOfLineStart + tmpIndex < end )
+ idx = indexOfLineStart + tmpIndex;
+ else
+ idx = end - 1;
+ }
+}
+
+void QTextCursor::gotoLineEnd()
+{
+ tmpIndex = -1;
+ int indexOfLineStart;
+ int line;
+ QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
+ if ( !c )
+ return;
+
+ if ( line == string->lines() - 1 ) {
+ idx = string->length() - 1;
+ } else {
+ c = string->lineStartOfLine( ++line, &indexOfLineStart );
+ indexOfLineStart--;
+ idx = indexOfLineStart;
+ }
+}
+
+void QTextCursor::gotoLineStart()
+{
+ tmpIndex = -1;
+ int indexOfLineStart;
+ int line;
+ QTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
+ if ( !c )
+ return;
+
+ idx = indexOfLineStart;
+}
+
+void QTextCursor::gotoHome()
+{
+ tmpIndex = -1;
+ if ( doc )
+ string = doc->firstParag();
+ idx = 0;
+}
+
+void QTextCursor::gotoEnd()
+{
+ if ( doc && !doc->lastParag()->isValid() )
+ return;
+
+ tmpIndex = -1;
+ if ( doc )
+ string = doc->lastParag();
+ idx = string->length() - 1;
+}
+
+void QTextCursor::gotoPageUp( int visibleHeight )
+{
+ tmpIndex = -1;
+ QTextParag *s = string;
+ int h = visibleHeight;
+ int y = s->rect().y();
+ while ( s ) {
+ if ( y - s->rect().y() >= h )
+ break;
+ s = s->prev();
+ }
+
+ if ( !s && doc )
+ s = doc->firstParag();
+
+ string = s;
+ idx = 0;
+}
+
+void QTextCursor::gotoPageDown( int visibleHeight )
+{
+ tmpIndex = -1;
+ QTextParag *s = string;
+ int h = visibleHeight;
+ int y = s->rect().y();
+ while ( s ) {
+ if ( s->rect().y() - y >= h )
+ break;
+ s = s->next();
+ }
+
+ if ( !s && doc ) {
+ s = doc->lastParag();
+ string = s;
+ idx = string->length() - 1;
+ return;
+ }
+
+ if ( !s->isValid() )
+ return;
+
+ string = s;
+ idx = 0;
+}
+
+void QTextCursor::gotoWordRight()
+{
+ if ( string->string()->isRightToLeft() )
+ gotoPreviousWord();
+ else
+ gotoNextWord();
+}
+
+void QTextCursor::gotoWordLeft()
+{
+ if ( string->string()->isRightToLeft() )
+ gotoNextWord();
+ else
+ gotoPreviousWord();
+}
+
+void QTextCursor::gotoPreviousWord()
+{
+ gotoPreviousLetter();
+ tmpIndex = -1;
+ QTextString *s = string->string();
+ bool allowSame = FALSE;
+ if ( idx == ((int)s->length()-1) )
+ return;
+ for ( int i = idx; i >= 0; --i ) {
+ if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
+ s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
+ if ( !allowSame )
+ continue;
+ idx = i + 1;
+ return;
+ }
+ if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
+ s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
+ allowSame = TRUE;
+ }
+ idx = 0;
+}
+
+void QTextCursor::gotoNextWord()
+{
+ tmpIndex = -1;
+ QTextString *s = string->string();
+ bool allowSame = FALSE;
+ for ( int i = idx; i < (int)s->length(); ++i ) {
+ if ( ! (s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
+ s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';') ) {
+ if ( !allowSame )
+ continue;
+ idx = i;
+ return;
+ }
+ if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
+ s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
+ allowSame = TRUE;
+
+ }
+
+ if ( idx < ((int)s->length()-1) ) {
+ gotoLineEnd();
+ } else if ( string->next() ) {
+ QTextParag *s = string->next();
+ while ( s && !s->isVisible() )
+ s = s->next();
+ if ( s ) {
+ string = s;
+ idx = 0;
+ }
+ } else {
+ gotoLineEnd();
+ }
+}
+
+bool QTextCursor::atParagStart()
+{
+ return idx == 0;
+}
+
+bool QTextCursor::atParagEnd()
+{
+ return idx == string->length() - 1;
+}
+
+void QTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
+{
+ if ( !doc )
+ return;
+ tmpIndex = -1;
+ QTextFormat *f = 0;
+ if ( doc->useFormatCollection() ) {
+ f = string->at( idx )->format();
+ if ( idx == string->length() - 1 && idx > 0 )
+ f = string->at( idx - 1 )->format();
+ if ( f->isMisspelled() ) {
+ f->removeRef();
+ f = doc->formatCollection()->format( f->font(), f->color() );
+ }
+ }
+
+ if ( atParagEnd() ) {
+ QTextParag *n = string->next();
+ QTextParag *s = doc->createParag( doc, string, n, updateIds );
+ if ( f )
+ s->setFormat( 0, 1, f, TRUE );
+ s->copyParagData( string );
+ if ( ind ) {
+ int oi, ni;
+ s->indent( &oi, &ni );
+ string = s;
+ idx = ni;
+ } else {
+ string = s;
+ idx = 0;
+ }
+ } else if ( atParagStart() ) {
+ QTextParag *p = string->prev();
+ QTextParag *s = doc->createParag( doc, p, string, updateIds );
+ if ( f )
+ s->setFormat( 0, 1, f, TRUE );
+ s->copyParagData( string );
+ if ( ind ) {
+ s->indent();
+ s->format();
+ indent();
+ string->format();
+ }
+ } else {
+ QString str = string->string()->toString().mid( idx, 0xFFFFFF );
+ QTextParag *n = string->next();
+ QTextParag *s = doc->createParag( doc, string, n, updateIds );
+ s->copyParagData( string );
+ s->remove( 0, 1 );
+ s->append( str, TRUE );
+ for ( uint i = 0; i < str.length(); ++i ) {
+ s->setFormat( i, 1, string->at( idx + i )->format(), TRUE );
+ if ( string->at( idx + i )->isCustom() ) {
+ QTextCustomItem * item = string->at( idx + i )->customItem();
+ s->at( i )->setCustomItem( item );
+ string->at( idx + i )->loseCustomItem();
+ }
+ }
+ string->truncate( idx );
+ if ( ind ) {
+ int oi, ni;
+ s->indent( &oi, &ni );
+ string = s;
+ idx = ni;
+ } else {
+ string = s;
+ idx = 0;
+ }
+ }
+
+ invalidateNested();
+}
+
+bool QTextCursor::remove()
+{
+ tmpIndex = -1;
+ if ( !atParagEnd() ) {
+ string->remove( idx, 1 );
+ int h = string->rect().height();
+ string->format( -1, TRUE );
+ if ( h != string->rect().height() )
+ invalidateNested();
+ else if ( doc && doc->parent() )
+ doc->nextDoubleBuffered = TRUE;
+ return FALSE;
+ } else if ( string->next() ) {
+ if ( string->length() == 1 ) {
+ string->next()->setPrev( string->prev() );
+ if ( string->prev() )
+ string->prev()->setNext( string->next() );
+ QTextParag *p = string->next();
+ delete string;
+ string = p;
+ string->invalidate( 0 );
+ QTextParag *s = string;
+ while ( s ) {
+ s->id = s->p ? s->p->id + 1 : 0;
+ s->state = -1;
+ s->needPreProcess = TRUE;
+ s->changed = TRUE;
+ s = s->n;
+ }
+ string->format();
+ } else {
+ string->join( string->next() );
+ }
+ invalidateNested();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void QTextCursor::killLine()
+{
+ if ( atParagEnd() )
+ return;
+ string->remove( idx, string->length() - idx - 1 );
+ int h = string->rect().height();
+ string->format( -1, TRUE );
+ if ( h != string->rect().height() )
+ invalidateNested();
+ else if ( doc && doc->parent() )
+ doc->nextDoubleBuffered = TRUE;
+}
+
+void QTextCursor::indent()
+{
+ int oi = 0, ni = 0;
+ string->indent( &oi, &ni );
+ if ( oi == ni )
+ return;
+
+ if ( idx >= oi )
+ idx += ni - oi;
+ else
+ idx = ni;
+}
+
+void QTextCursor::setDocument( QTextDocument *d )
+{
+ doc = d;
+ string = d->firstParag();
+ idx = 0;
+ nested = FALSE;
+ restoreState();
+ tmpIndex = -1;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextDocument::QTextDocument( QTextDocument *p )
+ : par( p ), parParag( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
+{
+ fCollection = new QTextFormatCollection;
+ init();
+}
+
+QTextDocument::QTextDocument( QTextDocument *p, QTextFormatCollection *f )
+ : par( p ), parParag( 0 ), tc( 0 ), tArray( 0 ), tStopWidth( 0 )
+{
+ fCollection = f;
+ init();
+}
+
+void QTextDocument::init()
+{
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + "new QTextDocument (%p)", this );
+#endif
+ oTextValid = TRUE;
+ mightHaveCustomItems = FALSE;
+ if ( par )
+ par->insertChild( this );
+ pProcessor = 0;
+ useFC = TRUE;
+ pFormatter = 0;
+ indenter = 0;
+ fParag = 0;
+ txtFormat = Qt::AutoText;
+ preferRichText = FALSE;
+ pages = FALSE;
+ focusIndicator.parag = 0;
+ minw = 0;
+ wused = 0;
+ minwParag = curParag = 0;
+ align = AlignAuto;
+ nSelections = 1;
+ addMargs = FALSE;
+
+ sheet_ = QStyleSheet::defaultSheet();
+ factory_ = QMimeSourceFactory::defaultFactory();
+ contxt = QString::null;
+ fCollection->setStyleSheet( sheet_ );
+
+ underlLinks = par ? par->underlLinks : TRUE;
+ backBrush = 0;
+ buf_pixmap = 0;
+ nextDoubleBuffered = FALSE;
+
+ if ( par )
+ withoutDoubleBuffer = par->withoutDoubleBuffer;
+ else
+ withoutDoubleBuffer = FALSE;
+
+ lParag = fParag = createParag( this, 0, 0 );
+ tmpCursor = 0;
+
+ cx = 0;
+ cy = 2;
+ if ( par )
+ cx = cy = 0;
+ cw = 600;
+ vw = 0;
+ flow_ = new QTextFlow;
+ flow_->setWidth( cw );
+
+ leftmargin = rightmargin = 4;
+
+ selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
+ selectionText[ Standard ] = TRUE;
+ commandHistory = new QTextCommandHistory( 100 );
+ tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
+}
+
+QTextDocument::~QTextDocument()
+{
+ if ( par )
+ par->removeChild( this );
+ clear();
+ delete commandHistory;
+ delete flow_;
+ if ( !par )
+ delete pFormatter;
+ delete fCollection;
+ delete pProcessor;
+ delete buf_pixmap;
+ delete indenter;
+ delete backBrush;
+ if ( tArray )
+ delete [] tArray;
+}
+
+void QTextDocument::clear( bool createEmptyParag )
+{
+ if ( flow_ )
+ flow_->clear();
+ while ( fParag ) {
+ QTextParag *p = fParag->next();
+ delete fParag;
+ fParag = p;
+ }
+ fParag = lParag = 0;
+ if ( createEmptyParag )
+ fParag = lParag = createParag( this );
+ selections.clear();
+}
+
+int QTextDocument::widthUsed() const
+{
+ return wused + border_tolerance;
+}
+
+int QTextDocument::height() const
+{
+ int h = 0;
+ if ( lParag )
+ h = lParag->rect().top() + lParag->rect().height() + 1;
+ int fh = flow_->boundingRect().bottom();
+ return QMAX( h, fh );
+}
+
+
+
+QTextParag *QTextDocument::createParag( QTextDocument *d, QTextParag *pr, QTextParag *nx, bool updateIds )
+{
+ return new QTextParag( d, pr, nx, updateIds );
+}
+
+bool QTextDocument::setMinimumWidth( int needed, int used, QTextParag *p )
+{
+ if ( needed == -1 ) {
+ minw = 0;
+ wused = 0;
+ p = 0;
+ }
+ if ( p == minwParag ) {
+ minw = needed;
+ emit minimumWidthChanged( minw );
+ } else if ( needed > minw ) {
+ minw = needed;
+ minwParag = p;
+ emit minimumWidthChanged( minw );
+ }
+ wused = QMAX( wused, used );
+ wused = QMAX( wused, minw );
+ cw = QMAX( minw, cw );
+ return TRUE;
+}
+
+void QTextDocument::setPlainText( const QString &text )
+{
+ clear();
+ preferRichText = FALSE;
+ oTextValid = TRUE;
+ oText = text;
+
+ int lastNl = 0;
+ int nl = text.find( '\n' );
+ if ( nl == -1 ) {
+ lParag = createParag( this, lParag, 0 );
+ if ( !fParag )
+ fParag = lParag;
+ QString s = text;
+ if ( !s.isEmpty() ) {
+ if ( s[ (int)s.length() - 1 ] == '\r' )
+ s.remove( s.length() - 1, 1 );
+ lParag->append( s );
+ }
+ } else {
+ for (;;) {
+ lParag = createParag( this, lParag, 0 );
+ if ( !fParag )
+ fParag = lParag;
+ QString s = text.mid( lastNl, nl - lastNl );
+ if ( !s.isEmpty() ) {
+ if ( s[ (int)s.length() - 1 ] == '\r' )
+ s.remove( s.length() - 1, 1 );
+ lParag->append( s );
+ }
+ if ( nl == 0xffffff )
+ break;
+ lastNl = nl + 1;
+ nl = text.find( '\n', nl + 1 );
+ if ( nl == -1 )
+ nl = 0xffffff;
+ }
+ }
+ if ( !lParag )
+ lParag = fParag = createParag( this, 0, 0 );
+}
+
+struct Q_EXPORT QTextDocumentTag {
+ QTextDocumentTag(){}
+ QTextDocumentTag( const QString&n, const QStyleSheetItem* s, const QTextFormat& f )
+ :name(n),style(s), format(f), alignment(Qt3::AlignAuto), direction(QChar::DirON),liststyle(QStyleSheetItem::ListDisc) {
+ wsm = QStyleSheetItem::WhiteSpaceNormal;
+ }
+ QString name;
+ const QStyleSheetItem* style;
+ QString anchorHref;
+ QStyleSheetItem::WhiteSpaceMode wsm;
+ QTextFormat format;
+ int alignment : 16;
+ int direction : 5;
+ QStyleSheetItem::ListStyle liststyle;
+
+ QTextDocumentTag( const QTextDocumentTag& t ) {
+ name = t.name;
+ style = t.style;
+ anchorHref = t.anchorHref;
+ wsm = t.wsm;
+ format = t.format;
+ alignment = t.alignment;
+ direction = t.direction;
+ liststyle = t.liststyle;
+ }
+ QTextDocumentTag& operator=(const QTextDocumentTag& t) {
+ name = t.name;
+ style = t.style;
+ anchorHref = t.anchorHref;
+ wsm = t.wsm;
+ format = t.format;
+ alignment = t.alignment;
+ direction = t.direction;
+ liststyle = t.liststyle;
+ return *this;
+ }
+
+#if defined(Q_FULL_TEMPLATE_INSTANTIATION)
+ bool operator==( const QTextDocumentTag& ) const { return FALSE; }
+#endif
+};
+
+#define NEWPAR do{ if ( !hasNewPar ) curpar = createParag( this, curpar ); \
+ if ( curpar->isBr ) curpar->isBr = FALSE; \
+ hasNewPar = TRUE; \
+ curpar->setAlignment( curtag.alignment ); \
+ curpar->setDirection( (QChar::Direction)curtag.direction ); \
+ space = TRUE; \
+ QPtrVector<QStyleSheetItem> vec( (uint)tags.count() + 1); \
+ int i = 0; \
+ for ( QValueStack<QTextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it ) \
+ vec.insert( i++, (*it).style ); \
+ vec.insert( i, curtag.style ); \
+ curpar->setStyleSheetItems( vec ); }while(FALSE)
+
+
+void QTextDocument::setRichText( const QString &text, const QString &context )
+{
+ setTextFormat( Qt::RichText );
+ if ( !context.isEmpty() )
+ setContext( context );
+ clear();
+ fParag = lParag = createParag( this );
+ setRichTextInternal( text );
+}
+
+static QStyleSheetItem::ListStyle chooseListStyle( const QStyleSheetItem *nstyle,
+ const QMap<QString, QString> &attr,
+ QStyleSheetItem::ListStyle curListStyle )
+{
+ if ( nstyle->name() == "ol" || nstyle->name() == "ul" ) {
+ curListStyle = nstyle->listStyle();
+ QMap<QString, QString>::ConstIterator it = attr.find( "type" );
+ if ( it != attr.end() ) {
+ QString sl = *it;
+ if ( sl == "1" ) {
+ curListStyle = QStyleSheetItem::ListDecimal;
+ } else if ( sl == "a" ) {
+ curListStyle = QStyleSheetItem::ListLowerAlpha;
+ } else if ( sl == "A" ) {
+ curListStyle = QStyleSheetItem::ListUpperAlpha;
+ } else {
+ sl = sl.lower();
+ if ( sl == "square" )
+ curListStyle = QStyleSheetItem::ListSquare;
+ else if ( sl == "disc" )
+ curListStyle = QStyleSheetItem::ListDisc;
+ else if ( sl == "circle" )
+ curListStyle = QStyleSheetItem::ListCircle;
+ }
+ }
+ }
+ return curListStyle;
+}
+
+void QTextDocument::setRichTextInternal( const QString &text )
+{
+ oTextValid = TRUE;
+ oText = text;
+ QTextParag* curpar = lParag;
+ int pos = 0;
+ QValueStack<QTextDocumentTag> tags;
+ QTextDocumentTag initag( "", sheet_->item(""), *formatCollection()->defaultFormat() );
+ QTextDocumentTag curtag = initag;
+ bool space = TRUE;
+
+ const QChar* doc = text.unicode();
+ int length = text.length();
+ bool hasNewPar = curpar->length() <= 1;
+ QString lastClose;
+ QString anchorName;
+ while ( pos < length ) {
+ if ( hasPrefix(doc, length, pos, '<' ) ){
+ if ( !hasPrefix( doc, length, pos+1, QChar('/') ) ) {
+ // open tag
+ QMap<QString, QString> attr;
+ bool emptyTag = FALSE;
+ QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
+ if ( tagname.isEmpty() )
+ continue; // nothing we could do with this, probably parse error
+
+ if ( tagname == "title" ) {
+ QString title;
+ while ( pos < length ) {
+ if ( hasPrefix( doc, length, pos, QChar('<') ) && hasPrefix( doc, length, pos+1, QChar('/') ) &&
+ parseCloseTag( doc, length, pos ) == "title" )
+ break;
+ title += doc[ pos ];
+ ++pos;
+ }
+ attribs.replace( "title", title );
+ }
+
+ const QStyleSheetItem* nstyle = sheet_->item(tagname);
+
+ if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
+// if ( tagname == "br" ) {
+// // our standard br emty-tag handling breaks
+// // inside list items, we would get another
+// // list item in this case. As workaround, fake
+// // a new paragraph instead
+// tagname = "p";
+// nstyle = sheet_->item( tagname );
+// }
+ if ( nstyle )
+ hasNewPar = FALSE; // we want empty paragraphs in this case
+ }
+
+ if ( nstyle ) {
+ // we might have to close some 'forgotten' tags
+ while ( !nstyle->allowedInContext( curtag.style ) ) {
+ QString msg;
+ msg.sprintf( "QText Warning: Document not valid ( '%s' not allowed in '%s' #%d)",
+ tagname.ascii(), curtag.style->name().ascii(), pos);
+ sheet_->error( msg );
+ if ( tags.isEmpty() )
+ break;
+ curtag = tags.pop();
+ }
+
+ // special handling for p. We do not want to nest there for HTML compatibility
+ if ( nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) {
+ while ( curtag.style->name() == "p" ) {
+ if ( tags.isEmpty() )
+ break;
+ curtag = tags.pop();
+ }
+ }
+
+ }
+
+ QTextCustomItem* custom = 0;
+ // some well-known empty tags
+ if ( tagname == "br" ) {
+ emptyTag = TRUE;
+ hasNewPar = FALSE;
+ if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
+ // when linebreaking a list item, we do not
+ // actually want a new list item but just a
+ // new line. Fake this by pushing a paragraph
+ // onto the stack
+ tags.push( curtag );
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ }
+ NEWPAR;
+ curpar->isBr = TRUE;
+ curpar->setAlignment( curtag.alignment );
+ } else if ( tagname == "hr" ) {
+ emptyTag = TRUE;
+ custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
+ NEWPAR;
+ } else if ( tagname == "table" ) {
+ QTextFormat format = curtag.format.makeTextFormat( nstyle, attr );
+ curpar->setAlignment( curtag.alignment );
+ custom = parseTable( attr, format, doc, length, pos, curpar );
+ (void)eatSpace( doc, length, pos );
+ emptyTag = TRUE;
+ } else if ( tagname == "qt" ) {
+ for ( QMap<QString, QString>::Iterator it = attr.begin(); it != attr.end(); ++it ) {
+ if ( it.key() == "bgcolor" ) {
+ QBrush *b = new QBrush( QColor( *it ) );
+ setPaper( b );
+ } else if ( it.key() == "background" ) {
+ QImage img;
+ const QMimeSource* m = factory_->data( *it, contxt );
+ if ( !m ) {
+ qWarning("QRichText: no mimesource for %s", (*it).latin1() );
+ } else {
+ if ( !QImageDrag::decode( m, img ) ) {
+ qWarning("QTextImage: cannot decode %s", (*it).latin1() );
+ }
+ }
+ if ( !img.isNull() ) {
+ QPixmap pm;
+ pm.convertFromImage( img );
+ QBrush *b = new QBrush( QColor(), pm );
+ setPaper( b );
+ }
+ } else if ( it.key() == "text" ) {
+ QColor c( *it );
+ if ( formatCollection()->defaultFormat()->color() != c ) {
+ QDict<QTextFormat> formats = formatCollection()->dict();
+ QDictIterator<QTextFormat> it( formats );
+ while ( it.current() ) {
+ if ( it.current() == formatCollection()->defaultFormat() ) {
+ ++it;
+ continue;
+ }
+ it.current()->setColor( c );
+ ++it;
+ }
+ formatCollection()->defaultFormat()->setColor( c );
+ curtag.format.setColor( c );
+ }
+ } else if ( it.key() == "link" ) {
+ linkColor = QColor( *it );
+ } else if ( it.key() == "title" ) {
+ attribs.replace( it.key(), *it );
+ }
+ }
+ } else {
+ custom = sheet_->tag( tagname, attr, contxt, *factory_ , emptyTag, this );
+ }
+
+ if ( !nstyle && !custom ) // we have no clue what this tag could be, ignore it
+ continue;
+
+ if ( custom ) {
+ int index = curpar->length() - 1;
+ if ( index < 0 )
+ index = 0;
+ QTextFormat format = curtag.format.makeTextFormat( nstyle, attr );
+ curpar->append( QChar('*') );
+ curpar->setFormat( index, 1, &format );
+ curpar->at( index )->setCustomItem( custom );
+ if ( !curtag.anchorHref.isEmpty() )
+ curpar->at(index)->setAnchor( QString::null, curtag.anchorHref );
+ if ( !anchorName.isEmpty() ) {
+ curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() );
+ anchorName = QString::null;
+ }
+ registerCustomItem( custom, curpar );
+ hasNewPar = FALSE;
+ } else if ( !emptyTag ) {
+ // ignore whitespace for inline elements if there was already one
+ if ( nstyle->whiteSpaceMode() == QStyleSheetItem::WhiteSpaceNormal
+ && ( space || nstyle->displayMode() != QStyleSheetItem::DisplayInline ) )
+ eatSpace( doc, length, pos );
+
+ // if we do nesting, push curtag on the stack,
+ // otherwise reinint curag.
+ if ( nstyle != curtag.style || nstyle->selfNesting() ) {
+ tags.push( curtag );
+ } else {
+ if ( !tags.isEmpty() )
+ curtag = tags.top();
+ else
+ curtag = initag;
+ }
+
+ const QStyleSheetItem* ostyle = curtag.style;
+
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ if ( nstyle->whiteSpaceMode() != QStyleSheetItem::WhiteSpaceNormal )
+ curtag.wsm = nstyle->whiteSpaceMode();
+ curtag.liststyle = chooseListStyle( nstyle, attr, curtag.liststyle );
+ curtag.format = curtag.format.makeTextFormat( nstyle, attr );
+ if ( nstyle->isAnchor() ) {
+ if ( !anchorName.isEmpty() )
+ anchorName += "#" + attr["name"];
+ else
+ anchorName = attr["name"];
+ curtag.anchorHref = attr["href"];
+ }
+
+ if ( nstyle->alignment() != QStyleSheetItem::Undefined )
+ curtag.alignment = nstyle->alignment();
+
+ if ( ostyle->displayMode() == QStyleSheetItem::DisplayListItem &&
+ curpar->length() <= 1
+ && nstyle->displayMode() == QStyleSheetItem::DisplayBlock ) {
+ // do not do anything, we reuse the paragraph we have
+ } else if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline && nstyle->displayMode() != QStyleSheetItem::DisplayNone ) {
+ NEWPAR;
+ }
+
+ if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
+ curpar->setListStyle( curtag.liststyle );
+ if ( attr.find( "value" ) != attr.end() )
+ curpar->setListValue( (*attr.find( "value" )).toInt() );
+ }
+
+ if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline )
+ curpar->setFormat( &curtag.format );
+
+ if ( attr.contains( "align" ) &&
+ ( curtag.name == "p" ||
+ curtag.name == "div" ||
+ curtag.name == "li" ||
+ curtag.name[ 0 ] == 'h' ) ) {
+ QString align = attr["align"];
+ if ( align == "center" )
+ curtag.alignment = Qt::AlignCenter;
+ else if ( align == "right" )
+ curtag.alignment = Qt::AlignRight;
+ else if ( align == "justify" )
+ curtag.alignment = Qt3::AlignJustify;
+ }
+ if ( attr.contains( "dir" ) &&
+ ( curtag.name == "p" ||
+ curtag.name == "div" ||
+ curtag.name == "li" ||
+ curtag.name[ 0 ] == 'h' ) ) {
+ QString dir = attr["dir"];
+ if ( dir == "rtl" )
+ curtag.direction = QChar::DirR;
+ else if ( dir == "ltr" )
+ curtag.direction = QChar::DirL;
+ }
+ if ( nstyle->displayMode() != QStyleSheetItem::DisplayInline ) {
+ curpar->setAlignment( curtag.alignment );
+ curpar->setDirection( (QChar::Direction)curtag.direction );
+ }
+ }
+ } else {
+ QString tagname = parseCloseTag( doc, length, pos );
+ lastClose = tagname;
+ if ( tagname.isEmpty() )
+ continue; // nothing we could do with this, probably parse error
+ if ( !sheet_->item( tagname ) ) // ignore unknown tags
+ continue;
+
+
+ // we close a block item. Since the text may continue, we need to have a new paragraph
+ bool needNewPar = curtag.style->displayMode() == QStyleSheetItem::DisplayBlock;
+
+ if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
+ needNewPar = TRUE;
+ hasNewPar = FALSE; // we want empty paragraphs in this case
+ }
+
+ // html slopiness: handle unbalanched tag closing
+ while ( curtag.name != tagname ) {
+ QString msg;
+ msg.sprintf( "QText Warning: Document not valid ( '%s' not closed before '%s' #%d)",
+ curtag.name.ascii(), tagname.ascii(), pos);
+ sheet_->error( msg );
+ if ( tags.isEmpty() )
+ break;
+ curtag = tags.pop();
+ }
+
+
+ // close the tag
+ if ( !tags.isEmpty() )
+ curtag = tags.pop();
+ else
+ curtag = initag;
+
+ if ( needNewPar ) {
+ if ( curtag.style->displayMode() == QStyleSheetItem::DisplayListItem ) {
+ tags.push( curtag );
+ curtag.name = "p";
+ curtag.style = sheet_->item( curtag.name ); // a list item continues, use p for that
+ }
+ NEWPAR;
+ }
+ }
+ } else {
+ // normal contents
+ QString s;
+ QChar c;
+ while ( pos < length && !hasPrefix(doc, length, pos, QChar('<') ) ){
+ QStyleSheetItem::WhiteSpaceMode wsm = curtag.wsm;
+ if ( s.length() > 4096 )
+ wsm = (QStyleSheetItem::WhiteSpaceMode)QStyleSheetItem_WhiteSpaceNormalWithNewlines;
+
+ c = parseChar( doc, length, pos, wsm );
+
+ if ( c == '\n' ) // happens only in whitespacepre-mode or with WhiteSpaceNormalWithNewlines.
+ break; // we want a new line in this case
+
+ bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U &&
+ curtag.wsm != QStyleSheetItem_WhiteSpaceNoCompression;
+
+ if ( curtag.wsm == QStyleSheetItem::WhiteSpaceNormal && c_isSpace && space )
+ continue;
+ if ( c == '\r' )
+ continue;
+ space = c_isSpace;
+ s += c;
+ }
+ if ( !s.isEmpty() && curtag.style->displayMode() != QStyleSheetItem::DisplayNone ) {
+ hasNewPar = FALSE;
+ int index = curpar->length() - 1;
+ if ( index < 0 )
+ index = 0;
+ curpar->append( s );
+ QTextFormat* f = formatCollection()->format( &curtag.format );
+ curpar->setFormat( index, s.length(), f, FALSE ); // do not use collection because we have done that already
+ f->ref += s.length() -1; // that what friends are for...
+ if ( !curtag.anchorHref.isEmpty() ) {
+ for ( int i = 0; i < int(s.length()); i++ )
+ curpar->at(index + i)->setAnchor( QString::null, curtag.anchorHref );
+ }
+ if ( !anchorName.isEmpty() ) {
+ curpar->at(index)->setAnchor( anchorName, curpar->at(index)->anchorHref() );
+ anchorName = QString::null;
+ }
+ }
+ if ( c == '\n' ) { // happens in WhiteSpacePre mode
+ hasNewPar = FALSE;
+ tags.push( curtag );
+ NEWPAR;
+ curtag = tags.pop();
+ }
+ }
+ }
+
+ if ( hasNewPar && curpar != fParag ) {
+ // 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;
+ }
+}
+
+void QTextDocument::setText( const QString &text, const QString &context )
+{
+ focusIndicator.parag = 0;
+ selections.clear();
+ if ( txtFormat == Qt::AutoText && QStyleSheet::mightBeRichText( text ) ||
+ txtFormat == Qt::RichText )
+ setRichText( text, context );
+ else
+ setPlainText( text );
+}
+
+QString QTextDocument::plainText( QTextParag *p ) const
+{
+ if ( !p ) {
+ QString buffer;
+ QString s;
+ QTextParag *p = fParag;
+ while ( p ) {
+ if ( !p->mightHaveCustomItems ) {
+ s = p->string()->toString();
+ } else {
+ for ( int i = 0; i < p->length() - 1; ++i ) {
+ if ( p->at( i )->isCustom() ) {
+ if ( p->at( i )->customItem()->isNested() ) {
+ s += "\n";
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
+ s += c->richText()->plainText() + "\n";
+ s += "\n";
+ }
+ } else {
+ s += p->at( i )->c;
+ }
+ }
+ }
+ s.remove( s.length() - 1, 1 );
+ if ( p->next() )
+ s += "\n";
+ buffer += s;
+ p = p->next();
+ }
+ return buffer;
+ } else {
+ return p->string()->toString();
+ }
+}
+
+static QString align_to_string( const QString &tag, int a )
+{
+ if ( tag == "p" || tag == "li" || ( tag[0] == 'h' && tag[1].isDigit() ) ) {
+ if ( a & Qt::AlignRight )
+ return " align=\"right\"";
+ if ( a & Qt::AlignCenter )
+ return " align=\"center\"";
+ if ( a & Qt3::AlignJustify )
+ return " align=\"justify\"";
+ }
+ return "";
+}
+
+static QString direction_to_string( const QString &tag, int d )
+{
+ if ( d != QChar::DirON &&
+ ( tag == "p" || tag == "div" || tag == "li" || ( tag[0] == 'h' && tag[1].isDigit() ) ) )
+ return ( d == QChar::DirL? " dir=\"ltr\"" : " dir=\"rtl\"" );
+ return "";
+}
+
+QString QTextDocument::richText( QTextParag *p ) const
+{
+ QString s,n;
+ if ( !p ) {
+ p = fParag;
+ QPtrVector<QStyleSheetItem> lastItems, items;
+ while ( p ) {
+ items = p->styleSheetItems();
+ if ( items.size() ) {
+ QStyleSheetItem *item = items[ items.size() - 1 ];
+ items.resize( items.size() - 1 );
+ if ( items.size() > lastItems.size() ) {
+ for ( int i = lastItems.size(); i < (int)items.size(); ++i ) {
+ n = items[i]->name();
+ if ( n.isEmpty() || n == "li" )
+ continue;
+ s += "<" + n + align_to_string( n, p->alignment() ) + ">";
+ }
+ } else {
+ QString end;
+ for ( int i = items.size(); i < (int)lastItems.size(); ++i ) {
+ n = lastItems[i]->name();
+ if ( n.isEmpty() || n == "li" || n == "br" )
+ continue;
+ end.prepend( "</" + lastItems[ i ]->name() + ">" );
+ }
+ s += end;
+ }
+ lastItems = items;
+ n = item->name();
+ if ( n == "li" && p->listValue() != -1 ) {
+ s += "<li value=\"" + QString::number( p->listValue() ) + "\">";
+ } else {
+ QString ps = p->richText();
+ if ( ps.isEmpty() )
+ s += "<br>"; // empty paragraph
+ else if ( !n.isEmpty() ) {
+ s += "<" + n + align_to_string( n, p->alignment() )
+ + direction_to_string( n, p->direction() ) + ">" + ps;
+ if ( n != "li" && n != "br")
+ s += "</" + n + ">";
+ } else
+ s += ps;
+ }
+ } else {
+ QString end;
+ for ( int i = 0; i < (int)lastItems.size(); ++i ) {
+ QString n = lastItems[i]->name();
+ if ( n.isEmpty() || n == "li" || n == "br" )
+ continue;
+ end.prepend( "</" + n + ">" );
+ }
+ s += end;
+ QString ps = p->richText();
+ if ( ps.isEmpty() )
+ s += "<br>"; // empty paragraph
+ else
+ s += "<p" + align_to_string( "p", p->alignment() ) + direction_to_string( "p", p->direction() )
+ + ">" + ps + "</p>";
+ lastItems = items;
+ }
+ if ( ( p = p->next() ) )
+ s += '\n';
+ }
+ } else {
+ s = p->richText();
+ }
+
+ return s;
+}
+
+QString QTextDocument::text() const
+{
+ if ( plainText().simplifyWhiteSpace().isEmpty() )
+ return QString("");
+ if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
+ return richText();
+ return plainText( 0 );
+}
+
+QString QTextDocument::text( int parag ) const
+{
+ QTextParag *p = paragAt( parag );
+ if ( !p )
+ return QString::null;
+
+ if ( txtFormat == Qt::AutoText && preferRichText || txtFormat == Qt::RichText )
+ return richText( p );
+ else
+ return plainText( p );
+}
+
+void QTextDocument::invalidate()
+{
+ QTextParag *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.parag()->paragId() : sel.endCursor.parag()->paragId();
+ index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+QTextCursor QTextDocument::selectionStartCursor( int id)
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return QTextCursor( this );
+ QTextDocumentSelection &sel = *it;
+ if ( sel.swapped )
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+QTextCursor QTextDocument::selectionEndCursor( int id)
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return QTextCursor( this );
+ QTextDocumentSelection &sel = *it;
+ if ( !sel.swapped )
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+void QTextDocument::selectionEnd( int id, int &paragId, int &index )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return;
+ QTextDocumentSelection &sel = *it;
+ paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
+ index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+QTextParag *QTextDocument::selectionStart( int id )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return 0;
+ QTextDocumentSelection &sel = *it;
+ if ( sel.startCursor.parag()->paragId() < sel.endCursor.parag()->paragId() )
+ return sel.startCursor.parag();
+ return sel.endCursor.parag();
+}
+
+QTextParag *QTextDocument::selectionEnd( int id )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return 0;
+ QTextDocumentSelection &sel = *it;
+ if ( sel.startCursor.parag()->paragId() > sel.endCursor.parag()->paragId() )
+ return sel.startCursor.parag();
+ return sel.endCursor.parag();
+}
+
+void QTextDocument::addSelection( int id )
+{
+ nSelections = QMAX( nSelections, id + 1 );
+}
+
+static void setSelectionEndHelper( int id, QTextDocumentSelection &sel, QTextCursor &start, QTextCursor &end )
+{
+ QTextCursor c1 = start;
+ QTextCursor c2 = end;
+ if ( sel.swapped ) {
+ c1 = end;
+ c2 = start;
+ }
+
+ c1.parag()->removeSelection( id );
+ c2.parag()->removeSelection( id );
+ if ( c1.parag() != c2.parag() ) {
+ c1.parag()->setSelection( id, c1.index(), c1.parag()->length() - 1 );
+ c2.parag()->setSelection( id, 0, c2.index() );
+ } else {
+ c1.parag()->setSelection( id, QMIN( c1.index(), c2.index() ), QMAX( c1.index(), c2.index() ) );
+ }
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if ( sel.startCursor.parag() == sel.endCursor.parag() )
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+}
+
+bool QTextDocument::setSelectionEnd( int id, QTextCursor *cursor )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return FALSE;
+ QTextDocumentSelection &sel = *it;
+
+ QTextCursor start = sel.startCursor;
+ QTextCursor end = *cursor;
+
+ if ( start == end ) {
+ removeSelection( id );
+ setSelectionStart( id, cursor );
+ return TRUE;
+ }
+
+ if ( sel.endCursor.parag() == end.parag() ) {
+ setSelectionEndHelper( id, sel, start, end );
+ return TRUE;
+ }
+
+ bool inSelection = FALSE;
+ QTextCursor c( this );
+ QTextCursor tmp = sel.startCursor;
+ if ( sel.swapped )
+ tmp = sel.endCursor;
+ tmp.restoreState();
+ QTextCursor tmp2 = *cursor;
+ tmp2.restoreState();
+ c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
+ QTextCursor old;
+ bool hadStart = FALSE;
+ bool hadEnd = FALSE;
+ bool hadStartParag = FALSE;
+ bool hadEndParag = FALSE;
+ bool hadOldStart = FALSE;
+ bool hadOldEnd = FALSE;
+ bool leftSelection = FALSE;
+ sel.swapped = FALSE;
+ for ( ;; ) {
+ if ( c == start )
+ hadStart = TRUE;
+ if ( c == end )
+ hadEnd = TRUE;
+ if ( c.parag() == start.parag() )
+ hadStartParag = TRUE;
+ if ( c.parag() == end.parag() )
+ hadEndParag = TRUE;
+ if ( c == sel.startCursor )
+ hadOldStart = TRUE;
+ if ( c == sel.endCursor )
+ hadOldEnd = TRUE;
+
+ if ( !sel.swapped &&
+ ( hadEnd && !hadStart ||
+ hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
+ sel.swapped = TRUE;
+
+ if ( c == end && hadStartParag ||
+ c == start && hadEndParag ) {
+ QTextCursor tmp = c;
+ tmp.restoreState();
+ if ( tmp.parag() != c.parag() ) {
+ int sstart = tmp.parag()->selectionStart( id );
+ tmp.parag()->removeSelection( id );
+ tmp.parag()->setSelection( id, sstart, tmp.index() );
+ }
+ }
+
+ if ( inSelection &&
+ ( c == end && hadStart || c == start && hadEnd ) )
+ leftSelection = TRUE;
+ else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
+ inSelection = TRUE;
+
+ bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
+ c.parag()->removeSelection( id );
+ if ( inSelection ) {
+ if ( c.parag() == start.parag() && start.parag() == end.parag() ) {
+ c.parag()->setSelection( id, QMIN( start.index(), end.index() ), QMAX( start.index(), end.index() ) );
+ } else if ( c.parag() == start.parag() && !hadEndParag ) {
+ c.parag()->setSelection( id, start.index(), c.parag()->length() - 1 );
+ } else if ( c.parag() == end.parag() && !hadStartParag ) {
+ c.parag()->setSelection( id, end.index(), c.parag()->length() - 1 );
+ } else if ( c.parag() == end.parag() && hadEndParag ) {
+ c.parag()->setSelection( id, 0, end.index() );
+ } else if ( c.parag() == start.parag() && hadStartParag ) {
+ c.parag()->setSelection( id, 0, start.index() );
+ } else {
+ c.parag()->setSelection( id, 0, c.parag()->length() - 1 );
+ }
+ }
+
+ if ( leftSelection )
+ inSelection = FALSE;
+
+ old = c;
+ c.gotoNextLetter();
+ if ( old == c || noSelectionAnymore )
+ break;
+ }
+
+ if ( !sel.swapped )
+ sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if ( sel.startCursor.parag() == sel.endCursor.parag() )
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+
+ setSelectionEndHelper( id, sel, start, end );
+
+ return TRUE;
+}
+
+void QTextDocument::selectAll( int id )
+{
+ removeSelection( id );
+
+ QTextDocumentSelection sel;
+ sel.swapped = FALSE;
+ QTextCursor c( this );
+
+ c.setParag( fParag );
+ c.setIndex( 0 );
+ sel.startCursor = c;
+
+ c.setParag( lParag );
+ c.setIndex( lParag->length() - 1 );
+ sel.endCursor = c;
+
+ QTextParag *p = fParag;
+ while ( p ) {
+ p->setSelection( id, 0, p->length() - 1 );
+ for ( int i = 0; i < (int)p->length(); ++i ) {
+ if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) {
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> tableCells = t->tableCells();
+ for ( QTextTableCell *c = tableCells.first(); c; c = tableCells.next() )
+ c->richText()->selectAll( id );
+ }
+ }
+ p = p->next();
+ }
+
+ selections.insert( id, sel );
+}
+
+bool QTextDocument::removeSelection( int id )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return FALSE;
+
+ QTextDocumentSelection &sel = *it;
+
+ if ( sel.startCursor == sel.endCursor ) {
+ selections.remove( id );
+ return TRUE;
+ }
+
+ if ( !mightHaveCustomItems ) {
+ QTextCursor start = sel.startCursor;
+ QTextCursor end = sel.endCursor;
+ if ( sel.swapped ) {
+ start = sel.endCursor;
+ end = sel.startCursor;
+ }
+
+ for ( QTextParag *p = start.parag(); p; p = p->next() ) {
+ p->removeSelection( id );
+ if ( p == end.parag() )
+ break;
+ }
+
+ selections.remove( id );
+ return TRUE;
+ }
+
+ QTextCursor c( this );
+ QTextCursor tmp = sel.startCursor;
+ if ( sel.swapped )
+ tmp = sel.endCursor;
+ tmp.restoreState();
+ c.setParag( tmp.parag() );
+ QTextCursor old;
+ bool hadStart = FALSE;
+ bool hadEnd = FALSE;
+ QTextParag *lastParag = 0;
+ bool leftSelection = FALSE;
+ bool inSelection = FALSE;
+ sel.swapped = FALSE;
+ for ( ;; ) {
+ if ( c.parag() == sel.startCursor.parag() )
+ hadStart = TRUE;
+ if ( c.parag() == sel.endCursor.parag() )
+ hadEnd = TRUE;
+
+ if ( inSelection &&
+ ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) )
+ leftSelection = TRUE;
+ else if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
+ inSelection = TRUE;
+
+ bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
+
+ if ( lastParag != c.parag() )
+ c.parag()->removeSelection( id );
+
+ old = c;
+ lastParag = c.parag();
+ c.gotoNextLetter();
+ if ( old == c || noSelectionAnymore )
+ break;
+ }
+
+ selections.remove( id );
+ return TRUE;
+}
+
+QString QTextDocument::selectedText( int id, bool withCustom ) const
+{
+ // ######## TODO: look at textFormat() and return rich text or plain text (like the text() method!)
+ QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
+ if ( it == selections.end() )
+ return QString::null;
+
+ QTextDocumentSelection sel = *it;
+
+
+ QTextCursor c1 = sel.startCursor;
+ QTextCursor c2 = sel.endCursor;
+ if ( sel.swapped ) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ /* 3.0.3 improvement: Make it possible to get a reasonable
+ selection inside a table. This approach is very conservative:
+ make sure that both cursors have the same depth level and point
+ to paragraphs within the same text document.
+
+ Meaning if you select text in two table cells, you will get the
+ entire table. This is still far better than the 3.0.2, where
+ you always got the entire table.
+
+ ### Fix this properly for 3.0.4.
+ */
+ while ( c2.nestedDepth() > c1.nestedDepth() )
+ c2.oneUp();
+ while ( c1.nestedDepth() > c2.nestedDepth() )
+ c1.oneUp();
+ while ( c1.nestedDepth() && c2.nestedDepth() &&
+ c1.parag()->document() != c2.parag()->document() ) {
+ c1.oneUp();
+ c2.oneUp();
+ }
+ // do not trust sel_swapped with tables. Fix this properly for 3.0.4 as well
+ if ( c1.parag()->paragId() > c2.parag()->paragId() ||
+ (c1.parag() == c2.parag() && c1.index() > c2.index() ) ) {
+ QTextCursor tmp = c1;
+ c2 = c1;
+ c1 = tmp;
+ }
+
+ // end selection 3.0.3 improvement
+
+
+ if ( c1.parag() == c2.parag() ) {
+ QString s;
+ QTextParag *p = c1.parag();
+ int end = c2.index();
+ if ( p->at( QMAX( 0, end - 1 ) )->isCustom() )
+ ++end;
+ if ( !withCustom || !p->mightHaveCustomItems ) {
+ s += p->string()->toString().mid( c1.index(), end - c1.index() );
+ } else {
+ for ( int i = c1.index(); i < end; ++i ) {
+ if ( p->at( i )->isCustom() ) {
+ if ( p->at( i )->customItem()->isNested() ) {
+ s += "\n";
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
+ s += c->richText()->plainText() + "\n";
+ s += "\n";
+ }
+ } else {
+ s += p->at( i )->c;
+ }
+ }
+ }
+ return s;
+ }
+
+ QString s;
+ QTextParag *p = c1.parag();
+ int start = c1.index();
+ while ( p ) {
+ int end = p == c2.parag() ? c2.index() : p->length() - 1;
+ if ( p == c2.parag() && p->at( QMAX( 0, end - 1 ) )->isCustom() )
+ ++end;
+ if ( !withCustom || !p->mightHaveCustomItems ) {
+ s += p->string()->toString().mid( start, end - start );
+ if ( p != c2.parag() )
+ s += "\n";
+ } else {
+ for ( int i = start; i < end; ++i ) {
+ if ( p->at( i )->isCustom() ) {
+ if ( p->at( i )->customItem()->isNested() ) {
+ s += "\n";
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ for ( QTextTableCell *c = cells.first(); c; c = cells.next() )
+ s += c->richText()->plainText() + "\n";
+ s += "\n";
+ }
+ } else {
+ s += p->at( i )->c;
+ }
+ }
+ }
+ start = 0;
+ if ( p == c2.parag() )
+ break;
+ p = p->next();
+ }
+ return s;
+}
+
+void QTextDocument::setFormat( int id, QTextFormat *f, int flags )
+{
+ QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( id );
+ if ( it == selections.end() )
+ return;
+
+ QTextDocumentSelection sel = *it;
+
+ QTextCursor c1 = sel.startCursor;
+ QTextCursor c2 = sel.endCursor;
+ if ( sel.swapped ) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ c2.restoreState();
+ c1.restoreState();
+
+ if ( c1.parag() == c2.parag() ) {
+ c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, TRUE, flags );
+ return;
+ }
+
+ c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f, TRUE, flags );
+ QTextParag *p = c1.parag()->next();
+ while ( p && p != c2.parag() ) {
+ p->setFormat( 0, p->length(), f, TRUE, flags );
+ p = p->next();
+ }
+ c2.parag()->setFormat( 0, c2.index(), f, TRUE, flags );
+}
+
+void QTextDocument::copySelectedText( int id )
+{
+#ifndef QT_NO_CLIPBOARD
+ if ( !hasSelection( id ) )
+ return;
+
+ QApplication::clipboard()->setText( selectedText( id ) );
+#endif
+}
+
+void QTextDocument::removeSelectedText( int id, QTextCursor *cursor )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return;
+
+ QTextDocumentSelection sel = *it;
+
+ QTextCursor c1 = sel.startCursor;
+ QTextCursor c2 = sel.endCursor;
+ if ( sel.swapped ) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ // ### no support for editing tables yet
+ if ( c1.nestedDepth() || c2.nestedDepth() )
+ return;
+
+ c2.restoreState();
+ c1.restoreState();
+
+ *cursor = c1;
+ removeSelection( id );
+
+ if ( c1.parag() == c2.parag() ) {
+ c1.parag()->remove( c1.index(), c2.index() - c1.index() );
+ return;
+ }
+
+ if ( c1.parag() == fParag && c1.index() == 0 &&
+ c2.parag() == lParag && c2.index() == lParag->length() - 1 )
+ cursor->setValid( FALSE );
+
+ bool didGoLeft = FALSE;
+ if ( c1.index() == 0 && c1.parag() != fParag ) {
+ cursor->gotoPreviousLetter();
+ if ( cursor->isValid() )
+ didGoLeft = TRUE;
+ }
+
+ c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
+ QTextParag *p = c1.parag()->next();
+ int dy = 0;
+ QTextParag *tmp;
+ while ( p && p != c2.parag() ) {
+ tmp = p->next();
+ dy -= p->rect().height();
+ delete p;
+ p = tmp;
+ }
+ c2.parag()->remove( 0, c2.index() );
+ while ( p ) {
+ p->move( dy );
+ p->invalidate( 0 );
+ p->setEndState( -1 );
+ p = p->next();
+ }
+
+ c1.parag()->join( c2.parag() );
+
+ if ( didGoLeft )
+ cursor->gotoNextLetter();
+}
+
+void QTextDocument::indentSelection( int id )
+{
+ QMap<int, QTextDocumentSelection>::Iterator it = selections.find( id );
+ if ( it == selections.end() )
+ return;
+
+ QTextDocumentSelection sel = *it;
+ QTextParag *startParag = sel.startCursor.parag();
+ QTextParag *endParag = sel.endCursor.parag();
+ if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
+ endParag = sel.startCursor.parag();
+ startParag = sel.endCursor.parag();
+ }
+
+ QTextParag *p = startParag;
+ while ( p && p != endParag ) {
+ p->indent();
+ p = p->next();
+ }
+}
+
+void QTextDocument::addCommand( QTextCommand *cmd )
+{
+ commandHistory->addCommand( cmd );
+}
+
+QTextCursor *QTextDocument::undo( QTextCursor *c )
+{
+ return commandHistory->undo( c );
+}
+
+QTextCursor *QTextDocument::redo( QTextCursor *c )
+{
+ return commandHistory->redo( c );
+}
+
+bool QTextDocument::find( const QString &expr, bool cs, bool wo, bool forward,
+ int *parag, int *index, QTextCursor *cursor )
+{
+ QTextParag *p = forward ? fParag : lParag;
+ if ( parag )
+ p = paragAt( *parag );
+ else if ( cursor )
+ p = cursor->parag();
+ bool first = TRUE;
+
+ while ( p ) {
+ QString s = p->string()->toString();
+ s.remove( s.length() - 1, 1 ); // get rid of trailing space
+ int start = forward ? 0 : s.length() - 1;
+ if ( first && index )
+ start = *index;
+ else if ( first )
+ start = cursor->index();
+ if ( !forward && first ) {
+ start -= expr.length() + 1;
+ if ( start < 0 ) {
+ first = FALSE;
+ p = p->prev();
+ continue;
+ }
+ }
+ first = FALSE;
+
+ for ( ;; ) {
+ int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
+ if ( res == -1 )
+ break;
+
+ bool ok = TRUE;
+ if ( wo ) {
+ int end = res + expr.length();
+ if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
+ ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) )
+ ok = TRUE;
+ else
+ ok = FALSE;
+ }
+ if ( ok ) {
+ cursor->setParag( p );
+ cursor->setIndex( res );
+ setSelectionStart( Standard, cursor );
+ cursor->setIndex( res + expr.length() );
+ setSelectionEnd( Standard, cursor );
+ if ( parag )
+ *parag = p->paragId();
+ if ( index )
+ *index = res;
+ return TRUE;
+ }
+ if ( forward ) {
+ start = res + 1;
+ } else {
+ if ( res == 0 )
+ break;
+ start = res - 1;
+ }
+ }
+ p = forward ? p->next() : p->prev();
+ }
+
+ return FALSE;
+}
+
+void QTextDocument::setTextFormat( Qt::TextFormat f )
+{
+ txtFormat = f;
+ if ( txtFormat == Qt::RichText && fParag && fParag == lParag && fParag->length() <= 1 ) {
+ QPtrVector<QStyleSheetItem> v = fParag->styleSheetItems();
+ v.resize( v.size() + 1 );
+ v.insert( v.size() - 1, styleSheet()->item( "p" ) );
+ fParag->setStyleSheetItems( v );
+ }
+
+}
+
+Qt::TextFormat QTextDocument::textFormat() const
+{
+ return txtFormat;
+}
+
+bool QTextDocument::inSelection( int selId, const QPoint &pos ) const
+{
+ QMap<int, QTextDocumentSelection>::ConstIterator it = selections.find( selId );
+ if ( it == selections.end() )
+ return FALSE;
+
+ QTextDocumentSelection sel = *it;
+ QTextParag *startParag = sel.startCursor.parag();
+ QTextParag *endParag = sel.endCursor.parag();
+ if ( sel.startCursor.parag() == sel.endCursor.parag() &&
+ sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) )
+ return FALSE;
+ if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
+ endParag = sel.startCursor.parag();
+ startParag = sel.endCursor.parag();
+ }
+
+ QTextParag *p = startParag;
+ while ( p ) {
+ if ( p->rect().contains( pos ) ) {
+ bool inSel = FALSE;
+ int selStart = p->selectionStart( selId );
+ int selEnd = p->selectionEnd( selId );
+ int y = 0;
+ int h = 0;
+ for ( int i = 0; i < p->length(); ++i ) {
+ if ( i == selStart )
+ inSel = TRUE;
+ if ( i == selEnd )
+ break;
+ if ( p->at( i )->lineStart ) {
+ y = (*p->lineStarts.find( i ))->y;
+ h = (*p->lineStarts.find( i ))->h;
+ }
+ if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
+ if ( inSel && pos.x() >= p->at( i )->x &&
+ pos.x() <= p->at( i )->x + p->at( i )->format()->width( p->at( i )->c ) )
+ return TRUE;
+ }
+ }
+ }
+ if ( pos.y() < p->rect().y() )
+ break;
+ if ( p == endParag )
+ break;
+ p = p->next();
+ }
+
+ return FALSE;
+}
+
+void QTextDocument::doLayout( QPainter *p, int w )
+{
+ minw = wused = 0;
+ if ( !is_printer( p ) )
+ p = 0;
+ withoutDoubleBuffer = ( p != 0 );
+ QPainter * oldPainter = QTextFormat::painter();
+ QTextFormat::setPainter( p );
+ flow_->setWidth( w );
+ cw = w;
+ vw = w;
+ QTextParag *parag = fParag;
+ while ( parag ) {
+ parag->invalidate( 0 );
+ if ( p )
+ parag->adjustToPainter( p );
+ parag->format();
+ parag = parag->next();
+ }
+ QTextFormat::setPainter( oldPainter );
+}
+
+QPixmap *QTextDocument::bufferPixmap( const QSize &s )
+{
+ if ( !buf_pixmap ) {
+ int w = QABS( s.width() );
+ int h = QABS( s.height() );
+ buf_pixmap = new QPixmap( w, h );
+ } else {
+ if ( buf_pixmap->width() < s.width() ||
+ buf_pixmap->height() < s.height() ) {
+ buf_pixmap->resize( QMAX( s.width(), buf_pixmap->width() ),
+ QMAX( s.height(), buf_pixmap->height() ) );
+ }
+ }
+
+ return buf_pixmap;
+}
+
+void QTextDocument::draw( QPainter *p, const QRect &rect, const QColorGroup &cg, const QBrush *paper )
+{
+ if ( !firstParag() )
+ return;
+
+ if ( paper ) {
+ p->setBrushOrigin( 0, 0 );
+
+ p->fillRect( rect, *paper );
+ }
+
+ if ( formatCollection()->defaultFormat()->color() != cg.text() ) {
+ QDict<QTextFormat> formats = formatCollection()->dict();
+ QDictIterator<QTextFormat> it( formats );
+ while ( it.current() ) {
+ if ( it.current() == formatCollection()->defaultFormat() ) {
+ ++it;
+ continue;
+ }
+ it.current()->setColor( cg.text() );
+ ++it;
+ }
+ formatCollection()->defaultFormat()->setColor( cg.text() );
+ }
+
+ QTextParag *parag = firstParag();
+ while ( parag ) {
+ if ( !parag->isValid() )
+ parag->format();
+ int y = parag->rect().y();
+ QRect pr( parag->rect() );
+ pr.setX( 0 );
+ pr.setWidth( QWIDGETSIZE_MAX );
+ if ( !rect.isNull() && !rect.intersects( pr ) ) {
+ parag = parag->next();
+ continue;
+ }
+ p->translate( 0, y );
+ if ( rect.isValid() )
+ parag->paint( *p, cg, 0, FALSE, rect.x(), rect.y(), rect.width(), rect.height() );
+ else
+ parag->paint( *p, cg, 0, FALSE );
+ p->translate( 0, -y );
+ parag = parag->next();
+ if ( !flow()->isEmpty() )
+ flow()->drawFloatingItems( p, rect.x(), rect.y(), rect.width(), rect.height(), cg, FALSE );
+ }
+}
+
+void QTextDocument::drawParag( QPainter *p, QTextParag *parag, int cx, int cy, int cw, int ch,
+ QPixmap *&doubleBuffer, const QColorGroup &cg,
+ bool drawCursor, QTextCursor *cursor, bool resetChanged )
+{
+ QPainter *painter = 0;
+ if ( resetChanged )
+ parag->setChanged( FALSE );
+ QRect ir( parag->rect() );
+ bool useDoubleBuffer = !parag->document()->parent();
+ if ( !useDoubleBuffer && parag->document()->nextDoubleBuffered )
+ useDoubleBuffer = TRUE;
+ if ( is_printer( p ) )
+ useDoubleBuffer = FALSE;
+
+ if ( useDoubleBuffer ) {
+ painter = new QPainter;
+ if ( cx >= 0 && cy >= 0 )
+ ir = ir.intersect( QRect( cx, cy, cw, ch ) );
+ if ( !doubleBuffer ||
+ ir.width() > doubleBuffer->width() ||
+ ir.height() > doubleBuffer->height() ) {
+ doubleBuffer = bufferPixmap( ir.size() );
+ painter->begin( doubleBuffer );
+ } else {
+ painter->begin( doubleBuffer );
+ }
+ } else {
+ painter = p;
+ painter->translate( ir.x(), ir.y() );
+ }
+
+ painter->setBrushOrigin( -ir.x(), -ir.y() );
+
+ if ( useDoubleBuffer || is_printer( painter ) ) {
+ if ( !parag->backgroundColor() )
+ painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
+ cg.brush( QColorGroup::Base ) );
+ else
+ painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ),
+ *parag->backgroundColor() );
+ } else {
+ if ( cursor && cursor->parag() == parag ) {
+ if ( !parag->backgroundColor() )
+ painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
+ cg.brush( QColorGroup::Base ) );
+ else
+ painter->fillRect( QRect( parag->at( cursor->index() )->x, 0, 2, ir.height() ),
+ *parag->backgroundColor() );
+ }
+ }
+
+ 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 ( parag->rect().x() + parag->rect().width() < parag->document()->x() + parag->document()->width() ) {
+ p->fillRect( parag->rect().x() + parag->rect().width(), parag->rect().y(),
+ ( parag->document()->x() + parag->document()->width() ) -
+ ( parag->rect().x() + parag->rect().width() ),
+ parag->rect().height(), cg.brush( QColorGroup::Base ) );
+ }
+
+ parag->document()->nextDoubleBuffered = FALSE;
+}
+
+QTextParag *QTextDocument::draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
+ bool onlyChanged, bool drawCursor, QTextCursor *cursor, bool resetChanged )
+{
+ if ( withoutDoubleBuffer || par && par->withoutDoubleBuffer ) {
+ withoutDoubleBuffer = TRUE;
+ QRect r;
+ draw( p, r, cg );
+ return 0;
+ }
+ withoutDoubleBuffer = FALSE;
+
+ if ( !firstParag() )
+ return 0;
+
+ if ( drawCursor && cursor )
+ tmpCursor = cursor;
+ if ( cx < 0 && cy < 0 ) {
+ cx = 0;
+ cy = 0;
+ cw = width();
+ ch = height();
+ }
+
+ QTextParag *lastFormatted = 0;
+ QTextParag *parag = firstParag();
+
+ QPixmap *doubleBuffer = 0;
+ QPainter painter;
+
+ while ( parag ) {
+ lastFormatted = parag;
+ if ( !parag->isValid() )
+ parag->format();
+
+ if ( !parag->rect().intersects( QRect( cx, cy, cw, ch ) ) ) {
+ QRect pr( parag->rect() );
+ pr.setWidth( parag->document()->width() );
+ if ( pr.intersects( QRect( cx, cy, cw, ch ) ) )
+ p->fillRect( pr.intersect( QRect( cx, cy, cw, ch ) ), cg.brush( QColorGroup::Base ) );
+ if ( parag->rect().y() > cy + ch ) {
+ tmpCursor = 0;
+ goto floating;
+ }
+ parag = parag->next();
+ continue;
+ }
+
+ if ( !parag->hasChanged() && onlyChanged ) {
+ parag = parag->next();
+ continue;
+ }
+
+ drawParag( p, parag, cx, cy, cw, ch, doubleBuffer, cg, drawCursor, cursor, resetChanged );
+ parag = parag->next();
+ }
+
+ parag = lastParag();
+
+ floating:
+ if ( parag->rect().y() + parag->rect().height() < parag->document()->height() ) {
+ if ( !parag->document()->parent() ) { // !useDoubleBuffer
+ p->fillRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
+ parag->document()->height() - ( parag->rect().y() + parag->rect().height() ),
+ cg.brush( QColorGroup::Base ) );
+ }
+ if ( !flow()->isEmpty() ) {
+ QRect cr( cx, cy, cw, ch );
+// cr = cr.intersect( QRect( 0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
+// parag->document()->height() - ( parag->rect().y() + parag->rect().height() ) ) );
+ flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
+ }
+ }
+
+ if ( buf_pixmap && buf_pixmap->height() > 300 ) {
+ delete buf_pixmap;
+ buf_pixmap = 0;
+ }
+
+ tmpCursor = 0;
+ return lastFormatted;
+}
+
+void QTextDocument::setDefaultFont( const QFont &f )
+{
+ int s = f.pointSize();
+ bool usePixels = FALSE;
+ if ( s == -1 ) {
+ s = f.pixelSize();
+ usePixels = TRUE;
+ }
+ updateFontSizes( s, usePixels );
+}
+
+void QTextDocument::registerCustomItem( QTextCustomItem *i, QTextParag *p )
+{
+ if ( i && i->placement() != QTextCustomItem::PlaceInline ) {
+ flow_->registerFloatingItem( i );
+ p->registerFloatingItem( i );
+ i->setParagraph( p );
+ }
+ p->mightHaveCustomItems = mightHaveCustomItems = TRUE;
+}
+
+void QTextDocument::unregisterCustomItem( QTextCustomItem *i, QTextParag *p )
+{
+ flow_->unregisterFloatingItem( i );
+ p->unregisterFloatingItem( i );
+ i->setParagraph( 0 );
+}
+
+bool QTextDocument::hasFocusParagraph() const
+{
+ return !!focusIndicator.parag;
+}
+
+QString QTextDocument::focusHref() const
+{
+ return focusIndicator.href;
+}
+
+bool QTextDocument::focusNextPrevChild( bool next )
+{
+ if ( !focusIndicator.parag ) {
+ if ( next ) {
+ focusIndicator.parag = fParag;
+ focusIndicator.start = 0;
+ focusIndicator.len = 0;
+ } else {
+ focusIndicator.parag = lParag;
+ focusIndicator.start = lParag->length();
+ focusIndicator.len = 0;
+ }
+ } else {
+ focusIndicator.parag->setChanged( TRUE );
+ }
+ focusIndicator.href = QString::null;
+
+ if ( next ) {
+ QTextParag *p = focusIndicator.parag;
+ int index = focusIndicator.start + focusIndicator.len;
+ while ( p ) {
+ for ( int i = index; i < p->length(); ++i ) {
+ if ( p->at( i )->isAnchor() ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = p->at( i )->anchorHref();
+ while ( i < p->length() ) {
+ if ( !p->at( i )->isAnchor() )
+ return TRUE;
+ focusIndicator.len++;
+ i++;
+ }
+ } else if ( p->at( i )->isCustom() ) {
+ if ( p->at( i )->customItem()->isNested() ) {
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ // first try to continue
+ QTextTableCell *c;
+ bool resetCells = TRUE;
+ for ( c = cells.first(); c; c = cells.next() ) {
+ if ( c->richText()->hasFocusParagraph() ) {
+ if ( c->richText()->focusNextPrevChild( next ) ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ return TRUE;
+ } else {
+ resetCells = FALSE;
+ c = cells.next();
+ break;
+ }
+ }
+ }
+ // now really try
+ if ( resetCells )
+ c = cells.first();
+ for ( ; c; c = cells.next() ) {
+ if ( c->richText()->focusNextPrevChild( next ) ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ index = 0;
+ p = p->next();
+ }
+ } else {
+ QTextParag *p = focusIndicator.parag;
+ int index = focusIndicator.start - 1;
+ if ( focusIndicator.len == 0 && index < focusIndicator.parag->length() - 1 )
+ index++;
+ while ( p ) {
+ for ( int i = index; i >= 0; --i ) {
+ if ( p->at( i )->isAnchor() ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = p->at( i )->anchorHref();
+ while ( i >= -1 ) {
+ if ( i < 0 || !p->at( i )->isAnchor() ) {
+ focusIndicator.start++;
+ return TRUE;
+ }
+ if ( i < 0 )
+ break;
+ focusIndicator.len++;
+ focusIndicator.start--;
+ i--;
+ }
+ } else if ( p->at( i )->isCustom() ) {
+ if ( p->at( i )->customItem()->isNested() ) {
+ QTextTable *t = (QTextTable*)p->at( i )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ // first try to continue
+ QTextTableCell *c;
+ bool resetCells = TRUE;
+ for ( c = cells.last(); c; c = cells.prev() ) {
+ if ( c->richText()->hasFocusParagraph() ) {
+ if ( c->richText()->focusNextPrevChild( next ) ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ return TRUE;
+ } else {
+ resetCells = FALSE;
+ c = cells.prev();
+ break;
+ }
+ }
+ if ( cells.at() == 0 )
+ break;
+ }
+ // now really try
+ if ( resetCells )
+ c = cells.last();
+ for ( ; c; c = cells.prev() ) {
+ if ( c->richText()->focusNextPrevChild( next ) ) {
+ p->setChanged( TRUE );
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ return TRUE;
+ }
+ if ( cells.at() == 0 )
+ break;
+ }
+ }
+ }
+ }
+ p = p->prev();
+ if ( p )
+ index = p->length() - 1;
+ }
+ }
+
+ focusIndicator.parag = 0;
+
+ return FALSE;
+}
+
+int QTextDocument::length() const
+{
+ int l = 0;
+ QTextParag *p = fParag;
+ 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 )
+{
+ int os = data.size();
+ data.resize( data.size() + s.length() );
+ if ( index < os ) {
+ memmove( data.data() + index + s.length(), data.data() + index,
+ sizeof( QTextStringChar ) * ( os - index ) );
+ }
+ for ( int i = 0; i < (int)s.length(); ++i ) {
+ data[ (int)index + i ].x = 0;
+ data[ (int)index + i ].lineStart = 0;
+ data[ (int)index + i ].d.format = 0;
+ data[ (int)index + i ].type = QTextStringChar::Regular;
+ data[ (int)index + i ].rightToLeft = 0;
+ data[ (int)index + i ].startOfRun = 0;
+ data[ (int)index + i ].c = s[ i ];
+ data[ (int)index + i ].setFormat( f );
+ }
+ bidiDirty = TRUE;
+}
+
+QTextString::~QTextString()
+{
+ clear();
+}
+
+void QTextString::insert( int index, QTextStringChar *c )
+{
+ int os = data.size();
+ data.resize( data.size() + 1 );
+ if ( index < os ) {
+ memmove( data.data() + index + 1, data.data() + index,
+ sizeof( QTextStringChar ) * ( os - index ) );
+ }
+ data[ (int)index ].c = c->c;
+ data[ (int)index ].x = 0;
+ data[ (int)index ].lineStart = 0;
+ data[ (int)index ].rightToLeft = 0;
+ data[ (int)index ].d.format = 0;
+ data[ (int)index ].type = QTextStringChar::Regular;
+ data[ (int)index ].setFormat( c->format() );
+ bidiDirty = TRUE;
+}
+
+void QTextString::truncate( int index )
+{
+ index = QMAX( index, 0 );
+ index = QMIN( index, (int)data.size() - 1 );
+ if ( index < (int)data.size() ) {
+ for ( int i = index + 1; i < (int)data.size(); ++i ) {
+ if ( !(data[ i ].type == QTextStringChar::Regular) ) {
+ delete data[ i ].customItem();
+ if ( data[ i ].d.custom->format )
+ data[ i ].d.custom->format->removeRef();
+ delete data[ i ].d.custom;
+ data[ i ].d.custom = 0;
+ } else if ( data[ i ].format() ) {
+ data[ i ].format()->removeRef();
+ }
+ }
+ }
+ data.truncate( index );
+ bidiDirty = TRUE;
+}
+
+void QTextString::remove( int index, int len )
+{
+ for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
+ if ( !(data[ i ].type == QTextStringChar::Regular) ) {
+ delete data[ i ].customItem();
+ if ( data[ i ].d.custom->format )
+ data[ i ].d.custom->format->removeRef();
+ delete data[ i ].d.custom;
+ data[ i ].d.custom = 0;
+ } else if ( data[ i ].format() ) {
+ data[ i ].format()->removeRef();
+ }
+ }
+ memmove( data.data() + index, data.data() + index + len,
+ sizeof( QTextStringChar ) * ( data.size() - index - len ) );
+ data.resize( data.size() - len );
+ bidiDirty = TRUE;
+}
+
+void QTextString::clear()
+{
+ for ( int i = 0; i < (int)data.count(); ++i ) {
+ if ( !(data[ i ].type == QTextStringChar::Regular) ) {
+ delete data[ i ].customItem();
+ if ( data[ i ].d.custom->format )
+ data[ i ].d.custom->format->removeRef();
+ delete data[ i ].d.custom;
+ data[ i ].d.custom = 0;
+ } else if ( data[ i ].format() ) {
+ data[ i ].format()->removeRef();
+ }
+ }
+ data.resize( 0 );
+}
+
+void QTextString::setFormat( int index, QTextFormat *f, bool useCollection )
+{
+ if ( useCollection && data[ index ].format() )
+ data[ index ].format()->removeRef();
+ data[ index ].setFormat( f );
+}
+
+void QTextString::checkBidi() const
+{
+ bool rtlKnown = FALSE;
+ if ( dir == QChar::DirR ) {
+ ((QTextString *)this)->bidi = TRUE;
+ ((QTextString *)this)->rightToLeft = TRUE;
+ rtlKnown = TRUE;
+ return;
+ } else if ( dir == QChar::DirL ) {
+ ((QTextString *)this)->rightToLeft = FALSE;
+ rtlKnown = TRUE;
+ } else {
+ ((QTextString *)this)->rightToLeft = FALSE;
+ }
+
+ int len = data.size();
+ const QTextStringChar *c = data.data();
+ ((QTextString *)this)->bidi = FALSE;
+ while( len ) {
+ if ( !rtlKnown ) {
+ switch( c->c.direction() )
+ {
+ case QChar::DirL:
+ case QChar::DirLRO:
+ case QChar::DirLRE:
+ ((QTextString *)this)->rightToLeft = FALSE;
+ rtlKnown = TRUE;
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirRLO:
+ case QChar::DirRLE:
+ ((QTextString *)this)->rightToLeft = TRUE;
+ rtlKnown = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ uchar row = c->c.row();
+ if( (row > 0x04 && row < 0x09) || (row > 0xfa && row < 0xff) ) {
+ ((QTextString *)this)->bidi = TRUE;
+ if ( rtlKnown )
+ return;
+ }
+ len--;
+ ++c;
+ }
+}
+
+void QTextDocument::setStyleSheet( QStyleSheet *s )
+{
+ if ( !s )
+ return;
+ sheet_ = s;
+ fCollection->setStyleSheet( s );
+ updateStyles();
+}
+
+void QTextDocument::updateStyles()
+{
+ invalidate();
+ if ( par )
+ underlLinks = par->underlLinks;
+ fCollection->updateStyles();
+ for ( QTextDocument *d = childList.first(); d; d = childList.next() )
+ d->updateStyles();
+}
+
+void QTextDocument::updateFontSizes( int base, bool usePixels )
+{
+ for ( QTextDocument *d = childList.first(); d; d = childList.next() )
+ d->updateFontSizes( base, usePixels );
+ invalidate();
+ fCollection->updateFontSizes( base, usePixels );
+}
+
+void QTextDocument::updateFontAttributes( const QFont &f, const QFont &old )
+{
+ for ( QTextDocument *d = childList.first(); d; d = childList.next() )
+ d->updateFontAttributes( f, old );
+ invalidate();
+ fCollection->updateFontAttributes( f, old );
+}
+
+void QTextStringChar::setFormat( QTextFormat *f )
+{
+ if ( type == Regular ) {
+ d.format = f;
+ } else {
+ if ( !d.custom ) {
+ d.custom = new CustomData;
+ d.custom->custom = 0;
+ }
+ d.custom->format = f;
+ }
+}
+
+void QTextStringChar::setCustomItem( QTextCustomItem *i )
+{
+ if ( type == Regular ) {
+ QTextFormat *f = format();
+ d.custom = new CustomData;
+ d.custom->format = f;
+ } else {
+ delete d.custom->custom;
+ }
+ d.custom->custom = i;
+ type = (type == Anchor ? CustomAnchor : Custom);
+}
+
+void QTextStringChar::loseCustomItem()
+{
+ if ( type == Custom ) {
+ QTextFormat *f = d.custom->format;
+ d.custom->custom = 0;
+ delete d.custom;
+ type = Regular;
+ d.format = f;
+ } else if ( type == CustomAnchor ) {
+ d.custom->custom = 0;
+ type = Anchor;
+ }
+}
+
+QString QTextStringChar::anchorName() const
+{
+ if ( type == Regular )
+ return QString::null;
+ else
+ return d.custom->anchorName;
+}
+
+QString QTextStringChar::anchorHref() const
+{
+ if ( type == Regular )
+ return QString::null;
+ else
+ return d.custom->anchorHref;
+}
+
+void QTextStringChar::setAnchor( const QString& name, const QString& href )
+{
+ if ( type == Regular ) {
+ QTextFormat *f = format();
+ d.custom = new CustomData;
+ d.custom->custom = 0;
+ d.custom->format = f;
+ type = Anchor;
+ } else if ( type == Custom ) {
+ type = CustomAnchor;
+ }
+ d.custom->anchorName = name;
+ d.custom->anchorHref = href;
+}
+
+
+int QTextString::width( int idx ) const
+{
+ int w = 0;
+ QTextStringChar *c = &at( idx );
+ if ( c->c.unicode() == 0xad )
+ return 0;
+ if( c->isCustom() ) {
+ if( c->customItem()->placement() == QTextCustomItem::PlaceInline )
+ w = c->customItem()->width;
+ } else {
+ int r = c->c.row();
+ if( r < 0x06 || r > 0x1f )
+ w = c->format()->width( c->c );
+ else {
+ // complex text. We need some hacks to get the right metric here
+ QString str;
+ int pos = 0;
+ if( idx > 4 )
+ pos = idx - 4;
+ int off = idx - pos;
+ int end = QMIN( length(), idx + 4 );
+ while ( pos < end ) {
+ str += at(pos).c;
+ pos++;
+ }
+ w = c->format()->width( str, off );
+ }
+ }
+ return w;
+}
+
+QMemArray<QTextStringChar> QTextString::subString( int start, int len ) const
+{
+ if ( len == 0xFFFFFF )
+ len = data.size();
+ QMemArray<QTextStringChar> a;
+ a.resize( len );
+ for ( int i = 0; i < len; ++i ) {
+ QTextStringChar *c = &data[ i + start ];
+ a[ i ].c = c->c;
+ a[ i ].x = 0;
+ a[ i ].lineStart = 0;
+ a[ i ].rightToLeft = 0;
+ a[ i ].d.format = 0;
+ a[ i ].type = QTextStringChar::Regular;
+ a[ i ].setFormat( c->format() );
+ if ( c->format() )
+ c->format()->addRef();
+ }
+ return a;
+}
+
+QTextStringChar *QTextStringChar::clone() const
+{
+ QTextStringChar *chr = new QTextStringChar;
+ chr->c = c;
+ chr->x = 0;
+ chr->lineStart = 0;
+ chr->rightToLeft = 0;
+ chr->d.format = 0;
+ chr->type = QTextStringChar::Regular;
+ chr->setFormat( format() );
+ if ( chr->format() )
+ chr->format()->addRef();
+ return chr;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextParag::QTextParag( QTextDocument *d, QTextParag *pr, QTextParag *nx, bool updateIds )
+ : invalid( 0 ), p( pr ), n( nx ), docOrPseudo( d ), align( 0 ),mSelections( 0 ),
+ mStyleSheetItemsVec( 0 ), mFloatingItems( 0 ), listS( QStyleSheetItem::ListDisc ),
+ numSubParag( -1 ), tm( -1 ), bm( -1 ), lm( -1 ), rm( -1 ), flm( -1 ),
+ tArray(0), tabStopWidth(0), eData( 0 )
+{
+ listS = QStyleSheetItem::ListDisc;
+ if ( ! (hasdoc = docOrPseudo != 0 ) )
+ docOrPseudo = new QTextParagPseudoDocument;
+ bgcol = 0;
+ breakable = TRUE;
+ isBr = FALSE;
+ movedDown = FALSE;
+ mightHaveCustomItems = FALSE;
+ visible = TRUE;
+ list_val = -1;
+ newLinesAllowed = FALSE;
+ lastInFrame = FALSE;
+ defFormat = formatCollection()->defaultFormat();
+ if ( !hasdoc ) {
+ tabStopWidth = defFormat->width( 'x' ) * 8;
+ pseudoDocument()->commandHistory = new QTextCommandHistory( 100 );
+ }
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + "new QTextParag" );
+#endif
+ fullWidth = TRUE;
+
+ if ( p )
+ p->n = this;
+ if ( n )
+ n->p = this;
+
+
+ if ( !p && hasdoc )
+ document()->setFirstParag( this );
+ if ( !n && hasdoc )
+ document()->setLastParag( this );
+
+ changed = FALSE;
+ firstFormat = TRUE;
+ state = -1;
+ needPreProcess = FALSE;
+
+ if ( p )
+ id = p->id + 1;
+ else
+ id = 0;
+ if ( n && updateIds ) {
+ QTextParag *s = n;
+ while ( s ) {
+ s->id = s->p->id + 1;
+ s->numSubParag = -1;
+ s->lm = s->rm = s->tm = s->bm = -1, s->flm = -1;
+ s = s->n;
+ }
+ }
+ firstPProcess = TRUE;
+
+ str = new QTextString();
+ str->insert( 0, " ", formatCollection()->defaultFormat() );
+}
+
+QTextParag::~QTextParag()
+{
+ delete str;
+ if ( hasdoc ) {
+ register QTextDocument *doc = document();
+ if ( this == doc->minwParag ) {
+ doc->minwParag = 0;
+ doc->minw = 0;
+ }
+ if ( this == doc->curParag )
+ doc->curParag = 0;
+ } else {
+ delete pseudoDocument();
+ }
+ if ( tArray )
+ delete [] tArray;
+ delete eData;
+ QMap<int, QTextParagLineStart*>::Iterator it = lineStarts.begin();
+ for ( ; it != lineStarts.end(); ++it )
+ delete *it;
+ if ( mSelections )
+ delete mSelections;
+ if ( mFloatingItems )
+ delete mFloatingItems;
+ if ( mStyleSheetItemsVec )
+ delete mStyleSheetItemsVec;
+ if ( p )
+ p->setNext( n );
+ if ( n )
+ n->setPrev( p );
+}
+
+void QTextParag::setNext( QTextParag *s )
+{
+ n = s;
+ if ( !n && hasdoc )
+ document()->setLastParag( this );
+}
+
+void QTextParag::setPrev( QTextParag *s )
+{
+ p = s;
+ if ( !p && hasdoc )
+ document()->setFirstParag( this );
+}
+
+void QTextParag::invalidate( int chr )
+{
+ if ( invalid < 0 )
+ invalid = chr;
+ else
+ invalid = QMIN( invalid, chr );
+ if ( mFloatingItems ) {
+ for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
+ i->ypos = -1;
+ }
+ lm = rm = bm = tm = flm = -1;
+}
+
+void QTextParag::insert( int index, const QString &s )
+{
+ if ( hasdoc && !document()->useFormatCollection() && document()->preProcessor() )
+ str->insert( index, s,
+ document()->preProcessor()->format( QTextPreProcessor::Standard ) );
+ else
+ str->insert( index, s, formatCollection()->defaultFormat() );
+ invalidate( index );
+ needPreProcess = TRUE;
+}
+
+void QTextParag::truncate( int index )
+{
+ str->truncate( index );
+ insert( length(), " " );
+ needPreProcess = TRUE;
+}
+
+void QTextParag::remove( int index, int len )
+{
+ if ( index + len - str->length() > 0 )
+ return;
+ for ( int i = index; i < index + len; ++i ) {
+ QTextStringChar *c = at( i );
+ if ( hasdoc && c->isCustom() ) {
+ document()->unregisterCustomItem( c->customItem(), this );
+ }
+ }
+ str->remove( index, len );
+ invalidate( 0 );
+ needPreProcess = TRUE;
+}
+
+void QTextParag::join( QTextParag *s )
+{
+ int oh = r.height() + s->r.height();
+ n = s->n;
+ if ( n )
+ n->p = this;
+ else if ( hasdoc )
+ document()->setLastParag( this );
+
+ int start = str->length();
+ if ( length() > 0 && at( length() - 1 )->c == ' ' ) {
+ remove( length() - 1, 1 );
+ --start;
+ }
+ append( s->str->toString(), TRUE );
+
+ for ( int i = 0; i < s->length(); ++i ) {
+ if ( !hasdoc || document()->useFormatCollection() ) {
+ s->str->at( i ).format()->addRef();
+ str->setFormat( i + start, s->str->at( i ).format(), TRUE );
+ }
+ if ( s->str->at( i ).isCustom() ) {
+ QTextCustomItem * item = s->str->at( i ).customItem();
+ str->at( i + start ).setCustomItem( item );
+ s->str->at( i ).loseCustomItem();
+ }
+ if ( s->str->at( i ).isAnchor() ) {
+ str->at( i + start ).setAnchor( s->str->at( i ).anchorName(),
+ s->str->at( i ).anchorHref() );
+ }
+ }
+
+ if ( !extraData() && s->extraData() ) {
+ setExtraData( s->extraData() );
+ s->setExtraData( 0 );
+ } else if ( extraData() && s->extraData() ) {
+ extraData()->join( s->extraData() );
+ }
+ delete s;
+ invalidate( 0 );
+ r.setHeight( oh );
+ needPreProcess = TRUE;
+ if ( n ) {
+ QTextParag *s = n;
+ while ( s ) {
+ s->id = s->p->id + 1;
+ s->state = -1;
+ s->needPreProcess = TRUE;
+ s->changed = TRUE;
+ s = s->n;
+ }
+ }
+ format();
+ state = -1;
+}
+
+void QTextParag::move( int &dy )
+{
+ if ( dy == 0 )
+ return;
+ changed = TRUE;
+ r.moveBy( 0, dy );
+ if ( mFloatingItems ) {
+ for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() )
+ i->ypos += dy;
+ }
+ if ( p )
+ p->lastInFrame = TRUE;
+
+ // do page breaks if required
+ if ( hasdoc && document()->isPageBreakEnabled() ) {
+ int shift;
+ if ( ( shift = document()->formatter()->formatVertically( document(), this ) ) ) {
+ if ( p )
+ p->setChanged( TRUE );
+ dy += shift;
+ }
+ }
+}
+
+void QTextParag::format( int start, bool doMove )
+{
+ if ( !str || str->length() == 0 || !formatter() )
+ return;
+
+ if ( hasdoc &&
+ document()->preProcessor() &&
+ ( needPreProcess || state == -1 ) )
+ document()->preProcessor()->process( document(), this, invalid <= 0 ? 0 : invalid );
+ needPreProcess = FALSE;
+
+ if ( invalid == -1 )
+ return;
+
+ r.moveTopLeft( QPoint( documentX(), p ? p->r.y() + p->r.height() : documentY() ) );
+ r.setWidth( documentWidth() );
+ if ( p )
+ p->lastInFrame = FALSE;
+
+ movedDown = FALSE;
+ bool formattedAgain = FALSE;
+
+ formatAgain:
+
+ if ( hasdoc && mFloatingItems ) {
+ for ( QTextCustomItem *i = mFloatingItems->first(); i; i = mFloatingItems->next() ) {
+ i->ypos = r.y();
+ if ( i->placement() == QTextCustomItem::PlaceRight ) {
+ i->xpos = r.x() + r.width() - i->width;
+ }
+ }
+ }
+ QMap<int, QTextParagLineStart*> oldLineStarts = lineStarts;
+ lineStarts.clear();
+ int y = formatter()->format( document(), this, start, oldLineStarts );
+
+
+ r.setWidth( QMAX( r.width(), formatter()->minimumWidth() ) );
+
+
+ QMap<int, QTextParagLineStart*>::Iterator it = oldLineStarts.begin();
+
+ for ( ; it != oldLineStarts.end(); ++it )
+ delete *it;
+
+ QTextStringChar *c = 0;
+ // do not do this on mac, as the paragraph
+ // with has to be the full document width on mac as the selections
+ // always extend completely to the right. This is a bit unefficient,
+ // as this results in a bigger double buffer than needed but ok for
+ // now.
+ if ( lineStarts.count() == 1 ) { //&& ( !doc || document()->flow()->isEmpty() ) ) {
+ if ( !string()->isBidi() ) {
+ c = &str->at( str->length() - 1 );
+ r.setWidth( c->x + str->width( str->length() - 1 ) );
+ } else {
+ r.setWidth( lineStarts[0]->w );
+ }
+ }
+
+ if ( newLinesAllowed ) {
+ it = lineStarts.begin();
+ int usedw = 0;
+ for ( ; it != lineStarts.end(); ++it )
+ usedw = QMAX( usedw, (*it)->w );
+ if ( r.width() <= 0 ) {
+ // if the user specifies an invalid rect, this means that the
+ // bounding box should grow to the width that the text actually
+ // needs
+ r.setWidth( usedw );
+ } else {
+ r.setWidth( QMIN( usedw, r.width() ) );
+ }
+ }
+
+ if ( y != r.height() )
+ r.setHeight( y );
+
+ if ( !visible ) {
+ r.setHeight( 0 );
+ } else {
+ int minw = formatter()->minimumWidth();
+ int wused = formatter()->widthUsed();
+ wused = QMAX( minw, wused );
+ if ( hasdoc ) {
+ document()->setMinimumWidth( minw, wused, this );
+ } else {
+ pseudoDocument()->minw = QMAX( pseudoDocument()->minw, minw );
+ pseudoDocument()->wused = QMAX( pseudoDocument()->wused, wused );
+ }
+ }
+
+ // do page breaks if required
+ if ( hasdoc && document()->isPageBreakEnabled() ) {
+ int shift = document()->formatter()->formatVertically( document(), this );
+ if ( shift && !formattedAgain ) {
+ formattedAgain = TRUE;
+ goto formatAgain;
+ }
+ }
+
+ if ( n && doMove && n->invalid == -1 && r.y() + r.height() != n->r.y() ) {
+ int dy = ( r.y() + r.height() ) - n->r.y();
+ QTextParag *s = n;
+ bool makeInvalid = p && p->lastInFrame;
+ while ( s && dy ) {
+ if ( !s->isFullWidth() )
+ makeInvalid = TRUE;
+ if ( makeInvalid )
+ s->invalidate( 0 );
+ s->move( dy );
+ if ( s->lastInFrame )
+ makeInvalid = TRUE;
+ s = s->n;
+ }
+ }
+
+ firstFormat = FALSE;
+ changed = TRUE;
+ invalid = -1;
+ //##### string()->setTextChanged( FALSE );
+}
+
+int QTextParag::lineHeightOfChar( int i, int *bl, int *y ) const
+{
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for ( ;; ) {
+ if ( i >= it.key() ) {
+ if ( bl )
+ *bl = ( *it )->baseLine;
+ if ( y )
+ *y = ( *it )->y;
+ return ( *it )->h;
+ }
+ if ( it == lineStarts.begin() )
+ break;
+ --it;
+ }
+
+ qWarning( "QTextParag::lineHeightOfChar: couldn't find lh for %d", i );
+ return 15;
+}
+
+QTextStringChar *QTextParag::lineStartOfChar( int i, int *index, int *line ) const
+{
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ int l = (int)lineStarts.count() - 1;
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for ( ;; ) {
+ if ( i >= it.key() ) {
+ if ( index )
+ *index = it.key();
+ if ( line )
+ *line = l;
+ return &str->at( it.key() );
+ }
+ if ( it == lineStarts.begin() )
+ break;
+ --it;
+ --l;
+ }
+
+ qWarning( "QTextParag::lineStartOfChar: couldn't find %d", i );
+ return 0;
+}
+
+int QTextParag::lines() const
+{
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ return (int)lineStarts.count();
+}
+
+QTextStringChar *QTextParag::lineStartOfLine( int line, int *index ) const
+{
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ if ( line >= 0 && line < (int)lineStarts.count() ) {
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while ( line-- > 0 )
+ ++it;
+ int i = it.key();
+ if ( index )
+ *index = i;
+ return &str->at( i );
+ }
+
+ qWarning( "QTextParag::lineStartOfLine: couldn't find %d", line );
+ return 0;
+}
+
+int QTextParag::leftGap() const
+{
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ int line = 0;
+ int x = str->at(0).x; /* set x to x of first char */
+ if ( str->isBidi() ) {
+ for ( int i = 1; i < str->length(); ++i )
+ x = QMIN(x, str->at(i).x);
+ return x;
+ }
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while (line < (int)lineStarts.count()) {
+ int i = it.key(); /* char index */
+ x = QMIN(x, str->at(i).x);
+ ++it;
+ ++line;
+ }
+ return x;
+}
+
+void QTextParag::setFormat( int index, int len, QTextFormat *f, bool useCollection, int flags )
+{
+ if ( !f )
+ return;
+ if ( index < 0 )
+ index = 0;
+ if ( index > str->length() - 1 )
+ index = str->length() - 1;
+ if ( index + len >= str->length() )
+ len = str->length() - index;
+
+ QTextFormatCollection *fc = 0;
+ if ( useCollection )
+ fc = formatCollection();
+ QTextFormat *of;
+ for ( int i = 0; i < len; ++i ) {
+ of = str->at( i + index ).format();
+ if ( !changed && f->key() != of->key() )
+ changed = TRUE;
+ if ( invalid == -1 &&
+ ( f->font().family() != of->font().family() ||
+ f->font().pointSize() != of->font().pointSize() ||
+ f->font().weight() != of->font().weight() ||
+ f->font().italic() != of->font().italic() ||
+ f->vAlign() != of->vAlign() ) ) {
+ invalidate( 0 );
+ }
+ if ( flags == -1 || flags == QTextFormat::Format || !fc ) {
+ if ( fc )
+ f = fc->format( f );
+ str->setFormat( i + index, f, useCollection );
+ } else {
+ QTextFormat *fm = fc->format( of, f, flags );
+ str->setFormat( i + index, fm, useCollection );
+ }
+ }
+}
+
+void QTextParag::indent( int *oldIndent, int *newIndent )
+{
+ if ( !hasdoc || !document()->indent() || style() && style()->displayMode() != QStyleSheetItem::DisplayBlock ) {
+ if ( oldIndent )
+ *oldIndent = 0;
+ if ( newIndent )
+ *newIndent = 0;
+ if ( oldIndent && newIndent )
+ *newIndent = *oldIndent;
+ return;
+ }
+ document()->indent()->indent( document(), this, oldIndent, newIndent );
+}
+
+void QTextParag::paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor, bool drawSelections,
+ int clipx, int clipy, int clipw, int cliph )
+{
+ if ( !visible )
+ return;
+ QTextStringChar *chr = at( 0 );
+ int i = 0;
+ int h = 0;
+ int baseLine = 0, lastBaseLine = 0;
+ QTextStringChar *formatChar = 0;
+ int lastY = -1;
+ int startX = 0;
+ int bw = 0;
+ int cy = 0;
+ int curx = -1, cury = 0, curh = 0;
+ bool lastDirection = chr->rightToLeft;
+ const int full_sel_width = (hasdoc ? document()->width() : r.width());
+#if 0 // seems we don't need that anymore
+ int tw = 0;
+#endif
+
+ QString qstr = str->toString();
+ // ### workaround so that \n are not drawn, actually this should be
+ // fixed in QFont somewhere (under Windows you get ugly boxes
+ // otherwise)
+ QChar* uc = (QChar*) qstr.unicode();
+ for ( uint ii = 0; ii < qstr.length(); ii++ ) {
+ if ( uc[(int)ii]== '\n' )
+ uc[(int)ii] = 0x20;
+ }
+
+
+ const int nSels = hasdoc ? document()->numSelections() : 1;
+ QMemArray<int> selectionStarts( nSels );
+ QMemArray<int> selectionEnds( nSels );
+ if ( drawSelections ) {
+ bool hasASelection = FALSE;
+ for ( i = 0; i < nSels; ++i ) {
+ if ( !hasSelection( i ) ) {
+ selectionStarts[ i ] = -1;
+ selectionEnds[ i ] = -1;
+ } else {
+ hasASelection = TRUE;
+ selectionStarts[ i ] = selectionStart( i );
+ int end = selectionEnd( i );
+ if ( end == length() - 1 && n && n->hasSelection( i ) )
+ end++;
+ selectionEnds[ i ] = end;
+ }
+ }
+ if ( !hasASelection )
+ drawSelections = FALSE;
+ }
+
+ int line = -1;
+ int cw;
+ bool didListLabel = FALSE;
+ int paintStart = 0;
+ int paintEnd = -1;
+ int lasth = 0;
+ for ( i = 0; i < length(); i++ ) {
+ chr = at( i );
+#if 0 // seems we don't need that anymore
+ if ( !str->isBidi() && is_printer( &painter ) ) { // ### fix our broken ps-printer
+ if ( !chr->lineStart )
+ chr->x = QMAX( chr->x, tw );
+ else
+ tw = 0;
+ }
+#endif
+ cw = string()->width( i );
+ if ( chr->c == '\t' && i < length() - 1 )
+ cw = at( i + 1 )->x - chr->x + 1;
+ if ( chr->c.unicode() == 0xad && i < length() - 1 )
+ cw = 0;
+
+ // init a new line
+ if ( chr->lineStart ) {
+#if 0 // seems we don't need that anymore
+ tw = 0;
+#endif
+ ++line;
+ lineInfo( line, cy, h, baseLine );
+ lasth = h;
+ if ( clipy != -1 && cy > clipy - r.y() + cliph ) // outside clip area, leave
+ break;
+ if ( lastBaseLine == 0 )
+ lastBaseLine = baseLine;
+ }
+
+ // draw bullet list items
+ if ( !didListLabel && line == 0 && style() && style()->displayMode() == QStyleSheetItem::DisplayListItem ) {
+ didListLabel = TRUE;
+ drawLabel( &painter, chr->x, cy, 0, 0, baseLine, cg );
+ }
+
+ // check for cursor mark
+ if ( cursor && this == cursor->parag() && i == cursor->index() ) {
+ curx = cursor->x();
+ QTextStringChar *c = chr;
+ if ( i > 0 )
+ --c;
+ curh = c->format()->height();
+ cury = cy + baseLine - c->format()->ascent();
+ }
+
+ // first time - start again...
+ if ( !formatChar || lastY == -1 ) {
+ formatChar = chr;
+ lastY = cy;
+ startX = chr->x;
+ if ( !chr->isCustom() && chr->c != '\n' )
+ paintEnd = i;
+ bw = cw;
+ if ( !chr->isCustom() )
+ continue;
+ }
+
+ // check if selection state changed
+ bool selectionChange = FALSE;
+ if ( drawSelections ) {
+ for ( int j = 0; j < nSels; ++j ) {
+ selectionChange = selectionStarts[ j ] == i || selectionEnds[ j ] == i;
+ if ( selectionChange )
+ break;
+ }
+ }
+
+ //if something (format, etc.) changed, draw what we have so far
+ if ( ( ( ( alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify && at(paintEnd)->c.isSpace() ) ||
+ lastDirection != (bool)chr->rightToLeft ||
+ chr->startOfRun ||
+ lastY != cy || chr->format() != formatChar->format() || chr->isAnchor() != formatChar->isAnchor() ||
+ ( paintEnd != -1 && at( paintEnd )->c =='\t' ) || chr->c == '\t' ||
+ ( paintEnd != -1 && at( paintEnd )->c.unicode() == 0xad ) || chr->c.unicode() == 0xad ||
+ selectionChange || chr->isCustom() ) ) {
+ if ( paintStart <= paintEnd ) {
+ // ### temporary hack until I get the new placement/shaping stuff working
+ int x = startX;
+ if ( ( alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify && paintEnd != -1 &&
+ paintEnd > 1 && at( paintEnd )->c.isSpace() ) {
+ int add = str->at(paintEnd).x - str->at(paintEnd-1).x - str->width(paintEnd-1);
+ bw += ( lastDirection ? 0 : add );
+ }
+ drawParagString( painter, qstr, paintStart, paintEnd - paintStart + 1, x, lastY,
+ lastBaseLine, bw, lasth, drawSelections,
+ formatChar, i, selectionStarts, selectionEnds, cg, lastDirection );
+ }
+#if 0 // seems we don't need that anymore
+ if ( !str->isBidi() && is_printer( &painter ) ) { // ### fix our broken ps-printer
+ if ( !chr->lineStart ) {
+ // ### the next line doesn't look 100% correct for arabic
+ tw = startX + painter.fontMetrics().width( qstr.mid(paintStart, paintEnd - paintStart +1) );
+ chr->x = QMAX( chr->x, tw );
+ } else {
+ tw = 0;
+ }
+ }
+#endif
+ if ( !chr->isCustom() ) {
+ if ( chr->c != '\n' ) {
+ paintStart = i;
+ paintEnd = i;
+ } else {
+ paintStart = i+1;
+ paintEnd = -1;
+ }
+ formatChar = chr;
+ lastY = cy;
+ startX = chr->x;
+ bw = cw;
+ } else {
+ if ( chr->customItem()->placement() == QTextCustomItem::PlaceInline ) {
+ chr->customItem()->draw( &painter, chr->x, cy, clipx - r.x(), clipy - r.y(), clipw, cliph, cg,
+ nSels && selectionStarts[ 0 ] <= i && selectionEnds[ 0 ] >= i );
+ paintStart = i+1;
+ paintEnd = -1;
+ formatChar = chr;
+ lastY = cy;
+ startX = chr->x + string()->width( i );
+ bw = 0;
+ } else {
+ chr->customItem()->resize( chr->customItem()->width );
+ paintStart = i+1;
+ paintEnd = -1;
+ formatChar = chr;
+ lastY = cy;
+ startX = chr->x + string()->width( i );
+ bw = 0;
+ }
+ }
+ } else {
+ if ( chr->c != '\n' ) {
+ if( chr->rightToLeft ) {
+ startX = chr->x;
+ }
+ paintEnd = i;
+ }
+ bw += cw;
+ }
+ lastBaseLine = baseLine;
+ lasth = h;
+ lastDirection = chr->rightToLeft;
+ }
+
+ // if we are through the parag, but still have some stuff left to draw, draw it now
+ if ( paintStart <= paintEnd ) {
+ bool selectionChange = FALSE;
+ if ( drawSelections ) {
+ for ( int j = 0; j < nSels; ++j ) {
+ selectionChange = selectionStarts[ j ] == i || selectionEnds[ j ] == i;
+ if ( selectionChange )
+ break;
+ }
+ }
+ int x = startX;
+ drawParagString( painter, qstr, paintStart, paintEnd-paintStart+1, x, lastY,
+ lastBaseLine, bw, h, drawSelections,
+ formatChar, i, selectionStarts, selectionEnds, cg, lastDirection );
+ }
+
+ // if we should draw a cursor, draw it now
+ if ( curx != -1 && cursor ) {
+ painter.fillRect( QRect( curx, cury, 1, curh - lineSpacing() ), cg.color( QColorGroup::Text ) );
+ painter.save();
+ if ( string()->isBidi() ) {
+ const int d = 4;
+ if ( at( cursor->index() )->rightToLeft ) {
+ painter.setPen( Qt::black );
+ painter.drawLine( curx, cury, curx - d / 2, cury + d / 2 );
+ painter.drawLine( curx, cury + d, curx - d / 2, cury + d / 2 );
+ } else {
+ painter.setPen( Qt::black );
+ painter.drawLine( curx, cury, curx + d / 2, cury + d / 2 );
+ painter.drawLine( curx, cury + d, curx + d / 2, cury + d / 2 );
+ }
+ }
+ painter.restore();
+ }
+}
+
+//#define BIDI_DEBUG
+
+void QTextParag::drawParagString( QPainter &painter, const QString &s, int start, int len, int startX,
+ int lastY, int baseLine, int bw, int h, bool drawSelections,
+ QTextStringChar *formatChar, int i, const QMemArray<int> &selectionStarts,
+ const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft )
+{
+ bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : FALSE;
+ QTextFormat* format = formatChar->format();
+ QString str( s );
+ if ( str[ (int)str.length() - 1 ].unicode() == 0xad )
+ str.remove( str.length() - 1, 1 );
+ if ( !plainText || hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color() )
+ painter.setPen( QPen( format->color() ) );
+ else
+ painter.setPen( cg.text() );
+ painter.setFont( format->font() );
+
+ if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() ) {
+ if ( format->useLinkColor() ) {
+ if ( document()->linkColor.isValid() )
+ painter.setPen( document()->linkColor );
+ else
+ painter.setPen( QPen( Qt::blue ) );
+ }
+ if ( document()->underlineLinks() ) {
+ QFont fn = format->font();
+ fn.setUnderline( TRUE );
+ painter.setFont( fn );
+ }
+ }
+
+ if ( drawSelections ) {
+ const int nSels = hasdoc ? document()->numSelections() : 1;
+ const int startSel = is_printer( 0 ) ? 1 : 0;
+ for ( int j = startSel; j < nSels; ++j ) {
+ if ( i > selectionStarts[ j ] && i <= selectionEnds[ j ] ) {
+ if ( !hasdoc || document()->invertSelectionText( j ) )
+ painter.setPen( QPen( cg.color( QColorGroup::HighlightedText ) ) );
+ if ( j == QTextDocument::Standard )
+ painter.fillRect( startX, lastY, bw, h, cg.color( QColorGroup::Highlight ) );
+ else
+ painter.fillRect( startX, lastY, bw, h, hasdoc ? document()->selectionColor( j ) : cg.color( QColorGroup::Highlight ) );
+ }
+ }
+ }
+
+ if ( str[ start ] != '\t' && str[ start ].unicode() != 0xad ) {
+ if ( format->vAlign() == QTextFormat::AlignNormal ) {
+ painter.drawText( startX, lastY + baseLine, str.mid( start ), len );
+#ifdef BIDI_DEBUG
+ painter.save();
+ painter.setPen ( Qt::red );
+ painter.drawLine( startX, lastY, startX, lastY + baseLine );
+ painter.drawLine( startX, lastY + baseLine/2, startX + 10, lastY + baseLine/2 );
+ int w = 0;
+ int i = 0;
+ while( i < len )
+ w += painter.fontMetrics().charWidth( str, start + i++ );
+ painter.setPen ( Qt::blue );
+ painter.drawLine( startX + w - 1, lastY, startX + w - 1, lastY + baseLine );
+ painter.drawLine( startX + w - 1, lastY + baseLine/2, startX + w - 1 - 10, lastY + baseLine/2 );
+ painter.restore();
+#endif
+ } else if ( format->vAlign() == QTextFormat::AlignSuperScript ) {
+ QFont f( painter.font() );
+ if ( format->fontSizesInPixels() )
+ f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
+ else
+ f.setPointSize( ( f.pointSize() * 2 ) / 3 );
+ painter.setFont( f );
+ painter.drawText( startX, lastY + baseLine - ( painter.fontMetrics().height() / 2 ),
+ str.mid( start ), len );
+ } else if ( format->vAlign() == QTextFormat::AlignSubScript ) {
+ QFont f( painter.font() );
+ if ( format->fontSizesInPixels() )
+ f.setPixelSize( ( f.pixelSize() * 2 ) / 3 );
+ else
+ f.setPointSize( ( f.pointSize() * 2 ) / 3 );
+ painter.setFont( f );
+ painter.drawText( startX, lastY + baseLine + painter.fontMetrics().height() / 6, str.mid( start ), len );
+ }
+ }
+ if ( i + 1 < length() && at( i + 1 )->lineStart && at( i )->c.unicode() == 0xad ) {
+ painter.drawText( startX + bw, lastY + baseLine, "\xad" );
+ }
+ if ( format->isMisspelled() ) {
+ painter.save();
+ painter.setPen( QPen( Qt::red, 1, Qt::DotLine ) );
+ painter.drawLine( startX, lastY + baseLine + 1, startX + bw, lastY + baseLine + 1 );
+ painter.restore();
+ }
+
+ i -= len;
+
+ if ( hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
+ document()->focusIndicator.parag == this &&
+ ( document()->focusIndicator.start >= i &&
+ document()->focusIndicator.start + document()->focusIndicator.len <= i + len ||
+ document()->focusIndicator.start <= i &&
+ document()->focusIndicator.start + document()->focusIndicator.len >= i + len ) ) {
+ painter.drawWinFocusRect( QRect( startX, lastY, bw, h ) );
+ }
+
+}
+
+void QTextParag::drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg )
+{
+ if ( !style() )
+ return;
+ QRect r ( x, y, w, h );
+ QStyleSheetItem::ListStyle s = listStyle();
+
+ p->save();
+ p->setPen( defFormat->color() );
+
+ QFont font2( defFormat->font() );
+ if ( length() > 0 ) {
+ QTextFormat *format = at( 0 )->format();
+ if ( format ) {
+ if ( format->fontSizesInPixels() )
+ font2.setPixelSize( at( 0 )->format()->font().pixelSize() );
+ else
+ font2.setPointSize( at( 0 )->format()->font().pointSize() );
+ }
+ }
+ p->setFont( font2 );
+ QFontMetrics fm( p->fontMetrics() );
+ int size = fm.lineSpacing() / 3;
+
+ switch ( s ) {
+ case QStyleSheetItem::ListDecimal:
+ case QStyleSheetItem::ListLowerAlpha:
+ case QStyleSheetItem::ListUpperAlpha:
+ {
+ int n = numberOfSubParagraph();
+ QString l;
+ switch ( s ) {
+ case QStyleSheetItem::ListLowerAlpha:
+ if ( n < 27 ) {
+ l = QChar( ('a' + (char) (n-1)));
+ break;
+ }
+ case QStyleSheetItem::ListUpperAlpha:
+ if ( n < 27 ) {
+ l = QChar( ('A' + (char) (n-1)));
+ break;
+ }
+ break;
+ default: //QStyleSheetItem::ListDecimal:
+ l.setNum( n );
+ break;
+ }
+ l += QString::fromLatin1(". ");
+ p->drawText( r.right() - fm.width( l ), r.top() + base, l );
+ }
+ break;
+ case QStyleSheetItem::ListSquare:
+ {
+ QRect er( r.right() - size * 2, r.top() + fm.height() / 2 - size / 2, size, size );
+ p->fillRect( er , cg.brush( QColorGroup::Foreground ) );
+ }
+ break;
+ case QStyleSheetItem::ListCircle:
+ {
+ QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse( er );
+ }
+ break;
+ case QStyleSheetItem::ListDisc:
+ default:
+ {
+ p->setBrush( cg.brush( QColorGroup::Foreground ));
+ QRect er( r.right()-size*2, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse( er );
+ p->setBrush( Qt::NoBrush );
+ }
+ break;
+ }
+
+ p->restore();
+}
+
+void QTextParag::setStyleSheetItems( const QPtrVector<QStyleSheetItem> &vec )
+{
+ styleSheetItemsVec() = vec;
+ invalidate( 0 );
+ lm = rm = tm = bm = flm = -1;
+ numSubParag = -1;
+}
+
+void QTextParag::setList( bool b, int listStyle )
+{
+ if ( !hasdoc )
+ return;
+
+ if ( !style() ) {
+ styleSheetItemsVec().resize( 2 );
+ mStyleSheetItemsVec->insert( 0, document()->styleSheet()->item( "html" ) );
+ mStyleSheetItemsVec->insert( 1, document()->styleSheet()->item( "p" ) );
+ }
+
+ if ( b ) {
+ if ( style()->displayMode() != QStyleSheetItem::DisplayListItem || this->listStyle() != listStyle ) {
+ styleSheetItemsVec().remove( styleSheetItemsVec().size() - 1 );
+ QStyleSheetItem *item = (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 1 ];
+ if ( item )
+ mStyleSheetItemsVec->remove( mStyleSheetItemsVec->size() - 1 );
+ mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1,
+ listStyle == QStyleSheetItem::ListDisc || listStyle == QStyleSheetItem::ListCircle
+ || listStyle == QStyleSheetItem::ListSquare ?
+ document()->styleSheet()->item( "ul" ) : document()->styleSheet()->item( "ol" ) );
+ mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1, document()->styleSheet()->item( "li" ) );
+ setListStyle( (QStyleSheetItem::ListStyle)listStyle );
+ } else {
+ return;
+ }
+ } else {
+ if ( style()->displayMode() != QStyleSheetItem::DisplayBlock ) {
+ styleSheetItemsVec().remove( styleSheetItemsVec().size() - 1 );
+ if ( mStyleSheetItemsVec->size() >= 2 ) {
+ mStyleSheetItemsVec->remove( mStyleSheetItemsVec->size() - 2 );
+ mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 2 );
+ } else {
+ mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 1 );
+ }
+ } else {
+ return;
+ }
+ }
+ invalidate( 0 );
+ lm = rm = tm = bm = flm = -1;
+ numSubParag = -1;
+ if ( next() ) {
+ QTextParag *s = next();
+ while ( s ) {
+ s->numSubParag = -1;
+ s->lm = s->rm = s->tm = s->bm = flm = -1;
+ s->numSubParag = -1;
+ s->invalidate( 0 );
+ s = s->next();
+ }
+ }
+}
+
+void QTextParag::incDepth()
+{
+ if ( !style() || !hasdoc )
+ return;
+ if ( style()->displayMode() != QStyleSheetItem::DisplayListItem )
+ return;
+ styleSheetItemsVec().resize( styleSheetItemsVec().size() + 1 );
+ mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 1, (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 2 ] );
+ mStyleSheetItemsVec->insert( mStyleSheetItemsVec->size() - 2,
+ listStyle() == QStyleSheetItem::ListDisc || listStyle() == QStyleSheetItem::ListCircle ||
+ listStyle() == QStyleSheetItem::ListSquare ?
+ document()->styleSheet()->item( "ul" ) : document()->styleSheet()->item( "ol" ) );
+ invalidate( 0 );
+ lm = -1;
+ flm = -1;
+}
+
+void QTextParag::decDepth()
+{
+ if ( !style() || !hasdoc )
+ return;
+ if ( style()->displayMode() != QStyleSheetItem::DisplayListItem )
+ return;
+ int numLists = 0;
+ QStyleSheetItem *lastList = 0;
+ int lastIndex = 0;
+ int i;
+ if ( mStyleSheetItemsVec ) {
+ for ( i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ QStyleSheetItem *item = (*mStyleSheetItemsVec)[ i ];
+ if ( item->name() == "ol" || item->name() == "ul" ) {
+ lastList = item;
+ lastIndex = i;
+ numLists++;
+ }
+ }
+ }
+
+ if ( !lastList )
+ return;
+ styleSheetItemsVec().remove( lastIndex );
+ for ( i = lastIndex; i < (int)mStyleSheetItemsVec->size() - 1; ++i )
+ mStyleSheetItemsVec->insert( i, (*mStyleSheetItemsVec)[ i + 1 ] );
+ mStyleSheetItemsVec->resize( mStyleSheetItemsVec->size() - 1 );
+ if ( numLists == 1 )
+ setList( FALSE, -1 );
+ invalidate( 0 );
+ lm = -1;
+ flm = -1;
+}
+
+int QTextParag::listDepth() const
+{
+ int numLists = 0;
+ int i;
+ if ( mStyleSheetItemsVec ) {
+ for ( i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ QStyleSheetItem *item = (*mStyleSheetItemsVec)[ i ];
+ if ( item->name() == "ol" || item->name() == "ul" )
+ numLists++;
+ }
+ }
+ return numLists - 1;
+}
+
+int *QTextParag::tabArray() const
+{
+ int *ta = tArray;
+ if ( !ta && hasdoc )
+ ta = document()->tabArray();
+ return ta;
+}
+
+int QTextParag::nextTab( int, int x )
+{
+ int *ta = tArray;
+ if ( hasdoc ) {
+ if ( !ta )
+ ta = document()->tabArray();
+ tabStopWidth = document()->tabStopWidth();
+ }
+ if ( ta ) {
+ int i = 0;
+ while ( ta[ i ] ) {
+ if ( ta[ i ] >= x )
+ return tArray[ i ];
+ ++i;
+ }
+ return tArray[ 0 ];
+ } else {
+ int d;
+ if ( tabStopWidth != 0 )
+ d = x / tabStopWidth;
+ else
+ return x;
+ return tabStopWidth * ( d + 1 );
+ }
+}
+
+void QTextParag::adjustToPainter( QPainter *p )
+{
+ for ( int i = 0; i < length(); ++i ) {
+ if ( at( i )->isCustom() )
+ at( i )->customItem()->adjustToPainter( p );
+ }
+}
+
+QTextFormatCollection *QTextParag::formatCollection() const
+{
+ if ( hasdoc )
+ return document()->formatCollection();
+ if ( !qFormatCollection ) {
+ qFormatCollection = new QTextFormatCollection;
+ static QSingleCleanupHandler<QTextFormatCollection> qtfCleanup;
+ qtfCleanup.set( &qFormatCollection );
+ }
+ return qFormatCollection;
+}
+
+QString QTextParag::richText() const
+{
+ QString s;
+ QTextStringChar *formatChar = 0;
+ QString spaces;
+ bool lastCharWasSpace = FALSE;
+ int firstcol = 0;
+ for ( int i = 0; i < length()-1; ++i ) {
+ QTextStringChar *c = &str->at( i );
+ if ( c->isAnchor() && !c->anchorName().isEmpty() ) {
+ if ( c->anchorName().contains( '#' ) ) {
+ QStringList l = QStringList::split( '#', c->anchorName() );
+ for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it )
+ s += "<a name=\"" + *it + "\"></a>";
+ } else {
+ s += "<a name=\"" + c->anchorName() + "\"></a>";
+ }
+ }
+ if ( !formatChar ) {
+ s += c->format()->makeFormatChangeTags( 0, QString::null, c->anchorHref() );
+ formatChar = c;
+ } else if ( ( formatChar->format()->key() != c->format()->key() ) ||
+ (formatChar->isAnchor() != c->isAnchor() &&
+ (!c->anchorHref().isEmpty() || !formatChar->anchorHref().isEmpty() ) ) ) {// lisp was here
+
+ if ( !spaces.isEmpty() ) {
+ if ( spaces[0] == '\t' || lastCharWasSpace )
+ s += "<wsp>" + spaces + "</wsp>";
+ else if ( spaces.length() > 1 )
+ s += "<wsp>" + spaces.mid(1) + "</wsp> ";
+ else
+ s += spaces;
+ lastCharWasSpace = TRUE;
+ spaces = QString::null;
+ }
+ s += c->format()->makeFormatChangeTags( formatChar->format() , formatChar->anchorHref(), c->anchorHref() );
+ formatChar = c;
+ }
+
+ if ( c->c == ' ' || c->c == '\t' ) {
+ spaces += c->c;
+ continue;
+ } else if ( !spaces.isEmpty() ) {
+ if ( spaces[0] == '\t' || lastCharWasSpace )
+ s += "<wsp>" + spaces + "</wsp>";
+ else if ( spaces.length() > 1 )
+ s += "<wsp>" + spaces.mid(1) + "</wsp> ";
+ else
+ s += spaces;
+ spaces = QString::null;
+ if ( s.length() - firstcol > 60 ) {
+ s += '\n';
+ firstcol = s.length();
+ }
+ }
+
+ lastCharWasSpace = FALSE;
+ if ( c->c == '<' ) {
+ s += "&lt;";
+ } else if ( c->c == '>' ) {
+ s += "&gt;";
+ } else if ( c->isCustom() ) {
+ s += c->customItem()->richText();
+ } else {
+ s += c->c;
+ }
+ }
+ if ( !spaces.isEmpty() ) {
+ if ( spaces.length() > 1 || spaces[0] == '\t' || lastCharWasSpace )
+ s += "<wsp>" + spaces + "</wsp>";
+ else
+ s += spaces;
+ }
+
+ if ( formatChar )
+ s += formatChar->format()->makeFormatEndTags( formatChar->anchorHref() );
+ return s;
+}
+
+void QTextParag::addCommand( QTextCommand *cmd )
+{
+ if ( !hasdoc )
+ pseudoDocument()->commandHistory->addCommand( cmd );
+ else
+ document()->commands()->addCommand( cmd );
+}
+
+QTextCursor *QTextParag::undo( QTextCursor *c )
+{
+ if ( !hasdoc )
+ return pseudoDocument()->commandHistory->undo( c );
+ return document()->commands()->undo( c );
+}
+
+QTextCursor *QTextParag::redo( QTextCursor *c )
+{
+ if ( !hasdoc )
+ return pseudoDocument()->commandHistory->redo( c );
+ return document()->commands()->redo( c );
+}
+
+int QTextParag::topMargin() const
+{
+ if ( !p && ( !hasdoc || !document()->addMargins() ) )
+ return 0;
+ if ( tm != -1 )
+ return tm;
+ QStyleSheetItem *item = style();
+ if ( !item ) {
+ ( (QTextParag*)this )->tm = 0;
+ return 0;
+ }
+
+ int m = 0;
+ if ( item->margin( QStyleSheetItem::MarginTop ) != QStyleSheetItem::Undefined )
+ m = item->margin( QStyleSheetItem::MarginTop );
+ if ( mStyleSheetItemsVec ) {
+ QStyleSheetItem *it = 0;
+ QStyleSheetItem *p = prev() ? prev()->style() : 0;
+ for ( int i = (int)mStyleSheetItemsVec->size() - 2 ; i >= 0; --i ) {
+ it = (*mStyleSheetItemsVec)[ i ];
+ if ( it != p )
+ break;
+ int mar = it->margin( QStyleSheetItem::MarginTop );
+ m += (mar != QStyleSheetItem::Undefined) ? mar : 0;
+ if ( it->displayMode() != QStyleSheetItem::DisplayInline )
+ break;
+ }
+ }
+ m = scale( m, QTextFormat::painter() );
+
+ ( (QTextParag*)this )->tm = m;
+ return tm;
+}
+
+int QTextParag::bottomMargin() const
+{
+ if ( bm != -1 )
+ return bm;
+ QStyleSheetItem *item = style();
+ if ( !item || !next() ) {
+ ( (QTextParag*)this )->bm = 0;
+ return 0;
+ }
+
+ int m = 0;
+ if ( item->margin( QStyleSheetItem::MarginBottom ) != QStyleSheetItem::Undefined )
+ m = item->margin( QStyleSheetItem::MarginBottom );
+ if ( mStyleSheetItemsVec ) {
+ QStyleSheetItem *it = 0;
+ QStyleSheetItem *n = next() ? next()->style() : 0;
+ for ( int i =(int)mStyleSheetItemsVec->size() - 2 ; i >= 0; --i ) {
+ it = (*mStyleSheetItemsVec)[ i ];
+ if ( it != n )
+ break;
+ int mar = it->margin( QStyleSheetItem::MarginBottom );
+ m += mar != QStyleSheetItem::Undefined ? mar : 0;
+ if ( it->displayMode() != QStyleSheetItem::DisplayInline )
+ break;
+ }
+ }
+ m = scale ( m, QTextFormat::painter() );
+
+ ( (QTextParag*)this )->bm = m;
+ return bm;
+}
+
+int QTextParag::leftMargin() const
+{
+ if ( lm != -1 )
+ return lm;
+ QStyleSheetItem *item = style();
+ if ( !item ) {
+ ( (QTextParag*)this )->lm = 0;
+ return 0;
+ }
+ int m = 0;
+ if ( mStyleSheetItemsVec ) {
+ for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ item = (*mStyleSheetItemsVec)[ i ];
+ int mar = item->margin( QStyleSheetItem::MarginLeft );
+ m += mar != QStyleSheetItem::Undefined ? mar : 0;
+ if ( item->name() == "ol" || item->name() == "ul" ) {
+ QPainter* oldPainter = QTextFormat::painter();
+ QTextFormat::setPainter( 0 );
+ m += defFormat->width( '1' ) +
+ defFormat->width( '2' ) +
+ defFormat->width( '3' ) +
+ defFormat->width( '.' );
+ QTextFormat::setPainter( oldPainter );
+ }
+ }
+ }
+
+ m = scale ( m, QTextFormat::painter() );
+
+ ( (QTextParag*)this )->lm = m;
+ return lm;
+}
+
+int QTextParag::firstLineMargin() const
+{
+ if ( flm != -1 )
+ return lm;
+ QStyleSheetItem *item = style();
+ if ( !item ) {
+ ( (QTextParag*)this )->flm = 0;
+ return 0;
+ }
+ int m = 0;
+ if ( mStyleSheetItemsVec ) {
+ for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ item = (*mStyleSheetItemsVec)[ i ];
+ int mar = item->margin( QStyleSheetItem::MarginFirstLine );
+ m += mar != QStyleSheetItem::Undefined ? mar : 0;
+ }
+ }
+
+ m = scale( m, QTextFormat::painter() );
+
+ ( (QTextParag*)this )->flm = m;
+ return flm;
+}
+
+int QTextParag::rightMargin() const
+{
+ if ( rm != -1 )
+ return rm;
+ QStyleSheetItem *item = style();
+ if ( !item ) {
+ ( (QTextParag*)this )->rm = 0;
+ return 0;
+ }
+ int m = 0;
+ if ( mStyleSheetItemsVec ) {
+ for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ item = (*mStyleSheetItemsVec)[ i ];
+ int mar = item->margin( QStyleSheetItem::MarginRight );
+ m += mar != QStyleSheetItem::Undefined ? mar : 0;
+ }
+ }
+ m = scale( m, QTextFormat::painter() );
+
+ ( (QTextParag*)this )->rm = m;
+ return rm;
+}
+
+int QTextParag::lineSpacing() const
+{
+ QStyleSheetItem *item = style();
+ if ( !item )
+ return 0;
+
+ int ls = item->lineSpacing();
+ if ( ls == QStyleSheetItem::Undefined )
+ return 0;
+ ls = scale( ls, QTextFormat::painter() );
+
+ return ls;
+}
+
+void QTextParag::copyParagData( QTextParag *parag )
+{
+ setStyleSheetItems( parag->styleSheetItems() );
+ setListStyle( parag->listStyle() );
+ setAlignment( parag->alignment() );
+ QColor *c = parag->backgroundColor();
+ if ( c )
+ setBackgroundColor( *c );
+}
+
+void QTextParag::show()
+{
+ if ( visible || !hasdoc )
+ return;
+ visible = TRUE;
+}
+
+void QTextParag::hide()
+{
+ if ( !visible || !hasdoc )
+ return;
+ visible = FALSE;
+}
+
+void QTextParag::setDirection( QChar::Direction d )
+{
+ if ( str && str->direction() != d ) {
+ str->setDirection( d );
+ invalidate( 0 );
+ }
+}
+
+QChar::Direction QTextParag::direction() const
+{
+ return (str ? str->direction() : QChar::DirON );
+}
+
+void QTextParag::setChanged( bool b, bool recursive )
+{
+ changed = b;
+ if ( recursive ) {
+ if ( document() && document()->parentParag() )
+ document()->parentParag()->setChanged( b, recursive );
+ }
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+QTextPreProcessor::QTextPreProcessor()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextFormatter::QTextFormatter()
+ : thisminw(0), thiswused(0), wrapEnabled( TRUE ), wrapColumn( -1 ), biw( FALSE )
+{
+}
+
+/* only used for bidi or complex text reordering
+ */
+QTextParagLineStart *QTextFormatter::formatLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line,
+ QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
+{
+#ifndef QT_NO_COMPLEXTEXT
+ if( string->isBidi() )
+ return bidiReorderLine( parag, string, line, startChar, lastChar, align, space );
+#endif
+ space = QMAX( space, 0 ); // #### with nested tables this gets negative because of a bug I didn't find yet, so workaround for now. This also means non-left aligned nested tables do not work at the moment
+ int start = (startChar - &string->at(0));
+ int last = (lastChar - &string->at(0) );
+ // do alignment Auto == Left in this case
+ if ( align & Qt::AlignHCenter || align & Qt::AlignRight ) {
+ if ( align & Qt::AlignHCenter )
+ space /= 2;
+ for ( int j = start; j <= last; ++j )
+ string->at( j ).x += space;
+ } else if ( align & Qt3::AlignJustify ) {
+ int numSpaces = 0;
+ for ( int j = start; j < last; ++j ) {
+ if( isBreakable( string, j ) ) {
+ numSpaces++;
+ }
+ }
+ int toAdd = 0;
+ for ( int k = start + 1; k <= last; ++k ) {
+ if( isBreakable( string, k ) && numSpaces ) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ }
+ string->at( k ).x += toAdd;
+ }
+ }
+
+ if ( last >= 0 && last < string->length() )
+ line->w = string->at( last ).x + string->width( last );
+ else
+ line->w = 0;
+
+ return new QTextParagLineStart();
+}
+
+#ifndef QT_NO_COMPLEXTEXT
+
+#ifdef BIDI_DEBUG
+#include <iostream>
+#endif
+
+// collects one line of the paragraph and transforms it to visual order
+QTextParagLineStart *QTextFormatter::bidiReorderLine( QTextParag * /*parag*/, QTextString *text, QTextParagLineStart *line,
+ QTextStringChar *startChar, QTextStringChar *lastChar, int align, int space )
+{
+ int start = (startChar - &text->at(0));
+ int last = (lastChar - &text->at(0) );
+ //qDebug("doing BiDi reordering from %d to %d!", start, last);
+
+ QBidiControl *control = new QBidiControl( line->context(), line->status );
+ QString str;
+ str.setUnicode( 0, last - start + 1 );
+ // fill string with logically ordered chars.
+ QTextStringChar *ch = startChar;
+ QChar *qch = (QChar *)str.unicode();
+ while ( ch <= lastChar ) {
+ *qch = ch->c;
+ qch++;
+ ch++;
+ }
+ int x = startChar->x;
+
+ QPtrList<QTextRun> *runs;
+ runs = QComplexText::bidiReorderLine(control, str, 0, last - start + 1,
+ (text->isRightToLeft() ? QChar::DirR : QChar::DirL) );
+
+ // now construct the reordered string out of the runs...
+
+ int numSpaces = 0;
+ // set the correct alignment. This is a bit messy....
+ if( align == Qt3::AlignAuto ) {
+ // align according to directionality of the paragraph...
+ if ( text->isRightToLeft() )
+ align = Qt::AlignRight;
+ }
+
+ if ( align & Qt::AlignHCenter )
+ x += space/2;
+ else if ( align & Qt::AlignRight )
+ x += space;
+ else if ( align & Qt3::AlignJustify ) {
+ for ( int j = start; j < last; ++j ) {
+ if( isBreakable( text, j ) ) {
+ numSpaces++;
+ }
+ }
+ }
+ int toAdd = 0;
+ bool first = TRUE;
+ QTextRun *r = runs->first();
+ int xmax = -0xffffff;
+ while ( r ) {
+ if(r->level %2) {
+ // odd level, need to reverse the string
+ int pos = r->stop + start;
+ while(pos >= r->start + start) {
+ QTextStringChar *c = &text->at(pos);
+ if( numSpaces && !first && isBreakable( text, pos ) ) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ } else if ( first ) {
+ first = FALSE;
+ if ( c->c == ' ' )
+ x -= c->format()->width( ' ' );
+ }
+ c->x = x + toAdd;
+ c->rightToLeft = TRUE;
+ c->startOfRun = FALSE;
+ int ww = 0;
+ if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
+ ww = text->width( pos );
+ } else {
+ ww = c->format()->width( ' ' );
+ }
+ if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
+ x += ww;
+ pos--;
+ }
+ } else {
+ int pos = r->start + start;
+ while(pos <= r->stop + start) {
+ QTextStringChar* c = &text->at(pos);
+ if( numSpaces && !first && isBreakable( text, pos ) ) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ } else if ( first ) {
+ first = FALSE;
+ if ( c->c == ' ' )
+ x -= c->format()->width( ' ' );
+ }
+ c->x = x + toAdd;
+ c->rightToLeft = FALSE;
+ c->startOfRun = FALSE;
+ int ww = 0;
+ if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
+ ww = text->width( pos );
+ } else {
+ ww = c->format()->width( ' ' );
+ }
+ //qDebug("setting char %d at pos %d", pos, x);
+ if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
+ x += ww;
+ pos++;
+ }
+ }
+ text->at( r->start + start ).startOfRun = TRUE;
+ r = runs->next();
+ }
+
+ line->w = xmax + 10;
+ QTextParagLineStart *ls = new QTextParagLineStart( control->context, control->status );
+ delete control;
+ delete runs;
+ return ls;
+}
+#endif
+
+bool QTextFormatter::isBreakable( QTextString *string, int pos ) const
+{
+ const QChar &c = string->at( pos ).c;
+ char ch = c.latin1();
+ if ( c.isSpace() && ch != '\n' && c.unicode() != 0x00a0U )
+ return TRUE;
+ if ( c.unicode() == 0xad ) // soft hyphen
+ return TRUE;
+ if ( !ch ) {
+ // not latin1, need to do more sophisticated checks for other scripts
+ uchar row = c.row();
+ if ( row == 0x0e ) {
+ // 0e00 - 0e7f == Thai
+ if ( c.cell() < 0x80 ) {
+#ifdef HAVE_THAI_BREAKS
+ // check for thai
+ if( string != cachedString ) {
+ // build up string of thai chars
+ QTextCodec *thaiCodec = QTextCodec::codecForMib(2259);
+ if ( !thaiCache )
+ thaiCache = new QCString;
+ if ( !thaiIt )
+ thaiIt = ThBreakIterator::createWordInstance();
+ *thaiCache = thaiCodec->fromUnicode( s->string() );
+ }
+ thaiIt->setText(thaiCache->data());
+ for(int i = thaiIt->first(); i != thaiIt->DONE; i = thaiIt->next() ) {
+ if( i == pos )
+ return TRUE;
+ if( i > pos )
+ return FALSE;
+ }
+ return FALSE;
+#else
+ // if we don't have a thai line breaking lib, allow
+ // breaks everywhere except directly before punctuation.
+ return TRUE;
+#endif
+ } else
+ return FALSE;
+ }
+ if ( row < 0x11 ) // no asian font
+ return FALSE;
+ if ( row > 0x2d && row < 0xfb || row == 0x11 )
+ // asian line breaking. Everywhere allowed except directly
+ // in front of a punctuation character.
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void QTextFormatter::insertLineStart( QTextParag *parag, int index, QTextParagLineStart *ls )
+{
+ if ( index > 0 ) { // we can assume that only first line starts are insrted multiple times
+ parag->lineStartList().insert( index, ls );
+ return;
+ }
+ QMap<int, QTextParagLineStart*>::Iterator it;
+ if ( ( it = parag->lineStartList().find( index ) ) == parag->lineStartList().end() ) {
+ parag->lineStartList().insert( index, ls );
+ } else {
+ delete *it;
+ parag->lineStartList().remove( it );
+ parag->lineStartList().insert( index, ls );
+ }
+}
+
+
+/* Standard pagebreak algorithm using QTextFlow::adjustFlow. Returns
+ the shift of the paragraphs bottom line.
+ */
+int QTextFormatter::formatVertically( QTextDocument* doc, QTextParag* parag )
+{
+ int oldHeight = parag->rect().height();
+ QMap<int, QTextParagLineStart*>& lineStarts = parag->lineStartList();
+ QMap<int, QTextParagLineStart*>::Iterator it = lineStarts.begin();
+ int h = doc->addMargins() ? parag->topMargin() : 0;
+ for ( ; it != lineStarts.end() ; ++it ) {
+ QTextParagLineStart * 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() && doc && !doc->addMargins() )
+ m = QMAX( m, parag->next()->topMargin() );
+ if ( parag->next() && parag->next()->isLineBreak() )
+ m = 0;
+ h += m;
+ parag->setHeight( h );
+ return h - oldHeight;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextFormatterBreakInWords::QTextFormatterBreakInWords()
+{
+}
+
+int QTextFormatterBreakInWords::format( QTextDocument *doc,QTextParag *parag,
+ int start, const QMap<int, QTextParagLineStart*> & )
+{
+ QTextStringChar *c = 0;
+ QTextStringChar *firstChar = 0;
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 4;
+ int x = left + ( doc ? parag->firstLineMargin() : 0 );
+ int dw = parag->documentVisibleWidth() - ( doc ? doc->rightMargin() : 0 );
+ int y = doc && doc->addMargins() ? parag->topMargin() : 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;
+ QTextParagLineStart *lineStart = new QTextParagLineStart( 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 QTextParagLineStart( y, h, h );
+ insertLineStart( parag, i, lineStart );
+ c->lineStart = 1;
+ firstChar = c;
+ x = 0xffffff;
+ continue;
+ }
+
+ if ( wrapEnabled &&
+ ( wrapAtColumn() == -1 && x + ww > w ||
+ wrapAtColumn() != -1 && col >= wrapAtColumn() ) ||
+ parag->isNewLinesAllowed() && lastChr == '\n' ) {
+ x = doc ? parag->document()->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
+ w = dw;
+ y += h;
+ h = c->height();
+ lineStart = formatLine( parag, parag->string(), lineStart, firstChar, c-1 );
+ lineStart->y = y;
+ insertLineStart( parag, i, lineStart );
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ col = 0;
+ if ( wrapAtColumn() != -1 )
+ minw = QMAX( minw, w );
+ } else if ( lineStart ) {
+ lineStart->baseLine = QMAX( lineStart->baseLine, c->ascent() );
+ h = QMAX( h, c->height() );
+ lineStart->h = h;
+ }
+
+ c->x = x;
+ x += ww;
+ wused = QMAX( wused, x );
+ }
+
+ int m = parag->bottomMargin();
+ if ( parag->next() && doc && !doc->addMargins() )
+ m = QMAX( m, parag->next()->topMargin() );
+ parag->setFullWidth( fullWidth );
+ if ( parag->next() && parag->next()->isLineBreak() )
+ m = 0;
+ y += h + m;
+ if ( !wrapEnabled )
+ minw = QMAX(minw, wused);
+
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextFormatterBreakWords::QTextFormatterBreakWords()
+{
+}
+
+#define DO_FLOW( lineStart ) do{ if ( doc && doc->isPageBreakEnabled() ) { \
+ int yflow = lineStart->y + parag->rect().y();\
+ int shift = doc->flow()->adjustFlow( yflow, dw, lineStart->h ); \
+ lineStart->y += shift;\
+ y += shift;\
+ }}while(FALSE)
+
+int QTextFormatterBreakWords::format( QTextDocument *doc, QTextParag *parag,
+ int start, const QMap<int, QTextParagLineStart*> & )
+{
+ QTextStringChar *c = 0;
+ QTextStringChar *firstChar = 0;
+ QTextString *string = parag->string();
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
+ int x = left + ( doc ? parag->firstLineMargin() : 0 );
+ int y = doc && doc->addMargins() ? parag->topMargin() : 0;
+ int h = y;
+ int len = parag->length();
+ if ( doc )
+ x = doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), x, 0 );
+ int dw = parag->documentVisibleWidth() - ( doc ? ( left != x ? 0 : doc->rightMargin() ) : 0 );
+
+ int curLeft = x;
+ int rm = parag->rightMargin();
+ int rdiff = doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 0 ) : 0;
+ int w = dw - rdiff;
+ bool fullWidth = TRUE;
+ int marg = left + rdiff;
+ int minw = 0;
+ int wused = 0;
+ int tminw = marg;
+ int linespace = doc ? parag->lineSpacing() : 0;
+ bool wrapEnabled = isWrapEnabled( parag );
+
+ start = 0;
+ if ( start == 0 )
+ c = &parag->string()->at( 0 );
+
+ int i = start;
+ QTextParagLineStart *lineStart = new QTextParagLineStart( y, y, 0 );
+ insertLineStart( parag, 0, lineStart );
+ int lastBreak = -1;
+ int tmpBaseLine = 0, tmph = 0;
+ bool lastWasNonInlineCustom = FALSE;
+
+ int align = parag->alignment();
+ if ( align == Qt3::AlignAuto && doc && doc->alignment() != Qt3::AlignAuto )
+ align = doc->alignment();
+
+ align &= Qt3::AlignHorizontal_Mask;
+
+ QPainter *painter = QTextFormat::painter();
+ int col = 0;
+ int ww = 0;
+ QChar lastChr;
+ for ( ; i < len; ++i, ++col ) {
+ if ( c )
+ lastChr = c->c;
+ // ### next line should not be needed
+ if ( painter )
+ c->format()->setPainter( painter );
+ c = &string->at( i );
+ c->rightToLeft = FALSE;
+ if ( i > 0 && (x > curLeft || ww == 0) || lastWasNonInlineCustom ) {
+ c->lineStart = 0;
+ } else {
+ c->lineStart = 1;
+ firstChar = c;
+ }
+
+ if ( c->isCustom() && c->customItem()->placement() != QTextCustomItem::PlaceInline )
+ lastWasNonInlineCustom = TRUE;
+ else
+ lastWasNonInlineCustom = FALSE;
+
+ if ( c->c.unicode() >= 32 || c->isCustom() ) {
+ ww = string->width( i );
+ } else if ( c->c == '\t' ) {
+ int nx = parag->nextTab( i, x - left ) + left;
+ if ( nx < x )
+ ww = w - x;
+ else
+ ww = nx - x;
+ } else {
+ ww = c->format()->width( ' ' );
+ }
+
+ // last character ("invisible" space) has no width
+ if ( i == len - 1 )
+ ww = 0;
+
+ QTextCustomItem* ci = c->customItem();
+ if ( c->isCustom() && ci->ownLine() ) {
+ x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
+ w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
+ QTextParagLineStart *lineStart2 = formatLine( parag, string, lineStart, firstChar, c-1, align, w - x );
+ ci->resize( w - x);
+ if ( ci->width < w - x ) {
+ if ( align & Qt::AlignHCenter )
+ x = ( w - ci->width ) / 2;
+ else if ( align & Qt::AlignRight ) {
+ x = w - ci->width;
+ }
+ }
+ c->x = x;
+ curLeft = x;
+ if ( i == 0 || !isBreakable( string, i - 1 ) || string->at( i - 1 ).lineStart == 0 ) {
+ y += QMAX( h, tmph );
+ tmph = c->height() + linespace;
+ h = tmph;
+ lineStart = lineStart2;
+ lineStart->y = y;
+ insertLineStart( parag, i, lineStart );
+ c->lineStart = 1;
+ firstChar = c;
+ } else {
+ tmph = c->height() + linespace;
+ 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();
+ if ( tw < QWIDGETSIZE_MAX )
+ tminw = tw;
+ else
+ tminw = marg;
+ wused = QMAX( wused, ci->width );
+ continue;
+ } else if ( c->isCustom() && ci->placement() != QTextCustomItem::PlaceInline ) {
+ int tw = ci->minimumWidth();
+ if ( tw < QWIDGETSIZE_MAX )
+ minw = QMAX( minw, tw );
+ }
+
+ if ( wrapEnabled && ( !c->c.isSpace() || lastBreak == -2 )
+ && ( lastBreak != -1 || allowBreakInWords() ) &&
+ ( wrapAtColumn() == -1 && x + ww > w && lastBreak != -1 ||
+ wrapAtColumn() == -1 && x + ww > w - 4 && lastBreak == -1 && allowBreakInWords() ||
+ wrapAtColumn() != -1 && col >= wrapAtColumn() ) ||
+ parag->isNewLinesAllowed() && lastChr == '\n' && firstChar < c ) {
+ if ( wrapAtColumn() != -1 )
+ minw = QMAX( minw, x + ww );
+ if ( lastBreak < 0 ) {
+ if ( lineStart ) {
+ lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
+ h = QMAX( h, tmph );
+ lineStart->h = h;
+ DO_FLOW( lineStart );
+ }
+ lineStart = formatLine( parag, string, lineStart, firstChar, c-1, align, w - x );
+ x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
+ w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
+ if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
+ int nx = parag->nextTab( i, x - left ) + left;
+ if ( nx < x )
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += h;
+ tmph = c->height() + linespace;
+ 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 {
+ DO_FLOW( lineStart );
+ i = lastBreak;
+ lineStart = formatLine( parag, string, lineStart, firstChar, parag->at( lastBreak ), align, w - string->at( i ).x );
+ x = doc ? doc->flow()->adjustLMargin( y + parag->rect().y(), parag->rect().height(), left, 4 ) : left;
+ w = dw - ( doc ? doc->flow()->adjustRMargin( y + parag->rect().y(), parag->rect().height(), rm, 4 ) : 0 );
+ if ( parag->isNewLinesAllowed() && c->c == '\t' ) {
+ int nx = parag->nextTab( i, x - left ) + left;
+ if ( nx < x )
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += h;
+ tmph = c->height() + linespace;
+ h = tmph;
+ lineStart->y = y;
+ insertLineStart( parag, i + 1, lineStart );
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -1;
+ col = 0;
+ tminw = marg;
+ continue;
+ }
+ } else if ( lineStart && ( isBreakable( string, i ) || parag->isNewLinesAllowed() && c->c == '\n' ) ) {
+ if ( len <= 2 || i < len - 1 ) {
+ tmpBaseLine = QMAX( tmpBaseLine, c->ascent() );
+ tmph = QMAX( tmph, c->height() + linespace );
+ }
+ minw = QMAX( minw, tminw );
+ tminw = marg + ww;
+ lineStart->baseLine = QMAX( lineStart->baseLine, tmpBaseLine );
+ h = QMAX( h, tmph );
+ lineStart->h = h;
+ if ( i < len - 2 || c->c != ' ' )
+ lastBreak = i;
+ } else {
+ tminw += ww;
+ int belowBaseLine = QMAX( tmph - tmpBaseLine, c->height() + linespace - c->ascent() );
+ 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 )
+ align = Qt3::AlignAuto;
+ DO_FLOW( lineStart );
+ lineStart = formatLine( parag, string, lineStart, firstChar, c, align, w - x );
+ delete lineStart;
+ }
+
+ minw = QMAX( minw, tminw );
+
+ int m = parag->bottomMargin();
+ if ( parag->next() && doc && !doc->addMargins() )
+ m = QMAX( m, parag->next()->topMargin() );
+ parag->setFullWidth( fullWidth );
+ if ( parag->next() && parag->next()->isLineBreak() )
+ m = 0;
+ y += h + m;
+
+ wused += rm;
+ if ( !wrapEnabled || wrapAtColumn() != -1 )
+ minw = QMAX(minw, wused);
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextIndent::QTextIndent()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+QTextFormatCollection::QTextFormatCollection()
+ : cKey( 307 ), sheet( 0 )
+{
+ defFormat = new QTextFormat( QApplication::font(),
+ QApplication::palette().color( QPalette::Active, QColorGroup::Text ) );
+ lastFormat = cres = 0;
+ cflags = -1;
+ cKey.setAutoDelete( TRUE );
+ cachedFormat = 0;
+}
+
+QTextFormatCollection::~QTextFormatCollection()
+{
+ delete defFormat;
+}
+
+QTextFormat *QTextFormatCollection::format( QTextFormat *f )
+{
+ if ( f->parent() == this || f == defFormat ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "need '%s', best case!", f->key().latin1() );
+#endif
+ lastFormat = f;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if ( f == lastFormat || ( lastFormat && f->key() == lastFormat->key() ) ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "need '%s', good case!", f->key().latin1() );
+#endif
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ QTextFormat *fm = cKey.find( f->key() );
+ if ( fm ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "need '%s', normal case!", f->key().latin1() );
+#endif
+ lastFormat = fm;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if ( f->key() == defFormat->key() )
+ return defFormat;
+
+#ifdef DEBUG_COLLECTION
+ qDebug( "need '%s', worst case!", f->key().latin1() );
+#endif
+ lastFormat = createFormat( *f );
+ lastFormat->collection = this;
+ cKey.insert( lastFormat->key(), lastFormat );
+ return lastFormat;
+}
+
+QTextFormat *QTextFormatCollection::format( QTextFormat *of, QTextFormat *nf, int flags )
+{
+ if ( cres && kof == of->key() && knf == nf->key() && cflags == flags ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "mix of '%s' and '%s, best case!", of->key().latin1(), nf->key().latin1() );
+#endif
+ cres->addRef();
+ return cres;
+ }
+
+ cres = createFormat( *of );
+ kof = of->key();
+ knf = nf->key();
+ cflags = flags;
+ if ( flags & QTextFormat::Bold )
+ cres->fn.setBold( nf->fn.bold() );
+ if ( flags & QTextFormat::Italic )
+ cres->fn.setItalic( nf->fn.italic() );
+ if ( flags & QTextFormat::Underline )
+ cres->fn.setUnderline( nf->fn.underline() );
+ if ( flags & QTextFormat::Family )
+ cres->fn.setFamily( nf->fn.family() );
+ if ( flags & QTextFormat::Size ) {
+ if ( of->usePixelSizes )
+ cres->fn.setPixelSize( nf->fn.pixelSize() );
+ else
+ cres->fn.setPointSize( nf->fn.pointSize() );
+ }
+ if ( flags & QTextFormat::Color )
+ cres->col = nf->col;
+ if ( flags & QTextFormat::Misspelled )
+ cres->missp = nf->missp;
+ if ( flags & QTextFormat::VAlign )
+ cres->ha = nf->ha;
+ cres->update();
+
+ QTextFormat *fm = cKey.find( cres->key() );
+ if ( !fm ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "mix of '%s' and '%s, worst case!", of->key().latin1(), nf->key().latin1() );
+#endif
+ cres->collection = this;
+ cKey.insert( cres->key(), cres );
+ } else {
+#ifdef DEBUG_COLLECTION
+ qDebug( "mix of '%s' and '%s, good case!", of->key().latin1(), nf->key().latin1() );
+#endif
+ delete cres;
+ cres = fm;
+ cres->addRef();
+ }
+
+ return cres;
+}
+
+QTextFormat *QTextFormatCollection::format( const QFont &f, const QColor &c )
+{
+ if ( cachedFormat && cfont == f && ccol == c ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "format of font and col '%s' - best case", cachedFormat->key().latin1() );
+#endif
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ QString key = QTextFormat::getKey( f, c, FALSE, QTextFormat::AlignNormal );
+ cachedFormat = cKey.find( key );
+ cfont = f;
+ ccol = c;
+
+ if ( cachedFormat ) {
+#ifdef DEBUG_COLLECTION
+ qDebug( "format of font and col '%s' - good case", cachedFormat->key().latin1() );
+#endif
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ if ( key == defFormat->key() )
+ return defFormat;
+
+ cachedFormat = createFormat( f, c );
+ cachedFormat->collection = this;
+ cKey.insert( cachedFormat->key(), cachedFormat );
+ if ( cachedFormat->key() != key )
+ qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1() );
+#ifdef DEBUG_COLLECTION
+ qDebug( "format of font and col '%s' - worst case", cachedFormat->key().latin1() );
+#endif
+ return cachedFormat;
+}
+
+void QTextFormatCollection::remove( QTextFormat *f )
+{
+ if ( lastFormat == f )
+ lastFormat = 0;
+ if ( cres == f )
+ cres = 0;
+ if ( cachedFormat == f )
+ cachedFormat = 0;
+ cKey.remove( f->key() );
+}
+
+void QTextFormatCollection::debug()
+{
+#ifdef DEBUG_COLLECTION
+ qDebug( "------------ QTextFormatCollection: debug --------------- BEGIN" );
+ QDictIterator<QTextFormat> it( cKey );
+ for ( ; it.current(); ++it ) {
+ qDebug( "format '%s' (%p): refcount: %d", it.current()->key().latin1(),
+ it.current(), it.current()->ref );
+ }
+ qDebug( "------------ QTextFormatCollection: debug --------------- END" );
+#endif
+}
+
+void QTextFormatCollection::updateStyles()
+{
+ QDictIterator<QTextFormat> it( cKey );
+ QTextFormat *f;
+ while ( ( f = it.current() ) ) {
+ ++it;
+ f->updateStyle();
+ }
+ updateKeys();
+}
+
+void QTextFormatCollection::updateFontSizes( int base, bool usePixels )
+{
+ QDictIterator<QTextFormat> it( cKey );
+ QTextFormat *f;
+ while ( ( f = it.current() ) ) {
+ ++it;
+ f->stdSize = base;
+ f->usePixelSizes = usePixels;
+ if ( usePixels )
+ f->fn.setPixelSize( f->stdSize );
+ else
+ f->fn.setPointSize( f->stdSize );
+ styleSheet()->scaleFont( f->fn, f->logicalFontSize );
+ f->update();
+ }
+ f = defFormat;
+ f->stdSize = base;
+ f->usePixelSizes = usePixels;
+ if ( usePixels )
+ f->fn.setPixelSize( f->stdSize );
+ else
+ f->fn.setPointSize( f->stdSize );
+ styleSheet()->scaleFont( f->fn, f->logicalFontSize );
+ f->update();
+ updateKeys();
+}
+
+void QTextFormatCollection::updateFontAttributes( const QFont &f, const QFont &old )
+{
+ QDictIterator<QTextFormat> it( cKey );
+ QTextFormat *fm;
+ while ( ( fm = it.current() ) ) {
+ ++it;
+ if ( fm->fn.family() == old.family() &&
+ fm->fn.weight() == old.weight() &&
+ fm->fn.italic() == old.italic() &&
+ fm->fn.underline() == old.underline() ) {
+ fm->fn.setFamily( f.family() );
+ fm->fn.setWeight( f.weight() );
+ fm->fn.setItalic( f.italic() );
+ fm->fn.setUnderline( f.underline() );
+ fm->update();
+ }
+ }
+ fm = defFormat;
+ if ( fm->fn.family() == old.family() &&
+ fm->fn.weight() == old.weight() &&
+ fm->fn.italic() == old.italic() &&
+ fm->fn.underline() == old.underline() ) {
+ fm->fn.setFamily( f.family() );
+ fm->fn.setWeight( f.weight() );
+ fm->fn.setItalic( f.italic() );
+ fm->fn.setUnderline( f.underline() );
+ fm->update();
+ }
+ updateKeys();
+}
+
+
+// the keys in cKey have changed, rebuild the hashtable
+void QTextFormatCollection::updateKeys()
+{
+ if ( cKey.isEmpty() )
+ return;
+ cKey.setAutoDelete( FALSE );
+ QTextFormat** formats = new QTextFormat*[ cKey.count() + 1];
+ QTextFormat **f = formats;
+ QDictIterator<QTextFormat> it( cKey );
+ while ( ( *f = it.current() ) ) {
+ ++it;
+ ++f;
+ }
+ cKey.clear();
+ for ( f = formats; *f; f++ )
+ cKey.insert( (*f)->key(), *f );
+ cKey.setAutoDelete( TRUE );
+}
+
+
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void QTextFormat::setBold( bool b )
+{
+ if ( b == fn.bold() )
+ return;
+ fn.setBold( b );
+ update();
+}
+
+void QTextFormat::setMisspelled( bool b )
+{
+ if ( b == (bool)missp )
+ return;
+ missp = b;
+ update();
+}
+
+void QTextFormat::setVAlign( VerticalAlignment a )
+{
+ if ( a == ha )
+ return;
+ ha = a;
+ update();
+}
+
+void QTextFormat::setItalic( bool b )
+{
+ if ( b == fn.italic() )
+ return;
+ fn.setItalic( b );
+ update();
+}
+
+void QTextFormat::setUnderline( bool b )
+{
+ if ( b == fn.underline() )
+ return;
+ fn.setUnderline( b );
+ update();
+}
+
+void QTextFormat::setFamily( const QString &f )
+{
+ if ( f == fn.family() )
+ return;
+ fn.setFamily( f );
+ update();
+}
+
+void QTextFormat::setPointSize( int s )
+{
+ if ( s == fn.pointSize() )
+ return;
+ fn.setPointSize( s );
+ usePixelSizes = FALSE;
+ update();
+}
+
+void QTextFormat::setFont( const QFont &f )
+{
+ if ( f == fn && !k.isEmpty() )
+ return;
+ fn = f;
+ update();
+}
+
+void QTextFormat::setColor( const QColor &c )
+{
+ if ( c == col )
+ return;
+ col = c;
+ update();
+}
+
+static int makeLogicFontSize( int s )
+{
+ int defSize = QApplication::font().pointSize();
+ if ( s < defSize - 4 )
+ return 1;
+ if ( s < defSize )
+ return 2;
+ if ( s < defSize + 4 )
+ return 3;
+ if ( s < defSize + 8 )
+ return 4;
+ if ( s < defSize + 12 )
+ return 5;
+ if (s < defSize + 16 )
+ return 6;
+ return 7;
+}
+
+static QTextFormat *defaultFormat = 0;
+
+QString QTextFormat::makeFormatChangeTags( QTextFormat *f, const QString& oldAnchorHref, const QString& anchorHref ) const
+{
+ if ( !defaultFormat ) // #### wrong, use the document's default format instead
+ defaultFormat = new QTextFormat( QApplication::font(),
+ QApplication::palette().color( QPalette::Active, QColorGroup::Text ) );
+
+ QString tag;
+ if ( f ) {
+ if ( f->font() != defaultFormat->font() ) {
+ if ( f->font().family() != defaultFormat->font().family()
+ || f->font().pointSize() != defaultFormat->font().pointSize()
+ || f->color().rgb() != defaultFormat->color().rgb() )
+ tag += "</font>";
+ if ( f->font().underline() && f->font().underline() != defaultFormat->font().underline() )
+ tag += "</u>";
+ if ( f->font().italic() && f->font().italic() != defaultFormat->font().italic() )
+ tag += "</i>";
+ if ( f->font().bold() && f->font().bold() != defaultFormat->font().bold() )
+ tag += "</b>";
+ }
+ if ( !oldAnchorHref.isEmpty() )
+ tag += "</a>";
+ }
+
+ if ( !anchorHref.isEmpty() )
+ tag += "<a href=\"" + anchorHref + "\">";
+
+ if ( font() != defaultFormat->font() ) {
+ if ( font().bold() && font().bold() != defaultFormat->font().bold() )
+ tag += "<b>";
+ if ( font().italic() && font().italic() != defaultFormat->font().italic() )
+ tag += "<i>";
+ if ( font().underline() && font().underline() != defaultFormat->font().underline() )
+ tag += "<u>";
+ }
+ if ( font() != defaultFormat->font()
+ || color().rgb() != defaultFormat->color().rgb() ) {
+ QString f;
+ if ( font().family() != defaultFormat->font().family() )
+ f +=" face=\"" + fn.family() + "\"";
+ if ( font().pointSize() != defaultFormat->font().pointSize() ) {
+ f +=" size=\"" + QString::number( makeLogicFontSize( fn.pointSize() ) ) + "\"";
+ f +=" style=\"font-size:" + QString::number( fn.pointSize() ) + "pt\"";
+ }
+ if ( color().rgb() != defaultFormat->color().rgb() )
+ f +=" color=\"" + col.name() + "\"";
+ if ( !f.isEmpty() )
+ tag += "<font" + f + ">";
+ }
+
+ return tag;
+}
+
+QString QTextFormat::makeFormatEndTags( const QString& anchorHref ) const
+{
+ if ( !defaultFormat )
+ defaultFormat = new QTextFormat( QApplication::font(),
+ QApplication::palette().color( QPalette::Active, QColorGroup::Text ) );
+
+ QString tag;
+ if ( font() != defaultFormat->font() ) {
+ if ( font().family() != defaultFormat->font().family()
+ || font().pointSize() != defaultFormat->font().pointSize()
+ || color().rgb() != defaultFormat->color().rgb() )
+ tag += "</font>";
+ if ( font().underline() && font().underline() != defaultFormat->font().underline() )
+ tag += "</u>";
+ if ( font().italic() && font().italic() != defaultFormat->font().italic() )
+ tag += "</i>";
+ if ( font().bold() && font().bold() != defaultFormat->font().bold() )
+ tag += "</b>";
+ }
+ if ( !anchorHref.isEmpty() )
+ tag += "</a>";
+ return tag;
+}
+
+QTextFormat QTextFormat::makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr ) const
+{
+ QTextFormat format(*this);
+ if ( style ) {
+ format.style = style->name();
+ if ( style->name() == "font") {
+ if ( attr.contains("color") ) {
+ QString s = attr["color"];
+ if ( !s.isEmpty() ) {
+ format.col.setNamedColor( s );
+ format.linkColor = FALSE;
+ }
+ }
+ if ( attr.contains("size") ) {
+ QString a = attr["size"];
+ int n = a.toInt();
+ if ( a[0] == '+' || a[0] == '-' )
+ n += format.logicalFontSize;
+ format.logicalFontSize = n;
+ if ( format.usePixelSizes )
+ format.fn.setPixelSize( format.stdSize );
+ else
+ format.fn.setPointSize( format.stdSize );
+ style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
+ }
+ if ( attr.contains("style" ) ) {
+ QString a = attr["style"];
+ if ( a.startsWith( "font-size:" ) ) {
+ QString s = a.mid( a.find( ':' ) + 1 );
+ int n = s.left( s.length() - 2 ).toInt();
+ format.logicalFontSize = 0;
+ if ( format.usePixelSizes )
+ format.fn.setPixelSize( n );
+ else
+ format.fn.setPointSize( n );
+ }
+ }
+ if ( attr.contains("face") ) {
+ QString a = attr["face"];
+ if ( a.contains(',') )
+ a = a.left( a.find(',') );
+ format.fn.setFamily( a );
+ }
+ } else {
+ if ( !style->isAnchor() && style->color().isValid() ) {
+ // the style is not an anchor and defines a color.
+ // It might be used inside an anchor and it should
+ // override the link color.
+ format.linkColor = FALSE;
+ }
+ switch ( style->verticalAlignment() ) {
+ case QStyleSheetItem::VAlignBaseline:
+ format.setVAlign( QTextFormat::AlignNormal );
+ break;
+ case QStyleSheetItem::VAlignSuper:
+ format.setVAlign( QTextFormat::AlignSuperScript );
+ break;
+ case QStyleSheetItem::VAlignSub:
+ format.setVAlign( QTextFormat::AlignSubScript );
+ break;
+ }
+
+ if ( style->fontWeight() != QStyleSheetItem::Undefined )
+ format.fn.setWeight( style->fontWeight() );
+ if ( style->fontSize() != QStyleSheetItem::Undefined ) {
+ format.fn.setPointSize( style->fontSize() );
+ } else if ( style->logicalFontSize() != QStyleSheetItem::Undefined ) {
+ format.logicalFontSize = style->logicalFontSize();
+ if ( format.usePixelSizes )
+ format.fn.setPixelSize( format.stdSize );
+ else
+ format.fn.setPointSize( format.stdSize );
+ style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
+ } else if ( style->logicalFontSizeStep() ) {
+ format.logicalFontSize += style->logicalFontSizeStep();
+ if ( format.usePixelSizes )
+ format.fn.setPixelSize( format.stdSize );
+ else
+ format.fn.setPointSize( format.stdSize );
+ style->styleSheet()->scaleFont( format.fn, format.logicalFontSize );
+ }
+ if ( !style->fontFamily().isEmpty() )
+ format.fn.setFamily( style->fontFamily() );
+ if ( style->color().isValid() )
+ format.col = style->color();
+ if ( style->definesFontItalic() )
+ format.fn.setItalic( style->fontItalic() );
+ if ( style->definesFontUnderline() )
+ format.fn.setUnderline( style->fontUnderline() );
+ }
+ }
+
+ format.update();
+ return format;
+}
+
+struct QPixmapInt
+{
+ QPixmapInt() : ref( 0 ) {}
+ QPixmap pm;
+ int ref;
+};
+
+static QMap<QString, QPixmapInt> *pixmap_map = 0;
+
+QTextImage::QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ QMimeSourceFactory &factory )
+ : QTextCustomItem( p )
+{
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + "new QTextImage (pappi: %p)", p );
+#endif
+
+ width = height = 0;
+ if ( attr.contains("width") )
+ width = attr["width"].toInt();
+ if ( attr.contains("height") )
+ height = attr["height"].toInt();
+
+ reg = 0;
+ QString imageName = attr["src"];
+
+ if (!imageName)
+ imageName = attr["source"];
+
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + " .." + imageName );
+#endif
+
+ if ( !imageName.isEmpty() ) {
+ imgId = QString( "%1,%2,%3,%4" ).arg( imageName ).arg( width ).arg( height ).arg( (ulong)&factory );
+ if ( !pixmap_map )
+ pixmap_map = new QMap<QString, QPixmapInt>;
+ if ( pixmap_map->contains( imgId ) ) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pm = pmi.pm;
+ pmi.ref++;
+ width = pm.width();
+ height = pm.height();
+ } else {
+ QImage img;
+ const QMimeSource* m =
+ factory.data( imageName, context );
+ if ( !m ) {
+ qWarning("QTextImage: no mimesource for %s", imageName.latin1() );
+ }
+ else {
+ if ( !QImageDrag::decode( m, img ) ) {
+ qWarning("QTextImage: cannot decode %s", imageName.latin1() );
+ }
+ }
+
+ if ( !img.isNull() ) {
+ if ( width == 0 ) {
+ width = img.width();
+ if ( height != 0 ) {
+ width = img.width() * height / img.height();
+ }
+ }
+ if ( height == 0 ) {
+ height = img.height();
+ if ( width != img.width() ) {
+ height = img.height() * width / img.width();
+ }
+ }
+ if ( img.width() != width || img.height() != height ){
+#ifndef QT_NO_IMAGE_SMOOTHSCALE
+ img = img.smoothScale(width, height);
+#endif
+ width = img.width();
+ height = img.height();
+ }
+ pm.convertFromImage( img );
+ }
+ if ( !pm.isNull() ) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pmi.pm = pm;
+ pmi.ref++;
+ }
+ }
+ if ( pm.mask() ) {
+ QRegion mask( *pm.mask() );
+ QRegion all( 0, 0, pm.width(), pm.height() );
+ reg = new QRegion( all.subtract( mask ) );
+ }
+ }
+
+ if ( pm.isNull() && (width*height)==0 )
+ width = height = 50;
+
+ place = PlaceInline;
+ if ( attr["align"] == "left" )
+ place = PlaceLeft;
+ else if ( attr["align"] == "right" )
+ place = PlaceRight;
+
+ tmpwidth = width;
+ tmpheight = height;
+
+ attributes = attr;
+}
+
+QTextImage::~QTextImage()
+{
+ if ( pixmap_map && pixmap_map->contains( imgId ) ) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pmi.ref--;
+ if ( !pmi.ref ) {
+ pixmap_map->remove( imgId );
+ if ( pixmap_map->isEmpty() ) {
+ delete pixmap_map;
+ pixmap_map = 0;
+ }
+ }
+ }
+}
+
+QString QTextImage::richText() const
+{
+ QString s;
+ s += "<img ";
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for ( ; it != attributes.end(); ++it )
+ s += it.key() + "=" + *it + " ";
+ s += ">";
+ return s;
+}
+
+void QTextImage::adjustToPainter( QPainter* p )
+{
+ width = scale( tmpwidth, p );
+ height = scale( tmpheight, p );
+}
+
+#if !defined(Q_WS_X11)
+#include <qbitmap.h>
+#include "qcleanuphandler.h"
+static QPixmap *qrt_selection = 0;
+static QSingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
+static void qrt_createSelectionPixmap( const QColorGroup &cg )
+{
+ qrt_selection = new QPixmap( 2, 2 );
+ qrt_cleanup_pixmap.set( &qrt_selection );
+ qrt_selection->fill( Qt::color0 );
+ QBitmap m( 2, 2 );
+ m.fill( Qt::color1 );
+ QPainter p( &m );
+ p.setPen( Qt::color0 );
+ for ( int j = 0; j < 2; ++j ) {
+ p.drawPoint( j % 2, j );
+ }
+ p.end();
+ qrt_selection->setMask( m );
+ qrt_selection->fill( cg.highlight() );
+}
+#endif
+
+void QTextImage::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
+{
+ if ( placement() != PlaceInline ) {
+ x = xpos;
+ y = ypos;
+ }
+
+ if ( pm.isNull() ) {
+ p->fillRect( x , y, width, height, cg.dark() );
+ return;
+ }
+
+ if ( is_printer( p ) ) {
+ p->drawPixmap( x, y, pm );
+ return;
+ }
+
+ if ( placement() != PlaceInline && !QRect( xpos, ypos, width, height ).intersects( QRect( cx, cy, cw, ch ) ) )
+ return;
+
+ if ( placement() == PlaceInline )
+ p->drawPixmap( x , y, pm );
+ else
+ p->drawPixmap( cx , cy, pm, cx - x, cy - y, cw, ch );
+
+ if ( selected && placement() == PlaceInline && is_printer( p ) ) {
+#if defined(Q_WS_X11)
+ p->fillRect( QRect( QPoint( x, y ), pm.size() ), QBrush( cg.highlight(), QBrush::Dense4Pattern) );
+#else // in WIN32 Dense4Pattern doesn't work correctly (transparency problem), so work around it
+ if ( !qrt_selection )
+ qrt_createSelectionPixmap( cg );
+ p->drawTiledPixmap( x, y, pm.width(), pm.height(), *qrt_selection );
+#endif
+ }
+}
+
+void QTextHorizontalLine::adjustToPainter( QPainter* p )
+{
+ height = scale( tmpheight, p );
+}
+
+
+QTextHorizontalLine::QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr,
+ const QString &,
+ QMimeSourceFactory & )
+ : QTextCustomItem( p )
+{
+ height = tmpheight = 8;
+ if ( attr.find( "color" ) != attr.end() )
+ color = QColor( *attr.find( "color" ) );
+}
+
+QTextHorizontalLine::~QTextHorizontalLine()
+{
+}
+
+QString QTextHorizontalLine::richText() const
+{
+ return "<hr>";
+}
+
+void QTextHorizontalLine::draw( QPainter* p, int x, int y, int , int , int , int , const QColorGroup& cg, bool selected )
+{
+ QRect r( x, y, width, height);
+ if ( is_printer( p ) ) {
+ QPen oldPen = p->pen();
+ if ( !color.isValid() )
+ p->setPen( QPen( cg.text(), height/8 ) );
+ else
+ p->setPen( QPen( color, height/8 ) );
+ p->drawLine( r.left()-1, y + height / 2, r.right() + 1, y + height / 2 );
+ p->setPen( oldPen );
+ } else {
+ QColorGroup g( cg );
+ if ( color.isValid() )
+ g.setColor( QColorGroup::Dark, color );
+ if ( selected )
+ p->fillRect( r.left(), y, r.right(), y + height, g.highlight() );
+ qDrawShadeLine( p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, g, TRUE, height / 8 );
+ }
+}
+
+
+/*****************************************************************/
+// Small set of utility functions to make the parser a bit simpler
+//
+
+bool QTextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
+{
+ if ( pos >= length )
+ return FALSE;
+ return doc[ pos ].lower() == c.lower();
+}
+
+bool QTextDocument::hasPrefix( const QChar* doc, int length, int pos, const QString& s )
+{
+ if ( pos + (int) s.length() >= length )
+ return FALSE;
+ for ( int i = 0; i < (int)s.length(); i++ ) {
+ if ( doc[ pos + i ].lower() != s[ i ].lower() )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool qt_is_cell_in_use( QPtrList<QTextTableCell>& cells, int row, int col )
+{
+ for ( QTextTableCell* c = cells.first(); c; c = cells.next() ) {
+ if ( row >= c->row() && row < c->row() + c->rowspan()
+ && col >= c->column() && col < c->column() + c->colspan() )
+ return TRUE;
+ }
+ return FALSE;
+}
+
+QTextCustomItem* QTextDocument::parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt,
+ const QChar* doc, int length, int& pos, QTextParag *curpar )
+{
+
+ QTextTable* table = new QTextTable( this, attr );
+ int row = -1;
+ int col = -1;
+
+ QString rowbgcolor;
+ QString rowalign;
+ QString tablebgcolor = attr["bgcolor"];
+
+ QPtrList<QTextTableCell> multicells;
+
+ QString tagname;
+ (void) eatSpace(doc, length, pos);
+ while ( pos < length) {
+ if (hasPrefix(doc, length, pos, QChar('<')) ){
+ if (hasPrefix(doc, length, pos+1, QChar('/'))) {
+ tagname = parseCloseTag( doc, length, pos );
+ if ( tagname == "table" ) {
+#if defined(PARSER_DEBUG)
+ debug_indent.remove( debug_indent.length() - 3, 2 );
+#endif
+ return table;
+ }
+ } else {
+ QMap<QString, QString> attr2;
+ bool emptyTag = FALSE;
+ tagname = parseOpenTag( doc, length, pos, attr2, emptyTag );
+ if ( tagname == "tr" ) {
+ rowbgcolor = attr2["bgcolor"];
+ rowalign = attr2["align"];
+ row++;
+ col = -1;
+ }
+ else if ( tagname == "td" || tagname == "th" ) {
+ col++;
+ while ( qt_is_cell_in_use( multicells, row, col ) ) {
+ col++;
+ }
+
+ if ( row >= 0 && col >= 0 ) {
+ const QStyleSheetItem* s = sheet_->item(tagname);
+ if ( !attr2.contains("bgcolor") ) {
+ if (!rowbgcolor.isEmpty() )
+ attr2["bgcolor"] = rowbgcolor;
+ else if (!tablebgcolor.isEmpty() )
+ attr2["bgcolor"] = tablebgcolor;
+ }
+ if ( !attr2.contains("align") ) {
+ if (!rowalign.isEmpty() )
+ attr2["align"] = rowalign;
+ }
+
+ // extract the cell contents
+ int end = pos;
+ while ( end < length
+ && !hasPrefix( doc, length, end, "</td")
+ && !hasPrefix( doc, length, end, "<td")
+ && !hasPrefix( doc, length, end, "</th")
+ && !hasPrefix( doc, length, end, "<th")
+ && !hasPrefix( doc, length, end, "<td")
+ && !hasPrefix( doc, length, end, "</tr")
+ && !hasPrefix( doc, length, end, "<tr")
+ && !hasPrefix( doc, length, end, "</table") ) {
+ if ( hasPrefix( doc, length, end, "<table" ) ) { // nested table
+ int nested = 1;
+ ++end;
+ while ( end < length && nested != 0 ) {
+ if ( hasPrefix( doc, length, end, "</table" ) )
+ nested--;
+ if ( hasPrefix( doc, length, end, "<table" ) )
+ nested++;
+ end++;
+ }
+ }
+ end++;
+ }
+ QTextTableCell* cell = new QTextTableCell( table, row, col,
+ attr2, s, fmt.makeTextFormat( s, attr2 ),
+ contxt, *factory_, sheet_,
+ QString( doc, length).mid( pos, end - pos ) );
+ cell->richText()->parParag = curpar;
+ if ( cell->colspan() > 1 || cell->rowspan() > 1 )
+ multicells.append( cell );
+ col += cell->colspan()-1;
+ pos = end;
+ }
+ }
+ }
+
+ } else {
+ ++pos;
+ }
+ }
+#if defined(PARSER_DEBUG)
+ debug_indent.remove( debug_indent.length() - 3, 2 );
+#endif
+ return table;
+}
+
+bool QTextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp )
+{
+ int old_pos = pos;
+ while (pos < length && doc[pos].isSpace() && ( includeNbsp || (doc[pos] != QChar::nbsp ) ) )
+ pos++;
+ return old_pos < pos;
+}
+
+bool QTextDocument::eat(const QChar* doc, int length, int& pos, QChar c)
+{
+ bool ok = pos < length && doc[pos] == c;
+ if ( ok )
+ pos++;
+ return ok;
+}
+/*****************************************************************/
+
+struct Entity {
+ const char * name;
+ Q_UINT16 code;
+};
+
+static const Entity entitylist [] = {
+ { "AElig", 0x00c6 },
+ { "Aacute", 0x00c1 },
+ { "Acirc", 0x00c2 },
+ { "Agrave", 0x00c0 },
+ { "Alpha", 0x0391 },
+ { "AMP", 38 },
+ { "Aring", 0x00c5 },
+ { "Atilde", 0x00c3 },
+ { "Auml", 0x00c4 },
+ { "Beta", 0x0392 },
+ { "Ccedil", 0x00c7 },
+ { "Chi", 0x03a7 },
+ { "Dagger", 0x2021 },
+ { "Delta", 0x0394 },
+ { "ETH", 0x00d0 },
+ { "Eacute", 0x00c9 },
+ { "Ecirc", 0x00ca },
+ { "Egrave", 0x00c8 },
+ { "Epsilon", 0x0395 },
+ { "Eta", 0x0397 },
+ { "Euml", 0x00cb },
+ { "Gamma", 0x0393 },
+ { "GT", 62 },
+ { "Iacute", 0x00cd },
+ { "Icirc", 0x00ce },
+ { "Igrave", 0x00cc },
+ { "Iota", 0x0399 },
+ { "Iuml", 0x00cf },
+ { "Kappa", 0x039a },
+ { "Lambda", 0x039b },
+ { "LT", 60 },
+ { "Mu", 0x039c },
+ { "Ntilde", 0x00d1 },
+ { "Nu", 0x039d },
+ { "OElig", 0x0152 },
+ { "Oacute", 0x00d3 },
+ { "Ocirc", 0x00d4 },
+ { "Ograve", 0x00d2 },
+ { "Omega", 0x03a9 },
+ { "Omicron", 0x039f },
+ { "Oslash", 0x00d8 },
+ { "Otilde", 0x00d5 },
+ { "Ouml", 0x00d6 },
+ { "Phi", 0x03a6 },
+ { "Pi", 0x03a0 },
+ { "Prime", 0x2033 },
+ { "Psi", 0x03a8 },
+ { "QUOT", 34 },
+ { "Rho", 0x03a1 },
+ { "Scaron", 0x0160 },
+ { "Sigma", 0x03a3 },
+ { "THORN", 0x00de },
+ { "Tau", 0x03a4 },
+ { "Theta", 0x0398 },
+ { "Uacute", 0x00da },
+ { "Ucirc", 0x00db },
+ { "Ugrave", 0x00d9 },
+ { "Upsilon", 0x03a5 },
+ { "Uuml", 0x00dc },
+ { "Xi", 0x039e },
+ { "Yacute", 0x00dd },
+ { "Yuml", 0x0178 },
+ { "Zeta", 0x0396 },
+ { "aacute", 0x00e1 },
+ { "acirc", 0x00e2 },
+ { "acute", 0x00b4 },
+ { "aelig", 0x00e6 },
+ { "agrave", 0x00e0 },
+ { "alefsym", 0x2135 },
+ { "alpha", 0x03b1 },
+ { "amp", 38 },
+ { "and", 0x22a5 },
+ { "ang", 0x2220 },
+ { "apos", 0x0027 },
+ { "aring", 0x00e5 },
+ { "asymp", 0x2248 },
+ { "atilde", 0x00e3 },
+ { "auml", 0x00e4 },
+ { "bdquo", 0x201e },
+ { "beta", 0x03b2 },
+ { "brvbar", 0x00a6 },
+ { "bull", 0x2022 },
+ { "cap", 0x2229 },
+ { "ccedil", 0x00e7 },
+ { "cedil", 0x00b8 },
+ { "cent", 0x00a2 },
+ { "chi", 0x03c7 },
+ { "circ", 0x02c6 },
+ { "clubs", 0x2663 },
+ { "cong", 0x2245 },
+ { "copy", 0x00a9 },
+ { "crarr", 0x21b5 },
+ { "cup", 0x222a },
+ { "curren", 0x00a4 },
+ { "dArr", 0x21d3 },
+ { "dagger", 0x2020 },
+ { "darr", 0x2193 },
+ { "deg", 0x00b0 },
+ { "delta", 0x03b4 },
+ { "diams", 0x2666 },
+ { "divide", 0x00f7 },
+ { "eacute", 0x00e9 },
+ { "ecirc", 0x00ea },
+ { "egrave", 0x00e8 },
+ { "empty", 0x2205 },
+ { "emsp", 0x2003 },
+ { "ensp", 0x2002 },
+ { "epsilon", 0x03b5 },
+ { "equiv", 0x2261 },
+ { "eta", 0x03b7 },
+ { "eth", 0x00f0 },
+ { "euml", 0x00eb },
+ { "euro", 0x20ac },
+ { "exist", 0x2203 },
+ { "fnof", 0x0192 },
+ { "forall", 0x2200 },
+ { "frac12", 0x00bd },
+ { "frac14", 0x00bc },
+ { "frac34", 0x00be },
+ { "frasl", 0x2044 },
+ { "gamma", 0x03b3 },
+ { "ge", 0x2265 },
+ { "gt", 62 },
+ { "hArr", 0x21d4 },
+ { "harr", 0x2194 },
+ { "hearts", 0x2665 },
+ { "hellip", 0x2026 },
+ { "iacute", 0x00ed },
+ { "icirc", 0x00ee },
+ { "iexcl", 0x00a1 },
+ { "igrave", 0x00ec },
+ { "image", 0x2111 },
+ { "infin", 0x221e },
+ { "int", 0x222b },
+ { "iota", 0x03b9 },
+ { "iquest", 0x00bf },
+ { "isin", 0x2208 },
+ { "iuml", 0x00ef },
+ { "kappa", 0x03ba },
+ { "lArr", 0x21d0 },
+ { "lambda", 0x03bb },
+ { "lang", 0x2329 },
+ { "laquo", 0x00ab },
+ { "larr", 0x2190 },
+ { "lceil", 0x2308 },
+ { "ldquo", 0x201c },
+ { "le", 0x2264 },
+ { "lfloor", 0x230a },
+ { "lowast", 0x2217 },
+ { "loz", 0x25ca },
+ { "lrm", 0x200e },
+ { "lsaquo", 0x2039 },
+ { "lsquo", 0x2018 },
+ { "lt", 60 },
+ { "macr", 0x00af },
+ { "mdash", 0x2014 },
+ { "micro", 0x00b5 },
+ { "middot", 0x00b7 },
+ { "minus", 0x2212 },
+ { "mu", 0x03bc },
+ { "nabla", 0x2207 },
+ { "nbsp", 0x00a0 },
+ { "ndash", 0x2013 },
+ { "ne", 0x2260 },
+ { "ni", 0x220b },
+ { "not", 0x00ac },
+ { "notin", 0x2209 },
+ { "nsub", 0x2284 },
+ { "ntilde", 0x00f1 },
+ { "nu", 0x03bd },
+ { "oacute", 0x00f3 },
+ { "ocirc", 0x00f4 },
+ { "oelig", 0x0153 },
+ { "ograve", 0x00f2 },
+ { "oline", 0x203e },
+ { "omega", 0x03c9 },
+ { "omicron", 0x03bf },
+ { "oplus", 0x2295 },
+ { "or", 0x22a6 },
+ { "ordf", 0x00aa },
+ { "ordm", 0x00ba },
+ { "oslash", 0x00f8 },
+ { "otilde", 0x00f5 },
+ { "otimes", 0x2297 },
+ { "ouml", 0x00f6 },
+ { "para", 0x00b6 },
+ { "part", 0x2202 },
+ { "percnt", 0x0025 },
+ { "permil", 0x2030 },
+ { "perp", 0x22a5 },
+ { "phi", 0x03c6 },
+ { "pi", 0x03c0 },
+ { "piv", 0x03d6 },
+ { "plusmn", 0x00b1 },
+ { "pound", 0x00a3 },
+ { "prime", 0x2032 },
+ { "prod", 0x220f },
+ { "prop", 0x221d },
+ { "psi", 0x03c8 },
+ { "quot", 34 },
+ { "rArr", 0x21d2 },
+ { "radic", 0x221a },
+ { "rang", 0x232a },
+ { "raquo", 0x00bb },
+ { "rarr", 0x2192 },
+ { "rceil", 0x2309 },
+ { "rdquo", 0x201d },
+ { "real", 0x211c },
+ { "reg", 0x00ae },
+ { "rfloor", 0x230b },
+ { "rho", 0x03c1 },
+ { "rlm", 0x200f },
+ { "rsaquo", 0x203a },
+ { "rsquo", 0x2019 },
+ { "sbquo", 0x201a },
+ { "scaron", 0x0161 },
+ { "sdot", 0x22c5 },
+ { "sect", 0x00a7 },
+ { "shy", 0x00ad },
+ { "sigma", 0x03c3 },
+ { "sigmaf", 0x03c2 },
+ { "sim", 0x223c },
+ { "spades", 0x2660 },
+ { "sub", 0x2282 },
+ { "sube", 0x2286 },
+ { "sum", 0x2211 },
+ { "sup1", 0x00b9 },
+ { "sup2", 0x00b2 },
+ { "sup3", 0x00b3 },
+ { "sup", 0x2283 },
+ { "supe", 0x2287 },
+ { "szlig", 0x00df },
+ { "tau", 0x03c4 },
+ { "there4", 0x2234 },
+ { "theta", 0x03b8 },
+ { "thetasym", 0x03d1 },
+ { "thinsp", 0x2009 },
+ { "thorn", 0x00fe },
+ { "tilde", 0x02dc },
+ { "times", 0x00d7 },
+ { "trade", 0x2122 },
+ { "uArr", 0x21d1 },
+ { "uacute", 0x00fa },
+ { "uarr", 0x2191 },
+ { "ucirc", 0x00fb },
+ { "ugrave", 0x00f9 },
+ { "uml", 0x00a8 },
+ { "upsih", 0x03d2 },
+ { "upsilon", 0x03c5 },
+ { "uuml", 0x00fc },
+ { "weierp", 0x2118 },
+ { "xi", 0x03be },
+ { "yacute", 0x00fd },
+ { "yen", 0x00a5 },
+ { "yuml", 0x00ff },
+ { "zeta", 0x03b6 },
+ { "zwj", 0x200d },
+ { "zwnj", 0x200c },
+ { "", 0x0000 }
+};
+
+
+
+
+
+static QMap<QCString, QChar> *html_map = 0;
+static void qt_cleanup_html_map()
+{
+ delete html_map;
+ html_map = 0;
+}
+
+static QMap<QCString, QChar> *htmlMap()
+{
+ if ( !html_map ) {
+ html_map = new QMap<QCString, QChar>;
+ qAddPostRoutine( qt_cleanup_html_map );
+
+ const Entity *ent = entitylist;
+ while( ent->code ) {
+ html_map->insert( ent->name, QChar(ent->code) );
+ ent++;
+ }
+ }
+ return html_map;
+}
+
+QChar QTextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
+{
+ QCString s;
+ pos++;
+ int recoverpos = pos;
+ while ( pos < length && doc[pos] != ';' && !doc[pos].isSpace() && pos < recoverpos + 6) {
+ s += doc[pos];
+ pos++;
+ }
+ if (doc[pos] != ';' && !doc[pos].isSpace() ) {
+ pos = recoverpos;
+ return '&';
+ }
+ pos++;
+
+ if ( s.length() > 1 && s[0] == '#') {
+ int num = s.mid(1).toInt();
+ if ( num == 151 ) // ### hack for designer manual
+ return '-';
+ return num;
+ }
+
+ QMap<QCString, QChar>::Iterator it = htmlMap()->find(s);
+ if ( it != htmlMap()->end() ) {
+ return *it;
+ }
+
+ pos = recoverpos;
+ return '&';
+}
+
+QString QTextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
+{
+ QString s;
+
+ if (doc[pos] == '"') {
+ pos++;
+ while ( pos < length && doc[pos] != '"' ) {
+ s += doc[pos];
+ pos++;
+ }
+ eat(doc, length, pos, '"');
+ } else {
+ static QString term = QString::fromLatin1("/>");
+ while( pos < length &&
+ (doc[pos] != '>' && !hasPrefix( doc, length, pos, term))
+ && doc[pos] != '<'
+ && doc[pos] != '='
+ && !doc[pos].isSpace())
+ {
+ if ( doc[pos] == '&')
+ s += parseHTMLSpecialChar( doc, length, pos );
+ else {
+ s += doc[pos];
+ pos++;
+ }
+ }
+ if (lower)
+ s = s.lower();
+ }
+ return s;
+}
+
+QChar QTextDocument::parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm )
+{
+ if ( pos >= length )
+ return QChar::null;
+
+ QChar c = doc[pos++];
+
+ if (c == '<' )
+ return QChar::null;
+
+ if ( c.isSpace() && c != QChar::nbsp ) {
+ if ( wsm == QStyleSheetItem::WhiteSpacePre ) {
+ if ( c == ' ' )
+ return QChar::nbsp;
+ else
+ return c;
+ } else if ( wsm == QStyleSheetItem_WhiteSpaceNoCompression ) {
+ return c;
+ } else if ( wsm == QStyleSheetItem_WhiteSpaceNormalWithNewlines ) {
+ if ( c == '\n' )
+ return c;
+ while ( pos< length &&
+ doc[pos].isSpace() && doc[pos] != QChar::nbsp && doc[pos] != '\n' )
+ pos++;
+ return ' ';
+ } else { // non-pre mode: collapse whitespace except nbsp
+ while ( pos< length &&
+ doc[pos].isSpace() && doc[pos] != QChar::nbsp )
+ pos++;
+ if ( wsm == QStyleSheetItem::WhiteSpaceNoWrap )
+ return QChar::nbsp;
+ else
+ return ' ';
+ }
+ }
+ else if ( c == '&' )
+ return parseHTMLSpecialChar( doc, length, --pos );
+ else
+ return c;
+}
+
+QString QTextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
+ QMap<QString, QString> &attr, bool& emptyTag)
+{
+ emptyTag = FALSE;
+ pos++;
+ if ( hasPrefix(doc, length, pos, '!') ) {
+ if ( hasPrefix( doc, length, pos+1, "--")) {
+ pos += 3;
+ // eat comments
+ QString pref = QString::fromLatin1("-->");
+ while ( !hasPrefix(doc, length, pos, pref ) && pos < length )
+ pos++;
+ if ( hasPrefix(doc, length, pos, pref ) ) {
+ pos += 3;
+ eatSpace(doc, length, pos, TRUE);
+ }
+ emptyTag = TRUE;
+ return QString::null;
+ }
+ else {
+ // eat strange internal tags
+ while ( !hasPrefix(doc, length, pos, '>') && pos < length )
+ pos++;
+ if ( hasPrefix(doc, length, pos, '>') ) {
+ pos++;
+ eatSpace(doc, length, pos, TRUE);
+ }
+ return QString::null;
+ }
+ }
+
+ QString tag = parseWord(doc, length, pos );
+ eatSpace(doc, length, pos, TRUE);
+ static QString term = QString::fromLatin1("/>");
+ static QString s_TRUE = QString::fromLatin1("TRUE");
+
+ while (doc[pos] != '>' && ! (emptyTag = hasPrefix(doc, length, pos, term) )) {
+ QString key = parseWord(doc, length, pos );
+ eatSpace(doc, length, pos, TRUE);
+ if ( key.isEmpty()) {
+ // error recovery
+ while ( pos < length && doc[pos] != '>' )
+ pos++;
+ break;
+ }
+ QString value;
+ if (hasPrefix(doc, length, pos, '=') ){
+ pos++;
+ eatSpace(doc, length, pos);
+ value = parseWord(doc, length, pos, FALSE);
+ }
+ else
+ value = s_TRUE;
+ attr.insert(key.lower(), value );
+ eatSpace(doc, length, pos, TRUE);
+ }
+
+ if (emptyTag) {
+ eat(doc, length, pos, '/');
+ eat(doc, length, pos, '>');
+ }
+ else
+ eat(doc, length, pos, '>');
+
+ return tag;
+}
+
+QString QTextDocument::parseCloseTag( const QChar* doc, int length, int& pos )
+{
+ pos++;
+ pos++;
+ QString tag = parseWord(doc, length, pos );
+ eatSpace(doc, length, pos, TRUE);
+ eat(doc, length, pos, '>');
+ return tag;
+}
+
+QTextFlow::QTextFlow()
+{
+ w = pagesize = 0;
+ leftItems.setAutoDelete( FALSE );
+ rightItems.setAutoDelete( FALSE );
+}
+
+QTextFlow::~QTextFlow()
+{
+}
+
+void QTextFlow::clear()
+{
+ leftItems.clear();
+ rightItems.clear();
+}
+
+void QTextFlow::setWidth( int width )
+{
+ w = width;
+}
+
+int QTextFlow::adjustLMargin( int yp, int, int margin, int space )
+{
+ for ( QTextCustomItem* item = leftItems.first(); item; item = leftItems.next() ) {
+ if ( item->ypos == -1 )
+ continue;
+ if ( yp >= item->ypos && yp < item->ypos + item->height )
+ margin = QMAX( margin, item->xpos + item->width + space );
+ }
+ return margin;
+}
+
+int QTextFlow::adjustRMargin( int yp, int, int margin, int space )
+{
+ for ( QTextCustomItem* item = rightItems.first(); item; item = rightItems.next() ) {
+ if ( item->ypos == -1 )
+ continue;
+ if ( yp >= item->ypos && yp < item->ypos + item->height )
+ margin = QMAX( margin, w - item->xpos - space );
+ }
+ return margin;
+}
+
+
+int QTextFlow::adjustFlow( int y, int /*w*/, int h )
+{
+ if ( pagesize > 0 ) { // check pages
+ int yinpage = y % pagesize;
+ if ( yinpage <= border_tolerance )
+ return border_tolerance - yinpage;
+ else
+ if ( yinpage + h > pagesize - border_tolerance )
+ return ( pagesize - yinpage ) + border_tolerance;
+ }
+ return 0;
+}
+
+void QTextFlow::unregisterFloatingItem( QTextCustomItem* item )
+{
+ leftItems.removeRef( item );
+ rightItems.removeRef( item );
+}
+
+void QTextFlow::registerFloatingItem( QTextCustomItem* item )
+{
+ if ( item->placement() == QTextCustomItem::PlaceRight ) {
+ if ( !rightItems.contains( item ) )
+ rightItems.append( item );
+ } else if ( item->placement() == QTextCustomItem::PlaceLeft &&
+ !leftItems.contains( item ) ) {
+ leftItems.append( item );
+ }
+}
+
+QRect QTextFlow::boundingRect() const
+{
+ QRect br;
+ QPtrListIterator<QTextCustomItem> l( leftItems );
+ while( l.current() ) {
+ br = br.unite( l.current()->geometry() );
+ ++l;
+ }
+ QPtrListIterator<QTextCustomItem> r( rightItems );
+ while( r.current() ) {
+ br = br.unite( r.current()->geometry() );
+ ++r;
+ }
+ return br;
+}
+
+
+void QTextFlow::drawFloatingItems( QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
+{
+ QTextCustomItem *item;
+ for ( item = leftItems.first(); item; item = leftItems.next() ) {
+ if ( item->xpos == -1 || item->ypos == -1 )
+ continue;
+ item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
+ }
+
+ for ( item = rightItems.first(); item; item = rightItems.next() ) {
+ if ( item->xpos == -1 || item->ypos == -1 )
+ continue;
+ item->draw( p, item->xpos, item->ypos, cx, cy, cw, ch, cg, selected );
+ }
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void QTextCustomItem::pageBreak( int /*y*/ , QTextFlow* /*flow*/ )
+{
+}
+
+QTextTable::QTextTable( QTextDocument *p, const QMap<QString, QString> & attr )
+ : QTextCustomItem( p )
+{
+ cells.setAutoDelete( FALSE );
+#if defined(PARSER_DEBUG)
+ debug_indent += "\t";
+ qDebug( debug_indent + "new QTextTable (%p)", this );
+ debug_indent += "\t";
+#endif
+ cellspacing = 2;
+ if ( attr.contains("cellspacing") )
+ cellspacing = attr["cellspacing"].toInt();
+ cellpadding = 1;
+ if ( attr.contains("cellpadding") )
+ cellpadding = attr["cellpadding"].toInt();
+ border = innerborder = 0;
+ if ( attr.contains("border" ) ) {
+ QString s( attr["border"] );
+ if ( s == "TRUE" )
+ border = 1;
+ else
+ border = attr["border"].toInt();
+ }
+ us_b = border;
+
+ innerborder = us_ib = border ? 1 : 0;
+
+ if ( border )
+ cellspacing += 2;
+
+ us_ib = innerborder;
+ us_cs = cellspacing;
+ us_cp = cellpadding;
+ outerborder = cellspacing + border;
+ us_ob = outerborder;
+ layout = new QGridLayout( 1, 1, cellspacing );
+
+ fixwidth = 0;
+ stretch = 0;
+ if ( attr.contains("width") ) {
+ bool b;
+ QString s( attr["width"] );
+ int w = s.toInt( &b );
+ if ( b ) {
+ fixwidth = w;
+ } else {
+ s = s.stripWhiteSpace();
+ if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
+ stretch = s.left( s.length()-1).toInt();
+ }
+ }
+
+ place = PlaceInline;
+ if ( attr["align"] == "left" )
+ place = PlaceLeft;
+ else if ( attr["align"] == "right" )
+ place = PlaceRight;
+ cachewidth = 0;
+ attributes = attr;
+ pageBreakFor = -1;
+}
+
+QTextTable::~QTextTable()
+{
+ delete layout;
+}
+
+QString QTextTable::richText() const
+{
+ QString s;
+ s = "<table ";
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for ( ; it != attributes.end(); ++it )
+ s += it.key() + "=" + *it + " ";
+ s += ">\n";
+
+ int lastRow = -1;
+ bool needEnd = FALSE;
+ QPtrListIterator<QTextTableCell> it2( cells );
+ while ( it2.current() ) {
+ QTextTableCell *cell = it2.current();
+ ++it2;
+ if ( lastRow != cell->row() ) {
+ if ( lastRow != -1 )
+ s += "</tr>\n";
+ s += "<tr>";
+ lastRow = cell->row();
+ needEnd = TRUE;
+ }
+ s += "<td ";
+ it = cell->attributes.begin();
+ for ( ; it != cell->attributes.end(); ++it )
+ s += it.key() + "=" + *it + " ";
+ s += ">";
+ s += cell->richText()->richText();
+ s += "</td>";
+ }
+ if ( needEnd )
+ s += "</tr>\n";
+ s += "</table>\n";
+ return s;
+}
+
+void QTextTable::adjustToPainter( QPainter* p )
+{
+ cellspacing = scale( us_cs, p );
+ cellpadding = scale( us_cp, p );
+ border = scale( us_b , p );
+ innerborder = scale( us_ib, p );
+ outerborder = scale( us_ob ,p );
+ width = 0;
+ cachewidth = 0;
+ for ( QTextTableCell* cell = cells.first(); cell; cell = cells.next() )
+ cell->adjustToPainter( p );
+}
+
+void QTextTable::adjustCells( int y , int shift )
+{
+ QPtrListIterator<QTextTableCell> it( cells );
+ QTextTableCell* cell;
+ bool enlarge = FALSE;
+ while ( ( cell = it.current() ) ) {
+ ++it;
+ QRect r = cell->geometry();
+ if ( y <= r.top() ) {
+ r.moveBy(0, shift );
+ cell->setGeometry( r );
+ enlarge = TRUE;
+ } else if ( y <= r.bottom() ) {
+ r.rBottom() += shift;
+ cell->setGeometry( r );
+ enlarge = TRUE;
+ }
+ }
+ if ( enlarge )
+ height += shift;
+}
+
+void QTextTable::pageBreak( int yt, QTextFlow* flow )
+{
+ if ( flow->pageSize() <= 0 )
+ return;
+ if ( layout && pageBreakFor > 0 && pageBreakFor != yt ) {
+ layout->invalidate();
+ int h = layout->heightForWidth( width-2*outerborder );
+ layout->setGeometry( QRect(0, 0, width-2*outerborder, h) );
+ height = layout->geometry().height()+2*outerborder;
+ }
+ pageBreakFor = yt;
+ QPtrListIterator<QTextTableCell> it( cells );
+ QTextTableCell* cell;
+ while ( ( cell = it.current() ) ) {
+ ++it;
+ int y = yt + outerborder + cell->geometry().y();
+ int shift = flow->adjustFlow( y - cellspacing, width, cell->richText()->height() + 2*cellspacing );
+ adjustCells( y - outerborder - yt, shift );
+ }
+}
+
+
+void QTextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected )
+{
+ if ( placement() != PlaceInline ) {
+ x = xpos;
+ y = ypos;
+ }
+
+ for (QTextTableCell* cell = cells.first(); cell; cell = cells.next() ) {
+ if ( cx < 0 && cy < 0 ||
+ QRect( cx, cy, cw, ch ).intersects( QRect( x + outerborder + cell->geometry().x(),
+ y + outerborder + cell->geometry().y(),
+ cell->geometry().width(), cell->geometry().height() ) ) ) {
+ cell->draw( p, x+outerborder, y+outerborder, cx, cy, cw, ch, cg, selected );
+ if ( border ) {
+ QRect r( x+outerborder+cell->geometry().x() - innerborder,
+ y+outerborder+cell->geometry().y() - innerborder,
+ cell->geometry().width() + 2 * innerborder,
+ cell->geometry().height() + 2 * innerborder );
+ if ( is_printer( p ) ) {
+ QPen oldPen = p->pen();
+ QRect r2 = r;
+ r2.setLeft( r2.left() + innerborder/2 );
+ r2.setTop( r2.top() + innerborder/2 );
+ r2.setRight( r2.right() - innerborder/2 );
+ r2.setBottom( r2.bottom() - innerborder/2 );
+ p->setPen( QPen( cg.text(), innerborder ) );
+ p->drawRect( r2 );
+ p->setPen( oldPen );
+ } else {
+ int s = QMAX( cellspacing-2*innerborder, 0);
+ if ( s ) {
+ p->fillRect( r.left()-s, r.top(), s+1, r.height(), cg.button() );
+ p->fillRect( r.right(), r.top(), s+1, r.height(), cg.button() );
+ p->fillRect( r.left()-s, r.top()-s, r.width()+2*s, s, cg.button() );
+ p->fillRect( r.left()-s, r.bottom(), r.width()+2*s, s, cg.button() );
+ }
+ qDrawShadePanel( p, r, cg, TRUE, innerborder );
+ }
+ }
+ }
+ }
+ if ( border ) {
+ QRect r ( x, y, width, height );
+ if ( is_printer( p ) ) {
+ QRect r2 = r;
+ r2.setLeft( r2.left() + border/2 );
+ r2.setTop( r2.top() + border/2 );
+ r2.setRight( r2.right() - border/2 );
+ r2.setBottom( r2.bottom() - border/2 );
+ QPen oldPen = p->pen();
+ p->setPen( QPen( cg.text(), border ) );
+ p->drawRect( r2 );
+ p->setPen( oldPen );
+ } else {
+ int s = border+QMAX( cellspacing-2*innerborder, 0);
+ if ( s ) {
+ p->fillRect( r.left(), r.top(), s, r.height(), cg.button() );
+ p->fillRect( r.right()-s, r.top(), s, r.height(), cg.button() );
+ p->fillRect( r.left(), r.top(), r.width(), s, cg.button() );
+ p->fillRect( r.left(), r.bottom()-s, r.width(), s, cg.button() );
+ }
+ qDrawShadePanel( p, r, cg, FALSE, border );
+ }
+ }
+
+#if defined(DEBUG_TABLE_RENDERING)
+ p->save();
+ p->setPen( Qt::red );
+ p->drawRect( x, y, width, height );
+ p->restore();
+#endif
+}
+
+int QTextTable::minimumWidth() const
+{
+ return (layout ? layout->minimumSize().width() : 0) + 2 * outerborder;
+}
+
+void QTextTable::resize( int nwidth )
+{
+ if ( fixwidth && cachewidth != 0 )
+ return;
+ if ( nwidth == cachewidth )
+ return;
+
+
+ cachewidth = nwidth;
+ int w = nwidth;
+
+ format( w );
+
+ if ( stretch )
+ nwidth = nwidth * stretch / 100;
+
+ width = nwidth;
+ layout->invalidate();
+ int shw = layout->sizeHint().width() + 2*outerborder;
+ int mw = layout->minimumSize().width() + 2*outerborder;
+ if ( stretch )
+ width = QMAX( mw, nwidth );
+ else
+ width = QMAX( mw, QMIN( nwidth, shw ) );
+
+ if ( fixwidth )
+ width = fixwidth;
+
+ layout->invalidate();
+ mw = layout->minimumSize().width() + 2*outerborder;
+ width = QMAX( width, mw );
+
+ int h = layout->heightForWidth( width-2*outerborder );
+ layout->setGeometry( QRect(0, 0, width-2*outerborder, h) );
+ height = layout->geometry().height()+2*outerborder;
+}
+
+void QTextTable::format( int w )
+{
+ for ( int i = 0; i < (int)cells.count(); ++i ) {
+ QTextTableCell *cell = cells.at( i );
+ QRect r = cell->geometry();
+ r.setWidth( w - 2*outerborder );
+ cell->setGeometry( r );
+ }
+}
+
+void QTextTable::addCell( QTextTableCell* cell )
+{
+ cells.append( cell );
+ layout->addMultiCell( cell, cell->row(), cell->row() + cell->rowspan()-1,
+ cell->column(), cell->column() + cell->colspan()-1 );
+}
+
+bool QTextTable::enter( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, bool atEnd )
+{
+ currCell.remove( c );
+ if ( !atEnd )
+ return next( c, doc, parag, idx, ox, oy );
+ currCell.insert( c, cells.count() );
+ return prev( c, doc, parag, idx, ox, oy );
+}
+
+bool QTextTable::enterAt( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, const QPoint &pos )
+{
+ currCell.remove( c );
+ int lastCell = -1;
+ int lastY = -1;
+ int i;
+ for ( i = 0; i < (int)cells.count(); ++i ) {
+ QTextTableCell *cell = cells.at( i );
+ if ( !cell )
+ continue;
+ QRect r( cell->geometry().x(),
+ cell->geometry().y(),
+ cell->geometry().width() + 2 * innerborder + 2 * outerborder,
+ cell->geometry().height() + 2 * innerborder + 2 * outerborder );
+
+ if ( r.left() <= pos.x() && r.right() >= pos.x() ) {
+ if ( cell->geometry().y() > lastY ) {
+ lastCell = i;
+ lastY = cell->geometry().y();
+ }
+ if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) {
+ currCell.insert( c, i );
+ break;
+ }
+ }
+ }
+ if ( i == (int) cells.count() )
+ return FALSE; // no cell found
+
+ if ( currCell.find( c ) == currCell.end() ) {
+ if ( lastY != -1 )
+ currCell.insert( c, lastCell );
+ else
+ return FALSE;
+ }
+
+ QTextTableCell *cell = cells.at( *currCell.find( c ) );
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ parag = doc->firstParag();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return TRUE;
+}
+
+bool QTextTable::next( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ int cc = -1;
+ if ( currCell.find( c ) != currCell.end() )
+ cc = *currCell.find( c );
+ if ( cc > (int)cells.count() - 1 || cc < 0 )
+ cc = -1;
+ currCell.remove( c );
+ currCell.insert( c, ++cc );
+ if ( cc >= (int)cells.count() ) {
+ currCell.insert( c, 0 );
+ QTextCustomItem::next( c, doc, parag, idx, ox, oy );
+ QTextTableCell *cell = cells.first();
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ idx = -1;
+ return TRUE;
+ }
+
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ QTextTableCell *cell = cells.at( *currCell.find( c ) );
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ parag = doc->firstParag();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return TRUE;
+}
+
+bool QTextTable::prev( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ int cc = -1;
+ if ( currCell.find( c ) != currCell.end() )
+ cc = *currCell.find( c );
+ if ( cc > (int)cells.count() - 1 || cc < 0 )
+ cc = cells.count();
+ currCell.remove( c );
+ currCell.insert( c, --cc );
+ if ( cc < 0 ) {
+ currCell.insert( c, 0 );
+ QTextCustomItem::prev( c, doc, parag, idx, ox, oy );
+ QTextTableCell *cell = cells.first();
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ idx = -1;
+ return TRUE;
+ }
+
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ QTextTableCell *cell = cells.at( *currCell.find( c ) );
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ parag = doc->firstParag();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return TRUE;
+}
+
+bool QTextTable::down( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ QTextTableCell *cell = cells.at( *currCell.find( c ) );
+ if ( cell->row_ == layout->numRows() - 1 ) {
+ currCell.insert( c, 0 );
+ QTextCustomItem::down( c, doc, parag, idx, ox, oy );
+ QTextTableCell *cell = cells.first();
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ idx = -1;
+ return TRUE;
+ }
+
+ int oldRow = cell->row_;
+ int oldCol = cell->col_;
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ int cc = *currCell.find( c );
+ for ( int i = cc; i < (int)cells.count(); ++i ) {
+ cell = cells.at( i );
+ if ( cell->row_ > oldRow && cell->col_ == oldCol ) {
+ currCell.insert( c, i );
+ break;
+ }
+ }
+ doc = cell->richText();
+ if ( !cell )
+ return FALSE;
+ parag = doc->firstParag();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return TRUE;
+}
+
+bool QTextTable::up( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ QTextTableCell *cell = cells.at( *currCell.find( c ) );
+ if ( cell->row_ == 0 ) {
+ currCell.insert( c, 0 );
+ QTextCustomItem::up( c, doc, parag, idx, ox, oy );
+ QTextTableCell *cell = cells.first();
+ if ( !cell )
+ return FALSE;
+ doc = cell->richText();
+ idx = -1;
+ return TRUE;
+ }
+
+ int oldRow = cell->row_;
+ int oldCol = cell->col_;
+ if ( currCell.find( c ) == currCell.end() )
+ return FALSE;
+ int cc = *currCell.find( c );
+ for ( int i = cc; i >= 0; --i ) {
+ cell = cells.at( i );
+ if ( cell->row_ < oldRow && cell->col_ == oldCol ) {
+ currCell.insert( c, i );
+ break;
+ }
+ }
+ doc = cell->richText();
+ if ( !cell )
+ return FALSE;
+ parag = doc->lastParag();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return TRUE;
+}
+
+QTextTableCell::QTextTableCell( QTextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const QStyleSheetItem* /*style*/, // ### use them
+ const QTextFormat& /*fmt*/, const QString& context,
+ QMimeSourceFactory &factory, QStyleSheet *sheet,
+ const QString& doc)
+{
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + "new QTextTableCell1 (pappi: %p)", table );
+ qDebug( debug_indent + doc );
+#endif
+ cached_width = -1;
+ cached_sizehint = -1;
+
+ maxw = QWIDGETSIZE_MAX;
+ minw = 0;
+
+ parent = table;
+ row_ = row;
+ col_ = column;
+ stretch_ = 0;
+ richtext = new QTextDocument( table->parent );
+ richtext->setTableCell( this );
+ QString a = *attr.find( "align" );
+ if ( !a.isEmpty() ) {
+ a = a.lower();
+ if ( a == "left" )
+ richtext->setAlignment( Qt::AlignLeft );
+ else if ( a == "center" )
+ richtext->setAlignment( Qt::AlignHCenter );
+ else if ( a == "right" )
+ richtext->setAlignment( Qt::AlignRight );
+ }
+ align = 0;
+ QString va = *attr.find( "valign" );
+ if ( !va.isEmpty() ) {
+ va = va.lower();
+ if ( va == "center" )
+ align |= Qt::AlignVCenter;
+ else if ( va == "bottom" )
+ align |= Qt::AlignBottom;
+ }
+ richtext->setFormatter( table->parent->formatter() );
+ richtext->setUseFormatCollection( table->parent->useFormatCollection() );
+ richtext->setMimeSourceFactory( &factory );
+ richtext->setStyleSheet( sheet );
+ richtext->setDefaultFont( table->parent->formatCollection()->defaultFormat()->font() );
+ richtext->setRichText( doc, context );
+ rowspan_ = 1;
+ colspan_ = 1;
+ if ( attr.contains("colspan") )
+ colspan_ = attr["colspan"].toInt();
+ if ( attr.contains("rowspan") )
+ rowspan_ = attr["rowspan"].toInt();
+
+ background = 0;
+ if ( attr.contains("bgcolor") ) {
+ background = new QBrush(QColor( attr["bgcolor"] ));
+ }
+
+
+ hasFixedWidth = FALSE;
+ if ( attr.contains("width") ) {
+ bool b;
+ QString s( attr["width"] );
+ int w = s.toInt( &b );
+ if ( b ) {
+ maxw = w;
+ minw = maxw;
+ hasFixedWidth = TRUE;
+ } else {
+ s = s.stripWhiteSpace();
+ if ( s.length() > 1 && s[ (int)s.length()-1 ] == '%' )
+ stretch_ = s.left( s.length()-1).toInt();
+ }
+ }
+
+ attributes = attr;
+
+ parent->addCell( this );
+}
+
+QTextTableCell::QTextTableCell( QTextTable* table, int row, int column )
+{
+#if defined(PARSER_DEBUG)
+ qDebug( debug_indent + "new QTextTableCell2( pappi: %p", table );
+#endif
+ maxw = QWIDGETSIZE_MAX;
+ minw = 0;
+ cached_width = -1;
+ cached_sizehint = -1;
+
+ parent = table;
+ row_ = row;
+ col_ = column;
+ stretch_ = 0;
+ richtext = new QTextDocument( table->parent );
+ richtext->setTableCell( this );
+ richtext->setFormatter( table->parent->formatter() );
+ richtext->setUseFormatCollection( table->parent->useFormatCollection() );
+ richtext->setDefaultFont( table->parent->formatCollection()->defaultFormat()->font() );
+ richtext->setRichText( "<html></html>", QString::null );
+ rowspan_ = 1;
+ colspan_ = 1;
+ background = 0;
+ hasFixedWidth = FALSE;
+ parent->addCell( this );
+}
+
+
+QTextTableCell::~QTextTableCell()
+{
+ delete background;
+ background = 0;
+ delete richtext;
+ richtext = 0;
+}
+
+QSize QTextTableCell::sizeHint() const
+{
+ int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
+ int used = richtext->widthUsed() + extra;
+
+ if (stretch_ ) {
+ int w = parent->width * stretch_ / 100 - 2*parent->cellspacing - 2*parent->cellpadding;
+ return QSize( QMIN( w, maxw ), 0 ).expandedTo( minimumSize() );
+ }
+
+ return QSize( used, 0 ).expandedTo( minimumSize() );
+}
+
+QSize QTextTableCell::minimumSize() const
+{
+ int extra = 2 * ( parent->innerborder + parent->cellpadding + border_tolerance);
+ return QSize( QMAX( richtext->minimumWidth() + extra, minw), 0 );
+}
+
+QSize QTextTableCell::maximumSize() const
+{
+ return QSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
+}
+
+QSizePolicy::ExpandData QTextTableCell::expanding() const
+{
+ return QSizePolicy::BothDirections;
+}
+
+bool QTextTableCell::isEmpty() const
+{
+ return FALSE;
+}
+void QTextTableCell::setGeometry( const QRect& r )
+{
+ int extra = 2 * ( parent->innerborder + parent->cellpadding );
+ if ( r.width() != cached_width )
+ richtext->doLayout( QTextFormat::painter(), r.width() - extra );
+ cached_width = r.width();
+ geom = r;
+}
+
+QRect QTextTableCell::geometry() const
+{
+ return geom;
+}
+
+bool QTextTableCell::hasHeightForWidth() const
+{
+ return TRUE;
+}
+
+int QTextTableCell::heightForWidth( int w ) const
+{
+ int extra = 2 * ( parent->innerborder + parent->cellpadding );
+ w = QMAX( minw, w );
+
+ if ( cached_width != w ) {
+ QTextTableCell* that = (QTextTableCell*) this;
+ that->richtext->doLayout( QTextFormat::painter(), w - extra );
+ that->cached_width = w;
+ }
+ return richtext->height() + extra;
+}
+
+void QTextTableCell::adjustToPainter( QPainter* p )
+{
+ QTextParag *parag = richtext->firstParag();
+ while ( parag ) {
+ parag->adjustToPainter( p );
+ parag = parag->next();
+ }
+}
+
+int QTextTableCell::horizontalAlignmentOffset() const
+{
+ return parent->cellpadding;
+}
+
+int QTextTableCell::verticalAlignmentOffset() const
+{
+ if ( (align & Qt::AlignVCenter ) == Qt::AlignVCenter )
+ return ( geom.height() - richtext->height() ) / 2;
+ else if ( ( align & Qt::AlignBottom ) == Qt::AlignBottom )
+ return geom.height() - parent->cellpadding - richtext->height() ;
+ return parent->cellpadding;
+}
+
+void QTextTableCell::draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool )
+{
+ if ( cached_width != geom.width() ) {
+ int extra = 2 * ( parent->innerborder + parent->cellpadding );
+ richtext->doLayout( p, geom.width() - extra );
+ cached_width = geom.width();
+ }
+ QColorGroup g( cg );
+ if ( background )
+ g.setBrush( QColorGroup::Base, *background );
+ else if ( richtext->paper() )
+ g.setBrush( QColorGroup::Base, *richtext->paper() );
+
+ p->save();
+ p->translate( x + geom.x(), y + geom.y() );
+ if ( background )
+ p->fillRect( 0, 0, geom.width(), geom.height(), *background );
+ else if ( richtext->paper() )
+ p->fillRect( 0, 0, geom.width(), geom.height(), *richtext->paper() );
+
+ p->translate( horizontalAlignmentOffset(), verticalAlignmentOffset() );
+
+ QRegion r;
+ QTextCursor *c = 0;
+ if ( richtext->parent()->tmpCursor )
+ c = richtext->parent()->tmpCursor;
+ if ( cx >= 0 && cy >= 0 )
+ richtext->draw( p, cx - ( x + horizontalAlignmentOffset() + geom.x() ),
+ cy - ( y + geom.y() + verticalAlignmentOffset() ),
+ cw, ch, g, FALSE, (c != 0), c );
+ else
+ richtext->draw( p, -1, -1, -1, -1, g, FALSE, (c != 0), c );
+
+ p->restore();
+}
diff --git a/noncore/apps/opie-write/qrichtext_p.cpp b/noncore/apps/opie-write/qrichtext_p.cpp
new file mode 100644
index 0000000..fb20730
--- a/dev/null
+++ b/noncore/apps/opie-write/qrichtext_p.cpp
@@ -0,0 +1,706 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of the internal Qt classes dealing with rich text
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qrichtext_p.h"
+
+using namespace Qt3;
+
+QTextCommand::~QTextCommand() {}
+QTextCommand::Commands QTextCommand::type() const { return Invalid; }
+
+
+QTextCustomItem::~QTextCustomItem() {}
+void QTextCustomItem::adjustToPainter( QPainter* p){ if ( p ) width = 0; }
+QTextCustomItem::Placement QTextCustomItem::placement() const { return PlaceInline; }
+
+bool QTextCustomItem::ownLine() const { return FALSE; }
+void QTextCustomItem::resize( int nwidth ){ width = nwidth; }
+void QTextCustomItem::invalidate() {}
+
+bool QTextCustomItem::isNested() const { return FALSE; }
+int QTextCustomItem::minimumWidth() const { return 0; }
+
+QString QTextCustomItem::richText() const { return QString::null; }
+
+bool QTextCustomItem::enter( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, bool atEnd )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; Q_UNUSED( atEnd ) return TRUE;
+
+}
+bool QTextCustomItem::enterAt( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, const QPoint & )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE;
+}
+bool QTextCustomItem::next( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE;
+}
+bool QTextCustomItem::prev( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE;
+}
+bool QTextCustomItem::down( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE;
+}
+bool QTextCustomItem::up( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy )
+{
+ doc = doc; parag = parag; idx = idx; ox = ox; oy = oy; return TRUE;
+}
+
+void QTextFlow::setPageSize( int ps ) { pagesize = ps; }
+bool QTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
+
+void QTextTableCell::invalidate() { cached_width = -1; cached_sizehint = -1; }
+
+void QTextTable::invalidate() { cachewidth = -1; }
+
+QTextParagData::~QTextParagData() {}
+void QTextParagData::join( QTextParagData * ) {}
+
+QTextFormatter::~QTextFormatter() {}
+void QTextFormatter::setWrapEnabled( bool b ) { wrapEnabled = b; }
+void QTextFormatter::setWrapAtColumn( int c ) { wrapColumn = c; }
+
+
+
+int QTextCursor::x() const
+{
+ QTextStringChar *c = string->at( idx );
+ int curx = c->x;
+ if ( !c->rightToLeft &&
+ c->c.isSpace() &&
+ idx > 0 &&
+ ( string->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify )
+ curx = string->at( idx - 1 )->x + string->string()->width( idx - 1 );
+ if ( c->rightToLeft )
+ curx += string->string()->width( idx );
+ return curx;
+}
+
+int QTextCursor::y() const
+{
+ int dummy, line;
+ string->lineStartOfChar( idx, &dummy, &line );
+ return string->lineY( line );
+}
+
+bool QTextDocument::hasSelection( int id, bool visible ) const
+{
+ return ( selections.find( id ) != selections.end() &&
+ ( !visible ||
+ ( (QTextDocument*)this )->selectionStartCursor( id ) !=
+ ( (QTextDocument*)this )->selectionEndCursor( id ) ) );
+}
+
+void QTextDocument::setSelectionStart( int id, QTextCursor *cursor )
+{
+ QTextDocumentSelection sel;
+ sel.startCursor = *cursor;
+ sel.endCursor = *cursor;
+ sel.swapped = FALSE;
+ selections[ id ] = sel;
+}
+
+QTextParag *QTextDocument::paragAt( int i ) const
+{
+ QTextParag* p = curParag;
+ if ( !p || p->paragId() > i )
+ p = fParag;
+ while ( p && p->paragId() != i )
+ p = p->next();
+ ((QTextDocument*)this)->curParag = p;
+ return p;
+}
+
+
+QTextFormat::~QTextFormat()
+{
+}
+
+QTextFormat::QTextFormat()
+ : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ),
+ different( NoFlags )
+{
+ ref = 0;
+
+ usePixelSizes = FALSE;
+ if ( stdSize == -1 ) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = TRUE;
+ }
+
+ missp = FALSE;
+ ha = AlignNormal;
+ collection = 0;
+}
+
+QTextFormat::QTextFormat( const QStyleSheetItem *style )
+ : fm( QFontMetrics( fn ) ), linkColor( TRUE ), logicalFontSize( 3 ), stdSize( qApp->font().pointSize() ),
+ different( NoFlags )
+{
+ ref = 0;
+
+ usePixelSizes = FALSE;
+ if ( stdSize == -1 ) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = TRUE;
+ }
+
+ this->style = style->name();
+ missp = FALSE;
+ ha = AlignNormal;
+ collection = 0;
+ fn = QFont( style->fontFamily(),
+ style->fontSize(),
+ style->fontWeight(),
+ style->fontItalic() );
+ fn.setUnderline( style->fontUnderline() );
+ col = style->color();
+ fm = QFontMetrics( fn );
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = FALSE;
+ ha = AlignNormal;
+ memset( widths, 0, 256 );
+ generateKey();
+ addRef();
+ updateStyleFlags();
+}
+
+QTextFormat::QTextFormat( const QFont &f, const QColor &c, QTextFormatCollection *parent )
+ : fn( f ), col( c ), fm( QFontMetrics( f ) ), linkColor( TRUE ),
+ logicalFontSize( 3 ), stdSize( f.pointSize() ), different( NoFlags )
+{
+ ref = 0;
+ usePixelSizes = FALSE;
+ if ( stdSize == -1 ) {
+ stdSize = f.pixelSize();
+ usePixelSizes = TRUE;
+ }
+ collection = parent;
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = FALSE;
+ ha = AlignNormal;
+ memset( widths, 0, 256 );
+ generateKey();
+ addRef();
+ updateStyleFlags();
+}
+
+QTextFormat::QTextFormat( const QTextFormat &f )
+ : fm( f.fm )
+{
+ ref = 0;
+ collection = 0;
+ fn = f.fn;
+ col = f.col;
+ leftBearing = f.leftBearing;
+ rightBearing = f.rightBearing;
+ memset( widths, 0, 256 );
+ hei = f.hei;
+ asc = f.asc;
+ dsc = f.dsc;
+ stdSize = f.stdSize;
+ usePixelSizes = f.usePixelSizes;
+ logicalFontSize = f.logicalFontSize;
+ missp = f.missp;
+ ha = f.ha;
+ k = f.k;
+ linkColor = f.linkColor;
+ style = f.style;
+ different = f.different;
+ addRef();
+}
+
+QTextFormat& QTextFormat::operator=( const QTextFormat &f )
+{
+ ref = 0;
+ collection = f.collection;
+ fn = f.fn;
+ col = f.col;
+ fm = f.fm;
+ leftBearing = f.leftBearing;
+ rightBearing = f.rightBearing;
+ memset( widths, 0, 256 );
+ hei = f.hei;
+ asc = f.asc;
+ dsc = f.dsc;
+ stdSize = f.stdSize;
+ usePixelSizes = f.usePixelSizes;
+ logicalFontSize = f.logicalFontSize;
+ missp = f.missp;
+ ha = f.ha;
+ k = f.k;
+ linkColor = f.linkColor;
+ style = f.style;
+ different = f.different;
+ addRef();
+ return *this;
+}
+
+void QTextFormat::update()
+{
+ fm = QFontMetrics( fn );
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ memset( widths, 0, 256 );
+ generateKey();
+ updateStyleFlags();
+}
+
+
+QPainter* QTextFormat::pntr = 0;
+
+void QTextFormat::setPainter( QPainter *p )
+{
+ pntr = p;
+}
+
+QPainter* QTextFormat::painter()
+{
+ return pntr;
+}
+
+
+int QTextFormat::minLeftBearing() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return leftBearing;
+ pntr->setFont( fn );
+ return pntr->fontMetrics().minLeftBearing();
+}
+
+int QTextFormat::minRightBearing() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return rightBearing;
+ pntr->setFont( fn );
+ return pntr->fontMetrics().minRightBearing();
+}
+
+int QTextFormat::height() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return hei;
+ pntr->setFont( fn );
+ return pntr->fontMetrics().lineSpacing();
+}
+
+int QTextFormat::ascent() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return asc;
+ pntr->setFont( fn );
+ return pntr->fontMetrics().ascent() + (pntr->fontMetrics().leading()+1)/2;
+}
+
+int QTextFormat::descent() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return dsc;
+ pntr->setFont( fn );
+ return pntr->fontMetrics().descent();
+}
+
+int QTextFormat::leading() const
+{
+ if ( !pntr || !pntr->isActive() )
+ return fm.leading();
+ pntr->setFont( fn );
+ return pntr->fontMetrics().leading();
+}
+
+void QTextFormat::generateKey()
+{
+ k = getKey( fn, col, isMisspelled(), vAlign() );
+}
+
+QString QTextFormat::getKey( const QFont &fn, const QColor &col, bool misspelled, VerticalAlignment a )
+{
+ QString k = fn.key();
+ k += '/';
+ k += QString::number( (uint)col.rgb() );
+ k += '/';
+ k += QString::number( (int)misspelled );
+ k += '/';
+ k += QString::number( (int)a );
+ return k;
+}
+
+void QTextFormat::updateStyle()
+{
+ if ( !collection || !collection->styleSheet() )
+ return;
+ QStyleSheetItem *item = collection->styleSheet()->item( style );
+ if ( !item )
+ return;
+ if ( !( different & Color ) && item->color().isValid() )
+ col = item->color();
+ if ( !( different & Size ) && item->fontSize() != -1 )
+ fn.setPointSize( item->fontSize() );
+ if ( !( different & Family ) && !item->fontFamily().isEmpty() )
+ fn.setFamily( item->fontFamily() );
+ if ( !( different & Bold ) && item->fontWeight() != -1 )
+ fn.setWeight( item->fontWeight() );
+ if ( !( different & Italic ) && item->definesFontItalic() )
+ fn.setItalic( item->fontItalic() );
+ if ( !( different & Underline ) && item->definesFontUnderline() )
+ fn.setUnderline( item->fontUnderline() );
+ generateKey();
+ update();
+
+}
+
+void QTextFormat::updateStyleFlags()
+{
+ different = NoFlags;
+ if ( !collection || !collection->styleSheet() )
+ return;
+ QStyleSheetItem *item = collection->styleSheet()->item( style );
+ if ( !item )
+ return;
+ if ( item->color() != col )
+ different |= Color;
+ if ( item->fontSize() != fn.pointSize() )
+ different |= Size;
+ if ( item->fontFamily() != fn.family() )
+ different |= Family;
+ if ( item->fontItalic() != fn.italic() )
+ different |= Italic;
+ if ( item->fontUnderline() != fn.underline() )
+ different |= Underline;
+ if ( item->fontWeight() != fn.weight() )
+ different |= Bold;
+}
+
+QString QTextString::toString( const QMemArray<QTextStringChar> &data )
+{
+ QString s;
+ int l = data.size();
+ s.setUnicode( 0, l );
+ QTextStringChar *c = data.data();
+ QChar *uc = (QChar *)s.unicode();
+ while ( l-- ) {
+ *uc = c->c;
+ // ### workaround so that non-breaking whitespaces are drawn
+ // properly, actually this should be fixed in QFont somewhere
+ if ( *uc == (char)0xa0 )
+ *uc = 0x20;
+ uc++;
+ c++;
+ }
+
+ return s;
+}
+
+QString QTextString::toString() const
+{
+ return toString( data );
+}
+
+void QTextParag::setSelection( int id, int start, int end )
+{
+ QMap<int, QTextParagSelection>::ConstIterator it = selections().find( id );
+ if ( it != mSelections->end() ) {
+ if ( start == ( *it ).start && end == ( *it ).end )
+ return;
+ }
+
+ QTextParagSelection sel;
+ sel.start = start;
+ sel.end = end;
+ (*mSelections)[ id ] = sel;
+ setChanged( TRUE, TRUE );
+}
+
+void QTextParag::removeSelection( int id )
+{
+ if ( !hasSelection( id ) )
+ return;
+ if ( mSelections )
+ mSelections->remove( id );
+ setChanged( TRUE, TRUE );
+}
+
+int QTextParag::selectionStart( int id ) const
+{
+ if ( !mSelections )
+ return -1;
+ QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id );
+ if ( it == mSelections->end() )
+ return -1;
+ return ( *it ).start;
+}
+
+int QTextParag::selectionEnd( int id ) const
+{
+ if ( !mSelections )
+ return -1;
+ QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id );
+ if ( it == mSelections->end() )
+ return -1;
+ return ( *it ).end;
+}
+
+bool QTextParag::hasSelection( int id ) const
+{
+ if ( !mSelections )
+ return FALSE;
+ QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id );
+ if ( it == mSelections->end() )
+ return FALSE;
+ return ( *it ).start != ( *it ).end || length() == 1;
+}
+
+bool QTextParag::fullSelected( int id ) const
+{
+ if ( !mSelections )
+ return FALSE;
+ QMap<int, QTextParagSelection>::ConstIterator it = mSelections->find( id );
+ if ( it == mSelections->end() )
+ return FALSE;
+ return ( *it ).start == 0 && ( *it ).end == str->length() - 1;
+}
+
+int QTextParag::lineY( int l ) const
+{
+ if ( l > (int)lineStarts.count() - 1 ) {
+ qWarning( "QTextParag::lineY: line %d out of range!", l );
+ return 0;
+ }
+
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while ( l-- > 0 )
+ ++it;
+ return ( *it )->y;
+}
+
+int QTextParag::lineBaseLine( int l ) const
+{
+ if ( l > (int)lineStarts.count() - 1 ) {
+ qWarning( "QTextParag::lineBaseLine: line %d out of range!", l );
+ return 10;
+ }
+
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while ( l-- > 0 )
+ ++it;
+ return ( *it )->baseLine;
+}
+
+int QTextParag::lineHeight( int l ) const
+{
+ if ( l > (int)lineStarts.count() - 1 ) {
+ qWarning( "QTextParag::lineHeight: line %d out of range!", l );
+ return 15;
+ }
+
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while ( l-- > 0 )
+ ++it;
+ return ( *it )->h;
+}
+
+void QTextParag::lineInfo( int l, int &y, int &h, int &bl ) const
+{
+ if ( l > (int)lineStarts.count() - 1 ) {
+ qWarning( "QTextParag::lineInfo: line %d out of range!", l );
+ qDebug( "%d %d", (int)lineStarts.count() - 1, l );
+ y = 0;
+ h = 15;
+ bl = 10;
+ return;
+ }
+
+ if ( !isValid() )
+ ( (QTextParag*)this )->format();
+
+ QMap<int, QTextParagLineStart*>::ConstIterator it = lineStarts.begin();
+ while ( l-- > 0 )
+ ++it;
+ y = ( *it )->y;
+ h = ( *it )->h;
+ bl = ( *it )->baseLine;
+}
+
+int QTextParag::alignment() const
+{
+ if ( align != -1 )
+ return align;
+ QStyleSheetItem *item = style();
+ if ( !item )
+ return Qt3::AlignAuto;
+ if ( mStyleSheetItemsVec ) {
+ for ( int i = 0; i < (int)mStyleSheetItemsVec->size(); ++i ) {
+ item = (*mStyleSheetItemsVec)[ i ];
+ if ( item->alignment() != QStyleSheetItem::Undefined )
+ return item->alignment();
+ }
+ }
+ return Qt3::AlignAuto;
+}
+
+QPtrVector<QStyleSheetItem> QTextParag::styleSheetItems() const
+{
+ QPtrVector<QStyleSheetItem> vec;
+ if ( mStyleSheetItemsVec ) {
+ vec.resize( mStyleSheetItemsVec->size() );
+ for ( int i = 0; i < (int)vec.size(); ++i )
+ vec.insert( i, (*mStyleSheetItemsVec)[ i ] );
+ }
+ return vec;
+}
+
+QStyleSheetItem *QTextParag::style() const
+{
+ if ( !mStyleSheetItemsVec || mStyleSheetItemsVec->size() == 0 )
+ return 0;
+ return (*mStyleSheetItemsVec)[ mStyleSheetItemsVec->size() - 1 ];
+}
+
+int QTextParag::numberOfSubParagraph() const
+{
+ if ( list_val != -1 )
+ return list_val;
+ if ( numSubParag != -1 )
+ return numSubParag;
+ int n = 0;
+ QTextParag *p = (QTextParag*)this;
+ while ( p && ( styleSheetItemsVec().size() >= p->styleSheetItemsVec().size() &&
+ styleSheetItemsVec()[ (int)p->styleSheetItemsVec().size() - 1 ] == p->style() ||
+ p->styleSheetItemsVec().size() >= styleSheetItemsVec().size() &&
+ p->styleSheetItemsVec()[ (int)styleSheetItemsVec().size() - 1 ] == style() ) ) {
+ if ( p->style() == style() && listStyle() != p->listStyle()
+ && p->styleSheetItemsVec().size() == styleSheetItemsVec().size() )
+ break;
+ if ( p->style()->displayMode() == QStyleSheetItem::DisplayListItem
+ && p->style() != style() || styleSheetItemsVec().size() == p->styleSheetItemsVec().size() )
+ ++n;
+ p = p->prev();
+ }
+ ( (QTextParag*)this )->numSubParag = n;
+ return n;
+}
+
+void QTextParag::setFormat( QTextFormat *fm )
+{
+ bool doUpdate = FALSE;
+ if (defFormat && (defFormat != formatCollection()->defaultFormat()))
+ doUpdate = TRUE;
+ defFormat = formatCollection()->format( fm );
+ if ( !doUpdate )
+ return;
+ for ( int i = 0; i < length(); ++i ) {
+ if ( at( i )->format()->styleName() == defFormat->styleName() )
+ at( i )->format()->updateStyle();
+ }
+}
+
+QTextFormatter *QTextParag::formatter() const
+{
+ if ( hasdoc )
+ return document()->formatter();
+ if ( pseudoDocument()->pFormatter )
+ return pseudoDocument()->pFormatter;
+ return ( ( (QTextParag*)this )->pseudoDocument()->pFormatter = new QTextFormatterBreakWords );
+}
+
+void QTextParag::setTabArray( int *a )
+{
+ delete [] tArray;
+ tArray = a;
+}
+
+void QTextParag::setTabStops( int tw )
+{
+ if ( hasdoc )
+ document()->setTabStops( tw );
+ else
+ tabStopWidth = tw;
+}
+
+QMap<int, QTextParagSelection> &QTextParag::selections() const
+{
+ if ( !mSelections )
+ ((QTextParag *)this)->mSelections = new QMap<int, QTextParagSelection>;
+ return *mSelections;
+}
+
+QPtrVector<QStyleSheetItem> &QTextParag::styleSheetItemsVec() const
+{
+ if ( !mStyleSheetItemsVec )
+ ((QTextParag *)this)->mStyleSheetItemsVec = new QPtrVector<QStyleSheetItem>;
+ return *mStyleSheetItemsVec;
+}
+
+QPtrList<QTextCustomItem> &QTextParag::floatingItems() const
+{
+ if ( !mFloatingItems )
+ ((QTextParag *)this)->mFloatingItems = new QPtrList<QTextCustomItem>;
+ return *mFloatingItems;
+}
+
+QTextStringChar::~QTextStringChar()
+{
+ if ( format() )
+ format()->removeRef();
+ if ( type ) // not Regular
+ delete d.custom;
+}
+
+QTextParagPseudoDocument::QTextParagPseudoDocument():pFormatter(0),commandHistory(0), minw(0),wused(0){}
+QTextParagPseudoDocument::~QTextParagPseudoDocument(){ delete pFormatter; delete commandHistory; }
diff --git a/noncore/apps/opie-write/qrichtext_p.h b/noncore/apps/opie-write/qrichtext_p.h
new file mode 100644
index 0000000..94ce913
--- a/dev/null
+++ b/noncore/apps/opie-write/qrichtext_p.h
@@ -0,0 +1,2158 @@
+/****************************************************************************
+** $Id$
+**
+** Definition of internal rich text classes
+**
+** Created : 990124
+**
+** Copyright (C) 1999-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QRICHTEXT_P_H
+#define QRICHTEXT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+//
+
+#ifndef QT_H
+#include "qt3namespace.h"
+#include "qstring.h"
+#include "qlist.h"
+#include "qrect.h"
+#include "qfontmetrics.h"
+#include "qintdict.h"
+#include "qmap.h"
+#include "qstringlist.h"
+#include "qfont.h"
+#include "qcolor.h"
+#include "qsize.h"
+#include "qvaluelist.h"
+#include "qvaluestack.h"
+#include "qobject.h"
+#include "qdict.h"
+#include "qtextstream.h"
+#include "qpixmap.h"
+#include "qstylesheet.h"
+#include "qvector.h"
+#include "qpainter.h"
+#include "qlayout.h"
+#include "qobject.h"
+#include "qcomplextext_p.h"
+#include "qapplication.h"
+#include <limits.h>
+#endif // QT_H
+
+//#define DEBUG_COLLECTION
+
+namespace Qt3 {
+
+class QTextDocument;
+class QTextString;
+class QTextPreProcessor;
+class QTextFormat;
+class QTextCursor;
+class QTextParag;
+class QTextFormatter;
+class QTextIndent;
+class QTextFormatCollection;
+class QStyleSheetItem;
+class QTextCustomItem;
+class QTextFlow;
+struct QBidiContext;
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextStringChar
+{
+ friend class QTextString;
+
+public:
+ // this is never called, initialize variables in QTextString::insert()!!!
+ QTextStringChar() : lineStart( 0 ), type( Regular ), startOfRun( 0 ) {d.format=0;}
+ ~QTextStringChar();
+
+ QChar c;
+ enum Type { Regular=0, Custom=1, Anchor=2, CustomAnchor=3 };
+ uint lineStart : 1;
+ uint rightToLeft : 1;
+ uint hasCursor : 1;
+ uint canBreak : 1;
+ Type type : 2;
+ uint startOfRun : 1;
+
+ int x;
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ bool isCustom() const { return (type & Custom) != 0; }
+ QTextFormat *format() const;
+ QTextCustomItem *customItem() const;
+ void setFormat( QTextFormat *f );
+ void setCustomItem( QTextCustomItem *i );
+ QTextStringChar *clone() const;
+ struct CustomData
+ {
+ QTextFormat *format;
+ QTextCustomItem *custom;
+ QString anchorName;
+ QString anchorHref;
+ };
+
+ void loseCustomItem();
+
+ union {
+ QTextFormat* format;
+ CustomData* custom;
+ } d;
+
+ bool isAnchor() const { return ( type & Anchor) != 0; }
+ QString anchorName() const;
+ QString anchorHref() const;
+ void setAnchor( const QString& name, const QString& href );
+
+private:
+ QTextStringChar &operator=( const QTextStringChar & ) {
+ //abort();
+ return *this;
+ }
+ friend class QComplexText;
+ friend class QTextParag;
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QMemArray<QTextStringChar>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextString
+{
+public:
+
+ QTextString();
+ QTextString( const QTextString &s );
+ virtual ~QTextString();
+
+ static QString toString( const QMemArray<QTextStringChar> &data );
+ QString toString() const;
+
+ QTextStringChar &at( int i ) const;
+ int length() const;
+
+ int width( int idx ) const;
+
+ void insert( int index, const QString &s, QTextFormat *f );
+ void insert( int index, QTextStringChar *c );
+ void truncate( int index );
+ void remove( int index, int len );
+ void clear();
+
+ void setFormat( int index, QTextFormat *f, bool useCollection );
+
+ void setBidi( bool b ) { bidi = b; }
+ bool isBidi() const;
+ bool isRightToLeft() const;
+ QChar::Direction direction() const;
+ void setDirection( QChar::Direction d ) { dir = d; bidiDirty = TRUE; }
+
+ QMemArray<QTextStringChar> subString( int start = 0, int len = 0xFFFFFF ) const;
+ QMemArray<QTextStringChar> rawData() const { return data; }
+
+ void operator=( const QString &s ) { clear(); insert( 0, s, 0 ); }
+ void operator+=( const QString &s );
+ void prepend( const QString &s ) { insert( 0, s, 0 ); }
+
+private:
+ void checkBidi() const;
+
+ QMemArray<QTextStringChar> data;
+ uint bidiDirty : 1;
+ uint bidi : 1; // true when the paragraph has right to left characters
+ uint rightToLeft : 1;
+ uint dir : 5;
+};
+
+inline bool QTextString::isBidi() const
+{
+ if ( bidiDirty )
+ checkBidi();
+ return bidi;
+}
+
+inline bool QTextString::isRightToLeft() const
+{
+ if ( bidiDirty )
+ checkBidi();
+ return rightToLeft;
+}
+
+inline QChar::Direction QTextString::direction() const
+{
+ return (QChar::Direction) dir;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QValueStack<int>;
+template class Q_EXPORT QValueStack<QTextParag*>;
+template class Q_EXPORT QValueStack<bool>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextCursor
+{
+public:
+ QTextCursor( QTextDocument *d );
+ QTextCursor();
+ QTextCursor( const QTextCursor &c );
+ QTextCursor &operator=( const QTextCursor &c );
+ virtual ~QTextCursor() {}
+
+ bool operator==( const QTextCursor &c ) const;
+ bool operator!=( const QTextCursor &c ) const { return !(*this == c); }
+
+ QTextDocument *document() const { return doc; }
+ void setDocument( QTextDocument *d );
+
+ QTextParag *parag() const;
+ int index() const;
+ void setParag( QTextParag *s, bool restore = TRUE );
+
+ void gotoLeft();
+ void gotoRight();
+ void gotoNextLetter();
+ void gotoPreviousLetter();
+ void gotoUp();
+ void gotoDown();
+ void gotoLineEnd();
+ void gotoLineStart();
+ void gotoHome();
+ void gotoEnd();
+ void gotoPageUp( int visibleHeight );
+ void gotoPageDown( int visibleHeight );
+ void gotoNextWord();
+ void gotoPreviousWord();
+ void gotoWordLeft();
+ void gotoWordRight();
+
+ void insert( const QString &s, bool checkNewLine, QMemArray<QTextStringChar> *formatting = 0 );
+ void splitAndInsertEmptyParag( bool ind = TRUE, bool updateIds = TRUE );
+ bool remove();
+ void killLine();
+ void indent();
+
+ bool atParagStart();
+ bool atParagEnd();
+
+ void setIndex( int i, bool restore = TRUE );
+
+ void checkIndex();
+
+ int offsetX() const { return ox; }
+ int offsetY() const { return oy; }
+
+ QTextParag *topParag() const { return parags.isEmpty() ? string : parags.first(); }
+ int totalOffsetX() const;
+ int totalOffsetY() const;
+
+ bool place( const QPoint &pos, QTextParag *s ) { return place( pos, s, FALSE ); }
+ bool place( const QPoint &pos, QTextParag *s, bool link );
+ void restoreState();
+
+ int x() const;
+ int y() const;
+
+ int nestedDepth() const { return (int)indices.count(); } //### size_t/int cast
+ void oneUp() { if ( !indices.isEmpty() ) pop(); }
+ void setValid( bool b ) { valid = b; }
+ bool isValid() const { return valid; }
+
+private:
+ enum Operation { EnterBegin, EnterEnd, Next, Prev, Up, Down };
+
+ void push();
+ void pop();
+ void processNesting( Operation op );
+ void invalidateNested();
+ void gotoIntoNested( const QPoint &globalPos );
+
+ QTextParag *string;
+ QTextDocument *doc;
+ int idx, tmpIndex;
+ int ox, oy;
+ QValueStack<int> indices;
+ QValueStack<QTextParag*> parags;
+ QValueStack<int> xOffsets;
+ QValueStack<int> yOffsets;
+ QValueStack<bool> nestedStack;
+ uint nested : 1;
+ uint valid : 1;
+
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextCommand
+{
+public:
+ enum Commands { Invalid, Insert, Delete, Format, Alignment, ParagType };
+
+ QTextCommand( QTextDocument *d ) : doc( d ), cursor( d ) {}
+ virtual ~QTextCommand();
+
+ virtual Commands type() const;
+
+ virtual QTextCursor *execute( QTextCursor *c ) = 0;
+ virtual QTextCursor *unexecute( QTextCursor *c ) = 0;
+
+protected:
+ QTextDocument *doc;
+ QTextCursor cursor;
+
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QPtrList<QTextCommand>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextCommandHistory
+{
+public:
+ QTextCommandHistory( int s ) : current( -1 ), steps( s ) { history.setAutoDelete( TRUE ); }
+ virtual ~QTextCommandHistory();
+
+ void clear() { history.clear(); current = -1; }
+
+ void addCommand( QTextCommand *cmd );
+ QTextCursor *undo( QTextCursor *c );
+ QTextCursor *redo( QTextCursor *c );
+
+ bool isUndoAvailable();
+ bool isRedoAvailable();
+
+ void setUndoDepth( int d ) { steps = d; }
+ int undoDepth() const { return steps; }
+
+ int historySize() const { return history.count(); }
+ int currentPosition() const { return current; }
+
+private:
+ QPtrList<QTextCommand> history;
+ int current, steps;
+
+};
+
+inline QTextCommandHistory::~QTextCommandHistory()
+{
+ clear();
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextCustomItem
+{
+public:
+ QTextCustomItem( QTextDocument *p )
+ : xpos(0), ypos(-1), width(-1), height(0), parent( p )
+ {}
+ virtual ~QTextCustomItem();
+ virtual void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected ) = 0;
+
+ virtual void adjustToPainter( QPainter* );
+
+ enum Placement { PlaceInline = 0, PlaceLeft, PlaceRight };
+ virtual Placement placement() const;
+ bool placeInline() { return placement() == PlaceInline; }
+
+ virtual bool ownLine() const;
+ virtual void resize( int nwidth );
+ virtual void invalidate();
+ virtual int ascent() const { return height; }
+
+ virtual bool isNested() const;
+ virtual int minimumWidth() const;
+
+ virtual QString richText() const;
+
+ int xpos; // used for floating items
+ int ypos; // used for floating items
+ int width;
+ int height;
+
+ QRect geometry() const { return QRect( xpos, ypos, width, height ); }
+
+ virtual bool enter( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, bool atEnd = FALSE );
+ virtual bool enterAt( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, const QPoint & );
+ virtual bool next( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool prev( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool down( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool up( QTextCursor *, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+
+ void setParagraph( QTextParag *p ) { parag = p; }
+ QTextParag *paragrapth() const { return parag; }
+
+ QTextDocument *parent;
+ QTextParag *parag;
+
+ virtual void pageBreak( int y, QTextFlow* flow );
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QMap<QString, QString>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextImage : public QTextCustomItem
+{
+public:
+ QTextImage( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ QMimeSourceFactory &factory );
+ virtual ~QTextImage();
+
+ Placement placement() const { return place; }
+ void adjustToPainter( QPainter* );
+ int minimumWidth() const { return width; }
+
+ QString richText() const;
+
+ void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected );
+
+private:
+ QRegion* reg;
+ QPixmap pm;
+ Placement place;
+ int tmpwidth, tmpheight;
+ QMap<QString, QString> attributes;
+ QString imgId;
+
+};
+
+class Q_EXPORT QTextHorizontalLine : public QTextCustomItem
+{
+public:
+ QTextHorizontalLine( QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ QMimeSourceFactory &factory );
+ virtual ~QTextHorizontalLine();
+
+ void adjustToPainter( QPainter* );
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected );
+ QString richText() const;
+
+ bool ownLine() const { return TRUE; }
+
+private:
+ int tmpheight;
+ QColor color;
+
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QPtrList<QTextCustomItem>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextFlow
+{
+ friend class QTextDocument;
+ friend class QTextTableCell;
+
+public:
+ QTextFlow();
+ virtual ~QTextFlow();
+
+ virtual void setWidth( int width );
+ int width() const;
+
+ virtual void setPageSize( int ps );
+ int pageSize() const { return pagesize; }
+
+ virtual int adjustLMargin( int yp, int h, int margin, int space );
+ virtual int adjustRMargin( int yp, int h, int margin, int space );
+
+ virtual void registerFloatingItem( QTextCustomItem* item );
+ virtual void unregisterFloatingItem( QTextCustomItem* item );
+ virtual QRect boundingRect() const;
+ virtual void drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected );
+
+ virtual int adjustFlow( int y, int w, int h ); // adjusts y according to the defined pagesize. Returns the shift.
+
+ virtual bool isEmpty();
+
+ void clear();
+
+private:
+ int w;
+ int pagesize;
+
+ QPtrList<QTextCustomItem> leftItems;
+ QPtrList<QTextCustomItem> rightItems;
+
+};
+
+inline int QTextFlow::width() const { return w; }
+
+class QTextTable;
+
+class Q_EXPORT QTextTableCell : public QLayoutItem
+{
+ friend class QTextTable;
+
+public:
+ QTextTableCell( QTextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const QStyleSheetItem* style,
+ const QTextFormat& fmt, const QString& context,
+ QMimeSourceFactory &factory, QStyleSheet *sheet, const QString& doc );
+ QTextTableCell( QTextTable* table, int row, int column );
+ virtual ~QTextTableCell();
+
+ QSize sizeHint() const ;
+ QSize minimumSize() const ;
+ QSize maximumSize() const ;
+ QSizePolicy::ExpandData expanding() const;
+ bool isEmpty() const;
+ void setGeometry( const QRect& ) ;
+ QRect geometry() const;
+
+ bool hasHeightForWidth() const;
+ int heightForWidth( int ) const;
+
+ void adjustToPainter( QPainter* );
+
+ int row() const { return row_; }
+ int column() const { return col_; }
+ int rowspan() const { return rowspan_; }
+ int colspan() const { return colspan_; }
+ int stretch() const { return stretch_; }
+
+ QTextDocument* richText() const { return richtext; }
+ QTextTable* table() const { return parent; }
+
+ void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected );
+
+ QBrush *backGround() const { return background; }
+ virtual void invalidate();
+
+ int verticalAlignmentOffset() const;
+ int horizontalAlignmentOffset() const;
+
+private:
+ QRect geom;
+ QTextTable* parent;
+ QTextDocument* richtext;
+ int row_;
+ int col_;
+ int rowspan_;
+ int colspan_;
+ int stretch_;
+ int maxw;
+ int minw;
+ bool hasFixedWidth;
+ QBrush *background;
+ int cached_width;
+ int cached_sizehint;
+ QMap<QString, QString> attributes;
+ int align;
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QPtrList<QTextTableCell>;
+template class Q_EXPORT QMap<QTextCursor*, int>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextTable: public QTextCustomItem
+{
+ friend class QTextTableCell;
+
+public:
+ QTextTable( QTextDocument *p, const QMap<QString, QString> &attr );
+ virtual ~QTextTable();
+
+ void adjustToPainter( QPainter *p );
+ void pageBreak( int y, QTextFlow* flow );
+ void draw( QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QColorGroup& cg, bool selected );
+
+ bool noErase() const { return TRUE; }
+ bool ownLine() const { return TRUE; }
+ Placement placement() const { return place; }
+ bool isNested() const { return TRUE; }
+ void resize( int nwidth );
+ virtual void invalidate();
+
+ virtual bool enter( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, bool atEnd = FALSE );
+ virtual bool enterAt( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy, const QPoint &pos );
+ virtual bool next( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool prev( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool down( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+ virtual bool up( QTextCursor *c, QTextDocument *&doc, QTextParag *&parag, int &idx, int &ox, int &oy );
+
+ QString richText() const;
+
+ int minimumWidth() const;
+
+ QPtrList<QTextTableCell> tableCells() const { return cells; }
+
+ bool isStretching() const { return stretch; }
+
+private:
+ void format( int w );
+ void addCell( QTextTableCell* cell );
+
+private:
+ QGridLayout* layout;
+ QPtrList<QTextTableCell> cells;
+ int cachewidth;
+ int fixwidth;
+ int cellpadding;
+ int cellspacing;
+ int border;
+ int outerborder;
+ int stretch;
+ int innerborder;
+ int us_cp, us_ib, us_b, us_ob, us_cs;
+ QMap<QString, QString> attributes;
+ QMap<QTextCursor*, int> currCell;
+ Placement place;
+ void adjustCells( int y , int shift );
+ int pageBreakFor;
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class QTextTableCell;
+class QTextParag;
+
+struct Q_EXPORT QTextDocumentSelection
+{
+ QTextCursor startCursor, endCursor;
+ bool swapped;
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QMap<int, QColor>;
+template class Q_EXPORT QMap<int, bool>;
+template class Q_EXPORT QMap<int, QTextDocumentSelection>;
+template class Q_EXPORT QPtrList<QTextDocument>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextDocument : public QObject
+{
+ Q_OBJECT
+
+ friend class QTextTableCell;
+ friend class QTextCursor;
+ friend class QTextEdit;
+ friend class QTextParag;
+
+public:
+ enum SelectionIds {
+ Standard = 0,
+ Temp = 32000 // This selection must not be drawn, it's used e.g. by undo/redo to
+ // remove multiple lines with removeSelectedText()
+ };
+
+ QTextDocument( QTextDocument *p );
+ QTextDocument( QTextDocument *d, QTextFormatCollection *f );
+ virtual ~QTextDocument();
+
+ QTextDocument *parent() const { return par; }
+ QTextParag *parentParag() const { return parParag; }
+
+ void setText( const QString &text, const QString &context );
+ QMap<QString, QString> attributes() const { return attribs; }
+ void setAttributes( const QMap<QString, QString> &attr ) { attribs = attr; }
+
+ QString text() const;
+ QString text( int parag ) const;
+ QString originalText() const;
+
+ int x() const;
+ int y() const;
+ int width() const;
+ int widthUsed() const;
+ int visibleWidth() const;
+ int height() const;
+ void setWidth( int w );
+ int minimumWidth() const;
+ bool setMinimumWidth( int needed, int used = -1, QTextParag *parag = 0 );
+
+ void setY( int y );
+ int leftMargin() const;
+ void setLeftMargin( int lm );
+ int rightMargin() const;
+ void setRightMargin( int rm );
+
+ QTextParag *firstParag() const;
+ QTextParag *lastParag() const;
+ void setFirstParag( QTextParag *p );
+ void setLastParag( QTextParag *p );
+
+ void invalidate();
+
+ void setPreProcessor( QTextPreProcessor *sh );
+ QTextPreProcessor *preProcessor() const;
+
+ void setFormatter( QTextFormatter *f );
+ QTextFormatter *formatter() const;
+
+ void setIndent( QTextIndent *i );
+ QTextIndent *indent() const;
+
+ QColor selectionColor( int id ) const;
+ bool invertSelectionText( int id ) const;
+ void setSelectionColor( int id, const QColor &c );
+ void setInvertSelectionText( int id, bool b );
+ bool hasSelection( int id, bool visible = FALSE ) const;
+ void setSelectionStart( int id, QTextCursor *cursor );
+ bool setSelectionEnd( int id, QTextCursor *cursor );
+ void selectAll( int id );
+ bool removeSelection( int id );
+ void selectionStart( int id, int &paragId, int &index );
+ QTextCursor selectionStartCursor( int id );
+ QTextCursor selectionEndCursor( int id );
+ void selectionEnd( int id, int &paragId, int &index );
+ void setFormat( int id, QTextFormat *f, int flags );
+ QTextParag *selectionStart( int id );
+ QTextParag *selectionEnd( int id );
+ int numSelections() const { return nSelections; }
+ void addSelection( int id );
+
+ QString selectedText( int id, bool withCustom = TRUE ) const;
+ void copySelectedText( int id );
+ void removeSelectedText( int id, QTextCursor *cursor );
+ void indentSelection( int id );
+
+ QTextParag *paragAt( int i ) const;
+
+ void addCommand( QTextCommand *cmd );
+ QTextCursor *undo( QTextCursor *c = 0 );
+ QTextCursor *redo( QTextCursor *c = 0 );
+ QTextCommandHistory *commands() const { return commandHistory; }
+
+ QTextFormatCollection *formatCollection() const;
+
+ bool find( const QString &expr, bool cs, bool wo, bool forward, int *parag, int *index, QTextCursor *cursor );
+
+ void setTextFormat( Qt::TextFormat f );
+ Qt::TextFormat textFormat() const;
+
+ bool inSelection( int selId, const QPoint &pos ) const;
+
+ QStyleSheet *styleSheet() const { return sheet_; }
+ QMimeSourceFactory *mimeSourceFactory() const { return factory_; }
+ QString context() const { return contxt; }
+
+ void setStyleSheet( QStyleSheet *s );
+ void updateStyles();
+ void updateFontSizes( int base, bool usePixels );
+ void updateFontAttributes( const QFont &f, const QFont &old );
+ void setMimeSourceFactory( QMimeSourceFactory *f ) { if ( f ) factory_ = f; }
+ void setContext( const QString &c ) { if ( !c.isEmpty() ) contxt = c; }
+
+ void setUnderlineLinks( bool b ) { underlLinks = b; }
+ bool underlineLinks() const { return underlLinks; }
+
+ void setPaper( QBrush *brush ) { if ( backBrush ) delete backBrush; backBrush = brush; }
+ QBrush *paper() const { return backBrush; }
+
+ void doLayout( QPainter *p, int w );
+ void draw( QPainter *p, const QRect& rect, const QColorGroup &cg, const QBrush *paper = 0 );
+ void drawParag( QPainter *p, QTextParag *parag, int cx, int cy, int cw, int ch,
+ QPixmap *&doubleBuffer, const QColorGroup &cg,
+ bool drawCursor, QTextCursor *cursor, bool resetChanged = TRUE );
+ QTextParag *draw( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
+ bool onlyChanged = FALSE, bool drawCursor = FALSE, QTextCursor *cursor = 0,
+ bool resetChanged = TRUE );
+
+ void setDefaultFont( const QFont &f );
+
+ void registerCustomItem( QTextCustomItem *i, QTextParag *p );
+ void unregisterCustomItem( QTextCustomItem *i, QTextParag *p );
+
+ void setFlow( QTextFlow *f );
+ void takeFlow();
+ QTextFlow *flow() const { return flow_; }
+ bool isPageBreakEnabled() const { return pages; }
+ void setPageBreakEnabled( bool b ) { pages = b; }
+
+ void setUseFormatCollection( bool b ) { useFC = b; }
+ bool useFormatCollection() const { return useFC; }
+
+ QTextTableCell *tableCell() const { return tc; }
+ void setTableCell( QTextTableCell *c ) { tc = c; }
+
+ void setPlainText( const QString &text );
+ void setRichText( const QString &text, const QString &context );
+ QString richText( QTextParag *p = 0 ) const;
+ QString plainText( QTextParag *p = 0 ) const;
+
+ bool focusNextPrevChild( bool next );
+
+ int alignment() const;
+ void setAlignment( int a );
+
+ int *tabArray() const;
+ int tabStopWidth() const;
+ void setTabArray( int *a );
+ void setTabStops( int tw );
+
+ void setUndoDepth( int d ) { commandHistory->setUndoDepth( d ); }
+ int undoDepth() const { return commandHistory->undoDepth(); }
+
+ int length() const;
+ void clear( bool createEmptyParag = FALSE );
+
+ virtual QTextParag *createParag( QTextDocument *d, QTextParag *pr = 0, QTextParag *nx = 0, bool updateIds = TRUE );
+ void insertChild( QObject *o ) { QObject::insertChild( o ); }
+ void removeChild( QObject *o ) { QObject::removeChild( o ); }
+ void insertChild( QTextDocument *d ) { childList.append( d ); }
+ void removeChild( QTextDocument *d ) { childList.removeRef( d ); }
+ QPtrList<QTextDocument> children() const { return childList; }
+
+ void setAddMargins( bool b ) { addMargs = b; }
+ int addMargins() const { return addMargs; }
+
+ bool hasFocusParagraph() const;
+ QString focusHref() const;
+
+ void invalidateOriginalText() { oTextValid = FALSE; oText = ""; }
+
+signals:
+ void minimumWidthChanged( int );
+
+private:
+ void init();
+ QPixmap *bufferPixmap( const QSize &s );
+ // HTML parser
+ bool hasPrefix(const QChar* doc, int length, int pos, QChar c);
+ bool hasPrefix(const QChar* doc, int length, int pos, const QString& s);
+ QTextCustomItem* parseTable( const QMap<QString, QString> &attr, const QTextFormat &fmt,
+ const QChar* doc, int length, int& pos, QTextParag *curpar );
+ bool eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp = FALSE );
+ bool eat(const QChar* doc, int length, int& pos, QChar c);
+ QString parseOpenTag(const QChar* doc, int length, int& pos, QMap<QString, QString> &attr, bool& emptyTag);
+ QString parseCloseTag( const QChar* doc, int length, int& pos );
+ QChar parseHTMLSpecialChar(const QChar* doc, int length, int& pos);
+ QString parseWord(const QChar* doc, int length, int& pos, bool lower = TRUE);
+ QChar parseChar(const QChar* doc, int length, int& pos, QStyleSheetItem::WhiteSpaceMode wsm );
+ void setRichTextInternal( const QString &text );
+
+private:
+ struct Q_EXPORT Focus {
+ QTextParag *parag;
+ int start, len;
+ QString href;
+ };
+
+ int cx, cy, cw, vw;
+ QTextParag *fParag, *lParag;
+ QTextPreProcessor *pProcessor;
+ QMap<int, QColor> selectionColors;
+ QMap<int, QTextDocumentSelection> selections;
+ QMap<int, bool> selectionText;
+ QTextCommandHistory *commandHistory;
+ QTextFormatter *pFormatter;
+ QTextIndent *indenter;
+ QTextFormatCollection *fCollection;
+ Qt::TextFormat txtFormat;
+ uint preferRichText : 1;
+ uint pages : 1;
+ uint useFC : 1;
+ uint withoutDoubleBuffer : 1;
+ uint underlLinks : 1;
+ uint nextDoubleBuffered : 1;
+ uint addMargs : 1;
+ uint oTextValid : 1;
+ uint mightHaveCustomItems : 1;
+ int align;
+ int nSelections;
+ QTextFlow *flow_;
+ QTextDocument *par;
+ QTextParag *parParag;
+ QTextTableCell *tc;
+ QTextCursor *tmpCursor;
+ QBrush *backBrush;
+ QPixmap *buf_pixmap;
+ Focus focusIndicator;
+ int minw;
+ int wused;
+ int leftmargin;
+ int rightmargin;
+ QTextParag *minwParag, *curParag;
+ QStyleSheet* sheet_;
+ QMimeSourceFactory* factory_;
+ QString contxt;
+ QMap<QString, QString> attribs;
+ int *tArray;
+ int tStopWidth;
+ int uDepth;
+ QString oText;
+ QPtrList<QTextDocument> childList;
+ QColor linkColor;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+class Q_EXPORT QTextDeleteCommand : public QTextCommand
+{
+public:
+ QTextDeleteCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str,
+ const QValueList< QPtrVector<QStyleSheetItem> > &os,
+ const QValueList<QStyleSheetItem::ListStyle> &ols,
+ const QMemArray<int> &oas );
+ QTextDeleteCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str );
+ virtual ~QTextDeleteCommand();
+
+ Commands type() const { return Delete; }
+ QTextCursor *execute( QTextCursor *c );
+ QTextCursor *unexecute( QTextCursor *c );
+
+protected:
+ int id, index;
+ QTextParag *parag;
+ QMemArray<QTextStringChar> text;
+ QValueList< QPtrVector<QStyleSheetItem> > oldStyles;
+ QValueList<QStyleSheetItem::ListStyle> oldListStyles;
+ QMemArray<int> oldAligns;
+
+};
+
+class Q_EXPORT QTextInsertCommand : public QTextDeleteCommand
+{
+public:
+ QTextInsertCommand( QTextDocument *d, int i, int idx, const QMemArray<QTextStringChar> &str,
+ const QValueList< QPtrVector<QStyleSheetItem> > &os,
+ const QValueList<QStyleSheetItem::ListStyle> &ols,
+ const QMemArray<int> &oas )
+ : QTextDeleteCommand( d, i, idx, str, os, ols, oas ) {}
+ QTextInsertCommand( QTextParag *p, int idx, const QMemArray<QTextStringChar> &str )
+ : QTextDeleteCommand( p, idx, str ) {}
+ virtual ~QTextInsertCommand() {}
+
+ Commands type() const { return Insert; }
+ QTextCursor *execute( QTextCursor *c ) { return QTextDeleteCommand::unexecute( c ); }
+ QTextCursor *unexecute( QTextCursor *c ) { return QTextDeleteCommand::execute( c ); }
+
+};
+
+class Q_EXPORT QTextFormatCommand : public QTextCommand
+{
+public:
+ QTextFormatCommand( QTextDocument *d, int sid, int sidx, int eid, int eidx, const QMemArray<QTextStringChar> &old, QTextFormat *f, int fl );
+ virtual ~QTextFormatCommand();
+
+ Commands type() const { return Format; }
+ QTextCursor *execute( QTextCursor *c );
+ QTextCursor *unexecute( QTextCursor *c );
+
+protected:
+ int startId, startIndex, endId, endIndex;
+ QTextFormat *format;
+ QMemArray<QTextStringChar> oldFormats;
+ int flags;
+
+};
+
+class Q_EXPORT QTextAlignmentCommand : public QTextCommand
+{
+public:
+ QTextAlignmentCommand( QTextDocument *d, int fParag, int lParag, int na, const QMemArray<int> &oa );
+ virtual ~QTextAlignmentCommand() {}
+
+ Commands type() const { return Alignment; }
+ QTextCursor *execute( QTextCursor *c );
+ QTextCursor *unexecute( QTextCursor *c );
+
+private:
+ int firstParag, lastParag;
+ int newAlign;
+ QMemArray<int> oldAligns;
+
+};
+
+class Q_EXPORT QTextParagTypeCommand : public QTextCommand
+{
+public:
+ QTextParagTypeCommand( QTextDocument *d, int fParag, int lParag, bool l,
+ QStyleSheetItem::ListStyle s, const QValueList< QPtrVector<QStyleSheetItem> > &os,
+ const QValueList<QStyleSheetItem::ListStyle> &ols );
+ virtual ~QTextParagTypeCommand() {}
+
+ Commands type() const { return ParagType; }
+ QTextCursor *execute( QTextCursor *c );
+ QTextCursor *unexecute( QTextCursor *c );
+
+private:
+ int firstParag, lastParag;
+ bool list;
+ QStyleSheetItem::ListStyle listStyle;
+ QValueList< QPtrVector<QStyleSheetItem> > oldStyles;
+ QValueList<QStyleSheetItem::ListStyle> oldListStyles;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+struct Q_EXPORT QTextParagSelection
+{
+ int start, end;
+};
+
+struct Q_EXPORT QTextParagLineStart
+{
+ QTextParagLineStart() : y( 0 ), baseLine( 0 ), h( 0 )
+#ifndef QT_NO_COMPLEXTEXT
+ , bidicontext( 0 )
+#endif
+ { }
+ QTextParagLineStart( ushort y_, ushort bl, ushort h_ ) : y( y_ ), baseLine( bl ), h( h_ ),
+ w( 0 )
+#ifndef QT_NO_COMPLEXTEXT
+ , bidicontext( 0 )
+#endif
+ { }
+#ifndef QT_NO_COMPLEXTEXT
+ QTextParagLineStart( QBidiContext *c, QBidiStatus s ) : y(0), baseLine(0), h(0),
+ status( s ), bidicontext( c ) { if ( bidicontext ) bidicontext->ref(); }
+#endif
+
+ virtual ~QTextParagLineStart()
+ {
+#ifndef QT_NO_COMPLEXTEXT
+ if ( bidicontext && bidicontext->deref() )
+ delete bidicontext;
+#endif
+ }
+
+#ifndef QT_NO_COMPLEXTEXT
+ void setContext( QBidiContext *c ) {
+ if ( c == bidicontext )
+ return;
+ if ( bidicontext && bidicontext->deref() )
+ delete bidicontext;
+ bidicontext = c;
+ if ( bidicontext )
+ bidicontext->ref();
+ }
+ QBidiContext *context() const { return bidicontext; }
+#endif
+
+public:
+ ushort y, baseLine, h;
+#ifndef QT_NO_COMPLEXTEXT
+ QBidiStatus status;
+#endif
+ int w;
+
+private:
+#ifndef QT_NO_COMPLEXTEXT
+ QBidiContext *bidicontext;
+#endif
+};
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QMap<int, QTextParagSelection>;
+template class Q_EXPORT QMap<int, QTextParagLineStart*>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextParagData
+{
+public:
+ QTextParagData() {}
+ virtual ~QTextParagData();
+ virtual void join( QTextParagData * );
+};
+
+class Q_EXPORT QTextParagPseudoDocument
+{
+public:
+ QTextParagPseudoDocument();
+ ~QTextParagPseudoDocument();
+ QRect docRect;
+ QTextFormatter *pFormatter;
+ QTextCommandHistory *commandHistory;
+ int minw;
+ int wused;
+};
+
+//nase
+class Q_EXPORT QTextParag
+{
+ friend class QTextDocument;
+ friend class QTextCursor;
+
+public:
+ QTextParag( QTextDocument *d, QTextParag *pr = 0, QTextParag *nx = 0, bool updateIds = TRUE );
+ virtual ~QTextParag();
+
+ QTextString *string() const;
+ QTextStringChar *at( int i ) const; // maybe remove later
+ int leftGap() const;
+ int length() const; // maybe remove later
+
+ void setListStyle( QStyleSheetItem::ListStyle ls );
+ QStyleSheetItem::ListStyle listStyle() const;
+ void setListValue( int v ) { list_val = v; }
+ int listValue() const { return list_val; }
+
+ void setList( bool b, int listStyle );
+ void incDepth();
+ void decDepth();
+ int listDepth() const;
+
+ void setFormat( QTextFormat *fm );
+ QTextFormat *paragFormat() const;
+
+ QTextDocument *document() const;
+ QTextParagPseudoDocument *pseudoDocument() const;
+
+ QRect rect() const;
+ void setHeight( int h ) { r.setHeight( h ); }
+ void show();
+ void hide();
+ bool isVisible() const { return visible; }
+
+ QTextParag *prev() const;
+ QTextParag *next() const;
+ void setPrev( QTextParag *s );
+ void setNext( QTextParag *s );
+
+ void insert( int index, const QString &s );
+ void append( const QString &s, bool reallyAtEnd = FALSE );
+ void truncate( int index );
+ void remove( int index, int len );
+ void join( QTextParag *s );
+
+ void invalidate( int chr );
+
+ void move( int &dy );
+ void format( int start = -1, bool doMove = TRUE );
+
+ bool isValid() const;
+ bool hasChanged() const;
+ void setChanged( bool b, bool recursive = FALSE );
+
+ int lineHeightOfChar( int i, int *bl = 0, int *y = 0 ) const;
+ QTextStringChar *lineStartOfChar( int i, int *index = 0, int *line = 0 ) const;
+ int lines() const;
+ QTextStringChar *lineStartOfLine( int line, int *index = 0 ) const;
+ int lineY( int l ) const;
+ int lineBaseLine( int l ) const;
+ int lineHeight( int l ) const;
+ void lineInfo( int l, int &y, int &h, int &bl ) const;
+
+ void setSelection( int id, int start, int end );
+ void removeSelection( int id );
+ int selectionStart( int id ) const;
+ int selectionEnd( int id ) const;
+ bool hasSelection( int id ) const;
+ bool hasAnySelection() const;
+ bool fullSelected( int id ) const;
+
+ void setEndState( int s );
+ int endState() const;
+
+ void setParagId( int i );
+ int paragId() const;
+
+ bool firstPreProcess() const;
+ void setFirstPreProcess( bool b );
+
+ void indent( int *oldIndent = 0, int *newIndent = 0 );
+
+ void setExtraData( QTextParagData *data );
+ QTextParagData *extraData() const;
+
+ QMap<int, QTextParagLineStart*> &lineStartList();
+
+ void setFormat( int index, int len, QTextFormat *f, bool useCollection = TRUE, int flags = -1 );
+
+ void setAlignment( int a );
+ int alignment() const;
+
+ virtual void paint( QPainter &painter, const QColorGroup &cg, QTextCursor *cursor = 0, bool drawSelections = FALSE,
+ int clipx = -1, int clipy = -1, int clipw = -1, int cliph = -1 );
+
+ void setStyleSheetItems( const QPtrVector<QStyleSheetItem> &vec );
+ QPtrVector<QStyleSheetItem> styleSheetItems() const;
+ QStyleSheetItem *style() const;
+
+ virtual int topMargin() const;
+ virtual int bottomMargin() const;
+ virtual int leftMargin() const;
+ virtual int firstLineMargin() const;
+ virtual int rightMargin() const;
+ virtual int lineSpacing() const;
+
+ int numberOfSubParagraph() const;
+ void registerFloatingItem( QTextCustomItem *i );
+ void unregisterFloatingItem( QTextCustomItem *i );
+
+ void setFullWidth( bool b ) { fullWidth = b; }
+ bool isFullWidth() const { return fullWidth; }
+
+ QTextTableCell *tableCell() const;
+
+ QBrush *background() const;
+
+ int documentWidth() const;
+ int documentVisibleWidth() const;
+ int documentX() const;
+ int documentY() const;
+ QTextFormatCollection *formatCollection() const;
+ QTextFormatter *formatter() const;
+
+ virtual int nextTab( int i, int x );
+ int *tabArray() const;
+ void setTabArray( int *a );
+ void setTabStops( int tw );
+
+ void adjustToPainter( QPainter *p );
+
+ void setNewLinesAllowed( bool b );
+ bool isNewLinesAllowed() const;
+
+ QString richText() const;
+
+ void addCommand( QTextCommand *cmd );
+ QTextCursor *undo( QTextCursor *c = 0 );
+ QTextCursor *redo( QTextCursor *c = 0 );
+ QTextCommandHistory *commands() const;
+ virtual void copyParagData( QTextParag *parag );
+
+ void setBreakable( bool b ) { breakable = b; }
+ bool isBreakable() const { return breakable; }
+
+ void setBackgroundColor( const QColor &c );
+ QColor *backgroundColor() const { return bgcol; }
+ void clearBackgroundColor();
+
+ bool isLineBreak() const { return isBr; }
+
+ void setMovedDown( bool b ) { movedDown = b; }
+ bool wasMovedDown() const { return movedDown; }
+
+ void setDirection( QChar::Direction d );
+ QChar::Direction direction() const;
+
+protected:
+ virtual void drawLabel( QPainter* p, int x, int y, int w, int h, int base, const QColorGroup& cg );
+ virtual void drawParagString( QPainter &painter, const QString &str, int start, int len, int startX,
+ int lastY, int baseLine, int bw, int h, bool drawSelections,
+ QTextStringChar *formatChar, int i, const QMemArray<int> &selectionStarts,
+ const QMemArray<int> &selectionEnds, const QColorGroup &cg, bool rightToLeft );
+
+private:
+ QMap<int, QTextParagSelection> &selections() const;
+ QPtrVector<QStyleSheetItem> &styleSheetItemsVec() const;
+ QPtrList<QTextCustomItem> &floatingItems() const;
+
+ QMap<int, QTextParagLineStart*> lineStarts;
+ int invalid;
+ QRect r;
+ QTextParag *p, *n;
+ void *docOrPseudo;
+ uint changed : 1;
+ uint firstFormat : 1;
+ uint firstPProcess : 1;
+ uint needPreProcess : 1;
+ uint fullWidth : 1;
+ uint newLinesAllowed : 1;
+ uint lastInFrame : 1;
+ uint visible : 1;
+ uint breakable : 1;
+ uint isBr : 1;
+ uint movedDown : 1;
+ uint mightHaveCustomItems : 1;
+ uint hasdoc : 1;
+ int align : 4;
+ int state, id;
+ QTextString *str;
+ QMap<int, QTextParagSelection> *mSelections;
+ QPtrVector<QStyleSheetItem> *mStyleSheetItemsVec;
+ QPtrList<QTextCustomItem> *mFloatingItems;
+ QStyleSheetItem::ListStyle listS;
+ int numSubParag;
+ int tm, bm, lm, rm, flm;
+ QTextFormat *defFormat;
+ int *tArray;
+ int tabStopWidth;
+ QTextParagData *eData;
+ int list_val;
+ QColor *bgcol;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextFormatter
+{
+public:
+ QTextFormatter();
+ virtual ~QTextFormatter();
+
+ virtual int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts ) = 0;
+ virtual int formatVertically( QTextDocument* doc, QTextParag* parag );
+
+ bool isWrapEnabled( QTextParag *p ) const { if ( !wrapEnabled ) return FALSE; if ( p && !p->isBreakable() ) return FALSE; return TRUE;}
+ int wrapAtColumn() const { return wrapColumn;}
+ virtual void setWrapEnabled( bool b );
+ virtual void setWrapAtColumn( int c );
+ virtual void setAllowBreakInWords( bool b ) { biw = b; }
+ bool allowBreakInWords() const { return biw; }
+
+ int minimumWidth() const { return thisminw; }
+ int widthUsed() const { return thiswused; }
+
+protected:
+ virtual QTextParagLineStart *formatLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line, QTextStringChar *start,
+ QTextStringChar *last, int align = Qt3::AlignAuto, int space = 0 );
+#ifndef QT_NO_COMPLEXTEXT
+ virtual QTextParagLineStart *bidiReorderLine( QTextParag *parag, QTextString *string, QTextParagLineStart *line, QTextStringChar *start,
+ QTextStringChar *last, int align, int space );
+#endif
+ virtual bool isBreakable( QTextString *string, int pos ) const;
+ void insertLineStart( QTextParag *parag, int index, QTextParagLineStart *ls );
+
+ int thisminw;
+ int thiswused;
+
+private:
+ bool wrapEnabled;
+ int wrapColumn;
+ bool biw;
+
+#ifdef HAVE_THAI_BREAKS
+ static QCString *thaiCache;
+ static QTextString *cachedString;
+ static ThBreakIterator *thaiIt;
+#endif
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextFormatterBreakInWords : public QTextFormatter
+{
+public:
+ QTextFormatterBreakInWords();
+ virtual ~QTextFormatterBreakInWords() {}
+
+ int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts );
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextFormatterBreakWords : public QTextFormatter
+{
+public:
+ QTextFormatterBreakWords();
+ virtual ~QTextFormatterBreakWords() {}
+
+ int format( QTextDocument *doc, QTextParag *parag, int start, const QMap<int, QTextParagLineStart*> &oldLineStarts );
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextIndent
+{
+public:
+ QTextIndent();
+ virtual ~QTextIndent() {}
+
+ virtual void indent( QTextDocument *doc, QTextParag *parag, int *oldIndent = 0, int *newIndent = 0 ) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextPreProcessor
+{
+public:
+ enum Ids {
+ Standard = 0
+ };
+
+ QTextPreProcessor();
+ virtual ~QTextPreProcessor() {}
+
+ virtual void process( QTextDocument *doc, QTextParag *, int, bool = TRUE ) = 0;
+ virtual QTextFormat *format( int id ) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_EXPORT QTextFormat
+{
+ friend class QTextFormatCollection;
+ friend class QTextDocument;
+
+public:
+ enum Flags {
+ NoFlags,
+ Bold = 1,
+ Italic = 2,
+ Underline = 4,
+ Family = 8,
+ Size = 16,
+ Color = 32,
+ Misspelled = 64,
+ VAlign = 128,
+ Font = Bold | Italic | Underline | Family | Size,
+ Format = Font | Color | Misspelled | VAlign
+ };
+
+ enum VerticalAlignment { AlignNormal, AlignSuperScript, AlignSubScript };
+
+ QTextFormat();
+ virtual ~QTextFormat();
+
+ QTextFormat( const QStyleSheetItem *s );
+ QTextFormat( const QFont &f, const QColor &c, QTextFormatCollection *parent = 0 );
+ QTextFormat( const QTextFormat &fm );
+ QTextFormat makeTextFormat( const QStyleSheetItem *style, const QMap<QString,QString>& attr ) const;
+ QTextFormat& operator=( const QTextFormat &fm );
+ QColor color() const;
+ QFont font() const;
+ bool isMisspelled() const;
+ VerticalAlignment vAlign() const;
+ int minLeftBearing() const;
+ int minRightBearing() const;
+ int width( const QChar &c ) const;
+ int width( const QString &str, int pos ) const;
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ int leading() const;
+ bool useLinkColor() const;
+
+ void setBold( bool b );
+ void setItalic( bool b );
+ void setUnderline( bool b );
+ void setFamily( const QString &f );
+ void setPointSize( int s );
+ void setFont( const QFont &f );
+ void setColor( const QColor &c );
+ void setMisspelled( bool b );
+ void setVAlign( VerticalAlignment a );
+
+ bool operator==( const QTextFormat &f ) const;
+ QTextFormatCollection *parent() const;
+ QString key() const;
+
+ static QString getKey( const QFont &f, const QColor &c, bool misspelled, VerticalAlignment vAlign );
+
+ void addRef();
+ void removeRef();
+
+ QString makeFormatChangeTags( QTextFormat *f, const QString& oldAnchorHref, const QString& anchorHref ) const;
+ QString makeFormatEndTags( const QString& anchorHref ) const;
+
+ static void setPainter( QPainter *p );
+ static QPainter* painter();
+ void updateStyle();
+ void updateStyleFlags();
+ void setStyle( const QString &s );
+ QString styleName() const { return style; }
+
+ int changed() const { return different; }
+ bool fontSizesInPixels() { return usePixelSizes; }
+
+protected:
+ virtual void generateKey();
+
+private:
+ void update();
+
+private:
+ QFont fn;
+ QColor col;
+ QFontMetrics fm;
+ uint missp : 1;
+ uint linkColor : 1;
+ uint usePixelSizes : 1;
+ int leftBearing, rightBearing;
+ VerticalAlignment ha;
+ uchar widths[ 256 ];
+ int hei, asc, dsc;
+ QTextFormatCollection *collection;
+ int ref;
+ QString k;
+ int logicalFontSize;
+ int stdSize;
+ static QPainter *pntr;
+ QString style;
+ int different;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QDict<QTextFormat>;
+// MOC_SKIP_END
+#endif
+
+class Q_EXPORT QTextFormatCollection
+{
+ friend class QTextDocument;
+ friend class QTextFormat;
+
+public:
+ QTextFormatCollection();
+ virtual ~QTextFormatCollection();
+
+ void setDefaultFormat( QTextFormat *f );
+ QTextFormat *defaultFormat() const;
+ virtual QTextFormat *format( QTextFormat *f );
+ virtual QTextFormat *format( QTextFormat *of, QTextFormat *nf, int flags );
+ virtual QTextFormat *format( const QFont &f, const QColor &c );
+ virtual void remove( QTextFormat *f );
+ virtual QTextFormat *createFormat( const QTextFormat &f ) { return new QTextFormat( f ); }
+ virtual QTextFormat *createFormat( const QFont &f, const QColor &c ) { return new QTextFormat( f, c, this ); }
+ void debug();
+
+ QStyleSheet *styleSheet() const { return sheet; }
+ void setStyleSheet( QStyleSheet *s ) { sheet = s; }
+ void updateStyles();
+ void updateFontSizes( int base, bool usePixels );
+ void updateFontAttributes( const QFont &f, const QFont &old );
+ QDict<QTextFormat> dict() const { return cKey; }
+
+private:
+ void updateKeys();
+
+private:
+ QTextFormat *defFormat, *lastFormat, *cachedFormat;
+ QDict<QTextFormat> cKey;
+ QTextFormat *cres;
+ QFont cfont;
+ QColor ccol;
+ QString kof, knf;
+ int cflags;
+ QStyleSheet *sheet;
+
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int QTextString::length() const
+{
+ return data.size();
+}
+
+inline void QTextString::operator+=( const QString &s )
+{
+ insert( length(), s, 0 );
+}
+
+inline int QTextParag::length() const
+{
+ return str->length();
+}
+
+inline QRect QTextParag::rect() const
+{
+ return r;
+}
+
+inline QTextParag *QTextCursor::parag() const
+{
+ return string;
+}
+
+inline int QTextCursor::index() const
+{
+ return idx;
+}
+
+inline void QTextCursor::setIndex( int i, bool restore )
+{
+ if ( restore )
+ restoreState();
+ if ( i < 0 || i >= string->length() ) {
+#if defined(QT_CHECK_RANGE)
+ qWarning( "QTextCursor::setIndex: %d out of range", i );
+#endif
+ i = i < 0 ? 0 : string->length() - 1;
+ }
+
+ tmpIndex = -1;
+ idx = i;
+}
+
+inline void QTextCursor::setParag( QTextParag *s, bool restore )
+{
+ if ( restore )
+ restoreState();
+ idx = 0;
+ string = s;
+ tmpIndex = -1;
+}
+
+inline void QTextCursor::checkIndex()
+{
+ if ( idx >= string->length() )
+ idx = string->length() - 1;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int QTextDocument::x() const
+{
+ return cx;
+}
+
+inline int QTextDocument::y() const
+{
+ return cy;
+}
+
+inline int QTextDocument::width() const
+{
+ return QMAX( cw, flow_->width() );
+}
+
+inline int QTextDocument::visibleWidth() const
+{
+ return vw;
+}
+
+inline QTextParag *QTextDocument::firstParag() const
+{
+ return fParag;
+}
+
+inline QTextParag *QTextDocument::lastParag() const
+{
+ return lParag;
+}
+
+inline void QTextDocument::setFirstParag( QTextParag *p )
+{
+ fParag = p;
+}
+
+inline void QTextDocument::setLastParag( QTextParag *p )
+{
+ lParag = p;
+}
+
+inline void QTextDocument::setWidth( int w )
+{
+ cw = QMAX( w, minw );
+ flow_->setWidth( cw );
+ vw = w;
+}
+
+inline int QTextDocument::minimumWidth() const
+{
+ return minw;
+}
+
+inline void QTextDocument::setY( int y )
+{
+ cy = y;
+}
+
+inline int QTextDocument::leftMargin() const
+{
+ return leftmargin;
+}
+
+inline void QTextDocument::setLeftMargin( int lm )
+{
+ leftmargin = lm;
+}
+
+inline int QTextDocument::rightMargin() const
+{
+ return rightmargin;
+}
+
+inline void QTextDocument::setRightMargin( int rm )
+{
+ rightmargin = rm;
+}
+
+inline QTextPreProcessor *QTextDocument::preProcessor() const
+{
+ return pProcessor;
+}
+
+inline void QTextDocument::setPreProcessor( QTextPreProcessor * sh )
+{
+ pProcessor = sh;
+}
+
+inline void QTextDocument::setFormatter( QTextFormatter *f )
+{
+ delete pFormatter;
+ pFormatter = f;
+}
+
+inline QTextFormatter *QTextDocument::formatter() const
+{
+ return pFormatter;
+}
+
+inline void QTextDocument::setIndent( QTextIndent *i )
+{
+ indenter = i;
+}
+
+inline QTextIndent *QTextDocument::indent() const
+{
+ return indenter;
+}
+
+inline QColor QTextDocument::selectionColor( int id ) const
+{
+ return selectionColors[ id ];
+}
+
+inline bool QTextDocument::invertSelectionText( int id ) const
+{
+ return selectionText[ id ];
+}
+
+inline void QTextDocument::setSelectionColor( int id, const QColor &c )
+{
+ selectionColors[ id ] = c;
+}
+
+inline void QTextDocument::setInvertSelectionText( int id, bool b )
+{
+ selectionText[ id ] = b;
+}
+
+inline QTextFormatCollection *QTextDocument::formatCollection() const
+{
+ return fCollection;
+}
+
+inline int QTextDocument::alignment() const
+{
+ return align;
+}
+
+inline void QTextDocument::setAlignment( int a )
+{
+ align = a;
+}
+
+inline int *QTextDocument::tabArray() const
+{
+ return tArray;
+}
+
+inline int QTextDocument::tabStopWidth() const
+{
+ return tStopWidth;
+}
+
+inline void QTextDocument::setTabArray( int *a )
+{
+ tArray = a;
+}
+
+inline void QTextDocument::setTabStops( int tw )
+{
+ tStopWidth = tw;
+}
+
+inline QString QTextDocument::originalText() const
+{
+ if ( oTextValid )
+ return oText;
+ return text();
+}
+
+inline void QTextDocument::setFlow( QTextFlow *f )
+{
+ if ( flow_ )
+ delete flow_;
+ flow_ = f;
+}
+
+inline void QTextDocument::takeFlow()
+{
+ flow_ = 0;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QColor QTextFormat::color() const
+{
+ return col;
+}
+
+inline QFont QTextFormat::font() const
+{
+ return fn;
+}
+
+inline bool QTextFormat::isMisspelled() const
+{
+ return missp;
+}
+
+inline QTextFormat::VerticalAlignment QTextFormat::vAlign() const
+{
+ return ha;
+}
+
+inline bool QTextFormat::operator==( const QTextFormat &f ) const
+{
+ return k == f.k;
+}
+
+inline QTextFormatCollection *QTextFormat::parent() const
+{
+ return collection;
+}
+
+inline void QTextFormat::addRef()
+{
+ ref++;
+#ifdef DEBUG_COLLECTION
+ qDebug( "add ref of '%s' to %d (%p)", k.latin1(), ref, this );
+#endif
+}
+
+inline void QTextFormat::removeRef()
+{
+ ref--;
+ if ( !collection )
+ return;
+ if ( this == collection->defFormat )
+ return;
+#ifdef DEBUG_COLLECTION
+ qDebug( "remove ref of '%s' to %d (%p)", k.latin1(), ref, this );
+#endif
+ if ( ref == 0 )
+ collection->remove( this );
+}
+
+inline QString QTextFormat::key() const
+{
+ return k;
+}
+
+inline bool QTextFormat::useLinkColor() const
+{
+ return linkColor;
+}
+
+inline void QTextFormat::setStyle( const QString &s )
+{
+ style = s;
+ updateStyleFlags();
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QTextStringChar &QTextString::at( int i ) const
+{
+ return data[ i ];
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QTextStringChar *QTextParag::at( int i ) const
+{
+ return &str->at( i );
+}
+
+inline bool QTextParag::isValid() const
+{
+ return invalid == -1;
+}
+
+inline bool QTextParag::hasChanged() const
+{
+ return changed;
+}
+
+inline void QTextParag::setBackgroundColor( const QColor & c )
+{
+ delete bgcol;
+ bgcol = new QColor( c );
+ setChanged( TRUE );
+}
+
+inline void QTextParag::clearBackgroundColor()
+{
+ delete bgcol; bgcol = 0; setChanged( TRUE );
+}
+
+inline void QTextParag::append( const QString &s, bool reallyAtEnd )
+{
+ if ( reallyAtEnd )
+ insert( str->length(), s );
+ else
+ insert( QMAX( str->length() - 1, 0 ), s );
+}
+
+inline QTextParag *QTextParag::prev() const
+{
+ return p;
+}
+
+inline QTextParag *QTextParag::next() const
+{
+ return n;
+}
+
+inline bool QTextParag::hasAnySelection() const
+{
+ return mSelections ? !selections().isEmpty() : FALSE;
+}
+
+inline void QTextParag::setEndState( int s )
+{
+ if ( s == state )
+ return;
+ state = s;
+}
+
+inline int QTextParag::endState() const
+{
+ return state;
+}
+
+inline void QTextParag::setParagId( int i )
+{
+ id = i;
+}
+
+inline int QTextParag::paragId() const
+{
+ if ( id == -1 )
+ qWarning( "invalid parag id!!!!!!!! (%p)", (void*)this );
+ return id;
+}
+
+inline bool QTextParag::firstPreProcess() const
+{
+ return firstPProcess;
+}
+
+inline void QTextParag::setFirstPreProcess( bool b )
+{
+ firstPProcess = b;
+}
+
+inline QMap<int, QTextParagLineStart*> &QTextParag::lineStartList()
+{
+ return lineStarts;
+}
+
+inline QTextString *QTextParag::string() const
+{
+ return str;
+}
+
+inline QTextDocument *QTextParag::document() const
+{
+ if ( hasdoc )
+ return (QTextDocument*) docOrPseudo;
+ return 0;
+}
+
+inline QTextParagPseudoDocument *QTextParag::pseudoDocument() const
+{
+ if ( hasdoc )
+ return 0;
+ return (QTextParagPseudoDocument*) docOrPseudo;
+}
+
+
+inline QTextTableCell *QTextParag::tableCell() const
+{
+ return hasdoc ? document()->tableCell () : 0;
+}
+
+inline QTextCommandHistory *QTextParag::commands() const
+{
+ return hasdoc ? document()->commands() : pseudoDocument()->commandHistory;
+}
+
+
+inline void QTextParag::setAlignment( int a )
+{
+ if ( a == (int)align )
+ return;
+ align = a;
+ invalidate( 0 );
+}
+
+inline void QTextParag::setListStyle( QStyleSheetItem::ListStyle ls )
+{
+ listS = ls;
+ invalidate( 0 );
+}
+
+inline QStyleSheetItem::ListStyle QTextParag::listStyle() const
+{
+ return listS;
+}
+
+inline QTextFormat *QTextParag::paragFormat() const
+{
+ return defFormat;
+}
+
+inline void QTextParag::registerFloatingItem( QTextCustomItem *i )
+{
+ floatingItems().append( i );
+}
+
+inline void QTextParag::unregisterFloatingItem( QTextCustomItem *i )
+{
+ floatingItems().removeRef( i );
+}
+
+inline QBrush *QTextParag::background() const
+{
+ return tableCell() ? tableCell()->backGround() : 0;
+}
+
+inline int QTextParag::documentWidth() const
+{
+ return hasdoc ? document()->width() : pseudoDocument()->docRect.width();
+}
+
+inline int QTextParag::documentVisibleWidth() const
+{
+ return hasdoc ? document()->visibleWidth() : pseudoDocument()->docRect.width();
+}
+
+inline int QTextParag::documentX() const
+{
+ return hasdoc ? document()->x() : pseudoDocument()->docRect.x();
+}
+
+inline int QTextParag::documentY() const
+{
+ return hasdoc ? document()->y() : pseudoDocument()->docRect.y();
+}
+
+inline void QTextParag::setExtraData( QTextParagData *data )
+{
+ eData = data;
+}
+
+inline QTextParagData *QTextParag::extraData() const
+{
+ return eData;
+}
+
+inline void QTextParag::setNewLinesAllowed( bool b )
+{
+ newLinesAllowed = b;
+}
+
+inline bool QTextParag::isNewLinesAllowed() const
+{
+ return newLinesAllowed;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline void QTextFormatCollection::setDefaultFormat( QTextFormat *f )
+{
+ defFormat = f;
+}
+
+inline QTextFormat *QTextFormatCollection::defaultFormat() const
+{
+ return defFormat;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QTextFormat *QTextStringChar::format() const
+{
+ return (type == Regular) ? d.format : d.custom->format;
+}
+
+
+inline QTextCustomItem *QTextStringChar::customItem() const
+{
+ return isCustom() ? d.custom->custom : 0;
+}
+
+inline int QTextStringChar::height() const
+{
+ return !isCustom() ? format()->height() : ( customItem()->placement() == QTextCustomItem::PlaceInline ? customItem()->height : 0 );
+}
+
+inline int QTextStringChar::ascent() const
+{
+ return !isCustom() ? format()->ascent() : ( customItem()->placement() == QTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
+}
+
+inline int QTextStringChar::descent() const
+{
+ return !isCustom() ? format()->descent() : 0;
+}
+
+} // namespace Qt3
+
+#endif
diff --git a/noncore/apps/opie-write/qstylesheet.cpp b/noncore/apps/opie-write/qstylesheet.cpp
new file mode 100644
index 0000000..7ab9ec6
--- a/dev/null
+++ b/noncore/apps/opie-write/qstylesheet.cpp
@@ -0,0 +1,1484 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of the QStyleSheet class
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qstylesheet.h"
+
+#include "qrichtext_p.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qcleanuphandler.h"
+
+#include <stdio.h>
+
+using namespace Qt3;
+
+namespace Qt3 {
+
+class QStyleSheetItemData
+{
+public:
+ QStyleSheetItem::DisplayMode disp;
+ int fontitalic;
+ int fontunderline;
+ int fontweight;
+ int fontsize;
+ int fontsizelog;
+ int fontsizestep;
+ int lineSpacing;
+ QString fontfamily;
+ QStyleSheetItem *parentstyle;
+ QString stylename;
+ int ncolumns;
+ QColor col;
+ bool anchor;
+ int align;
+ QStyleSheetItem::VerticalAlignment valign;
+ int margin[5];
+ QStyleSheetItem::ListStyle list;
+ QStyleSheetItem::WhiteSpaceMode whitespacemode;
+ QString contxt;
+ bool selfnest;
+ QStyleSheet* sheet;
+};
+
+}
+
+/*!
+ \class QStyleSheetItem qstylesheet.h
+ \ingroup text
+ \brief The QStyleSheetItem class provides an encapsulation of a set of text styles.
+
+ A style sheet item consists of a name and a set of attributes that
+ specifiy its font, color, etc. When used in a \link QStyleSheet
+ style sheet\endlink (see styleSheet()), items define the name() of a
+ rich text tag and the display property changes associated with it.
+
+ The \link QStyleSheetItem::DisplayMode display mode\endlink
+ attribute indicates whether the item is a block, an inline element
+ or a list element; see setDisplayMode(). The treatment of whitespace
+ is controlled by the \link QStyleSheetItem::WhiteSpaceMode white
+ space mode\endlink; see setWhiteSpaceMode(). An item's margins are
+ set with setMargin(), and line spacing is set with setLineSpacing().
+ In the case of list items, the list style is set with
+ setListStyle(). An item may be a hypertext link anchor; see
+ setAnchor(). Other attributes are set with setAlignment(),
+ setVerticalAlignment(), setFontFamily(), setFontSize(),
+ setFontWeight(), setFontItalic(), setFontUnderline() and setColor().
+*/
+
+/*! \enum QStyleSheetItem::AdditionalStyleValues
+ \internal
+*/
+
+/*! \enum QStyleSheetItem::WhiteSpaceMode
+
+ This enum defines the ways in which QStyleSheet can treat whitespace. There are three values at present:
+
+ \value WhiteSpaceNormal any sequence of whitespace (including
+ line-breaks) is equivalent to a single space.
+
+ \value WhiteSpacePre whitespace must be output exactly as given
+ in the input.
+
+ \value WhiteSpaceNoWrap multiple spaces are collapsed as with
+ WhiteSpaceNormal, but no automatic line-breaks occur. To break lines manually,
+ use the \c{<br>} tag.
+
+*/
+
+/*! \enum QStyleSheetItem::Margin
+
+ \value MarginLeft left margin
+ \value MarginRight right margin
+ \value MarginTop top margin
+ \value MarginBottom bottom margin
+ \value MarginAll all margins (left, right, top and bottom)
+ \value MarginVertical top and bottom margins
+ \value MarginHorizontal left and right margins
+ \value MarginFirstLine margin (indentation) of the first line of a paragarph (in addition to the MarginLeft of the paragraph)
+*/
+
+/*!
+ Constructs a new style named \a name for the stylesheet \a parent.
+
+ All properties in QStyleSheetItem are initially in the "do not change" state,
+ except \link QStyleSheetItem::DisplayMode display mode\endlink, which defaults
+ to \c DisplayInline.
+*/
+QStyleSheetItem::QStyleSheetItem( QStyleSheet* parent, const QString& name )
+{
+ d = new QStyleSheetItemData;
+ d->stylename = name.lower();
+ d->sheet = parent;
+ init();
+ if (parent)
+ parent->insert( this );
+}
+
+/*!
+ Copy constructor. Constructs a copy of \a other that is
+ not bound to any style sheet.
+ */
+QStyleSheetItem::QStyleSheetItem( const QStyleSheetItem & other )
+{
+ d = new QStyleSheetItemData;
+ *d = *other.d;
+}
+
+
+/*!
+ Destroys the style. Note that QStyleSheetItem objects become owned
+ by QStyleSheet when they are created.
+ */
+QStyleSheetItem::~QStyleSheetItem()
+{
+ delete d;
+}
+
+
+
+/*!
+ Returns the style sheet this item is in.
+ */
+QStyleSheet* QStyleSheetItem::styleSheet()
+{
+ return d->sheet;
+}
+
+/*!
+ \overload
+ Returns the style sheet this item is in.
+ */
+const QStyleSheet* QStyleSheetItem::styleSheet() const
+{
+ return d->sheet;
+}
+
+/*!
+ \internal
+ Internal initialization
+ */
+void QStyleSheetItem::init()
+{
+ d->disp = DisplayInline;
+
+ d->fontitalic = Undefined;
+ d->fontunderline = Undefined;
+ d->fontweight = Undefined;
+ d->fontsize = Undefined;
+ d->fontsizelog = Undefined;
+ d->fontsizestep = 0;
+ d->ncolumns = Undefined;
+ d->col = QColor(); // !isValid()
+ d->anchor = FALSE;
+ d->align = Undefined;
+ d->valign = VAlignBaseline;
+ d->margin[0] = Undefined;
+ d->margin[1] = Undefined;
+ d->margin[2] = Undefined;
+ d->margin[3] = Undefined;
+ d->margin[4] = Undefined;
+ d->list = QStyleSheetItem::ListDisc;
+ d->whitespacemode = QStyleSheetItem::WhiteSpaceNormal;
+ d->selfnest = TRUE;
+ d->lineSpacing = Undefined;
+}
+
+/*!
+ Returns the name of the style item.
+*/
+QString QStyleSheetItem::name() const
+{
+ return d->stylename;
+}
+
+/*!
+ Returns the \link QStyleSheetItem::DisplayMode display mode\endlink
+ of the style.
+
+ \sa setDisplayMode()
+ */
+QStyleSheetItem::DisplayMode QStyleSheetItem::displayMode() const
+{
+ return d->disp;
+}
+
+/*! \enum QStyleSheetItem::DisplayMode
+
+ This enum type defines the way adjacent elements are displayed. The possible values are:
+
+ \value DisplayBlock elements are displayed as a rectangular block
+ (e.g. \c{<p>...</p>}).
+
+ \value DisplayInline elements are displayed in a horizontally flowing
+ sequence (e.g. \c{<em>...</em>}).
+
+ \value DisplayListItem elements are displayed in a vertical sequence
+ (e.g. \c{<li>...</li>}).
+
+ \value DisplayNone elements are not displayed at all.
+*/
+
+/*!
+ Sets the display mode of the style to \a m.
+
+ \sa displayMode()
+ */
+void QStyleSheetItem::setDisplayMode(DisplayMode m)
+{
+ d->disp=m;
+}
+
+
+/*!
+ Returns the alignment of this style. Possible values are AlignAuto, AlignLeft,
+ AlignRight, AlignCenter and AlignJustify.
+
+ \sa setAlignment(), Qt::AlignmentFlags
+ */
+int QStyleSheetItem::alignment() const
+{
+ return d->align;
+}
+
+/*!
+ Sets the alignment to \a f. This only makes sense for styles with a
+ \link QStyleSheetItem::DisplayMode display mode\endlink of
+ DisplayBlock. Possible values are AlignAuto, AlignLeft, AlignRight,
+ AlignCenter and AlignJustify.
+
+ \sa alignment(), displayMode(), Qt::AlignmentFlags
+ */
+void QStyleSheetItem::setAlignment( int f )
+{
+ d->align = f;
+}
+
+
+/*!
+ Returns the vertical alignment of the style. Possible values are
+ VAlignBaseline, VAlignSub and VAlignSuper.
+
+ psa setVerticalAlignment()
+ */
+QStyleSheetItem::VerticalAlignment QStyleSheetItem::verticalAlignment() const
+{
+ return d->valign;
+}
+
+/*! \enum QStyleSheetItem::VerticalAlignment
+
+ This enum type defines the way elements are aligned vertically. This
+ is supported for text elements only. The possible values are:
+
+ \value VAlignBaseline align the baseline of the element (or the
+ bottom, if the element doesn't have a baseline) with the baseline of
+ the parent
+
+ \value VAlignSub subscript the element
+
+ \value VAlignSuper superscript the element
+
+*/
+
+
+/*!
+ Sets the vertical alignment to \a valign. Possible values are
+ VAlignBaseline, VAlignSub and VAlignSuper.
+
+ The vertical alignment property is not inherited.
+
+ \sa verticalAlignment()
+ */
+void QStyleSheetItem::setVerticalAlignment( VerticalAlignment valign )
+{
+ d->valign = valign;
+}
+
+
+/*!
+ Returns TRUE if the style sets an italic font; otherwise returns FALSE.
+
+ \sa setFontItalic(), definesFontItalic()
+ */
+bool QStyleSheetItem::fontItalic() const
+{
+ return d->fontitalic > 0;
+}
+
+/*!
+ If \a italic is TRUE sets italic for the style; otherwise sets
+ upright.
+
+ \sa fontItalic(), definesFontItalic()
+ */
+void QStyleSheetItem::setFontItalic(bool italic)
+{
+ d->fontitalic = italic?1:0;
+}
+
+/*!
+ Returns whether the style defines a font shape. A style
+ does not define any shape until setFontItalic() is called.
+
+ \sa setFontItalic(), fontItalic()
+ */
+bool QStyleSheetItem::definesFontItalic() const
+{
+ return d->fontitalic != Undefined;
+}
+
+/*!
+ Returns TRUE if the style sets an underlined font; otherwise returns FALSE.
+
+ \sa setFontUnderline(), definesFontUnderline()
+ */
+bool QStyleSheetItem::fontUnderline() const
+{
+ return d->fontunderline > 0;
+}
+
+/*!
+ If \a underline is TRUE sets underline for the style; otherwise sets
+ no underline.
+
+ \sa fontUnderline(), definesFontUnderline()
+ */
+void QStyleSheetItem::setFontUnderline(bool underline)
+{
+ d->fontunderline = underline?1:0;
+}
+
+/*!
+ Returns whether the style defines a setting for the underline
+ property of the font. A style does not define this until
+ setFontUnderline() is called.
+
+ \sa setFontUnderline(), fontUnderline() */
+bool QStyleSheetItem::definesFontUnderline() const
+{
+ return d->fontunderline != Undefined;
+}
+
+
+/*!
+ Returns the font weight setting of the style. This is either a
+ valid QFont::Weight or the value QStyleSheetItem::Undefined.
+
+ \sa setFontWeight(), QFont
+ */
+int QStyleSheetItem::fontWeight() const
+{
+ return d->fontweight;
+}
+
+/*!
+ Sets the font weight setting of the style to \a w. Valid values are
+ those defined by QFont::Weight.
+
+ \sa QFont, fontWeight()
+ */
+void QStyleSheetItem::setFontWeight(int w)
+{
+ d->fontweight = w;
+}
+
+/*!
+ Returns the logical font size setting of the style. This is either a valid
+ size between 1 and 7 or QStyleSheetItem::Undefined.
+
+ \sa setLogicalFontSize(), setLogicalFontSizeStep(), QFont::pointSize(), QFont::setPointSize()
+ */
+int QStyleSheetItem::logicalFontSize() const
+{
+ return d->fontsizelog;
+}
+
+
+/*!
+ Sets the logical font size setting of the style to \a s.
+ Valid logical sizes are 1 to 7.
+
+ \sa logicalFontSize(), QFont::pointSize(), QFont::setPointSize()
+ */
+void QStyleSheetItem::setLogicalFontSize(int s)
+{
+ d->fontsizelog = s;
+}
+
+/*!
+ Returns the logical font size step of this style.
+
+ The default is 0. Tags such as \c big define \c +1; \c small defines
+ \c -1.
+
+ \sa setLogicalFontSizeStep()
+ */
+int QStyleSheetItem::logicalFontSizeStep() const
+{
+ return d->fontsizestep;
+}
+
+/*!
+ Sets the logical font size step of this style to \a s.
+
+ \sa logicalFontSizeStep()
+ */
+void QStyleSheetItem::setLogicalFontSizeStep( int s )
+{
+ d->fontsizestep = s;
+}
+
+
+
+/*!
+ Sets the font size setting of the style to \a s points.
+
+ \sa fontSize(), QFont::pointSize(), QFont::setPointSize()
+ */
+void QStyleSheetItem::setFontSize(int s)
+{
+ d->fontsize = s;
+}
+
+/*!
+ Returns the font size setting of the style. This is either a valid
+ point size or QStyleSheetItem::Undefined.
+
+ \sa setFontSize(), QFont::pointSize(), QFont::setPointSize()
+ */
+int QStyleSheetItem::fontSize() const
+{
+ return d->fontsize;
+}
+
+
+/*!
+ Returns the font family setting of the style. This is either a valid
+ font family or QString::null if no family has been set.
+
+ \sa setFontFamily(), QFont::family(), QFont::setFamily()
+ */
+QString QStyleSheetItem::fontFamily() const
+{
+ return d->fontfamily;
+}
+
+/*!
+ Sets the font family setting of the style to \a fam.
+
+ \sa fontFamily(), QFont::family(), QFont::setFamily()
+ */
+void QStyleSheetItem::setFontFamily( const QString& fam)
+{
+ d->fontfamily = fam;
+}
+
+
+/*!\obsolete
+ Returns the number of columns for this style.
+
+ \sa setNumberOfColumns(), displayMode(), setDisplayMode()
+
+ */
+int QStyleSheetItem::numberOfColumns() const
+{
+ return d->ncolumns;
+}
+
+
+/*!\obsolete
+ Sets the number of columns for this style. Elements in the style
+ are divided into columns.
+
+ This makes sense only if the style uses a block display mode
+ (see QStyleSheetItem::DisplayMode).
+
+ \sa numberOfColumns()
+ */
+void QStyleSheetItem::setNumberOfColumns(int ncols)
+{
+ if (ncols > 0)
+ d->ncolumns = ncols;
+}
+
+
+/*!
+ Returns the text color of this style or an invalid color
+ if no color has been set.
+
+ \sa setColor() QColor::isValid()
+ */
+QColor QStyleSheetItem::color() const
+{
+ return d->col;
+}
+
+/*!
+ Sets the text color of this style to \a c.
+
+ \sa color()
+ */
+void QStyleSheetItem::setColor( const QColor &c)
+{
+ d->col = c;
+}
+
+/*!
+ Returns whether this style is an anchor.
+
+ \sa setAnchor()
+ */
+bool QStyleSheetItem::isAnchor() const
+{
+ return d->anchor;
+}
+
+/*!
+ If \a anc is TRUE sets this style to be an anchor (hypertext link);
+ otherwise sets it to not be an anchor. Elements in this style have
+ connections to other documents or anchors.
+
+ \sa isAnchor()
+ */
+void QStyleSheetItem::setAnchor(bool anc)
+{
+ d->anchor = anc;
+}
+
+
+/*!
+ Returns the whitespace mode.
+
+ \sa setWhiteSpaceMode() WhiteSpaceMode
+ */
+QStyleSheetItem::WhiteSpaceMode QStyleSheetItem::whiteSpaceMode() const
+{
+ return d->whitespacemode;
+}
+
+/*!
+ Sets the whitespace mode to \a m.
+ \sa WhiteSpaceMode
+ */
+void QStyleSheetItem::setWhiteSpaceMode(WhiteSpaceMode m)
+{
+ d->whitespacemode = m;
+}
+
+
+/*!
+ Returns the width of margin \a m in pixels.
+
+ The margin, \a m, can be \c MarginLeft, \c MarginRight,
+ \c MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c
+ MarginHorizontal.
+
+ \sa setMargin() Margin
+ */
+int QStyleSheetItem::margin(Margin m) const
+{
+ return d->margin[m];
+}
+
+
+/*!
+ Sets the width of margin \a m to \a v pixels.
+
+ The margin, \a m, can be \c MarginLeft, \c MarginRight,
+ \c MarginTop, \c MarginBottom, \c MarginAll, \c MarginVertical or \c
+ MarginHorizontal. The value \a v must be >= 0.
+
+ \sa margin()
+ */
+void QStyleSheetItem::setMargin(Margin m, int v)
+{
+ if (m == MarginAll ) {
+ d->margin[0] = v;
+ d->margin[1] = v;
+ d->margin[2] = v;
+ d->margin[3] = v;
+ d->margin[4] = v;
+ } else if (m == MarginVertical ) {
+ d->margin[MarginTop] = v;
+ d->margin[MarginBottom] = v;
+ } else if (m == MarginHorizontal ) {
+ d->margin[MarginLeft] = v;
+ d->margin[MarginRight] = v;
+ } else {
+ d->margin[m] = v;
+ }
+}
+
+
+/*!
+ Returns the list style of the style.
+
+ \sa setListStyle() ListStyle
+ */
+QStyleSheetItem::ListStyle QStyleSheetItem::listStyle() const
+{
+ return d->list;
+}
+
+/*! \enum QStyleSheetItem::ListStyle
+
+ This enum type defines how the items in a list are prefixed when
+ displayed. The currently defined values are:
+
+ \value ListDisc a filled circle (i.e. a bullet)
+ \value ListCircle an unfilled circle
+ \value ListSquare a filled square
+ \value ListDecimal an integer in base 10: \e 1, \e 2, \e 3, ...
+ \value ListLowerAlpha a lowercase letter: \e a, \e b, \e c, ...
+ \value ListUpperAlpha an uppercase letter: \e A, \e B, \e C, ...
+*/
+/*!
+ Sets the list style of the style to \a s.
+
+ This is used by nested elements that have a display mode of
+ \c DisplayListItem.
+
+ \sa listStyle() DisplayMode ListStyle
+ */
+void QStyleSheetItem::setListStyle(ListStyle s)
+{
+ d->list=s;
+}
+
+
+/*! Returns a space-separated list of names of styles that may
+ contain elements of this style. If nothing has been set, contexts()
+ returns an empty string, which indicates that this style can be
+ nested everywhere.
+
+ \sa setContexts()
+ */
+QString QStyleSheetItem::contexts() const
+{
+ return d->contxt;
+}
+
+/*!
+ Sets a space-separated list of names of styles that may contain
+ elements of this style. If \a c is empty, the style can be nested
+ everywhere.
+
+ \sa contexts()
+ */
+void QStyleSheetItem::setContexts( const QString& c)
+{
+ d->contxt = QChar(' ') + c + QChar(' ');
+}
+
+/*!
+ Returns TRUE if this style can be nested into an element
+ of style \a s; otherwise returns FALSE.
+
+ \sa contexts(), setContexts()
+ */
+bool QStyleSheetItem::allowedInContext( const QStyleSheetItem* s) const
+{
+ if ( d->contxt.isEmpty() )
+ return TRUE;
+ return d->contxt.find( QChar(' ')+s->name()+QChar(' ')) != -1;
+}
+
+
+/*!
+ Returns TRUE if this style has self-nesting enabled; otherwise
+ returns FALSE.
+
+ \sa setSelfNesting()
+ */
+bool QStyleSheetItem::selfNesting() const
+{
+ return d->selfnest;
+}
+
+/*!
+ Sets the self-nesting property for this style to \a nesting.
+
+ In order to support "dirty" HTML, paragraphs \c{<p>} and list items
+ \c{<li>} are not self-nesting. This means that starting a new
+ paragraph or list item automatically closes the previous one.
+
+ \sa selfNesting()
+ */
+void QStyleSheetItem::setSelfNesting( bool nesting )
+{
+ d->selfnest = nesting;
+}
+
+/*! Sets the linespacing to be \a ls pixels */
+
+void QStyleSheetItem::setLineSpacing( int ls )
+{
+ d->lineSpacing = ls;
+}
+
+/*! Returns the linespacing */
+
+int QStyleSheetItem::lineSpacing() const
+{
+ return d->lineSpacing;
+}
+
+//************************************************************************
+
+
+
+
+//************************************************************************
+
+
+/*!
+ \class QStyleSheet qstylesheet.h
+ \ingroup text
+ \brief The QStyleSheet class is a collection of styles for rich text
+ rendering and a generator of tags.
+
+ \ingroup graphics
+ \ingroup helpsystem
+
+ By creating QStyleSheetItem objects for a style sheet you build a
+ definition of a set of tags. This definition will be used by the
+ internal rich text rendering system to parse and display text
+ documents to which the style sheet applies. Rich text is normally
+ visualized in a QTextView or a QTextBrowser. However, QLabel,
+ QWhatsThis and QMessageBox also support it, and other classes are
+ likely to follow. With QSimpleRichText it is possible to use the
+ rich text renderer for custom widgets as well.
+
+ The default QStyleSheet object has the following style bindings,
+ sorted by structuring bindings, anchors, character style bindings
+ (i.e. inline styles), special elements such as horizontal lines or
+ images, and other tags. In addition, rich text supports simple HTML
+ tables.
+
+ The structuring tags are
+ \list
+ \i \c{<qt>}...\c{</qt>}
+ - A Qt rich text document. It understands the following attributes:
+ \list
+ \i title
+ - The caption of the document. This attribute is easily accessible with
+ QTextView::documentTitle().
+ \i type
+ - The type of the document. The default type is \c page . It
+ indicates that the document is displayed in a page of its
+ own. Another style is \c detail, which can be used to
+ explain certain expressions in more detail in a few
+ sentences. The QTextBrowser will then keep the current page
+ and display the new document in a small popup similar to
+ QWhatsThis. Note that links will not work in documents with
+ \c{<qt type="detail">...</qt>}.
+ \i bgcolor
+ - The background color, for example \c bgcolor="yellow" or \c
+ bgcolor="#0000FF".
+ \i background
+ - The background pixmap, for example \c
+ background="granit.xpm". The pixmap name will be resolved by
+ a QMimeSourceFactory().
+ \i text
+ - The default text color, for example \c text="red".
+ \i link
+ - The link color, for example \c link="green".
+ \endlist
+ \i \c{<h1>...</h1>}
+ - A top-level heading.
+ \i \c{<h2>...</h2>}
+ - A sublevel heading.
+ \i \c{<h3>...</h3>}
+ - A sub-sublevel heading.
+ \i \c{<p>...</p>}
+ - A left-aligned paragraph. Adjust the alignment with
+ the \c align attribute. Possible values are
+ \c left, \c right and \c center.
+ \i \c{<center>...</center>}
+ - A centered paragraph.
+ \i \c{<blockquote>...</blockquote>}
+ - An indented paragraph that is useful for quotes.
+ \i \c{<ul>...</ul>}
+ - An unordered list. You can also pass a type argument to
+ define the bullet style. The default is \c type=disc; other
+ types are \c circle and \c square.
+ \i \c{<ol>...</ol>}
+ - An ordered list. You can also pass a type argument to define
+ the enumeration label style. The default is \c type="1"; other
+ types are \c "a" and \c "A".
+ \i <tt>&lt;li&gt;</tt>...<tt>&lt;/li&gt;</tt>
+ - A list item. This tag can be used only within the context of
+ \c ol or \c ul.
+ \i \c{<pre>...</pre>}
+ - For larger chunks of code. Whitespaces in the contents are preserved.
+ For small bits of code use the inline-style \c code.
+ \endlist
+
+ Anchors and links are done with a single tag:
+ \list
+ \i \c{<a>...</a>}
+ - An anchor or link. The reference target is defined in the \c
+ href attribute of the tag as in \c{<a
+ href="target.qml">...</a>}. You can also specify an
+ additional anchor within the specified target document, for
+ example \c{<a href="target.qml#123">...</a>}. If \c a is
+ meant to be an anchor, the reference source is given in the
+ \c name attribute.
+ \endlist
+
+ The default character style bindings are
+ \list
+ \i \c{<em>...</em>}
+ - Emphasized. By default this is the same as
+ \c{<i>...</i>} (italic).
+ \i \c{<strong>...</strong>}
+ - Strong. By default this is the same as
+ \c{<b>...</b>} (bold).
+ \i \c{<i>...</i>}
+ - Italic font style.
+ \i \c{<b>...</b>}
+ - Bold font style.
+ \i \c{<u>...</u>}
+ - Underlined font style.
+ \i \c{<big>...</big>}
+ - A larger font size.
+ \i \c{<small>...</small>}
+ - A smaller font size.
+ \i \c{<code>...</code>}
+ - Indicates code. By default this is the same as
+ \c{<tt>...</tt>} (typewriter). For
+ larger junks of code use the block-tag \c pre.
+ \i \c{<tt>...</tt>}
+ - Typewriter font style.
+ \i \c{<font>...</font>}
+ - Customizes the font size, family and text color. The tag understands
+ the following attributes:
+ \list
+ \i color
+ - The text color, for example \c color="red" or \c color="#FF0000".
+ \i size
+ - The logical size of the font. Logical sizes 1 to 7 are supported.
+ The value may either be absolute (for example,
+ \c size=3) or relative (\c size=-2). In the latter case the sizes
+ are simply added.
+ \i face
+ - The family of the font, for example \c face=times.
+ \endlist
+ \endlist
+
+ Special elements are:
+ \list
+ \i \c{<img>}
+ - An image. The image name for the mime source
+ factory is given in the source attribute, for example
+ \c{<img src="qt.xpm">}
+ The image tag also understands the attributes \c width and \c
+ height that determine the size of the image. If the pixmap
+ does not fit the specified size it will be scaled
+ automatically (by using QImage::smoothScale()).
+
+ The \c align attribute determines where the image is
+ placed. By default, an image is placed inline just like a
+ normal character. Specify \c left or \c right to place the
+ image at the respective side.
+ \i \c{<hr>}
+ - A horizonal line.
+ \i \c{<br>}
+ - A line break.
+ \endlist
+
+ Another tag not in any of the above cathegories is
+ \list
+ \i \c{<nobr>...</nobr>}
+ - No break. Prevents word wrap.
+ \endlist
+
+ In addition, rich text supports simple HTML tables. A table consists
+ of one or more rows each of which contains one or more cells. Cells
+ are either data cells or header cells, depending on their
+ content. Cells which span rows and columns are supported.
+
+ \list
+ \i \c{<table>...</table>}
+ - A table. Tables support the following attributes:
+ \list
+ \i bgcolor
+ - The background color.
+ \i width
+ - The table width. This is either an absolute pixel width or a relative
+ percentage of the table's width, for example \c width=80%.
+ \i border
+ - The width of the table border. The default is 0 (= no border).
+ \i cellspacing
+ - Additional space around the table cells. The default is 2.
+ \i cellpadding
+ - Additional space around the contents of table cells. The default is 1.
+ \endlist
+ \i \c{<tr>...</tr>}
+ - A table row. This is only valid within a \c table. Rows support
+ the following attribute:
+ \list
+ \i bgcolor
+ - The background color.
+ \endlist
+ \i \c{<th>...</th>}
+ - A table header cell. Similar to \c td, but defaults to center alignment
+ and a bold font.
+ \i \c{<td>...</td>}
+ - A table data cell. This is only valid within a \c tr. Cells
+ support the following attributes:
+ \list
+ \i bgcolor
+ - The background color.
+ \i width
+ - The cell width. This is either an absolute pixel width or a relative
+ percentage of table's width, for example \c width=50%.
+ \i colspan
+ - Specifies how many columns this cell spans. The default is 1.
+ \i rowspan
+ - Specifies how many rows this cell spans. The default is 1.
+ \i align
+ - Alignment; possible values are \c left, \c right, and \c center. The
+ default is left.
+ \endlist
+ \endlist
+*/
+
+/*!
+ Creates a style sheet with parent \a parent and name \a name. Like
+ any QObject it will be deleted when its parent is
+ destroyed (if the child still exists).
+
+ By default the style sheet has the tag definitions defined above.
+*/
+QStyleSheet::QStyleSheet( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ init();
+}
+
+/*!
+ Destroys the style sheet. All styles inserted into the style sheet
+ will be deleted.
+*/
+QStyleSheet::~QStyleSheet()
+{
+}
+
+/*!
+ \internal
+ Initialized the style sheet to the basic Qt style.
+*/
+void QStyleSheet::init()
+{
+ styles.setAutoDelete( TRUE );
+
+ nullstyle = new QStyleSheetItem( this,
+ QString::fromLatin1("") );
+
+ QStyleSheetItem* style;
+
+ style = new QStyleSheetItem( this, "qml" ); // compatibility
+ style->setDisplayMode( QStyleSheetItem::DisplayBlock );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("qt") );
+ style->setDisplayMode( QStyleSheetItem::DisplayBlock );
+ //style->setMargin( QStyleSheetItem::MarginAll, 4 );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("a") );
+ style->setAnchor( TRUE );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("em") );
+ style->setFontItalic( TRUE );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("i") );
+ style->setFontItalic( TRUE );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("big") );
+ style->setLogicalFontSizeStep( 1 );
+ style = new QStyleSheetItem( this, QString::fromLatin1("large") ); // compatibility
+ style->setLogicalFontSizeStep( 1 );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("small") );
+ style->setLogicalFontSizeStep( -1 );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("strong") );
+ style->setFontWeight( QFont::Bold);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("b") );
+ style->setFontWeight( QFont::Bold);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("h1") );
+ style->setFontWeight( QFont::Bold);
+ style->setLogicalFontSize(6);
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginTop, 12);
+ style-> setMargin(QStyleSheetItem::MarginBottom, 6);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("h2") );
+ style->setFontWeight( QFont::Bold);
+ style->setLogicalFontSize(5);
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginTop, 10);
+ style-> setMargin(QStyleSheetItem::MarginBottom, 5);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("h3") );
+ style->setFontWeight( QFont::Bold);
+ style->setLogicalFontSize(4);
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginTop, 8);
+ style-> setMargin(QStyleSheetItem::MarginBottom, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("h4") );
+ style->setFontWeight( QFont::Bold);
+ style->setLogicalFontSize(3);
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginTop, 8);
+ style-> setMargin(QStyleSheetItem::MarginBottom, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("h5") );
+ style->setFontWeight( QFont::Bold);
+ style->setLogicalFontSize(2);
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginTop, 8);
+ style-> setMargin(QStyleSheetItem::MarginBottom, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("p") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginVertical, 8);
+ style->setSelfNesting( FALSE );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("center") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setAlignment( AlignCenter );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("twocolumn") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setNumberOfColumns( 2 );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("multicol") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ (void) new QStyleSheetItem( this, QString::fromLatin1("font") );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("ul") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style-> setMargin(QStyleSheetItem::MarginVertical, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("ol") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setListStyle( QStyleSheetItem::ListDecimal );
+ style-> setMargin(QStyleSheetItem::MarginVertical, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("li") );
+ style->setDisplayMode(QStyleSheetItem::DisplayListItem);
+ style->setSelfNesting( FALSE );
+ style->setContexts(QString::fromLatin1("ol ul"));
+ style-> setMargin(QStyleSheetItem::MarginVertical, 4);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("code") );
+ style->setFontFamily( QString::fromLatin1("courier") );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("tt") );
+ style->setFontFamily( QString::fromLatin1("courier") );
+
+ new QStyleSheetItem(this, QString::fromLatin1("img"));
+ new QStyleSheetItem(this, QString::fromLatin1("br"));
+ new QStyleSheetItem(this, QString::fromLatin1("hr"));
+ style = new QStyleSheetItem(this, QString::fromLatin1("sub"));
+ style->setVerticalAlignment( QStyleSheetItem::VAlignSub );
+ style = new QStyleSheetItem(this, QString::fromLatin1("sup"));
+ style->setVerticalAlignment( QStyleSheetItem::VAlignSuper );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("pre") );
+ style->setFontFamily( QString::fromLatin1("courier") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setWhiteSpaceMode(QStyleSheetItem::WhiteSpacePre);
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("blockquote") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setMargin(QStyleSheetItem::MarginHorizontal, 40 );
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("head") );
+ style->setDisplayMode(QStyleSheetItem::DisplayNone);
+ style = new QStyleSheetItem( this, QString::fromLatin1("div") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock) ;
+ style = new QStyleSheetItem( this, QString::fromLatin1("dl") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style = new QStyleSheetItem( this, QString::fromLatin1("dt") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setContexts(QString::fromLatin1("dl") );
+ style = new QStyleSheetItem( this, QString::fromLatin1("dd") );
+ style->setDisplayMode(QStyleSheetItem::DisplayBlock);
+ style->setMargin(QStyleSheetItem::MarginLeft, 30);
+ style->setContexts(QString::fromLatin1("dt dl") );
+ style = new QStyleSheetItem( this, QString::fromLatin1("u") );
+ style->setFontUnderline( TRUE);
+ style = new QStyleSheetItem( this, QString::fromLatin1("nobr") );
+ style->setWhiteSpaceMode( QStyleSheetItem::WhiteSpaceNoWrap );
+ style = new QStyleSheetItem( this, QString::fromLatin1("wsp") ); // qt extension for QTextEdit
+ style->setWhiteSpaceMode( (QStyleSheetItem::WhiteSpaceMode) 3 ); // WhiteSpaceModeNoCompression
+
+ // tables
+ style = new QStyleSheetItem( this, QString::fromLatin1("table") );
+ style = new QStyleSheetItem( this, QString::fromLatin1("tr") );
+ style->setContexts(QString::fromLatin1("table"));
+ style = new QStyleSheetItem( this, QString::fromLatin1("td") );
+ style->setContexts(QString::fromLatin1("tr"));
+ style = new QStyleSheetItem( this, QString::fromLatin1("th") );
+ style->setFontWeight( QFont::Bold );
+ style->setAlignment( Qt::AlignCenter );
+ style->setContexts(QString::fromLatin1("tr"));
+
+ style = new QStyleSheetItem( this, QString::fromLatin1("html") );
+}
+
+
+
+static QStyleSheet* defaultsheet = 0;
+static QSingleCleanupHandler<QStyleSheet> qt_cleanup_stylesheet;
+
+/*!
+ Returns the application-wide default style sheet. This style sheet is
+ used by rich text rendering classes such as QSimpleRichText,
+ QWhatsThis and QMessageBox to define the rendering style and
+ available tags within rich text documents. It serves also as initial
+ style sheet for the more complex render widgets QTextEdit and
+ QTextBrowser.
+
+ \sa setDefaultSheet()
+*/
+QStyleSheet* QStyleSheet::defaultSheet()
+{
+ if (!defaultsheet) {
+ defaultsheet = new QStyleSheet();
+ qt_cleanup_stylesheet.set( &defaultsheet );
+ }
+ return defaultsheet;
+}
+
+/*!
+ Sets the application-wide default style sheet to \a sheet, deleting
+ any style sheet previously set. The ownership is transferred to
+ QStyleSheet.
+
+ \sa defaultSheet()
+*/
+void QStyleSheet::setDefaultSheet( QStyleSheet* sheet)
+{
+ if ( defaultsheet != sheet ) {
+ if ( defaultsheet )
+ qt_cleanup_stylesheet.reset();
+ delete defaultsheet;
+ }
+ defaultsheet = sheet;
+ if ( defaultsheet )
+ qt_cleanup_stylesheet.set( &defaultsheet );
+}
+
+/*!\internal
+ Inserts \a style. Any tags generated after this time will be
+ bound to this style. Note that \a style becomes owned by the
+ style sheet and will be deleted when the style sheet is destroyed.
+*/
+void QStyleSheet::insert( QStyleSheetItem* style )
+{
+ styles.insert(style->name(), style);
+}
+
+
+/*!
+ Returns the style with name \a name or 0 if there is no such style.
+ */
+QStyleSheetItem* QStyleSheet::item( const QString& name)
+{
+ if ( name.isNull() )
+ return 0;
+ return styles[name];
+}
+
+/*!
+ \overload
+ Returns the style with name \a name or 0 if there is no such style (const version)
+ */
+const QStyleSheetItem* QStyleSheet::item( const QString& name) const
+{
+ if ( name.isNull() )
+ return 0;
+ return styles[name];
+}
+
+
+/*!
+ \preliminary
+ Generates an internal object for the tag called \a name, given the
+ attributes \a attr, and using additional information provided
+ by the mime source factory \a factory.
+
+ \a context is the optional context of the document, i.e. the path to
+ look for relative links. This becomes important if the text contains
+ relative references, for example within image tags. QSimpleRichText
+ always uses the default mime source factory (see
+ \l{QMimeSourceFactory::defaultFactory()}) to resolve these references.
+ The context will then be used to calculate the absolute path. See
+ QMimeSourceFactory::makeAbsolute() for details.
+
+ \a emptyTag and \a doc are for internal use only.
+
+ This function should not (yet) be used in application code.
+*/
+QTextCustomItem* QStyleSheet::tag( const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const QMimeSourceFactory& factory,
+ bool /*emptyTag */, QTextDocument *doc ) const
+{
+ static QString s_img = QString::fromLatin1("img");
+ static QString s_hr = QString::fromLatin1("hr");
+
+ const QStyleSheetItem* style = item( name );
+ // first some known tags
+ if ( !style )
+ return 0;
+ if ( style->name() == s_img )
+ return new QTextImage( doc, attr, context, (QMimeSourceFactory&)factory );
+ if ( style->name() == s_hr )
+ return new QTextHorizontalLine( doc, attr, context, (QMimeSourceFactory&)factory );
+ return 0;
+}
+
+
+/*!
+ Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted paragraph while preserving its look.
+
+ \a mode defines the whitespace mode. Possible values are \c
+ QStyleSheetItem::WhiteSpacePre (no wrapping, all whitespaces
+ preserved) and \c QStyleSheetItem::WhiteSpaceNormal (wrapping,
+ simplified whitespaces).
+
+ \sa escape()
+ */
+QString QStyleSheet::convertFromPlainText( const QString& plain, QStyleSheetItem::WhiteSpaceMode mode )
+{
+ int col = 0;
+ QString rich;
+ rich += "<p>";
+ for ( int i = 0; i < int(plain.length()); ++i ) {
+ if ( plain[i] == '\n' ){
+ if ( col == 1 )
+ rich += "<p></p>";
+ else
+ rich += "<br>";
+ col = 0;
+ }
+ else if ( mode == QStyleSheetItem::WhiteSpacePre && plain[i] == '\t' ){
+ rich += 0x00a0U;
+ while ( col % 4 ) {
+ rich += 0x00a0U;
+ ++col;
+ }
+ }
+ else if ( mode == QStyleSheetItem::WhiteSpacePre && plain[i].isSpace() )
+ rich += 0x00a0U;
+ else if ( plain[i] == '<' )
+ rich +="&lt;";
+ else if ( plain[i] == '>' )
+ rich +="&gt;";
+ else if ( plain[i] == '&' )
+ rich +="&amp;";
+ else
+ rich += plain[i];
+ ++col;
+ }
+ rich += "</p>";
+ return rich;
+}
+
+/*!
+ Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted string with any HTML meta-characters escaped.
+
+ \sa convertFromPlainText()
+ */
+QString QStyleSheet::escape( const QString& plain)
+{
+ QString rich;
+ for ( int i = 0; i < int(plain.length()); ++i ) {
+ if ( plain[i] == '<' )
+ rich +="&lt;";
+ else if ( plain[i] == '>' )
+ rich +="&gt;";
+ else if ( plain[i] == '&' )
+ rich +="&amp;";
+ else
+ rich += plain[i];
+ }
+ return rich;
+}
+
+// Must doc this enum somewhere, and it is logically related to QStyleSheet
+
+/*!
+ \enum Qt::TextFormat
+
+ This enum is used in widgets that can display both plain text and
+ rich text, e.g. QLabel. It is used for deciding whether a text
+ string should be interpreted as one or the other. This is
+ normally done by passing one of the enum values to a setTextFormat()
+ function.
+
+ \value PlainText The text string is interpreted as a plain text string.
+
+ \value RichText The text string is interpreted as a rich text string
+ using the current QStyleSheet::defaultSheet().
+
+ \value AutoText The text string is interpreted as for \c RichText if
+ QStyleSheet::mightBeRichText() returns TRUE, otherwise as for \c
+ PlainText.
+*/
+
+/*!
+ Returns TRUE if the string \a text is likely to be rich text;
+ otherwise returns FALSE.
+
+ Note: The function uses a fast and therefore simple heuristic. It
+ mainly checks whether there is something that looks like a tag
+ before the first line break. Although the result may be correct for
+ most common cases, there is no guarantee.
+*/
+bool QStyleSheet::mightBeRichText( const QString& text)
+{
+ if ( text.isEmpty() )
+ return FALSE;
+ if ( text.left(5).lower() == "<!doc" )
+ return TRUE;
+ int open = 0;
+ while ( open < int(text.length()) && text[open] != '<'
+ && text[open] != '\n' && text[open] != '&')
+ ++open;
+ if ( text[open] == '&' ) {
+ if ( text.mid(open+1,3) == "lt;" )
+ return TRUE; // support desperate attempt of user to see <...>
+ } else if ( text[open] == '<' ) {
+ int close = text.find('>', open);
+ if ( close > -1 ) {
+ QString tag;
+ for (int i = open+1; i < close; ++i) {
+ if ( text[i].isDigit() || text[i].isLetter() )
+ tag += text[i];
+ else if ( !tag.isEmpty() && text[i].isSpace() )
+ break;
+ else if ( !text[i].isSpace() && (!tag.isEmpty() || text[i] != '!' ) )
+ return FALSE; // that's not a tag
+ }
+ return defaultSheet()->item( tag.lower() ) != 0;
+ }
+ }
+ return FALSE;
+}
+
+
+/*! \fn void QStyleSheet::error( const QString& msg) const
+
+ This virtual function is called when an error occurs when
+ processing rich text. Reimplement it if you need to catch
+ error messages.
+
+ Errors might occur if some rich text strings contain tags that are
+ not understood by the stylesheet, if some tags are nested incorrectly, or
+ if tags are not closed properly.
+
+ \a msg is the error message.
+*/
+void QStyleSheet::error( const QString& ) const
+{
+}
+
+
+/*!
+ Scales the font \a font to the appropriate physical point size
+ corresponding to the logical font size \a logicalSize.
+
+ When calling this function, \a font has a point size corresponding to
+ the logical font size 3.
+
+ Logical font sizes range from 1 to 7, with 1 being the smallest.
+
+ \sa QStyleSheetItem::logicalFontSize(),
+ QStyleSheetItem::logicalFontSizeStep(), QFont::setPointSize()
+ */
+void QStyleSheet::scaleFont( QFont& font, int logicalSize ) const
+{
+ if ( logicalSize < 1 )
+ logicalSize = 1;
+ if ( logicalSize > 7 )
+ logicalSize = 7;
+ int baseSize = font.pointSize();
+ bool pixel = FALSE;
+ if ( baseSize == -1 ) {
+ baseSize = font.pixelSize();
+ pixel = TRUE;
+ }
+ int s;
+ switch ( logicalSize ) {
+ case 1:
+ s = baseSize/2;
+ break;
+ case 2:
+ s = (8 * baseSize) / 10;
+ break;
+ case 4:
+ s = (12 * baseSize) / 10;
+ break;
+ case 5:
+ s = (15 * baseSize) / 10;
+ break;
+ case 6:
+ s = 2 * baseSize;
+ break;
+ case 7:
+ s = (24 * baseSize) / 10;
+ break;
+ default:
+ s = baseSize;
+ }
+ if ( pixel )
+ font.setPixelSize( s );
+ else
+ font.setPointSize( s );
+}
diff --git a/noncore/apps/opie-write/qstylesheet.h b/noncore/apps/opie-write/qstylesheet.h
new file mode 100644
index 0000000..bb209fa
--- a/dev/null
+++ b/noncore/apps/opie-write/qstylesheet.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+** $Id$
+**
+** Definition of the QStyleSheet class
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QSTYLESHEET_H
+#define QSTYLESHEET_H
+
+#ifndef QT_H
+#include "qt3namespace.h"
+#include "qstring.h"
+#include "qvaluelist.h"
+#include "qvector.h"
+#include "qdict.h"
+#include "qobject.h"
+#endif // QT_H
+
+template<class Key, class T> class QMap;
+
+namespace Qt3 {
+
+class QStyleSheet;
+class QTextDocument;
+class QStyleSheetItemData;
+
+class Q_EXPORT QStyleSheetItem : public Qt
+{
+public:
+ QStyleSheetItem( QStyleSheet* parent, const QString& name );
+ QStyleSheetItem( const QStyleSheetItem & );
+ ~QStyleSheetItem();
+
+ QString name() const;
+
+ QStyleSheet* styleSheet();
+ const QStyleSheet* styleSheet() const;
+
+ enum AdditionalStyleValues { Undefined = - 1};
+
+ enum DisplayMode {
+ DisplayBlock,
+ DisplayInline,
+ DisplayListItem,
+ DisplayNone
+ };
+
+ DisplayMode displayMode() const;
+ void setDisplayMode(DisplayMode m);
+
+ int alignment() const;
+ void setAlignment( int f);
+
+ enum VerticalAlignment {
+ VAlignBaseline,
+ VAlignSub,
+ VAlignSuper
+ };
+
+ VerticalAlignment verticalAlignment() const;
+ void setVerticalAlignment( VerticalAlignment valign );
+
+ int fontWeight() const;
+ void setFontWeight(int w);
+
+ int logicalFontSize() const;
+ void setLogicalFontSize(int s);
+
+ int logicalFontSizeStep() const;
+ void setLogicalFontSizeStep( int s );
+
+ int fontSize() const;
+ void setFontSize(int s);
+
+ QString fontFamily() const;
+ void setFontFamily( const QString& );
+
+ int numberOfColumns() const;
+ void setNumberOfColumns(int ncols);
+
+ QColor color() const;
+ void setColor( const QColor &);
+
+ bool fontItalic() const;
+ void setFontItalic( bool );
+ bool definesFontItalic() const;
+
+ bool fontUnderline() const;
+ void setFontUnderline( bool );
+ bool definesFontUnderline() const;
+
+ bool isAnchor() const;
+ void setAnchor(bool anc);
+
+ enum WhiteSpaceMode { WhiteSpaceNormal, WhiteSpacePre, WhiteSpaceNoWrap };
+ WhiteSpaceMode whiteSpaceMode() const;
+ void setWhiteSpaceMode(WhiteSpaceMode m);
+
+ enum Margin {
+ MarginLeft,
+ MarginRight,
+ MarginTop,
+ MarginBottom,
+ MarginFirstLine,
+ MarginAll,
+ MarginVertical,
+ MarginHorizontal
+ };
+
+ int margin( Margin m) const;
+ void setMargin( Margin, int);
+
+ enum ListStyle {
+ ListDisc,
+ ListCircle,
+ ListSquare,
+ ListDecimal,
+ ListLowerAlpha,
+ ListUpperAlpha
+ };
+
+ ListStyle listStyle() const;
+ void setListStyle( ListStyle );
+
+ QString contexts() const;
+ void setContexts( const QString& );
+ bool allowedInContext( const QStyleSheetItem* ) const;
+
+ bool selfNesting() const;
+ void setSelfNesting( bool );
+
+ void setLineSpacing( int ls );
+ int lineSpacing() const;
+
+private:
+ void init();
+ QStyleSheetItemData* d;
+};
+
+
+#if defined(Q_TEMPLATEDLL)
+// MOC_SKIP_BEGIN
+template class Q_EXPORT QDict<QStyleSheetItem>;
+template class Q_EXPORT QValueList< QPtrVector<QStyleSheetItem> >;
+template class Q_EXPORT QPtrVector<QStyleSheetItem>;
+template class Q_EXPORT QValueList<QStyleSheetItem::ListStyle>;
+// MOC_SKIP_END
+#endif
+
+class QTextCustomItem;
+
+class Q_EXPORT QStyleSheet : public QObject
+{
+ Q_OBJECT
+public:
+ QStyleSheet( QObject *parent=0, const char *name=0 );
+ virtual ~QStyleSheet();
+
+ static QStyleSheet* defaultSheet();
+ static void setDefaultSheet( QStyleSheet* );
+
+
+ QStyleSheetItem* item( const QString& name);
+ const QStyleSheetItem* item( const QString& name) const;
+
+ void insert( QStyleSheetItem* item);
+
+ virtual QTextCustomItem* tag( const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const QMimeSourceFactory& factory,
+ bool emptyTag, QTextDocument *doc ) const;
+
+ static QString escape( const QString& );
+ static QString convertFromPlainText( const QString&, QStyleSheetItem::WhiteSpaceMode mode = QStyleSheetItem::WhiteSpacePre );
+ static bool mightBeRichText( const QString& );
+
+ virtual void scaleFont( QFont& font, int logicalSize ) const;
+
+ virtual void error( const QString& ) const;
+
+private:
+ void init();
+ QDict<QStyleSheetItem> styles;
+ QStyleSheetItem* nullstyle;
+};
+
+} // namespace Qt3
+
+#endif // QSTYLESHEET_H
diff --git a/noncore/apps/opie-write/qt3namespace.h b/noncore/apps/opie-write/qt3namespace.h
new file mode 100644
index 0000000..81c5020
--- a/dev/null
+++ b/noncore/apps/opie-write/qt3namespace.h
@@ -0,0 +1,28 @@
+#ifndef QT3NAMESPACE_H
+#define QT3NAMESPACE_H
+
+#include <qnamespace.h>
+
+#define Q_ASSERT ASSERT
+#define Q_WS_QWS
+
+#define QMemArray QArray
+#define QPtrList QList
+#define QPtrListIterator QListIterator
+#define QPtrVector QVector
+
+namespace Qt3 {
+
+enum NewAlignmentFlags {
+ AlignAuto = 0x0000,
+ AlignJustify = 0x0080,
+ AlignHorizontal_Mask = Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | AlignJustify
+};
+
+enum NewWidgetFlags {
+ WStaticContents = Qt::WNorthWestGravity
+};
+
+}
+
+#endif // QT3NAMESPACE_H
diff --git a/noncore/apps/opie-write/qtextedit.cpp b/noncore/apps/opie-write/qtextedit.cpp
new file mode 100644
index 0000000..9c5ea79
--- a/dev/null
+++ b/noncore/apps/opie-write/qtextedit.cpp
@@ -0,0 +1,4516 @@
+/****************************************************************************
+** $Id$
+**
+** Implementation of the QTextEdit class
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#include "qtextedit.h"
+
+#include "qrichtext_p.h"
+#include "qpainter.h"
+#include "qpen.h"
+#include "qbrush.h"
+#include "qpixmap.h"
+#include "qfont.h"
+#include "qcolor.h"
+#include "qstyle.h"
+#include "qsize.h"
+#include "qevent.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qlistbox.h"
+#include "qvbox.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+#include "qcolordialog.h"
+#include "qfontdialog.h"
+#include "qstylesheet.h"
+#include "qdragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+#include "qregexp.h"
+#include "qpopupmenu.h"
+
+#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
+
+using namespace Qt3;
+
+struct QUndoRedoInfoPrivate
+{
+ QTextString text;
+};
+
+namespace Qt3 {
+
+class QTextEditPrivate
+{
+public:
+ QTextEditPrivate()
+ :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE) {}
+ int id[ 7 ];
+ int preeditStart;
+ int preeditLength;
+ bool ensureCursorVisibleInShowEvent;
+};
+
+}
+
+static bool block_set_alignment = FALSE;
+
+/*!
+ \class QTextEdit qtextedit.h
+ \brief The QTextEdit widget provides a sophisticated single-page rich text editor.
+
+ \ingroup basic
+ \ingroup text
+ \mainclass
+
+ QTextEdit is an advanced WYSIWYG editor supporting rich text
+ formatting. It is optimized to handle large documents and to
+ respond quickly to user input.
+
+ QTextEdit works on paragraphs and characters. A paragraph is a
+ formatted string which is word-wrapped to fit into the width of
+ the widget. A document consists of zero or more paragraphs,
+ indexed from 0. Characters are indexed on a per-paragraph basis,
+ also indexed from 0. The words in the paragraph are aligned in
+ accordance with the paragraph's alignment(). Paragraphs are
+ separated by hard line breaks. Each character within a paragraph
+ has its own attributes, for example, font and color.
+
+ QTextEdit can display images (using QMimeSourceFactory), lists and
+ tables. If the text is too large to view within the text edit's
+ viewport, scrollbars will appear. The text edit can load both
+ plain text and HTML files (a subset of HTML 3.2 and 4). The
+ rendering style and the set of valid tags are defined by a
+ styleSheet(). Change the style sheet with \l{setStyleSheet()}; see
+ QStyleSheet for details. The images identified by image tags are
+ displayed if they can be interpreted using the text edit's
+ \l{QMimeSourceFactory}; see setMimeSourceFactory().
+
+ If you want a text browser with more navigation use QTextBrowser.
+ If you just need to display a small piece of rich text use QLabel
+ or QSimpleRichText.
+
+ If you create a new QTextEdit, and want to allow the user to edit
+ rich text, call setTextFormat(Qt::RichText) to ensure that the
+ text is treated as rich text. (Rich text uses HTML tags to set
+ text formatting attributes. See QStyleSheet for information on the
+ HTML tags that are supported.). If you don't call setTextFormat()
+ explicitly the text edit will guess from the text itself whether
+ it is rich text or plain text. This means that if the text looks
+ like HTML or XML it will probably be interpreted as rich text, so
+ you should call setTextFormat(Qt::PlainText) to preserve such
+ text.
+
+ The text edit documentation uses the following concepts:
+ \list
+ \i <i>current format</i> --
+ this is the format at the current cursor position, \e and it
+ is the format of the selected text if any.
+ \i <i>current paragraph</i> -- the paragraph which contains the
+ cursor.
+ \endlist
+
+ The text is set or replaced using setText() which deletes any
+ existing text and replaces it with the text passed in the
+ setText() call. Text can be inserted with insert(), paste() and
+ pasteSubType(). Text can also be cut(). The entire text is deleted
+ with clear() and the selected text is deleted with
+ removeSelectedText(). Selected (marked) text can also be deleted
+ with del() (which will delete the character to the right of the
+ cursor if no text is selected).
+
+ The current format's attributes are set with setItalic(),
+ setBold(), setUnderline(), setFamily() (font family),
+ setPointSize(), setColor() and setCurrentFont(). The current
+ paragraph's style is set with setParagType() and its alignment is
+ set with setAlignment().
+
+ Use setSelection() to select text. The setSelectionAttributes()
+ function is used to indicate how selected text should be
+ displayed. Use hasSelectedText() to find out if any text is
+ selected. The currently selected text's position is available
+ using getSelection() and the selected text itself is returned by
+ selectedText(). The selection can be copied to the clipboard with
+ copy(), or cut to the clipboard with cut(). It can be deleted with
+ removeSelectedText(). The entire text can be selected (or
+ unselected) using selectAll(). QTextEdit supports multiple
+ selections. Most of the selection functions operate on the default
+ selection, selection 0. If the user presses a non-selecting key,
+ e.g. a cursor key without also holding down Shift, all selections
+ are cleared.
+
+ Set and get the position of the cursor with setCursorPosition()
+ and getCursorPosition() respectively. When the cursor is moved,
+ the signals currentFontChanged(), currentColorChanged() and
+ currentAlignmentChanged() are emitted to reflect the font, color
+ and alignment at the new cursor position.
+
+ If the text changes, the textChanged() signal is emitted, and if
+ the user inserts a new line by pressing Return or Enter,
+ returnPressed() is emitted. The isModified() function will return
+ TRUE if the text has been modified.
+
+ QTextEdit provides command-based undo and redo. To set the depth
+ of the command history use setUndoDepth() which defaults to 100
+ steps. To undo or redo the last operation call undo() or redo().
+ The signals undoAvailable() and redoAvailable() indicate whether
+ the undo and redo operations can be executed.
+
+ The indent() function is used to reindent a paragraph. It is
+ useful for code editors, for example in <em>Qt Designer</em>'s
+ code editor \e{Ctrl+I} invokes the indent() function.
+
+ Loading and saving text is achieved using setText() and text(),
+ for example:
+ \code
+ QFile file( fileName ); // Read the text from a file
+ if ( file.open( IO_ReadOnly ) ) {
+ QTextStream ts( &file );
+ textEdit->setText( ts.read() );
+ }
+ \endcode
+ \code
+ QFile file( fileName ); // Write the text to a file
+ if ( file.open( IO_WriteOnly ) ) {
+ QTextStream ts( &file );
+ ts << textEdit->text();
+ textEdit->setModified( FALSE );
+ }
+ \endcode
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setWordWrap() function is used to
+ specify the kind of word wrap you want, or \c NoWrap if you don't
+ want any wrapping. Call setWordWrap() to set a fixed pixel width
+ \c FixedPixelWidth, or character column (e.g. 80 column) \c
+ FixedColumnWidth with the pixels or columns specified with
+ setWrapColumnOrWidth(). If you use word wrap to the widget's width
+ \c WidgetWidth, you can specify whether to break on whitespace or
+ anywhere with setWrapPolicy().
+
+ The background color is set differently than other widgets, using
+ setPaper(). You specify a brush style which could be a plain color
+ or a complex pixmap.
+
+ Hypertext links are automatically underlined; this can be changed
+ with setLinkUnderline(). The tab stop width is set with
+ setTabStopWidth().
+
+ The zoomIn() and zoomOut() functions can be used to resize the
+ text by increasing (decreasing for zoomOut()) the point size used.
+ Images are not affected by the zoom functions.
+
+ The lines() function returns the number of lines in the text and
+ paragraphs() returns the number of paragraphs. The number of lines
+ within a particular paragraph is returned by linesOfParagraph().
+ The length of the entire text in characters is returned by
+ length().
+
+ You can scroll to an anchor in the text, e.g. \c{<a
+ name="anchor">} with scrollToAnchor(). The find() function can be
+ used to find and select a given string within the text.
+
+ The list of key-bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i \e{Backspace} \i Delete the character to the left of the cursor
+ \row \i \e{Delete} \i Delete the character to the right of the cursor
+ \row \i \e{Ctrl+A} \i Move the cursor to the beginning of the line
+ \row \i \e{Ctrl+B} \i Move the cursor one character left
+ \row \i \e{Ctrl+C} \i Copy the marked text to the clipboard (also
+ \e{Ctrl+Insert} under Windows)
+ \row \i \e{Ctrl+D} \i Delete the character to the right of the cursor
+ \row \i \e{Ctrl+E} \i Move the cursor to the end of the line
+ \row \i \e{Ctrl+F} \i Move the cursor one character right
+ \row \i \e{Ctrl+H} \i Delete the character to the left of the cursor
+ \row \i \e{Ctrl+K} \i Delete to end of line
+ \row \i \e{Ctrl+N} \i Move the cursor one line down
+ \row \i \e{Ctrl+P} \i Move the cursor one line up
+ \row \i \e{Ctrl+V} \i Paste the clipboard text into line edit
+ (also \e{Shift+Insert} under Windows)
+ \row \i \e{Ctrl+X} \i Cut the marked text, copy to clipboard
+ (also \e{Shift+Delete} under Windows)
+ \row \i \e{Ctrl+Z} \i Undo the last operation
+ \row \i \e{Ctrl+Y} \i Redo the last operation
+ \row \i \e{LeftArrow} \i Move the cursor one character left
+ \row \i \e{Ctrl+LeftArrow} \i Move the cursor one word left
+ \row \i \e{RightArrow} \i Move the cursor one character right
+ \row \i \e{Ctrl+RightArrow} \i Move the cursor one word right
+ \row \i \e{UpArrow} \i Move the cursor one line up
+ \row \i \e{Ctrl+UpArrow} \i Move the cursor one word up
+ \row \i \e{DownArrow} \i Move the cursor one line down
+ \row \i \e{Ctrl+Down Arrow} \i Move the cursor one word down
+ \row \i \e{PageUp} \i Move the cursor one page up
+ \row \i \e{PageDown} \i Move the cursor one page down
+ \row \i \e{Home} \i Move the cursor to the beginning of the line
+ \row \i \e{Ctrl+Home} \i Move the cursor to the beginning of the text
+ \row \i \e{End} \i Move the cursor to the end of the line
+ \row \i \e{Ctrl+End} \i Move the cursor to the end of the text
+ \row \i \e{Shift+Wheel} \i Scroll the page horizontally
+ (the Wheel is the mouse wheel)
+ \row \i \e{Ctrl+Wheel} \i Zoom the text
+ \endtable
+
+ To select (mark) text hold down the Shift key whilst pressing one
+ of the movement keystrokes, for example, <i>Shift+Right Arrow</i>
+ will select the character to the right, and <i>Shift+Ctrl+Right
+ Arrow</i> will select the word to the right, etc.
+
+ By default the text edit widget operates in insert mode so all
+ text that the user enters is inserted into the text edit and any
+ text to the right of the cursor is moved out of the way. The mode
+ can be changed to overwrite, where new text overwrites any text to
+ the right of the cursor, using setOverwriteMode().
+
+ QTextEdit can also be used as read-only text viewer. Call
+ setReadOnly( TRUE ) to disable editing. A read-only QTextEdit
+ provides the same functionality as the (obsolete) QTextView.
+ (QTextView is still supplied for compatibility with old code.)
+
+ When QTextEdit is used read-only the key-bindings are limited to
+ navigation, and text may only be selected with the mouse:
+ \table
+ \header \i Keypresses \i Action
+ \row \i \e{UpArrow} \i Move one line up
+ \row \i \e{DownArrow} \i Move one line down
+ \row \i \e{LeftArrow} \i Move one character left
+ \row \i \e{RightArrow} \i Move one character right
+ \row \i \e{PageUp} \i Move one (viewport) page up
+ \row \i \e{PageDown} \i Move one (viewport) page down
+ \row \i \e{Home} \i Move to the beginning of the text
+ \row \i \e{End} \i Move to the end of the text
+ \row \i \e{Shift+Wheel} \i Scroll the page horizontally (the Wheel is the mouse wheel)
+ \row \i \e{Ctrl+Wheel} \i Zoom the text
+ \endtable
+
+ The text edit may be able to provide some meta-information. For
+ example, the documentTitle() function will return the text from
+ within HTML \c{<title>} tags.
+
+ The text displayed in a text edit has a \e context. The context is
+ a path which the text edit's QMimeSourceFactory uses to resolve
+ the locations of files and images. It is passed to the
+ mimeSourceFactory() when quering data. (See QTextEdit() and
+ \l{context()}.)
+
+ Note that we do not intend to add a full-featured web browser
+ widget to Qt (because that would easily double Qt's size and only
+ a few applications would benefit from it). The rich
+ text support in Qt is designed to provide a fast, portable and
+ efficient way to add reasonable online help facilities to
+ applications, and to provide a basis for rich text editors.
+*/
+
+/*! \enum QTextEdit::KeyboardAction
+
+ This enum is used by doKeyboardAction() to specify which action
+ should be executed:
+
+ \value ActionBackspace Delete the character to the left of the
+ cursor.
+
+ \value ActionDelete Delete the character to the right of the cursor.
+
+ \value ActionReturn Split the paragraph at the cursor position.
+
+ \value ActionKill If the cursor is not at the end of the paragraph,
+ delete the text from the cursor position until the end of the
+ paragraph. If the cursor is at the end of the paragraph, delete the
+ hard line break at the end of the paragraph - this will cause this
+ paragraph to be joined with the following paragraph.
+*/
+
+/*! \enum QTextEdit::VerticalAlignment
+
+ This enum is used to set the vertical alignment of the text.
+
+ \value AlignNormal Normal alignment
+ \value AlignSuperScript Superscript
+ \value AlignSubScript Subscript
+*/
+
+/*! \fn void QTextEdit::copyAvailable (bool yes)
+
+ This signal is emitted when text is selected or de-selected in the text
+ edit.
+
+ When text is selected this signal will be emitted with \a yes set to
+ TRUE. If no text has been selected or if the selected text is
+ de-selected this signal is emitted with \a yes set to FALSE.
+
+ If \a yes is TRUE then copy() can be used to copy the selection to the
+ clipboard. If \a yes is FALSE then copy() does nothing.
+
+ \sa selectionChanged()
+*/
+
+
+/*! \fn void QTextEdit::textChanged()
+
+ This signal is emitted whenever the text in the text edit changes.
+
+ \sa setText() append()
+ */
+
+/*! \fn void QTextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa setSelection() copyAvailable()
+*/
+
+/*! \fn QTextDocument *QTextEdit::document() const
+
+ \internal
+
+ This function returns the QTextDocument which is used by the text
+ edit.
+*/
+
+/*! \fn void QTextEdit::setDocument( QTextDocument *doc )
+
+ \internal
+
+ This function sets the QTextDocument which should be used by the text
+ edit to \a doc. This can be used, for example, if you want to
+ display a document using multiple views. You would create a
+ QTextDocument and set it to the text edits which should display it.
+ You would need to connect to the textChanged() and
+ selectionChanged() signals of all the text edits and update them all
+ accordingly (preferably with a slight delay for efficiency reasons).
+*/
+
+/*! \enum QTextEdit::CursorAction
+
+ This enum is used by moveCursor() to specify in which direction
+ the cursor should be moved:
+
+ \value MoveBackward Moves the cursor one character backward
+
+ \value MoveWordBackward Moves the cursor one word backward
+
+ \value MoveForward Moves the cursor one character forward
+
+ \value MoveWordForward Moves the cursor one word forward
+
+ \value MoveUp Moves the cursor up one line
+
+ \value MoveDown Moves the cursor down one line
+
+ \value MoveLineStart Moves the cursor to the beginning of the line
+
+ \value MoveLineEnd Moves the cursor to the end of the line
+
+ \value MoveHome Moves the cursor to the beginning of the document
+
+ \value MoveEnd Moves the cursor to the end of the document
+
+ \value MovePgUp Moves the cursor one page up
+
+ \value MovePgDown Moves the cursor one page down
+*/
+
+
+/*!
+ \property QTextEdit::overwriteMode
+ \brief the text edit's overwrite mode
+
+ If FALSE (the default) characters entered by the user are inserted
+ with any characters to the right being moved out of the way.
+ If TRUE, the editor is in overwrite mode, i.e. characters entered by
+ the user overwrite any characters to the right of the cursor position.
+*/
+
+/*! \fn void QTextEdit::setCurrentFont( const QFont &f )
+
+ Sets the font of the current format to \a f.
+
+ \sa font() setPointSize() setFamily()
+*/
+
+/*!
+ \property QTextEdit::undoDepth
+ \brief the depth of the undo history
+
+ The maximum number of steps in the undo/redo history.
+ The default is 100.
+
+ \sa undo() redo()
+*/
+
+/*! \fn void QTextEdit::undoAvailable( bool yes )
+
+ This signal is emitted when the availability of undo changes. If \a
+ yes is TRUE, then undo() will work until undoAvailable( FALSE ) is
+ next emitted.
+
+ \sa undo() undoDepth()
+*/
+
+/*! \fn void QTextEdit::modificationChanged( bool m )
+
+ This signal is emitted when the modification of the document
+ changed. If \a m is TRUE, the document was modified, otherwise the
+ modification state has been reset to unmodified.
+
+ \sa modified
+*/
+
+/*! \fn void QTextEdit::redoAvailable( bool yes )
+
+ This signal is emitted when the availability of redo changes. If \a
+ yes is TRUE, then redo() will work until redoAvailable( FALSE ) is
+ next emitted.
+
+ \sa redo() undoDepth()
+*/
+
+/*! \fn void QTextEdit::currentFontChanged( const QFont &f )
+
+ This signal is emitted if the font of the current format has changed.
+
+ The new font is \a f.
+
+ \sa setCurrentFont()
+*/
+
+/*! \fn void QTextEdit::currentColorChanged( const QColor &c )
+
+ This signal is emitted if the color of the current format has changed.
+
+ The new color is \a c.
+
+ \sa setColor()
+*/
+
+/*! \fn void QTextEdit::currentVerticalAlignmentChanged( VerticalAlignment a )
+
+ This signal is emitted if the vertical alignment of the current
+ format has changed.
+
+ The new vertical alignment is \a a.
+
+ \sa setVerticalAlignment()
+*/
+
+/*! \fn void QTextEdit::currentAlignmentChanged( int a )
+
+ This signal is emitted if the alignment of the current paragraph
+ has changed.
+
+ The new alignment is \a a.
+
+ \sa setAlignment()
+*/
+
+/*! \fn void QTextEdit::cursorPositionChanged( QTextCursor *c )
+
+ This signal is emitted if the position of the cursor changed. \a c
+ points to the text cursor object.
+
+ \sa setCursorPosition()
+*/
+
+/*! \overload void QTextEdit::cursorPositionChanged( int para, int pos )
+
+ This signal is emitted if the position of the cursor changed. \a
+ para contains the paragraph index and \a pos contains the character
+ position within the paragraph.
+
+ \sa setCursorPosition()
+*/
+
+/*! \fn void QTextEdit::returnPressed()
+
+ This signal is emitted if the user pressed the Return or the Enter key.
+*/
+
+/*!
+ \fn QTextCursor *QTextEdit::textCursor() const
+
+ Returns the text edit's text cursor.
+
+ \warning QTextCursor is not in the public API, but in special
+ circumstances you might wish to use it.
+*/
+
+/*! Constructs an empty QTextEdit with parent \a parent and name \a
+ name.
+*/
+
+QTextEdit::QTextEdit( QWidget *parent, const char *name )
+ : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ),
+ doc( new QTextDocument( 0 ) ), undoRedoInfo( doc )
+{
+ init();
+}
+
+/*!
+ Constructs a QTextEdit with parent \a parent and name \a name. The
+ text edit will display the text \a text using context \a context.
+
+ The \a context is a path which the text edit's QMimeSourceFactory
+ uses to resolve the locations of files and images. It is passed to
+ the mimeSourceFactory() when quering data.
+
+ For example if the text contains an image tag,
+ \c{<img src="image.png">}, and the context is "path/to/look/in", the
+ QMimeSourceFactory will try to load the image from
+ "path/to/look/in/image.png". If the tag was
+ \c{<img src="/image.png">}, the context will not be used (because
+ QMimeSourceFactory recognizes that we have used an absolute path)
+ and will try to load "/image.png". The context is applied in exactly
+ the same way to \e hrefs, for example,
+ \c{<a href="target.html">Target</a>}, would resolve to
+ "path/to/look/in/target.html".
+
+*/
+
+QTextEdit::QTextEdit( const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ),
+ doc( new QTextDocument( 0 ) ), undoRedoInfo( doc )
+{
+ init();
+ setText( text, context );
+}
+
+/*! \reimp */
+
+QTextEdit::~QTextEdit()
+{
+ delete undoRedoInfo.d;
+ undoRedoInfo.d = 0;
+ delete cursor;
+ delete doc;
+ delete d;
+}
+
+void QTextEdit::init()
+{
+ setFrameStyle( Sunken );
+ undoEnabled = TRUE;
+ readonly = TRUE;
+ setReadOnly( FALSE );
+ d = new QTextEditPrivate;
+ connect( doc, SIGNAL( minimumWidthChanged( int ) ),
+ this, SLOT( documentWidthChanged( int ) ) );
+
+ mousePressed = FALSE;
+ inDoubleClick = FALSE;
+ modified = FALSE;
+ onLink = QString::null;
+ overWrite = FALSE;
+ wrapMode = WidgetWidth;
+ wrapWidth = -1;
+ wPolicy = AtWhiteSpace;
+ inDnD = FALSE;
+
+ doc->setFormatter( new QTextFormatterBreakWords );
+ currentFormat = doc->formatCollection()->defaultFormat();
+ currentAlignment = Qt3::AlignAuto;
+
+ viewport()->setBackgroundMode( PaletteBase );
+ viewport()->setAcceptDrops( TRUE );
+ resizeContents( 0, doc->lastParag() ?
+ ( doc->lastParag()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 );
+
+ setKeyCompression( TRUE );
+ viewport()->setMouseTracking( TRUE );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ cursor = new QTextCursor( doc );
+
+ formatTimer = new QTimer( this );
+ connect( formatTimer, SIGNAL( timeout() ),
+ this, SLOT( formatMore() ) );
+ lastFormatted = doc->firstParag();
+
+ scrollTimer = new QTimer( this );
+ connect( scrollTimer, SIGNAL( timeout() ),
+ this, SLOT( autoScrollTimerDone() ) );
+
+ interval = 0;
+ changeIntervalTimer = new QTimer( this );
+ connect( changeIntervalTimer, SIGNAL( timeout() ),
+ this, SLOT( doChangeInterval() ) );
+
+ cursorVisible = TRUE;
+ blinkTimer = new QTimer( this );
+ connect( blinkTimer, SIGNAL( timeout() ),
+ this, SLOT( blinkCursor() ) );
+
+#ifndef QT_NO_DRAGANDDROP
+ dragStartTimer = new QTimer( this );
+ connect( dragStartTimer, SIGNAL( timeout() ),
+ this, SLOT( startDrag() ) );
+#endif
+
+
+ formatMore();
+
+ blinkCursorVisible = FALSE;
+
+ viewport()->setFocusProxy( this );
+ viewport()->setFocusPolicy( WheelFocus );
+ viewport()->installEventFilter( this );
+ installEventFilter( this );
+}
+
+void QTextEdit::paintDocument( bool drawAll, QPainter *p, int cx, int cy, int cw, int ch )
+{
+ bool drawCur = hasFocus() || viewport()->hasFocus();
+ if ( hasSelectedText() || isReadOnly() || !cursorVisible )
+ drawCur = FALSE;
+ QColorGroup g = colorGroup();
+ if ( doc->paper() )
+ g.setBrush( QColorGroup::Base, *doc->paper() );
+
+ if ( contentsY() < doc->y() ) {
+ p->fillRect( contentsX(), contentsY(), visibleWidth(), doc->y(),
+ g.brush( QColorGroup::Base ) );
+ }
+ if ( drawAll && doc->width() - contentsX() < cx + cw ) {
+ p->fillRect( doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
+ g.brush( QColorGroup::Base ) );
+ }
+
+ p->setBrushOrigin( -contentsX(), -contentsY() );
+
+ lastFormatted = doc->draw( p, cx, cy, cw, ch, g, !drawAll, drawCur, cursor );
+
+ if ( lastFormatted == doc->lastParag() )
+ resizeContents( contentsWidth(), doc->height() );
+
+ if ( contentsHeight() < visibleHeight() && ( !doc->lastParag() || doc->lastParag()->isValid() ) && drawAll )
+ p->fillRect( 0, contentsHeight(), visibleWidth(),
+ visibleHeight() - contentsHeight(), g.brush( QColorGroup::Base ) );
+}
+
+/*! \reimp */
+
+void QTextEdit::drawContents( QPainter *p, int cx, int cy, int cw, int ch )
+{
+ paintDocument( TRUE, p, cx, cy, cw, ch );
+ int v;
+ p->setPen( foregroundColor() );
+ if ( document()->isPageBreakEnabled() && ( v = document()->flow()->pageSize() ) > 0 ) {
+ int l = int(cy / v) * v;
+ while ( l < cy + ch ) {
+ p->drawLine( cx, l, cx + cw - 1, l );
+ l += v;
+ }
+ }
+
+}
+
+/*! \reimp */
+
+void QTextEdit::drawContents( QPainter * )
+{
+}
+
+/*! \reimp */
+
+bool QTextEdit::event( QEvent *e )
+{
+ if ( e->type() == QEvent::AccelOverride && !isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if ( ke->state() == NoButton || ke->state() == Keypad ) {
+ if ( ke->key() < Key_Escape ) {
+ ke->accept();
+ } else {
+ switch ( ke->key() ) {
+ case Key_Return:
+ case Key_Enter:
+ case Key_Delete:
+ case Key_Home:
+ case Key_End:
+ case Key_Backspace:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ } else if ( ke->state() & ControlButton ) {
+ switch ( ke->key() ) {
+// Those are too frequently used for application functionality
+/* case Key_A:
+ case Key_B:
+ case Key_D:
+ case Key_E:
+ case Key_F:
+ case Key_H:
+ case Key_I:
+ case Key_K:
+ case Key_N:
+ case Key_P:
+ case Key_T:
+*/
+ case Key_C:
+ case Key_V:
+ case Key_X:
+ case Key_Y:
+ case Key_Z:
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+ case Key_Tab:
+#if defined (Q_WS_WIN)
+ case Key_Insert:
+ case Key_Delete:
+#endif
+ ke->accept();
+ default:
+ break;
+ }
+ } else {
+ switch ( ke->key() ) {
+#if defined (Q_WS_WIN)
+ case Key_Insert:
+ ke->accept();
+#endif
+ default:
+ break;
+ }
+ }
+ }
+
+ if ( e->type() == QEvent::Show && d->ensureCursorVisibleInShowEvent ) {
+ sync();
+ ensureCursorVisible();
+ d->ensureCursorVisibleInShowEvent = FALSE;
+ }
+ return QWidget::event( e );
+}
+
+/*!
+ Processes the key event, \a e.
+ By default key events are used to provide keyboard navigation and
+ text editing.
+*/
+
+void QTextEdit::keyPressEvent( QKeyEvent *e )
+{
+ changeIntervalTimer->stop();
+ interval = 10;
+ bool unknown = FALSE;
+ if ( isReadOnly() ) {
+ if ( !handleReadOnlyKeyEvent( e ) )
+ QScrollView::keyPressEvent( e );
+ changeIntervalTimer->start( 100, TRUE );
+ return;
+ }
+
+
+ bool selChanged = FALSE;
+ for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection
+ selChanged = doc->removeSelection( i ) || selChanged;
+
+ if ( selChanged ) {
+ cursor->parag()->document()->nextDoubleBuffered = TRUE;
+ repaintChanged();
+ }
+
+ bool clearUndoRedoInfo = TRUE;
+
+
+ switch ( e->key() ) {
+ case Key_Left:
+ case Key_Right: {
+ // a bit hacky, but can't change this without introducing new enum values for move and keeping the
+ // correct semantics and movement for BiDi and non BiDi text.
+ CursorAction a;
+ if ( cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) )
+ a = e->state() & ControlButton ? MoveWordBackward : MoveBackward;
+ else
+ a = e->state() & ControlButton ? MoveWordForward : MoveForward;
+ moveCursor( a, e->state() & ShiftButton );
+ break;
+ }
+ case Key_Up:
+ moveCursor( e->state() & ControlButton ? MovePgUp : MoveUp, e->state() & ShiftButton );
+ break;
+ case Key_Down:
+ moveCursor( e->state() & ControlButton ? MovePgDown : MoveDown, e->state() & ShiftButton );
+ break;
+ case Key_Home:
+ moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton );
+ break;
+ case Key_End:
+ moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton );
+ break;
+ case Key_Prior:
+ moveCursor( MovePgUp, e->state() & ShiftButton );
+ break;
+ case Key_Next:
+ moveCursor( MovePgDown, e->state() & ShiftButton );
+ break;
+ case Key_Return: case Key_Enter:
+ if ( doc->hasSelection( QTextDocument::Standard, FALSE ) )
+ removeSelectedText();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ clearUndoRedoInfo = FALSE;
+ doKeyboardAction( ActionReturn );
+ emit returnPressed();
+ break;
+ case Key_Delete:
+#if defined (Q_WS_WIN)
+ if ( e->state() & ShiftButton ) {
+ cut();
+ break;
+ } else
+#endif
+ if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction( ActionDelete );
+ clearUndoRedoInfo = FALSE;
+
+ break;
+ case Key_Insert:
+ if ( e->state() & ShiftButton )
+ paste();
+ break;
+ case Key_Backspace:
+ if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) {
+ removeSelectedText();
+ break;
+ }
+
+ if ( !cursor->parag()->prev() &&
+ cursor->atParagStart() )
+ break;
+
+ doKeyboardAction( ActionBackspace );
+ clearUndoRedoInfo = FALSE;
+
+ break;
+ case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Key_F18: // Paste key on Sun keyboards
+ paste();
+ break;
+ case Key_F20: // Cut key on Sun keyboards
+ cut();
+ break;
+ default: {
+ if ( e->text().length() &&
+ ( !( e->state() & ControlButton ) &&
+ !( e->state() & AltButton ) ||
+ ( ( e->state() & ControlButton | AltButton ) == (ControlButton|AltButton) ) ) &&
+ ( !e->ascii() || e->ascii() >= 32 || e->text() == "\t" ) ) {
+ clearUndoRedoInfo = FALSE;
+ if ( e->key() == Key_Tab ) {
+ if ( textFormat() == Qt::RichText &&
+ cursor->index() == 0 && cursor->parag()->style() &&
+ cursor->parag()->style()->displayMode() ==
+ QStyleSheetItem::DisplayListItem ) {
+ cursor->parag()->incDepth();
+ drawCursor( FALSE );
+ repaintChanged();
+ drawCursor( TRUE );
+ break;
+ }
+ }
+ if ( textFormat() == Qt::RichText && ( !cursor->parag()->style() ||
+ cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayBlock ) &&
+ cursor->index() == 0 && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) {
+ setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc );
+ cursor->parag()->incDepth();
+ drawCursor( FALSE );
+ repaintChanged();
+ drawCursor( TRUE );
+ } else {
+ if ( overWrite && !cursor->atParagEnd() )
+ cursor->remove();
+ QString t = e->text();
+ QTextParag *p = cursor->parag();
+ if ( p && p->string() && p->string()->isRightToLeft() ) {
+ QChar *c = (QChar *)t.unicode();
+ int l = t.length();
+ while( l-- ) {
+ if ( c->mirrored() )
+ *c = c->mirroredChar();
+ c++;
+ }
+ }
+ insert( t, TRUE, FALSE, TRUE );
+ }
+ break;
+ } else if ( e->state() & ControlButton ) {
+ switch ( e->key() ) {
+ case Key_C: case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Key_V:
+ paste();
+ break;
+ case Key_X:
+ cut();
+ break;
+ case Key_I: case Key_T: case Key_Tab:
+ indent();
+ break;
+ case Key_A:
+#if defined(Q_WS_X11)
+ moveCursor( MoveLineStart, e->state() & ShiftButton );
+#else
+ selectAll( TRUE );
+#endif
+ break;
+ case Key_B:
+ moveCursor( MoveBackward, e->state() & ShiftButton );
+ break;
+ case Key_F:
+ moveCursor( MoveForward, e->state() & ShiftButton );
+ break;
+ case Key_D:
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction( ActionDelete );
+ clearUndoRedoInfo = FALSE;
+ break;
+ case Key_H:
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ break;
+ }
+ if ( !cursor->parag()->prev() &&
+ cursor->atParagStart() )
+ break;
+
+ doKeyboardAction( ActionBackspace );
+ clearUndoRedoInfo = FALSE;
+ break;
+ case Key_E:
+ moveCursor( MoveLineEnd, e->state() & ShiftButton );
+ break;
+ case Key_N:
+ moveCursor( MoveDown, e->state() & ShiftButton );
+ break;
+ case Key_P:
+ moveCursor( MoveUp, e->state() & ShiftButton );
+ break;
+ case Key_Z:
+ undo();
+ break;
+ case Key_Y:
+ redo();
+ break;
+ case Key_K:
+ doKeyboardAction( ActionKill );
+ break;
+#if defined(Q_WS_WIN)
+ case Key_Insert:
+ copy();
+ break;
+ case Key_Delete:
+ del();
+ break;
+#endif
+ default:
+ unknown = FALSE;
+ break;
+ }
+ } else {
+ unknown = TRUE;
+ }
+ }
+ }
+
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() );
+ if ( clearUndoRedoInfo )
+ clearUndoRedo();
+ changeIntervalTimer->start( 100, TRUE );
+ if ( unknown )
+ e->ignore();
+}
+
+/*!
+ Executes keyboard action \a action. This is normally called by
+ a key event handler.
+*/
+
+void QTextEdit::doKeyboardAction( KeyboardAction action )
+{
+ if ( isReadOnly() )
+ return;
+
+ if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough
+ return;
+
+ lastFormatted = cursor->parag();
+ drawCursor( FALSE );
+ bool doUpdateCurrentFormat = TRUE;
+
+ switch ( action ) {
+ case ActionDelete: {
+ checkUndoRedoInfo( UndoRedoInfo::Delete );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c;
+ if ( cursor->parag()->at( cursor->index() )->format() ) {
+ cursor->parag()->at( cursor->index() )->format()->addRef();
+ undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() );
+ }
+ QTextParag *old = cursor->parag();
+ if ( cursor->remove() ) {
+ if ( old != cursor->parag() && lastFormatted == old )
+ lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0;
+ undoRedoInfo.d->text += "\n";
+ }
+ } break;
+ case ActionBackspace:
+ if ( textFormat() == Qt::RichText &&
+ cursor->parag()->style() &&
+ cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayListItem &&
+ cursor->index() == 0 ) {
+ cursor->parag()->decDepth();
+ lastFormatted = cursor->parag();
+ repaintChanged();
+ drawCursor( TRUE );
+ return;
+ }
+ checkUndoRedoInfo( UndoRedoInfo::Delete );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ cursor->gotoPreviousLetter();
+ undoRedoInfo.d->text.prepend( QString( cursor->parag()->at( cursor->index() )->c ) );
+ if ( cursor->parag()->at( cursor->index() )->format() ) {
+ cursor->parag()->at( cursor->index() )->format()->addRef();
+ undoRedoInfo.d->text.at( 0 ).setFormat( cursor->parag()->at( cursor->index() )->format() );
+ }
+ undoRedoInfo.index = cursor->index();
+ if ( cursor->remove() ) {
+ undoRedoInfo.d->text.remove( 0, 1 );
+ undoRedoInfo.d->text.prepend( "\n" );
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.id = cursor->parag()->paragId();
+ }
+ lastFormatted = cursor->parag();
+ break;
+ case ActionReturn: {
+ checkUndoRedoInfo( UndoRedoInfo::Return );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ undoRedoInfo.d->text += "\n";
+ cursor->splitAndInsertEmptyParag();
+ if ( cursor->parag()->prev() ) {
+ lastFormatted = cursor->parag()->prev();
+ lastFormatted->invalidate( 0 );
+ }
+ doUpdateCurrentFormat = FALSE;
+ } break;
+ case ActionKill:
+ checkUndoRedoInfo( UndoRedoInfo::Delete );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ if ( cursor->atParagEnd() ) {
+ undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c;
+ if ( cursor->parag()->at( cursor->index() )->format() ) {
+ cursor->parag()->at( cursor->index() )->format()->addRef();
+ undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() );
+ }
+ QTextParag *old = cursor->parag();
+ if ( cursor->remove() ) {
+ if ( old != cursor->parag() && lastFormatted == old )
+ lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0;
+ undoRedoInfo.d->text += "\n";
+ }
+ } else {
+ int oldLen = undoRedoInfo.d->text.length();
+ undoRedoInfo.d->text += cursor->parag()->string()->toString().mid( cursor->index() );
+ for ( int i = cursor->index(); i < cursor->parag()->length(); ++i ) {
+ if ( cursor->parag()->at( i )->format() ) {
+ cursor->parag()->at( i )->format()->addRef();
+ undoRedoInfo.d->text.at( oldLen + i - cursor->index() ).setFormat( cursor->parag()->at( i )->format() );
+ }
+ }
+ undoRedoInfo.d->text.remove( undoRedoInfo.d->text.length() - 1, 1 );
+ cursor->killLine();
+ }
+ break;
+ }
+
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+
+ if ( doUpdateCurrentFormat )
+ updateCurrentFormat();
+ setModified();
+ emit textChanged();
+}
+
+void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, int oldLen, QTextString &text, bool fillStyles )
+{
+ c2.restoreState();
+ c1.restoreState();
+ if ( c1.parag() == c2.parag() ) {
+ for ( int i = c1.index(); i < c2.index(); ++i ) {
+ if ( c1.parag()->at( i )->format() ) {
+ c1.parag()->at( i )->format()->addRef();
+ text.at( oldLen + i - c1.index() ).setFormat( c1.parag()->at( i )->format() );
+ }
+ }
+ if ( fillStyles ) {
+ undoRedoInfo.oldAligns[ 0 ] = c1.parag()->alignment();
+ undoRedoInfo.oldStyles << c1.parag()->styleSheetItems();
+ undoRedoInfo.oldListStyles << c1.parag()->listStyle();
+ }
+ } else {
+ int lastIndex = oldLen;
+ int i;
+ for ( i = c1.index(); i < c1.parag()->length(); ++i ) {
+ if ( c1.parag()->at( i )->format() ) {
+ c1.parag()->at( i )->format()->addRef();
+ text.at( lastIndex ).setFormat( c1.parag()->at( i )->format() );
+ lastIndex++;
+ }
+ }
+ QTextParag *p = c1.parag()->next();
+ while ( p && p != c2.parag() ) {
+ for ( int i = 0; i < p->length(); ++i ) {
+ if ( p->at( i )->format() ) {
+ p->at( i )->format()->addRef();
+ text.at( i + lastIndex ).setFormat( p->at( i )->format() );
+ }
+ }
+ lastIndex += p->length();
+ p = p->next();
+ }
+ for ( i = 0; i < c2.index(); ++i ) {
+ if ( c2.parag()->at( i )->format() ) {
+ c2.parag()->at( i )->format()->addRef();
+ text.at( i + lastIndex ).setFormat( c2.parag()->at( i )->format() );
+ }
+ }
+ if ( fillStyles ) {
+ QTextParag *p = c1.parag();
+ i = 0;
+ while ( p ) {
+ if ( i < (int)undoRedoInfo.oldAligns.size() )
+ undoRedoInfo.oldAligns[ i ] = p->alignment();
+ undoRedoInfo.oldStyles << p->styleSheetItems();
+ undoRedoInfo.oldListStyles << p->listStyle();
+ if ( p == c2.parag() )
+ break;
+ p = p->next();
+ ++i;
+ }
+ }
+ }
+}
+
+/*! Removes the selection \a selNum (by default 0). This does not
+ remove the selected text.
+
+ \sa removeSelectedText()
+*/
+
+void QTextEdit::removeSelection( int selNum )
+{
+ doc->removeSelection( selNum );
+ repaintChanged();
+}
+
+/*! Deletes the selected text (i.e. the default selection's text) of
+ the selection \a selNum (by default, 0). If there is no selected text
+ nothing happens.
+
+ \sa selectedText removeSelection()
+*/
+
+void QTextEdit::removeSelectedText( int selNum )
+{
+ if ( isReadOnly() )
+ return;
+
+ QTextCursor c1 = doc->selectionStartCursor( selNum );
+ QTextCursor c2 = doc->selectionEndCursor( selNum );
+
+ // ### no support for editing tables yet
+ if ( c1.nestedDepth() || c2.nestedDepth() )
+ return;
+
+ for ( int i = 0; i < (int)doc->numSelections(); ++i ) {
+ if ( i == selNum )
+ continue;
+ doc->removeSelection( i );
+ }
+
+ drawCursor( FALSE );
+ checkUndoRedoInfo( UndoRedoInfo::RemoveSelected );
+ if ( !undoRedoInfo.valid() ) {
+ doc->selectionStart( selNum, undoRedoInfo.id, undoRedoInfo.index );
+ undoRedoInfo.d->text = QString::null;
+ }
+ int oldLen = undoRedoInfo.d->text.length();
+ undoRedoInfo.d->text = doc->selectedText( selNum, FALSE );
+ undoRedoInfo.oldAligns.resize( undoRedoInfo.oldAligns.size() + QMAX( 0, c2.parag()->paragId() - c1.parag()->paragId() + 1 ) );
+ readFormats( c1, c2, oldLen, undoRedoInfo.d->text, TRUE );
+ doc->removeSelectedText( selNum, cursor );
+ if ( cursor->isValid() ) {
+ ensureCursorVisible();
+ lastFormatted = cursor->parag();
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ clearUndoRedo();
+#if defined(Q_WS_WIN)
+ // there seems to be a problem with repainting or erasing the area
+ // of the scrollview which is not the contents on windows
+ if ( contentsHeight() < visibleHeight() )
+ viewport()->repaint( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), TRUE );
+#endif
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+ } else {
+ cursor->setDocument( doc );
+ cursor->setParag( doc->firstParag() );
+ cursor->setIndex( 0 );
+ drawCursor( TRUE );
+ viewport()->repaint( TRUE );
+ }
+ setModified();
+ emit textChanged();
+ emit selectionChanged();
+}
+
+/*! Moves the text cursor according to \a action. This is normally
+ used by some key event handler. \a select specifies whether the text
+ between the current cursor position and the new position should be
+ selected.
+*/
+
+void QTextEdit::moveCursor( CursorAction action, bool select )
+{
+ drawCursor( FALSE );
+ if ( select ) {
+ if ( !doc->hasSelection( QTextDocument::Standard ) )
+ doc->setSelectionStart( QTextDocument::Standard, cursor );
+ moveCursor( action );
+ if ( doc->setSelectionEnd( QTextDocument::Standard, cursor ) ) {
+ cursor->parag()->document()->nextDoubleBuffered = TRUE;
+ repaintChanged();
+ } else {
+ drawCursor( TRUE );
+ }
+ ensureCursorVisible();
+ emit selectionChanged();
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ } else {
+ bool redraw = doc->removeSelection( QTextDocument::Standard );
+ moveCursor( action );
+ if ( !redraw ) {
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ } else {
+ cursor->parag()->document()->nextDoubleBuffered = TRUE;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ if ( redraw ) {
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+ }
+ }
+
+ drawCursor( TRUE );
+ updateCurrentFormat();
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+/*! \overload
+*/
+
+void QTextEdit::moveCursor( CursorAction action )
+{
+ switch ( action ) {
+ case MoveBackward:
+ cursor->gotoPreviousLetter();
+ break;
+ case MoveWordBackward:
+ cursor->gotoPreviousWord();
+ break;
+ case MoveForward:
+ cursor->gotoNextLetter();
+ break;
+ case MoveWordForward:
+ cursor->gotoNextWord();
+ break;
+ case MoveUp:
+ cursor->gotoUp();
+ break;
+ case MovePgUp:
+ cursor->gotoPageUp( visibleHeight() );
+ break;
+ case MoveDown:
+ cursor->gotoDown();
+ break;
+ case MovePgDown:
+ cursor->gotoPageDown( visibleHeight() );
+ break;
+ case MoveLineStart:
+ cursor->gotoLineStart();
+ break;
+ case MoveHome:
+ cursor->gotoHome();
+ break;
+ case MoveLineEnd:
+ cursor->gotoLineEnd();
+ break;
+ case MoveEnd:
+ ensureFormatted( doc->lastParag() );
+ cursor->gotoEnd();
+ break;
+ }
+
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+ updateCurrentFormat();
+}
+
+/*! \reimp */
+
+void QTextEdit::resizeEvent( QResizeEvent *e )
+{
+ QScrollView::resizeEvent( e );
+}
+
+/*! \reimp */
+
+void QTextEdit::viewportResizeEvent( QResizeEvent *e )
+{
+ QScrollView::viewportResizeEvent( e );
+ if ( e->oldSize().width() != e->size().width() )
+ doResize();
+}
+
+static bool blockEnsureCursorVisible = FALSE;
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+
+ \sa setCursorPosition()
+*/
+
+void QTextEdit::ensureCursorVisible()
+{
+ if ( blockEnsureCursorVisible )
+ return;
+ if ( !isVisible() ) {
+ d->ensureCursorVisibleInShowEvent = TRUE;
+ return;
+ }
+ lastFormatted = cursor->parag();
+ formatMore();
+ QTextStringChar *chr = cursor->parag()->at( cursor->index() );
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ int x = cursor->parag()->rect().x() + chr->x + cursor->offsetX();
+ int y = 0; int dummy;
+ cursor->parag()->lineHeightOfChar( cursor->index(), &dummy, &y );
+ y += cursor->parag()->rect().y() + cursor->offsetY();
+ int w = 1;
+ ensureVisible( x, y + h / 2, w, h / 2 + 2 );
+}
+
+/*!
+ \internal
+*/
+void QTextEdit::drawCursor( bool visible )
+{
+ if ( !isUpdatesEnabled() ||
+ !viewport()->isUpdatesEnabled() ||
+ !cursor->parag() ||
+ !cursor->parag()->isValid() ||
+ !selectedText().isEmpty() ||
+ ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) ||
+ isReadOnly() )
+ return;
+
+ QPainter p( viewport() );
+ QRect r( cursor->topParag()->rect() );
+ cursor->parag()->setChanged( TRUE );
+ p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() );
+ QPixmap *pix = 0;
+ QColorGroup cg( colorGroup() );
+ if ( cursor->parag()->background() )
+ cg.setBrush( QColorGroup::Base, *cursor->parag()->background() );
+ else if ( doc->paper() )
+ cg.setBrush( QColorGroup::Base, *doc->paper() );
+ p.setBrushOrigin( -contentsX(), -contentsY() );
+ cursor->parag()->document()->nextDoubleBuffered = TRUE;
+ if ( !cursor->nestedDepth() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ int dist = 5;
+ if ( ( cursor->parag()->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify )
+ dist = 50;
+ int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist;
+ x = QMAX( x, 0 );
+ p.setClipRect( QRect( x - contentsX(),
+ r.y() - cursor->totalOffsetY() + cursor->y() - contentsY(), 2 * dist, h ) );
+ doc->drawParag( &p, cursor->parag(), x,
+ r.y() - cursor->totalOffsetY() + cursor->y(), 2 * dist, h, pix, cg, visible, cursor );
+ } else {
+ doc->drawParag( &p, cursor->parag(), r.x() - cursor->totalOffsetX(),
+ r.y() - cursor->totalOffsetY(), r.width(), r.height(),
+ pix, cg, visible, cursor );
+ }
+ cursorVisible = visible;
+}
+
+enum {
+ IdUndo = 0,
+ IdRedo = 1,
+ IdCut = 2,
+ IdCopy = 3,
+ IdPaste = 4,
+ IdClear = 5,
+ IdSelectAll = 6
+};
+
+/*! \reimp */
+#ifndef QT_NO_WHEELEVENT
+void QTextEdit::contentsWheelEvent( QWheelEvent *e )
+{
+ if ( isReadOnly() ) {
+ if ( e->state() & ControlButton ) {
+ if ( e->delta() > 0 )
+ zoomOut();
+ else if ( e->delta() < 0 )
+ zoomIn();
+ return;
+ }
+ }
+ QScrollView::contentsWheelEvent( e );
+}
+#endif
+
+/*! \reimp */
+
+void QTextEdit::contentsMousePressEvent( QMouseEvent *e )
+{
+ clearUndoRedo();
+ QTextCursor oldCursor = *cursor;
+ QTextCursor c = *cursor;
+ mousePos = e->pos();
+ mightStartDrag = FALSE;
+ pressedLink = QString::null;
+
+ if ( e->button() == LeftButton ) {
+ mousePressed = TRUE;
+ drawCursor( FALSE );
+ placeCursor( e->pos() );
+ ensureCursorVisible();
+
+ if ( isReadOnly() && linksEnabled() ) {
+ QTextCursor c = *cursor;
+ placeCursor( e->pos(), &c, TRUE );
+ if ( c.parag() && c.parag()->at( c.index() ) &&
+ c.parag()->at( c.index() )->isAnchor() ) {
+ pressedLink = c.parag()->at( c.index() )->anchorHref();
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ if ( doc->inSelection( QTextDocument::Standard, e->pos() ) ) {
+ mightStartDrag = TRUE;
+ drawCursor( TRUE );
+ dragStartTimer->start( QApplication::startDragTime(), TRUE );
+ dragStartPos = e->pos();
+ return;
+ }
+#endif
+
+ bool redraw = FALSE;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ if ( !( e->state() & ShiftButton ) ) {
+ redraw = doc->removeSelection( QTextDocument::Standard );
+ doc->setSelectionStart( QTextDocument::Standard, cursor );
+ } else {
+ redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw;
+ }
+ } else {
+ if ( isReadOnly() || !( e->state() & ShiftButton ) ) {
+ doc->setSelectionStart( QTextDocument::Standard, cursor );
+ } else {
+ doc->setSelectionStart( QTextDocument::Standard, &c );
+ redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw;
+ }
+ }
+
+ for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection
+ redraw = doc->removeSelection( i ) || redraw;
+
+ if ( !redraw ) {
+ drawCursor( TRUE );
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ } else if ( e->button() == MidButton ) {
+ bool redraw = doc->removeSelection( QTextDocument::Standard );
+ if ( !redraw ) {
+ drawCursor( TRUE );
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ }
+
+ if ( *cursor != oldCursor )
+ updateCurrentFormat();
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsMouseMoveEvent( QMouseEvent *e )
+{
+ if ( mousePressed ) {
+#ifndef QT_NO_DRAGANDDROP
+ if ( mightStartDrag ) {
+ dragStartTimer->stop();
+ if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() )
+ startDrag();
+#ifndef QT_NO_CURSOR
+ if ( !isReadOnly() )
+ viewport()->setCursor( ibeamCursor );
+#endif
+ return;
+ }
+#endif
+ mousePos = e->pos();
+ handleMouseMove( mousePos );
+ oldMousePos = mousePos;
+ }
+
+#ifndef QT_NO_CURSOR
+ if ( !isReadOnly() && !mousePressed ) {
+ if ( doc->hasSelection( QTextDocument::Standard ) && doc->inSelection( QTextDocument::Standard, e->pos() ) )
+ viewport()->setCursor( arrowCursor );
+ else
+ viewport()->setCursor( ibeamCursor );
+ }
+#endif
+ updateCursor( e->pos() );
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e )
+{
+ QTextCursor oldCursor = *cursor;
+ if ( scrollTimer->isActive() )
+ scrollTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ if ( dragStartTimer->isActive() )
+ dragStartTimer->stop();
+ if ( mightStartDrag ) {
+ selectAll( FALSE );
+ mousePressed = FALSE;
+ }
+#endif
+ if ( mousePressed ) {
+ mousePressed = FALSE;
+ }
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() );
+ if ( oldCursor != *cursor )
+ updateCurrentFormat();
+ inDoubleClick = FALSE;
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ if ( !onLink.isEmpty() && onLink == pressedLink && linksEnabled() ) {
+ QUrl u( doc->context(), onLink, TRUE );
+ emitLinkClicked( u.toString( FALSE, FALSE ) );
+
+ // emitting linkClicked() may result in that the cursor winds
+ // up hovering over a different valid link - check this and
+ // set the appropriate cursor shape
+ updateCursor( e->pos() );
+ }
+#endif
+ drawCursor( TRUE );
+ if ( !doc->hasSelection( QTextDocument::Standard, TRUE ) )
+ doc->removeSelection( QTextDocument::Standard );
+
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * )
+{
+ QTextCursor c1 = *cursor;
+ QTextCursor c2 = *cursor;
+ if ( cursor->index() > 0 && !cursor->parag()->at( cursor->index()-1 )->c.isSpace() )
+ c1.gotoPreviousWord();
+ if ( !cursor->parag()->at( cursor->index() )->c.isSpace() && !cursor->atParagEnd() )
+ c2.gotoNextWord();
+
+ doc->setSelectionStart( QTextDocument::Standard, &c1 );
+ doc->setSelectionEnd( QTextDocument::Standard, &c2 );
+
+ *cursor = c2;
+
+ repaintChanged();
+
+ inDoubleClick = TRUE;
+ mousePressed = TRUE;
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*! \reimp */
+
+void QTextEdit::contentsDragEnterEvent( QDragEnterEvent *e )
+{
+ if ( isReadOnly() || !QTextDrag::canDecode( e ) ) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+ inDnD = TRUE;
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsDragMoveEvent( QDragMoveEvent *e )
+{
+ if ( isReadOnly() || !QTextDrag::canDecode( e ) ) {
+ e->ignore();
+ return;
+ }
+ drawCursor( FALSE );
+ placeCursor( e->pos(), cursor );
+ drawCursor( TRUE );
+ e->acceptAction();
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * )
+{
+ inDnD = FALSE;
+}
+
+/*! \reimp */
+
+void QTextEdit::contentsDropEvent( QDropEvent *e )
+{
+ if ( isReadOnly() )
+ return;
+ inDnD = FALSE;
+ e->acceptAction();
+ QString text;
+ bool intern = FALSE;
+ if ( QTextDrag::decode( e, text ) ) {
+ if ( ( e->source() == this ||
+ e->source() == viewport() ) &&
+ e->action() == QDropEvent::Move ) {
+ removeSelectedText();
+ intern = TRUE;
+ } else {
+ doc->removeSelection( QTextDocument::Standard );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ drawCursor( FALSE );
+ placeCursor( e->pos(), cursor );
+ drawCursor( TRUE );
+ if ( !cursor->nestedDepth() ) {
+ insert( text, FALSE, TRUE, FALSE );
+ } else {
+ if ( intern )
+ undo();
+ e->ignore();
+ }
+ }
+}
+
+#endif
+
+void QTextEdit::autoScrollTimerDone()
+{
+ if ( mousePressed )
+ handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) );
+}
+
+void QTextEdit::handleMouseMove( const QPoint& pos )
+{
+ if ( !mousePressed )
+ return;
+
+ if ( !scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight() )
+ scrollTimer->start( 100, FALSE );
+ else if ( scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight() )
+ scrollTimer->stop();
+
+ drawCursor( FALSE );
+ QTextCursor oldCursor = *cursor;
+
+ placeCursor( pos );
+
+ if ( inDoubleClick ) {
+ QTextCursor cl = *cursor;
+ cl.gotoPreviousWord();
+ QTextCursor cr = *cursor;
+ cr.gotoNextWord();
+
+ int diff = QABS( oldCursor.parag()->at( oldCursor.index() )->x - mousePos.x() );
+ int ldiff = QABS( cl.parag()->at( cl.index() )->x - mousePos.x() );
+ int rdiff = QABS( cr.parag()->at( cr.index() )->x - mousePos.x() );
+
+
+ if ( cursor->parag()->lineStartOfChar( cursor->index() ) !=
+ oldCursor.parag()->lineStartOfChar( oldCursor.index() ) )
+ diff = 0xFFFFFF;
+
+ if ( rdiff < diff && rdiff < ldiff )
+ *cursor = cr;
+ else if ( ldiff < diff && ldiff < rdiff )
+ *cursor = cl;
+ else
+ *cursor = oldCursor;
+
+ }
+ ensureCursorVisible();
+
+ bool redraw = FALSE;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw;
+ }
+
+ if ( !redraw ) {
+ drawCursor( TRUE );
+ } else {
+ repaintChanged();
+ drawCursor( TRUE );
+ }
+
+ if ( currentFormat && currentFormat->key() != cursor->parag()->at( cursor->index() )->format()->key() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( cursor->parag()->at( cursor->index() )->format() );
+ if ( currentFormat->isMisspelled() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() );
+ }
+ emit currentFontChanged( currentFormat->font() );
+ emit currentColorChanged( currentFormat->color() );
+ emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() );
+ }
+
+ if ( currentAlignment != cursor->parag()->alignment() ) {
+ currentAlignment = cursor->parag()->alignment();
+ block_set_alignment = TRUE;
+ emit currentAlignmentChanged( currentAlignment );
+ block_set_alignment = FALSE;
+ }
+}
+
+/*!
+ \fn void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c )
+ Places the cursor \a c at the character which is closest to position
+ \a pos (in contents coordinates). If \a c is 0, the default text
+ cursor is used.
+
+ \sa setCursorPosition()
+*/
+
+void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c, bool link )
+{
+ if ( !c )
+ c = cursor;
+
+ c->restoreState();
+ QTextParag *s = doc->firstParag();
+ c->place( pos, s, link );
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+void QTextEdit::formatMore()
+{
+ if ( !lastFormatted )
+ return;
+
+ int bottom = contentsHeight();
+ int lastBottom = -1;
+ int to = !sender() ? 2 : 20;
+ bool firstVisible = FALSE;
+ QRect cr( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ for ( int i = 0; ( i < to || firstVisible ) && lastFormatted; ++i ) {
+ lastFormatted->format();
+ if ( i == 0 )
+ firstVisible = lastFormatted->rect().intersects( cr );
+ else if ( firstVisible )
+ firstVisible = lastFormatted->rect().intersects( cr );
+ bottom = QMAX( bottom, lastFormatted->rect().top() +
+ lastFormatted->rect().height() );
+ lastBottom = lastFormatted->rect().top() + lastFormatted->rect().height();
+ lastFormatted = lastFormatted->next();
+ if ( lastFormatted )
+ lastBottom = -1;
+ }
+
+ if ( bottom > contentsHeight() )
+ resizeContents( contentsWidth(), QMAX( doc->height(), bottom ) );
+ else if ( lastBottom != -1 && lastBottom < contentsHeight() )
+ resizeContents( contentsWidth(), QMAX( doc->height(), lastBottom ) );
+
+ if ( lastFormatted )
+ formatTimer->start( interval, TRUE );
+ else
+ interval = QMAX( 0, interval );
+}
+
+void QTextEdit::doResize()
+{
+ if ( wrapMode == FixedPixelWidth )
+ return;
+ doc->setMinimumWidth( -1 );
+ resizeContents( 0, 0 );
+ doc->setWidth( visibleWidth() );
+ doc->invalidate();
+ lastFormatted = doc->firstParag();
+ interval = 0;
+ formatMore();
+ repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE );
+}
+
+/*! \internal */
+
+void QTextEdit::doChangeInterval()
+{
+ interval = 0;
+}
+
+/*! \reimp */
+
+bool QTextEdit::eventFilter( QObject *o, QEvent *e )
+{
+ if ( o == this || o == viewport() ) {
+ if ( e->type() == QEvent::FocusIn ) {
+ blinkTimer->start( QApplication::cursorFlashTime() / 2 );
+ drawCursor( TRUE );
+
+ if ( !readonly ) {
+ // make sure the micro focus hint is updated...
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() -
+ contentsY() + frameWidth(), 0,
+ cursor->parag()->lineHeightOfChar( cursor->index() ),
+ TRUE );
+ }
+ } else if ( e->type() == QEvent::FocusOut ) {
+ blinkTimer->stop();
+ drawCursor( FALSE );
+ }
+ }
+
+ return QScrollView::eventFilter( o, e );
+}
+
+/*!
+ Inserts \a text at the current cursor position. If \a indent is TRUE,
+ the paragraph is re-indented. If \a checkNewLine is TRUE, newline
+ characters in \a text result in hard line breaks (i.e. new
+ paragraphs). If \a checkNewLine is FALSE the behaviour of the editor
+ is undefined if the \a text contains newlines. If \a removeSelected is
+ TRUE, any selected text (in selection 0) is removed before the text is
+ inserted.
+
+ \sa paste() pasteSubType()
+*/
+
+void QTextEdit::insert( const QString &text, bool indent, bool checkNewLine, bool removeSelected )
+{
+ if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough
+ return;
+ QString txt( text );
+ drawCursor( FALSE );
+ if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected )
+ removeSelectedText();
+ QTextCursor c2 = *cursor;
+ int oldLen = 0;
+
+ if ( undoEnabled && !isReadOnly() ) {
+ checkUndoRedoInfo( UndoRedoInfo::Insert );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ oldLen = undoRedoInfo.d->text.length();
+ }
+
+ lastFormatted = checkNewLine && cursor->parag()->prev() ?
+ cursor->parag()->prev() : cursor->parag();
+ QTextCursor oldCursor = *cursor;
+ cursor->insert( txt, checkNewLine );
+ if ( doc->useFormatCollection() ) {
+ doc->setSelectionStart( QTextDocument::Temp, &oldCursor );
+ doc->setSelectionEnd( QTextDocument::Temp, cursor );
+ doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format );
+ doc->removeSelection( QTextDocument::Temp );
+ }
+
+ if ( indent && ( txt == "{" || txt == "}" || txt == ":" || txt == "#" ) )
+ cursor->indent();
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+
+ if ( undoEnabled && !isReadOnly() ) {
+ undoRedoInfo.d->text += txt;
+ if ( !doc->preProcessor() ) {
+ for ( int i = 0; i < (int)txt.length(); ++i ) {
+ if ( txt[ i ] != '\n' && c2.parag()->at( c2.index() )->format() ) {
+ c2.parag()->at( c2.index() )->format()->addRef();
+ undoRedoInfo.d->text.setFormat( oldLen + i, c2.parag()->at( c2.index() )->format(), TRUE );
+ }
+ c2.gotoNextLetter();
+ }
+ }
+ }
+
+ setModified();
+ emit textChanged();
+ if ( !removeSelected ) {
+ doc->setSelectionStart( QTextDocument::Standard, &oldCursor );
+ doc->setSelectionEnd( QTextDocument::Standard, cursor );
+ repaintChanged();
+ }
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+/*! Inserts \a text in the paragraph \a para and position \a index */
+
+void QTextEdit::insertAt( const QString &text, int para, int index )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ QTextCursor tmp = *cursor;
+ cursor->setParag( p );
+ cursor->setIndex( index );
+ insert( text, FALSE, TRUE, FALSE );
+ *cursor = tmp;
+ removeSelection( QTextDocument::Standard );
+}
+
+/*! Inserts \a text as the paragraph at position \a para. If \a para
+ is -1, the text is appended.
+*/
+
+void QTextEdit::insertParagraph( const QString &text, int para )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( p ) {
+ QTextCursor tmp( doc );
+ tmp.setParag( p );
+ tmp.setIndex( 0 );
+ tmp.insert( text, TRUE );
+ tmp.splitAndInsertEmptyParag();
+ repaintChanged();
+ } else {
+ append( text );
+ }
+}
+
+/*! Removes the paragraph \a para */
+
+void QTextEdit::removeParagraph( int para )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ for ( int i = 0; i < doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+ if ( p == doc->firstParag() && p == doc->lastParag() ) {
+ p->remove( 0, p->length() - 1 );
+ repaintChanged();
+ return;
+ }
+ drawCursor( FALSE );
+ bool resetCursor = cursor->parag() == p;
+ if ( p->prev() )
+ p->prev()->setNext( p->next() );
+ else
+ doc->setFirstParag( p->next() );
+ if ( p->next() )
+ p->next()->setPrev( p->prev() );
+ else
+ doc->setLastParag( p->prev() );
+ QTextParag *start = p->next();
+ int h = p->rect().height();
+ delete p;
+ p = start;
+ int dy = -h;
+ while ( p ) {
+ p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 );
+ p->move( dy );
+ p->invalidate( 0 );
+ p->setEndState( -1 );
+ p = p->next();
+ }
+
+ if ( resetCursor ) {
+ cursor->setParag( doc->firstParag() );
+ cursor->setIndex( 0 );
+ }
+ repaintChanged();
+ drawCursor( TRUE );
+}
+
+/*!
+ Undoes the last operation.
+
+ If there is no operation to undo, e.g. there is no undo step in the
+ undo/redo history, nothing happens.
+
+ \sa undoAvailable() redo() undoDepth()
+*/
+
+void QTextEdit::undo()
+{
+ // XXX FIXME The next line is here because there may be a command
+ // that needs to be 'flushed'. The FIXME is because I am not
+ // 100% certain this is the right call to do this.
+ clearUndoRedo();
+ if ( isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled )
+ return;
+
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+
+ clearUndoRedo();
+ drawCursor( FALSE );
+ QTextCursor *c = doc->undo( cursor );
+ if ( !c ) {
+ drawCursor( TRUE );
+ return;
+ }
+ lastFormatted = 0;
+ ensureCursorVisible();
+ repaintChanged();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+/*!
+ Redoes the last operation.
+
+ If there is no operation to redo, e.g. there is no redo step in the
+ undo/redo history, nothing happens.
+
+ \sa redoAvailable() undo() undoDepth()
+*/
+
+void QTextEdit::redo()
+{
+ if ( isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled )
+ return;
+
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+
+ clearUndoRedo();
+ drawCursor( FALSE );
+ QTextCursor *c = doc->redo( cursor );
+ if ( !c ) {
+ drawCursor( TRUE );
+ return;
+ }
+ lastFormatted = 0;
+ ensureCursorVisible();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+/*!
+ Pastes the text from the clipboard into the text edit at the current
+ cursor position. Only plain text is pasted.
+
+ If there is no text in the clipboard nothing happens.
+
+ \sa pasteSubType() cut() QTextEdit::copy()
+*/
+
+void QTextEdit::paste()
+{
+#ifndef QT_NO_CLIPBOARD
+ if ( isReadOnly() )
+ return;
+ pasteSubType( "plain" );
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+#endif
+}
+
+void QTextEdit::checkUndoRedoInfo( UndoRedoInfo::Type t )
+{
+ if ( undoRedoInfo.valid() && t != undoRedoInfo.type ) {
+ clearUndoRedo();
+ }
+ undoRedoInfo.type = t;
+}
+
+/*! Repaints any paragraphs that have changed.
+
+ Although used extensively internally you shouldn't need to call this
+ yourself.
+*/
+
+void QTextEdit::repaintChanged()
+{
+ if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() )
+ return;
+ QPainter p( viewport() );
+ p.translate( -contentsX(), -contentsY() );
+ paintDocument( FALSE, &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+/*!
+ Copies the selected text (from selection 0) to the clipboard and
+ deletes it from the text edit.
+
+ If there is no selected text (in selection 0) nothing happens.
+
+ \sa QTextEdit::copy() paste() pasteSubType()
+*/
+
+void QTextEdit::cut()
+{
+ if ( isReadOnly() )
+ return;
+
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ doc->copySelectedText( QTextDocument::Standard );
+ removeSelectedText();
+ }
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = cursor->parag()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = cursor->parag()->at( cursor->index() )->format()->font();
+ setMicroFocusHint( cursor->x() - contentsX() + frameWidth(),
+ cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE );
+ }
+ }
+}
+
+/*! Copies any selected text (from selection 0) to the clipboard.
+
+ \sa hasSelectedText() copyAvailable()
+ */
+
+void QTextEdit::copy()
+{
+ if ( !doc->selectedText( QTextDocument::Standard ).isEmpty() )
+ doc->copySelectedText( QTextDocument::Standard );
+}
+
+/*!
+ Re-indents the current paragraph.
+*/
+
+void QTextEdit::indent()
+{
+ if ( isReadOnly() )
+ return;
+
+ drawCursor( FALSE );
+ if ( !doc->hasSelection( QTextDocument::Standard ) )
+ cursor->indent();
+ else
+ doc->indentSelection( QTextDocument::Standard );
+ repaintChanged();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+}
+
+/*! Reimplemented to allow tabbing through links.
+ If \a n is TRUE the tab moves the focus to the next child; if \a n
+ is FALSE the tab moves the focus to the previous child.
+ Returns TRUE if the focus was moved; otherwise returns FALSE.
+ */
+
+bool QTextEdit::focusNextPrevChild( bool n )
+{
+ if ( !isReadOnly() || !linksEnabled() )
+ return FALSE;
+ bool b = doc->focusNextPrevChild( n );
+ repaintChanged();
+ if ( b )
+ //##### this does not work with tables. The focusIndicator
+ //should really be a QTextCursor. Fix 3.1
+ makeParagVisible( doc->focusIndicator.parag );
+ return b;
+}
+
+/*!
+ \internal
+
+ This functions sets the current format to \a f. Only the fields of \a
+ f which are specified by the \a flags are used.
+*/
+
+void QTextEdit::setFormat( QTextFormat *f, int flags )
+{
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ drawCursor( FALSE );
+ QString str = doc->selectedText( QTextDocument::Standard );
+ QTextCursor c1 = doc->selectionStartCursor( QTextDocument::Standard );
+ QTextCursor c2 = doc->selectionEndCursor( QTextDocument::Standard );
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Format;
+ undoRedoInfo.id = c1.parag()->paragId();
+ undoRedoInfo.index = c1.index();
+ undoRedoInfo.eid = c2.parag()->paragId();
+ undoRedoInfo.eindex = c2.index();
+ undoRedoInfo.d->text = str;
+ readFormats( c1, c2, 0, undoRedoInfo.d->text );
+ undoRedoInfo.format = f;
+ undoRedoInfo.flags = flags;
+ clearUndoRedo();
+ doc->setFormat( QTextDocument::Standard, f, flags );
+ repaintChanged();
+ formatMore();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+ }
+ if ( currentFormat && currentFormat->key() != f->key() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( f );
+ if ( currentFormat->isMisspelled() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() );
+ }
+ emit currentFontChanged( currentFormat->font() );
+ emit currentColorChanged( currentFormat->color() );
+ emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() );
+ if ( cursor->index() == cursor->parag()->length() - 1 ) {
+ currentFormat->addRef();
+ cursor->parag()->string()->setFormat( cursor->index(), currentFormat, TRUE );
+ if ( cursor->parag()->length() == 1 ) {
+ cursor->parag()->invalidate( 0 );
+ cursor->parag()->format();
+ repaintChanged();
+ }
+ }
+ }
+}
+
+/*! \reimp */
+
+void QTextEdit::setPalette( const QPalette &p )
+{
+ QScrollView::setPalette( p );
+ if ( textFormat() == PlainText ) {
+ QTextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor( colorGroup().text() );
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ }
+}
+
+/*!
+ Sets the paragraph style of the current paragraph
+ to \a dm. If \a dm is QStyleSheetItem::DisplayListItem, the
+ type of the list item is set to \a listStyle.
+
+ \sa setAlignment()
+*/
+
+void QTextEdit::setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle )
+{
+ if ( isReadOnly() )
+ return;
+
+ drawCursor( FALSE );
+ if ( !doc->hasSelection( QTextDocument::Standard ) ) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::ParagType;
+ QValueList< QPtrVector<QStyleSheetItem> > oldStyles;
+ undoRedoInfo.oldStyles.clear();
+ undoRedoInfo.oldStyles << cursor->parag()->styleSheetItems();
+ undoRedoInfo.oldListStyles.clear();
+ undoRedoInfo.oldListStyles << cursor->parag()->listStyle();
+ undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem;
+ undoRedoInfo.listStyle = listStyle;
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.eid = cursor->parag()->paragId();
+ undoRedoInfo.d->text = " ";
+ undoRedoInfo.index = 1;
+ clearUndoRedo();
+ cursor->parag()->setList( dm == QStyleSheetItem::DisplayListItem, listStyle );
+ repaintChanged();
+ } else {
+ QTextParag *start = doc->selectionStart( QTextDocument::Standard );
+ QTextParag *end = doc->selectionEnd( QTextDocument::Standard );
+ lastFormatted = start;
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::ParagType;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem;
+ undoRedoInfo.listStyle = listStyle;
+ undoRedoInfo.oldStyles.clear();
+ undoRedoInfo.oldListStyles.clear();
+ while ( start ) {
+ undoRedoInfo.oldStyles << start->styleSheetItems();
+ undoRedoInfo.oldListStyles << start->listStyle();
+ start->setList( dm == QStyleSheetItem::DisplayListItem, listStyle );
+ if ( start == end )
+ break;
+ start = start->next();
+ }
+ undoRedoInfo.d->text = " ";
+ undoRedoInfo.index = 1;
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ }
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Sets the alignment of the current paragraph to \a a. Valid alignments
+ are \c Qt::AlignLeft, \c Qt::AlignRight, Qt::AlignJustify and
+ Qt::AlignCenter (which centers horizontally).
+
+ \sa setParagType()
+*/
+
+void QTextEdit::setAlignment( int a )
+{
+ if ( isReadOnly() || block_set_alignment )
+ return;
+
+ drawCursor( FALSE );
+ if ( !doc->hasSelection( QTextDocument::Standard ) ) {
+ if ( cursor->parag()->alignment() != a ) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Alignment;
+ QMemArray<int> oa( 1 );
+ oa[ 0 ] = cursor->parag()->alignment();
+ undoRedoInfo.oldAligns = oa;
+ undoRedoInfo.newAlign = a;
+ undoRedoInfo.id = cursor->parag()->paragId();
+ undoRedoInfo.eid = cursor->parag()->paragId();
+ undoRedoInfo.d->text = " ";
+ undoRedoInfo.index = 1;
+ clearUndoRedo();
+ cursor->parag()->setAlignment( a );
+ repaintChanged();
+ }
+ } else {
+ QTextParag *start = doc->selectionStart( QTextDocument::Standard );
+ QTextParag *end = doc->selectionEnd( QTextDocument::Standard );
+ lastFormatted = start;
+ int len = end->paragId() - start->paragId() + 1;
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Alignment;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ QMemArray<int> oa( QMAX( 0, len ) );
+ int i = 0;
+ while ( start ) {
+ if ( i < (int)oa.size() )
+ oa[ i ] = start->alignment();
+ start->setAlignment( a );
+ if ( start == end )
+ break;
+ start = start->next();
+ ++i;
+ }
+ undoRedoInfo.oldAligns = oa;
+ undoRedoInfo.newAlign = a;
+ undoRedoInfo.d->text = " ";
+ undoRedoInfo.index = 1;
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ }
+ drawCursor( TRUE );
+ if ( currentAlignment != a ) {
+ currentAlignment = a;
+ emit currentAlignmentChanged( currentAlignment );
+ }
+ setModified();
+ emit textChanged();
+}
+
+void QTextEdit::updateCurrentFormat()
+{
+ int i = cursor->index();
+ if ( i > 0 )
+ --i;
+ if ( doc->useFormatCollection() &&
+ ( !currentFormat || currentFormat->key() != cursor->parag()->at( i )->format()->key() ) ) {
+ if ( currentFormat )
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( cursor->parag()->at( i )->format() );
+ if ( currentFormat->isMisspelled() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() );
+ }
+ emit currentFontChanged( currentFormat->font() );
+ emit currentColorChanged( currentFormat->color() );
+ emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() );
+ }
+
+ if ( currentAlignment != cursor->parag()->alignment() ) {
+ currentAlignment = cursor->parag()->alignment();
+ block_set_alignment = TRUE;
+ emit currentAlignmentChanged( currentAlignment );
+ block_set_alignment = FALSE;
+ }
+}
+
+/*!
+ If \a b is TRUE sets the current format to italic; otherwise sets
+ the current format to non-italic.
+
+ \sa italic()
+*/
+
+void QTextEdit::setItalic( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setItalic( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Italic );
+}
+
+/*!
+ If \a b is TRUE sets the current format to bold; otherwise sets the
+ current format to non-bold.
+
+ \sa bold()
+*/
+
+void QTextEdit::setBold( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setBold( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Bold );
+}
+
+/*!
+ If \a b is TRUE sets the current format to underline; otherwise sets
+ the current format to non-underline.
+
+ \sa underline()
+*/
+
+void QTextEdit::setUnderline( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setUnderline( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Underline );
+}
+
+/*!
+ Sets the font family of the current format to \a fontFamily.
+
+ \sa family() setCurrentFont()
+*/
+
+void QTextEdit::setFamily( const QString &fontFamily )
+{
+ QTextFormat f( *currentFormat );
+ f.setFamily( fontFamily );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Family );
+}
+
+/*!
+ Sets the point size of the current format to \a s.
+
+ Note that if \a s is zero or negative, the behaviour of this
+ function is not defined.
+
+ \sa pointSize() setCurrentFont() setFamily()
+*/
+
+void QTextEdit::setPointSize( int s )
+{
+ QTextFormat f( *currentFormat );
+ f.setPointSize( s );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Size );
+}
+
+/*!
+ Sets the color of the current format, i.e. of the text, to \a c.
+
+ \sa color() setPaper()
+*/
+
+void QTextEdit::setColor( const QColor &c )
+{
+ QTextFormat f( *currentFormat );
+ f.setColor( c );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Color );
+}
+
+/*!
+ Sets the vertical alignment of the current format, i.e. of the text, to \a a.
+
+ \sa color() setPaper()
+*/
+
+void QTextEdit::setVerticalAlignment( VerticalAlignment a )
+{
+ QTextFormat f( *currentFormat );
+ f.setVAlign( (QTextFormat::VerticalAlignment)a );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::VAlign );
+}
+
+void QTextEdit::setFontInternal( const QFont &f_ )
+{
+ QTextFormat f( *currentFormat );
+ f.setFont( f_ );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Font );
+}
+
+
+QString QTextEdit::text() const
+{
+ if ( isReadOnly() )
+ return doc->originalText();
+ return doc->text();
+}
+
+/*!
+ \overload
+ Returns the text of paragraph \a para.
+
+ If textFormat() is \c RichText the text will contain HTML
+ formatting tags.
+*/
+
+QString QTextEdit::text( int para ) const
+{
+ return doc->text( para );
+}
+
+/*!
+ \overload
+
+ Changes the text of the text edit to the string \a text and the
+ context to \a context. Any previous text is removed.
+
+ \a text may be interpreted either as plain text or as rich text,
+ depending on the textFormat(). The default setting is \c AutoText,
+ i.e. the text edit autodetects the format from \a text.
+
+ The optional \a context is a path which the text edit's
+ QMimeSourceFactory uses to resolve the locations of files and images.
+ (See \l{QTextEdit::QTextEdit()}.) It is passed to the text edit's
+ QMimeSourceFactory when quering data.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa text(), setTextFormat()
+*/
+
+void QTextEdit::setText( const QString &text, const QString &context )
+{
+ if ( !isModified() && this->context() == context && this->text() == text )
+ return;
+
+ emit undoAvailable( FALSE );
+ emit redoAvailable( FALSE );
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ lastFormatted = 0;
+ cursor->restoreState();
+ doc->setText( text, context );
+
+ if ( wrapMode == FixedPixelWidth ) {
+ resizeContents( wrapWidth, 0 );
+ doc->setWidth( wrapWidth );
+ doc->setMinimumWidth( wrapWidth );
+ } else {
+ doc->setMinimumWidth( -1 );
+ resizeContents( 0, 0 );
+ }
+
+ cursor->setDocument( doc );
+ lastFormatted = doc->firstParag();
+ cursor->setParag( doc->firstParag() );
+ cursor->setIndex( 0 );
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+
+ if ( isModified() )
+ setModified( FALSE );
+ emit textChanged();
+ formatMore();
+ updateCurrentFormat();
+}
+
+/*!
+ \property QTextEdit::text
+ \brief the text edit's text
+
+ There is no default text.
+
+ On setting, any previous text is deleted.
+
+ The text may be interpreted either as plain text or as rich text,
+ depending on the textFormat(). The default setting is \c AutoText,
+ i.e. the text edit autodetects the format of the text.
+
+ For richtext, calling text() on an editable QTextEdit will cause the text
+ to be regenerated from the textedit. This may mean that the QString returned
+ may not be exactly the same as the one that was set.
+
+ \sa textFormat
+*/
+
+
+/*!
+ \property QTextEdit::readOnly
+ \brief whether the text edit is read-only
+
+ In a read-only text edit the user can only navigate through the text
+ and select text; modifying the text is not possible.
+
+ This property's default is FALSE.
+*/
+
+/*!
+ Finds the next occurrence of the string, \a expr. Returns TRUE if
+ \a expr is found; otherwise returns FALSE.
+
+ If \a para and \a index are both null the search begins from the
+ start of the text. If \a para and \a index are both not null, the
+ search begins from the \e *\a index character position in the \e
+ *\a para paragraph.
+
+ If \a cs is TRUE the search is case sensitive, otherwise it is
+ case insensitive. If \a wo is TRUE the search looks for whole word
+ matches only; otherwise it searches for any matching text. If \a
+ forward is TRUE (the default) the search works forward from the
+ starting position to the end of the text, otherwise it works
+ backwards to the beginning of the text.
+
+ If \a expr is found the function returns TRUE. If \a index and \a
+ para are not null, the number of the paragraph in which the first
+ character of the match was found is put into \e *\a para, and the
+ index position of that character within the paragraph is put into
+ \e *\a index.
+
+ If \a expr is not found the function returns FALSE. If \a index
+ and \a para are not null and \a expr is not found, \e *\a index
+ and \e *\a para are undefined.
+*/
+
+bool QTextEdit::find( const QString &expr, bool cs, bool wo, bool forward,
+ int *para, int *index )
+{
+ drawCursor( FALSE );
+ doc->removeSelection( QTextDocument::Standard );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ bool found = doc->find( expr, cs, wo, forward, para, index, cursor );
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ repaintChanged();
+ return found;
+}
+
+void QTextEdit::blinkCursor()
+{
+ if ( !cursorVisible )
+ return;
+ bool cv = cursorVisible;
+ blinkCursorVisible = !blinkCursorVisible;
+ drawCursor( blinkCursorVisible );
+ cursorVisible = cv;
+}
+
+/*!
+ Sets the cursor to position \a index in paragraph \a para.
+
+ \sa getCursorPosition()
+*/
+
+void QTextEdit::setCursorPosition( int para, int index )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return;
+
+ if ( index > p->length() - 1 )
+ index = p->length() - 1;
+
+ drawCursor( FALSE );
+ cursor->setParag( p );
+ cursor->setIndex( index );
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() );
+}
+
+/*!
+ This function sets the \e *\a para and \e *\a index parameters to the
+ current cursor position. \a para and \a index must be non-null int
+ pointers.
+
+ \sa setCursorPosition()
+ */
+
+void QTextEdit::getCursorPosition( int *para, int *index ) const
+{
+ if ( !para || !index )
+ return;
+ *para = cursor->parag()->paragId();
+ *index = cursor->index();
+}
+
+/*! Sets a selection which starts at position \a indexFrom in
+ paragraph \a paraFrom and ends at position \a indexTo in paragraph
+ \a paraTo. Existing selections which have a different id (selNum)
+ are not removed, existing selections which have the same id as \a
+ selNum are removed.
+
+ Uses the selection settings of selection \a selNum. If \a selNum is 0,
+ this is the default selection.
+
+ The cursor is moved to the end of the selection if \a selNum is 0,
+ otherwise the cursor position remains unchanged.
+
+ \sa getSelection() selectedText
+*/
+
+void QTextEdit::setSelection( int paraFrom, int indexFrom,
+ int paraTo, int indexTo, int selNum )
+{
+ if ( doc->hasSelection( selNum ) ) {
+ doc->removeSelection( selNum );
+ repaintChanged();
+ }
+ if ( selNum > doc->numSelections() - 1 )
+ doc->addSelection( selNum );
+ QTextParag *p1 = doc->paragAt( paraFrom );
+ if ( !p1 )
+ return;
+ QTextParag *p2 = doc->paragAt( paraTo );
+ if ( !p2 )
+ return;
+
+ if ( indexFrom > p1->length() - 1 )
+ indexFrom = p1->length() - 1;
+ if ( indexTo > p2->length() - 1 )
+ indexTo = p2->length() - 1;
+
+ drawCursor( FALSE );
+ QTextCursor c = *cursor;
+ QTextCursor oldCursor = *cursor;
+ c.setParag( p1 );
+ c.setIndex( indexFrom );
+ cursor->setParag( p2 );
+ cursor->setIndex( indexTo );
+ doc->setSelectionStart( selNum, &c );
+ doc->setSelectionEnd( selNum, cursor );
+ repaintChanged();
+ ensureCursorVisible();
+ if ( selNum != QTextDocument::Standard )
+ *cursor = oldCursor;
+ drawCursor( TRUE );
+}
+
+/*!
+ If there is a selection, \e *\a paraFrom is set to the number of the
+ paragraph in which the selection begins and \e *\a paraTo is set to
+ the number of the paragraph in which the selection ends. (They could
+ be the same.) \e *\a indexFrom is set to the index at which the
+ selection begins within \e *\a paraFrom, and \e *\a indexTo is set to
+ the index at which the selection ends within \e *\a paraTo.
+
+ If there is no selection, \e *\a paraFrom, \e *\a indexFrom, \e *\a
+ paraTo and \e *\a indexTo are all set to -1.
+
+ \a paraFrom, \a indexFrom, \a paraTo and \a indexTo must be non-null
+ int pointers.
+
+ The \a selNum is the number of the selection (multiple selections
+ are supported). It defaults to 0 (the default selection).
+
+ \sa setSelection() selectedText
+*/
+
+void QTextEdit::getSelection( int *paraFrom, int *indexFrom,
+ int *paraTo, int *indexTo, int selNum ) const
+{
+ if ( !paraFrom || !paraTo || !indexFrom || !indexTo )
+ return;
+ if ( !doc->hasSelection( selNum ) ) {
+ *paraFrom = -1;
+ *indexFrom = -1;
+ *paraTo = -1;
+ *indexTo = -1;
+ return;
+ }
+
+ doc->selectionStart( selNum, *paraFrom, *indexFrom );
+ doc->selectionEnd( selNum, *paraTo, *indexTo );
+}
+
+/*!
+ \property QTextEdit::textFormat
+ \brief the text format: rich text, plain text or auto text
+
+ The text format is one of the following:
+ \list
+ \i PlainText - all characters, except newlines, are displayed
+ verbatim, including spaces. Whenever a newline appears in the text the
+ text edit inserts a hard line break and begins a new paragraph.
+ \i RichText - rich text rendering. The available styles are
+ defined in the default stylesheet QStyleSheet::defaultSheet().
+ \i AutoText - this is the default. The text edit autodetects
+ which rendering style is best, \c PlainText or \c RichText. This is
+ done by using the QStyleSheet::mightBeRichText() function.
+ \endlist
+*/
+
+void QTextEdit::setTextFormat( TextFormat format )
+{
+ doc->setTextFormat( format );
+}
+
+Qt::TextFormat QTextEdit::textFormat() const
+{
+ return doc->textFormat();
+}
+
+/*!
+ Returns the number of paragraphs in the text; this could be 0.
+*/
+
+int QTextEdit::paragraphs() const
+{
+ return doc->lastParag()->paragId() + 1;
+}
+
+/*!
+ Returns the number of lines in paragraph \a para, or -1 if there
+ is no paragraph with index \a para.
+*/
+
+int QTextEdit::linesOfParagraph( int para ) const
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return -1;
+ return p->lines();
+}
+
+/*!
+ Returns the length of the paragraph \a para (number of
+ characters), or -1 if there is no paragraph with index \a para
+*/
+
+int QTextEdit::paragraphLength( int para ) const
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return -1;
+ return p->length() - 1;
+}
+
+/*!
+ Returns the number of lines in the text edit; this could be 0.
+
+ \warning This function may be slow. Lines change all the time
+ during word wrapping, so this function has to iterate over all the
+ paragraphs and get the number of lines from each one individually.
+*/
+
+int QTextEdit::lines() const
+{
+ QTextParag *p = doc->firstParag();
+ int l = 0;
+ while ( p ) {
+ l += p->lines();
+ p = p->next();
+ }
+
+ return l;
+}
+
+/*!
+ Returns the line number of the line in paragraph \a para in which
+ the character at position \a index appears. The \a index position is
+ relative to the beginning of the paragraph. If there is no such
+ paragraph or no such character at the \a index position (e.g. the
+ index is out of range) -1 is returned.
+*/
+
+int QTextEdit::lineOfChar( int para, int index )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return -1;
+
+ int idx, line;
+ QTextStringChar *c = p->lineStartOfChar( index, &idx, &line );
+ if ( !c )
+ return -1;
+
+ return line;
+}
+
+void QTextEdit::setModified( bool m )
+{
+ bool oldModified = modified;
+ modified = m;
+ if ( modified && doc->oTextValid )
+ doc->invalidateOriginalText();
+ if ( oldModified != modified )
+ emit modificationChanged( modified );
+}
+
+/*! \property QTextEdit::modified
+ \brief whether the document has been modified by the user
+*/
+
+bool QTextEdit::isModified() const
+{
+ return modified;
+}
+
+void QTextEdit::setModified()
+{
+ if ( !isModified() )
+ setModified( TRUE );
+}
+
+/*!
+ Returns TRUE if the current format is italic; otherwise returns FALSE.
+
+ \sa setItalic()
+*/
+
+bool QTextEdit::italic() const
+{
+ return currentFormat->font().italic();
+}
+
+/*!
+ Returns TRUE if the current format is bold; otherwise returns FALSE.
+
+ \sa setBold()
+*/
+
+bool QTextEdit::bold() const
+{
+ return currentFormat->font().bold();
+}
+
+/*!
+ Returns TRUE if the current format is underlined; otherwise returns
+ FALSE.
+
+ \sa setUnderline()
+*/
+
+bool QTextEdit::underline() const
+{
+ return currentFormat->font().underline();
+}
+
+/*!
+ Returns the font family of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+QString QTextEdit::family() const
+{
+ return currentFormat->font().family();
+}
+
+/*!
+ Returns the point size of the font of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+
+*/
+
+int QTextEdit::pointSize() const
+{
+ return currentFormat->font().pointSize();
+}
+
+/*!
+ Returns the color of the current format.
+
+ \sa setColor() setPaper()
+*/
+
+QColor QTextEdit::color() const
+{
+ return currentFormat->color();
+}
+
+/*!
+ Returns the font of the current format.
+
+ \sa setCurrentFont() setFamily() setPointSize()
+
+*/
+
+QFont QTextEdit::font() const
+{
+ return currentFormat->font();
+}
+
+/*!
+ Returns the alignment of the current paragraph.
+
+ \sa setAlignment()
+*/
+
+int QTextEdit::alignment() const
+{
+ return currentAlignment;
+}
+
+void QTextEdit::startDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ mousePressed = FALSE;
+ inDoubleClick = FALSE;
+ QDragObject *drag = new QTextDrag( doc->selectedText( QTextDocument::Standard ), viewport() );
+ if ( isReadOnly() ) {
+ drag->dragCopy();
+ } else {
+ if ( drag->drag() && QDragObject::target() != this && QDragObject::target() != viewport() )
+ removeSelectedText();
+ }
+#endif
+}
+
+/*!
+ If \a select is TRUE (the default), all the text is selected as
+ selection 0.
+ If \a select is FALSE any selected text is unselected, i.e., the
+ default selection (selection 0) is cleared.
+
+ \sa selectedText
+*/
+
+void QTextEdit::selectAll( bool select )
+{
+ if ( !select )
+ doc->removeSelection( QTextDocument::Standard );
+ else
+ doc->selectAll( QTextDocument::Standard );
+ repaintChanged();
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+}
+
+void QTextEdit::UndoRedoInfo::clear()
+{
+ if ( valid() ) {
+ if ( type == Insert || type == Return )
+ doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) );
+ else if ( type == Format )
+ doc->addCommand( new QTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) );
+ else if ( type == Alignment )
+ doc->addCommand( new QTextAlignmentCommand( doc, id, eid, newAlign, oldAligns ) );
+ else if ( type == ParagType )
+ doc->addCommand( new QTextParagTypeCommand( doc, id, eid, list, listStyle, oldStyles, oldListStyles ) );
+ else if ( type != Invalid )
+ doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) );
+ }
+ d->text = QString::null;
+ id = -1;
+ index = -1;
+ oldStyles.clear();
+ oldListStyles.clear();
+ oldAligns.resize( 0 );
+}
+
+
+/*!
+ If there is some selected text (in selection 0) it is deleted. If
+ there is no selected text (in selection 0) the character to the
+ right of the text cursor is deleted.
+
+ \sa removeSelectedText() cut()
+
+*/
+
+void QTextEdit::del()
+{
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ return;
+ }
+
+ doKeyboardAction( ActionDelete );
+}
+
+
+QTextEdit::UndoRedoInfo::UndoRedoInfo( QTextDocument *dc )
+ : type( Invalid ), doc( dc )
+{
+ d = new QUndoRedoInfoPrivate;
+ d->text = QString::null;
+ id = -1;
+ index = -1;
+}
+
+QTextEdit::UndoRedoInfo::~UndoRedoInfo()
+{
+ delete d;
+}
+
+bool QTextEdit::UndoRedoInfo::valid() const
+{
+ return d->text.length() > 0 && id >= 0 && index >= 0;
+}
+
+/*!
+ \internal
+
+ Resets the current format to the default format.
+*/
+
+void QTextEdit::resetFormat()
+{
+ setAlignment( Qt3::AlignAuto );
+ setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc );
+ setFormat( doc->formatCollection()->defaultFormat(), QTextFormat::Format );
+}
+
+/*! Returns the QStyleSheet which is currently used in this text edit.
+
+ \sa setStyleSheet()
+ */
+
+QStyleSheet* QTextEdit::styleSheet() const
+{
+ return doc->styleSheet();
+}
+
+/*! Sets the stylesheet to use with this text edit to \a styleSheet. Changes
+ will only take effect for new text added with setText() or append().
+
+ \sa styleSheet()
+ */
+
+void QTextEdit::setStyleSheet( QStyleSheet* styleSheet )
+{
+ doc->setStyleSheet( styleSheet );
+}
+
+/*!
+ \property QTextEdit::paper
+ \brief the background (paper) brush.
+
+ The brush that is currently used to draw the background of the
+ text edit. The initial setting is an empty brush.
+ */
+
+void QTextEdit::setPaper( const QBrush& pap )
+{
+ doc->setPaper( new QBrush( pap ) );
+ viewport()->setBackgroundColor( pap.color() );
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+QBrush QTextEdit::paper() const
+{
+ if ( doc->paper() )
+ return *doc->paper();
+ return QBrush();
+}
+
+/*!
+ \property QTextEdit::linkUnderline
+ \brief whether hypertext links will be underlined
+
+ If TRUE (the default) hypertext links will be displayed underlined.
+ If FALSE links will not be displayed underlined.
+*/
+
+void QTextEdit::setLinkUnderline( bool b )
+{
+ if ( b == doc->underlineLinks() )
+ return;
+ doc->setUnderlineLinks( b );
+ updateStyles();
+}
+
+bool QTextEdit::linkUnderline() const
+{
+ return doc->underlineLinks();
+}
+
+/*! Sets the text edit's mimesource factory to \a factory. See
+ QMimeSourceFactory for further details.
+
+ \sa mimeSourceFactory()
+ */
+
+void QTextEdit::setMimeSourceFactory( QMimeSourceFactory* factory )
+{
+ doc->setMimeSourceFactory( factory );
+}
+
+/*! Returns the QMimeSourceFactory which is currently used by this
+ text edit.
+
+ \sa setMimeSourceFactory()
+*/
+
+QMimeSourceFactory* QTextEdit::mimeSourceFactory() const
+{
+ return doc->mimeSourceFactory();
+}
+
+/*!
+ Returns how many pixels high the text edit needs to be to display
+ all the text if the text edit is \a w pixels wide.
+*/
+
+int QTextEdit::heightForWidth( int w ) const
+{
+ int oldw = doc->width();
+ doc->doLayout( 0, w );
+ int h = doc->height();
+ doc->setWidth( oldw );
+ doc->invalidate();
+ ( (QTextEdit*)this )->formatMore();
+ return h;
+}
+
+/*! Appends the text \a text to the end of the text edit.
+ Note that the undo/redo history is cleared by this function.
+ */
+
+void QTextEdit::append( const QString &text )
+{
+ // flush and clear the undo/redo stack if necessary
+ if ( isReadOnly() && undoRedoInfo.valid() ) {
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+ }
+ doc->removeSelection( QTextDocument::Standard );
+ TextFormat f = doc->textFormat();
+ if ( f == AutoText ) {
+ if ( QStyleSheet::mightBeRichText( text ) )
+ f = RichText;
+ else
+ f = PlainText;
+ }
+ if ( f == PlainText ) {
+ QTextCursor oldc( *cursor );
+ ensureFormatted( doc->lastParag() );
+ bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight() -
+ ( horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0 );
+ if ( !scrollToEnd )
+ blockEnsureCursorVisible = TRUE;
+ cursor->gotoEnd();
+ if ( cursor->index() > 0 )
+ cursor->splitAndInsertEmptyParag();
+ QTextCursor oldCursor2 = *cursor;
+ cursor->insert( text, TRUE );
+ if ( doc->useFormatCollection() && currentFormat != cursor->parag()->at( cursor->index() )->format() ) {
+ doc->setSelectionStart( QTextDocument::Temp, &oldCursor2 );
+ doc->setSelectionEnd( QTextDocument::Temp, cursor );
+ doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format );
+ doc->removeSelection( QTextDocument::Temp );
+ }
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ *cursor = oldc;
+ if ( !scrollToEnd )
+ blockEnsureCursorVisible = FALSE;
+ } else if ( f == RichText ) {
+ doc->setRichTextInternal( text );
+ repaintChanged();
+ }
+ setModified();
+ emit textChanged();
+}
+
+/*! \property QTextEdit::hasSelectedText
+ \brief whether some text is selected in selection 0
+ */
+
+bool QTextEdit::hasSelectedText() const
+{
+ return doc->hasSelection( QTextDocument::Standard );
+}
+
+/*!\property QTextEdit::selectedText
+ \brief The selected text (from selection 0) or an empty string if
+ there is no currently selected text (in selection 0).
+
+ The text is always returned as \c PlainText regardless of the text
+ format. In a future version of Qt an HTML subset \e may be returned
+ depending on the text format.
+
+ \sa hasSelectedText
+ */
+
+QString QTextEdit::selectedText() const
+{
+ return doc->selectedText( QTextDocument::Standard );
+}
+
+bool QTextEdit::handleReadOnlyKeyEvent( QKeyEvent *e )
+{
+ switch( e->key() ) {
+ case Key_Down:
+ setContentsPos( contentsX(), contentsY() + 10 );
+ break;
+ case Key_Up:
+ setContentsPos( contentsX(), contentsY() - 10 );
+ break;
+ case Key_Left:
+ setContentsPos( contentsX() - 10, contentsY() );
+ break;
+ case Key_Right:
+ setContentsPos( contentsX() + 10, contentsY() );
+ break;
+ case Key_PageUp:
+ setContentsPos( contentsX(), contentsY() - visibleHeight() );
+ break;
+ case Key_PageDown:
+ setContentsPos( contentsX(), contentsY() + visibleHeight() );
+ break;
+ case Key_Home:
+ setContentsPos( contentsX(), 0 );
+ break;
+ case Key_End:
+ setContentsPos( contentsX(), contentsHeight() - visibleHeight() );
+ break;
+ case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifndef QT_NO_NETWORKPROTOCOL
+ case Key_Return:
+ case Key_Enter:
+ case Key_Space: {
+ if ( !doc->focusIndicator.href.isEmpty() ) {
+ QUrl u( doc->context(), doc->focusIndicator.href, TRUE );
+ emitLinkClicked( u.toString( FALSE, FALSE ) );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ } break;
+#endif
+ default:
+ if ( e->state() & ControlButton ) {
+ switch ( e->key() ) {
+ case Key_C: case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ }
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*! Returns the context of the edit.
+ The context is a path which the text edit's QMimeSourceFactory
+ uses to resolve the locations of files and images.
+
+ \sa text
+*/
+
+QString QTextEdit::context() const
+{
+ return doc->context();
+}
+
+/*!
+ \property QTextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ For \c PlainText the title will be an empty string. For \c RichText
+ the title will be the text between the \c{<title>} tags, if present,
+ otherwise an empty string.
+*/
+
+QString QTextEdit::documentTitle() const
+{
+ return doc->attributes()[ "title" ];
+}
+
+void QTextEdit::makeParagVisible( QTextParag *p )
+{
+ setContentsPos( contentsX(), QMIN( p->rect().y(), contentsHeight() - visibleHeight() ) );
+}
+
+/*! Scrolls the text edit to make the text at the anchor called \a name
+ visible, if it can be found in the document. If the anchor isn't found
+ no scrolling will occur. An anchor is defined using the HTML anchor
+ tag, e.g. \c{<a name="target">}.
+*/
+
+void QTextEdit::scrollToAnchor( const QString& name )
+{
+ if ( name.isEmpty() )
+ return;
+ sync();
+ QTextCursor cursor( doc );
+ QTextParag* last = doc->lastParag();
+ do {
+ QTextStringChar* c = cursor.parag()->at( cursor.index() );
+ if( c->isAnchor() ) {
+ QString a = c->anchorName();
+ if ( a == name ||
+ (a.contains( '#' ) && QStringList::split( '#', a ).contains( name ) ) ) {
+ setContentsPos( contentsX(), QMIN( cursor.parag()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) );
+ return;
+ }
+ }
+ cursor.gotoNextLetter();
+ } while( cursor.parag() != last || !cursor.atParagEnd() );
+}
+
+/*! If there is an anchor at position \a pos (in contents
+ coordinates), its name is returned, otherwise an empty string is
+ returned.
+*/
+
+QString QTextEdit::anchorAt( const QPoint& pos )
+{
+ QTextCursor c( doc );
+ placeCursor( pos, &c );
+ return c.parag()->at( c.index() )->anchorHref();
+}
+
+void QTextEdit::documentWidthChanged( int w )
+{
+ resizeContents( QMAX( visibleWidth(), w), contentsHeight() );
+}
+
+/*!
+ Updates all the rendering styles used to display the text. You will
+ probably want to call this function after calling setStyleSheet().
+*/
+
+void QTextEdit::updateStyles()
+{
+ doc->updateStyles();
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+void QTextEdit::setDocument( QTextDocument *dc )
+{
+ if ( dc == doc )
+ return;
+ doc = dc;
+ cursor->setDocument( doc );
+ clearUndoRedo();
+ lastFormatted = 0;
+}
+
+#ifndef QT_NO_CLIPBOARD
+
+/*!
+ Pastes the text with format \a subtype from the clipboard into the
+ text edit at the current cursor position. The \a subtype can be
+ "plain" or "html".
+
+ If there is no text with format \a subtype in the clipboard nothing
+ happens.
+
+ \sa paste() cut() QTextEdit::copy()
+*/
+void QTextEdit::pasteSubType( const QCString& subtype )
+{
+ QCString st = subtype;
+ QString t = QApplication::clipboard()->text(st);
+ if ( !t.isEmpty() ) {
+#if defined(Q_OS_WIN32)
+ // Need to convert CRLF to LF
+ int index = t.find( QString::fromLatin1("\r\n"), 0 );
+ while ( index != -1 ) {
+ t.replace( index, 2, QChar('\n') );
+ index = t.find( "\r\n", index );
+ }
+#elif defined(Q_OS_MAC)
+ //need to convert CR to LF
+ for( unsigned int index = 0; index < t.length(); index++ )
+ if(t[index] == '\r')
+ t[index] = '\n';
+#endif
+ for ( int i=0; (uint) i<t.length(); i++ ) {
+ if ( t[ i ] < ' ' && t[ i ] != '\n' && t[ i ] != '\t' )
+ t[ i ] = ' ';
+ }
+ if ( !t.isEmpty() )
+ insert( t, FALSE, TRUE, TRUE );
+ }
+}
+
+#ifndef QT_NO_MIMECLIPBOARD
+/*!
+ Prompts the user to choose a type from a list of text types available,
+ then copies text from the clipboard (if there is any) into the text
+ edit at the current text cursor position. Any selected text (in
+ selection 0) is first deleted.
+*/
+void QTextEdit::pasteSpecial( const QPoint& pt )
+{
+ QCString st = pickSpecial( QApplication::clipboard()->data(), TRUE, pt );
+ if ( !st.isEmpty() )
+ pasteSubType( st );
+}
+#endif
+#ifndef QT_NO_MIME
+QCString QTextEdit::pickSpecial( QMimeSource* ms, bool always_ask, const QPoint& pt )
+{
+ if ( ms ) {
+#ifndef QT_NO_POPUPMENU
+ QPopupMenu popup( this, "qt_pickspecial_menu" );
+ QString fmt;
+ int n = 0;
+ QDict<void> done;
+ for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) {
+ int semi = fmt.find( ";" );
+ if ( semi >= 0 )
+ fmt = fmt.left( semi );
+ if ( fmt.left( 5 ) == "text/" ) {
+ fmt = fmt.mid( 5 );
+ if ( !done.find( fmt ) ) {
+ done.insert( fmt,(void*)1 );
+ popup.insertItem( fmt, i );
+ n++;
+ }
+ }
+ }
+ if ( n ) {
+ int i = n ==1 && !always_ask ? popup.idAt( 0 ) : popup.exec( pt );
+ if ( i >= 0 )
+ return popup.text(i).latin1();
+ }
+#else
+ QString fmt;
+ for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) {
+ int semi = fmt.find( ";" );
+ if ( semi >= 0 )
+ fmt = fmt.left( semi );
+ if ( fmt.left( 5 ) == "text/" ) {
+ fmt = fmt.mid( 5 );
+ return fmt.latin1();
+ }
+ }
+#endif
+ }
+ return QCString();
+}
+#endif // QT_NO_MIME
+#endif // QT_NO_CLIPBOARD
+
+/*! \enum QTextEdit::WordWrap
+
+ This enum defines the QTextEdit's word wrap modes. The following
+ values are valid:
+
+ \value NoWrap Do not wrap the text.
+
+ \value WidgetWidth Wrap the text at the current width of the
+ widget (this is the default). Wrapping is at whitespace by default;
+ this can be changed with setWrapPolicy().
+
+ \value FixedPixelWidth Wrap the text at a fixed number of pixels from
+ the widget's left side. The number of pixels is set with
+ wrapColumnOrWidth().
+
+ \value FixedColumnWidth Wrap the text at a fixed number of character
+ columns from the widget's left side. The number of characters is set
+ with wrapColumnOrWidth().
+ This is useful if you need formatted text that can also be
+ displayed gracefully on devices with monospaced fonts, for example a
+ standard VT100 terminal, where you might set wrapColumnOrWidth() to
+ 80.
+
+ \sa setWordWrap() wordWrap()
+*/
+
+/*!
+ \property QTextEdit::wordWrap
+ \brief the word wrap mode
+
+ The default mode is \c WidgetWidth which causes words to be wrapped
+ at the right edge of the text edit. Wrapping occurs at whitespace,
+ keeping whole words intact. If you want wrapping to occur within
+ words use setWrapPolicy(). If you set a wrap mode of \c
+ FixedPixelWidth or \c FixedColumnWidth you should also call
+ setWrapColumnOrWidth() with the width you want.
+
+ \sa WordWrap, wrapColumnOrWidth, wrapPolicy,
+*/
+
+void QTextEdit::setWordWrap( WordWrap mode )
+{
+ if ( wrapMode == mode )
+ return;
+ wrapMode = mode;
+ switch ( mode ) {
+ case NoWrap:
+ document()->formatter()->setWrapEnabled( FALSE );
+ document()->formatter()->setWrapAtColumn( -1 );
+ doc->setWidth( visibleWidth() );
+ doc->setMinimumWidth( -1 );
+ doc->invalidate();
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ lastFormatted = doc->firstParag();
+ interval = 0;
+ formatMore();
+ break;
+ case WidgetWidth:
+ document()->formatter()->setWrapEnabled( TRUE );
+ document()->formatter()->setWrapAtColumn( -1 );
+ doResize();
+ break;
+ case FixedPixelWidth:
+ document()->formatter()->setWrapEnabled( TRUE );
+ document()->formatter()->setWrapAtColumn( -1 );
+ if ( wrapWidth < 0 )
+ wrapWidth = 200;
+ setWrapColumnOrWidth( wrapWidth );
+ break;
+ case FixedColumnWidth:
+ if ( wrapWidth < 0 )
+ wrapWidth = 80;
+ document()->formatter()->setWrapEnabled( TRUE );
+ document()->formatter()->setWrapAtColumn( wrapWidth );
+ setWrapColumnOrWidth( wrapWidth );
+ break;
+ }
+}
+
+QTextEdit::WordWrap QTextEdit::wordWrap() const
+{
+ return wrapMode;
+}
+
+/*!
+ \property QTextEdit::wrapColumnOrWidth
+ \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
+
+ If the wrap mode is \c FixedPixelWidth, the value is the number
+ of pixels from the left edge of the text edit at which text should
+ be wrapped. If the wrap mode is \c FixedColumnWidth, the value is
+ the column number (in character columns) from the left edge of the
+ text edit at which text should be wrapped.
+
+ \sa wordWrap
+ */
+void QTextEdit::setWrapColumnOrWidth( int value )
+{
+ wrapWidth = value;
+ if ( wrapMode == FixedColumnWidth ) {
+ document()->formatter()->setWrapAtColumn( wrapWidth );
+ resizeContents( 0, 0 );
+ doc->setWidth( visibleWidth() );
+ doc->setMinimumWidth( -1 );
+ } else if (wrapMode == FixedPixelWidth ) {
+ document()->formatter()->setWrapAtColumn( -1 );
+ resizeContents( wrapWidth, 0 );
+ doc->setWidth( wrapWidth );
+ doc->setMinimumWidth( wrapWidth );
+ } else {
+ return;
+ }
+ doc->invalidate();
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ lastFormatted = doc->firstParag();
+ interval = 0;
+ formatMore();
+}
+
+int QTextEdit::wrapColumnOrWidth() const
+{
+ if ( wrapMode == WidgetWidth )
+ return visibleWidth();
+ return wrapWidth;
+}
+
+
+/*! \enum QTextEdit::WrapPolicy
+
+ This enum defines where text can be wrapped in word wrap mode.
+
+ The following values are valid:
+ \value AtWhiteSpace Break lines at whitespace, e.g. spaces or
+ newlines.
+ \value Anywhere Break anywhere, including within words.
+ \value AtWordBoundary Don't use this deprecated value (it is a
+ synonym for AtWhiteSpace which you should use instead).
+
+ \sa setWrapPolicy()
+*/
+
+/*!
+ \property QTextEdit::wrapPolicy
+ \brief the word wrap policy, at whitespace or anywhere
+
+ Defines where text can be wrapped when word wrap mode is not
+ \c NoWrap. The choices are \c AtWhiteSpace (the default) and \c
+ Anywhere.
+
+ \sa wordWrap
+ */
+
+void QTextEdit::setWrapPolicy( WrapPolicy policy )
+{
+ if ( wPolicy == policy )
+ return;
+ wPolicy = policy;
+ QTextFormatter *formatter;
+ if ( policy == AtWhiteSpace )
+ formatter = new QTextFormatterBreakWords;
+ else
+ formatter = new QTextFormatterBreakInWords;
+ formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() );
+ formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) );
+ document()->setFormatter( formatter );
+ doc->invalidate();
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ lastFormatted = doc->firstParag();
+ interval = 0;
+ formatMore();
+}
+
+QTextEdit::WrapPolicy QTextEdit::wrapPolicy() const
+{
+ return wPolicy;
+}
+
+/*!
+ Deletes all the text in the text edit.
+
+ \sa cut() removeSelectedText() setText()
+
+*/
+
+void QTextEdit::clear()
+{
+ // make clear undoable
+ doc->selectAll( QTextDocument::Temp );
+ removeSelectedText( QTextDocument::Temp );
+
+ setContentsPos( 0, 0 );
+ if ( cursor->isValid() )
+ cursor->restoreState();
+ doc->clear( TRUE );
+ cursor->setDocument( doc );
+ cursor->setParag( doc->firstParag() );
+ cursor->setIndex( 0 );
+ lastFormatted = 0;
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() );
+}
+
+int QTextEdit::undoDepth() const
+{
+ return document()->undoDepth();
+}
+
+/*!
+ \property QTextEdit::length
+ \brief the number of characters in the text
+
+*/
+
+int QTextEdit::length() const
+{
+ return document()->length();
+}
+
+/*!
+ \property QTextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+
+*/
+
+int QTextEdit::tabStopWidth() const
+{
+ return document()->tabStopWidth();
+}
+
+void QTextEdit::setUndoDepth( int d )
+{
+ document()->setUndoDepth( d );
+}
+
+void QTextEdit::setTabStopWidth( int ts )
+{
+ document()->setTabStops( ts );
+ doc->invalidate();
+ lastFormatted = doc->firstParag();
+ interval = 0;
+ formatMore();
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+/*! \reimp */
+
+QSize QTextEdit::sizeHint() const
+{
+ // ### calculate a reasonable one
+ return QSize( 100, 100 );
+}
+
+void QTextEdit::clearUndoRedo()
+{
+ undoRedoInfo.clear();
+ emit undoAvailable( doc->commands()->isUndoAvailable() );
+ emit redoAvailable( doc->commands()->isRedoAvailable() );
+}
+
+/*! This function gets the format of the character at position \a
+ index in paragraph \a para. Sets \a font to the character's font, \a
+ color to the character's color and \a verticalAlignment to the
+ character's vertical alignment.
+
+ Returns FALSE if \a para or \a index is out of range otherwise
+ returns TRUE.
+*/
+
+bool QTextEdit::getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment )
+{
+ if ( !font || !color )
+ return FALSE;
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return FALSE;
+ if ( index < 0 || index >= p->length() )
+ return FALSE;
+ *font = p->at( index )->format()->font();
+ *color = p->at( index )->format()->color();
+ *verticalAlignment = (VerticalAlignment)p->at( index )->format()->vAlign();
+ return TRUE;
+}
+
+/*! This function gets the format of the paragraph \a para. Sets \a
+ font to the paragraphs's font, \a color to the paragraph's color, \a
+ verticalAlignment to the paragraph's vertical alignment, \a
+ alignment to the paragraph's alignment, \a displayMode to the
+ paragraph's display mode, \a listStyle to the paragraph's list style
+ (if the display mode is QStyleSheetItem::DisplayListItem) and \a
+ listDepth to the depth of the list (if the display mode is
+ QStyleSheetItem::DisplayListItem).
+
+ Returns FALSE if \a para is out of range otherwise returns TRUE.
+*/
+
+bool QTextEdit::getParagraphFormat( int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ QStyleSheetItem::DisplayMode *displayMode,
+ QStyleSheetItem::ListStyle *listStyle,
+ int *listDepth )
+{
+ if ( !font || !color || !alignment || !displayMode || !listStyle )
+ return FALSE;
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return FALSE;
+ *font = p->paragFormat()->font();
+ *color = p->paragFormat()->color();
+ *verticalAlignment = (VerticalAlignment)p->paragFormat()->vAlign();
+ *alignment = p->alignment();
+ *displayMode = p->style() ? p->style()->displayMode() : QStyleSheetItem::DisplayBlock;
+ *listStyle = p->listStyle();
+ *listDepth = p->listDepth();
+ return TRUE;
+}
+
+
+
+/*!
+
+ This function is called to create a right mouse button popup menu
+ at the document position \a pos. If you want to create a custom
+ popup menu, reimplement this function and return the created
+ popup menu. Ownership of the popup menu is transferred to the
+ caller.
+*/
+
+QPopupMenu *QTextEdit::createPopupMenu( const QPoint& pos )
+{
+#ifndef QT_NO_POPUPMENU
+ QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" );
+ if ( !isReadOnly() ) {
+ d->id[ IdUndo ] = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) );
+ d->id[ IdRedo ] = popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) );
+ popup->insertSeparator();
+ }
+#ifndef QT_NO_CLIPBOARD
+ if ( !isReadOnly() )
+ d->id[ IdCut ] = popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) );
+ d->id[ IdCopy ] = popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) );
+ if ( !isReadOnly() )
+ d->id[ IdPaste ] = popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) );
+#endif
+ if ( !isReadOnly() ) {
+ d->id[ IdClear ] = popup->insertItem( tr( "Clear" ) );
+ popup->insertSeparator();
+ }
+#if defined(Q_WS_X11)
+ d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) );
+#else
+ d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) + ACCEL_KEY( A ) );
+#endif
+ popup->setItemEnabled( d->id[ IdUndo ], !isReadOnly() && doc->commands()->isUndoAvailable() );
+ popup->setItemEnabled( d->id[ IdRedo ], !isReadOnly() && doc->commands()->isRedoAvailable() );
+#ifndef QT_NO_CLIPBOARD
+ popup->setItemEnabled( d->id[ IdCut ], !isReadOnly() && doc->hasSelection( QTextDocument::Standard, TRUE ) );
+ popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( QTextDocument::Standard, TRUE ) );
+ popup->setItemEnabled( d->id[ IdPaste ], !isReadOnly() && !QApplication::clipboard()->text().isEmpty() );
+#endif
+ popup->setItemEnabled( d->id[ IdClear ], !isReadOnly() && !text().isEmpty() );
+ popup->setItemEnabled( d->id[ IdSelectAll ], (bool)text().length() );
+ return popup;
+#else
+ return 0;
+#endif
+}
+
+/*! \overload
+ This function is called to create a right mouse button popup menu.
+ If you want to create a custom popup menu, reimplement this function
+ and return the created popup menu. Ownership of the popup menu is
+ transferred to the caller.
+*/
+
+QPopupMenu *QTextEdit::createPopupMenu()
+{
+ return 0;
+}
+
+/*! \reimp */
+
+void QTextEdit::setFont( const QFont &f )
+{
+ QFont old( QScrollView::font() );
+ QScrollView::setFont( f );
+ doc->setMinimumWidth( -1 );
+
+ // ### that is a bit hacky
+ static short diff = 1;
+ diff *= -1;
+ doc->setWidth( visibleWidth() + diff );
+
+ int s = f.pointSize();
+ bool usePixels = FALSE;
+ if ( s == -1 ) {
+ s = f.pixelSize();
+ usePixels = TRUE;
+ }
+ doc->updateFontSizes( s, usePixels );
+ doc->updateFontAttributes( f, old );
+ lastFormatted = doc->firstParag();
+ formatMore();
+ repaintChanged();
+}
+
+/*! \fn QTextEdit::zoomIn()
+
+ \overload
+
+ Zooms in on the text by by making the base font size one
+ point larger and recalculating all font sizes. This does not change
+ the size of any images.
+
+ \sa zoomOut()
+
+*/
+
+/*! \fn QTextEdit::zoomOut()
+
+ \overload
+
+ Zooms out on the text by by making the base font size one
+ point smaller and recalculating all font sizes. This does not change
+ the size of any images.
+
+ \sa zoomIn()
+*/
+
+
+/*!
+ Zooms in on the text by by making the base font size \a range
+ points larger and recalculating all font sizes. This does not change
+ the size of any images.
+
+ \sa zoomOut()
+*/
+
+void QTextEdit::zoomIn( int range )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( f.pointSize() + range );
+ setFont( f );
+}
+
+/*! Zooms out on the text by making the base font size \a range
+ points smaller and recalculating all font sizes. This does not
+ change the size of any images.
+
+ \sa zoomIn()
+*/
+
+void QTextEdit::zoomOut( int range )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( QMAX( 1, f.pointSize() - range ) );
+ setFont( f );
+}
+
+/*! Zooms the text by making the base font size \a size points and
+ recalculating all font sizes. This does not change the size of any
+ images.
+*/
+
+void QTextEdit::zoomTo( int size )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( size );
+ setFont( f );
+}
+
+/*!
+ \internal
+
+ QTextEdit is optimized for large amounts text. One of its
+ optimizations is to format only the visible text, formatting the rest
+ on demand, e.g. as the user scrolls, so you don't usually need to
+ call this function.
+
+ In some situations you may want to force the whole text
+ to be formatted. For example, if after calling setText(), you wanted
+ to know the height of the document (using contentsHeight()), you
+ would call this function first.
+*/
+
+void QTextEdit::sync()
+{
+ QTextParag *p = lastFormatted;
+ while ( p ) {
+ p->format();
+ p = p->next();
+ }
+ resizeContents( contentsWidth(), doc->height() );
+}
+
+/*! \reimp */
+
+void QTextEdit::setEnabled( bool b )
+{
+ QScrollView::setEnabled( b );
+ if ( !b ) {
+ blinkTimer->stop();
+ drawCursor( FALSE );
+ }
+ if ( textFormat() == PlainText ) {
+ QTextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor( colorGroup().text() );
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ }
+ if ( b ) {
+ blinkTimer->start( QApplication::cursorFlashTime() / 2 );
+ drawCursor( TRUE );
+ }
+}
+
+/*!
+ Sets the background color of selection number \a selNum to \a back and
+ specifies whether the text of this selection should be inverted with \a
+ invertText.
+
+ This only works for \a selNum > 0. The default selection (\a selNum ==
+ 0) gets its attributes from the colorGroup() of this widget.
+*/
+
+void QTextEdit::setSelectionAttributes( int selNum, const QColor &back, bool invertText )
+{
+ if ( selNum < 1 )
+ return;
+ if ( selNum > doc->numSelections() )
+ doc->addSelection( selNum );
+ doc->setSelectionColor( selNum, back );
+ doc->setInvertSelectionText( selNum, invertText );
+}
+
+/*! \reimp */
+void QTextEdit::windowActivationChange( bool )
+{
+ if ( !isVisible() )
+ return;
+
+ if ( palette().active() != palette().inactive() )
+ updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+void QTextEdit::setReadOnly( bool b )
+{
+ if ( readonly == b )
+ return;
+ readonly = b;
+#ifndef QT_NO_CURSOR
+ if ( readonly )
+ viewport()->setCursor( arrowCursor );
+ else
+ viewport()->setCursor( ibeamCursor );
+#endif
+}
+
+/*! Scrolls to the bottom of the document and does formatting if
+ required */
+
+void QTextEdit::scrollToBottom()
+{
+ sync();
+ setContentsPos( contentsX(), contentsHeight() - visibleHeight() );
+}
+
+/*! Returns the rectangle of the paragraph \a para in contents
+ coordinates, or an invalid rectangle if \a para is out of range.
+*/
+
+QRect QTextEdit::paragraphRect( int para ) const
+{
+ QTextEdit *that = (QTextEdit *)this;
+ that->sync();
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return QRect( -1, -1, -1, -1 );
+ return p->rect();
+}
+
+/*!
+ Returns the paragraph which is at position \a pos (in contents
+ coordinates), or -1 if there is no paragraph with index \a pos.
+*/
+
+int QTextEdit::paragraphAt( const QPoint &pos ) const
+{
+ QTextCursor c( doc );
+ c.place( pos, doc->firstParag() );
+ if ( c.parag() )
+ return c.parag()->paragId();
+ return -1;
+}
+
+/*!
+ Returns the index of the character (relative to its paragraph) at
+ position \a pos (in contents coordinates). If \a para is not null,
+ \e *\a para is set to this paragraph. If there is no character at
+ \a pos, -1 is returned.
+*/
+
+int QTextEdit::charAt( const QPoint &pos, int *para ) const
+{
+ QTextCursor c( doc );
+ c.place( pos, doc->firstParag() );
+ if ( c.parag() ) {
+ if ( para )
+ *para = c.parag()->paragId();
+ return c.index();
+ }
+ return -1;
+}
+
+/*! Sets the background color of the paragraph \a para to \a bg */
+
+void QTextEdit::setParagraphBackgroundColor( int para, const QColor &bg )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ p->setBackgroundColor( bg );
+ repaintChanged();
+}
+
+/*! Clears the background color of the paragraph \a para, so that the
+ default color is used again.
+*/
+
+void QTextEdit::clearParagraphBackground( int para )
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ p->clearBackgroundColor();
+ repaintChanged();
+}
+
+/*! Returns the background color of the paragraph \a para or an
+ invalid color if \a para is out of range or the paragraph has no
+ background set
+*/
+
+QColor QTextEdit::paragraphBackgroundColor( int para ) const
+{
+ QTextParag *p = doc->paragAt( para );
+ if ( !p )
+ return QColor();
+ QColor *c = p->backgroundColor();
+ if ( c )
+ return *c;
+ return QColor();
+}
+
+/*! \property QTextEdit::undoRedoEnabled
+ \brief whether undo/redo is enabled
+
+ The default is TRUE.
+*/
+
+void QTextEdit::setUndoRedoEnabled( bool b )
+{
+ undoEnabled = b;
+}
+
+bool QTextEdit::isUndoRedoEnabled() const
+{
+ return undoEnabled;
+}
+
+/*! Returns whether undo is available */
+
+bool QTextEdit::isUndoAvailable() const
+{
+ return doc->commands()->isUndoAvailable() || undoRedoInfo.valid();
+}
+
+/*! Returns whether redo is available */
+
+bool QTextEdit::isRedoAvailable() const
+{
+ return doc->commands()->isRedoAvailable();
+}
+
+void QTextEdit::ensureFormatted( QTextParag *p )
+{
+ while ( !p->isValid() ) {
+ if ( !lastFormatted )
+ return;
+ formatMore();
+ }
+}
+
+/*! \internal */
+void QTextEdit::updateCursor( const QPoint & pos )
+{
+ if ( isReadOnly() && linksEnabled() ) {
+ QTextCursor c = *cursor;
+ placeCursor( pos, &c, TRUE );
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ if ( c.parag() && c.parag()->at( c.index() ) &&
+ c.parag()->at( c.index() )->isAnchor() &&
+ !c.parag()->at( c.index() )->anchorHref().isEmpty() ) {
+ if ( c.index() < c.parag()->length() - 1 )
+ onLink = c.parag()->at( c.index() )->anchorHref();
+ else
+ onLink = QString::null;
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor );
+#endif
+ QUrl u( doc->context(), onLink, TRUE );
+ emitHighlighted( u.toString( FALSE, FALSE ) );
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ onLink = QString::null;
+ emitHighlighted( QString::null );
+ }
+#endif
+ }
+}
+
+void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c )
+{
+ placeCursor( pos, c, FALSE );
+}
diff --git a/noncore/apps/opie-write/qtextedit.h b/noncore/apps/opie-write/qtextedit.h
new file mode 100644
index 0000000..b4e5701
--- a/dev/null
+++ b/noncore/apps/opie-write/qtextedit.h
@@ -0,0 +1,448 @@
+/****************************************************************************
+** $Id$
+**
+** Definition of the QTextEdit class
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+#ifndef QTEXTEDIT_H
+#define QTEXTEDIT_H
+
+#ifndef QT_H
+#include "qscrollview.h"
+#include "qstylesheet.h"
+#include "qvector.h"
+#include "qvaluelist.h"
+#endif // QT_H
+
+class QPainter;
+class QKeyEvent;
+class QResizeEvent;
+class QMouseEvent;
+class QTimer;
+class QFont;
+class QColor;
+struct QUndoRedoInfoPrivate;
+class QPopupMenu;
+
+namespace Qt3 {
+
+class QTextString;
+class QTextDocument;
+class QTextCursor;
+class QTextCommand;
+class QTextParag;
+class QTextFormat;
+class QTextBrowser;
+class QTextEditPrivate;
+
+class Q_EXPORT QTextEdit : public QScrollView
+{
+ friend class QTextBrowser;
+
+ Q_OBJECT
+ Q_ENUMS( WordWrap WrapPolicy )
+ Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat )
+ Q_PROPERTY( QString text READ text WRITE setText )
+ Q_PROPERTY( QBrush paper READ paper WRITE setPaper )
+ Q_PROPERTY( bool linkUnderline READ linkUnderline WRITE setLinkUnderline )
+ Q_PROPERTY( QString documentTitle READ documentTitle )
+ Q_PROPERTY( int length READ length )
+ Q_PROPERTY( WordWrap wordWrap READ wordWrap WRITE setWordWrap )
+ Q_PROPERTY( int wrapColumnOrWidth READ wrapColumnOrWidth WRITE setWrapColumnOrWidth )
+ Q_PROPERTY( WrapPolicy wrapPolicy READ wrapPolicy WRITE setWrapPolicy )
+ Q_PROPERTY( bool hasSelectedText READ hasSelectedText )
+ Q_PROPERTY( QString selectedText READ selectedText )
+ Q_PROPERTY( int undoDepth READ undoDepth WRITE setUndoDepth )
+ Q_PROPERTY( bool overwriteMode READ isOverwriteMode WRITE setOverwriteMode )
+ Q_PROPERTY( bool modified READ isModified WRITE setModified DESIGNABLE false )
+ Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
+ Q_PROPERTY( bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled )
+ Q_PROPERTY( int tabStopWidth READ tabStopWidth WRITE setTabStopWidth )
+
+public:
+ enum WordWrap {
+ NoWrap,
+ WidgetWidth,
+ FixedPixelWidth,
+ FixedColumnWidth
+ };
+
+ enum WrapPolicy {
+ AtWordBoundary,
+ Anywhere,
+ AtWhiteSpace = AtWordBoundary // deprecated, don't use
+ };
+
+ enum KeyboardAction {
+ ActionBackspace,
+ ActionDelete,
+ ActionReturn,
+ ActionKill
+ };
+
+ enum CursorAction {
+ MoveBackward,
+ MoveForward,
+ MoveWordBackward,
+ MoveWordForward,
+ MoveUp,
+ MoveDown,
+ MoveLineStart,
+ MoveLineEnd,
+ MoveHome,
+ MoveEnd,
+ MovePgUp,
+ MovePgDown
+ };
+
+ enum VerticalAlignment {
+ AlignNormal,
+ AlignSuperScript,
+ AlignSubScript
+ };
+
+ QTextEdit( const QString& text, const QString& context = QString::null,
+ QWidget* parent=0, const char* name=0);
+ QTextEdit( QWidget* parent=0, const char* name=0 );
+ virtual ~QTextEdit();
+ void setPalette( const QPalette & );
+
+ QString text() const;
+ QString text( int para ) const;
+ TextFormat textFormat() const;
+ QString context() const;
+ QString documentTitle() const;
+
+ void getSelection( int *paraFrom, int *indexFrom,
+ int *paraTo, int *indexTo, int selNum = 0 ) const;
+ virtual bool find( const QString &expr, bool cs, bool wo, bool forward = TRUE,
+ int *para = 0, int *index = 0 );
+
+ int paragraphs() const;
+ int lines() const;
+ int linesOfParagraph( int para ) const;
+ int lineOfChar( int para, int chr );
+ int length() const;
+ QRect paragraphRect( int para ) const;
+ int paragraphAt( const QPoint &pos ) const;
+ int charAt( const QPoint &pos, int *para ) const;
+ int paragraphLength( int para ) const;
+
+ QStyleSheet* styleSheet() const;
+ QMimeSourceFactory* mimeSourceFactory() const;
+
+ QBrush paper() const;
+ bool linkUnderline() const;
+
+ int heightForWidth( int w ) const;
+
+ bool hasSelectedText() const;
+ QString selectedText() const;
+ bool isUndoAvailable() const;
+ bool isRedoAvailable() const;
+
+ WordWrap wordWrap() const;
+ int wrapColumnOrWidth() const;
+ WrapPolicy wrapPolicy() const;
+
+ int tabStopWidth() const;
+
+ QString anchorAt( const QPoint& pos );
+
+ QSize sizeHint() const;
+
+ bool isReadOnly() const { return readonly; }
+
+ void getCursorPosition( int *parag, int *index ) const;
+
+ bool isModified() const;
+ bool italic() const;
+ bool bold() const;
+ bool underline() const;
+ QString family() const;
+ int pointSize() const;
+ QColor color() const;
+ QFont font() const;
+ int alignment() const;
+ int undoDepth() const;
+ virtual bool getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment );
+ virtual bool getParagraphFormat( int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ QStyleSheetItem::DisplayMode *displayMode,
+ QStyleSheetItem::ListStyle *listStyle,
+ int *listDepth );
+ bool isOverwriteMode() const { return overWrite; }
+ QColor paragraphBackgroundColor( int para ) const;
+
+ bool isUndoRedoEnabled() const;
+ bool eventFilter( QObject *o, QEvent *e );
+
+public slots:
+ void setEnabled( bool );
+ virtual void setMimeSourceFactory( QMimeSourceFactory* factory );
+ virtual void setStyleSheet( QStyleSheet* styleSheet );
+ virtual void scrollToAnchor( const QString& name );
+ virtual void setPaper( const QBrush& pap );
+ virtual void setLinkUnderline( bool );
+
+ virtual void setWordWrap( WordWrap mode );
+ virtual void setWrapColumnOrWidth( int );
+ virtual void setWrapPolicy( WrapPolicy policy );
+
+ virtual void copy();
+ virtual void append( const QString& text );
+
+ void setText( const QString &txt ) { setText( txt, QString::null ); }
+ virtual void setText( const QString &txt, const QString &context );
+ virtual void setTextFormat( TextFormat f );
+
+ virtual void selectAll( bool select = TRUE );
+ virtual void setTabStopWidth( int ts );
+ virtual void zoomIn( int range );
+ virtual void zoomIn() { zoomIn( 1 ); }
+ virtual void zoomOut( int range );
+ virtual void zoomOut() { zoomOut( 1 ); }
+ virtual void zoomTo( int size );
+
+ virtual void sync();
+ virtual void setReadOnly( bool b );
+
+ virtual void undo();
+ virtual void redo();
+ virtual void cut();
+ virtual void paste();
+#ifndef QT_NO_CLIPBOARD
+ virtual void pasteSubType( const QCString &subtype );
+#endif
+ virtual void clear();
+ virtual void del();
+ virtual void indent();
+ virtual void setItalic( bool b );
+ virtual void setBold( bool b );
+ virtual void setUnderline( bool b );
+ virtual void setFamily( const QString &f );
+ virtual void setPointSize( int s );
+ virtual void setColor( const QColor &c );
+ virtual void setFont( const QFont &f );
+ virtual void setVerticalAlignment( VerticalAlignment a );
+ virtual void setAlignment( int a );
+ virtual void setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle );
+ virtual void setCursorPosition( int parag, int index );
+ virtual void setSelection( int parag_from, int index_from, int parag_to, int index_to, int selNum = 0 );
+ virtual void setSelectionAttributes( int selNum, const QColor &back, bool invertText );
+ virtual void setModified( bool m );
+ virtual void resetFormat();
+ virtual void setUndoDepth( int d );
+ virtual void setFormat( QTextFormat *f, int flags );
+ virtual void ensureCursorVisible();
+ virtual void placeCursor( const QPoint &pos, QTextCursor *c = 0 );
+ virtual void moveCursor( CursorAction action, bool select );
+ virtual void doKeyboardAction( KeyboardAction action );
+ virtual void removeSelectedText( int selNum = 0 );
+ virtual void removeSelection( int selNum = 0 );
+ virtual void setCurrentFont( const QFont &f );
+ virtual void setOverwriteMode( bool b ) { overWrite = b; }
+
+ virtual void scrollToBottom();
+
+ virtual void insert( const QString &text, bool indent, bool checkNewLine, bool removeSelected );
+ virtual void insertAt( const QString &text, int para, int index );
+ virtual void removeParagraph( int para );
+ virtual void insertParagraph( const QString &text, int para );
+
+ virtual void setParagraphBackgroundColor( int para, const QColor &bg );
+ virtual void clearParagraphBackground( int para );
+
+ virtual void setUndoRedoEnabled( bool b );
+
+signals:
+ void textChanged();
+ void selectionChanged();
+ void copyAvailable( bool );
+ void undoAvailable( bool yes );
+ void redoAvailable( bool yes );
+ void currentFontChanged( const QFont &f );
+ void currentColorChanged( const QColor &c );
+ void currentAlignmentChanged( int a );
+ void currentVerticalAlignmentChanged( VerticalAlignment a );
+ void cursorPositionChanged( QTextCursor *c );
+ void cursorPositionChanged( int para, int pos );
+ void returnPressed();
+ void modificationChanged( bool m );
+
+protected:
+ void repaintChanged();
+ void updateStyles();
+ void drawContents( QPainter *p, int cx, int cy, int cw, int ch );
+ bool event( QEvent *e );
+ void keyPressEvent( QKeyEvent *e );
+ void resizeEvent( QResizeEvent *e );
+ void viewportResizeEvent( QResizeEvent* );
+ void contentsMousePressEvent( QMouseEvent *e );
+ void contentsMouseMoveEvent( QMouseEvent *e );
+ void contentsMouseReleaseEvent( QMouseEvent *e );
+ void contentsMouseDoubleClickEvent( QMouseEvent *e );
+#ifndef QT_NO_WHEELEVENT
+ void contentsWheelEvent( QWheelEvent *e );
+#endif
+#ifndef QT_NO_DRAGANDDROP
+ void contentsDragEnterEvent( QDragEnterEvent *e );
+ void contentsDragMoveEvent( QDragMoveEvent *e );
+ void contentsDragLeaveEvent( QDragLeaveEvent *e );
+ void contentsDropEvent( QDropEvent *e );
+#endif
+ bool focusNextPrevChild( bool next );
+ QTextDocument *document() const;
+ QTextCursor *textCursor() const;
+ void setDocument( QTextDocument *doc );
+ virtual QPopupMenu *createPopupMenu( const QPoint& pos );
+ virtual QPopupMenu *createPopupMenu();
+ void drawCursor( bool visible );
+
+ void windowActivationChange( bool );
+
+protected slots:
+ virtual void doChangeInterval();
+
+private slots:
+ void formatMore();
+ void doResize();
+ void autoScrollTimerDone();
+ void blinkCursor();
+ void setModified();
+ void startDrag();
+ void documentWidthChanged( int w );
+
+private:
+ struct Q_EXPORT UndoRedoInfo {
+ enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Alignment, ParagType };
+
+ UndoRedoInfo( QTextDocument *dc );
+ ~UndoRedoInfo();
+ void clear();
+ bool valid() const;
+
+ QUndoRedoInfoPrivate *d;
+ int id;
+ int index;
+ int eid;
+ int eindex;
+ QTextFormat *format;
+ int flags;
+ Type type;
+ QTextDocument *doc;
+ QMemArray<int> oldAligns;
+ int newAlign;
+ bool list;
+ QStyleSheetItem::ListStyle listStyle;
+ QValueList< QPtrVector<QStyleSheetItem> > oldStyles;
+ QValueList<QStyleSheetItem::ListStyle> oldListStyles;
+ };
+
+private:
+ void updateCursor( const QPoint & pos );
+ void handleMouseMove( const QPoint& pos );
+ void drawContents( QPainter * );
+ virtual bool linksEnabled() const { return FALSE; }
+ void init();
+ void checkUndoRedoInfo( UndoRedoInfo::Type t );
+ void updateCurrentFormat();
+ bool handleReadOnlyKeyEvent( QKeyEvent *e );
+ void makeParagVisible( QTextParag *p );
+#ifndef QT_NO_MIME
+ QCString pickSpecial(QMimeSource* ms, bool always_ask, const QPoint&);
+#endif
+#ifndef QT_NO_MIMECLIPBOARD
+ void pasteSpecial(const QPoint&);
+#endif
+ void setFontInternal( const QFont &f );
+
+ virtual void emitHighlighted( const QString & ) {}
+ virtual void emitLinkClicked( const QString & ) {}
+
+ void readFormats( QTextCursor &c1, QTextCursor &c2, int oldLen, QTextString &text, bool fillStyles = FALSE );
+ void clearUndoRedo();
+ void paintDocument( bool drawAll, QPainter *p, int cx = -1, int cy = -1, int cw = -1, int ch = -1 );
+ void moveCursor( CursorAction action );
+ void ensureFormatted( QTextParag *p );
+ void placeCursor( const QPoint &pos, QTextCursor *c, bool link );
+
+private:
+ QTextDocument *doc;
+ QTextCursor *cursor;
+ QTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer;
+ QTextParag *lastFormatted;
+ int interval;
+ UndoRedoInfo undoRedoInfo;
+ QTextFormat *currentFormat;
+ int currentAlignment;
+ QPoint oldMousePos, mousePos;
+ QPoint dragStartPos;
+ QString onLink;
+ WordWrap wrapMode;
+ WrapPolicy wPolicy;
+ int wrapWidth;
+ QString pressedLink;
+ QTextEditPrivate *d;
+ bool inDoubleClick : 1;
+ bool mousePressed : 1;
+ bool cursorVisible : 1;
+ bool blinkCursorVisible : 1;
+ bool readOnly : 1;
+ bool modified : 1;
+ bool mightStartDrag : 1;
+ bool inDnD : 1;
+ bool readonly : 1;
+ bool undoEnabled : 1;
+ bool overWrite : 1;
+};
+
+inline QTextDocument *QTextEdit::document() const
+{
+ return doc;
+}
+
+inline QTextCursor *QTextEdit::textCursor() const
+{
+ return cursor;
+}
+
+inline void QTextEdit::setCurrentFont( const QFont &f )
+{
+ QTextEdit::setFontInternal( f );
+}
+
+}
+
+#endif //QTEXTVIEW_H