summaryrefslogtreecommitdiffstats
path: root/src/qt3support/text
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/qt3support/text
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'src/qt3support/text')
-rw-r--r--src/qt3support/text/q3multilineedit.cpp535
-rw-r--r--src/qt3support/text/q3multilineedit.h143
-rw-r--r--src/qt3support/text/q3richtext.cpp8353
-rw-r--r--src/qt3support/text/q3richtext_p.cpp636
-rw-r--r--src/qt3support/text/q3richtext_p.h2102
-rw-r--r--src/qt3support/text/q3simplerichtext.cpp421
-rw-r--r--src/qt3support/text/q3simplerichtext.h109
-rw-r--r--src/qt3support/text/q3stylesheet.cpp1471
-rw-r--r--src/qt3support/text/q3stylesheet.h235
-rw-r--r--src/qt3support/text/q3syntaxhighlighter.cpp223
-rw-r--r--src/qt3support/text/q3syntaxhighlighter.h89
-rw-r--r--src/qt3support/text/q3syntaxhighlighter_p.h114
-rw-r--r--src/qt3support/text/q3textbrowser.cpp526
-rw-r--r--src/qt3support/text/q3textbrowser.h108
-rw-r--r--src/qt3support/text/q3textedit.cpp7244
-rw-r--r--src/qt3support/text/q3textedit.h613
-rw-r--r--src/qt3support/text/q3textstream.cpp2436
-rw-r--r--src/qt3support/text/q3textstream.h297
-rw-r--r--src/qt3support/text/q3textview.cpp84
-rw-r--r--src/qt3support/text/q3textview.h76
-rw-r--r--src/qt3support/text/text.pri25
21 files changed, 25840 insertions, 0 deletions
diff --git a/src/qt3support/text/q3multilineedit.cpp b/src/qt3support/text/q3multilineedit.cpp
new file mode 100644
index 0000000..23b9776
--- /dev/null
+++ b/src/qt3support/text/q3multilineedit.cpp
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qplatformdefs.h>
+#include "q3multilineedit.h"
+#ifndef QT_NO_MULTILINEEDIT
+#include "qpainter.h"
+#include "qscrollbar.h"
+#include "qcursor.h"
+#include "qclipboard.h"
+#include "qpixmap.h"
+#include "qregexp.h"
+#include "qapplication.h"
+#include "q3dragobject.h"
+#include "qtimer.h"
+#include <private/q3richtext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3MultiLineEdit
+
+ \brief The Q3MultiLineEdit widget is a simple editor for inputting text.
+
+ \compat
+
+ The Q3MultiLineEdit was a simple editor widget in former Qt versions. Qt
+ 3.0 includes a new richtext engine which obsoletes Q3MultiLineEdit. It is
+ still included for compatibility reasons. It is now a subclass of
+ \l Q3TextEdit, and provides enough of the old Q3MultiLineEdit API to keep old
+ applications working.
+
+ If you implement something new with Q3MultiLineEdit, we suggest using
+ \l Q3TextEdit instead and call Q3TextEdit::setTextFormat(Qt::PlainText).
+
+ Although most of the old Q3MultiLineEdit API is still available, there is
+ a few difference. The old Q3MultiLineEdit operated on lines, not on
+ paragraphs. As lines change all the time during wordwrap, the new
+ richtext engine uses paragraphs as basic elements in the data structure.
+ All functions (numLines(), textLine(), etc.) that operated on lines, now
+ operate on paragraphs. Further, getString() has been removed completely.
+ It revealed too much of the internal data structure.
+
+ Applications which made normal and reasonable use of Q3MultiLineEdit
+ should still work without problems. Some odd usage will require some
+ porting. In these cases, it may be better to use \l Q3TextEdit now.
+
+ \sa Q3TextEdit
+*/
+
+/*!
+ \fn bool Q3MultiLineEdit::autoUpdate() const
+
+ This function is a noop that always returns true.
+*/
+
+/*!
+ \fn virtual void Q3MultiLineEdit::setAutoUpdate(bool b)
+
+ \internal
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::totalWidth() const
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::totalHeight() const
+*/
+
+/*!
+ \fn int Q3MultiLineEdit::maxLines() const
+*/
+
+/*!
+ \fn void Q3MultiLineEdit::setMaxLines(int max)
+
+ Sets the maximum number of lines this Q3MultiLineEdit will hold to
+ \a max.
+*/
+
+/*!
+ \fn void Q3MultiLineEdit::deselect()
+*/
+
+
+class Q3MultiLineEditData
+{
+};
+
+
+/*!
+ Constructs a new, empty, Q3MultiLineEdit with parent \a parent called
+ \a name.
+*/
+
+Q3MultiLineEdit::Q3MultiLineEdit(QWidget *parent , const char *name)
+ : Q3TextEdit(parent, name)
+{
+ d = new Q3MultiLineEditData;
+ setTextFormat(Qt::PlainText);
+}
+
+/*! \property Q3MultiLineEdit::numLines
+ \brief the number of paragraphs in the editor
+
+ The count includes any empty paragraph at top and bottom, so for an
+ empty editor this method returns 1.
+*/
+
+int Q3MultiLineEdit::numLines() const
+{
+ return document()->lastParagraph()->paragId() + 1;
+}
+
+/*! \property Q3MultiLineEdit::atEnd
+ \brief whether the cursor is placed at the end of the text
+
+ \sa atBeginning
+*/
+
+bool Q3MultiLineEdit::atEnd() const
+{
+ return textCursor()->paragraph() == document()->lastParagraph() && textCursor()->atParagEnd();
+}
+
+
+/*! \property Q3MultiLineEdit::atBeginning
+ \brief whether the cursor is placed at the beginning of the text
+
+ \sa atEnd
+*/
+
+bool Q3MultiLineEdit::atBeginning() const
+{
+ return textCursor()->paragraph() == document()->firstParagraph() && textCursor()->atParagStart();
+}
+
+/*! Returns the number of characters at paragraph number \a row. If
+ \a row is out of range, -1 is returned.
+*/
+
+int Q3MultiLineEdit::lineLength(int row) const
+{
+ if (row < 0 || row > numLines())
+ return -1;
+ return document()->paragAt(row)->length() - 1;
+}
+
+
+/*! Destructor. */
+
+Q3MultiLineEdit::~Q3MultiLineEdit()
+{
+ delete d;
+}
+
+/*!
+ If there is selected text, sets \a line1, \a col1, \a line2 and \a col2
+ to the start and end of the selected region and returns true. Returns
+ false if there is no selected text.
+ */
+bool Q3MultiLineEdit::getMarkedRegion(int *line1, int *col1,
+ int *line2, int *col2) const
+{
+ int p1,c1, p2, c2;
+ getSelection(&p1, &c1, &p2, &c2);
+ if (p1 == -1 && c1 == -1 && p2 == -1 && c2 == -1)
+ return false;
+ if (line1)
+ *line1 = p1;
+ if (col1)
+ *col1 = c1;
+ if (line2)
+ *line2 = p2;
+ if (col2)
+ *col2 = c2;
+ return true;
+}
+
+
+/*!
+ Returns true if there is selected text.
+*/
+
+bool Q3MultiLineEdit::hasMarkedText() const
+{
+ return hasSelectedText();
+}
+
+
+/*!
+ Returns a copy of the selected text.
+*/
+
+QString Q3MultiLineEdit::markedText() const
+{
+ return selectedText();
+}
+
+/*!
+ Moves the cursor one page down. If \a mark is true, the text
+ is selected.
+*/
+
+void Q3MultiLineEdit::pageDown(bool mark)
+{
+ moveCursor(MoveDown, mark);
+}
+
+
+/*!
+ Moves the cursor one page up. If \a mark is true, the text
+ is selected.
+*/
+
+void Q3MultiLineEdit::pageUp(bool mark)
+{
+ moveCursor(MovePgUp, mark);
+}
+
+
+/*! Inserts \a txt at paragraph number \a line. If \a line is less
+ than zero, or larger than the number of paragraphs, the new text is
+ put at the end. If \a txt contains newline characters, several
+ paragraphs are inserted.
+
+ The cursor position is not changed.
+*/
+
+void Q3MultiLineEdit::insertLine(const QString &txt, int line)
+{
+ insertParagraph(txt, line);
+}
+
+/*! Deletes the paragraph at paragraph number \a paragraph. If \a
+ paragraph is less than zero or larger than the number of paragraphs,
+ nothing is deleted.
+*/
+
+void Q3MultiLineEdit::removeLine(int paragraph)
+{
+ removeParagraph(paragraph);
+}
+
+/*! Inserts \a str at the current cursor position and selects the
+ text if \a mark is true.
+*/
+
+void Q3MultiLineEdit::insertAndMark(const QString& str, bool mark)
+{
+ insert(str);
+ if (mark)
+ document()->setSelectionEnd(Q3TextDocument::Standard, *textCursor());
+}
+
+/*! Splits the paragraph at the current cursor position.
+*/
+
+void Q3MultiLineEdit::newLine()
+{
+ insert(QString(QLatin1Char('\n')));
+}
+
+
+/*! Deletes the character on the left side of the text cursor and
+ moves the cursor one position to the left. If a text has been selected
+ by the user (e.g. by clicking and dragging) the cursor is put at the
+ beginning of the selected text and the selected text is removed. \sa
+ del()
+*/
+
+void Q3MultiLineEdit::backspace()
+{
+ if (document()->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ return;
+ }
+
+ if (!textCursor()->paragraph()->prev() &&
+ textCursor()->atParagStart())
+ return;
+
+ doKeyboardAction(ActionBackspace);
+}
+
+
+/*! Moves the text cursor to the left end of the line. If \a mark is
+ true, text is selected toward the first position. If it is false and the
+ cursor is moved, all selected text is unselected.
+
+ \sa end()
+*/
+
+void Q3MultiLineEdit::home(bool mark)
+{
+ moveCursor(MoveLineStart, mark);
+}
+
+/*! Moves the text cursor to the right end of the line. If \a mark is
+ true, text is selected toward the last position. If it is false and the
+ cursor is moved, all selected text is unselected.
+
+ \sa home()
+*/
+
+void Q3MultiLineEdit::end(bool mark)
+{
+ moveCursor(MoveLineEnd, mark);
+}
+
+
+/*!
+ \fn void Q3MultiLineEdit::setCursorPosition(int line, int col)
+ \reimp
+*/
+
+/*! Sets the cursor position to character number \a col in paragraph
+ number \a line. The parameters are adjusted to lie within the legal
+ range.
+
+ If \a mark is false, the selection is cleared. otherwise it is extended.
+
+*/
+
+void Q3MultiLineEdit::setCursorPosition(int line, int col, bool mark)
+{
+ if (!mark)
+ selectAll(false);
+ Q3TextEdit::setCursorPosition(line, col);
+ if (mark)
+ document()->setSelectionEnd(Q3TextDocument::Standard, *textCursor());
+}
+
+/*! Returns the top center point where the cursor is drawn.
+*/
+
+QPoint Q3MultiLineEdit::cursorPoint() const
+{
+ return QPoint(textCursor()->x(), textCursor()->y() + textCursor()->paragraph()->rect().y());
+}
+
+/*! \property Q3MultiLineEdit::alignment
+ \brief The editor's paragraph alignment
+
+ Sets the alignment to flag, which must be Qt::AlignLeft,
+ Qt::AlignHCenter, or \c Qt::AlignRight.
+
+ If flag is an illegal flag, nothing happens.
+*/
+void Q3MultiLineEdit::setAlignment(Qt::Alignment flag)
+{
+ if (flag == Qt::AlignCenter)
+ flag = Qt::AlignHCenter;
+ if (flag != Qt::AlignLeft && flag != Qt::AlignRight && flag != Qt::AlignHCenter)
+ return;
+ Q3TextParagraph *p = document()->firstParagraph();
+ while (p) {
+ p->setAlignment(flag);
+ p = p->next();
+ }
+}
+
+Qt::Alignment Q3MultiLineEdit::alignment() const
+{
+ return QFlag(document()->firstParagraph()->alignment());
+}
+
+
+void Q3MultiLineEdit::setEdited(bool e)
+{
+ setModified(e);
+}
+
+/*! \property Q3MultiLineEdit::edited
+ \brief whether the document has been edited by the user
+
+ This is the same as Q3TextEdit's "modifed" property.
+*/
+bool Q3MultiLineEdit::edited() const
+{
+ return isModified();
+}
+
+/*! Moves the cursor one word to the right. If \a mark is true, the text
+ is selected.
+
+ \sa cursorWordBackward()
+*/
+void Q3MultiLineEdit::cursorWordForward(bool mark)
+{
+ moveCursor(MoveWordForward, mark);
+}
+
+/*! Moves the cursor one word to the left. If \a mark is true, the
+ text is selected.
+
+ \sa cursorWordForward()
+*/
+void Q3MultiLineEdit::cursorWordBackward(bool mark)
+{
+ moveCursor(MoveWordBackward, mark);
+}
+
+/*!
+ \fn Q3MultiLineEdit::insertAt(const QString &s, int line, int col)
+ \reimp
+*/
+
+/*! Inserts string \a s at paragraph number \a line, after character
+ number \a col in the paragraph. If \a s contains newline
+ characters, new lines are inserted.
+ If \a mark is true the inserted string will be selected.
+
+ The cursor position is adjusted.
+ */
+
+void Q3MultiLineEdit::insertAt(const QString &s, int line, int col, bool mark)
+{
+ Q3TextEdit::insertAt(s, line, col);
+ if (mark)
+ setSelection(line, col, line, col + s.length());
+}
+
+// ### reggie - is this documentation correct?
+
+/*! Deletes text from the current cursor position to the end of the
+ line. (Note that this function still operates on lines, not paragraphs.)
+*/
+
+void Q3MultiLineEdit::killLine()
+{
+ doKeyboardAction(ActionKill);
+}
+
+/*! Moves the cursor one character to the left. If \a mark is true,
+ the text is selected.
+ The \a wrap parameter is currently ignored.
+
+ \sa cursorRight() cursorUp() cursorDown()
+*/
+
+void Q3MultiLineEdit::cursorLeft(bool mark, bool)
+{
+ moveCursor(MoveBackward, mark);
+}
+
+/*! Moves the cursor one character to the right. If \a mark is true,
+ the text is selected.
+ The \a wrap parameter is currently ignored.
+
+ \sa cursorLeft() cursorUp() cursorDown()
+*/
+
+void Q3MultiLineEdit::cursorRight(bool mark, bool)
+{
+ moveCursor(MoveForward, mark);
+}
+
+/*! Moves the cursor up one line. If \a mark is true, the text is
+ selected.
+
+ \sa cursorDown() cursorLeft() cursorRight()
+*/
+
+void Q3MultiLineEdit::cursorUp(bool mark)
+{
+ moveCursor(MoveUp, mark);
+}
+
+/*!
+ Moves the cursor one line down. If \a mark is true, the text
+ is selected.
+ \sa cursorUp() cursorLeft() cursorRight()
+*/
+
+void Q3MultiLineEdit::cursorDown(bool mark)
+{
+ moveCursor(MoveDown, mark);
+}
+
+
+/*! Returns the text at line number \a line (possibly the empty
+ string), or a null if \a line is invalid.
+*/
+
+QString Q3MultiLineEdit::textLine(int line) const
+{
+ if (line < 0 || line >= numLines())
+ return QString();
+ QString str = document()->paragAt(line)->string()->toString();
+ str.truncate(str.length() - 1);
+ return str;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3multilineedit.h b/src/qt3support/text/q3multilineedit.h
new file mode 100644
index 0000000..d97e548
--- /dev/null
+++ b/src/qt3support/text/q3multilineedit.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3MULTILINEEDIT_H
+#define Q3MULTILINEEDIT_H
+
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_MULTILINEEDIT
+
+class Q3MultiLineEditCommand;
+class QValidator;
+class Q3MultiLineEditData;
+
+class Q_COMPAT_EXPORT Q3MultiLineEdit : public Q3TextEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(int numLines READ numLines)
+ Q_PROPERTY(bool atBeginning READ atBeginning)
+ Q_PROPERTY(bool atEnd READ atEnd)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
+ Q_PROPERTY(bool edited READ edited WRITE setEdited DESIGNABLE false)
+
+public:
+ Q3MultiLineEdit(QWidget* parent=0, const char* name=0);
+ ~Q3MultiLineEdit();
+
+ QString textLine(int line) const;
+ int numLines() const;
+
+ virtual void insertLine(const QString &s, int line = -1);
+ virtual void insertAt(const QString &s, int line, int col) {
+ insertAt(s, line, col, false);
+ }
+ virtual void insertAt(const QString &s, int line, int col, bool mark);
+ virtual void removeLine(int line);
+ virtual void setCursorPosition(int line, int col) {
+ setCursorPosition(line, col, false);
+ }
+ virtual void setCursorPosition(int line, int col, bool mark);
+ bool atBeginning() const;
+ bool atEnd() const;
+
+ void setAlignment(Qt::Alignment flags);
+ Qt::Alignment alignment() const;
+
+ void setEdited(bool);
+ bool edited() const;
+
+ bool hasMarkedText() const;
+ QString markedText() const;
+
+ void cursorWordForward(bool mark);
+ void cursorWordBackward(bool mark);
+
+ // noops
+ bool autoUpdate() const { return true; }
+ virtual void setAutoUpdate(bool) {}
+
+ int totalWidth() const { return contentsWidth(); }
+ int totalHeight() const { return contentsHeight(); }
+
+ int maxLines() const { return QWIDGETSIZE_MAX; }
+ void setMaxLines(int) {}
+
+public Q_SLOTS:
+ void deselect() { selectAll(false); }
+
+protected:
+ QPoint cursorPoint() const;
+ virtual void insertAndMark(const QString&, bool mark);
+ virtual void newLine();
+ virtual void killLine();
+ virtual void pageUp(bool mark=false);
+ virtual void pageDown(bool mark=false);
+ virtual void cursorLeft(bool mark=false, bool wrap = true);
+ virtual void cursorRight(bool mark=false, bool wrap = true);
+ virtual void cursorUp(bool mark=false);
+ virtual void cursorDown(bool mark=false);
+ virtual void backspace();
+ virtual void home(bool mark=false);
+ virtual void end(bool mark=false);
+
+ bool getMarkedRegion(int *line1, int *col1, int *line2, int *col2) const;
+ int lineLength(int row) const;
+
+private:
+ Q_DISABLE_COPY(Q3MultiLineEdit)
+
+ Q3MultiLineEditData *d;
+};
+
+#endif // QT_NO_MULTILINEEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3MULTILINEEDIT_H
diff --git a/src/qt3support/text/q3richtext.cpp b/src/qt3support/text/q3richtext.cpp
new file mode 100644
index 0000000..c058e37
--- /dev/null
+++ b/src/qt3support/text/q3richtext.cpp
@@ -0,0 +1,8353 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3richtext_p.h"
+
+#ifndef QT_NO_RICHTEXT
+
+#include "qbitmap.h"
+#include "qapplication.h"
+#include "q3cleanuphandler.h"
+#include "qcursor.h"
+#include "qdatastream.h"
+#include "q3dragobject.h"
+#include "qdrawutil.h"
+#include "qfile.h"
+#include "qfileinfo.h"
+#include "qfont.h"
+#include "qimage.h"
+#include "qmap.h"
+#include "qmime.h"
+#include "q3paintdevicemetrics.h"
+#include "qpainter.h"
+#include "qstringlist.h"
+#include "qstyle.h"
+#include "qstyleoption.h"
+#include "q3stylesheet.h"
+#include "qtextstream.h"
+#include <private/qtextengine_p.h>
+#include <private/qunicodetables_p.h>
+
+#include <stdlib.h>
+
+#if defined(Q_WS_X11)
+#include "qx11info_x11.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static Q3TextCursor* richTextExportStart = 0;
+static Q3TextCursor* richTextExportEnd = 0;
+
+class Q3TextFormatCollection;
+
+const int border_tolerance = 2;
+
+#ifdef Q_WS_WIN
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qt_windows.h"
+QT_END_INCLUDE_NAMESPACE
+#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)) {
+ Q3PaintDeviceMetrics metrics(painter->device());
+#if defined(Q_WS_X11)
+ value = value * metrics.logicalDpiY() /
+ QX11Info::appDpiY(painter->device()->x11Screen());
+#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;
+}
+
+
+static inline bool isBreakable(Q3TextString *string, int pos)
+{
+ if (string->at(pos).nobreak)
+ return false;
+ return (pos < string->length()-1 && string->at(pos+1).softBreak);
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void Q3TextCommandHistory::addCommand(Q3TextCommand *cmd)
+{
+ if (current < history.count() - 1) {
+ QList<Q3TextCommand *> commands;
+
+ for (int i = 0; i <= current; ++i)
+ commands.insert(i, history.takeFirst());
+
+ commands.append(cmd);
+ while (!history.isEmpty())
+ delete history.takeFirst();
+ history = commands;
+ } else {
+ history.append(cmd);
+ }
+
+ if (history.count() > steps)
+ delete history.takeFirst();
+ else
+ ++current;
+}
+
+Q3TextCursor *Q3TextCommandHistory::undo(Q3TextCursor *c)
+{
+ if (current > -1) {
+ Q3TextCursor *c2 = history.at(current)->unexecute(c);
+ --current;
+ return c2;
+ }
+ return 0;
+}
+
+Q3TextCursor *Q3TextCommandHistory::redo(Q3TextCursor *c)
+{
+ if (current > -1) {
+ if (current < 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 Q3TextCommandHistory::isUndoAvailable()
+{
+ return current > -1;
+}
+
+bool Q3TextCommandHistory::isRedoAvailable()
+{
+ return (current > -1 && current < history.count() - 1) || (current == -1 && history.count() > 0);
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyleInfo)
+ : Q3TextCommand(dc), id(i), index(idx), parag(0), text(str), styleInformation(oldStyleInfo)
+{
+ for (int j = 0; j < (int)text.size(); ++j) {
+ if (text[j].format())
+ text[j].format()->addRef();
+ }
+}
+
+Q3TextDeleteCommand::Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
+ : Q3TextCommand(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();
+ }
+}
+
+Q3TextDeleteCommand::~Q3TextDeleteCommand()
+{
+ for (int i = 0; i < (int)text.size(); ++i) {
+ if (text[i].format())
+ text[i].format()->removeRef();
+ }
+ text.resize(0);
+}
+
+Q3TextCursor *Q3TextDeleteCommand::execute(Q3TextCursor *c)
+{
+ Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
+ if (!s) {
+ qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
+ return 0;
+ }
+
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+ int len = text.size();
+ if (c)
+ *c = cursor;
+ if (doc) {
+ doc->setSelectionStart(Q3TextDocument::Temp, cursor);
+ for (int i = 0; i < len; ++i)
+ cursor.gotoNextLetter();
+ doc->setSelectionEnd(Q3TextDocument::Temp, cursor);
+ doc->removeSelectedText(Q3TextDocument::Temp, &cursor);
+ if (c)
+ *c = cursor;
+ } else {
+ s->remove(index, len);
+ }
+
+ return c;
+}
+
+Q3TextCursor *Q3TextDeleteCommand::unexecute(Q3TextCursor *c)
+{
+ Q3TextParagraph *s = doc ? doc->paragAt(id) : parag;
+ if (!s) {
+ qWarning("can't locate parag at %d, last parag: %d", id, doc->lastParagraph()->paragId());
+ return 0;
+ }
+
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+ QString str = Q3TextString::toString(text);
+ cursor.insert(str, true, &text);
+ if (c)
+ *c = cursor;
+ cursor.setParagraph(s);
+ cursor.setIndex(index);
+
+#ifndef QT_NO_DATASTREAM
+ if (!styleInformation.isEmpty()) {
+ QDataStream styleStream(&styleInformation, IO_ReadOnly);
+ int num;
+ styleStream >> num;
+ Q3TextParagraph *p = s;
+ while (num-- && p) {
+ p->readStyleInformation(styleStream);
+ p = p->next();
+ }
+ }
+#endif
+ s = cursor.paragraph();
+ while (s) {
+ s->format();
+ s->setChanged(true);
+ if (s == c->paragraph())
+ break;
+ s = s->next();
+ }
+
+ return &cursor;
+}
+
+Q3TextFormatCommand::Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx,
+ const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl)
+ : Q3TextCommand(dc), startId(sid), startIndex(sidx), endId(eid), endIndex(eidx), format(f), oldFormats(old), flags(fl)
+{
+ format = dc->formatCollection()->format(f);
+ for (int j = 0; j < (int)oldFormats.size(); ++j) {
+ if (oldFormats[j].format())
+ oldFormats[j].format()->addRef();
+ }
+}
+
+Q3TextFormatCommand::~Q3TextFormatCommand()
+{
+ format->removeRef();
+ for (int j = 0; j < (int)oldFormats.size(); ++j) {
+ if (oldFormats[j].format())
+ oldFormats[j].format()->removeRef();
+ }
+}
+
+Q3TextCursor *Q3TextFormatCommand::execute(Q3TextCursor *c)
+{
+ Q3TextParagraph *sp = doc->paragAt(startId);
+ Q3TextParagraph *ep = doc->paragAt(endId);
+ if (!sp || !ep)
+ return c;
+
+ Q3TextCursor start(doc);
+ start.setParagraph(sp);
+ start.setIndex(startIndex);
+ Q3TextCursor end(doc);
+ end.setParagraph(ep);
+ end.setIndex(endIndex);
+
+ doc->setSelectionStart(Q3TextDocument::Temp, start);
+ doc->setSelectionEnd(Q3TextDocument::Temp, end);
+ doc->setFormat(Q3TextDocument::Temp, format, flags);
+ doc->removeSelection(Q3TextDocument::Temp);
+ if (endIndex == ep->length())
+ end.gotoLeft();
+ *c = end;
+ return c;
+}
+
+Q3TextCursor *Q3TextFormatCommand::unexecute(Q3TextCursor *c)
+{
+ Q3TextParagraph *sp = doc->paragAt(startId);
+ Q3TextParagraph *ep = doc->paragAt(endId);
+ if (!sp || !ep)
+ return 0;
+
+ int idx = startIndex;
+ int fIndex = 0;
+ while ( fIndex < int(oldFormats.size()) ) {
+ if (oldFormats.at(fIndex).c == QLatin1Char('\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;
+ }
+ }
+
+ Q3TextCursor end(doc);
+ end.setParagraph(ep);
+ end.setIndex(endIndex);
+ if (endIndex == ep->length())
+ end.gotoLeft();
+ *c = end;
+ return c;
+}
+
+Q3TextStyleCommand::Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange)
+ : Q3TextCommand(dc), firstParag(fParag), lastParag(lParag), before(beforeChange)
+{
+ after = readStyleInformation( dc, fParag, lParag);
+}
+
+
+QByteArray Q3TextStyleCommand::readStyleInformation( Q3TextDocument* doc, int fParag, int lParag)
+{
+ QByteArray style;
+#ifndef QT_NO_DATASTREAM
+ Q3TextParagraph *p = doc->paragAt(fParag);
+ if (!p)
+ return style;
+ QDataStream styleStream(&style, IO_WriteOnly);
+ int num = lParag - fParag + 1;
+ styleStream << num;
+ while (num -- && p) {
+ p->writeStyleInformation(styleStream);
+ p = p->next();
+ }
+#endif
+ return style;
+}
+
+void Q3TextStyleCommand::writeStyleInformation( Q3TextDocument* doc, int fParag, const QByteArray& style)
+{
+#ifndef QT_NO_DATASTREAM
+ Q3TextParagraph *p = doc->paragAt(fParag);
+ if (!p)
+ return;
+ QByteArray copy = style;
+ QDataStream styleStream(&copy, IO_ReadOnly);
+ int num;
+ styleStream >> num;
+ while (num-- && p) {
+ p->readStyleInformation(styleStream);
+ p = p->next();
+ }
+#endif
+}
+
+Q3TextCursor *Q3TextStyleCommand::execute(Q3TextCursor *c)
+{
+ writeStyleInformation(doc, firstParag, after);
+ return c;
+}
+
+Q3TextCursor *Q3TextStyleCommand::unexecute(Q3TextCursor *c)
+{
+ writeStyleInformation(doc, firstParag, before);
+ return c;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextCursor::Q3TextCursor(Q3TextDocument *dc)
+ : idx(0), tmpX(-1), ox(0), oy(0),
+ valid(true)
+{
+ para = dc ? dc->firstParagraph() : 0;
+}
+
+Q3TextCursor::Q3TextCursor(const Q3TextCursor &c)
+{
+ ox = c.ox;
+ oy = c.oy;
+ idx = c.idx;
+ para = c.para;
+ tmpX = c.tmpX;
+ indices = c.indices;
+ paras = c.paras;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+}
+
+Q3TextCursor::~Q3TextCursor()
+{
+}
+
+Q3TextCursor &Q3TextCursor::operator=(const Q3TextCursor &c)
+{
+ ox = c.ox;
+ oy = c.oy;
+ idx = c.idx;
+ para = c.para;
+ tmpX = c.tmpX;
+ indices = c.indices;
+ paras = c.paras;
+ xOffsets = c.xOffsets;
+ yOffsets = c.yOffsets;
+ valid = c.valid;
+
+ return *this;
+}
+
+bool Q3TextCursor::operator==(const Q3TextCursor &c) const
+{
+ return para == c.para && idx == c.idx;
+}
+
+int Q3TextCursor::totalOffsetX() const
+{
+ int xoff = ox;
+ for (QStack<int>::ConstIterator xit = xOffsets.begin(); xit != xOffsets.end(); ++xit)
+ xoff += *xit;
+ return xoff;
+}
+
+int Q3TextCursor::totalOffsetY() const
+{
+ int yoff = oy;
+ for (QStack<int>::ConstIterator yit = yOffsets.begin(); yit != yOffsets.end(); ++yit)
+ yoff += *yit;
+ return yoff;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextCursor::gotoIntoNested(const QPoint &globalPos)
+{
+ if (!para)
+ return;
+ Q_ASSERT(para->at(idx)->isCustom());
+ push();
+ ox = 0;
+ int bl, y;
+ para->lineHeightOfChar(idx, &bl, &y);
+ oy = y + para->rect().y();
+ ox = para->at(idx)->x;
+ Q3TextDocument* doc = document();
+ para->at(idx)->customItem()->enterAt(this, doc, para, idx, ox, oy, globalPos-QPoint(ox,oy));
+}
+#endif
+
+void Q3TextCursor::invalidateNested()
+{
+ if (nestedDepth()) {
+ QStack<Q3TextParagraph*>::Iterator it = paras.begin();
+ QStack<int>::Iterator it2 = indices.begin();
+ for (; it != paras.end(); ++it, ++it2) {
+ if (*it == para)
+ continue;
+ (*it)->invalidate(0);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if ((*it)->at(*it2)->isCustom())
+ (*it)->at(*it2)->customItem()->invalidate();
+#endif
+ }
+ }
+}
+
+void Q3TextCursor::insert(const QString &str, bool checkNewLine, QVector<Q3TextStringChar> *formatting)
+{
+ tmpX = -1;
+ bool justInsert = true;
+ QString s(str);
+#if defined(Q_WS_WIN)
+ if (checkNewLine) {
+ int i = 0;
+ while ((i = s.indexOf(QLatin1Char('\r'), i)) != -1)
+ s.remove(i ,1);
+ }
+#endif
+ if (checkNewLine)
+ justInsert = s.indexOf(QLatin1Char('\n')) == -1;
+ if (justInsert) { // we ignore new lines and insert all in the current para at the current index
+ para->insert(idx, s.unicode(), s.length());
+ if (formatting) {
+ for (int i = 0; i < (int)s.length(); ++i) {
+ if (formatting->at(i).format()) {
+ formatting->at(i).format()->addRef();
+ para->string()->setFormat(idx + i, formatting->at(i).format(), true);
+ }
+ }
+ }
+ idx += s.length();
+ } else { // we split at new lines
+ int start = -1;
+ int end;
+ int y = para->rect().y() + para->rect().height();
+ int lastIndex = 0;
+ do {
+ end = s.indexOf(QLatin1Char('\n'), start + 1); // find line break
+ if (end == -1) // didn't find one, so end of line is end of string
+ end = s.length();
+ int len = (start == -1 ? end : end - start - 1);
+ if (len > 0) // insert the line
+ para->insert(idx, s.unicode() + start + 1, len);
+ else
+ para->invalidate(0);
+ if (formatting) { // set formats to the chars of the line
+ for (int i = 0; i < len; ++i) {
+ if (formatting->at(i + lastIndex).format()) {
+ formatting->at(i + lastIndex).format()->addRef();
+ para->string()->setFormat(i + idx, formatting->at(i + lastIndex).format(), true);
+ }
+ }
+ lastIndex += len;
+ }
+ start = end; // next start is at the end of this line
+ idx += len; // increase the index of the cursor to the end of the inserted text
+ if (s[end] == QLatin1Char('\n')) { // if at the end was a line break, break the line
+ splitAndInsertEmptyParagraph(false, true);
+ para->setEndState(-1);
+ para->prev()->format(-1, false);
+ lastIndex++;
+ }
+
+ } while (end < (int)s.length());
+
+ para->format(-1, false);
+ int dy = para->rect().y() + para->rect().height() - y;
+ Q3TextParagraph *p = para;
+ p->setParagId(p->prev() ? p->prev()->paragId() + 1 : 0);
+ p = p->next();
+ while (p) {
+ p->setParagId(p->prev()->paragId() + 1);
+ p->move(dy);
+ p->invalidate(0);
+ p->setEndState(-1);
+ p = p->next();
+ }
+ }
+
+ int h = para->rect().height();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoLeft()
+{
+ if (para->string()->isRightToLeft())
+ gotoNextLetter();
+ else
+ gotoPreviousLetter();
+}
+
+void Q3TextCursor::gotoPreviousLetter()
+{
+ tmpX = -1;
+
+ if (idx > 0) {
+ idx = para->string()->previousCursorPosition(idx);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
+ processNesting(EnterEnd);
+#endif
+ } else if (para->prev()) {
+ para = para->prev();
+ while (!para->isVisible() && para->prev())
+ para = para->prev();
+ idx = para->length() - 1;
+ } else if (nestedDepth()) {
+ pop();
+ processNesting(Prev);
+ if (idx == -1) {
+ pop();
+ if (idx > 0) {
+ idx = para->string()->previousCursorPosition(idx);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested())
+ processNesting(EnterEnd);
+#endif
+ } else if (para->prev()) {
+ para = para->prev();
+ idx = para->length() - 1;
+ }
+ }
+ }
+}
+
+void Q3TextCursor::push()
+{
+ indices.push(idx);
+ paras.push(para);
+ xOffsets.push(ox);
+ yOffsets.push(oy);
+}
+
+void Q3TextCursor::pop()
+{
+ if (indices.isEmpty())
+ return;
+ idx = indices.pop();
+ para = paras.pop();
+ ox = xOffsets.pop();
+ oy = yOffsets.pop();
+}
+
+void Q3TextCursor::restoreState()
+{
+ while (!indices.isEmpty())
+ pop();
+}
+
+bool Q3TextCursor::place(const QPoint &p, Q3TextParagraph *s, bool link)
+{
+ QPoint pos(p);
+ QRect r;
+ Q3TextParagraph *str = s;
+ if (pos.y() < s->rect().y()) {
+ pos.setY(s->rect().y());
+#ifdef Q_WS_MAC
+ pos.setX(s->rect().x());
+#endif
+ }
+ while (s) {
+ r = s->rect();
+ r.setWidth(document() ? document()->width() : QWIDGETSIZE_MAX);
+ if (s->isVisible())
+ str = s;
+ if (pos.y() >= r.y() && pos.y() <= r.y() + r.height())
+ break;
+ if (!s->next()) {
+#ifdef Q_WS_MAC
+ pos.setX(s->rect().x() + s->rect().width());
+#endif
+ break;
+ }
+ s = s->next();
+ }
+
+ if (!s || !str)
+ return false;
+
+ s = str;
+
+ setParagraph(s);
+ int y = s->rect().y();
+ int lines = s->lines();
+ Q3TextStringChar *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 + 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);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ 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
+#endif
+ {
+ if(chr->rightToLeft)
+ cpos += cw;
+ int diff = cpos - pos.x();
+ bool dm = diff < 0 ? !chr->rightToLeft : chr->rightToLeft;
+ if ((QABS(diff) < dist || (dist == diff && dm == true)) && para->string()->validCursorPosition(i)) {
+ dist = QABS(diff);
+ if (!link || pos.x() >= x + chr->x)
+ curpos = i;
+ }
+ }
+ i++;
+ }
+ setIndex(curpos);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (inCustom && para->document() && para->at(curpos)->isCustom() && para->at(curpos)->customItem()->isNested()) {
+ Q3TextDocument *oldDoc = para->document();
+ gotoIntoNested(pos);
+ if (oldDoc == para->document())
+ return true;
+ QPoint p(pos.x() - offsetX(), pos.y() - offsetY());
+ if (!place(p, document()->firstParagraph(), link))
+ pop();
+ }
+#endif
+ return true;
+}
+
+bool Q3TextCursor::processNesting(Operation op)
+{
+ if (!para->document())
+ return false;
+ Q3TextDocument* doc = para->document();
+ push();
+ ox = para->at(idx)->x;
+ int bl, y;
+ para->lineHeightOfChar(idx, &bl, &y);
+ oy = y + para->rect().y();
+ bool ok = false;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ switch (op) {
+ case EnterBegin:
+ ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy);
+ break;
+ case EnterEnd:
+ ok = para->at(idx)->customItem()->enter(this, doc, para, idx, ox, oy, true);
+ break;
+ case Next:
+ ok = para->at(idx)->customItem()->next(this, doc, para, idx, ox, oy);
+ break;
+ case Prev:
+ ok = para->at(idx)->customItem()->prev(this, doc, para, idx, ox, oy);
+ break;
+ case Down:
+ ok = para->at(idx)->customItem()->down(this, doc, para, idx, ox, oy);
+ break;
+ case Up:
+ ok = para->at(idx)->customItem()->up(this, doc, para, idx, ox, oy);
+ break;
+ }
+ if (!ok)
+#endif
+ pop();
+ return ok;
+}
+
+void Q3TextCursor::gotoRight()
+{
+ if (para->string()->isRightToLeft())
+ gotoPreviousLetter();
+ else
+ gotoNextLetter();
+}
+
+void Q3TextCursor::gotoNextLetter()
+{
+ tmpX = -1;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ const Q3TextStringChar *tsc = para->at(idx);
+ if (tsc && tsc->isCustom() && tsc->customItem()->isNested()) {
+ if (processNesting(EnterBegin))
+ return;
+ }
+#endif
+
+ if (idx < para->length() - 1) {
+ idx = para->string()->nextCursorPosition(idx);
+ } else if (para->next()) {
+ para = para->next();
+ while (!para->isVisible() && para->next())
+ para = para->next();
+ idx = 0;
+ } else if (nestedDepth()) {
+ pop();
+ processNesting(Next);
+ if (idx == -1) {
+ pop();
+ if (idx < para->length() - 1) {
+ idx = para->string()->nextCursorPosition(idx);
+ } else if (para->next()) {
+ para = para->next();
+ idx = 0;
+ }
+ }
+ }
+}
+
+void Q3TextCursor::gotoUp()
+{
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (tmpX < 0)
+ tmpX = x();
+
+ if (indexOfLineStart == 0) {
+ if (!para->prev()) {
+ if (!nestedDepth())
+ return;
+ pop();
+ processNesting(Up);
+ if (idx == -1) {
+ pop();
+ if (!para->prev())
+ return;
+ idx = tmpX = 0;
+ } else {
+ tmpX = -1;
+ return;
+ }
+ }
+ Q3TextParagraph *p = para->prev();
+ while (p && !p->isVisible())
+ p = p->prev();
+ if (p)
+ para = p;
+ int lastLine = para->lines() - 1;
+ if (!para->lineStartOfLine(lastLine, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < para->length()-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ } else {
+ --line;
+ int oldIndexOfLineStart = indexOfLineStart;
+ if (!para->lineStartOfLine(line, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < oldIndexOfLineStart-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ }
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoDown()
+{
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (tmpX < 0)
+ tmpX = x();
+
+ if (line == para->lines() - 1) {
+ if (!para->next()) {
+ if (!nestedDepth())
+ return;
+ pop();
+ processNesting(Down);
+ if (idx == -1) {
+ pop();
+ if (!para->next())
+ return;
+ idx = tmpX = 0;
+ } else {
+ tmpX = -1;
+ return;
+ }
+ }
+ Q3TextParagraph *s = para->next();
+ while (s && !s->isVisible())
+ s = s->next();
+ if (s)
+ para = s;
+ if (!para->lineStartOfLine(0, &indexOfLineStart))
+ return;
+ int end;
+ if (para->lines() == 1)
+ end = para->length();
+ else
+ para->lineStartOfLine(1, &end);
+
+ idx = indexOfLineStart;
+ while (idx < end-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ } else {
+ ++line;
+ int end;
+ if (line == para->lines() - 1)
+ end = para->length();
+ else
+ para->lineStartOfLine(line + 1, &end);
+ if (!para->lineStartOfLine(line, &indexOfLineStart))
+ return;
+ idx = indexOfLineStart;
+ while (idx < end-1 && para->at(idx)->x < tmpX)
+ ++idx;
+ if (idx > indexOfLineStart &&
+ para->at(idx)->x - tmpX > tmpX - para->at(idx-1)->x)
+ --idx;
+ }
+ fixCursorPosition();
+}
+
+void Q3TextCursor::gotoLineEnd()
+{
+ tmpX = -1;
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ if (line == para->lines() - 1) {
+ idx = para->length() - 1;
+ } else {
+ c = para->lineStartOfLine(++line, &indexOfLineStart);
+ indexOfLineStart--;
+ idx = indexOfLineStart;
+ }
+}
+
+void Q3TextCursor::gotoLineStart()
+{
+ tmpX = -1;
+ int indexOfLineStart;
+ int line;
+ Q3TextStringChar *c = para->lineStartOfChar(idx, &indexOfLineStart, &line);
+ if (!c)
+ return;
+
+ idx = indexOfLineStart;
+}
+
+void Q3TextCursor::gotoHome()
+{
+ if (topParagraph()->document())
+ gotoPosition(topParagraph()->document()->firstParagraph());
+ else
+ gotoLineStart();
+}
+
+void Q3TextCursor::gotoEnd()
+{
+ if (topParagraph()->document() && topParagraph()->document()->lastParagraph()->isValid())
+ gotoPosition(topParagraph()->document()->lastParagraph(),
+ topParagraph()->document()->lastParagraph()->length() - 1);
+ else
+ gotoLineEnd();
+}
+
+void Q3TextCursor::gotoPageUp(int visibleHeight)
+{
+ int targetY = globalY() - visibleHeight;
+ Q3TextParagraph* old; int index;
+ do {
+ old = para; index = idx;
+ gotoUp();
+ } while ((old != para || index != idx) && globalY() > targetY);
+}
+
+void Q3TextCursor::gotoPageDown(int visibleHeight)
+{
+ int targetY = globalY() + visibleHeight;
+ Q3TextParagraph* old; int index;
+ do {
+ old = para; index = idx;
+ gotoDown();
+ } while ((old != para || index != idx) && globalY() < targetY);
+}
+
+void Q3TextCursor::gotoWordRight()
+{
+ if (para->string()->isRightToLeft())
+ gotoPreviousWord();
+ else
+ gotoNextWord();
+}
+
+void Q3TextCursor::gotoWordLeft()
+{
+ if (para->string()->isRightToLeft())
+ gotoNextWord();
+ else
+ gotoPreviousWord();
+}
+
+static bool is_seperator(const QChar &c, bool onlySpace)
+{
+ if (onlySpace)
+ return c.isSpace();
+ return c.isSpace() ||
+ c == QLatin1Char('\t') ||
+ c == QLatin1Char('.') ||
+ c == QLatin1Char(',') ||
+ c == QLatin1Char(':') ||
+ c == QLatin1Char(';') ||
+ c == QLatin1Char('-') ||
+ c == QLatin1Char('<') ||
+ c == QLatin1Char('>') ||
+ c == QLatin1Char('[') ||
+ c == QLatin1Char(']') ||
+ c == QLatin1Char('(') ||
+ c == QLatin1Char(')') ||
+ c == QLatin1Char('{') ||
+ c == QLatin1Char('}');
+}
+
+void Q3TextCursor::gotoPreviousWord(bool onlySpace)
+{
+ gotoPreviousLetter();
+ tmpX = -1;
+ Q3TextString *s = para->string();
+ bool allowSame = false;
+ if (idx == ((int)s->length()-1))
+ return;
+ for (int i = idx; i >= 0; --i) {
+ if (is_seperator(s->at(i).c, onlySpace)) {
+ if (!allowSame)
+ continue;
+ idx = i + 1;
+ return;
+ }
+ if (!allowSame && !is_seperator(s->at(i).c, onlySpace))
+ allowSame = true;
+ }
+ idx = 0;
+}
+
+void Q3TextCursor::gotoNextWord(bool onlySpace)
+{
+ tmpX = -1;
+ Q3TextString *s = para->string();
+ bool allowSame = false;
+ for (int i = idx; i < (int)s->length(); ++i) {
+ if (!is_seperator(s->at(i).c, onlySpace)) {
+ if (!allowSame)
+ continue;
+ idx = i;
+ return;
+ }
+ if (!allowSame && is_seperator(s->at(i).c, onlySpace))
+ allowSame = true;
+
+ }
+
+ if (idx < ((int)s->length()-1)) {
+ gotoLineEnd();
+ } else if (para->next()) {
+ Q3TextParagraph *p = para->next();
+ while (p && !p->isVisible())
+ p = p->next();
+ if (s) {
+ para = p;
+ idx = 0;
+ }
+ } else {
+ gotoLineEnd();
+ }
+}
+
+bool Q3TextCursor::atParagStart()
+{
+ return idx == 0;
+}
+
+bool Q3TextCursor::atParagEnd()
+{
+ return idx == para->length() - 1;
+}
+
+void Q3TextCursor::splitAndInsertEmptyParagraph(bool ind, bool updateIds)
+{
+ if (!para->document())
+ return;
+ tmpX = -1;
+ Q3TextFormat *f = 0;
+ if (para->document()->useFormatCollection()) {
+ f = para->at(idx)->format();
+ if (idx == para->length() - 1 && idx > 0)
+ f = para->at(idx - 1)->format();
+ if (f->isMisspelled()) {
+ f->removeRef();
+ f = para->document()->formatCollection()->format(f->font(), f->color());
+ }
+ }
+
+ if (atParagEnd()) {
+ Q3TextParagraph *n = para->next();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
+ if (f)
+ s->setFormat(0, 1, f, true);
+ s->copyParagData(para);
+ if (ind) {
+ int oi, ni;
+ s->indent(&oi, &ni);
+ para = s;
+ idx = ni;
+ } else {
+ para = s;
+ idx = 0;
+ }
+ } else if (atParagStart()) {
+ Q3TextParagraph *p = para->prev();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), p, para, updateIds);
+ if (f)
+ s->setFormat(0, 1, f, true);
+ s->copyParagData(para);
+ if (ind) {
+ s->indent();
+ s->format();
+ indent();
+ para->format();
+ }
+ } else {
+ QString str = para->string()->toString().mid(idx, 0xFFFFFF);
+ Q3TextParagraph *n = para->next();
+ Q3TextParagraph *s = para->document()->createParagraph(para->document(), para, n, updateIds);
+ s->copyParagData(para);
+ s->remove(0, 1);
+ s->append(str, true);
+ for (int i = 0; i < str.length(); ++i) {
+ Q3TextStringChar* tsc = para->at(idx + i);
+ s->setFormat(i, 1, tsc->format(), true);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (tsc->isCustom()) {
+ Q3TextCustomItem * item = tsc->customItem();
+ s->at(i)->setCustomItem(item);
+ tsc->loseCustomItem();
+ }
+#endif
+ if (tsc->isAnchor())
+ s->at(i)->setAnchor(tsc->anchorName(),
+ tsc->anchorHref());
+ }
+ para->truncate(idx);
+ if (ind) {
+ int oi, ni;
+ s->indent(&oi, &ni);
+ para = s;
+ idx = ni;
+ } else {
+ para = s;
+ idx = 0;
+ }
+ }
+
+ invalidateNested();
+}
+
+bool Q3TextCursor::remove()
+{
+ tmpX = -1;
+ if (!atParagEnd()) {
+ int next = para->string()->nextCursorPosition(idx);
+ para->remove(idx, next-idx);
+ int h = para->rect().height();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+ return false;
+ } else if (para->next()) {
+ para->join(para->next());
+ invalidateNested();
+ return true;
+ }
+ return false;
+}
+
+/* needed to implement backspace the correct way */
+bool Q3TextCursor::removePreviousChar()
+{
+ tmpX = -1;
+ if (!atParagStart()) {
+ para->remove(idx-1, 1);
+ int h = para->rect().height();
+ idx--;
+ // shouldn't be needed, just to make sure.
+ fixCursorPosition();
+ para->format(-1, true);
+ if (h != para->rect().height())
+ invalidateNested();
+ else if (para->document() && para->document()->parent())
+ para->document()->nextDoubleBuffered = true;
+ return false;
+ } else if (para->prev()) {
+ para = para->prev();
+ para->join(para->next());
+ invalidateNested();
+ return true;
+ }
+ return false;
+}
+
+void Q3TextCursor::indent()
+{
+ int oi = 0, ni = 0;
+ para->indent(&oi, &ni);
+ if (oi == ni)
+ return;
+
+ if (idx >= oi)
+ idx += ni - oi;
+ else
+ idx = ni;
+}
+
+void Q3TextCursor::fixCursorPosition()
+{
+ // searches for the closest valid cursor position
+ if (para->string()->validCursorPosition(idx))
+ return;
+
+ int lineIdx;
+ Q3TextStringChar *start = para->lineStartOfChar(idx, &lineIdx, 0);
+ int x = para->string()->at(idx).x;
+ int diff = QABS(start->x - x);
+ int best = lineIdx;
+
+ Q3TextStringChar *c = start;
+ ++c;
+
+ Q3TextStringChar *end = &para->string()->at(para->length()-1);
+ while (c <= end && !c->lineStart) {
+ int xp = c->x;
+ if (c->rightToLeft)
+ xp += para->string()->width(lineIdx + (c-start));
+ int ndiff = QABS(xp - x);
+ if (ndiff < diff && para->string()->validCursorPosition(lineIdx + (c-start))) {
+ diff = ndiff;
+ best = lineIdx + (c-start);
+ }
+ ++c;
+ }
+ idx = best;
+}
+
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextDocument::Q3TextDocument(Q3TextDocument *p)
+ : par(p), parentPar(0)
+#ifndef QT_NO_TEXTCUSTOMITEM
+ , tc(0)
+#endif
+ , tArray(0), tStopWidth(0)
+{
+ fCollection = par ? par->fCollection : new Q3TextFormatCollection;
+ init();
+}
+
+void Q3TextDocument::init()
+{
+ oTextValid = true;
+ mightHaveCustomItems = false;
+ if (par)
+ par->insertChild(this);
+ pProcessor = 0;
+ useFC = true;
+ pFormatter = 0;
+ indenter = 0;
+ fParag = 0;
+ txtFormat = Qt::AutoText;
+ preferRichText = false;
+ pages = false;
+ focusIndicator.parag = 0;
+ minw = 0;
+ wused = 0;
+ minwParag = curParag = 0;
+ align = Qt::AlignAuto;
+ nSelections = 1;
+
+ setStyleSheet(Q3StyleSheet::defaultSheet());
+#ifndef QT_NO_MIME
+ factory_ = Q3MimeSourceFactory::defaultFactory();
+#endif
+ contxt.clear();
+
+ underlLinks = par ? par->underlLinks : true;
+ backBrush = 0;
+ buf_pixmap = 0;
+ nextDoubleBuffered = false;
+
+ if (par)
+ withoutDoubleBuffer = par->withoutDoubleBuffer;
+ else
+ withoutDoubleBuffer = false;
+
+ lParag = fParag = createParagraph(this, 0, 0);
+
+ cx = 0;
+ cy = 2;
+ if (par)
+ cx = cy = 0;
+ cw = 600;
+ vw = 0;
+ flow_ = new Q3TextFlow;
+ flow_->setWidth(cw);
+
+ leftmargin = rightmargin = 4;
+ scaleFontsFactor = 1;
+
+ commandHistory = new Q3TextCommandHistory(100);
+ tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
+}
+
+Q3TextDocument::~Q3TextDocument()
+{
+ delete commandHistory;
+ if (par)
+ par->removeChild(this);
+ clear();
+ delete flow_;
+ if (!par) {
+ delete pFormatter;
+ delete fCollection;
+ }
+ delete pProcessor;
+ delete buf_pixmap;
+ delete indenter;
+ delete backBrush;
+ delete [] tArray;
+}
+
+void Q3TextDocument::clear(bool createEmptyParag)
+{
+ while (fParag) {
+ Q3TextParagraph *p = fParag->next();
+ delete fParag;
+ fParag = p;
+ }
+ if (flow_)
+ flow_->clear();
+ fParag = lParag = 0;
+ if (createEmptyParag)
+ fParag = lParag = createParagraph(this);
+ selections.clear();
+ oText.clear();
+ oTextValid = false;
+}
+
+int Q3TextDocument::widthUsed() const
+{
+ return wused + 2*border_tolerance;
+}
+
+int Q3TextDocument::height() const
+{
+ int h = 0;
+ if (lParag)
+ h = lParag->rect().top() + lParag->rect().height() + 1;
+ int fh = flow_->boundingRect().bottom();
+ return qMax(h, fh);
+}
+
+
+
+Q3TextParagraph *Q3TextDocument::createParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
+{
+ return new Q3TextParagraph(dc, pr, nx, updateIds);
+}
+
+bool Q3TextDocument::setMinimumWidth(int needed, int used, Q3TextParagraph *p)
+{
+ if (needed == -1) {
+ minw = 0;
+ wused = 0;
+ p = 0;
+ }
+ if (p == minwParag) {
+ if (minw > needed) {
+ Q3TextParagraph *tp = fParag;
+ while (tp) {
+ if (tp != p && tp->minwidth > needed) {
+ needed = tp->minwidth;
+ minwParag = tp;
+ }
+ tp = tp->n;
+ }
+ }
+ 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 Q3TextDocument::setPlainText(const QString &text)
+{
+ preferRichText = false;
+ clear();
+ oTextValid = true;
+ oText = text;
+
+ int lastNl = 0;
+ int nl = text.indexOf(QLatin1Char('\n'));
+ if (nl == -1) {
+ lParag = createParagraph(this, lParag, 0);
+ if (!fParag)
+ fParag = lParag;
+ QString s = text;
+ if (!s.isEmpty()) {
+ if (s[(int)s.length() - 1] == QLatin1Char('\r'))
+ s.remove(s.length() - 1, 1);
+ lParag->append(s);
+ }
+ } else {
+ for (;;) {
+ lParag = createParagraph(this, lParag, 0);
+ if (!fParag)
+ fParag = lParag;
+ int l = nl - lastNl;
+ if (l > 0) {
+ if (text.unicode()[nl-1] == QLatin1Char('\r'))
+ l--;
+ QString cs = QString::fromRawData(text.unicode()+lastNl, l);
+ lParag->append(cs);
+ }
+ if (nl == (int)text.length())
+ break;
+ lastNl = nl + 1;
+ nl = text.indexOf(QLatin1Char('\n'), nl + 1);
+ if (nl == -1)
+ nl = text.length();
+ }
+ }
+ if (!lParag)
+ lParag = fParag = createParagraph(this, 0, 0);
+}
+
+struct Q3TextDocumentTag {
+ Q3TextDocumentTag(){}
+ Q3TextDocumentTag(const QString&n, const Q3StyleSheetItem* s, const Q3TextFormat& f)
+ :name(n),style(s), format(f), alignment(Qt::AlignAuto), direction(QChar::DirON),liststyle(Q3StyleSheetItem::ListDisc) {
+ wsm = Q3StyleSheetItem::WhiteSpaceNormal;
+ }
+ QString name;
+ const Q3StyleSheetItem* style;
+ QString anchorHref;
+ Q3StyleSheetItem::WhiteSpaceMode wsm;
+ Q3TextFormat format;
+ signed int alignment : 16;
+ signed int direction : 5;
+ Q3StyleSheetItem::ListStyle liststyle;
+
+ Q3TextDocumentTag( const Q3TextDocumentTag& 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;
+ }
+ Q3TextDocumentTag& operator=(const Q3TextDocumentTag& 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;
+ }
+
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentTag)
+};
+
+
+#define NEWPAR \
+ do{ \
+ if (!hasNewPar) { \
+ if (!textEditMode && curpar && curpar->length()>1 \
+ && curpar->at(curpar->length()-2)->c == QChar::LineSeparator) \
+ curpar->remove(curpar->length()-2, 1); \
+ curpar = createParagraph(this, curpar, curpar->next()); \
+ styles.append(vec); \
+ vec = 0; \
+ } \
+ hasNewPar = true; \
+ curpar->rtext = true; \
+ curpar->align = curtag.alignment; \
+ curpar->lstyle = curtag.liststyle; \
+ curpar->litem = (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem); \
+ curpar->str->setDirection((QChar::Direction)curtag.direction); \
+ space = true; \
+ tabExpansionColumn = 0; \
+ delete vec; \
+ vec = new QVector<Q3StyleSheetItem *>(); \
+ for (QStack<Q3TextDocumentTag>::Iterator it = tags.begin(); it != tags.end(); ++it) \
+ vec->append(const_cast<Q3StyleSheetItem *>((*it).style)); \
+ vec->append(const_cast<Q3StyleSheetItem *>(curtag.style)); \
+ } while(false);
+
+
+void Q3TextDocument::setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat)
+{
+ preferRichText = true;
+ if (!context.isEmpty())
+ setContext(context);
+ clear();
+ fParag = lParag = createParagraph(this);
+ oTextValid = true;
+ oText = text;
+ setRichTextInternal(text, 0, initialFormat);
+ fParag->rtext = true;
+}
+
+void Q3TextDocument::setRichTextInternal(const QString &text, Q3TextCursor* cursor, const Q3TextFormat *initialFormat)
+{
+ Q3TextParagraph* curpar = lParag;
+ int pos = 0;
+ QStack<Q3TextDocumentTag> tags;
+ if (!initialFormat)
+ initialFormat = formatCollection()->defaultFormat();
+ Q3TextDocumentTag initag(QLatin1String(""), sheet_->item(QLatin1String("")), *initialFormat);
+ if (bodyText.isValid())
+ initag.format.setColor(bodyText);
+ Q3TextDocumentTag curtag = initag;
+ bool space = true;
+ bool canMergeLi = false;
+
+ bool textEditMode = false;
+ int tabExpansionColumn = 0;
+
+ const QChar* doc = text.unicode();
+ int length = text.length();
+ bool hasNewPar = curpar->length() <= 1;
+ QString anchorName;
+
+ // style sheet handling for margin and line spacing calculation below
+ Q3TextParagraph* stylesPar = curpar;
+ QVector<Q3StyleSheetItem *>* vec = 0;
+ QList< QVector<Q3StyleSheetItem *> *> styles;
+
+ if (cursor) {
+ cursor->splitAndInsertEmptyParagraph();
+ Q3TextCursor tmp = *cursor;
+ tmp.gotoPreviousLetter();
+ stylesPar = curpar = tmp.paragraph();
+ hasNewPar = true;
+ textEditMode = true;
+ } else {
+ NEWPAR;
+ }
+
+ // set rtext spacing to false for the initial paragraph.
+ curpar->rtext = false;
+
+ QString wellKnownTags = QLatin1String("br hr wsp table qt body meta title");
+
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (!hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
+ // open tag
+ QMap<QString, QString> attr;
+ QMap<QString, QString>::Iterator it, end = attr.end();
+ bool emptyTag = false;
+ QString tagname = parseOpenTag(doc, length, pos, attr, emptyTag);
+ if (tagname.isEmpty())
+ continue; // nothing we could do with this, probably parse error
+
+ const Q3StyleSheetItem* nstyle = sheet_->item(tagname);
+
+ if (nstyle) {
+ // we might have to close some 'forgotten' tags
+ while (!nstyle->allowedInContext(curtag.style)) {
+ QString msg;
+ msg.sprintf("QText Warning: Document not valid ('%s' not allowed in '%s' #%d)",
+ tagname.ascii(), curtag.style->name().ascii(), pos);
+ sheet_->error(msg);
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+ /* special handling for p and li for HTML
+ compatibility. We do not want to embed blocks in
+ p, and we do not want new blocks inside non-empty
+ lis. Plus we want to merge empty lis sometimes. */
+ if(nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ canMergeLi = true;
+ } else if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock) {
+ while (curtag.style->name() == QLatin1String("p")) {
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+ if (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ // we are in a li and a new block comes along
+ if (nstyle->name() == QString(QLatin1String("ul")) || nstyle->name() == QLatin1String("ol"))
+ hasNewPar = false; // we want an empty li (like most browsers)
+ if (!hasNewPar) {
+ /* do not add new blocks inside
+ non-empty lis */
+ while (curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+ } else if (canMergeLi) {
+ /* we have an empty li and a block
+ comes along, merge them */
+ nstyle = curtag.style;
+ }
+ canMergeLi = false;
+ }
+ }
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* custom = 0;
+#else
+ bool custom = false;
+#endif
+
+ // some well-known tags, some have a nstyle, some not
+ if (wellKnownTags.contains(tagname)) {
+ if (tagname == QLatin1String("br")) {
+ emptyTag = space = true;
+ int index = qMax(curpar->length(),1) - 1;
+ Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ curpar->append(QString(QChar(QChar::LineSeparator)));
+ curpar->setFormat(index, 1, &format);
+ hasNewPar = false;
+ } else if (tagname == QLatin1String("hr")) {
+ emptyTag = space = true;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
+#endif
+ } else if (tagname == QLatin1String("table")) {
+ emptyTag = space = true;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextFormat format = curtag.format.makeTextFormat( nstyle, attr, scaleFontsFactor);
+ curpar->setAlignment(curtag.alignment);
+ custom = parseTable(attr, format, doc, length, pos, curpar);
+#endif
+ } else if (tagname == QLatin1String("qt") || tagname == QLatin1String("body")) {
+ it = attr.find(QLatin1String("bgcolor"));
+ if (it != end) {
+ QBrush *b = new QBrush(QColor(*it));
+ setPaper(b);
+ }
+ it = attr.find(QLatin1String("background"));
+ if (it != end) {
+#ifndef QT_NO_MIME
+ QImage img;
+ QString bg = *it;
+ const QMimeSource* m = factory_->data(bg, contxt);
+ if (!m) {
+ qCritical("QRichText: no mimesource for %s",
+ QFile::encodeName(bg).data());
+ } else {
+ if (!Q3ImageDrag::decode(m, img)) {
+ qCritical("Q3TextImage: cannot decode %s",
+ QFile::encodeName(bg).data());
+ }
+ }
+ if (!img.isNull()) {
+ QBrush *b = new QBrush(QColor(), QPixmap(img));
+ setPaper(b);
+ }
+#endif
+ }
+ it = attr.find(QLatin1String("text"));
+ if (it != end) {
+ QColor c(*it);
+ initag.format.setColor(c);
+ curtag.format.setColor(c);
+ bodyText = c;
+ }
+ it = attr.find(QLatin1String("link"));
+ if (it != end)
+ linkColor = QColor(*it);
+ it = attr.find(QLatin1String("title"));
+ if (it != end)
+ attribs.insert(QLatin1String("title"), *it);
+
+ if (textEditMode) {
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ int count = a.count(QLatin1Char(';')) + 1;
+ for (int s = 0; s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
+ scaleFontsFactor = double(formatCollection()->defaultFormat()->fn.pointSize()) /
+ style.mid(10, style.length() - 12).toInt();
+ }
+ }
+ }
+ nstyle = 0; // ignore body in textEditMode
+ }
+ // end qt- and body-tag handling
+ } else if (tagname == QLatin1String("meta")) {
+ if (attr[QLatin1String("name")] == QLatin1String("qrichtext") && attr[QLatin1String("content")] == QLatin1String("1"))
+ textEditMode = true;
+ } else if (tagname == QLatin1String("title")) {
+ QString title;
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<')) && hasPrefix(doc, length, pos+1, QLatin1Char('/')) &&
+ parseCloseTag(doc, length, pos) == QLatin1String("title"))
+ break;
+ title += doc[pos];
+ ++pos;
+ }
+ attribs.insert(QLatin1String("title"), title);
+ }
+ } // end of well-known tag handling
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!custom) // try generic custom item
+ custom = tag(sheet_, tagname, attr, contxt, *factory_ , emptyTag, this);
+#endif
+ if (!nstyle && !custom) // we have no clue what this tag could be, ignore it
+ continue;
+
+ if (custom) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ int index = qMax(curpar->length(),1) - 1;
+ Q3TextFormat format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ curpar->append(QString(QLatin1Char('*')));
+ Q3TextFormat* f = formatCollection()->format(&format);
+ curpar->setFormat(index, 1, f);
+ curpar->at(index)->setCustomItem(custom);
+ if (!curtag.anchorHref.isEmpty())
+ curpar->at(index)->setAnchor(QString(), curtag.anchorHref);
+ if (!anchorName.isEmpty() ) {
+ curpar->at(index)->setAnchor(anchorName, curpar->at(index)->anchorHref());
+ anchorName.clear();
+ }
+ registerCustomItem(custom, curpar);
+ hasNewPar = false;
+#endif
+ } else if (!emptyTag) {
+ /* if we do nesting, push curtag on the stack,
+ otherwise reinint curag. */
+ if (curtag.style->name() != tagname || nstyle->selfNesting()) {
+ tags.push(curtag);
+ } else {
+ if (!tags.isEmpty())
+ curtag = tags.top();
+ else
+ curtag = initag;
+ }
+
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ curtag.name = tagname;
+ curtag.style = nstyle;
+ if (nstyle->whiteSpaceMode() != Q3StyleSheetItem::WhiteSpaceModeUndefined)
+ curtag.wsm = nstyle->whiteSpaceMode();
+
+ /* netscape compatibility: eat a newline and only a newline if a pre block starts */
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre &&
+ nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock)
+ eat(doc, length, pos, QLatin1Char('\n'));
+
+ /* ignore whitespace for inline elements if there
+ was already one*/
+ if (!textEditMode &&
+ (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal
+ || curtag.wsm == Q3StyleSheetItem::WhiteSpaceNoWrap)
+ && (space || nstyle->displayMode() != Q3StyleSheetItem::DisplayInline))
+ eatSpace(doc, length, pos);
+
+ curtag.format = curtag.format.makeTextFormat(nstyle, attr, scaleFontsFactor);
+ if (nstyle->isAnchor()) {
+ if (!anchorName.isEmpty())
+ anchorName += QLatin1String("#") + attr[QLatin1String("name")];
+ else
+ anchorName = attr[QLatin1String("name")];
+ curtag.anchorHref = attr[QLatin1String("href")];
+ }
+
+ if (nstyle->alignment() != Q3StyleSheetItem::Undefined)
+ curtag.alignment = nstyle->alignment();
+
+ if (nstyle->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ curtag.liststyle = nstyle->listStyle();
+
+ if (nstyle->displayMode() == Q3StyleSheetItem::DisplayBlock
+ || nstyle->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+
+ if (nstyle->name() == QLatin1String("ol") ||
+ nstyle->name() == QLatin1String("ul") ||
+ nstyle->name() == QLatin1String("li")) {
+ QString type = attr[QLatin1String("type")];
+ if (!type.isEmpty()) {
+ if (type == QLatin1String("1")) {
+ curtag.liststyle = Q3StyleSheetItem::ListDecimal;
+ } else if (type == QLatin1String("a")) {
+ curtag.liststyle = Q3StyleSheetItem::ListLowerAlpha;
+ } else if (type == QLatin1String("A")) {
+ curtag.liststyle = Q3StyleSheetItem::ListUpperAlpha;
+ } else {
+ type = type.toLower();
+ if (type == QLatin1String("square"))
+ curtag.liststyle = Q3StyleSheetItem::ListSquare;
+ else if (type == QLatin1String("disc"))
+ curtag.liststyle = Q3StyleSheetItem::ListDisc;
+ else if (type == QLatin1String("circle"))
+ curtag.liststyle = Q3StyleSheetItem::ListCircle;
+ }
+ }
+ }
+
+
+ /* Internally we treat ordered and bullet
+ lists the same for margin calculations. In
+ order to have fast pointer compares in the
+ xMargin() functions we restrict ourselves to
+ <ol>. Once we calculate the margins in the
+ parser rathern than later, the unelegance of
+ this approach goes awy
+ */
+ if (nstyle->name() == QLatin1String("ul"))
+ curtag.style = sheet_->item(QLatin1String("ol"));
+
+ it = attr.find(QLatin1String("align"));
+ if (it != end) {
+ QString align = (*it).toLower();
+ if (align == QLatin1String("center"))
+ curtag.alignment = Qt::AlignCenter;
+ else if (align == QLatin1String("right"))
+ curtag.alignment = Qt::AlignRight;
+ else if (align == QLatin1String("justify"))
+ curtag.alignment = Qt::AlignJustify;
+ }
+ it = attr.find(QLatin1String("dir"));
+ if (it != end) {
+ QString dir = (*it).toLower();
+ if (dir == QLatin1String("rtl"))
+ curtag.direction = QChar::DirR;
+ else if (dir == QLatin1String("ltr"))
+ curtag.direction = QChar::DirL;
+ }
+
+ NEWPAR;
+
+ if (curtag.style && curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem) {
+ it = attr.find(QLatin1String("value"));
+ if (it != end)
+ curpar->setListValue((*it).toInt());
+ }
+
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ bool ok = true;
+ int count = a.count(QLatin1Char(';'))+1;
+ for (int s = 0; ok && s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("margin-top:")) && style.endsWith(QLatin1String("px")))
+ curpar->utm = 1+style.mid(11, style.length() - 13).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-bottom:")) && style.endsWith(QLatin1String("px")))
+ curpar->ubm = 1+style.mid(14, style.length() - 16).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-left:")) && style.endsWith(QLatin1String("px")))
+ curpar->ulm = 1+style.mid(12, style.length() - 14).toInt(&ok);
+ else if (style.startsWith(QLatin1String("margin-right:")) && style.endsWith(QLatin1String("px")))
+ curpar->urm = 1+style.mid(13, style.length() - 15).toInt(&ok);
+ else if (style.startsWith(QLatin1String("text-indent:")) && style.endsWith(QLatin1String("px")))
+ curpar->uflm = 1+style.mid(12, style.length() - 14).toInt(&ok);
+ }
+ if (!ok) // be pressmistic
+ curpar->utm = curpar->ubm = curpar->urm = curpar->ulm = 0;
+ }
+ } else if (nstyle->name() == QLatin1String("html")) {
+ it = attr.find(QLatin1String("dir"));
+ if (it != end) {
+ QString dir = (*it).toLower();
+ if (dir == QLatin1String("rtl"))
+ curtag.direction = QChar::DirR;
+ else if (dir == QLatin1String("ltr"))
+ curtag.direction = QChar::DirL;
+ }
+ }
+ }
+ } else {
+ QString tagname = parseCloseTag(doc, length, pos);
+ if (tagname.isEmpty())
+ continue; // nothing we could do with this, probably parse error
+ if (!sheet_->item(tagname)) // ignore unknown tags
+ continue;
+ if (tagname == QLatin1String("li"))
+ continue;
+
+ // we close a block item. Since the text may continue, we need to have a new paragraph
+ bool needNewPar = curtag.style->displayMode() == Q3StyleSheetItem::DisplayBlock
+ || curtag.style->displayMode() == Q3StyleSheetItem::DisplayListItem;
+
+
+ // html slopiness: handle unbalanched tag closing
+ while (curtag.name != tagname) {
+ QString msg;
+ msg.sprintf("QText Warning: Document not valid ('%s' not closed before '%s' #%d)",
+ curtag.name.ascii(), tagname.ascii(), pos);
+ sheet_->error(msg);
+ if (tags.isEmpty())
+ break;
+ curtag = tags.pop();
+ }
+
+
+ // close the tag
+ if (!tags.isEmpty())
+ curtag = tags.pop();
+ else
+ curtag = initag;
+
+ if (needNewPar) {
+ if (textEditMode && (tagname == QLatin1String("p") || tagname == QLatin1String("div"))) // preserve empty paragraphs
+ hasNewPar = false;
+ NEWPAR;
+ }
+ }
+ } else {
+ // normal contents
+ QString s;
+ QChar c;
+ while (pos < length && !hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (textEditMode) {
+ // text edit mode: we handle all white space but ignore newlines
+ c = parseChar(doc, length, pos, Q3StyleSheetItem::WhiteSpacePre);
+ if (c == QChar::LineSeparator)
+ break;
+ } else {
+ int l = pos;
+ c = parseChar(doc, length, pos, curtag.wsm);
+
+ // in white space pre mode: treat any space as non breakable
+ // and expand tabs to eight character wide columns.
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpacePre) {
+ if (c == QLatin1Char('\t')) {
+ c = QLatin1Char(' ');
+ while((++tabExpansionColumn)%8)
+ s += c;
+ }
+ if (c == QChar::LineSeparator)
+ tabExpansionColumn = 0;
+ else
+ tabExpansionColumn++;
+
+ }
+ if (c == QLatin1Char(' ') || c == QChar::LineSeparator) {
+ /* avoid overlong paragraphs by forcing a new
+ paragraph after 4096 characters. This case can
+ occur when loading undiscovered plain text
+ documents in rich text mode. Instead of hanging
+ forever, we do the trick.
+ */
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && s.length() > 4096) do {
+ if (doc[l] == QLatin1Char('\n')) {
+ hasNewPar = false; // for a new paragraph ...
+ NEWPAR;
+ hasNewPar = false; // ... and make it non-reusable
+ c = QLatin1Char('\n'); // make sure we break below
+ break;
+ }
+ } while (++l < pos);
+ }
+ }
+
+ if (c == QLatin1Char('\n'))
+ break; // break on newlines, pre delievers a QChar::LineSeparator
+
+ bool c_isSpace = c.isSpace() && c.unicode() != 0x00a0U && !textEditMode;
+
+ if (curtag.wsm == Q3StyleSheetItem::WhiteSpaceNormal && c_isSpace && space)
+ continue;
+ if (c == QLatin1Char('\r'))
+ continue;
+ space = c_isSpace;
+ s += c;
+ }
+ if (!s.isEmpty() && curtag.style->displayMode() != Q3StyleSheetItem::DisplayNone) {
+ hasNewPar = false;
+ int index = qMax(curpar->length(),1) - 1;
+ curpar->append(s);
+ if (curtag.wsm != Q3StyleSheetItem::WhiteSpaceNormal) {
+ Q3TextString *str = curpar->string();
+ for (int i = index; i < index + s.length(); ++i)
+ str->at(i).nobreak = true;
+ }
+
+ Q3TextFormat* 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(), curtag.anchorHref);
+ }
+ if (!anchorName.isEmpty() ) {
+ for (int i = 0; i < int(s.length()); i++)
+ curpar->at(index + i)->setAnchor(anchorName, curpar->at(index + i)->anchorHref());
+ anchorName.clear();
+ }
+ }
+ }
+ }
+
+ if (hasNewPar && curpar != fParag && !cursor && stylesPar != curpar) {
+ // 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.clear();
+ }
+
+ setRichTextMarginsInternal(styles, stylesPar);
+
+ if (cursor) {
+ cursor->gotoPreviousLetter();
+ cursor->remove();
+ }
+ while (!styles.isEmpty())
+ delete styles.takeFirst();
+ delete vec;
+}
+
+void Q3TextDocument::setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar)
+{
+ // margin and line spacing calculation
+ // qDebug("setRichTextMarginsInternal: styles.size() = %d", styles.size());
+ QVector<Q3StyleSheetItem *>* prevStyle = 0;
+ int stylesIndex = 0;
+ QVector<Q3StyleSheetItem *>* curStyle = styles.size() ? styles.first() : 0;
+ QVector<Q3StyleSheetItem *>* nextStyle =
+ (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ while (stylesPar) {
+ if (!curStyle) {
+ stylesPar = stylesPar->next();
+ prevStyle = curStyle;
+ curStyle = nextStyle;
+ nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ continue;
+ }
+
+ int i, mar;
+ Q3StyleSheetItem* mainStyle = curStyle->size() ? (*curStyle)[curStyle->size()-1] : 0;
+ if (mainStyle && mainStyle->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ stylesPar->setListItem(true);
+ int numLists = 0;
+ for (i = 0; i < (int)curStyle->size(); ++i) {
+ if ((*curStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
+ && (*curStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ numLists++;
+ }
+ stylesPar->ldepth = numLists;
+ if (stylesPar->next() && nextStyle) {
+ // also set the depth of the next paragraph, required for the margin calculation
+ numLists = 0;
+ for (i = 0; i < (int)nextStyle->size(); ++i) {
+ if ((*nextStyle)[i]->displayMode() == Q3StyleSheetItem::DisplayBlock
+ && (*nextStyle)[i]->listStyle() != Q3StyleSheetItem::ListStyleUndefined)
+ numLists++;
+ }
+ stylesPar->next()->ldepth = numLists;
+ }
+
+ // do the top margin
+ Q3StyleSheetItem* item = mainStyle;
+ int m;
+ if (stylesPar->utm > 0) {
+ m = stylesPar->utm-1;
+ stylesPar->utm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
+ if (stylesPar->ldepth) {
+ if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ m /= stylesPar->ldepth * stylesPar->ldepth;
+ else
+ m = 0;
+ }
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (prevStyle && i < (int) prevStyle->size() &&
+ ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
+ (*prevStyle)[i] == item))
+ break;
+ // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
+ if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
+ (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
+ continue;
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginTop));
+ m = qMax(m, mar);
+ }
+ stylesPar->utm = m - stylesPar->topMargin();
+
+ // do the bottom margin
+ item = mainStyle;
+ if (stylesPar->ubm > 0) {
+ m = stylesPar->ubm-1;
+ stylesPar->ubm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
+ if (stylesPar->ldepth) {
+ if (item->displayMode() == Q3StyleSheetItem::DisplayListItem)
+ m /= stylesPar->ldepth * stylesPar->ldepth;
+ else
+ m = 0;
+ }
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (nextStyle && i < (int) nextStyle->size() &&
+ ( item->displayMode() == Q3StyleSheetItem::DisplayBlock &&
+ (*nextStyle)[i] == item))
+ break;
+ // emulate CSS2' standard 0 vertical margin for multiple ul or ol tags
+ if (item->listStyle() != Q3StyleSheetItem::ListStyleUndefined &&
+ (( i> 0 && (*curStyle)[i-1] == item) || (*curStyle)[i+1] == item))
+ continue;
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginBottom));
+ m = qMax(m, mar);
+ }
+ stylesPar->ubm = m - stylesPar->bottomMargin();
+
+ // do the left margin, simplyfied
+ item = mainStyle;
+ if (stylesPar->ulm > 0) {
+ m = stylesPar->ulm-1;
+ stylesPar->ulm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ m += qMax(0, item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ stylesPar->ulm = m - stylesPar->leftMargin();
+
+ // do the right margin, simplyfied
+ item = mainStyle;
+ if (stylesPar->urm > 0) {
+ m = stylesPar->urm-1;
+ stylesPar->urm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ m += qMax(0, item->margin(Q3StyleSheetItem::MarginRight));
+ }
+ stylesPar->urm = m - stylesPar->rightMargin();
+
+ // do the first line margin, which really should be called text-indent
+ item = mainStyle;
+ if (stylesPar->uflm > 0) {
+ m = stylesPar->uflm-1;
+ stylesPar->uflm = 0;
+ } else {
+ m = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
+ }
+ for (i = (int)curStyle->size() - 2 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ mar = qMax(0, item->margin(Q3StyleSheetItem::MarginFirstLine));
+ m = qMax(m, mar);
+ }
+ stylesPar->uflm =m - stylesPar->firstLineMargin();
+
+ // do the bogus line "spacing", which really is just an extra margin
+ item = mainStyle;
+ for (i = (int)curStyle->size() - 1 ; i >= 0; --i) {
+ item = (*curStyle)[i];
+ if (item->lineSpacing() != Q3StyleSheetItem::Undefined) {
+ stylesPar->ulinespacing = item->lineSpacing();
+ if (formatCollection() &&
+ stylesPar->ulinespacing < formatCollection()->defaultFormat()->height())
+ stylesPar->ulinespacing += formatCollection()->defaultFormat()->height();
+ break;
+ }
+ }
+
+ stylesPar = stylesPar->next();
+ prevStyle = curStyle;
+ curStyle = nextStyle;
+ nextStyle = (++stylesIndex) < styles.size() ? styles.at(stylesIndex) : 0;
+ }
+}
+
+void Q3TextDocument::setText(const QString &text, const QString &context)
+{
+ focusIndicator.parag = 0;
+ selections.clear();
+ if ((txtFormat == Qt::AutoText && Q3StyleSheet::mightBeRichText(text))
+ || txtFormat == Qt::RichText)
+ setRichText(text, context);
+ else
+ setPlainText(text);
+}
+
+QString Q3TextDocument::plainText() const
+{
+ QString buffer;
+ QString s;
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ if (!p->mightHaveCustomItems) {
+ const Q3TextString *ts = p->string(); // workaround VC++ and Borland
+ s = ts->toString(); // with false we don't fix spaces (nbsp)
+ } else {
+ for (int i = 0; i < p->length() - 1; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String("\n");
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ s.remove(s.length() - 1, 1);
+ if (p->next())
+ s += QLatin1String("\n");
+ buffer += s;
+ p = p->next();
+ }
+ return buffer;
+}
+
+static QString align_to_string(int a)
+{
+ if (a & Qt::AlignRight)
+ return QLatin1String(" align=\"right\"");
+ if (a & Qt::AlignHCenter)
+ return QLatin1String(" align=\"center\"");
+ if (a & Qt::AlignJustify)
+ return QLatin1String(" align=\"justify\"");
+ return QString();
+}
+
+static QString direction_to_string(int dir)
+{
+ if (dir != QChar::DirON)
+ return (dir == QChar::DirL? QLatin1String(" dir=\"ltr\"") : QLatin1String(" dir=\"rtl\""));
+ return QString();
+}
+
+static QString list_value_to_string(int v)
+{
+ if (v != -1)
+ return QLatin1String(" listvalue=\"") + QString::number(v) + QLatin1Char('"');
+ return QString();
+}
+
+static QString list_style_to_string(int v)
+{
+ switch(v) {
+ case Q3StyleSheetItem::ListDecimal: return QLatin1String("\"1\"");
+ case Q3StyleSheetItem::ListLowerAlpha: return QLatin1String("\"a\"");
+ case Q3StyleSheetItem::ListUpperAlpha: return QLatin1String("\"A\"");
+ case Q3StyleSheetItem::ListDisc: return QLatin1String("\"disc\"");
+ case Q3StyleSheetItem::ListSquare: return QLatin1String("\"square\"");
+ case Q3StyleSheetItem::ListCircle: return QLatin1String("\"circle\"");
+ default:
+ return QString();
+ }
+}
+
+static inline bool list_is_ordered(int v)
+{
+ return v == Q3StyleSheetItem::ListDecimal ||
+ v == Q3StyleSheetItem::ListLowerAlpha ||
+ v == Q3StyleSheetItem::ListUpperAlpha;
+}
+
+
+static QString margin_to_string(Q3StyleSheetItem* style, int t, int b, int l, int r, int fl)
+{
+ QString s;
+ if (l > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-left:") +
+ QString::number(l+qMax(0,style->margin(Q3StyleSheetItem::MarginLeft))) + QLatin1String("px");
+ if (r > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-right:") +
+ QString::number(r+qMax(0,style->margin(Q3StyleSheetItem::MarginRight))) + QLatin1String("px");
+ if (t > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-top:") +
+ QString::number(t+qMax(0,style->margin(Q3StyleSheetItem::MarginTop))) + QLatin1String("px");
+ if (b > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("margin-bottom:") +
+ QString::number(b+qMax(0,style->margin(Q3StyleSheetItem::MarginBottom))) + QLatin1String("px");
+ if (fl > 0)
+ s += QString(s.size() ? QLatin1String(";") : QLatin1String("")) + QLatin1String("text-indent:") +
+ QString::number(fl+qMax(0,style->margin(Q3StyleSheetItem::MarginFirstLine))) + QLatin1String("px");
+ if (s.size())
+ return QLatin1String(" style=\"") + s + QLatin1String("\"");
+ return QString();
+}
+
+QString Q3TextDocument::richText() const
+{
+ QString s = QLatin1String("");
+ if (!par) {
+ s += QLatin1String("<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body style=\"font-size:");
+ s += QString::number(formatCollection()->defaultFormat()->font().pointSize());
+ s += QLatin1String("pt;font-family:");
+ s += formatCollection()->defaultFormat()->font().family();
+ s += QLatin1String("\">");
+ }
+ Q3TextParagraph* p = fParag;
+
+ Q3StyleSheetItem* item_p = styleSheet()->item(QLatin1String("p"));
+ Q3StyleSheetItem* item_div = styleSheet()->item(QLatin1String("div"));
+ Q3StyleSheetItem* item_ul = styleSheet()->item(QLatin1String("ul"));
+ Q3StyleSheetItem* item_ol = styleSheet()->item(QLatin1String("ol"));
+ Q3StyleSheetItem* item_li = styleSheet()->item(QLatin1String("li"));
+ if (!item_p || !item_div || !item_ul || !item_ol || !item_li) {
+ qWarning("QTextEdit: cannot export HTML due to insufficient stylesheet (lack of p, div, ul, ol, or li)");
+ return QString();
+ }
+ int pastListDepth = 0;
+ int listDepth = 0;
+#if 0
+ int futureListDepth = 0;
+#endif
+ QVector<int> listStyles(10);
+
+ while (p) {
+ listDepth = p->listDepth();
+ if (listDepth < pastListDepth) {
+ for (int i = pastListDepth; i > listDepth; i--)
+ s += list_is_ordered(listStyles[i]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
+ s += QLatin1Char('\n');
+ } else if (listDepth > pastListDepth) {
+ s += QLatin1Char('\n');
+ listStyles.resize(qMax((int)listStyles.size(), listDepth+1));
+ QString list_type;
+ listStyles[listDepth] = p->listStyle();
+ if (!list_is_ordered(p->listStyle()) || item_ol->listStyle() != p->listStyle())
+ list_type = QLatin1String(" type=") + list_style_to_string(p->listStyle());
+ for (int i = pastListDepth; i < listDepth; i++) {
+ s += list_is_ordered(p->listStyle()) ? QLatin1String("<ol") : QLatin1String("<ul");
+ s += list_type + QLatin1String(">");
+ }
+ } else {
+ s += QLatin1Char('\n');
+ }
+
+ QString ps = p->richText();
+
+#if 0
+ // for the bottom margin we need to know whether we are at the end of a list
+ futureListDepth = 0;
+ if (listDepth > 0 && p->next())
+ futureListDepth = p->next()->listDepth();
+#endif
+
+ if (richTextExportStart && richTextExportStart->paragraph() ==p &&
+ richTextExportStart->index() == 0)
+ s += QLatin1String("<!--StartFragment-->");
+
+ if (p->isListItem()) {
+ s += QLatin1String("<li");
+ if (p->listStyle() != listStyles[listDepth])
+ s += QLatin1String(" type=") + list_style_to_string(p->listStyle());
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_li, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += list_value_to_string(p->listValue());
+ s += direction_to_string(p->direction());
+ s += QLatin1String(">");
+ s += ps;
+ s += QLatin1String("</li>");
+ } else if (p->listDepth()) {
+ s += QLatin1String("<div");
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_div, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += direction_to_string(p->direction());
+ s += QLatin1String(">");
+ s += ps;
+ s += QLatin1String("</div>");
+ } else {
+ // normal paragraph item
+ s += QLatin1String("<p");
+ s += align_to_string(p->alignment());
+ s += margin_to_string(item_p, p->utm, p->ubm, p->ulm, p->urm, p->uflm);
+ s += direction_to_string(p->direction());
+ s += QLatin1String(">");
+ s += ps;
+ s += QLatin1String("</p>");
+ }
+ pastListDepth = listDepth;
+ p = p->next();
+ }
+ while (listDepth > 0) {
+ s += list_is_ordered(listStyles[listDepth]) ? QLatin1String("</ol>") : QLatin1String("</ul>");
+ listDepth--;
+ }
+
+ if (!par)
+ s += QLatin1String("\n</body></html>\n");
+
+ return s;
+}
+
+QString Q3TextDocument::text() const
+{
+ if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
+ return richText();
+ return plainText();
+}
+
+QString Q3TextDocument::text(int parag) const
+{
+ Q3TextParagraph *p = paragAt(parag);
+ if (!p)
+ return QString();
+
+ if ((txtFormat == Qt::AutoText && preferRichText) || txtFormat == Qt::RichText)
+ return p->richText();
+ else
+ return p->string()->toString();
+}
+
+void Q3TextDocument::invalidate()
+{
+ Q3TextParagraph *s = fParag;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextDocument::selectionStart(int id, int &paragId, int &index)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+ Q3TextDocumentSelection &sel = *it;
+ paragId = !sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
+ index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+Q3TextCursor Q3TextDocument::selectionStartCursor(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return Q3TextCursor(this);
+ Q3TextDocumentSelection &sel = *it;
+ if (sel.swapped)
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+Q3TextCursor Q3TextDocument::selectionEndCursor(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return Q3TextCursor(this);
+ Q3TextDocumentSelection &sel = *it;
+ if (!sel.swapped)
+ return sel.endCursor;
+ return sel.startCursor;
+}
+
+void Q3TextDocument::selectionEnd(int id, int &paragId, int &index)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+ Q3TextDocumentSelection &sel = *it;
+ paragId = sel.swapped ? sel.startCursor.paragraph()->paragId() : sel.endCursor.paragraph()->paragId();
+ index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
+}
+
+void Q3TextDocument::addSelection(int id)
+{
+ nSelections = qMax(nSelections, id + 1);
+}
+
+static void setSelectionEndHelper(int id, Q3TextDocumentSelection &sel, Q3TextCursor &start, Q3TextCursor &end)
+{
+ Q3TextCursor c1 = start;
+ Q3TextCursor c2 = end;
+ if (sel.swapped) {
+ c1 = end;
+ c2 = start;
+ }
+
+ c1.paragraph()->removeSelection(id);
+ c2.paragraph()->removeSelection(id);
+ if (c1.paragraph() != c2.paragraph()) {
+ c1.paragraph()->setSelection(id, c1.index(), c1.paragraph()->length() - 1);
+ c2.paragraph()->setSelection(id, 0, c2.index());
+ } else {
+ c1.paragraph()->setSelection(id, qMin(c1.index(), c2.index()), qMax(c1.index(), c2.index()));
+ }
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+}
+
+bool Q3TextDocument::setSelectionEnd(int id, const Q3TextCursor &cursor)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return false;
+ Q3TextDocumentSelection &sel = *it;
+
+ Q3TextCursor start = sel.startCursor;
+ Q3TextCursor end = cursor;
+
+ if (start == end) {
+ removeSelection(id);
+ setSelectionStart(id, cursor);
+ return true;
+ }
+
+ if (sel.endCursor.paragraph() == end.paragraph()) {
+ setSelectionEndHelper(id, sel, start, end);
+ return true;
+ }
+
+ bool inSelection = false;
+ Q3TextCursor c(this);
+ Q3TextCursor tmp = sel.startCursor;
+ if (sel.swapped)
+ tmp = sel.endCursor;
+ tmp.restoreState();
+ Q3TextCursor tmp2 = cursor;
+ tmp2.restoreState();
+ c.setParagraph(tmp.paragraph()->paragId() < tmp2.paragraph()->paragId() ? tmp.paragraph() : tmp2.paragraph());
+ bool hadStart = false;
+ bool hadEnd = false;
+ bool hadStartParag = false;
+ bool hadEndParag = false;
+ bool hadOldStart = false;
+ bool hadOldEnd = false;
+ bool leftSelection = false;
+ sel.swapped = false;
+ for (;;) {
+ if (c == start)
+ hadStart = true;
+ if (c == end)
+ hadEnd = true;
+ if (c.paragraph() == start.paragraph())
+ hadStartParag = true;
+ if (c.paragraph() == end.paragraph())
+ hadEndParag = true;
+ if (c == sel.startCursor)
+ hadOldStart = true;
+ if (c == sel.endCursor)
+ hadOldEnd = true;
+
+ if (!sel.swapped &&
+ ((hadEnd && !hadStart)
+ || (hadEnd && hadStart && start.paragraph() == end.paragraph() && start.index() > end.index())))
+ sel.swapped = true;
+
+ if ((c == end && hadStartParag) || (c == start && hadEndParag)) {
+ Q3TextCursor tmp = c;
+ tmp.restoreState();
+ if (tmp.paragraph() != c.paragraph()) {
+ int sstart = tmp.paragraph()->selectionStart(id);
+ tmp.paragraph()->removeSelection(id);
+ tmp.paragraph()->setSelection(id, sstart, tmp.index());
+ }
+ }
+
+ if (inSelection &&
+ ((c == end && hadStart) || (c == start && hadEnd)))
+ leftSelection = true;
+ else if (!leftSelection && !inSelection && (hadStart || hadEnd))
+ inSelection = true;
+
+ bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.paragraph()->hasSelection(id) && c.atParagEnd();
+ c.paragraph()->removeSelection(id);
+ if (inSelection) {
+ if (c.paragraph() == start.paragraph() && start.paragraph() == end.paragraph()) {
+ c.paragraph()->setSelection(id, qMin(start.index(), end.index()), qMax(start.index(), end.index()));
+ } else if (c.paragraph() == start.paragraph() && !hadEndParag) {
+ c.paragraph()->setSelection(id, start.index(), c.paragraph()->length() - 1);
+ } else if (c.paragraph() == end.paragraph() && !hadStartParag) {
+ c.paragraph()->setSelection(id, end.index(), c.paragraph()->length() - 1);
+ } else if (c.paragraph() == end.paragraph() && hadEndParag) {
+ c.paragraph()->setSelection(id, 0, end.index());
+ } else if (c.paragraph() == start.paragraph() && hadStartParag) {
+ c.paragraph()->setSelection(id, 0, start.index());
+ } else {
+ c.paragraph()->setSelection(id, 0, c.paragraph()->length() - 1);
+ }
+ }
+
+ if (leftSelection)
+ inSelection = false;
+
+ if (noSelectionAnymore)
+ break;
+ // *ugle*hack optimization
+ Q3TextParagraph *p = c.paragraph();
+ if ( p->mightHaveCustomItems || p == start.paragraph() || p == end.paragraph() || p == lastParagraph()) {
+ c.gotoNextLetter();
+ if (p == lastParagraph() && c.atParagEnd())
+ break;
+ } else {
+ if (p->document()->parent())
+ do {
+ c.gotoNextLetter();
+ } while (c.paragraph() == p);
+ else
+ c.setParagraph(p->next());
+ }
+ }
+
+ if (!sel.swapped)
+ sel.startCursor.paragraph()->setSelection(id, sel.startCursor.index(), sel.startCursor.paragraph()->length() - 1);
+
+ sel.startCursor = start;
+ sel.endCursor = end;
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph())
+ sel.swapped = sel.startCursor.index() > sel.endCursor.index();
+
+ setSelectionEndHelper(id, sel, start, end);
+
+ return true;
+}
+
+void Q3TextDocument::selectAll(int id)
+{
+ removeSelection(id);
+
+ Q3TextDocumentSelection sel;
+ sel.swapped = false;
+ Q3TextCursor c(this);
+
+ c.setParagraph(fParag);
+ c.setIndex(0);
+ sel.startCursor = c;
+
+ c.setParagraph(lParag);
+ c.setIndex(lParag->length() - 1);
+ sel.endCursor = c;
+
+ selections.insert(id, sel);
+
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ p->setSelection(id, 0, p->length() - 1);
+ p = p->next();
+ }
+
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->selectAll(id);
+ }
+}
+
+bool Q3TextDocument::removeSelection(int id)
+{
+ if (!selections.contains(id))
+ return false;
+
+ Q3TextDocumentSelection &sel = selections[id];
+
+ Q3TextCursor start = sel.swapped ? sel.endCursor : sel.startCursor;
+ Q3TextCursor end = sel.swapped ? sel.startCursor : sel.endCursor;
+ Q3TextParagraph* p = 0;
+ while (start != end) {
+ if (p != start.paragraph()) {
+ p = start.paragraph();
+ p->removeSelection(id);
+ //### avoid endless loop by all means necessary, did somebody mention refactoring?
+ if (!parent() && p == lParag)
+ break;
+ }
+ start.gotoNextLetter();
+ }
+ p = start.paragraph();
+ p->removeSelection(id);
+ selections.remove(id);
+ return true;
+}
+
+QString Q3TextDocument::selectedText(int id, bool asRichText) const
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(id);
+ if (it == selections.end())
+ return QString();
+
+ Q3TextDocumentSelection sel = *it;
+
+
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor 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 when refactoring
+ */
+ while (c2.nestedDepth() > c1.nestedDepth())
+ c2.oneUp();
+ while (c1.nestedDepth() > c2.nestedDepth())
+ c1.oneUp();
+ while (c1.nestedDepth() && c2.nestedDepth() &&
+ c1.paragraph()->document() != c2.paragraph()->document()) {
+ c1.oneUp();
+ c2.oneUp();
+ }
+ // do not trust sel_swapped with tables. Fix this properly when refactoring as well
+ if (c1.paragraph()->paragId() > c2.paragraph()->paragId() ||
+ (c1.paragraph() == c2.paragraph() && c1.index() > c2.index())) {
+ Q3TextCursor tmp = c1;
+ c2 = c1;
+ c1 = tmp;
+ }
+
+ // end selection 3.0.3 improvement
+
+ if (asRichText && !parent()) {
+ richTextExportStart = &c1;
+ richTextExportEnd = &c2;
+
+ QString sel = richText();
+ int from = sel.indexOf(QLatin1String("<!--StartFragment-->"));
+ if (from >= 0) {
+ from += 20;
+ // find the previous span and move it into the start fragment before we clip it
+ QString prevspan;
+ int pspan = sel.lastIndexOf(QLatin1String("<span"), from-21);
+ if (pspan > sel.lastIndexOf(QLatin1String("</span"), from-21)) {
+ int spanend = sel.indexOf(QLatin1Char('>'), pspan);
+ prevspan = sel.mid(pspan, spanend - pspan + 1);
+ }
+ int to = sel.lastIndexOf(QLatin1String("<!--EndFragment-->"));
+ if (from <= to)
+ sel = QLatin1String("<!--StartFragment-->") + prevspan + sel.mid(from, to - from);
+ }
+ richTextExportStart = richTextExportEnd = 0;
+ return sel;
+ }
+
+ QString s;
+ if (c1.paragraph() == c2.paragraph()) {
+ Q3TextParagraph *p = c1.paragraph();
+ int end = c2.index();
+ if (p->at(qMax(0, end - 1))->isCustom())
+ ++end;
+ if (!p->mightHaveCustomItems) {
+ s += p->string()->toString().mid(c1.index(), end - c1.index());
+ } else {
+ for (int i = c1.index(); i < end; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String("\n");
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ } else {
+ Q3TextParagraph *p = c1.paragraph();
+ int start = c1.index();
+ while (p) {
+ int end = p == c2.paragraph() ? c2.index() : p->length() - 1;
+ if (p == c2.paragraph() && p->at(qMax(0, end - 1))->isCustom())
+ ++end;
+ if (!p->mightHaveCustomItems) {
+ s += p->string()->toString().mid(start, end - start);
+ if (p != c2.paragraph())
+ s += QLatin1String("\n");
+ } else {
+ for (int i = start; i < end; ++i) {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ s += QLatin1String(QLatin1String("\n"));
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ s += c->richText()->plainText() + QLatin1String("\n");
+ }
+ s += QLatin1String("\n");
+ }
+ } else
+#endif
+ {
+ s += p->at(i)->c;
+ }
+ }
+ }
+ start = 0;
+ if (p == c2.paragraph())
+ break;
+ p = p->next();
+ }
+ }
+ // ### workaround for plain text export until we get proper
+ // mime types: turn unicode line seperators into the more
+ // widely understood \n. Makes copy and pasting code snipplets
+ // from within Assistent possible
+ QChar* uc = (QChar*) s.unicode();
+ for (int ii = 0; ii < s.length(); ii++) {
+ if (uc[(int)ii] == QChar::LineSeparator)
+ uc[(int)ii] = QLatin1Char('\n');
+ else if ( uc[(int)ii] == QChar::Nbsp )
+ uc[(int)ii] = QLatin1Char(' ');
+ }
+ return s;
+}
+
+void Q3TextDocument::setFormat(int id, Q3TextFormat *f, int flags)
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.constFind(id);
+ if (it == selections.constEnd())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor c2 = sel.endCursor;
+ if (sel.swapped) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ c2.restoreState();
+ c1.restoreState();
+
+ if (c1.paragraph() == c2.paragraph()) {
+ c1.paragraph()->setFormat(c1.index(), c2.index() - c1.index(), f, true, flags);
+ return;
+ }
+
+ c1.paragraph()->setFormat(c1.index(), c1.paragraph()->length() - c1.index(), f, true, flags);
+ Q3TextParagraph *p = c1.paragraph()->next();
+ while (p && p != c2.paragraph()) {
+ p->setFormat(0, p->length(), f, true, flags);
+ p = p->next();
+ }
+ c2.paragraph()->setFormat(0, c2.index(), f, true, flags);
+}
+
+void Q3TextDocument::removeSelectedText(int id, Q3TextCursor *cursor)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextCursor c1 = sel.startCursor;
+ Q3TextCursor c2 = sel.endCursor;
+ if (sel.swapped) {
+ c2 = sel.startCursor;
+ c1 = sel.endCursor;
+ }
+
+ // ### no support for editing tables yet
+ if (c1.nestedDepth() || c2.nestedDepth())
+ return;
+
+ c2.restoreState();
+ c1.restoreState();
+
+ *cursor = c1;
+ removeSelection(id);
+
+ if (c1.paragraph() == c2.paragraph()) {
+ c1.paragraph()->remove(c1.index(), c2.index() - c1.index());
+ return;
+ }
+
+ if (c1.paragraph() == fParag && c1.index() == 0 &&
+ c2.paragraph() == lParag && c2.index() == lParag->length() - 1)
+ cursor->setValid(false);
+
+ bool didGoLeft = false;
+ if ( c1.index() == 0 && c1.paragraph() != fParag) {
+ cursor->gotoPreviousLetter();
+ didGoLeft = cursor->isValid();
+ }
+
+ c1.paragraph()->remove(c1.index(), c1.paragraph()->length() - 1 - c1.index());
+ Q3TextParagraph *p = c1.paragraph()->next();
+ int dy = 0;
+ Q3TextParagraph *tmp;
+ while (p && p != c2.paragraph()) {
+ tmp = p->next();
+ dy -= p->rect().height();
+ delete p;
+ p = tmp;
+ }
+ c2.paragraph()->remove(0, c2.index());
+ while (p) {
+ p->move(dy);
+ p->invalidate(0);
+ p->setEndState(-1);
+ p = p->next();
+ }
+
+
+ c1.paragraph()->join(c2.paragraph());
+
+ if (didGoLeft)
+ cursor->gotoNextLetter();
+}
+
+void Q3TextDocument::indentSelection(int id)
+{
+ QMap<int, Q3TextDocumentSelection>::Iterator it = selections.find(id);
+ if (it == selections.end())
+ return;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextParagraph *startParag = sel.startCursor.paragraph();
+ Q3TextParagraph *endParag = sel.endCursor.paragraph();
+ if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
+ endParag = sel.startCursor.paragraph();
+ startParag = sel.endCursor.paragraph();
+ }
+
+ Q3TextParagraph *p = startParag;
+ while (p && p != endParag) {
+ p->indent();
+ p = p->next();
+ }
+}
+
+void Q3TextCommandHistory::clear()
+{
+ while (!history.isEmpty())
+ delete history.takeFirst();
+ current = -1;
+}
+
+void Q3TextDocument::addCommand(Q3TextCommand *cmd)
+{
+ commandHistory->addCommand(cmd);
+}
+
+Q3TextCursor *Q3TextDocument::undo(Q3TextCursor *c)
+{
+ return commandHistory->undo(c);
+}
+
+Q3TextCursor *Q3TextDocument::redo(Q3TextCursor *c)
+{
+ return commandHistory->redo(c);
+}
+
+bool Q3TextDocument::find(Q3TextCursor& cursor, const QString &expr, bool cs, bool wo, bool forward)
+{
+ Qt::CaseSensitivity caseSensitive = cs ? Qt::CaseSensitive : Qt::CaseInsensitive;
+ removeSelection(Standard);
+ if (expr.isEmpty())
+ return false;
+ for (;;) {
+ QString s = cursor.paragraph()->string()->toString();
+ int start = cursor.index();
+ for (;;) {
+ int res = forward
+ ? s.indexOf(expr, start, caseSensitive)
+ : s.lastIndexOf(expr, start, caseSensitive);
+ int end = res + expr.length();
+ if (res == -1 || (!forward && start <= res))
+ break;
+ if (!wo || ((res == 0 || !s[res-1].isLetterOrNumber())
+ && (end == (int)s.length() || !s[end].isLetterOrNumber()))) {
+ removeSelection(Standard);
+ cursor.setIndex(forward ? end : res);
+ setSelectionStart(Standard, cursor);
+ cursor.setIndex(forward ? res : end);
+ setSelectionEnd(Standard, cursor);
+ if (!forward)
+ cursor.setIndex(res);
+ return true;
+ }
+ start = res + (forward ? 1 : -1);
+ }
+ if (forward) {
+ if (cursor.paragraph() == lastParagraph() && cursor.atParagEnd())
+ break;
+ cursor.gotoNextLetter();
+ } else {
+ if (cursor.paragraph() == firstParagraph() && cursor.atParagStart())
+ break;
+ cursor.gotoPreviousLetter();
+ }
+ }
+ return false;
+}
+
+void Q3TextDocument::setTextFormat(Qt::TextFormat f)
+{
+ txtFormat = f;
+ if (fParag == lParag && fParag->length() <= 1)
+ fParag->rtext = (f == Qt::RichText);
+}
+
+Qt::TextFormat Q3TextDocument::textFormat() const
+{
+ return txtFormat;
+}
+
+bool Q3TextDocument::inSelection(int selId, const QPoint &pos) const
+{
+ QMap<int, Q3TextDocumentSelection>::ConstIterator it = selections.find(selId);
+ if (it == selections.end())
+ return false;
+
+ Q3TextDocumentSelection sel = *it;
+ Q3TextParagraph *startParag = sel.startCursor.paragraph();
+ Q3TextParagraph *endParag = sel.endCursor.paragraph();
+ if (sel.startCursor.paragraph() == sel.endCursor.paragraph() &&
+ sel.startCursor.paragraph()->selectionStart(selId) == sel.endCursor.paragraph()->selectionEnd(selId))
+ return false;
+ if (sel.endCursor.paragraph()->paragId() < sel.startCursor.paragraph()->paragId()) {
+ endParag = sel.startCursor.paragraph();
+ startParag = sel.endCursor.paragraph();
+ }
+
+ Q3TextParagraph *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 Q3TextDocument::doLayout(QPainter *p, int w)
+{
+ minw = wused = 0;
+ if (!is_printer(p))
+ p = 0;
+ withoutDoubleBuffer = (p != 0);
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+ tStopWidth = formatCollection()->defaultFormat()->width( QLatin1Char('x') ) * 8;
+ flow_->setWidth(w);
+ cw = w;
+ vw = w;
+ Q3TextParagraph *parag = fParag;
+ while (parag) {
+ parag->invalidate(0);
+ if (p)
+ parag->adjustToPainter(p);
+ parag->format();
+ parag = parag->next();
+ }
+ Q3TextFormat::setPainter(oldPainter);
+}
+
+QPixmap *Q3TextDocument::bufferPixmap(const QSize &s)
+{
+ if (!buf_pixmap)
+ buf_pixmap = new QPixmap(s.expandedTo(QSize(1,1)));
+ else if (buf_pixmap->size() != s)
+ buf_pixmap->resize(s.expandedTo(buf_pixmap->size()));
+ return buf_pixmap;
+}
+
+void Q3TextDocument::draw(QPainter *p, const QRect &rect, const QPalette &pal,
+ const QBrush *paper)
+{
+ if (!firstParagraph())
+ return;
+
+ if (paper) {
+ p->setBrushOrigin(-qIntCast(p->translationX()),
+ -qIntCast(p->translationY()));
+
+ p->fillRect(rect, *paper);
+ }
+
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+
+ if (formatCollection()->defaultFormat()->color() != pal.text().color())
+ setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
+
+ Q3TextParagraph *parag = firstParagraph();
+ while (parag) {
+ if (!parag->isValid())
+ parag->format();
+ int y = parag->rect().y();
+ QRect pr(parag->rect());
+ pr.setX(0);
+ pr.setWidth(QWIDGETSIZE_MAX);
+ if (!rect.isNull() && !rect.intersects(pr)) {
+ parag = parag->next();
+ continue;
+ }
+ p->translate(0, y);
+ if (rect.isValid())
+ parag->paint(*p, pal, 0, false, rect.x(), rect.y(), rect.width(), rect.height());
+ else
+ parag->paint(*p, pal, 0, false);
+ p->translate(0, -y);
+ parag = parag->next();
+ if (!flow()->isEmpty())
+ flow()->drawFloatingItems(p, rect.x(), rect.y(), rect.width(), rect.height(), pal, false);
+ }
+ Q3TextFormat::setPainter(oldPainter);
+}
+
+void Q3TextDocument::drawParagraph(QPainter *painter, Q3TextParagraph *parag, int cx, int cy,
+ int cw, int ch,
+ QPixmap *&/*doubleBuffer*/, const QPalette &pal,
+ bool drawCursor, Q3TextCursor *cursor, bool resetChanged)
+{
+ if (resetChanged)
+ parag->setChanged(false);
+ QRect ir(parag->rect());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!parag->tableCell())
+#endif
+ ir.setWidth(width());
+
+ painter->translate(ir.x(), ir.y());
+
+ if (!parag->document()->parent()) {
+ const QPoint oldOrigin = painter->brushOrigin();
+ painter->setBrushOrigin(-ir.topLeft());
+ painter->fillRect(QRect(0, 0, ir.width(), ir.height()), parag->backgroundBrush(pal));
+ painter->setBrushOrigin(oldOrigin);
+ }
+
+ painter->translate(-(ir.x() - parag->rect().x()),
+ -(ir.y() - parag->rect().y()));
+ parag->paint(*painter, pal, drawCursor ? cursor : 0, true, cx, cy, cw, ch);
+
+ painter->translate(-ir.x(), -ir.y());
+
+ parag->document()->nextDoubleBuffered = false;
+}
+
+Q3TextParagraph *Q3TextDocument::draw(QPainter *p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool onlyChanged, bool drawCursor,
+ Q3TextCursor *cursor, bool resetChanged)
+{
+ if (withoutDoubleBuffer || (par && par->withoutDoubleBuffer)) {
+ withoutDoubleBuffer = true;
+ QRect r;
+ draw(p, r, pal);
+ return 0;
+ }
+ withoutDoubleBuffer = false;
+
+ if (!firstParagraph())
+ return 0;
+
+ QPainter * oldPainter = Q3TextFormat::painter();
+ Q3TextFormat::setPainter(p);
+ if (formatCollection()->defaultFormat()->color() != pal.text().color())
+ setDefaultFormat(formatCollection()->defaultFormat()->font(), pal.text().color());
+
+ if (cx < 0 && cy < 0) {
+ cx = 0;
+ cy = 0;
+ cw = width();
+ ch = height();
+ }
+
+ Q3TextParagraph *lastFormatted = 0;
+ Q3TextParagraph *parag = firstParagraph();
+
+ QPixmap *doubleBuffer = 0;
+
+ while (parag) {
+ lastFormatted = parag;
+ if (!parag->isValid())
+ parag->format();
+
+ QRect pr = parag->rect();
+ pr.setWidth(parag->document()->width());
+ if (pr.y() > cy + ch)
+ goto floating;
+ QRect clipr(cx, cy, cw, ch);
+ if (!pr.intersects(clipr) || (onlyChanged && !parag->hasChanged())) {
+ pr.setWidth(parag->document()->width());
+ parag = parag->next();
+ continue;
+ }
+
+ drawParagraph(p, parag, cx, cy, cw, ch, doubleBuffer, pal, drawCursor,
+ cursor, resetChanged);
+ parag = parag->next();
+ }
+
+ parag = lastParagraph();
+
+ floating:
+ if (parag->rect().y() + parag->rect().height() < parag->document()->height()) {
+ if (!parag->document()->parent()) {
+ QRect fillRect = QRect(0, parag->rect().y() + parag->rect().height(), parag->document()->width(),
+ parag->document()->height() - (parag->rect().y() + parag->rect().height()));
+ if (QRect(cx, cy, cw, ch).intersects(fillRect))
+ p->fillRect(fillRect, pal.brush(QPalette::Base));
+ }
+ if (!flow()->isEmpty()) {
+ QRect cr(cx, cy, cw, ch);
+ flow()->drawFloatingItems(p, cr.x(), cr.y(), cr.width(), cr.height(), pal, false);
+ }
+ }
+
+ if (buf_pixmap && buf_pixmap->height() > 300) {
+ delete buf_pixmap;
+ buf_pixmap = 0;
+ }
+
+ Q3TextFormat::setPainter(oldPainter);
+ return lastFormatted;
+}
+
+/*
+ #### this function only sets the default font size in the format collection
+ */
+void Q3TextDocument::setDefaultFormat(const QFont &font, const QColor &color)
+{
+ bool reformat = font != fCollection->defaultFormat()->font();
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->setDefaultFormat(font, color);
+ }
+ fCollection->updateDefaultFormat(font, color, sheet_);
+
+ if (!reformat)
+ return;
+ tStopWidth = formatCollection()->defaultFormat()->width(QLatin1Char('x')) * 8;
+
+ // invalidate paragraphs and custom items
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ p->invalidate(0);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = 0; i < p->length() - 1; ++i)
+ if (p->at(i)->isCustom())
+ p->at(i)->customItem()->invalidate();
+#endif
+ p = p->next();
+ }
+}
+
+
+/*!
+ \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{Q3MimeSourceFactory::defaultFactory()}) to resolve these
+ references. The context will then be used to calculate the
+ absolute path. See Q3MimeSourceFactory::makeAbsolute() for details.
+
+ \a emptyTag and \a doc are for internal use only.
+
+ This function should not be used in application code.
+*/
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextCustomItem* Q3TextDocument::tag(Q3StyleSheet *sheet, const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const Q3MimeSourceFactory& factory,
+ bool /*emptyTag */, Q3TextDocument *doc)
+{
+ const Q3StyleSheetItem* style = sheet->item(name);
+ // first some known tags
+ if (!style)
+ return 0;
+ if (style->name() == QLatin1String("img"))
+ return new Q3TextImage(doc, attr, context, (Q3MimeSourceFactory&)factory);
+ if (style->name() == QLatin1String("hr"))
+ return new Q3TextHorizontalLine(doc, attr, context, (Q3MimeSourceFactory&)factory );
+ return 0;
+}
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextDocument::registerCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
+{
+ if (i && i->placement() != Q3TextCustomItem::PlaceInline) {
+ flow_->registerFloatingItem(i);
+ p->registerFloatingItem(i);
+ i->setParagraph(p);
+ }
+ p->mightHaveCustomItems = mightHaveCustomItems = true;
+}
+
+void Q3TextDocument::unregisterCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p)
+{
+ p->unregisterFloatingItem(i);
+ i->setParagraph(0);
+ flow_->unregisterFloatingItem(i);
+}
+#endif
+
+bool Q3TextDocument::hasFocusParagraph() const
+{
+ return !!focusIndicator.parag;
+}
+
+QString Q3TextDocument::focusHref() const
+{
+ return focusIndicator.href;
+}
+
+QString Q3TextDocument::focusName() const
+{
+ return focusIndicator.name;
+}
+
+bool Q3TextDocument::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.clear();
+ focusIndicator.name.clear();
+
+ if (next) {
+ Q3TextParagraph *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();
+ focusIndicator.name = p->at(i)->anchorName();
+ while (i < p->length()) {
+ if (!p->at(i)->isAnchor())
+ return true;
+ focusIndicator.len++;
+ i++;
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ } else if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ // first try to continue
+ int idx;
+ bool resetCells = true;
+ for (idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ 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();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ } else {
+ resetCells = false;
+ ++idx;
+ break;
+ }
+ }
+ }
+ // now really try
+ if (resetCells)
+ idx = 0;
+ for (; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ }
+ }
+ }
+#endif
+ }
+ }
+ index = 0;
+ p = p->next();
+ }
+ } else {
+ Q3TextParagraph *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();
+ focusIndicator.name = p->at(i)->anchorName();
+ while (i >= -1) {
+ if (i < 0 || !p->at(i)->isAnchor()) {
+ focusIndicator.start++;
+ return true;
+ }
+ if (i < 0)
+ break;
+ focusIndicator.len++;
+ focusIndicator.start--;
+ i--;
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ } else if (p->at(i)->isCustom()) {
+ if (p->at(i)->customItem()->isNested()) {
+ Q3TextTable *t = (Q3TextTable*)p->at(i)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ // first try to continue
+ int idx;
+ bool resetCells = true;
+ for (idx = cells.size()-1; idx >= 0; --idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ 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();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ } else {
+ resetCells = false;
+ --idx;
+ break;
+ }
+ }
+ }
+ // now really try
+ if (resetCells)
+ idx = cells.size()-1;
+ for (; idx >= 0; --idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (c->richText()->focusNextPrevChild(next)) {
+ p->setChanged(true);
+ focusIndicator.parag = p;
+ focusIndicator.start = i;
+ focusIndicator.len = 0;
+ focusIndicator.href = c->richText()->focusHref();
+ focusIndicator.name = c->richText()->focusName();
+ return true;
+ }
+ }
+ }
+#endif
+ }
+ }
+ p = p->prev();
+ if (p)
+ index = p->length() - 1;
+ }
+ }
+
+ focusIndicator.parag = 0;
+
+ return false;
+}
+
+int Q3TextDocument::length() const
+{
+ int l = -1;
+ Q3TextParagraph *p = fParag;
+ while (p) {
+ l += p->length();
+ p = p->next();
+ }
+ return qMax(0,l);
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+int Q3TextFormat::width(const QChar &c) const
+{
+ if (c.unicode() == 0xad) // soft hyphen
+ return 0;
+ if (!pntr || !pntr->isActive()) {
+ if (c == QLatin1Char('\t'))
+ return fm.width(QLatin1Char(' '));
+ 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);
+ ((Q3TextFormat*)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);
+ }
+ applyFont(f);
+
+ return pntr_fm->width(c);
+}
+
+int Q3TextFormat::width(const QString &str, int pos) const
+{
+ int w = 0;
+ if (str.unicode()[pos].unicode() == 0xad)
+ return w;
+ if (!pntr || !pntr->isActive()) {
+ if (ha == AlignNormal) {
+ w = fm.charWidth(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_.charWidth(str, pos);
+ }
+ } else {
+ QFont f(fn);
+ if (ha != AlignNormal) {
+ if (usePixelSizes)
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ }
+ applyFont(f);
+ w = pntr_fm->charWidth(str, pos);
+ }
+ return w;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextString::Q3TextString()
+{
+ bidiDirty = true;
+ bidi = false;
+ rightToLeft = false;
+ dir = QChar::DirON;
+}
+
+Q3TextString::Q3TextString(const Q3TextString &s)
+{
+ bidiDirty = true;
+ bidi = s.bidi;
+ rightToLeft = s.rightToLeft;
+ dir = s.dir;
+ data = s.data;
+ data.detach();
+ for (int i = 0; i < (int)data.size(); ++i) {
+ Q3TextFormat *f = data[i].format();
+ if (f)
+ f->addRef();
+ }
+}
+
+void Q3TextString::insert(int index, const QString &s, Q3TextFormat *f)
+{
+ insert(index, s.unicode(), s.length(), f);
+}
+
+void Q3TextString::insert(int index, const QChar *unicode, int len, Q3TextFormat *f)
+{
+ int os = data.size();
+ data.resize(data.size() + len);
+ if (index < os) {
+ memmove(data.data() + index + len, data.data() + index,
+ sizeof(Q3TextStringChar) * (os - index));
+ }
+ Q3TextStringChar *ch = data.data() + index;
+ for (int i = 0; i < len; ++i) {
+ ch->x = 0;
+ ch->lineStart = 0;
+ ch->nobreak = false;
+ ch->type = Q3TextStringChar::Regular;
+ ch->p.format = f;
+ ch->rightToLeft = 0;
+ ch->c = unicode[i];
+ ++ch;
+ }
+ bidiDirty = true;
+}
+
+Q3TextString::~Q3TextString()
+{
+ clear();
+}
+
+void Q3TextString::insert(int index, Q3TextStringChar *c, bool doAddRefFormat )
+{
+ int os = data.size();
+ data.resize(data.size() + 1);
+ if (index < os) {
+ memmove(data.data() + index + 1, data.data() + index,
+ sizeof(Q3TextStringChar) * (os - index));
+ }
+ Q3TextStringChar &ch = data[(int)index];
+ ch.c = c->c;
+ ch.x = 0;
+ ch.lineStart = 0;
+ ch.rightToLeft = 0;
+ ch.p.format = 0;
+ ch.type = Q3TextStringChar::Regular;
+ ch.nobreak = false;
+ if (doAddRefFormat && c->format())
+ c->format()->addRef();
+ ch.setFormat(c->format());
+ bidiDirty = true;
+}
+
+int Q3TextString::appendParagraphs( Q3TextParagraph *start, Q3TextParagraph *end )
+{
+ int paragCount = 0;
+ int newLength = data.size();
+ for (Q3TextParagraph *p = start; p != end; p = p->next()) {
+ newLength += p->length();
+ ++paragCount;
+ }
+
+ const int oldLength = data.size();
+ data.resize(newLength);
+
+ Q3TextStringChar *d = &data[oldLength];
+ for (Q3TextParagraph *p = start; p != end; p = p->next()) {
+ const Q3TextStringChar * const src = p->at(0);
+ int i = 0;
+ for (; i < p->length() - 1; ++i) {
+ d[i].c = src[i].c;
+ d[i].x = 0;
+ d[i].lineStart = 0;
+ d[i].rightToLeft = 0;
+ d[i].type = Q3TextStringChar::Regular;
+ d[i].nobreak = false;
+ d[i].p.format = src[i].format();
+ if (d[i].p.format)
+ d[i].p.format->addRef();
+ }
+ d[i].x = 0;
+ d[i].lineStart = 0;
+ d[i].nobreak = false;
+ d[i].type = Q3TextStringChar::Regular;
+ d[i].p.format = 0;
+ d[i].rightToLeft = 0;
+ d[i].c = QLatin1Char('\n');
+ d += p->length();
+ }
+
+ bidiDirty = true;
+ return paragCount;
+}
+
+void Q3TextString::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) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ }
+ data.resize(index);
+ bidiDirty = true;
+}
+
+void Q3TextString::remove(int index, int len)
+{
+ for (int i = index; i < (int)data.size() && i - index < len; ++i) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ memmove(data.data() + index, data.data() + index + len,
+ sizeof(Q3TextStringChar) * (data.size() - index - len));
+ data.resize(data.size() - len);
+ bidiDirty = true;
+}
+
+void Q3TextString::clear()
+{
+ for (int i = 0; i < (int)data.count(); ++i) {
+ Q3TextStringChar &ch = data[i];
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!(ch.type == Q3TextStringChar::Regular)) {
+ if (ch.customItem() && ch.customItem()->placement() == Q3TextCustomItem::PlaceInline)
+ delete ch.customItem();
+ if (ch.p.custom->format)
+ ch.p.custom->format->removeRef();
+ delete ch.p.custom;
+ ch.p.custom = 0;
+ } else
+#endif
+ if (ch.format()) {
+ ch.format()->removeRef();
+ }
+ }
+ data.resize(0);
+ bidiDirty = true;
+}
+
+void Q3TextString::setFormat(int index, Q3TextFormat *f, bool useCollection)
+{
+ Q3TextStringChar &ch = data[index];
+ if (useCollection && ch.format())
+ ch.format()->removeRef();
+ ch.setFormat(f);
+}
+
+void Q3TextString::checkBidi() const
+{
+ // ############ fix BIDI handling
+ Q3TextString *that = (Q3TextString *)this;
+ that->bidiDirty = false;
+ int length = data.size();
+ if (!length) {
+ that->bidi = rightToLeft;
+ that->rightToLeft = (dir == QChar::DirR);
+ return;
+ }
+
+ if (dir == QChar::DirR) {
+ that->rightToLeft = true;
+ } else if (dir == QChar::DirL) {
+ that->rightToLeft = false;
+ } else {
+ that->rightToLeft = (QApplication::layoutDirection() == Qt::RightToLeft);
+ }
+
+ const Q3TextStringChar *start = data.data();
+ const Q3TextStringChar *end = start + length;
+
+ ((Q3TextString *)this)->stringCache = toString(data);
+
+ // determines the properties we need for layouting
+ QTextEngine textEngine;
+ textEngine.text = toString();
+ textEngine.option.setTextDirection(rightToLeft ? Qt::RightToLeft : Qt::LeftToRight);
+ textEngine.itemize();
+ const HB_CharAttributes *ca = textEngine.attributes() + length-1;
+ Q3TextStringChar *ch = (Q3TextStringChar *)end - 1;
+ QScriptItem *item = &textEngine.layoutData->items[textEngine.layoutData->items.size()-1];
+ unsigned char bidiLevel = item->analysis.bidiLevel;
+ that->bidi = (bidiLevel || rightToLeft);
+ int pos = length-1;
+ while (ch >= start) {
+ if (item->position > pos) {
+ --item;
+ Q_ASSERT(item >= &textEngine.layoutData->items[0]);
+ bidiLevel = item->analysis.bidiLevel;
+ if (bidiLevel)
+ that->bidi = true;
+ }
+ ch->softBreak = ca->lineBreakType >= HB_Break;
+ ch->whiteSpace = ca->whiteSpace;
+ ch->charStop = ca->charStop;
+ ch->bidiLevel = bidiLevel;
+ ch->rightToLeft = (bidiLevel%2);
+ --ch;
+ --ca;
+ --pos;
+ }
+}
+
+void Q3TextDocument::setStyleSheet(Q3StyleSheet *s)
+{
+ if (!s)
+ return;
+ sheet_ = s;
+ list_tm = list_bm = par_tm = par_bm = 12;
+ list_lm = 40;
+ li_tm = li_bm = 0;
+ Q3StyleSheetItem* item = s->item(QLatin1String("ol"));
+ if (item) {
+ list_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ list_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ list_lm = qMax(0,item->margin(Q3StyleSheetItem::MarginLeft));
+ }
+ if ((item = s->item(QLatin1String("li")))) {
+ li_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ li_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ }
+ if ((item = s->item(QLatin1String("p")))) {
+ par_tm = qMax(0,item->margin(Q3StyleSheetItem::MarginTop));
+ par_bm = qMax(0,item->margin(Q3StyleSheetItem::MarginBottom));
+ }
+}
+
+void Q3TextDocument::setUnderlineLinks(bool b) {
+ underlLinks = b;
+ for (int idx = 0; idx < childList.size(); ++idx) {
+ Q3TextDocument *dc = childList.at(idx);
+ dc->setUnderlineLinks(b);
+ }
+}
+
+void Q3TextStringChar::setFormat(Q3TextFormat *f)
+{
+ if (type == Regular) {
+ p.format = f;
+ } else {
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (!p.custom) {
+ p.custom = new CustomData;
+ p.custom->custom = 0;
+ }
+ p.custom->format = f;
+#endif
+ }
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextStringChar::setCustomItem(Q3TextCustomItem *i)
+{
+ if (type == Regular) {
+ Q3TextFormat *f = format();
+ p.custom = new CustomData;
+ p.custom->format = f;
+ } else {
+ delete p.custom->custom;
+ }
+ p.custom->custom = i;
+ type = (type == Anchor ? CustomAnchor : Custom);
+}
+
+void Q3TextStringChar::loseCustomItem()
+{
+ if (type == Custom) {
+ Q3TextFormat *f = p.custom->format;
+ p.custom->custom = 0;
+ delete p.custom;
+ type = Regular;
+ p.format = f;
+ } else if (type == CustomAnchor) {
+ p.custom->custom = 0;
+ type = Anchor;
+ }
+}
+
+#endif
+
+QString Q3TextStringChar::anchorName() const
+{
+ if (type == Regular)
+ return QString();
+ else
+ return p.custom->anchorName;
+}
+
+QString Q3TextStringChar::anchorHref() const
+{
+ if (type == Regular)
+ return QString();
+ else
+ return p.custom->anchorHref;
+}
+
+void Q3TextStringChar::setAnchor(const QString& name, const QString& href)
+{
+ if (type == Regular) {
+ Q3TextFormat *f = format();
+ p.custom = new CustomData;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ p.custom->custom = 0;
+#endif
+ p.custom->format = f;
+ type = Anchor;
+ } else if (type == Custom) {
+ type = CustomAnchor;
+ }
+ p.custom->anchorName = name;
+ p.custom->anchorHref = href;
+}
+
+
+int Q3TextString::width(int idx) const
+{
+ int w = 0;
+ Q3TextStringChar *c = &at(idx);
+ if (!c->charStop || c->c.unicode() == 0xad || c->c.unicode() == 0x2028)
+ return 0;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if(c->isCustom()) {
+ if(c->customItem()->placement() == Q3TextCustomItem::PlaceInline)
+ w = c->customItem()->width;
+ } else
+#endif
+ {
+ int r = c->c.row();
+ if(r < 0x06
+#ifndef Q_WS_WIN
+ // Uniscribe's handling of Asian makes the condition below fail.
+ || (r > 0x1f && !(r > 0xd7 && r < 0xe0))
+#endif
+ ) {
+ w = c->format()->width(c->c);
+ } else {
+ // complex text. We need some hacks to get the right metric here
+ w = c->format()->width(toString(), idx);
+ }
+ }
+ return w;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextParagraph::Q3TextParagraph(Q3TextDocument *dc, Q3TextParagraph *pr, Q3TextParagraph *nx, bool updateIds)
+ : p(pr), n(nx), docOrPseudo(dc),
+ changed(false), firstFormat(true), firstPProcess(true), needPreProcess(false), fullWidth(true),
+ lastInFrame(false), visible(true), breakable(true), movedDown(false),
+ mightHaveCustomItems(false), hasdoc(dc != 0), litem(false), rtext(false),
+ align(0), lstyle(Q3StyleSheetItem::ListDisc), invalid(0), mSelections(0),
+#ifndef QT_NO_TEXTCUSTOMITEM
+ mFloatingItems(0),
+#endif
+ utm(0), ubm(0), ulm(0), urm(0), uflm(0), ulinespacing(0),
+ tabStopWidth(0), minwidth(0), tArray(0), eData(0), ldepth(0)
+{
+ lstyle = Q3StyleSheetItem::ListDisc;
+ if (!hasdoc)
+ docOrPseudo = new Q3TextParagraphPseudoDocument;
+ bgcol = 0;
+ list_val = -1;
+ paintdevice = 0;
+ Q3TextFormat* defFormat = formatCollection()->defaultFormat();
+ if (!hasdoc) {
+ tabStopWidth = defFormat->width(QLatin1Char('x')) * 8;
+ pseudoDocument()->commandHistory = new Q3TextCommandHistory(100);
+ }
+
+ if (p)
+ p->n = this;
+ if (n)
+ n->p = this;
+
+ if (!p && hasdoc)
+ document()->setFirstParagraph(this);
+ if (!n && hasdoc)
+ document()->setLastParagraph(this);
+
+ state = -1;
+
+ if (p)
+ id = p->id + 1;
+ else
+ id = 0;
+ if (n && updateIds) {
+ Q3TextParagraph *s = n;
+ while (s) {
+ s->id = s->p->id + 1;
+ s->invalidateStyleCache();
+ s = s->n;
+ }
+ }
+
+ str = new Q3TextString();
+ const QChar ch(QLatin1Char(' '));
+ str->insert(0, &ch, 1, formatCollection()->defaultFormat());
+}
+
+Q3TextParagraph::~Q3TextParagraph()
+{
+ delete str;
+ if (hasdoc) {
+ register Q3TextDocument *doc = document();
+ if (this == doc->minwParag) {
+ doc->minwParag = 0;
+ doc->minw = 0;
+ }
+ if (this == doc->curParag)
+ doc->curParag = 0;
+ } else {
+ delete pseudoDocument();
+ }
+ delete [] tArray;
+ delete eData;
+ QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
+ for (; it != lineStarts.end(); ++it)
+ delete *it;
+ if (mSelections)
+ delete mSelections;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems)
+ delete mFloatingItems;
+#endif
+ if (p)
+ p->setNext(n);
+ if (n)
+ n->setPrev(p);
+ delete bgcol;
+}
+
+void Q3TextParagraph::setNext(Q3TextParagraph *s)
+{
+ n = s;
+ if (!n && hasdoc)
+ document()->setLastParagraph(this);
+}
+
+void Q3TextParagraph::setPrev(Q3TextParagraph *s)
+{
+ p = s;
+ if (!p && hasdoc)
+ document()->setFirstParagraph(this);
+}
+
+void Q3TextParagraph::invalidate(int chr)
+{
+ if (invalid < 0)
+ invalid = chr;
+ else
+ invalid = qMin(invalid, chr);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos = -1;
+ }
+ }
+#endif
+ invalidateStyleCache();
+}
+
+void Q3TextParagraph::invalidateStyleCache()
+{
+ if (list_val < 0)
+ list_val = -1;
+}
+
+
+void Q3TextParagraph::insert(int index, const QString &s)
+{
+ insert(index, s.unicode(), s.length());
+}
+
+void Q3TextParagraph::insert(int index, const QChar *unicode, int len)
+{
+ if (hasdoc && !document()->useFormatCollection() && document()->preProcessor())
+ str->insert(index, unicode, len,
+ document()->preProcessor()->format(Q3TextPreProcessor::Standard));
+ else
+ str->insert(index, unicode, len, formatCollection()->defaultFormat());
+ invalidate(index);
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::truncate(int index)
+{
+ str->truncate(index);
+ insert(length(), QLatin1String(" "));
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::remove(int index, int len)
+{
+ if (index + len - str->length() > 0)
+ return;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = index; i < index + len; ++i) {
+ Q3TextStringChar *c = at(i);
+ if (hasdoc && c->isCustom()) {
+ document()->unregisterCustomItem(c->customItem(), this);
+ }
+ }
+#endif
+ str->remove(index, len);
+ invalidate(0);
+ needPreProcess = true;
+}
+
+void Q3TextParagraph::join(Q3TextParagraph *s)
+{
+ int oh = r.height() + s->r.height();
+ n = s->n;
+ if (n)
+ n->p = this;
+ else if (hasdoc)
+ document()->setLastParagraph(this);
+
+ int start = str->length();
+ if (length() > 0 && at(length() - 1)->c == QLatin1Char(' ')) {
+ 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);
+ }
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (s->str->at(i).isCustom()) {
+ Q3TextCustomItem * item = s->str->at(i).customItem();
+ str->at(i + start).setCustomItem(item);
+ s->str->at(i).loseCustomItem();
+ if (hasdoc) {
+ document()->unregisterCustomItem(item, s);
+ document()->registerCustomItem(item, this);
+ }
+ }
+ if (s->str->at(i).isAnchor()) {
+ str->at(i + start).setAnchor(s->str->at(i).anchorName(),
+ s->str->at(i).anchorHref());
+ }
+#endif
+ }
+
+ 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) {
+ Q3TextParagraph *s = n;
+ s->invalidate(0);
+ while (s) {
+ s->id = s->p->id + 1;
+ s->state = -1;
+ s->needPreProcess = true;
+ s->changed = true;
+ s->invalidateStyleCache();
+ s = s->n;
+ }
+ }
+ format();
+ state = -1;
+}
+
+void Q3TextParagraph::move(int &dy)
+{
+ if (dy == 0)
+ return;
+ changed = true;
+ r.moveBy(0, dy);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos += dy;
+ }
+ }
+#endif
+ 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 Q3TextParagraph::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()));
+ if (p)
+ p->lastInFrame = false;
+
+ movedDown = false;
+ bool formattedAgain = false;
+
+ formatAgain:
+
+ r.setWidth(documentWidth());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (hasdoc && mFloatingItems) {
+ for (int idx = 0; idx < mFloatingItems->size(); ++idx) {
+ Q3TextCustomItem *i = mFloatingItems->at(idx);
+ i->ypos = r.y();
+ if (i->placement() == Q3TextCustomItem::PlaceRight) {
+ i->xpos = r.x() + r.width() - i->width;
+ }
+ }
+ }
+#endif
+ QMap<int, QTextLineStart*> oldLineStarts = lineStarts;
+ lineStarts.clear();
+ int y = formatter()->format(document(), this, start, oldLineStarts);
+
+
+ r.setWidth(qMax(r.width(), formatter()->minimumWidth()));
+
+
+ QMap<int, QTextLineStart*>::Iterator it = oldLineStarts.begin();
+
+ for (; it != oldLineStarts.end(); ++it)
+ delete *it;
+
+ if (!hasdoc) { // qt_format_text bounding rect handling
+ it = lineStarts.begin();
+ int usedw = 0;
+ for (; it != lineStarts.end(); ++it)
+ usedw = qMax(usedw, (*it)->w);
+ if (r.width() <= 0) {
+ // if the user specifies an invalid rect, this means that the
+ // bounding box should grow to the width that the text actually
+ // needs
+ r.setWidth(usedw);
+ } else {
+ r.setWidth(qMin(usedw, r.width()));
+ }
+ }
+
+ if (y != r.height())
+ r.setHeight(y);
+
+ if (!visible) {
+ r.setHeight(0);
+ } else {
+ int minw = minwidth = 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();
+ Q3TextParagraph *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 Q3TextParagraph::lineHeightOfChar(int i, int *bl, int *y) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for (;;) {
+ if (i >= it.key()) {
+ if (bl)
+ *bl = (*it)->baseLine;
+ if (y)
+ *y = (*it)->y;
+ return (*it)->h;
+ }
+ if (it == lineStarts.begin())
+ break;
+ --it;
+ }
+
+ qWarning("Q3TextParagraph::lineHeightOfChar: couldn't find lh for %d", i);
+ return 15;
+}
+
+Q3TextStringChar *Q3TextParagraph::lineStartOfChar(int i, int *index, int *line) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ int l = (int)lineStarts.count() - 1;
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.end();
+ --it;
+ for (;;) {
+ if (i >= it.key()) {
+ if (index)
+ *index = it.key();
+ if (line)
+ *line = l;
+ return &str->at(it.key());
+ }
+ if (it == lineStarts.begin())
+ break;
+ --it;
+ --l;
+ }
+
+ qWarning("Q3TextParagraph::lineStartOfChar: couldn't find %d", i);
+ return 0;
+}
+
+int Q3TextParagraph::lines() const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ return (int)lineStarts.count();
+}
+
+Q3TextStringChar *Q3TextParagraph::lineStartOfLine(int line, int *index) const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ if (line >= 0 && line < (int)lineStarts.count()) {
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (line-- > 0)
+ ++it;
+ int i = it.key();
+ if (index)
+ *index = i;
+ return &str->at(i);
+ }
+
+ qWarning("Q3TextParagraph::lineStartOfLine: couldn't find %d", line);
+ return 0;
+}
+
+int Q3TextParagraph::leftGap() const
+{
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ if (str->length() == 0)
+ return 0;
+
+ int line = 0;
+ int x = str->length() ? str->at(0).x : 0; /* set x to x of first char */
+ if (str->isBidi()) {
+ for (int i = 1; i < str->length()-1; ++i)
+ x = qMin(x, str->at(i).x);
+ return x;
+ }
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (line < (int)lineStarts.count()) {
+ int i = it.key(); /* char index */
+ x = qMin(x, str->at(i).x);
+ ++it;
+ ++line;
+ }
+ return x;
+}
+
+void Q3TextParagraph::setFormat(int index, int len, Q3TextFormat *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;
+
+ Q3TextFormatCollection *fc = 0;
+ if (useCollection)
+ fc = formatCollection();
+ Q3TextFormat *of;
+ for (int i = 0; i < len; ++i) {
+ of = str->at(i + index).format();
+ if (!changed && (!of || 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 == Q3TextFormat::Format || !fc) {
+ if (fc)
+ f = fc->format(f);
+ str->setFormat(i + index, f, useCollection);
+ } else {
+ Q3TextFormat *fm = fc->format(of, f, flags);
+ str->setFormat(i + index, fm, useCollection);
+ }
+ }
+}
+
+void Q3TextParagraph::indent(int *oldIndent, int *newIndent)
+{
+ if (!hasdoc || !document()->indent() || isListItem()) {
+ if (oldIndent)
+ *oldIndent = 0;
+ if (newIndent)
+ *newIndent = 0;
+ if (oldIndent && newIndent)
+ *newIndent = *oldIndent;
+ return;
+ }
+ document()->indent()->indent(document(), this, oldIndent, newIndent);
+}
+
+void Q3TextParagraph::paint(QPainter &painter, const QPalette &pal, Q3TextCursor *cursor,
+ bool drawSelections, int clipx, int clipy, int clipw, int cliph)
+{
+ if (!visible)
+ return;
+ int i, y, h, baseLine, xstart, xend = 0;
+ i = y =h = baseLine = 0;
+ QRect cursorRect;
+ drawSelections &= (mSelections != 0);
+ // macintosh full-width selection style
+ bool fullWidthStyle = QApplication::style()->styleHint(QStyle::SH_RichText_FullWidthSelection);
+ int fullSelectionWidth = 0;
+ if (drawSelections && fullWidthStyle)
+ fullSelectionWidth = (hasdoc ? document()->width() : r.width());
+
+ QString qstr = str->toString();
+ qstr.detach();
+ // ### 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 (int ii = 0; ii < qstr.length(); ii++)
+ if (uc[(int)ii]== QLatin1Char(QLatin1Char('\n')) || uc[(int)ii] == QLatin1Char('\t'))
+ uc[(int)ii] = 0x20;
+
+ int line = -1;
+ int paintStart = 0;
+ Q3TextStringChar *chr = 0;
+ Q3TextStringChar *nextchr = at(0);
+ for (i = 0; i < length(); i++) {
+ chr = nextchr;
+ if (i < length()-1)
+ nextchr = at(i+1);
+
+ // we flush at end of document
+ bool flush = (i == length()-1);
+ bool ignoreSoftHyphen = false;
+ if (!flush) {
+ // we flush at end of line
+ flush |= nextchr->lineStart;
+ // we flush on format changes
+ flush |= (nextchr->format() != chr->format());
+ // we flush on link changes
+ flush |= (nextchr->isLink() != chr->isLink());
+ // we flush on start of run
+ flush |= (nextchr->bidiLevel != chr->bidiLevel);
+ // we flush on bidi changes
+ flush |= (nextchr->rightToLeft != chr->rightToLeft);
+ // we flush before and after tabs
+ flush |= (chr->c == QLatin1Char('\t') || nextchr->c == QLatin1Char('\t'));
+ // we flush on soft hyphens
+ if (chr->c.unicode() == 0xad) {
+ flush = true;
+ if (!nextchr->lineStart)
+ ignoreSoftHyphen = true;
+ }
+ // we flush on custom items
+ flush |= chr->isCustom();
+ // we flush before custom items
+ flush |= nextchr->isCustom();
+ // when painting justified, we flush on spaces
+ if ((alignment() & Qt::AlignJustify) == Qt::AlignJustify)
+ flush |= chr->whiteSpace;
+ }
+
+ // init a new line
+ if (chr->lineStart) {
+ ++line;
+ paintStart = i;
+ lineInfo(line, y, h, baseLine);
+ if (clipy != -1 && cliph != 0 && y + r.y() - h > clipy + cliph) { // outside clip area, leave
+ break;
+ }
+
+ // if this is the first line and we are a list item, draw the the bullet label
+ if (line == 0 && isListItem()) {
+ int x = chr->x;
+ if (str->isBidi()) {
+ if (str->isRightToLeft()) {
+ x = chr->x + str->width(0);
+ for (int k = 1; k < length(); ++k) {
+ if (str->at(k).lineStart)
+ break;
+ x = qMax(x, str->at(k).x + str->width(k));
+ }
+ } else {
+ x = chr->x;
+ for (int k = 1; k < length(); ++k) {
+ if (str->at(k).lineStart)
+ break;
+ x = qMin(x, str->at(k).x);
+ }
+ }
+ }
+ drawLabel(&painter, x, y, 0, 0, baseLine, pal);
+ }
+ }
+
+ // check for cursor mark
+ if (cursor && this == cursor->paragraph() && i == cursor->index()) {
+ Q3TextStringChar *c = i == 0 ? chr : chr - 1;
+ cursorRect.setRect(cursor->x() , y + baseLine - c->format()->ascent(),
+ 1, c->format()->height());
+ }
+
+ if (flush) { // something changed, draw what we have so far
+ if (chr->rightToLeft) {
+ xstart = chr->x;
+ xend = at(paintStart)->x + str->width(paintStart);
+ } else {
+ xstart = at(paintStart)->x;
+ xend = chr->x;
+ if (i < length() - 1) {
+ if (!str->at(i + 1).lineStart &&
+ str->at(i + 1).rightToLeft == chr->rightToLeft)
+ xend = str->at(i + 1).x;
+ else
+ xend += str->width(i);
+ }
+ }
+
+ if ((clipx == -1 || clipw <= 0 || (xend >= clipx && xstart <= clipx + clipw)) &&
+ (clipy == -1 || clipy < y+r.y()+h)) {
+ if (!chr->isCustom())
+ drawString(painter, qstr, paintStart, i - paintStart + (ignoreSoftHyphen ? 0 : 1), xstart, y,
+ baseLine, xend-xstart, h, drawSelections, fullSelectionWidth,
+ chr, pal, chr->rightToLeft);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ else if (chr->customItem()->placement() == Q3TextCustomItem::PlaceInline) {
+ bool inSelection = false;
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
+ inSelection = (it != mSelections->constEnd() && (*it).start <= i && (*it).end > i);
+ }
+ chr->customItem()->draw(&painter, chr->x, y,
+ clipx == -1 ? clipx : (clipx - r.x()),
+ clipy == -1 ? clipy : (clipy - r.y()),
+ clipw, cliph, pal, inSelection);
+ }
+#endif
+ }
+ paintStart = i+1;
+ }
+
+ }
+
+ // time to draw the cursor
+ const int cursor_extent = 4;
+ if (!cursorRect.isNull() && cursor &&
+ ((clipx == -1 || clipw == -1) || (cursorRect.right()+cursor_extent >= clipx && cursorRect.left()-cursor_extent <= clipx + clipw))) {
+ painter.fillRect(cursorRect, pal.color(QPalette::Text));
+ painter.save();
+ if (string()->isBidi()) {
+ if (at(cursor->index())->rightToLeft) {
+ painter.setPen(Qt::black);
+ painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() - cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ } else {
+ painter.setPen(Qt::black);
+ painter.drawLine(cursorRect.x(), cursorRect.y(), cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ painter.drawLine(cursorRect.x(), cursorRect.y() + cursor_extent, cursorRect.x() + cursor_extent / 2, cursorRect.y() + cursor_extent / 2);
+ }
+ }
+ painter.restore();
+ }
+}
+
+//#define BIDI_DEBUG
+
+void Q3TextParagraph::setColorForSelection(QColor &color, QPainter &painter,
+ const QPalette &pal, int selection)
+{
+ if (selection < 0)
+ return;
+ color = (hasdoc && selection != Q3TextDocument::Standard) ?
+ document()->selectionColor(selection) :
+ pal.color(QPalette::Highlight);
+ QColor text = (hasdoc && document()->hasSelectionTextColor(selection)) ? document()->selectionTextColor(selection) : pal.color(QPalette::HighlightedText);
+ if (text.isValid())
+ painter.setPen(text);
+}
+
+void Q3TextParagraph::drawString(QPainter &painter, const QString &str, int start, int len,
+ int xstart, int y, int baseLine, int w, int h,
+ bool drawSelections, int fullSelectionWidth,
+ Q3TextStringChar *formatChar, const QPalette& pal,
+ bool rightToLeft)
+{
+ bool plainText = hasdoc ? document()->textFormat() == Qt::PlainText : false;
+ Q3TextFormat* format = formatChar->format();
+
+ int textFlags = int(rightToLeft ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight);
+
+ if (!plainText || (hasdoc && format->color() != document()->formatCollection()->defaultFormat()->color()))
+ painter.setPen(QPen(format->color()));
+ else
+ painter.setPen(pal.text().color());
+ painter.setFont(format->font());
+
+ if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty()) {
+ if (format->useLinkColor())
+ painter.setPen(document()->linkColor.isValid() ? document()->linkColor :
+ pal.link().color());
+ if (document()->underlineLinks()) {
+ QFont fn = format->font();
+ fn.setUnderline(true);
+ painter.setFont(fn);
+ }
+ }
+
+ int real_length = len;
+ if (len && !rightToLeft && start + len == length()) // don't draw the last character (trailing space)
+ len--;
+ if (len && str.unicode()[start+len-1] == QChar::LineSeparator)
+ len--;
+
+
+ Q3TextFormat::VerticalAlignment vAlign = format->vAlign();
+ if (vAlign != Q3TextFormat::AlignNormal) {
+ // sub or superscript
+ QFont f(painter.font());
+ if (format->fontSizesInPixels())
+ f.setPixelSize((f.pixelSize() * 2) / 3);
+ else
+ f.setPointSize((f.pointSize() * 2) / 3);
+ painter.setFont(f);
+ int h = painter.fontMetrics().height();
+ baseLine += (vAlign == Q3TextFormat::AlignSubScript) ? h/6 : -h/2;
+ }
+
+ bool allSelected = false;
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(Q3TextDocument::Standard);
+ allSelected = (it != mSelections->constEnd() && (*it).start <= start && (*it).end >= start+len);
+ }
+ if (!allSelected)
+ painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
+
+#ifdef BIDI_DEBUG
+ painter.save();
+ painter.setPen (Qt::red);
+ painter.drawLine(xstart, y, xstart, y + baseLine);
+ painter.drawLine(xstart, y + baseLine/2, xstart + 10, y + baseLine/2);
+ int w = 0;
+ int i = 0;
+ while(i < len)
+ w += painter.fontMetrics().charWidth(str, start + i++);
+ painter.setPen (Qt::blue);
+ painter.drawLine(xstart + w - 1, y, xstart + w - 1, y + baseLine);
+ painter.drawLine(xstart + w - 1, y + baseLine/2, xstart + w - 1 - 10, y + baseLine/2);
+ painter.restore();
+#endif
+
+ // check if we are in a selection and draw it
+ if (drawSelections) {
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constEnd();
+ while (it != mSelections->constBegin()) {
+ --it;
+ int selStart = (*it).start;
+ int selEnd = (*it).end;
+ int tmpw = w;
+
+ selStart = qMax(selStart, start);
+ int real_selEnd = qMin(selEnd, start+real_length);
+ selEnd = qMin(selEnd, start+len);
+ bool extendRight = false;
+ bool extendLeft = false;
+ bool selWrap = (real_selEnd == length()-1 && n && n->hasSelection(it.key()));
+ if (selWrap || this->str->at(real_selEnd).lineStart) {
+ extendRight = (fullSelectionWidth != 0);
+ if (!extendRight && !rightToLeft)
+ tmpw += painter.fontMetrics().width(QLatin1Char(' '));
+ }
+ if (fullSelectionWidth && (selStart == 0 || this->str->at(selStart).lineStart)) {
+ extendLeft = true;
+ }
+ if (this->str->isRightToLeft() != rightToLeft)
+ extendLeft = extendRight = false;
+
+ if (this->str->isRightToLeft()) {
+ bool tmp = extendLeft;
+ extendLeft = extendRight;
+ extendRight = tmp;
+ }
+
+ if (selStart < real_selEnd ||
+ (selWrap && fullSelectionWidth && extendRight &&
+ // don't draw the standard selection on a printer=
+ (it.key() != Q3TextDocument::Standard || !is_printer(&painter)))) {
+ int selection = it.key();
+ QColor color;
+ setColorForSelection(color, painter, pal, selection);
+ if (selStart != start || selEnd != start + len || selWrap) {
+ // have to clip
+ painter.save();
+ int cs, ce;
+ if (rightToLeft) {
+ cs = (selEnd != start + len) ?
+ this->str->at(this->str->previousCursorPosition(selEnd)).x : xstart;
+ ce = (selStart != start) ?
+ this->str->at(this->str->previousCursorPosition(selStart)).x : xstart+tmpw;
+ } else {
+ cs = (selStart != start) ? this->str->at(selStart).x : xstart;
+ ce = (selEnd != start + len) ? this->str->at(selEnd).x : xstart+tmpw;
+ }
+ QRect r(cs, y, ce-cs, h);
+ if (extendLeft)
+ r.setLeft(0);
+ if (extendRight)
+ r.setRight(fullSelectionWidth);
+ QRegion reg(r);
+ if (painter.hasClipping())
+ reg &= painter.clipRegion();
+ painter.setClipRegion(reg);
+ }
+ int xleft = xstart;
+ if (extendLeft) {
+ tmpw += xstart;
+ xleft = 0;
+ }
+ if (extendRight)
+ tmpw = fullSelectionWidth - xleft;
+ if(color.isValid())
+ painter.fillRect(xleft, y, tmpw, h, color);
+ painter.drawText(QPointF(xstart, y + baseLine), str.mid(start, len), textFlags, /*justificationPadding*/0);
+ if (selStart != start || selEnd != start + len || selWrap)
+ painter.restore();
+ }
+ }
+ }
+
+ if (format->isMisspelled()) {
+ painter.save();
+ painter.setPen(QPen(Qt::red, 1, Qt::DotLine));
+ painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1);
+ painter.restore();
+ }
+
+ if (hasdoc && formatChar->isAnchor() && !formatChar->anchorHref().isEmpty() &&
+ document()->focusIndicator.parag == this &&
+ ((document()->focusIndicator.start >= start &&
+ document()->focusIndicator.start + document()->focusIndicator.len <= start + len)
+ || (document()->focusIndicator.start <= start &&
+ document()->focusIndicator.start + document()->focusIndicator.len >= start + len))) {
+ QStyleOptionFocusRect opt;
+ opt.rect.setRect(xstart, y, w, h);
+#ifndef Q_WS_WIN
+ opt.state = QStyle::State_None;
+#else
+ // force drawing a focus rect but only on windows because it's
+ // configurable by the user in windows settings (see
+ // SH_UnderlineShortcut style hint) and we want to override
+ // this settings.
+ opt.state = QStyle::State_KeyboardFocusChange;
+#endif
+ opt.palette = pal;
+ QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &painter);
+ }
+}
+
+void Q3TextParagraph::drawLabel(QPainter* p, int x, int y, int w, int h, int base,
+ const QPalette& pal)
+{
+ QRect r (x, y, w, h);
+ Q3StyleSheetItem::ListStyle s = listStyle();
+
+ p->save();
+ Q3TextFormat *format = at(0)->format();
+ if (format) {
+ p->setPen(format->color());
+ p->setFont(format->font());
+ }
+ QFontMetrics fm(p->fontMetrics());
+ int size = fm.lineSpacing() / 3;
+
+ bool rtl = str->isRightToLeft();
+
+ switch (s) {
+ case Q3StyleSheetItem::ListDecimal:
+ case Q3StyleSheetItem::ListLowerAlpha:
+ case Q3StyleSheetItem::ListUpperAlpha:
+ {
+ if (list_val == -1) { // uninitialised list value, calcluate the right one
+ int depth = listDepth();
+ list_val--;
+ // ### evil, square and expensive. This needs to be done when formatting, not when painting
+ Q3TextParagraph* s = prev();
+ int depth_s;
+ while (s && (depth_s = s->listDepth()) >= depth) {
+ if (depth_s == depth && s->isListItem())
+ list_val--;
+ s = s->prev();
+ }
+ }
+
+ int n = list_val;
+ if (n < -1)
+ n = -n - 1;
+ QString l;
+ switch (s) {
+ case Q3StyleSheetItem::ListLowerAlpha:
+ if (n < 27) {
+ l = QLatin1Char(('a' + (char) (n-1)));
+ break;
+ }
+ case Q3StyleSheetItem::ListUpperAlpha:
+ if (n < 27) {
+ l = QLatin1Char(('A' + (char) (n-1)));
+ break;
+ }
+ break;
+ default: //Q3StyleSheetItem::ListDecimal:
+ l.setNum(n);
+ break;
+ }
+ if (rtl)
+ l.prepend(QLatin1String(" ."));
+ else
+ l += QString::fromLatin1(". ");
+ int x = (rtl ? r.left() : r.right() - fm.width(l));
+ p->drawText(x, r.top() + base, l);
+ }
+ break;
+ case Q3StyleSheetItem::ListSquare:
+ {
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->fillRect(er , pal.brush(QPalette::Text));
+ }
+ break;
+ case Q3StyleSheetItem::ListCircle:
+ {
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse(er);
+ }
+ break;
+ case Q3StyleSheetItem::ListDisc:
+ default:
+ {
+ p->setBrush(pal.brush(QPalette::Text));
+ int x = rtl ? r.left() + size : r.right() - size*2;
+ QRect er(x, r.top() + fm.height() / 2 - size / 2, size, size);
+ p->drawEllipse(er);
+ p->setBrush(Qt::NoBrush);
+ }
+ break;
+ }
+
+ p->restore();
+}
+
+#ifndef QT_NO_DATASTREAM
+void Q3TextParagraph::readStyleInformation(QDataStream &stream)
+{
+ int int_align, int_lstyle;
+ uchar uchar_litem, uchar_rtext, uchar_dir;
+ stream >> int_align >> int_lstyle >> utm >> ubm >> ulm >> urm >> uflm
+ >> ulinespacing >> ldepth >> uchar_litem >> uchar_rtext >> uchar_dir;
+ align = int_align; lstyle = (Q3StyleSheetItem::ListStyle) int_lstyle;
+ litem = uchar_litem; rtext = uchar_rtext; str->setDirection((QChar::Direction)uchar_dir);
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextParagraph::writeStyleInformation(QDataStream& stream) const
+{
+ stream << (int) align << (int) lstyle << utm << ubm << ulm << urm << uflm << ulinespacing << ldepth << (uchar)litem << (uchar)rtext << (uchar)str->direction();
+}
+#endif
+
+
+void Q3TextParagraph::setListItem(bool li)
+{
+ if ((bool)litem == li)
+ return;
+ litem = li;
+ changed = true;
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+void Q3TextParagraph::setListDepth(int depth) {
+ if (!hasdoc || depth == ldepth)
+ return;
+ ldepth = depth;
+ Q3TextParagraph* s = prev() ? prev() : this;
+ while (s) {
+ s->invalidate(0);
+ s = s->next();
+ }
+}
+
+int *Q3TextParagraph::tabArray() const
+{
+ int *ta = tArray;
+ if (!ta && hasdoc)
+ ta = document()->tabArray();
+ return ta;
+}
+
+int Q3TextParagraph::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 n;
+ if (tabStopWidth != 0)
+ n = x / tabStopWidth;
+ else
+ return x;
+ return tabStopWidth * (n + 1);
+ }
+}
+
+void Q3TextParagraph::adjustToPainter(QPainter *p)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int i = 0; i < length(); ++i) {
+ if (at(i)->isCustom())
+ at(i)->customItem()->adjustToPainter(p);
+ }
+#endif
+}
+
+Q3TextFormatCollection *Q3TextParagraph::formatCollection() const
+{
+ if (hasdoc)
+ return document()->formatCollection();
+ Q3TextFormatCollection* fc = &pseudoDocument()->collection;
+ if (paintdevice != fc->paintDevice())
+ fc->setPaintDevice(paintdevice);
+ return fc;
+}
+
+QString Q3TextParagraph::richText() const
+{
+ QString s;
+ Q3TextStringChar *formatChar = 0;
+ QString spaces;
+ bool doStart = richTextExportStart && richTextExportStart->paragraph() == this;
+ bool doEnd = richTextExportEnd && richTextExportEnd->paragraph() == this;
+ int i;
+ QString lastAnchorName;
+ for (i = 0; i < length()-1; ++i) {
+ if (doStart && i && richTextExportStart->index() == i)
+ s += QLatin1String("<!--StartFragment-->");
+ if (doEnd && richTextExportEnd->index() == i)
+ s += QLatin1String("<!--EndFragment-->");
+ Q3TextStringChar *c = &str->at(i);
+ if (c->isAnchor() && !c->anchorName().isEmpty() && c->anchorName() != lastAnchorName) {
+ lastAnchorName = c->anchorName();
+ if (c->anchorName().contains(QLatin1Char('#'))) {
+ QStringList l = c->anchorName().split(QLatin1Char('#'));
+ for (QStringList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it)
+ s += QLatin1String("<a name=\"") + *it + QLatin1String("\"></a>");
+ } else {
+ s += QLatin1String("<a name=\"") + c->anchorName() + QLatin1String("\"></a>");
+ }
+ }
+ if (!formatChar) {
+ s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
+ 0, QString(), c->anchorHref());
+ formatChar = c;
+ } else if ((formatChar->format()->key() != c->format()->key()) ||
+ (c->anchorHref() != formatChar->anchorHref())) {
+ s += c->format()->makeFormatChangeTags(formatCollection()->defaultFormat(),
+ formatChar->format() , formatChar->anchorHref(), c->anchorHref());
+ formatChar = c;
+ }
+ if (c->c == QLatin1Char('<'))
+ s += QLatin1String("&lt;");
+ else if (c->c == QLatin1Char('>'))
+ s += QLatin1String("&gt;");
+ else if (c->c == QLatin1Char('&'))
+ s += QLatin1String("&amp;");
+ else if (c->c == QLatin1Char('\"'))
+ s += QLatin1String("&quot;");
+#ifndef QT_NO_TEXTCUSTOMITEM
+ else if (c->isCustom())
+ s += c->customItem()->richText();
+#endif
+ else if (c->c == QLatin1Char('\n') || c->c == QChar::LineSeparator)
+ s += QLatin1String("<br />"); // space on purpose for compatibility with Netscape, Lynx & Co.
+ else
+ s += c->c;
+ }
+ if (doEnd && richTextExportEnd->index() == i)
+ s += QLatin1String("<!--EndFragment-->");
+ if (formatChar)
+ s += formatChar->format()->makeFormatEndTags(formatCollection()->defaultFormat(), formatChar->anchorHref());
+ return s;
+}
+
+void Q3TextParagraph::addCommand(Q3TextCommand *cmd)
+{
+ if (!hasdoc)
+ pseudoDocument()->commandHistory->addCommand(cmd);
+ else
+ document()->commands()->addCommand(cmd);
+}
+
+Q3TextCursor *Q3TextParagraph::undo(Q3TextCursor *c)
+{
+ if (!hasdoc)
+ return pseudoDocument()->commandHistory->undo(c);
+ return document()->commands()->undo(c);
+}
+
+Q3TextCursor *Q3TextParagraph::redo(Q3TextCursor *c)
+{
+ if (!hasdoc)
+ return pseudoDocument()->commandHistory->redo(c);
+ return document()->commands()->redo(c);
+}
+
+int Q3TextParagraph::topMargin() const
+{
+ int m = 0;
+ if (rtext) {
+ m = isListItem() ? (document()->li_tm/qMax(1,listDepth()*listDepth())) :
+ (listDepth() ? 0 : document()->par_tm);
+ if (listDepth() == 1 &&( !prev() || prev()->listDepth() < listDepth()))
+ m = qMax<int>(m, document()->list_tm);
+ }
+ m += utm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::bottomMargin() const
+{
+ int m = 0;
+ if (rtext) {
+ m = isListItem() ? (document()->li_bm/qMax(1,listDepth()*listDepth())) :
+ (listDepth() ? 0 : document()->par_bm);
+ if (listDepth() == 1 &&( !next() || next()->listDepth() < listDepth()))
+ m = qMax<int>(m, document()->list_bm);
+ }
+ m += ubm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::leftMargin() const
+{
+ int m = ulm;
+ if (listDepth() && !string()->isRightToLeft())
+ m += listDepth() * document()->list_lm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::firstLineMargin() const
+{
+ int m = uflm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::rightMargin() const
+{
+ int m = urm;
+ if (listDepth() && string()->isRightToLeft())
+ m += listDepth() * document()->list_lm;
+ return scale(m, Q3TextFormat::painter());
+}
+
+int Q3TextParagraph::lineSpacing() const
+{
+ int l = ulinespacing;
+ l = scale(l, Q3TextFormat::painter());
+ return l;
+}
+
+void Q3TextParagraph::copyParagData(Q3TextParagraph *parag)
+{
+ rtext = parag->rtext;
+ lstyle = parag->lstyle;
+ ldepth = parag->ldepth;
+ litem = parag->litem;
+ align = parag->align;
+ utm = parag->utm;
+ ubm = parag->ubm;
+ urm = parag->urm;
+ ulm = parag->ulm;
+ uflm = parag->uflm;
+ ulinespacing = parag->ulinespacing;
+ QColor *c = parag->backgroundColor();
+ if (c)
+ setBackgroundColor(*c);
+ str->setDirection(parag->str->direction());
+}
+
+void Q3TextParagraph::show()
+{
+ if (visible || !hasdoc)
+ return;
+ visible = true;
+}
+
+void Q3TextParagraph::hide()
+{
+ if (!visible || !hasdoc)
+ return;
+ visible = false;
+}
+
+void Q3TextParagraph::setDirection(QChar::Direction dir)
+{
+ if (str && str->direction() != dir) {
+ str->setDirection(dir);
+ invalidate(0);
+ }
+}
+
+QChar::Direction Q3TextParagraph::direction() const
+{
+ return (str ? str->direction() : QChar::DirON);
+}
+
+void Q3TextParagraph::setChanged(bool b, bool recursive)
+{
+ changed = b;
+ if (recursive) {
+ if (document() && document()->parentParagraph())
+ document()->parentParagraph()->setChanged(b, recursive);
+ }
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+Q3TextPreProcessor::Q3TextPreProcessor()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatter::Q3TextFormatter()
+ : thisminw(0), thiswused(0), wrapEnabled(true), wrapColumn(-1), biw(false)
+{
+}
+
+QTextLineStart *Q3TextFormatter::formatLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line,
+ Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
+{
+ if (lastChar < startChar)
+ return new QTextLineStart;
+#ifndef QT_NO_COMPLEXTEXT
+ if(string->isBidi())
+ return bidiReorderLine(parag, string, line, startChar, lastChar, align, space);
+#endif
+ int start = (startChar - &string->at(0));
+ int last = (lastChar - &string->at(0));
+
+ // ignore white space at the end of the line.
+ Q3TextStringChar *ch = lastChar;
+ while (ch > startChar && ch->whiteSpace) {
+ space += ch->format()->width(QLatin1Char(' '));
+ --ch;
+ }
+
+ if (space < 0)
+ space = 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 & Qt::AlignJustify) {
+ int numSpaces = 0;
+ // End at "last-1", the last space ends up with a width of 0
+ for (int j = last-1; j >= start; --j) {
+ // Start at last tab, if any.
+ Q3TextStringChar &ch = string->at(j);
+ if (ch.c == QLatin1Char('\t')) {
+ start = j+1;
+ break;
+ }
+ if(ch.whiteSpace)
+ numSpaces++;
+ }
+ int toAdd = 0;
+ for (int k = start + 1; k <= last; ++k) {
+ Q3TextStringChar &ch = string->at(k);
+ if(numSpaces && ch.whiteSpace) {
+ 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 QTextLineStart;
+}
+
+#ifndef QT_NO_COMPLEXTEXT
+
+#ifdef BIDI_DEBUG
+QT_BEGIN_INCLUDE_NAMESPACE
+#include <iostream>
+QT_END_INCLUDE_NAMESPACE
+#endif
+
+// collects one line of the paragraph and transforms it to visual order
+QTextLineStart *Q3TextFormatter::bidiReorderLine(Q3TextParagraph * /*parag*/, Q3TextString *text, QTextLineStart *line,
+ Q3TextStringChar *startChar, Q3TextStringChar *lastChar, int align, int space)
+{
+ // ignore white space at the end of the line.
+ int endSpaces = 0;
+ while (lastChar > startChar && lastChar->whiteSpace) {
+ space += lastChar->format()->width(QLatin1Char(' '));
+ --lastChar;
+ ++endSpaces;
+ }
+
+ int start = (startChar - &text->at(0));
+ int last = (lastChar - &text->at(0));
+
+ int length = lastChar - startChar + 1;
+
+
+ int x = startChar->x;
+
+ unsigned char _levels[256];
+ int _visual[256];
+
+ unsigned char *levels = _levels;
+ int *visual = _visual;
+
+ if (length > 255) {
+ levels = (unsigned char *)malloc(length*sizeof(unsigned char));
+ visual = (int *)malloc(length*sizeof(int));
+ }
+
+ //qDebug("bidiReorderLine: length=%d (%d-%d)", length, start, last);
+
+ Q3TextStringChar *ch = startChar;
+ unsigned char *l = levels;
+ while (ch <= lastChar) {
+ //qDebug(" level: %d", ch->bidiLevel);
+ *(l++) = (ch++)->bidiLevel;
+ }
+
+ QTextEngine::bidiReorder(length, levels, visual);
+
+ // now construct the reordered string out of the runs...
+
+ int numSpaces = 0;
+ align = QStyle::visualAlignment(text->isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight, QFlag(align));
+
+ // This is not really correct, but as we can't make the scroll bar move to the left of the origin,
+ // this ensures all text can be scrolled to and read.
+ if (space < 0)
+ space = 0;
+
+ if (align & Qt::AlignHCenter)
+ x += space/2;
+ else if (align & Qt::AlignRight)
+ x += space;
+ else if (align & Qt::AlignJustify) {
+ // End at "last-1", the last space ends up with a width of 0
+ for (int j = last-1; j >= start; --j) {
+ // Start at last tab, if any.
+ Q3TextStringChar &ch = text->at(j);
+ if (ch.c == QLatin1Char('\t')) {
+ start = j+1;
+ break;
+ }
+ if(ch.whiteSpace)
+ numSpaces++;
+ }
+ }
+
+ int toAdd = 0;
+ int xorig = x;
+ Q3TextStringChar *lc = startChar + visual[0];
+ for (int i = 0; i < length; i++) {
+ Q3TextStringChar *ch = startChar + visual[i];
+ if (numSpaces && ch->whiteSpace) {
+ int s = space / numSpaces;
+ toAdd += s;
+ space -= s;
+ numSpaces--;
+ }
+
+ if (lc->format() != ch->format() && !ch->c.isSpace()
+ && lc->format()->font().italic() && !ch->format()->font().italic()) {
+ int rb = lc->format()->fontMetrics().rightBearing(lc->c);
+ if (rb < 0)
+ x -= rb;
+ }
+
+ ch->x = x + toAdd;
+ ch->rightToLeft = ch->bidiLevel % 2;
+ //qDebug("visual: %d (%p) placed at %d rightToLeft=%d", visual[i], ch, x +toAdd, ch->rightToLeft );
+ int ww = 0;
+ if (ch->c.unicode() >= 32 || ch->c == QLatin1Char(QLatin1Char('\t')) || ch->c == QLatin1Char('\n') || ch->isCustom()) {
+ ww = text->width(start+visual[i]);
+ } else {
+ ww = ch->format()->width(QLatin1Char(' '));
+ }
+ x += ww;
+ lc = ch;
+ }
+ x += toAdd;
+
+ while (endSpaces--) {
+ ++lastChar;
+ int sw = lastChar->format()->width(QLatin1Char(' '));
+ if (text->isRightToLeft()) {
+ xorig -= sw;
+ lastChar->x = xorig;
+ ch->rightToLeft = true;
+ } else {
+ lastChar->x = x;
+ x += sw;
+ ch->rightToLeft = false;
+ }
+ }
+
+ line->w = x;
+
+ if (length > 255) {
+ free(levels);
+ free(visual);
+ }
+
+ return new QTextLineStart;
+}
+#endif
+
+
+void Q3TextFormatter::insertLineStart(Q3TextParagraph *parag, int index, QTextLineStart *ls)
+{
+ QMap<int, QTextLineStart*>::Iterator it;
+ if ((it = parag->lineStartList().find(index)) == parag->lineStartList().end()) {
+ parag->lineStartList().insert(index, ls);
+ } else {
+ delete *it;
+ parag->lineStartList().erase(it);
+ parag->lineStartList().insert(index, ls);
+ }
+}
+
+
+/* Standard pagebreak algorithm using Q3TextFlow::adjustFlow. Returns
+ the shift of the paragraphs bottom line.
+ */
+int Q3TextFormatter::formatVertically(Q3TextDocument* doc, Q3TextParagraph* parag)
+{
+ int oldHeight = parag->rect().height();
+ QMap<int, QTextLineStart*>& lineStarts = parag->lineStartList();
+ QMap<int, QTextLineStart*>::Iterator it = lineStarts.begin();
+ int h = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ for (; it != lineStarts.end() ; ++it ) {
+ QTextLineStart * ls = it.value();
+ ls->y = h;
+ Q3TextStringChar *c = &parag->string()->at(it.key());
+#ifndef QT_NO_TEXTCUSTOMITEM
+ 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
+#endif
+ {
+
+ int shift = doc->flow()->adjustFlow(parag->rect().y() + ls->y, ls->w, ls->h);
+ ls->y += shift;
+ if (shift)
+ parag->setMovedDown(true);
+ }
+ h = ls->y + ls->h;
+ }
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ h += m;
+ parag->setHeight(h);
+ return h - oldHeight;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatterBreakInWords::Q3TextFormatterBreakInWords()
+{
+}
+
+#define SPACE(s) s
+
+int Q3TextFormatterBreakInWords::format(Q3TextDocument *doc,Q3TextParagraph *parag,
+ int start, const QMap<int, QTextLineStart*> &)
+{
+ // make sure bidi information is correct.
+ (void)parag->string()->isBidi();
+
+ Q3TextStringChar *c = 0;
+ Q3TextStringChar *firstChar = 0;
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
+ int x = left + (doc ? parag->firstLineMargin() : 0);
+ int dw = parag->documentVisibleWidth() - (doc ? doc->rightMargin() : 0);
+ int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ int h = y;
+ int len = parag->length();
+ if (doc)
+ x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 4);
+ int rm = parag->rightMargin();
+ int w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ bool fullWidth = true;
+ int minw = 0;
+ int wused = 0;
+ bool wrapEnabled = isWrapEnabled(parag);
+
+ start = 0; //######### what is the point with start?! (Matthias)
+ if (start == 0)
+ c = &parag->string()->at(0);
+
+ int i = start;
+ QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
+ insertLineStart(parag, 0, lineStart);
+
+ QPainter *painter = Q3TextFormat::painter();
+
+ int col = 0;
+ int ww = 0;
+ QChar lastChr;
+ int tabBase = left < x ? left : x;
+ for (; i < len; ++i, ++col) {
+ if (c)
+ lastChr = c->c;
+ c = &parag->string()->at(i);
+ // ### 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 == QLatin1Char('\t')) {
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ } else {
+ ww = c->format()->width(QLatin1Char(' '));
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ if (c->isCustom() && c->customItem()->ownLine()) {
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ c->customItem()->resize(w - x);
+ w = dw;
+ y += h;
+ h = c->height();
+ lineStart = new QTextLineStart(y, h, h);
+ insertLineStart(parag, i, lineStart);
+ c->lineStart = 1;
+ firstChar = c;
+ x = 0xffffff;
+ continue;
+ }
+#endif
+
+ if (wrapEnabled &&
+ ((wrapAtColumn() == -1 && x + ww > w) ||
+ (wrapAtColumn() != -1 && col >= wrapAtColumn()))) {
+ x = doc ? parag->document()->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw;
+ y += h;
+ h = c->height();
+ lineStart = formatLine(parag, parag->string(), lineStart, firstChar, c-1);
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ col = 0;
+ if (wrapAtColumn() != -1)
+ minw = qMax(minw, w);
+ } else if (lineStart) {
+ lineStart->baseLine = qMax(lineStart->baseLine, c->ascent());
+ h = qMax(h, c->height());
+ lineStart->h = h;
+ }
+
+ c->x = x;
+ x += ww;
+ wused = qMax(wused, x);
+ }
+
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ parag->setFullWidth(fullWidth);
+ y += h + m;
+ if (doc)
+ minw += doc->rightMargin();
+ if (!wrapEnabled)
+ minw = qMax(minw, wused);
+
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatterBreakWords::Q3TextFormatterBreakWords()
+{
+}
+
+#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 Q3TextFormatterBreakWords::format(Q3TextDocument *doc, Q3TextParagraph *parag,
+ int start, const QMap<int, QTextLineStart*> &)
+{
+ // make sure bidi information is correct.
+ (void)parag->string()->isBidi();
+
+ Q3TextStringChar *c = 0;
+ Q3TextStringChar *firstChar = 0;
+ Q3TextString *string = parag->string();
+ int left = doc ? parag->leftMargin() + doc->leftMargin() : 0;
+ int x = left + (doc ? parag->firstLineMargin() : 0);
+ int y = parag->prev() ? qMax(parag->prev()->bottomMargin(),parag->topMargin()) / 2: 0;
+ int h = y;
+ int len = parag->length();
+ if (doc)
+ x = doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), x, 0);
+ int dw = parag->documentVisibleWidth() - (doc ? (left != x ? 0 : doc->rightMargin()) : 0);
+
+ int curLeft = x;
+ int rm = parag->rightMargin();
+ int rdiff = doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 0) : 0;
+ int w = dw - rdiff;
+ bool fullWidth = true;
+ int marg = left + rdiff;
+ int minw = 0;
+ int wused = 0;
+ int tminw = marg;
+ int linespacing = doc ? parag->lineSpacing() : 0;
+ bool wrapEnabled = isWrapEnabled(parag);
+
+ start = 0;
+
+ int i = start;
+ QTextLineStart *lineStart = new QTextLineStart(y, y, 0);
+ insertLineStart(parag, 0, lineStart);
+ int lastBreak = -1;
+ int tmpBaseLine = 0, tmph = 0;
+ bool lastWasNonInlineCustom = false;
+
+ int align = parag->alignment();
+ if (align == Qt::AlignAuto && doc && doc->alignment() != Qt::AlignAuto)
+ align = doc->alignment();
+
+ align &= Qt::AlignHorizontal_Mask;
+
+ // ### 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 = &parag->string()->at(len - 1);
+ if (!c->isAnchor()) {
+ if (c->format())
+ c->format()->removeRef();
+ c->setFormat(string->at(len - 2).format());
+ if (c->format())
+ c->format()->addRef();
+ }
+ }
+
+ c = &parag->string()->at(0);
+
+ QPainter *painter = Q3TextFormat::painter();
+ int col = 0;
+ int ww = 0;
+ QChar lastChr = c->c;
+ Q3TextFormat *lastFormat = c->format();
+ int tabBase = left < x ? left : x;
+ for (; i < len; ++i, ++col) {
+ if (i) {
+ c = &parag->string()->at(i-1);
+ lastChr = c->c;
+ lastFormat = c->format();
+ }
+ bool lastWasOwnLineCustomItem = lastBreak == -2;
+ bool hadBreakableChar = lastBreak != -1;
+ bool lastWasHardBreak = lastChr == QChar::LineSeparator;
+
+ // ### next line should not be needed
+ if (painter)
+ c->format()->setPainter(painter);
+ c = &string->at(i);
+
+ if (lastFormat != c->format() && !c->c.isSpace()
+ && lastFormat->font().italic() && !c->format()->font().italic()) {
+ int rb = lastFormat->fontMetrics().rightBearing(lastChr);
+ if (rb < 0)
+ x -= rb;
+ }
+
+ if ((i > 0 && (x > curLeft || ww == 0)) || lastWasNonInlineCustom) {
+ c->lineStart = 0;
+ } else {
+ c->lineStart = 1;
+ firstChar = c;
+ }
+
+ // ignore non spacing marks for column count.
+ if (col != 0 && QChar::category(c->c.unicode()) == QChar::Mark_NonSpacing)
+ --col;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ lastWasNonInlineCustom = (c->isCustom() && c->customItem()->placement() != Q3TextCustomItem::PlaceInline);
+#endif
+
+ if (c->c.unicode() >= 32 || c->isCustom()) {
+ ww = string->width(i);
+ } else if (c->c == QLatin1Char('\t')) {
+ if (align == Qt::AlignRight || align == Qt::AlignCenter) {
+ // we can not (yet) do tabs
+ ww = c->format()->width(QLatin1Char(' '));
+ } else {
+ int tabx = lastWasHardBreak ? (left + (doc ? parag->firstLineMargin() : 0)) : x;
+ int nx = parag->nextTab(i, tabx - tabBase) + tabBase;
+ if (nx < tabx) // strrrange...
+ ww = 0;
+ else
+ ww = nx - tabx;
+ }
+ } else {
+ ww = c->format()->width(QLatin1Char(' '));
+ }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* ci = c->customItem();
+ if (c->isCustom() && ci->ownLine()) {
+ QTextLineStart *lineStart2 = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x - ww));
+ 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);
+ ci->resize(w - x);
+ if (ci->width < w - x) {
+ if (align & Qt::AlignHCenter)
+ x = (w - ci->width) / 2;
+ else if (align & Qt::AlignRight) {
+ x = w - ci->width;
+ }
+ }
+ c->x = x;
+ curLeft = x;
+ if (i == 0 || !isBreakable(string, i-1) ||
+ string->at(i - 1).lineStart == 0) {
+ y += qMax(h, qMax(tmph, linespacing));
+ tmph = c->height();
+ h = tmph;
+ lineStart = lineStart2;
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ c->lineStart = 1;
+ firstChar = c;
+ } else {
+ tmph = c->height();
+ h = tmph;
+ delete lineStart2;
+ }
+ lineStart->h = h;
+ lineStart->baseLine = h;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -2;
+ x = w;
+ minw = qMax(minw, tminw);
+
+ int tw = ci->minimumWidth() + (doc ? doc->leftMargin() : 0);
+ if (tw < QWIDGETSIZE_MAX)
+ tminw = tw;
+ else
+ tminw = marg;
+ wused = qMax(wused, ci->width);
+ continue;
+ } else if (c->isCustom() && ci->placement() != Q3TextCustomItem::PlaceInline) {
+ int tw = ci->minimumWidth();
+ if (tw < QWIDGETSIZE_MAX)
+ minw = qMax(minw, tw);
+ }
+#endif
+ // we break if
+ // 1. the last character was a hard break (QChar::LineSeparator) or
+ // 2. the last character was a own-line custom item (eg. table or ruler) or
+ // 3. wrapping was enabled, it was not a space and following
+ // condition is true: We either had a breakable character
+ // previously or we ar allowed to break in words and - either
+ // we break at w pixels and the current char would exceed that
+ // or - we break at a column and the current character would
+ // exceed that.
+ if (lastWasHardBreak || lastWasOwnLineCustomItem ||
+ (wrapEnabled &&
+ ((!c->c.isSpace() && (hadBreakableChar || allowBreakInWords()) &&
+ ((wrapAtColumn() == -1 && x + ww > w) ||
+ (wrapAtColumn() != -1 && col >= wrapAtColumn()))))
+ )
+ ) {
+ if (wrapAtColumn() != -1)
+ minw = qMax(minw, x + ww);
+ // if a break was forced (no breakable char, hard break or own line custom item), break immediately....
+ if (!hadBreakableChar || lastWasHardBreak || lastWasOwnLineCustomItem) {
+ if (lineStart) {
+ lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
+ h = qMax(h, tmph);
+ lineStart->h = h;
+ DO_FLOW(lineStart);
+ }
+ lineStart = formatLine(parag, string, lineStart, firstChar, c-1, align, SPACE(w - x));
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += qMax(h, linespacing);
+ tmph = c->height();
+ h = 0;
+ lineStart->y = y;
+ insertLineStart(parag, i, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -1;
+ col = 0;
+ if (allowBreakInWords() || lastWasHardBreak) {
+ minw = qMax(minw, tminw);
+ tminw = marg + ww;
+ }
+ } else { // ... otherwise if we had a breakable char, break there
+ DO_FLOW(lineStart);
+ c->x = x;
+ i = lastBreak;
+ lineStart = formatLine(parag, string, lineStart, firstChar, parag->at(lastBreak),align, SPACE(w - string->at(i+1).x));
+ x = doc ? doc->flow()->adjustLMargin(y + parag->rect().y(), parag->rect().height(), left, 4) : left;
+ w = dw - (doc ? doc->flow()->adjustRMargin(y + parag->rect().y(), parag->rect().height(), rm, 4) : 0);
+ if (!doc && c->c == QLatin1Char('\t')) { // qt_format_text tab handling
+ int nx = parag->nextTab(i, x - tabBase) + tabBase;
+ if (nx < x)
+ ww = w - x;
+ else
+ ww = nx - x;
+ }
+ curLeft = x;
+ y += qMax(h, linespacing);
+ tmph = c->height();
+ h = tmph;
+ lineStart->y = y;
+ insertLineStart(parag, i + 1, lineStart);
+ lineStart->baseLine = c->ascent();
+ lineStart->h = c->height();
+ c->lineStart = 1;
+ firstChar = c;
+ tmpBaseLine = lineStart->baseLine;
+ lastBreak = -1;
+ col = 0;
+ minw = qMax(minw, tminw);
+ tminw = marg;
+ continue;
+ }
+ } else if (lineStart && isBreakable(string, i)) {
+ if (len <= 2 || i < len - 1) {
+ tmpBaseLine = qMax(tmpBaseLine, c->ascent());
+ tmph = qMax(tmph, c->height());
+ }
+ minw = qMax(minw, tminw);
+
+ tminw = marg + ww;
+ lineStart->baseLine = qMax(lineStart->baseLine, tmpBaseLine);
+ h = qMax(h, tmph);
+ lineStart->h = h;
+ if (i < len - 2 || c->c != QLatin1Char(' '))
+ lastBreak = i;
+ } else {
+ tminw += ww;
+ int cascent = c->ascent();
+ int cheight = c->height();
+ int belowBaseLine = qMax(tmph - tmpBaseLine, cheight-cascent);
+ tmpBaseLine = qMax(tmpBaseLine, cascent);
+ tmph = tmpBaseLine + belowBaseLine;
+ }
+
+ c->x = x;
+ x += ww;
+ wused = qMax(wused, x);
+ }
+
+ 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 & Qt::AlignJustify) {
+ align |= Qt::AlignLeft;
+ align &= ~(Qt::AlignJustify|Qt::AlignAbsolute);
+ }
+ DO_FLOW(lineStart);
+ lineStart = formatLine(parag, string, lineStart, firstChar, c, align, SPACE(w - x));
+ delete lineStart;
+ }
+
+ minw = qMax(minw, tminw);
+ if (doc)
+ minw += doc->rightMargin();
+
+ int m = parag->bottomMargin();
+ if (!parag->next())
+ m = 0;
+ else
+ m = qMax(m, parag->next()->topMargin()) / 2;
+ parag->setFullWidth(fullWidth);
+ y += qMax(h, linespacing) + m;
+
+ wused += rm;
+ if (!wrapEnabled || wrapAtColumn() != -1)
+ minw = qMax(minw, wused);
+
+ // This is the case where we are breaking wherever we darn well please
+ // in cases like that, the minw should not be the length of the entire
+ // word, because we necessarily want to show the word on the whole line.
+ // example: word wrap in iconview
+ if (allowBreakInWords() && minw > wused)
+ minw = wused;
+
+ thisminw = minw;
+ thiswused = wused;
+ return y;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextIndent::Q3TextIndent()
+{
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Q3TextFormatCollection::Q3TextFormatCollection()
+ : paintdevice(0)
+{
+ defFormat = new Q3TextFormat(QApplication::font(),
+ QApplication::palette().color(QPalette::Active, QPalette::Text));
+ lastFormat = cres = 0;
+ cflags = -1;
+ cachedFormat = 0;
+}
+
+Q3TextFormatCollection::~Q3TextFormatCollection()
+{
+ QHash<QString, Q3TextFormat *>::ConstIterator it = cKey.constBegin();
+ while (it != cKey.constEnd()) {
+ delete it.value();
+ ++it;
+ }
+ delete defFormat;
+}
+
+void Q3TextFormatCollection::setPaintDevice(QPaintDevice *pd)
+{
+ paintdevice = pd;
+
+#if defined(Q_WS_X11)
+ int scr = (paintdevice) ? paintdevice->x11Screen() : QX11Info::appScreen();
+
+ defFormat->fn.x11SetScreen(scr);
+ defFormat->update();
+
+ QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
+ for (; it != cKey.end(); ++it) {
+ Q3TextFormat *format = *it;
+ format->fn.x11SetScreen(scr);
+ format->update();
+ }
+#endif // Q_WS_X11
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *f)
+{
+ if (f->parent() == this || f == defFormat) {
+ lastFormat = f;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if (f == lastFormat || (lastFormat && f->key() == lastFormat->key())) {
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ Q3TextFormat *fm = cKey.value(f->key());
+ if (fm) {
+ lastFormat = fm;
+ lastFormat->addRef();
+ return lastFormat;
+ }
+
+ if (f->key() == defFormat->key())
+ return defFormat;
+
+ lastFormat = createFormat(*f);
+ lastFormat->collection = this;
+ cKey.insert(lastFormat->key(), lastFormat);
+ return lastFormat;
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(Q3TextFormat *of, Q3TextFormat *nf, int flags)
+{
+ if (cres && kof == of->key() && knf == nf->key() && cflags == flags) {
+ cres->addRef();
+ return cres;
+ }
+
+ cres = createFormat(*of);
+ kof = of->key();
+ knf = nf->key();
+ cflags = flags;
+ if (flags & Q3TextFormat::Bold)
+ cres->fn.setBold(nf->fn.bold());
+ if (flags & Q3TextFormat::Italic)
+ cres->fn.setItalic(nf->fn.italic());
+ if (flags & Q3TextFormat::Underline)
+ cres->fn.setUnderline(nf->fn.underline());
+ if (flags & Q3TextFormat::StrikeOut)
+ cres->fn.setStrikeOut(nf->fn.strikeOut());
+ if (flags & Q3TextFormat::Family)
+ cres->fn.setFamily(nf->fn.family());
+ if (flags & Q3TextFormat::Size) {
+ if (of->usePixelSizes)
+ cres->fn.setPixelSize(nf->fn.pixelSize());
+ else
+ cres->fn.setPointSize(nf->fn.pointSize());
+ }
+ if (flags & Q3TextFormat::Color)
+ cres->col = nf->col;
+ if (flags & Q3TextFormat::Misspelled)
+ cres->missp = nf->missp;
+ if (flags & Q3TextFormat::VAlign)
+ cres->ha = nf->ha;
+ cres->update();
+
+ Q3TextFormat *fm = cKey.value(cres->key());
+ if (!fm) {
+ cres->collection = this;
+ cKey.insert(cres->key(), cres);
+ } else {
+ delete cres;
+ cres = fm;
+ cres->addRef();
+ }
+
+ return cres;
+}
+
+Q3TextFormat *Q3TextFormatCollection::format(const QFont &f, const QColor &c)
+{
+ if (cachedFormat && cfont == f && ccol == c) {
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ QString key = Q3TextFormat::getKey(f, c, false, Q3TextFormat::AlignNormal);
+ cachedFormat = cKey.value(key);
+ cfont = f;
+ ccol = c;
+
+ if (cachedFormat) {
+ cachedFormat->addRef();
+ return cachedFormat;
+ }
+
+ if (key == defFormat->key())
+ return defFormat;
+
+ cachedFormat = createFormat(f, c);
+ cachedFormat->collection = this;
+ cKey.insert(cachedFormat->key(), cachedFormat);
+ if (cachedFormat->key() != key)
+ qWarning("ASSERT: keys for format not identical: '%s '%s'", cachedFormat->key().latin1(), key.latin1());
+ return cachedFormat;
+}
+
+void Q3TextFormatCollection::remove(Q3TextFormat *f)
+{
+ if (lastFormat == f)
+ lastFormat = 0;
+ if (cres == f)
+ cres = 0;
+ if (cachedFormat == f)
+ cachedFormat = 0;
+ if (cKey.value(f->key()) == f)
+ delete cKey.take(f->key());
+}
+
+#define UPDATE(up, lo, rest) \
+ if (font.lo##rest() != defFormat->fn.lo##rest() && fm->fn.lo##rest() == defFormat->fn.lo##rest()) \
+ fm->fn.set##up##rest(font.lo##rest())
+
+void Q3TextFormatCollection::updateDefaultFormat(const QFont &font, const QColor &color, Q3StyleSheet *sheet)
+{
+ bool usePixels = font.pointSize() == -1;
+ bool changeSize = usePixels ? font.pixelSize() != defFormat->fn.pixelSize() :
+ font.pointSize() != defFormat->fn.pointSize();
+ int base = usePixels ? font.pixelSize() : font.pointSize();
+ QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin();
+ for (; it != cKey.end(); ++it) {
+ Q3TextFormat *fm = *it;
+ UPDATE(F, f, amily);
+ UPDATE(W, w, eight);
+ UPDATE(B, b, old);
+ UPDATE(I, i, talic);
+ UPDATE(U, u, nderline);
+ if (changeSize) {
+ fm->stdSize = base;
+ fm->usePixelSizes = usePixels;
+ if (usePixels)
+ fm->fn.setPixelSize(fm->stdSize);
+ else
+ fm->fn.setPointSize(fm->stdSize);
+ sheet->scaleFont(fm->fn, fm->logicalFontSize);
+ }
+ if (color.isValid() && color != defFormat->col && fm->col == defFormat->col)
+ fm->col = color;
+ fm->update();
+ }
+
+ defFormat->fn = font;
+ defFormat->col = color;
+ defFormat->update();
+ defFormat->stdSize = base;
+ defFormat->usePixelSizes = usePixels;
+
+ updateKeys();
+}
+
+// the keys in cKey have changed, rebuild the hashtable
+void Q3TextFormatCollection::updateKeys()
+{
+ if (cKey.isEmpty())
+ return;
+ Q3TextFormat** formats = new Q3TextFormat *[cKey.count() + 1];
+ Q3TextFormat **f = formats;
+ for (QHash<QString, Q3TextFormat *>::Iterator it = cKey.begin(); it != cKey.end(); ++it, ++f)
+ *f = *it;
+ *f = 0;
+ cKey.clear();
+ for (f = formats; *f; f++)
+ cKey.insert((*f)->key(), *f);
+ delete [] formats;
+}
+
+
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+void Q3TextFormat::setBold(bool b)
+{
+ if (b == fn.bold())
+ return;
+ fn.setBold(b);
+ update();
+}
+
+void Q3TextFormat::setMisspelled(bool b)
+{
+ if (b == (bool)missp)
+ return;
+ missp = b;
+ update();
+}
+
+void Q3TextFormat::setVAlign(VerticalAlignment a)
+{
+ if (a == ha)
+ return;
+ ha = a;
+ update();
+}
+
+void Q3TextFormat::setItalic(bool b)
+{
+ if (b == fn.italic())
+ return;
+ fn.setItalic(b);
+ update();
+}
+
+void Q3TextFormat::setUnderline(bool b)
+{
+ if (b == fn.underline())
+ return;
+ fn.setUnderline(b);
+ update();
+}
+
+void Q3TextFormat::setStrikeOut(bool b)
+{
+ if (b == fn.strikeOut())
+ return;
+ fn.setStrikeOut(b);
+ update();
+}
+
+void Q3TextFormat::setFamily(const QString &f)
+{
+ if (f == fn.family())
+ return;
+ fn.setFamily(f);
+ update();
+}
+
+void Q3TextFormat::setPointSize(int s)
+{
+ if (s == fn.pointSize())
+ return;
+ fn.setPointSize(s);
+ usePixelSizes = false;
+ update();
+}
+
+void Q3TextFormat::setFont(const QFont &f)
+{
+ if (f == fn && !k.isEmpty())
+ return;
+ fn = f;
+ update();
+}
+
+void Q3TextFormat::setColor(const QColor &c)
+{
+ if (c == col)
+ return;
+ col = c;
+ update();
+}
+
+QString Q3TextFormat::makeFormatChangeTags(Q3TextFormat* defaultFormat, Q3TextFormat *f,
+ const QString& oldAnchorHref, const QString& anchorHref ) const
+{
+ QString tag;
+ if (f)
+ tag += f->makeFormatEndTags(defaultFormat, oldAnchorHref);
+
+ if (!anchorHref.isEmpty())
+ tag += QLatin1String("<a href=\"") + anchorHref + QLatin1String("\">");
+
+ if (font() != defaultFormat->font()
+ || vAlign() != defaultFormat->vAlign()
+ || color().rgb() != defaultFormat->color().rgb()) {
+ QString s;
+ if (font().family() != defaultFormat->font().family())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-family:") + fn.family();
+ if (font().italic() && font().italic() != defaultFormat->font().italic())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-style:") + (font().italic() ? QLatin1String("italic") : QLatin1String("normal"));
+ if (font().pointSize() != defaultFormat->font().pointSize())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-size:") + QString::number(fn.pointSize()) + QLatin1String("pt");
+ if (font().weight() != defaultFormat->font().weight())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("font-weight:") + QString::number(fn.weight() * 8);
+ QString textDecoration;
+ bool none = false;
+ if ( font().underline() != defaultFormat->font().underline() ) {
+ if (font().underline())
+ textDecoration = QLatin1String("underline");
+ else
+ none = true;
+ }
+ if ( font().overline() != defaultFormat->font().overline() ) {
+ if (font().overline())
+ textDecoration += QLatin1String(" overline");
+ else
+ none = true;
+ }
+ if ( font().strikeOut() != defaultFormat->font().strikeOut() ) {
+ if (font().strikeOut())
+ textDecoration += QLatin1String(" line-through");
+ else
+ none = true;
+ }
+ if (none && textDecoration.isEmpty())
+ textDecoration = QLatin1String("none");
+ if (!textDecoration.isEmpty())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("text-decoration:") + textDecoration;
+ if (vAlign() != defaultFormat->vAlign()) {
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("vertical-align:");
+ if (vAlign() == Q3TextFormat::AlignSuperScript)
+ s += QLatin1String("super");
+ else if (vAlign() == Q3TextFormat::AlignSubScript)
+ s += QLatin1String("sub");
+ else
+ s += QLatin1String("normal");
+ }
+ if (color().rgb() != defaultFormat->color().rgb())
+ s += QString(s.size()?QLatin1String(";"):QLatin1String("")) + QLatin1String("color:") + col.name();
+ if (!s.isEmpty())
+ tag += QLatin1String("<span style=\"") + s + QLatin1String("\">");
+ }
+
+ return tag;
+}
+
+QString Q3TextFormat::makeFormatEndTags(Q3TextFormat* defaultFormat, const QString& anchorHref) const
+{
+ QString tag;
+ if (font().family() != defaultFormat->font().family()
+ || font().pointSize() != defaultFormat->font().pointSize()
+ || font().weight() != defaultFormat->font().weight()
+ || font().italic() != defaultFormat->font().italic()
+ || font().underline() != defaultFormat->font().underline()
+ || font().strikeOut() != defaultFormat->font().strikeOut()
+ || vAlign() != defaultFormat->vAlign()
+ || color().rgb() != defaultFormat->color().rgb())
+ tag += QLatin1String("</span>");
+ if (!anchorHref.isEmpty())
+ tag += QLatin1String("</a>");
+ return tag;
+}
+
+Q3TextFormat Q3TextFormat::makeTextFormat(const Q3StyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor) const
+{
+ Q3TextFormat format(*this);
+ if (!style)
+ return format;
+
+ if (!style->isAnchor() && style->color().isValid()) {
+ // the style is not an anchor and defines a color.
+ // It might be used inside an anchor and it should
+ // override the link color.
+ format.linkColor = false;
+ }
+ switch (style->verticalAlignment()) {
+ case Q3StyleSheetItem::VAlignBaseline:
+ format.setVAlign(Q3TextFormat::AlignNormal);
+ break;
+ case Q3StyleSheetItem::VAlignSuper:
+ format.setVAlign(Q3TextFormat::AlignSuperScript);
+ break;
+ case Q3StyleSheetItem::VAlignSub:
+ format.setVAlign(Q3TextFormat::AlignSubScript);
+ break;
+ }
+
+ if (style->fontWeight() != Q3StyleSheetItem::Undefined)
+ format.fn.setWeight(style->fontWeight());
+ if (style->fontSize() != Q3StyleSheetItem::Undefined) {
+ format.fn.setPointSize(style->fontSize());
+ } else if (style->logicalFontSize() != Q3StyleSheetItem::Undefined) {
+ format.logicalFontSize = style->logicalFontSize();
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ } else if (style->logicalFontSizeStep()) {
+ format.logicalFontSize += style->logicalFontSizeStep();
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ }
+ if (!style->fontFamily().isEmpty())
+ format.fn.setFamily(style->fontFamily());
+ if (style->color().isValid())
+ format.col = style->color();
+ if (style->definesFontItalic())
+ format.fn.setItalic(style->fontItalic());
+ if (style->definesFontUnderline())
+ format.fn.setUnderline(style->fontUnderline());
+ if (style->definesFontStrikeOut())
+ format.fn.setStrikeOut(style->fontStrikeOut());
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+
+ if (style->name() == QLatin1String("font")) {
+ it = attr.find(QLatin1String("color"));
+ if (it != end && ! (*it).isEmpty()){
+ format.col.setNamedColor(*it);
+ format.linkColor = false;
+ }
+ it = attr.find(QLatin1String("face"));
+ if (it != end) {
+ QString family = (*it).section(QLatin1Char(','), 0, 0);
+ if (family.size())
+ format.fn.setFamily(family);
+ }
+ it = attr.find(QLatin1String("size"));
+ if (it != end) {
+ QString a = *it;
+ int n = a.toInt();
+ if (a[0] == QLatin1Char('+') || a[0] == QLatin1Char('-'))
+ n += 3;
+ format.logicalFontSize = n;
+ if (format.usePixelSizes)
+ format.fn.setPixelSize(format.stdSize);
+ else
+ format.fn.setPointSize(format.stdSize);
+ style->styleSheet()->scaleFont(format.fn, format.logicalFontSize);
+ }
+ }
+
+ it = attr.find(QLatin1String("style"));
+ if (it != end) {
+ QString a = *it;
+ int count = a.count(QLatin1Char(';'))+1;
+ for (int s = 0; s < count; s++) {
+ QString style = a.section(QLatin1Char(';'), s, s);
+ if (style.startsWith(QLatin1String("font-size:")) && style.endsWith(QLatin1String("pt"))) {
+ format.logicalFontSize = 0;
+ int size = int(scaleFontsFactor * style.mid(10, style.length() - 12).toDouble());
+ format.setPointSize(size);
+ } else if (style.startsWith(QLatin1String("font-style:"))) {
+ QString s = style.mid(11).trimmed();
+ if (s == QLatin1String("normal"))
+ format.fn.setItalic(false);
+ else if (s == QLatin1String("italic") || s == QLatin1String("oblique"))
+ format.fn.setItalic(true);
+ } else if (style.startsWith(QLatin1String("font-weight:"))) {
+ QString s = style.mid(12);
+ bool ok = true;
+ int n = s.toInt(&ok);
+ if (ok)
+ format.fn.setWeight(n/8);
+ } else if (style.startsWith(QLatin1String("font-family:"))) {
+ QString family = style.mid(12).section(QLatin1Char(','),0,0);
+ family.replace(QLatin1Char('\"'), QLatin1Char(' '));
+ family.replace(QLatin1Char('\''), QLatin1Char(' '));
+ family = family.trimmed();
+ format.fn.setFamily(family);
+ } else if (style.startsWith(QLatin1String("text-decoration:"))) {
+ QString s = style.mid( 16 );
+ format.fn.setOverline(s.contains(QLatin1String("overline")));
+ format.fn.setStrikeOut(s.contains(QLatin1String("line-through")));
+ format.fn.setUnderline(s.contains(QLatin1String("underline")));
+ } else if (style.startsWith(QLatin1String("vertical-align:"))) {
+ QString s = style.mid(15).trimmed();
+ if (s == QLatin1String("sub"))
+ format.setVAlign(Q3TextFormat::AlignSubScript);
+ else if (s == QLatin1String("super"))
+ format.setVAlign(Q3TextFormat::AlignSuperScript);
+ else
+ format.setVAlign(Q3TextFormat::AlignNormal);
+ } else if (style.startsWith(QLatin1String("color:"))) {
+ format.col.setNamedColor(style.mid(6));
+ format.linkColor = false;
+ }
+ }
+ }
+
+ format.update();
+ return format;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+
+struct QPixmapInt
+{
+ QPixmapInt() : ref(0) {}
+ QPixmap pm;
+ int ref;
+ Q_DUMMY_COMPARISON_OPERATOR(QPixmapInt)
+};
+
+static QMap<QString, QPixmapInt> *pixmap_map = 0;
+
+Q3TextImage::Q3TextImage(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory)
+ : Q3TextCustomItem(p)
+{
+ width = height = 0;
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("width"));
+ if (it != end)
+ width = (*it).toInt();
+ it = attr.find(QLatin1String("height"));
+ if (it != end)
+ height = (*it).toInt();
+
+ reg = 0;
+ QString imageName = attr[QLatin1String("src")];
+
+ if (imageName.size() == 0)
+ imageName = attr[QLatin1String("source")];
+
+ if (!imageName.isEmpty()) {
+ imgId = QString(QLatin1String("%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) {
+ qCritical("Q3TextImage: no mimesource for %s", imageName.latin1());
+ }
+ else {
+ if (!Q3ImageDrag::decode(m, img)) {
+ qCritical("Q3TextImage: 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.hasAlphaChannel()) {
+ QRegion mask(pm.mask());
+ QRegion all(0, 0, pm.width(), pm.height());
+ reg = new QRegion(all.subtracted(mask));
+ }
+ }
+
+ if (pm.isNull() && (width*height)==0)
+ width = height = 50;
+
+ place = PlaceInline;
+ if (attr[QLatin1String("align")] == QLatin1String("left"))
+ place = PlaceLeft;
+ else if (attr[QLatin1String("align")] == QLatin1String("right"))
+ place = PlaceRight;
+
+ tmpwidth = width;
+ tmpheight = height;
+
+ attributes = attr;
+}
+
+Q3TextImage::~Q3TextImage()
+{
+ if (pixmap_map && pixmap_map->contains(imgId)) {
+ QPixmapInt& pmi = pixmap_map->operator[](imgId);
+ pmi.ref--;
+ if (!pmi.ref) {
+ pixmap_map->remove(imgId);
+ if (pixmap_map->isEmpty()) {
+ delete pixmap_map;
+ pixmap_map = 0;
+ }
+ }
+ }
+ delete reg;
+}
+
+QString Q3TextImage::richText() const
+{
+ QString s;
+ s += QLatin1String("<img ");
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for (; it != attributes.end(); ++it) {
+ s += it.key() + QLatin1String("=");
+ if ((*it).contains(QLatin1Char(' ')))
+ s += QLatin1String("\"") + *it + QLatin1String("\" ");
+ else
+ s += *it + QLatin1String(" ");
+ }
+ s += QLatin1String(">");
+ return s;
+}
+
+void Q3TextImage::adjustToPainter(QPainter* p)
+{
+ width = scale(tmpwidth, p);
+ height = scale(tmpheight, p);
+}
+
+#if !defined(Q_WS_X11)
+static QPixmap *qrt_selection = 0;
+static Q3SingleCleanupHandler<QPixmap> qrt_cleanup_pixmap;
+static void qrt_createSelectionPixmap(const QPalette &pal)
+{
+ 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(pal.highlight().color());
+}
+#endif
+
+void Q3TextImage::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+ if (placement() != PlaceInline) {
+ x = xpos;
+ y = ypos;
+ }
+
+ if (pm.isNull()) {
+ p->fillRect(x , y, width, height, pal.dark());
+ return;
+ }
+
+ if (is_printer(p)) {
+ p->drawPixmap(QRect(x, y, width, height), 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(pal.highlight(),
+ Qt::Dense4Pattern));
+#else // in WIN32 Qt::Dense4Pattern doesn't work correctly (transparency problem), so work around it
+ if (!qrt_selection)
+ qrt_createSelectionPixmap(pal);
+ p->drawTiledPixmap(x, y, pm.width(), pm.height(), *qrt_selection);
+#endif
+ }
+}
+
+void Q3TextHorizontalLine::adjustToPainter(QPainter* p)
+{
+ height = scale(tmpheight, p);
+}
+
+
+Q3TextHorizontalLine::Q3TextHorizontalLine(Q3TextDocument *p, const QMap<QString, QString> &attr,
+ const QString &,
+ Q3MimeSourceFactory &)
+ : Q3TextCustomItem(p)
+{
+ height = tmpheight = 8;
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("color"));
+ if (it != end)
+ color = QColor(*it);
+ shade = attr.find(QLatin1String("noshade")) == end;
+}
+
+Q3TextHorizontalLine::~Q3TextHorizontalLine()
+{
+}
+
+QString Q3TextHorizontalLine::richText() const
+{
+ return QLatin1String("<hr>");
+}
+
+void Q3TextHorizontalLine::draw(QPainter* p, int x, int y, int , int , int , int ,
+ const QPalette& pal, bool selected)
+{
+ QRect r(x, y, width, height);
+ if (is_printer(p) || !shade) {
+ QPen oldPen = p->pen();
+ if (!color.isValid())
+ p->setPen(QPen(pal.text().color(), is_printer(p) ? height/8 : qMax(2, height/4)));
+ else
+ p->setPen(QPen(color, is_printer(p) ? height/8 : qMax(2, height/4)));
+ p->drawLine(r.left()-1, y + height / 2, r.right() + 1, y + height / 2);
+ p->setPen(oldPen);
+ } else {
+ if (selected)
+ p->fillRect(r, pal.highlight());
+ QPalette pal2(pal);
+ if (color.isValid())
+ pal2.setColor(pal2.currentColorGroup(), QPalette::Dark, color);
+ qDrawShadeLine(p, r.left() - 1, y + height / 2, r.right() + 1, y + height / 2, pal2,
+ true, height / 8);
+ }
+}
+#endif //QT_NO_TEXTCUSTOMITEM
+
+/*****************************************************************/
+// Small set of utility functions to make the parser a bit simpler
+//
+
+bool Q3TextDocument::hasPrefix(const QChar* doc, int length, int pos, QChar c)
+{
+ if (pos + 1 > length)
+ return false;
+ return doc[pos].toLower() == c.toLower();
+}
+
+bool Q3TextDocument::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].toLower() != s[i].toLower())
+ return false;
+ }
+ return true;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+static bool qt_is_cell_in_use(QList<Q3TextTableCell *>& cells, int row, int col)
+{
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ if (row >= c->row() && row < c->row() + c->rowspan()
+ && col >= c->column() && col < c->column() + c->colspan())
+ return true;
+ }
+ return false;
+}
+
+Q3TextCustomItem* Q3TextDocument::parseTable(const QMap<QString, QString> &attr, const Q3TextFormat &fmt,
+ const QChar* doc, int length, int& pos, Q3TextParagraph *curpar)
+{
+
+ Q3TextTable* table = new Q3TextTable(this, attr);
+ int row = -1;
+ int col = -1;
+
+ QString rowbgcolor;
+ QString rowalign;
+ QString tablebgcolor = attr[QLatin1String("bgcolor")];
+
+ QList<Q3TextTableCell *> multicells;
+
+ QString tagname;
+ (void) eatSpace(doc, length, pos);
+ while (pos < length) {
+ if (hasPrefix(doc, length, pos, QLatin1Char('<'))){
+ if (hasPrefix(doc, length, pos+1, QLatin1Char('/'))) {
+ tagname = parseCloseTag(doc, length, pos);
+ if (tagname == QLatin1String("table")) {
+ return table;
+ }
+ } else {
+ QMap<QString, QString> attr2;
+ bool emptyTag = false;
+ tagname = parseOpenTag(doc, length, pos, attr2, emptyTag);
+ if (tagname == QLatin1String("tr")) {
+ rowbgcolor = attr2[QLatin1String("bgcolor")];
+ rowalign = attr2[QLatin1String("align")];
+ row++;
+ col = -1;
+ }
+ else if (tagname == QLatin1String("td") || tagname == QLatin1String("th")) {
+ col++;
+ while (qt_is_cell_in_use(multicells, row, col)) {
+ col++;
+ }
+
+ if (row >= 0 && col >= 0) {
+ const Q3StyleSheetItem* s = sheet_->item(tagname);
+ if (!attr2.contains(QLatin1String("bgcolor"))) {
+ if (!rowbgcolor.isEmpty())
+ attr2[QLatin1String("bgcolor")] = rowbgcolor;
+ else if (!tablebgcolor.isEmpty())
+ attr2[QLatin1String("bgcolor")] = tablebgcolor;
+ }
+ if (!attr2.contains(QLatin1String("align"))) {
+ if (!rowalign.isEmpty())
+ attr2[QLatin1String("align")] = rowalign;
+ }
+
+ // extract the cell contents
+ int end = pos;
+ while (end < length
+ && !hasPrefix(doc, length, end, QLatin1String("</td"))
+ && !hasPrefix(doc, length, end, QLatin1String("<td"))
+ && !hasPrefix(doc, length, end, QLatin1String("</th"))
+ && !hasPrefix(doc, length, end, QLatin1String("<th"))
+ && !hasPrefix(doc, length, end, QLatin1String("<td"))
+ && !hasPrefix(doc, length, end, QLatin1String("</tr"))
+ && !hasPrefix(doc, length, end, QLatin1String("<tr"))
+ && !hasPrefix(doc, length, end, QLatin1String("</table"))) {
+ if (hasPrefix(doc, length, end, QLatin1String("<table"))) { // nested table
+ int nested = 1;
+ ++end;
+ while (end < length && nested != 0) {
+ if (hasPrefix(doc, length, end, QLatin1String("</table")))
+ nested--;
+ if (hasPrefix(doc, length, end, QLatin1String("<table")))
+ nested++;
+ end++;
+ }
+ }
+ end++;
+ }
+ Q3TextTableCell* cell = new Q3TextTableCell(table, row, col,
+ attr2, s, fmt.makeTextFormat(s, attr2, scaleFontsFactor),
+ contxt, *factory_, sheet_,
+ QString::fromRawData(doc + pos, end - pos));
+ cell->richText()->parentPar = curpar;
+ if (cell->colspan() > 1 || cell->rowspan() > 1)
+ multicells.append(cell);
+ col += cell->colspan()-1;
+ pos = end;
+ }
+ }
+ }
+
+ } else {
+ ++pos;
+ }
+ }
+ return table;
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+bool Q3TextDocument::eatSpace(const QChar* doc, int length, int& pos, bool includeNbsp)
+{
+ int old_pos = pos;
+ while (pos < length && doc[pos].isSpace() && (includeNbsp || (doc[pos] != QChar(QChar::nbsp))))
+ pos++;
+ return old_pos < pos;
+}
+
+bool Q3TextDocument::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 },
+ { "cur" "ren", 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<QByteArray, QChar> *html_map = 0;
+static void qt_cleanup_html_map()
+{
+ delete html_map;
+ html_map = 0;
+}
+
+static QMap<QByteArray, QChar> *htmlMap()
+{
+ if (!html_map) {
+ html_map = new QMap<QByteArray, QChar>;
+ qAddPostRoutine(qt_cleanup_html_map);
+
+ const Entity *ent = entitylist;
+ while(ent->code) {
+ html_map->insert(QByteArray(ent->name), QChar(ent->code));
+ ent++;
+ }
+ }
+ return html_map;
+}
+
+QChar Q3TextDocument::parseHTMLSpecialChar(const QChar* doc, int length, int& pos)
+{
+ QString s;
+ pos++;
+ int recoverpos = pos;
+ while (pos < length && doc[pos] != QLatin1Char(';') && !doc[pos].isSpace() && pos < recoverpos + 8) {
+ s += doc[pos];
+ pos++;
+ }
+ if (doc[pos] != QLatin1Char(';') && !doc[pos].isSpace()) {
+ pos = recoverpos;
+ return QLatin1Char('&');
+ }
+ pos++;
+
+ if (s.length() > 1 && s[0] == QLatin1Char('#')) {
+ int off = 1;
+ int base = 10;
+ if (s[1] == QLatin1Char('x')) {
+ off = 2;
+ base = 16;
+ }
+ bool ok;
+ int num = s.mid(off).toInt(&ok, base);
+ if (num == 151) // ### hack for designer manual
+ return QLatin1Char('-');
+ return num;
+ }
+
+ QMap<QByteArray, QChar>::Iterator it = htmlMap()->find(s.toLatin1());
+ if (it != htmlMap()->end()) {
+ return *it;
+ }
+
+ pos = recoverpos;
+ return QLatin1Char('&');
+}
+
+QString Q3TextDocument::parseWord(const QChar* doc, int length, int& pos, bool lower)
+{
+ QString s;
+
+ if (doc[pos] == QLatin1Char('"')) {
+ pos++;
+ while (pos < length && doc[pos] != QLatin1Char('"')) {
+ if (doc[pos] == QLatin1Char('&')) {
+ s += parseHTMLSpecialChar(doc, length, pos);
+ } else {
+ s += doc[pos];
+ pos++;
+ }
+ }
+ eat(doc, length, pos, QLatin1Char('"'));
+ } else if (doc[pos] == QLatin1Char('\'')) {
+ pos++;
+ while (pos < length && doc[pos] != QLatin1Char('\'')) {
+ s += doc[pos];
+ pos++;
+ }
+ eat(doc, length, pos, QLatin1Char('\''));
+ } else {
+ static QString term = QString::fromLatin1("/>");
+ while (pos < length
+ && doc[pos] != QLatin1Char('>')
+ && !hasPrefix(doc, length, pos, term)
+ && doc[pos] != QLatin1Char('<')
+ && doc[pos] != QLatin1Char('=')
+ && !doc[pos].isSpace())
+ {
+ if (doc[pos] == QLatin1Char('&')) {
+ s += parseHTMLSpecialChar(doc, length, pos);
+ } else {
+ s += doc[pos];
+ pos++;
+ }
+ }
+ if (lower)
+ s = s.toLower();
+ }
+ return s;
+}
+
+QChar Q3TextDocument::parseChar(const QChar* doc, int length, int& pos, Q3StyleSheetItem::WhiteSpaceMode wsm)
+{
+ if (pos >= length)
+ return QChar::null;
+
+ QChar c = doc[pos++];
+
+ if (c == QLatin1Char('<'))
+ return QChar::null;
+
+ if (c.isSpace() && c != QChar(QChar::nbsp)) {
+ if (wsm == Q3StyleSheetItem::WhiteSpacePre) {
+ if (c == QLatin1Char('\n'))
+ return QChar::LineSeparator;
+ else
+ return c;
+ } else { // non-pre mode: collapse whitespace except nbsp
+ while (pos< length &&
+ doc[pos].isSpace() && doc[pos] != QChar(QChar::nbsp))
+ pos++;
+ return QLatin1Char(' ');
+ }
+ }
+ else if (c == QLatin1Char('&'))
+ return parseHTMLSpecialChar(doc, length, --pos);
+ else
+ return c;
+}
+
+QString Q3TextDocument::parseOpenTag(const QChar* doc, int length, int& pos,
+ QMap<QString, QString> &attr, bool& emptyTag)
+{
+ emptyTag = false;
+ pos++;
+ if (hasPrefix(doc, length, pos, QLatin1Char('!'))) {
+ if (hasPrefix(doc, length, pos+1, QLatin1String("--"))) {
+ 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();
+ }
+ else {
+ // eat strange internal tags
+ while (!hasPrefix(doc, length, pos, QLatin1Char('>')) && pos < length)
+ pos++;
+ if (hasPrefix(doc, length, pos, QLatin1Char('>'))) {
+ pos++;
+ eatSpace(doc, length, pos, true);
+ }
+ return QString();
+ }
+ }
+
+ 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] != QLatin1Char('>') && ! (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] != QLatin1Char('>'))
+ pos++;
+ break;
+ }
+ QString value;
+ if (hasPrefix(doc, length, pos, QLatin1Char('='))){
+ pos++;
+ eatSpace(doc, length, pos);
+ value = parseWord(doc, length, pos, false);
+ }
+ else
+ value = s_TRUE;
+ attr.insert(key.toLower(), value);
+ eatSpace(doc, length, pos, true);
+ }
+
+ if (emptyTag) {
+ eat(doc, length, pos, QLatin1Char('/'));
+ eat(doc, length, pos, QLatin1Char('>'));
+ }
+ else
+ eat(doc, length, pos, QLatin1Char('>'));
+
+ return tag;
+}
+
+QString Q3TextDocument::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, QLatin1Char('>'));
+ return tag;
+}
+
+Q3TextFlow::Q3TextFlow()
+{
+ w = pagesize = 0;
+}
+
+Q3TextFlow::~Q3TextFlow()
+{
+ clear();
+}
+
+void Q3TextFlow::clear()
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ while (!leftItems.isEmpty())
+ delete leftItems.takeFirst();
+ while (!rightItems.isEmpty())
+ delete rightItems.takeFirst();
+#endif
+}
+
+void Q3TextFlow::setWidth(int width)
+{
+ w = width;
+}
+
+int Q3TextFlow::adjustLMargin(int yp, int, int margin, int space)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ if (item->ypos == -1)
+ continue;
+ if (yp >= item->ypos && yp < item->ypos + item->height)
+ margin = qMax(margin, item->xpos + item->width + space);
+ }
+#endif
+ return margin;
+}
+
+int Q3TextFlow::adjustRMargin(int yp, int, int margin, int space)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ if (item->ypos == -1)
+ continue;
+ if (yp >= item->ypos && yp < item->ypos + item->height)
+ margin = qMax(margin, w - item->xpos - space);
+ }
+#endif
+ return margin;
+}
+
+
+int Q3TextFlow::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;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextFlow::unregisterFloatingItem(Q3TextCustomItem* item)
+{
+ leftItems.removeAll(item);
+ rightItems.removeAll(item);
+}
+
+void Q3TextFlow::registerFloatingItem(Q3TextCustomItem* item)
+{
+ if (item->placement() == Q3TextCustomItem::PlaceRight) {
+ if (!rightItems.contains(item))
+ rightItems.append(item);
+ } else if (item->placement() == Q3TextCustomItem::PlaceLeft &&
+ !leftItems.contains(item)) {
+ leftItems.append(item);
+ }
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+QRect Q3TextFlow::boundingRect() const
+{
+ QRect br;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ br = br.united(item->geometry());
+ }
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ br = br.united(item->geometry());
+ }
+#endif
+ return br;
+}
+
+
+void Q3TextFlow::drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ for (int idx = 0; idx < leftItems.size(); ++idx) {
+ Q3TextCustomItem* item = leftItems.at(idx);
+ if (item->xpos == -1 || item->ypos == -1)
+ continue;
+ item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
+ }
+
+ for (int idx = 0; idx < rightItems.size(); ++idx) {
+ Q3TextCustomItem* item = rightItems.at(idx);
+ if (item->xpos == -1 || item->ypos == -1)
+ continue;
+ item->draw(p, item->xpos, item->ypos, cx, cy, cw, ch, pal, selected);
+ }
+#endif
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextCustomItem::pageBreak(int /*y*/ , Q3TextFlow* /*flow*/)
+{
+}
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextTable::Q3TextTable(Q3TextDocument *p, const QMap<QString, QString> & attr )
+ : Q3TextCustomItem(p)
+{
+ cellspacing = 2;
+ cellpadding = 1;
+ border = innerborder = 0;
+
+ QMap<QString, QString>::ConstIterator it, end = attr.end();
+ it = attr.find(QLatin1String("cellspacing"));
+ if (it != end)
+ cellspacing = (*it).toInt();
+ it = attr.find(QLatin1String("cellpadding"));
+ if (it != end)
+ cellpadding = (*it).toInt();
+ it = attr.find(QLatin1String("border"));
+ if (it != end) {
+ if (*it == QLatin1String("TRUE"))
+ border = 1;
+ else
+ border = (*it).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;
+ it = attr.find(QLatin1String("width"));
+ if (it != end) {
+ bool b;
+ QString s(*it);
+ int w = s.toInt(&b);
+ if (b) {
+ fixwidth = w;
+ } else {
+ s = s.trimmed();
+ if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
+ stretch = s.left(s.length()-1).toInt();
+ }
+ }
+ us_fixwidth = fixwidth;
+
+ place = PlaceInline;
+ if (attr[QLatin1String("align")] == QLatin1String("left"))
+ place = PlaceLeft;
+ else if (attr[QLatin1String("align")] == QLatin1String("right"))
+ place = PlaceRight;
+ cachewidth = 0;
+ attributes = attr;
+ pageBreakFor = -1;
+}
+
+Q3TextTable::~Q3TextTable()
+{
+ delete layout;
+}
+
+QString Q3TextTable::richText() const
+{
+ QString s;
+ s = QLatin1String("<table ");
+ QMap<QString, QString>::ConstIterator it = attributes.begin();
+ for (; it != attributes.end(); ++it)
+ s += it.key() + QLatin1String("=") + *it + QLatin1String(" ");
+ s += QLatin1String(">\n");
+
+ int lastRow = -1;
+ bool needEnd = false;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ if (lastRow != cell->row()) {
+ if (lastRow != -1)
+ s += QLatin1String("</tr>\n");
+ s += QLatin1String("<tr>");
+ lastRow = cell->row();
+ needEnd = true;
+ }
+ s += QLatin1String("<td");
+ it = cell->attributes.constBegin();
+ for (; it != cell->attributes.constEnd(); ++it)
+ s += QLatin1String(" ") + it.key() + QLatin1String("=") + *it;
+ s += QLatin1String(">");
+ s += cell->richText()->richText();
+ s += QLatin1String("</td>");
+ }
+ if (needEnd)
+ s += QLatin1String("</tr>\n");
+ s += QLatin1String("</table>\n");
+ return s;
+}
+
+void Q3TextTable::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);
+ fixwidth = scale( us_fixwidth, p);
+ width = 0;
+ cachewidth = 0;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ cell->adjustToPainter(p);
+ }
+}
+
+void Q3TextTable::adjustCells(int y , int shift)
+{
+ bool enlarge = false;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ 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 Q3TextTable::pageBreak(int yt, Q3TextFlow* 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;
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ 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 Q3TextTable::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected)
+{
+ if (placement() != PlaceInline) {
+ x = xpos;
+ y = ypos;
+ }
+
+ for (int idx = 0; idx < cells.size(); ++idx) {
+ Q3TextTableCell *cell = cells.at(idx);
+ 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, pal, 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.adjust(innerborder/2, innerborder/2, -innerborder/2, -innerborder/2);
+ p->setPen(QPen(pal.text().color(), 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(), pal.button());
+ p->fillRect(r.right(), r.top(), s+1, r.height(), pal.button());
+ p->fillRect(r.left()-s, r.top()-s, r.width()+2*s, s, pal.button());
+ p->fillRect(r.left()-s, r.bottom(), r.width()+2*s, s, pal.button());
+ }
+ qDrawShadePanel(p, r, pal, true, innerborder);
+ }
+ }
+ }
+ }
+ if (border) {
+ QRect r (x, y, width, height);
+ if (is_printer(p)) {
+ QRect r2 = r;
+ r2.adjust(border/2, border/2, -border/2, -border/2);
+ QPen oldPen = p->pen();
+ p->setPen(QPen(pal.text().color(), 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(), pal.button());
+ p->fillRect(r.right()-s, r.top(), s, r.height(), pal.button());
+ p->fillRect(r.left(), r.top(), r.width(), s, pal.button());
+ p->fillRect(r.left(), r.bottom()-s, r.width(), s, pal.button());
+ }
+ qDrawShadePanel(p, r, pal, false, border);
+ }
+ }
+
+}
+
+int Q3TextTable::minimumWidth() const
+{
+ return qMax(fixwidth, ((layout ? layout->minimumSize().width() : 0) + 2 * outerborder));
+}
+
+void Q3TextTable::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 Q3TextTable::format(int w)
+{
+ for (int i = 0; i < (int)cells.count(); ++i) {
+ Q3TextTableCell *cell = cells.at(i);
+ QRect r = cell->geometry();
+ r.setWidth(w - 2*outerborder);
+ cell->setGeometry(r);
+ }
+}
+
+void Q3TextTable::addCell(Q3TextTableCell* cell)
+{
+ cells.append(cell);
+ layout->addMultiCell(cell, cell->row(), cell->row() + cell->rowspan()-1,
+ cell->column(), cell->column() + cell->colspan()-1);
+}
+
+bool Q3TextTable::enter(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&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 Q3TextTable::enterAt(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&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) {
+ Q3TextTableCell *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;
+ }
+
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::next(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&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);
+ Q3TextCustomItem::next(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::prev(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&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);
+ Q3TextCustomItem::prev(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *cell = cells.first();
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ idx = -1;
+ return true;
+ }
+
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (!cell)
+ return false;
+ doc = cell->richText();
+ parag = doc->lastParagraph();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::down(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (cell->row_ == layout->numRows() - 1) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::down(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *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->firstParagraph();
+ idx = 0;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+bool Q3TextTable::up(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy)
+{
+ if (currCell.find(c) == currCell.end())
+ return false;
+ Q3TextTableCell *cell = cells.at(*currCell.find(c));
+ if (cell->row_ == 0) {
+ currCell.insert(c, 0);
+ Q3TextCustomItem::up(c, doc, parag, idx, ox, oy);
+ Q3TextTableCell *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->lastParagraph();
+ idx = parag->length() - 1;
+ ox += cell->geometry().x() + cell->horizontalAlignmentOffset() + outerborder + parent->x();
+ oy += cell->geometry().y() + cell->verticalAlignmentOffset() + outerborder;
+ return true;
+}
+
+Q3TextTableCell::Q3TextTableCell(Q3TextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const Q3StyleSheetItem* style,
+ const Q3TextFormat& fmt, const QString& context,
+ Q3MimeSourceFactory &factory, Q3StyleSheet *sheet,
+ const QString& doc)
+{
+ cached_width = -1;
+ cached_sizehint = -1;
+
+ maxw = QWIDGETSIZE_MAX;
+ minw = 0;
+
+ parent = table;
+ row_ = row;
+ col_ = column;
+ stretch_ = 0;
+ richtext = new Q3TextDocument(table->parent);
+ richtext->formatCollection()->setPaintDevice(table->parent->formatCollection()->paintDevice());
+ richtext->bodyText = fmt.color();
+ richtext->setTableCell(this);
+
+ QMap<QString,QString>::ConstIterator it, end = attr.end();
+ int halign = style->alignment();
+ if (halign != Q3StyleSheetItem::Undefined)
+ richtext->setAlignment(halign);
+ it = attr.find(QLatin1String("align"));
+ if (it != end && ! (*it).isEmpty()) {
+ QString a = (*it).toLower();
+ if (a == QLatin1String("left"))
+ richtext->setAlignment(Qt::AlignLeft);
+ else if (a == QLatin1String("center"))
+ richtext->setAlignment(Qt::AlignHCenter);
+ else if (a == QLatin1String("right"))
+ richtext->setAlignment(Qt::AlignRight);
+ }
+ align = 0;
+ it = attr.find(QLatin1String("valign"));
+ if (it != end && ! (*it).isEmpty()) {
+ QString va = (*it).toLower();
+ if ( va == QLatin1String("top") )
+ align |= Qt::AlignTop;
+ else if ( va == QLatin1String("center") || va == QLatin1String("middle") )
+ align |= Qt::AlignVCenter;
+ else if (va == QLatin1String("bottom"))
+ align |= Qt::AlignBottom;
+ }
+ richtext->setFormatter(table->parent->formatter());
+ richtext->setUseFormatCollection(table->parent->useFormatCollection());
+ richtext->setMimeSourceFactory(&factory);
+ richtext->setStyleSheet(sheet);
+ richtext->setRichText(doc, context, &fmt);
+ rowspan_ = 1;
+ colspan_ = 1;
+
+ it = attr.find(QLatin1String("colspan"));
+ if (it != end)
+ colspan_ = (*it).toInt();
+ it = attr.find(QLatin1String("rowspan"));
+ if (it != end)
+ rowspan_ = (*it).toInt();
+
+ background = 0;
+ it = attr.find(QLatin1String("bgcolor"));
+ if (it != end) {
+ background = new QBrush(QColor(*it));
+ }
+
+ hasFixedWidth = false;
+ it = attr.find(QLatin1String("width"));
+ if (it != end) {
+ bool b;
+ QString s(*it);
+ int w = s.toInt(&b);
+ if (b) {
+ maxw = w;
+ minw = maxw;
+ hasFixedWidth = true;
+ } else {
+ s = s.trimmed();
+ if (s.length() > 1 && s[(int)s.length()-1] == QLatin1Char('%'))
+ stretch_ = s.left(s.length()-1).toInt();
+ }
+ }
+
+ attributes = attr;
+
+ parent->addCell(this);
+}
+
+Q3TextTableCell::~Q3TextTableCell()
+{
+ delete background;
+ background = 0;
+ delete richtext;
+ richtext = 0;
+}
+
+QSize Q3TextTableCell::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 Q3TextTableCell::minimumSize() const
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding + border_tolerance);
+ return QSize(qMax(richtext->minimumWidth() + extra, minw), 0);
+}
+
+QSize Q3TextTableCell::maximumSize() const
+{
+ return QSize(maxw, QWIDGETSIZE_MAX);
+}
+
+Qt::Orientations Q3TextTableCell::expandingDirections() const
+{
+ return Qt::Horizontal | Qt::Vertical;
+}
+
+bool Q3TextTableCell::isEmpty() const
+{
+ return false;
+}
+void Q3TextTableCell::setGeometry(const QRect& r)
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ if (r.width() != cached_width)
+ richtext->doLayout(Q3TextFormat::painter(), r.width() - extra);
+ cached_width = r.width();
+ geom = r;
+}
+
+QRect Q3TextTableCell::geometry() const
+{
+ return geom;
+}
+
+bool Q3TextTableCell::hasHeightForWidth() const
+{
+ return true;
+}
+
+int Q3TextTableCell::heightForWidth(int w) const
+{
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ w = qMax(minw, w);
+
+ if (cached_width != w) {
+ Q3TextTableCell* that = (Q3TextTableCell*) this;
+ that->richtext->doLayout(Q3TextFormat::painter(), w - extra);
+ that->cached_width = w;
+ }
+ return richtext->height() + extra;
+}
+
+void Q3TextTableCell::adjustToPainter(QPainter* p)
+{
+ Q3TextParagraph *parag = richtext->firstParagraph();
+ while (parag) {
+ parag->adjustToPainter(p);
+ parag = parag->next();
+ }
+}
+
+int Q3TextTableCell::horizontalAlignmentOffset() const
+{
+ return parent->cellpadding;
+}
+
+int Q3TextTableCell::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 Q3TextTableCell::draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool)
+{
+ if (cached_width != geom.width()) {
+ int extra = 2 * (parent->innerborder + parent->cellpadding);
+ richtext->doLayout(p, geom.width() - extra);
+ cached_width = geom.width();
+ }
+ QPalette pal2(pal);
+ if (background)
+ pal2.setBrush(QPalette::Base, *background);
+ else if (richtext->paper())
+ pal2.setBrush(QPalette::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;
+ if (cx >= 0 && cy >= 0)
+ richtext->draw(p, cx - (x + horizontalAlignmentOffset() + geom.x()),
+ cy - (y + geom.y() + verticalAlignmentOffset()),
+ cw, ch, pal2, false, false, 0);
+ else
+ richtext->draw(p, -1, -1, -1, -1, pal2, false, false, 0);
+
+ p->restore();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3richtext_p.cpp b/src/qt3support/text/q3richtext_p.cpp
new file mode 100644
index 0000000..6249f1b
--- /dev/null
+++ b/src/qt3support/text/q3richtext_p.cpp
@@ -0,0 +1,636 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3richtext_p.h"
+
+#ifndef QT_NO_RICHTEXT
+
+QT_BEGIN_NAMESPACE
+
+Q3TextCommand::~Q3TextCommand() {}
+Q3TextCommand::Commands Q3TextCommand::type() const { return Invalid; }
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+Q3TextCustomItem::~Q3TextCustomItem() {}
+void Q3TextCustomItem::adjustToPainter(QPainter* p){ if (p) width = 0; }
+Q3TextCustomItem::Placement Q3TextCustomItem::placement() const { return PlaceInline; }
+
+bool Q3TextCustomItem::ownLine() const { return false; }
+void Q3TextCustomItem::resize(int nwidth){ width = nwidth; }
+void Q3TextCustomItem::invalidate() {}
+
+bool Q3TextCustomItem::isNested() const { return false; }
+int Q3TextCustomItem::minimumWidth() const { return 0; }
+
+QString Q3TextCustomItem::richText() const { return QString(); }
+
+bool Q3TextCustomItem::enter(Q3TextCursor *, Q3TextDocument*&, Q3TextParagraph *&, int &, int &, int &, bool)
+{
+ return true;
+}
+bool Q3TextCustomItem::enterAt(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &, const QPoint &)
+{
+ return true;
+}
+bool Q3TextCustomItem::next(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::prev(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::down(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+bool Q3TextCustomItem::up(Q3TextCursor *, Q3TextDocument *&, Q3TextParagraph *&, int &, int &, int &)
+{
+ return true;
+}
+#endif // QT_NO_TEXTCUSTOMITEM
+
+void Q3TextFlow::setPageSize(int ps) { pagesize = ps; }
+#ifndef QT_NO_TEXTCUSTOMITEM
+bool Q3TextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
+#else
+bool Q3TextFlow::isEmpty() { return true; }
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+void Q3TextTableCell::invalidate() { cached_width = -1; cached_sizehint = -1; }
+
+void Q3TextTable::invalidate() { cachewidth = -1; }
+#endif
+
+Q3TextParagraphData::~Q3TextParagraphData() {}
+void Q3TextParagraphData::join(Q3TextParagraphData *) {}
+
+Q3TextFormatter::~Q3TextFormatter() {}
+void Q3TextFormatter::setWrapEnabled(bool b) { wrapEnabled = b; }
+void Q3TextFormatter::setWrapAtColumn(int c) { wrapColumn = c; }
+
+
+
+int Q3TextCursor::x() const
+{
+ if (idx >= para->length())
+ return 0;
+ Q3TextStringChar *c = para->at(idx);
+ int curx = c->x;
+ if (!c->rightToLeft &&
+ c->c.isSpace() &&
+ idx > 0 &&
+ para->at(idx - 1)->c != QLatin1Char('\t') &&
+ !c->lineStart &&
+ (para->alignment() & Qt::AlignJustify) == Qt::AlignJustify)
+ curx = para->at(idx - 1)->x + para->string()->width(idx - 1);
+ if (c->rightToLeft)
+ curx += para->string()->width(idx);
+ return curx;
+}
+
+int Q3TextCursor::y() const
+{
+ int dummy, line;
+ para->lineStartOfChar(idx, &dummy, &line);
+ return para->lineY(line);
+}
+
+int Q3TextCursor::globalX() const { return totalOffsetX() + para->rect().x() + x(); }
+int Q3TextCursor::globalY() const { return totalOffsetY() + para->rect().y() + y(); }
+
+Q3TextDocument *Q3TextCursor::document() const
+{
+ return para ? para->document() : 0;
+}
+
+void Q3TextCursor::gotoPosition(Q3TextParagraph* p, int index)
+{
+ if (para && p != para) {
+ while (!indices.isEmpty() && para->document() != p->document())
+ pop();
+ Q_ASSERT(indices.isEmpty() || para->document() == p->document());
+ }
+ para = p;
+ if (index < 0 || index >= para->length()) {
+ qWarning("Q3TextCursor::gotoParagraph Index: %d out of range", index);
+ if (index < 0 || para->length() == 0)
+ index = 0;
+ else
+ index = para->length() - 1;
+ }
+
+ tmpX = -1;
+ idx = index;
+ fixCursorPosition();
+}
+
+bool Q3TextDocument::hasSelection(int id, bool visible) const
+{
+ return (selections.find(id) != selections.end() &&
+ (!visible ||
+ ((Q3TextDocument*)this)->selectionStartCursor(id) !=
+ ((Q3TextDocument*)this)->selectionEndCursor(id)));
+}
+
+void Q3TextDocument::setSelectionStart(int id, const Q3TextCursor &cursor)
+{
+ Q3TextDocumentSelection sel;
+ sel.startCursor = cursor;
+ sel.endCursor = cursor;
+ sel.swapped = false;
+ selections[id] = sel;
+}
+
+Q3TextParagraph *Q3TextDocument::paragAt(int i) const
+{
+ Q3TextParagraph* p = curParag;
+ if (!p || p->paragId() > i)
+ p = fParag;
+ while (p && p->paragId() != i)
+ p = p->next();
+ ((Q3TextDocument*)this)->curParag = p;
+ return p;
+}
+
+
+Q3TextFormat::~Q3TextFormat()
+{
+}
+
+Q3TextFormat::Q3TextFormat()
+ : fm(QFontMetrics(fn)), linkColor(true), logicalFontSize(3), stdSize(qApp->font().pointSize())
+{
+ ref = 0;
+
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = true;
+ }
+
+ missp = false;
+ ha = AlignNormal;
+ collection = 0;
+}
+
+Q3TextFormat::Q3TextFormat(const Q3StyleSheetItem *style)
+ : fm(QFontMetrics(fn)), linkColor(true), logicalFontSize(3), stdSize(qApp->font().pointSize())
+{
+ ref = 0;
+
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = qApp->font().pixelSize();
+ usePixelSizes = true;
+ }
+
+ missp = false;
+ ha = AlignNormal;
+ collection = 0;
+ fn = QFont(style->fontFamily(),
+ style->fontSize(),
+ style->fontWeight(),
+ style->fontItalic());
+ fn.setUnderline(style->fontUnderline());
+ fn.setStrikeOut(style->fontStrikeOut());
+ col = style->color();
+ fm = QFontMetrics(fn);
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = false;
+ ha = AlignNormal;
+ memset(widths, 0, 256);
+ generateKey();
+ addRef();
+}
+
+Q3TextFormat::Q3TextFormat(const QFont &f, const QColor &c, Q3TextFormatCollection *parent)
+ : fn(f), col(c), fm(QFontMetrics(f)), linkColor(true),
+ logicalFontSize(3), stdSize(f.pointSize())
+{
+ ref = 0;
+ usePixelSizes = false;
+ if (stdSize == -1) {
+ stdSize = f.pixelSize();
+ usePixelSizes = true;
+ }
+ collection = parent;
+ leftBearing = fm.minLeftBearing();
+ rightBearing = fm.minRightBearing();
+ hei = fm.lineSpacing();
+ asc = fm.ascent() + (fm.leading()+1)/2;
+ dsc = fm.descent();
+ missp = false;
+ ha = AlignNormal;
+ memset(widths, 0, 256);
+ generateKey();
+ addRef();
+}
+
+Q3TextFormat::Q3TextFormat(const Q3TextFormat &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;
+ addRef();
+}
+
+Q3TextFormat& Q3TextFormat::operator=(const Q3TextFormat &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;
+ addRef();
+ return *this;
+}
+
+void Q3TextFormat::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();
+}
+
+
+QPainter* Q3TextFormat::pntr = 0;
+QFontMetrics* Q3TextFormat::pntr_fm = 0;
+int Q3TextFormat::pntr_ldg=-1;
+int Q3TextFormat::pntr_asc=-1;
+int Q3TextFormat::pntr_hei=-1;
+int Q3TextFormat::pntr_dsc=-1;
+
+void Q3TextFormat::setPainter(QPainter *p)
+{
+ pntr = p;
+}
+
+QPainter* Q3TextFormat::painter()
+{
+ return pntr;
+}
+
+void Q3TextFormat::applyFont(const QFont &f)
+{
+ QFontMetrics fm(pntr->fontMetrics());
+ if (!pntr_fm || pntr->font() != f) {
+ pntr->setFont(f);
+ delete pntr_fm;
+ pntr_fm = new QFontMetrics(pntr->fontMetrics());
+ pntr_ldg = pntr_fm->leading();
+ pntr_asc = pntr_fm->ascent()+(pntr_ldg+1)/2;
+ pntr_hei = pntr_fm->lineSpacing();
+ pntr_dsc = -1;
+ }
+}
+
+int Q3TextFormat::minLeftBearing() const
+{
+ if (!pntr || !pntr->isActive())
+ return leftBearing;
+ applyFont(fn);
+ return pntr_fm->minLeftBearing();
+}
+
+int Q3TextFormat::minRightBearing() const
+{
+ if (!pntr || !pntr->isActive())
+ return rightBearing;
+ applyFont(fn);
+ return pntr_fm->minRightBearing();
+}
+
+int Q3TextFormat::height() const
+{
+ if (!pntr || !pntr->isActive())
+ return hei;
+ applyFont(fn);
+ return pntr_hei;
+}
+
+int Q3TextFormat::ascent() const
+{
+ if (!pntr || !pntr->isActive())
+ return asc;
+ applyFont(fn);
+ return pntr_asc;
+}
+
+int Q3TextFormat::descent() const
+{
+ if (!pntr || !pntr->isActive())
+ return dsc;
+ applyFont(fn);
+ if (pntr_dsc < 0)
+ pntr_dsc = pntr_fm->descent();
+ return pntr_dsc;
+}
+
+int Q3TextFormat::leading() const
+{
+ if (!pntr || !pntr->isActive())
+ return fm.leading();
+ applyFont(fn);
+ return pntr_ldg;
+}
+
+void Q3TextFormat::generateKey()
+{
+ k = getKey(fn, col, isMisspelled(), vAlign());
+}
+
+QString Q3TextFormat::getKey(const QFont &fn, const QColor &col, bool misspelled, VerticalAlignment a)
+{
+ QString k = fn.key();
+ k += QLatin1Char('/');
+ k += QString::number((uint)col.rgb());
+ k += QLatin1Char('/');
+ k += QString::number((int)misspelled);
+ k += QLatin1Char('/');
+ k += QString::number((int)a);
+ return k;
+}
+
+QString Q3TextString::toString(const QVector<Q3TextStringChar> &data)
+{
+ QString s;
+ int l = data.size();
+ s.setUnicode(0, l);
+ const Q3TextStringChar *c = data.data();
+ QChar *uc = (QChar *)s.unicode();
+ while (l--)
+ *(uc++) = (c++)->c;
+
+ return s;
+}
+
+void Q3TextParagraph::setSelection(int id, int start, int end)
+{
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = selections().constFind(id);
+ if (it != mSelections->constEnd()) {
+ if (start == (*it).start && end == (*it).end)
+ return;
+ }
+
+ Q3TextParagraphSelection sel;
+ sel.start = start;
+ sel.end = end;
+ (*mSelections)[id] = sel;
+ setChanged(true, true);
+}
+
+void Q3TextParagraph::removeSelection(int id)
+{
+ if (!hasSelection(id))
+ return;
+ if (mSelections)
+ mSelections->remove(id);
+ setChanged(true, true);
+}
+
+int Q3TextParagraph::selectionStart(int id) const
+{
+ if (!mSelections)
+ return -1;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return -1;
+ return (*it).start;
+}
+
+int Q3TextParagraph::selectionEnd(int id) const
+{
+ if (!mSelections)
+ return -1;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return -1;
+ return (*it).end;
+}
+
+bool Q3TextParagraph::hasSelection(int id) const
+{
+ return mSelections ? mSelections->contains(id) : false;
+}
+
+bool Q3TextParagraph::fullSelected(int id) const
+{
+ if (!mSelections)
+ return false;
+ QMap<int, Q3TextParagraphSelection>::ConstIterator it = mSelections->constFind(id);
+ if (it == mSelections->constEnd())
+ return false;
+ return (*it).start == 0 && (*it).end == str->length() - 1;
+}
+
+int Q3TextParagraph::lineY(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineY: line %d out of range!", l);
+ return 0;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->y;
+}
+
+int Q3TextParagraph::lineBaseLine(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineBaseLine: line %d out of range!", l);
+ return 10;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->baseLine;
+}
+
+int Q3TextParagraph::lineHeight(int l) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineHeight: line %d out of range!", l);
+ return 15;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ return (*it)->h;
+}
+
+void Q3TextParagraph::lineInfo(int l, int &y, int &h, int &bl) const
+{
+ if (l > (int)lineStarts.count() - 1) {
+ qWarning("Q3TextParagraph::lineInfo: line %d out of range!", l);
+ qDebug("%d %d", (int)lineStarts.count() - 1, l);
+ y = 0;
+ h = 15;
+ bl = 10;
+ return;
+ }
+
+ if (!isValid())
+ ((Q3TextParagraph*)this)->format();
+
+ QMap<int, QTextLineStart*>::ConstIterator it = lineStarts.begin();
+ while (l-- > 0)
+ ++it;
+ y = (*it)->y;
+ h = (*it)->h;
+ bl = (*it)->baseLine;
+}
+
+
+void Q3TextParagraph::setAlignment(int a)
+{
+ if (a == (int)align)
+ return;
+ align = a;
+ invalidate(0);
+}
+
+Q3TextFormatter *Q3TextParagraph::formatter() const
+{
+ if (hasdoc)
+ return document()->formatter();
+ if (pseudoDocument()->pFormatter)
+ return pseudoDocument()->pFormatter;
+ return (((Q3TextParagraph*)this)->pseudoDocument()->pFormatter = new Q3TextFormatterBreakWords);
+}
+
+void Q3TextParagraph::setTabArray(int *a)
+{
+ delete [] tArray;
+ tArray = a;
+}
+
+void Q3TextParagraph::setTabStops(int tw)
+{
+ if (hasdoc)
+ document()->setTabStops(tw);
+ else
+ tabStopWidth = tw;
+}
+
+QMap<int, Q3TextParagraphSelection> &Q3TextParagraph::selections() const
+{
+ if (!mSelections)
+ ((Q3TextParagraph *)this)->mSelections = new QMap<int, Q3TextParagraphSelection>;
+ return *mSelections;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+QList<Q3TextCustomItem *> &Q3TextParagraph::floatingItems() const
+{
+ if (!mFloatingItems)
+ ((Q3TextParagraph *)this)->mFloatingItems = new QList<Q3TextCustomItem *>;
+ return *mFloatingItems;
+}
+#endif
+
+Q3TextStringChar::~Q3TextStringChar()
+{
+ if (format())
+ format()->removeRef();
+ if (type) // not Regular
+ delete p.custom;
+}
+
+Q3TextParagraphPseudoDocument::Q3TextParagraphPseudoDocument():pFormatter(0),commandHistory(0), minw(0),wused(0),collection(){}
+Q3TextParagraphPseudoDocument::~Q3TextParagraphPseudoDocument(){ delete pFormatter; delete commandHistory; }
+
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3richtext_p.h b/src/qt3support/text/q3richtext_p.h
new file mode 100644
index 0000000..2248e52
--- /dev/null
+++ b/src/qt3support/text/q3richtext_p.h
@@ -0,0 +1,2102 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3RICHTEXT_P_H
+#define Q3RICHTEXT_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.
+//
+
+#include "QtGui/qapplication.h"
+#include "QtGui/qcolor.h"
+#include "QtCore/qhash.h"
+#include "QtGui/qfont.h"
+#include "QtGui/qfontmetrics.h"
+#include "QtGui/qlayout.h"
+#include "QtCore/qmap.h"
+#include "QtCore/qvector.h"
+#include "QtCore/qstack.h"
+#include "QtCore/qlist.h"
+#include "QtCore/qobject.h"
+#include "QtGui/qpainter.h"
+#include "QtGui/qpixmap.h"
+#include "QtCore/qrect.h"
+#include "QtCore/qsize.h"
+#include "QtCore/qstring.h"
+#include "QtCore/qstringlist.h"
+#include "Qt3Support/q3stylesheet.h"
+#include "Qt3Support/q3mimefactory.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_RICHTEXT
+
+class Q3TextDocument;
+class Q3TextString;
+class Q3TextPreProcessor;
+class Q3TextFormat;
+class Q3TextCursor;
+class Q3TextParagraph;
+class Q3TextFormatter;
+class Q3TextIndent;
+class Q3TextFormatCollection;
+class Q3StyleSheetItem;
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextCustomItem;
+#endif
+class Q3TextFlow;
+struct QBidiContext;
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextStringChar
+{
+ friend class Q3TextString;
+
+public:
+ // this is never called, initialize variables in Q3TextString::insert()!!!
+ Q3TextStringChar() : nobreak(false), lineStart(0), type(Regular) {p.format=0;}
+ ~Q3TextStringChar();
+
+ struct CustomData
+ {
+ Q3TextFormat *format;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem *custom;
+#endif
+ QString anchorName;
+ QString anchorHref;
+ };
+ enum Type { Regular=0, Custom=1, Anchor=2, CustomAnchor=3 };
+
+ QChar c;
+ // this is the same struct as in qtextengine_p.h. Don't change!
+ uchar softBreak :1; // Potential linebreak point
+ uchar whiteSpace :1; // A unicode whitespace character, except NBSP, ZWNBSP
+ uchar charStop :1; // Valid cursor position (for left/right arrow)
+ uchar nobreak :1;
+
+ uchar lineStart : 1;
+ uchar /*Type*/ type : 2;
+ uchar bidiLevel :7;
+ uchar rightToLeft : 1;
+
+ int x;
+ union {
+ Q3TextFormat* format;
+ CustomData* custom;
+ } p;
+
+
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ bool isCustom() const { return (type & Custom) != 0; }
+ Q3TextFormat *format() const;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem *customItem() const;
+#endif
+ void setFormat(Q3TextFormat *f);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void setCustomItem(Q3TextCustomItem *i);
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void loseCustomItem();
+#endif
+
+
+ bool isAnchor() const { return (type & Anchor) != 0; }
+ bool isLink() const { return isAnchor() && p.custom->anchorHref.count(); }
+ QString anchorName() const;
+ QString anchorHref() const;
+ void setAnchor(const QString& name, const QString& href);
+
+ Q3TextStringChar(const Q3TextStringChar &) {
+ Q_ASSERT(false);
+ }
+private:
+ Q3TextStringChar &operator=(const Q3TextStringChar &) {
+ //abort();
+ return *this;
+ }
+ friend class Q3TextParagraph;
+};
+
+Q_DECLARE_TYPEINFO(Q3TextStringChar, Q_PRIMITIVE_TYPE);
+
+class Q_COMPAT_EXPORT Q3TextString
+{
+public:
+
+ Q3TextString();
+ Q3TextString(const Q3TextString &s);
+ virtual ~Q3TextString();
+
+ static QString toString(const QVector<Q3TextStringChar> &data);
+ QString toString() const;
+
+ inline Q3TextStringChar &at(int i) const {
+ return const_cast<Q3TextString *>(this)->data[i];
+ }
+ inline int length() const { return data.size(); }
+
+ int width(int idx) const;
+
+ void insert(int index, const QString &s, Q3TextFormat *f);
+ void insert(int index, const QChar *unicode, int len, Q3TextFormat *f);
+ void insert(int index, Q3TextStringChar *c, bool doAddRefFormat = false);
+ void truncate(int index);
+ void remove(int index, int len);
+ void clear();
+
+ void setFormat(int index, Q3TextFormat *f, bool useCollection);
+
+ void setBidi(bool b) { bidi = b; }
+ bool isBidi() const;
+ bool isRightToLeft() const;
+ QChar::Direction direction() const;
+ void setDirection(QChar::Direction dr) { dir = dr; bidiDirty = true; }
+
+ QVector<Q3TextStringChar> rawData() const { return data; }
+
+ void operator=(const QString &s) { clear(); insert(0, s, 0); }
+ void operator+=(const QString &s) { insert(length(), s, 0); }
+ void prepend(const QString &s) { insert(0, s, 0); }
+ int appendParagraphs( Q3TextParagraph *start, Q3TextParagraph *end );
+
+ // return next and previous valid cursor positions.
+ bool validCursorPosition(int idx);
+ int nextCursorPosition(int idx);
+ int previousCursorPosition(int idx);
+
+private:
+ void checkBidi() const;
+
+ QVector<Q3TextStringChar> data;
+ QString stringCache;
+ uint bidiDirty : 1;
+ uint bidi : 1; // true when the paragraph has right to left characters
+ uint rightToLeft : 1;
+ uint dir : 5;
+};
+
+inline bool Q3TextString::isBidi() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return bidi;
+}
+
+inline bool Q3TextString::isRightToLeft() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return rightToLeft;
+}
+
+inline QString Q3TextString::toString() const
+{
+ if (bidiDirty)
+ checkBidi();
+ return stringCache;
+}
+
+inline QChar::Direction Q3TextString::direction() const
+{
+ return rightToLeft ? QChar::DirR : QChar::DirL;
+}
+
+inline int Q3TextString::nextCursorPosition(int next)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ const Q3TextStringChar *c = data.data();
+ int len = length();
+
+ if (next < len - 1) {
+ next++;
+ while (next < len - 1 && !c[next].charStop)
+ next++;
+ }
+ return next;
+}
+
+inline int Q3TextString::previousCursorPosition(int prev)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ const Q3TextStringChar *c = data.data();
+
+ if (prev) {
+ prev--;
+ while (prev && !c[prev].charStop)
+ prev--;
+ }
+ return prev;
+}
+
+inline bool Q3TextString::validCursorPosition(int idx)
+{
+ if (bidiDirty)
+ checkBidi();
+
+ return (at(idx).charStop);
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextCursor
+{
+public:
+ Q3TextCursor(Q3TextDocument * = 0);
+ Q3TextCursor(const Q3TextCursor &c);
+ Q3TextCursor &operator=(const Q3TextCursor &c);
+ virtual ~Q3TextCursor();
+
+ bool operator==(const Q3TextCursor &c) const;
+ bool operator!=(const Q3TextCursor &c) const { return !(*this == c); }
+
+ inline Q3TextParagraph *paragraph() const { return para; }
+
+ Q3TextDocument *document() const;
+ int index() const;
+
+ void gotoPosition(Q3TextParagraph* p, int index = 0);
+ void setIndex(int index) { gotoPosition(paragraph(), index); }
+ void setParagraph(Q3TextParagraph*p) { gotoPosition(p, 0); }
+
+ void gotoLeft();
+ void gotoRight();
+ void gotoNextLetter();
+ void gotoPreviousLetter();
+ void gotoUp();
+ void gotoDown();
+ void gotoLineEnd();
+ void gotoLineStart();
+ void gotoHome();
+ void gotoEnd();
+ void gotoPageUp(int visibleHeight);
+ void gotoPageDown(int visibleHeight);
+ void gotoNextWord(bool onlySpace = false);
+ void gotoPreviousWord(bool onlySpace = false);
+ void gotoWordLeft();
+ void gotoWordRight();
+
+ void insert(const QString &s, bool checkNewLine, QVector<Q3TextStringChar> *formatting = 0);
+ void splitAndInsertEmptyParagraph(bool ind = true, bool updateIds = true);
+ bool remove();
+ bool removePreviousChar();
+ void indent();
+
+ bool atParagStart();
+ bool atParagEnd();
+
+ int x() const; // x in current paragraph
+ int y() const; // y in current paragraph
+
+ int globalX() const;
+ int globalY() const;
+
+ Q3TextParagraph *topParagraph() const { return paras.isEmpty() ? para : paras.first(); }
+ int offsetX() const { return ox; } // inner document offset
+ int offsetY() const { return oy; } // inner document offset
+ int totalOffsetX() const; // total document offset
+ int totalOffsetY() const; // total document offset
+
+ bool place(const QPoint &pos, Q3TextParagraph *s) { return place(pos, s, false); }
+ bool place(const QPoint &pos, Q3TextParagraph *s, bool link);
+ void restoreState();
+
+
+ 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; }
+
+ void fixCursorPosition();
+private:
+ enum Operation { EnterBegin, EnterEnd, Next, Prev, Up, Down };
+
+ void push();
+ void pop();
+ bool processNesting(Operation op);
+ void invalidateNested();
+ void gotoIntoNested(const QPoint &globalPos);
+
+ Q3TextParagraph *para;
+ int idx, tmpX;
+ int ox, oy;
+ QStack<int> indices;
+ QStack<Q3TextParagraph*> paras;
+ QStack<int> xOffsets;
+ QStack<int> yOffsets;
+ uint valid : 1;
+
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextCommand
+{
+public:
+ enum Commands { Invalid, Insert, Delete, Format, Style };
+
+ Q3TextCommand(Q3TextDocument *dc) : doc(dc), cursor(dc) {}
+ virtual ~Q3TextCommand();
+
+ virtual Commands type() const;
+
+ virtual Q3TextCursor *execute(Q3TextCursor *c) = 0;
+ virtual Q3TextCursor *unexecute(Q3TextCursor *c) = 0;
+
+protected:
+ Q3TextDocument *doc;
+ Q3TextCursor cursor;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextCommandHistory
+{
+public:
+ Q3TextCommandHistory(int s) : current(-1), steps(s) { }
+ virtual ~Q3TextCommandHistory(); // ### why is it virtual?
+
+ void clear();
+
+ void addCommand(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c);
+ Q3TextCursor *redo(Q3TextCursor *c);
+
+ bool isUndoAvailable();
+ bool isRedoAvailable();
+
+ void setUndoDepth(int depth) { steps = depth; }
+ int undoDepth() const { return steps; }
+
+ int historySize() const { return history.count(); }
+ int currentPosition() const { return current; }
+
+private:
+ QList<Q3TextCommand *> history;
+ int current, steps;
+};
+
+inline Q3TextCommandHistory::~Q3TextCommandHistory()
+{
+ clear();
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextCustomItem
+{
+public:
+ Q3TextCustomItem(Q3TextDocument *p)
+ : xpos(0), ypos(-1), width(-1), height(0), parent(p)
+ {}
+ virtual ~Q3TextCustomItem();
+ virtual void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, 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(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd = false);
+ virtual bool enterAt(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &);
+ virtual bool next(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool prev(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool down(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool up(Q3TextCursor *, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+
+ void setParagraph(Q3TextParagraph *p) { parag = p; }
+ Q3TextParagraph *paragraph() const { return parag; }
+
+ Q3TextDocument *parent;
+ Q3TextParagraph *parag;
+
+ virtual void pageBreak(int y, Q3TextFlow* flow);
+};
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextImage : public Q3TextCustomItem
+{
+public:
+ Q3TextImage(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory);
+ virtual ~Q3TextImage();
+
+ 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 QPalette &pal, bool selected);
+
+private:
+ QRegion* reg;
+ QPixmap pm;
+ Placement place;
+ int tmpwidth, tmpheight;
+ QMap<QString, QString> attributes;
+ QString imgId;
+
+};
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextHorizontalLine : public Q3TextCustomItem
+{
+public:
+ Q3TextHorizontalLine(Q3TextDocument *p, const QMap<QString, QString> &attr, const QString& context,
+ Q3MimeSourceFactory &factory);
+ virtual ~Q3TextHorizontalLine();
+
+ void adjustToPainter(QPainter*);
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, bool selected);
+ QString richText() const;
+
+ bool ownLine() const { return true; }
+
+private:
+ int tmpheight;
+ QColor color;
+ bool shade;
+
+};
+#endif
+
+class Q_COMPAT_EXPORT Q3TextFlow
+{
+ friend class Q3TextDocument;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ friend class Q3TextTableCell;
+#endif
+
+public:
+ Q3TextFlow();
+ virtual ~Q3TextFlow();
+
+ 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);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ virtual void registerFloatingItem(Q3TextCustomItem* item);
+ virtual void unregisterFloatingItem(Q3TextCustomItem* item);
+#endif
+ virtual QRect boundingRect() const;
+ virtual void drawFloatingItems(QPainter* p, int cx, int cy, int cw, int ch,
+ const QPalette &pal, 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;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> leftItems;
+ QList<Q3TextCustomItem *> rightItems;
+#endif
+};
+
+inline int Q3TextFlow::width() const { return w; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextTable;
+
+class Q_COMPAT_EXPORT Q3TextTableCell : public QLayoutItem
+{
+ friend class Q3TextTable;
+
+public:
+ Q3TextTableCell(Q3TextTable* table,
+ int row, int column,
+ const QMap<QString, QString> &attr,
+ const Q3StyleSheetItem* style,
+ const Q3TextFormat& fmt, const QString& context,
+ Q3MimeSourceFactory &factory, Q3StyleSheet *sheet, const QString& doc);
+ virtual ~Q3TextTableCell();
+
+ QSize sizeHint() const ;
+ QSize minimumSize() const ;
+ QSize maximumSize() const ;
+ Qt::Orientations expandingDirections() 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_; }
+
+ Q3TextDocument* richText() const { return richtext; }
+ Q3TextTable* table() const { return parent; }
+
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &cg, bool selected);
+
+ QBrush *backGround() const { return background; }
+ virtual void invalidate();
+
+ int verticalAlignmentOffset() const;
+ int horizontalAlignmentOffset() const;
+
+private:
+ QRect geom;
+ Q3TextTable* parent;
+ Q3TextDocument* 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;
+};
+#endif
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q_COMPAT_EXPORT Q3TextTable: public Q3TextCustomItem
+{
+ friend class Q3TextTableCell;
+
+public:
+ Q3TextTable(Q3TextDocument *p, const QMap<QString, QString> &attr);
+ virtual ~Q3TextTable();
+
+ void adjustToPainter(QPainter *p);
+ void pageBreak(int y, Q3TextFlow* flow);
+ void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch,
+ const QPalette &pal, 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(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, bool atEnd = false);
+ virtual bool enterAt(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy, const QPoint &pos);
+ virtual bool next(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool prev(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool down(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+ virtual bool up(Q3TextCursor *c, Q3TextDocument *&doc, Q3TextParagraph *&parag, int &idx, int &ox, int &oy);
+
+ QString richText() const;
+
+ int minimumWidth() const;
+
+ QList<Q3TextTableCell *> tableCells() const { return cells; }
+
+ bool isStretching() const { return stretch; }
+
+private:
+ void format(int w);
+ void addCell(Q3TextTableCell* cell);
+
+private:
+ QGridLayout* layout;
+ QList<Q3TextTableCell *> 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;
+ int us_fixwidth;
+ QMap<QString, QString> attributes;
+ QMap<Q3TextCursor*, int> currCell;
+ Placement place;
+ void adjustCells(int y , int shift);
+ int pageBreakFor;
+};
+#endif
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextTableCell;
+class Q3TextParagraph;
+#endif
+
+struct Q_COMPAT_EXPORT Q3TextDocumentSelection
+{
+ Q3TextCursor startCursor, endCursor;
+ bool swapped;
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextDocumentSelection)
+};
+
+class Q_COMPAT_EXPORT Q3TextDocument : public QObject
+{
+ Q_OBJECT
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ friend class Q3TextTableCell;
+#endif
+ friend class Q3TextCursor;
+ friend class Q3TextEdit;
+ friend class Q3TextParagraph;
+
+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()
+ };
+
+ Q3TextDocument(Q3TextDocument *p);
+ virtual ~Q3TextDocument();
+
+ Q3TextDocument *parent() const { return par; }
+ Q3TextParagraph *parentParagraph() const { return parentPar; }
+
+ void setText(const QString &text, const QString &context);
+ QMap<QString, QString> attributes() const { return attribs; }
+ void setAttributes(const QMap<QString, QString> &attr) { attribs = attr; }
+
+ QString text() const;
+ QString text(int parag) const;
+ QString originalText() const;
+
+ int x() const;
+ int y() const;
+ int width() const;
+ int widthUsed() const;
+ int visibleWidth() const;
+ int height() const;
+ void setWidth(int w);
+ int minimumWidth() const;
+ bool setMinimumWidth(int needed, int used = -1, Q3TextParagraph *parag = 0);
+
+ void setY(int y);
+ int leftMargin() const;
+ void setLeftMargin(int lm);
+ int rightMargin() const;
+ void setRightMargin(int rm);
+
+ Q3TextParagraph *firstParagraph() const;
+ Q3TextParagraph *lastParagraph() const;
+ void setFirstParagraph(Q3TextParagraph *p);
+ void setLastParagraph(Q3TextParagraph *p);
+
+ void invalidate();
+
+ void setPreProcessor(Q3TextPreProcessor *sh);
+ Q3TextPreProcessor *preProcessor() const;
+
+ void setFormatter(Q3TextFormatter *f);
+ Q3TextFormatter *formatter() const;
+
+ void setIndent(Q3TextIndent *i);
+ Q3TextIndent *indent() const;
+
+ QColor selectionColor(int id) const;
+ QColor selectionTextColor(int id) const;
+ bool hasSelectionTextColor(int id) const;
+ void setSelectionColor(int id, const QColor &c);
+ void setSelectionTextColor(int id, const QColor &b);
+ bool hasSelection(int id, bool visible = false) const;
+ void setSelectionStart(int id, const Q3TextCursor &cursor);
+ bool setSelectionEnd(int id, const Q3TextCursor &cursor);
+ void selectAll(int id);
+ bool removeSelection(int id);
+ void selectionStart(int id, int &paragId, int &index);
+ Q3TextCursor selectionStartCursor(int id);
+ Q3TextCursor selectionEndCursor(int id);
+ void selectionEnd(int id, int &paragId, int &index);
+ void setFormat(int id, Q3TextFormat *f, int flags);
+ int numSelections() const { return nSelections; }
+ void addSelection(int id);
+
+ QString selectedText(int id, bool asRichText = false) const;
+ void removeSelectedText(int id, Q3TextCursor *cursor);
+ void indentSelection(int id);
+
+ Q3TextParagraph *paragAt(int i) const;
+
+ void addCommand(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c = 0);
+ Q3TextCursor *redo(Q3TextCursor *c = 0);
+ Q3TextCommandHistory *commands() const { return commandHistory; }
+
+ Q3TextFormatCollection *formatCollection() const;
+
+ bool find(Q3TextCursor &cursor, const QString &expr, bool cs, bool wo, bool forward);
+
+ void setTextFormat(Qt::TextFormat f);
+ Qt::TextFormat textFormat() const;
+
+ bool inSelection(int selId, const QPoint &pos) const;
+
+ Q3StyleSheet *styleSheet() const { return sheet_; }
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory *mimeSourceFactory() const { return factory_; }
+#endif
+ QString context() const { return contxt; }
+
+ void setStyleSheet(Q3StyleSheet *s);
+ void setDefaultFormat(const QFont &font, const QColor &color);
+#ifndef QT_NO_MIME
+ void setMimeSourceFactory(Q3MimeSourceFactory *f) { if (f) factory_ = f; }
+#endif
+ void setContext(const QString &c) { if (!c.isEmpty()) contxt = c; }
+
+ void setUnderlineLinks(bool b);
+ bool underlineLinks() const { return underlLinks; }
+
+ void setPaper(QBrush *brush) { if (backBrush) delete backBrush; backBrush = brush; }
+ QBrush *paper() const { return backBrush; }
+
+ void doLayout(QPainter *p, int w);
+ void draw(QPainter *p, const QRect& rect, const QPalette &pal, const QBrush *paper = 0);
+
+ void drawParagraph(QPainter *p, Q3TextParagraph *parag, int cx, int cy, int cw, int ch,
+ QPixmap *&doubleBuffer, const QPalette &pal,
+ bool drawCursor, Q3TextCursor *cursor, bool resetChanged = true);
+ Q3TextParagraph *draw(QPainter *p, int cx, int cy, int cw, int ch, const QPalette &pal,
+ bool onlyChanged = false, bool drawCursor = false, Q3TextCursor *cursor = 0,
+ bool resetChanged = true);
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ static Q3TextCustomItem* tag(Q3StyleSheet *sheet, const QString& name,
+ const QMap<QString, QString> &attr,
+ const QString& context,
+ const Q3MimeSourceFactory& factory,
+ bool emptyTag, Q3TextDocument *doc);
+#endif
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void registerCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p);
+ void unregisterCustomItem(Q3TextCustomItem *i, Q3TextParagraph *p);
+#endif
+
+ void setFlow(Q3TextFlow *f);
+ void takeFlow();
+ Q3TextFlow *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; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tableCell() const { return tc; }
+ void setTableCell(Q3TextTableCell *c) { tc = c; }
+#endif
+
+ void setPlainText(const QString &text);
+ void setRichText(const QString &text, const QString &context, const Q3TextFormat *initialFormat = 0);
+ QString richText() const;
+ QString plainText() const;
+
+ bool focusNextPrevChild(bool next);
+
+ int alignment() const;
+ void setAlignment(int a);
+
+ int *tabArray() const;
+ int tabStopWidth() const;
+ void setTabArray(int *a);
+ void setTabStops(int tw);
+
+ void setUndoDepth(int depth) { commandHistory->setUndoDepth(depth); }
+ int undoDepth() const { return commandHistory->undoDepth(); }
+
+ int length() const;
+ void clear(bool createEmptyParag = false);
+
+ virtual Q3TextParagraph *createParagraph(Q3TextDocument *, Q3TextParagraph *pr = 0, Q3TextParagraph *nx = 0, bool updateIds = true);
+ void insertChild(Q3TextDocument *dc) { childList.append(dc); }
+ void removeChild(Q3TextDocument *dc) { childList.removeAll(dc); }
+ QList<Q3TextDocument *> children() const { return childList; }
+
+ bool hasFocusParagraph() const;
+ QString focusHref() const;
+ QString focusName() const;
+
+ void invalidateOriginalText() { oTextValid = false; oText = QLatin1String(""); }
+
+Q_SIGNALS:
+ void minimumWidthChanged(int);
+
+private:
+ Q_DISABLE_COPY(Q3TextDocument)
+
+ 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);
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextCustomItem* parseTable(const QMap<QString, QString> &attr, const Q3TextFormat &fmt,
+ const QChar* doc, int length, int& pos, Q3TextParagraph *curpar);
+#endif
+ 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, Q3StyleSheetItem::WhiteSpaceMode wsm);
+ void setRichTextInternal(const QString &text, Q3TextCursor* cursor = 0, const Q3TextFormat *initialFormat = 0);
+ void setRichTextMarginsInternal(QList< QVector<Q3StyleSheetItem *> *>& styles, Q3TextParagraph* stylesPar);
+
+ struct Q_COMPAT_EXPORT Focus {
+ Q3TextParagraph *parag;
+ int start, len;
+ QString href;
+ QString name;
+ };
+
+ int cx, cy, cw, vw;
+ Q3TextParagraph *fParag, *lParag;
+ Q3TextPreProcessor *pProcessor;
+ struct SelectionColor {
+ QColor background;
+ QColor text;
+ };
+ QMap<int, SelectionColor> selectionColors;
+ QMap<int, Q3TextDocumentSelection> selections;
+ Q3TextCommandHistory *commandHistory;
+ Q3TextFormatter *pFormatter;
+ Q3TextIndent *indenter;
+ Q3TextFormatCollection *fCollection;
+ Qt::TextFormat txtFormat;
+ uint preferRichText : 1;
+ uint pages : 1;
+ uint useFC : 1;
+ uint withoutDoubleBuffer : 1;
+ uint underlLinks : 1;
+ uint nextDoubleBuffered : 1;
+ uint oTextValid : 1;
+ uint mightHaveCustomItems : 1;
+ int align;
+ int nSelections;
+ Q3TextFlow *flow_;
+ Q3TextDocument *par;
+ Q3TextParagraph *parentPar;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tc;
+#endif
+ QBrush *backBrush;
+ QPixmap *buf_pixmap;
+ Focus focusIndicator;
+ int minw;
+ int wused;
+ int leftmargin;
+ int rightmargin;
+ Q3TextParagraph *minwParag, *curParag;
+ Q3StyleSheet* sheet_;
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory* factory_;
+#endif
+ QString contxt;
+ QMap<QString, QString> attribs;
+ int *tArray;
+ int tStopWidth;
+ int uDepth;
+ QString oText;
+ QList<Q3TextDocument *> childList;
+ QColor linkColor, bodyText;
+ double scaleFontsFactor;
+
+ short list_tm,list_bm, list_lm, li_tm, li_bm, par_tm, par_bm;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+class Q_COMPAT_EXPORT Q3TextDeleteCommand : public Q3TextCommand
+{
+public:
+ Q3TextDeleteCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyle);
+ Q3TextDeleteCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str);
+ virtual ~Q3TextDeleteCommand();
+
+ Commands type() const { return Delete; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+protected:
+ int id, index;
+ Q3TextParagraph *parag;
+ QVector<Q3TextStringChar> text;
+ QByteArray styleInformation;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextInsertCommand : public Q3TextDeleteCommand
+{
+public:
+ Q3TextInsertCommand(Q3TextDocument *dc, int i, int idx, const QVector<Q3TextStringChar> &str,
+ const QByteArray& oldStyleInfo)
+ : Q3TextDeleteCommand(dc, i, idx, str, oldStyleInfo) {}
+ Q3TextInsertCommand(Q3TextParagraph *p, int idx, const QVector<Q3TextStringChar> &str)
+ : Q3TextDeleteCommand(p, idx, str) {}
+ virtual ~Q3TextInsertCommand() {}
+
+ Commands type() const { return Insert; }
+ Q3TextCursor *execute(Q3TextCursor *c) { return Q3TextDeleteCommand::unexecute(c); }
+ Q3TextCursor *unexecute(Q3TextCursor *c) { return Q3TextDeleteCommand::execute(c); }
+
+};
+
+class Q_COMPAT_EXPORT Q3TextFormatCommand : public Q3TextCommand
+{
+public:
+ Q3TextFormatCommand(Q3TextDocument *dc, int sid, int sidx, int eid, int eidx, const QVector<Q3TextStringChar> &old, Q3TextFormat *f, int fl);
+ virtual ~Q3TextFormatCommand();
+
+ Commands type() const { return Format; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+protected:
+ int startId, startIndex, endId, endIndex;
+ Q3TextFormat *format;
+ QVector<Q3TextStringChar> oldFormats;
+ int flags;
+
+};
+
+class Q_COMPAT_EXPORT Q3TextStyleCommand : public Q3TextCommand
+{
+public:
+ Q3TextStyleCommand(Q3TextDocument *dc, int fParag, int lParag, const QByteArray& beforeChange );
+ virtual ~Q3TextStyleCommand() {}
+
+ Commands type() const { return Style; }
+ Q3TextCursor *execute(Q3TextCursor *c);
+ Q3TextCursor *unexecute(Q3TextCursor *c);
+
+ static QByteArray readStyleInformation( Q3TextDocument* dc, int fParag, int lParag);
+ static void writeStyleInformation( Q3TextDocument* dc, int fParag, const QByteArray& style);
+
+private:
+ int firstParag, lastParag;
+ QByteArray before;
+ QByteArray after;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+struct Q_COMPAT_EXPORT Q3TextParagraphSelection
+{
+ int start, end;
+ Q_DUMMY_COMPARISON_OPERATOR(Q3TextParagraphSelection)
+};
+
+struct Q_COMPAT_EXPORT QTextLineStart
+{
+ QTextLineStart() : y(0), baseLine(0), h(0)
+ { }
+ QTextLineStart(int y_, int bl, int h_) : y(y_), baseLine(bl), h(h_),
+ w(0)
+ { }
+
+public:
+ int y, baseLine, h;
+ int w;
+};
+
+class Q_COMPAT_EXPORT Q3TextParagraphData
+{
+public:
+ Q3TextParagraphData() {}
+ virtual ~Q3TextParagraphData();
+ virtual void join(Q3TextParagraphData *);
+};
+
+class Q3TextParagraphPseudoDocument;
+
+class Q3SyntaxHighlighter;
+
+class Q_COMPAT_EXPORT Q3TextParagraph
+{
+ friend class Q3TextDocument;
+ friend class Q3TextCursor;
+ friend class Q3SyntaxHighlighter;
+
+public:
+ Q3TextParagraph(Q3TextDocument *dc, Q3TextParagraph *pr = 0, Q3TextParagraph *nx = 0, bool updateIds = true);
+ ~Q3TextParagraph();
+
+ Q3TextString *string() const;
+ Q3TextStringChar *at(int i) const; // maybe remove later
+ int leftGap() const;
+ int length() const; // maybe remove later
+
+ void setListStyle(Q3StyleSheetItem::ListStyle ls) { lstyle = ls; changed = true; }
+ Q3StyleSheetItem::ListStyle listStyle() const { return (Q3StyleSheetItem::ListStyle)lstyle; }
+ void setListItem(bool li);
+ bool isListItem() const { return litem; }
+ void setListValue(int v) { list_val = v; }
+ int listValue() const { return list_val > 0 ? list_val : -1; }
+
+ void setListDepth(int depth);
+ int listDepth() const { return ldepth; }
+
+// void setFormat(Q3TextFormat *fm);
+// Q3TextFormat *paragFormat() const;
+
+ inline Q3TextDocument *document() const {
+ if (hasdoc) return (Q3TextDocument*) docOrPseudo;
+ return 0;
+ }
+ Q3TextParagraphPseudoDocument *pseudoDocument() const;
+
+ QRect rect() const;
+ void setHeight(int h) { r.setHeight(h); }
+ void show();
+ void hide();
+ bool isVisible() const { return visible; }
+
+ Q3TextParagraph *prev() const;
+ Q3TextParagraph *next() const;
+ void setPrev(Q3TextParagraph *s);
+ void setNext(Q3TextParagraph *s);
+
+ void insert(int index, const QString &s);
+ void insert(int index, const QChar *unicode, int len);
+ void append(const QString &s, bool reallyAtEnd = false);
+ void truncate(int index);
+ void remove(int index, int len);
+ void join(Q3TextParagraph *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;
+ Q3TextStringChar *lineStartOfChar(int i, int *index = 0, int *line = 0) const;
+ int lines() const;
+ Q3TextStringChar *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(Q3TextParagraphData *data);
+ Q3TextParagraphData *extraData() const;
+
+ QMap<int, QTextLineStart*> &lineStartList();
+
+ void setFormat(int index, int len, Q3TextFormat *f, bool useCollection = true, int flags = -1);
+
+ void setAlignment(int a);
+ int alignment() const;
+
+ void paint(QPainter &painter, const QPalette &pal, Q3TextCursor *cursor = 0,
+ bool drawSelections = false, int clipx = -1, int clipy = -1,
+ int clipw = -1, int cliph = -1);
+
+ int topMargin() const;
+ int bottomMargin() const;
+ int leftMargin() const;
+ int firstLineMargin() const;
+ int rightMargin() const;
+ int lineSpacing() const;
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ void registerFloatingItem(Q3TextCustomItem *i);
+ void unregisterFloatingItem(Q3TextCustomItem *i);
+#endif
+
+ void setFullWidth(bool b) { fullWidth = b; }
+ bool isFullWidth() const { return fullWidth; }
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+ Q3TextTableCell *tableCell() const;
+#endif
+
+ QBrush *background() const;
+
+ int documentWidth() const;
+ int documentVisibleWidth() const;
+ int documentX() const;
+ int documentY() const;
+ Q3TextFormatCollection *formatCollection() const;
+ Q3TextFormatter *formatter() const;
+
+ 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(Q3TextCommand *cmd);
+ Q3TextCursor *undo(Q3TextCursor *c = 0);
+ Q3TextCursor *redo(Q3TextCursor *c = 0);
+ Q3TextCommandHistory *commands() const;
+ void copyParagData(Q3TextParagraph *parag);
+
+ void setBreakable(bool b) { breakable = b; }
+ bool isBreakable() const { return breakable; }
+
+ void setBackgroundColor(const QColor &c);
+ QColor *backgroundColor() const { return bgcol; }
+ void clearBackgroundColor();
+
+ void setMovedDown(bool b) { movedDown = b; }
+ bool wasMovedDown() const { return movedDown; }
+
+ void setDirection(QChar::Direction);
+ QChar::Direction direction() const;
+ void setPaintDevice(QPaintDevice *pd) { paintdevice = pd; }
+
+ void readStyleInformation(QDataStream& stream);
+ void writeStyleInformation(QDataStream& stream) const;
+
+protected:
+ void setColorForSelection(QColor &c, QPainter &p, const QPalette &pal, int selection);
+ void drawLabel(QPainter* p, int x, int y, int w, int h, int base, const QPalette &pal);
+ void drawString(QPainter &painter, const QString &str, int start, int len, int xstart,
+ int y, int baseLine, int w, int h, bool drawSelections, int fullSelectionWidth,
+ Q3TextStringChar *formatChar, const QPalette &pal,
+ bool rightToLeft);
+
+private:
+ QMap<int, Q3TextParagraphSelection> &selections() const;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> &floatingItems() const;
+#endif
+ inline QBrush backgroundBrush(const QPalette &pal) {
+ if (bgcol)
+ return *bgcol;
+ return pal.brush(QPalette::Base);
+ }
+ void invalidateStyleCache();
+
+ QMap<int, QTextLineStart*> lineStarts;
+ QRect r;
+ Q3TextParagraph *p, *n;
+ void *docOrPseudo;
+ uint changed : 1;
+ uint firstFormat : 1;
+ uint firstPProcess : 1;
+ uint needPreProcess : 1;
+ uint fullWidth : 1;
+ uint lastInFrame : 1;
+ uint visible : 1;
+ uint breakable : 1;
+ uint movedDown : 1;
+ uint mightHaveCustomItems : 1;
+ uint hasdoc : 1;
+ uint litem : 1; // whether the paragraph is a list item
+ uint rtext : 1; // whether the paragraph needs rich text margin
+ signed int align : 5;
+ uint /*Q3StyleSheetItem::ListStyle*/ lstyle : 4;
+ int invalid;
+ int state, id;
+ Q3TextString *str;
+ QMap<int, Q3TextParagraphSelection> *mSelections;
+#ifndef QT_NO_TEXTCUSTOMITEM
+ QList<Q3TextCustomItem *> *mFloatingItems;
+#endif
+ short utm, ubm, ulm, urm, uflm, ulinespacing;
+ short tabStopWidth, minwidth;
+ int *tArray;
+ Q3TextParagraphData *eData;
+ short list_val;
+ ushort ldepth;
+ QColor *bgcol;
+ QPaintDevice *paintdevice;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatter
+{
+public:
+ Q3TextFormatter();
+ virtual ~Q3TextFormatter();
+
+ virtual int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts) = 0;
+ virtual int formatVertically(Q3TextDocument* doc, Q3TextParagraph* parag);
+
+ bool isWrapEnabled(Q3TextParagraph *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 QTextLineStart *formatLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line, Q3TextStringChar *start,
+ Q3TextStringChar *last, int align = Qt::AlignAuto, int space = 0);
+#ifndef QT_NO_COMPLEXTEXT
+ virtual QTextLineStart *bidiReorderLine(Q3TextParagraph *parag, Q3TextString *string, QTextLineStart *line, Q3TextStringChar *start,
+ Q3TextStringChar *last, int align, int space);
+#endif
+ void insertLineStart(Q3TextParagraph *parag, int index, QTextLineStart *ls);
+
+ int thisminw;
+ int thiswused;
+
+private:
+ bool wrapEnabled;
+ int wrapColumn;
+ bool biw;
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatterBreakInWords : public Q3TextFormatter
+{
+public:
+ Q3TextFormatterBreakInWords();
+ virtual ~Q3TextFormatterBreakInWords() {}
+
+ int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts);
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatterBreakWords : public Q3TextFormatter
+{
+public:
+ Q3TextFormatterBreakWords();
+ virtual ~Q3TextFormatterBreakWords() {}
+
+ int format(Q3TextDocument *doc, Q3TextParagraph *parag, int start, const QMap<int, QTextLineStart*> &oldLineStarts);
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextIndent
+{
+public:
+ Q3TextIndent();
+ virtual ~Q3TextIndent() {}
+
+ virtual void indent(Q3TextDocument *doc, Q3TextParagraph *parag, int *oldIndent = 0, int *newIndent = 0) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextPreProcessor
+{
+public:
+ enum Ids {
+ Standard = 0
+ };
+
+ Q3TextPreProcessor();
+ virtual ~Q3TextPreProcessor() {}
+
+ virtual void process(Q3TextDocument *doc, Q3TextParagraph *, int, bool = true) = 0;
+ virtual Q3TextFormat *format(int id) = 0;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormat
+{
+ friend class Q3TextFormatCollection;
+ friend class Q3TextDocument;
+
+public:
+ enum Flags {
+ NoFlags,
+ Bold = 1,
+ Italic = 2,
+ Underline = 4,
+ Family = 8,
+ Size = 16,
+ Color = 32,
+ Misspelled = 64,
+ VAlign = 128,
+ StrikeOut= 256,
+ Font = Bold | Italic | Underline | Family | Size | StrikeOut,
+ Format = Font | Color | Misspelled | VAlign
+ };
+
+ enum VerticalAlignment { AlignNormal, AlignSuperScript, AlignSubScript };
+
+ Q3TextFormat();
+ virtual ~Q3TextFormat();
+
+ Q3TextFormat(const Q3StyleSheetItem *s);
+ Q3TextFormat(const QFont &f, const QColor &c, Q3TextFormatCollection *parent = 0);
+ Q3TextFormat(const Q3TextFormat &fm);
+ Q3TextFormat makeTextFormat(const Q3StyleSheetItem *style, const QMap<QString,QString>& attr, double scaleFontsFactor) const;
+ Q3TextFormat& operator=(const Q3TextFormat &fm);
+ QColor color() const;
+ QFont font() const;
+ QFontMetrics fontMetrics() const { return fm; }
+ bool isMisspelled() const;
+ VerticalAlignment vAlign() const;
+ int minLeftBearing() const;
+ int minRightBearing() const;
+ int width(const QChar &c) const;
+ int width(const QString &str, int pos) const;
+ int height() const;
+ int ascent() const;
+ int descent() const;
+ int leading() const;
+ bool useLinkColor() const;
+
+ void setBold(bool b);
+ void setItalic(bool b);
+ void setUnderline(bool b);
+ void setStrikeOut(bool b);
+ void setFamily(const QString &f);
+ void setPointSize(int s);
+ void setFont(const QFont &f);
+ void setColor(const QColor &c);
+ void setMisspelled(bool b);
+ void setVAlign(VerticalAlignment a);
+
+ bool operator==(const Q3TextFormat &f) const;
+ Q3TextFormatCollection *parent() const;
+ const QString &key() const;
+
+ static QString getKey(const QFont &f, const QColor &c, bool misspelled, VerticalAlignment vAlign);
+
+ void addRef();
+ void removeRef();
+
+ QString makeFormatChangeTags(Q3TextFormat* defaultFormat, Q3TextFormat *f, const QString& oldAnchorHref, const QString& anchorHref) const;
+ QString makeFormatEndTags(Q3TextFormat* defaultFormat, const QString& anchorHref) const;
+
+ static void setPainter(QPainter *p);
+ static QPainter* painter();
+
+ bool fontSizesInPixels() { return usePixelSizes; }
+
+protected:
+ virtual void generateKey();
+
+private:
+ void update();
+ static void applyFont(const QFont &f);
+
+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;
+ Q3TextFormatCollection *collection;
+ int ref;
+ QString k;
+ int logicalFontSize;
+ int stdSize;
+ static QPainter *pntr;
+ static QFontMetrics *pntr_fm;
+ static int pntr_asc;
+ static int pntr_hei;
+ static int pntr_ldg;
+ static int pntr_dsc;
+
+};
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+class Q_COMPAT_EXPORT Q3TextFormatCollection
+{
+ friend class Q3TextDocument;
+ friend class Q3TextFormat;
+
+public:
+ Q3TextFormatCollection();
+ virtual ~Q3TextFormatCollection();
+
+ void setDefaultFormat(Q3TextFormat *f);
+ Q3TextFormat *defaultFormat() const;
+ virtual Q3TextFormat *format(Q3TextFormat *f);
+ virtual Q3TextFormat *format(Q3TextFormat *of, Q3TextFormat *nf, int flags);
+ virtual Q3TextFormat *format(const QFont &f, const QColor &c);
+ virtual void remove(Q3TextFormat *f);
+ virtual Q3TextFormat *createFormat(const Q3TextFormat &f) { return new Q3TextFormat(f); }
+ virtual Q3TextFormat *createFormat(const QFont &f, const QColor &c) { return new Q3TextFormat(f, c, this); }
+
+ void updateDefaultFormat(const QFont &font, const QColor &c, Q3StyleSheet *sheet);
+
+ QPaintDevice *paintDevice() const { return paintdevice; }
+ void setPaintDevice(QPaintDevice *);
+
+private:
+ void updateKeys();
+
+private:
+ Q3TextFormat *defFormat, *lastFormat, *cachedFormat;
+ QHash<QString, Q3TextFormat *> cKey;
+ Q3TextFormat *cres;
+ QFont cfont;
+ QColor ccol;
+ QString kof, knf;
+ int cflags;
+
+ QPaintDevice *paintdevice;
+};
+
+class Q_COMPAT_EXPORT Q3TextParagraphPseudoDocument
+{
+public:
+ Q3TextParagraphPseudoDocument();
+ ~Q3TextParagraphPseudoDocument();
+ QRect docRect;
+ Q3TextFormatter *pFormatter;
+ Q3TextCommandHistory *commandHistory;
+ int minw;
+ int wused;
+ Q3TextFormatCollection collection;
+};
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int Q3TextParagraph::length() const
+{
+ return str->length();
+}
+
+inline QRect Q3TextParagraph::rect() const
+{
+ return r;
+}
+
+inline int Q3TextCursor::index() const
+{
+ return idx;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline int Q3TextDocument::x() const
+{
+ return cx;
+}
+
+inline int Q3TextDocument::y() const
+{
+ return cy;
+}
+
+inline int Q3TextDocument::width() const
+{
+ return qMax(cw, flow_->width());
+}
+
+inline int Q3TextDocument::visibleWidth() const
+{
+ return vw;
+}
+
+inline Q3TextParagraph *Q3TextDocument::firstParagraph() const
+{
+ return fParag;
+}
+
+inline Q3TextParagraph *Q3TextDocument::lastParagraph() const
+{
+ return lParag;
+}
+
+inline void Q3TextDocument::setFirstParagraph(Q3TextParagraph *p)
+{
+ fParag = p;
+}
+
+inline void Q3TextDocument::setLastParagraph(Q3TextParagraph *p)
+{
+ lParag = p;
+}
+
+inline void Q3TextDocument::setWidth(int w)
+{
+ cw = qMax(w, minw);
+ flow_->setWidth(cw);
+ vw = w;
+}
+
+inline int Q3TextDocument::minimumWidth() const
+{
+ return minw;
+}
+
+inline void Q3TextDocument::setY(int y)
+{
+ cy = y;
+}
+
+inline int Q3TextDocument::leftMargin() const
+{
+ return leftmargin;
+}
+
+inline void Q3TextDocument::setLeftMargin(int lm)
+{
+ leftmargin = lm;
+}
+
+inline int Q3TextDocument::rightMargin() const
+{
+ return rightmargin;
+}
+
+inline void Q3TextDocument::setRightMargin(int rm)
+{
+ rightmargin = rm;
+}
+
+inline Q3TextPreProcessor *Q3TextDocument::preProcessor() const
+{
+ return pProcessor;
+}
+
+inline void Q3TextDocument::setPreProcessor(Q3TextPreProcessor * sh)
+{
+ pProcessor = sh;
+}
+
+inline void Q3TextDocument::setFormatter(Q3TextFormatter *f)
+{
+ delete pFormatter;
+ pFormatter = f;
+}
+
+inline Q3TextFormatter *Q3TextDocument::formatter() const
+{
+ return pFormatter;
+}
+
+inline void Q3TextDocument::setIndent(Q3TextIndent *i)
+{
+ indenter = i;
+}
+
+inline Q3TextIndent *Q3TextDocument::indent() const
+{
+ return indenter;
+}
+
+inline QColor Q3TextDocument::selectionColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors[id].background;
+}
+
+inline QColor Q3TextDocument::selectionTextColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors[id].text;
+}
+
+inline bool Q3TextDocument::hasSelectionTextColor(int id) const
+{
+ const Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ return p->selectionColors.contains(id);
+}
+
+inline void Q3TextDocument::setSelectionColor(int id, const QColor &c)
+{
+ Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ p->selectionColors[id].background = c;
+}
+
+inline void Q3TextDocument::setSelectionTextColor(int id, const QColor &c)
+{
+ Q3TextDocument *p = this;
+ while (p->par)
+ p = p->par;
+ p->selectionColors[id].text = c;
+}
+
+inline Q3TextFormatCollection *Q3TextDocument::formatCollection() const
+{
+ return fCollection;
+}
+
+inline int Q3TextDocument::alignment() const
+{
+ return align;
+}
+
+inline void Q3TextDocument::setAlignment(int a)
+{
+ align = a;
+}
+
+inline int *Q3TextDocument::tabArray() const
+{
+ return tArray;
+}
+
+inline int Q3TextDocument::tabStopWidth() const
+{
+ return tStopWidth;
+}
+
+inline void Q3TextDocument::setTabArray(int *a)
+{
+ tArray = a;
+}
+
+inline void Q3TextDocument::setTabStops(int tw)
+{
+ tStopWidth = tw;
+}
+
+inline QString Q3TextDocument::originalText() const
+{
+ if (oTextValid)
+ return oText;
+ return text();
+}
+
+inline void Q3TextDocument::setFlow(Q3TextFlow *f)
+{
+ if (flow_)
+ delete flow_;
+ flow_ = f;
+}
+
+inline void Q3TextDocument::takeFlow()
+{
+ flow_ = 0;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline QColor Q3TextFormat::color() const
+{
+ return col;
+}
+
+inline QFont Q3TextFormat::font() const
+{
+ return fn;
+}
+
+inline bool Q3TextFormat::isMisspelled() const
+{
+ return missp;
+}
+
+inline Q3TextFormat::VerticalAlignment Q3TextFormat::vAlign() const
+{
+ return ha;
+}
+
+inline bool Q3TextFormat::operator==(const Q3TextFormat &f) const
+{
+ return k == f.k;
+}
+
+inline Q3TextFormatCollection *Q3TextFormat::parent() const
+{
+ return collection;
+}
+
+inline void Q3TextFormat::addRef()
+{
+ ref++;
+}
+
+inline void Q3TextFormat::removeRef()
+{
+ ref--;
+ if (!collection)
+ return;
+ if (this == collection->defFormat)
+ return;
+ if (ref == 0)
+ collection->remove(this);
+}
+
+inline const QString &Q3TextFormat::key() const
+{
+ return k;
+}
+
+inline bool Q3TextFormat::useLinkColor() const
+{
+ return linkColor;
+}
+
+inline Q3TextStringChar *Q3TextParagraph::at(int i) const
+{
+ return &str->at(i);
+}
+
+inline bool Q3TextParagraph::isValid() const
+{
+ return invalid == -1;
+}
+
+inline bool Q3TextParagraph::hasChanged() const
+{
+ return changed;
+}
+
+inline void Q3TextParagraph::setBackgroundColor(const QColor & c)
+{
+ delete bgcol;
+ bgcol = new QColor(c);
+ setChanged(true);
+}
+
+inline void Q3TextParagraph::clearBackgroundColor()
+{
+ delete bgcol; bgcol = 0; setChanged(true);
+}
+
+inline void Q3TextParagraph::append(const QString &s, bool reallyAtEnd)
+{
+ if (reallyAtEnd) {
+ insert(str->length(), s);
+ } else {
+ int str_end = str->length() - 1;
+ insert(str_end > 0 ? str_end : 0, s);
+ }
+}
+
+inline Q3TextParagraph *Q3TextParagraph::prev() const
+{
+ return p;
+}
+
+inline Q3TextParagraph *Q3TextParagraph::next() const
+{
+ return n;
+}
+
+inline bool Q3TextParagraph::hasAnySelection() const
+{
+ return mSelections ? !selections().isEmpty() : false;
+}
+
+inline void Q3TextParagraph::setEndState(int s)
+{
+ if (s == state)
+ return;
+ state = s;
+}
+
+inline int Q3TextParagraph::endState() const
+{
+ return state;
+}
+
+inline void Q3TextParagraph::setParagId(int i)
+{
+ id = i;
+}
+
+inline int Q3TextParagraph::paragId() const
+{
+ if (id == -1)
+ qWarning("invalid parag id!!!!!!!! (%p)", (void*)this);
+ return id;
+}
+
+inline bool Q3TextParagraph::firstPreProcess() const
+{
+ return firstPProcess;
+}
+
+inline void Q3TextParagraph::setFirstPreProcess(bool b)
+{
+ firstPProcess = b;
+}
+
+inline QMap<int, QTextLineStart*> &Q3TextParagraph::lineStartList()
+{
+ return lineStarts;
+}
+
+inline Q3TextString *Q3TextParagraph::string() const
+{
+ return str;
+}
+
+inline Q3TextParagraphPseudoDocument *Q3TextParagraph::pseudoDocument() const
+{
+ if (hasdoc)
+ return 0;
+ return (Q3TextParagraphPseudoDocument*) docOrPseudo;
+}
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline Q3TextTableCell *Q3TextParagraph::tableCell() const
+{
+ return hasdoc ? document()->tableCell () : 0;
+}
+#endif
+
+inline Q3TextCommandHistory *Q3TextParagraph::commands() const
+{
+ return hasdoc ? document()->commands() : pseudoDocument()->commandHistory;
+}
+
+
+inline int Q3TextParagraph::alignment() const
+{
+ return align;
+}
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline void Q3TextParagraph::registerFloatingItem(Q3TextCustomItem *i)
+{
+ floatingItems().append(i);
+}
+
+inline void Q3TextParagraph::unregisterFloatingItem(Q3TextCustomItem *i)
+{
+ floatingItems().removeAll(i);
+}
+#endif
+
+inline QBrush *Q3TextParagraph::background() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return tableCell() ? tableCell()->backGround() : 0;
+#else
+ return 0;
+#endif
+}
+
+inline int Q3TextParagraph::documentWidth() const
+{
+ return hasdoc ? document()->width() : pseudoDocument()->docRect.width();
+}
+
+inline int Q3TextParagraph::documentVisibleWidth() const
+{
+ return hasdoc ? document()->visibleWidth() : pseudoDocument()->docRect.width();
+}
+
+inline int Q3TextParagraph::documentX() const
+{
+ return hasdoc ? document()->x() : pseudoDocument()->docRect.x();
+}
+
+inline int Q3TextParagraph::documentY() const
+{
+ return hasdoc ? document()->y() : pseudoDocument()->docRect.y();
+}
+
+inline void Q3TextParagraph::setExtraData(Q3TextParagraphData *data)
+{
+ eData = data;
+}
+
+inline Q3TextParagraphData *Q3TextParagraph::extraData() const
+{
+ return eData;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline void Q3TextFormatCollection::setDefaultFormat(Q3TextFormat *f)
+{
+ defFormat = f;
+}
+
+inline Q3TextFormat *Q3TextFormatCollection::defaultFormat() const
+{
+ return defFormat;
+}
+
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+inline Q3TextFormat *Q3TextStringChar::format() const
+{
+ return (type == Regular) ? p.format : p.custom->format;
+}
+
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+inline Q3TextCustomItem *Q3TextStringChar::customItem() const
+{
+ return isCustom() ? p.custom->custom : 0;
+}
+#endif
+
+inline int Q3TextStringChar::height() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->height() : (customItem()->placement() == Q3TextCustomItem::PlaceInline ? customItem()->height : 0);
+#else
+ return format()->height();
+#endif
+}
+
+inline int Q3TextStringChar::ascent() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->ascent() : (customItem()->placement() == Q3TextCustomItem::PlaceInline ? customItem()->ascent() : 0);
+#else
+ return format()->ascent();
+#endif
+}
+
+inline int Q3TextStringChar::descent() const
+{
+#ifndef QT_NO_TEXTCUSTOMITEM
+ return !isCustom() ? format()->descent() : 0;
+#else
+ return format()->descent();
+#endif
+}
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+#endif // Q3RICHTEXT_P_H
diff --git a/src/qt3support/text/q3simplerichtext.cpp b/src/qt3support/text/q3simplerichtext.cpp
new file mode 100644
index 0000000..5abf04a
--- /dev/null
+++ b/src/qt3support/text/q3simplerichtext.cpp
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3simplerichtext.h"
+
+#ifndef QT_NO_RICHTEXT
+#include "q3richtext_p.h"
+#include "qapplication.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SimpleRichTextData
+{
+public:
+ Q3TextDocument *doc;
+ QFont font;
+ int cachedWidth;
+ bool cachedWidthWithPainter;
+ void adjustSize();
+};
+
+// Pull this private function in from qglobal.cpp
+Q_CORE_EXPORT unsigned int qt_int_sqrt(unsigned int n);
+
+void Q3SimpleRichTextData::adjustSize() {
+ QFontMetrics fm(font);
+ int mw = fm.width(QString(QLatin1Char('x'))) * 80;
+ int w = mw;
+ doc->doLayout(0,w);
+ if (doc->widthUsed() != 0) {
+ w = qt_int_sqrt(5 * doc->height() * doc->widthUsed() / 3);
+ doc->doLayout(0, qMin(w, mw));
+
+ if (w*3 < 5*doc->height()) {
+ w = qt_int_sqrt(2 * doc->height() * doc->widthUsed());
+ doc->doLayout(0,qMin(w, mw));
+ }
+ }
+ cachedWidth = doc->width();
+ cachedWidthWithPainter = false;
+}
+
+/*!
+ \class Q3SimpleRichText
+ \brief The Q3SimpleRichText class provides a small displayable piece of rich text.
+
+ \compat
+
+ This class encapsulates simple rich text usage in which a string
+ is interpreted as rich text and can be drawn. This is particularly
+ useful if you want to display some rich text in a custom widget. A
+ Q3StyleSheet is needed to interpret the tags and format the rich
+ text. Qt provides a default HTML-like style sheet, but you may
+ define custom style sheets.
+
+ Once created, the rich text object can be queried for its width(),
+ height(), and the actual width used (see widthUsed()). Most
+ importantly, it can be drawn on any given QPainter with draw().
+ Q3SimpleRichText can also be used to implement hypertext or active
+ text facilities by using anchorAt(). A hit test through inText()
+ makes it possible to use simple rich text for text objects in
+ editable drawing canvases.
+
+ Once constructed from a string the contents cannot be changed,
+ only resized. If the contents change, just throw the rich text
+ object away and make a new one with the new contents.
+
+ For large documents use QTextEdit or QTextBrowser. For very small
+ items of rich text you can use a QLabel.
+
+ If you are using Q3SimpleRichText to print in high resolution you
+ should call setWidth(QPainter, int) so that the content will be
+ laid out properly on the page.
+*/
+
+/*!
+ Constructs a Q3SimpleRichText from the rich text string \a text and
+ the font \a fnt.
+
+ The font is used as a basis for the text rendering. When using
+ rich text rendering on a widget \e w, you would normally specify
+ the widget's font, for example:
+
+ \snippet doc/src/snippets/code/src_qt3support_text_q3simplerichtext.cpp 0
+
+ \a context is the optional context of the rich text object. This
+ becomes important if \a text contains relative references, for
+ example within image tags. Q3SimpleRichText always uses the default
+ mime source factory (see \l{Q3MimeSourceFactory::defaultFactory()})
+ to resolve those references. The context will then be used to
+ calculate the absolute path. See
+ Q3MimeSourceFactory::makeAbsolute() for details.
+
+ The \a sheet is an optional style sheet. If it is 0, the default
+ style sheet will be used (see \l{Q3StyleSheet::defaultSheet()}).
+*/
+
+Q3SimpleRichText::Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet* sheet)
+{
+ d = new Q3SimpleRichTextData;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->font = fnt;
+ d->doc = new Q3TextDocument(0);
+ d->doc->setTextFormat(Qt::RichText);
+ d->doc->setLeftMargin(0);
+ d->doc->setRightMargin(0);
+ d->doc->setFormatter(new Q3TextFormatterBreakWords);
+ d->doc->setStyleSheet((Q3StyleSheet*)sheet);
+ d->doc->setDefaultFormat(fnt, QColor());
+ d->doc->setText(text, context);
+}
+
+
+/*!
+ Constructs a Q3SimpleRichText from the rich text string \a text and
+ the font \a fnt.
+
+ This is a slightly more complex constructor for Q3SimpleRichText
+ that takes an additional mime source factory \a factory, a page
+ break parameter \a pageBreak and a bool \a linkUnderline. \a
+ linkColor is only provided for compatibility, but has no effect,
+ as QPalette::link() color is used now.
+
+ \a context is the optional context of the rich text object. This
+ becomes important if \a text contains relative references, for
+ example within image tags. Q3SimpleRichText always uses the default
+ mime source factory (see \l{Q3MimeSourceFactory::defaultFactory()})
+ to resolve those references. The context will then be used to
+ calculate the absolute path. See
+ Q3MimeSourceFactory::makeAbsolute() for details.
+
+ The \a sheet is an optional style sheet. If it is 0, the default
+ style sheet will be used (see \l{Q3StyleSheet::defaultSheet()}).
+
+ This constructor is useful for creating a Q3SimpleRichText object
+ suitable for printing. Set \a pageBreak to be the height of the
+ contents area of the pages.
+*/
+
+Q3SimpleRichText::Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet* sheet,
+ const Q3MimeSourceFactory* factory, int pageBreak,
+ const QColor& /*linkColor*/, bool linkUnderline)
+{
+ d = new Q3SimpleRichTextData;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->font = fnt;
+ d->doc = new Q3TextDocument(0);
+ d->doc->setTextFormat(Qt::RichText);
+ d->doc->setFormatter(new Q3TextFormatterBreakWords);
+ d->doc->setStyleSheet((Q3StyleSheet*)sheet);
+ d->doc->setDefaultFormat(fnt, QColor());
+ d->doc->flow()->setPageSize(pageBreak);
+ d->doc->setPageBreakEnabled(true);
+#ifndef QT_NO_MIME
+ d->doc->setMimeSourceFactory((Q3MimeSourceFactory*)factory);
+#endif
+ d->doc->setUnderlineLinks(linkUnderline);
+ d->doc->setText(text, context);
+}
+
+/*!
+ Destroys the rich text object, freeing memory.
+*/
+
+Q3SimpleRichText::~Q3SimpleRichText()
+{
+ delete d->doc;
+ delete d;
+}
+
+/*!
+ \overload
+
+ Sets the width of the rich text object to \a w pixels.
+
+ \sa height(), adjustSize()
+*/
+
+void Q3SimpleRichText::setWidth(int w)
+{
+ if (w == d->cachedWidth && !d->cachedWidthWithPainter)
+ return;
+ d->doc->formatter()->setAllowBreakInWords(d->doc->isPageBreakEnabled());
+ d->cachedWidth = w;
+ d->cachedWidthWithPainter = false;
+ d->doc->doLayout(0, w);
+}
+
+/*!
+ Sets the width of the rich text object to \a w pixels,
+ recalculating the layout as if it were to be drawn with painter \a
+ p.
+
+ Passing a painter is useful when you intend drawing on devices
+ other than the screen, for example a QPrinter.
+
+ \sa height(), adjustSize()
+*/
+
+void Q3SimpleRichText::setWidth(QPainter *p, int w)
+{
+ if (w == d->cachedWidth && d->cachedWidthWithPainter)
+ return;
+ d->doc->formatter()->setAllowBreakInWords(d->doc->isPageBreakEnabled() ||
+ (p && p->device() &&
+ p->device()->devType() == QInternal::Printer));
+ p->save();
+ d->cachedWidth = w;
+ d->cachedWidthWithPainter = true;
+ d->doc->doLayout(p, w);
+ p->restore();
+}
+
+/*!
+ Returns the set width of the rich text object in pixels.
+
+ \sa widthUsed()
+*/
+
+int Q3SimpleRichText::width() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->width();
+}
+
+/*!
+ Returns the width in pixels that is actually used by the rich text
+ object. This can be smaller or wider than the set width.
+
+ It may be wider, for example, if the text contains images or
+ non-breakable words that are already wider than the available
+ space. It's smaller when the object only consists of lines that do
+ not fill the width completely.
+
+ \sa width()
+*/
+
+int Q3SimpleRichText::widthUsed() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->widthUsed();
+}
+
+/*!
+ Returns the height of the rich text object in pixels.
+
+ \sa setWidth()
+*/
+
+int Q3SimpleRichText::height() const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ return d->doc->height();
+}
+
+/*!
+ Adjusts the rich text object to a reasonable size.
+
+ \sa setWidth()
+*/
+
+void Q3SimpleRichText::adjustSize()
+{
+ d->adjustSize();
+}
+
+/*!
+ Draws the formatted text with painter \a p, at position (\a x, \a
+ y), clipped to \a clipRect. The clipping rectangle is given in the
+ rich text object's coordinates translated by (\a x, \a y). Passing
+ an null rectangle results in no clipping. Colors from the color
+ group \a cg are used as needed, and if not 0, *\a{paper} is
+ used as the background brush.
+
+ Note that the display code is highly optimized to reduce flicker,
+ so passing a brush for \a paper is preferable to simply clearing
+ the area to be painted and then calling this without a brush.
+*/
+
+void Q3SimpleRichText::draw(QPainter *p, int x, int y, const QRect& clipRect,
+ const QColorGroup &cg, const QBrush* paper) const
+{
+ p->save();
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ QRect r = clipRect;
+ if (!r.isNull())
+ r.moveBy(-x, -y);
+
+ if (paper)
+ d->doc->setPaper(new QBrush(*paper));
+ QPalette pal2 = cg;
+ if (d->doc->paper())
+ pal2.setBrush(QPalette::Base, *d->doc->paper());
+
+ if (!clipRect.isNull())
+ p->setClipRect(clipRect);
+ p->translate(x, y);
+ d->doc->draw(p, r, pal2, paper);
+ p->translate(-x, -y);
+ p->restore();
+}
+
+
+/*! \fn void Q3SimpleRichText::draw(QPainter *p, int x, int y, const QRegion& clipRegion,
+ const QColorGroup &cg, const QBrush* paper) const
+
+ Use the version with clipRect instead of this \a clipRegion version,
+ since this region version has problems with larger documents on some
+ platforms (on X11 regions internally are represented with 16-bit
+ coordinates).
+*/
+
+
+
+/*!
+ Returns the context of the rich text object. If no context has
+ been specified in the constructor, an empty string is returned. The
+ context is the path to use to look up relative links, such as
+ image tags and anchor references.
+*/
+
+QString Q3SimpleRichText::context() const
+{
+ return d->doc->context();
+}
+
+/*!
+ Returns the anchor at the requested position, \a pos. An empty
+ string is returned if no anchor is specified for this position.
+*/
+
+QString Q3SimpleRichText::anchorAt(const QPoint& pos) const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ Q3TextCursor c(d->doc);
+ c.place(pos, d->doc->firstParagraph(), true);
+ return c.paragraph()->at(c.index())->anchorHref();
+}
+
+/*!
+ Returns true if \a pos is within a text line of the rich text
+ object; otherwise returns false.
+*/
+
+bool Q3SimpleRichText::inText(const QPoint& pos) const
+{
+ if (d->cachedWidth < 0)
+ d->adjustSize();
+ if (pos.y() > d->doc->height())
+ return false;
+ Q3TextCursor c(d->doc);
+ c.place(pos, d->doc->firstParagraph());
+ return c.totalOffsetX() + c.paragraph()->at(c.index())->x +
+ c.paragraph()->at(c.index())->format()->width(c.paragraph()->at(c.index())->c) > pos.x();
+}
+
+/*!
+ Sets the default font for the rich text object to \a f
+*/
+
+void Q3SimpleRichText::setDefaultFont(const QFont &f)
+{
+ if (d->font == f)
+ return;
+ d->font = f;
+ d->cachedWidth = -1;
+ d->cachedWidthWithPainter = false;
+ d->doc->setDefaultFormat(f, QColor());
+ d->doc->setText(d->doc->originalText(), d->doc->context());
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3simplerichtext.h b/src/qt3support/text/q3simplerichtext.h
new file mode 100644
index 0000000..450ce62
--- /dev/null
+++ b/src/qt3support/text/q3simplerichtext.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SIMPLERICHTEXT_H
+#define Q3SIMPLERICHTEXT_H
+
+#include <QtCore/qnamespace.h>
+#include <QtCore/qstring.h>
+#include <QtGui/qregion.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_RICHTEXT
+
+class QPainter;
+class QWidget;
+class Q3StyleSheet;
+class QBrush;
+class Q3MimeSourceFactory;
+class Q3SimpleRichTextData;
+
+class Q_COMPAT_EXPORT Q3SimpleRichText
+{
+public:
+ Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context = QString(), const Q3StyleSheet* sheet = 0);
+ Q3SimpleRichText(const QString& text, const QFont& fnt,
+ const QString& context, const Q3StyleSheet *sheet,
+ const Q3MimeSourceFactory* factory, int pageBreak = -1,
+ const QColor& linkColor = Qt::blue, bool linkUnderline = true);
+ ~Q3SimpleRichText();
+
+ void setWidth(int);
+ void setWidth(QPainter*, int);
+ void setDefaultFont(const QFont &f);
+ int width() const;
+ int widthUsed() const;
+ int height() const;
+ void adjustSize();
+
+ void draw(QPainter* p, int x, int y, const QRect& clipRect,
+ const QColorGroup& cg, const QBrush* paper = 0) const;
+
+ void draw(QPainter* p, int x, int y, const QRegion& clipRegion,
+ const QColorGroup& cg, const QBrush* paper = 0) const {
+ draw(p, x, y, clipRegion.boundingRect(), cg, paper);
+ }
+
+ QString context() const;
+ QString anchorAt(const QPoint& pos) const;
+
+ bool inText(const QPoint& pos) const;
+
+private:
+ Q_DISABLE_COPY(Q3SimpleRichText)
+
+ Q3SimpleRichTextData* d;
+};
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SIMPLERICHTEXT_H
diff --git a/src/qt3support/text/q3stylesheet.cpp b/src/qt3support/text/q3stylesheet.cpp
new file mode 100644
index 0000000..ec39f5d
--- /dev/null
+++ b/src/qt3support/text/q3stylesheet.cpp
@@ -0,0 +1,1471 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3stylesheet.h"
+
+#ifndef QT_NO_RICHTEXT
+
+#include "qlayout.h"
+#include "qpainter.h"
+#include "q3cleanuphandler.h"
+#include <qtextdocument.h>
+
+#include <stdio.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3StyleSheetItemData
+{
+public:
+ Q3StyleSheetItem::DisplayMode disp;
+ int fontitalic;
+ int fontunderline;
+ int fontstrikeout;
+ int fontweight;
+ int fontsize;
+ int fontsizelog;
+ int fontsizestep;
+ int lineSpacing;
+ QString fontfamily;
+ Q3StyleSheetItem *parentstyle;
+ QString stylename;
+ int ncolumns;
+ QColor col;
+ bool anchor;
+ int align;
+ Q3StyleSheetItem::VerticalAlignment valign;
+ int margin[5];
+ Q3StyleSheetItem::ListStyle list;
+ Q3StyleSheetItem::WhiteSpaceMode whitespacemode;
+ QString contxt;
+ bool selfnest;
+ Q3StyleSheet* sheet;
+};
+
+/*!
+ \class Q3StyleSheetItem
+ \brief The Q3StyleSheetItem class provides an encapsulation of a set of text styles.
+
+ \compat
+
+ A style sheet item consists of a name and a set of attributes that
+ specify its font, color, etc. When used in a \link Q3StyleSheet
+ style sheet\endlink (see styleSheet()), items define the name() of
+ a rich text tag and the display property changes associated with
+ it.
+
+ The \link Q3StyleSheetItem::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
+ Q3StyleSheetItem::WhiteSpaceMode white space mode\endlink; see
+ setWhiteSpaceMode(). An item's margins are set with setMargin(),
+ In the case of list items, the list style is set with
+ setListStyle(). An item may be a hypertext link anchor; see
+ setAnchor(). Other attributes are set with setAlignment(),
+ setVerticalAlignment(), setFontFamily(), setFontSize(),
+ setFontWeight(), setFontItalic(), setFontUnderline(),
+ setFontStrikeOut and setColor().
+*/
+
+/*! \enum Q3StyleSheetItem::AdditionalStyleValues
+ \internal
+*/
+
+/*!
+ \enum Q3StyleSheetItem::WhiteSpaceMode
+
+ This enum defines the ways in which Q3StyleSheet can treat
+ whitespace.
+
+ \value WhiteSpaceNormal any sequence of whitespace (including
+ line-breaks) is equivalent to a single space.
+
+ \value WhiteSpacePre whitespace must be output exactly as given
+ in the input.
+
+ \value WhiteSpaceNoWrap multiple spaces are collapsed as with
+ WhiteSpaceNormal, but no automatic line-breaks occur. To break
+ lines manually, use the \c{<br>} tag.
+
+ \omitvalue WhiteSpaceModeUndefined
+*/
+
+/*!
+ \enum Q3StyleSheetItem::Margin
+
+ \value MarginLeft left margin
+ \value MarginRight right margin
+ \value MarginTop top margin
+ \value MarginBottom bottom margin
+ \value MarginAll all margins (left, right, top and bottom)
+ \value MarginVertical top and bottom margins
+ \value MarginHorizontal left and right margins
+ \value MarginFirstLine margin (indentation) of the first line of
+ a paragarph (in addition to the MarginLeft of the paragraph)
+ \value MarginUndefined
+*/
+
+/*!
+ Constructs a new style called \a name for the stylesheet \a
+ parent.
+
+ All properties in Q3StyleSheetItem are initially in the "do not
+ change" state, except \link Q3StyleSheetItem::DisplayMode display
+ mode\endlink, which defaults to \c DisplayInline.
+*/
+Q3StyleSheetItem::Q3StyleSheetItem(Q3StyleSheet* parent, const QString& name)
+{
+ d = new Q3StyleSheetItemData;
+ d->stylename = name.toLower();
+ 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.
+*/
+Q3StyleSheetItem::Q3StyleSheetItem(const Q3StyleSheetItem & other)
+{
+ d = new Q3StyleSheetItemData;
+ *d = *other.d;
+}
+
+
+/*!
+ Destroys the style. Note that Q3StyleSheetItem objects become
+ owned by Q3StyleSheet when they are created.
+*/
+Q3StyleSheetItem::~Q3StyleSheetItem()
+{
+ delete d;
+}
+
+/*!
+ Assignment. Assings a copy of \a other that is not bound to any style sheet.
+ Unbounds first from previous style sheet.
+ */
+Q3StyleSheetItem& Q3StyleSheetItem::operator=(const Q3StyleSheetItem& other)
+{
+ if (&other == this)
+ return *this;
+ delete d;
+ d = new Q3StyleSheetItemData;
+ *d = *other.d;
+ return *this;
+}
+
+/*!
+ Returns the style sheet this item is in.
+*/
+Q3StyleSheet* Q3StyleSheetItem::styleSheet()
+{
+ return d->sheet;
+}
+
+/*!
+ \overload
+
+ Returns the style sheet this item is in.
+*/
+const Q3StyleSheet* Q3StyleSheetItem::styleSheet() const
+{
+ return d->sheet;
+}
+
+/*!
+ \internal
+ Internal initialization
+ */
+void Q3StyleSheetItem::init()
+{
+ d->disp = DisplayInline;
+
+ d->fontitalic = Undefined;
+ d->fontunderline = Undefined;
+ d->fontstrikeout = Undefined;
+ d->fontweight = Undefined;
+ d->fontsize = Undefined;
+ d->fontsizelog = Undefined;
+ d->fontsizestep = 0;
+ d->ncolumns = Undefined;
+ d->col = QColor(); // !isValid()
+ d->anchor = false;
+ d->align = Undefined;
+ d->valign = VAlignBaseline;
+ d->margin[0] = Undefined;
+ d->margin[1] = Undefined;
+ d->margin[2] = Undefined;
+ d->margin[3] = Undefined;
+ d->margin[4] = Undefined;
+ d->list = ListStyleUndefined;
+ d->whitespacemode = WhiteSpaceModeUndefined;
+ d->selfnest = true;
+ d->lineSpacing = Undefined;
+}
+
+/*!
+ Returns the name of the style item.
+*/
+QString Q3StyleSheetItem::name() const
+{
+ return d->stylename;
+}
+
+/*!
+ Returns the \link Q3StyleSheetItem::DisplayMode display
+ mode\endlink of the style.
+
+ \sa setDisplayMode()
+*/
+Q3StyleSheetItem::DisplayMode Q3StyleSheetItem::displayMode() const
+{
+ return d->disp;
+}
+
+/*!
+ \enum Q3StyleSheetItem::DisplayMode
+
+ This enum type defines the way adjacent elements are displayed.
+
+ \value DisplayBlock elements are displayed as a rectangular block
+ (e.g. \c{<p>...</p>}).
+
+ \value DisplayInline elements are displayed in a horizontally
+ flowing sequence (e.g. \c{<em>...</em>}).
+
+ \value DisplayListItem elements are displayed in a vertical
+ sequence (e.g. \c{<li>...</li>}).
+
+ \value DisplayNone elements are not displayed at all.
+
+ \omitvalue DisplayModeUndefined
+*/
+
+/*!
+ Sets the display mode of the style to \a m.
+
+ \sa displayMode()
+ */
+void Q3StyleSheetItem::setDisplayMode(DisplayMode m)
+{
+ d->disp=m;
+}
+
+
+/*!
+ Returns the alignment of this style. Possible values are
+ Qt::AlignAuto, Qt::AlignLeft, Qt::AlignRight, Qt::AlignCenter or
+ Qt::AlignJustify.
+
+ \sa setAlignment(), Qt::Alignment
+*/
+int Q3StyleSheetItem::alignment() const
+{
+ return d->align;
+}
+
+/*!
+ Sets the alignment to \a f. This only makes sense for styles with
+ a \link Q3StyleSheetItem::DisplayMode display mode\endlink of
+ DisplayBlock. Possible values are Qt::AlignAuto, Qt::AlignLeft,
+ Qt::AlignRight, Qt::AlignCenter or Qt::AlignJustify.
+
+ \sa alignment(), displayMode(), Qt::Alignment
+*/
+void Q3StyleSheetItem::setAlignment(int f)
+{
+ d->align = f;
+}
+
+
+/*!
+ Returns the vertical alignment of the style. Possible values are
+ VAlignBaseline, VAlignSub or VAlignSuper.
+
+ \sa setVerticalAlignment()
+*/
+Q3StyleSheetItem::VerticalAlignment Q3StyleSheetItem::verticalAlignment() const
+{
+ return d->valign;
+}
+
+/*!
+ \enum Q3StyleSheetItem::VerticalAlignment
+
+ This enum type defines the way elements are aligned vertically.
+ This is only supported for text elements.
+
+ \value VAlignBaseline align the baseline of the element (or the
+ bottom, if the element doesn't have a baseline) with the
+ baseline of the parent
+
+ \value VAlignSub subscript the element
+
+ \value VAlignSuper superscript the element
+
+*/
+
+
+/*!
+ Sets the vertical alignment to \a valign. Possible values are
+ VAlignBaseline, VAlignSub or VAlignSuper.
+
+ The vertical alignment property is not inherited.
+
+ \sa verticalAlignment()
+*/
+void Q3StyleSheetItem::setVerticalAlignment(VerticalAlignment valign)
+{
+ d->valign = valign;
+}
+
+
+/*!
+ Returns true if the style sets an italic font; otherwise returns
+ false.
+
+ \sa setFontItalic(), definesFontItalic()
+*/
+bool Q3StyleSheetItem::fontItalic() const
+{
+ return d->fontitalic > 0;
+}
+
+/*!
+ If \a italic is true sets italic for the style; otherwise sets
+ upright.
+
+ \sa fontItalic(), definesFontItalic()
+*/
+void Q3StyleSheetItem::setFontItalic(bool italic)
+{
+ d->fontitalic = italic?1:0;
+}
+
+/*!
+ Returns true if the style defines a font shape; otherwise returns
+ false. A style does not define any shape until setFontItalic() is
+ called.
+
+ \sa setFontItalic(), fontItalic()
+*/
+bool Q3StyleSheetItem::definesFontItalic() const
+{
+ return d->fontitalic != Undefined;
+}
+
+/*!
+ Returns true if the style sets an underlined font; otherwise
+ returns false.
+
+ \sa setFontUnderline(), definesFontUnderline()
+*/
+bool Q3StyleSheetItem::fontUnderline() const
+{
+ return d->fontunderline > 0;
+}
+
+/*!
+ If \a underline is true, sets underline for the style; otherwise
+ sets no underline.
+
+ \sa fontUnderline(), definesFontUnderline()
+*/
+void Q3StyleSheetItem::setFontUnderline(bool underline)
+{
+ d->fontunderline = underline?1:0;
+}
+
+/*!
+ Returns true if the style defines a setting for the underline
+ property of the font; otherwise returns false. A style does not
+ define this until setFontUnderline() is called.
+
+ \sa setFontUnderline(), fontUnderline()
+*/
+bool Q3StyleSheetItem::definesFontUnderline() const
+{
+ return d->fontunderline != Undefined;
+}
+
+
+/*!
+ Returns true if the style sets a strike out font; otherwise
+ returns false.
+
+ \sa setFontStrikeOut(), definesFontStrikeOut()
+*/
+bool Q3StyleSheetItem::fontStrikeOut() const
+{
+ return d->fontstrikeout > 0;
+}
+
+/*!
+ If \a strikeOut is true, sets strike out for the style; otherwise
+ sets no strike out.
+
+ \sa fontStrikeOut(), definesFontStrikeOut()
+*/
+void Q3StyleSheetItem::setFontStrikeOut(bool strikeOut)
+{
+ d->fontstrikeout = strikeOut?1:0;
+}
+
+/*!
+ Returns true if the style defines a setting for the strikeOut
+ property of the font; otherwise returns false. A style does not
+ define this until setFontStrikeOut() is called.
+
+ \sa setFontStrikeOut(), fontStrikeOut()
+*/
+bool Q3StyleSheetItem::definesFontStrikeOut() const
+{
+ return d->fontstrikeout != Undefined;
+}
+
+
+/*!
+ Returns the font weight setting of the style. This is either a
+ valid QFont::Weight or the value Q3StyleSheetItem::Undefined.
+
+ \sa setFontWeight(), QFont
+*/
+int Q3StyleSheetItem::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 Q3StyleSheetItem::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 Q3StyleSheetItem::Undefined.
+
+ \sa setLogicalFontSize(), setLogicalFontSizeStep(), QFont::pointSize(), QFont::setPointSize()
+*/
+int Q3StyleSheetItem::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 Q3StyleSheetItem::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 Q3StyleSheetItem::logicalFontSizeStep() const
+{
+ return d->fontsizestep;
+}
+
+/*!
+ Sets the logical font size step of this style to \a s.
+
+ \sa logicalFontSizeStep()
+*/
+void Q3StyleSheetItem::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 Q3StyleSheetItem::setFontSize(int s)
+{
+ d->fontsize = s;
+}
+
+/*!
+ Returns the font size setting of the style. This is either a valid
+ point size or Q3StyleSheetItem::Undefined.
+
+ \sa setFontSize(), QFont::pointSize(), QFont::setPointSize()
+*/
+int Q3StyleSheetItem::fontSize() const
+{
+ return d->fontsize;
+}
+
+
+/*!
+ Returns the style's font family setting. This is either a valid
+ font family or an empty string if no family has been set.
+
+ \sa setFontFamily(), QFont::family(), QFont::setFamily()
+*/
+QString Q3StyleSheetItem::fontFamily() const
+{
+ return d->fontfamily;
+}
+
+/*!
+ Sets the font family setting of the style to \a fam.
+
+ \sa fontFamily(), QFont::family(), QFont::setFamily()
+*/
+void Q3StyleSheetItem::setFontFamily(const QString& fam)
+{
+ d->fontfamily = fam;
+}
+
+
+/*!
+ Returns the number of columns for this style.
+
+ \sa setNumberOfColumns(), displayMode(), setDisplayMode()
+
+ */
+int Q3StyleSheetItem::numberOfColumns() const
+{
+ return d->ncolumns;
+}
+
+
+/*!
+ Sets the number of columns for this style to \a ncols. Elements in the style
+ are divided into columns.
+
+ This makes sense only if the style uses a block display mode
+ (see Q3StyleSheetItem::DisplayMode).
+
+ \sa numberOfColumns()
+ */
+void Q3StyleSheetItem::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 Q3StyleSheetItem::color() const
+{
+ return d->col;
+}
+
+/*!
+ Sets the text color of this style to \a c.
+
+ \sa color()
+*/
+void Q3StyleSheetItem::setColor(const QColor &c)
+{
+ d->col = c;
+}
+
+/*!
+ Returns whether this style is an anchor.
+
+ \sa setAnchor()
+*/
+bool Q3StyleSheetItem::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 link to other documents or anchors.
+
+ \sa isAnchor()
+*/
+void Q3StyleSheetItem::setAnchor(bool anc)
+{
+ d->anchor = anc;
+}
+
+
+/*!
+ Returns the whitespace mode.
+
+ \sa setWhiteSpaceMode() WhiteSpaceMode
+*/
+Q3StyleSheetItem::WhiteSpaceMode Q3StyleSheetItem::whiteSpaceMode() const
+{
+ return d->whitespacemode;
+}
+
+/*!
+ Sets the whitespace mode to \a m.
+
+ \sa WhiteSpaceMode
+*/
+void Q3StyleSheetItem::setWhiteSpaceMode(WhiteSpaceMode m)
+{
+ d->whitespacemode = m;
+}
+
+
+/*!
+ Returns the width of margin \a m in pixels.
+
+ The margin, \a m, can be MarginLeft, MarginRight,
+ MarginTop, MarginBottom, or MarginFirstLine
+
+ \sa setMargin() Margin
+*/
+int Q3StyleSheetItem::margin(Margin m) const
+{
+ if (m == MarginAll) {
+ return d->margin[MarginLeft];
+ } else if (m == MarginVertical) {
+ return d->margin[MarginTop];
+ } else if (m == MarginHorizontal) {
+ return d->margin[MarginLeft];
+ } else {
+ 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, MarginFirstLine, \c MarginAll,
+ \c MarginVertical or \c MarginHorizontal. The value \a v must
+ be >= 0.
+
+ \sa margin()
+*/
+void Q3StyleSheetItem::setMargin(Margin m, int v)
+{
+ if (m == MarginAll) {
+ d->margin[MarginLeft] = v;
+ d->margin[MarginRight] = v;
+ d->margin[MarginTop] = v;
+ d->margin[MarginBottom] = 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
+ */
+Q3StyleSheetItem::ListStyle Q3StyleSheetItem::listStyle() const
+{
+ return d->list;
+}
+
+/*!
+ \enum Q3StyleSheetItem::ListStyle
+
+ This enum type defines how the items in a list are prefixed when
+ displayed.
+
+ \value ListDisc a filled circle (i.e. a bullet)
+ \value ListCircle an unfilled circle
+ \value ListSquare a filled square
+ \value ListDecimal an integer in base 10: \e 1, \e 2, \e 3, ...
+ \value ListLowerAlpha a lowercase letter: \e a, \e b, \e c, ...
+ \value ListUpperAlpha an uppercase letter: \e A, \e B, \e C, ...
+ \omitvalue ListStyleUndefined
+*/
+
+/*!
+ 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 Q3StyleSheetItem::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 Q3StyleSheetItem::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 Q3StyleSheetItem::setContexts(const QString& c)
+{
+ d->contxt = QLatin1Char(' ') + c + QLatin1Char(' ');
+}
+
+/*!
+ Returns true if this style can be nested into an element of style
+ \a s; otherwise returns false.
+
+ \sa contexts(), setContexts()
+*/
+bool Q3StyleSheetItem::allowedInContext(const Q3StyleSheetItem* s) const
+{
+ if (d->contxt.isEmpty())
+ return true;
+ return d->contxt.contains(QLatin1Char(' ')+s->name()+QLatin1Char(' '));
+}
+
+
+/*!
+ Returns true if this style has self-nesting enabled; otherwise
+ returns false.
+
+ \sa setSelfNesting()
+*/
+bool Q3StyleSheetItem::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 Q3StyleSheetItem::setSelfNesting(bool nesting)
+{
+ d->selfnest = nesting;
+}
+
+/*!
+ \internal
+ Sets the linespacing to be at least \a ls pixels.
+
+ For compatibility with previous Qt releases, small values get
+ treated differently: If \a ls is smaller than the default font
+ line spacing in pixels at parse time, the resulting line spacing
+ is the sum of the default line spacing plus \a ls. We recommend
+ not relying on this behavior.
+*/
+
+void Q3StyleSheetItem::setLineSpacing(int ls)
+{
+ d->lineSpacing = ls;
+}
+
+/*!
+ Returns the line spacing.
+*/
+
+int Q3StyleSheetItem::lineSpacing() const
+{
+ return d->lineSpacing;
+}
+
+//************************************************************************
+
+
+
+
+//************************************************************************
+
+
+/*!
+ \class Q3StyleSheet
+ \brief The Q3StyleSheet class is a collection of styles for rich text
+ rendering and a generator of tags.
+
+ \compat
+
+ By creating Q3StyleSheetItem 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 QTextEdit 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 Q3StyleSheet 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
+ \table
+ \header \i Structuring tags \i Notes
+ \row \i \c{<qt>}...\c{</qt>}
+ \i A Qt rich text document. It understands the following
+ attributes:
+ \list
+ \i \c title -- The caption of the document. This attribute is
+ easily accessible with QTextEdit::documentTitle().
+ \i \c type -- The type of the document. The default type is \c
+ page. It indicates that the document is displayed in a
+ page of its own. Another style is \c detail, which can be
+ used to explain certain expressions in more detail in a
+ few sentences. For \c detail, QTextBrowser will then keep
+ the current page and display the new document in a small
+ popup similar to QWhatsThis. Note that links will not work
+ in documents with \c{<qt type="detail">...</qt>}.
+ \i \c bgcolor -- The background color, for example \c
+ bgcolor="yellow" or \c bgcolor="#0000FF".
+ \i \c background -- The background pixmap, for example \c
+ background="granite.xpm". The pixmap name will be resolved
+ by a Q3MimeSourceFactory().
+ \i \c text -- The default text color, for example \c text="red".
+ \i \c link -- The link color, for example \c link="green".
+ \endlist
+ \row \i \c{<h1>...</h1>}
+ \i A top-level heading.
+ \row \i \c{<h2>...</h2>}
+ \i A sublevel heading.
+ \row \i \c{<h3>...</h3>}
+ \i A sub-sublevel heading.
+ \row \i \c{<p>...</p>}
+ \i A left-aligned paragraph. Adjust the alignment with the \c
+ align attribute. Possible values are \c left, \c right and
+ \c center.
+ \row \i \c{<center>...}<br>\c{</center>}
+ \i A centered paragraph.
+ \row \i \c{<blockquote>...}<br>\c{</blockquote>}
+ \i An indented paragraph that is useful for quotes.
+ \row \i \c{<ul>...</ul>}
+ \i An unordered list. You can also pass a type argument to
+ define the bullet style. The default is \c type=disc;
+ other types are \c circle and \c square.
+ \row \i \c{<ol>...</ol>}
+ \i An ordered list. You can also pass a type argument to
+ define the enumeration label style. The default is \c
+ type="1"; other types are \c "a" and \c "A".
+ \row \i \c{<li>...</li>}
+ \i A list item. This tag can be used only within the context
+ of \c{<ol>} or \c{<ul>}.
+ \row \i \c{<pre>...</pre>}
+ \i For larger chunks of code. Whitespaces in the contents are
+ preserved. For small bits of code use the inline-style \c
+ code.
+ \endtable
+
+ Anchors and links are done with a single tag:
+ \table
+ \header \i Anchor tags \i Notes
+ \row \i \c{<a>...</a>}
+ \i An anchor or link.
+ \list
+ \i A link is created by using an \c href
+ attribute, for example
+ <br>\c{<a href="target.qml">Link Text</a>}. Links to
+ targets within a document are achieved in the same way
+ as for HTML, e.g.
+ <br>\c{<a href="target.qml#subtitle">Link Text</a>}.
+ \i A target is created by using a \c name
+ attribute, for example
+ <br>\c{<a name="subtitle"><h2>Sub Title</h2></a>}.
+ \endlist
+ \endtable
+
+ The default character style bindings are
+ \table
+ \header \i Style tags \i Notes
+ \row \i \c{<em>...</em>}
+ \i Emphasized. By default this is the same as \c{<i>...</i>}
+ (italic).
+ \row \i \c{<strong>...</strong>}
+ \i Strong. By default this is the same as \c{<b>...</b>}
+ (bold).
+ \row \i \c{<i>...</i>}
+ \i Italic font style.
+ \row \i \c{<b>...</b>}
+ \i Bold font style.
+ \row \i \c{<u>...</u>}
+ \i Underlined font style.
+ \row \i \c{<s>...</s>}
+ \i Strike out font style.
+ \row \i \c{<big>...</big>}
+ \i A larger font size.
+ \row \i \c{<small>...</small>}
+ \i A smaller font size.
+ \row \i \c{<sub>...</sub>}
+ \i Subscripted text
+ \row \i \c{<sup>...</sup>}
+ \i Superscripted text
+ \row \i \c{<code>...</code>}
+ \i Indicates code. By default this is the same as
+ \c{<tt>...</tt>} (typewriter). For larger chunks of code
+ use the block-tag \c{<}\c{pre>}.
+ \row \i \c{<tt>...</tt>}
+ \i Typewriter font style.
+ \row \i \c{<font>...</font>}
+ \i Customizes the font size, family and text color. The tag
+ understands the following attributes:
+ \list
+ \i \c color -- The text color, for example \c color="red" or
+ \c color="#FF0000".
+ \i \c size -- The logical size of the font. Logical sizes 1
+ to 7 are supported. The value may either be absolute
+ (for example, \c size=3) or relative (\c size=-2). In
+ the latter case the sizes are simply added.
+ \i \c face -- The family of the font, for example \c face=times.
+ \endlist
+ \endtable
+
+ Special elements are:
+ \table
+ \header \i Special tags \i Notes
+ \row \i \c{<img>}
+ \i An image. The image name for the mime source factory is
+ given in the source attribute, for example
+ \c{<img src="qt.xpm">} The image tag also understands the
+ attributes \c width and \c height that determine the size
+ of the image. If the pixmap does not fit the specified
+ size it will be scaled automatically (by using
+ QImage::smoothScale()).
+
+ 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.
+ \row \i \c{<hr>}
+ \i A horizontal line.
+ \row \i \c{<br>}
+ \i A line break.
+ \row \i \c{<nobr>...</nobr>}
+ \i No break. Prevents word wrap.
+ \endtable
+
+ In addition, rich text supports simple HTML tables. A table
+ consists of one or more rows each of which contains one or more
+ cells. Cells are either data cells or header cells, depending on
+ their content. Cells which span rows and columns are supported.
+
+ \table
+ \header \i Table tags \i Notes
+ \row \i \c{<table>...</table>}
+ \i A table. Tables support the following attributes:
+ \list
+ \i \c bgcolor -- The background color.
+ \i \c width -- The table width. This is either an absolute
+ pixel width or a relative percentage of the table's
+ width, for example \c width=80%.
+ \i \c border -- The width of the table border. The default is
+ 0 (= no border).
+ \i \c cellspacing -- Additional space around the table cells.
+ The default is 2.
+ \i \c cellpadding -- Additional space around the contents of
+ table cells. The default is 1.
+ \endlist
+ \row \i \c{<tr>...</tr>}
+ \i A table row. This is only valid within a \c table. Rows
+ support the following attribute:
+ \list
+ \i \c bgcolor -- The background color.
+ \endlist
+ \row \i \c{<th>...</th>}
+ \i A table header cell. Similar to \c td, but defaults to
+ center alignment and a bold font.
+ \row \i \c{<td>...</td>}
+ \i A table data cell. This is only valid within a \c tr.
+ Cells support the following attributes:
+ \list
+ \i \c bgcolor -- The background color.
+ \i \c width -- The cell width. This is either an absolute
+ pixel width or a relative percentage of table's width,
+ for example \c width=50%.
+ \i \c colspan -- Specifies how many columns this cell spans.
+ The default is 1.
+ \i \c rowspan -- Specifies how many rows this cell spans. The
+ default is 1.
+ \i \c align -- Qt::Alignment; possible values are \c left, \c
+ right, and \c center. The default is \c left.
+ \i \c valign -- Qt::Vertical alignment; possible values are \c
+ top, \c middle, and \c bottom. The default is \c middle.
+ \endlist
+ \endtable
+*/
+
+/*!
+ Creates a style sheet called \a name, with parent \a parent. Like
+ any QObject it will be deleted when its parent is destroyed (if
+ the child still exists).
+
+ By default the style sheet has the tag definitions defined above.
+*/
+Q3StyleSheet::Q3StyleSheet(QObject *parent, const char *name)
+ : QObject(parent)
+{
+ setObjectName(QLatin1String(name));
+ init();
+}
+
+/*!
+ Destroys the style sheet. All styles inserted into the style sheet
+ will be deleted.
+*/
+Q3StyleSheet::~Q3StyleSheet()
+{
+ QHash<QString, Q3StyleSheetItem *>::iterator it = styles.begin();
+ while (it != styles.end()) {
+ delete it.value();
+ ++it;
+ }
+}
+
+/*!
+ \internal
+ Initialized the style sheet to the basic Qt style.
+*/
+void Q3StyleSheet::init()
+{
+ nullstyle = new Q3StyleSheetItem(this, QString::fromLatin1(""));
+
+ Q3StyleSheetItem *style;
+
+ style = new Q3StyleSheetItem(this, QLatin1String("qml")); // compatibility
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("qt"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("a"));
+ style->setAnchor(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("em"));
+ style->setFontItalic(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("i"));
+ style->setFontItalic(true);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("big"));
+ style->setLogicalFontSizeStep(1);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("large")); // compatibility
+ style->setLogicalFontSizeStep(1);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("small"));
+ style->setLogicalFontSizeStep(-1);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("strong"));
+ style->setFontWeight(QFont::Bold);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("b"));
+ style->setFontWeight(QFont::Bold);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h1"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(6);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 18);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h2"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(5);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 16);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h3"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(4);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 14);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h4"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(3);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("h5"));
+ style->setFontWeight(QFont::Bold);
+ style->setLogicalFontSize(2);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginTop, 12);
+ style-> setMargin(Q3StyleSheetItem::MarginBottom, 4);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("p"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setSelfNesting(false);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("center"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setAlignment(Qt::AlignCenter);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("twocolumn"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setNumberOfColumns(2);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("multicol"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ (void) new Q3StyleSheetItem(this, QString::fromLatin1("font"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("ul"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setListStyle(Q3StyleSheetItem::ListDisc);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("ol"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setListStyle(Q3StyleSheetItem::ListDecimal);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("li"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayListItem);
+ style->setSelfNesting(false);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("code"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("tt"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+
+ new Q3StyleSheetItem(this, QString::fromLatin1("img"));
+ new Q3StyleSheetItem(this, QString::fromLatin1("br"));
+ new Q3StyleSheetItem(this, QString::fromLatin1("hr"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("sub"));
+ style->setVerticalAlignment(Q3StyleSheetItem::VAlignSub);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("sup"));
+ style->setVerticalAlignment(Q3StyleSheetItem::VAlignSuper);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("pre"));
+ style->setFontFamily(QString::fromLatin1("Courier New,courier"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpacePre);
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 12);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("blockquote"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setMargin(Q3StyleSheetItem::MarginHorizontal, 40);
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("head"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayNone);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("body"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("div"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock) ;
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("span"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dl"));
+ style-> setMargin(Q3StyleSheetItem::MarginVertical, 8);
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dt"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setContexts(QString::fromLatin1("dl"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("dd"));
+ style->setDisplayMode(Q3StyleSheetItem::DisplayBlock);
+ style->setMargin(Q3StyleSheetItem::MarginLeft, 30);
+ style->setContexts(QString::fromLatin1("dt dl"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("u"));
+ style->setFontUnderline(true);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("s"));
+ style->setFontStrikeOut(true);
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("nobr"));
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpaceNoWrap);
+
+ // compatibily with some minor 3.0.x Qt versions that had an
+ // undocumented <wsp> tag. ### Remove 3.1
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("wsp"));
+ style->setWhiteSpaceMode(Q3StyleSheetItem::WhiteSpacePre);
+
+ // tables
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("table"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("tr"));
+ style->setContexts(QString::fromLatin1("table"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("td"));
+ style->setContexts(QString::fromLatin1("tr"));
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("th"));
+ style->setFontWeight(QFont::Bold);
+ style->setAlignment(Qt::AlignCenter);
+ style->setContexts(QString::fromLatin1("tr"));
+
+ style = new Q3StyleSheetItem(this, QString::fromLatin1("html"));
+}
+
+
+
+static Q3StyleSheet* defaultsheet = 0;
+static Q3SingleCleanupHandler<Q3StyleSheet> 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 also serves as the
+ initial style sheet for the more complex render widgets, QTextEdit
+ and QTextBrowser.
+
+ \sa setDefaultSheet()
+*/
+Q3StyleSheet* Q3StyleSheet::defaultSheet()
+{
+ if (!defaultsheet) {
+ defaultsheet = new Q3StyleSheet();
+ 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 Q3StyleSheet.
+
+ \sa defaultSheet()
+*/
+void Q3StyleSheet::setDefaultSheet(Q3StyleSheet* 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 Q3StyleSheet::insert(Q3StyleSheetItem* style)
+{
+ styles.insert(style->name(), style);
+}
+
+
+/*!
+ Returns the style called \a name or 0 if there is no such style.
+*/
+Q3StyleSheetItem* Q3StyleSheet::item(const QString& name)
+{
+ if (name.isNull())
+ return 0;
+ return styles.value(name);
+}
+
+/*!
+ \overload
+
+ Returns the style called \a name or 0 if there is no such style
+ (const version)
+*/
+const Q3StyleSheetItem* Q3StyleSheet::item(const QString& name) const
+{
+ if (name.isNull())
+ return 0;
+ return styles.value(name);
+}
+
+/*! Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted paragraph while preserving most of its look.
+
+ \a mode defines the whitespace mode. Possible values are \c
+ Q3StyleSheetItem::WhiteSpacePre (no wrapping, all whitespaces
+ preserved) and Q3StyleSheetItem::WhiteSpaceNormal (wrapping,
+ simplified whitespaces).
+
+ \sa escape()
+*/
+QString Q3StyleSheet::convertFromPlainText(const QString& plain, Q3StyleSheetItem::WhiteSpaceMode mode)
+{
+ return Qt::convertFromPlainText(plain, Qt::WhiteSpaceMode(mode));
+}
+
+/*!
+ Auxiliary function. Converts the plain text string \a plain to a
+ rich text formatted string with any HTML meta-characters escaped.
+
+ \sa convertFromPlainText()
+*/
+QString Q3StyleSheet::escape(const QString& plain)
+{
+ return Qt::escape(plain);
+}
+
+// Must doc this enum somewhere, and it is logically related to Q3StyleSheet
+
+/*!
+ Returns true if the string \a text is likely to be rich text;
+ otherwise returns false.
+
+ This function uses a fast and therefore simple heuristic. It
+ mainly checks whether there is something that looks like a tag
+ before the first line break. Although the result may be correct
+ for common cases, there is no guarantee.
+*/
+bool Q3StyleSheet::mightBeRichText(const QString& text)
+{
+ return Qt::mightBeRichText(text);
+}
+
+
+/*!
+ \fn void Q3StyleSheet::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 Q3StyleSheet::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 Q3StyleSheetItem::logicalFontSize(), Q3StyleSheetItem::logicalFontSizeStep(), QFont::setPointSize()
+ */
+void Q3StyleSheet::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 = 7*baseSize/10;
+ 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(qMax(1, s));
+ else
+ font.setPointSize(qMax(1, s));
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_RICHTEXT
diff --git a/src/qt3support/text/q3stylesheet.h b/src/qt3support/text/q3stylesheet.h
new file mode 100644
index 0000000..d13b3e9
--- /dev/null
+++ b/src/qt3support/text/q3stylesheet.h
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3STYLESHEET_H
+#define Q3STYLESHEET_H
+
+#include <QtCore/qstring.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qcolor.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_RICHTEXT
+
+class Q3StyleSheet;
+class Q3TextDocument;
+template<class Key, class T> class QMap;
+class Q3StyleSheetItemData;
+
+class Q_COMPAT_EXPORT Q3StyleSheetItem
+{
+public:
+ Q3StyleSheetItem(Q3StyleSheet* parent, const QString& name);
+ Q3StyleSheetItem(const Q3StyleSheetItem &);
+ ~Q3StyleSheetItem();
+
+ Q3StyleSheetItem& operator=(const Q3StyleSheetItem& other);
+
+ QString name() const;
+
+ Q3StyleSheet* styleSheet();
+ const Q3StyleSheet* styleSheet() const;
+
+ enum AdditionalStyleValues { Undefined = -1 };
+
+ enum DisplayMode {
+ DisplayBlock,
+ DisplayInline,
+ DisplayListItem,
+ DisplayNone,
+ DisplayModeUndefined = -1
+ };
+
+ DisplayMode displayMode() const;
+ void setDisplayMode(DisplayMode m);
+
+ int alignment() const;
+ void setAlignment(int f);
+
+ enum VerticalAlignment {
+ VAlignBaseline,
+ VAlignSub,
+ VAlignSuper
+ };
+
+ VerticalAlignment verticalAlignment() const;
+ void setVerticalAlignment(VerticalAlignment valign);
+
+ int fontWeight() const;
+ void setFontWeight(int w);
+
+ int logicalFontSize() const;
+ void setLogicalFontSize(int s);
+
+ int logicalFontSizeStep() const;
+ void setLogicalFontSizeStep(int s);
+
+ int fontSize() const;
+ void setFontSize(int s);
+
+ QString fontFamily() const;
+ void setFontFamily(const QString&);
+
+ int numberOfColumns() const;
+ void setNumberOfColumns(int ncols);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ bool fontItalic() const;
+ void setFontItalic(bool);
+ bool definesFontItalic() const;
+
+ bool fontUnderline() const;
+ void setFontUnderline(bool);
+ bool definesFontUnderline() const;
+
+ bool fontStrikeOut() const;
+ void setFontStrikeOut(bool);
+ bool definesFontStrikeOut() const;
+
+ bool isAnchor() const;
+ void setAnchor(bool anc);
+
+ enum WhiteSpaceMode {
+ WhiteSpaceNormal,
+ WhiteSpacePre,
+ WhiteSpaceNoWrap,
+ WhiteSpaceModeUndefined = -1
+ };
+ WhiteSpaceMode whiteSpaceMode() const;
+ void setWhiteSpaceMode(WhiteSpaceMode m);
+
+ enum Margin {
+ MarginLeft,
+ MarginRight,
+ MarginTop,
+ MarginBottom,
+ MarginFirstLine,
+ MarginAll,
+ MarginVertical,
+ MarginHorizontal,
+ MarginUndefined = -1
+ };
+
+ int margin(Margin m) const;
+ void setMargin(Margin, int);
+
+ enum ListStyle {
+ ListDisc,
+ ListCircle,
+ ListSquare,
+ ListDecimal,
+ ListLowerAlpha,
+ ListUpperAlpha,
+ ListStyleUndefined = -1
+ };
+
+ ListStyle listStyle() const;
+ void setListStyle(ListStyle);
+
+ QString contexts() const;
+ void setContexts(const QString&);
+ bool allowedInContext(const Q3StyleSheetItem*) const;
+
+ bool selfNesting() const;
+ void setSelfNesting(bool);
+
+ void setLineSpacing(int ls);
+ int lineSpacing() const;
+
+private:
+ void init();
+ Q3StyleSheetItemData* d;
+};
+
+#ifndef QT_NO_TEXTCUSTOMITEM
+class Q3TextCustomItem;
+#endif
+
+class Q_COMPAT_EXPORT Q3StyleSheet : public QObject
+{
+ Q_OBJECT
+public:
+ Q3StyleSheet(QObject *parent=0, const char *name=0);
+ virtual ~Q3StyleSheet();
+
+ static Q3StyleSheet* defaultSheet();
+ static void setDefaultSheet(Q3StyleSheet*);
+
+
+ Q3StyleSheetItem* item(const QString& name);
+ const Q3StyleSheetItem* item(const QString& name) const;
+
+ void insert(Q3StyleSheetItem* item);
+
+ static QString escape(const QString&);
+ static QString convertFromPlainText(const QString&,
+ Q3StyleSheetItem::WhiteSpaceMode mode = Q3StyleSheetItem::WhiteSpacePre);
+ static bool mightBeRichText(const QString&);
+
+ virtual void scaleFont(QFont& font, int logicalSize) const;
+
+ virtual void error(const QString&) const;
+
+private:
+ Q_DISABLE_COPY(Q3StyleSheet)
+
+ void init();
+ QHash<QString, Q3StyleSheetItem *> styles;
+ Q3StyleSheetItem* nullstyle;
+};
+
+#endif // QT_NO_RICHTEXT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3STYLESHEET_H
diff --git a/src/qt3support/text/q3syntaxhighlighter.cpp b/src/qt3support/text/q3syntaxhighlighter.cpp
new file mode 100644
index 0000000..b81a529
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3syntaxhighlighter.h"
+#include "q3syntaxhighlighter_p.h"
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+#include "q3textedit.h"
+#include "qtimer.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3SyntaxHighlighter
+ \brief The Q3SyntaxHighlighter class is a base class for
+ implementing Q3TextEdit syntax highlighters.
+
+ \compat
+
+ A syntax highligher automatically highlights parts of the text in
+ a Q3TextEdit. Syntax highlighters are often used when the user is
+ entering text in a specific format (for example, source code) and
+ help the user to read the text and identify syntax errors.
+
+ To provide your own syntax highlighting for Q3TextEdit, you must
+ subclass Q3SyntaxHighlighter and reimplement highlightParagraph().
+
+ When you create an instance of your Q3SyntaxHighlighter subclass,
+ pass it the Q3TextEdit that you want the syntax highlighting to be
+ applied to. After this your highlightParagraph() function will be
+ called automatically whenever necessary. Use your
+ highlightParagraph() function to apply formatting (e.g. setting
+ the font and color) to the text that is passed to it.
+*/
+
+/*!
+ Constructs the Q3SyntaxHighlighter and installs it on \a textEdit.
+ Ownership of the Q3SyntaxHighlighter is transferred to the \a
+ textEdit
+*/
+
+Q3SyntaxHighlighter::Q3SyntaxHighlighter(Q3TextEdit *textEdit)
+ : para(0), edit(textEdit), d(new Q3SyntaxHighlighterPrivate)
+{
+ textEdit->document()->setPreProcessor(new Q3SyntaxHighlighterInternal(this));
+ textEdit->document()->invalidate();
+ QTimer::singleShot(0, textEdit->viewport(), SLOT(update()));
+}
+
+/*!
+ Destructor. Uninstalls this syntax highlighter from the textEdit()
+*/
+
+Q3SyntaxHighlighter::~Q3SyntaxHighlighter()
+{
+ delete d;
+ textEdit()->document()->setPreProcessor(0);
+}
+
+/*!
+ \fn int Q3SyntaxHighlighter::highlightParagraph(const QString &text, int endStateOfLastPara)
+
+ This function is called when necessary by the rich text engine,
+ i.e. on paragraphs which have changed.
+
+ In your reimplementation you should parse the paragraph's \a text
+ and call setFormat() as often as necessary to apply any font and
+ color changes that you require. Your function must return a value
+ which indicates the paragraph's end state: see below.
+
+ Some syntaxes can have constructs that span paragraphs. For
+ example, a C++ syntax highlighter should be able to cope with
+ \c{/}\c{*...*}\c{/} comments that span paragraphs. To deal
+ with these cases it is necessary to know the end state of the
+ previous paragraph (e.g. "in comment").
+
+ If your syntax does not have paragraph spanning constructs, simply
+ ignore the \a endStateOfLastPara parameter and always return 0.
+
+ Whenever highlightParagraph() is called it is passed a value for
+ \a endStateOfLastPara. For the very first paragraph this value is
+ always -2. For any other paragraph the value is the value returned
+ by the most recent highlightParagraph() call that applied to the
+ preceding paragraph.
+
+ The value you return is up to you. We recommend only returning 0
+ (to signify that this paragraph's syntax highlighting does not
+ affect the following paragraph), or a positive integer (to signify
+ that this paragraph has ended in the middle of a paragraph
+ spanning construct).
+
+ To find out which paragraph is highlighted, call
+ currentParagraph().
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment". For a paragraph
+ that ended in the middle of a comment you'd return 1, and for
+ other paragraphs you'd return 0. In your parsing code if \a
+ endStateOfLastPara was 1, you would highlight the text as a C++
+ comment until you reached the closing \c{*}\c{/}.
+*/
+
+/*!
+ This function is applied to the syntax highlighter's current
+ paragraph (the text of which is passed to the highlightParagraph()
+ function).
+
+ The specified \a font and \a color are applied to the text from
+ position \a start for \a count characters. (If \a count is 0,
+ nothing is done.)
+*/
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QFont &font, const QColor &color)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ f = para->document()->formatCollection()->format(font, color);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*! \overload */
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QColor &color)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ QFont fnt = textEdit()->QWidget::font();
+ f = para->document()->formatCollection()->format(fnt, color);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*! \overload */
+
+void Q3SyntaxHighlighter::setFormat(int start, int count, const QFont &font)
+{
+ if (!para || count <= 0)
+ return;
+ Q3TextFormat *f = 0;
+ QColor c = textEdit()->viewport()->palette().color(textEdit()->viewport()->foregroundRole());
+ f = para->document()->formatCollection()->format(font, c);
+ para->setFormat(start, count, f);
+ f->removeRef();
+}
+
+/*!
+ \fn Q3TextEdit *Q3SyntaxHighlighter::textEdit() const
+
+ Returns the Q3TextEdit on which this syntax highlighter is
+ installed
+*/
+
+/*! Redoes the highlighting of the whole document.
+*/
+
+void Q3SyntaxHighlighter::rehighlight()
+{
+ Q3TextParagraph *s = edit->document()->firstParagraph();
+ while (s) {
+ s->invalidate(0);
+ s->state = -1;
+ s->needPreProcess = true;
+ s = s->next();
+ }
+ edit->repaintContents();
+}
+
+/*!
+ Returns the id of the paragraph which is highlighted, or -1 of no
+ paragraph is currently highlighted.
+
+ Usually this function is called from within highlightParagraph().
+*/
+
+int Q3SyntaxHighlighter::currentParagraph() const
+{
+ return d->currentParagraph;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3syntaxhighlighter.h b/src/qt3support/text/q3syntaxhighlighter.h
new file mode 100644
index 0000000..432ce74
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SYNTAXHIGHLIGHTER_H
+#define Q3SYNTAXHIGHLIGHTER_H
+
+#include <QtGui/qfont.h>
+#include <QtGui/qcolor.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3TextEdit;
+class Q3SyntaxHighlighterInternal;
+class Q3SyntaxHighlighterPrivate;
+class Q3TextParagraph;
+
+class Q_COMPAT_EXPORT Q3SyntaxHighlighter
+{
+ friend class Q3SyntaxHighlighterInternal;
+
+public:
+ Q3SyntaxHighlighter(Q3TextEdit *textEdit);
+ virtual ~Q3SyntaxHighlighter();
+
+ virtual int highlightParagraph(const QString &text, int endStateOfLastPara) = 0;
+
+ void setFormat(int start, int count, const QFont &font, const QColor &color);
+ void setFormat(int start, int count, const QColor &color);
+ void setFormat(int start, int count, const QFont &font);
+ Q3TextEdit *textEdit() const { return edit; }
+
+ void rehighlight();
+
+ int currentParagraph() const;
+
+private:
+ Q3TextParagraph *para;
+ Q3TextEdit *edit;
+ Q3SyntaxHighlighterPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3SYNTAXHIGHLIGHTER_H
diff --git a/src/qt3support/text/q3syntaxhighlighter_p.h b/src/qt3support/text/q3syntaxhighlighter_p.h
new file mode 100644
index 0000000..73820c7
--- /dev/null
+++ b/src/qt3support/text/q3syntaxhighlighter_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3SYNTAXHIGHLIGHTER_P_H
+#define Q3SYNTAXHIGHLIGHTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_SYNTAXHIGHLIGHTER
+#include "q3syntaxhighlighter.h"
+#include "private/q3richtext_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3SyntaxHighlighterPrivate
+{
+public:
+ Q3SyntaxHighlighterPrivate() :
+ currentParagraph(-1)
+ {}
+
+ int currentParagraph;
+};
+
+class Q3SyntaxHighlighterInternal : public Q3TextPreProcessor
+{
+public:
+ Q3SyntaxHighlighterInternal(Q3SyntaxHighlighter *h) : highlighter(h) {}
+ void process(Q3TextDocument *doc, Q3TextParagraph *p, int, bool invalidate) {
+ if (p->prev() && p->prev()->endState() == -1)
+ process(doc, p->prev(), 0, false);
+
+ highlighter->para = p;
+ QString text = p->string()->toString();
+ int endState = p->prev() ? p->prev()->endState() : -2;
+ int oldEndState = p->endState();
+ highlighter->d->currentParagraph = p->paragId();
+ p->setEndState(highlighter->highlightParagraph(text, endState));
+ highlighter->d->currentParagraph = -1;
+ highlighter->para = 0;
+
+ p->setFirstPreProcess(false);
+ Q3TextParagraph *op = p;
+ p = p->next();
+ if ((!!oldEndState || !!op->endState()) && oldEndState != op->endState() &&
+ invalidate && p && !p->firstPreProcess() && p->endState() != -1) {
+ while (p) {
+ if (p->endState() == -1)
+ return;
+ p->setEndState(-1);
+ p = p->next();
+ }
+ }
+ }
+ Q3TextFormat *format(int) { return 0; }
+
+private:
+ Q3SyntaxHighlighter *highlighter;
+
+ friend class Q3TextEdit;
+};
+
+#endif // QT_NO_SYNTAXHIGHLIGHTER
+
+QT_END_NAMESPACE
+
+#endif // Q3SYNTAXHIGHLIGHTER_P_H
diff --git a/src/qt3support/text/q3textbrowser.cpp b/src/qt3support/text/q3textbrowser.cpp
new file mode 100644
index 0000000..8f4280f
--- /dev/null
+++ b/src/qt3support/text/q3textbrowser.cpp
@@ -0,0 +1,526 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textbrowser.h"
+#ifndef QT_NO_TEXTBROWSER
+#include <private/q3richtext_p.h>
+
+#include "qevent.h"
+#include "qdesktopwidget.h"
+#include "qapplication.h"
+#include "qlayout.h"
+#include "qpainter.h"
+#include "qstack.h"
+#include "stdio.h"
+#include "qfile.h"
+#include "qtextstream.h"
+#include "qbitmap.h"
+#include "qtimer.h"
+#include "qimage.h"
+#include "q3simplerichtext.h"
+#include "q3dragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3TextBrowser
+ \brief The Q3TextBrowser class provides a rich text browser with hypertext navigation.
+
+ \compat
+
+ This class extends Q3TextEdit (in read-only mode), adding some
+ navigation functionality so that users can follow links in
+ hypertext documents. The contents of Q3TextEdit is set with
+ setText(), but Q3TextBrowser has an additional function,
+ setSource(), which makes it possible to set the text to a named
+ document. The name is looked up in the text view's mime source
+ factory. If a document name ends with an anchor (for example, "\c
+ #anchor"), the text browser automatically scrolls to that position
+ (using scrollToAnchor()). When the user clicks on a hyperlink, the
+ browser will call setSource() itself, with the link's \c href
+ value as argument. You can track the current source by connetion
+ to the sourceChanged() signal.
+
+ Q3TextBrowser provides backward() and forward() slots which you can
+ use to implement Back and Forward buttons. The home() slot sets
+ the text to the very first document displayed. The linkClicked()
+ signal is emitted when the user clicks a link.
+
+ By using Q3TextEdit::setMimeSourceFactory() you can provide your
+ own subclass of Q3MimeSourceFactory. This makes it possible to
+ access data from anywhere, for example from a network or from a
+ database. See Q3MimeSourceFactory::data() for details.
+
+ If you intend using the mime factory to read the data directly
+ from the file system, you may have to specify the encoding for the
+ file extension you are using. For example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textbrowser.cpp 0
+ This is to ensure that the factory is able to resolve the document
+ names.
+
+ Q3TextBrowser interprets the tags it processes in accordance with
+ the default style sheet. Change the style sheet with
+ \l{setStyleSheet()}; see QStyleSheet for details.
+
+ If you want to provide your users with editable rich text use
+ Q3TextEdit. If you want a text browser without hypertext navigation
+ use Q3TextEdit, and use Q3TextEdit::setReadOnly() to disable
+ editing. If you just need to display a small piece of rich text
+ use QSimpleRichText or QLabel.
+*/
+
+class Q3TextBrowserData
+{
+public:
+ Q3TextBrowserData():textOrSourceChanged(false) {}
+
+ QStack<QString> stack;
+ QStack<QString> forwardStack;
+ QString home;
+ QString curmain;
+ QString curmark;
+
+ /*flag necessary to give the linkClicked() signal some meaningful
+ semantics when somebody connected to it calls setText() or
+ setSource() */
+ bool textOrSourceChanged;
+};
+
+
+/*!
+ Constructs an empty Q3TextBrowser called \a name, with parent \a
+ parent.
+*/
+Q3TextBrowser::Q3TextBrowser(QWidget *parent, const char *name)
+ : Q3TextEdit(parent, name)
+{
+ setReadOnly(true);
+ d = new Q3TextBrowserData;
+
+ viewport()->setMouseTracking(true);
+}
+
+/*!
+ \internal
+*/
+Q3TextBrowser::~Q3TextBrowser()
+{
+ delete d;
+}
+
+
+/*!
+ \property Q3TextBrowser::source
+ \brief the name of the displayed document.
+
+ This is a an empty string if no document is displayed or if the
+ source is unknown.
+
+ Setting this property uses the mimeSourceFactory() to lookup the
+ named document. It also checks for optional anchors and scrolls
+ the document accordingly.
+
+ If the first tag in the document is \c{<qt type=detail>}, the
+ document is displayed as a popup rather than as new document in
+ the browser window itself. Otherwise, the document is displayed
+ normally in the text browser with the text set to the contents of
+ the named document with setText().
+
+ If you are using the filesystem access capabilities of the mime
+ source factory, you must ensure that the factory knows about the
+ encoding of specified files; otherwise no data will be available.
+ The default factory handles a couple of common file extensions
+ such as \c *.html and \c *.txt with reasonable defaults. See
+ Q3MimeSourceFactory::data() for details.
+*/
+
+QString Q3TextBrowser::source() const
+{
+ if (d->stack.isEmpty())
+ return QString();
+ else
+ return d->stack.top();
+}
+
+/*!
+ Reloads the current set source.
+*/
+
+void Q3TextBrowser::reload()
+{
+ QString s = d->curmain;
+ d->curmain = QLatin1String("");
+ setSource(s);
+}
+
+
+void Q3TextBrowser::setSource(const QString& name)
+{
+#ifndef QT_NO_CURSOR
+ if (isVisible())
+ qApp->setOverrideCursor(Qt::WaitCursor);
+#endif
+ d->textOrSourceChanged = true;
+ QString source = name;
+ QString mark;
+ int hash = name.indexOf(QLatin1Char('#'));
+ if (hash != -1) {
+ source = name.left(hash);
+ mark = name.mid(hash+1);
+ }
+
+ if (source.left(5) == QLatin1String("file:"))
+ source = source.mid(6);
+
+ QString url = mimeSourceFactory()->makeAbsolute(source, context());
+ QString txt;
+ bool dosettext = false;
+
+ if (!source.isEmpty() && url != d->curmain) {
+ const QMimeSource* m =
+ mimeSourceFactory()->data(source, context());
+ if (!m){
+ qWarning("Q3TextBrowser: no mimesource for %s", source.latin1());
+ }
+ else {
+ if (!Q3TextDrag::decode(m, txt)) {
+ qWarning("Q3TextBrowser: cannot decode %s", source.latin1());
+ }
+ }
+ if (isVisible()) {
+ QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1);
+ if (firstTag.left(3) == QLatin1String("<qt") && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {
+ popupDetail(txt, QCursor::pos());
+#ifndef QT_NO_CURSOR
+ qApp->restoreOverrideCursor();
+#endif
+ return;
+ }
+ }
+
+ d->curmain = url;
+ dosettext = true;
+ }
+
+ d->curmark = mark;
+
+ if (!mark.isEmpty()) {
+ url += QLatin1Char('#');
+ url += mark;
+ }
+ if (d->home.count() == 0)
+ d->home = url;
+
+ if (d->stack.isEmpty() || d->stack.top() != url)
+ d->stack.push(url);
+
+ int stackCount = (int)d->stack.count();
+ if (d->stack.top() == url)
+ stackCount--;
+ emit backwardAvailable(stackCount > 0);
+ stackCount = (int)d->forwardStack.count();
+ if (d->forwardStack.isEmpty() || d->forwardStack.top() == url)
+ stackCount--;
+ emit forwardAvailable(stackCount > 0);
+
+ if (dosettext)
+ Q3TextEdit::setText(txt, url);
+
+ if (!mark.isEmpty())
+ scrollToAnchor(mark);
+ else
+ setContentsPos(0, 0);
+
+#ifndef QT_NO_CURSOR
+ if (isVisible())
+ qApp->restoreOverrideCursor();
+#endif
+
+ emit sourceChanged(url);
+}
+
+/*!
+ \fn void Q3TextBrowser::backwardAvailable(bool available)
+
+ This signal is emitted when the availability of backward()
+ changes. \a available is false when the user is at home();
+ otherwise it is true.
+*/
+
+/*!
+ \fn void Q3TextBrowser::forwardAvailable(bool available)
+
+ This signal is emitted when the availability of forward() changes.
+ \a available is true after the user navigates backward() and false
+ when the user navigates or goes forward().
+*/
+
+/*!
+ \fn void Q3TextBrowser::sourceChanged(const QString& src)
+
+ This signal is emitted when the mime source has changed, \a src
+ being the new source.
+
+ Source changes happen both programmatically when calling
+ setSource(), forward(), backword() or home() or when the user
+ clicks on links or presses the equivalent key sequences.
+*/
+
+/*! \fn void Q3TextBrowser::highlighted (const QString &link)
+
+ This signal is emitted when the user has selected but not
+ activated a link in the document. \a link is the value of the \c
+ href i.e. the name of the target document.
+*/
+
+/*!
+ \fn void Q3TextBrowser::linkClicked(const QString& link)
+
+ This signal is emitted when the user clicks a link. The \a link is
+ the value of the \c href i.e. the name of the target document.
+
+ The \a link will be the absolute location of the document, based
+ on the value of the anchor's href tag and the current context of
+ the document.
+
+ \sa anchorClicked()
+*/
+
+/*!
+ \fn void Q3TextBrowser::anchorClicked(const QString& name, const QString &link)
+
+ This signal is emitted when the user clicks an anchor. The \a link is
+ the value of the \c href i.e. the name of the target document. The \a name
+ is the name of the anchor.
+
+ \sa linkClicked()
+*/
+
+/*!
+ Changes the document displayed to the previous document in the
+ list of documents built by navigating links. Does nothing if there
+ is no previous document.
+
+ \sa forward(), backwardAvailable()
+*/
+void Q3TextBrowser::backward()
+{
+ if (d->stack.count() <= 1)
+ return;
+ d->forwardStack.push(d->stack.pop());
+ setSource(d->stack.pop());
+ emit forwardAvailable(true);
+}
+
+/*!
+ Changes the document displayed to the next document in the list of
+ documents built by navigating links. Does nothing if there is no
+ next document.
+
+ \sa backward(), forwardAvailable()
+*/
+void Q3TextBrowser::forward()
+{
+ if (d->forwardStack.isEmpty())
+ return;
+ setSource(d->forwardStack.pop());
+ emit forwardAvailable(!d->forwardStack.isEmpty());
+}
+
+/*!
+ Changes the document displayed to be the first document the
+ browser displayed.
+*/
+void Q3TextBrowser::home()
+{
+ if (!d->home.isNull())
+ setSource(d->home);
+}
+
+/*!
+ The event \a e is used to provide the following keyboard shortcuts:
+ \table
+ \header \i Keypress \i Action
+ \row \i Alt+Left Arrow \i \l backward()
+ \row \i Alt+Right Arrow \i \l forward()
+ \row \i Alt+Up Arrow \i \l home()
+ \endtable
+*/
+void Q3TextBrowser::keyPressEvent(QKeyEvent * e)
+{
+ if (e->state() & Qt::AltButton) {
+ switch (e->key()) {
+ case Qt::Key_Right:
+ forward();
+ return;
+ case Qt::Key_Left:
+ backward();
+ return;
+ case Qt::Key_Up:
+ home();
+ return;
+ }
+ }
+ Q3TextEdit::keyPressEvent(e);
+}
+
+class QTextDetailPopup : public QWidget
+{
+public:
+ QTextDetailPopup()
+ : QWidget (0, "automatic QText detail widget", Qt::WType_Popup)
+ {
+ setAttribute(Qt::WA_DeleteOnClose, true);
+ }
+
+protected:
+ void mousePressEvent(QMouseEvent *)
+ {
+ close();
+ }
+};
+
+
+void Q3TextBrowser::popupDetail(const QString& contents, const QPoint& pos)
+{
+
+ const int shadowWidth = 6; // also used as '5' and '6' and even '8' below
+ const int vMargin = 8;
+ const int hMargin = 12;
+
+ QWidget* popup = new QTextDetailPopup;
+ popup->setAttribute(Qt::WA_NoSystemBackground, true);
+
+ Q3SimpleRichText* doc = new Q3SimpleRichText(contents, popup->font());
+ doc->adjustSize();
+ QRect r(0, 0, doc->width(), doc->height());
+
+ int w = r.width() + 2*hMargin;
+ int h = r.height() + 2*vMargin;
+
+ popup->resize(w + shadowWidth, h + shadowWidth);
+
+ // okay, now to find a suitable location
+ //###### we need a global fancy popup positioning somewhere
+ popup->move(pos - popup->rect().center());
+ if (popup->geometry().right() > QApplication::desktop()->width())
+ popup->move(QApplication::desktop()->width() - popup->width(),
+ popup->y());
+ if (popup->geometry().bottom() > QApplication::desktop()->height())
+ popup->move(popup->x(),
+ QApplication::desktop()->height() - popup->height());
+ if (popup->x() < 0)
+ popup->move(0, popup->y());
+ if (popup->y() < 0)
+ popup->move(popup->x(), 0);
+
+
+ popup->show();
+
+ // now for super-clever shadow stuff. super-clever mostly in
+ // how many window system problems it skirts around.
+
+ QPainter p(popup);
+ p.setPen(QApplication::palette().color(QPalette::Active, QPalette::WindowText));
+ p.drawRect(0, 0, w, h);
+ p.setPen(QApplication::palette().color(QPalette::Active, QPalette::Mid));
+ p.setBrush(QColor(255, 255, 240));
+ p.drawRect(1, 1, w-2, h-2);
+ p.setPen(Qt::black);
+
+ doc->draw(&p, hMargin, vMargin, r, popup->palette(), 0);
+ delete doc;
+
+ p.drawPoint(w + 5, 6);
+ p.drawLine(w + 3, 6,
+ w + 5, 8);
+ p.drawLine(w + 1, 6,
+ w + 5, 10);
+ int i;
+ for(i=7; i < h; i += 2)
+ p.drawLine(w, i,
+ w + 5, i + 5);
+ for(i = w - i + h; i > 6; i -= 2)
+ p.drawLine(i, h,
+ i + 5, h + 5);
+ for(; i > 0 ; i -= 2)
+ p.drawLine(6, h + 6 - i,
+ i + 5, h + 5);
+}
+
+/*!
+ \fn void Q3TextBrowser::setText(const QString &txt)
+
+ \overload
+
+ Sets the text to \a txt.
+*/
+
+/*!
+ \reimp
+*/
+
+void Q3TextBrowser::setText(const QString &txt, const QString &context)
+{
+ d->textOrSourceChanged = true;
+ d->curmark = QLatin1String("");
+ d->curmain = QLatin1String("");
+ Q3TextEdit::setText(txt, context);
+}
+
+void Q3TextBrowser::emitHighlighted(const QString &s)
+{
+ emit highlighted(s);
+}
+
+void Q3TextBrowser::emitLinkClicked(const QString &s)
+{
+ d->textOrSourceChanged = false;
+ emit linkClicked(s);
+ if (!d->textOrSourceChanged)
+ setSource(s);
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTBROWSER
diff --git a/src/qt3support/text/q3textbrowser.h b/src/qt3support/text/q3textbrowser.h
new file mode 100644
index 0000000..888685b
--- /dev/null
+++ b/src/qt3support/text/q3textbrowser.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTBROWSER_H
+#define Q3TEXTBROWSER_H
+
+#include <QtGui/qpixmap.h>
+#include <QtGui/qcolor.h>
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTBROWSER
+
+class Q3TextBrowserData;
+
+class Q_COMPAT_EXPORT Q3TextBrowser : public Q3TextEdit
+{
+ Q_OBJECT
+ Q_PROPERTY(QString source READ source WRITE setSource)
+
+ friend class Q3TextEdit;
+
+public:
+ Q3TextBrowser(QWidget* parent=0, const char* name=0);
+ ~Q3TextBrowser();
+
+ QString source() const;
+
+public Q_SLOTS:
+ virtual void setSource(const QString& name);
+ virtual void backward();
+ virtual void forward();
+ virtual void home();
+ virtual void reload();
+ void setText(const QString &txt) { setText(txt, QString()); }
+ virtual void setText(const QString &txt, const QString &context);
+
+Q_SIGNALS:
+ void backwardAvailable(bool);
+ void forwardAvailable(bool);
+ void sourceChanged(const QString&);
+ void highlighted(const QString&);
+ void linkClicked(const QString&);
+ void anchorClicked(const QString&, const QString&);
+
+protected:
+ void keyPressEvent(QKeyEvent * e);
+
+private:
+ Q_DISABLE_COPY(Q3TextBrowser)
+
+ void popupDetail(const QString& contents, const QPoint& pos);
+ bool linksEnabled() const { return true; }
+ void emitHighlighted(const QString &s);
+ void emitLinkClicked(const QString &s);
+ Q3TextBrowserData *d;
+};
+
+#endif // QT_NO_TEXTBROWSER
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTBROWSER_H
diff --git a/src/qt3support/text/q3textedit.cpp b/src/qt3support/text/q3textedit.cpp
new file mode 100644
index 0000000..7577dce
--- /dev/null
+++ b/src/qt3support/text/q3textedit.cpp
@@ -0,0 +1,7244 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textedit.h"
+
+#ifndef QT_NO_TEXTEDIT
+
+#include <private/q3richtext_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 "q3listbox.h"
+#include "qclipboard.h"
+#include "qcolordialog.h"
+#include "q3stylesheet.h"
+#include "q3dragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+#include "qregexp.h"
+#include "q3popupmenu.h"
+#include "qstack.h"
+#include "qmetaobject.h"
+#include "q3textbrowser.h"
+#include "private/q3syntaxhighlighter_p.h"
+#include "qtextformat.h"
+#ifndef QT_NO_IM
+#include <qinputcontext.h>
+#endif
+
+#ifndef QT_NO_ACCEL
+#include <qkeysequence.h>
+#define ACCEL_KEY(k) QLatin1Char('\t') + QString(QKeySequence(Qt::CTRL | Qt::Key_ ## k))
+#else
+#define ACCEL_KEY(k) QLatin1Char('\t' )+ QString(QLatin1String("Ctrl+" #k))
+#endif
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+#define LOGOFFSET(i) d->logOffset + i
+#endif
+
+QT_BEGIN_NAMESPACE
+
+struct QUndoRedoInfoPrivate
+{
+ Q3TextString text;
+};
+
+class Q3TextEditPrivate
+{
+public:
+ Q3TextEditPrivate()
+ :preeditStart(-1),preeditLength(-1),numPreeditSelections(0),ensureCursorVisibleInShowEvent(false),
+ tabChangesFocus(false),
+#ifndef QT_NO_CLIPBOARD
+ clipboard_mode(QClipboard::Clipboard),
+#endif
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ od(0), optimMode(false),
+ maxLogLines(-1),
+ logOffset(0),
+#endif
+ autoFormatting((uint)Q3TextEdit::AutoAll),
+ cursorRepaintMode(false),
+ cursorBlinkActive(false)
+
+ {
+ for (int i=0; i<7; i++)
+ id[i] = 0;
+ }
+ int id[7];
+ int preeditStart;
+ int preeditLength;
+ int numPreeditSelections;
+ uint ensureCursorVisibleInShowEvent : 1;
+ uint tabChangesFocus : 1;
+ QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized
+ QString pressedName;
+ QString onName;
+#ifndef QT_NO_CLIPBOARD
+ QClipboard::Mode clipboard_mode;
+#endif
+ QTimer *trippleClickTimer;
+ QPoint trippleClickPoint;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ Q3TextEditOptimPrivate * od;
+ bool optimMode : 1;
+ int maxLogLines;
+ int logOffset;
+#endif
+ Q3TextEdit::AutoFormatting autoFormatting;
+ uint cursorRepaintMode : 1;
+ uint cursorBlinkActive : 1;
+};
+
+#ifndef QT_NO_MIME
+class Q3RichTextDrag : public Q3TextDrag
+{
+public:
+ Q3RichTextDrag(QWidget *dragSource = 0, const char *name = 0);
+
+ void setPlainText(const QString &txt) { setText(txt); }
+ void setRichText(const QString &txt) { richTxt = txt; }
+
+ virtual QByteArray encodedData(const char *mime) const;
+ virtual const char* format(int i) const;
+
+ static bool decode(QMimeSource *e, QString &str, const QString &mimetype,
+ const QString &subtype);
+ static bool canDecode(QMimeSource* e);
+
+private:
+ QString richTxt;
+
+};
+
+Q3RichTextDrag::Q3RichTextDrag(QWidget *dragSource, const char *name)
+ : Q3TextDrag(dragSource, name)
+{
+}
+
+QByteArray Q3RichTextDrag::encodedData(const char *mime) const
+{
+ if (qstrcmp("application/x-qrichtext", mime) == 0) {
+ return richTxt.toUtf8(); // #### perhaps we should use USC2 instead?
+ } else
+ return Q3TextDrag::encodedData(mime);
+}
+
+bool Q3RichTextDrag::decode(QMimeSource *e, QString &str, const QString &mimetype,
+ const QString &subtype)
+{
+ if (mimetype == QLatin1String("application/x-qrichtext")) {
+ // do richtext decode
+ const char *mime;
+ int i;
+ for (i = 0; (mime = e->format(i)); ++i) {
+ if (qstrcmp("application/x-qrichtext", mime) != 0)
+ continue;
+ str = QString::fromUtf8(e->encodedData(mime));
+ return true;
+ }
+ return false;
+ }
+
+ // do a regular text decode
+ QString st = subtype;
+ return Q3TextDrag::decode(e, str, st);
+}
+
+bool Q3RichTextDrag::canDecode(QMimeSource* e)
+{
+ if (e->provides("application/x-qrichtext"))
+ return true;
+ return Q3TextDrag::canDecode(e);
+}
+
+const char* Q3RichTextDrag::format(int i) const
+{
+ if (Q3TextDrag::format(i))
+ return Q3TextDrag::format(i);
+ if (Q3TextDrag::format(i-1))
+ return "application/x-qrichtext";
+ return 0;
+}
+
+#endif
+
+static bool block_set_alignment = false;
+
+/*!
+ \class Q3TextEdit
+ \brief The Q3TextEdit widget provides a powerful single-page rich text editor.
+
+ \compat
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ Q3TextEdit is an advanced WYSIWYG viewer/editor supporting rich
+ text formatting using HTML-style tags. It is optimized to handle
+ large documents and to respond quickly to user input.
+
+ Q3TextEdit has four modes of operation:
+ \table
+ \header \i Mode \i Command \i Notes
+ \row \i Plain Text Editor \i setTextFormat(Qt::PlainText)
+ \i Set text with setText(); text() returns plain text. Text
+ attributes (e.g. colors) can be set, but plain text is always
+ returned.
+ \row \i Rich Text Editor \i setTextFormat(Qt::RichText)
+ \i Set text with setText(); text() returns rich text. Rich
+ text editing is fairly limited. You can't set margins or
+ insert images for example (although you can read and
+ correctly display files that have margins set and that
+ include images). This mode is mostly useful for editing small
+ amounts of rich text.
+ \row \i Text Viewer \i setReadOnly(true)
+ \i Set text with setText() or append() (which has no undo
+ history so is faster and uses less memory); text() returns
+ plain or rich text depending on the textFormat(). This mode
+ can correctly display a large subset of HTML tags.
+ \row \i Log Viewer \i setTextFormat(Qt::LogText)
+ \i Append text using append(). The widget is set to be read
+ only and rich text support is disabled although a few HTML
+ tags (for color, bold, italic and underline) may be used.
+ (See \link #logtextmode Qt::LogText mode\endlink for details.)
+ \endtable
+
+ Q3TextEdit can be used as a syntax highlighting editor when used in
+ conjunction with QSyntaxHighlighter.
+
+ We recommend that you always call setTextFormat() to set the mode
+ you want to use. If you use Qt::AutoText then setText() and
+ append() will try to determine whether the text they are given is
+ plain text or rich text. If you use Qt::RichText then setText() and
+ append() will assume that the text they are given is rich text.
+ insert() simply inserts the text it is given.
+
+ Q3TextEdit works on paragraphs and characters. A paragraph is a
+ formatted string which is word-wrapped to fit into the width of
+ the widget. By default when reading plain text, one newline
+ signify a paragraph. A document consists of zero or more
+ paragraphs, indexed from 0. Characters are indexed on a
+ per-paragraph basis, also indexed from 0. The words in the
+ paragraph are aligned in accordance with the paragraph's
+ alignment(). Paragraphs are separated by hard line breaks. Each
+ character within a paragraph has its own attributes, for example,
+ font and color.
+
+ The text edit documentation uses the following concepts:
+ \list
+ \i \e{current format} --
+ this is the format at the current cursor position, \e and it
+ is the format of the selected text if any.
+ \i \e{current paragraph} -- the paragraph which contains the
+ cursor.
+ \endlist
+
+ Q3TextEdit can display images (using Q3MimeSourceFactory), lists and
+ tables. If the text is too large to view within the text edit's
+ viewport, scroll bars 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(). Custom tags can be created and placed in a custom
+ style sheet. Change the style sheet with \l{setStyleSheet()}; see
+ Q3StyleSheet for details. The images identified by image tags are
+ displayed if they can be interpreted using the text edit's
+ \l{Q3MimeSourceFactory}; 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 Q3TextEdit, 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 Q3StyleSheet 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.
+
+ Note that we do not intend to add a full-featured web browser
+ widget to Qt (because that would easily double Qt's size and only
+ a few applications would benefit from it). The rich
+ text support in Qt is designed to provide a fast, portable and
+ efficient way to add reasonable online help facilities to
+ applications, and to provide a basis for rich text editors.
+
+ \section1 Using Q3TextEdit as a Display Widget
+
+ Q3TextEdit can display a large HTML subset, including tables and
+ images.
+
+ The text is set or replaced using setText() which deletes any
+ existing text and replaces it with the text passed in the
+ setText() call. If you call setText() with legacy HTML (with
+ setTextFormat(Qt::RichText) in force), and then call text(), the text
+ that is returned may have different markup, but will render the
+ same. Text can be inserted with insert(), paste(), pasteSubType()
+ and append(). Text that is appended does not go into the undo
+ history; this makes append() faster and consumes less memory. Text
+ can also be cut(). The entire text is deleted with clear() and the
+ selected text is deleted with removeSelectedText(). Selected
+ (marked) text can also be deleted with del() (which will delete
+ the character to the right of the cursor if no text is selected).
+
+ Loading and saving text is achieved using setText() and text(),
+ for example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 0
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setWordWrap() function is used to
+ specify the kind of word wrap you want, or \c NoWrap if you don't
+ want any wrapping. Call setWordWrap() to set a fixed pixel width
+ \c FixedPixelWidth, or character column (e.g. 80 column) \c
+ FixedColumnWidth with the pixels or columns specified with
+ setWrapColumnOrWidth(). If you use word wrap to the widget's width
+ \c WidgetWidth, you can specify whether to break on whitespace or
+ anywhere with setWrapPolicy().
+
+ The background color is set differently than other widgets, using
+ setPaper(). You specify a brush style which could be a plain color
+ or a complex pixmap.
+
+ Hypertext links are automatically underlined; this can be changed
+ with setLinkUnderline(). The tab stop width is set with
+ setTabStopWidth().
+
+ The zoomIn() and zoomOut() functions can be used to resize the
+ text by increasing (decreasing for zoomOut()) the point size used.
+ Images are not affected by the zoom functions.
+
+ The lines() function returns the number of lines in the text and
+ paragraphs() returns the number of paragraphs. The number of lines
+ within a particular paragraph is returned by linesOfParagraph().
+ The length of the entire text in characters is returned by
+ length().
+
+ You can scroll to an anchor in the text, e.g.
+ \c{<a name="anchor">} with scrollToAnchor(). The find() function
+ can be used to find and select a given string within the text.
+
+ A read-only Q3TextEdit provides the same functionality as the
+ (obsolete) QTextView. (QTextView is still supplied for
+ compatibility with old code.)
+
+ \section2 Read-only key bindings
+
+ When Q3TextEdit 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 Up \i Move one line up
+ \row \i Down \i Move one line down
+ \row \i Left \i Move one character left
+ \row \i Right \i Move one character right
+ \row \i PageUp \i Move one (viewport) page up
+ \row \i PageDown \i Move one (viewport) page down
+ \row \i Home \i Move to the beginning of the text
+ \row \i End \i Move to the end of the text
+ \row \i Shift+Wheel
+ \i Scroll the page horizontally (the Wheel is the mouse wheel)
+ \row \i 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 Q3MimeSourceFactory uses to resolve
+ the locations of files and images. It is passed to the
+ mimeSourceFactory() when quering data. (See Q3TextEdit() and
+ \l{context()}.)
+
+ \target logtextmode
+ \section2 Using Q3TextEdit in Qt::LogText Mode
+
+ Setting the text format to Qt::LogText puts the widget in a special
+ mode which is optimized for very large texts. In this mode editing
+ and rich text support are disabled (the widget is explicitly set
+ to read-only mode). This allows the text to be stored in a
+ different, more memory efficient manner. However, a certain degree
+ of text formatting is supported through the use of formatting
+ tags. A tag is delimited by \c < and \c {>}. The characters \c
+ {<}, \c > and \c & are escaped by using \c {&lt;}, \c {&gt;} and
+ \c {&amp;}. A tag pair consists of a left and a right tag (or
+ open/close tags). Left-tags mark the starting point for
+ formatting, while right-tags mark the ending point. A right-tag
+ always start with a \c / before the tag keyword. For example \c
+ <b> and \c </b> are a tag pair. Tags can be nested, but they
+ have to be closed in the same order as they are opened. For
+ example, \c <b><u></u></b> is valid, while \c
+ <b><u></b></u> will output an error message.
+
+ By using tags it is possible to change the color, bold, italic and
+ underline settings for a piece of text. A color can be specified
+ by using the HTML font tag \c {<font color=colorname>}. The color
+ name can be one of the color names from the X11 color database, or
+ a RGB hex value (e.g \c {#00ff00}). Example of valid color tags:
+ \c {<font color=red>}, \c{<font color="light blue">},\c {<font
+ color="#223344">}. Bold, italic and underline settings can be
+ specified by the tags \c {<b>}, \c <i> and \c {<u>}. Note that a
+ tag does not necessarily have to be closed. A valid example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 1
+
+ Stylesheets can also be used in Qt::LogText mode. To create and use a
+ custom tag, you could do the following:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 2
+ Note that only the color, bold, underline and italic attributes of
+ a Q3StyleSheetItem is used in Qt::LogText mode.
+
+ Note that you can use setMaxLogLines() to limit the number of
+ lines the widget can hold in Qt::LogText mode.
+
+ There are a few things that you need to be aware of when the
+ widget is in this mode:
+ \list
+ \i Functions that deal with rich text formatting and cursor
+ movement will not work or return anything valid.
+ \i Lines are equivalent to paragraphs.
+ \endlist
+
+ \section1 Using Q3TextEdit as an Editor
+
+ All the information about using Q3TextEdit as a display widget also
+ applies here.
+
+ The current format's attributes are set with setItalic(),
+ setBold(), setUnderline(), setFamily() (font family),
+ setPointSize(), setColor() and setCurrentFont(). The current
+ paragraph's alignment is set with setAlignment().
+
+ Use setSelection() to select text. The setSelectionAttributes()
+ function is used to indicate how selected text should be
+ displayed. Use hasSelectedText() to find out if any text is
+ selected. The currently selected text's position is available
+ using getSelection() and the selected text itself is returned by
+ selectedText(). The selection can be copied to the clipboard with
+ copy(), or cut to the clipboard with cut(). It can be deleted with
+ removeSelectedText(). The entire text can be selected (or
+ unselected) using selectAll(). Q3TextEdit 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.
+
+ Q3TextEdit 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.
+
+ \section2 Editing key bindings
+
+ The list of key-bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i Backspace \i Delete the character to the left of the cursor
+ \row \i Delete \i Delete the character to the right of the cursor
+ \row \i Ctrl+A \i Move the cursor to the beginning of the line
+ \row \i Ctrl+B \i Move the cursor one character left
+ \row \i Ctrl+C \i Copy the marked text to the clipboard (also
+ Ctrl+Insert under Windows)
+ \row \i Ctrl+D \i Delete the character to the right of the cursor
+ \row \i Ctrl+E \i Move the cursor to the end of the line
+ \row \i Ctrl+F \i Move the cursor one character right
+ \row \i Ctrl+H \i Delete the character to the left of the cursor
+ \row \i Ctrl+K \i Delete to end of line
+ \row \i Ctrl+N \i Move the cursor one line down
+ \row \i Ctrl+P \i Move the cursor one line up
+ \row \i Ctrl+V \i Paste the clipboard text into line edit
+ (also Shift+Insert under Windows)
+ \row \i Ctrl+X \i Cut the marked text, copy to clipboard
+ (also Shift+Delete under Windows)
+ \row \i Ctrl+Z \i Undo the last operation
+ \row \i Ctrl+Y \i Redo the last operation
+ \row \i Left \i Move the cursor one character left
+ \row \i Ctrl+Left \i Move the cursor one word left
+ \row \i Right \i Move the cursor one character right
+ \row \i Ctrl+Right \i Move the cursor one word right
+ \row \i Up \i Move the cursor one line up
+ \row \i Ctrl+Qt::Up \i Move the cursor one word up
+ \row \i DownArrow \i Move the cursor one line down
+ \row \i Ctrl+Down \i Move the cursor one word down
+ \row \i PageUp \i Move the cursor one page up
+ \row \i PageDown \i Move the cursor one page down
+ \row \i Home \i Move the cursor to the beginning of the line
+ \row \i Ctrl+Home \i Move the cursor to the beginning of the text
+ \row \i End \i Move the cursor to the end of the line
+ \row \i Ctrl+End \i Move the cursor to the end of the text
+ \row \i Shift+Wheel \i Scroll the page horizontally
+ (the Wheel is the mouse wheel)
+ \row \i 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, \e{Shift+Right}
+ will select the character to the right, and \e{Shift+Ctrl+Right} 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().
+*/
+
+/*!
+ \enum Q3TextEdit::AutoFormattingFlag
+
+ \value AutoNone Do not perform any automatic formatting
+ \value AutoBulletList Only automatically format bulletted lists
+ \value AutoAll Apply all available autoformatting
+*/
+
+
+/*!
+ \enum Q3TextEdit::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.
+
+ \value ActionWordBackspace Delete the word to the left of the
+ cursor position.
+
+ \value ActionWordDelete Delete the word to the right of the
+ cursor position
+
+*/
+
+/*!
+ \enum Q3TextEdit::VerticalAlignment
+
+ This enum is used to set the vertical alignment of the text.
+
+ \value AlignNormal Normal alignment
+ \value AlignSuperScript Superscript
+ \value AlignSubScript Subscript
+*/
+
+/*!
+ \enum Q3TextEdit::TextInsertionFlags
+
+ \internal
+
+ \value RedoIndentation
+ \value CheckNewLines
+ \value RemoveSelected
+*/
+
+
+/*!
+ \fn void Q3TextEdit::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 Q3TextEdit::textChanged()
+
+ This signal is emitted whenever the text in the text edit changes.
+
+ \sa setText() append()
+*/
+
+/*!
+ \fn void Q3TextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa setSelection() copyAvailable()
+*/
+
+/*! \fn Q3TextDocument *Q3TextEdit::document() const
+
+ \internal
+
+ This function returns the Q3TextDocument which is used by the text
+ edit.
+*/
+
+/*! \fn void Q3TextEdit::setDocument(Q3TextDocument *doc)
+
+ \internal
+
+ This function sets the Q3TextDocument 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
+ Q3TextDocument 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 Q3TextEdit::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 viewport page up
+
+ \value MovePgDown Moves the cursor one viewport page down
+*/
+
+/*!
+ \property Q3TextEdit::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 Q3TextEdit::setCurrentFont(const QFont &f)
+
+ Sets the font of the current format to \a f.
+
+ If the widget is in Qt::LogText mode this function will do
+ nothing. Use setFont() instead.
+
+ \sa currentFont() setPointSize() setFamily()
+*/
+
+/*!
+ \property Q3TextEdit::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 Q3TextEdit::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 Q3TextEdit::modificationChanged(bool m)
+
+ This signal is emitted when the modification status of the
+ document has changed. If \a m is true, the document was modified,
+ otherwise the modification state has been reset to unmodified.
+
+ \sa modified
+*/
+
+/*!
+ \fn void Q3TextEdit::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 Q3TextEdit::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 Q3TextEdit::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 Q3TextEdit::currentVerticalAlignmentChanged(Q3TextEdit::VerticalAlignment a)
+
+ This signal is emitted if the vertical alignment of the current
+ format has changed.
+
+ The new vertical alignment is \a a.
+*/
+
+/*!
+ \fn void Q3TextEdit::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 Q3TextEdit::cursorPositionChanged(Q3TextCursor *c)
+
+ \internal
+*/
+
+/*!
+ \fn void Q3TextEdit::cursorPositionChanged(int para, int pos)
+
+ \overload
+
+ This signal is emitted if the position of the cursor has changed.
+ \a para contains the paragraph index and \a pos contains the
+ character position within the paragraph.
+
+ \sa setCursorPosition()
+*/
+
+/*!
+ \fn void Q3TextEdit::clicked(int para, int pos)
+
+ This signal is emitted when the mouse is clicked on the paragraph
+ \a para at character position \a pos.
+
+ \sa doubleClicked()
+*/
+
+/*! \fn void Q3TextEdit::doubleClicked(int para, int pos)
+
+ This signal is emitted when the mouse is double-clicked on the
+ paragraph \a para at character position \a pos.
+
+ \sa clicked()
+*/
+
+
+/*!
+ \fn void Q3TextEdit::returnPressed()
+
+ This signal is emitted if the user pressed the Return or the Enter
+ key.
+*/
+
+/*!
+ \fn Q3TextCursor *Q3TextEdit::textCursor() const
+
+ Returns the text edit's text cursor.
+
+ \warning Q3TextCursor is not in the public API, but in special
+ circumstances you might wish to use it.
+*/
+
+/*!
+ Constructs an empty Q3TextEdit called \a name, with parent \a
+ parent.
+*/
+
+Q3TextEdit::Q3TextEdit(QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
+ doc(new Q3TextDocument(0)), undoRedoInfo(doc)
+{
+ init();
+}
+
+/*!
+ Constructs a Q3TextEdit called \a name, with parent \a parent. The
+ text edit will display the text \a text using context \a context.
+
+ The \a context is a path which the text edit's Q3MimeSourceFactory
+ 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
+ Q3MimeSourceFactory 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
+ Q3MimeSourceFactory 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".
+*/
+
+Q3TextEdit::Q3TextEdit(const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
+ doc(new Q3TextDocument(0)), undoRedoInfo(doc)
+{
+ init();
+ setText(text, context);
+}
+
+/*!
+ Destructor.
+*/
+
+Q3TextEdit::~Q3TextEdit()
+{
+ delete undoRedoInfo.d;
+ undoRedoInfo.d = 0;
+ delete cursor;
+ delete doc;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ delete d->od;
+#endif
+ delete d;
+}
+
+void Q3TextEdit::init()
+{
+ d = new Q3TextEditPrivate;
+ doc->formatCollection()->setPaintDevice(this);
+ undoEnabled = true;
+ readonly = true;
+ setReadOnly(false);
+ setFrameStyle(LineEditPanel | Sunken);
+ connect(doc, SIGNAL(minimumWidthChanged(int)),
+ this, SLOT(documentWidthChanged(int)));
+
+ mousePressed = false;
+ inDoubleClick = false;
+ modified = false;
+ mightStartDrag = false;
+ onLink.clear();
+ d->onName.clear();
+ overWrite = false;
+ wrapMode = WidgetWidth;
+ wrapWidth = -1;
+ wPolicy = AtWhiteSpace;
+ inDnD = false;
+ doc->setFormatter(new Q3TextFormatterBreakWords);
+ QFont f = Q3ScrollView::font();
+ if (f.kerning())
+ f.setKerning(false);
+ doc->formatCollection()->defaultFormat()->setFont(f);
+ doc->formatCollection()->defaultFormat()->setColor(palette().color(QPalette::Text));
+ currentFormat = doc->formatCollection()->defaultFormat();
+ currentAlignment = Qt::AlignAuto;
+
+ setBackgroundRole(QPalette::Base);
+ viewport()->setBackgroundRole(QPalette::Base);
+
+ viewport()->setAcceptDrops(true);
+ resizeContents(0, doc->lastParagraph() ?
+ (doc->lastParagraph()->paragId() + 1) * doc->formatCollection()->defaultFormat()->height() : 0);
+
+ setAttribute(Qt::WA_KeyCompression, true);
+ viewport()->setMouseTracking(true);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ cursor = new Q3TextCursor(doc);
+
+ formatTimer = new QTimer(this);
+ connect(formatTimer, SIGNAL(timeout()),
+ this, SLOT(formatMore()));
+ lastFormatted = doc->firstParagraph();
+
+ scrollTimer = new QTimer(this);
+ connect(scrollTimer, SIGNAL(timeout()),
+ this, SLOT(autoScrollTimerDone()));
+
+ interval = 0;
+ changeIntervalTimer = new QTimer(this);
+ connect(changeIntervalTimer, SIGNAL(timeout()),
+ this, SLOT(doChangeInterval()));
+
+ cursorVisible = true;
+ blinkTimer = new QTimer(this);
+ connect(blinkTimer, SIGNAL(timeout()),
+ this, SLOT(blinkCursor()));
+
+#ifndef QT_NO_DRAGANDDROP
+ dragStartTimer = new QTimer(this);
+ connect(dragStartTimer, SIGNAL(timeout()),
+ this, SLOT(startDrag()));
+#endif
+
+ d->trippleClickTimer = new QTimer(this);
+
+ formatMore();
+
+ blinkCursorVisible = false;
+
+ viewport()->setFocusProxy(this);
+ viewport()->setFocusPolicy(Qt::WheelFocus);
+ setFocusPolicy(Qt::WheelFocus);
+ setInputMethodEnabled(true);
+ viewport()->installEventFilter(this);
+ connect(this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased()));
+ connect(this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased()));
+ installEventFilter(this);
+}
+
+void Q3TextEdit::paintDocument(bool drawAll, QPainter *p, int cx, int cy, int cw, int ch)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ Q_ASSERT(!d->optimMode);
+ if (d->optimMode)
+ return;
+#endif
+
+ bool drawCur = blinkCursorVisible && (hasFocus() || viewport()->hasFocus());
+ if ((hasSelectedText() && !style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, 0, this)) ||
+ isReadOnly() || !cursorVisible)
+ drawCur = false;
+ QPalette pal = palette();
+ if (doc->paper())
+ pal.setBrush(QPalette::Base, *doc->paper());
+
+ if (contentsY() < doc->y()) {
+ p->fillRect(contentsX(), contentsY(), visibleWidth(), doc->y(),
+ pal.base());
+ }
+ if (drawAll && doc->width() - contentsX() < cx + cw) {
+ p->fillRect(doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
+ pal.base());
+ }
+
+ p->setBrushOrigin(-contentsX(), -contentsY());
+
+ lastFormatted = doc->draw(p, cx, cy, cw, ch, pal, !drawAll, drawCur, cursor);
+
+ if (lastFormatted == doc->lastParagraph())
+ resizeContents(contentsWidth(), doc->height());
+
+ if (contentsHeight() < visibleHeight() && (!doc->lastParagraph() || doc->lastParagraph()->isValid()) && drawAll)
+ p->fillRect(0, contentsHeight(), visibleWidth(),
+ visibleHeight() - contentsHeight(), pal.base());
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimDrawContents(p, cx, cy, cw, ch);
+ return;
+ }
+#endif
+ paintDocument(true, p, cx, cy, cw, ch);
+ int v;
+ p->setPen(palette().color(foregroundRole()));
+ 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;
+ }
+ }
+}
+
+/*!
+ \internal
+*/
+
+void Q3TextEdit::drawContents(QPainter *p)
+{
+ if (horizontalScrollBar()->isVisible() &&
+ verticalScrollBar()->isVisible()) {
+ const QRect verticalRect = verticalScrollBar()->geometry();
+ const QRect horizontalRect = horizontalScrollBar()->geometry();
+
+ QRect cornerRect;
+ cornerRect.setTop(verticalRect.bottom());
+ cornerRect.setBottom(horizontalRect.bottom());
+ cornerRect.setLeft(verticalRect.left());
+ cornerRect.setRight(verticalRect.right());
+
+ p->fillRect(cornerRect, palette().background());
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3TextEdit::event(QEvent *e)
+{
+ if (e->type() == QEvent::AccelOverride && !isReadOnly()) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ switch(ke->state()) {
+ case Qt::NoButton:
+ case Qt::Keypad:
+ case Qt::ShiftButton:
+ if (ke->key() < Qt::Key_Escape) {
+ ke->accept();
+ } else {
+ switch (ke->key()) {
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Delete:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Backspace:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ break;
+
+ case Qt::ControlButton:
+ case Qt::ControlButton|Qt::ShiftButton:
+ case Qt::ControlButton|Qt::Keypad:
+ case Qt::ControlButton|Qt::ShiftButton|Qt::Keypad:
+ switch (ke->key()) {
+ case Qt::Key_Tab:
+ case Qt::Key_Backtab:
+ ke->ignore();
+ break;
+// Those are too frequently used for application functionality
+/* case Qt::Key_A:
+ case Qt::Key_B:
+ case Qt::Key_D:
+ case Qt::Key_E:
+ case Qt::Key_F:
+ case Qt::Key_H:
+ case Qt::Key_I:
+ case Qt::Key_K:
+ case Qt::Key_N:
+ case Qt::Key_P:
+ case Qt::Key_T:
+*/
+ case Qt::Key_C:
+ case Qt::Key_V:
+ case Qt::Key_X:
+ case Qt::Key_Y:
+ case Qt::Key_Z:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+#if defined (Q_WS_WIN)
+ case Qt::Key_Insert:
+ case Qt::Key_Delete:
+#endif
+ ke->accept();
+ default:
+ break;
+ }
+ break;
+
+ default:
+ switch (ke->key()) {
+#if defined (Q_WS_WIN)
+ case Qt::Key_Insert:
+ ke->accept();
+#endif
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ if (e->type() == QEvent::Show) {
+ if (
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ !d->optimMode &&
+#endif
+ d->ensureCursorVisibleInShowEvent ) {
+ ensureCursorVisible();
+ d->ensureCursorVisibleInShowEvent = false;
+ }
+ if (!d->scrollToAnchor.isEmpty()) {
+ scrollToAnchor(d->scrollToAnchor);
+ d->scrollToAnchor.clear();
+ }
+ }
+ return QWidget::event(e);
+}
+
+/*!
+ Processes the key event, \a e. By default key events are used to
+ provide keyboard navigation and text editing.
+*/
+
+void Q3TextEdit::keyPressEvent(QKeyEvent *e)
+{
+ changeIntervalTimer->stop();
+ interval = 10;
+ bool unknownKey = false;
+ if (isReadOnly()) {
+ if (!handleReadOnlyKeyEvent(e))
+ Q3ScrollView::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->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ }
+
+ bool clearUndoRedoInfo = true;
+
+
+ switch (e->key()) {
+ case Qt::Key_Left:
+ case Qt::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->paragraph()->string()->isRightToLeft() == (e->key() == Qt::Key_Right))
+ a = e->state() & Qt::ControlButton ? MoveWordBackward : MoveBackward;
+ else
+ a = e->state() & Qt::ControlButton ? MoveWordForward : MoveForward;
+ moveCursor(a, e->state() & Qt::ShiftButton);
+ break;
+ }
+ case Qt::Key_Up:
+ moveCursor(e->state() & Qt::ControlButton ? MovePgUp : MoveUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Down:
+ moveCursor(e->state() & Qt::ControlButton ? MovePgDown : MoveDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Home:
+ moveCursor(e->state() & Qt::ControlButton ? MoveHome : MoveLineStart, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_End:
+ moveCursor(e->state() & Qt::ControlButton ? MoveEnd : MoveLineEnd, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Prior:
+ moveCursor(MovePgUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Next:
+ moveCursor(MovePgDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Return: case Qt::Key_Enter:
+ if (doc->hasSelection(Q3TextDocument::Standard, false))
+ removeSelectedText();
+ if (textFormat() == Qt::RichText && (e->state() & Qt::ControlButton)) {
+ // Ctrl-Enter inserts a line break in rich text mode
+ insert(QString(QChar(QChar::LineSeparator)), true, false);
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ clearUndoRedoInfo = false;
+ doKeyboardAction(ActionReturn);
+ emit returnPressed();
+ }
+ break;
+ case Qt::Key_Delete:
+#if defined (Q_WS_WIN)
+ if (e->state() & Qt::ShiftButton) {
+ cut();
+ break;
+ } else
+#endif
+ if (doc->hasSelection(Q3TextDocument::Standard, true)) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordDelete
+ : ActionDelete);
+ clearUndoRedoInfo = false;
+
+ break;
+ case Qt::Key_Insert:
+ if (e->state() & Qt::ShiftButton)
+ paste();
+#if defined (Q_WS_WIN)
+ else if (e->state() & Qt::ControlButton)
+ copy();
+#endif
+ else
+ setOverwriteMode(!isOverwriteMode());
+ break;
+ case Qt::Key_Backspace:
+#if defined (Q_WS_WIN)
+ if (e->state() & Qt::AltButton) {
+ if (e->state() & Qt::ControlButton) {
+ break;
+ } else if (e->state() & Qt::ShiftButton) {
+ redo();
+ break;
+ } else {
+ undo();
+ break;
+ }
+ } else
+#endif
+ if (doc->hasSelection(Q3TextDocument::Standard, true)) {
+ removeSelectedText();
+ break;
+ }
+
+ doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordBackspace
+ : ActionBackspace);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Qt::Key_F18: // Paste key on Sun keyboards
+ paste();
+ break;
+ case Qt::Key_F20: // Cut key on Sun keyboards
+ cut();
+ break;
+ case Qt::Key_Direction_L:
+ if (doc->textFormat() == Qt::PlainText) {
+ // change the whole doc
+ Q3TextParagraph *p = doc->firstParagraph();
+ while (p) {
+ p->setDirection(QChar::DirL);
+ p->setAlignment(Qt::AlignLeft);
+ p->invalidate(0);
+ p = p->next();
+ }
+ } else {
+ if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL)
+ return;
+ cursor->paragraph()->setDirection(QChar::DirL);
+ if (cursor->paragraph()->length() <= 1&&
+ ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
+ setAlignment(Qt::AlignLeft);
+ }
+ repaintChanged();
+ break;
+ case Qt::Key_Direction_R:
+ if (doc->textFormat() == Qt::PlainText) {
+ // change the whole doc
+ Q3TextParagraph *p = doc->firstParagraph();
+ while (p) {
+ p->setDirection(QChar::DirR);
+ p->setAlignment(Qt::AlignRight);
+ p->invalidate(0);
+ p = p->next();
+ }
+ } else {
+ if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR)
+ return;
+ cursor->paragraph()->setDirection(QChar::DirR);
+ if (cursor->paragraph()->length() <= 1&&
+ ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
+ setAlignment(Qt::AlignRight);
+ }
+ repaintChanged();
+ break;
+ default: {
+ unsigned char ascii = e->text().length() ? e->text().unicode()->latin1() : 0;
+ if (e->text().length() &&
+ ((!(e->state() & Qt::ControlButton) &&
+#ifndef Q_OS_MAC
+ !(e->state() & Qt::AltButton) &&
+#endif
+ !(e->state() & Qt::MetaButton)) ||
+ (((e->state() & (Qt::ControlButton | Qt::AltButton))) == (Qt::ControlButton|Qt::AltButton))) &&
+ (!ascii || ascii >= 32 || e->text() == QString(QLatin1Char('\t')))) {
+ clearUndoRedoInfo = false;
+ if (e->key() == Qt::Key_Tab) {
+ if (d->tabChangesFocus) {
+ e->ignore();
+ break;
+ }
+ if (textFormat() == Qt::RichText && cursor->index() == 0
+ && (cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth())) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ cursor->paragraph()->setListDepth(cursor->paragraph()->listDepth() +1);
+ clearUndoRedo();
+ drawCursor(false);
+ repaintChanged();
+ drawCursor(true);
+ break;
+ }
+ } else if (e->key() == Qt::Key_BackTab) {
+ if (d->tabChangesFocus) {
+ e->ignore();
+ break;
+ }
+ }
+
+ if ((autoFormatting() & AutoBulletList) &&
+ textFormat() == Qt::RichText && cursor->index() == 0
+ && !cursor->paragraph()->isListItem()
+ && (e->text()[0] == QLatin1Char('-') || e->text()[0] == QLatin1Char('*'))) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ setParagType(Q3StyleSheetItem::DisplayListItem, Q3StyleSheetItem::ListDisc);
+ clearUndoRedo();
+ drawCursor(false);
+ repaintChanged();
+ drawCursor(true);
+ break;
+ }
+ if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(Q3TextDocument::Standard)) {
+ doKeyboardAction(ActionDelete);
+ clearUndoRedoInfo = false;
+ }
+ QString t = e->text();
+ insert(t, true, false);
+ break;
+ } else if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Qt::Key_V:
+ paste();
+ break;
+ case Qt::Key_X:
+ cut();
+ break;
+ case Qt::Key_I: case Qt::Key_T: case Qt::Key_Tab:
+ if (!d->tabChangesFocus)
+ indent();
+ break;
+ case Qt::Key_A:
+#if defined(Q_WS_X11)
+ moveCursor(MoveLineStart, e->state() & Qt::ShiftButton);
+#else
+ selectAll(true);
+#endif
+ break;
+ case Qt::Key_B:
+ moveCursor(MoveBackward, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_F:
+ moveCursor(MoveForward, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_D:
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction(ActionDelete);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_H:
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ break;
+ }
+ if (!cursor->paragraph()->prev() &&
+ cursor->atParagStart())
+ break;
+
+ doKeyboardAction(ActionBackspace);
+ clearUndoRedoInfo = false;
+ break;
+ case Qt::Key_E:
+ moveCursor(MoveLineEnd, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_N:
+ moveCursor(MoveDown, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_P:
+ moveCursor(MoveUp, e->state() & Qt::ShiftButton);
+ break;
+ case Qt::Key_Z:
+ if(e->state() & Qt::ShiftButton)
+ redo();
+ else
+ undo();
+ break;
+ case Qt::Key_Y:
+ redo();
+ break;
+ case Qt::Key_K:
+ doKeyboardAction(ActionKill);
+ break;
+#if defined(Q_WS_WIN)
+ case Qt::Key_Insert:
+ copy();
+ break;
+ case Qt::Key_Delete:
+ del();
+ break;
+#endif
+ default:
+ unknownKey = false;
+ break;
+ }
+ } else {
+ unknownKey = true;
+ }
+ }
+ }
+
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ if (clearUndoRedoInfo)
+ clearUndoRedo();
+ changeIntervalTimer->start(100, true);
+ if (unknownKey)
+ e->ignore();
+}
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::inputMethodEvent(QInputMethodEvent *e)
+{
+ if (isReadOnly()) {
+ e->ignore();
+ return;
+ }
+
+ if (hasSelectedText())
+ removeSelectedText();
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::IME;
+
+ bool oldupdate = updatesEnabled();
+ if (oldupdate)
+ setUpdatesEnabled(false);
+ bool sigs_blocked = signalsBlocked();
+ blockSignals(true);
+ const int preeditSelectionBase = 31900;
+ for (int i = 0; i < d->numPreeditSelections; ++i)
+ doc->removeSelection(preeditSelectionBase + i);
+ d->numPreeditSelections = 0;
+
+ if (d->preeditLength > 0 && cursor->paragraph()) {
+ cursor->setIndex(d->preeditStart);
+ cursor->paragraph()->remove(d->preeditStart, d->preeditLength);
+ d->preeditStart = d->preeditLength = -1;
+ }
+
+ if (!e->commitString().isEmpty() || e->replacementLength()) {
+ int c = cursor->index(); // cursor position after insertion of commit string
+ if (e->replacementStart() <= 0)
+ c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength());
+ cursor->setIndex(cursor->index() + e->replacementStart());
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ cursor->setIndex(cursor->index() + e->replacementLength());
+ doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
+ removeSelectedText();
+ if (undoRedoInfo.type == UndoRedoInfo::IME)
+ undoRedoInfo.type = UndoRedoInfo::Invalid;
+ insert(e->commitString());
+ undoRedoInfo.type = UndoRedoInfo::IME;
+ cursor->setIndex(c);
+ }
+
+ if (!e->preeditString().isEmpty()) {
+ d->preeditStart = cursor->index();
+ d->preeditLength = e->preeditString().length();
+ insert(e->preeditString());
+ cursor->setIndex(d->preeditStart);
+
+ Q3TextCursor c = *cursor;
+ for (int i = 0; i < e->attributes().size(); ++i) {
+ const QInputMethodEvent::Attribute &a = e->attributes().at(i);
+ if (a.type == QInputMethodEvent::Cursor)
+ cursor->setIndex(cursor->index() + a.start);
+ else if (a.type != QInputMethodEvent::TextFormat)
+ continue;
+ QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
+ if (f.isValid()) {
+ Q3TextCursor c2 = c;
+ c2.setIndex(c.index() + a.start);
+ doc->setSelectionStart(preeditSelectionBase + d->numPreeditSelections, c2);
+ c2.setIndex(c.index() + a.start + a.length);
+ doc->setSelectionEnd(preeditSelectionBase + d->numPreeditSelections, c2);
+
+ QColor c = f.hasProperty(QTextFormat::BackgroundBrush) ? f.background().color() : QColor();
+ doc->setSelectionColor(preeditSelectionBase + d->numPreeditSelections, c);
+ c = f.hasProperty(QTextFormat::ForegroundBrush) ? f.foreground().color() : QColor();
+ doc->setSelectionTextColor(preeditSelectionBase + d->numPreeditSelections, c);
+ if (f.fontUnderline()) {
+ Q3TextParagraph *par = cursor->paragraph();
+ Q3TextFormat f(*par->string()->at(d->preeditStart).format());
+ f.setUnderline(true);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ par->setFormat(d->preeditStart + a.start, a.length, f2);
+ }
+ ++d->numPreeditSelections;
+ }
+ }
+ } else {
+ undoRedoInfo.type = UndoRedoInfo::Invalid;
+ }
+ blockSignals(sigs_blocked);
+ if (oldupdate)
+ setUpdatesEnabled(true);
+ if (!e->commitString().isEmpty())
+ emit textChanged();
+ repaintChanged();
+}
+
+
+static bool qtextedit_ignore_readonly = false;
+
+/*!
+ Executes keyboard action \a action. This is normally called by a
+ key event handler.
+*/
+
+void Q3TextEdit::doKeyboardAction(Q3TextEdit::KeyboardAction action)
+{
+ if (isReadOnly() && !qtextedit_ignore_readonly)
+ return;
+
+ if (cursor->nestedDepth() != 0)
+ return;
+
+ lastFormatted = cursor->paragraph();
+ drawCursor(false);
+ bool doUpdateCurrentFormat = true;
+
+ switch (action) {
+ case ActionWordDelete:
+ case ActionDelete:
+ if (action == ActionDelete && !cursor->atParagEnd()) {
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Delete);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ int idx = cursor->index();
+ do {
+ undoRedoInfo.d->text.insert(undoRedoInfo.d->text.length(), cursor->paragraph()->at(idx++), true);
+ } while (!cursor->paragraph()->string()->validCursorPosition(idx));
+ }
+ cursor->remove();
+ } else {
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (action == ActionWordDelete && !cursor->atParagEnd()) {
+ cursor->gotoNextWord();
+ } else {
+ cursor->gotoNextLetter();
+ }
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ }
+ break;
+ case ActionWordBackspace:
+ case ActionBackspace:
+ if (textFormat() == Qt::RichText
+ && (cursor->paragraph()->isListItem()
+ || cursor->paragraph()->listDepth())
+ && cursor->index() == 0) {
+ if (undoEnabled) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+ }
+ int ldepth = cursor->paragraph()->listDepth();
+ if (cursor->paragraph()->isListItem() && ldepth == 1) {
+ cursor->paragraph()->setListItem(false);
+ } else if (qMax(ldepth, 1) == 1) {
+ cursor->paragraph()->setListItem(false);
+ cursor->paragraph()->setListDepth(0);
+ } else {
+ cursor->paragraph()->setListDepth(ldepth - 1);
+ }
+ clearUndoRedo();
+ lastFormatted = cursor->paragraph();
+ repaintChanged();
+ drawCursor(true);
+ return;
+ }
+
+ if (action == ActionBackspace && !cursor->atParagStart()) {
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Delete);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ undoRedoInfo.d->text.insert(0, cursor->paragraph()->at(cursor->index()-1), true);
+ undoRedoInfo.index = cursor->index()-1;
+ }
+ cursor->removePreviousChar();
+ lastFormatted = cursor->paragraph();
+ } else if (cursor->paragraph()->prev()
+ || (action == ActionWordBackspace
+ && !cursor->atParagStart())) {
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (action == ActionWordBackspace && !cursor->atParagStart()) {
+ cursor->gotoPreviousWord();
+ } else {
+ cursor->gotoPreviousLetter();
+ }
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ }
+ break;
+ case ActionReturn:
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::Return);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ undoRedoInfo.d->text += QString(QLatin1Char('\n'));
+ }
+ cursor->splitAndInsertEmptyParagraph();
+ if (cursor->paragraph()->prev()) {
+ lastFormatted = cursor->paragraph()->prev();
+ lastFormatted->invalidate(0);
+ }
+ doUpdateCurrentFormat = false;
+ break;
+ case ActionKill:
+ clearUndoRedo();
+ doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
+ if (cursor->atParagEnd())
+ cursor->gotoNextLetter();
+ else
+ cursor->setIndex(cursor->paragraph()->length() - 1);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ removeSelectedText(Q3TextDocument::Temp);
+ break;
+ }
+
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ if (doUpdateCurrentFormat)
+ updateCurrentFormat();
+ setModified();
+ emit textChanged();
+}
+
+void Q3TextEdit::readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles)
+{
+#ifndef QT_NO_DATASTREAM
+ QDataStream styleStream(&undoRedoInfo.styleInformation, IO_WriteOnly);
+#endif
+ c2.restoreState();
+ c1.restoreState();
+ int lastIndex = text.length();
+ if (c1.paragraph() == c2.paragraph()) {
+ for (int i = c1.index(); i < c2.index(); ++i)
+ text.insert(lastIndex + i - c1.index(), c1.paragraph()->at(i), true);
+#ifndef QT_NO_DATASTREAM
+ if (fillStyles) {
+ styleStream << (int) 1;
+ c1.paragraph()->writeStyleInformation(styleStream);
+ }
+#endif
+ } else {
+ int i;
+ for (i = c1.index(); i < c1.paragraph()->length()-1; ++i)
+ text.insert(lastIndex++, c1.paragraph()->at(i), true);
+ int num = 2; // start and end, being different
+ text += QString(QLatin1Char('\n')); lastIndex++;
+
+ if (c1.paragraph()->next() != c2.paragraph()) {
+ num += text.appendParagraphs(c1.paragraph()->next(), c2.paragraph());
+ lastIndex = text.length();
+ }
+
+ for (i = 0; i < c2.index(); ++i)
+ text.insert(i + lastIndex, c2.paragraph()->at(i), true);
+#ifndef QT_NO_DATASTREAM
+ if (fillStyles) {
+ styleStream << num;
+ for (Q3TextParagraph *p = c1.paragraph(); --num >= 0; p = p->next())
+ p->writeStyleInformation(styleStream);
+ }
+#endif
+ }
+}
+
+/*!
+ Removes the selection \a selNum (by default 0). This does not
+ remove the selected text.
+
+ \sa removeSelectedText()
+*/
+
+void Q3TextEdit::removeSelection(int selNum)
+{
+ doc->removeSelection(selNum);
+ repaintChanged();
+}
+
+/*!
+ Deletes the text of selection \a selNum (by default, the default
+ selection, 0). If there is no selected text nothing happens.
+
+ \sa selectedText removeSelection()
+*/
+
+void Q3TextEdit::removeSelectedText(int selNum)
+{
+ Q3TextCursor c1 = doc->selectionStartCursor(selNum);
+ c1.restoreState();
+ Q3TextCursor c2 = doc->selectionEndCursor(selNum);
+ c2.restoreState();
+
+ // ### no support for editing tables yet, plus security for broken selections
+ if (c1.nestedDepth() || c2.nestedDepth())
+ return;
+
+ for (int i = 0; i < (int)doc->numSelections(); ++i) {
+ if (i == selNum)
+ continue;
+ doc->removeSelection(i);
+ }
+
+ drawCursor(false);
+ if (undoEnabled) {
+ checkUndoRedoInfo(UndoRedoInfo::RemoveSelected);
+ if (!undoRedoInfo.valid()) {
+ doc->selectionStart(selNum, undoRedoInfo.id, undoRedoInfo.index);
+ undoRedoInfo.d->text.clear();
+ }
+ readFormats(c1, c2, undoRedoInfo.d->text, true);
+ }
+
+ doc->removeSelectedText(selNum, cursor);
+ if (cursor->isValid()) {
+ lastFormatted = 0; // make sync a noop
+ ensureCursorVisible();
+ lastFormatted = cursor->paragraph();
+ formatMore();
+ repaintContents();
+ 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());
+#endif
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ } else {
+ lastFormatted = doc->firstParagraph();
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ drawCursor(true);
+ repaintContents();
+ }
+ setModified();
+ emit textChanged();
+ emit selectionChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+}
+
+/*!
+ 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 Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action, bool select)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+#ifdef Q_WS_MAC
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2;
+#endif
+ drawCursor(false);
+ if (select) {
+ if (!doc->hasSelection(Q3TextDocument::Standard))
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ moveCursor(action);
+#ifdef Q_WS_MAC
+ c2 = *cursor;
+ if (c1 == c2)
+ if (action == MoveDown || action == MovePgDown)
+ moveCursor(MoveEnd);
+ else if (action == MoveUp || action == MovePgUp)
+ moveCursor(MoveHome);
+#endif
+ if (doc->setSelectionEnd(Q3TextDocument::Standard, *cursor)) {
+ cursor->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ } else {
+ drawCursor(true);
+ }
+ ensureCursorVisible();
+ emit selectionChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ } else {
+#ifdef Q_WS_MAC
+ Q3TextCursor cStart = doc->selectionStartCursor(Q3TextDocument::Standard);
+ Q3TextCursor cEnd = doc->selectionEndCursor(Q3TextDocument::Standard);
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ if (redraw && action == MoveDown)
+ *cursor = cEnd;
+ else if (redraw && action == MoveUp)
+ *cursor = cStart;
+ if (redraw && action == MoveForward)
+ *cursor = cEnd;
+ else if (redraw && action == MoveBackward)
+ *cursor = cStart;
+ else
+ moveCursor(action);
+ c2 = *cursor;
+ if (c1 == c2)
+ if (action == MoveDown)
+ moveCursor(MoveEnd);
+ else if (action == MoveUp)
+ moveCursor(MoveHome);
+#else
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ moveCursor(action);
+#endif
+ if (!redraw) {
+ ensureCursorVisible();
+ drawCursor(true);
+ } else {
+ cursor->paragraph()->document()->nextDoubleBuffered = true;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ if (redraw) {
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+ }
+ }
+
+ drawCursor(true);
+ updateCurrentFormat();
+}
+
+/*!
+ \overload
+*/
+
+void Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action)
+{
+ resetInputContext();
+ 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->lastParagraph());
+ cursor->gotoEnd();
+ break;
+ }
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::resizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::resizeEvent(e);
+ if (doc->visibleWidth() == 0)
+ doResize();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::viewportResizeEvent(QResizeEvent *e)
+{
+ Q3ScrollView::viewportResizeEvent(e);
+ if (e->oldSize().width() != e->size().width()) {
+ bool stayAtBottom = e->oldSize().height() != e->size().height() &&
+ contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height();
+ doResize();
+ if (stayAtBottom)
+ scrollToBottom();
+ }
+}
+
+/*!
+ Ensures that the cursor is visible by scrolling the text edit if
+ necessary.
+
+ \sa setCursorPosition()
+*/
+
+void Q3TextEdit::ensureCursorVisible()
+{
+ // Not visible or the user is dragging the window, so don't position to caret yet
+ if (!updatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed()) {
+ d->ensureCursorVisibleInShowEvent = true;
+ return;
+ }
+ sync();
+ Q3TextStringChar *chr = cursor->paragraph()->at(cursor->index());
+ int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
+ int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX();
+ int y = 0; int dummy;
+ cursor->paragraph()->lineHeightOfChar(cursor->index(), &dummy, &y);
+ y += cursor->paragraph()->rect().y() + cursor->offsetY();
+ int w = 1;
+ ensureVisible(x, y + h / 2, w, h / 2 + 2);
+}
+
+/*!
+ \internal
+*/
+void Q3TextEdit::sliderReleased()
+{
+ if (d->ensureCursorVisibleInShowEvent && isVisible()) {
+ d->ensureCursorVisibleInShowEvent = false;
+ ensureCursorVisible();
+ }
+}
+
+/*!
+ \internal
+
+ If \a visible is true, the cursor is shown; otherwise it is
+ hidden.
+*/
+void Q3TextEdit::drawCursor(bool visible)
+{
+ d->cursorRepaintMode = true;
+ blinkCursorVisible = visible;
+ QRect r(cursor->topParagraph()->rect());
+ if (!cursor->nestedDepth()) {
+ int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
+ r = QRect(r.x(), r.y() + cursor->y(), r.width(), h);
+ }
+ r.moveBy(-contentsX(), -contentsY());
+ viewport()->update(r);
+}
+
+enum {
+ IdUndo = 0,
+ IdRedo = 1,
+ IdCut = 2,
+ IdCopy = 3,
+ IdPaste = 4,
+ IdClear = 5,
+ IdSelectAll = 6
+};
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void Q3TextEdit::contentsWheelEvent(QWheelEvent *e)
+{
+ if (isReadOnly()) {
+ if (e->state() & Qt::ControlButton) {
+ if (e->delta() > 0)
+ zoomOut();
+ else if (e->delta() < 0)
+ zoomIn();
+ return;
+ }
+ }
+ Q3ScrollView::contentsWheelEvent(e);
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMousePressEvent(QMouseEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMousePressEvent(e);
+ return;
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ if (e->button() == Qt::LeftButton && d->preeditLength > 0 && cursor->paragraph()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(e->pos(), &c, false);
+ inputContext()->mouseHandler(c.index() - d->preeditStart, e);
+ if (d->preeditLength > 0)
+ return;
+ }
+#endif
+
+ if (d->trippleClickTimer->isActive() &&
+ (e->globalPos() - d->trippleClickPoint).manhattanLength() <
+ QApplication::startDragDistance()) {
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2 = *cursor;
+ c1.gotoLineStart();
+ c2.gotoLineEnd();
+ doc->setSelectionStart(Q3TextDocument::Standard, c1);
+ doc->setSelectionEnd(Q3TextDocument::Standard, c2);
+ *cursor = c2;
+ repaintChanged();
+ mousePressed = true;
+ return;
+ }
+
+ clearUndoRedo();
+ Q3TextCursor oldCursor = *cursor;
+ Q3TextCursor c = *cursor;
+ mousePos = e->pos();
+ mightStartDrag = false;
+ pressedLink.clear();
+ d->pressedName.clear();
+
+ if (e->button() == Qt::LeftButton) {
+ mousePressed = true;
+ drawCursor(false);
+ placeCursor(e->pos());
+ ensureCursorVisible();
+
+ if (isReadOnly() && linksEnabled()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(e->pos(), &c, true);
+ if (c.paragraph() && c.paragraph()->at(c.index()) &&
+ c.paragraph()->at(c.index())->isAnchor()) {
+ pressedLink = c.paragraph()->at(c.index())->anchorHref();
+ d->pressedName = c.paragraph()->at(c.index())->anchorName();
+ }
+ }
+
+#ifndef QT_NO_DRAGANDDROP
+ if (doc->inSelection(Q3TextDocument::Standard, e->pos())) {
+ mightStartDrag = true;
+ drawCursor(true);
+ dragStartTimer->start(QApplication::startDragTime(), true);
+ dragStartPos = e->pos();
+ return;
+ }
+#endif
+
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ if (!(e->state() & Qt::ShiftButton)) {
+ redraw = doc->removeSelection(Q3TextDocument::Standard);
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
+ }
+ } else {
+ if (isReadOnly() || !(e->state() & Qt::ShiftButton)) {
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ doc->setSelectionStart(Q3TextDocument::Standard, c);
+ redraw = doc->setSelectionEnd(Q3TextDocument::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() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ } else if (e->button() == Qt::MidButton) {
+ bool redraw = doc->removeSelection(Q3TextDocument::Standard);
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ }
+
+ if (*cursor != oldCursor)
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseMoveEvent(QMouseEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMouseMoveEvent(e);
+ return;
+ }
+#endif
+
+#if !defined(QT_NO_IM)
+ if (d->preeditLength > 0)
+ return;
+#endif
+
+ 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(Qt::IBeamCursor);
+#endif
+ return;
+ }
+#endif
+ mousePos = e->pos();
+ handleMouseMove(mousePos);
+ oldMousePos = mousePos;
+ }
+
+#ifndef QT_NO_CURSOR
+ if (!isReadOnly() && !mousePressed) {
+ if (doc->hasSelection(Q3TextDocument::Standard) && doc->inSelection(Q3TextDocument::Standard, e->pos()))
+ viewport()->setCursor(Qt::ArrowCursor);
+ else
+ viewport()->setCursor(Qt::IBeamCursor);
+ }
+#endif
+ updateCursor(e->pos());
+}
+
+void Q3TextEdit::copyToClipboard()
+{
+#ifndef QT_NO_CLIPBOARD
+ if (QApplication::clipboard()->supportsSelection()) {
+ d->clipboard_mode = QClipboard::Selection;
+
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+ copy();
+ // listen to selection changes
+ connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
+ this, SLOT(clipboardChanged()));
+
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+#endif
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseReleaseEvent(QMouseEvent * e)
+{
+ if (!inDoubleClick) { // could be the release of a dblclick
+ int para = 0;
+ int index = charAt(e->pos(), &para);
+ emit clicked(para, index);
+ }
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimMouseReleaseEvent(e);
+ return;
+ }
+#endif
+ Q3TextCursor 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;
+ copyToClipboard();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if (e->button() == Qt::MidButton && !isReadOnly()) {
+ // only do middle-click pasting on systems that have selections (ie. X11)
+ if (QApplication::clipboard()->supportsSelection()) {
+ drawCursor(false);
+ placeCursor(e->pos());
+ ensureCursorVisible();
+ doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ redraw = doc->removeSelection(Q3TextDocument::Standard);
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ } else {
+ doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
+ }
+ // start with 1 as we don't want to remove the Standard-Selection
+ for (int i = 1; i < doc->numSelections(); ++i)
+ redraw = doc->removeSelection(i) || redraw;
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(Qt::IBeamCursor);
+#endif
+ }
+ d->clipboard_mode = QClipboard::Selection;
+ paste();
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+ }
+#endif
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ if (oldCursor != *cursor)
+ updateCurrentFormat();
+ inDoubleClick = false;
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ if (( (!onLink.isEmpty() && onLink == pressedLink)
+ || (!d->onName.isEmpty() && d->onName == d->pressedName))
+ && linksEnabled()) {
+ if (!onLink.isEmpty()) {
+ QUrl u = QUrl(doc->context()).resolved(onLink);
+ emitLinkClicked(u.toString(QUrl::None));
+ }
+ if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
+ emit browser->anchorClicked(d->onName, onLink);
+
+ // 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(Q3TextDocument::Standard, true))
+ doc->removeSelection(Q3TextDocument::Standard);
+
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsMouseDoubleClickEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton) {
+ e->ignore();
+ return;
+ }
+#if !defined(QT_NO_IM)
+ if (d->preeditLength > 0)
+ return;
+#endif
+
+ int para = 0;
+ int index = charAt(e->pos(), &para);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QString str = d->od->lines[LOGOFFSET(para)];
+ int startIdx = index, endIdx = index, i;
+ if (!str[index].isSpace()) {
+ i = startIdx;
+ // find start of word
+ while (i >= 0 && !str[i].isSpace()) {
+ startIdx = i--;
+ }
+ i = endIdx;
+ // find end of word..
+ while (i < str.length() && !str[i].isSpace()) {
+ endIdx = ++i;
+ }
+ // ..and start of next
+ while (i < str.length() && str[i].isSpace()) {
+ endIdx = ++i;
+ }
+ optimSetSelection(para, startIdx, para, endIdx);
+ repaintContents();
+ }
+ } else
+#endif
+ {
+ Q3TextCursor c1 = *cursor;
+ Q3TextCursor c2 = *cursor;
+#if defined(Q_OS_MAC)
+ Q3TextParagraph *para = cursor->paragraph();
+ if (cursor->isValid()) {
+ if (para->at(cursor->index())->c.isLetterOrNumber()) {
+ while (c1.index() > 0 &&
+ c1.paragraph()->at(c1.index()-1)->c.isLetterOrNumber())
+ c1.gotoPreviousLetter();
+ while (c2.paragraph()->at(c2.index())->c.isLetterOrNumber() &&
+ !c2.atParagEnd())
+ c2.gotoNextLetter();
+ } else if (para->at(cursor->index())->c.isSpace()) {
+ while (c1.index() > 0 &&
+ c1.paragraph()->at(c1.index()-1)->c.isSpace())
+ c1.gotoPreviousLetter();
+ while (c2.paragraph()->at(c2.index())->c.isSpace() &&
+ !c2.atParagEnd())
+ c2.gotoNextLetter();
+ } else if (!c2.atParagEnd()) {
+ c2.gotoNextLetter();
+ }
+ }
+#else
+ if (cursor->index() > 0 && !cursor->paragraph()->at(cursor->index()-1)->c.isSpace())
+ c1.gotoPreviousWord();
+ if (!cursor->paragraph()->at(cursor->index())->c.isSpace() && !cursor->atParagEnd())
+ c2.gotoNextWord();
+#endif
+ doc->setSelectionStart(Q3TextDocument::Standard, c1);
+ doc->setSelectionEnd(Q3TextDocument::Standard, c2);
+
+ *cursor = c2;
+
+ repaintChanged();
+
+ d->trippleClickTimer->start(qApp->doubleClickInterval(), true);
+ d->trippleClickPoint = e->globalPos();
+ }
+ inDoubleClick = true;
+ mousePressed = true;
+ emit doubleClicked(para, index);
+}
+
+#ifndef QT_NO_DRAGANDDROP
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragEnterEvent(QDragEnterEvent *e)
+{
+ if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+ inDnD = true;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragMoveEvent(QDragMoveEvent *e)
+{
+ if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
+ e->ignore();
+ return;
+ }
+ drawCursor(false);
+ placeCursor(e->pos(), cursor);
+ drawCursor(true);
+ e->acceptAction();
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDragLeaveEvent(QDragLeaveEvent *)
+{
+ drawCursor(false);
+ inDnD = false;
+}
+
+/*!
+ \reimp
+*/
+
+void Q3TextEdit::contentsDropEvent(QDropEvent *e)
+{
+ if (isReadOnly())
+ return;
+ inDnD = false;
+ e->acceptAction();
+ bool intern = false;
+ if (Q3RichTextDrag::canDecode(e)) {
+ bool hasSel = doc->hasSelection(Q3TextDocument::Standard);
+ bool internalDrag = e->source() == this || e->source() == viewport();
+ int dropId, dropIndex;
+ Q3TextCursor insertCursor = *cursor;
+ dropId = cursor->paragraph()->paragId();
+ dropIndex = cursor->index();
+ if (hasSel && internalDrag) {
+ Q3TextCursor c1, c2;
+ int selStartId, selStartIndex;
+ int selEndId, selEndIndex;
+ c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
+ c1.restoreState();
+ c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
+ c2.restoreState();
+ selStartId = c1.paragraph()->paragId();
+ selStartIndex = c1.index();
+ selEndId = c2.paragraph()->paragId();
+ selEndIndex = c2.index();
+ if (((dropId > selStartId) ||
+ (dropId == selStartId && dropIndex > selStartIndex)) &&
+ ((dropId < selEndId) ||
+ (dropId == selEndId && dropIndex <= selEndIndex)))
+ insertCursor = c1;
+ if (dropId == selEndId && dropIndex > selEndIndex) {
+ insertCursor = c1;
+ if (selStartId == selEndId) {
+ insertCursor.setIndex(dropIndex -
+ (selEndIndex - selStartIndex));
+ } else {
+ insertCursor.setIndex(dropIndex - selEndIndex +
+ selStartIndex);
+ }
+ }
+ }
+
+ if (internalDrag && e->action() == QDropEvent::Move) {
+ removeSelectedText();
+ intern = true;
+ doc->removeSelection(Q3TextDocument::Standard);
+ } else {
+ doc->removeSelection(Q3TextDocument::Standard);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ drawCursor(false);
+ cursor->setParagraph(insertCursor.paragraph());
+ cursor->setIndex(insertCursor.index());
+ drawCursor(true);
+ if (!cursor->nestedDepth()) {
+ QString subType = QLatin1String("plain");
+ if (textFormat() != Qt::PlainText) {
+ if (e->provides("application/x-qrichtext"))
+ subType = QLatin1String("x-qrichtext");
+ }
+#ifndef QT_NO_CLIPBOARD
+ pasteSubType(subType.toLatin1(), e);
+#endif
+ // emit appropriate signals.
+ emit selectionChanged();
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ } else {
+ if (intern)
+ undo();
+ e->ignore();
+ }
+ }
+}
+
+#endif
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::contentsContextMenuEvent(QContextMenuEvent *e)
+{
+ clearUndoRedo();
+ mousePressed = false;
+
+ e->accept();
+#ifndef QT_NO_POPUPMENU
+ Q3PopupMenu *popup = createPopupMenu(e->pos());
+ if (!popup)
+ popup = createPopupMenu();
+ if (!popup)
+ return;
+ int r = popup->exec(e->globalPos(), -1);
+ delete popup;
+
+ if (r == d->id[IdClear])
+ clear();
+ else if (r == d->id[IdSelectAll]) {
+ selectAll();
+#ifndef QT_NO_CLIPBOARD
+ // if the clipboard support selections, put the newly selected text into
+ // the clipboard
+ if (QApplication::clipboard()->supportsSelection()) {
+ d->clipboard_mode = QClipboard::Selection;
+
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+ copy();
+ // listen to selection changes
+ connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
+ this, SLOT(clipboardChanged()));
+
+ d->clipboard_mode = QClipboard::Clipboard;
+ }
+#endif
+ } else if (r == d->id[IdUndo])
+ undo();
+ else if (r == d->id[IdRedo])
+ redo();
+#ifndef QT_NO_CLIPBOARD
+ else if (r == d->id[IdCut])
+ cut();
+ else if (r == d->id[IdCopy])
+ copy();
+ else if (r == d->id[IdPaste])
+ paste();
+#endif
+#endif
+}
+
+
+void Q3TextEdit::autoScrollTimerDone()
+{
+ if (mousePressed)
+ handleMouseMove( viewportToContents(viewport()->mapFromGlobal(QCursor::pos()) ));
+}
+
+void Q3TextEdit::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);
+ Q3TextCursor oldCursor = *cursor;
+
+ placeCursor(pos);
+
+ if (inDoubleClick) {
+ Q3TextCursor cl = *cursor;
+ cl.gotoPreviousWord();
+ Q3TextCursor cr = *cursor;
+ cr.gotoNextWord();
+
+ int diff = QABS(oldCursor.paragraph()->at(oldCursor.index())->x - mousePos.x());
+ int ldiff = QABS(cl.paragraph()->at(cl.index())->x - mousePos.x());
+ int rdiff = QABS(cr.paragraph()->at(cr.index())->x - mousePos.x());
+
+
+ if (cursor->paragraph()->lineStartOfChar(cursor->index()) !=
+ oldCursor.paragraph()->lineStartOfChar(oldCursor.index()))
+ diff = 0xFFFFFF;
+
+ if (rdiff < diff && rdiff < ldiff)
+ *cursor = cr;
+ else if (ldiff < diff && ldiff < rdiff)
+ *cursor = cl;
+ else
+ *cursor = oldCursor;
+
+ }
+ ensureCursorVisible();
+
+ bool redraw = false;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
+ }
+
+ if (!redraw) {
+ drawCursor(true);
+ } else {
+ repaintChanged();
+ drawCursor(true);
+ }
+
+ if (currentFormat && currentFormat->key() != cursor->paragraph()->at(cursor->index())->format()->key()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(cursor->index())->format());
+ if (currentFormat->isMisspelled()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
+ }
+ emit currentFontChanged(currentFormat->font());
+ emit currentColorChanged(currentFormat->color());
+ emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
+ }
+
+ if (currentAlignment != cursor->paragraph()->alignment()) {
+ currentAlignment = cursor->paragraph()->alignment();
+ block_set_alignment = true;
+ emit currentAlignmentChanged(currentAlignment);
+ block_set_alignment = false;
+ }
+}
+
+/*! \internal */
+
+void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c, bool link)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+ if (!c)
+ c = cursor;
+
+ if(c == cursor)
+ resetInputContext();
+ c->restoreState();
+ Q3TextParagraph *s = doc->firstParagraph();
+ c->place(pos, s, link);
+}
+
+
+QVariant Q3TextEdit::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q3TextCursor c(*cursor);
+
+ switch(query) {
+ case Qt::ImMicroFocus: {
+ int h = c.paragraph()->lineHeightOfChar(cursor->index());
+ return QRect(c.x() - contentsX() + frameWidth(),
+ c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 1, h);
+ }
+ case Qt::ImFont:
+ return c.paragraph()->at(c.index())->format()->font();
+ default:
+ // ##### fix the others!
+ return QWidget::inputMethodQuery(query);
+ }
+}
+
+
+
+void Q3TextEdit::formatMore()
+{
+ if (!lastFormatted)
+ return;
+
+ int bottom = contentsHeight();
+ int lastTop = -1;
+ int lastBottom = -1;
+ int to = 20;
+ bool firstVisible = false;
+ QRect cr(contentsX(), contentsY(), visibleWidth(), visibleHeight());
+ for (int i = 0; lastFormatted &&
+ (i < to || (firstVisible && lastTop < contentsY()+height()));
+ i++) {
+ lastFormatted->format();
+ lastTop = lastFormatted->rect().top();
+ lastBottom = lastFormatted->rect().bottom();
+ if (i == 0)
+ firstVisible = lastBottom < cr.bottom();
+ bottom = qMax(bottom, lastBottom);
+ lastFormatted = lastFormatted->next();
+ }
+
+ if (bottom > contentsHeight()) {
+ resizeContents(contentsWidth(), qMax(doc->height(), bottom));
+ } else if (!lastFormatted && lastBottom < contentsHeight()) {
+ resizeContents(contentsWidth(), qMax(doc->height(), lastBottom));
+ if (contentsHeight() < visibleHeight())
+ updateContents(0, contentsHeight(), visibleWidth(),
+ visibleHeight() - contentsHeight());
+ }
+
+ if (lastFormatted)
+ formatTimer->start(interval, true);
+ else
+ interval = qMax(0, interval);
+}
+
+void Q3TextEdit::doResize()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (!d->optimMode)
+#endif
+ {
+ if (wrapMode == FixedPixelWidth)
+ return;
+ doc->setMinimumWidth(-1);
+ resizeContents(0, 0);
+ doc->setWidth(visibleWidth());
+ doc->invalidate();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ }
+ repaintContents();
+}
+
+/*! \internal */
+
+void Q3TextEdit::doChangeInterval()
+{
+ interval = 0;
+}
+
+/*!
+ \reimp
+*/
+
+bool Q3TextEdit::eventFilter(QObject *o, QEvent *e)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (!d->optimMode && (o == this || o == viewport())) {
+#else
+ if (o == this || o == viewport()) {
+#endif
+ if (d->cursorBlinkActive && e->type() == QEvent::FocusIn) {
+ if (QApplication::cursorFlashTime() > 0)
+ blinkTimer->start(QApplication::cursorFlashTime() / 2);
+ drawCursor(true);
+ } else if (e->type() == QEvent::FocusOut) {
+ blinkTimer->stop();
+ drawCursor(false);
+ }
+ }
+
+ if (o == this && e->type() == QEvent::PaletteChange) {
+ QColor old(viewport()->palette().color(QPalette::Text));
+ if (old != palette().color(QPalette::Text)) {
+ QColor c(palette().color(QPalette::Text));
+ doc->setMinimumWidth(-1);
+ doc->setDefaultFormat(doc->formatCollection()->defaultFormat()->font(), c);
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+ }
+ }
+
+ return Q3ScrollView::eventFilter(o, e);
+}
+
+/*!
+ Inserts the given \a text. If \a indent is true the paragraph that
+ contains the text is reindented; if \a checkNewLine is true the \a
+ text is checked for newlines and relaid out. If \a removeSelected
+ is true and there is a selection, the insertion replaces the
+ selected text.
+ */
+void Q3TextEdit::insert(const QString &text, bool indent,
+ bool checkNewLine, bool removeSelected)
+{
+ uint f = 0;
+ if (indent)
+ f |= RedoIndentation;
+ if (checkNewLine)
+ f |= CheckNewLines;
+ if (removeSelected)
+ f |= RemoveSelected;
+ insert(text, f);
+}
+
+/*!
+ Inserts \a text at the current cursor position.
+
+ The \a insertionFlags define how the text is inserted. If \c
+ RedoIndentation is set, the paragraph is re-indented. If \c
+ CheckNewLines is set, newline characters in \a text result in hard
+ line breaks (i.e. new paragraphs). If \c checkNewLine is not set,
+ the behavior of the editor is undefined if the \a text contains
+ newlines. (It is not possible to change Q3TextEdit's newline handling
+ behavior, but you can use QString::replace() to preprocess text
+ before inserting it.) If \c RemoveSelected is set, any selected
+ text (in selection 0) is removed before the text is inserted.
+
+ The default flags are \c CheckNewLines | \c RemoveSelected.
+
+ If the widget is in Qt::LogText mode this function will do nothing.
+
+ \sa paste() pasteSubType()
+*/
+
+
+void Q3TextEdit::insert(const QString &text, uint insertionFlags)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+
+ if (cursor->nestedDepth() != 0) // #### for 3.0, disable editing of tables as this is not advanced enough
+ return;
+
+ bool indent = insertionFlags & RedoIndentation;
+ bool checkNewLine = insertionFlags & CheckNewLines;
+ bool removeSelected = insertionFlags & RemoveSelected;
+ QString txt(text);
+ drawCursor(false);
+ if (!isReadOnly() && doc->hasSelection(Q3TextDocument::Standard) && removeSelected)
+ removeSelectedText();
+ Q3TextCursor c2 = *cursor;
+ int oldLen = 0;
+
+ if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) {
+ checkUndoRedoInfo(UndoRedoInfo::Insert);
+
+ // If we are inserting at the end of the previous insertion, we keep this in
+ // the same undo/redo command. Otherwise, we separate them in two different commands.
+ if (undoRedoInfo.valid() && undoRedoInfo.index + undoRedoInfo.d->text.length() != cursor->index()) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Insert;
+ }
+
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text.clear();
+ }
+ oldLen = undoRedoInfo.d->text.length();
+ }
+
+ lastFormatted = checkNewLine && cursor->paragraph()->prev() ?
+ cursor->paragraph()->prev() : cursor->paragraph();
+ Q3TextCursor oldCursor = *cursor;
+ cursor->insert(txt, checkNewLine);
+ if (doc->useFormatCollection() && !doc->preProcessor()) {
+ doc->setSelectionStart(Q3TextDocument::Temp, oldCursor);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+ doc->setFormat(Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format);
+ doc->removeSelection(Q3TextDocument::Temp);
+ }
+
+ if (indent && (txt == QString(QLatin1Char('{')) || txt == QString(QLatin1Char('}')) || txt == QString(QLatin1Char(':')) || txt == QString(QLatin1Char('#'))))
+ cursor->indent();
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+
+ if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) {
+ undoRedoInfo.d->text += txt;
+ if (!doc->preProcessor()) {
+ for (int i = 0; i < (int)txt.length(); ++i) {
+ if (txt[i] != QLatin1Char('\n') && c2.paragraph()->at(c2.index())->format()) {
+ c2.paragraph()->at(c2.index())->format()->addRef();
+ undoRedoInfo.d->text.
+ setFormat(oldLen + i,
+ c2.paragraph()->at(c2.index())->format(), true);
+ }
+ c2.gotoNextLetter();
+ }
+ }
+ }
+
+ if (!removeSelected) {
+ doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
+ doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
+ repaintChanged();
+ }
+
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Inserts \a text in the paragraph \a para at position \a index.
+*/
+
+void Q3TextEdit::insertAt(const QString &text, int para, int index)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimInsert(text, para, index);
+ return;
+ }
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+ removeSelection(Q3TextDocument::Standard);
+ Q3TextCursor tmp = *cursor;
+ cursor->setParagraph(p);
+ cursor->setIndex(index);
+ insert(text, false, true, false);
+ *cursor = tmp;
+ removeSelection(Q3TextDocument::Standard);
+}
+
+/*!
+ Inserts \a text as a new paragraph at position \a para. If \a para
+ is -1, the text is appended. Use append() if the append operation
+ is performance critical.
+*/
+
+void Q3TextEdit::insertParagraph(const QString &text, int para)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimInsert(text + QLatin1Char('\n'), para, 0);
+ return;
+ }
+#endif
+ for (int i = 0; i < (int)doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+ Q3TextParagraph *p = doc->paragAt(para);
+
+ bool append = !p;
+ if (!p)
+ p = doc->lastParagraph();
+
+ Q3TextCursor old = *cursor;
+ drawCursor(false);
+
+ cursor->setParagraph(p);
+ cursor->setIndex(0);
+ clearUndoRedo();
+ qtextedit_ignore_readonly = true;
+ if (append && cursor->paragraph()->length() > 1) {
+ cursor->setIndex(cursor->paragraph()->length() - 1);
+ doKeyboardAction(ActionReturn);
+ }
+ insert(text, false, true, true);
+ doKeyboardAction(ActionReturn);
+ qtextedit_ignore_readonly = false;
+
+ drawCursor(false);
+ *cursor = old;
+ drawCursor(true);
+
+ repaintChanged();
+}
+
+/*!
+ Removes the paragraph \a para.
+*/
+
+void Q3TextEdit::removeParagraph(int para)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return;
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+
+ for (int i = 0; i < doc->numSelections(); ++i)
+ doc->removeSelection(i);
+
+ Q3TextCursor start(doc);
+ Q3TextCursor end(doc);
+ start.setParagraph(p);
+ start.setIndex(0);
+ end.setParagraph(p);
+ end.setIndex(p->length() - 1);
+
+ if (!(p == doc->firstParagraph() && p == doc->lastParagraph())) {
+ if (p->next()) {
+ end.setParagraph(p->next());
+ end.setIndex(0);
+ } else if (p->prev()) {
+ start.setParagraph(p->prev());
+ start.setIndex(p->prev()->length() - 1);
+ }
+ }
+
+ doc->setSelectionStart(Q3TextDocument::Temp, start);
+ doc->setSelectionEnd(Q3TextDocument::Temp, end);
+ removeSelectedText(Q3TextDocument::Temp);
+}
+
+/*!
+ Undoes the last operation.
+
+ If there is no operation to undo, i.e. there is no undo step in
+ the undo/redo history, nothing happens.
+
+ \sa undoAvailable() redo() undoDepth()
+*/
+
+void Q3TextEdit::undo()
+{
+ 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() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+
+ clearUndoRedo();
+ drawCursor(false);
+ Q3TextCursor *c = doc->undo(cursor);
+ if (!c) {
+ drawCursor(true);
+ return;
+ }
+ lastFormatted = 0;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ setModified();
+ // ### If we get back to a completely blank textedit, it
+ // is possible that cursor is invalid and further actions
+ // might not fix the problem, so reset the cursor here.
+ // This is copied from removeSeletedText(), it might be
+ // okay to just call that.
+ if (!cursor->isValid()) {
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ drawCursor(true);
+ repaintContents();
+ }
+ emit undoAvailable(isUndoAvailable());
+ emit redoAvailable(isRedoAvailable());
+ emit textChanged();
+}
+
+/*!
+ Redoes the last operation.
+
+ If there is no operation to redo, i.e. there is no redo step in
+ the undo/redo history, nothing happens.
+
+ \sa redoAvailable() undo() undoDepth()
+*/
+
+void Q3TextEdit::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() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+
+ clearUndoRedo();
+ drawCursor(false);
+ Q3TextCursor *c = doc->redo(cursor);
+ if (!c) {
+ drawCursor(true);
+ return;
+ }
+ lastFormatted = 0;
+ ensureCursorVisible();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor(true);
+ setModified();
+ emit undoAvailable(isUndoAvailable());
+ emit redoAvailable(isRedoAvailable());
+ emit textChanged();
+}
+
+/*!
+ 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() Q3TextEdit::copy()
+*/
+
+void Q3TextEdit::paste()
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ if (isReadOnly())
+ return;
+ QString subType = QLatin1String("plain");
+ if (textFormat() != Qt::PlainText) {
+ QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
+ if (!m)
+ return;
+ if (m->provides("application/x-qrichtext"))
+ subType = QLatin1String("x-qrichtext");
+ }
+
+ pasteSubType(subType.toLatin1());
+#endif
+}
+
+void Q3TextEdit::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 Q3TextEdit::repaintChanged()
+{
+ if (!updatesEnabled() || !viewport()->updatesEnabled())
+ return;
+
+ if (doc->firstParagraph())
+ lastFormatted = doc->firstParagraph();
+ updateContents(); // good enough until this class is rewritten
+}
+
+#ifndef QT_NO_MIME
+Q3TextDrag *Q3TextEdit::dragObject(QWidget *parent) const
+{
+ if (!doc->hasSelection(Q3TextDocument::Standard) ||
+ doc->selectedText(Q3TextDocument::Standard).isEmpty())
+ return 0;
+ if (textFormat() != Qt::RichText)
+ return new Q3TextDrag(doc->selectedText(Q3TextDocument::Standard), parent);
+ Q3RichTextDrag *drag = new Q3RichTextDrag(parent);
+ drag->setPlainText(doc->selectedText(Q3TextDocument::Standard));
+ drag->setRichText(doc->selectedText(Q3TextDocument::Standard, true));
+ return drag;
+}
+#endif
+
+/*!
+ 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 Q3TextEdit::copy() paste() pasteSubType()
+*/
+
+void Q3TextEdit::cut()
+{
+ if (isReadOnly())
+ return;
+ normalCopy();
+ removeSelectedText();
+}
+
+void Q3TextEdit::normalCopy()
+{
+#ifndef QT_NO_MIME
+ Q3TextDrag *drag = dragObject();
+ if (!drag)
+ return;
+#ifndef QT_NO_MIMECLIPBOARD
+ QApplication::clipboard()->setData(drag, d->clipboard_mode);
+#endif // QT_NO_MIMECLIPBOARD
+#endif // QT_NO_MIME
+}
+
+/*!
+ Copies any selected text (from selection 0) to the clipboard.
+
+ \sa hasSelectedText() copyAvailable()
+*/
+
+void Q3TextEdit::copy()
+{
+#ifndef QT_NO_CLIPBOARD
+# ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && optimHasSelection())
+ QApplication::clipboard()->setText(optimSelectedText(), d->clipboard_mode);
+ else
+ normalCopy();
+# else
+ normalCopy();
+# endif
+#endif
+}
+
+/*!
+ \internal
+
+ Re-indents the current paragraph.
+*/
+
+void Q3TextEdit::indent()
+{
+ if (isReadOnly())
+ return;
+
+ drawCursor(false);
+ if (!doc->hasSelection(Q3TextDocument::Standard))
+ cursor->indent();
+ else
+ doc->indentSelection(Q3TextDocument::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 Q3TextEdit::focusNextPrevChild(bool n)
+{
+ if (!isReadOnly() || !linksEnabled())
+ return false;
+ bool b = doc->focusNextPrevChild(n);
+ repaintChanged();
+ if (b) {
+ Q3TextParagraph *p = doc->focusIndicator.parag;
+ int start = doc->focusIndicator.start;
+ int len = doc->focusIndicator.len;
+
+ int y = p->rect().y();
+ while (p
+ && len == 0
+ && p->at(start)->isCustom()
+ && p->at(start)->customItem()->isNested()) {
+
+ Q3TextTable *t = (Q3TextTable*)p->at(start)->customItem();
+ QList<Q3TextTableCell *> cells = t->tableCells();
+ for (int idx = 0; idx < cells.count(); ++idx) {
+ Q3TextTableCell *c = cells.at(idx);
+ Q3TextDocument *cellDoc = c->richText();
+ if ( cellDoc->hasFocusParagraph() ) {
+ y += c->geometry().y() + c->verticalAlignmentOffset();
+
+ p = cellDoc->focusIndicator.parag;
+ start = cellDoc->focusIndicator.start;
+ len = cellDoc->focusIndicator.len;
+ if ( p )
+ y += p->rect().y();
+
+ break;
+ }
+ }
+ }
+ setContentsPos( contentsX(), QMIN( y, contentsHeight() - visibleHeight() ) );
+ }
+ 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 Q3TextEdit::setFormat(Q3TextFormat *f, int flags)
+{
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ drawCursor(false);
+ Q3TextCursor c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
+ c1.restoreState();
+ Q3TextCursor c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
+ c2.restoreState();
+ if (undoEnabled) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Format;
+ undoRedoInfo.id = c1.paragraph()->paragId();
+ undoRedoInfo.index = c1.index();
+ undoRedoInfo.eid = c2.paragraph()->paragId();
+ undoRedoInfo.eindex = c2.index();
+ readFormats(c1, c2, undoRedoInfo.d->text);
+ undoRedoInfo.format = f;
+ undoRedoInfo.flags = flags;
+ clearUndoRedo();
+ }
+ doc->setFormat(Q3TextDocument::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->paragraph()->length() - 1) {
+ currentFormat->addRef();
+ cursor->paragraph()->string()->setFormat(cursor->index(), currentFormat, true);
+ if (cursor->paragraph()->length() == 1) {
+ cursor->paragraph()->invalidate(0);
+ cursor->paragraph()->format();
+ repaintChanged();
+ }
+ }
+ }
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ Sets the paragraph style of the current paragraph
+ to \a dm. If \a dm is Q3StyleSheetItem::DisplayListItem, the
+ type of the list item is set to \a listStyle.
+
+ \sa setAlignment()
+*/
+
+void Q3TextEdit::setParagType(Q3StyleSheetItem::DisplayMode dm,
+ Q3StyleSheetItem::ListStyle listStyle)
+{
+ if (isReadOnly())
+ return;
+
+ drawCursor(false);
+ Q3TextParagraph *start = cursor->paragraph();
+ Q3TextParagraph *end = start;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
+ end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
+ if (end->paragId() < start->paragId())
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+
+ while (start != end->next()) {
+ start->setListStyle(listStyle);
+ if (dm == Q3StyleSheetItem::DisplayListItem) {
+ start->setListItem(true);
+ if(start->listDepth() == 0)
+ start->setListDepth(1);
+ } else if (start->isListItem()) {
+ start->setListItem(false);
+ start->setListDepth(qMax(start->listDepth()-1, 0));
+ }
+ start = start->next();
+ }
+
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ drawCursor(true);
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Sets the alignment of the current paragraph to \a a. Valid
+ alignments are Qt::AlignLeft, Qt::AlignRight,
+ Qt::AlignJustify and Qt::AlignCenter (which centers
+ horizontally).
+*/
+
+void Q3TextEdit::setAlignment(int a)
+{
+ if (isReadOnly() || block_set_alignment)
+ return;
+
+ drawCursor(false);
+ Q3TextParagraph *start = cursor->paragraph();
+ Q3TextParagraph *end = start;
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
+ end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
+ if (end->paragId() < start->paragId())
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
+
+ while (start != end->next()) {
+ start->setAlignment(a);
+ start = start->next();
+ }
+
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ drawCursor(true);
+ if (currentAlignment != a) {
+ currentAlignment = a;
+ emit currentAlignmentChanged(currentAlignment);
+ }
+ setModified();
+ emit textChanged();
+}
+
+void Q3TextEdit::updateCurrentFormat()
+{
+ int i = cursor->index();
+ if (i > 0)
+ --i;
+ if (doc->useFormatCollection() &&
+ (!currentFormat || currentFormat->key() != cursor->paragraph()->at(i)->format()->key())) {
+ if (currentFormat)
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(i)->format());
+ if (currentFormat->isMisspelled()) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
+ }
+ emit currentFontChanged(currentFormat->font());
+ emit currentColorChanged(currentFormat->color());
+ emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
+ }
+
+ if (currentAlignment != cursor->paragraph()->alignment()) {
+ currentAlignment = cursor->paragraph()->alignment();
+ block_set_alignment = true;
+ emit currentAlignmentChanged(currentAlignment);
+ block_set_alignment = false;
+ }
+}
+
+/*!
+ If \a b is true sets the current format to italic; otherwise sets
+ the current format to non-italic.
+
+ \sa italic()
+*/
+
+void Q3TextEdit::setItalic(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setItalic(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Italic);
+}
+
+/*!
+ If \a b is true sets the current format to bold; otherwise sets
+ the current format to non-bold.
+
+ \sa bold()
+*/
+
+void Q3TextEdit::setBold(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setBold(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Bold);
+}
+
+/*!
+ If \a b is true sets the current format to underline; otherwise
+ sets the current format to non-underline.
+
+ \sa underline()
+*/
+
+void Q3TextEdit::setUnderline(bool b)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setUnderline(b);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Underline);
+}
+
+/*!
+ Sets the font family of the current format to \a fontFamily.
+
+ \sa family() setCurrentFont()
+*/
+
+void Q3TextEdit::setFamily(const QString &fontFamily)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setFamily(fontFamily);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Family);
+}
+
+/*!
+ Sets the point size of the current format to \a s.
+
+ Note that if \a s is zero or negative, the behavior of this
+ function is not defined.
+
+ \sa pointSize() setCurrentFont() setFamily()
+*/
+
+void Q3TextEdit::setPointSize(int s)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setPointSize(s);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Size);
+}
+
+/*!
+ Sets the color of the current format, i.e. of the text, to \a c.
+
+ \sa color() setPaper()
+*/
+
+void Q3TextEdit::setColor(const QColor &c)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setColor(c);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Color);
+}
+
+/*!
+ Sets the vertical alignment of the current format, i.e. of the
+ text, to \a a.
+
+ \sa color() setPaper()
+*/
+
+void Q3TextEdit::setVerticalAlignment(Q3TextEdit::VerticalAlignment a)
+{
+ Q3TextFormat f(*currentFormat);
+ f.setVAlign((Q3TextFormat::VerticalAlignment)a);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::VAlign);
+}
+
+void Q3TextEdit::setFontInternal(const QFont &f_)
+{
+ QFont font = f_;
+ if (font.kerning())
+ font.setKerning(false);
+ Q3TextFormat f(*currentFormat);
+ f.setFont(font);
+ Q3TextFormat *f2 = doc->formatCollection()->format(&f);
+ setFormat(f2, Q3TextFormat::Font);
+}
+
+
+QString Q3TextEdit::text() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimText();
+#endif
+
+ Q3TextParagraph *p = doc->firstParagraph();
+ if (!p || (!p->next() && p->length() <= 1))
+ return QString::fromLatin1("");
+
+ if (isReadOnly())
+ return doc->originalText();
+ return doc->text();
+}
+
+/*!
+ \overload
+
+ Returns the text of paragraph \a para.
+
+ If textFormat() is Qt::RichText the text will contain HTML
+ formatting tags.
+*/
+
+QString Q3TextEdit::text(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && (d->od->numLines >= para)) {
+ QString paraStr = d->od->lines[LOGOFFSET(para)];
+ if (paraStr.isEmpty())
+ paraStr = QLatin1Char('\n');
+ return paraStr;
+ } else
+#endif
+ 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 Qt::AutoText,
+ i.e. the text edit auto-detects the format from \a text.
+
+ For rich text the rendering style and available tags are defined
+ by a styleSheet(); see Q3StyleSheet for details.
+
+ The optional \a context is a path which the text edit's
+ Q3MimeSourceFactory uses to resolve the locations of files and
+ images. (See \l{Q3TextEdit::Q3TextEdit()}.) It is passed to the text
+ edit's Q3MimeSourceFactory when quering data.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa text(), setTextFormat()
+*/
+
+void Q3TextEdit::setText(const QString &text, const QString &context)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetText(text);
+ return;
+ }
+#endif
+ if (!isModified() && isReadOnly() &&
+ this->context() == context && this->text() == text)
+ return;
+
+ emit undoAvailable(false);
+ emit redoAvailable(false);
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ lastFormatted = 0;
+ int oldCursorPos = cursor->index();
+ int oldCursorPar = cursor->paragraph()->paragId();
+ cursor->restoreState();
+ delete cursor;
+ doc->setText(text, context);
+
+ if (wrapMode == FixedPixelWidth) {
+ resizeContents(wrapWidth, 0);
+ doc->setWidth(wrapWidth);
+ doc->setMinimumWidth(wrapWidth);
+ } else {
+ doc->setMinimumWidth(-1);
+ resizeContents(0, 0);
+ }
+
+ lastFormatted = doc->firstParagraph();
+ cursor = new Q3TextCursor(doc);
+ updateContents();
+
+ if (isModified())
+ setModified(false);
+ emit textChanged();
+ if (cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar) {
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ }
+ formatMore();
+ updateCurrentFormat();
+ d->scrollToAnchor.clear();
+}
+
+/*!
+ \property Q3TextEdit::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 Qt::AutoText,
+ i.e. the text edit auto-detects the format of the text.
+
+ For richtext, calling text() on an editable Q3TextEdit 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 Q3TextEdit::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 was found; otherwise returns false.
+
+ If \a para and \a index are both 0 the search begins from the
+ current cursor position. If \a para and \a index are both not 0,
+ the search begins from the \c{*}\a{index} character position in the
+ \c{*}\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 0, the number of the paragraph in which the first
+ character of the match was found is put into \c{*}\a{para}, and the
+ index position of that character within the paragraph is put into
+ \c{*}\a{index}.
+
+ If \a expr is not found the function returns false. If \a index
+ and \a para are not 0 and \a expr is not found, \c{*}\a{index}
+ and \c{*}\a{para} are undefined.
+
+ Please note that this function will make the next occurrence of
+ the string (if found) the current selection, and will thus
+ modify the cursor position.
+
+ Using the \a para and \a index parameters will not work correctly
+ in case the document contains tables.
+*/
+
+bool Q3TextEdit::find(const QString &expr, bool cs, bool wo, bool forward,
+ int *para, int *index)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimFind(expr, cs, wo, forward, para, index);
+#endif
+ drawCursor(false);
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ Q3TextCursor findcur = *cursor;
+ if (para && index) {
+ if (doc->paragAt(*para))
+ findcur.gotoPosition(doc->paragAt(*para), *index);
+ else
+ findcur.gotoEnd();
+ } else if (doc->hasSelection(Q3TextDocument::Standard)){
+ // maks sure we do not find the same selection again
+ if (forward)
+ findcur.gotoNextLetter();
+ else
+ findcur.gotoPreviousLetter();
+ } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) {
+ findcur.gotoEnd();
+ }
+ removeSelection(Q3TextDocument::Standard);
+ bool found = doc->find(findcur, expr, cs, wo, forward);
+ if (found) {
+ if (para)
+ *para = findcur.paragraph()->paragId();
+ if (index)
+ *index = findcur.index();
+ *cursor = findcur;
+ repaintChanged();
+ ensureCursorVisible();
+ }
+ drawCursor(true);
+ if (found) {
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+ }
+ return found;
+}
+
+void Q3TextEdit::blinkCursor()
+{
+ bool cv = cursorVisible;
+ blinkCursorVisible = !blinkCursorVisible;
+ drawCursor(blinkCursorVisible);
+ cursorVisible = cv;
+}
+
+/*!
+ Sets the cursor to position \a index in paragraph \a para.
+
+ \sa getCursorPosition()
+*/
+
+void Q3TextEdit::setCursorPosition(int para, int index)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return;
+
+ if (index > p->length() - 1)
+ index = p->length() - 1;
+
+ drawCursor(false);
+ cursor->setParagraph(p);
+ cursor->setIndex(index);
+ ensureCursorVisible();
+ drawCursor(true);
+ updateCurrentFormat();
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+}
+
+/*!
+ This function sets the \c{*}\a{para} and \c{*}\a{index} parameters to the
+ current cursor position. \a para and \a index must not be 0.
+
+ \sa setCursorPosition()
+*/
+
+void Q3TextEdit::getCursorPosition(int *para, int *index) const
+{
+ if (!para || !index)
+ return;
+ *para = cursor->paragraph()->paragId();
+ *index = cursor->index();
+}
+
+/*!
+ Sets a selection which starts at position \a indexFrom in
+ paragraph \a paraFrom and ends at position \a indexTo in paragraph
+ \a paraTo.
+
+ Any existing selections which have a different id (\a selNum) are
+ left alone, but if an existing selection has the same id as \a
+ selNum it is removed and replaced by this selection.
+
+ 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 Q3TextEdit::setSelection(int paraFrom, int indexFrom,
+ int paraTo, int indexTo, int selNum)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetSelection(paraFrom, indexFrom, paraTo, indexTo);
+ repaintContents();
+ return;
+ }
+#endif
+ if (doc->hasSelection(selNum)) {
+ doc->removeSelection(selNum);
+ repaintChanged();
+ }
+ if (selNum > doc->numSelections() - 1)
+ doc->addSelection(selNum);
+ Q3TextParagraph *p1 = doc->paragAt(paraFrom);
+ if (!p1)
+ return;
+ Q3TextParagraph *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);
+ Q3TextCursor c = *cursor;
+ Q3TextCursor oldCursor = *cursor;
+ c.setParagraph(p1);
+ c.setIndex(indexFrom);
+ cursor->setParagraph(p2);
+ cursor->setIndex(indexTo);
+ doc->setSelectionStart(selNum, c);
+ doc->setSelectionEnd(selNum, *cursor);
+ repaintChanged();
+ ensureCursorVisible();
+ if (selNum != Q3TextDocument::Standard)
+ *cursor = oldCursor;
+ drawCursor(true);
+}
+
+/*!
+ If there is a selection, \c{*}\a{paraFrom} is set to the number of the
+ paragraph in which the selection begins and \c{*}\a{paraTo} is set to
+ the number of the paragraph in which the selection ends. (They
+ could be the same.) \c{*}\a{indexFrom} is set to the index at which the
+ selection begins within \c{*}\a{paraFrom}, and \c{*}\a{indexTo} is set to
+ the index at which the selection ends within \c{*}\a{paraTo}.
+
+ If there is no selection, \c{*}\a{paraFrom}, \c{*}\a{indexFrom},
+ \c{*}\a{paraTo} and \c{*}\a{indexTo} are all set to -1.
+
+ If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this
+ function does nothing.
+
+ The \a selNum is the number of the selection (multiple selections
+ are supported). It defaults to 0 (the default selection).
+
+ \sa setSelection() selectedText
+*/
+
+void Q3TextEdit::getSelection(int *paraFrom, int *indexFrom,
+ int *paraTo, int *indexTo, int selNum) const
+{
+ if (!paraFrom || !paraTo || !indexFrom || !indexTo)
+ return;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ *paraFrom = d->od->selStart.line;
+ *paraTo = d->od->selEnd.line;
+ *indexFrom = d->od->selStart.index;
+ *indexTo = d->od->selEnd.index;
+ return;
+ }
+#endif
+ if (!doc->hasSelection(selNum)) {
+ *paraFrom = -1;
+ *indexFrom = -1;
+ *paraTo = -1;
+ *indexTo = -1;
+ return;
+ }
+
+ doc->selectionStart(selNum, *paraFrom, *indexFrom);
+ doc->selectionEnd(selNum, *paraTo, *indexTo);
+}
+
+/*!
+ \property Q3TextEdit::textFormat
+ \brief the text format: rich text, plain text, log text or auto text.
+
+ The text format is one of the following:
+ \list
+ \i Qt::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 Qt::RichText - rich text rendering. The available styles are
+ defined in the default stylesheet Q3StyleSheet::defaultSheet().
+ \i Qt::LogText - optimized mode for very large texts. Supports a very
+ limited set of formatting tags (color, bold, underline and italic
+ settings).
+ \i Qt::AutoText - this is the default. The text edit autodetects which
+ rendering style is best, Qt::PlainText or Qt::RichText. This is done
+ by using the Q3StyleSheet::mightBeRichText() function.
+ \endlist
+*/
+
+void Q3TextEdit::setTextFormat(Qt::TextFormat format)
+{
+ doc->setTextFormat(format);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+Qt::TextFormat Q3TextEdit::textFormat() const
+{
+ return doc->textFormat();
+}
+
+/*!
+ Returns the number of paragraphs in the text; an empty textedit is always
+ considered to have one paragraph, so 1 is returned in this case.
+*/
+
+int Q3TextEdit::paragraphs() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ return d->od->numLines;
+ }
+#endif
+ return doc->lastParagraph()->paragId() + 1;
+}
+
+/*!
+ Returns the number of lines in paragraph \a para, or -1 if there
+ is no paragraph with index \a para.
+*/
+
+int Q3TextEdit::linesOfParagraph(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (d->od->numLines >= para)
+ return 1;
+ else
+ return -1;
+ }
+#endif
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return -1;
+ return p->lines();
+}
+
+/*!
+ Returns the length of the paragraph \a para (i.e. the number of
+ characters), or -1 if there is no paragraph with index \a para.
+
+ This function ignores newlines.
+*/
+
+int Q3TextEdit::paragraphLength(int para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (d->od->numLines >= para) {
+ if (d->od->lines[LOGOFFSET(para)].isEmpty()) // CR
+ return 1;
+ else
+ return d->od->lines[LOGOFFSET(para)].length();
+ }
+ return -1;
+ }
+#endif
+ Q3TextParagraph *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 Q3TextEdit::lines() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ return d->od->numLines;
+ }
+#endif
+ Q3TextParagraph *p = doc->firstParagraph();
+ int l = 0;
+ while (p) {
+ l += p->lines();
+ p = p->next();
+ }
+
+ return l;
+}
+
+/*!
+ Returns the line number of the line in paragraph \a para in which
+ the character at position \a index appears. The \a index position is
+ relative to the beginning of the paragraph. If there is no such
+ paragraph or no such character at the \a index position (e.g. the
+ index is out of range) -1 is returned.
+*/
+
+int Q3TextEdit::lineOfChar(int para, int index)
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return -1;
+
+ int idx, line;
+ Q3TextStringChar *c = p->lineStartOfChar(index, &idx, &line);
+ if (!c)
+ return -1;
+
+ return line;
+}
+
+void Q3TextEdit::setModified(bool m)
+{
+ bool oldModified = modified;
+ modified = m;
+ if (modified && doc->oTextValid)
+ doc->invalidateOriginalText();
+ if (oldModified != modified)
+ emit modificationChanged(modified);
+}
+
+/*!
+ \property Q3TextEdit::modified
+ \brief whether the document has been modified by the user
+*/
+
+bool Q3TextEdit::isModified() const
+{
+ return modified;
+}
+
+void Q3TextEdit::setModified()
+{
+ if (!isModified())
+ setModified(true);
+}
+
+/*!
+ Returns true if the current format is italic; otherwise returns false.
+
+ \sa setItalic()
+*/
+
+bool Q3TextEdit::italic() const
+{
+ return currentFormat->font().italic();
+}
+
+/*!
+ Returns true if the current format is bold; otherwise returns false.
+
+ \sa setBold()
+*/
+
+bool Q3TextEdit::bold() const
+{
+ return currentFormat->font().bold();
+}
+
+/*!
+ Returns true if the current format is underlined; otherwise returns
+ false.
+
+ \sa setUnderline()
+*/
+
+bool Q3TextEdit::underline() const
+{
+ return currentFormat->font().underline();
+}
+
+/*!
+ Returns the font family of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+QString Q3TextEdit::family() const
+{
+ return currentFormat->font().family();
+}
+
+/*!
+ Returns the point size of the font of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+int Q3TextEdit::pointSize() const
+{
+ return currentFormat->font().pointSize();
+}
+
+/*!
+ Returns the color of the current format.
+
+ \sa setColor() setPaper()
+*/
+
+QColor Q3TextEdit::color() const
+{
+ return currentFormat->color();
+}
+
+/*!
+ Returns Q3ScrollView::font()
+
+ \warning In previous versions this function returned the font of
+ the current format. This lead to confusion. Please use
+ currentFont() instead.
+*/
+
+QFont Q3TextEdit::font() const
+{
+ return Q3ScrollView::font();
+}
+
+/*!
+ Returns the font of the current format.
+
+ \sa setCurrentFont() setFamily() setPointSize()
+*/
+
+QFont Q3TextEdit::currentFont() const
+{
+ return currentFormat->font();
+}
+
+
+/*!
+ Returns the alignment of the current paragraph.
+
+ \sa setAlignment()
+*/
+
+int Q3TextEdit::alignment() const
+{
+ return currentAlignment;
+}
+
+/*!
+ Returns the vertical alignment of the current format.
+
+ \sa setVerticalAlignment()
+*/
+
+Q3TextEdit::VerticalAlignment Q3TextEdit::verticalAlignment() const
+{
+ return (Q3TextEdit::VerticalAlignment) currentFormat->vAlign();
+}
+
+void Q3TextEdit::startDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ mousePressed = false;
+ inDoubleClick = false;
+ Q3DragObject *drag = dragObject(viewport());
+ if (!drag)
+ return;
+ if (isReadOnly()) {
+ drag->dragCopy();
+ } else {
+ if (drag->drag() && Q3DragObject::target() != this && Q3DragObject::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 Q3TextEdit::selectAll(bool select)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ if (select)
+ optimSelectAll();
+ else
+ optimRemoveSelection();
+ return;
+ }
+#endif
+ if (!select)
+ doc->removeSelection(Q3TextDocument::Standard);
+ else
+ doc->selectAll(Q3TextDocument::Standard);
+ repaintChanged();
+ emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
+ emit selectionChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+}
+
+void Q3TextEdit::UndoRedoInfo::clear()
+{
+ if (valid()) {
+ if (type == Insert || type == Return)
+ doc->addCommand(new Q3TextInsertCommand(doc, id, index, d->text.rawData(), styleInformation));
+ else if (type == Format)
+ doc->addCommand(new Q3TextFormatCommand(doc, id, index, eid, eindex, d->text.rawData(), format, flags));
+ else if (type == Style)
+ doc->addCommand(new Q3TextStyleCommand(doc, id, eid, styleInformation));
+ else if (type != Invalid) {
+ doc->addCommand(new Q3TextDeleteCommand(doc, id, index, d->text.rawData(), styleInformation));
+ }
+ }
+ type = Invalid;
+ d->text.clear();
+ id = -1;
+ index = -1;
+ styleInformation = QByteArray();
+}
+
+
+/*!
+ If there is some selected text (in selection 0) it is deleted. If
+ there is no selected text (in selection 0) the character to the
+ right of the text cursor is deleted.
+
+ \sa removeSelectedText() cut()
+*/
+
+void Q3TextEdit::del()
+{
+ if (doc->hasSelection(Q3TextDocument::Standard)) {
+ removeSelectedText();
+ return;
+ }
+
+ doKeyboardAction(ActionDelete);
+}
+
+
+Q3TextEdit::UndoRedoInfo::UndoRedoInfo(Q3TextDocument *dc)
+ : type(Invalid), doc(dc)
+{
+ d = new QUndoRedoInfoPrivate;
+ d->text.clear();
+ id = -1;
+ index = -1;
+}
+
+Q3TextEdit::UndoRedoInfo::~UndoRedoInfo()
+{
+ delete d;
+}
+
+bool Q3TextEdit::UndoRedoInfo::valid() const
+{
+ return id >= 0 && type != Invalid;
+}
+
+/*!
+ \internal
+
+ Resets the current format to the default format.
+*/
+
+void Q3TextEdit::resetFormat()
+{
+ setAlignment(Qt::AlignAuto);
+ setParagType(Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc);
+ setFormat(doc->formatCollection()->defaultFormat(), Q3TextFormat::Format);
+}
+
+/*!
+ Returns the Q3StyleSheet which is being used by this text edit.
+
+ \sa setStyleSheet()
+*/
+
+Q3StyleSheet* Q3TextEdit::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 Q3TextEdit::setStyleSheet(Q3StyleSheet* styleSheet)
+{
+ doc->setStyleSheet(styleSheet);
+}
+
+/*!
+ \property Q3TextEdit::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 Q3TextEdit::setPaper(const QBrush& pap)
+{
+ doc->setPaper(new QBrush(pap));
+ if ( pap.pixmap() )
+ viewport()->setBackgroundPixmap( *pap.pixmap() );
+ QPalette pal = palette();
+ pal.setColor(QPalette::Window, pap.color());
+ setPalette(pal);
+ pal = viewport()->palette();
+ pal.setColor(QPalette::Window, pap.color());
+ viewport()->setPalette(pal);
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ // force a repaint of the entire viewport - using updateContents()
+ // would clip the coords to the content size
+ if (d->optimMode)
+ repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
+ else
+#endif
+ updateContents();
+}
+
+QBrush Q3TextEdit::paper() const
+{
+ if (doc->paper())
+ return *doc->paper();
+ return QBrush(palette().base());
+}
+
+/*!
+ \property Q3TextEdit::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 Q3TextEdit::setLinkUnderline(bool b)
+{
+ if (doc->underlineLinks() == b)
+ return;
+ doc->setUnderlineLinks(b);
+ repaintChanged();
+}
+
+bool Q3TextEdit::linkUnderline() const
+{
+ return doc->underlineLinks();
+}
+
+/*!
+ Sets the text edit's mimesource factory to \a factory. See
+ Q3MimeSourceFactory for further details.
+
+ \sa mimeSourceFactory()
+ */
+
+#ifndef QT_NO_MIME
+void Q3TextEdit::setMimeSourceFactory(Q3MimeSourceFactory* factory)
+{
+ doc->setMimeSourceFactory(factory);
+}
+
+/*!
+ Returns the Q3MimeSourceFactory which is being used by this text
+ edit.
+
+ \sa setMimeSourceFactory()
+*/
+
+Q3MimeSourceFactory* Q3TextEdit::mimeSourceFactory() const
+{
+ return doc->mimeSourceFactory();
+}
+#endif
+
+/*!
+ 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 Q3TextEdit::heightForWidth(int w) const
+{
+ int oldw = doc->width();
+ doc->doLayout(0, w);
+ int h = doc->height();
+ doc->setWidth(oldw);
+ doc->invalidate();
+ ((Q3TextEdit*)this)->formatMore();
+ return h;
+}
+
+/*!
+ Appends a new paragraph with \a text to the end of the text edit. Note that
+ the undo/redo history is cleared by this function, and no undo
+ history is kept for appends which makes them faster than
+ insert()s. If you want to append text which is added to the
+ undo/redo history as well, use insertParagraph().
+*/
+
+void Q3TextEdit::append(const QString &text)
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimAppend(text);
+ return;
+ }
+#endif
+ // flush and clear the undo/redo stack if necessary
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ doc->removeSelection(Q3TextDocument::Standard);
+ Qt::TextFormat f = doc->textFormat();
+ if (f == Qt::AutoText) {
+ if (Q3StyleSheet::mightBeRichText(text))
+ f = Qt::RichText;
+ else
+ f = Qt::PlainText;
+ }
+
+ drawCursor(false);
+ Q3TextCursor oldc(*cursor);
+ ensureFormatted(doc->lastParagraph());
+ bool atBottom = contentsY() >= contentsHeight() - visibleHeight();
+ cursor->gotoEnd();
+ if (cursor->index() > 0)
+ cursor->splitAndInsertEmptyParagraph();
+ Q3TextCursor oldCursor2 = *cursor;
+
+ if (f == Qt::PlainText) {
+ cursor->insert(text, true);
+ if (doc->useFormatCollection() && !doc->preProcessor() &&
+ currentFormat != cursor->paragraph()->at( cursor->index() )->format()) {
+ doc->setSelectionStart( Q3TextDocument::Temp, oldCursor2 );
+ doc->setSelectionEnd( Q3TextDocument::Temp, *cursor );
+ doc->setFormat( Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format );
+ doc->removeSelection( Q3TextDocument::Temp );
+ }
+ } else {
+ cursor->paragraph()->setListItem(false);
+ cursor->paragraph()->setListDepth(0);
+ if (cursor->paragraph()->prev())
+ cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change
+ doc->setRichTextInternal(text);
+ }
+ formatMore();
+ repaintChanged();
+ if (atBottom)
+ scrollToBottom();
+ *cursor = oldc;
+ if (!isReadOnly())
+ cursorVisible = true;
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ \property Q3TextEdit::hasSelectedText
+ \brief whether some text is selected in selection 0
+*/
+
+bool Q3TextEdit::hasSelectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimHasSelection();
+ else
+#endif
+ return doc->hasSelection(Q3TextDocument::Standard);
+}
+
+/*!
+ \property Q3TextEdit::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 Qt::PlainText if the textFormat() is
+ Qt::PlainText or Qt::AutoText, otherwise it is returned as HTML.
+
+ \sa hasSelectedText
+*/
+
+QString Q3TextEdit::selectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return optimSelectedText();
+ else
+#endif
+ return doc->selectedText(Q3TextDocument::Standard, textFormat() == Qt::RichText);
+}
+
+bool Q3TextEdit::handleReadOnlyKeyEvent(QKeyEvent *e)
+{
+ switch(e->key()) {
+ case Qt::Key_Down:
+ setContentsPos(contentsX(), contentsY() + 10);
+ break;
+ case Qt::Key_Up:
+ setContentsPos(contentsX(), contentsY() - 10);
+ break;
+ case Qt::Key_Left:
+ setContentsPos(contentsX() - 10, contentsY());
+ break;
+ case Qt::Key_Right:
+ setContentsPos(contentsX() + 10, contentsY());
+ break;
+ case Qt::Key_PageUp:
+ setContentsPos(contentsX(), contentsY() - visibleHeight());
+ break;
+ case Qt::Key_PageDown:
+ setContentsPos(contentsX(), contentsY() + visibleHeight());
+ break;
+ case Qt::Key_Home:
+ setContentsPos(contentsX(), 0);
+ break;
+ case Qt::Key_End:
+ setContentsPos(contentsX(), contentsHeight() - visibleHeight());
+ break;
+ case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifndef QT_NO_NETWORKPROTOCOL
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Space: {
+ if (!doc->focusIndicator.href.isEmpty()
+ || !doc->focusIndicator.name.isEmpty()) {
+ if (!doc->focusIndicator.href.isEmpty()) {
+ QUrl u = QUrl(doc->context()).resolved(doc->focusIndicator.href);
+ emitLinkClicked(u.toString(QUrl::None));
+ }
+ if (!doc->focusIndicator.name.isEmpty())
+ if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
+ emit browser->anchorClicked(doc->focusIndicator.name, doc->focusIndicator.href);
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ }
+ } break;
+#endif
+ default:
+ if (e->state() & Qt::ControlButton) {
+ switch (e->key()) {
+ case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifdef Q_WS_WIN
+ case Qt::Key_Insert:
+ copy();
+ break;
+ case Qt::Key_A:
+ selectAll();
+ break;
+#endif
+ }
+
+ }
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns the context of the text edit. The context is a path which
+ the text edit's Q3MimeSourceFactory uses to resolve the locations
+ of files and images.
+
+ \sa text
+*/
+
+QString Q3TextEdit::context() const
+{
+ return doc->context();
+}
+
+/*!
+ \property Q3TextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ For Qt::PlainText the title will be an empty string. For \c
+ Qt::RichText the title will be the text between the \c{<title>} tags,
+ if present, otherwise an empty string.
+*/
+
+QString Q3TextEdit::documentTitle() const
+{
+ return doc->attributes()[QLatin1String("title")];
+}
+
+void Q3TextEdit::makeParagVisible(Q3TextParagraph *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 Q3TextEdit::scrollToAnchor(const QString& name)
+{
+ if (!isVisible()) {
+ d->scrollToAnchor = name;
+ return;
+ }
+ if (name.isEmpty())
+ return;
+ sync();
+ Q3TextCursor cursor(doc);
+ Q3TextParagraph* last = doc->lastParagraph();
+ for (;;) {
+ Q3TextStringChar* c = cursor.paragraph()->at(cursor.index());
+ if(c->isAnchor()) {
+ QString a = c->anchorName();
+ if (a == name ||
+ (a.contains(QLatin1Char('#')) && a.split(QLatin1Char('#')).contains(name))) {
+ setContentsPos(contentsX(), qMin(cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight()));
+ break;
+ }
+ }
+ if (cursor.paragraph() == last && cursor.atParagEnd() )
+ break;
+ cursor.gotoNextLetter();
+ }
+}
+
+/*!
+ Returns the text for the attribute \a attr (Qt::AnchorHref by
+ default) if there is an anchor at position \a pos (in contents
+ coordinates); otherwise returns an empty string.
+*/
+
+QString Q3TextEdit::anchorAt(const QPoint& pos, Qt::AnchorAttribute attr)
+{
+ Q3TextCursor c(doc);
+ placeCursor(pos, &c, true);
+ switch(attr) {
+ case Qt::AnchorName:
+ return c.paragraph()->at(c.index())->anchorName();
+ case Qt::AnchorHref:
+ return c.paragraph()->at(c.index())->anchorHref();
+ }
+ // incase the compiler is really dumb about determining if a function
+ // returns something :)
+ return QString();
+}
+
+void Q3TextEdit::documentWidthChanged(int w)
+{
+ resizeContents(qMax(visibleWidth(), w), contentsHeight());
+}
+
+/*! \internal
+
+ This function does nothing
+*/
+
+void Q3TextEdit::updateStyles()
+{
+}
+
+void Q3TextEdit::setDocument(Q3TextDocument *dc)
+{
+ if (dc == 0) {
+ qWarning("Q3TextEdit::setDocument() called with null Q3TextDocument pointer");
+ return;
+ }
+ if (dc == doc)
+ return;
+ doc = dc;
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ clearUndoRedo();
+ undoRedoInfo.doc = doc;
+ lastFormatted = 0;
+}
+
+#ifndef QT_NO_CLIPBOARD
+
+/*!
+ Pastes the text with format \a subtype from the clipboard into the
+ text edit at the current cursor position. The \a subtype can be
+ "plain" or "html".
+
+ If there is no text with format \a subtype in the clipboard
+ nothing happens.
+
+ \sa paste() cut() Q3TextEdit::copy()
+*/
+
+void Q3TextEdit::pasteSubType(const QByteArray &subtype)
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
+ pasteSubType(subtype, m);
+#endif
+}
+
+/*! \internal */
+
+void Q3TextEdit::pasteSubType(const QByteArray& subtype, QMimeSource *m)
+{
+#ifndef QT_NO_MIME
+ QByteArray st = subtype;
+
+ if (subtype != "x-qrichtext")
+ st.prepend("text/");
+ else
+ st.prepend("application/");
+ if (!m)
+ return;
+ if (doc->hasSelection(Q3TextDocument::Standard))
+ removeSelectedText();
+ if (!Q3RichTextDrag::canDecode(m))
+ return;
+ QString t;
+ if (!Q3RichTextDrag::decode(m, t, QString::fromLatin1(st), QString::fromLatin1(subtype)))
+ return;
+ if (st == "application/x-qrichtext") {
+ int start;
+ if ((start = t.indexOf(QLatin1String("<!--StartFragment-->"))) != -1) {
+ start += 20;
+ int end = t.indexOf(QLatin1String("<!--EndFragment-->"));
+ Q3TextCursor oldC = *cursor;
+
+ // during the setRichTextInternal() call the cursors
+ // paragraph might get joined with the provious one, so
+ // the cursors one would get deleted and oldC.paragraph()
+ // would be a dnagling pointer. To avoid that try to go
+ // one letter back and later go one forward again.
+ oldC.gotoPreviousLetter();
+ bool couldGoBack = oldC != *cursor;
+ // first para might get deleted, so remember to reset it
+ bool wasAtFirst = oldC.paragraph() == doc->firstParagraph();
+
+ if (start < end)
+ t = t.mid(start, end - start);
+ else
+ t = t.mid(start);
+ lastFormatted = cursor->paragraph();
+ if (lastFormatted->prev())
+ lastFormatted = lastFormatted->prev();
+ doc->setRichTextInternal(t, cursor);
+
+ // the first para might have been deleted in
+ // setRichTextInternal(). To be sure, reset it if
+ // necessary.
+ if (wasAtFirst) {
+ int index = oldC.index();
+ oldC.setParagraph(doc->firstParagraph());
+ oldC.setIndex(index);
+ }
+
+ // if we went back one letter before (see last comment),
+ // go one forward to point to the right position
+ if (couldGoBack)
+ oldC.gotoNextLetter();
+
+ if (undoEnabled && !isReadOnly()) {
+ doc->setSelectionStart(Q3TextDocument::Temp, oldC);
+ doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
+
+ checkUndoRedoInfo(UndoRedoInfo::Insert);
+ if (!undoRedoInfo.valid()) {
+ undoRedoInfo.id = oldC.paragraph()->paragId();
+ undoRedoInfo.index = oldC.index();
+ undoRedoInfo.d->text.clear();
+ }
+ int oldLen = undoRedoInfo.d->text.length();
+ if (!doc->preProcessor()) {
+ QString txt = doc->selectedText(Q3TextDocument::Temp);
+ undoRedoInfo.d->text += txt;
+ for (int i = 0; i < (int)txt.length(); ++i) {
+ if (txt[i] != QLatin1Char('\n') && oldC.paragraph()->at(oldC.index())->format()) {
+ oldC.paragraph()->at(oldC.index())->format()->addRef();
+ undoRedoInfo.d->text.
+ setFormat(oldLen + i, oldC.paragraph()->at(oldC.index())->format(), true);
+ }
+ oldC.gotoNextLetter();
+ }
+ }
+ undoRedoInfo.clear();
+ removeSelection(Q3TextDocument::Temp);
+ }
+
+ formatMore();
+ setModified();
+ emit textChanged();
+ repaintChanged();
+ ensureCursorVisible();
+ return;
+ }
+ } else {
+#if defined(Q_OS_WIN32)
+ // Need to convert CRLF to LF
+ t.replace(QLatin1String("\r\n"), QLatin1String("\n"));
+#elif defined(Q_OS_MAC)
+ //need to convert CR to LF
+ t.replace(QLatin1Char('\r'), QLatin1Char('\n'));
+#endif
+ QChar *uc = (QChar *)t.unicode();
+ for (int i = 0; i < t.length(); i++) {
+ if (uc[i] < QLatin1Char(' ') && uc[i] != QLatin1Char('\n') && uc[i] != QLatin1Char('\t'))
+ uc[i] = QLatin1Char(' ');
+ }
+ if (!t.isEmpty())
+ insert(t, false, true);
+ }
+#endif //QT_NO_MIME
+}
+
+#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 Q3TextEdit::pasteSpecial(const QPoint& pt)
+{
+ QByteArray st = pickSpecial(QApplication::clipboard()->data(d->clipboard_mode),
+ true, pt);
+ if (!st.isEmpty())
+ pasteSubType(st);
+}
+#endif
+#ifndef QT_NO_MIME
+QByteArray Q3TextEdit::pickSpecial(QMimeSource* ms, bool always_ask, const QPoint& pt)
+{
+ if (ms) {
+#ifndef QT_NO_MENU
+ QMenu popup(this);
+ QString fmt;
+ int n = 0;
+ QHash<QString, bool> done;
+ for (int i = 0; !(fmt = QLatin1String(ms->format(i))).isNull(); i++) {
+ int semi = fmt.indexOf(QLatin1Char(';'));
+ if (semi >= 0)
+ fmt = fmt.left(semi);
+ if (fmt.left(5) == QLatin1String("text/")) {
+ fmt = fmt.mid(5);
+ if (!done.contains(fmt)) {
+ done.insert(fmt,true);
+ popup.insertItem(fmt, i);
+ n++;
+ }
+ }
+ }
+ if (n) {
+ QAction *action = (n == 1 && !always_ask)
+ ? popup.actions().at(0)
+ : popup.exec(pt);
+ if (action)
+ return action->text().toLatin1();
+ }
+#else
+ QString fmt;
+ for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
+ int semi = fmt.indexOf(';');
+ if (semi >= 0)
+ fmt = fmt.left(semi);
+ if (fmt.left(5) == "text/") {
+ fmt = fmt.mid(5);
+ return fmt.latin1();
+ }
+ }
+#endif
+ }
+ return QByteArray();
+}
+#endif // QT_NO_MIME
+#endif // QT_NO_CLIPBOARD
+
+/*!
+ \enum Q3TextEdit::WordWrap
+
+ This enum defines the Q3TextEdit's word wrap modes.
+
+ \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 Q3TextEdit::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 Q3TextEdit::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();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ break;
+ case WidgetWidth:
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(-1);
+ doResize();
+ break;
+ case FixedPixelWidth:
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(-1);
+ if (wrapWidth < 0)
+ wrapWidth = 200;
+ setWrapColumnOrWidth(wrapWidth);
+ break;
+ case FixedColumnWidth:
+ if (wrapWidth < 0)
+ wrapWidth = 80;
+ document()->formatter()->setWrapEnabled(true);
+ document()->formatter()->setWrapAtColumn(wrapWidth);
+ setWrapColumnOrWidth(wrapWidth);
+ break;
+ }
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+Q3TextEdit::WordWrap Q3TextEdit::wordWrap() const
+{
+ return wrapMode;
+}
+
+/*!
+ \property Q3TextEdit::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 Q3TextEdit::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();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+int Q3TextEdit::wrapColumnOrWidth() const
+{
+ if (wrapMode == WidgetWidth)
+ return visibleWidth();
+ return wrapWidth;
+}
+
+
+/*!
+ \enum Q3TextEdit::WrapPolicy
+
+ This enum defines where text can be wrapped in word wrap mode.
+
+ \value AtWhiteSpace Don't use this deprecated value (it is a
+ synonym for \c AtWordBoundary which you should use instead).
+ \value Anywhere Break anywhere, including within words.
+ \value AtWordBoundary Break lines at word boundaries, e.g. spaces or
+ newlines
+ \value AtWordOrDocumentBoundary Break lines at whitespace, e.g.
+ spaces or newlines if possible. Break it anywhere otherwise.
+
+ \sa setWrapPolicy()
+*/
+
+/*!
+ \property Q3TextEdit::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 AtWordBoundary (the default), \c
+ Anywhere and \c AtWordOrDocumentBoundary
+
+ \sa wordWrap
+*/
+
+void Q3TextEdit::setWrapPolicy(WrapPolicy policy)
+{
+ if (wPolicy == policy)
+ return;
+ wPolicy = policy;
+ Q3TextFormatter *formatter;
+ if (policy == AtWordBoundary || policy == AtWordOrDocumentBoundary) {
+ formatter = new Q3TextFormatterBreakWords;
+ formatter->setAllowBreakInWords(policy == AtWordOrDocumentBoundary);
+ } else {
+ formatter = new Q3TextFormatterBreakInWords;
+ }
+ formatter->setWrapAtColumn(document()->formatter()->wrapAtColumn());
+ formatter->setWrapEnabled(document()->formatter()->isWrapEnabled(0));
+ document()->setFormatter(formatter);
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+Q3TextEdit::WrapPolicy Q3TextEdit::wrapPolicy() const
+{
+ return wPolicy;
+}
+
+/*!
+ Deletes all the text in the text edit.
+
+ \sa cut() removeSelectedText() setText()
+*/
+
+void Q3TextEdit::clear()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetText(QLatin1String(""));
+ } else
+#endif
+ {
+ // make clear undoable
+ doc->selectAll(Q3TextDocument::Temp);
+ removeSelectedText(Q3TextDocument::Temp);
+ setContentsPos(0, 0);
+ if (cursor->isValid())
+ cursor->restoreState();
+ doc->clear(true);
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ lastFormatted = 0;
+ }
+ updateContents();
+
+ emit cursorPositionChanged(cursor);
+ emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
+}
+
+int Q3TextEdit::undoDepth() const
+{
+ return document()->undoDepth();
+}
+
+/*!
+ \property Q3TextEdit::length
+ \brief the number of characters in the text
+*/
+
+int Q3TextEdit::length() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode)
+ return d->od->len;
+ else
+#endif
+ return document()->length();
+}
+
+/*!
+ \property Q3TextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+*/
+
+int Q3TextEdit::tabStopWidth() const
+{
+ return document()->tabStopWidth();
+}
+
+void Q3TextEdit::setUndoDepth(int d)
+{
+ document()->setUndoDepth(d);
+}
+
+void Q3TextEdit::setTabStopWidth(int ts)
+{
+ document()->setTabStops(ts);
+ doc->invalidate();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ updateContents();
+}
+
+/*!
+ \reimp
+*/
+
+QSize Q3TextEdit::sizeHint() const
+{
+ // cf. Q3ScrollView::sizeHint()
+ ensurePolished();
+ int f = 2 * frameWidth();
+ int h = fontMetrics().height();
+ QSize sz(f, f);
+ return sz.expandedTo(QSize(12 * h, 8 * h));
+}
+
+void Q3TextEdit::clearUndoRedo()
+{
+ if (!undoEnabled)
+ return;
+ undoRedoInfo.clear();
+ emit undoAvailable(doc->commands()->isUndoAvailable());
+ emit redoAvailable(doc->commands()->isRedoAvailable());
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ This function gets the format of the character at position \a
+ index in paragraph \a para. Sets \a font to the character's font, \a
+ color to the character's color and \a verticalAlignment to the
+ character's vertical alignment.
+
+ Returns false if \a para or \a index is out of range otherwise
+ returns true.
+*/
+
+bool Q3TextEdit::getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment)
+{
+ if (!font || !color)
+ return false;
+ Q3TextParagraph *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;
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ This function gets the format of the paragraph \a para. Sets \a
+ font to the paragraphs's font, \a color to the paragraph's color, \a
+ verticalAlignment to the paragraph's vertical alignment, \a
+ alignment to the paragraph's alignment, \a displayMode to the
+ paragraph's display mode, \a listStyle to the paragraph's list style
+ (if the display mode is Q3StyleSheetItem::DisplayListItem) and \a
+ listDepth to the depth of the list (if the display mode is
+ Q3StyleSheetItem::DisplayListItem).
+
+ Returns false if \a para is out of range otherwise returns true.
+*/
+
+bool Q3TextEdit::getParagraphFormat(int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ Q3StyleSheetItem::DisplayMode *displayMode,
+ Q3StyleSheetItem::ListStyle *listStyle,
+ int *listDepth)
+{
+ if (!font || !color || !alignment || !displayMode || !listStyle)
+ return false;
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return false;
+ *font = p->at(0)->format()->font();
+ *color = p->at(0)->format()->color();
+ *verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign();
+ *alignment = p->alignment();
+ *displayMode = p->isListItem() ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::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.
+
+ \warning The QPopupMenu ID values 0-7 are reserved, and they map to the
+ standard operations. When inserting items into your custom popup menu, be
+ sure to specify ID values larger than 7.
+*/
+
+Q3PopupMenu *Q3TextEdit::createPopupMenu(const QPoint& pos)
+{
+ Q_UNUSED(pos)
+#ifndef QT_NO_POPUPMENU
+ Q3PopupMenu *popup = new Q3PopupMenu(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->addSeparator();
+ }
+#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->addSeparator();
+ }
+#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(Q3TextDocument::Standard, true));
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ popup->setItemEnabled(d->id[IdCopy], d->optimMode ? optimHasSelection() : doc->hasSelection(Q3TextDocument::Standard, true));
+#else
+ popup->setItemEnabled(d->id[IdCopy], doc->hasSelection(Q3TextDocument::Standard, true));
+#endif
+ popup->setItemEnabled(d->id[IdPaste], !isReadOnly() && !QApplication::clipboard()->text(d->clipboard_mode).isEmpty());
+#endif
+ const bool isEmptyDocument = (length() == 0);
+ popup->setItemEnabled(d->id[IdClear], !isReadOnly() && !isEmptyDocument);
+ popup->setItemEnabled(d->id[IdSelectAll], !isEmptyDocument);
+ 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.
+
+ This function is only called if createPopupMenu(const QPoint &)
+ returns 0.
+*/
+
+Q3PopupMenu *Q3TextEdit::createPopupMenu()
+{
+ return 0;
+}
+
+/*!
+ \fn Q3TextEdit::zoomIn()
+
+ \overload
+
+ Zooms in on the text by making the base font size one point
+ larger and recalculating all font sizes to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomOut()
+*/
+
+/*!
+ \fn Q3TextEdit::zoomOut()
+
+ \overload
+
+ Zooms out on the text by making the base font size one point
+ smaller and recalculating all font sizes to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomIn()
+*/
+
+
+/*!
+ Zooms in on the text by making the base font size \a range
+ points larger and recalculating all font sizes to be the new size.
+ This does not change the size of any images.
+
+ \sa zoomOut()
+*/
+
+void Q3TextEdit::zoomIn(int range)
+{
+ QFont f(Q3ScrollView::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 to be the new size. This
+ does not change the size of any images.
+
+ \sa zoomIn()
+*/
+
+void Q3TextEdit::zoomOut(int range)
+{
+ QFont f(Q3ScrollView::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 to be the new size. This does not
+ change the size of any images.
+*/
+
+void Q3TextEdit::zoomTo(int size)
+{
+ QFont f(Q3ScrollView::font());
+ f.setPointSize(size);
+ setFont(f);
+}
+
+/*!
+ Q3TextEdit 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 Q3TextEdit::sync()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QFontMetrics fm(Q3ScrollView::font());
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ } else
+#endif
+ {
+ while (lastFormatted) {
+ lastFormatted->format();
+ lastFormatted = lastFormatted->next();
+ }
+ resizeContents(contentsWidth(), doc->height());
+ }
+ updateScrollBars();
+}
+
+/*!
+ 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 text edit's
+ palette().
+*/
+
+void Q3TextEdit::setSelectionAttributes(int selNum, const QColor &back, bool invertText)
+{
+ if (selNum < 1)
+ return;
+ if (selNum > doc->numSelections())
+ doc->addSelection(selNum);
+ doc->setSelectionColor(selNum, back);
+ if (invertText)
+ doc->setSelectionTextColor(selNum, palette().color(QPalette::HighlightedText));
+}
+
+/*!
+ \reimp
+*/
+void Q3TextEdit::changeEvent(QEvent *ev)
+{
+ if(ev->type() == QEvent::ActivationChange) {
+ if (!isActiveWindow() && scrollTimer)
+ scrollTimer->stop();
+ if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
+ updateContents();
+ }
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode && (ev->type() == QEvent::ApplicationFontChange
+ || ev->type() == QEvent::FontChange)) {
+ QFont f = font();
+ if (f.kerning())
+ f.setKerning(false);
+
+ setFont(f);
+
+ Q3ScrollView::setFont(f);
+ doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color());
+ // recalculate the max string width
+ QFontMetrics fm(f);
+ int i, sw;
+ d->od->maxLineWidth = 0;
+ for (i = 0; i < d->od->numLines; i++) {
+ sw = fm.width(d->od->lines[LOGOFFSET(i)]);
+ if (d->od->maxLineWidth < sw)
+ d->od->maxLineWidth = sw;
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ return;
+ }
+#endif
+
+ Q3ScrollView::changeEvent(ev);
+
+ if (textFormat() == Qt::PlainText) {
+ if (ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange
+ || ev->type() == QEvent::EnabledChange) {
+ Q3TextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor(palette().text().color());
+ updateContents();
+ }
+ }
+
+ if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) {
+ QFont f = font();
+ if (f.kerning())
+ f.setKerning(false);
+ doc->setMinimumWidth(-1);
+ doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color());
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+ }
+}
+
+void Q3TextEdit::setReadOnly(bool b)
+{
+ if (readonly == b)
+ return;
+ readonly = b;
+ d->cursorBlinkActive = !b;
+#ifndef QT_NO_CURSOR
+ if (readonly)
+ viewport()->setCursor(Qt::ArrowCursor);
+ else
+ viewport()->setCursor(Qt::IBeamCursor);
+ setInputMethodEnabled(!readonly);
+#endif
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+/*!
+ Scrolls to the bottom of the document and does formatting if
+ required.
+*/
+
+void Q3TextEdit::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 Q3TextEdit::paragraphRect(int para) const
+{
+ Q3TextEdit *that = (Q3TextEdit *)this;
+ that->sync();
+ Q3TextParagraph *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).
+*/
+
+int Q3TextEdit::paragraphAt(const QPoint &pos) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ QFontMetrics fm(Q3ScrollView::font());
+ int parag = pos.y() / fm.lineSpacing();
+ if (parag <= d->od->numLines)
+ return parag;
+ else
+ return 0;
+ }
+#endif
+ Q3TextCursor c(doc);
+ c.place(pos, doc->firstParagraph());
+ if (c.paragraph())
+ return c.paragraph()->paragId();
+ return -1; // should never happen..
+}
+
+/*!
+ Returns the index of the character (relative to its paragraph) at
+ position \a pos (in contents coordinates). If \a para is not 0,
+ \c{*}\a{para} is set to the character's paragraph.
+*/
+
+int Q3TextEdit::charAt(const QPoint &pos, int *para) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ int par = paragraphAt(pos);
+ if (para)
+ *para = par;
+ return optimCharIndex(d->od->lines[LOGOFFSET(par)], pos.x());
+ }
+#endif
+ Q3TextCursor c(doc);
+ c.place(pos, doc->firstParagraph());
+ if (c.paragraph()) {
+ if (para)
+ *para = c.paragraph()->paragId();
+ return c.index();
+ }
+ return -1; // should never happen..
+}
+
+/*!
+ Sets the background color of the paragraph \a para to \a bg.
+*/
+
+void Q3TextEdit::setParagraphBackgroundColor(int para, const QColor &bg)
+{
+ Q3TextParagraph *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 Q3TextEdit::clearParagraphBackground(int para)
+{
+ Q3TextParagraph *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 Q3TextEdit::paragraphBackgroundColor(int para) const
+{
+ Q3TextParagraph *p = doc->paragAt(para);
+ if (!p)
+ return QColor();
+ QColor *c = p->backgroundColor();
+ if (c)
+ return *c;
+ return QColor();
+}
+
+/*!
+ \property Q3TextEdit::undoRedoEnabled
+ \brief whether undo/redo is enabled
+
+ When changing this property, the undo/redo history is cleared.
+
+ The default is true.
+*/
+
+void Q3TextEdit::setUndoRedoEnabled(bool b)
+{
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ undoEnabled = b;
+}
+
+bool Q3TextEdit::isUndoRedoEnabled() const
+{
+ return undoEnabled;
+}
+
+/*!
+ Returns true if undo is available; otherwise returns false.
+*/
+
+bool Q3TextEdit::isUndoAvailable() const
+{
+ return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid());
+}
+
+/*!
+ Returns true if redo is available; otherwise returns false.
+*/
+
+bool Q3TextEdit::isRedoAvailable() const
+{
+ return undoEnabled && doc->commands()->isRedoAvailable();
+}
+
+void Q3TextEdit::ensureFormatted(Q3TextParagraph *p)
+{
+ while (!p->isValid()) {
+ if (!lastFormatted)
+ return;
+ formatMore();
+ }
+}
+
+/*! \internal */
+void Q3TextEdit::updateCursor(const QPoint & pos)
+{
+ if (isReadOnly() && linksEnabled()) {
+ Q3TextCursor c = *cursor;
+ placeCursor(pos, &c, true);
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ bool insideParagRect = true;
+ if (c.paragraph() == doc->lastParagraph()
+ && c.paragraph()->rect().y() + c.paragraph()->rect().height() < pos.y())
+ insideParagRect = false;
+ if (insideParagRect && c.paragraph() && c.paragraph()->at(c.index()) &&
+ c.paragraph()->at(c.index())->isAnchor()) {
+ if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()
+ && c.index() < c.paragraph()->length() - 1)
+ onLink = c.paragraph()->at(c.index())->anchorHref();
+ else
+ onLink.clear();
+
+ if (!c.paragraph()->at(c.index())->anchorName().isEmpty()
+ && c.index() < c.paragraph()->length() - 1)
+ d->onName = c.paragraph()->at(c.index())->anchorName();
+ else
+ d->onName.clear();
+
+ if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()) {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(onLink.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
+#endif
+ QUrl u = QUrl(doc->context()).resolved(onLink);
+ emitHighlighted(u.toString(QUrl::None));
+ }
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
+#endif
+ onLink.clear();
+ emitHighlighted(QString());
+ }
+#endif
+ }
+}
+
+/*!
+ 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 Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c)
+{
+ placeCursor(pos, c, false);
+}
+
+/*! \internal */
+void Q3TextEdit::clipboardChanged()
+{
+#ifndef QT_NO_CLIPBOARD
+ // don't listen to selection changes
+ disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+#endif
+ selectAll(false);
+}
+
+/*! \property Q3TextEdit::tabChangesFocus
+ \brief whether TAB changes focus or is accepted as input
+
+ In some occasions text edits should not allow the user to input
+ tabulators or change indentation using the TAB key, as this breaks
+ the focus chain. The default is false.
+
+*/
+
+void Q3TextEdit::setTabChangesFocus(bool b)
+{
+ d->tabChangesFocus = b;
+}
+
+bool Q3TextEdit::tabChangesFocus() const
+{
+ return d->tabChangesFocus;
+}
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+/* Implementation of optimized Qt::LogText mode follows */
+
+static void qSwap(int * a, int * b)
+{
+ if (!a || !b)
+ return;
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/*! \internal */
+bool Q3TextEdit::checkOptimMode()
+{
+ bool oldMode = d->optimMode;
+ if (textFormat() == Qt::LogText) {
+ d->optimMode = true;
+ setReadOnly(true);
+ } else {
+ d->optimMode = false;
+ }
+
+ // when changing mode - try to keep selections and text
+ if (oldMode != d->optimMode) {
+ if (d->optimMode) {
+ d->od = new Q3TextEditOptimPrivate;
+ connect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
+ disconnect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
+ disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
+ disconnect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
+ optimSetText(doc->originalText());
+ doc->clear(true);
+ delete cursor;
+ cursor = new Q3TextCursor(doc);
+ } else {
+ disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
+ connect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
+ connect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
+ connect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
+ setText(optimText());
+ delete d->od;
+ d->od = 0;
+ }
+ }
+ return d->optimMode;
+}
+
+/*! \internal */
+QString Q3TextEdit::optimText() const
+{
+ QString str, tmp;
+
+ if (d->od->len == 0)
+ return str;
+
+ // concatenate all strings
+ int i;
+ int offset;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ Q3TextEditOptimPrivate::Tag * ftag = 0;
+ for (i = 0; i < d->od->numLines; i++) {
+ if (d->od->lines[LOGOFFSET(i)].isEmpty()) { // CR lines are empty
+ str += QLatin1Char('\n');
+ } else {
+ tmp = d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n');
+ // inject the tags for this line
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd())
+ ftag = it.value();
+ offset = 0;
+ while (ftag && ftag->line == i) {
+ tmp.insert(ftag->index + offset, QLatin1Char('<') + ftag->tag + QLatin1Char('>'));
+ offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars
+ ftag = ftag->next;
+ }
+ str += tmp;
+ }
+ }
+ return str;
+}
+
+/*! \internal */
+void Q3TextEdit::optimSetText(const QString &str)
+{
+ optimRemoveSelection();
+// this is just too slow - but may have to go in due to compatibility reasons
+// if (str == optimText())
+// return;
+ d->od->numLines = 0;
+ d->od->lines.clear();
+ d->od->maxLineWidth = 0;
+ d->od->len = 0;
+ d->od->clearTags();
+ QFontMetrics fm(Q3ScrollView::font());
+ if (!(str.isEmpty() || str.isNull() || d->maxLogLines == 0)) {
+ QStringList strl = str.split(QLatin1Char('\n'));
+ int lWidth = 0;
+ for (QStringList::Iterator it = strl.begin(); it != strl.end(); ++it) {
+ optimParseTags(&*it);
+ optimCheckLimit(*it);
+ lWidth = fm.width(*it);
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ repaintContents();
+ emit textChanged();
+}
+
+/*! \internal
+
+ Append \a tag to the tag list.
+*/
+Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimAppendTag(int index, const QString & tag)
+{
+ Q3TextEditOptimPrivate::Tag * t = new Q3TextEditOptimPrivate::Tag, * tmp;
+
+ if (d->od->tags == 0)
+ d->od->tags = t;
+ t->bold = t->italic = t->underline = false;
+ t->line = d->od->numLines;
+ t->index = index;
+ t->tag = tag;
+ t->leftTag = 0;
+ t->parent = 0;
+ t->prev = d->od->lastTag;
+ if (d->od->lastTag)
+ d->od->lastTag->next = t;
+ t->next = 0;
+ d->od->lastTag = t;
+ tmp = d->od->tagIndex[LOGOFFSET(t->line)];
+ if (!tmp || (tmp && tmp->index > t->index)) {
+ d->od->tagIndex.insert(LOGOFFSET(t->line), t);
+ }
+ return t;
+}
+
+/*! \internal
+
+ Insert \a tag in the tag - according to line and index numbers
+*/
+Q3TextEditOptimPrivate::Tag *Q3TextEdit::optimInsertTag(int line, int index, const QString &tag)
+{
+ Q3TextEditOptimPrivate::Tag *t = new Q3TextEditOptimPrivate::Tag, *tmp;
+
+ if (d->od->tags == 0)
+ d->od->tags = t;
+ t->bold = t->italic = t->underline = false;
+ t->line = line;
+ t->index = index;
+ t->tag = tag;
+ t->leftTag = 0;
+ t->parent = 0;
+ t->next = 0;
+ t->prev = 0;
+
+ // find insertion pt. in tag struct.
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tmp = *it;
+ if (tmp->index >= index) { // the existing tag may be placed AFTER the one we want to insert
+ tmp = tmp->prev;
+ } else {
+ while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index)
+ tmp = tmp->next;
+ }
+ } else {
+ tmp = d->od->tags;
+ while (tmp && tmp->next && tmp->next->line < line)
+ tmp = tmp->next;
+ if (tmp == d->od->tags)
+ tmp = 0;
+ }
+
+ t->prev = tmp;
+ t->next = tmp ? tmp->next : 0;
+ if (t->next)
+ t->next->prev = t;
+ if (tmp)
+ tmp->next = t;
+
+ tmp = d->od->tagIndex[LOGOFFSET(t->line)];
+ if (!tmp || (tmp && tmp->index >= t->index)) {
+ d->od->tagIndex.insert(LOGOFFSET(t->line), t);
+ }
+ return t;
+}
+
+/*! \internal
+
+ Find tags in \a line, remove them from \a line and put them in a
+ structure.
+
+ A tag is delimited by '<' and '>'. The characters '<', '>' and '&'
+ are escaped by using '&lt;', '&gt;' and '&amp;'. Left-tags marks
+ the starting point for formatting, while right-tags mark the ending
+ point. A right-tag is the same as a left-tag, but with a '/'
+ appearing before the tag keyword. E.g a valid left-tag: <b>, and
+ a valid right-tag: </b>. Tags can be nested, but they have to be
+ closed in the same order as they are opened. E.g:
+ <font color=red><font color=blue>blue</font>red</font> - is valid, while:
+ <font color=red><b>bold red</font> just bold</b> - is invalid since the font tag is
+ closed before the bold tag. Note that a tag does not have to be
+ closed: '<font color=blue>Lots of text - and then some..' is perfectly valid for
+ setting all text appearing after the tag to blue. A tag can be used
+ to change the color of a piece of text, or set one of the following
+ formatting attributes: bold, italic and underline. These attributes
+ are set using the <b>, <i> and <u> tags. Example of valid tags:
+ <font color=red>, </font>, <b>, <u>, <i>, </i>.
+ Example of valid text:
+ This is some <font color=red>red text</font>, while this is some <font color=green>green
+ text</font>. <font color=blue><font color=yellow>This is yellow</font>, while this is
+ blue.</font>
+
+ Note that only the color attribute of the HTML font tag is supported.
+
+ Limitations:
+ 1. A tag cannot span several lines.
+ 2. Very limited error checking - mismatching left/right-tags is the
+ only thing that is detected.
+
+*/
+void Q3TextEdit::optimParseTags(QString * line, int lineNo, int indexOffset)
+{
+ int len = line->length();
+ int i, startIndex = -1, endIndex = -1, escIndex = -1;
+ int state = 0; // 0 = outside tag, 1 = inside tag
+ bool tagOpen, tagClose;
+ int bold = 0, italic = 0, underline = 0;
+ QString tagStr;
+ QStack<Q3TextEditOptimPrivate::Tag *> tagStack;
+
+ for (i = 0; i < len; i++) {
+ tagOpen = (*line)[i] == QLatin1Char('<');
+ tagClose = (*line)[i] == QLatin1Char('>');
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ((*line)[i] == QLatin1Char('&')) {
+ escIndex = i;
+ continue;
+ } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) {
+ QString esc = line->mid(escIndex, i - escIndex + 1);
+ QString c;
+ if (esc == QLatin1String("&lt;"))
+ c = QLatin1Char('<');
+ else if (esc == QLatin1String("&gt;"))
+ c = QLatin1Char('>');
+ else if (esc == QLatin1String("&amp;"))
+ c = QLatin1Char('&');
+ line->replace(escIndex, i - escIndex + 1, c);
+ len = line->length();
+ i -= i-escIndex;
+ escIndex = -1;
+ continue;
+ }
+
+ if (state == 0 && tagOpen) {
+ state = 1;
+ startIndex = i;
+ continue;
+ }
+ if (state == 1 && tagClose) {
+ state = 0;
+ endIndex = i;
+ if (!tagStr.isEmpty()) {
+ Q3TextEditOptimPrivate::Tag * tag, * cur, * tmp;
+ bool format = true;
+
+ if (tagStr == QLatin1String("b"))
+ bold++;
+ else if (tagStr == QLatin1String("/b"))
+ bold--;
+ else if (tagStr == QLatin1String("i"))
+ italic++;
+ else if (tagStr == QLatin1String("/i"))
+ italic--;
+ else if (tagStr == QLatin1String("u"))
+ underline++;
+ else if (tagStr == QLatin1String("/u"))
+ underline--;
+ else
+ format = false;
+ if (lineNo > -1)
+ tag = optimInsertTag(lineNo, startIndex + indexOffset, tagStr);
+ else
+ tag = optimAppendTag(startIndex, tagStr);
+ // everything that is not a b, u or i tag is considered
+ // to be a color tag.
+ tag->type = format ? Q3TextEditOptimPrivate::Format
+ : Q3TextEditOptimPrivate::Color;
+ if (tagStr[0] == QLatin1Char('/')) {
+ // this is a right-tag - search for the left-tag
+ // and possible parent tag
+ cur = tag->prev;
+ if (!cur) {
+ qWarning("Q3TextEdit::optimParseTags: no left-tag for '<%s>' in line %d.",
+ tag->tag.latin1(), tag->line + 1);
+ return; // something is wrong - give up
+ }
+ while (cur) {
+ if (cur->leftTag) { // push right-tags encountered
+ tagStack.push(cur);
+ } else {
+ tmp = tagStack.isEmpty() ? 0 : tagStack.pop();
+ if (!tmp) {
+ if (((QLatin1Char('/') + cur->tag) == tag->tag) ||
+ (tag->tag == QLatin1String("/font") && cur->tag.left(4) == QLatin1String("font"))) {
+ // set up the left and parent of this tag
+ tag->leftTag = cur;
+ tmp = cur->prev;
+ if (tmp && tmp->parent) {
+ tag->parent = tmp->parent;
+ } else if (tmp && !tmp->leftTag) {
+ tag->parent = tmp;
+ }
+ break;
+ } else if (!cur->leftTag) {
+ qWarning("Q3TextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.",
+ qPrintable(QString(cur->tag[0] == QLatin1Char('/') ? QLatin1String("left") : QLatin1String("right"))),
+ cur->tag.latin1(), cur->line + 1);
+ return; // something is amiss - give up
+ }
+ }
+ }
+ cur = cur->prev;
+ }
+ } else {
+ tag->bold = bold > 0;
+ tag->italic = italic > 0;
+ tag->underline = underline > 0;
+ tmp = tag->prev;
+ while (tmp && tmp->leftTag) {
+ tmp = tmp->leftTag->parent;
+ }
+ if (tmp) {
+ tag->bold |= tmp->bold;
+ tag->italic |= tmp->italic;
+ tag->underline |= tmp->underline;
+ }
+ }
+ }
+ if (startIndex != -1) {
+ int l = (endIndex == -1) ?
+ line->length() - startIndex : endIndex - startIndex;
+ line->remove(startIndex, l+1);
+ len = line->length();
+ i -= l+1;
+ }
+ tagStr = QLatin1String("");
+ continue;
+ }
+
+ if (state == 1) {
+ tagStr += (*line)[i];
+ }
+ }
+}
+
+// calculate the width of a string in pixels inc. tabs
+static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm)
+{
+ int tabs = str.count(QLatin1Char('\t'));
+
+ if (!tabs)
+ return fm.width(str);
+
+ int newIdx = 0;
+ int lastIdx = 0;
+ int strWidth = 0;
+ int tn;
+ for (tn = 1; tn <= tabs; ++tn) {
+ newIdx = str.indexOf(QLatin1Char('\t'), newIdx);
+ strWidth += fm.width(str.mid(lastIdx, newIdx - lastIdx));
+ if (strWidth >= tn * tabWidth) {
+ int u = tn;
+ while (strWidth >= u * tabWidth)
+ ++u;
+ strWidth = u * tabWidth;
+ } else {
+ strWidth = tn * tabWidth;
+ }
+ lastIdx = ++newIdx;
+ }
+ if ((int)str.length() > newIdx)
+ strWidth += fm.width(str.mid(newIdx));
+ return strWidth;
+}
+
+bool Q3TextEdit::optimHasBoldMetrics(int line)
+{
+ Q3TextEditOptimPrivate::Tag *t;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(line)) != d->od->tagIndex.constEnd()) {
+ t = *it;
+ while (t && t->line == line) {
+ if (t->bold)
+ return true;
+ t = t->next;
+ }
+ } else if ((t = optimPreviousLeftTag(line)) && t->bold) {
+ return true;
+ }
+ return false;
+}
+
+/*! \internal
+
+ Append \a str to the current text buffer. Parses each line to find
+ formatting tags.
+*/
+void Q3TextEdit::optimAppend(const QString &str)
+{
+ if (str.isEmpty() || str.isNull() || d->maxLogLines == 0)
+ return;
+
+ QStringList strl = str.split(QLatin1Char('\n'));
+ QStringList::Iterator it = strl.begin();
+
+ QFontMetrics fm(Q3ScrollView::font());
+ int lWidth = 0;
+ for (; it != strl.end(); ++it) {
+ optimParseTags(&*it);
+ optimCheckLimit(*it);
+ if (optimHasBoldMetrics(d->od->numLines-1)) {
+ QFont fnt = Q3ScrollView::font();
+ fnt.setBold(true);
+ fm = QFontMetrics(fnt);
+ }
+ lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4;
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight();
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ if (scrollToEnd) {
+ updateScrollBars();
+ ensureVisible(contentsX(), contentsHeight(), 0, 0);
+ }
+ // when a max log size is set, the text may not be redrawn because
+ // the size of the viewport may not have changed
+ if (d->maxLogLines > -1)
+ viewport()->update();
+ emit textChanged();
+}
+
+static void qStripTags(QString *line)
+{
+ int len = line->length();
+ int i, startIndex = -1, endIndex = -1, escIndex = -1;
+ int state = 0; // 0 = outside tag, 1 = inside tag
+ bool tagOpen, tagClose;
+
+ for (i = 0; i < len; i++) {
+ tagOpen = (*line)[i] == QLatin1Char('<');
+ tagClose = (*line)[i] == QLatin1Char('>');
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ((*line)[i] == QLatin1Char('&')) {
+ escIndex = i;
+ continue;
+ } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) {
+ QString esc = line->mid(escIndex, i - escIndex + 1);
+ QString c;
+ if (esc == QLatin1String("&lt;"))
+ c = QLatin1Char('<');
+ else if (esc == QLatin1String("&gt;"))
+ c = QLatin1Char('>');
+ else if (esc == QLatin1String("&amp;"))
+ c = QLatin1Char('&');
+ line->replace(escIndex, i - escIndex + 1, c);
+ len = line->length();
+ i -= i-escIndex;
+ escIndex = -1;
+ continue;
+ }
+
+ if (state == 0 && tagOpen) {
+ state = 1;
+ startIndex = i;
+ continue;
+ }
+ if (state == 1 && tagClose) {
+ state = 0;
+ endIndex = i;
+ if (startIndex != -1) {
+ int l = (endIndex == -1) ?
+ line->length() - startIndex : endIndex - startIndex;
+ line->remove(startIndex, l+1);
+ len = line->length();
+ i -= l+1;
+ }
+ continue;
+ }
+ }
+}
+
+/*! \internal
+
+ Inserts the text into \a line at index \a index.
+*/
+
+void Q3TextEdit::optimInsert(const QString& text, int line, int index)
+{
+ if (text.isEmpty() || d->maxLogLines == 0)
+ return;
+ if (line < 0)
+ line = 0;
+ if (line > d->od->numLines-1)
+ line = d->od->numLines-1;
+ if (index < 0)
+ index = 0;
+ if (index > d->od->lines[line].length())
+ index = d->od->lines[line].length();
+
+ QStringList strl = text.split(QLatin1Char('\n'));
+ int numNewLines = strl.count() - 1;
+ Q3TextEditOptimPrivate::Tag *tag = 0;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator ii;
+ int x;
+
+ if (numNewLines == 0) {
+ // Case 1. Fast single line case - just inject it!
+ QString stripped = text;
+ qStripTags(&stripped);
+ d->od->lines[LOGOFFSET(line)].insert(index, stripped);
+ // move the tag indices following the insertion pt.
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tag = *ii;
+ while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
+ tag = tag->next;
+ while (tag && (LOGOFFSET(tag->line) == line)) {
+ tag->index += stripped.length();
+ tag = tag->next;
+ }
+ }
+ stripped = text;
+ optimParseTags(&stripped, line, index);
+ } else if (numNewLines > 0) {
+ // Case 2. We have at least 1 newline char - split at
+ // insertion pt. and make room for new lines - complex and slow!
+ QString left = d->od->lines[LOGOFFSET(line)].left(index);
+ QString right = d->od->lines[LOGOFFSET(line)].mid(index);
+
+ // rearrange lines for insertion
+ for (x = d->od->numLines - 1; x > line; x--)
+ d->od->lines[x + numNewLines] = d->od->lines[x];
+ d->od->numLines += numNewLines;
+
+ // fix the tag index and the tag line/index numbers - this
+ // might take a while..
+ for (x = line; x < d->od->numLines; x++) {
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) {
+ tag = ii.value();
+ if (LOGOFFSET(tag->line) == line)
+ while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
+ tag = tag->next;
+ }
+ }
+
+ // relabel affected tags with new line numbers and new index
+ // positions
+ while (tag) {
+ if (LOGOFFSET(tag->line) == line)
+ tag->index -= index;
+ tag->line += numNewLines;
+ tag = tag->next;
+ }
+
+ // generate a new tag index
+ d->od->tagIndex.clear();
+ tag = d->od->tags;
+ while (tag) {
+ if (!((ii = d->od->tagIndex.constFind(LOGOFFSET(tag->line))) != d->od->tagIndex.constEnd()))
+ d->od->tagIndex[LOGOFFSET(tag->line)] = tag;
+ tag = tag->next;
+ }
+
+ // update the tag indices on the spliced line - needs to be done before new tags are added
+ QString stripped = strl[strl.count() - 1];
+ qStripTags(&stripped);
+ if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.constEnd()) {
+ tag = *ii;
+ while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) {
+ tag->index += stripped.length();
+ tag = tag->next;
+ }
+ }
+
+ // inject the new lines
+ QStringList::Iterator it = strl.begin();
+ x = line;
+ int idx;
+ for (; it != strl.end(); ++it) {
+ stripped = *it;
+ qStripTags(&stripped);
+ if (x == line) {
+ stripped = left + stripped;
+ idx = index;
+ } else {
+ idx = 0;
+ }
+ d->od->lines[LOGOFFSET(x)] = stripped;
+ optimParseTags(&*it, x++, idx);
+ }
+ d->od->lines[LOGOFFSET(x - 1)] += right;
+ }
+ // recalculate the pixel width of the longest injected line -
+ QFontMetrics fm(Q3ScrollView::font());
+ int lWidth = 0;
+ for (x = line; x < line + numNewLines; x++) {
+ if (optimHasBoldMetrics(x)) {
+ QFont fnt = Q3ScrollView::font();
+ fnt.setBold(true);
+ fm = QFontMetrics(fnt);
+ }
+ lWidth = fm.width(d->od->lines[x]) + 4;
+ if (lWidth > d->od->maxLineWidth)
+ d->od->maxLineWidth = lWidth;
+ }
+ resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
+ repaintContents();
+ emit textChanged();
+}
+
+
+/*! \internal
+
+ Returns the first open left-tag appearing before line \a line.
+ */
+Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimPreviousLeftTag(int line)
+{
+ Q3TextEditOptimPrivate::Tag * ftag = 0;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd())
+ ftag = it.value();
+ if (!ftag) {
+ // start searching for an open tag
+ ftag = d->od->tags;
+ while (ftag) {
+ if (ftag->line > line || ftag->next == 0) {
+ if (ftag->line > line)
+ ftag = ftag->prev;
+ break;
+ }
+ ftag = ftag->next;
+ }
+ } else {
+ ftag = ftag->prev;
+ }
+
+ if (ftag) {
+ if (ftag && ftag->parent) // use the open parent tag
+ ftag = ftag->parent;
+ else if (ftag && ftag->leftTag) // this is a right-tag with no parent
+ ftag = 0;
+ }
+ return ftag;
+}
+
+/*! \internal
+
+ Set the format for the string starting at index \a start and ending
+ at \a end according to \a tag. If \a tag is a Format tag, find the
+ first open color tag appearing before \a tag and use that tag to
+ color the string.
+*/
+void Q3TextEdit::optimSetTextFormat(Q3TextDocument * td, Q3TextCursor * cur,
+ Q3TextFormat * f, int start, int end,
+ Q3TextEditOptimPrivate::Tag * tag)
+{
+ int formatFlags = Q3TextFormat::Bold | Q3TextFormat::Italic |
+ Q3TextFormat::Underline;
+ cur->setIndex(start);
+ td->setSelectionStart(0, *cur);
+ cur->setIndex(end);
+ td->setSelectionEnd(0, *cur);
+ Q3StyleSheetItem * ssItem = styleSheet()->item(tag->tag);
+ if (!ssItem || tag->type == Q3TextEditOptimPrivate::Format) {
+ f->setBold(tag->bold);
+ f->setItalic(tag->italic);
+ f->setUnderline(tag->underline);
+ if (tag->type == Q3TextEditOptimPrivate::Format) {
+ // check to see if there are any open color tags prior to
+ // this format tag
+ tag = tag->prev;
+ while (tag && (tag->type == Q3TextEditOptimPrivate::Format ||
+ tag->leftTag)) {
+ tag = tag->leftTag ? tag->parent : tag->prev;
+ }
+ }
+ if (tag) {
+ QString col = tag->tag.simplified();
+ if (col.left(10) == QLatin1String("font color")) {
+ int i = col.indexOf(QLatin1Char('='), 10);
+ col = col.mid(i + 1).simplified();
+ if (col[0] == QLatin1Char('\"'))
+ col = col.mid(1, col.length() - 2);
+ }
+ QColor color = QColor(col);
+ if (color.isValid()) {
+ formatFlags |= Q3TextFormat::Color;
+ f->setColor(color);
+ }
+ }
+ } else { // use the stylesheet tag definition
+ if (ssItem->color().isValid()) {
+ formatFlags |= Q3TextFormat::Color;
+ f->setColor(ssItem->color());
+ }
+ f->setBold(ssItem->fontWeight() == QFont::Bold);
+ f->setItalic(ssItem->fontItalic());
+ f->setUnderline(ssItem->fontUnderline());
+ }
+ td->setFormat(0, f, formatFlags);
+ td->removeSelection(0);
+}
+
+/*! \internal */
+void Q3TextEdit::optimDrawContents(QPainter * p, int clipx, int clipy,
+ int clipw, int cliph)
+{
+ QFontMetrics fm(Q3ScrollView::font());
+ int startLine = clipy / fm.lineSpacing();
+
+ // we always have to fetch at least two lines for drawing because the
+ // painter may be translated so that parts of two lines cover the area
+ // of a single line
+ int nLines = (cliph / fm.lineSpacing()) + 2;
+ int endLine = startLine + nLines;
+
+ if (startLine >= d->od->numLines)
+ return;
+ if ((startLine + nLines) > d->od->numLines)
+ nLines = d->od->numLines - startLine;
+
+ int i = 0;
+ QString str;
+ for (i = startLine; i < (startLine + nLines); i++)
+ str.append(d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n'));
+
+ Q3TextDocument * td = new Q3TextDocument(0);
+ td->setDefaultFormat(Q3ScrollView::font(), QColor());
+ td->setPlainText(str);
+ td->setFormatter(new Q3TextFormatterBreakWords); // deleted by QTextDoc
+ td->formatter()->setWrapEnabled(false);
+ td->setTabStops(doc->tabStopWidth());
+
+ // get the current text color from the current format
+ td->selectAll(Q3TextDocument::Standard);
+ Q3TextFormat f;
+ f.setColor(palette().text().color());
+ f.setFont(Q3ScrollView::font());
+ td->setFormat(Q3TextDocument::Standard, &f,
+ Q3TextFormat::Color | Q3TextFormat::Font);
+ td->removeSelection(Q3TextDocument::Standard);
+
+ // add tag formatting
+ if (d->od->tags) {
+ int i = startLine;
+ QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
+ Q3TextEditOptimPrivate::Tag * tag = 0, * tmp = 0;
+ Q3TextCursor cur(td);
+ // Step 1 - find previous left-tag
+ tmp = optimPreviousLeftTag(i);
+ for (; i < startLine + nLines; i++) {
+ if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd())
+ tag = it.value();
+ // Step 2 - iterate over tags on the current line
+ int lastIndex = 0;
+ while (tag && tag->line == i) {
+ tmp = 0;
+ if (tag->prev && !tag->prev->leftTag) {
+ tmp = tag->prev;
+ } else if (tag->prev && tag->prev->parent) {
+ tmp = tag->prev->parent;
+ }
+ if ((tag->index - lastIndex) > 0 && tmp) {
+ optimSetTextFormat(td, &cur, &f, lastIndex, tag->index, tmp);
+ }
+ lastIndex = tag->index;
+ tmp = tag;
+ tag = tag->next;
+ }
+ // Step 3 - color last part of the line - if necessary
+ if (tmp && tmp->parent)
+ tmp = tmp->parent;
+ if ((cur.paragraph()->length()-1 - lastIndex) > 0 && tmp && !tmp->leftTag) {
+ optimSetTextFormat(td, &cur, &f, lastIndex,
+ cur.paragraph()->length() - 1, tmp);
+ }
+ cur.setParagraph(cur.paragraph()->next());
+ }
+ // useful debug info
+ //
+// tag = d->od->tags;
+// qWarning("###");
+// while (tag) {
+// qWarning("Tag: %p, parent: %09p, leftTag: %09p, Name: %-15s, ParentName: %s, %d%d%d", tag,
+// tag->parent, tag->leftTag, tag->tag.latin1(), tag->parent ? tag->parent->tag.latin1():"<none>",
+// tag->bold, tag->italic, tag->underline);
+// tag = tag->next;
+// }
+ }
+
+ // if there is a selection, make sure that the selection in the
+ // part we need to redraw is set correctly
+ if (optimHasSelection()) {
+ Q3TextCursor c1(td);
+ Q3TextCursor c2(td);
+ int selStart = d->od->selStart.line;
+ int idxStart = d->od->selStart.index;
+ int selEnd = d->od->selEnd.line;
+ int idxEnd = d->od->selEnd.index;
+ if (selEnd < selStart) {
+ qSwap(&selStart, &selEnd);
+ qSwap(&idxStart, &idxEnd);
+ }
+ if (selEnd > d->od->numLines-1) {
+ selEnd = d->od->numLines-1;
+ }
+ if (startLine <= selStart && endLine >= selEnd) {
+ // case 1: area to paint covers entire selection
+ int paragS = selStart - startLine;
+ int paragE = paragS + (selEnd - selStart);
+ Q3TextParagraph * parag = td->paragAt(paragS);
+ if (parag) {
+ c1.setParagraph(parag);
+ if (td->text(paragS).length() >= idxStart)
+ c1.setIndex(idxStart);
+ }
+ parag = td->paragAt(paragE);
+ if (parag) {
+ c2.setParagraph(parag);
+ if (td->text(paragE).length() >= idxEnd)
+ c2.setIndex(idxEnd);
+ }
+ } else if (startLine > selStart && endLine < selEnd) {
+ // case 2: area to paint is all part of the selection
+ td->selectAll(Q3TextDocument::Standard);
+ } else if (startLine > selStart && endLine >= selEnd &&
+ startLine <= selEnd) {
+ // case 3: area to paint starts inside a selection, ends past it
+ c1.setParagraph(td->firstParagraph());
+ c1.setIndex(0);
+ int paragE = selEnd - startLine;
+ Q3TextParagraph * parag = td->paragAt(paragE);
+ if (parag) {
+ c2.setParagraph(parag);
+ if (td->text(paragE).length() >= idxEnd)
+ c2.setIndex(idxEnd);
+ }
+ } else if (startLine <= selStart && endLine < selEnd &&
+ endLine > selStart) {
+ // case 4: area to paint starts before a selection, ends inside it
+ int paragS = selStart - startLine;
+ Q3TextParagraph * parag = td->paragAt(paragS);
+ if (parag) {
+ c1.setParagraph(parag);
+ c1.setIndex(idxStart);
+ }
+ c2.setParagraph(td->lastParagraph());
+ c2.setIndex(td->lastParagraph()->string()->toString().length() - 1);
+
+ }
+ // previously selected?
+ if (!td->hasSelection(Q3TextDocument::Standard)) {
+ td->setSelectionStart(Q3TextDocument::Standard, c1);
+ td->setSelectionEnd(Q3TextDocument::Standard, c2);
+ }
+ }
+ td->doLayout(p, contentsWidth());
+
+ // have to align the painter so that partly visible lines are
+ // drawn at the correct position within the area that needs to be
+ // painted
+ int offset = clipy % fm.lineSpacing() + 2;
+ QRect r(clipx, 0, clipw, cliph + offset);
+ p->translate(0, clipy - offset);
+ td->draw(p, r.x(), r.y(), r.width(), r.height(), palette());
+ p->translate(0, -(clipy - offset));
+ delete td;
+}
+
+/*! \internal */
+void Q3TextEdit::optimMousePressEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+
+ QFontMetrics fm(Q3ScrollView::font());
+ mousePressed = true;
+ mousePos = e->pos();
+ d->od->selStart.line = e->y() / fm.lineSpacing();
+ if (d->od->selStart.line > d->od->numLines-1) {
+ d->od->selStart.line = d->od->numLines-1;
+ d->od->selStart.index = d->od->lines[LOGOFFSET(d->od->numLines-1)].length();
+ } else {
+ QString str = d->od->lines[LOGOFFSET(d->od->selStart.line)];
+ d->od->selStart.index = optimCharIndex(str, mousePos.x());
+ }
+ d->od->selEnd.line = d->od->selStart.line;
+ d->od->selEnd.index = d->od->selStart.index;
+ oldMousePos = e->pos();
+ repaintContents();
+}
+
+/*! \internal */
+void Q3TextEdit::optimMouseReleaseEvent(QMouseEvent * e)
+{
+ if (e->button() != Qt::LeftButton)
+ return;
+
+ if (scrollTimer->isActive())
+ scrollTimer->stop();
+ if (!inDoubleClick) {
+ QFontMetrics fm(Q3ScrollView::font());
+ d->od->selEnd.line = e->y() / fm.lineSpacing();
+ if (d->od->selEnd.line > d->od->numLines-1) {
+ d->od->selEnd.line = d->od->numLines-1;
+ }
+ QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
+ mousePos = e->pos();
+ d->od->selEnd.index = optimCharIndex(str, mousePos.x());
+ if (d->od->selEnd.line < d->od->selStart.line) {
+ qSwap(&d->od->selStart.line, &d->od->selEnd.line);
+ qSwap(&d->od->selStart.index, &d->od->selEnd.index);
+ } else if (d->od->selStart.line == d->od->selEnd.line &&
+ d->od->selStart.index > d->od->selEnd.index) {
+ qSwap(&d->od->selStart.index, &d->od->selEnd.index);
+ }
+ oldMousePos = e->pos();
+ repaintContents();
+ }
+ if (mousePressed) {
+ mousePressed = false;
+ copyToClipboard();
+ }
+
+ inDoubleClick = false;
+ emit copyAvailable(optimHasSelection());
+ emit selectionChanged();
+}
+
+/*! \internal */
+void Q3TextEdit::optimMouseMoveEvent(QMouseEvent * e)
+{
+ mousePos = e->pos();
+ optimDoAutoScroll();
+ oldMousePos = mousePos;
+}
+
+/*! \internal */
+void Q3TextEdit::optimDoAutoScroll()
+{
+ if (!mousePressed)
+ return;
+
+ QFontMetrics fm(Q3ScrollView::font());
+ QPoint pos(mapFromGlobal(QCursor::pos()));
+ bool doScroll = false;
+ int xx = contentsX() + pos.x();
+ int yy = contentsY() + pos.y();
+
+ // find out how much we have to scroll in either dir.
+ if (pos.x() < 0 || pos.x() > viewport()->width() ||
+ pos.y() < 0 || pos.y() > viewport()->height()) {
+ int my = yy;
+ if (pos.x() < 0)
+ xx = contentsX() - fm.width(QLatin1Char('w'));
+ else if (pos.x() > viewport()->width())
+ xx = contentsX() + viewport()->width() + fm.width(QLatin1Char('w'));
+
+ if (pos.y() < 0) {
+ my = contentsY() - 1;
+ yy = (my / fm.lineSpacing()) * fm.lineSpacing() + 1;
+ } else if (pos.y() > viewport()->height()) {
+ my = contentsY() + viewport()->height() + 1;
+ yy = (my / fm.lineSpacing() + 1) * fm.lineSpacing() - 1;
+ }
+ d->od->selEnd.line = my / fm.lineSpacing();
+ mousePos.setX(xx);
+ mousePos.setY(my);
+ doScroll = true;
+ } else {
+ d->od->selEnd.line = mousePos.y() / fm.lineSpacing();
+ }
+
+ if (d->od->selEnd.line < 0) {
+ d->od->selEnd.line = 0;
+ } else if (d->od->selEnd.line > d->od->numLines-1) {
+ d->od->selEnd.line = d->od->numLines-1;
+ }
+
+ QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
+ d->od->selEnd.index = optimCharIndex(str, mousePos.x());
+
+ // have to have a valid index before generating a paint event
+ if (doScroll)
+ ensureVisible(xx, yy, 1, 1);
+
+ // if the text document is smaller than the height of the viewport
+ // - redraw the whole thing otherwise calculate the rect that
+ // needs drawing.
+ if (d->od->numLines * fm.lineSpacing() < viewport()->height()) {
+ repaintContents(contentsX(), contentsY(), width(), height());
+ } else {
+ int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2;
+ int y;
+ if (oldMousePos.y() < mousePos.y()) {
+ y = oldMousePos.y() - fm.lineSpacing();
+ } else {
+ // expand paint area for a fully selected line
+ h += fm.lineSpacing();
+ y = mousePos.y() - fm.lineSpacing()*2;
+ }
+ if (y < 0)
+ y = 0;
+ repaintContents(contentsX(), y, width(), h);
+ }
+
+ if ((!scrollTimer->isActive() && pos.y() < 0) || pos.y() > height())
+ scrollTimer->start(100, false);
+ else if (scrollTimer->isActive() && pos.y() >= 0 && pos.y() <= height())
+ scrollTimer->stop();
+}
+
+/*! \internal
+
+ Returns the index of the character in the string \a str that is
+ currently under the mouse pointer.
+*/
+int Q3TextEdit::optimCharIndex(const QString &str, int mx) const
+{
+ QFontMetrics fm(Q3ScrollView::font());
+ int i = 0;
+ int dd, dist = 10000000;
+ int curpos = 0;
+ int strWidth;
+ mx = mx - 4; // ### get the real margin from somewhere
+
+ if (!str.contains(QLatin1Char('\t')) && mx > fm.width(str))
+ return str.length();
+
+ while (i < str.length()) {
+ strWidth = qStrWidth(str.left(i), tabStopWidth(), fm);
+ dd = strWidth - mx;
+ if (QABS(dd) <= dist) {
+ dist = QABS(dd);
+ if (mx >= strWidth)
+ curpos = i;
+ }
+ ++i;
+ }
+ return curpos;
+}
+
+/*! \internal */
+void Q3TextEdit::optimSelectAll()
+{
+ d->od->selStart.line = d->od->selStart.index = 0;
+ d->od->selEnd.line = d->od->numLines - 1;
+ d->od->selEnd.index = d->od->lines[LOGOFFSET(d->od->selEnd.line)].length();
+
+ repaintContents();
+ emit copyAvailable(optimHasSelection());
+ emit selectionChanged();
+}
+
+/*! \internal */
+void Q3TextEdit::optimRemoveSelection()
+{
+ d->od->selStart.line = d->od->selEnd.line = -1;
+ d->od->selStart.index = d->od->selEnd.index = -1;
+ repaintContents();
+}
+
+/*! \internal */
+void Q3TextEdit::optimSetSelection(int startLine, int startIdx,
+ int endLine, int endIdx)
+{
+ d->od->selStart.line = startLine;
+ d->od->selEnd.line = endLine;
+ d->od->selStart.index = startIdx;
+ d->od->selEnd.index = endIdx;
+}
+
+/*! \internal */
+bool Q3TextEdit::optimHasSelection() const
+{
+ if (d->od->selStart.line != d->od->selEnd.line ||
+ d->od->selStart.index != d->od->selEnd.index)
+ return true;
+ return false;
+}
+
+/*! \internal */
+QString Q3TextEdit::optimSelectedText() const
+{
+ QString str;
+
+ if (!optimHasSelection())
+ return str;
+
+ // concatenate all strings
+ if (d->od->selStart.line == d->od->selEnd.line) {
+ str = d->od->lines[LOGOFFSET(d->od->selEnd.line)].mid(d->od->selStart.index,
+ d->od->selEnd.index - d->od->selStart.index);
+ } else {
+ int i = d->od->selStart.line;
+ str = d->od->lines[LOGOFFSET(i)].right(d->od->lines[LOGOFFSET(i)].length() -
+ d->od->selStart.index) + QLatin1Char('\n');
+ i++;
+ for (; i < d->od->selEnd.line; i++) {
+ if (d->od->lines[LOGOFFSET(i)].isEmpty()) // CR lines are empty
+ str += QLatin1Char('\n');
+ else
+ str += d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n');
+ }
+ str += d->od->lines[LOGOFFSET(d->od->selEnd.line)].left(d->od->selEnd.index);
+ }
+ return str;
+}
+
+/*! \internal */
+bool Q3TextEdit::optimFind(const QString & expr, bool cs, bool /*wo*/,
+ bool fw, int * para, int * index)
+{
+ bool found = false;
+ int parag = para ? *para : d->od->search.line,
+ idx = index ? *index : d->od->search.index, i;
+
+ if (d->od->len == 0)
+ return false;
+
+ for (i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i--) {
+ idx = fw
+ ? d->od->lines[LOGOFFSET(i)].indexOf(expr, idx,
+ cs ? Qt::CaseSensitive : Qt::CaseInsensitive)
+ : d->od->lines[LOGOFFSET(i)].lastIndexOf(expr, idx,
+ cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
+ if (idx != -1) {
+ found = true;
+ break;
+ } else if (fw)
+ idx = 0;
+ }
+
+ if (found) {
+ if (index)
+ *index = idx;
+ if (para)
+ *para = i;
+ d->od->search.index = idx;
+ d->od->search.line = i;
+ optimSetSelection(i, idx, i, idx + expr.length());
+ QFontMetrics fm(Q3ScrollView::font());
+ int h = fm.lineSpacing();
+ int x = fm.width(d->od->lines[LOGOFFSET(i)].left(idx + expr.length())) + 4;
+ ensureVisible(x, i * h + h / 2, 1, h / 2 + 2);
+ repaintContents(); // could possibly be optimized
+ }
+ return found;
+}
+
+/*! \reimp */
+void Q3TextEdit::polishEvent(QEvent*)
+{
+ // this will ensure that the last line is visible if text have
+ // been added to the widget before it is shown
+ if (d->optimMode)
+ scrollToBottom();
+}
+
+/*!
+ Sets the maximum number of lines a Q3TextEdit can hold in \c
+ Qt::LogText mode to \a limit. If \a limit is -1 (the default), this
+ signifies an unlimited number of lines.
+
+ \warning Never use formatting tags that span more than one line
+ when the maximum log lines is set. When lines are removed from the
+ top of the buffer it could result in an unbalanced tag pair, i.e.
+ the left formatting tag is removed before the right one.
+ */
+void Q3TextEdit::setMaxLogLines(int limit)
+{
+ d->maxLogLines = limit;
+ if (d->maxLogLines < -1)
+ d->maxLogLines = -1;
+ if (d->maxLogLines == -1)
+ d->logOffset = 0;
+}
+
+/*!
+ Returns the maximum number of lines Q3TextEdit can hold in \c
+ Qt::LogText mode. By default the number of lines is unlimited, which
+ is signified by a value of -1.
+ */
+int Q3TextEdit::maxLogLines() const
+{
+ return d->maxLogLines;
+}
+
+/*!
+ Check if the number of lines in the buffer is limited, and uphold
+ that limit when appending new lines.
+ */
+void Q3TextEdit::optimCheckLimit(const QString& str)
+{
+ if (d->maxLogLines > -1 && d->maxLogLines <= d->od->numLines) {
+ // NB! Removing the top line in the buffer will potentially
+ // destroy the structure holding the formatting tags - if line
+ // spanning tags are used.
+ Q3TextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr;
+ QList<Q3TextEditOptimPrivate::Tag *> lst;
+ while (t) {
+ t->line -= 1;
+ // unhook the ptr from the tag structure
+ if (((uint) LOGOFFSET(t->line) < (uint) d->logOffset &&
+ (uint) LOGOFFSET(t->line) < (uint) LOGOFFSET(d->od->numLines) &&
+ (uint) LOGOFFSET(d->od->numLines) > (uint) d->logOffset))
+ {
+ if (t->prev)
+ t->prev->next = t->next;
+ if (t->next)
+ t->next->prev = t->prev;
+ if (d->od->tags == t)
+ d->od->tags = t->next;
+ if (d->od->lastTag == t) {
+ if (t->prev)
+ d->od->lastTag = t->prev;
+ else
+ d->od->lastTag = d->od->tags;
+ }
+ tmp = t;
+ t = t->next;
+ lst.append(tmp);
+ delete tmp;
+ } else {
+ t = t->next;
+ }
+ }
+ // Remove all references to the ptrs we just deleted
+ itr = d->od->tags;
+ while (itr) {
+ for (int i = 0; i < lst.size(); ++i) {
+ tmp = lst.at(i);
+ if (itr->parent == tmp)
+ itr->parent = 0;
+ if (itr->leftTag == tmp)
+ itr->leftTag = 0;
+ }
+ itr = itr->next;
+ }
+ // ...in the tag index as well
+ QMap<int, Q3TextEditOptimPrivate::Tag *>::Iterator idx;
+ if ((idx = d->od->tagIndex.find(d->logOffset)) != d->od->tagIndex.end())
+ d->od->tagIndex.erase(idx);
+
+ QMap<int,QString>::Iterator it;
+ if ((it = d->od->lines.find(d->logOffset)) != d->od->lines.end()) {
+ d->od->len -= (*it).length();
+ d->od->lines.erase(it);
+ d->od->numLines--;
+ d->logOffset = LOGOFFSET(1);
+ }
+ }
+ d->od->len += str.length();
+ d->od->lines[LOGOFFSET(d->od->numLines++)] = str;
+}
+
+#endif // QT_TEXTEDIT_OPTIMIZATION
+
+/*!
+ \property Q3TextEdit::autoFormatting
+ \brief the enabled set of auto formatting features
+
+ The value can be any combination of the values in the \c
+ AutoFormattingFlag enum. The default is \c AutoAll. Choose \c AutoNone
+ to disable all automatic formatting.
+
+ Currently, the only automatic formatting feature provided is \c
+ AutoBulletList; future versions of Qt may offer more.
+*/
+
+void Q3TextEdit::setAutoFormatting(AutoFormatting features)
+{
+ d->autoFormatting = features;
+}
+
+Q3TextEdit::AutoFormatting Q3TextEdit::autoFormatting() const
+{
+ return d->autoFormatting;
+}
+
+/*!
+ Returns the QSyntaxHighlighter set on this Q3TextEdit. 0 is
+ returned if no syntax highlighter is set.
+ */
+Q3SyntaxHighlighter * Q3TextEdit::syntaxHighlighter() const
+{
+ if (document()->preProcessor())
+ return ((Q3SyntaxHighlighterInternal *) document()->preProcessor())->highlighter;
+ else
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_TEXTEDIT
diff --git a/src/qt3support/text/q3textedit.h b/src/qt3support/text/q3textedit.h
new file mode 100644
index 0000000..fae22b0
--- /dev/null
+++ b/src/qt3support/text/q3textedit.h
@@ -0,0 +1,613 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTEDIT_H
+#define Q3TEXTEDIT_H
+
+#include <Qt3Support/q3scrollview.h>
+#include <Qt3Support/q3stylesheet.h>
+#include <Qt3Support/q3mimefactory.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTEDIT
+// uncomment below to enable optimization mode - also uncomment the
+// optimDoAutoScroll() private slot since moc ignores #ifdefs..
+#define QT_TEXTEDIT_OPTIMIZATION
+
+class QPainter;
+class Q3TextDocument;
+class Q3TextCursor;
+class QKeyEvent;
+class QResizeEvent;
+class QMouseEvent;
+class QTimer;
+class Q3TextString;
+class QTextCommand;
+class Q3TextParagraph;
+class Q3TextFormat;
+class QFont;
+class QColor;
+class Q3TextEdit;
+class QTextBrowser;
+class Q3TextString;
+struct QUndoRedoInfoPrivate;
+class Q3PopupMenu;
+class Q3TextEditPrivate;
+class Q3SyntaxHighlighter;
+class Q3TextDrag;
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+class Q3TextEditOptimPrivate
+{
+public:
+ // Note: no left-tag has any value for leftTag or parent, and
+ // no right-tag has any formatting flags set.
+ enum TagType { Color = 0, Format = 1 };
+ struct Tag {
+ TagType type:2;
+ bool bold:1;
+ bool italic:1;
+ bool underline:1;
+ int line;
+ int index;
+ Tag * leftTag; // ptr to left-tag in a left-right tag pair
+ Tag * parent; // ptr to parent left-tag in a nested tag
+ Tag * prev;
+ Tag * next;
+ QString tag;
+ };
+ Q3TextEditOptimPrivate()
+ {
+ len = numLines = maxLineWidth = 0;
+ selStart.line = selStart.index = -1;
+ selEnd.line = selEnd.index = -1;
+ search.line = search.index = 0;
+ tags = lastTag = 0;
+ }
+ void clearTags()
+ {
+ Tag * itr = tags;
+ while (tags) {
+ itr = tags;
+ tags = tags->next;
+ delete itr;
+ }
+ tags = lastTag = 0;
+ tagIndex.clear();
+ }
+ ~Q3TextEditOptimPrivate()
+ {
+ clearTags();
+ }
+ int len;
+ int numLines;
+ int maxLineWidth;
+ struct Selection {
+ int line;
+ int index;
+ };
+ Selection selStart, selEnd, search;
+ Tag * tags, * lastTag;
+ QMap<int, QString> lines;
+ QMap<int, Tag *> tagIndex;
+};
+#endif
+
+class Q_COMPAT_EXPORT Q3TextEdit : public Q3ScrollView
+{
+ friend class Q3TextBrowser;
+ friend class Q3SyntaxHighlighter;
+
+ Q_OBJECT
+ Q_ENUMS(WordWrap WrapPolicy)
+ Q_FLAGS(AutoFormattingFlag)
+ Q_PROPERTY(Qt::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)
+ Q_PROPERTY(bool tabChangesFocus READ tabChangesFocus WRITE setTabChangesFocus)
+ Q_PROPERTY(AutoFormattingFlag autoFormatting READ autoFormatting WRITE setAutoFormatting)
+
+public:
+ enum WordWrap {
+ NoWrap,
+ WidgetWidth,
+ FixedPixelWidth,
+ FixedColumnWidth
+ };
+
+ enum WrapPolicy {
+ AtWordBoundary,
+ AtWhiteSpace = AtWordBoundary, // AtWhiteSpace is deprecated
+ Anywhere,
+ AtWordOrDocumentBoundary
+ };
+
+ enum AutoFormattingFlag {
+ AutoNone = 0,
+ AutoBulletList = 0x00000001,
+ AutoAll = 0xffffffff
+ };
+
+ Q_DECLARE_FLAGS(AutoFormatting, AutoFormattingFlag)
+
+ enum KeyboardAction {
+ ActionBackspace,
+ ActionDelete,
+ ActionReturn,
+ ActionKill,
+ ActionWordBackspace,
+ ActionWordDelete
+ };
+
+ enum CursorAction {
+ MoveBackward,
+ MoveForward,
+ MoveWordBackward,
+ MoveWordForward,
+ MoveUp,
+ MoveDown,
+ MoveLineStart,
+ MoveLineEnd,
+ MoveHome,
+ MoveEnd,
+ MovePgUp,
+ MovePgDown
+ };
+
+ enum VerticalAlignment {
+ AlignNormal,
+ AlignSuperScript,
+ AlignSubScript
+ };
+
+ enum TextInsertionFlags {
+ RedoIndentation = 0x0001,
+ CheckNewLines = 0x0002,
+ RemoveSelected = 0x0004
+ };
+
+ Q3TextEdit(const QString& text, const QString& context = QString(),
+ QWidget* parent=0, const char* name=0);
+ Q3TextEdit(QWidget* parent=0, const char* name=0);
+ virtual ~Q3TextEdit();
+
+ QString text() const;
+ QString text(int para) const;
+ Qt::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;
+
+ Q3StyleSheet* styleSheet() const;
+#ifndef QT_NO_MIME
+ Q3MimeSourceFactory* mimeSourceFactory() const;
+#endif
+ 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, Qt::AnchorAttribute a = Qt::AnchorHref);
+
+ 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;
+ QFont currentFont() const;
+ int alignment() const;
+ VerticalAlignment verticalAlignment() const;
+ int undoDepth() const;
+
+ // do not use, will go away
+ virtual bool getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment);
+ // do not use, will go away
+ virtual bool getParagraphFormat(int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ Q3StyleSheetItem::DisplayMode *displayMode,
+ Q3StyleSheetItem::ListStyle *listStyle,
+ int *listDepth);
+
+
+ bool isOverwriteMode() const { return overWrite; }
+ QColor paragraphBackgroundColor(int para) const;
+
+ bool isUndoRedoEnabled() const;
+ bool eventFilter(QObject *o, QEvent *e);
+ bool tabChangesFocus() const;
+
+ void setAutoFormatting(AutoFormatting);
+ AutoFormatting autoFormatting() const;
+ Q3SyntaxHighlighter *syntaxHighlighter() const;
+
+public Q_SLOTS:
+#ifndef QT_NO_MIME
+ virtual void setMimeSourceFactory(Q3MimeSourceFactory* factory);
+#endif
+ virtual void setStyleSheet(Q3StyleSheet* styleSheet);
+ virtual void scrollToAnchor(const QString& name);
+ virtual void setPaper(const QBrush& pap);
+ virtual void setLinkUnderline(bool);
+
+ virtual void setWordWrap(Q3TextEdit::WordWrap mode);
+ virtual void setWrapColumnOrWidth(int);
+ virtual void setWrapPolicy(Q3TextEdit::WrapPolicy policy);
+
+ virtual void copy();
+ virtual void append(const QString& text);
+
+ void setText(const QString &txt) { setText(txt, QString()); }
+ virtual void setText(const QString &txt, const QString &context);
+ virtual void setTextFormat(Qt::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 QByteArray &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 setVerticalAlignment(Q3TextEdit::VerticalAlignment a);
+ virtual void setAlignment(int a);
+
+ // do not use, will go away
+ virtual void setParagType(Q3StyleSheetItem::DisplayMode dm, Q3StyleSheetItem::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(Q3TextFormat *f, int flags);
+ virtual void ensureCursorVisible();
+ virtual void placeCursor(const QPoint &pos, Q3TextCursor *c = 0);
+ virtual void moveCursor(Q3TextEdit::CursorAction action, bool select);
+ virtual void doKeyboardAction(Q3TextEdit::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, uint insertionFlags = CheckNewLines | RemoveSelected);
+
+ // obsolete
+ virtual void insert(const QString &text, bool, bool = true, bool = true);
+
+ 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);
+ virtual void setTabChangesFocus(bool b);
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ void polishEvent(QEvent*);
+ void setMaxLogLines(int numLines);
+ int maxLogLines() const;
+#endif
+
+Q_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(Q3TextEdit::VerticalAlignment a);
+ void cursorPositionChanged(Q3TextCursor *c);
+ void cursorPositionChanged(int para, int pos);
+ void returnPressed();
+ void modificationChanged(bool m);
+ void clicked(int parag, int index);
+ void doubleClicked(int parag, int index);
+
+protected:
+ void repaintChanged();
+ void updateStyles();
+ void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+ bool event(QEvent *e);
+ void changeEvent(QEvent *);
+ 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
+ void inputMethodEvent(QInputMethodEvent *);
+#ifndef QT_NO_DRAGANDDROP
+ void contentsDragEnterEvent(QDragEnterEvent *e);
+ void contentsDragMoveEvent(QDragMoveEvent *e);
+ void contentsDragLeaveEvent(QDragLeaveEvent *e);
+ void contentsDropEvent(QDropEvent *e);
+#endif
+ void contentsContextMenuEvent(QContextMenuEvent *e);
+ bool focusNextPrevChild(bool next);
+ Q3TextDocument *document() const;
+ Q3TextCursor *textCursor() const;
+ void setDocument(Q3TextDocument *doc);
+ virtual Q3PopupMenu *createPopupMenu(const QPoint& pos);
+ virtual Q3PopupMenu *createPopupMenu();
+ void drawCursor(bool visible);
+
+protected Q_SLOTS:
+ virtual void doChangeInterval();
+ virtual void sliderReleased();
+
+private Q_SLOTS:
+ void formatMore();
+ void doResize();
+ void autoScrollTimerDone();
+ void blinkCursor();
+ void setModified();
+ void startDrag();
+ void documentWidthChanged(int w);
+ void clipboardChanged();
+
+private:
+ struct Q_COMPAT_EXPORT UndoRedoInfo {
+ enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Style, IME };
+
+ UndoRedoInfo(Q3TextDocument *dc);
+ ~UndoRedoInfo();
+ void clear();
+ bool valid() const;
+
+ QUndoRedoInfoPrivate *d;
+ int id;
+ int index;
+ int eid;
+ int eindex;
+ Q3TextFormat *format;
+ int flags;
+ Type type;
+ Q3TextDocument *doc;
+ QByteArray styleInformation;
+ };
+
+private:
+ void updateCursor(const QPoint & pos);
+ void handleMouseMove(const QPoint& pos);
+ void drawContents(QPainter *);
+ virtual bool linksEnabled() const { return false; }
+ void init();
+ void checkUndoRedoInfo(UndoRedoInfo::Type t);
+ void updateCurrentFormat();
+ bool handleReadOnlyKeyEvent(QKeyEvent *e);
+ void makeParagVisible(Q3TextParagraph *p);
+ void normalCopy();
+ void copyToClipboard();
+#ifndef QT_NO_MIME
+ QByteArray pickSpecial(QMimeSource* ms, bool always_ask, const QPoint&);
+ Q3TextDrag *dragObject(QWidget *parent = 0) const;
+#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(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &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(Q3TextParagraph *p);
+ void placeCursor(const QPoint &pos, Q3TextCursor *c, bool link);
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ bool checkOptimMode();
+ QString optimText() const;
+ void optimSetText(const QString &str);
+ void optimAppend(const QString &str);
+ void optimInsert(const QString &str, int line, int index);
+ void optimDrawContents(QPainter * p, int cx, int cy, int cw, int ch);
+ void optimMousePressEvent(QMouseEvent * e);
+ void optimMouseReleaseEvent(QMouseEvent * e);
+ void optimMouseMoveEvent(QMouseEvent * e);
+ int optimCharIndex(const QString &str, int mx) const;
+ void optimSelectAll();
+ void optimRemoveSelection();
+ void optimSetSelection(int startLine, int startIdx, int endLine,
+ int endIdx);
+ bool optimHasSelection() const;
+ QString optimSelectedText() const;
+ bool optimFind(const QString & str, bool, bool, bool, int *, int *);
+ void optimParseTags(QString * str, int lineNo = -1, int indexOffset = 0);
+ Q3TextEditOptimPrivate::Tag * optimPreviousLeftTag(int line);
+ void optimSetTextFormat(Q3TextDocument *, Q3TextCursor *, Q3TextFormat * f,
+ int, int, Q3TextEditOptimPrivate::Tag * t);
+ Q3TextEditOptimPrivate::Tag * optimAppendTag(int index, const QString & tag);
+ Q3TextEditOptimPrivate::Tag * optimInsertTag(int line, int index, const QString & tag);
+ void optimCheckLimit(const QString& str);
+ bool optimHasBoldMetrics(int line);
+
+private Q_SLOTS:
+ void optimDoAutoScroll();
+#endif // QT_TEXTEDIT_OPTIMIZATION
+
+private:
+#ifndef QT_NO_CLIPBOARD
+ void pasteSubType(const QByteArray &subtype, QMimeSource *m);
+#endif
+
+private:
+ Q_DISABLE_COPY(Q3TextEdit)
+
+ Q3TextDocument *doc;
+ Q3TextCursor *cursor;
+ QTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer;
+ Q3TextParagraph *lastFormatted;
+ int interval;
+ UndoRedoInfo undoRedoInfo;
+ Q3TextFormat *currentFormat;
+ int currentAlignment;
+ QPoint oldMousePos, mousePos;
+ QPoint dragStartPos;
+ QString onLink;
+ WordWrap wrapMode;
+ WrapPolicy wPolicy;
+ int wrapWidth;
+ QString pressedLink;
+ Q3TextEditPrivate *d;
+ bool inDoubleClick : 1;
+ bool mousePressed : 1;
+ bool cursorVisible : 1;
+ bool blinkCursorVisible : 1;
+ bool modified : 1;
+ bool mightStartDrag : 1;
+ bool inDnD : 1;
+ bool readonly : 1;
+ bool undoEnabled : 1;
+ bool overWrite : 1;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Q3TextEdit::AutoFormatting)
+
+inline Q3TextDocument *Q3TextEdit::document() const
+{
+ return doc;
+}
+
+inline Q3TextCursor *Q3TextEdit::textCursor() const
+{
+ return cursor;
+}
+
+inline void Q3TextEdit::setCurrentFont(const QFont &f)
+{
+ Q3TextEdit::setFontInternal(f);
+}
+
+#endif // QT_NO_TEXTEDIT
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTEDIT_H
diff --git a/src/qt3support/text/q3textstream.cpp b/src/qt3support/text/q3textstream.cpp
new file mode 100644
index 0000000..15fa6b0
--- /dev/null
+++ b/src/qt3support/text/q3textstream.cpp
@@ -0,0 +1,2436 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textstream.h"
+#include <qdebug.h>
+
+#ifndef QT_NO_TEXTSTREAM
+#include "qtextcodec.h"
+#include "qregexp.h"
+#include "qbuffer.h"
+#include "qfile.h"
+#include "q3cstring.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef Q_OS_WINCE
+#include <locale.h>
+#endif
+
+#if defined(Q_OS_WIN32)
+#include "qt_windows.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_TEXTCODEC
+static void resetCodecConverterState(QTextCodec::ConverterState *state) {
+ state->flags = QTextCodec::DefaultConversion;
+ state->remainingChars = state->invalidChars =
+ state->state_data[0] = state->state_data[1] = state->state_data[2] = 0;
+ if (state->d) qFree(state->d);
+ state->d = 0;
+}
+#endif
+
+/*!
+ \class Q3TextStream
+ \compat
+ \reentrant
+ \brief The Q3TextStream class provides basic functions for reading
+ and writing text using a QIODevice.
+
+ The text stream class has a functional interface that is very
+ similar to that of the standard C++ iostream class.
+
+ Qt provides several global functions similar to the ones in iostream:
+ \table
+ \header \i Function \i Meaning
+ \row \i bin \i sets the Q3TextStream to read/write binary numbers
+ \row \i oct \i sets the Q3TextStream to read/write octal numbers
+ \row \i dec \i sets the Q3TextStream to read/write decimal numbers
+ \row \i hex \i sets the Q3TextStream to read/write hexadecimal numbers
+ \row \i endl \i forces a line break
+ \row \i flush \i forces the QIODevice to flush any buffered data
+ \row \i ws \i eats any available whitespace (on input)
+ \row \i reset \i resets the Q3TextStream to its default mode (see reset())
+ \row \i qSetW(int) \i sets the \link width() field width \endlink
+ to the given argument
+ \row \i qSetFill(int) \i sets the \link fill() fill character
+ \endlink to the given argument
+ \row \i qSetPrecision(int) \i sets the \link precision() precision
+ \endlink to the given argument
+ \endtable
+
+ \warning By default Q3TextStream will automatically detect whether
+ integers in the stream are in decimal, octal, hexadecimal or
+ binary format when reading from the stream. In particular, a
+ leading '0' signifies octal, i.e. the sequence "0100" will be
+ interpreted as 64.
+
+ The Q3TextStream class reads and writes text; it is not appropriate
+ for dealing with binary data (but QDataStream is).
+
+ By default, output of Unicode text (i.e. QString) is done using
+ the local 8-bit encoding. This can be changed using the
+ setEncoding() method. For input, the Q3TextStream will auto-detect
+ standard Unicode "byte order marked" text files; otherwise the
+ local 8-bit encoding is used.
+
+ The QIODevice is set in the constructor, or later using
+ setDevice(). If the end of the input is reached atEnd() returns
+ TRUE. Data can be read into variables of the appropriate type
+ using the operator>>() overloads, or read in its entirety into a
+ single string using read(), or read a line at a time using
+ readLine(). Whitespace can be skipped over using skipWhiteSpace().
+ You can set flags for the stream using flags() or setf(). The
+ stream also supports width(), precision() and fill(); use reset()
+ to reset the defaults.
+
+ \sa QDataStream
+*/
+
+/*!
+ \enum Q3TextStream::Encoding
+
+ \value Locale
+ \value Latin1
+ \value Unicode
+ \value UnicodeNetworkOrder
+ \value UnicodeReverse
+ \value RawUnicode
+ \value UnicodeUTF8
+
+ See setEncoding() for an explanation of the encodings.
+*/
+
+/*
+ \class QTSManip
+ \internal
+*/
+
+#if defined(QT_CHECK_STATE)
+#undef CHECK_STREAM_PRECOND
+#define CHECK_STREAM_PRECOND if ( !dev ) { \
+ qWarning( "Q3TextStream: No device" ); \
+ return *this; }
+#else
+#define CHECK_STREAM_PRECOND
+#endif
+
+
+#define I_SHORT 0x0010
+#define I_INT 0x0020
+#define I_LONG 0x0030
+#define I_TYPE_MASK 0x00f0
+
+#define I_BASE_2 Q3TextStream::bin
+#define I_BASE_8 Q3TextStream::oct
+#define I_BASE_10 Q3TextStream::dec
+#define I_BASE_16 Q3TextStream::hex
+#define I_BASE_MASK (Q3TextStream::bin | Q3TextStream::oct | Q3TextStream::dec | Q3TextStream::hex)
+
+#define I_SIGNED 0x0100
+#define I_UNSIGNED 0x0200
+#define I_SIGN_MASK 0x0f00
+
+
+static const QChar QEOF = QChar((ushort)0xffff); //guaranteed not to be a character.
+static const uint getline_buf_size = 256; // bufsize used by ts_getline()
+
+const int Q3TextStream::basefield = I_BASE_MASK;
+const int Q3TextStream::adjustfield = ( Q3TextStream::left |
+ Q3TextStream::right |
+ Q3TextStream::internal );
+const int Q3TextStream::floatfield = ( Q3TextStream::scientific |
+ Q3TextStream::fixed );
+
+
+class Q3TextStreamPrivate {
+public:
+#ifndef QT_NO_TEXTCODEC
+ Q3TextStreamPrivate()
+ : sourceType( NotSet ) { }
+ ~Q3TextStreamPrivate() {
+ }
+#else
+ Q3TextStreamPrivate() : sourceType( NotSet ) { }
+ ~Q3TextStreamPrivate() { }
+#endif
+ QString ungetcBuf;
+
+ enum SourceType { NotSet, IODevice, String, ByteArray, File };
+ SourceType sourceType;
+};
+
+
+// skips whitespace and returns the first non-whitespace character
+QChar Q3TextStream::eat_ws()
+{
+ QChar c;
+ do { c = ts_getc(); } while ( c != QEOF && ts_isspace(c) );
+ return c;
+}
+
+void Q3TextStream::init()
+{
+ // ### ungetcBuf = QEOF;
+ dev = 0;
+ owndev = FALSE;
+ mapper = 0;
+#ifndef QT_NO_TEXTCODEC
+ resetCodecConverterState(&mapperReadState);
+ resetCodecConverterState(&mapperWriteState);
+#endif
+ d = new Q3TextStreamPrivate;
+ doUnicodeHeader = TRUE; // autodetect
+ latin1 = TRUE; // should use locale?
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+}
+
+/*!
+ Constructs a data stream that has no IO device.
+*/
+
+Q3TextStream::Q3TextStream()
+{
+ init();
+ setEncoding( Locale );
+ reset();
+ d->sourceType = Q3TextStreamPrivate::NotSet;
+}
+
+/*!
+ Constructs a text stream that uses the IO device \a iod.
+*/
+
+Q3TextStream::Q3TextStream( QIODevice *iod )
+{
+ init();
+ setEncoding( Locale );
+ dev = iod;
+ reset();
+ d->sourceType = Q3TextStreamPrivate::IODevice;
+}
+
+// TODO: use special-case handling of this case in Q3TextStream, and
+// simplify this class to only deal with QChar or QString data.
+class QStringBuffer : public QIODevice {
+public:
+ QStringBuffer( QString* str );
+ ~QStringBuffer();
+ bool open( OpenMode m );
+ void close();
+ qint64 size() const;
+
+protected:
+ qint64 readData( char *p, qint64 len );
+ qint64 writeData( const char *p, qint64 len );
+
+ QString* s;
+
+private:
+ QStringBuffer( const QStringBuffer & );
+ QStringBuffer &operator=( const QStringBuffer & );
+};
+
+
+QStringBuffer::QStringBuffer( QString* str )
+{
+ s = str;
+}
+
+QStringBuffer::~QStringBuffer()
+{
+}
+
+
+bool QStringBuffer::open( OpenMode m )
+{
+ if ( !s ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "QStringBuffer::open: No string" );
+#endif
+ return FALSE;
+ }
+ if ( isOpen() ) {
+#if defined(QT_CHECK_STATE)
+ qWarning( "QStringBuffer::open: Buffer already open" );
+#endif
+ return FALSE;
+ }
+ setOpenMode( m );
+ if ( m & QIODevice::Truncate )
+ s->truncate( 0 );
+
+ if ( m & QIODevice::Append ) {
+ seek(s->length()*sizeof(QChar));
+ } else {
+ seek(0);
+ }
+ return TRUE;
+}
+
+void QStringBuffer::close()
+{
+ if ( isOpen() ) {
+ seek(0);
+ QIODevice::close();
+ }
+}
+
+qint64 QStringBuffer::size() const
+{
+ return s ? s->length()*sizeof(QChar) : 0;
+}
+
+qint64 QStringBuffer::readData( char *p, qint64 len )
+{
+#if defined(QT_CHECK_STATE)
+ Q_CHECK_PTR( p );
+ if ( !isOpen() ) {
+ qWarning( "QStringBuffer::readBlock: Buffer not open" );
+ return qint64(-1);
+ }
+ if ( !isReadable() ) {
+ qWarning( "QStringBuffer::readBlock: Read operation not permitted" );
+ return qint64(-1);
+ }
+#endif
+ if ( pos() + len > qint64(s->length()*sizeof(QChar)) ) {
+ // overflow
+ if ( pos() >= qint64(s->length()*sizeof(QChar)) ) {
+ return -1;
+ } else {
+ len = s->length()*2 - pos();
+ }
+ }
+ memcpy( p, ((const char*)(s->unicode()))+pos(), len );
+ return len;
+}
+
+qint64 QStringBuffer::writeData( const char *p, qint64 len )
+{
+#if defined(QT_CHECK_NULL)
+ if ( p == 0 && len != 0 )
+ qWarning( "QStringBuffer::writeBlock: Null pointer error" );
+#endif
+#if defined(QT_CHECK_STATE)
+ if ( !isOpen() ) {
+ qWarning( "QStringBuffer::writeBlock: Buffer not open" );
+ return -1;
+ }
+ if ( !isWritable() ) {
+ qWarning( "QStringBuffer::writeBlock: Write operation not permitted" );
+ return -1;
+ }
+ if ( pos()&1 ) {
+ qWarning( "QStringBuffer::writeBlock: non-even index - non Unicode" );
+ return -1;
+ }
+ if ( len&1 ) {
+ qWarning( "QStringBuffer::writeBlock: non-even length - non Unicode" );
+ return -1;
+ }
+#endif
+ s->replace(pos()/2, len/2, (QChar*)p, len/2);
+ return len;
+}
+
+/*!
+ Constructs a text stream that operates on the Unicode QString, \a
+ str, through an internal device. The \a filemode argument is
+ passed to the device's open() function; see \l{QIODevice::mode()}.
+
+ If you set an encoding or codec with setEncoding() or setCodec(),
+ this setting is ignored for text streams that operate on QString.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 0
+
+ Writing data to the text stream will modify the contents of the
+ string. The string will be expanded when data is written beyond
+ the end of the string. Note that the string will not be truncated:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 1
+
+ Note that because QString is Unicode, you should not use
+ readRawBytes() or writeRawBytes() on such a stream.
+*/
+
+Q3TextStream::Q3TextStream( QString* str, int filemode )
+{
+ // TODO: optimize for this case as it becomes more common
+ // (see QStringBuffer above)
+ init();
+ dev = new QStringBuffer( str );
+ ((QStringBuffer *)dev)->open( QIODevice::OpenMode(filemode) );
+ owndev = TRUE;
+ setEncoding(RawUnicode);
+ reset();
+ d->sourceType = Q3TextStreamPrivate::String;
+}
+
+/*! \obsolete
+
+ This constructor is equivalent to the constructor taking a QString*
+ parameter.
+*/
+
+Q3TextStream::Q3TextStream( QString& str, int filemode )
+{
+ init();
+ dev = new QStringBuffer( &str );
+ ((QStringBuffer *)dev)->open( QIODevice::OpenMode(filemode) );
+ owndev = TRUE;
+ setEncoding(RawUnicode);
+ reset();
+ d->sourceType = Q3TextStreamPrivate::String;
+}
+
+/*!
+ Constructs a text stream that operates on the byte array, \a a,
+ through an internal QBuffer device. The \a mode argument is passed
+ to the device's open() function; see \l{QIODevice::mode()}.
+
+ Example:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 2
+
+ Writing data to the text stream will modify the contents of the
+ array. The array will be expanded when data is written beyond the
+ end of the string.
+
+ Same example, using a QBuffer:
+ \snippet doc/src/snippets/code/src_qt3support_text_q3textstream.cpp 3
+*/
+
+Q3TextStream::Q3TextStream( QByteArray &a, int mode )
+{
+ init();
+ QBuffer *buffer = new QBuffer;
+ buffer->setBuffer( &a );
+ buffer->open( QIODevice::OpenMode(mode) );
+ dev = buffer;
+ owndev = TRUE;
+ setEncoding( Latin1 ); //### Locale???
+ reset();
+ d->sourceType = Q3TextStreamPrivate::ByteArray;
+}
+
+/*!
+ Constructs a text stream that operates on an existing file handle
+ \a fh through an internal QFile device. The \a mode argument is
+ passed to the device's open() function; see \l{QIODevice::mode()}.
+
+ Note that if you create a Q3TextStream \c cout or another name that
+ is also used for another variable of a different type, some
+ linkers may confuse the two variables, which will often cause
+ crashes.
+*/
+
+Q3TextStream::Q3TextStream( FILE *fh, int mode )
+{
+ init();
+ setEncoding( Locale ); //###
+ dev = new QFile;
+ ((QFile *)dev)->open( QIODevice::OpenMode(mode), fh );
+ owndev = TRUE;
+ reset();
+ d->sourceType = Q3TextStreamPrivate::File;
+}
+
+/*!
+ Destroys the text stream.
+
+ The destructor does not affect the current IO device.
+*/
+
+Q3TextStream::~Q3TextStream()
+{
+ if ( owndev )
+ delete dev;
+ delete d;
+}
+
+/*!
+ \since 4.2
+
+ Positions the read pointer at the first non-whitespace character.
+*/
+void Q3TextStream::skipWhiteSpace()
+{
+ ts_ungetc( eat_ws() );
+}
+
+
+/*!
+ Tries to read \a len characters from the stream and stores them in
+ \a buf. Returns the number of characters really read.
+
+ \warning There will no QEOF appended if the read reaches the end
+ of the file. EOF is reached when the return value does not equal
+ \a len.
+*/
+uint Q3TextStream::ts_getbuf( QChar* buf, uint len )
+{
+ if ( len < 1 )
+ return 0;
+
+ uint rnum = 0; // the number of QChars really read
+
+ if ( d && d->ungetcBuf.length() ) {
+ while ( rnum < len && rnum < uint(d->ungetcBuf.length()) ) {
+ *buf = d->ungetcBuf.constref( rnum );
+ buf++;
+ rnum++;
+ }
+ d->ungetcBuf = d->ungetcBuf.mid( rnum );
+ if ( rnum >= len )
+ return rnum;
+ }
+
+ // we use dev->ungetch() for one of the bytes of the unicode
+ // byte-order mark, but a local unget hack for the other byte:
+ int ungetHack = EOF;
+
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE; // only at the top
+ int c1 = dev->getch();
+ if ( c1 == EOF )
+ return rnum;
+ int c2 = dev->getch();
+ if ( c1 == 0xfe && c2 == 0xff ) {
+ mapper = 0;
+ latin1 = FALSE;
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+ } else if ( c1 == 0xff && c2 == 0xfe ) {
+ mapper = 0;
+ latin1 = FALSE;
+ internalOrder = !QChar::networkOrdered();
+ networkOrder = FALSE;
+ } else {
+ if ( c2 != EOF ) {
+ dev->ungetch( c2 );
+ ungetHack = c1;
+ } else {
+ /*
+ A small bug might hide here. If only the first byte
+ of a file has made it so far, and that first byte
+ is half of the byte-order mark, then the utfness
+ will not be detected.
+ */
+ dev->ungetch( c1 );
+ }
+ }
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ bool shortRead = FALSE;
+ while( rnum < len ) {
+ QString s;
+ bool readBlock = !( len == 1+rnum );
+ for (;;) {
+ // for efficiency: normally read a whole block
+ if ( readBlock ) {
+ // guess buffersize; this may be wrong (too small or too
+ // big). But we can handle this (either iterate reading
+ // or use ungetcBuf).
+ // Note that this might cause problems for codecs where
+ // one byte can result in >1 Unicode Characters if bytes
+ // are written to the stream in the meantime (loss of
+ // synchronicity).
+ uint rlen = len - rnum;
+ char *cbuf = new char[ rlen ];
+ if ( ungetHack != EOF ) {
+ rlen = 1+dev->readBlock( cbuf+1, rlen-1 );
+ cbuf[0] = (char)ungetHack;
+ ungetHack = EOF;
+ } else {
+ rlen = dev->readBlock( cbuf, rlen );
+ }
+ s += mapper->toUnicode( cbuf, rlen, &mapperWriteState );
+ delete[] cbuf;
+ // use buffered reading only for the first time, because we
+ // have to get the stream synchronous again (this is easier
+ // with single character reading)
+ readBlock = FALSE;
+ }
+ // get stream (and codec) in sync
+ int c;
+ if ( ungetHack == EOF ) {
+ c = dev->getch();
+ } else {
+ c = ungetHack;
+ ungetHack = EOF;
+ }
+ if ( c == EOF ) {
+ shortRead = TRUE;
+ break;
+ }
+ char b = c;
+ uint lengthBefore = s.length();
+ s += mapper->toUnicode( &b, 1, &mapperWriteState );
+
+ if ( uint(s.length()) > lengthBefore )
+ break; // it seems we are in sync now
+ }
+ uint i = 0;
+ uint end = QMIN( len-rnum, uint(s.length()) );
+ while( i < end ) {
+ *buf = s.constref(i++);
+ buf++;
+ }
+ rnum += end;
+ if ( uint(s.length()) > i ) {
+ // could be = but append is clearer
+ d->ungetcBuf.append( s.mid( i ) );
+ }
+ if ( shortRead )
+ return rnum;
+ }
+ } else
+#endif
+ if ( latin1 ) {
+ if ( len == 1+rnum ) {
+ // use this method for one character because it is more efficient
+ // (arnt doubts whether it makes a difference, but lets it stand)
+ int c = (ungetHack == EOF) ? dev->getch() : ungetHack;
+ if ( c != EOF ) {
+ *buf = QLatin1Char((char)c);
+ buf++;
+ rnum++;
+ }
+ } else {
+ if ( ungetHack != EOF ) {
+ *buf = QLatin1Char((char)ungetHack);
+ buf++;
+ rnum++;
+ ungetHack = EOF;
+ }
+ char *cbuf = new char[len - rnum];
+ while ( !dev->atEnd() && rnum < len ) {
+ uint rlen = len - rnum;
+ rlen = dev->readBlock( cbuf, rlen );
+ char *it = cbuf;
+ char *end = cbuf + rlen;
+ while ( it < end ) {
+ *buf = QLatin1Char(*it);
+ buf++;
+ it++;
+ }
+ rnum += rlen;
+ }
+ delete[] cbuf;
+ }
+ } else { // UCS-2 or UTF-16
+ if ( len == 1+rnum ) {
+ int c1 = (ungetHack == EOF) ? dev->getch() : ungetHack;
+
+
+ if ( c1 == EOF )
+ return rnum;
+ int c2 = dev->getch();
+
+
+ if ( c2 == EOF )
+ return rnum;
+
+ if ( networkOrder ) {
+ *buf = QChar( c2, c1 );
+ } else {
+ *buf = QChar( c1, c2 );
+ }
+ buf++;
+ rnum++;
+ } else {
+ char *cbuf = new char[ 2*( len - rnum ) ]; // for paranoids: overflow possible
+ while ( !dev->atEnd() && rnum < len ) {
+ uint rlen = 2 * ( len-rnum );
+ if ( ungetHack != EOF ) {
+ rlen = 1+dev->readBlock( cbuf+1, rlen-1 );
+ cbuf[0] = (char)ungetHack;
+ ungetHack = EOF;
+ } else {
+ rlen = dev->readBlock( cbuf, rlen );
+ }
+ // We can't use an odd number of bytes, so put it back. But
+ // do it only if we are capable of reading more -- normally
+ // there should not be an odd number, but the file might be
+ // truncated or not in UTF-16...
+ if ( (rlen & 1) == 1 )
+ if ( !dev->atEnd() )
+ dev->ungetch( cbuf[--rlen] );
+ uint i = 0;
+ if ( networkOrder ) {
+ while( i < rlen ) {
+ *buf = QChar( cbuf[i+1], cbuf[i] );
+ buf++;
+ i+=2;
+ }
+ } else {
+ while( i < rlen ) {
+ *buf = QChar( cbuf[i], cbuf[i+1] );
+ buf++;
+ i+=2;
+ }
+ }
+ rnum += i/2;
+ }
+ delete[] cbuf;
+ }
+ }
+ return rnum;
+}
+
+/*!
+ Tries to read one line, but at most len characters from the stream
+ and stores them in \a buf.
+
+ Returns the number of characters really read. Newlines are not
+ stripped.
+
+ There will be a QEOF appended if the read reaches the end of file;
+ this is different to ts_getbuf().
+
+ This function works only if a newline (as byte) is also a newline
+ (as resulting character) since it uses QIODevice::readLine(). So
+ use it only for such codecs where this is true!
+
+ This function is (almost) a no-op for UTF 16. Don't use it if
+ doUnicodeHeader is TRUE!
+*/
+uint Q3TextStream::ts_getline( QChar* buf )
+{
+ uint rnum=0; // the number of QChars really read
+ char cbuf[ getline_buf_size+1 ];
+
+ if ( d && d->ungetcBuf.length() ) {
+ while( rnum < getline_buf_size && rnum < uint(d->ungetcBuf.length()) ) {
+ buf[rnum] = d->ungetcBuf.constref(rnum);
+ rnum++;
+ }
+ d->ungetcBuf = d->ungetcBuf.mid( rnum );
+ if ( rnum >= getline_buf_size )
+ return rnum;
+ }
+
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ QString s;
+ bool readBlock = TRUE;
+ for (;;) {
+ // for efficiency: try to read a line
+ if ( readBlock ) {
+ int rlen = getline_buf_size - rnum;
+ rlen = dev->readLine( cbuf, rlen+1 );
+ if ( rlen == -1 )
+ rlen = 0;
+ s += mapper->toUnicode( cbuf, rlen, &mapperWriteState );
+ readBlock = FALSE;
+ }
+ if ( dev->atEnd()
+ || s.at( s.length()-1 ) == QLatin1Char('\n')
+ || s.at( s.length()-1 ) == QLatin1Char('\r')
+ ) {
+ break;
+ } else {
+ // get stream (and codec) in sync
+ int c;
+ c = dev->getch();
+ if ( c == EOF ) {
+ break;
+ }
+ char b = c;
+ uint lengthBefore = s.length();
+ s += mapper->toUnicode( &b, 1, &mapperWriteState );
+ if ( uint(s.length()) > lengthBefore )
+ break; // it seems we are in sync now
+ }
+ }
+ uint i = 0;
+ while( rnum < getline_buf_size && i < uint(s.length()) )
+ buf[rnum++] = s.constref(i++);
+ if ( uint(s.length()) > i )
+ // could be = but append is clearer
+ d->ungetcBuf.append( s.mid( i ) );
+ if ( rnum < getline_buf_size && dev->atEnd() )
+ buf[rnum++] = QEOF;
+ } else
+#endif
+ if ( latin1 ) {
+ int rlen = getline_buf_size - rnum;
+ rlen = dev->readLine( cbuf, rlen+1 );
+ if ( rlen == -1 )
+ rlen = 0;
+ char *end = cbuf+rlen;
+ char *it = cbuf;
+ buf +=rnum;
+ while ( it != end ) {
+ buf->setCell( *(it++) );
+ buf->setRow( 0 );
+ buf++;
+ }
+ rnum += rlen;
+ if ( rnum < getline_buf_size && dev->atEnd() )
+ buf[1] = QEOF;
+ }
+ return rnum;
+}
+
+
+/*!
+ Puts one character into the stream.
+*/
+void Q3TextStream::ts_putc( QChar c )
+{
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ int len = 1;
+ QString s = c;
+ Q3CString block = mapper->fromUnicode( s.data(), len );//, &mapperReadState );
+ dev->writeBlock( block );
+ } else
+#endif
+ if ( latin1 ) {
+ if ( c.row() )
+ dev->putch( '?' ); // unknown character
+ else
+ dev->putch( c.cell() );
+ } else {
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ ts_putc( QChar::ByteOrderMark );
+ }
+ if ( internalOrder ) {
+ // this case is needed by QStringBuffer
+ dev->writeBlock( (char*)&c, sizeof(QChar) );
+ } else if ( networkOrder ) {
+ dev->putch( c.row() );
+ dev->putch( c.cell() );
+ } else {
+ dev->putch( c.cell() );
+ dev->putch( c.row() );
+ }
+ }
+}
+
+/*!
+ Puts one character into the stream.
+*/
+void Q3TextStream::ts_putc( int ch )
+{
+ ts_putc( QChar((ushort)ch) );
+}
+
+bool Q3TextStream::ts_isdigit( QChar c )
+{
+ return c.isDigit();
+}
+
+bool Q3TextStream::ts_isspace( QChar c )
+{
+ return c.isSpace();
+}
+
+void Q3TextStream::ts_ungetc( QChar c )
+{
+ if ( c.unicode() == 0xffff )
+ return;
+
+ d->ungetcBuf.prepend( c );
+}
+
+/*!
+ \since 4.2
+
+ Reads \a len bytes from the stream into \a s and returns a
+ reference to the stream.
+
+ The buffer \a s must be preallocated.
+
+ Note that no encoding is done by this function.
+
+ \warning The behavior of this function is undefined unless the
+ stream's encoding is set to Unicode or Latin1.
+
+ \sa QIODevice::readBlock()
+*/
+
+Q3TextStream &Q3TextStream::readRawBytes( char *s, uint len )
+{
+ dev->readBlock( s, len );
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Writes the \a len bytes from \a s to the stream and returns a
+ reference to the stream.
+
+ Note that no encoding is done by this function.
+
+ \sa QIODevice::writeBlock()
+*/
+
+Q3TextStream &Q3TextStream::writeRawBytes( const char* s, uint len )
+{
+ dev->writeBlock( s, len );
+ return *this;
+}
+
+
+Q3TextStream &Q3TextStream::writeBlock( const char* p, uint len )
+{
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ if ( !mapper && !latin1 ) {
+ ts_putc( QChar::ByteOrderMark );
+ }
+ }
+ // QCString and const char * are treated as Latin-1
+ if ( !mapper && latin1 ) {
+ dev->writeBlock( p, len );
+ } else if ( !mapper && internalOrder ) {
+ QChar *u = new QChar[len];
+ for ( uint i = 0; i < len; i++ )
+ u[i] = QLatin1Char(p[i]);
+ dev->writeBlock( (char*)u, len * sizeof(QChar) );
+ delete [] u;
+ }
+#ifndef QT_NO_TEXTCODEC
+ else if (mapper) {
+ QString s = QString::fromLatin1(p, len);
+ int l = len;
+ Q3CString block = mapper->fromUnicode(s.data(), l );//, &mapperReadState );
+ dev->writeBlock( block );
+ }
+#endif
+ else {
+ for ( uint i = 0; i < len; i++ )
+ ts_putc( (uchar)p[i] );
+ }
+ return *this;
+}
+
+Q3TextStream &Q3TextStream::writeBlock( const QChar* p, uint len )
+{
+#ifndef QT_NO_TEXTCODEC
+ if ( mapper ) {
+ QConstString s( p, len );
+ int l = len;
+ Q3CString block = mapper->fromUnicode( s.string().data(), l );//, &mapperReadState );
+ dev->writeBlock( block );
+ } else
+#endif
+ if ( latin1 ) {
+ dev->write(QString( p, len ).toLatin1());
+ } else if ( internalOrder ) {
+ if ( doUnicodeHeader ) {
+ doUnicodeHeader = FALSE;
+ ts_putc( QChar::ByteOrderMark );
+ }
+ dev->writeBlock( (char*)p, sizeof(QChar)*len );
+ } else {
+ for (uint i=0; i<len; i++)
+ ts_putc( p[i] );
+ }
+ return *this;
+}
+
+/*!
+ \since 4.2
+
+ Resets the text stream.
+
+ \list
+ \i All flags are set to 0.
+ \i The field width is set to 0.
+ \i The fill character is set to ' ' (Space).
+ \i The precision is set to 6.
+ \endlist
+
+ \sa setf(), width(), fill(), precision()
+*/
+
+void Q3TextStream::reset()
+{
+ fflags = 0;
+ fwidth = 0;
+ fillchar = ' ';
+ fprec = 6;
+}
+
+/*!
+ \fn QIODevice *Q3TextStream::device() const
+ \since 4.2
+
+ Returns the IO device currently set.
+
+ \sa setDevice(), unsetDevice()
+*/
+
+/*!
+ \since 4.2
+
+ Sets the IO device to \a iod.
+
+ \sa device(), unsetDevice()
+*/
+
+void Q3TextStream::setDevice( QIODevice *iod )
+{
+ if ( owndev ) {
+ delete dev;
+ owndev = FALSE;
+ }
+ dev = iod;
+ d->sourceType = Q3TextStreamPrivate::IODevice;
+}
+
+/*!
+ \since 4.2
+
+ Unsets the IO device. Equivalent to setDevice( 0 ).
+
+ \sa device(), setDevice()
+*/
+
+void Q3TextStream::unsetDevice()
+{
+ setDevice( 0 );
+ d->sourceType = Q3TextStreamPrivate::NotSet;
+}
+
+/*!
+ \fn bool Q3TextStream::atEnd() const
+ \since 4.2
+
+ Returns TRUE if the IO device has reached the end position (end of
+ the stream or file) or if there is no IO device set; otherwise
+ returns FALSE.
+
+ \sa QIODevice::atEnd()
+*/
+
+/*!\fn bool Q3TextStream::eof() const
+
+ \obsolete
+
+ This function has been renamed to atEnd().
+
+ \sa QIODevice::atEnd()
+*/
+
+/*****************************************************************************
+ Q3TextStream read functions
+ *****************************************************************************/
+
+
+/*!
+ \overload
+
+ Reads a char \a c from the stream and returns a reference to the
+ stream. Note that whitespace is skipped.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( char &c )
+{
+ CHECK_STREAM_PRECOND
+ c = eat_ws().toLatin1();
+ return *this;
+}
+
+/*!
+ Reads a char \a c from the stream and returns a reference to the
+ stream. Note that whitespace is \e not skipped.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( QChar &c )
+{
+ CHECK_STREAM_PRECOND
+ c = ts_getc();
+ return *this;
+}
+
+
+ulong Q3TextStream::input_bin()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( dv == 0 || dv == 1 ) {
+ val = ( val << 1 ) + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_oct()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( dv >= 0 && dv <= 7 ) {
+ val = ( val << 3 ) + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( dv == 8 || dv == 9 ) {
+ while ( ts_isdigit(ch) )
+ ch = ts_getc();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_dec()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ int dv = ch.digitValue();
+ while ( ts_isdigit(ch) ) {
+ val = val * 10 + dv;
+ ch = ts_getc();
+ dv = ch.digitValue();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+ulong Q3TextStream::input_hex()
+{
+ ulong val = 0;
+ QChar ch = eat_ws();
+ char c = ch.toLatin1();
+ while ( isxdigit((uchar) c) ) {
+ val <<= 4;
+ if ( ts_isdigit(QLatin1Char(c)) )
+ val += c - '0';
+ else
+ val += 10 + tolower( (uchar) c ) - 'a';
+ ch = ts_getc();
+ c = ch.toLatin1();
+ }
+ if ( ch != QEOF )
+ ts_ungetc( ch );
+ return val;
+}
+
+long Q3TextStream::input_int()
+{
+ long val;
+ QChar ch;
+ char c;
+ switch ( flags() & basefield ) {
+ case bin:
+ val = (long)input_bin();
+ break;
+ case oct:
+ val = (long)input_oct();
+ break;
+ case dec:
+ ch = eat_ws();
+ c = ch.toLatin1();
+ if ( ch == QEOF ) {
+ val = 0;
+ } else {
+ if ( !(c == '-' || c == '+') )
+ ts_ungetc( ch );
+ if ( c == '-' ) {
+ ulong v = input_dec();
+ if ( v ) { // ensure that LONG_MIN can be read
+ v--;
+ val = -((long)v) - 1;
+ } else {
+ val = 0;
+ }
+ } else {
+ val = (long)input_dec();
+ }
+ }
+ break;
+ case hex:
+ val = (long)input_hex();
+ break;
+ default:
+ val = 0;
+ ch = eat_ws();
+ c = ch.toLatin1();
+ if ( c == '0' ) { // bin, oct or hex
+ ch = ts_getc();
+ c = ch.toLatin1();
+ if ( tolower((uchar) c) == 'x' )
+ val = (long)input_hex();
+ else if ( tolower((uchar) c) == 'b' )
+ val = (long)input_bin();
+ else { // octal
+ ts_ungetc( ch );
+ if ( c >= '0' && c <= '7' ) {
+ val = (long)input_oct();
+ } else {
+ val = 0;
+ }
+ }
+ } else if ( ts_isdigit(ch) ) {
+ ts_ungetc( ch );
+ val = (long)input_dec();
+ } else if ( c == '-' || c == '+' ) {
+ ulong v = input_dec();
+ if ( c == '-' ) {
+ if ( v ) { // ensure that LONG_MIN can be read
+ v--;
+ val = -((long)v) - 1;
+ } else {
+ val = 0;
+ }
+ } else {
+ val = (long)v;
+ }
+ }
+ }
+ return val;
+}
+
+//
+// We use a table-driven FSM to parse floating point numbers
+// strtod() cannot be used directly since we're reading from a QIODevice
+//
+
+double Q3TextStream::input_double()
+{
+ const int Init = 0; // states
+ const int Sign = 1;
+ const int Mantissa = 2;
+ const int Dot = 3;
+ const int Abscissa = 4;
+ const int ExpMark = 5;
+ const int ExpSign = 6;
+ const int Exponent = 7;
+ const int Done = 8;
+
+ const int InputSign = 1; // input tokens
+ const int InputDigit = 2;
+ const int InputDot = 3;
+ const int InputExp = 4;
+
+ static const uchar table[8][5] = {
+ /* None InputSign InputDigit InputDot InputExp */
+ { 0, Sign, Mantissa, Dot, 0, }, // Init
+ { 0, 0, Mantissa, Dot, 0, }, // Sign
+ { Done, Done, Mantissa, Dot, ExpMark,}, // Mantissa
+ { 0, 0, Abscissa, 0, 0, }, // Dot
+ { Done, Done, Abscissa, Done, ExpMark,}, // Abscissa
+ { 0, ExpSign, Exponent, 0, 0, }, // ExpMark
+ { 0, 0, Exponent, 0, 0, }, // ExpSign
+ { Done, Done, Exponent, Done, Done } // Exponent
+ };
+
+ int state = Init; // parse state
+ int input; // input token
+
+ char buf[256];
+ int i = 0;
+ QChar c = eat_ws();
+
+ for (;;) {
+
+ switch ( c.toLatin1() ) {
+ case '+':
+ case '-':
+ input = InputSign;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ input = InputDigit;
+ break;
+ case '.':
+ input = InputDot;
+ break;
+ case 'e':
+ case 'E':
+ input = InputExp;
+ break;
+ default:
+ input = 0;
+ break;
+ }
+
+ state = table[state][input];
+
+ if ( state == 0 || state == Done || i > 250 ) {
+ if ( i > 250 ) { // ignore rest of digits
+ do { c = ts_getc(); } while ( c != QEOF && ts_isdigit(c) );
+ }
+ if ( c != QEOF )
+ ts_ungetc( c );
+ buf[i] = '\0';
+ char *end;
+ return strtod( buf, &end );
+ }
+
+ buf[i++] = c.toLatin1();
+ c = ts_getc();
+ }
+
+#if !defined(Q_CC_EDG)
+ return 0.0;
+#endif
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c short integer \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed short &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed short)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c short integer \a i from the stream and
+ returns a reference to the stream. See flags() for an explanation
+ of the expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned short &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned short)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c int \a i from the stream and returns a reference
+ to the stream. See flags() for an explanation of the expected
+ input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed int &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed int)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned int &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned int)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a signed \c long int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( signed long &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (signed long)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads an unsigned \c long int \a i from the stream and returns a
+ reference to the stream. See flags() for an explanation of the
+ expected input format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( unsigned long &i )
+{
+ CHECK_STREAM_PRECOND
+ i = (unsigned long)input_int();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a \c float \a f from the stream and returns a reference to
+ the stream. See flags() for an explanation of the expected input
+ format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( float &f )
+{
+ CHECK_STREAM_PRECOND
+ f = (float)input_double();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a \c double \a f from the stream and returns a reference to
+ the stream. See flags() for an explanation of the expected input
+ format.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( double &f )
+{
+ CHECK_STREAM_PRECOND
+ f = input_double();
+ return *this;
+}
+
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a s and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( char *s )
+{
+ CHECK_STREAM_PRECOND
+ int maxlen = width( 0 );
+ QChar c = eat_ws();
+ if ( !maxlen )
+ maxlen = -1;
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) || maxlen-- == 0 ) {
+ ts_ungetc( c );
+ break;
+ }
+ *s++ = c.toLatin1();
+ c = ts_getc();
+ }
+
+ *s = '\0';
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a str and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( QString &str )
+{
+ CHECK_STREAM_PRECOND
+ str=QString::fromLatin1("");
+ QChar c = eat_ws();
+
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) ) {
+ ts_ungetc( c );
+ break;
+ }
+
+ str += c;
+ c = ts_getc();
+ }
+
+ return *this;
+}
+
+/*!
+ \overload
+
+ Reads a "word" from the stream into \a str and returns a reference
+ to the stream.
+
+ A word consists of characters for which isspace() returns FALSE.
+*/
+
+Q3TextStream &Q3TextStream::operator>>( Q3CString &str )
+{
+ CHECK_STREAM_PRECOND
+ Q3CString *dynbuf = 0;
+ const int buflen = 256;
+ char buffer[buflen];
+ char *s = buffer;
+ int i = 0;
+ QChar c = eat_ws();
+
+ while ( c != QEOF ) {
+ if ( ts_isspace(c) ) {
+ ts_ungetc( c );
+ break;
+ }
+ if ( i >= buflen-1 ) {
+ if ( !dynbuf ) { // create dynamic buffer
+ dynbuf = new Q3CString(buflen*2);
+ memcpy( dynbuf->data(), s, i ); // copy old data
+ } else if ( i >= (int)dynbuf->size()-1 ) {
+ dynbuf->resize( dynbuf->size()*2 );
+ }
+ s = dynbuf->data();
+ }
+ s[i++] = c.toLatin1();
+ c = ts_getc();
+ }
+ str.resize( i );
+ memcpy( str.data(), s, i );
+
+ delete dynbuf;
+ return *this;
+}
+
+
+/*!
+ \since 4.2
+
+ Reads a line from the stream and returns a string containing the
+ text.
+
+ The returned string does not contain any trailing newline or
+ carriage return. Note that this is different from
+ QIODevice::readLine(), which does not strip the newline at the end
+ of the line.
+
+ On EOF you will get a QString that is null. On reading an empty
+ line the returned QString is empty but not null.
+
+ \sa QIODevice::readLine()
+*/
+
+QString Q3TextStream::readLine()
+{
+#if defined(QT_CHECK_STATE)
+ if ( !dev ) {
+ qWarning( "Q3TextStream::readLine: No device" );
+ return QString::null;
+ }
+#endif
+ bool readCharByChar = TRUE;
+ QString result;
+#if 0
+ if ( !doUnicodeHeader && (
+ (latin1) ||
+ (mapper != 0 && mapper->mibEnum() == 106 ) // UTF 8
+ ) ) {
+ readCharByChar = FALSE;
+ // use optimized read line
+ QChar c[getline_buf_size];
+ int pos = 0;
+ bool eof = FALSE;
+
+ for (;;) {
+ pos = ts_getline( c );
+ if ( pos == 0 ) {
+ // something went wrong; try fallback
+ readCharByChar = TRUE;
+ //dev->resetStatus();
+ break;
+ }
+ if ( c[pos-1] == QEOF || c[pos-1] == '\n' ) {
+ if ( pos>2 && c[pos-1]==QEOF && c[pos-2]=='\n' ) {
+ result += QString( c, pos-2 );
+ } else if ( pos > 1 ) {
+ result += QString( c, pos-1 );
+ }
+ if ( pos == 1 && c[pos-1] == QEOF )
+ eof = TRUE;
+ break;
+ } else {
+ result += QString( c, pos );
+ }
+ }
+ if ( eof && result.isEmpty() )
+ return QString::null;
+ }
+#endif
+ if ( readCharByChar ) {
+ const int buf_size = 256;
+ QChar c[buf_size];
+ int pos = 0;
+
+ c[pos] = ts_getc();
+ if ( c[pos] == QEOF )
+ return QString::null;
+
+ while ( c[pos] != QEOF && c[pos] != QLatin1Char('\n') ) {
+ if ( c[pos] == QLatin1Char('\r') ) { // ( handle mac and dos )
+ QChar nextc = ts_getc();
+ if ( nextc != QLatin1Char('\n') )
+ ts_ungetc( nextc );
+ break;
+ }
+ pos++;
+ if ( pos >= buf_size ) {
+ result += QString( c, pos );
+ pos = 0;
+ }
+ c[pos] = ts_getc();
+ }
+ result += QString( c, pos );
+ }
+
+ return result;
+}
+
+
+/*!
+ \since 4.2
+
+ Reads the entire stream from the current position, and returns a string
+ containing the text.
+
+ \sa readLine()
+*/
+
+QString Q3TextStream::read()
+{
+#if defined(QT_CHECK_STATE)
+ if ( !dev ) {
+ qWarning( "Q3TextStream::read: No device" );
+ return QString::null;
+ }
+#endif
+ QString result;
+ const uint bufsize = 512;
+ QChar buf[bufsize];
+ uint i, num, start;
+ bool skipped_cr = FALSE;
+
+ for (;;) {
+ num = ts_getbuf(buf,bufsize);
+ // convert dos (\r\n) and mac (\r) style eol to unix style (\n)
+ start = 0;
+ for ( i=0; i<num; i++ ) {
+ if ( buf[i] == QLatin1Char('\r') ) {
+ // Only skip single cr's preceding lf's
+ if ( skipped_cr ) {
+ result += buf[i];
+ start++;
+ } else {
+ result += QString( &buf[start], i-start );
+ start = i+1;
+ skipped_cr = TRUE;
+ }
+ } else {
+ if ( skipped_cr ) {
+ if ( buf[i] != QLatin1Char('\n') ) {
+ // Should not have skipped it
+ result += QLatin1Char('\n');
+ }
+ skipped_cr = FALSE;
+ }
+ }
+ }
+ if ( start < num )
+ result += QString( &buf[start], i-start );
+ if ( num != bufsize ) // if ( EOF )
+ break;
+ }
+ return result;
+}
+
+
+
+/*****************************************************************************
+ Q3TextStream write functions
+ *****************************************************************************/
+
+/*!
+ \since 4.2
+
+ Writes character \c char to the stream and returns a reference to
+ the stream.
+
+ The character \a c is assumed to be Latin1 encoded independent of
+ the Encoding set for the Q3TextStream.
+*/
+Q3TextStream &Q3TextStream::operator<<( QChar c )
+{
+ CHECK_STREAM_PRECOND
+ ts_putc( c );
+ return *this;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes character \a c to the stream and returns a reference to the
+ stream.
+*/
+Q3TextStream &Q3TextStream::operator<<( char c )
+{
+ CHECK_STREAM_PRECOND
+ unsigned char uc = (unsigned char) c;
+ ts_putc( uc );
+ return *this;
+}
+
+Q3TextStream &Q3TextStream::output_int( int format, ulong n, bool neg )
+{
+ static const char hexdigits_lower[] = "0123456789abcdef";
+ static const char hexdigits_upper[] = "0123456789ABCDEF";
+ CHECK_STREAM_PRECOND
+ char buf[76];
+ register char *p;
+ int len;
+ const char *hexdigits;
+
+ switch ( flags() & I_BASE_MASK ) {
+
+ case I_BASE_2: // output binary number
+ switch ( format & I_TYPE_MASK ) {
+ case I_SHORT: len=16; break;
+ case I_INT: len=sizeof(int)*8; break;
+ case I_LONG: len=32; break;
+ default: len = 0;
+ }
+ p = &buf[74]; // go reverse order
+ *p = '\0';
+ while ( len-- ) {
+ *--p = (char)(n&1) + '0';
+ n >>= 1;
+ if ( !n )
+ break;
+ }
+ if ( flags() & showbase ) { // show base
+ *--p = (flags() & uppercase) ? 'B' : 'b';
+ *--p = '0';
+ }
+ break;
+
+ case I_BASE_8: // output octal number
+ p = &buf[74];
+ *p = '\0';
+ do {
+ *--p = (char)(n&7) + '0';
+ n >>= 3;
+ } while ( n );
+ if ( flags() & showbase )
+ *--p = '0';
+ break;
+
+ case I_BASE_16: // output hexadecimal number
+ p = &buf[74];
+ *p = '\0';
+ hexdigits = (flags() & uppercase) ?
+ hexdigits_upper : hexdigits_lower;
+ do {
+ *--p = hexdigits[(int)n&0xf];
+ n >>= 4;
+ } while ( n );
+ if ( flags() & showbase ) {
+ *--p = (flags() & uppercase) ? 'X' : 'x';
+ *--p = '0';
+ }
+ break;
+
+ default: // decimal base is default
+ p = &buf[74];
+ *p = '\0';
+ if ( neg )
+ n = (ulong)(-(long)n);
+ do {
+ *--p = ((int)(n%10)) + '0';
+ n /= 10;
+ } while ( n );
+ if ( neg )
+ *--p = '-';
+ else if ( flags() & showpos )
+ *--p = '+';
+ if ( (flags() & internal) && fwidth && !ts_isdigit(QLatin1Char(*p)) ) {
+ ts_putc( *p ); // special case for internal
+ ++p; // padding
+ fwidth--;
+ return *this << (const char*)p;
+ }
+ }
+ if ( fwidth ) { // adjustment required
+ if ( !(flags() & left) ) { // but NOT left adjustment
+ len = qstrlen(p);
+ int padlen = fwidth - len;
+ if ( padlen <= 0 ) { // no padding required
+ writeBlock( p, len );
+ } else if ( padlen < (int)(p-buf) ) { // speeds up padding
+ memset( p-padlen, (char)fillchar, padlen );
+ writeBlock( p-padlen, padlen+len );
+ }
+ else // standard padding
+ *this << (const char*)p;
+ }
+ else
+ *this << (const char*)p;
+ fwidth = 0; // reset field width
+ }
+ else {
+ writeBlock( p, qstrlen(p) );
+ }
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c short integer \a i to the stream and returns a
+ reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed short i )
+{
+ return output_int( I_SHORT | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c short integer \a i to the stream and
+ returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned short i )
+{
+ return output_int( I_SHORT | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c int \a i to the stream and returns a reference to the
+ stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed int i )
+{
+ return output_int( I_INT | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c int \a i to the stream and returns a
+ reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned int i )
+{
+ return output_int( I_INT | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c long \c int \a i to the stream and returns a reference
+ to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( signed long i )
+{
+ return output_int( I_LONG | I_SIGNED, i, i < 0 );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes an \c unsigned \c long \c int \a i to the stream and
+ returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( unsigned long i )
+{
+ return output_int( I_LONG | I_UNSIGNED, i, FALSE );
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c float \a f to the stream and returns a reference to
+ the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( float f )
+{
+ return *this << (double)f;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a \c double \a f to the stream and returns a reference to
+ the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( double f )
+{
+ CHECK_STREAM_PRECOND
+ char f_char;
+ char format[16];
+ if ( (flags()&floatfield) == fixed )
+ f_char = 'f';
+ else if ( (flags()&floatfield) == scientific )
+ f_char = (flags() & uppercase) ? 'E' : 'e';
+ else
+ f_char = (flags() & uppercase) ? 'G' : 'g';
+ register char *fs = format; // generate format string
+ *fs++ = '%'; // "%.<prec>l<f_char>"
+ *fs++ = '.';
+ int prec = precision();
+ if ( prec > 99 )
+ prec = 99;
+ if ( prec >= 10 ) {
+ *fs++ = prec / 10 + '0';
+ *fs++ = prec % 10 + '0';
+ } else {
+ *fs++ = prec + '0';
+ }
+ *fs++ = 'l';
+ *fs++ = f_char;
+ *fs = '\0';
+ QString num;
+ num.sprintf(format, f); // convert to text
+ if ( fwidth ) // padding
+ *this << num.latin1();
+ else // just write it
+ writeBlock(num.latin1(), num.length());
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a string to the stream and returns a reference to the
+ stream.
+
+ The string \a s is assumed to be Latin1 encoded independent of the
+ Encoding set for the Q3TextStream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const char* s )
+{
+ CHECK_STREAM_PRECOND
+ char padbuf[48];
+ uint len = qstrlen( s ); // don't write null terminator
+ if ( fwidth ) { // field width set
+ int padlen = fwidth - len;
+ fwidth = 0; // reset width
+ if ( padlen > 0 ) {
+ char *ppad;
+ if ( padlen > 46 ) { // create extra big fill buffer
+ ppad = new char[padlen];
+ Q_CHECK_PTR( ppad );
+ } else {
+ ppad = padbuf;
+ }
+ memset( ppad, (char)fillchar, padlen ); // fill with fillchar
+ if ( !(flags() & left) ) {
+ writeBlock( ppad, padlen );
+ padlen = 0;
+ }
+ writeBlock( s, len );
+ if ( padlen )
+ writeBlock( ppad, padlen );
+ if ( ppad != padbuf ) // delete extra big fill buf
+ delete[] ppad;
+ return *this;
+ }
+ }
+ writeBlock( s, len );
+ return *this;
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes \a s to the stream and returns a reference to the stream.
+
+ The string \a s is assumed to be Latin1 encoded independent of the
+ Encoding set for the Q3TextStream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const Q3CString & s )
+{
+ return operator<<(s.data());
+}
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes \a s to the stream and returns a reference to the stream.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( const QString& s )
+{
+ if ( !mapper && latin1 )
+ return operator<<(s.latin1());
+ CHECK_STREAM_PRECOND
+ QString s1 = s;
+ if ( fwidth ) { // field width set
+ if ( !(flags() & left) ) {
+ s1 = s.rightJustify(fwidth, QLatin1Char((char)fillchar));
+ } else {
+ s1 = s.leftJustify(fwidth, QLatin1Char((char)fillchar));
+ }
+ fwidth = 0; // reset width
+ }
+ writeBlock( s1.unicode(), s1.length() );
+ return *this;
+}
+
+
+/*!
+ \overload
+ \since 4.2
+
+ Writes a pointer to the stream and returns a reference to the
+ stream.
+
+ The \a ptr is output as an unsigned long hexadecimal integer.
+*/
+
+Q3TextStream &Q3TextStream::operator<<( void *ptr )
+{
+ int f = flags();
+ setf( hex, basefield );
+ setf( showbase );
+ unsetf( uppercase );
+ output_int( I_LONG | I_UNSIGNED, (ulong)ptr, FALSE );
+ flags( f );
+ return *this;
+}
+
+
+/*!
+ \fn int Q3TextStream::flags() const
+ \since 4.2
+
+ Returns the current stream flags. The default value is 0.
+
+ \table
+ \header \i Flag \i Meaning
+ \row \i \c skipws \i Not currently used; whitespace always skipped
+ \row \i \c left \i Numeric fields are left-aligned
+ \row \i \c right
+ \i Not currently used (by default, numerics are right-aligned)
+ \row \i \c internal \i Puts any padding spaces between +/- and value
+ \row \i \c bin \i Output \e and input only in binary
+ \row \i \c oct \i Output \e and input only in octal
+ \row \i \c dec \i Output \e and input only in decimal
+ \row \i \c hex \i Output \e and input only in hexadecimal
+ \row \i \c showbase
+ \i Annotates numeric outputs with 0b, 0, or 0x if in \c bin,
+ \c oct, or \c hex format
+ \row \i \c showpoint \i Not currently used
+ \row \i \c uppercase \i Uses 0B and 0X rather than 0b and 0x
+ \row \i \c showpos \i Shows + for positive numeric values
+ \row \i \c scientific \i Uses scientific notation for floating point values
+ \row \i \c fixed \i Uses fixed-point notation for floating point values
+ \endtable
+
+ Note that unless \c bin, \c oct, \c dec, or \c hex is set, the
+ input base is octal if the value starts with 0, hexadecimal if it
+ starts with 0x, binary if it starts with 0b, and decimal
+ otherwise.
+
+ \sa setf(), unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::flags( int f )
+
+ \overload
+
+ Sets the stream flags to \a f. Returns the previous stream flags.
+
+ \sa setf(), unsetf(), flags()
+*/
+
+/*!
+ \fn int Q3TextStream::setf( int bits )
+ \since 4.2
+
+ Sets the stream flag bits \a bits. Returns the previous stream
+ flags.
+
+ Equivalent to \c{flags( flags() | bits )}.
+
+ \sa unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::setf( int bits, int mask )
+
+ \overload
+
+ Sets the stream flag bits \a bits with a bit mask \a mask. Returns
+ the previous stream flags.
+
+ Equivalent to \c{flags( (flags() & ~mask) | (bits & mask) )}.
+
+ \sa setf(), unsetf()
+*/
+
+/*!
+ \fn int Q3TextStream::unsetf( int bits )
+ \since 4.2
+
+ Clears the stream flag bits \a bits. Returns the previous stream
+ flags.
+
+ Equivalent to \c{flags( flags() & ~mask )}.
+
+ \sa setf()
+*/
+
+/*!
+ \fn int Q3TextStream::width() const
+ \since 4.2
+
+ Returns the field width. The default value is 0.
+*/
+
+/*!
+ \fn int Q3TextStream::width( int w )
+
+ \overload
+
+ Sets the field width to \a w. Returns the previous field width.
+*/
+
+/*!
+ \fn int Q3TextStream::fill() const
+ \since 4.2
+
+ Returns the fill character. The default value is ' ' (space).
+*/
+
+/*!
+ \fn int Q3TextStream::fill( int f )
+ \overload
+
+ Sets the fill character to \a f. Returns the previous fill character.
+*/
+
+/*!
+ \fn int Q3TextStream::precision() const
+ \since 4.2
+
+ Returns the precision. The default value is 6.
+*/
+
+/*!
+ \fn int Q3TextStream::precision( int p )
+
+ \overload
+
+ Sets the precision to \a p. Returns the previous precision setting.
+*/
+
+
+Q3TextStream &bin( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::bin,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &oct( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::oct,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &dec( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::dec,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &hex( Q3TextStream &s )
+{
+ s.setf(Q3TextStream::hex,Q3TextStream::basefield);
+ return s;
+}
+
+Q3TextStream &endl( Q3TextStream &s )
+{
+ return s << '\n';
+}
+
+Q3TextStream &flush( Q3TextStream &s )
+{
+ return s;
+}
+
+Q3TextStream &ws( Q3TextStream &s )
+{
+ s.skipWhiteSpace();
+ return s;
+}
+
+Q3TextStream &reset( Q3TextStream &s )
+{
+ s.reset();
+ return s;
+}
+
+/*!
+ \since 4.2
+
+ Sets the encoding of this stream to \a e, where \a e is one of the
+ following values:
+ \table
+ \header \i Encoding \i Meaning
+ \row \i Locale
+ \i Uses local file format (Latin1 if locale is not set), but
+ autodetecting Unicode(utf16) on input.
+ \row \i Unicode
+ \i Uses Unicode(utf16) for input and output. Output will be
+ written in the order most efficient for the current platform
+ (i.e. the order used internally in QString).
+ \row \i UnicodeUTF8
+ \i Using Unicode(utf8) for input and output. If you use it for
+ input it will autodetect utf16 and use it instead of utf8.
+ \row \i Latin1
+ \i ISO-8859-1. Will not autodetect utf16.
+ \row \i UnicodeNetworkOrder
+ \i Uses network order Unicode(utf16) for input and output.
+ Useful when reading Unicode data that does not start with the
+ byte order marker.
+ \row \i UnicodeReverse
+ \i Uses reverse network order Unicode(utf16) for input and
+ output. Useful when reading Unicode data that does not start
+ with the byte order marker or when writing data that should be
+ read by buggy Windows applications.
+ \row \i RawUnicode
+ \i Like Unicode, but does not write the byte order marker nor
+ does it auto-detect the byte order. Useful only when writing to
+ non-persistent storage used by a single process.
+ \endtable
+
+ \c Locale and all Unicode encodings, except \c RawUnicode, will look
+ at the first two bytes in an input stream to determine the byte
+ order. The initial byte order marker will be stripped off before
+ data is read.
+
+ Note that this function should be called before any data is read to
+ or written from the stream.
+
+ \sa setCodec()
+*/
+
+void Q3TextStream::setEncoding( Encoding e )
+{
+ resetCodecConverterState(&mapperReadState);
+ resetCodecConverterState(&mapperWriteState);
+
+ if ( d->sourceType == Q3TextStreamPrivate::String )
+ return;
+
+ switch ( e ) {
+ case Unicode:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+ break;
+ case UnicodeUTF8:
+#ifndef QT_NO_TEXTCODEC
+ mapper = QTextCodec::codecForMib( 106 );
+ mapperWriteState.flags |= QTextCodec::IgnoreHeader;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+#else
+ mapper = 0;
+ latin1 = TRUE;
+ doUnicodeHeader = TRUE;
+#endif
+ break;
+ case UnicodeNetworkOrder:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = QChar::networkOrdered();
+ networkOrder = TRUE;
+ break;
+ case UnicodeReverse:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = TRUE;
+ internalOrder = !QChar::networkOrdered();
+ networkOrder = FALSE;
+ break;
+ case RawUnicode:
+ mapper = 0;
+ latin1 = FALSE;
+ doUnicodeHeader = FALSE;
+ internalOrder = TRUE;
+ networkOrder = QChar::networkOrdered();
+ break;
+ case Locale:
+ latin1 = TRUE; // fallback to Latin-1
+#ifndef QT_NO_TEXTCODEC
+ mapper = QTextCodec::codecForLocale();
+ mapperReadState.flags |= QTextCodec::IgnoreHeader;
+ mapperWriteState.flags |= QTextCodec::IgnoreHeader;
+ // optimized Latin-1 processing
+#if defined(Q_OS_WIN32)
+ if ( GetACP() == 1252 )
+ mapper = 0;
+#endif
+ if ( mapper && mapper->mibEnum() == 4 )
+#endif
+ mapper = 0;
+
+ doUnicodeHeader = TRUE; // If it reads as Unicode, accept it
+ break;
+ case Latin1:
+ mapper = 0;
+ doUnicodeHeader = FALSE;
+ latin1 = TRUE;
+ break;
+ }
+}
+
+
+#ifndef QT_NO_TEXTCODEC
+/*!
+ \since 4.2
+
+ Sets the codec for this stream to \a codec. Will not try to
+ autodetect Unicode.
+
+ Note that this function should be called before any data is read
+ to/written from the stream.
+
+ \sa setEncoding(), codec()
+*/
+
+void Q3TextStream::setCodec( QTextCodec *codec )
+{
+ if ( d->sourceType == Q3TextStreamPrivate::String )
+ return; // QString does not need any codec
+ mapper = codec;
+ latin1 = ( codec->mibEnum() == 4 );
+ if ( latin1 )
+ mapper = 0;
+ doUnicodeHeader = FALSE;
+}
+
+/*!
+ Returns the codec actually used for this stream.
+ \since 4.2
+
+ If Unicode is automatically detected in input, a codec with \link
+ QTextCodec::name() name() \endlink "ISO-10646-UCS-2" is returned.
+
+ \sa setCodec()
+*/
+
+QTextCodec *Q3TextStream::codec()
+{
+ if ( mapper ) {
+ return mapper;
+ } else {
+ // 4 is "ISO 8859-1", 1000 is "ISO-10646-UCS-2"
+ return QTextCodec::codecForMib( latin1 ? 4 : 1000 );
+ }
+}
+
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_TEXTSTREAM
diff --git a/src/qt3support/text/q3textstream.h b/src/qt3support/text/q3textstream.h
new file mode 100644
index 0000000..1c6e6d2
--- /dev/null
+++ b/src/qt3support/text/q3textstream.h
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTSTREAM_H
+#define Q3TEXTSTREAM_H
+
+#include <QtCore/qiodevice.h>
+#include <QtCore/qstring.h>
+#ifndef QT_NO_TEXTCODEC
+#include <QtCore/qtextcodec.h>
+#endif
+#include <Qt3Support/q3cstring.h>
+
+#include <stdio.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+class Q3TextStreamPrivate;
+
+class Q_COMPAT_EXPORT Q3TextStream // text stream class
+{
+public:
+ enum Encoding { Locale, Latin1, Unicode, UnicodeNetworkOrder,
+ UnicodeReverse, RawUnicode, UnicodeUTF8 };
+
+ void setEncoding( Encoding );
+#ifndef QT_NO_TEXTCODEC
+ void setCodec( QTextCodec* );
+ QTextCodec *codec();
+#endif
+
+ Q3TextStream();
+ Q3TextStream( QIODevice * );
+ Q3TextStream( QString*, int mode );
+ Q3TextStream( QString&, int mode ); // obsolete
+ Q3TextStream( QByteArray&, int mode );
+ Q3TextStream( FILE *, int mode );
+ virtual ~Q3TextStream();
+
+ QIODevice *device() const;
+ void setDevice( QIODevice * );
+ void unsetDevice();
+
+ bool atEnd() const;
+ bool eof() const;
+
+ Q3TextStream &operator>>( QChar & );
+ Q3TextStream &operator>>( char & );
+ Q3TextStream &operator>>( signed short & );
+ Q3TextStream &operator>>( unsigned short & );
+ Q3TextStream &operator>>( signed int & );
+ Q3TextStream &operator>>( unsigned int & );
+ Q3TextStream &operator>>( signed long & );
+ Q3TextStream &operator>>( unsigned long & );
+ Q3TextStream &operator>>( float & );
+ Q3TextStream &operator>>( double & );
+ Q3TextStream &operator>>( char * );
+ Q3TextStream &operator>>( QString & );
+ Q3TextStream &operator>>( Q3CString & );
+
+ Q3TextStream &operator<<( QChar );
+ Q3TextStream &operator<<( char );
+ Q3TextStream &operator<<( signed short );
+ Q3TextStream &operator<<( unsigned short );
+ Q3TextStream &operator<<( signed int );
+ Q3TextStream &operator<<( unsigned int );
+ Q3TextStream &operator<<( signed long );
+ Q3TextStream &operator<<( unsigned long );
+ Q3TextStream &operator<<( float );
+ Q3TextStream &operator<<( double );
+ Q3TextStream &operator<<( const char* );
+ Q3TextStream &operator<<( const QString & );
+ Q3TextStream &operator<<( const Q3CString & );
+ Q3TextStream &operator<<( void * ); // any pointer
+
+ Q3TextStream &readRawBytes( char *, uint len );
+ Q3TextStream &writeRawBytes( const char* , uint len );
+
+ QString readLine();
+ QString read();
+ void skipWhiteSpace();
+
+ enum {
+ skipws = 0x0001, // skip whitespace on input
+ left = 0x0002, // left-adjust output
+ right = 0x0004, // right-adjust output
+ internal = 0x0008, // pad after sign
+ bin = 0x0010, // binary format integer
+ oct = 0x0020, // octal format integer
+ dec = 0x0040, // decimal format integer
+ hex = 0x0080, // hex format integer
+ showbase = 0x0100, // show base indicator
+ showpoint = 0x0200, // force decimal point (float)
+ uppercase = 0x0400, // upper-case hex output
+ showpos = 0x0800, // add '+' to positive integers
+ scientific= 0x1000, // scientific float output
+ fixed = 0x2000 // fixed float output
+ };
+
+ static const int basefield; // bin | oct | dec | hex
+ static const int adjustfield; // left | right | internal
+ static const int floatfield; // scientific | fixed
+
+ int flags() const;
+ int flags( int f );
+ int setf( int bits );
+ int setf( int bits, int mask );
+ int unsetf( int bits );
+
+ void reset();
+
+ int width() const;
+ int width( int );
+ int fill() const;
+ int fill( int );
+ int precision() const;
+ int precision( int );
+
+private:
+ long input_int();
+ void init();
+ Q3TextStream &output_int( int, ulong, bool );
+ QIODevice *dev;
+
+ int fflags;
+ int fwidth;
+ int fillchar;
+ int fprec;
+ bool doUnicodeHeader;
+ bool owndev;
+ QTextCodec *mapper;
+ QTextCodec::ConverterState mapperReadState;
+ QTextCodec::ConverterState mapperWriteState;
+ Q3TextStreamPrivate * d;
+ QChar unused1; // ### remove in Qt 4.0
+ bool latin1;
+ bool internalOrder;
+ bool networkOrder;
+ void *unused2; // ### remove in Qt 4.0
+
+ QChar eat_ws();
+ uint ts_getline( QChar* );
+ void ts_ungetc( QChar );
+ QChar ts_getc();
+ uint ts_getbuf( QChar*, uint );
+ void ts_putc(int);
+ void ts_putc(QChar);
+ bool ts_isspace(QChar);
+ bool ts_isdigit(QChar);
+ ulong input_bin();
+ ulong input_oct();
+ ulong input_dec();
+ ulong input_hex();
+ double input_double();
+ Q3TextStream &writeBlock( const char* p, uint len );
+ Q3TextStream &writeBlock( const QChar* p, uint len );
+
+private: // Disabled copy constructor and operator=
+#if defined(Q_DISABLE_COPY)
+ Q3TextStream( const Q3TextStream & );
+ Q3TextStream &operator=( const Q3TextStream & );
+#endif
+};
+
+/*****************************************************************************
+ Q3TextStream inline functions
+ *****************************************************************************/
+
+inline QIODevice *Q3TextStream::device() const
+{ return dev; }
+
+inline bool Q3TextStream::atEnd() const
+{ return dev ? dev->atEnd() : FALSE; }
+
+inline bool Q3TextStream::eof() const
+{ return atEnd(); }
+
+inline int Q3TextStream::flags() const
+{ return fflags; }
+
+inline int Q3TextStream::flags( int f )
+{ int oldf = fflags; fflags = f; return oldf; }
+
+inline int Q3TextStream::setf( int bits )
+{ int oldf = fflags; fflags |= bits; return oldf; }
+
+inline int Q3TextStream::setf( int bits, int mask )
+{ int oldf = fflags; fflags = (fflags & ~mask) | (bits & mask); return oldf; }
+
+inline int Q3TextStream::unsetf( int bits )
+{ int oldf = fflags; fflags &= ~bits; return oldf; }
+
+inline int Q3TextStream::width() const
+{ return fwidth; }
+
+inline int Q3TextStream::width( int w )
+{ int oldw = fwidth; fwidth = w; return oldw; }
+
+inline int Q3TextStream::fill() const
+{ return fillchar; }
+
+inline int Q3TextStream::fill( int f )
+{ int oldc = fillchar; fillchar = f; return oldc; }
+
+inline int Q3TextStream::precision() const
+{ return fprec; }
+
+inline int Q3TextStream::precision( int p )
+{ int oldp = fprec; fprec = p; return oldp; }
+
+/*!
+ Returns one character from the stream, or EOF.
+*/
+inline QChar Q3TextStream::ts_getc()
+{ QChar r; return ( ts_getbuf( &r,1 ) == 1 ? r : QChar((ushort)0xffff) ); }
+
+/*****************************************************************************
+ Q3TextStream manipulators
+ *****************************************************************************/
+
+typedef Q3TextStream & (*Q3TSFUNC)(Q3TextStream &);// manipulator function
+typedef int (Q3TextStream::*Q3TSMFI)(int); // manipulator w/int argument
+
+class Q_COMPAT_EXPORT Q3TSManip { // text stream manipulator
+public:
+ Q3TSManip( Q3TSMFI m, int a ) { mf=m; arg=a; }
+ void exec( Q3TextStream &s ) { (s.*mf)(arg); }
+private:
+ Q3TSMFI mf; // Q3TextStream member function
+ int arg; // member function argument
+};
+
+Q_COMPAT_EXPORT inline Q3TextStream &operator>>( Q3TextStream &s, Q3TSFUNC f )
+{ return (*f)( s ); }
+
+Q_COMPAT_EXPORT inline Q3TextStream &operator<<( Q3TextStream &s, Q3TSFUNC f )
+{ return (*f)( s ); }
+
+Q_COMPAT_EXPORT inline Q3TextStream &operator<<( Q3TextStream &s, Q3TSManip m )
+{ m.exec(s); return s; }
+
+Q_COMPAT_EXPORT Q3TextStream &bin( Q3TextStream &s ); // set bin notation
+Q_COMPAT_EXPORT Q3TextStream &oct( Q3TextStream &s ); // set oct notation
+Q_COMPAT_EXPORT Q3TextStream &dec( Q3TextStream &s ); // set dec notation
+Q_COMPAT_EXPORT Q3TextStream &hex( Q3TextStream &s ); // set hex notation
+Q_COMPAT_EXPORT Q3TextStream &endl( Q3TextStream &s ); // insert EOL ('\n')
+Q_COMPAT_EXPORT Q3TextStream &flush( Q3TextStream &s ); // flush output
+Q_COMPAT_EXPORT Q3TextStream &ws( Q3TextStream &s ); // eat whitespace on input
+Q_COMPAT_EXPORT Q3TextStream &reset( Q3TextStream &s ); // set default flags
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTSTREAM_H
diff --git a/src/qt3support/text/q3textview.cpp b/src/qt3support/text/q3textview.cpp
new file mode 100644
index 0000000..2625e8e
--- /dev/null
+++ b/src/qt3support/text/q3textview.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3textview.h"
+
+#ifndef QT_NO_TEXTVIEW
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class Q3TextView
+ \brief The Q3TextView class provides a rich text viewer.
+
+ \compat
+
+ This class wraps a read-only \l Q3TextEdit.
+ Use a \l Q3TextEdit instead, and call setReadOnly(true)
+ to disable editing.
+*/
+
+/*! \internal */
+
+Q3TextView::Q3TextView(const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : Q3TextEdit(text, context, parent, name)
+{
+ setReadOnly(true);
+}
+
+/*! \internal */
+
+Q3TextView::Q3TextView(QWidget *parent, const char *name)
+ : Q3TextEdit(parent, name)
+{
+ setReadOnly(true);
+}
+
+/*! \internal */
+
+Q3TextView::~Q3TextView()
+{
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qt3support/text/q3textview.h b/src/qt3support/text/q3textview.h
new file mode 100644
index 0000000..2846b09
--- /dev/null
+++ b/src/qt3support/text/q3textview.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Qt3Support module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3TEXTVIEW_H
+#define Q3TEXTVIEW_H
+
+#include <Qt3Support/q3textedit.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Qt3SupportLight)
+
+#ifndef QT_NO_TEXTVIEW
+
+class Q_COMPAT_EXPORT Q3TextView : public Q3TextEdit
+{
+ Q_OBJECT
+
+public:
+ Q3TextView(const QString& text, const QString& context = QString(),
+ QWidget* parent=0, const char* name=0);
+ Q3TextView(QWidget* parent=0, const char* name=0);
+
+ virtual ~Q3TextView();
+
+private:
+ Q_DISABLE_COPY(Q3TextView)
+};
+
+#endif // QT_NO_TEXTVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // Q3TEXTVIEW_H
diff --git a/src/qt3support/text/text.pri b/src/qt3support/text/text.pri
new file mode 100644
index 0000000..0e74761
--- /dev/null
+++ b/src/qt3support/text/text.pri
@@ -0,0 +1,25 @@
+HEADERS += \
+ text/q3syntaxhighlighter.h \
+ text/q3syntaxhighlighter_p.h \
+ text/q3textview.h \
+ text/q3textbrowser.h \
+ text/q3textedit.h \
+ text/q3multilineedit.h \
+ text/q3richtext_p.h \
+ text/q3simplerichtext.h \
+ text/q3stylesheet.h \
+ text/q3textstream.h
+
+SOURCES += \
+ text/q3syntaxhighlighter.cpp \
+ text/q3textview.cpp \
+ text/q3textbrowser.cpp \
+ text/q3textedit.cpp \
+ text/q3multilineedit.cpp \
+ text/q3richtext.cpp \
+ text/q3richtext_p.cpp \
+ text/q3simplerichtext.cpp \
+ text/q3stylesheet.cpp \
+ text/q3textstream.cpp
+
+INCLUDEPATH += ../3rdparty/harfbuzz/src