summaryrefslogtreecommitdiffstats
path: root/src/widgets/qtextedit.cpp
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-07-10 15:24:15 -0500
commitbd0f3345a938b35ce6a12f6150373b0955b8dd12 (patch)
tree7a520322212d48ebcb9fbe1087e7fca28b76185c /src/widgets/qtextedit.cpp
downloadqt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.tar.gz
qt3-bd0f3345a938b35ce6a12f6150373b0955b8dd12.zip
Add Qt3 development HEAD version
Diffstat (limited to 'src/widgets/qtextedit.cpp')
-rw-r--r--src/widgets/qtextedit.cpp7467
1 files changed, 7467 insertions, 0 deletions
diff --git a/src/widgets/qtextedit.cpp b/src/widgets/qtextedit.cpp
new file mode 100644
index 0000000..0908e84
--- /dev/null
+++ b/src/widgets/qtextedit.cpp
@@ -0,0 +1,7467 @@
+/****************************************************************************
+**
+** Implementation of the QTextEdit class
+**
+** Created : 990101
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at sales@trolltech.com.
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qtextedit.h"
+
+#ifndef QT_NO_TEXTEDIT
+
+// Keep this position to avoid patch rejection
+#ifndef QT_NO_IM
+#include "qinputcontext.h"
+#endif
+
+#include "../kernel/qrichtext_p.h"
+#include "qpainter.h"
+#include "qpen.h"
+#include "qbrush.h"
+#include "qpixmap.h"
+#include "qfont.h"
+#include "qcolor.h"
+#include "qstyle.h"
+#include "qsize.h"
+#include "qevent.h"
+#include "qtimer.h"
+#include "qapplication.h"
+#include "qlistbox.h"
+#include "qvbox.h"
+#include "qapplication.h"
+#include "qclipboard.h"
+#include "qcolordialog.h"
+#include "qfontdialog.h"
+#include "qstylesheet.h"
+#include "qdragobject.h"
+#include "qurl.h"
+#include "qcursor.h"
+#include "qregexp.h"
+#include "qpopupmenu.h"
+#include "qptrstack.h"
+#include "qmetaobject.h"
+#include "qtextbrowser.h"
+#include <private/qucom_p.h>
+#include "private/qsyntaxhighlighter_p.h"
+#include <qguardedptr.h>
+
+#ifndef QT_NO_ACCEL
+#include <qkeysequence.h>
+#define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k ))
+#else
+#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
+#endif
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+#define LOGOFFSET(i) d->logOffset + i
+#endif
+
+struct QUndoRedoInfoPrivate
+{
+ QTextString text;
+};
+
+class QTextEditPrivate
+{
+public:
+ QTextEditPrivate()
+ :preeditStart(-1),preeditLength(-1),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)QTextEdit::AutoAll )
+ {
+ for ( int i=0; i<7; i++ )
+ id[i] = 0;
+ }
+ int id[ 7 ];
+ int preeditStart;
+ int preeditLength;
+ bool composeMode() const { return ( preeditLength > 0 ); }
+
+ 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
+ QTextEditOptimPrivate * od;
+ bool optimMode : 1;
+ int maxLogLines;
+ int logOffset;
+#endif
+ uint autoFormatting;
+};
+
+#ifndef QT_NO_MIME
+class QRichTextDrag : public QTextDrag
+{
+public:
+ QRichTextDrag( 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 QCString &mimetype,
+ const QCString &subtype );
+ static bool canDecode( QMimeSource* e );
+
+private:
+ QString richTxt;
+
+};
+
+QRichTextDrag::QRichTextDrag( QWidget *dragSource, const char *name )
+ : QTextDrag( dragSource, name )
+{
+}
+
+QByteArray QRichTextDrag::encodedData( const char *mime ) const
+{
+ if ( qstrcmp( "application/x-qrichtext", mime ) == 0 ) {
+ return richTxt.utf8(); // #### perhaps we should use USC2 instead?
+ } else
+ return QTextDrag::encodedData( mime );
+}
+
+bool QRichTextDrag::decode( QMimeSource *e, QString &str, const QCString &mimetype,
+ const QCString &subtype )
+{
+ if ( mimetype == "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
+ QCString subt = subtype;
+ return QTextDrag::decode( e, str, subt );
+}
+
+bool QRichTextDrag::canDecode( QMimeSource* e )
+{
+ if ( e->provides( "application/x-qrichtext" ) )
+ return TRUE;
+ return QTextDrag::canDecode( e );
+}
+
+const char* QRichTextDrag::format( int i ) const
+{
+ if ( QTextDrag::format( i ) )
+ return QTextDrag::format( i );
+ if ( QTextDrag::format( i-1 ) )
+ return "application/x-qrichtext";
+ return 0;
+}
+
+#endif
+
+static bool block_set_alignment = FALSE;
+
+/*!
+ \class QTextEdit qtextedit.h
+ \brief The QTextEdit widget provides a powerful single-page rich text editor.
+
+ \ingroup basic
+ \ingroup text
+ \mainclass
+
+ \tableofcontents
+
+ \section1 Introduction and Concepts
+
+ QTextEdit is an advanced WYSIWYG viewer/editor supporting rich
+ text formatting using HTML-style tags. It is optimized to handle
+ large documents and to respond quickly to user input.
+
+ QTextEdit has four modes of operation:
+ \table
+ \header \i Mode \i Command \i Notes
+ \row \i Plain Text Editor \i setTextFormat(PlainText)
+ \i Set text with setText(); text() returns plain text. Text
+ attributes (e.g. colors) can be set, but plain text is always
+ returned.
+ \row \i Rich Text Editor \i setTextFormat(RichText)
+ \i Set text with setText(); text() returns rich text. Rich
+ text editing is fairly limited. You can't set margins or
+ insert images for example (although you can read and
+ correctly display files that have margins set and that
+ include images). This mode is mostly useful for editing small
+ amounts of rich text. <sup>1.</sup>
+ \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(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 LogText mode\endlink for details.)
+ \endtable
+
+ <sup>1.</sup><small>A more complete API that supports setting
+ margins, images, etc., is planned for a later Qt release.</small>
+
+ QTextEdit 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 \c AutoText then setText() and
+ append() will try to determine whether the text they are given is
+ plain text or rich text. If you use \c RichText then setText() and
+ append() will assume that the text they are given is rich text.
+ insert() simply inserts the text it is given.
+
+ QTextEdit works on paragraphs and characters. A paragraph is a
+ formatted string which is word-wrapped to fit into the width of
+ the widget. 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
+
+ QTextEdit can display images (using QMimeSourceFactory), lists and
+ tables. If the text is too large to view within the text edit's
+ viewport, scrollbars will appear. The text edit can load both
+ plain text and HTML files (a subset of HTML 3.2 and 4). The
+ rendering style and the set of valid tags are defined by a
+ styleSheet(). Custom tags can be created and placed in a custom
+ style sheet. Change the style sheet with \l{setStyleSheet()}; see
+ QStyleSheet for details. The images identified by image tags are
+ displayed if they can be interpreted using the text edit's
+ \l{QMimeSourceFactory}; see setMimeSourceFactory().
+
+ If you want a text browser with more navigation use QTextBrowser.
+ If you just need to display a small piece of rich text use QLabel
+ or QSimpleRichText.
+
+ If you create a new QTextEdit, and want to allow the user to edit
+ rich text, call setTextFormat(Qt::RichText) to ensure that the
+ text is treated as rich text. (Rich text uses HTML tags to set
+ text formatting attributes. See QStyleSheet for information on the
+ HTML tags that are supported.). If you don't call setTextFormat()
+ explicitly the text edit will guess from the text itself whether
+ it is rich text or plain text. This means that if the text looks
+ like HTML or XML it will probably be interpreted as rich text, so
+ you should call setTextFormat(Qt::PlainText) to preserve such
+ text.
+
+ Note that we do not intend to add a full-featured web browser
+ widget to Qt (because that would easily double Qt's size and only
+ a few applications would benefit from it). The rich
+ text support in Qt is designed to provide a fast, portable and
+ efficient way to add reasonable online help facilities to
+ applications, and to provide a basis for rich text editors.
+
+ \section1 Using QTextEdit as a Display Widget
+
+ QTextEdit can display a large HTML subset, including tables and
+ images.
+
+ The text is set or replaced using setText() which deletes any
+ existing text and replaces it with the text passed in the
+ setText() call. If you call setText() with legacy HTML (with
+ setTextFormat(RichText) in force), and then call text(), the text
+ that is returned may have different markup, but will render the
+ same. Text can be inserted with insert(), paste(), pasteSubType()
+ and append(). Text that is appended does not go into the undo
+ history; this makes append() faster and consumes less memory. Text
+ can also be cut(). The entire text is deleted with clear() and the
+ selected text is deleted with removeSelectedText(). Selected
+ (marked) text can also be deleted with del() (which will delete
+ the character to the right of the cursor if no text is selected).
+
+ Loading and saving text is achieved using setText() and text(),
+ for example:
+ \code
+ QFile file( fileName ); // Read the text from a file
+ if ( file.open( IO_ReadOnly ) ) {
+ QTextStream stream( &file );
+ textEdit->setText( stream.read() );
+ }
+
+ QFile file( fileName ); // Write the text to a file
+ if ( file.open( IO_WriteOnly ) ) {
+ QTextStream stream( &file );
+ stream << textEdit->text();
+ textEdit->setModified( FALSE );
+ }
+ \endcode
+
+ By default the text edit wraps words at whitespace to fit within
+ the text edit widget. The setWordWrap() function is used to
+ specify the kind of word wrap you want, or \c NoWrap if you don't
+ want any wrapping. Call setWordWrap() to set a fixed pixel width
+ \c FixedPixelWidth, or character column (e.g. 80 column) \c
+ FixedColumnWidth with the pixels or columns specified with
+ setWrapColumnOrWidth(). If you use word wrap to the widget's width
+ \c WidgetWidth, you can specify whether to break on whitespace or
+ anywhere with setWrapPolicy().
+
+ The background color is set differently than other widgets, using
+ setPaper(). You specify a brush style which could be a plain color
+ or a complex pixmap.
+
+ Hypertext links are automatically underlined; this can be changed
+ with setLinkUnderline(). The tab stop width is set with
+ setTabStopWidth().
+
+ The zoomIn() and zoomOut() functions can be used to resize the
+ text by increasing (decreasing for zoomOut()) the point size used.
+ Images are not affected by the zoom functions.
+
+ The lines() function returns the number of lines in the text and
+ paragraphs() returns the number of paragraphs. The number of lines
+ within a particular paragraph is returned by linesOfParagraph().
+ The length of the entire text in characters is returned by
+ length().
+
+ You can scroll to an anchor in the text, e.g.
+ \c{<a name="anchor">} with scrollToAnchor(). The find() function
+ can be used to find and select a given string within the text.
+
+ A read-only QTextEdit provides the same functionality as the
+ (obsolete) QTextView. (QTextView is still supplied for
+ compatibility with old code.)
+
+ \section2 Read-only key bindings
+
+ When QTextEdit is used read-only the key-bindings are limited to
+ navigation, and text may only be selected with the mouse:
+ \table
+ \header \i Keypresses \i Action
+ \row \i UpArrow \i Move one line up
+ \row \i DownArrow \i Move one line down
+ \row \i LeftArrow \i Move one character left
+ \row \i RightArrow \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 QMimeSourceFactory uses to resolve
+ the locations of files and images. It is passed to the
+ mimeSourceFactory() when quering data. (See QTextEdit() and
+ \l{context()}.)
+
+ \target logtextmode
+ \section2 Using QTextEdit in LogText Mode
+
+ Setting the text format to \c LogText puts the widget in a special
+ mode which is optimized for very large texts. Editing, word wrap,
+ and rich text support are disabled in this mode (the widget is
+ explicitly made read-only). 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:
+ \code
+ This is <font color=red>red</font> while <b>this</b> is <font color=blue>blue</font>.
+ <font color=green><font color=yellow>Yellow,</font> and <u>green</u>.
+ \endcode
+
+ Stylesheets can also be used in LogText mode. To create and use a
+ custom tag, you could do the following:
+ \code
+ QTextEdit * log = new QTextEdit( this );
+ log->setTextFormat( Qt::LogText );
+ QStyleSheetItem * item = new QStyleSheetItem( log->styleSheet(), "mytag" );
+ item->setColor( "red" );
+ item->setFontWeight( QFont::Bold );
+ item->setFontUnderline( TRUE );
+ log->append( "This is a <mytag>custom tag</mytag>!" );
+ \endcode
+ Note that only the color, bold, underline and italic attributes of
+ a QStyleSheetItem is used in LogText mode.
+
+ Note that you can use setMaxLogLines() to limit the number of
+ lines the widget can hold in 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 QTextEdit as an Editor
+
+ All the information about using QTextEdit as a display widget also
+ applies here.
+
+ The current format's attributes are set with setItalic(),
+ setBold(), setUnderline(), setFamily() (font family),
+ setPointSize(), setColor() and setCurrentFont(). The current
+ paragraph's alignment is set with setAlignment().
+
+ Use setSelection() to select text. The setSelectionAttributes()
+ function is used to indicate how selected text should be
+ displayed. Use hasSelectedText() to find out if any text is
+ selected. The currently selected text's position is available
+ using getSelection() and the selected text itself is returned by
+ selectedText(). The selection can be copied to the clipboard with
+ copy(), or cut to the clipboard with cut(). It can be deleted with
+ removeSelectedText(). The entire text can be selected (or
+ unselected) using selectAll(). QTextEdit supports multiple
+ selections. Most of the selection functions operate on the default
+ selection, selection 0. If the user presses a non-selecting key,
+ e.g. a cursor key without also holding down Shift, all selections
+ are cleared.
+
+ Set and get the position of the cursor with setCursorPosition()
+ and getCursorPosition() respectively. When the cursor is moved,
+ the signals currentFontChanged(), currentColorChanged() and
+ currentAlignmentChanged() are emitted to reflect the font, color
+ and alignment at the new cursor position.
+
+ If the text changes, the textChanged() signal is emitted, and if
+ the user inserts a new line by pressing Return or Enter,
+ returnPressed() is emitted. The isModified() function will return
+ TRUE if the text has been modified.
+
+ QTextEdit provides command-based undo and redo. To set the depth
+ of the command history use setUndoDepth() which defaults to 100
+ steps. To undo or redo the last operation call undo() or redo().
+ The signals undoAvailable() and redoAvailable() indicate whether
+ the undo and redo operations can be executed.
+
+ The indent() function is used to reindent a paragraph. It is
+ useful for code editors, for example in \link designer-manual.book
+ Qt Designer\endlink's code editor \e{Ctrl+I} invokes the indent()
+ function.
+
+ \section2 Editing key bindings
+
+ The list of key-bindings which are implemented for editing:
+ \table
+ \header \i Keypresses \i Action
+ \row \i 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 LeftArrow \i Move the cursor one character left
+ \row \i Ctrl+LeftArrow \i Move the cursor one word left
+ \row \i RightArrow \i Move the cursor one character right
+ \row \i Ctrl+RightArrow \i Move the cursor one word right
+ \row \i UpArrow \i Move the cursor one line up
+ \row \i Ctrl+UpArrow \i Move the cursor one word up
+ \row \i DownArrow \i Move the cursor one line down
+ \row \i Ctrl+Down Arrow \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 Arrow}
+ will select the character to the right, and \e{Shift+Ctrl+Right
+ Arrow} 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 QTextEdit::AutoFormatting
+
+ \value AutoNone Do not perform any automatic formatting
+ \value AutoBulletList Only automatically format bulletted lists
+ \value AutoAll Apply all available autoformatting
+*/
+
+
+/*!
+ \enum QTextEdit::KeyboardAction
+
+ This enum is used by doKeyboardAction() to specify which action
+ should be executed:
+
+ \value ActionBackspace Delete the character to the left of the
+ cursor.
+
+ \value ActionDelete Delete the character to the right of the
+ cursor.
+
+ \value ActionReturn Split the paragraph at the cursor position.
+
+ \value ActionKill If the cursor is not at the end of the
+ paragraph, delete the text from the cursor position until the end
+ of the paragraph. If the cursor is at the end of the paragraph,
+ delete the hard line break at the end of the paragraph: this will
+ cause this paragraph to be joined with the following paragraph.
+
+ \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 QTextEdit::VerticalAlignment
+
+ This enum is used to set the vertical alignment of the text.
+
+ \value AlignNormal Normal alignment
+ \value AlignSuperScript Superscript
+ \value AlignSubScript Subscript
+*/
+
+/*!
+ \enum QTextEdit::TextInsertionFlags
+
+ \internal
+
+ \value RedoIndentation
+ \value CheckNewLines
+ \value RemoveSelected
+*/
+
+
+/*!
+ \fn void QTextEdit::copyAvailable(bool yes)
+
+ This signal is emitted when text is selected or de-selected in the
+ text edit.
+
+ When text is selected this signal will be emitted with \a yes set
+ to TRUE. If no text has been selected or if the selected text is
+ de-selected this signal is emitted with \a yes set to FALSE.
+
+ If \a yes is TRUE then copy() can be used to copy the selection to
+ the clipboard. If \a yes is FALSE then copy() does nothing.
+
+ \sa selectionChanged()
+*/
+
+
+/*!
+ \fn void QTextEdit::textChanged()
+
+ This signal is emitted whenever the text in the text edit changes.
+
+ \sa setText() append()
+*/
+
+/*!
+ \fn void QTextEdit::selectionChanged()
+
+ This signal is emitted whenever the selection changes.
+
+ \sa setSelection() copyAvailable()
+*/
+
+/*! \fn QTextDocument *QTextEdit::document() const
+
+ \internal
+
+ This function returns the QTextDocument which is used by the text
+ edit.
+*/
+
+/*! \fn void QTextEdit::setDocument( QTextDocument *doc )
+
+ \internal
+
+ This function sets the QTextDocument which should be used by the text
+ edit to \a doc. This can be used, for example, if you want to
+ display a document using multiple views. You would create a
+ QTextDocument and set it to the text edits which should display it.
+ You would need to connect to the textChanged() and
+ selectionChanged() signals of all the text edits and update them all
+ accordingly (preferably with a slight delay for efficiency reasons).
+*/
+
+/*!
+ \enum QTextEdit::CursorAction
+
+ This enum is used by moveCursor() to specify in which direction
+ the cursor should be moved:
+
+ \value MoveBackward Moves the cursor one character backward
+
+ \value MoveWordBackward Moves the cursor one word backward
+
+ \value MoveForward Moves the cursor one character forward
+
+ \value MoveWordForward Moves the cursor one word forward
+
+ \value MoveUp Moves the cursor up one line
+
+ \value MoveDown Moves the cursor down one line
+
+ \value MoveLineStart Moves the cursor to the beginning of the line
+
+ \value MoveLineEnd Moves the cursor to the end of the line
+
+ \value MoveHome Moves the cursor to the beginning of the document
+
+ \value MoveEnd Moves the cursor to the end of the document
+
+ \value MovePgUp Moves the cursor one viewport page up
+
+ \value MovePgDown Moves the cursor one viewport page down
+*/
+
+/*!
+ \enum Qt::AnchorAttribute
+
+ An anchor has one or more of the following attributes:
+
+ \value AnchorName the name attribute of the anchor. This attribute is
+ used when scrolling to an anchor in the document.
+
+ \value AnchorHref the href attribute of the anchor. This attribute is
+ used when a link is clicked to determine what content to load.
+*/
+
+/*!
+ \property QTextEdit::overwriteMode
+ \brief the text edit's overwrite mode
+
+ If FALSE (the default) characters entered by the user are inserted
+ with any characters to the right being moved out of the way. If
+ TRUE, the editor is in overwrite mode, i.e. characters entered by
+ the user overwrite any characters to the right of the cursor
+ position.
+*/
+
+/*!
+ \fn void QTextEdit::setCurrentFont( const QFont &f )
+
+ Sets the font of the current format to \a f.
+
+ If the widget is in \c LogText mode this function will do
+ nothing. Use setFont() instead.
+
+ \sa currentFont() setPointSize() setFamily()
+*/
+
+/*!
+ \property QTextEdit::undoDepth
+ \brief the depth of the undo history
+
+ The maximum number of steps in the undo/redo history. The default
+ is 100.
+
+ \sa undo() redo()
+*/
+
+/*!
+ \fn void QTextEdit::undoAvailable( bool yes )
+
+ This signal is emitted when the availability of undo changes. If
+ \a yes is TRUE, then undo() will work until undoAvailable( FALSE )
+ is next emitted.
+
+ \sa undo() undoDepth()
+*/
+
+/*!
+ \fn void QTextEdit::modificationChanged( bool m )
+
+ This signal is emitted when the modification 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 QTextEdit::redoAvailable( bool yes )
+
+ This signal is emitted when the availability of redo changes. If
+ \a yes is TRUE, then redo() will work until redoAvailable( FALSE )
+ is next emitted.
+
+ \sa redo() undoDepth()
+*/
+
+/*!
+ \fn void QTextEdit::currentFontChanged( const QFont &f )
+
+ This signal is emitted if the font of the current format has
+ changed.
+
+ The new font is \a f.
+
+ \sa setCurrentFont()
+*/
+
+/*!
+ \fn void QTextEdit::currentColorChanged( const QColor &c )
+
+ This signal is emitted if the color of the current format has
+ changed.
+
+ The new color is \a c.
+
+ \sa setColor()
+*/
+
+/*!
+ \fn void QTextEdit::currentVerticalAlignmentChanged( VerticalAlignment a )
+
+ This signal is emitted if the vertical alignment of the current
+ format has changed.
+
+ The new vertical alignment is \a a.
+
+ \sa setVerticalAlignment()
+*/
+
+/*!
+ \fn void QTextEdit::currentAlignmentChanged( int a )
+
+ This signal is emitted if the alignment of the current paragraph
+ has changed.
+
+ The new alignment is \a a.
+
+ \sa setAlignment()
+*/
+
+/*!
+ \fn void QTextEdit::cursorPositionChanged( QTextCursor *c )
+
+ \internal
+*/
+
+/*!
+ \fn void QTextEdit::cursorPositionChanged( int para, int pos )
+
+ 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 QTextEdit::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 QTextEdit::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 QTextEdit::returnPressed()
+
+ This signal is emitted if the user pressed the Return or the Enter
+ key.
+*/
+
+/*!
+ \fn QTextCursor *QTextEdit::textCursor() const
+
+ Returns the text edit's text cursor.
+
+ \warning QTextCursor is not in the public API, but in special
+ circumstances you might wish to use it.
+*/
+
+/*!
+ Constructs an empty QTextEdit called \a name, with parent \a
+ parent.
+*/
+
+QTextEdit::QTextEdit( QWidget *parent, const char *name )
+ : QScrollView( parent, name, WStaticContents | WNoAutoErase ),
+ doc( new QTextDocument( 0 ) ), undoRedoInfo( doc )
+{
+ init();
+}
+
+/*!
+ Constructs a QTextEdit 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 QMimeSourceFactory
+ uses to resolve the locations of files and images. It is passed to
+ the mimeSourceFactory() when quering data.
+
+ For example if the text contains an image tag,
+ \c{<img src="image.png">}, and the context is "path/to/look/in", the
+ QMimeSourceFactory will try to load the image from
+ "path/to/look/in/image.png". If the tag was
+ \c{<img src="/image.png">}, the context will not be used (because
+ QMimeSourceFactory recognizes that we have used an absolute path)
+ and will try to load "/image.png". The context is applied in exactly
+ the same way to \e hrefs, for example,
+ \c{<a href="target.html">Target</a>}, would resolve to
+ "path/to/look/in/target.html".
+*/
+
+QTextEdit::QTextEdit( const QString& text, const QString& context,
+ QWidget *parent, const char *name)
+ : QScrollView( parent, name, WStaticContents | WNoAutoErase ),
+ doc( new QTextDocument( 0 ) ), undoRedoInfo( doc )
+{
+ init();
+ setText( text, context );
+}
+
+/*!
+ \reimp
+*/
+
+QTextEdit::~QTextEdit()
+{
+ delete undoRedoInfo.d;
+ undoRedoInfo.d = 0;
+ delete cursor;
+ delete doc;
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ delete d->od;
+#endif
+ delete d;
+}
+
+void QTextEdit::init()
+{
+ d = new QTextEditPrivate;
+ 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;
+ onLink = QString::null;
+ d->onName = QString::null;
+ overWrite = FALSE;
+ wrapMode = WidgetWidth;
+ wrapWidth = -1;
+ wPolicy = AtWhiteSpace;
+ inDnD = FALSE;
+ doc->setFormatter( new QTextFormatterBreakWords );
+ doc->formatCollection()->defaultFormat()->setFont( QScrollView::font() );
+ doc->formatCollection()->defaultFormat()->setColor( colorGroup().color( QColorGroup::Text ) );
+ currentFormat = doc->formatCollection()->defaultFormat();
+ currentAlignment = Qt::AlignAuto;
+
+ setBackgroundMode( PaletteBase );
+ viewport()->setBackgroundMode( PaletteBase );
+ viewport()->setAcceptDrops( TRUE );
+ resizeContents( 0, doc->lastParagraph() ?
+ ( doc->lastParagraph()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 );
+
+ setKeyCompression( TRUE );
+ viewport()->setMouseTracking( TRUE );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ cursor = new QTextCursor( doc );
+
+ formatTimer = new QTimer( this );
+ connect( formatTimer, SIGNAL( timeout() ),
+ this, SLOT( formatMore() ) );
+ lastFormatted = doc->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( WheelFocus );
+ setInputMethodEnabled( TRUE );
+ viewport()->installEventFilter( this );
+ connect( this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased()) );
+ connect( this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased()) );
+ installEventFilter( this );
+}
+
+void QTextEdit::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 = hasFocus() || viewport()->hasFocus();
+ if (( hasSelectedText() && !style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) ) ||
+ isReadOnly() || !cursorVisible || doc->hasSelection( QTextDocument::IMSelectionText ))
+ drawCur = FALSE;
+ QColorGroup g = colorGroup();
+ const QColorGroup::ColorRole backRole = QPalette::backgroundRoleFromMode(backgroundMode());
+ if ( doc->paper() )
+ g.setBrush( backRole, *doc->paper() );
+
+ if ( contentsY() < doc->y() ) {
+ p->fillRect( contentsX(), contentsY(), visibleWidth(), doc->y(),
+ g.brush( backRole ) );
+ }
+ if ( drawAll && doc->width() - contentsX() < cx + cw ) {
+ p->fillRect( doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
+ g.brush( backRole ) );
+ }
+
+ p->setBrushOrigin( -contentsX(), -contentsY() );
+
+ lastFormatted = doc->draw( p, cx, cy, cw, ch, g, !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(), g.brush( backRole ) );
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::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( foregroundColor() );
+ if ( document()->isPageBreakEnabled() && ( v = document()->flow()->pageSize() ) > 0 ) {
+ int l = int(cy / v) * v;
+ while ( l < cy + ch ) {
+ p->drawLine( cx, l, cx + cw - 1, l );
+ l += v;
+ }
+ }
+
+ // This invocation is required to follow dragging of active window
+ // by the showed candidate window.
+ updateMicroFocusHint();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::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, colorGroup().background() );
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool QTextEdit::event( QEvent *e )
+{
+ if ( e->type() == QEvent::AccelOverride && !isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ switch(ke->state()) {
+ case NoButton:
+ case Keypad:
+ case ShiftButton:
+ if ( ke->key() < Key_Escape ) {
+ ke->accept();
+ } else {
+ switch ( ke->key() ) {
+ case Key_Return:
+ case Key_Enter:
+ case Key_Delete:
+ case Key_Home:
+ case Key_End:
+ case Key_Backspace:
+ case Key_Left:
+ case Key_Right:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ break;
+
+ case ControlButton:
+ case ControlButton|ShiftButton:
+ case ControlButton|Keypad:
+ case ControlButton|ShiftButton|Keypad:
+ switch ( ke->key() ) {
+ case Key_Tab:
+ case Key_Backtab:
+ ke->ignore();
+ break;
+// Those are too frequently used for application functionality
+/* case Key_A:
+ case Key_B:
+ case Key_D:
+ case Key_E:
+ case Key_F:
+ case Key_H:
+ case Key_I:
+ case Key_K:
+ case Key_N:
+ case Key_P:
+ case Key_T:
+*/
+ case Key_C:
+ case Key_V:
+ case Key_X:
+ case Key_Y:
+ case Key_Z:
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+#if defined (Q_WS_WIN)
+ case Key_Insert:
+ case Key_Delete:
+#endif
+ ke->accept();
+ default:
+ break;
+ }
+ break;
+
+ default:
+ switch ( ke->key() ) {
+#if defined (Q_WS_WIN)
+ case 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 = QString::null;
+ }
+ }
+ return QWidget::event( e );
+}
+
+/*!
+ Processes the key event, \a e. By default key events are used to
+ provide keyboard navigation and text editing.
+*/
+
+void QTextEdit::keyPressEvent( QKeyEvent *e )
+{
+ changeIntervalTimer->stop();
+ interval = 10;
+ bool unknownKey = FALSE;
+ if ( isReadOnly() ) {
+ if ( !handleReadOnlyKeyEvent( e ) )
+ QScrollView::keyPressEvent( e );
+ changeIntervalTimer->start( 100, TRUE );
+ return;
+ }
+
+
+ bool selChanged = FALSE;
+ for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection
+ selChanged = doc->removeSelection( i ) || selChanged;
+
+ if ( selChanged ) {
+ cursor->paragraph()->document()->nextDoubleBuffered = TRUE;
+ repaintChanged();
+ }
+
+ bool clearUndoRedoInfo = TRUE;
+
+
+ switch ( e->key() ) {
+ case Key_Left:
+ case Key_Right: {
+ // a bit hacky, but can't change this without introducing new enum values for move and keeping the
+ // correct semantics and movement for BiDi and non BiDi text.
+ CursorAction a;
+ if ( cursor->paragraph()->string()->isRightToLeft() == (e->key() == Key_Right) )
+ a = e->state() & ControlButton ? MoveWordBackward : MoveBackward;
+ else
+ a = e->state() & ControlButton ? MoveWordForward : MoveForward;
+ moveCursor( a, e->state() & ShiftButton );
+ break;
+ }
+ case Key_Up:
+ moveCursor( e->state() & ControlButton ? MovePgUp : MoveUp, e->state() & ShiftButton );
+ break;
+ case Key_Down:
+ moveCursor( e->state() & ControlButton ? MovePgDown : MoveDown, e->state() & ShiftButton );
+ break;
+ case Key_Home:
+ moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton );
+ break;
+ case Key_End:
+ moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton );
+ break;
+ case Key_Prior:
+ moveCursor( MovePgUp, e->state() & ShiftButton );
+ break;
+ case Key_Next:
+ moveCursor( MovePgDown, e->state() & ShiftButton );
+ break;
+ case Key_Return: case Key_Enter:
+ if ( doc->hasSelection( QTextDocument::Standard, FALSE ) )
+ removeSelectedText();
+ if ( textFormat() == Qt::RichText && ( e->state() & ControlButton ) ) {
+ // Ctrl-Enter inserts a line break in rich text mode
+ insert( QString( QChar( 0x2028) ), TRUE, FALSE );
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ clearUndoRedoInfo = FALSE;
+ doKeyboardAction( ActionReturn );
+ emit returnPressed();
+ }
+ break;
+ case Key_Delete:
+#if defined (Q_WS_WIN)
+ if ( e->state() & ShiftButton ) {
+ cut();
+ break;
+ } else
+#endif
+ if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction( e->state() & ControlButton ? ActionWordDelete
+ : ActionDelete );
+ clearUndoRedoInfo = FALSE;
+
+ break;
+ case Key_Insert:
+ if ( e->state() & ShiftButton )
+ paste();
+#if defined (Q_WS_WIN)
+ else if ( e->state() & ControlButton )
+ copy();
+#endif
+ else
+ setOverwriteMode( !isOverwriteMode() );
+ break;
+ case Key_Backspace:
+#if defined (Q_WS_WIN)
+ if ( e->state() & AltButton ) {
+ if (e->state() & ControlButton ) {
+ break;
+ } else if ( e->state() & ShiftButton ) {
+ redo();
+ break;
+ } else {
+ undo();
+ break;
+ }
+ } else
+#endif
+ if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) {
+ removeSelectedText();
+ break;
+ }
+
+ doKeyboardAction( e->state() & ControlButton ? ActionWordBackspace
+ : ActionBackspace );
+ clearUndoRedoInfo = FALSE;
+ break;
+ case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Key_F18: // Paste key on Sun keyboards
+ paste();
+ break;
+ case Key_F20: // Cut key on Sun keyboards
+ cut();
+ break;
+ case Key_Direction_L:
+ if ( doc->textFormat() == Qt::PlainText ) {
+ // change the whole doc
+ QTextParagraph *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 Key_Direction_R:
+ if ( doc->textFormat() == Qt::PlainText ) {
+ // change the whole doc
+ QTextParagraph *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: {
+ if ( e->text().length() &&
+ ( !( e->state() & ControlButton ) &&
+#ifndef Q_OS_MACX
+ !( e->state() & AltButton ) &&
+#endif
+ !( e->state() & MetaButton ) ||
+ ( ( (e->state()&ControlButton) | AltButton ) == (ControlButton|AltButton) ) ) &&
+ ( !e->ascii() || e->ascii() >= 32 || e->text() == "\t" ) ) {
+ clearUndoRedoInfo = FALSE;
+ if ( e->key() == 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 = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid );
+ cursor->paragraph()->setListDepth( cursor->paragraph()->listDepth() +1 );
+ clearUndoRedo();
+ drawCursor( FALSE );
+ repaintChanged();
+ drawCursor( TRUE );
+ break;
+ }
+ } else if ( e->key() == Key_BackTab ) {
+ if ( d->tabChangesFocus ) {
+ e->ignore();
+ break;
+ }
+ }
+
+ if ( ( autoFormatting() & AutoBulletList ) &&
+ textFormat() == Qt::RichText && cursor->index() == 0
+ && !cursor->paragraph()->isListItem()
+ && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) {
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.eid = undoRedoInfo.id;
+ undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid );
+ setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc );
+ clearUndoRedo();
+ drawCursor( FALSE );
+ repaintChanged();
+ drawCursor( TRUE );
+ break;
+ }
+ if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(QTextDocument::Standard)) {
+ doKeyboardAction(ActionDelete);
+ clearUndoRedoInfo = FALSE;
+ }
+ QString t = e->text();
+#ifdef Q_WS_X11
+ extern bool qt_hebrew_keyboard_hack;
+ if ( qt_hebrew_keyboard_hack ) {
+ // the X11 keyboard layout is broken and does not reverse
+ // braces correctly. This is a hack to get halfway correct
+ // behaviour
+ QTextParagraph *p = cursor->paragraph();
+ if ( p && p->string() && p->string()->isRightToLeft() ) {
+ QChar *c = (QChar *)t.unicode();
+ int l = t.length();
+ while( l-- ) {
+ if ( c->mirrored() )
+ *c = c->mirroredChar();
+ c++;
+ }
+ }
+ }
+#endif
+ insert( t, TRUE, FALSE );
+ break;
+ } else if ( e->state() & ControlButton ) {
+ switch ( e->key() ) {
+ case Key_C: case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+ case Key_V:
+ paste();
+ break;
+ case Key_X:
+ cut();
+ break;
+ case Key_I: case Key_T: case Key_Tab:
+ if ( !d->tabChangesFocus )
+ indent();
+ break;
+ case Key_A:
+#if defined(Q_WS_X11)
+ moveCursor( MoveLineStart, e->state() & ShiftButton );
+#else
+ selectAll( TRUE );
+#endif
+ break;
+ case Key_B:
+ moveCursor( MoveBackward, e->state() & ShiftButton );
+ break;
+ case Key_F:
+ moveCursor( MoveForward, e->state() & ShiftButton );
+ break;
+ case Key_D:
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ break;
+ }
+ doKeyboardAction( ActionDelete );
+ clearUndoRedoInfo = FALSE;
+ break;
+ case Key_H:
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ break;
+ }
+ if ( !cursor->paragraph()->prev() &&
+ cursor->atParagStart() )
+ break;
+
+ doKeyboardAction( ActionBackspace );
+ clearUndoRedoInfo = FALSE;
+ break;
+ case Key_E:
+ moveCursor( MoveLineEnd, e->state() & ShiftButton );
+ break;
+ case Key_N:
+ moveCursor( MoveDown, e->state() & ShiftButton );
+ break;
+ case Key_P:
+ moveCursor( MoveUp, e->state() & ShiftButton );
+ break;
+ case Key_Z:
+ if(e->state() & ShiftButton)
+ redo();
+ else
+ undo();
+ break;
+ case Key_Y:
+ redo();
+ break;
+ case Key_K:
+ doKeyboardAction( ActionKill );
+ break;
+#if defined(Q_WS_WIN)
+ case Key_Insert:
+ copy();
+ break;
+ case Key_Delete:
+ del();
+ break;
+#endif
+ default:
+ 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();
+}
+
+/*!
+ This function is not intended as polymorphic usage. Just a shared code
+ fragment that calls QWidget::sendMouseEventToInputContext() easily for this
+ class.
+ */
+bool QTextEdit::sendMouseEventToInputContext( QMouseEvent *e )
+{
+#ifndef QT_NO_IM
+ if ( d->composeMode() ) {
+ QTextCursor c( doc );
+ if ( c.place( e->pos(), doc->firstParagraph(), FALSE, FALSE, FALSE ) ) {
+ int mousePos = c.index() - d->preeditStart;
+ if ( cursor->globalY() == c.globalY() &&
+ mousePos >= 0 && mousePos < d->preeditLength ) {
+ QWidget::sendMouseEventToInputContext( mousePos, e->type(),
+ e->button(), e->state() );
+ }
+ } else if ( e->type() != QEvent::MouseMove ) {
+ // send button events on out of preedit
+ QWidget::sendMouseEventToInputContext( -1, e->type(),
+ e->button(), e->state() );
+ }
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+
+/*!
+ \reimp
+*/
+void QTextEdit::imStartEvent( QIMEvent *e )
+{
+ if ( isReadOnly() ) {
+ e->ignore();
+ return;
+ }
+
+ if ( hasSelectedText() )
+ removeSelectedText();
+ d->preeditStart = cursor->index();
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::IME;
+}
+
+/*!
+ \reimp
+*/
+void QTextEdit::imComposeEvent( QIMEvent *e )
+{
+ if ( isReadOnly() ) {
+ e->ignore();
+ return;
+ }
+
+ doc->removeSelection( QTextDocument::IMCompositionText );
+ doc->removeSelection( QTextDocument::IMSelectionText );
+
+ if ( d->composeMode() && cursor->paragraph() )
+ cursor->paragraph()->remove( d->preeditStart, d->preeditLength );
+ cursor->setIndex( d->preeditStart );
+ d->preeditLength = e->text().length();
+
+ int sellen = e->selectionLength();
+ uint insertionFlags = CheckNewLines | RemoveSelected | AsIMCompositionText;
+ if ( sellen > 0 ) {
+ insertionFlags |= WithIMSelection;
+ }
+ insert( e->text(), insertionFlags );
+ // insert can trigger an imEnd event as it emits a textChanged signal, so better
+ // be careful
+ if(d->preeditStart != -1) {
+ cursor->setIndex( d->preeditStart + d->preeditLength );
+ QTextCursor c = *cursor;
+ cursor->setIndex( d->preeditStart );
+ doc->setSelectionStart( QTextDocument::IMCompositionText, *cursor );
+ doc->setSelectionEnd( QTextDocument::IMCompositionText, c );
+
+ cursor->setIndex( d->preeditStart + e->cursorPos() );
+
+ if ( sellen > 0 ) {
+ cursor->setIndex( d->preeditStart + e->cursorPos() + sellen );
+ c = *cursor;
+ cursor->setIndex( d->preeditStart + e->cursorPos() );
+ doc->setSelectionStart( QTextDocument::IMSelectionText, *cursor );
+ doc->setSelectionEnd( QTextDocument::IMSelectionText, c );
+#if 0
+ // Disabled for Asian input method that shows candidate
+ // window. This behavior is same as Qt/E 2.3.7 which supports
+ // Asian input methods. Asian input methods need start point
+ // of IM selection text to place candidate window as adjacent
+ // to the selection text.
+ cursor->setIndex( d->preeditStart + d->preeditLength );
+#endif
+ }
+ }
+
+ updateMicroFocusHint();
+ repaintChanged();
+}
+
+/*!
+ \reimp
+*/
+void QTextEdit::imEndEvent( QIMEvent *e )
+{
+ if ( isReadOnly() ) {
+ e->ignore();
+ return;
+ }
+
+ doc->removeSelection( QTextDocument::IMCompositionText );
+ doc->removeSelection( QTextDocument::IMSelectionText );
+
+ if (undoRedoInfo.type == UndoRedoInfo::IME)
+ undoRedoInfo.type = UndoRedoInfo::Invalid;
+
+ if ( d->composeMode() && cursor->paragraph() )
+ cursor->paragraph()->remove( d->preeditStart, d->preeditLength );
+ if ( d->preeditStart >= 0 ) {
+ cursor->setIndex( d->preeditStart );
+ //TODO: Qt 4 we should use the new virtual insert function
+ insert( e->text(), FALSE );
+ }
+ d->preeditStart = d->preeditLength = -1;
+
+ repaintChanged();
+}
+
+
+static bool qtextedit_ignore_readonly = FALSE;
+
+/*!
+ Executes keyboard action \a action. This is normally called by a
+ key event handler.
+*/
+
+void QTextEdit::doKeyboardAction( KeyboardAction action )
+{
+ if ( isReadOnly() && !qtextedit_ignore_readonly )
+ return;
+
+ if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough
+ 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 = QString::null;
+ }
+ 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( QTextDocument::Temp, *cursor );
+ if ( action == ActionWordDelete && !cursor->atParagEnd() ) {
+ cursor->gotoNextWord();
+ } else {
+ cursor->gotoNextLetter();
+ }
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+ removeSelectedText( QTextDocument::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 = QTextStyleCommand::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 = QString::null;
+ }
+ 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( QTextDocument::Temp, *cursor );
+ if ( action == ActionWordBackspace && !cursor->atParagStart() ) {
+ cursor->gotoPreviousWord();
+ } else {
+ cursor->gotoPreviousLetter();
+ }
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+ removeSelectedText( QTextDocument::Temp );
+ }
+ break;
+ case ActionReturn:
+ if ( undoEnabled ) {
+ checkUndoRedoInfo( UndoRedoInfo::Return );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = cursor->paragraph()->paragId();
+ undoRedoInfo.index = cursor->index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ undoRedoInfo.d->text += "\n";
+ }
+ cursor->splitAndInsertEmptyParagraph();
+ if ( cursor->paragraph()->prev() ) {
+ lastFormatted = cursor->paragraph()->prev();
+ lastFormatted->invalidate( 0 );
+ }
+ doUpdateCurrentFormat = FALSE;
+ break;
+ case ActionKill:
+ clearUndoRedo();
+ doc->setSelectionStart( QTextDocument::Temp, *cursor );
+ if ( cursor->atParagEnd() )
+ cursor->gotoNextLetter();
+ else
+ cursor->setIndex( cursor->paragraph()->length() - 1 );
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+ removeSelectedText( QTextDocument::Temp );
+ break;
+ }
+
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ updateMicroFocusHint();
+ if ( doUpdateCurrentFormat )
+ updateCurrentFormat();
+ setModified();
+ emit textChanged();
+}
+
+void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, QTextString &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 += "\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 ( QTextParagraph *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 QTextEdit::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 QTextEdit::removeSelectedText( int selNum )
+{
+ if(selNum != 0)
+ resetInputContext();
+
+ QTextCursor c1 = doc->selectionStartCursor( selNum );
+ c1.restoreState();
+ QTextCursor 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 = QString::null;
+ }
+ 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( FALSE );
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ clearUndoRedo();
+#if defined(Q_WS_WIN)
+ // there seems to be a problem with repainting or erasing the area
+ // of the scrollview which is not the contents on windows
+ if ( contentsHeight() < visibleHeight() )
+ viewport()->repaint( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), TRUE );
+#endif
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ updateMicroFocusHint();
+ } else {
+ delete cursor;
+ cursor = new QTextCursor( doc );
+ drawCursor( TRUE );
+ repaintContents( TRUE );
+ }
+ setModified();
+ emit textChanged();
+ emit selectionChanged();
+ emit copyAvailable( doc->hasSelection( QTextDocument::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 QTextEdit::moveCursor( CursorAction action, bool select )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return;
+#endif
+#ifdef Q_WS_MACX
+ QTextCursor c1 = *cursor;
+ QTextCursor c2;
+#endif
+ drawCursor( FALSE );
+ if ( select ) {
+ if ( !doc->hasSelection( QTextDocument::Standard ) )
+ doc->setSelectionStart( QTextDocument::Standard, *cursor );
+ moveCursor( action );
+#ifdef Q_WS_MACX
+ c2 = *cursor;
+ if (c1 == c2)
+ if (action == MoveDown || action == MovePgDown)
+ moveCursor( MoveEnd );
+ else if (action == MoveUp || action == MovePgUp)
+ moveCursor( MoveHome );
+#endif
+ if ( doc->setSelectionEnd( QTextDocument::Standard, *cursor ) ) {
+ cursor->paragraph()->document()->nextDoubleBuffered = TRUE;
+ repaintChanged();
+ } else {
+ drawCursor( TRUE );
+ }
+ ensureCursorVisible();
+ emit selectionChanged();
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ } else {
+#ifdef Q_WS_MACX
+ QTextCursor cStart = doc->selectionStartCursor( QTextDocument::Standard );
+ QTextCursor cEnd = doc->selectionEndCursor( QTextDocument::Standard );
+ bool redraw = doc->removeSelection( QTextDocument::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( QTextDocument::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() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ if ( redraw ) {
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+ }
+ }
+
+ drawCursor( TRUE );
+ updateCurrentFormat();
+ updateMicroFocusHint();
+}
+
+/*!
+ \overload
+*/
+
+void QTextEdit::moveCursor( 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;
+ }
+ updateMicroFocusHint();
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::resizeEvent( QResizeEvent *e )
+{
+ QScrollView::resizeEvent( e );
+ if ( doc->visibleWidth() == 0 )
+ doResize();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::viewportResizeEvent( QResizeEvent *e )
+{
+ QScrollView::viewportResizeEvent( e );
+ if ( e->oldSize().width() != e->size().width() ) {
+ 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 QTextEdit::ensureCursorVisible()
+{
+ // Not visible or the user is draging the window, so don't position to caret yet
+ if ( !isUpdatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed() ) {
+ d->ensureCursorVisibleInShowEvent = TRUE;
+ return;
+ }
+ sync();
+ QTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
+ int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
+ int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX();
+ int y = 0; int dummy;
+ cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
+ y += cursor->paragraph()->rect().y() + cursor->offsetY();
+ int w = 1;
+ ensureVisible( x, y + h / 2, w, h / 2 + 2 );
+}
+
+/*!
+ \internal
+*/
+void QTextEdit::sliderReleased()
+{
+ if ( d->ensureCursorVisibleInShowEvent && isVisible() ) {
+ d->ensureCursorVisibleInShowEvent = FALSE;
+ ensureCursorVisible();
+ }
+}
+
+/*!
+ \internal
+*/
+void QTextEdit::drawCursor( bool visible )
+{
+ if ( !isUpdatesEnabled() ||
+ !viewport()->isUpdatesEnabled() ||
+ !cursor->paragraph() ||
+ !cursor->paragraph()->isValid() ||
+ ( !style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) &&
+ ( d->optimMode ? optimHasSelection() : doc->hasSelection( QTextDocument::Standard, TRUE ))) ||
+ ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) ||
+ doc->hasSelection( QTextDocument::IMSelectionText ) ||
+ isReadOnly() )
+ return;
+
+ // Asian users regard selection text as cursor on candidate
+ // selection phase of input method, so ordinary cursor should be
+ // invisible if IM selection text exists.
+ if ( doc->hasSelection( QTextDocument::IMSelectionText ) ) {
+ visible = FALSE;
+ }
+
+ QPainter p( viewport() );
+ QRect r( cursor->topParagraph()->rect() );
+ cursor->paragraph()->setChanged( TRUE );
+ p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() );
+ QPixmap *pix = 0;
+ QColorGroup cg( colorGroup() );
+ const QColorGroup::ColorRole backRole = QPalette::backgroundRoleFromMode(backgroundMode());
+ if ( cursor->paragraph()->background() )
+ cg.setBrush( backRole, *cursor->paragraph()->background() );
+ else if ( doc->paper() )
+ cg.setBrush( backRole, *doc->paper() );
+ p.setBrushOrigin( -contentsX(), -contentsY() );
+ cursor->paragraph()->document()->nextDoubleBuffered = TRUE;
+ if ( !cursor->nestedDepth() ) {
+ int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
+ int dist = 5;
+ if ( ( cursor->paragraph()->alignment() & Qt::AlignJustify ) == Qt::AlignJustify )
+ dist = 50;
+ int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist;
+ x = QMAX( x, 0 );
+ p.setClipRect( QRect( x - contentsX(),
+ r.y() - cursor->totalOffsetY() + cursor->y() - contentsY(), 2 * dist, h ) );
+ doc->drawParagraph( &p, cursor->paragraph(), x,
+ r.y() - cursor->totalOffsetY() + cursor->y(), 2 * dist, h, pix, cg, visible, cursor );
+ } else {
+ doc->drawParagraph( &p, cursor->paragraph(), r.x() - cursor->totalOffsetX(),
+ r.y() - cursor->totalOffsetY(), r.width(), r.height(),
+ pix, cg, visible, cursor );
+ }
+ cursorVisible = visible;
+}
+
+enum {
+ IdUndo = 0,
+ IdRedo = 1,
+ IdCut = 2,
+ IdCopy = 3,
+ IdPaste = 4,
+ IdClear = 5,
+ IdSelectAll = 6
+};
+
+/*!
+ \reimp
+*/
+#ifndef QT_NO_WHEELEVENT
+void QTextEdit::contentsWheelEvent( QWheelEvent *e )
+{
+ if ( isReadOnly() ) {
+ if ( e->state() & ControlButton ) {
+ if ( e->delta() > 0 )
+ zoomOut();
+ else if ( e->delta() < 0 )
+ zoomIn();
+ return;
+ }
+ }
+ QScrollView::contentsWheelEvent( e );
+}
+#endif
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsMousePressEvent( QMouseEvent *e )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimMousePressEvent( e );
+ return;
+ }
+#endif
+
+ if ( sendMouseEventToInputContext( e ) )
+ return;
+
+ if ( d->trippleClickTimer->isActive() &&
+ ( e->globalPos() - d->trippleClickPoint ).manhattanLength() <
+ QApplication::startDragDistance() ) {
+ QTextCursor c1 = *cursor;
+ QTextCursor c2 = *cursor;
+ c1.gotoLineStart();
+ c2.gotoLineEnd();
+ doc->setSelectionStart( QTextDocument::Standard, c1 );
+ doc->setSelectionEnd( QTextDocument::Standard, c2 );
+ *cursor = c2;
+ repaintChanged();
+ mousePressed = TRUE;
+ return;
+ }
+
+ clearUndoRedo();
+ QTextCursor oldCursor = *cursor;
+ QTextCursor c = *cursor;
+ mousePos = e->pos();
+ mightStartDrag = FALSE;
+ pressedLink = QString::null;
+ d->pressedName = QString::null;
+
+ if ( e->button() == LeftButton ) {
+ mousePressed = TRUE;
+ drawCursor( FALSE );
+ placeCursor( e->pos() );
+ ensureCursorVisible();
+
+ if ( isReadOnly() && linksEnabled() ) {
+ QTextCursor c = *cursor;
+ placeCursor( e->pos(), &c, TRUE );
+ if ( c.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( QTextDocument::Standard, e->pos() ) ) {
+ mightStartDrag = TRUE;
+ drawCursor( TRUE );
+ dragStartTimer->start( QApplication::startDragTime(), TRUE );
+ dragStartPos = e->pos();
+ return;
+ }
+#endif
+
+ bool redraw = FALSE;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ if ( !( e->state() & ShiftButton ) ) {
+ redraw = doc->removeSelection( QTextDocument::Standard );
+ doc->setSelectionStart( QTextDocument::Standard, *cursor );
+ } else {
+ redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw;
+ }
+ } else {
+ if ( isReadOnly() || !( e->state() & ShiftButton ) ) {
+ doc->setSelectionStart( QTextDocument::Standard, *cursor );
+ } else {
+ doc->setSelectionStart( QTextDocument::Standard, c );
+ redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw;
+ }
+ }
+
+ for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection
+ redraw = doc->removeSelection( i ) || redraw;
+
+ if ( !redraw ) {
+ drawCursor( TRUE );
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ } else if ( e->button() == MidButton ) {
+ bool redraw = doc->removeSelection( QTextDocument::Standard );
+ if ( !redraw ) {
+ drawCursor( TRUE );
+ } else {
+ repaintChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ }
+
+ if ( *cursor != oldCursor )
+ updateCurrentFormat();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsMouseMoveEvent( QMouseEvent *e )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimMouseMoveEvent( e );
+ return;
+ }
+#endif
+ if ( sendMouseEventToInputContext( e ) ) {
+ // don't return from here to avoid cursor vanishing
+ } else if ( mousePressed ) {
+#ifndef QT_NO_DRAGANDDROP
+ if ( mightStartDrag ) {
+ dragStartTimer->stop();
+ if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() ) {
+ QGuardedPtr<QTextEdit> guard( this );
+ startDrag();
+ if (guard.isNull()) // we got deleted during the dnd
+ return;
+ }
+#ifndef QT_NO_CURSOR
+ if ( !isReadOnly() )
+ viewport()->setCursor( ibeamCursor );
+#endif
+ return;
+ }
+#endif
+ mousePos = e->pos();
+ handleMouseMove( mousePos );
+ oldMousePos = mousePos;
+ }
+
+#ifndef QT_NO_CURSOR
+ if ( !isReadOnly() && !mousePressed ) {
+ if ( doc->hasSelection( QTextDocument::Standard ) && doc->inSelection( QTextDocument::Standard, e->pos() ) )
+ viewport()->setCursor( arrowCursor );
+ else
+ viewport()->setCursor( ibeamCursor );
+ }
+#endif
+ updateCursor( e->pos() );
+}
+
+void QTextEdit::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 QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e )
+{
+ if ( !inDoubleClick && !d->composeMode() ) { // 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
+ if ( sendMouseEventToInputContext( e ) )
+ return;
+ QTextCursor oldCursor = *cursor;
+ if ( scrollTimer->isActive() )
+ scrollTimer->stop();
+#ifndef QT_NO_DRAGANDDROP
+ if ( dragStartTimer->isActive() )
+ dragStartTimer->stop();
+ if ( mightStartDrag ) {
+ selectAll( FALSE );
+ mousePressed = FALSE;
+ }
+#endif
+ bool mouseWasPressed = mousePressed;
+ if ( mousePressed ) {
+ mousePressed = FALSE;
+ copyToClipboard();
+ }
+#ifndef QT_NO_CLIPBOARD
+ else if ( e->button() == 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( QTextDocument::Standard, oldCursor );
+ bool redraw = FALSE;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ redraw = doc->removeSelection( QTextDocument::Standard );
+ doc->setSelectionStart( QTextDocument::Standard, *cursor );
+ } else {
+ doc->setSelectionStart( QTextDocument::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( 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() && mouseWasPressed ) {
+ if (!onLink.isEmpty()) {
+ QUrl u( doc->context(), onLink, TRUE );
+ emitLinkClicked( u.toString( FALSE, FALSE ) );
+ }
+ if (::qt_cast<QTextBrowser*>(this)) { // change for 4.0
+ QConnectionList *clist = receivers(
+ "anchorClicked(const QString&,const QString&)");
+ if (!signalsBlocked() && clist) {
+ QUObject o[3];
+ static_QUType_QString.set(o+1, d->onName);
+ static_QUType_QString.set(o+2, onLink);
+ activate_signal( clist, o);
+ }
+ }
+
+ // emitting linkClicked() may result in that the cursor winds
+ // up hovering over a different valid link - check this and
+ // set the appropriate cursor shape
+ updateCursor( e->pos() );
+ }
+#endif
+ drawCursor( TRUE );
+ if ( !doc->hasSelection( QTextDocument::Standard, TRUE ) )
+ doc->removeSelection( QTextDocument::Standard );
+
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * e )
+{
+ if ( e->button() != Qt::LeftButton && !d->composeMode() ) {
+ e->ignore();
+ return;
+ }
+ 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 ( (uint) i < str.length() && !str[ i ].isSpace() ) {
+ endIdx = ++i;
+ }
+ // ..and start of next
+ while ( (uint) i < str.length() && str[ i ].isSpace() ) {
+ endIdx = ++i;
+ }
+ optimSetSelection( para, startIdx, para, endIdx );
+ repaintContents( FALSE );
+ }
+ } else
+#endif
+ {
+ if ( sendMouseEventToInputContext( e ) )
+ return;
+
+ QTextCursor c1 = *cursor;
+ QTextCursor c2 = *cursor;
+#if defined(Q_OS_MAC)
+ QTextParagraph *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( QTextDocument::Standard, c1 );
+ doc->setSelectionEnd( QTextDocument::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 QTextEdit::contentsDragEnterEvent( QDragEnterEvent *e )
+{
+ if ( isReadOnly() || !QTextDrag::canDecode( e ) ) {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+ inDnD = TRUE;
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsDragMoveEvent( QDragMoveEvent *e )
+{
+ if ( isReadOnly() || !QTextDrag::canDecode( e ) ) {
+ e->ignore();
+ return;
+ }
+ drawCursor( FALSE );
+ placeCursor( e->pos(), cursor );
+ drawCursor( TRUE );
+ e->acceptAction();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * )
+{
+ drawCursor( FALSE );
+ inDnD = FALSE;
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::contentsDropEvent( QDropEvent *e )
+{
+ if ( isReadOnly() )
+ return;
+ inDnD = FALSE;
+ e->acceptAction();
+ bool intern = FALSE;
+ if ( QRichTextDrag::canDecode( e ) ) {
+ bool hasSel = doc->hasSelection( QTextDocument::Standard );
+ bool internalDrag = e->source() == this || e->source() == viewport();
+ int dropId, dropIndex;
+ QTextCursor insertCursor = *cursor;
+ dropId = cursor->paragraph()->paragId();
+ dropIndex = cursor->index();
+ if ( hasSel && internalDrag ) {
+ QTextCursor c1, c2;
+ int selStartId, selStartIndex;
+ int selEndId, selEndIndex;
+ c1 = doc->selectionStartCursor( QTextDocument::Standard );
+ c1.restoreState();
+ c2 = doc->selectionEndCursor( QTextDocument::Standard );
+ c2.restoreState();
+ selStartId = c1.paragraph()->paragId();
+ selStartIndex = c1.index();
+ selEndId = c2.paragraph()->paragId();
+ selEndIndex = c2.index();
+ if ( ( ( dropId > selStartId ) ||
+ ( dropId == selStartId && dropIndex > selStartIndex ) ) &&
+ ( ( dropId < selEndId ) ||
+ ( dropId == selEndId && dropIndex <= selEndIndex ) ) )
+ insertCursor = c1;
+ if ( dropId == selEndId && dropIndex > selEndIndex ) {
+ insertCursor = c1;
+ if ( selStartId == selEndId ) {
+ insertCursor.setIndex( dropIndex -
+ ( selEndIndex - selStartIndex ) );
+ } else {
+ insertCursor.setIndex( dropIndex - selEndIndex +
+ selStartIndex );
+ }
+ }
+ }
+
+ if ( internalDrag && e->action() == QDropEvent::Move ) {
+ removeSelectedText();
+ intern = TRUE;
+ doc->removeSelection( QTextDocument::Standard );
+ } else {
+ doc->removeSelection( QTextDocument::Standard );
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ drawCursor( FALSE );
+ cursor->setParagraph( insertCursor.paragraph() );
+ cursor->setIndex( insertCursor.index() );
+ drawCursor( TRUE );
+ if ( !cursor->nestedDepth() ) {
+ QString subType = "plain";
+ if ( textFormat() != PlainText ) {
+ if ( e->provides( "application/x-qrichtext" ) )
+ subType = "x-qrichtext";
+ }
+#ifndef QT_NO_CLIPBOARD
+ pasteSubType( subType.latin1(), 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 QTextEdit::contentsContextMenuEvent( QContextMenuEvent *e )
+{
+ e->accept();
+#ifndef QT_NO_IM
+ if ( d->composeMode() )
+ return;
+#endif
+
+ clearUndoRedo();
+ mousePressed = FALSE;
+
+#ifndef QT_NO_POPUPMENU
+ QGuardedPtr<QTextEdit> that = this;
+ QGuardedPtr<QPopupMenu> popup = createPopupMenu( e->pos() );
+ if ( !popup )
+ popup = createPopupMenu();
+ if ( !popup )
+ return;
+
+ int r = popup->exec( e->globalPos() );
+ delete popup;
+ if (!that)
+ return;
+
+ 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 QTextEdit::autoScrollTimerDone()
+{
+ if ( mousePressed )
+ handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) );
+}
+
+void QTextEdit::handleMouseMove( const QPoint& pos )
+{
+ if ( !mousePressed )
+ return;
+
+ if ( !scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight() )
+ scrollTimer->start( 100, FALSE );
+ else if ( scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight() )
+ scrollTimer->stop();
+
+ drawCursor( FALSE );
+ QTextCursor oldCursor = *cursor;
+
+ placeCursor( pos );
+
+ if ( inDoubleClick ) {
+ QTextCursor cl = *cursor;
+ cl.gotoPreviousWord();
+ QTextCursor cr = *cursor;
+ cr.gotoNextWord();
+
+ int diff = QABS( oldCursor.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( QTextDocument::Standard ) ) {
+ redraw = doc->setSelectionEnd( QTextDocument::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 QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c, bool link )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return;
+#endif
+ if ( !c )
+ c = cursor;
+
+ resetInputContext();
+ c->restoreState();
+ QTextParagraph *s = doc->firstParagraph();
+ c->place( pos, s, link );
+ updateMicroFocusHint();
+}
+
+
+void QTextEdit::updateMicroFocusHint()
+{
+ QTextCursor c( *cursor );
+#if 0
+ // Disabled for Asian input method that shows candidate
+ // window. This behavior is same as Qt/E 2.3.7 which supports
+ // Asian input methods. Asian input methods need start point of IM
+ // selection text to place candidate window as adjacent to the
+ // selection text.
+ if ( d->preeditStart != -1 ) {
+ c.setIndex( d->preeditStart );
+ if(doc->hasSelection(QTextDocument::IMSelectionText)) {
+ int para, index;
+ doc->selectionStart(QTextDocument::IMSelectionText, para, index);
+ c.setIndex(index);
+ }
+ }
+#endif
+
+ if ( hasFocus() || viewport()->hasFocus() ) {
+ int h = c.paragraph()->lineHeightOfChar( cursor->index() );
+ if ( !readonly ) {
+ QFont f = c.paragraph()->at( c.index() )->format()->font();
+ setMicroFocusHint( c.x() - contentsX() + frameWidth(),
+ c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE, &f );
+ }
+ }
+}
+
+
+
+void QTextEdit::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 QTextEdit::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( FALSE );
+}
+
+/*! \internal */
+
+void QTextEdit::doChangeInterval()
+{
+ interval = 0;
+}
+
+/*!
+ \reimp
+*/
+
+bool QTextEdit::eventFilter( QObject *o, QEvent *e )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( !d->optimMode && (o == this || o == viewport()) ) {
+#else
+ if ( o == this || o == viewport() ) {
+#endif
+ if ( e->type() == QEvent::FocusIn ) {
+ if ( QApplication::cursorFlashTime() > 0 )
+ blinkTimer->start( QApplication::cursorFlashTime() / 2 );
+ drawCursor( TRUE );
+ updateMicroFocusHint();
+ } else if ( e->type() == QEvent::FocusOut ) {
+ blinkTimer->stop();
+ drawCursor( FALSE );
+ }
+ }
+
+ if ( o == this && e->type() == QEvent::PaletteChange ) {
+ QColor old( viewport()->colorGroup().color( QColorGroup::Text ) );
+ if ( old != colorGroup().color( QColorGroup::Text ) ) {
+ QColor c( colorGroup().color( QColorGroup::Text ) );
+ doc->setMinimumWidth( -1 );
+ doc->setDefaultFormat( doc->formatCollection()->defaultFormat()->font(), c );
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+ }
+ }
+
+ return QScrollView::eventFilter( o, e );
+}
+
+/*!
+ \obsolete
+ */
+void QTextEdit::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 behaviour of the editor is undefined if the \a text contains
+ newlines. (It is not possible to change QTextEdit'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 \c LogText mode this function will do nothing.
+
+ \sa paste() pasteSubType()
+*/
+
+
+void QTextEdit::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;
+ bool imComposition = insertionFlags & AsIMCompositionText;
+ bool imSelection = insertionFlags & WithIMSelection;
+ QString txt( text );
+ drawCursor( FALSE );
+ if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected )
+ removeSelectedText();
+ QTextCursor c2 = *cursor;
+ int oldLen = 0;
+
+ if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) {
+ checkUndoRedoInfo( UndoRedoInfo::Insert );
+
+ 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 = QString::null;
+ }
+ oldLen = undoRedoInfo.d->text.length();
+ }
+
+ lastFormatted = checkNewLine && cursor->paragraph()->prev() ?
+ cursor->paragraph()->prev() : cursor->paragraph();
+ QTextCursor oldCursor = *cursor;
+ cursor->insert( txt, checkNewLine );
+ if ( doc->useFormatCollection() && !doc->preProcessor() ) {
+ doc->setSelectionStart( QTextDocument::Temp, oldCursor );
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+ doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format );
+ doc->removeSelection( QTextDocument::Temp );
+ }
+
+ if ( indent && ( txt == "{" || txt == "}" || txt == ":" || txt == "#" ) )
+ cursor->indent();
+ formatMore();
+ repaintChanged();
+ ensureCursorVisible();
+ // Asian users regard selection text as cursor on candidate
+ // selection phase of input method, so ordinary cursor should be
+ // invisible if IM selection text exists.
+ drawCursor( !imSelection );
+
+ 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 ] != '\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( QTextDocument::Standard, oldCursor );
+ doc->setSelectionEnd( QTextDocument::Standard, *cursor );
+ repaintChanged();
+ }
+ // updateMicroFocusHint() should not be invoked here when this
+ // function is invoked from imComposeEvent() because cursor
+ // postion is incorrect yet. imComposeEvent() invokes
+ // updateMicroFocusHint() later.
+ if ( !imComposition ) {
+ updateMicroFocusHint();
+ }
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Inserts \a text in the paragraph \a para at position \a index.
+*/
+
+void QTextEdit::insertAt( const QString &text, int para, int index )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimInsert( text, para, index );
+ return;
+ }
+#endif
+ resetInputContext();
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ removeSelection( QTextDocument::Standard );
+ QTextCursor tmp = *cursor;
+ cursor->setParagraph( p );
+ cursor->setIndex( index );
+ insert( text, FALSE, TRUE, FALSE );
+ *cursor = tmp;
+ removeSelection( QTextDocument::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 QTextEdit::insertParagraph( const QString &text, int para )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimInsert( text + "\n", para, 0 );
+ return;
+ }
+#endif
+ resetInputContext();
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+ QTextParagraph *p = doc->paragAt( para );
+
+ bool append = !p;
+ if ( !p )
+ p = doc->lastParagraph();
+
+ QTextCursor 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 QTextEdit::removeParagraph( int para )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return;
+#endif
+ resetInputContext();
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return;
+
+ for ( int i = 0; i < doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+ QTextCursor start( doc );
+ QTextCursor 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( QTextDocument::Temp, start );
+ doc->setSelectionEnd( QTextDocument::Temp, end );
+ removeSelectedText( QTextDocument::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 QTextEdit::undo()
+{
+ clearUndoRedo();
+ if ( isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled )
+ return;
+
+ resetInputContext();
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+
+ clearUndoRedo();
+ drawCursor( FALSE );
+ QTextCursor *c = doc->undo( cursor );
+ if ( !c ) {
+ drawCursor( TRUE );
+ return;
+ }
+ lastFormatted = 0;
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ updateMicroFocusHint();
+ 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 QTextCursor( doc );
+ drawCursor( TRUE );
+ repaintContents( TRUE );
+ }
+ 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 QTextEdit::redo()
+{
+ if ( isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled )
+ return;
+
+ resetInputContext();
+ for ( int i = 0; i < (int)doc->numSelections(); ++i )
+ doc->removeSelection( i );
+
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+
+ clearUndoRedo();
+ drawCursor( FALSE );
+ QTextCursor *c = doc->redo( cursor );
+ if ( !c ) {
+ drawCursor( TRUE );
+ return;
+ }
+ lastFormatted = 0;
+ ensureCursorVisible();
+ repaintChanged();
+ ensureCursorVisible();
+ drawCursor( TRUE );
+ updateMicroFocusHint();
+ setModified();
+ emit 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() QTextEdit::copy()
+*/
+
+void QTextEdit::paste()
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ if ( isReadOnly() )
+ return;
+ QString subType = "plain";
+ if ( textFormat() != PlainText ) {
+ QMimeSource *m = QApplication::clipboard()->data( d->clipboard_mode );
+ if ( !m )
+ return;
+ if ( m->provides( "application/x-qrichtext" ) )
+ subType = "x-qrichtext";
+ }
+
+ pasteSubType( subType.latin1() );
+ updateMicroFocusHint();
+#endif
+}
+
+void QTextEdit::checkUndoRedoInfo( UndoRedoInfo::Type t )
+{
+ if ( undoRedoInfo.valid() && t != undoRedoInfo.type ) {
+ clearUndoRedo();
+ }
+ undoRedoInfo.type = t;
+}
+
+/*!
+ Repaints any paragraphs that have changed.
+
+ Although used extensively internally you shouldn't need to call
+ this yourself.
+*/
+
+void QTextEdit::repaintChanged()
+{
+ if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() )
+ return;
+
+ QPainter p( viewport() );
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimDrawContents( &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+ return;
+ }
+#endif
+ p.translate( -contentsX(), -contentsY() );
+ paintDocument( FALSE, &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() );
+}
+
+#ifndef QT_NO_MIME
+QTextDrag *QTextEdit::dragObject( QWidget *parent ) const
+{
+ if ( !doc->hasSelection( QTextDocument::Standard ) ||
+ doc->selectedText( QTextDocument::Standard ).isEmpty() )
+ return 0;
+ if ( textFormat() != RichText )
+ return new QTextDrag( doc->selectedText( QTextDocument::Standard ), parent );
+ QRichTextDrag *drag = new QRichTextDrag( parent );
+ drag->setPlainText( doc->selectedText( QTextDocument::Standard ) );
+ drag->setRichText( doc->selectedText( QTextDocument::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 QTextEdit::copy() paste() pasteSubType()
+*/
+
+void QTextEdit::cut()
+{
+ if ( isReadOnly() )
+ return;
+ resetInputContext();
+ normalCopy();
+ removeSelectedText();
+ updateMicroFocusHint();
+}
+
+void QTextEdit::normalCopy()
+{
+#ifndef QT_NO_MIME
+ QTextDrag *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 QTextEdit::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 QTextEdit::indent()
+{
+ if ( isReadOnly() )
+ return;
+
+ drawCursor( FALSE );
+ if ( !doc->hasSelection( QTextDocument::Standard ) )
+ cursor->indent();
+ else
+ doc->indentSelection( QTextDocument::Standard );
+ repaintChanged();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Reimplemented to allow tabbing through links. If \a n is TRUE the
+ tab moves the focus to the next child; if \a n is FALSE the tab
+ moves the focus to the previous child. Returns TRUE if the focus
+ was moved; otherwise returns FALSE.
+ */
+
+bool QTextEdit::focusNextPrevChild( bool n )
+{
+ if ( !isReadOnly() || !linksEnabled() )
+ return FALSE;
+ bool b = doc->focusNextPrevChild( n );
+ repaintChanged();
+ if ( b ) {
+ QTextParagraph *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() ) {
+
+ QTextTable *t = (QTextTable*)p->at( start )->customItem();
+ QPtrList<QTextTableCell> cells = t->tableCells();
+ QTextTableCell *c;
+ for ( c = cells.first(); c; c = cells.next() ) {
+ QTextDocument *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 QTextEdit::setFormat( QTextFormat *f, int flags )
+{
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ drawCursor( FALSE );
+ QTextCursor c1 = doc->selectionStartCursor( QTextDocument::Standard );
+ c1.restoreState();
+ QTextCursor c2 = doc->selectionEndCursor( QTextDocument::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( QTextDocument::Standard, f, flags );
+ repaintChanged();
+ formatMore();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+ }
+ if ( currentFormat && currentFormat->key() != f->key() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( f );
+ if ( currentFormat->isMisspelled() ) {
+ currentFormat->removeRef();
+ currentFormat = doc->formatCollection()->format( currentFormat->font(),
+ currentFormat->color() );
+ }
+ emit currentFontChanged( currentFormat->font() );
+ emit currentColorChanged( currentFormat->color() );
+ emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() );
+ if ( cursor->index() == cursor->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();
+ }
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::setPalette( const QPalette &p )
+{
+ QScrollView::setPalette( p );
+ if ( textFormat() == PlainText ) {
+ QTextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor( colorGroup().text() );
+ updateContents();
+ }
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ Sets the paragraph style of the current paragraph
+ to \a dm. If \a dm is QStyleSheetItem::DisplayListItem, the
+ type of the list item is set to \a listStyle.
+
+ \sa setAlignment()
+*/
+
+void QTextEdit::setParagType( QStyleSheetItem::DisplayMode dm,
+ QStyleSheetItem::ListStyle listStyle )
+{
+ if ( isReadOnly() )
+ return;
+
+ drawCursor( FALSE );
+ QTextParagraph *start = cursor->paragraph();
+ QTextParagraph *end = start;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph();
+ end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph();
+ if ( end->paragId() < start->paragId() )
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid );
+
+ while ( start != end->next() ) {
+ start->setListStyle( listStyle );
+ if ( dm == QStyleSheetItem::DisplayListItem ) {
+ start->setListItem( TRUE );
+ if( start->listDepth() == 0 )
+ start->setListDepth( 1 );
+ } else if ( start->isListItem() ) {
+ start->setListItem( FALSE );
+ start->setListDepth( QMAX( start->listDepth()-1, 0 ) );
+ }
+ start = start->next();
+ }
+
+ clearUndoRedo();
+ repaintChanged();
+ formatMore();
+ drawCursor( TRUE );
+ setModified();
+ emit textChanged();
+}
+
+/*!
+ Sets the alignment of the current paragraph to \a a. Valid
+ alignments are \c Qt::AlignLeft, \c Qt::AlignRight,
+ \c Qt::AlignJustify and \c Qt::AlignCenter (which centers
+ horizontally).
+*/
+
+void QTextEdit::setAlignment( int a )
+{
+ if ( isReadOnly() || block_set_alignment )
+ return;
+
+ drawCursor( FALSE );
+ QTextParagraph *start = cursor->paragraph();
+ QTextParagraph *end = start;
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph();
+ end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph();
+ if ( end->paragId() < start->paragId() )
+ return; // do not trust our selections
+ }
+
+ clearUndoRedo();
+ undoRedoInfo.type = UndoRedoInfo::Style;
+ undoRedoInfo.id = start->paragId();
+ undoRedoInfo.eid = end->paragId();
+ undoRedoInfo.styleInformation = QTextStyleCommand::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 QTextEdit::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 QTextEdit::setItalic( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setItalic( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat(f2, QTextFormat::Italic );
+}
+
+/*!
+ If \a b is TRUE sets the current format to bold; otherwise sets
+ the current format to non-bold.
+
+ \sa bold()
+*/
+
+void QTextEdit::setBold( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setBold( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Bold );
+}
+
+/*!
+ If \a b is TRUE sets the current format to underline; otherwise
+ sets the current format to non-underline.
+
+ \sa underline()
+*/
+
+void QTextEdit::setUnderline( bool b )
+{
+ QTextFormat f( *currentFormat );
+ f.setUnderline( b );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Underline );
+}
+
+/*!
+ Sets the font family of the current format to \a fontFamily.
+
+ \sa family() setCurrentFont()
+*/
+
+void QTextEdit::setFamily( const QString &fontFamily )
+{
+ QTextFormat f( *currentFormat );
+ f.setFamily( fontFamily );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Family );
+}
+
+/*!
+ Sets the point size of the current format to \a s.
+
+ Note that if \a s is zero or negative, the behaviour of this
+ function is not defined.
+
+ \sa pointSize() setCurrentFont() setFamily()
+*/
+
+void QTextEdit::setPointSize( int s )
+{
+ QTextFormat f( *currentFormat );
+ f.setPointSize( s );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Size );
+}
+
+/*!
+ Sets the color of the current format, i.e. of the text, to \a c.
+
+ \sa color() setPaper()
+*/
+
+void QTextEdit::setColor( const QColor &c )
+{
+ QTextFormat f( *currentFormat );
+ f.setColor( c );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Color );
+}
+
+/*!
+ Sets the vertical alignment of the current format, i.e. of the
+ text, to \a a.
+
+ \sa color() setPaper()
+*/
+
+void QTextEdit::setVerticalAlignment( VerticalAlignment a )
+{
+ QTextFormat f( *currentFormat );
+ f.setVAlign( (QTextFormat::VerticalAlignment)a );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::VAlign );
+}
+
+void QTextEdit::setFontInternal( const QFont &f_ )
+{
+ QTextFormat f( *currentFormat );
+ f.setFont( f_ );
+ QTextFormat *f2 = doc->formatCollection()->format( &f );
+ setFormat( f2, QTextFormat::Font );
+}
+
+
+QString QTextEdit::text() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return optimText();
+#endif
+
+ QTextParagraph *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 \c RichText the text will contain HTML
+ formatting tags.
+*/
+
+QString QTextEdit::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 = "\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 \c 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 QStyleSheet for details.
+
+ The optional \a context is a path which the text edit's
+ QMimeSourceFactory uses to resolve the locations of files and
+ images. (See \l{QTextEdit::QTextEdit()}.) It is passed to the text
+ edit's QMimeSourceFactory when quering data.
+
+ Note that the undo/redo history is cleared by this function.
+
+ \sa text(), setTextFormat()
+*/
+
+void QTextEdit::setText( const QString &text, const QString &context )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimSetText( text );
+ return;
+ }
+#endif
+ resetInputContext();
+ 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 QTextCursor( 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 = QString::null;
+}
+
+/*!
+ \property QTextEdit::text
+ \brief the text edit's text
+
+ There is no default text.
+
+ On setting, any previous text is deleted.
+
+ The text may be interpreted either as plain text or as rich text,
+ depending on the textFormat(). The default setting is \c AutoText,
+ i.e. the text edit auto-detects the format of the text.
+
+ For richtext, calling text() on an editable QTextEdit will cause
+ the text to be regenerated from the textedit. This may mean that
+ the QString returned may not be exactly the same as the one that
+ was set.
+
+ \sa textFormat
+*/
+
+
+/*!
+ \property QTextEdit::readOnly
+ \brief whether the text edit is read-only
+
+ In a read-only text edit the user can only navigate through the
+ text and select text; modifying the text is not possible.
+
+ This property's default is FALSE.
+*/
+
+/*!
+ Finds the next occurrence of the string, \a expr. Returns TRUE if
+ \a expr 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 \a *index character position in the
+ \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 \a *para, and the
+ index position of that character within the paragraph is put into
+ \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, \a *index
+ and \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 QTextEdit::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() ? arrowCursor : ibeamCursor );
+#endif
+ QTextCursor findcur = *cursor;
+ if ( para && index ) {
+ if ( doc->paragAt( *para ) )
+ findcur.gotoPosition( doc->paragAt(*para), *index );
+ else
+ findcur.gotoEnd();
+ } else if ( doc->hasSelection( QTextDocument::Standard ) ){
+ // maks sure we do not find the same selection again
+ if ( forward )
+ findcur.gotoNextLetter();
+ else
+ findcur.gotoPreviousLetter();
+ } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) {
+ findcur.gotoEnd();
+ }
+ removeSelection( QTextDocument::Standard );
+ bool found = doc->find( findcur, expr, cs, wo, forward );
+ if ( found ) {
+ if ( para )
+ *para = findcur.paragraph()->paragId();
+ if ( index )
+ *index = findcur.index();
+ *cursor = findcur;
+ repaintChanged();
+ ensureCursorVisible();
+ }
+ drawCursor( TRUE );
+ if (found) {
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() );
+ }
+ return found;
+}
+
+void QTextEdit::blinkCursor()
+{
+ if ( !cursorVisible )
+ return;
+ bool cv = cursorVisible;
+ blinkCursorVisible = !blinkCursorVisible;
+ drawCursor( blinkCursorVisible );
+ cursorVisible = cv;
+}
+
+/*!
+ Sets the cursor to position \a index in paragraph \a para.
+
+ \sa getCursorPosition()
+*/
+
+void QTextEdit::setCursorPosition( int para, int index )
+{
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return;
+
+ resetInputContext();
+ 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 \a *para and \a *index parameters to the
+ current cursor position. \a para and \a index must not be 0.
+
+ \sa setCursorPosition()
+*/
+
+void QTextEdit::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 QTextEdit::setSelection( int paraFrom, int indexFrom,
+ int paraTo, int indexTo, int selNum )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if (d->optimMode) {
+ optimSetSelection(paraFrom, indexFrom, paraTo, indexTo);
+ repaintContents(FALSE);
+ return;
+ }
+#endif
+ resetInputContext();
+ if ( doc->hasSelection( selNum ) ) {
+ doc->removeSelection( selNum );
+ repaintChanged();
+ }
+ if ( selNum > doc->numSelections() - 1 )
+ doc->addSelection( selNum );
+ QTextParagraph *p1 = doc->paragAt( paraFrom );
+ if ( !p1 )
+ return;
+ QTextParagraph *p2 = doc->paragAt( paraTo );
+ if ( !p2 )
+ return;
+
+ if ( indexFrom > p1->length() - 1 )
+ indexFrom = p1->length() - 1;
+ if ( indexTo > p2->length() - 1 )
+ indexTo = p2->length() - 1;
+
+ drawCursor( FALSE );
+ QTextCursor c = *cursor;
+ QTextCursor oldCursor = *cursor;
+ c.setParagraph( p1 );
+ c.setIndex( indexFrom );
+ cursor->setParagraph( p2 );
+ cursor->setIndex( indexTo );
+ doc->setSelectionStart( selNum, c );
+ doc->setSelectionEnd( selNum, *cursor );
+ repaintChanged();
+ ensureCursorVisible();
+ if ( selNum != QTextDocument::Standard )
+ *cursor = oldCursor;
+ drawCursor( TRUE );
+}
+
+/*!
+ If there is a selection, \a *paraFrom is set to the number of the
+ paragraph in which the selection begins and \a *paraTo is set to
+ the number of the paragraph in which the selection ends. (They
+ could be the same.) \a *indexFrom is set to the index at which the
+ selection begins within \a *paraFrom, and \a *indexTo is set to
+ the index at which the selection ends within \a *paraTo.
+
+ If there is no selection, \a *paraFrom, \a *indexFrom, \a *paraTo
+ and \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 QTextEdit::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 QTextEdit::textFormat
+ \brief the text format: rich text, plain text, log text or auto text.
+
+ The text format is one of the following:
+ \list
+ \i PlainText - all characters, except newlines, are displayed
+ verbatim, including spaces. Whenever a newline appears in the text
+ the text edit inserts a hard line break and begins a new
+ paragraph.
+ \i RichText - rich text rendering. The available styles are
+ defined in the default stylesheet QStyleSheet::defaultSheet().
+ \i LogText - optimized mode for very large texts. Supports a very
+ limited set of formatting tags (color, bold, underline and italic
+ settings).
+ \i AutoText - this is the default. The text edit autodetects which
+ rendering style is best, \c PlainText or \c RichText. This is done
+ by using the QStyleSheet::mightBeRichText() function.
+ \endlist
+*/
+
+void QTextEdit::setTextFormat( TextFormat format )
+{
+ doc->setTextFormat( format );
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+Qt::TextFormat QTextEdit::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 QTextEdit::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 QTextEdit::linesOfParagraph( int para ) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ if ( d->od->numLines >= para )
+ return 1;
+ else
+ return -1;
+ }
+#endif
+ QTextParagraph *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 QTextEdit::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
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return -1;
+ return p->length() - 1;
+}
+
+/*!
+ Returns the number of lines in the text edit; this could be 0.
+
+ \warning This function may be slow. Lines change all the time
+ during word wrapping, so this function has to iterate over all the
+ paragraphs and get the number of lines from each one individually.
+*/
+
+int QTextEdit::lines() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ return d->od->numLines;
+ }
+#endif
+ QTextParagraph *p = doc->firstParagraph();
+ int l = 0;
+ while ( p ) {
+ l += p->lines();
+ p = p->next();
+ }
+
+ return l;
+}
+
+/*!
+ Returns the line number of the line in paragraph \a para in which
+ the character at position \a index appears. The \a index position is
+ relative to the beginning of the paragraph. If there is no such
+ paragraph or no such character at the \a index position (e.g. the
+ index is out of range) -1 is returned.
+*/
+
+int QTextEdit::lineOfChar( int para, int index )
+{
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return -1;
+
+ int idx, line;
+ QTextStringChar *c = p->lineStartOfChar( index, &idx, &line );
+ if ( !c )
+ return -1;
+
+ return line;
+}
+
+void QTextEdit::setModified( bool m )
+{
+ bool oldModified = modified;
+ modified = m;
+ if ( modified && doc->oTextValid )
+ doc->invalidateOriginalText();
+ if ( oldModified != modified )
+ emit modificationChanged( modified );
+}
+
+/*!
+ \property QTextEdit::modified
+ \brief whether the document has been modified by the user
+*/
+
+bool QTextEdit::isModified() const
+{
+ return modified;
+}
+
+void QTextEdit::setModified()
+{
+ if ( !isModified() )
+ setModified( TRUE );
+}
+
+/*!
+ Returns TRUE if the current format is italic; otherwise returns FALSE.
+
+ \sa setItalic()
+*/
+
+bool QTextEdit::italic() const
+{
+ return currentFormat->font().italic();
+}
+
+/*!
+ Returns TRUE if the current format is bold; otherwise returns FALSE.
+
+ \sa setBold()
+*/
+
+bool QTextEdit::bold() const
+{
+ return currentFormat->font().bold();
+}
+
+/*!
+ Returns TRUE if the current format is underlined; otherwise returns
+ FALSE.
+
+ \sa setUnderline()
+*/
+
+bool QTextEdit::underline() const
+{
+ return currentFormat->font().underline();
+}
+
+/*!
+ Returns the font family of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+QString QTextEdit::family() const
+{
+ return currentFormat->font().family();
+}
+
+/*!
+ Returns the point size of the font of the current format.
+
+ \sa setFamily() setCurrentFont() setPointSize()
+*/
+
+int QTextEdit::pointSize() const
+{
+ return currentFormat->font().pointSize();
+}
+
+/*!
+ Returns the color of the current format.
+
+ \sa setColor() setPaper()
+*/
+
+QColor QTextEdit::color() const
+{
+ return currentFormat->color();
+}
+
+/*!
+ \obsolete
+
+ Returns QScrollView::font()
+
+ \warning In previous versions this function returned the font of
+ the current format. This lead to confusion. Please use
+ currentFont() instead.
+*/
+
+QFont QTextEdit::font() const
+{
+ return QScrollView::font();
+}
+
+/*!
+ Returns the font of the current format.
+
+ \sa setCurrentFont() setFamily() setPointSize()
+*/
+
+QFont QTextEdit::currentFont() const
+{
+ return currentFormat->font();
+}
+
+
+/*!
+ Returns the alignment of the current paragraph.
+
+ \sa setAlignment()
+*/
+
+int QTextEdit::alignment() const
+{
+ return currentAlignment;
+}
+
+void QTextEdit::startDrag()
+{
+#ifndef QT_NO_DRAGANDDROP
+ mousePressed = FALSE;
+ inDoubleClick = FALSE;
+ QDragObject *drag = dragObject( viewport() );
+ if ( !drag )
+ return;
+ if ( isReadOnly() ) {
+ drag->dragCopy();
+ } else {
+ if ( drag->drag() && QDragObject::target() != this && QDragObject::target() != viewport() )
+ removeSelectedText();
+ }
+#endif
+}
+
+/*!
+ If \a select is TRUE (the default), all the text is selected as
+ selection 0. If \a select is FALSE any selected text is
+ unselected, i.e. the default selection (selection 0) is cleared.
+
+ \sa selectedText
+*/
+
+void QTextEdit::selectAll( bool select )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ if ( select )
+ optimSelectAll();
+ else
+ optimRemoveSelection();
+ return;
+ }
+#endif
+ if ( !select )
+ doc->removeSelection( QTextDocument::Standard );
+ else
+ doc->selectAll( QTextDocument::Standard );
+ repaintChanged();
+ emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) );
+ emit selectionChanged();
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+}
+
+void QTextEdit::UndoRedoInfo::clear()
+{
+ if ( valid() ) {
+ if ( type == Insert || type == Return )
+ doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), styleInformation ) );
+ else if ( type == Format )
+ doc->addCommand( new QTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) );
+ else if ( type == Style )
+ doc->addCommand( new QTextStyleCommand( doc, id, eid, styleInformation ) );
+ else if ( type != Invalid ) {
+ doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), styleInformation ) );
+ }
+ }
+ type = Invalid;
+ d->text = QString::null;
+ id = -1;
+ index = -1;
+ styleInformation = QByteArray();
+}
+
+
+/*!
+ If there is some selected text (in selection 0) it is deleted. If
+ there is no selected text (in selection 0) the character to the
+ right of the text cursor is deleted.
+
+ \sa removeSelectedText() cut()
+*/
+
+void QTextEdit::del()
+{
+ if ( doc->hasSelection( QTextDocument::Standard ) ) {
+ removeSelectedText();
+ return;
+ }
+
+ doKeyboardAction( ActionDelete );
+}
+
+
+QTextEdit::UndoRedoInfo::UndoRedoInfo( QTextDocument *dc )
+ : type( Invalid ), doc( dc )
+{
+ d = new QUndoRedoInfoPrivate;
+ d->text = QString::null;
+ id = -1;
+ index = -1;
+}
+
+QTextEdit::UndoRedoInfo::~UndoRedoInfo()
+{
+ delete d;
+}
+
+bool QTextEdit::UndoRedoInfo::valid() const
+{
+ return id >= 0 && type != Invalid;
+}
+
+/*!
+ \internal
+
+ Resets the current format to the default format.
+*/
+
+void QTextEdit::resetFormat()
+{
+ setAlignment( Qt::AlignAuto );
+ setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc );
+ setFormat( doc->formatCollection()->defaultFormat(), QTextFormat::Format );
+}
+
+/*!
+ Returns the QStyleSheet which is being used by this text edit.
+
+ \sa setStyleSheet()
+*/
+
+QStyleSheet* QTextEdit::styleSheet() const
+{
+ return doc->styleSheet();
+}
+
+/*!
+ Sets the stylesheet to use with this text edit to \a styleSheet.
+ Changes will only take effect for new text added with setText() or
+ append().
+
+ \sa styleSheet()
+*/
+
+void QTextEdit::setStyleSheet( QStyleSheet* styleSheet )
+{
+ doc->setStyleSheet( styleSheet );
+}
+
+/*!
+ \property QTextEdit::paper
+ \brief the background (paper) brush.
+
+ The brush that is currently used to draw the background of the
+ text edit. The initial setting is an empty brush.
+*/
+
+void QTextEdit::setPaper( const QBrush& pap )
+{
+ doc->setPaper( new QBrush( pap ) );
+
+ if ( pap.pixmap() ) {
+ viewport()->setBackgroundPixmap( *pap.pixmap() );
+ } else {
+ setPaletteBackgroundColor( pap.color() );
+ viewport()->setPaletteBackgroundColor( pap.color() );
+ }
+
+#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 QTextEdit::paper() const
+{
+ if ( doc->paper() )
+ return *doc->paper();
+ return QBrush( colorGroup().base() );
+}
+
+/*!
+ \property QTextEdit::linkUnderline
+ \brief whether hypertext links will be underlined
+
+ If TRUE (the default) hypertext links will be displayed
+ underlined. If FALSE links will not be displayed underlined.
+*/
+
+void QTextEdit::setLinkUnderline( bool b )
+{
+ if ( doc->underlineLinks() == b )
+ return;
+ doc->setUnderlineLinks( b );
+ repaintChanged();
+}
+
+bool QTextEdit::linkUnderline() const
+{
+ return doc->underlineLinks();
+}
+
+/*!
+ Sets the text edit's mimesource factory to \a factory. See
+ QMimeSourceFactory for further details.
+
+ \sa mimeSourceFactory()
+ */
+
+#ifndef QT_NO_MIME
+void QTextEdit::setMimeSourceFactory( QMimeSourceFactory* factory )
+{
+ doc->setMimeSourceFactory( factory );
+}
+
+/*!
+ Returns the QMimeSourceFactory which is being used by this text
+ edit.
+
+ \sa setMimeSourceFactory()
+*/
+
+QMimeSourceFactory* QTextEdit::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 QTextEdit::heightForWidth( int w ) const
+{
+ int oldw = doc->width();
+ doc->doLayout( 0, w );
+ int h = doc->height();
+ doc->setWidth( oldw );
+ doc->invalidate();
+ ( (QTextEdit*)this )->formatMore();
+ return h;
+}
+
+/*!
+ Appends 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 QTextEdit::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( QTextDocument::Standard );
+ TextFormat f = doc->textFormat();
+ if ( f == AutoText ) {
+ if ( QStyleSheet::mightBeRichText( text ) )
+ f = RichText;
+ else
+ f = PlainText;
+ }
+
+ drawCursor( FALSE );
+ QTextCursor oldc( *cursor );
+ ensureFormatted( doc->lastParagraph() );
+ bool atBottom = contentsY() >= contentsHeight() - visibleHeight();
+ cursor->gotoEnd();
+ if ( cursor->index() > 0 )
+ cursor->splitAndInsertEmptyParagraph();
+ QTextCursor oldCursor2 = *cursor;
+
+ if ( f == Qt::PlainText ) {
+ cursor->insert( text, TRUE );
+ if ( doc->useFormatCollection() && !doc->preProcessor() &&
+ currentFormat != cursor->paragraph()->at( cursor->index() )->format() ) {
+ doc->setSelectionStart( QTextDocument::Temp, oldCursor2 );
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+ doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format );
+ doc->removeSelection( QTextDocument::Temp );
+ }
+ } else {
+ 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 QTextEdit::hasSelectedText
+ \brief whether some text is selected in selection 0
+*/
+
+bool QTextEdit::hasSelectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return optimHasSelection();
+ else
+#endif
+ return doc->hasSelection( QTextDocument::Standard );
+}
+
+/*!
+ \property QTextEdit::selectedText
+ \brief The selected text (from selection 0) or an empty string if
+ there is no currently selected text (in selection 0).
+
+ The text is always returned as \c PlainText if the textFormat() is
+ \c PlainText or \c AutoText, otherwise it is returned as HTML.
+
+ \sa hasSelectedText
+*/
+
+QString QTextEdit::selectedText() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return optimSelectedText();
+ else
+#endif
+ return doc->selectedText( QTextDocument::Standard, textFormat() == RichText );
+}
+
+bool QTextEdit::handleReadOnlyKeyEvent( QKeyEvent *e )
+{
+ switch( e->key() ) {
+ case Key_Down:
+ setContentsPos( contentsX(), contentsY() + 10 );
+ break;
+ case Key_Up:
+ setContentsPos( contentsX(), contentsY() - 10 );
+ break;
+ case Key_Left:
+ setContentsPos( contentsX() - 10, contentsY() );
+ break;
+ case Key_Right:
+ setContentsPos( contentsX() + 10, contentsY() );
+ break;
+ case Key_PageUp:
+ setContentsPos( contentsX(), contentsY() - visibleHeight() );
+ break;
+ case Key_PageDown:
+ setContentsPos( contentsX(), contentsY() + visibleHeight() );
+ break;
+ case Key_Home:
+ setContentsPos( contentsX(), 0 );
+ break;
+ case Key_End:
+ setContentsPos( contentsX(), contentsHeight() - visibleHeight() );
+ break;
+ case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifndef QT_NO_NETWORKPROTOCOL
+ case Key_Return:
+ case Key_Enter:
+ case Key_Space: {
+ if (!doc->focusIndicator.href.isEmpty()
+ || !doc->focusIndicator.name.isEmpty()) {
+ if (!doc->focusIndicator.href.isEmpty()) {
+ QUrl u( doc->context(), doc->focusIndicator.href, TRUE );
+ emitLinkClicked( u.toString( FALSE, FALSE ) );
+ }
+ if (!doc->focusIndicator.name.isEmpty()) {
+ if (::qt_cast<QTextBrowser*>(this)) { // change for 4.0
+ QConnectionList *clist = receivers(
+ "anchorClicked(const QString&,const QString&)");
+ if (!signalsBlocked() && clist) {
+ QUObject o[3];
+ static_QUType_QString.set(o+1,
+ doc->focusIndicator.name);
+ static_QUType_QString.set(o+2,
+ doc->focusIndicator.href);
+ activate_signal( clist, o);
+ }
+ }
+ }
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ }
+ } break;
+#endif
+ default:
+ if ( e->state() & ControlButton ) {
+ switch ( e->key() ) {
+ case Key_C: case Key_F16: // Copy key on Sun keyboards
+ copy();
+ break;
+#ifdef Q_WS_WIN
+ case Key_Insert:
+ copy();
+ break;
+ case 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 QMimeSourceFactory uses to resolve the locations
+ of files and images.
+
+ \sa text
+*/
+
+QString QTextEdit::context() const
+{
+ return doc->context();
+}
+
+/*!
+ \property QTextEdit::documentTitle
+ \brief the title of the document parsed from the text.
+
+ For \c PlainText the title will be an empty string. For \c
+ RichText the title will be the text between the \c{<title>} tags,
+ if present, otherwise an empty string.
+*/
+
+QString QTextEdit::documentTitle() const
+{
+ return doc->attributes()[ "title" ];
+}
+
+void QTextEdit::makeParagVisible( QTextParagraph *p )
+{
+ setContentsPos( contentsX(), QMIN( p->rect().y(), contentsHeight() - visibleHeight() ) );
+}
+
+/*!
+ Scrolls the text edit to make the text at the anchor called \a
+ name visible, if it can be found in the document. If the anchor
+ isn't found no scrolling will occur. An anchor is defined using
+ the HTML anchor tag, e.g. \c{<a name="target">}.
+*/
+
+void QTextEdit::scrollToAnchor( const QString& name )
+{
+ if ( !isVisible() ) {
+ d->scrollToAnchor = name;
+ return;
+ }
+ if ( name.isEmpty() )
+ return;
+ sync();
+ QTextCursor cursor( doc );
+ QTextParagraph* last = doc->lastParagraph();
+ for (;;) {
+ QTextStringChar* c = cursor.paragraph()->at( cursor.index() );
+ if( c->isAnchor() ) {
+ QString a = c->anchorName();
+ if ( a == name ||
+ (a.contains( '#' ) && QStringList::split( '#', a ).contains( name ) ) ) {
+ setContentsPos( contentsX(), QMIN( cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) );
+ break;
+ }
+ }
+ if ( cursor.paragraph() == last && cursor.atParagEnd() )
+ break;
+ cursor.gotoNextLetter();
+ }
+}
+
+#if (QT_VERSION-0 >= 0x040000)
+#error "function anchorAt(const QPoint& pos) should be merged into function anchorAt(const QPoint& pos, AnchorAttribute attr)"
+#endif
+
+/*!
+ \overload
+
+ If there is an anchor at position \a pos (in contents
+ coordinates), its \c href is returned, otherwise QString::null is
+ returned.
+*/
+
+QString QTextEdit::anchorAt( const QPoint& pos )
+{
+ return anchorAt(pos, AnchorHref);
+}
+
+/*!
+ If there is an anchor at position \a pos (in contents
+ coordinates), the text for attribute \a attr is returned,
+ otherwise QString::null is returned.
+*/
+
+QString QTextEdit::anchorAt( const QPoint& pos, AnchorAttribute attr )
+{
+ QTextCursor c( doc );
+ placeCursor( pos, &c );
+ switch(attr) {
+ case AnchorName:
+ return c.paragraph()->at( c.index() )->anchorName();
+ case AnchorHref:
+ return c.paragraph()->at( c.index() )->anchorHref();
+ }
+ // incase the compiler is really dumb about determining if a function
+ // returns something :)
+ return QString::null;
+}
+
+void QTextEdit::documentWidthChanged( int w )
+{
+ resizeContents( QMAX( visibleWidth(), w), contentsHeight() );
+}
+
+/*! \internal
+
+ This function does nothing
+*/
+
+void QTextEdit::updateStyles()
+{
+}
+
+void QTextEdit::setDocument( QTextDocument *dc )
+{
+ if ( dc == 0 ) {
+ qWarning( "Q3TextEdit::setDocument() called with null Q3TextDocument pointer" );
+ return;
+ }
+ if ( dc == doc )
+ return;
+ resetInputContext();
+ doc = dc;
+ delete cursor;
+ cursor = new QTextCursor( doc );
+ clearUndoRedo();
+ undoRedoInfo.doc = doc;
+ lastFormatted = 0;
+}
+
+#ifndef QT_NO_CLIPBOARD
+
+/*!
+ Pastes the text with format \a subtype from the clipboard into the
+ text edit at the current cursor position. The \a subtype can be
+ "plain" or "html".
+
+ If there is no text with format \a subtype in the clipboard
+ nothing happens.
+
+ \sa paste() cut() QTextEdit::copy()
+*/
+
+void QTextEdit::pasteSubType( const QCString &subtype )
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ QMimeSource *m = QApplication::clipboard()->data( d->clipboard_mode );
+ pasteSubType( subtype, m );
+#endif
+}
+
+/*! \internal */
+
+void QTextEdit::pasteSubType( const QCString& subtype, QMimeSource *m )
+{
+#ifndef QT_NO_MIME
+ QCString st = subtype;
+ if ( subtype != "x-qrichtext" )
+ st.prepend( "text/" );
+ else
+ st.prepend( "application/" );
+ if ( !m )
+ return;
+ if ( doc->hasSelection( QTextDocument::Standard ) )
+ removeSelectedText();
+ if ( !QRichTextDrag::canDecode( m ) )
+ return;
+ QString t;
+ if ( !QRichTextDrag::decode( m, t, st.data(), subtype ) )
+ return;
+ if ( st == "application/x-qrichtext" ) {
+ int start;
+ if ( (start = t.find( "<!--StartFragment-->" )) != -1 ) {
+ start += 20;
+ int end = t.find( "<!--EndFragment-->" );
+ QTextCursor 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( QTextDocument::Temp, oldC );
+ doc->setSelectionEnd( QTextDocument::Temp, *cursor );
+
+ checkUndoRedoInfo( UndoRedoInfo::Insert );
+ if ( !undoRedoInfo.valid() ) {
+ undoRedoInfo.id = oldC.paragraph()->paragId();
+ undoRedoInfo.index = oldC.index();
+ undoRedoInfo.d->text = QString::null;
+ }
+ int oldLen = undoRedoInfo.d->text.length();
+ if ( !doc->preProcessor() ) {
+ QString txt = doc->selectedText( QTextDocument::Temp );
+ undoRedoInfo.d->text += txt;
+ for ( int i = 0; i < (int)txt.length(); ++i ) {
+ if ( txt[ i ] != '\n' && oldC.paragraph()->at( oldC.index() )->format() ) {
+ oldC.paragraph()->at( oldC.index() )->format()->addRef();
+ undoRedoInfo.d->text.
+ setFormat( oldLen + i, oldC.paragraph()->at( oldC.index() )->format(), TRUE );
+ }
+ oldC.gotoNextLetter();
+ }
+ }
+ undoRedoInfo.clear();
+ removeSelection( QTextDocument::Temp );
+ }
+
+ formatMore();
+ setModified();
+ emit textChanged();
+ repaintChanged();
+ ensureCursorVisible();
+ return;
+ }
+ } else {
+#if defined(Q_OS_WIN32)
+ // Need to convert CRLF to LF
+ t.replace( "\r\n", "\n" );
+#elif defined(Q_OS_MAC)
+ //need to convert CR to LF
+ t.replace( '\r', '\n' );
+#endif
+ QChar *uc = (QChar *)t.unicode();
+ for ( int i=0; (uint) i<t.length(); i++ ) {
+ if ( uc[ i ] < ' ' && uc[ i ] != '\n' && uc[ i ] != '\t' )
+ uc[ i ] = ' ';
+ }
+ 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 QTextEdit::pasteSpecial( const QPoint& pt )
+{
+ QCString st = pickSpecial( QApplication::clipboard()->data( d->clipboard_mode ),
+ TRUE, pt );
+ if ( !st.isEmpty() )
+ pasteSubType( st );
+}
+#endif
+#ifndef QT_NO_MIME
+QCString QTextEdit::pickSpecial( QMimeSource* ms, bool always_ask, const QPoint& pt )
+{
+ if ( ms ) {
+#ifndef QT_NO_POPUPMENU
+ QPopupMenu popup( this, "qt_pickspecial_menu" );
+ QString fmt;
+ int n = 0;
+ QDict<void> done;
+ for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) {
+ int semi = fmt.find( ";" );
+ if ( semi >= 0 )
+ fmt = fmt.left( semi );
+ if ( fmt.left( 5 ) == "text/" ) {
+ fmt = fmt.mid( 5 );
+ if ( !done.find( fmt ) ) {
+ done.insert( fmt,(void*)1 );
+ popup.insertItem( fmt, i );
+ n++;
+ }
+ }
+ }
+ if ( n ) {
+ int i = n ==1 && !always_ask ? popup.idAt( 0 ) : popup.exec( pt );
+ if ( i >= 0 )
+ return popup.text(i).latin1();
+ }
+#else
+ QString fmt;
+ for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) {
+ int semi = fmt.find( ";" );
+ if ( semi >= 0 )
+ fmt = fmt.left( semi );
+ if ( fmt.left( 5 ) == "text/" ) {
+ fmt = fmt.mid( 5 );
+ return fmt.latin1();
+ }
+ }
+#endif
+ }
+ return QCString();
+}
+#endif // QT_NO_MIME
+#endif // QT_NO_CLIPBOARD
+
+/*!
+ \enum QTextEdit::WordWrap
+
+ This enum defines the QTextEdit's word wrap modes.
+
+ \value NoWrap Do not wrap the text.
+
+ \value WidgetWidth Wrap the text at the current width of the
+ widget (this is the default). Wrapping is at whitespace by
+ default; this can be changed with setWrapPolicy().
+
+ \value FixedPixelWidth Wrap the text at a fixed number of pixels
+ from the widget's left side. The number of pixels is set with
+ wrapColumnOrWidth().
+
+ \value FixedColumnWidth Wrap the text at a fixed number of
+ character columns from the widget's left side. The number of
+ characters is set with wrapColumnOrWidth(). This is useful if you
+ need formatted text that can also be displayed gracefully on
+ devices with monospaced fonts, for example a standard VT100
+ terminal, where you might set wrapColumnOrWidth() to 80.
+
+ \sa setWordWrap() wordWrap()
+*/
+
+/*!
+ \property QTextEdit::wordWrap
+ \brief the word wrap mode
+
+ The default mode is \c WidgetWidth which causes words to be
+ wrapped at the right edge of the text edit. Wrapping occurs at
+ whitespace, keeping whole words intact. If you want wrapping to
+ occur within words use setWrapPolicy(). If you set a wrap mode of
+ \c FixedPixelWidth or \c FixedColumnWidth you should also call
+ setWrapColumnOrWidth() with the width you want.
+
+ \sa WordWrap, wrapColumnOrWidth, wrapPolicy,
+*/
+
+void QTextEdit::setWordWrap( WordWrap mode )
+{
+ if ( wrapMode == mode )
+ return;
+ wrapMode = mode;
+ switch ( mode ) {
+ case NoWrap:
+ document()->formatter()->setWrapEnabled( FALSE );
+ document()->formatter()->setWrapAtColumn( -1 );
+ doc->setWidth( visibleWidth() );
+ doc->setMinimumWidth( -1 );
+ doc->invalidate();
+ updateContents();
+ 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
+}
+
+QTextEdit::WordWrap QTextEdit::wordWrap() const
+{
+ return wrapMode;
+}
+
+/*!
+ \property QTextEdit::wrapColumnOrWidth
+ \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
+
+ If the wrap mode is \c FixedPixelWidth, the value is the number of
+ pixels from the left edge of the text edit at which text should be
+ wrapped. If the wrap mode is \c FixedColumnWidth, the value is the
+ column number (in character columns) from the left edge of the
+ text edit at which text should be wrapped.
+
+ \sa wordWrap
+*/
+void QTextEdit::setWrapColumnOrWidth( int value )
+{
+ wrapWidth = value;
+ if ( wrapMode == FixedColumnWidth ) {
+ document()->formatter()->setWrapAtColumn( wrapWidth );
+ resizeContents( 0, 0 );
+ doc->setWidth( visibleWidth() );
+ doc->setMinimumWidth( -1 );
+ } else if (wrapMode == FixedPixelWidth ) {
+ document()->formatter()->setWrapAtColumn( -1 );
+ resizeContents( wrapWidth, 0 );
+ doc->setWidth( wrapWidth );
+ doc->setMinimumWidth( wrapWidth );
+ } else {
+ return;
+ }
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+int QTextEdit::wrapColumnOrWidth() const
+{
+ if ( wrapMode == WidgetWidth )
+ return visibleWidth();
+ return wrapWidth;
+}
+
+
+/*!
+ \enum QTextEdit::WrapPolicy
+
+ This enum defines where text can be wrapped in word wrap mode.
+
+ \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 QTextEdit::wrapPolicy
+ \brief the word wrap policy, at whitespace or anywhere
+
+ Defines where text can be wrapped when word wrap mode is not \c
+ NoWrap. The choices are \c AtWordBoundary (the default), \c
+ Anywhere and \c AtWordOrDocumentBoundary
+
+ \sa wordWrap
+*/
+
+void QTextEdit::setWrapPolicy( WrapPolicy policy )
+{
+ if ( wPolicy == policy )
+ return;
+ wPolicy = policy;
+ QTextFormatter *formatter;
+ if ( policy == AtWordBoundary || policy == AtWordOrDocumentBoundary ) {
+ formatter = new QTextFormatterBreakWords;
+ formatter->setAllowBreakInWords( policy == AtWordOrDocumentBoundary );
+ } else {
+ formatter = new QTextFormatterBreakInWords;
+ }
+ formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() );
+ formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) );
+ document()->setFormatter( formatter );
+ doc->invalidate();
+ updateContents();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+}
+
+QTextEdit::WrapPolicy QTextEdit::wrapPolicy() const
+{
+ return wPolicy;
+}
+
+/*!
+ Deletes all the text in the text edit.
+
+ \sa cut() removeSelectedText() setText()
+*/
+
+void QTextEdit::clear()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ optimSetText("");
+ } else
+#endif
+ {
+ // make clear undoable
+ doc->selectAll( QTextDocument::Temp );
+ removeSelectedText( QTextDocument::Temp );
+ setContentsPos( 0, 0 );
+ if ( cursor->isValid() )
+ cursor->restoreState();
+ doc->clear( TRUE );
+ delete cursor;
+ cursor = new QTextCursor( doc );
+ lastFormatted = 0;
+ }
+ updateContents();
+
+ emit cursorPositionChanged( cursor );
+ emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() );
+}
+
+int QTextEdit::undoDepth() const
+{
+ return document()->undoDepth();
+}
+
+/*!
+ \property QTextEdit::length
+ \brief the number of characters in the text
+*/
+
+int QTextEdit::length() const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode )
+ return d->od->len;
+ else
+#endif
+ return document()->length();
+}
+
+/*!
+ \property QTextEdit::tabStopWidth
+ \brief the tab stop width in pixels
+*/
+
+int QTextEdit::tabStopWidth() const
+{
+ return document()->tabStopWidth();
+}
+
+void QTextEdit::setUndoDepth( int d )
+{
+ document()->setUndoDepth( d );
+}
+
+void QTextEdit::setTabStopWidth( int ts )
+{
+ document()->setTabStops( ts );
+ doc->invalidate();
+ lastFormatted = doc->firstParagraph();
+ interval = 0;
+ formatMore();
+ updateContents();
+}
+
+/*!
+ \reimp
+*/
+
+QSize QTextEdit::sizeHint() const
+{
+ // cf. QScrollView::sizeHint()
+ constPolish();
+ int f = 2 * frameWidth();
+ int h = fontMetrics().height();
+ QSize sz( f, f );
+ return sz.expandedTo( QSize(12 * h, 8 * h) );
+}
+
+void QTextEdit::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 QTextEdit::getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment )
+{
+ if ( !font || !color )
+ return FALSE;
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return FALSE;
+ if ( index < 0 || index >= p->length() )
+ return FALSE;
+ *font = p->at( index )->format()->font();
+ *color = p->at( index )->format()->color();
+ *verticalAlignment = (VerticalAlignment)p->at( index )->format()->vAlign();
+ return TRUE;
+}
+
+/*! \internal
+ \warning In Qt 3.1 we will provide a cleaer API for the
+ functionality which is provided by this function and in Qt 4.0 this
+ function will go away.
+
+ This function gets the format of the paragraph \a para. Sets \a
+ font to the paragraphs's font, \a color to the paragraph's color, \a
+ verticalAlignment to the paragraph's vertical alignment, \a
+ alignment to the paragraph's alignment, \a displayMode to the
+ paragraph's display mode, \a listStyle to the paragraph's list style
+ (if the display mode is QStyleSheetItem::DisplayListItem) and \a
+ listDepth to the depth of the list (if the display mode is
+ QStyleSheetItem::DisplayListItem).
+
+ Returns FALSE if \a para is out of range otherwise returns TRUE.
+*/
+
+bool QTextEdit::getParagraphFormat( int para, QFont *font, QColor *color,
+ VerticalAlignment *verticalAlignment, int *alignment,
+ QStyleSheetItem::DisplayMode *displayMode,
+ QStyleSheetItem::ListStyle *listStyle,
+ int *listDepth )
+{
+ if ( !font || !color || !alignment || !displayMode || !listStyle )
+ return FALSE;
+ QTextParagraph *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() ? QStyleSheetItem::DisplayListItem : QStyleSheetItem::DisplayBlock;
+ *listStyle = p->listStyle();
+ *listDepth = p->listDepth();
+ return TRUE;
+}
+
+
+
+/*!
+ This function is called to create a right mouse button popup menu
+ at the document position \a pos. If you want to create a custom
+ popup menu, reimplement this function and return the created popup
+ menu. Ownership of the popup menu is transferred to the caller.
+
+ \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.
+*/
+
+QPopupMenu *QTextEdit::createPopupMenu( const QPoint& pos )
+{
+ Q_UNUSED( pos )
+#ifndef QT_NO_POPUPMENU
+ QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" );
+ if ( !isReadOnly() ) {
+ d->id[ IdUndo ] = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) );
+ d->id[ IdRedo ] = popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) );
+ popup->insertSeparator();
+ }
+#ifndef QT_NO_CLIPBOARD
+ if ( !isReadOnly() )
+ d->id[ IdCut ] = popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) );
+ d->id[ IdCopy ] = popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) );
+ if ( !isReadOnly() )
+ d->id[ IdPaste ] = popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) );
+#endif
+ if ( !isReadOnly() ) {
+ d->id[ IdClear ] = popup->insertItem( tr( "Clear" ) );
+ popup->insertSeparator();
+ }
+#if defined(Q_WS_X11)
+ d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) );
+#else
+ d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) + ACCEL_KEY( A ) );
+#endif
+
+#ifndef QT_NO_IM
+ QInputContext *qic = getInputContext();
+ if ( qic )
+ qic->addMenusTo( popup );
+#endif
+
+ popup->setItemEnabled( d->id[ IdUndo ], !isReadOnly() && doc->commands()->isUndoAvailable() );
+ popup->setItemEnabled( d->id[ IdRedo ], !isReadOnly() && doc->commands()->isRedoAvailable() );
+#ifndef QT_NO_CLIPBOARD
+ popup->setItemEnabled( d->id[ IdCut ], !isReadOnly() && doc->hasSelection( QTextDocument::Standard, TRUE ) );
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ popup->setItemEnabled( d->id[ IdCopy ], d->optimMode ? optimHasSelection() : doc->hasSelection( QTextDocument::Standard, TRUE ) );
+#else
+ popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( QTextDocument::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
+ \obsolete
+ This function is called to create a right mouse button popup menu.
+ If you want to create a custom popup menu, reimplement this function
+ and return the created popup menu. Ownership of the popup menu is
+ transferred to the caller.
+
+ This function is only called if createPopupMenu( const QPoint & )
+ returns 0.
+*/
+
+QPopupMenu *QTextEdit::createPopupMenu()
+{
+ return 0;
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::setFont( const QFont &f )
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ QScrollView::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
+ QScrollView::setFont( f );
+ doc->setMinimumWidth( -1 );
+ doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() );
+ lastFormatted = doc->firstParagraph();
+ formatMore();
+ repaintChanged();
+}
+
+/*!
+ \fn QTextEdit::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 QTextEdit::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 QTextEdit::zoomIn( int range )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( QFontInfo(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 QTextEdit::zoomOut( int range )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( QMAX( 1, QFontInfo(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 QTextEdit::zoomTo( int size )
+{
+ QFont f( QScrollView::font() );
+ f.setPointSize( size );
+ setFont( f );
+}
+
+/*!
+ QTextEdit is optimized for large amounts text. One of its
+ optimizations is to format only the visible text, formatting the rest
+ on demand, e.g. as the user scrolls, so you don't usually need to
+ call this function.
+
+ In some situations you may want to force the whole text
+ to be formatted. For example, if after calling setText(), you wanted
+ to know the height of the document (using contentsHeight()), you
+ would call this function first.
+*/
+
+void QTextEdit::sync()
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ QFontMetrics fm( QScrollView::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();
+}
+
+/*!
+ \reimp
+*/
+
+void QTextEdit::setEnabled( bool b )
+{
+ QScrollView::setEnabled( b );
+ if ( textFormat() == PlainText ) {
+ QTextFormat *f = doc->formatCollection()->defaultFormat();
+ f->setColor( colorGroup().text() );
+ updateContents();
+ }
+}
+
+/*!
+ 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
+ colorGroup().
+*/
+
+void QTextEdit::setSelectionAttributes( int selNum, const QColor &back, bool invertText )
+{
+ if ( selNum < 1 )
+ return;
+ if ( selNum > doc->numSelections() )
+ doc->addSelection( selNum );
+ doc->setSelectionColor( selNum, back );
+ doc->setInvertSelectionText( selNum, invertText );
+}
+
+/*!
+ \reimp
+*/
+void QTextEdit::windowActivationChange( bool oldActive )
+{
+ if ( oldActive && scrollTimer )
+ scrollTimer->stop();
+ if ( palette().active() != palette().inactive() )
+ updateContents();
+ QScrollView::windowActivationChange( oldActive );
+}
+
+void QTextEdit::setReadOnly( bool b )
+{
+ if ( (bool) readonly == b )
+ return;
+ readonly = b;
+#ifndef QT_NO_CURSOR
+ if ( readonly )
+ viewport()->setCursor( arrowCursor );
+ else
+ viewport()->setCursor( ibeamCursor );
+ setInputMethodEnabled( !readonly );
+#endif
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ checkOptimMode();
+#endif
+}
+
+/*!
+ Scrolls to the bottom of the document and does formatting if
+ required.
+*/
+
+void QTextEdit::scrollToBottom()
+{
+ sync();
+ setContentsPos( contentsX(), contentsHeight() - visibleHeight() );
+}
+
+/*!
+ Returns the rectangle of the paragraph \a para in contents
+ coordinates, or an invalid rectangle if \a para is out of range.
+*/
+
+QRect QTextEdit::paragraphRect( int para ) const
+{
+ QTextEdit *that = (QTextEdit *)this;
+ that->sync();
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return QRect( -1, -1, -1, -1 );
+ return p->rect();
+}
+
+/*!
+ Returns the paragraph which is at position \a pos (in contents
+ coordinates).
+*/
+
+int QTextEdit::paragraphAt( const QPoint &pos ) const
+{
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+ if ( d->optimMode ) {
+ QFontMetrics fm( QScrollView::font() );
+ int parag = pos.y() / fm.lineSpacing();
+ if ( parag <= d->od->numLines )
+ return parag;
+ else
+ return 0;
+ }
+#endif
+ QTextCursor 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,
+ \a *para is set to the character's paragraph.
+*/
+
+int QTextEdit::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
+ QTextCursor 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 QTextEdit::setParagraphBackgroundColor( int para, const QColor &bg )
+{
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ p->setBackgroundColor( bg );
+ repaintChanged();
+}
+
+/*!
+ Clears the background color of the paragraph \a para, so that the
+ default color is used again.
+*/
+
+void QTextEdit::clearParagraphBackground( int para )
+{
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return;
+ p->clearBackgroundColor();
+ repaintChanged();
+}
+
+/*!
+ Returns the background color of the paragraph \a para or an
+ invalid color if \a para is out of range or the paragraph has no
+ background set
+*/
+
+QColor QTextEdit::paragraphBackgroundColor( int para ) const
+{
+ QTextParagraph *p = doc->paragAt( para );
+ if ( !p )
+ return QColor();
+ QColor *c = p->backgroundColor();
+ if ( c )
+ return *c;
+ return QColor();
+}
+
+/*!
+ \property QTextEdit::undoRedoEnabled
+ \brief whether undo/redo is enabled
+
+ When changing this property, the undo/redo history is cleared.
+
+ The default is TRUE.
+*/
+
+void QTextEdit::setUndoRedoEnabled( bool b )
+{
+ undoRedoInfo.clear();
+ doc->commands()->clear();
+
+ undoEnabled = b;
+}
+
+bool QTextEdit::isUndoRedoEnabled() const
+{
+ return undoEnabled;
+}
+
+/*!
+ Returns TRUE if undo is available; otherwise returns FALSE.
+*/
+
+bool QTextEdit::isUndoAvailable() const
+{
+ return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid());
+}
+
+/*!
+ Returns TRUE if redo is available; otherwise returns FALSE.
+*/
+
+bool QTextEdit::isRedoAvailable() const
+{
+ return undoEnabled && doc->commands()->isRedoAvailable();
+}
+
+void QTextEdit::ensureFormatted( QTextParagraph *p )
+{
+ while ( !p->isValid() ) {
+ if ( !lastFormatted )
+ return;
+ formatMore();
+ }
+}
+
+/*! \internal */
+void QTextEdit::updateCursor( const QPoint & pos )
+{
+ if ( isReadOnly() && linksEnabled() ) {
+ QTextCursor c = *cursor;
+ placeCursor( pos, &c, TRUE );
+
+#ifndef QT_NO_NETWORKPROTOCOL
+ 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 = QString::null;
+
+ 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 = QString::null;
+
+ if (!c.paragraph()->at( c.index() )->anchorHref().isEmpty() ) {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor );
+#endif
+ QUrl u( doc->context(), onLink, TRUE );
+ emitHighlighted( u.toString( FALSE, FALSE ) );
+ }
+ } else {
+#ifndef QT_NO_CURSOR
+ viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor );
+#endif
+ onLink = QString::null;
+ emitHighlighted( QString::null );
+ }
+#endif
+ }
+}
+
+/*!
+ Places the cursor \a c at the character which is closest to position
+ \a pos (in contents coordinates). If \a c is 0, the default text
+ cursor is used.
+
+ \sa setCursorPosition()
+*/
+void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c )
+{
+ placeCursor( pos, c, FALSE );
+}
+
+/*! \internal */
+void QTextEdit::clipboardChanged()
+{
+#ifndef QT_NO_CLIPBOARD
+ // don't listen to selection changes
+ disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
+#endif
+ selectAll(FALSE);
+}
+
+/*! \property QTextEdit::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 QTextEdit::setTabChangesFocus( bool b )
+{
+ d->tabChangesFocus = b;
+}
+
+bool QTextEdit::tabChangesFocus() const
+{
+ return d->tabChangesFocus;
+}
+
+#ifdef QT_TEXTEDIT_OPTIMIZATION
+/* Implementation of optimized LogText mode follows */
+
+static void qSwap( int * a, int * b )
+{
+ if ( !a || !b )
+ return;
+ int tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/*! \internal */
+bool QTextEdit::checkOptimMode()
+{
+ bool oldMode = d->optimMode;
+ if ( textFormat() == LogText ) {
+ setReadOnly( TRUE );
+ d->optimMode = TRUE;
+ } else {
+ d->optimMode = FALSE;
+ }
+
+ // when changing mode - try to keep selections and text
+ if ( oldMode != d->optimMode ) {
+ if ( d->optimMode ) {
+ d->od = new QTextEditOptimPrivate;
+ 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 QTextCursor( 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 QTextEdit::optimText() const
+{
+ QString str, tmp;
+
+ if ( d->od->len == 0 )
+ return str;
+
+ // concatenate all strings
+ int i;
+ int offset;
+ QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it;
+ QTextEditOptimPrivate::Tag * ftag = 0;
+ for ( i = 0; i < d->od->numLines; i++ ) {
+ if ( d->od->lines[ LOGOFFSET(i) ].isEmpty() ) { // CR lines are empty
+ str += "\n";
+ } else {
+ tmp = d->od->lines[ LOGOFFSET(i) ] + "\n";
+ // inject the tags for this line
+ if ( (it = d->od->tagIndex.find( LOGOFFSET(i) )) != d->od->tagIndex.end() )
+ ftag = it.data();
+ offset = 0;
+ while ( ftag && ftag->line == i ) {
+ tmp.insert( ftag->index + offset, "<" + ftag->tag + ">" );
+ offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars
+ ftag = ftag->next;
+ }
+ str += tmp;
+ }
+ }
+ return str;
+}
+
+/*! \internal */
+void QTextEdit::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( QScrollView::font() );
+ if ( !(str.isEmpty() || str.isNull() || d->maxLogLines == 0) ) {
+ QStringList strl = QStringList::split( '\n', str, TRUE );
+ 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.
+*/
+QTextEditOptimPrivate::Tag * QTextEdit::optimAppendTag( int index, const QString & tag )
+{
+ QTextEditOptimPrivate::Tag * t = new QTextEditOptimPrivate::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.replace( LOGOFFSET(t->line), t );
+ }
+ return t;
+}
+
+ /*! \internal
+
+ Insert \a tag in the tag - according to line and index numbers
+*/
+
+QTextEditOptimPrivate::Tag *QTextEdit::optimInsertTag(int line, int index, const QString &tag)
+{
+ QTextEditOptimPrivate::Tag *t = new QTextEditOptimPrivate::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,QTextEditOptimPrivate::Tag *>::ConstIterator it;
+ if ((it = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
+ tmp = *it;
+ if (tmp->index >= index) { // the exisiting 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.replace(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 QTextEdit::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;
+ QPtrStack<QTextEditOptimPrivate::Tag> tagStack;
+
+ for ( i = 0; i < len; i++ ) {
+ tagOpen = (*line)[i] == '<';
+ tagClose = (*line)[i] == '>';
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ( (*line)[i] == '&' ) {
+ escIndex = i;
+ continue;
+ } else if ( escIndex != -1 && (*line)[i] == ';' ) {
+ QString esc = line->mid( escIndex, i - escIndex + 1 );
+ QString c;
+ if ( esc == "&lt;" )
+ c = '<';
+ else if ( esc == "&gt;" )
+ c = '>';
+ else if ( esc == "&amp;" )
+ c = '&';
+ 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() ) {
+ QTextEditOptimPrivate::Tag * tag, * cur, * tmp;
+ bool format = TRUE;
+
+ if ( tagStr == "b" )
+ bold++;
+ else if ( tagStr == "/b" )
+ bold--;
+ else if ( tagStr == "i" )
+ italic++;
+ else if ( tagStr == "/i" )
+ italic--;
+ else if ( tagStr == "u" )
+ underline++;
+ else if ( tagStr == "/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 ? QTextEditOptimPrivate::Format
+ : QTextEditOptimPrivate::Color;
+ if ( tagStr[0] == '/' ) {
+ // this is a right-tag - search for the left-tag
+ // and possible parent tag
+ cur = tag->prev;
+ if ( !cur ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QTextEdit::optimParseTags: no left-tag for '<%s>' in line %d.", tag->tag.ascii(), tag->line + 1 );
+#endif
+ return; // something is wrong - give up
+ }
+ while ( cur ) {
+ if ( cur->leftTag ) { // push right-tags encountered
+ tagStack.push( cur );
+ } else {
+ tmp = tagStack.pop();
+ if ( !tmp ) {
+ if ( (("/" + cur->tag) == tag->tag) ||
+ (tag->tag == "/font" && cur->tag.left(4) == "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 ) {
+#ifdef QT_CHECK_RANGE
+ qWarning( "QTextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.", cur->tag[0] == '/' ? "left" : "right", cur->tag.ascii(), cur->line + 1 );
+#endif
+ 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 = "";
+ 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.contains('\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.find('\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 QTextEdit::optimHasBoldMetrics(int line)
+{
+ QTextEditOptimPrivate::Tag *t;
+ QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it;
+ if ((it = d->od->tagIndex.find(line)) != d->od->tagIndex.end()) {
+ 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 QTextEdit::optimAppend( const QString &str )
+{
+ if ( str.isEmpty() || str.isNull() || d->maxLogLines == 0 )
+ return;
+
+ QStringList strl = QStringList::split( '\n', str, TRUE );
+ QStringList::Iterator it = strl.begin();
+
+ QFontMetrics fm(QScrollView::font());
+ int lWidth = 0;
+
+ for ( ; it != strl.end(); ++it ) {
+ optimParseTags( &*it );
+ optimCheckLimit( *it );
+ if (optimHasBoldMetrics(d->od->numLines-1)) {
+ QFont fnt = QScrollView::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] == '<';
+ tagClose = (*line)[i] == '>';
+
+ // handle '&lt;' and '&gt;' and '&amp;'
+ if ( (*line)[i] == '&' ) {
+ escIndex = i;
+ continue;
+ } else if ( escIndex != -1 && (*line)[i] == ';' ) {
+ QString esc = line->mid( escIndex, i - escIndex + 1 );
+ QString c;
+ if ( esc == "&lt;" )
+ c = '<';
+ else if ( esc == "&gt;" )
+ c = '>';
+ else if ( esc == "&amp;" )
+ c = '&';
+ 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 QTextEdit::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 > (int) d->od->lines[line].length())
+ index = d->od->lines[line].length();
+
+ QStringList strl = QStringList::split('\n', text, TRUE);
+ int numNewLines = (int)strl.count() - 1;
+ QTextEditOptimPrivate::Tag *tag = 0;
+ QMap<int,QTextEditOptimPrivate::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.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
+ 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.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
+ tag = ii.data();
+ 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.find(LOGOFFSET(tag->line))) != d->od->tagIndex.end()))
+ 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.find(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.end()) {
+ 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(QScrollView::font());
+ int lWidth = 0;
+
+ for (x = line; x < line + numNewLines; x++) {
+ if (optimHasBoldMetrics(x)) {
+ QFont fnt = QScrollView::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.
+ */
+QTextEditOptimPrivate::Tag * QTextEdit::optimPreviousLeftTag( int line )
+{
+ QTextEditOptimPrivate::Tag * ftag = 0;
+ QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it;
+ if ( (it = d->od->tagIndex.find( LOGOFFSET(line) )) != d->od->tagIndex.end() )
+ ftag = it.data();
+ 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 QTextEdit::optimSetTextFormat( QTextDocument * td, QTextCursor * cur,
+ QTextFormat * f, int start, int end,
+ QTextEditOptimPrivate::Tag * tag )
+{
+ int formatFlags = QTextFormat::Bold | QTextFormat::Italic |
+ QTextFormat::Underline;
+ cur->setIndex( start );
+ td->setSelectionStart( 0, *cur );
+ cur->setIndex( end );
+ td->setSelectionEnd( 0, *cur );
+ QStyleSheetItem * ssItem = styleSheet()->item( tag->tag );
+ if ( !ssItem || tag->type == QTextEditOptimPrivate::Format ) {
+ f->setBold( tag->bold );
+ f->setItalic( tag->italic );
+ f->setUnderline( tag->underline );
+ if ( tag->type == QTextEditOptimPrivate::Format ) {
+ // check to see if there are any open color tags prior to
+ // this format tag
+ tag = tag->prev;
+ while ( tag && (tag->type == QTextEditOptimPrivate::Format ||
+ tag->leftTag) ) {
+ tag = tag->leftTag ? tag->parent : tag->prev;
+ }
+ }
+ if ( tag ) {
+ QString col = tag->tag.simplifyWhiteSpace();
+ if ( col.left( 10 ) == "font color" ) {
+ int i = col.find( '=', 10 );
+ col = col.mid( i + 1 ).simplifyWhiteSpace();
+ if ( col[0] == '\"' )
+ col = col.mid( 1, col.length() - 2 );
+ }
+ QColor color = QColor( col );
+ if ( color.isValid() ) {
+ formatFlags |= QTextFormat::Color;
+ f->setColor( color );
+ }
+ }
+ } else { // use the stylesheet tag definition
+ if ( ssItem->color().isValid() ) {
+ formatFlags |= QTextFormat::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 QTextEdit::optimDrawContents( QPainter * p, int clipx, int clipy,
+ int clipw, int cliph )
+{
+ QFontMetrics fm( QScrollView::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) ] + "\n" );
+
+ QTextDocument * td = new QTextDocument( 0 );
+ td->setDefaultFormat( QScrollView::font(), QColor() );
+ td->setPlainText( str );
+ td->setFormatter( new QTextFormatterBreakWords ); // deleted by QTextDoc
+ td->formatter()->setWrapEnabled( FALSE );
+ td->setTabStops(doc->tabStopWidth());
+
+ // get the current text color from the current format
+ td->selectAll( QTextDocument::Standard );
+ QTextFormat f;
+ f.setColor( colorGroup().text() );
+ f.setFont( QScrollView::font() );
+ td->setFormat( QTextDocument::Standard, &f,
+ QTextFormat::Color | QTextFormat::Font );
+ td->removeSelection( QTextDocument::Standard );
+
+ // add tag formatting
+ if ( d->od->tags ) {
+ int i = startLine;
+ QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it;
+ QTextEditOptimPrivate::Tag * tag = 0, * tmp = 0;
+ QTextCursor cur( td );
+ // Step 1 - find previous left-tag
+ tmp = optimPreviousLeftTag( i );
+ for ( ; i < startLine + nLines; i++ ) {
+ if ( (it = d->od->tagIndex.find( LOGOFFSET(i) )) != d->od->tagIndex.end() )
+ tag = it.data();
+ // 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() ) {
+ QTextCursor c1( td );
+ QTextCursor 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);
+ QTextParagraph * parag = td->paragAt( paragS );
+ if ( parag ) {
+ c1.setParagraph( parag );
+ if ( td->text( paragS ).length() >= (uint) idxStart )
+ c1.setIndex( idxStart );
+ }
+ parag = td->paragAt( paragE );
+ if ( parag ) {
+ c2.setParagraph( parag );
+ if ( td->text( paragE ).length() >= (uint) idxEnd )
+ c2.setIndex( idxEnd );
+ }
+ } else if ( startLine > selStart && endLine < selEnd ) {
+ // case 2: area to paint is all part of the selection
+ td->selectAll( QTextDocument::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;
+ QTextParagraph * parag = td->paragAt( paragE );
+ if ( parag ) {
+ c2.setParagraph( parag );
+ if ( td->text( paragE ).length() >= (uint) 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;
+ QTextParagraph * 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( QTextDocument::Standard ) ) {
+ td->setSelectionStart( QTextDocument::Standard, c1 );
+ td->setSelectionEnd( QTextDocument::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(), colorGroup() );
+ p->translate( 0, -(clipy - offset) );
+ delete td;
+}
+
+/*! \internal */
+void QTextEdit::optimMousePressEvent( QMouseEvent * e )
+{
+ if ( e->button() != LeftButton )
+ return;
+
+ QFontMetrics fm( QScrollView::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( FALSE );
+}
+
+/*! \internal */
+void QTextEdit::optimMouseReleaseEvent( QMouseEvent * e )
+{
+ if ( e->button() != LeftButton )
+ return;
+
+ if ( scrollTimer->isActive() )
+ scrollTimer->stop();
+ if ( !inDoubleClick ) {
+ QFontMetrics fm( QScrollView::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( FALSE );
+ }
+ if ( mousePressed ) {
+ mousePressed = FALSE;
+ copyToClipboard();
+ }
+
+ inDoubleClick = FALSE;
+ emit copyAvailable( optimHasSelection() );
+ emit selectionChanged();
+}
+
+/*! \internal */
+void QTextEdit::optimMouseMoveEvent( QMouseEvent * e )
+{
+ mousePos = e->pos();
+ optimDoAutoScroll();
+ oldMousePos = mousePos;
+}
+
+/*! \internal */
+void QTextEdit::optimDoAutoScroll()
+{
+ if ( !mousePressed )
+ return;
+
+ QFontMetrics fm( QScrollView::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( 'w');
+ else if ( pos.x() > viewport()->width() )
+ xx = contentsX() + viewport()->width() + fm.width('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 heigth 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(), FALSE );
+ } 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, FALSE );
+ }
+
+ 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 QTextEdit::optimCharIndex( const QString &str, int mx ) const
+{
+ QFontMetrics fm(QScrollView::font());
+ uint i = 0;
+ int dd, dist = 10000000;
+ int curpos = 0;
+ int strWidth;
+ mx = mx - 4; // ### get the real margin from somewhere
+
+ if (!str.contains('\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 QTextEdit::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( FALSE );
+ emit copyAvailable( optimHasSelection() );
+ emit selectionChanged();
+}
+
+/*! \internal */
+void QTextEdit::optimRemoveSelection()
+{
+ d->od->selStart.line = d->od->selEnd.line = -1;
+ d->od->selStart.index = d->od->selEnd.index = -1;
+ repaintContents( FALSE );
+}
+
+/*! \internal */
+void QTextEdit::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 QTextEdit::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 QTextEdit::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 ) + "\n";
+ i++;
+ for ( ; i < d->od->selEnd.line; i++ ) {
+ if ( d->od->lines[ LOGOFFSET(i) ].isEmpty() ) // CR lines are empty
+ str += "\n";
+ else
+ str += d->od->lines[ LOGOFFSET(i) ] + "\n";
+ }
+ str += d->od->lines[ LOGOFFSET(d->od->selEnd.line) ].left( d->od->selEnd.index );
+ }
+ return str;
+}
+
+/*! \internal */
+bool QTextEdit::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) ].find( expr, idx, cs ) :
+ d->od->lines[ LOGOFFSET(i) ].findRev( expr, idx, cs );
+ 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 + 1;
+ d->od->search.line = i;
+ optimSetSelection( i, idx, i, idx + expr.length() );
+ QFontMetrics fm( QScrollView::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 QTextEdit::polish()
+{
+ // 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();
+ QWidget::polish();
+}
+
+/*!
+ Sets the maximum number of lines a QTextEdit can hold in \c
+ 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 QTextEdit::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 QTextEdit can hold in \c
+ LogText mode. By default the number of lines is unlimited, which
+ is signified by a value of -1.
+ */
+int QTextEdit::maxLogLines()
+{
+ return d->maxLogLines;
+}
+
+/*!
+ Check if the number of lines in the buffer is limited, and uphold
+ that limit when appending new lines.
+ */
+void QTextEdit::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.
+ QTextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr;
+ QPtrList<QTextEditOptimPrivate::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 ( tmp = lst.first(); tmp; tmp = lst.next() ) {
+ if ( itr->parent == tmp )
+ itr->parent = 0;
+ if ( itr->leftTag == tmp )
+ itr->leftTag = 0;
+ }
+ itr = itr->next;
+ }
+ // ...in the tag index as well
+ QMapIterator<int, QTextEditOptimPrivate::Tag *> idx;
+ if ( (idx = d->od->tagIndex.find( d->logOffset )) != d->od->tagIndex.end() )
+ d->od->tagIndex.remove( idx );
+
+ QMapIterator<int,QString> it;
+ if ( (it = d->od->lines.find( d->logOffset )) != d->od->lines.end() ) {
+ d->od->len -= (*it).length();
+ d->od->lines.remove( 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 QTextEdit::autoFormatting
+ \brief the enabled set of auto formatting features
+
+ The value can be any combination of the values in the \c
+ AutoFormatting 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 QTextEdit::setAutoFormatting( uint features )
+{
+ d->autoFormatting = features;
+}
+
+uint QTextEdit::autoFormatting() const
+{
+ return d->autoFormatting;
+}
+
+/*!
+ Returns the QSyntaxHighlighter set on this QTextEdit. 0 is
+ returned if no syntax highlighter is set.
+ */
+QSyntaxHighlighter * QTextEdit::syntaxHighlighter() const
+{
+ if (document()->preProcessor())
+ return ((QSyntaxHighlighterInternal *) document()->preProcessor())->highlighter;
+ else
+ return 0;
+}
+
+#endif //QT_NO_TEXTEDIT