1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the Qt 3 compatibility classes of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "q3textedit.h"
31
#ifndef QT_NO_TEXTEDIT
33
#include <private/q3richtext_p.h>
44
#include "qapplication.h"
45
#include "q3listbox.h"
46
#include "qclipboard.h"
47
#include "qcolordialog.h"
48
#include "q3stylesheet.h"
49
#include "q3dragobject.h"
53
#include "q3popupmenu.h"
55
#include "qmetaobject.h"
56
#include "q3textbrowser.h"
57
#include "private/q3syntaxhighlighter_p.h"
58
#include "qtextformat.h"
61
#include <qkeysequence.h>
62
#define ACCEL_KEY(k) "\t" + QString(QKeySequence(Qt::CTRL | Qt::Key_ ## k))
64
#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
67
#ifdef QT_TEXTEDIT_OPTIMIZATION
68
#define LOGOFFSET(i) d->logOffset + i
71
struct QUndoRedoInfoPrivate
76
class Q3TextEditPrivate
80
:preeditStart(-1),preeditLength(-1),numPreeditSelections(0),ensureCursorVisibleInShowEvent(false),
81
tabChangesFocus(false),
82
#ifndef QT_NO_CLIPBOARD
83
clipboard_mode(QClipboard::Clipboard),
85
#ifdef QT_TEXTEDIT_OPTIMIZATION
86
od(0), optimMode(false),
90
autoFormatting((uint)Q3TextEdit::AutoAll),
91
cursorRepaintMode(false),
92
cursorBlinkActive(false)
95
for (int i=0; i<7; i++)
101
int numPreeditSelections;
102
uint ensureCursorVisibleInShowEvent : 1;
103
uint tabChangesFocus : 1;
104
QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized
107
#ifndef QT_NO_CLIPBOARD
108
QClipboard::Mode clipboard_mode;
110
QTimer *trippleClickTimer;
111
QPoint trippleClickPoint;
112
#ifdef QT_TEXTEDIT_OPTIMIZATION
113
Q3TextEditOptimPrivate * od;
118
Q3TextEdit::AutoFormatting autoFormatting;
119
uint cursorRepaintMode : 1;
120
uint cursorBlinkActive : 1;
124
class Q3RichTextDrag : public Q3TextDrag
127
Q3RichTextDrag(QWidget *dragSource = 0, const char *name = 0);
129
void setPlainText(const QString &txt) { setText(txt); }
130
void setRichText(const QString &txt) { richTxt = txt; }
132
virtual QByteArray encodedData(const char *mime) const;
133
virtual const char* format(int i) const;
135
static bool decode(QMimeSource *e, QString &str, const QString &mimetype,
136
const QString &subtype);
137
static bool canDecode(QMimeSource* e);
144
Q3RichTextDrag::Q3RichTextDrag(QWidget *dragSource, const char *name)
145
: Q3TextDrag(dragSource, name)
149
QByteArray Q3RichTextDrag::encodedData(const char *mime) const
151
if (qstrcmp("application/x-qrichtext", mime) == 0) {
152
return richTxt.toUtf8(); // #### perhaps we should use USC2 instead?
154
return Q3TextDrag::encodedData(mime);
157
bool Q3RichTextDrag::decode(QMimeSource *e, QString &str, const QString &mimetype,
158
const QString &subtype)
160
if (mimetype == "application/x-qrichtext") {
161
// do richtext decode
164
for (i = 0; (mime = e->format(i)); ++i) {
165
if (qstrcmp("application/x-qrichtext", mime) != 0)
167
str = QString::fromUtf8(e->encodedData(mime));
173
// do a regular text decode
174
QString st = subtype;
175
return Q3TextDrag::decode(e, str, st);
178
bool Q3RichTextDrag::canDecode(QMimeSource* e)
180
if (e->provides("application/x-qrichtext"))
182
return Q3TextDrag::canDecode(e);
185
const char* Q3RichTextDrag::format(int i) const
187
if (Q3TextDrag::format(i))
188
return Q3TextDrag::format(i);
189
if (Q3TextDrag::format(i-1))
190
return "application/x-qrichtext";
196
static bool block_set_alignment = false;
199
\class Q3TextEdit qtextedit.h
200
\brief The Q3TextEdit widget provides a powerful single-page rich text editor.
206
\section1 Introduction and Concepts
208
Q3TextEdit is an advanced WYSIWYG viewer/editor supporting rich
209
text formatting using HTML-style tags. It is optimized to handle
210
large documents and to respond quickly to user input.
212
Q3TextEdit has four modes of operation:
214
\header \i Mode \i Command \i Notes
215
\row \i Plain Text Editor \i setTextFormat(Qt::PlainText)
216
\i Set text with setText(); text() returns plain text. Text
217
attributes (e.g. colors) can be set, but plain text is always
219
\row \i Rich Text Editor \i setTextFormat(Qt::RichText)
220
\i Set text with setText(); text() returns rich text. Rich
221
text editing is fairly limited. You can't set margins or
222
insert images for example (although you can read and
223
correctly display files that have margins set and that
224
include images). This mode is mostly useful for editing small
225
amounts of rich text.
226
\row \i Text Viewer \i setReadOnly(true)
227
\i Set text with setText() or append() (which has no undo
228
history so is faster and uses less memory); text() returns
229
plain or rich text depending on the textFormat(). This mode
230
can correctly display a large subset of HTML tags.
231
\row \i Log Viewer \i setTextFormat(Qt::LogText)
232
\i Append text using append(). The widget is set to be read
233
only and rich text support is disabled although a few HTML
234
tags (for color, bold, italic and underline) may be used.
235
(See \link #logtextmode Qt::LogText mode\endlink for details.)
238
Q3TextEdit can be used as a syntax highlighting editor when used in
239
conjunction with QSyntaxHighlighter.
241
We recommend that you always call setTextFormat() to set the mode
242
you want to use. If you use \c Qt::AutoText then setText() and
243
append() will try to determine whether the text they are given is
244
plain text or rich text. If you use \c Qt::RichText then setText() and
245
append() will assume that the text they are given is rich text.
246
insert() simply inserts the text it is given.
248
Q3TextEdit works on paragraphs and characters. A paragraph is a
249
formatted string which is word-wrapped to fit into the width of
250
the widget. By default when reading plain text, one newline
251
signify a paragraph. A document consists of zero or more
252
paragraphs, indexed from 0. Characters are indexed on a
253
per-paragraph basis, also indexed from 0. The words in the
254
paragraph are aligned in accordance with the paragraph's
255
alignment(). Paragraphs are separated by hard line breaks. Each
256
character within a paragraph has its own attributes, for example,
259
The text edit documentation uses the following concepts:
261
\i \e{current format} --
262
this is the format at the current cursor position, \e and it
263
is the format of the selected text if any.
264
\i \e{current paragraph} -- the paragraph which contains the
268
Q3TextEdit can display images (using Q3MimeSourceFactory), lists and
269
tables. If the text is too large to view within the text edit's
270
viewport, scrollbars will appear. The text edit can load both
271
plain text and HTML files (a subset of HTML 3.2 and 4). The
272
rendering style and the set of valid tags are defined by a
273
styleSheet(). Custom tags can be created and placed in a custom
274
style sheet. Change the style sheet with \l{setStyleSheet()}; see
275
Q3StyleSheet for details. The images identified by image tags are
276
displayed if they can be interpreted using the text edit's
277
\l{Q3MimeSourceFactory}; see setMimeSourceFactory().
279
If you want a text browser with more navigation use QTextBrowser.
280
If you just need to display a small piece of rich text use QLabel
283
If you create a new Q3TextEdit, and want to allow the user to edit
284
rich text, call setTextFormat(Qt::RichText) to ensure that the
285
text is treated as rich text. (Rich text uses HTML tags to set
286
text formatting attributes. See Q3StyleSheet for information on the
287
HTML tags that are supported.). If you don't call setTextFormat()
288
explicitly the text edit will guess from the text itself whether
289
it is rich text or plain text. This means that if the text looks
290
like HTML or XML it will probably be interpreted as rich text, so
291
you should call setTextFormat(Qt::PlainText) to preserve such
294
Note that we do not intend to add a full-featured web browser
295
widget to Qt (because that would easily double Qt's size and only
296
a few applications would benefit from it). The rich
297
text support in Qt is designed to provide a fast, portable and
298
efficient way to add reasonable online help facilities to
299
applications, and to provide a basis for rich text editors.
301
\section1 Using Q3TextEdit as a Display Widget
303
Q3TextEdit can display a large HTML subset, including tables and
306
The text is set or replaced using setText() which deletes any
307
existing text and replaces it with the text passed in the
308
setText() call. If you call setText() with legacy HTML (with
309
setTextFormat(Qt::RichText) in force), and then call text(), the text
310
that is returned may have different markup, but will render the
311
same. Text can be inserted with insert(), paste(), pasteSubType()
312
and append(). Text that is appended does not go into the undo
313
history; this makes append() faster and consumes less memory. Text
314
can also be cut(). The entire text is deleted with clear() and the
315
selected text is deleted with removeSelectedText(). Selected
316
(marked) text can also be deleted with del() (which will delete
317
the character to the right of the cursor if no text is selected).
319
Loading and saving text is achieved using setText() and text(),
322
QFile file(fileName); // Read the text from a file
323
if (file.open(IO_ReadOnly)) {
324
QTextStream stream(&file);
325
textEdit->setText(stream.read());
328
QFile file(fileName); // Write the text to a file
329
if (file.open(IO_WriteOnly)) {
330
QTextStream stream(&file);
331
stream << textEdit->text();
332
textEdit->setModified(false);
336
By default the text edit wraps words at whitespace to fit within
337
the text edit widget. The setWordWrap() function is used to
338
specify the kind of word wrap you want, or \c NoWrap if you don't
339
want any wrapping. Call setWordWrap() to set a fixed pixel width
340
\c FixedPixelWidth, or character column (e.g. 80 column) \c
341
FixedColumnWidth with the pixels or columns specified with
342
setWrapColumnOrWidth(). If you use word wrap to the widget's width
343
\c WidgetWidth, you can specify whether to break on whitespace or
344
anywhere with setWrapPolicy().
346
The background color is set differently than other widgets, using
347
setPaper(). You specify a brush style which could be a plain color
350
Hypertext links are automatically underlined; this can be changed
351
with setLinkUnderline(). The tab stop width is set with
354
The zoomIn() and zoomOut() functions can be used to resize the
355
text by increasing (decreasing for zoomOut()) the point size used.
356
Images are not affected by the zoom functions.
358
The lines() function returns the number of lines in the text and
359
paragraphs() returns the number of paragraphs. The number of lines
360
within a particular paragraph is returned by linesOfParagraph().
361
The length of the entire text in characters is returned by
364
You can scroll to an anchor in the text, e.g.
365
\c{<a name="anchor">} with scrollToAnchor(). The find() function
366
can be used to find and select a given string within the text.
368
A read-only Q3TextEdit provides the same functionality as the
369
(obsolete) QTextView. (QTextView is still supplied for
370
compatibility with old code.)
372
\section2 Read-only key bindings
374
When Q3TextEdit is used read-only the key-bindings are limited to
375
navigation, and text may only be selected with the mouse:
377
\header \i Keypresses \i Action
378
\row \i Qt::UpArrow \i Move one line up
379
\row \i Qt::DownArrow \i Move one line down
380
\row \i Qt::LeftArrow \i Move one character left
381
\row \i Qt::RightArrow \i Move one character right
382
\row \i PageUp \i Move one (viewport) page up
383
\row \i PageDown \i Move one (viewport) page down
384
\row \i Home \i Move to the beginning of the text
385
\row \i End \i Move to the end of the text
387
\i Scroll the page horizontally (the Wheel is the mouse wheel)
388
\row \i Ctrl+Wheel \i Zoom the text
391
The text edit may be able to provide some meta-information. For
392
example, the documentTitle() function will return the text from
393
within HTML \c{<title>} tags.
395
The text displayed in a text edit has a \e context. The context is
396
a path which the text edit's Q3MimeSourceFactory uses to resolve
397
the locations of files and images. It is passed to the
398
mimeSourceFactory() when quering data. (See Q3TextEdit() and
402
\section2 Using Q3TextEdit in Qt::LogText Mode
404
Setting the text format to \c Qt::LogText puts the widget in a special
405
mode which is optimized for very large texts. In this mode editing
406
and rich text support are disabled (the widget is explicitly set
407
to read-only mode). This allows the text to be stored in a
408
different, more memory efficient manner. However, a certain degree
409
of text formatting is supported through the use of formatting
410
tags. A tag is delimited by \c < and \c {>}. The characters \c
411
{<}, \c > and \c & are escaped by using \c {<}, \c {>} and
412
\c {&}. A tag pair consists of a left and a right tag (or
413
open/close tags). Left-tags mark the starting point for
414
formatting, while right-tags mark the ending point. A right-tag
415
always start with a \c / before the tag keyword. For example \c
416
<b> and \c </b> are a tag pair. Tags can be nested, but they
417
have to be closed in the same order as they are opened. For
418
example, \c <b><u></u></b> is valid, while \c
419
<b><u></b></u> will output an error message.
421
By using tags it is possible to change the color, bold, italic and
422
underline settings for a piece of text. A color can be specified
423
by using the HTML font tag \c {<font color=colorname>}. The color
424
name can be one of the color names from the X11 color database, or
425
a RGB hex value (e.g \c {#00ff00}). Example of valid color tags:
426
\c {<font color=red>}, \c{<font color="light blue">},\c {<font
427
color="#223344">}. Bold, italic and underline settings can be
428
specified by the tags \c {<b>}, \c <i> and \c {<u>}. Note that a
429
tag does not necessarily have to be closed. A valid example:
431
This is <font color=red>red</font> while <b>this</b> is <font color=blue>blue</font>.
432
<font color=green><font color=yellow>Yellow,</font> and <u>green</u>.
435
Stylesheets can also be used in Qt::LogText mode. To create and use a
436
custom tag, you could do the following:
438
Q3TextEdit * log = new Q3TextEdit(this);
439
log->setTextFormat(Qt::LogText);
440
Q3StyleSheetItem * item = new Q3StyleSheetItem(log->styleSheet(), "mytag");
441
item->setColor("red");
442
item->setFontWeight(QFont::Bold);
443
item->setFontUnderline(true);
444
log->append("This is a <mytag>custom tag</mytag>!");
446
Note that only the color, bold, underline and italic attributes of
447
a Q3StyleSheetItem is used in Qt::LogText mode.
449
Note that you can use setMaxLogLines() to limit the number of
450
lines the widget can hold in Qt::LogText mode.
452
There are a few things that you need to be aware of when the
453
widget is in this mode:
455
\i Functions that deal with rich text formatting and cursor
456
movement will not work or return anything valid.
457
\i Lines are equivalent to paragraphs.
460
\section1 Using Q3TextEdit as an Editor
462
All the information about using Q3TextEdit as a display widget also
465
The current format's attributes are set with setItalic(),
466
setBold(), setUnderline(), setFamily() (font family),
467
setPointSize(), setColor() and setCurrentFont(). The current
468
paragraph's alignment is set with setAlignment().
470
Use setSelection() to select text. The setSelectionAttributes()
471
function is used to indicate how selected text should be
472
displayed. Use hasSelectedText() to find out if any text is
473
selected. The currently selected text's position is available
474
using getSelection() and the selected text itself is returned by
475
selectedText(). The selection can be copied to the clipboard with
476
copy(), or cut to the clipboard with cut(). It can be deleted with
477
removeSelectedText(). The entire text can be selected (or
478
unselected) using selectAll(). Q3TextEdit supports multiple
479
selections. Most of the selection functions operate on the default
480
selection, selection 0. If the user presses a non-selecting key,
481
e.g. a cursor key without also holding down Shift, all selections
484
Set and get the position of the cursor with setCursorPosition()
485
and getCursorPosition() respectively. When the cursor is moved,
486
the signals currentFontChanged(), currentColorChanged() and
487
currentAlignmentChanged() are emitted to reflect the font, color
488
and alignment at the new cursor position.
490
If the text changes, the textChanged() signal is emitted, and if
491
the user inserts a new line by pressing Return or Enter,
492
returnPressed() is emitted. The isModified() function will return
493
true if the text has been modified.
495
Q3TextEdit provides command-based undo and redo. To set the depth
496
of the command history use setUndoDepth() which defaults to 100
497
steps. To undo or redo the last operation call undo() or redo().
498
The signals undoAvailable() and redoAvailable() indicate whether
499
the undo and redo operations can be executed.
501
\section2 Editing key bindings
503
The list of key-bindings which are implemented for editing:
505
\header \i Keypresses \i Action
506
\row \i Backspace \i Delete the character to the left of the cursor
507
\row \i Delete \i Delete the character to the right of the cursor
508
\row \i Ctrl+A \i Move the cursor to the beginning of the line
509
\row \i Ctrl+B \i Move the cursor one character left
510
\row \i Ctrl+C \i Copy the marked text to the clipboard (also
511
Ctrl+Insert under Windows)
512
\row \i Ctrl+D \i Delete the character to the right of the cursor
513
\row \i Ctrl+E \i Move the cursor to the end of the line
514
\row \i Ctrl+F \i Move the cursor one character right
515
\row \i Ctrl+H \i Delete the character to the left of the cursor
516
\row \i Ctrl+K \i Delete to end of line
517
\row \i Ctrl+N \i Move the cursor one line down
518
\row \i Ctrl+P \i Move the cursor one line up
519
\row \i Ctrl+V \i Paste the clipboard text into line edit
520
(also Shift+Insert under Windows)
521
\row \i Ctrl+X \i Cut the marked text, copy to clipboard
522
(also Shift+Delete under Windows)
523
\row \i Ctrl+Z \i Undo the last operation
524
\row \i Ctrl+Y \i Redo the last operation
525
\row \i Qt::LeftArrow \i Move the cursor one character left
526
\row \i Ctrl+Qt::LeftArrow \i Move the cursor one word left
527
\row \i Qt::RightArrow \i Move the cursor one character right
528
\row \i Ctrl+Qt::RightArrow \i Move the cursor one word right
529
\row \i Qt::UpArrow \i Move the cursor one line up
530
\row \i Ctrl+Qt::UpArrow \i Move the cursor one word up
531
\row \i Qt::DownArrow \i Move the cursor one line down
532
\row \i Ctrl+Down Arrow \i Move the cursor one word down
533
\row \i PageUp \i Move the cursor one page up
534
\row \i PageDown \i Move the cursor one page down
535
\row \i Home \i Move the cursor to the beginning of the line
536
\row \i Ctrl+Home \i Move the cursor to the beginning of the text
537
\row \i End \i Move the cursor to the end of the line
538
\row \i Ctrl+End \i Move the cursor to the end of the text
539
\row \i Shift+Wheel \i Scroll the page horizontally
540
(the Wheel is the mouse wheel)
541
\row \i Ctrl+Wheel \i Zoom the text
544
To select (mark) text hold down the Shift key whilst pressing one
545
of the movement keystrokes, for example, \e{Shift+Right Arrow}
546
will select the character to the right, and \e{Shift+Ctrl+Right
547
Arrow} will select the word to the right, etc.
549
By default the text edit widget operates in insert mode so all
550
text that the user enters is inserted into the text edit and any
551
text to the right of the cursor is moved out of the way. The mode
552
can be changed to overwrite, where new text overwrites any text to
553
the right of the cursor, using setOverwriteMode().
557
\enum Q3TextEdit::AutoFormattingFlag
559
\value AutoNone Do not perform any automatic formatting
560
\value AutoBulletList Only automatically format bulletted lists
561
\value AutoAll Apply all available autoformatting
566
\enum Q3TextEdit::KeyboardAction
568
This enum is used by doKeyboardAction() to specify which action
571
\value ActionBackspace Delete the character to the left of the
574
\value ActionDelete Delete the character to the right of the
577
\value ActionReturn Split the paragraph at the cursor position.
579
\value ActionKill If the cursor is not at the end of the
580
paragraph, delete the text from the cursor position until the end
581
of the paragraph. If the cursor is at the end of the paragraph,
582
delete the hard line break at the end of the paragraph: this will
583
cause this paragraph to be joined with the following paragraph.
585
\value ActionWordBackspace Delete the word to the left of the
588
\value ActionWordDelete Delete the word to the right of the
594
\enum Q3TextEdit::VerticalAlignment
596
This enum is used to set the vertical alignment of the text.
598
\value AlignNormal Normal alignment
599
\value AlignSuperScript Superscript
600
\value AlignSubScript Subscript
604
\enum Q3TextEdit::TextInsertionFlags
608
\value RedoIndentation
610
\value RemoveSelected
615
\fn void Q3TextEdit::copyAvailable(bool yes)
617
This signal is emitted when text is selected or de-selected in the
620
When text is selected this signal will be emitted with \a yes set
621
to true. If no text has been selected or if the selected text is
622
de-selected this signal is emitted with \a yes set to false.
624
If \a yes is true then copy() can be used to copy the selection to
625
the clipboard. If \a yes is false then copy() does nothing.
627
\sa selectionChanged()
632
\fn void Q3TextEdit::textChanged()
634
This signal is emitted whenever the text in the text edit changes.
636
\sa setText() append()
640
\fn void Q3TextEdit::selectionChanged()
642
This signal is emitted whenever the selection changes.
644
\sa setSelection() copyAvailable()
647
/*! \fn Q3TextDocument *Q3TextEdit::document() const
651
This function returns the Q3TextDocument which is used by the text
655
/*! \fn void Q3TextEdit::setDocument(Q3TextDocument *doc)
659
This function sets the Q3TextDocument which should be used by the text
660
edit to \a doc. This can be used, for example, if you want to
661
display a document using multiple views. You would create a
662
Q3TextDocument and set it to the text edits which should display it.
663
You would need to connect to the textChanged() and
664
selectionChanged() signals of all the text edits and update them all
665
accordingly (preferably with a slight delay for efficiency reasons).
669
\enum Q3TextEdit::CursorAction
671
This enum is used by moveCursor() to specify in which direction
672
the cursor should be moved:
674
\value MoveBackward Moves the cursor one character backward
676
\value MoveWordBackward Moves the cursor one word backward
678
\value MoveForward Moves the cursor one character forward
680
\value MoveWordForward Moves the cursor one word forward
682
\value MoveUp Moves the cursor up one line
684
\value MoveDown Moves the cursor down one line
686
\value MoveLineStart Moves the cursor to the beginning of the line
688
\value MoveLineEnd Moves the cursor to the end of the line
690
\value MoveHome Moves the cursor to the beginning of the document
692
\value MoveEnd Moves the cursor to the end of the document
694
\value MovePgUp Moves the cursor one viewport page up
696
\value MovePgDown Moves the cursor one viewport page down
700
\property Q3TextEdit::overwriteMode
701
\brief the text edit's overwrite mode
703
If false (the default) characters entered by the user are inserted
704
with any characters to the right being moved out of the way. If
705
true, the editor is in overwrite mode, i.e. characters entered by
706
the user overwrite any characters to the right of the cursor
711
\fn void Q3TextEdit::setCurrentFont(const QFont &f)
713
Sets the font of the current format to \a f.
715
If the widget is in \c Qt::LogText mode this function will do
716
nothing. Use setFont() instead.
718
\sa currentFont() setPointSize() setFamily()
722
\property Q3TextEdit::undoDepth
723
\brief the depth of the undo history
725
The maximum number of steps in the undo/redo history. The default
732
\fn void Q3TextEdit::undoAvailable(bool yes)
734
This signal is emitted when the availability of undo changes. If
735
\a yes is true, then undo() will work until undoAvailable(false)
738
\sa undo() undoDepth()
742
\fn void Q3TextEdit::modificationChanged(bool m)
744
This signal is emitted when the modification status of the
745
document has changed. If \a m is true, the document was modified,
746
otherwise the modification state has been reset to unmodified.
752
\fn void Q3TextEdit::redoAvailable(bool yes)
754
This signal is emitted when the availability of redo changes. If
755
\a yes is true, then redo() will work until redoAvailable(false)
758
\sa redo() undoDepth()
762
\fn void Q3TextEdit::currentFontChanged(const QFont &f)
764
This signal is emitted if the font of the current format has
767
The new font is \a f.
773
\fn void Q3TextEdit::currentColorChanged(const QColor &c)
775
This signal is emitted if the color of the current format has
778
The new color is \a c.
784
\fn void Q3TextEdit::currentVerticalAlignmentChanged(VerticalAlignment a)
786
This signal is emitted if the vertical alignment of the current
789
The new vertical alignment is \a a.
791
\sa setVerticalAlignment()
795
\fn void Q3TextEdit::currentAlignmentChanged(int a)
797
This signal is emitted if the alignment of the current paragraph
800
The new alignment is \a a.
806
\fn void Q3TextEdit::cursorPositionChanged(Q3TextCursor *c)
812
\fn void Q3TextEdit::cursorPositionChanged(int para, int pos)
816
This signal is emitted if the position of the cursor has changed.
817
\a para contains the paragraph index and \a pos contains the
818
character position within the paragraph.
820
\sa setCursorPosition()
824
\fn void Q3TextEdit::clicked(int para, int pos)
826
This signal is emitted when the mouse is clicked on the paragraph
827
\a para at character position \a pos.
832
/*! \fn void Q3TextEdit::doubleClicked(int para, int pos)
834
This signal is emitted when the mouse is double-clicked on the
835
paragraph \a para at character position \a pos.
842
\fn void Q3TextEdit::returnPressed()
844
This signal is emitted if the user pressed the Return or the Enter
849
\fn Q3TextCursor *Q3TextEdit::textCursor() const
851
Returns the text edit's text cursor.
853
\warning Q3TextCursor is not in the public API, but in special
854
circumstances you might wish to use it.
858
Constructs an empty Q3TextEdit called \a name, with parent \a
862
Q3TextEdit::Q3TextEdit(QWidget *parent, const char *name)
863
: Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
864
doc(new Q3TextDocument(0)), undoRedoInfo(doc)
870
Constructs a Q3TextEdit called \a name, with parent \a parent. The
871
text edit will display the text \a text using context \a context.
873
The \a context is a path which the text edit's Q3MimeSourceFactory
874
uses to resolve the locations of files and images. It is passed to
875
the mimeSourceFactory() when quering data.
877
For example if the text contains an image tag,
878
\c{<img src="image.png">}, and the context is "path/to/look/in", the
879
Q3MimeSourceFactory will try to load the image from
880
"path/to/look/in/image.png". If the tag was
881
\c{<img src="/image.png">}, the context will not be used (because
882
Q3MimeSourceFactory recognizes that we have used an absolute path)
883
and will try to load "/image.png". The context is applied in exactly
884
the same way to \e hrefs, for example,
885
\c{<a href="target.html">Target</a>}, would resolve to
886
"path/to/look/in/target.html".
889
Q3TextEdit::Q3TextEdit(const QString& text, const QString& context,
890
QWidget *parent, const char *name)
891
: Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
892
doc(new Q3TextDocument(0)), undoRedoInfo(doc)
895
setText(text, context);
902
Q3TextEdit::~Q3TextEdit()
904
delete undoRedoInfo.d;
908
#ifdef QT_TEXTEDIT_OPTIMIZATION
914
void Q3TextEdit::init()
916
d = new Q3TextEditPrivate;
917
doc->formatCollection()->setPaintDevice(this);
921
setFrameStyle(LineEditPanel | Sunken);
922
connect(doc, SIGNAL(minimumWidthChanged(int)),
923
this, SLOT(documentWidthChanged(int)));
925
mousePressed = false;
926
inDoubleClick = false;
928
mightStartDrag = false;
932
wrapMode = WidgetWidth;
934
wPolicy = AtWhiteSpace;
936
doc->setFormatter(new Q3TextFormatterBreakWords);
937
doc->formatCollection()->defaultFormat()->setFont(Q3ScrollView::font());
938
doc->formatCollection()->defaultFormat()->setColor(palette().color(QPalette::Text));
939
currentFormat = doc->formatCollection()->defaultFormat();
940
currentAlignment = Qt::AlignAuto;
942
setBackgroundRole(QPalette::Base);
943
viewport()->setBackgroundRole(QPalette::Base);
945
viewport()->setAcceptDrops(true);
946
resizeContents(0, doc->lastParagraph() ?
947
(doc->lastParagraph()->paragId() + 1) * doc->formatCollection()->defaultFormat()->height() : 0);
949
setAttribute(Qt::WA_KeyCompression, true);
950
viewport()->setMouseTracking(true);
952
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
954
cursor = new Q3TextCursor(doc);
956
formatTimer = new QTimer(this);
957
connect(formatTimer, SIGNAL(timeout()),
958
this, SLOT(formatMore()));
959
lastFormatted = doc->firstParagraph();
961
scrollTimer = new QTimer(this);
962
connect(scrollTimer, SIGNAL(timeout()),
963
this, SLOT(autoScrollTimerDone()));
966
changeIntervalTimer = new QTimer(this);
967
connect(changeIntervalTimer, SIGNAL(timeout()),
968
this, SLOT(doChangeInterval()));
970
cursorVisible = true;
971
blinkTimer = new QTimer(this);
972
connect(blinkTimer, SIGNAL(timeout()),
973
this, SLOT(blinkCursor()));
975
#ifndef QT_NO_DRAGANDDROP
976
dragStartTimer = new QTimer(this);
977
connect(dragStartTimer, SIGNAL(timeout()),
978
this, SLOT(startDrag()));
981
d->trippleClickTimer = new QTimer(this);
985
blinkCursorVisible = false;
987
viewport()->setFocusProxy(this);
988
viewport()->setFocusPolicy(Qt::WheelFocus);
989
setInputMethodEnabled(true);
990
viewport()->installEventFilter(this);
991
connect(this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased()));
992
connect(this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased()));
993
installEventFilter(this);
996
void Q3TextEdit::paintDocument(bool drawAll, QPainter *p, int cx, int cy, int cw, int ch)
998
#ifdef QT_TEXTEDIT_OPTIMIZATION
999
Q_ASSERT(!d->optimMode);
1004
bool drawCur = blinkCursorVisible && (hasFocus() || viewport()->hasFocus());
1005
if ((hasSelectedText() && !style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, 0, this)) ||
1006
isReadOnly() || !cursorVisible)
1008
QPalette pal = palette();
1010
pal.setBrush(QPalette::Base, *doc->paper());
1012
if (contentsY() < doc->y()) {
1013
p->fillRect(contentsX(), contentsY(), visibleWidth(), doc->y(),
1016
if (drawAll && doc->width() - contentsX() < cx + cw) {
1017
p->fillRect(doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
1021
p->setBrushOrigin(-contentsX(), -contentsY());
1023
lastFormatted = doc->draw(p, cx, cy, cw, ch, pal, !drawAll, drawCur, cursor);
1025
if (lastFormatted == doc->lastParagraph())
1026
resizeContents(contentsWidth(), doc->height());
1028
if (contentsHeight() < visibleHeight() && (!doc->lastParagraph() || doc->lastParagraph()->isValid()) && drawAll)
1029
p->fillRect(0, contentsHeight(), visibleWidth(),
1030
visibleHeight() - contentsHeight(), pal.base());
1037
void Q3TextEdit::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
1039
#ifdef QT_TEXTEDIT_OPTIMIZATION
1041
optimDrawContents(p, cx, cy, cw, ch);
1045
paintDocument(true, p, cx, cy, cw, ch);
1047
p->setPen(palette().color(foregroundRole()));
1048
if (document()->isPageBreakEnabled() && (v = document()->flow()->pageSize()) > 0) {
1049
int l = int(cy / v) * v;
1050
while (l < cy + ch) {
1051
p->drawLine(cx, l, cx + cw - 1, l);
1061
void Q3TextEdit::drawContents(QPainter *p)
1063
if (horizontalScrollBar()->isVisible() &&
1064
verticalScrollBar()->isVisible()) {
1065
const QRect verticalRect = verticalScrollBar()->geometry();
1066
const QRect horizontalRect = horizontalScrollBar()->geometry();
1069
cornerRect.setTop(verticalRect.bottom());
1070
cornerRect.setBottom(horizontalRect.bottom());
1071
cornerRect.setLeft(verticalRect.left());
1072
cornerRect.setRight(verticalRect.right());
1074
p->fillRect(cornerRect, palette().background());
1082
bool Q3TextEdit::event(QEvent *e)
1084
if (e->type() == QEvent::AccelOverride && !isReadOnly()) {
1085
QKeyEvent* ke = (QKeyEvent*) e;
1086
switch(ke->state()) {
1089
case Qt::ShiftButton:
1090
if (ke->key() < Qt::Key_Escape) {
1092
} else if (ke->state() == Qt::NoButton
1093
|| ke->state() == Qt::ShiftButton) {
1094
switch (ke->key()) {
1095
case Qt::Key_Return:
1097
case Qt::Key_Delete:
1100
case Qt::Key_Backspace:
1110
case Qt::ControlButton:
1111
case Qt::ControlButton|Qt::ShiftButton:
1112
case Qt::ControlButton|Qt::Keypad:
1113
case Qt::ControlButton|Qt::ShiftButton|Qt::Keypad:
1114
switch (ke->key()) {
1116
case Qt::Key_Backtab:
1119
// Those are too frequently used for application functionality
1143
#if defined (Q_WS_WIN)
1144
case Qt::Key_Insert:
1145
case Qt::Key_Delete:
1154
switch (ke->key()) {
1155
#if defined (Q_WS_WIN)
1156
case Qt::Key_Insert:
1166
if (e->type() == QEvent::Show) {
1168
#ifdef QT_TEXTEDIT_OPTIMIZATION
1171
d->ensureCursorVisibleInShowEvent ) {
1172
ensureCursorVisible();
1173
d->ensureCursorVisibleInShowEvent = false;
1175
if (!d->scrollToAnchor.isEmpty()) {
1176
scrollToAnchor(d->scrollToAnchor);
1177
d->scrollToAnchor.clear();
1180
return QWidget::event(e);
1184
Processes the key event, \a e. By default key events are used to
1185
provide keyboard navigation and text editing.
1188
void Q3TextEdit::keyPressEvent(QKeyEvent *e)
1190
changeIntervalTimer->stop();
1192
bool unknownKey = false;
1194
if (!handleReadOnlyKeyEvent(e))
1195
Q3ScrollView::keyPressEvent(e);
1196
changeIntervalTimer->start(100, true);
1201
bool selChanged = false;
1202
for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection
1203
selChanged = doc->removeSelection(i) || selChanged;
1206
cursor->paragraph()->document()->nextDoubleBuffered = true;
1210
bool clearUndoRedoInfo = true;
1215
case Qt::Key_Right: {
1216
// a bit hacky, but can't change this without introducing new enum values for move and keeping the
1217
// correct semantics and movement for BiDi and non BiDi text.
1219
if (cursor->paragraph()->string()->isRightToLeft() == (e->key() == Qt::Key_Right))
1220
a = e->state() & Qt::ControlButton ? MoveWordBackward : MoveBackward;
1222
a = e->state() & Qt::ControlButton ? MoveWordForward : MoveForward;
1223
moveCursor(a, e->state() & Qt::ShiftButton);
1227
moveCursor(e->state() & Qt::ControlButton ? MovePgUp : MoveUp, e->state() & Qt::ShiftButton);
1230
moveCursor(e->state() & Qt::ControlButton ? MovePgDown : MoveDown, e->state() & Qt::ShiftButton);
1233
moveCursor(e->state() & Qt::ControlButton ? MoveHome : MoveLineStart, e->state() & Qt::ShiftButton);
1236
moveCursor(e->state() & Qt::ControlButton ? MoveEnd : MoveLineEnd, e->state() & Qt::ShiftButton);
1239
moveCursor(MovePgUp, e->state() & Qt::ShiftButton);
1242
moveCursor(MovePgDown, e->state() & Qt::ShiftButton);
1244
case Qt::Key_Return: case Qt::Key_Enter:
1245
if (doc->hasSelection(Q3TextDocument::Standard, false))
1246
removeSelectedText();
1247
if (textFormat() == Qt::RichText && (e->state() & Qt::ControlButton)) {
1248
// Ctrl-Enter inserts a line break in rich text mode
1249
insert(QString(QChar(QChar::LineSeparator)), true, false);
1251
#ifndef QT_NO_CURSOR
1252
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
1254
clearUndoRedoInfo = false;
1255
doKeyboardAction(ActionReturn);
1256
emit returnPressed();
1259
case Qt::Key_Delete:
1260
#if defined (Q_WS_WIN)
1261
if (e->state() & Qt::ShiftButton) {
1266
if (doc->hasSelection(Q3TextDocument::Standard, true)) {
1267
removeSelectedText();
1270
doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordDelete
1272
clearUndoRedoInfo = false;
1275
case Qt::Key_Insert:
1276
if (e->state() & Qt::ShiftButton)
1278
#if defined (Q_WS_WIN)
1279
else if (e->state() & Qt::ControlButton)
1283
setOverwriteMode(!isOverwriteMode());
1285
case Qt::Key_Backspace:
1286
#if defined (Q_WS_WIN)
1287
if (e->state() & Qt::AltButton) {
1288
if (e->state() & Qt::ControlButton) {
1290
} else if (e->state() & Qt::ShiftButton) {
1299
if (doc->hasSelection(Q3TextDocument::Standard, true)) {
1300
removeSelectedText();
1304
doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordBackspace
1306
clearUndoRedoInfo = false;
1308
case Qt::Key_F16: // Copy key on Sun keyboards
1311
case Qt::Key_F18: // Paste key on Sun keyboards
1314
case Qt::Key_F20: // Cut key on Sun keyboards
1317
case Qt::Key_Direction_L:
1318
if (doc->textFormat() == Qt::PlainText) {
1319
// change the whole doc
1320
Q3TextParagraph *p = doc->firstParagraph();
1322
p->setDirection(QChar::DirL);
1323
p->setAlignment(Qt::AlignLeft);
1328
if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL)
1330
cursor->paragraph()->setDirection(QChar::DirL);
1331
if (cursor->paragraph()->length() <= 1&&
1332
((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
1333
setAlignment(Qt::AlignLeft);
1337
case Qt::Key_Direction_R:
1338
if (doc->textFormat() == Qt::PlainText) {
1339
// change the whole doc
1340
Q3TextParagraph *p = doc->firstParagraph();
1342
p->setDirection(QChar::DirR);
1343
p->setAlignment(Qt::AlignRight);
1348
if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR)
1350
cursor->paragraph()->setDirection(QChar::DirR);
1351
if (cursor->paragraph()->length() <= 1&&
1352
((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0))
1353
setAlignment(Qt::AlignRight);
1358
char ascii = e->text().length() ? e->text().unicode()->latin1() : 0;
1359
if (e->text().length() &&
1360
(!(e->state() & Qt::ControlButton) &&
1362
!(e->state() & Qt::AltButton) &&
1364
!(e->state() & Qt::MetaButton) ||
1365
(((e->state()&Qt::ControlButton) | Qt::AltButton) == (Qt::ControlButton|Qt::AltButton))) &&
1366
(!ascii || ascii >= 32 || e->text() == "\t")) {
1367
clearUndoRedoInfo = false;
1368
if (e->key() == Qt::Key_Tab) {
1369
if (d->tabChangesFocus) {
1373
if (textFormat() == Qt::RichText && cursor->index() == 0
1374
&& (cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth())) {
1376
undoRedoInfo.type = UndoRedoInfo::Style;
1377
undoRedoInfo.id = cursor->paragraph()->paragId();
1378
undoRedoInfo.eid = undoRedoInfo.id;
1379
undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
1380
cursor->paragraph()->setListDepth(cursor->paragraph()->listDepth() +1);
1387
} else if (e->key() == Qt::Key_BackTab) {
1388
if (d->tabChangesFocus) {
1394
if ((autoFormatting() & AutoBulletList) &&
1395
textFormat() == Qt::RichText && cursor->index() == 0
1396
&& !cursor->paragraph()->isListItem()
1397
&& (e->text()[0] == '-' || e->text()[0] == '*')) {
1399
undoRedoInfo.type = UndoRedoInfo::Style;
1400
undoRedoInfo.id = cursor->paragraph()->paragId();
1401
undoRedoInfo.eid = undoRedoInfo.id;
1402
undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
1403
setParagType(Q3StyleSheetItem::DisplayListItem, Q3StyleSheetItem::ListDisc);
1410
if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(Q3TextDocument::Standard)) {
1411
doKeyboardAction(ActionDelete);
1412
clearUndoRedoInfo = false;
1414
QString t = e->text();
1415
insert(t, true, false);
1417
} else if (e->state() & Qt::ControlButton) {
1419
case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
1428
case Qt::Key_I: case Qt::Key_T: case Qt::Key_Tab:
1429
if (!d->tabChangesFocus)
1433
#if defined(Q_WS_X11)
1434
moveCursor(MoveLineStart, e->state() & Qt::ShiftButton);
1440
moveCursor(MoveBackward, e->state() & Qt::ShiftButton);
1443
moveCursor(MoveForward, e->state() & Qt::ShiftButton);
1446
if (doc->hasSelection(Q3TextDocument::Standard)) {
1447
removeSelectedText();
1450
doKeyboardAction(ActionDelete);
1451
clearUndoRedoInfo = false;
1454
if (doc->hasSelection(Q3TextDocument::Standard)) {
1455
removeSelectedText();
1458
if (!cursor->paragraph()->prev() &&
1459
cursor->atParagStart())
1462
doKeyboardAction(ActionBackspace);
1463
clearUndoRedoInfo = false;
1466
moveCursor(MoveLineEnd, e->state() & Qt::ShiftButton);
1469
moveCursor(MoveDown, e->state() & Qt::ShiftButton);
1472
moveCursor(MoveUp, e->state() & Qt::ShiftButton);
1475
if(e->state() & Qt::ShiftButton)
1484
doKeyboardAction(ActionKill);
1486
#if defined(Q_WS_WIN)
1487
case Qt::Key_Insert:
1490
case Qt::Key_Delete:
1504
emit cursorPositionChanged(cursor);
1505
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
1506
if (clearUndoRedoInfo)
1508
changeIntervalTimer->start(100, true);
1516
void Q3TextEdit::inputMethodEvent(QInputMethodEvent *e)
1523
if (hasSelectedText())
1524
removeSelectedText();
1526
bool oldupdate = updatesEnabled();
1528
setUpdatesEnabled(false);
1529
const int preeditSelectionBase = 31900;
1530
for (int i = 0; i < d->numPreeditSelections; ++i)
1531
doc->removeSelection(preeditSelectionBase + i);
1532
d->numPreeditSelections = 0;
1534
if (d->preeditLength > 0 && cursor->paragraph()) {
1535
cursor->paragraph()->remove(d->preeditStart, d->preeditLength);
1536
d->preeditStart = d->preeditLength = -1;
1539
if (!e->commitString().isEmpty() || e->replacementLength()) {
1540
int c = cursor->index(); // cursor position after insertion of commit string
1541
if (e->replacementStart() <= 0)
1542
c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength());
1543
cursor->setIndex(cursor->index() + e->replacementStart());
1544
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
1545
cursor->setIndex(cursor->index() + e->replacementLength());
1546
doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
1547
removeSelectedText();
1548
insert(e->commitString());
1549
cursor->setIndex(c);
1552
if (!e->preeditString().isEmpty()) {
1553
d->preeditStart = cursor->index();
1554
d->preeditLength = e->preeditString().length();
1555
insert(e->preeditString());
1556
cursor->setIndex(d->preeditStart);
1558
Q3TextCursor c = *cursor;
1559
for (int i = 0; i < e->attributes().size(); ++i) {
1560
const QInputMethodEvent::Attribute &a = e->attributes().at(i);
1561
if (a.type == QInputMethodEvent::Cursor)
1562
cursor->setIndex(cursor->index() + a.start);
1563
else if (a.type != QInputMethodEvent::TextFormat)
1565
QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
1567
Q3TextCursor c2 = c;
1568
c2.setIndex(c.index() + a.start);
1569
doc->setSelectionStart(preeditSelectionBase + d->numPreeditSelections, c2);
1570
c2.setIndex(c.index() + a.start + a.length);
1571
doc->setSelectionEnd(preeditSelectionBase + d->numPreeditSelections, c2);
1573
doc->setSelectionColor(preeditSelectionBase + d->numPreeditSelections, f.background().color());
1574
doc->setSelectionTextColor(preeditSelectionBase + d->numPreeditSelections, f.foreground().color());
1575
++d->numPreeditSelections;
1580
setUpdatesEnabled(true);
1585
static bool qtextedit_ignore_readonly = false;
1588
Executes keyboard action \a action. This is normally called by a
1592
void Q3TextEdit::doKeyboardAction(KeyboardAction action)
1594
if (isReadOnly() && !qtextedit_ignore_readonly)
1597
if (cursor->nestedDepth() != 0)
1600
lastFormatted = cursor->paragraph();
1602
bool doUpdateCurrentFormat = true;
1605
case ActionWordDelete:
1607
if (action == ActionDelete && !cursor->atParagEnd()) {
1609
checkUndoRedoInfo(UndoRedoInfo::Delete);
1610
if (!undoRedoInfo.valid()) {
1611
undoRedoInfo.id = cursor->paragraph()->paragId();
1612
undoRedoInfo.index = cursor->index();
1613
undoRedoInfo.d->text.clear();
1615
int idx = cursor->index();
1617
undoRedoInfo.d->text.insert(undoRedoInfo.d->text.length(), cursor->paragraph()->at(idx++), true);
1618
} while (!cursor->paragraph()->string()->validCursorPosition(idx));
1623
doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
1624
if (action == ActionWordDelete && !cursor->atParagEnd()) {
1625
cursor->gotoNextWord();
1627
cursor->gotoNextLetter();
1629
doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
1630
removeSelectedText(Q3TextDocument::Temp);
1633
case ActionWordBackspace:
1634
case ActionBackspace:
1635
if (textFormat() == Qt::RichText
1636
&& (cursor->paragraph()->isListItem()
1637
|| cursor->paragraph()->listDepth())
1638
&& cursor->index() == 0) {
1641
undoRedoInfo.type = UndoRedoInfo::Style;
1642
undoRedoInfo.id = cursor->paragraph()->paragId();
1643
undoRedoInfo.eid = undoRedoInfo.id;
1644
undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
1646
int ldepth = cursor->paragraph()->listDepth();
1647
if (cursor->paragraph()->isListItem() && ldepth == 1) {
1648
cursor->paragraph()->setListItem(false);
1649
} else if (qMax(ldepth, 1) == 1) {
1650
cursor->paragraph()->setListItem(false);
1651
cursor->paragraph()->setListDepth(0);
1653
cursor->paragraph()->setListDepth(ldepth - 1);
1656
lastFormatted = cursor->paragraph();
1662
if (action == ActionBackspace && !cursor->atParagStart()) {
1664
checkUndoRedoInfo(UndoRedoInfo::Delete);
1665
if (!undoRedoInfo.valid()) {
1666
undoRedoInfo.id = cursor->paragraph()->paragId();
1667
undoRedoInfo.index = cursor->index();
1668
undoRedoInfo.d->text.clear();
1670
undoRedoInfo.d->text.insert(0, cursor->paragraph()->at(cursor->index()-1), true);
1671
undoRedoInfo.index = cursor->index()-1;
1673
cursor->removePreviousChar();
1674
lastFormatted = cursor->paragraph();
1675
} else if (cursor->paragraph()->prev()
1676
|| (action == ActionWordBackspace
1677
&& !cursor->atParagStart())) {
1679
doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
1680
if (action == ActionWordBackspace && !cursor->atParagStart()) {
1681
cursor->gotoPreviousWord();
1683
cursor->gotoPreviousLetter();
1685
doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
1686
removeSelectedText(Q3TextDocument::Temp);
1691
checkUndoRedoInfo(UndoRedoInfo::Return);
1692
if (!undoRedoInfo.valid()) {
1693
undoRedoInfo.id = cursor->paragraph()->paragId();
1694
undoRedoInfo.index = cursor->index();
1695
undoRedoInfo.d->text.clear();
1697
undoRedoInfo.d->text += "\n";
1699
cursor->splitAndInsertEmptyParagraph();
1700
if (cursor->paragraph()->prev()) {
1701
lastFormatted = cursor->paragraph()->prev();
1702
lastFormatted->invalidate(0);
1704
doUpdateCurrentFormat = false;
1708
doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
1709
if (cursor->atParagEnd())
1710
cursor->gotoNextLetter();
1712
cursor->setIndex(cursor->paragraph()->length() - 1);
1713
doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
1714
removeSelectedText(Q3TextDocument::Temp);
1720
ensureCursorVisible();
1722
if (doUpdateCurrentFormat)
1723
updateCurrentFormat();
1728
void Q3TextEdit::readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles)
1730
#ifndef QT_NO_DATASTREAM
1731
QDataStream styleStream(&undoRedoInfo.styleInformation, IO_WriteOnly);
1735
int lastIndex = text.length();
1736
if (c1.paragraph() == c2.paragraph()) {
1737
for (int i = c1.index(); i < c2.index(); ++i)
1738
text.insert(lastIndex + i - c1.index(), c1.paragraph()->at(i), true);
1739
#ifndef QT_NO_DATASTREAM
1741
styleStream << (int) 1;
1742
c1.paragraph()->writeStyleInformation(styleStream);
1747
for (i = c1.index(); i < c1.paragraph()->length()-1; ++i)
1748
text.insert(lastIndex++, c1.paragraph()->at(i), true);
1749
int num = 2; // start and end, being different
1750
text += "\n"; lastIndex++;
1751
Q3TextParagraph *p = c1.paragraph()->next();
1752
while (p && p != c2.paragraph()) {
1753
for (i = 0; i < p->length()-1; ++i)
1754
text.insert(lastIndex++ , p->at(i), true);
1755
text += "\n"; num++; lastIndex++;
1758
for (i = 0; i < c2.index(); ++i)
1759
text.insert(i + lastIndex, c2.paragraph()->at(i), true);
1760
#ifndef QT_NO_DATASTREAM
1763
for (Q3TextParagraph *p = c1.paragraph(); --num >= 0; p = p->next())
1764
p->writeStyleInformation(styleStream);
1771
Removes the selection \a selNum (by default 0). This does not
1772
remove the selected text.
1774
\sa removeSelectedText()
1777
void Q3TextEdit::removeSelection(int selNum)
1779
doc->removeSelection(selNum);
1784
Deletes the text of selection \a selNum (by default, the default
1785
selection, 0). If there is no selected text nothing happens.
1787
\sa selectedText removeSelection()
1790
void Q3TextEdit::removeSelectedText(int selNum)
1792
Q3TextCursor c1 = doc->selectionStartCursor(selNum);
1794
Q3TextCursor c2 = doc->selectionEndCursor(selNum);
1797
// ### no support for editing tables yet, plus security for broken selections
1798
if (c1.nestedDepth() || c2.nestedDepth())
1801
for (int i = 0; i < (int)doc->numSelections(); ++i) {
1804
doc->removeSelection(i);
1809
checkUndoRedoInfo(UndoRedoInfo::RemoveSelected);
1810
if (!undoRedoInfo.valid()) {
1811
doc->selectionStart(selNum, undoRedoInfo.id, undoRedoInfo.index);
1812
undoRedoInfo.d->text.clear();
1814
readFormats(c1, c2, undoRedoInfo.d->text, true);
1817
doc->removeSelectedText(selNum, cursor);
1818
if (cursor->isValid()) {
1819
lastFormatted = 0; // make sync a noop
1820
ensureCursorVisible();
1821
lastFormatted = cursor->paragraph();
1824
ensureCursorVisible();
1827
#if defined(Q_WS_WIN)
1828
// there seems to be a problem with repainting or erasing the area
1829
// of the scrollview which is not the contents on windows
1830
if (contentsHeight() < visibleHeight())
1831
viewport()->repaint(0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight());
1833
#ifndef QT_NO_CURSOR
1834
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
1838
cursor = new Q3TextCursor(doc);
1844
emit selectionChanged();
1845
emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
1849
Moves the text cursor according to \a action. This is normally
1850
used by some key event handler. \a select specifies whether the
1851
text between the current cursor position and the new position
1855
void Q3TextEdit::moveCursor(CursorAction action, bool select)
1857
#ifdef QT_TEXTEDIT_OPTIMIZATION
1862
Q3TextCursor c1 = *cursor;
1867
if (!doc->hasSelection(Q3TextDocument::Standard))
1868
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
1873
if (action == MoveDown || action == MovePgDown)
1874
moveCursor(MoveEnd);
1875
else if (action == MoveUp || action == MovePgUp)
1876
moveCursor(MoveHome);
1878
if (doc->setSelectionEnd(Q3TextDocument::Standard, *cursor)) {
1879
cursor->paragraph()->document()->nextDoubleBuffered = true;
1884
ensureCursorVisible();
1885
emit selectionChanged();
1886
emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
1889
Q3TextCursor cStart = doc->selectionStartCursor(Q3TextDocument::Standard);
1890
Q3TextCursor cEnd = doc->selectionEndCursor(Q3TextDocument::Standard);
1891
bool redraw = doc->removeSelection(Q3TextDocument::Standard);
1892
if (redraw && action == MoveDown)
1894
else if (redraw && action == MoveUp)
1896
if (redraw && action == MoveForward)
1898
else if (redraw && action == MoveBackward)
1904
if (action == MoveDown)
1905
moveCursor(MoveEnd);
1906
else if (action == MoveUp)
1907
moveCursor(MoveHome);
1909
bool redraw = doc->removeSelection(Q3TextDocument::Standard);
1913
ensureCursorVisible();
1916
cursor->paragraph()->document()->nextDoubleBuffered = true;
1918
ensureCursorVisible();
1920
#ifndef QT_NO_CURSOR
1921
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
1925
emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
1926
emit selectionChanged();
1931
updateCurrentFormat();
1938
void Q3TextEdit::moveCursor(CursorAction action)
1940
resetInputContext();
1943
cursor->gotoPreviousLetter();
1945
case MoveWordBackward:
1946
cursor->gotoPreviousWord();
1949
cursor->gotoNextLetter();
1951
case MoveWordForward:
1952
cursor->gotoNextWord();
1958
cursor->gotoPageUp(visibleHeight());
1964
cursor->gotoPageDown(visibleHeight());
1967
cursor->gotoLineStart();
1973
cursor->gotoLineEnd();
1976
ensureFormatted(doc->lastParagraph());
1980
updateCurrentFormat();
1987
void Q3TextEdit::resizeEvent(QResizeEvent *e)
1989
Q3ScrollView::resizeEvent(e);
1990
if (doc->visibleWidth() == 0)
1998
void Q3TextEdit::viewportResizeEvent(QResizeEvent *e)
2000
Q3ScrollView::viewportResizeEvent(e);
2001
if (e->oldSize().width() != e->size().width()) {
2002
bool stayAtBottom = e->oldSize().height() != e->size().height() &&
2003
contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height();
2011
Ensures that the cursor is visible by scrolling the text edit if
2014
\sa setCursorPosition()
2017
void Q3TextEdit::ensureCursorVisible()
2019
// Not visible or the user is draging the window, so don't position to caret yet
2020
if (!updatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed()) {
2021
d->ensureCursorVisibleInShowEvent = true;
2025
Q3TextStringChar *chr = cursor->paragraph()->at(cursor->index());
2026
int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
2027
int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX();
2028
int y = 0; int dummy;
2029
cursor->paragraph()->lineHeightOfChar(cursor->index(), &dummy, &y);
2030
y += cursor->paragraph()->rect().y() + cursor->offsetY();
2032
ensureVisible(x, y + h / 2, w, h / 2 + 2);
2038
void Q3TextEdit::sliderReleased()
2040
if (d->ensureCursorVisibleInShowEvent && isVisible()) {
2041
d->ensureCursorVisibleInShowEvent = false;
2042
ensureCursorVisible();
2049
If \a visible is true, the cursor is shown; otherwise it is
2052
void Q3TextEdit::drawCursor(bool visible)
2054
d->cursorRepaintMode = true;
2055
blinkCursorVisible = visible;
2056
QRect r(cursor->topParagraph()->rect());
2057
if (!cursor->nestedDepth()) {
2058
int h = cursor->paragraph()->lineHeightOfChar(cursor->index());
2059
r = QRect(r.x(), r.y() + cursor->y(), r.width(), h);
2061
r.moveBy(-contentsX(), -contentsY());
2062
viewport()->repaint(r);
2078
#ifndef QT_NO_WHEELEVENT
2079
void Q3TextEdit::contentsWheelEvent(QWheelEvent *e)
2082
if (e->state() & Qt::ControlButton) {
2085
else if (e->delta() < 0)
2090
Q3ScrollView::contentsWheelEvent(e);
2098
void Q3TextEdit::contentsMousePressEvent(QMouseEvent *e)
2100
#ifdef QT_TEXTEDIT_OPTIMIZATION
2102
optimMousePressEvent(e);
2107
if (d->trippleClickTimer->isActive() &&
2108
(e->globalPos() - d->trippleClickPoint).manhattanLength() <
2109
QApplication::startDragDistance()) {
2110
Q3TextCursor c1 = *cursor;
2111
Q3TextCursor c2 = *cursor;
2114
doc->setSelectionStart(Q3TextDocument::Standard, c1);
2115
doc->setSelectionEnd(Q3TextDocument::Standard, c2);
2118
mousePressed = true;
2123
Q3TextCursor oldCursor = *cursor;
2124
Q3TextCursor c = *cursor;
2125
mousePos = e->pos();
2126
mightStartDrag = false;
2127
pressedLink.clear();
2128
d->pressedName.clear();
2130
if (e->button() == Qt::LeftButton) {
2131
mousePressed = true;
2133
placeCursor(e->pos());
2134
ensureCursorVisible();
2136
if (isReadOnly() && linksEnabled()) {
2137
Q3TextCursor c = *cursor;
2138
placeCursor(e->pos(), &c, true);
2139
if (c.paragraph() && c.paragraph()->at(c.index()) &&
2140
c.paragraph()->at(c.index())->isAnchor()) {
2141
pressedLink = c.paragraph()->at(c.index())->anchorHref();
2142
d->pressedName = c.paragraph()->at(c.index())->anchorName();
2146
#ifndef QT_NO_DRAGANDDROP
2147
if (doc->inSelection(Q3TextDocument::Standard, e->pos())) {
2148
mightStartDrag = true;
2150
dragStartTimer->start(QApplication::startDragTime(), true);
2151
dragStartPos = e->pos();
2156
bool redraw = false;
2157
if (doc->hasSelection(Q3TextDocument::Standard)) {
2158
if (!(e->state() & Qt::ShiftButton)) {
2159
redraw = doc->removeSelection(Q3TextDocument::Standard);
2160
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
2162
redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
2165
if (isReadOnly() || !(e->state() & Qt::ShiftButton)) {
2166
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
2168
doc->setSelectionStart(Q3TextDocument::Standard, c);
2169
redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
2173
for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection
2174
redraw = doc->removeSelection(i) || redraw;
2180
#ifndef QT_NO_CURSOR
2181
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
2184
} else if (e->button() == Qt::MidButton) {
2185
bool redraw = doc->removeSelection(Q3TextDocument::Standard);
2190
#ifndef QT_NO_CURSOR
2191
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
2196
if (*cursor != oldCursor)
2197
updateCurrentFormat();
2204
void Q3TextEdit::contentsMouseMoveEvent(QMouseEvent *e)
2206
#ifdef QT_TEXTEDIT_OPTIMIZATION
2208
optimMouseMoveEvent(e);
2213
#ifndef QT_NO_DRAGANDDROP
2214
if (mightStartDrag) {
2215
dragStartTimer->stop();
2216
if ((e->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance())
2218
#ifndef QT_NO_CURSOR
2220
viewport()->setCursor(Qt::IBeamCursor);
2225
mousePos = e->pos();
2226
handleMouseMove(mousePos);
2227
oldMousePos = mousePos;
2230
#ifndef QT_NO_CURSOR
2231
if (!isReadOnly() && !mousePressed) {
2232
if (doc->hasSelection(Q3TextDocument::Standard) && doc->inSelection(Q3TextDocument::Standard, e->pos()))
2233
viewport()->setCursor(Qt::ArrowCursor);
2235
viewport()->setCursor(Qt::IBeamCursor);
2238
updateCursor(e->pos());
2241
void Q3TextEdit::copyToClipboard()
2243
#ifndef QT_NO_CLIPBOARD
2244
if (QApplication::clipboard()->supportsSelection()) {
2245
d->clipboard_mode = QClipboard::Selection;
2247
// don't listen to selection changes
2248
disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
2250
// listen to selection changes
2251
connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
2252
this, SLOT(clipboardChanged()));
2254
d->clipboard_mode = QClipboard::Clipboard;
2263
void Q3TextEdit::contentsMouseReleaseEvent(QMouseEvent * e)
2265
if (!inDoubleClick) { // could be the release of a dblclick
2267
int index = charAt(e->pos(), ¶);
2268
emit clicked(para, index);
2270
#ifdef QT_TEXTEDIT_OPTIMIZATION
2272
optimMouseReleaseEvent(e);
2276
Q3TextCursor oldCursor = *cursor;
2277
if (scrollTimer->isActive())
2278
scrollTimer->stop();
2279
#ifndef QT_NO_DRAGANDDROP
2280
if (dragStartTimer->isActive())
2281
dragStartTimer->stop();
2282
if (mightStartDrag) {
2284
mousePressed = false;
2288
mousePressed = false;
2291
#ifndef QT_NO_CLIPBOARD
2292
else if (e->button() == Qt::MidButton && !isReadOnly()) {
2293
// only do middle-click pasting on systems that have selections (ie. X11)
2294
if (QApplication::clipboard()->supportsSelection()) {
2296
placeCursor(e->pos());
2297
ensureCursorVisible();
2298
doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
2299
bool redraw = false;
2300
if (doc->hasSelection(Q3TextDocument::Standard)) {
2301
redraw = doc->removeSelection(Q3TextDocument::Standard);
2302
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
2304
doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
2306
// start with 1 as we don't want to remove the Standard-Selection
2307
for (int i = 1; i < doc->numSelections(); ++i)
2308
redraw = doc->removeSelection(i) || redraw;
2313
#ifndef QT_NO_CURSOR
2314
viewport()->setCursor(Qt::IBeamCursor);
2317
d->clipboard_mode = QClipboard::Selection;
2319
d->clipboard_mode = QClipboard::Clipboard;
2323
emit cursorPositionChanged(cursor);
2324
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
2325
if (oldCursor != *cursor)
2326
updateCurrentFormat();
2327
inDoubleClick = false;
2329
#ifndef QT_NO_NETWORKPROTOCOL
2330
if (( (!onLink.isEmpty() && onLink == pressedLink)
2331
|| (!d->onName.isEmpty() && d->onName == d->pressedName))
2332
&& linksEnabled()) {
2333
if (!onLink.isEmpty()) {
2334
QUrl u = QUrl(doc->context()).resolved(onLink);
2335
emitLinkClicked(u.toString(QUrl::None));
2337
if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
2338
emit browser->anchorClicked(d->onName, onLink);
2340
// emitting linkClicked() may result in that the cursor winds
2341
// up hovering over a different valid link - check this and
2342
// set the appropriate cursor shape
2343
updateCursor(e->pos());
2347
if (!doc->hasSelection(Q3TextDocument::Standard, true))
2348
doc->removeSelection(Q3TextDocument::Standard);
2350
emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
2351
emit selectionChanged();
2358
void Q3TextEdit::contentsMouseDoubleClickEvent(QMouseEvent * e)
2360
if (e->button() != Qt::LeftButton) {
2365
int index = charAt(e->pos(), ¶);
2366
#ifdef QT_TEXTEDIT_OPTIMIZATION
2368
QString str = d->od->lines[LOGOFFSET(para)];
2369
int startIdx = index, endIdx = index, i;
2370
if (!str[index].isSpace()) {
2372
// find start of word
2373
while (i >= 0 && !str[i].isSpace()) {
2377
// find end of word..
2378
while (i < str.length() && !str[i].isSpace()) {
2381
// ..and start of next
2382
while (i < str.length() && str[i].isSpace()) {
2385
optimSetSelection(para, startIdx, para, endIdx);
2391
Q3TextCursor c1 = *cursor;
2392
Q3TextCursor c2 = *cursor;
2393
#if defined(Q_OS_MAC)
2394
Q3TextParagraph *para = cursor->paragraph();
2395
if (cursor->isValid()) {
2396
if (para->at(cursor->index())->c.isLetterOrNumber()) {
2397
while (c1.index() > 0 &&
2398
c1.paragraph()->at(c1.index()-1)->c.isLetterOrNumber())
2399
c1.gotoPreviousLetter();
2400
while (c2.paragraph()->at(c2.index())->c.isLetterOrNumber() &&
2402
c2.gotoNextLetter();
2403
} else if (para->at(cursor->index())->c.isSpace()) {
2404
while (c1.index() > 0 &&
2405
c1.paragraph()->at(c1.index()-1)->c.isSpace())
2406
c1.gotoPreviousLetter();
2407
while (c2.paragraph()->at(c2.index())->c.isSpace() &&
2409
c2.gotoNextLetter();
2410
} else if (!c2.atParagEnd()) {
2411
c2.gotoNextLetter();
2415
if (cursor->index() > 0 && !cursor->paragraph()->at(cursor->index()-1)->c.isSpace())
2416
c1.gotoPreviousWord();
2417
if (!cursor->paragraph()->at(cursor->index())->c.isSpace() && !cursor->atParagEnd())
2420
doc->setSelectionStart(Q3TextDocument::Standard, c1);
2421
doc->setSelectionEnd(Q3TextDocument::Standard, c2);
2427
d->trippleClickTimer->start(qApp->doubleClickInterval(), true);
2428
d->trippleClickPoint = e->globalPos();
2430
inDoubleClick = true;
2431
mousePressed = true;
2432
emit doubleClicked(para, index);
2435
#ifndef QT_NO_DRAGANDDROP
2441
void Q3TextEdit::contentsDragEnterEvent(QDragEnterEvent *e)
2443
if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
2455
void Q3TextEdit::contentsDragMoveEvent(QDragMoveEvent *e)
2457
if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
2462
placeCursor(e->pos(), cursor);
2471
void Q3TextEdit::contentsDragLeaveEvent(QDragLeaveEvent *)
2481
void Q3TextEdit::contentsDropEvent(QDropEvent *e)
2487
bool intern = false;
2488
if (Q3RichTextDrag::canDecode(e)) {
2489
bool hasSel = doc->hasSelection(Q3TextDocument::Standard);
2490
bool internalDrag = e->source() == this || e->source() == viewport();
2491
int dropId, dropIndex;
2492
Q3TextCursor insertCursor = *cursor;
2493
dropId = cursor->paragraph()->paragId();
2494
dropIndex = cursor->index();
2495
if (hasSel && internalDrag) {
2496
Q3TextCursor c1, c2;
2497
int selStartId, selStartIndex;
2498
int selEndId, selEndIndex;
2499
c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
2501
c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
2503
selStartId = c1.paragraph()->paragId();
2504
selStartIndex = c1.index();
2505
selEndId = c2.paragraph()->paragId();
2506
selEndIndex = c2.index();
2507
if (((dropId > selStartId) ||
2508
(dropId == selStartId && dropIndex > selStartIndex)) &&
2509
((dropId < selEndId) ||
2510
(dropId == selEndId && dropIndex <= selEndIndex)))
2512
if (dropId == selEndId && dropIndex > selEndIndex) {
2514
if (selStartId == selEndId) {
2515
insertCursor.setIndex(dropIndex -
2516
(selEndIndex - selStartIndex));
2518
insertCursor.setIndex(dropIndex - selEndIndex +
2524
if (internalDrag && e->action() == QDropEvent::Move) {
2525
removeSelectedText();
2527
doc->removeSelection(Q3TextDocument::Standard);
2529
doc->removeSelection(Q3TextDocument::Standard);
2530
#ifndef QT_NO_CURSOR
2531
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
2535
cursor->setParagraph(insertCursor.paragraph());
2536
cursor->setIndex(insertCursor.index());
2538
if (!cursor->nestedDepth()) {
2539
QString subType = "plain";
2540
if (textFormat() != Qt::PlainText) {
2541
if (e->provides("application/x-qrichtext"))
2542
subType = "x-qrichtext";
2544
#ifndef QT_NO_CLIPBOARD
2545
pasteSubType(subType.toLatin1(), e);
2547
// emit appropriate signals.
2548
emit selectionChanged();
2549
emit cursorPositionChanged(cursor);
2550
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
2564
void Q3TextEdit::contentsContextMenuEvent(QContextMenuEvent *e)
2567
mousePressed = false;
2570
#ifndef QT_NO_POPUPMENU
2571
Q3PopupMenu *popup = createPopupMenu(e->pos());
2573
popup = createPopupMenu();
2576
int r = popup->exec(e->globalPos(), -1);
2579
if (r == d->id[IdClear])
2581
else if (r == d->id[IdSelectAll]) {
2583
#ifndef QT_NO_CLIPBOARD
2584
// if the clipboard support selections, put the newly selected text into
2586
if (QApplication::clipboard()->supportsSelection()) {
2587
d->clipboard_mode = QClipboard::Selection;
2589
// don't listen to selection changes
2590
disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
2592
// listen to selection changes
2593
connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
2594
this, SLOT(clipboardChanged()));
2596
d->clipboard_mode = QClipboard::Clipboard;
2599
} else if (r == d->id[IdUndo])
2601
else if (r == d->id[IdRedo])
2603
#ifndef QT_NO_CLIPBOARD
2604
else if (r == d->id[IdCut])
2606
else if (r == d->id[IdCopy])
2608
else if (r == d->id[IdPaste])
2615
void Q3TextEdit::autoScrollTimerDone()
2618
handleMouseMove( viewportToContents(viewport()->mapFromGlobal(QCursor::pos()) ));
2621
void Q3TextEdit::handleMouseMove(const QPoint& pos)
2626
if (!scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight())
2627
scrollTimer->start(100, false);
2628
else if (scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight())
2629
scrollTimer->stop();
2632
Q3TextCursor oldCursor = *cursor;
2636
if (inDoubleClick) {
2637
Q3TextCursor cl = *cursor;
2638
cl.gotoPreviousWord();
2639
Q3TextCursor cr = *cursor;
2642
int diff = QABS(oldCursor.paragraph()->at(oldCursor.index())->x - mousePos.x());
2643
int ldiff = QABS(cl.paragraph()->at(cl.index())->x - mousePos.x());
2644
int rdiff = QABS(cr.paragraph()->at(cr.index())->x - mousePos.x());
2647
if (cursor->paragraph()->lineStartOfChar(cursor->index()) !=
2648
oldCursor.paragraph()->lineStartOfChar(oldCursor.index()))
2651
if (rdiff < diff && rdiff < ldiff)
2653
else if (ldiff < diff && ldiff < rdiff)
2656
*cursor = oldCursor;
2659
ensureCursorVisible();
2661
bool redraw = false;
2662
if (doc->hasSelection(Q3TextDocument::Standard)) {
2663
redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
2673
if (currentFormat && currentFormat->key() != cursor->paragraph()->at(cursor->index())->format()->key()) {
2674
currentFormat->removeRef();
2675
currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(cursor->index())->format());
2676
if (currentFormat->isMisspelled()) {
2677
currentFormat->removeRef();
2678
currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
2680
emit currentFontChanged(currentFormat->font());
2681
emit currentColorChanged(currentFormat->color());
2682
emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
2685
if (currentAlignment != cursor->paragraph()->alignment()) {
2686
currentAlignment = cursor->paragraph()->alignment();
2687
block_set_alignment = true;
2688
emit currentAlignmentChanged(currentAlignment);
2689
block_set_alignment = false;
2695
void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c, bool link)
2697
#ifdef QT_TEXTEDIT_OPTIMIZATION
2704
resetInputContext();
2706
Q3TextParagraph *s = doc->firstParagraph();
2707
c->place(pos, s, link);
2711
QVariant Q3TextEdit::inputMethodQuery(Qt::InputMethodQuery query) const
2713
Q3TextCursor c(*cursor);
2714
if (d->preeditStart != -1)
2715
c.setIndex(d->preeditStart);
2718
case Qt::ImMicroFocus: {
2719
int h = c.paragraph()->lineHeightOfChar(cursor->index());
2720
return QRect(c.x() - contentsX() + frameWidth(),
2721
c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 1, h);
2724
return c.paragraph()->at(c.index())->format()->font();
2726
// ##### fix the others!
2727
return QWidget::inputMethodQuery(query);
2733
void Q3TextEdit::formatMore()
2738
int bottom = contentsHeight();
2740
int lastBottom = -1;
2742
bool firstVisible = false;
2743
QRect cr(contentsX(), contentsY(), visibleWidth(), visibleHeight());
2744
for (int i = 0; lastFormatted &&
2745
(i < to || (firstVisible && lastTop < contentsY()+height()));
2747
lastFormatted->format();
2748
lastTop = lastFormatted->rect().top();
2749
lastBottom = lastFormatted->rect().bottom();
2751
firstVisible = lastBottom < cr.bottom();
2752
bottom = qMax(bottom, lastBottom);
2753
lastFormatted = lastFormatted->next();
2756
if (bottom > contentsHeight()) {
2757
resizeContents(contentsWidth(), qMax(doc->height(), bottom));
2758
} else if (!lastFormatted && lastBottom < contentsHeight()) {
2759
resizeContents(contentsWidth(), qMax(doc->height(), lastBottom));
2760
if (contentsHeight() < visibleHeight())
2761
updateContents(0, contentsHeight(), visibleWidth(),
2762
visibleHeight() - contentsHeight());
2766
formatTimer->start(interval, true);
2768
interval = qMax(0, interval);
2771
void Q3TextEdit::doResize()
2773
#ifdef QT_TEXTEDIT_OPTIMIZATION
2777
if (wrapMode == FixedPixelWidth)
2779
doc->setMinimumWidth(-1);
2780
resizeContents(0, 0);
2781
doc->setWidth(visibleWidth());
2783
lastFormatted = doc->firstParagraph();
2792
void Q3TextEdit::doChangeInterval()
2801
bool Q3TextEdit::eventFilter(QObject *o, QEvent *e)
2803
#ifdef QT_TEXTEDIT_OPTIMIZATION
2804
if (!d->optimMode && (o == this || o == viewport())) {
2806
if (o == this || o == viewport()) {
2808
if (d->cursorBlinkActive && e->type() == QEvent::FocusIn) {
2809
if (QApplication::cursorFlashTime() > 0)
2810
blinkTimer->start(QApplication::cursorFlashTime() / 2);
2812
} else if (e->type() == QEvent::FocusOut) {
2818
if (o == this && e->type() == QEvent::PaletteChange) {
2819
QColor old(viewport()->palette().color(QPalette::Text));
2820
if (old != palette().color(QPalette::Text)) {
2821
QColor c(palette().color(QPalette::Text));
2822
doc->setMinimumWidth(-1);
2823
doc->setDefaultFormat(doc->formatCollection()->defaultFormat()->font(), c);
2824
lastFormatted = doc->firstParagraph();
2830
return Q3ScrollView::eventFilter(o, e);
2834
Inserts the given \a text. If \a indent is true the paragraph that
2835
contains the text is reindented; if \a checkNewLine is true the \a
2836
text is checked for newlines and relaid out. If \a removeSelected
2837
is true and there is a selection, the insertion replaces the
2840
void Q3TextEdit::insert(const QString &text, bool indent,
2841
bool checkNewLine, bool removeSelected)
2845
f |= RedoIndentation;
2849
f |= RemoveSelected;
2854
Inserts \a text at the current cursor position.
2856
The \a insertionFlags define how the text is inserted. If \c
2857
RedoIndentation is set, the paragraph is re-indented. If \c
2858
CheckNewLines is set, newline characters in \a text result in hard
2859
line breaks (i.e. new paragraphs). If \c checkNewLine is not set,
2860
the behavior of the editor is undefined if the \a text contains
2861
newlines. (It is not possible to change Q3TextEdit's newline handling
2862
behavior, but you can use QString::replace() to preprocess text
2863
before inserting it.) If \c RemoveSelected is set, any selected
2864
text (in selection 0) is removed before the text is inserted.
2866
The default flags are \c CheckNewLines | \c RemoveSelected.
2868
If the widget is in \c Qt::LogText mode this function will do nothing.
2870
\sa paste() pasteSubType()
2874
void Q3TextEdit::insert(const QString &text, uint insertionFlags)
2876
#ifdef QT_TEXTEDIT_OPTIMIZATION
2881
if (cursor->nestedDepth() != 0) // #### for 3.0, disable editing of tables as this is not advanced enough
2884
bool indent = insertionFlags & RedoIndentation;
2885
bool checkNewLine = insertionFlags & CheckNewLines;
2886
bool removeSelected = insertionFlags & RemoveSelected;
2889
if (!isReadOnly() && doc->hasSelection(Q3TextDocument::Standard) && removeSelected)
2890
removeSelectedText();
2891
Q3TextCursor c2 = *cursor;
2894
if (undoEnabled && !isReadOnly()) {
2895
checkUndoRedoInfo(UndoRedoInfo::Insert);
2897
// If we are inserting at the end of the previous insertion, we keep this in
2898
// the same undo/redo command. Otherwise, we separate them in two different commands.
2899
if (undoRedoInfo.valid() && undoRedoInfo.index + undoRedoInfo.d->text.length() != cursor->index()) {
2901
undoRedoInfo.type = UndoRedoInfo::Insert;
2904
if (!undoRedoInfo.valid()) {
2905
undoRedoInfo.id = cursor->paragraph()->paragId();
2906
undoRedoInfo.index = cursor->index();
2907
undoRedoInfo.d->text.clear();
2909
oldLen = undoRedoInfo.d->text.length();
2912
lastFormatted = checkNewLine && cursor->paragraph()->prev() ?
2913
cursor->paragraph()->prev() : cursor->paragraph();
2914
Q3TextCursor oldCursor = *cursor;
2915
cursor->insert(txt, checkNewLine);
2916
if (doc->useFormatCollection() && !doc->preProcessor()) {
2917
doc->setSelectionStart(Q3TextDocument::Temp, oldCursor);
2918
doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
2919
doc->setFormat(Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format);
2920
doc->removeSelection(Q3TextDocument::Temp);
2923
if (indent && (txt == "{" || txt == "}" || txt == ":" || txt == "#"))
2927
ensureCursorVisible();
2930
if (undoEnabled && !isReadOnly()) {
2931
undoRedoInfo.d->text += txt;
2932
if (!doc->preProcessor()) {
2933
for (int i = 0; i < (int)txt.length(); ++i) {
2934
if (txt[i] != '\n' && c2.paragraph()->at(c2.index())->format()) {
2935
c2.paragraph()->at(c2.index())->format()->addRef();
2936
undoRedoInfo.d->text.
2937
setFormat(oldLen + i,
2938
c2.paragraph()->at(c2.index())->format(), true);
2940
c2.gotoNextLetter();
2945
if (!removeSelected) {
2946
doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
2947
doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
2956
Inserts \a text in the paragraph \a para at position \a index.
2959
void Q3TextEdit::insertAt(const QString &text, int para, int index)
2961
#ifdef QT_TEXTEDIT_OPTIMIZATION
2963
optimInsert(text, para, index);
2967
Q3TextParagraph *p = doc->paragAt(para);
2970
removeSelection(Q3TextDocument::Standard);
2971
Q3TextCursor tmp = *cursor;
2972
cursor->setParagraph(p);
2973
cursor->setIndex(index);
2974
insert(text, false, true, false);
2976
removeSelection(Q3TextDocument::Standard);
2980
Inserts \a text as a new paragraph at position \a para. If \a para
2981
is -1, the text is appended. Use append() if the append operation
2982
is performance critical.
2985
void Q3TextEdit::insertParagraph(const QString &text, int para)
2987
#ifdef QT_TEXTEDIT_OPTIMIZATION
2989
optimInsert(text + "\n", para, 0);
2993
for (int i = 0; i < (int)doc->numSelections(); ++i)
2994
doc->removeSelection(i);
2996
Q3TextParagraph *p = doc->paragAt(para);
3000
p = doc->lastParagraph();
3002
Q3TextCursor old = *cursor;
3005
cursor->setParagraph(p);
3006
cursor->setIndex(0);
3008
qtextedit_ignore_readonly = true;
3009
if (append && cursor->paragraph()->length() > 1) {
3010
cursor->setIndex(cursor->paragraph()->length() - 1);
3011
doKeyboardAction(ActionReturn);
3013
insert(text, false, true, true);
3014
doKeyboardAction(ActionReturn);
3015
qtextedit_ignore_readonly = false;
3025
Removes the paragraph \a para.
3028
void Q3TextEdit::removeParagraph(int para)
3030
#ifdef QT_TEXTEDIT_OPTIMIZATION
3034
Q3TextParagraph *p = doc->paragAt(para);
3038
for (int i = 0; i < doc->numSelections(); ++i)
3039
doc->removeSelection(i);
3041
Q3TextCursor start(doc);
3042
Q3TextCursor end(doc);
3043
start.setParagraph(p);
3045
end.setParagraph(p);
3046
end.setIndex(p->length() - 1);
3048
if (!(p == doc->firstParagraph() && p == doc->lastParagraph())) {
3050
end.setParagraph(p->next());
3052
} else if (p->prev()) {
3053
start.setParagraph(p->prev());
3054
start.setIndex(p->prev()->length() - 1);
3058
doc->setSelectionStart(Q3TextDocument::Temp, start);
3059
doc->setSelectionEnd(Q3TextDocument::Temp, end);
3060
removeSelectedText(Q3TextDocument::Temp);
3064
Undoes the last operation.
3066
If there is no operation to undo, i.e. there is no undo step in
3067
the undo/redo history, nothing happens.
3069
\sa undoAvailable() redo() undoDepth()
3072
void Q3TextEdit::undo()
3075
if (isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled)
3078
for (int i = 0; i < (int)doc->numSelections(); ++i)
3079
doc->removeSelection(i);
3081
#ifndef QT_NO_CURSOR
3082
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
3087
Q3TextCursor *c = doc->undo(cursor);
3094
ensureCursorVisible();
3097
// ### If we get back to a completely blank textedit, it
3098
// is possible that cursor is invalid and further actions
3099
// might not fix the problem, so reset the cursor here.
3100
// This is copied from removeSeletedText(), it might be
3101
// okay to just call that.
3102
if (!cursor->isValid()) {
3104
cursor = new Q3TextCursor(doc);
3108
emit undoAvailable(isUndoAvailable());
3109
emit redoAvailable(isRedoAvailable());
3114
Redoes the last operation.
3116
If there is no operation to redo, i.e. there is no redo step in
3117
the undo/redo history, nothing happens.
3119
\sa redoAvailable() undo() undoDepth()
3122
void Q3TextEdit::redo()
3124
if (isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled)
3127
for (int i = 0; i < (int)doc->numSelections(); ++i)
3128
doc->removeSelection(i);
3130
#ifndef QT_NO_CURSOR
3131
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
3136
Q3TextCursor *c = doc->redo(cursor);
3142
ensureCursorVisible();
3144
ensureCursorVisible();
3147
emit undoAvailable(isUndoAvailable());
3148
emit redoAvailable(isRedoAvailable());
3153
Pastes the text from the clipboard into the text edit at the
3154
current cursor position. Only plain text is pasted.
3156
If there is no text in the clipboard nothing happens.
3158
\sa pasteSubType() cut() Q3TextEdit::copy()
3161
void Q3TextEdit::paste()
3163
#ifndef QT_NO_MIMECLIPBOARD
3166
QString subType = "plain";
3167
if (textFormat() != Qt::PlainText) {
3168
QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
3171
if (m->provides("application/x-qrichtext"))
3172
subType = "x-qrichtext";
3175
pasteSubType(subType.toLatin1());
3179
void Q3TextEdit::checkUndoRedoInfo(UndoRedoInfo::Type t)
3181
if (undoRedoInfo.valid() && t != undoRedoInfo.type) {
3184
undoRedoInfo.type = t;
3188
Repaints any paragraphs that have changed.
3190
Although used extensively internally you shouldn't need to call
3193
void Q3TextEdit::repaintChanged()
3195
if (!updatesEnabled() || !viewport()->updatesEnabled())
3198
updateContents(); // good enough until this class is rewritten
3202
Q3TextDrag *Q3TextEdit::dragObject(QWidget *parent) const
3204
if (!doc->hasSelection(Q3TextDocument::Standard) ||
3205
doc->selectedText(Q3TextDocument::Standard).isEmpty())
3207
if (textFormat() != Qt::RichText)
3208
return new Q3TextDrag(doc->selectedText(Q3TextDocument::Standard), parent);
3209
Q3RichTextDrag *drag = new Q3RichTextDrag(parent);
3210
drag->setPlainText(doc->selectedText(Q3TextDocument::Standard));
3211
drag->setRichText(doc->selectedText(Q3TextDocument::Standard, true));
3217
Copies the selected text (from selection 0) to the clipboard and
3218
deletes it from the text edit.
3220
If there is no selected text (in selection 0) nothing happens.
3222
\sa Q3TextEdit::copy() paste() pasteSubType()
3225
void Q3TextEdit::cut()
3230
removeSelectedText();
3233
void Q3TextEdit::normalCopy()
3236
Q3TextDrag *drag = dragObject();
3239
#ifndef QT_NO_MIMECLIPBOARD
3240
QApplication::clipboard()->setData(drag, d->clipboard_mode);
3241
#endif // QT_NO_MIMECLIPBOARD
3242
#endif // QT_NO_MIME
3246
Copies any selected text (from selection 0) to the clipboard.
3248
\sa hasSelectedText() copyAvailable()
3251
void Q3TextEdit::copy()
3253
#ifndef QT_NO_CLIPBOARD
3254
# ifdef QT_TEXTEDIT_OPTIMIZATION
3255
if (d->optimMode && optimHasSelection())
3256
QApplication::clipboard()->setText(optimSelectedText(), d->clipboard_mode);
3268
Re-indents the current paragraph.
3271
void Q3TextEdit::indent()
3277
if (!doc->hasSelection(Q3TextDocument::Standard))
3280
doc->indentSelection(Q3TextDocument::Standard);
3288
Reimplemented to allow tabbing through links. If \a n is true the
3289
tab moves the focus to the next child; if \a n is false the tab
3290
moves the focus to the previous child. Returns true if the focus
3291
was moved; otherwise returns false.
3294
bool Q3TextEdit::focusNextPrevChild(bool n)
3296
if (!isReadOnly() || !linksEnabled())
3298
bool b = doc->focusNextPrevChild(n);
3301
//##### this does not work with tables. The focusIndicator
3302
//should really be a Q3TextCursor. Fix 3.1
3303
makeParagVisible(doc->focusIndicator.parag);
3310
This functions sets the current format to \a f. Only the fields of \a
3311
f which are specified by the \a flags are used.
3314
void Q3TextEdit::setFormat(Q3TextFormat *f, int flags)
3316
if (doc->hasSelection(Q3TextDocument::Standard)) {
3318
Q3TextCursor c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
3320
Q3TextCursor c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
3324
undoRedoInfo.type = UndoRedoInfo::Format;
3325
undoRedoInfo.id = c1.paragraph()->paragId();
3326
undoRedoInfo.index = c1.index();
3327
undoRedoInfo.eid = c2.paragraph()->paragId();
3328
undoRedoInfo.eindex = c2.index();
3329
readFormats(c1, c2, undoRedoInfo.d->text);
3330
undoRedoInfo.format = f;
3331
undoRedoInfo.flags = flags;
3334
doc->setFormat(Q3TextDocument::Standard, f, flags);
3341
if (currentFormat && currentFormat->key() != f->key()) {
3342
currentFormat->removeRef();
3343
currentFormat = doc->formatCollection()->format(f);
3344
if (currentFormat->isMisspelled()) {
3345
currentFormat->removeRef();
3346
currentFormat = doc->formatCollection()->format(currentFormat->font(),
3347
currentFormat->color());
3349
emit currentFontChanged(currentFormat->font());
3350
emit currentColorChanged(currentFormat->color());
3351
emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
3352
if (cursor->index() == cursor->paragraph()->length() - 1) {
3353
currentFormat->addRef();
3354
cursor->paragraph()->string()->setFormat(cursor->index(), currentFormat, true);
3355
if (cursor->paragraph()->length() == 1) {
3356
cursor->paragraph()->invalidate(0);
3357
cursor->paragraph()->format();
3365
\warning In Qt 3.1 we will provide a cleaer API for the
3366
functionality which is provided by this function and in Qt 4.0 this
3367
function will go away.
3369
Sets the paragraph style of the current paragraph
3370
to \a dm. If \a dm is Q3StyleSheetItem::DisplayListItem, the
3371
type of the list item is set to \a listStyle.
3376
void Q3TextEdit::setParagType(Q3StyleSheetItem::DisplayMode dm,
3377
Q3StyleSheetItem::ListStyle listStyle)
3383
Q3TextParagraph *start = cursor->paragraph();
3384
Q3TextParagraph *end = start;
3385
if (doc->hasSelection(Q3TextDocument::Standard)) {
3386
start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
3387
end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
3388
if (end->paragId() < start->paragId())
3389
return; // do not trust our selections
3393
undoRedoInfo.type = UndoRedoInfo::Style;
3394
undoRedoInfo.id = start->paragId();
3395
undoRedoInfo.eid = end->paragId();
3396
undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
3398
while (start != end->next()) {
3399
start->setListStyle(listStyle);
3400
if (dm == Q3StyleSheetItem::DisplayListItem) {
3401
start->setListItem(true);
3402
if(start->listDepth() == 0)
3403
start->setListDepth(1);
3404
} else if (start->isListItem()) {
3405
start->setListItem(false);
3406
start->setListDepth(qMax(start->listDepth()-1, 0));
3408
start = start->next();
3420
Sets the alignment of the current paragraph to \a a. Valid
3421
alignments are \c Qt::AlignLeft, \c Qt::AlignRight,
3422
\c Qt::AlignJustify and \c Qt::AlignCenter (which centers
3426
void Q3TextEdit::setAlignment(int a)
3428
if (isReadOnly() || block_set_alignment)
3432
Q3TextParagraph *start = cursor->paragraph();
3433
Q3TextParagraph *end = start;
3434
if (doc->hasSelection(Q3TextDocument::Standard)) {
3435
start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph();
3436
end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph();
3437
if (end->paragId() < start->paragId())
3438
return; // do not trust our selections
3442
undoRedoInfo.type = UndoRedoInfo::Style;
3443
undoRedoInfo.id = start->paragId();
3444
undoRedoInfo.eid = end->paragId();
3445
undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid);
3447
while (start != end->next()) {
3448
start->setAlignment(a);
3449
start = start->next();
3456
if (currentAlignment != a) {
3457
currentAlignment = a;
3458
emit currentAlignmentChanged(currentAlignment);
3464
void Q3TextEdit::updateCurrentFormat()
3466
int i = cursor->index();
3469
if (doc->useFormatCollection() &&
3470
(!currentFormat || currentFormat->key() != cursor->paragraph()->at(i)->format()->key())) {
3472
currentFormat->removeRef();
3473
currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(i)->format());
3474
if (currentFormat->isMisspelled()) {
3475
currentFormat->removeRef();
3476
currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color());
3478
emit currentFontChanged(currentFormat->font());
3479
emit currentColorChanged(currentFormat->color());
3480
emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
3483
if (currentAlignment != cursor->paragraph()->alignment()) {
3484
currentAlignment = cursor->paragraph()->alignment();
3485
block_set_alignment = true;
3486
emit currentAlignmentChanged(currentAlignment);
3487
block_set_alignment = false;
3492
If \a b is true sets the current format to italic; otherwise sets
3493
the current format to non-italic.
3498
void Q3TextEdit::setItalic(bool b)
3500
Q3TextFormat f(*currentFormat);
3502
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3503
setFormat(f2, Q3TextFormat::Italic);
3507
If \a b is true sets the current format to bold; otherwise sets
3508
the current format to non-bold.
3513
void Q3TextEdit::setBold(bool b)
3515
Q3TextFormat f(*currentFormat);
3517
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3518
setFormat(f2, Q3TextFormat::Bold);
3522
If \a b is true sets the current format to underline; otherwise
3523
sets the current format to non-underline.
3528
void Q3TextEdit::setUnderline(bool b)
3530
Q3TextFormat f(*currentFormat);
3532
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3533
setFormat(f2, Q3TextFormat::Underline);
3537
Sets the font family of the current format to \a fontFamily.
3539
\sa family() setCurrentFont()
3542
void Q3TextEdit::setFamily(const QString &fontFamily)
3544
Q3TextFormat f(*currentFormat);
3545
f.setFamily(fontFamily);
3546
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3547
setFormat(f2, Q3TextFormat::Family);
3551
Sets the point size of the current format to \a s.
3553
Note that if \a s is zero or negative, the behavior of this
3554
function is not defined.
3556
\sa pointSize() setCurrentFont() setFamily()
3559
void Q3TextEdit::setPointSize(int s)
3561
Q3TextFormat f(*currentFormat);
3563
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3564
setFormat(f2, Q3TextFormat::Size);
3568
Sets the color of the current format, i.e. of the text, to \a c.
3570
\sa color() setPaper()
3573
void Q3TextEdit::setColor(const QColor &c)
3575
Q3TextFormat f(*currentFormat);
3577
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3578
setFormat(f2, Q3TextFormat::Color);
3582
Sets the vertical alignment of the current format, i.e. of the
3585
\sa color() setPaper()
3588
void Q3TextEdit::setVerticalAlignment(VerticalAlignment a)
3590
Q3TextFormat f(*currentFormat);
3591
f.setVAlign((Q3TextFormat::VerticalAlignment)a);
3592
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3593
setFormat(f2, Q3TextFormat::VAlign);
3596
void Q3TextEdit::setFontInternal(const QFont &f_)
3598
Q3TextFormat f(*currentFormat);
3600
Q3TextFormat *f2 = doc->formatCollection()->format(&f);
3601
setFormat(f2, Q3TextFormat::Font);
3605
QString Q3TextEdit::text() const
3607
#ifdef QT_TEXTEDIT_OPTIMIZATION
3612
Q3TextParagraph *p = doc->firstParagraph();
3613
if (!p || (!p->next() && p->length() <= 1))
3614
return QString::fromLatin1("");
3617
return doc->originalText();
3624
Returns the text of paragraph \a para.
3626
If textFormat() is \c Qt::RichText the text will contain HTML
3630
QString Q3TextEdit::text(int para) const
3632
#ifdef QT_TEXTEDIT_OPTIMIZATION
3633
if (d->optimMode && (d->od->numLines >= para)) {
3634
QString paraStr = d->od->lines[LOGOFFSET(para)];
3635
if (paraStr.isEmpty())
3640
return doc->text(para);
3646
Changes the text of the text edit to the string \a text and the
3647
context to \a context. Any previous text is removed.
3649
\a text may be interpreted either as plain text or as rich text,
3650
depending on the textFormat(). The default setting is \c Qt::AutoText,
3651
i.e. the text edit auto-detects the format from \a text.
3653
For rich text the rendering style and available tags are defined
3654
by a styleSheet(); see Q3StyleSheet for details.
3656
The optional \a context is a path which the text edit's
3657
Q3MimeSourceFactory uses to resolve the locations of files and
3658
images. (See \l{Q3TextEdit::Q3TextEdit()}.) It is passed to the text
3659
edit's Q3MimeSourceFactory when quering data.
3661
Note that the undo/redo history is cleared by this function.
3663
\sa text(), setTextFormat()
3666
void Q3TextEdit::setText(const QString &text, const QString &context)
3668
#ifdef QT_TEXTEDIT_OPTIMIZATION
3674
if (!isModified() && isReadOnly() &&
3675
this->context() == context && this->text() == text)
3678
emit undoAvailable(false);
3679
emit redoAvailable(false);
3680
undoRedoInfo.clear();
3681
doc->commands()->clear();
3684
int oldCursorPos = cursor->index();
3685
int oldCursorPar = cursor->paragraph()->paragId();
3686
cursor->restoreState();
3688
doc->setText(text, context);
3690
if (wrapMode == FixedPixelWidth) {
3691
resizeContents(wrapWidth, 0);
3692
doc->setWidth(wrapWidth);
3693
doc->setMinimumWidth(wrapWidth);
3695
doc->setMinimumWidth(-1);
3696
resizeContents(0, 0);
3699
lastFormatted = doc->firstParagraph();
3700
cursor = new Q3TextCursor(doc);
3706
if (cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar) {
3707
emit cursorPositionChanged(cursor);
3708
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
3711
updateCurrentFormat();
3712
d->scrollToAnchor.clear();
3716
\property Q3TextEdit::text
3717
\brief the text edit's text
3719
There is no default text.
3721
On setting, any previous text is deleted.
3723
The text may be interpreted either as plain text or as rich text,
3724
depending on the textFormat(). The default setting is \c Qt::AutoText,
3725
i.e. the text edit auto-detects the format of the text.
3727
For richtext, calling text() on an editable Q3TextEdit will cause
3728
the text to be regenerated from the textedit. This may mean that
3729
the QString returned may not be exactly the same as the one that
3737
\property Q3TextEdit::readOnly
3738
\brief whether the text edit is read-only
3740
In a read-only text edit the user can only navigate through the
3741
text and select text; modifying the text is not possible.
3743
This property's default is false.
3747
Finds the next occurrence of the string, \a expr. Returns true if
3748
\a expr was found; otherwise returns false.
3750
If \a para and \a index are both 0 the search begins from the
3751
current cursor position. If \a para and \a index are both not 0,
3752
the search begins from the \c{*}\a{index} character position in the
3753
\c{*}\a{para} paragraph.
3755
If \a cs is true the search is case sensitive, otherwise it is
3756
case insensitive. If \a wo is true the search looks for whole word
3757
matches only; otherwise it searches for any matching text. If \a
3758
forward is true (the default) the search works forward from the
3759
starting position to the end of the text, otherwise it works
3760
backwards to the beginning of the text.
3762
If \a expr is found the function returns true. If \a index and \a
3763
para are not 0, the number of the paragraph in which the first
3764
character of the match was found is put into \c{*}\a{para}, and the
3765
index position of that character within the paragraph is put into
3768
If \a expr is not found the function returns false. If \a index
3769
and \a para are not 0 and \a expr is not found, \c{*}\a{index}
3770
and \c{*}\a{para} are undefined.
3772
Please note that this function will make the next occurrence of
3773
the string (if found) the current selection, and will thus
3774
modify the cursor position.
3777
bool Q3TextEdit::find(const QString &expr, bool cs, bool wo, bool forward,
3778
int *para, int *index)
3780
#ifdef QT_TEXTEDIT_OPTIMIZATION
3782
return optimFind(expr, cs, wo, forward, para, index);
3785
#ifndef QT_NO_CURSOR
3786
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
3788
Q3TextCursor findcur = *cursor;
3789
if (para && index) {
3790
if (doc->paragAt(*para))
3791
findcur.gotoPosition(doc->paragAt(*para), *index);
3794
} else if (doc->hasSelection(Q3TextDocument::Standard)){
3795
// maks sure we do not find the same selection again
3797
findcur.gotoNextLetter();
3799
findcur.gotoPreviousLetter();
3800
} else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) {
3803
removeSelection(Q3TextDocument::Standard);
3804
bool found = doc->find(findcur, expr, cs, wo, forward);
3807
*para = findcur.paragraph()->paragId();
3809
*index = findcur.index();
3812
ensureCursorVisible();
3816
emit cursorPositionChanged(cursor);
3817
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
3822
void Q3TextEdit::blinkCursor()
3824
bool cv = cursorVisible;
3825
blinkCursorVisible = !blinkCursorVisible;
3826
drawCursor(blinkCursorVisible);
3831
Sets the cursor to position \a index in paragraph \a para.
3833
\sa getCursorPosition()
3836
void Q3TextEdit::setCursorPosition(int para, int index)
3838
Q3TextParagraph *p = doc->paragAt(para);
3842
if (index > p->length() - 1)
3843
index = p->length() - 1;
3846
cursor->setParagraph(p);
3847
cursor->setIndex(index);
3848
ensureCursorVisible();
3850
updateCurrentFormat();
3851
emit cursorPositionChanged(cursor);
3852
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
3856
This function sets the \c{*}\a{para} and \c{*}\a{index} parameters to the
3857
current cursor position. \a para and \a index must not be 0.
3859
\sa setCursorPosition()
3862
void Q3TextEdit::getCursorPosition(int *para, int *index) const
3864
if (!para || !index)
3866
*para = cursor->paragraph()->paragId();
3867
*index = cursor->index();
3871
Sets a selection which starts at position \a indexFrom in
3872
paragraph \a paraFrom and ends at position \a indexTo in paragraph
3875
Any existing selections which have a different id (\a selNum) are
3876
left alone, but if an existing selection has the same id as \a
3877
selNum it is removed and replaced by this selection.
3879
Uses the selection settings of selection \a selNum. If \a selNum
3880
is 0, this is the default selection.
3882
The cursor is moved to the end of the selection if \a selNum is 0,
3883
otherwise the cursor position remains unchanged.
3885
\sa getSelection() selectedText
3888
void Q3TextEdit::setSelection(int paraFrom, int indexFrom,
3889
int paraTo, int indexTo, int selNum)
3891
#ifdef QT_TEXTEDIT_OPTIMIZATION
3893
optimSetSelection(paraFrom, indexFrom, paraTo, indexTo);
3898
if (doc->hasSelection(selNum)) {
3899
doc->removeSelection(selNum);
3902
if (selNum > doc->numSelections() - 1)
3903
doc->addSelection(selNum);
3904
Q3TextParagraph *p1 = doc->paragAt(paraFrom);
3907
Q3TextParagraph *p2 = doc->paragAt(paraTo);
3911
if (indexFrom > p1->length() - 1)
3912
indexFrom = p1->length() - 1;
3913
if (indexTo > p2->length() - 1)
3914
indexTo = p2->length() - 1;
3917
Q3TextCursor c = *cursor;
3918
Q3TextCursor oldCursor = *cursor;
3920
c.setIndex(indexFrom);
3921
cursor->setParagraph(p2);
3922
cursor->setIndex(indexTo);
3923
doc->setSelectionStart(selNum, c);
3924
doc->setSelectionEnd(selNum, *cursor);
3926
ensureCursorVisible();
3927
if (selNum != Q3TextDocument::Standard)
3928
*cursor = oldCursor;
3933
If there is a selection, \c{*}\a{paraFrom} is set to the number of the
3934
paragraph in which the selection begins and \c{*}\a{paraTo} is set to
3935
the number of the paragraph in which the selection ends. (They
3936
could be the same.) \c{*}\a{indexFrom} is set to the index at which the
3937
selection begins within \c{*}\a{paraFrom}, and \c{*}\a{indexTo} is set to
3938
the index at which the selection ends within \c{*}\a{paraTo}.
3940
If there is no selection, \c{*}\a{paraFrom}, \c{*}\a{indexFrom},
3941
\c{*}\a{paraTo} and \c{*}\a{indexTo} are all set to -1.
3943
If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this
3944
function does nothing.
3946
The \a selNum is the number of the selection (multiple selections
3947
are supported). It defaults to 0 (the default selection).
3949
\sa setSelection() selectedText
3952
void Q3TextEdit::getSelection(int *paraFrom, int *indexFrom,
3953
int *paraTo, int *indexTo, int selNum) const
3955
if (!paraFrom || !paraTo || !indexFrom || !indexTo)
3957
#ifdef QT_TEXTEDIT_OPTIMIZATION
3959
*paraFrom = d->od->selStart.line;
3960
*paraTo = d->od->selEnd.line;
3961
*indexFrom = d->od->selStart.index;
3962
*indexTo = d->od->selEnd.index;
3966
if (!doc->hasSelection(selNum)) {
3974
doc->selectionStart(selNum, *paraFrom, *indexFrom);
3975
doc->selectionEnd(selNum, *paraTo, *indexTo);
3979
\property Q3TextEdit::textFormat
3980
\brief the text format: rich text, plain text, log text or auto text.
3982
The text format is one of the following:
3984
\i Qt::PlainText - all characters, except newlines, are displayed
3985
verbatim, including spaces. Whenever a newline appears in the text
3986
the text edit inserts a hard line break and begins a new
3988
\i Qt::RichText - rich text rendering. The available styles are
3989
defined in the default stylesheet Q3StyleSheet::defaultSheet().
3990
\i Qt::LogText - optimized mode for very large texts. Supports a very
3991
limited set of formatting tags (color, bold, underline and italic
3993
\i Qt::AutoText - this is the default. The text edit autodetects which
3994
rendering style is best, \c Qt::PlainText or \c Qt::RichText. This is done
3995
by using the Q3StyleSheet::mightBeRichText() function.
3999
void Q3TextEdit::setTextFormat(Qt::TextFormat format)
4001
doc->setTextFormat(format);
4002
#ifdef QT_TEXTEDIT_OPTIMIZATION
4007
Qt::TextFormat Q3TextEdit::textFormat() const
4009
return doc->textFormat();
4013
Returns the number of paragraphs in the text; an empty textedit is always
4014
considered to have one paragraph, so 1 is returned in this case.
4017
int Q3TextEdit::paragraphs() const
4019
#ifdef QT_TEXTEDIT_OPTIMIZATION
4021
return d->od->numLines;
4024
return doc->lastParagraph()->paragId() + 1;
4028
Returns the number of lines in paragraph \a para, or -1 if there
4029
is no paragraph with index \a para.
4032
int Q3TextEdit::linesOfParagraph(int para) const
4034
#ifdef QT_TEXTEDIT_OPTIMIZATION
4036
if (d->od->numLines >= para)
4042
Q3TextParagraph *p = doc->paragAt(para);
4049
Returns the length of the paragraph \a para (i.e. the number of
4050
characters), or -1 if there is no paragraph with index \a para.
4052
This function ignores newlines.
4055
int Q3TextEdit::paragraphLength(int para) const
4057
#ifdef QT_TEXTEDIT_OPTIMIZATION
4059
if (d->od->numLines >= para) {
4060
if (d->od->lines[LOGOFFSET(para)].isEmpty()) // CR
4063
return d->od->lines[LOGOFFSET(para)].length();
4068
Q3TextParagraph *p = doc->paragAt(para);
4071
return p->length() - 1;
4075
Returns the number of lines in the text edit; this could be 0.
4077
\warning This function may be slow. Lines change all the time
4078
during word wrapping, so this function has to iterate over all the
4079
paragraphs and get the number of lines from each one individually.
4082
int Q3TextEdit::lines() const
4084
#ifdef QT_TEXTEDIT_OPTIMIZATION
4086
return d->od->numLines;
4089
Q3TextParagraph *p = doc->firstParagraph();
4100
Returns the line number of the line in paragraph \a para in which
4101
the character at position \a index appears. The \a index position is
4102
relative to the beginning of the paragraph. If there is no such
4103
paragraph or no such character at the \a index position (e.g. the
4104
index is out of range) -1 is returned.
4107
int Q3TextEdit::lineOfChar(int para, int index)
4109
Q3TextParagraph *p = doc->paragAt(para);
4114
Q3TextStringChar *c = p->lineStartOfChar(index, &idx, &line);
4121
void Q3TextEdit::setModified(bool m)
4123
bool oldModified = modified;
4125
if (modified && doc->oTextValid)
4126
doc->invalidateOriginalText();
4127
if (oldModified != modified)
4128
emit modificationChanged(modified);
4132
\property Q3TextEdit::modified
4133
\brief whether the document has been modified by the user
4136
bool Q3TextEdit::isModified() const
4141
void Q3TextEdit::setModified()
4148
Returns true if the current format is italic; otherwise returns false.
4153
bool Q3TextEdit::italic() const
4155
return currentFormat->font().italic();
4159
Returns true if the current format is bold; otherwise returns false.
4164
bool Q3TextEdit::bold() const
4166
return currentFormat->font().bold();
4170
Returns true if the current format is underlined; otherwise returns
4176
bool Q3TextEdit::underline() const
4178
return currentFormat->font().underline();
4182
Returns the font family of the current format.
4184
\sa setFamily() setCurrentFont() setPointSize()
4187
QString Q3TextEdit::family() const
4189
return currentFormat->font().family();
4193
Returns the point size of the font of the current format.
4195
\sa setFamily() setCurrentFont() setPointSize()
4198
int Q3TextEdit::pointSize() const
4200
return currentFormat->font().pointSize();
4204
Returns the color of the current format.
4206
\sa setColor() setPaper()
4209
QColor Q3TextEdit::color() const
4211
return currentFormat->color();
4215
Returns Q3ScrollView::font()
4217
\warning In previous versions this function returned the font of
4218
the current format. This lead to confusion. Please use
4219
currentFont() instead.
4222
QFont Q3TextEdit::font() const
4224
return Q3ScrollView::font();
4228
Returns the font of the current format.
4230
\sa setCurrentFont() setFamily() setPointSize()
4233
QFont Q3TextEdit::currentFont() const
4235
return currentFormat->font();
4240
Returns the alignment of the current paragraph.
4245
int Q3TextEdit::alignment() const
4247
return currentAlignment;
4251
Returns the vertical alignment of the current format.
4253
\sa setVerticalAlignment()
4256
Q3TextEdit::VerticalAlignment Q3TextEdit::verticalAlignment() const
4258
return (Q3TextEdit::VerticalAlignment) currentFormat->vAlign();
4261
void Q3TextEdit::startDrag()
4263
#ifndef QT_NO_DRAGANDDROP
4264
mousePressed = false;
4265
inDoubleClick = false;
4266
Q3DragObject *drag = dragObject(viewport());
4272
if (drag->drag() && Q3DragObject::target() != this && Q3DragObject::target() != viewport())
4273
removeSelectedText();
4279
If \a select is true (the default), all the text is selected as
4280
selection 0. If \a select is false any selected text is
4281
unselected, i.e. the default selection (selection 0) is cleared.
4286
void Q3TextEdit::selectAll(bool select)
4288
#ifdef QT_TEXTEDIT_OPTIMIZATION
4293
optimRemoveSelection();
4298
doc->removeSelection(Q3TextDocument::Standard);
4300
doc->selectAll(Q3TextDocument::Standard);
4302
emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
4303
emit selectionChanged();
4304
#ifndef QT_NO_CURSOR
4305
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
4309
void Q3TextEdit::UndoRedoInfo::clear()
4312
if (type == Insert || type == Return)
4313
doc->addCommand(new Q3TextInsertCommand(doc, id, index, d->text.rawData(), styleInformation));
4314
else if (type == Format)
4315
doc->addCommand(new Q3TextFormatCommand(doc, id, index, eid, eindex, d->text.rawData(), format, flags));
4316
else if (type == Style)
4317
doc->addCommand(new Q3TextStyleCommand(doc, id, eid, styleInformation));
4318
else if (type != Invalid) {
4319
doc->addCommand(new Q3TextDeleteCommand(doc, id, index, d->text.rawData(), styleInformation));
4326
styleInformation = QByteArray();
4331
If there is some selected text (in selection 0) it is deleted. If
4332
there is no selected text (in selection 0) the character to the
4333
right of the text cursor is deleted.
4335
\sa removeSelectedText() cut()
4338
void Q3TextEdit::del()
4340
if (doc->hasSelection(Q3TextDocument::Standard)) {
4341
removeSelectedText();
4345
doKeyboardAction(ActionDelete);
4349
Q3TextEdit::UndoRedoInfo::UndoRedoInfo(Q3TextDocument *dc)
4350
: type(Invalid), doc(dc)
4352
d = new QUndoRedoInfoPrivate;
4358
Q3TextEdit::UndoRedoInfo::~UndoRedoInfo()
4363
bool Q3TextEdit::UndoRedoInfo::valid() const
4365
return id >= 0 && type != Invalid;
4371
Resets the current format to the default format.
4374
void Q3TextEdit::resetFormat()
4376
setAlignment(Qt::AlignAuto);
4377
setParagType(Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc);
4378
setFormat(doc->formatCollection()->defaultFormat(), Q3TextFormat::Format);
4382
Returns the Q3StyleSheet which is being used by this text edit.
4387
Q3StyleSheet* Q3TextEdit::styleSheet() const
4389
return doc->styleSheet();
4393
Sets the stylesheet to use with this text edit to \a styleSheet.
4394
Changes will only take effect for new text added with setText() or
4400
void Q3TextEdit::setStyleSheet(Q3StyleSheet* styleSheet)
4402
doc->setStyleSheet(styleSheet);
4406
\property Q3TextEdit::paper
4407
\brief the background (paper) brush.
4409
The brush that is currently used to draw the background of the
4410
text edit. The initial setting is an empty brush.
4413
void Q3TextEdit::setPaper(const QBrush& pap)
4415
doc->setPaper(new QBrush(pap));
4417
viewport()->setBackgroundPixmap( *pap.pixmap() );
4418
QPalette pal = palette();
4419
pal.setColor(QPalette::Background, pap.color());
4421
pal = viewport()->palette();
4422
pal.setColor(QPalette::Background, pap.color());
4423
viewport()->setPalette(pal);
4424
#ifdef QT_TEXTEDIT_OPTIMIZATION
4425
// force a repaint of the entire viewport - using updateContents()
4426
// would clip the coords to the content size
4428
repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
4434
QBrush Q3TextEdit::paper() const
4437
return *doc->paper();
4438
return QBrush(palette().base());
4442
\property Q3TextEdit::linkUnderline
4443
\brief whether hypertext links will be underlined
4445
If true (the default) hypertext links will be displayed
4446
underlined. If false links will not be displayed underlined.
4449
void Q3TextEdit::setLinkUnderline(bool b)
4451
if (doc->underlineLinks() == b)
4453
doc->setUnderlineLinks(b);
4457
bool Q3TextEdit::linkUnderline() const
4459
return doc->underlineLinks();
4463
Sets the text edit's mimesource factory to \a factory. See
4464
Q3MimeSourceFactory for further details.
4466
\sa mimeSourceFactory()
4470
void Q3TextEdit::setMimeSourceFactory(Q3MimeSourceFactory* factory)
4472
doc->setMimeSourceFactory(factory);
4476
Returns the Q3MimeSourceFactory which is being used by this text
4479
\sa setMimeSourceFactory()
4482
Q3MimeSourceFactory* Q3TextEdit::mimeSourceFactory() const
4484
return doc->mimeSourceFactory();
4489
Returns how many pixels high the text edit needs to be to display
4490
all the text if the text edit is \a w pixels wide.
4493
int Q3TextEdit::heightForWidth(int w) const
4495
int oldw = doc->width();
4496
doc->doLayout(0, w);
4497
int h = doc->height();
4498
doc->setWidth(oldw);
4500
((Q3TextEdit*)this)->formatMore();
4505
Appends a new paragraph with \a text to the end of the text edit. Note that
4506
the undo/redo history is cleared by this function, and no undo
4507
history is kept for appends which makes them faster than
4508
insert()s. If you want to append text which is added to the
4509
undo/redo history as well, use insertParagraph().
4512
void Q3TextEdit::append(const QString &text)
4514
#ifdef QT_TEXTEDIT_OPTIMIZATION
4520
// flush and clear the undo/redo stack if necessary
4521
undoRedoInfo.clear();
4522
doc->commands()->clear();
4524
doc->removeSelection(Q3TextDocument::Standard);
4525
Qt::TextFormat f = doc->textFormat();
4526
if (f == Qt::AutoText) {
4527
if (Q3StyleSheet::mightBeRichText(text))
4534
Q3TextCursor oldc(*cursor);
4535
ensureFormatted(doc->lastParagraph());
4536
bool atBottom = contentsY() >= contentsHeight() - visibleHeight();
4538
if (cursor->index() > 0)
4539
cursor->splitAndInsertEmptyParagraph();
4541
if (f == Qt::PlainText) {
4542
cursor->insert(text, true);
4544
cursor->paragraph()->setListItem(false);
4545
cursor->paragraph()->setListDepth(0);
4546
if (cursor->paragraph()->prev())
4547
cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change
4548
doc->setRichTextInternal(text);
4556
cursorVisible = true;
4562
\property Q3TextEdit::hasSelectedText
4563
\brief whether some text is selected in selection 0
4566
bool Q3TextEdit::hasSelectedText() const
4568
#ifdef QT_TEXTEDIT_OPTIMIZATION
4570
return optimHasSelection();
4573
return doc->hasSelection(Q3TextDocument::Standard);
4577
\property Q3TextEdit::selectedText
4578
\brief The selected text (from selection 0) or an empty string if
4579
there is no currently selected text (in selection 0).
4581
The text is always returned as \c Qt::PlainText if the textFormat() is
4582
\c Qt::PlainText or \c Qt::AutoText, otherwise it is returned as HTML.
4587
QString Q3TextEdit::selectedText() const
4589
#ifdef QT_TEXTEDIT_OPTIMIZATION
4591
return optimSelectedText();
4594
return doc->selectedText(Q3TextDocument::Standard, textFormat() == Qt::RichText);
4597
bool Q3TextEdit::handleReadOnlyKeyEvent(QKeyEvent *e)
4601
setContentsPos(contentsX(), contentsY() + 10);
4604
setContentsPos(contentsX(), contentsY() - 10);
4607
setContentsPos(contentsX() - 10, contentsY());
4610
setContentsPos(contentsX() + 10, contentsY());
4612
case Qt::Key_PageUp:
4613
setContentsPos(contentsX(), contentsY() - visibleHeight());
4615
case Qt::Key_PageDown:
4616
setContentsPos(contentsX(), contentsY() + visibleHeight());
4619
setContentsPos(contentsX(), 0);
4622
setContentsPos(contentsX(), contentsHeight() - visibleHeight());
4624
case Qt::Key_F16: // Copy key on Sun keyboards
4627
#ifndef QT_NO_NETWORKPROTOCOL
4628
case Qt::Key_Return:
4630
case Qt::Key_Space: {
4631
if (!doc->focusIndicator.href.isEmpty()
4632
|| !doc->focusIndicator.name.isEmpty()) {
4633
if (!doc->focusIndicator.href.isEmpty()) {
4634
QUrl u = QUrl(doc->context()).resolved(doc->focusIndicator.href);
4635
emitLinkClicked(u.toString(QUrl::None));
4637
if (!doc->focusIndicator.name.isEmpty())
4638
if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
4639
emit browser->anchorClicked(doc->focusIndicator.name, doc->focusIndicator.href);
4641
#ifndef QT_NO_CURSOR
4642
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
4648
if (e->state() & Qt::ControlButton) {
4650
case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
4654
case Qt::Key_Insert:
4670
Returns the context of the text edit. The context is a path which
4671
the text edit's Q3MimeSourceFactory uses to resolve the locations
4672
of files and images.
4677
QString Q3TextEdit::context() const
4679
return doc->context();
4683
\property Q3TextEdit::documentTitle
4684
\brief the title of the document parsed from the text.
4686
For \c Qt::PlainText the title will be an empty string. For \c
4687
Qt::RichText the title will be the text between the \c{<title>} tags,
4688
if present, otherwise an empty string.
4691
QString Q3TextEdit::documentTitle() const
4693
return doc->attributes()["title"];
4696
void Q3TextEdit::makeParagVisible(Q3TextParagraph *p)
4698
setContentsPos(contentsX(), qMin(p->rect().y(), contentsHeight() - visibleHeight()));
4702
Scrolls the text edit to make the text at the anchor called \a
4703
name visible, if it can be found in the document. If the anchor
4704
isn't found no scrolling will occur. An anchor is defined using
4705
the HTML anchor tag, e.g. \c{<a name="target">}.
4708
void Q3TextEdit::scrollToAnchor(const QString& name)
4711
d->scrollToAnchor = name;
4717
Q3TextCursor cursor(doc);
4718
Q3TextParagraph* last = doc->lastParagraph();
4720
Q3TextStringChar* c = cursor.paragraph()->at(cursor.index());
4722
QString a = c->anchorName();
4724
(a.contains('#') && a.split('#').contains(name))) {
4725
setContentsPos(contentsX(), qMin(cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight()));
4729
if (cursor.paragraph() == last && cursor.atParagEnd() )
4731
cursor.gotoNextLetter();
4736
Returns the text for the attribute \a attr (\c Qt::AnchorHref by
4737
default) if there is an anchor at position \a pos (in contents
4738
coordinates); otherwise returns an empty string.
4741
QString Q3TextEdit::anchorAt(const QPoint& pos, Qt::AnchorAttribute attr)
4743
Q3TextCursor c(doc);
4744
placeCursor(pos, &c, true);
4746
case Qt::AnchorName:
4747
return c.paragraph()->at(c.index())->anchorName();
4748
case Qt::AnchorHref:
4749
return c.paragraph()->at(c.index())->anchorHref();
4751
// incase the compiler is really dumb about determining if a function
4752
// returns something :)
4756
void Q3TextEdit::documentWidthChanged(int w)
4758
resizeContents(qMax(visibleWidth(), w), contentsHeight());
4763
This function does nothing
4766
void Q3TextEdit::updateStyles()
4770
void Q3TextEdit::setDocument(Q3TextDocument *dc)
4776
cursor = new Q3TextCursor(doc);
4778
undoRedoInfo.doc = doc;
4782
#ifndef QT_NO_CLIPBOARD
4785
Pastes the text with format \a subtype from the clipboard into the
4786
text edit at the current cursor position. The \a subtype can be
4789
If there is no text with format \a subtype in the clipboard
4792
\sa paste() cut() Q3TextEdit::copy()
4795
void Q3TextEdit::pasteSubType(const QByteArray &subtype)
4797
#ifndef QT_NO_MIMECLIPBOARD
4798
QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
4799
pasteSubType(subtype, m);
4805
void Q3TextEdit::pasteSubType(const QByteArray& subtype, QMimeSource *m)
4808
QByteArray st = subtype;
4810
if (subtype != "x-qrichtext")
4811
st.prepend("text/");
4813
st.prepend("application/");
4816
if (doc->hasSelection(Q3TextDocument::Standard))
4817
removeSelectedText();
4818
if (!Q3RichTextDrag::canDecode(m))
4821
if (!Q3RichTextDrag::decode(m, t, st, subtype))
4823
if (st == "application/x-qrichtext") {
4825
if ((start = t.indexOf("<!--StartFragment-->")) != -1) {
4827
int end = t.indexOf("<!--EndFragment-->");
4828
Q3TextCursor oldC = *cursor;
4830
// during the setRichTextInternal() call the cursors
4831
// paragraph might get joined with the provious one, so
4832
// the cursors one would get deleted and oldC.paragraph()
4833
// would be a dnagling pointer. To avoid that try to go
4834
// one letter back and later go one forward again.
4835
oldC.gotoPreviousLetter();
4836
bool couldGoBack = oldC != *cursor;
4837
// first para might get deleted, so remember to reset it
4838
bool wasAtFirst = oldC.paragraph() == doc->firstParagraph();
4841
t = t.mid(start, end - start);
4844
lastFormatted = cursor->paragraph();
4845
if (lastFormatted->prev())
4846
lastFormatted = lastFormatted->prev();
4847
doc->setRichTextInternal(t, cursor);
4849
// the first para might have been deleted in
4850
// setRichTextInternal(). To be sure, reset it if
4853
int index = oldC.index();
4854
oldC.setParagraph(doc->firstParagraph());
4855
oldC.setIndex(index);
4858
// if we went back one letter before (see last comment),
4859
// go one forward to point to the right position
4861
oldC.gotoNextLetter();
4863
if (undoEnabled && !isReadOnly()) {
4864
doc->setSelectionStart(Q3TextDocument::Temp, oldC);
4865
doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
4867
checkUndoRedoInfo(UndoRedoInfo::Insert);
4868
if (!undoRedoInfo.valid()) {
4869
undoRedoInfo.id = oldC.paragraph()->paragId();
4870
undoRedoInfo.index = oldC.index();
4871
undoRedoInfo.d->text.clear();
4873
int oldLen = undoRedoInfo.d->text.length();
4874
if (!doc->preProcessor()) {
4875
QString txt = doc->selectedText(Q3TextDocument::Temp);
4876
undoRedoInfo.d->text += txt;
4877
for (int i = 0; i < (int)txt.length(); ++i) {
4878
if (txt[i] != '\n' && oldC.paragraph()->at(oldC.index())->format()) {
4879
oldC.paragraph()->at(oldC.index())->format()->addRef();
4880
undoRedoInfo.d->text.
4881
setFormat(oldLen + i, oldC.paragraph()->at(oldC.index())->format(), true);
4883
oldC.gotoNextLetter();
4886
undoRedoInfo.clear();
4887
removeSelection(Q3TextDocument::Temp);
4894
ensureCursorVisible();
4898
#if defined(Q_OS_WIN32)
4899
// Need to convert CRLF to LF
4900
t.replace("\r\n", "\n");
4901
#elif defined(Q_OS_MAC)
4902
//need to convert CR to LF
4903
t.replace('\r', '\n');
4905
QChar *uc = (QChar *)t.unicode();
4906
for (int i = 0; i < t.length(); i++) {
4907
if (uc[i] < ' ' && uc[i] != '\n' && uc[i] != '\t')
4911
insert(t, false, true);
4916
#ifndef QT_NO_MIMECLIPBOARD
4918
Prompts the user to choose a type from a list of text types
4919
available, then copies text from the clipboard (if there is any)
4920
into the text edit at the current text cursor position. Any
4921
selected text (in selection 0) is first deleted.
4923
void Q3TextEdit::pasteSpecial(const QPoint& pt)
4925
QByteArray st = pickSpecial(QApplication::clipboard()->data(d->clipboard_mode),
4932
QByteArray Q3TextEdit::pickSpecial(QMimeSource* ms, bool always_ask, const QPoint& pt)
4939
QHash<QString, bool> done;
4940
for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
4941
int semi = fmt.indexOf(';');
4943
fmt = fmt.left(semi);
4944
if (fmt.left(5) == "text/") {
4946
if (!done.contains(fmt)) {
4947
done.insert(fmt,true);
4948
popup.insertItem(fmt, i);
4954
QAction *action = (n == 1 && !always_ask)
4955
? popup.actions().at(0)
4958
return action->text().toLatin1();
4962
for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
4963
int semi = fmt.indexOf(';');
4965
fmt = fmt.left(semi);
4966
if (fmt.left(5) == "text/") {
4968
return fmt.latin1();
4973
return QByteArray();
4975
#endif // QT_NO_MIME
4976
#endif // QT_NO_CLIPBOARD
4979
\enum Q3TextEdit::WordWrap
4981
This enum defines the Q3TextEdit's word wrap modes.
4983
\value NoWrap Do not wrap the text.
4985
\value WidgetWidth Wrap the text at the current width of the
4986
widget (this is the default). Wrapping is at whitespace by
4987
default; this can be changed with setWrapPolicy().
4989
\value FixedPixelWidth Wrap the text at a fixed number of pixels
4990
from the widget's left side. The number of pixels is set with
4991
wrapColumnOrWidth().
4993
\value FixedColumnWidth Wrap the text at a fixed number of
4994
character columns from the widget's left side. The number of
4995
characters is set with wrapColumnOrWidth(). This is useful if you
4996
need formatted text that can also be displayed gracefully on
4997
devices with monospaced fonts, for example a standard VT100
4998
terminal, where you might set wrapColumnOrWidth() to 80.
5000
\sa setWordWrap() wordWrap()
5004
\property Q3TextEdit::wordWrap
5005
\brief the word wrap mode
5007
The default mode is \c WidgetWidth which causes words to be
5008
wrapped at the right edge of the text edit. Wrapping occurs at
5009
whitespace, keeping whole words intact. If you want wrapping to
5010
occur within words use setWrapPolicy(). If you set a wrap mode of
5011
\c FixedPixelWidth or \c FixedColumnWidth you should also call
5012
setWrapColumnOrWidth() with the width you want.
5014
\sa WordWrap, wrapColumnOrWidth, wrapPolicy,
5017
void Q3TextEdit::setWordWrap(WordWrap mode)
5019
if (wrapMode == mode)
5024
document()->formatter()->setWrapEnabled(false);
5025
document()->formatter()->setWrapAtColumn(-1);
5026
doc->setWidth(visibleWidth());
5027
doc->setMinimumWidth(-1);
5030
lastFormatted = doc->firstParagraph();
5035
document()->formatter()->setWrapEnabled(true);
5036
document()->formatter()->setWrapAtColumn(-1);
5039
case FixedPixelWidth:
5040
document()->formatter()->setWrapEnabled(true);
5041
document()->formatter()->setWrapAtColumn(-1);
5044
setWrapColumnOrWidth(wrapWidth);
5046
case FixedColumnWidth:
5049
document()->formatter()->setWrapEnabled(true);
5050
document()->formatter()->setWrapAtColumn(wrapWidth);
5051
setWrapColumnOrWidth(wrapWidth);
5054
#ifdef QT_TEXTEDIT_OPTIMIZATION
5059
Q3TextEdit::WordWrap Q3TextEdit::wordWrap() const
5065
\property Q3TextEdit::wrapColumnOrWidth
5066
\brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
5068
If the wrap mode is \c FixedPixelWidth, the value is the number of
5069
pixels from the left edge of the text edit at which text should be
5070
wrapped. If the wrap mode is \c FixedColumnWidth, the value is the
5071
column number (in character columns) from the left edge of the
5072
text edit at which text should be wrapped.
5076
void Q3TextEdit::setWrapColumnOrWidth(int value)
5079
if (wrapMode == FixedColumnWidth) {
5080
document()->formatter()->setWrapAtColumn(wrapWidth);
5081
resizeContents(0, 0);
5082
doc->setWidth(visibleWidth());
5083
doc->setMinimumWidth(-1);
5084
} else if (wrapMode == FixedPixelWidth) {
5085
document()->formatter()->setWrapAtColumn(-1);
5086
resizeContents(wrapWidth, 0);
5087
doc->setWidth(wrapWidth);
5088
doc->setMinimumWidth(wrapWidth);
5094
lastFormatted = doc->firstParagraph();
5099
int Q3TextEdit::wrapColumnOrWidth() const
5101
if (wrapMode == WidgetWidth)
5102
return visibleWidth();
5108
\enum Q3TextEdit::WrapPolicy
5110
This enum defines where text can be wrapped in word wrap mode.
5112
\value AtWhiteSpace Don't use this deprecated value (it is a
5113
synonym for \c AtWordBoundary which you should use instead).
5114
\value Anywhere Break anywhere, including within words.
5115
\value AtWordBoundary Break lines at word boundaries, e.g. spaces or
5117
\value AtWordOrDocumentBoundary Break lines at whitespace, e.g.
5118
spaces or newlines if possible. Break it anywhere otherwise.
5124
\property Q3TextEdit::wrapPolicy
5125
\brief the word wrap policy, at whitespace or anywhere
5127
Defines where text can be wrapped when word wrap mode is not \c
5128
NoWrap. The choices are \c AtWordBoundary (the default), \c
5129
Anywhere and \c AtWordOrDocumentBoundary
5134
void Q3TextEdit::setWrapPolicy(WrapPolicy policy)
5136
if (wPolicy == policy)
5139
Q3TextFormatter *formatter;
5140
if (policy == AtWordBoundary || policy == AtWordOrDocumentBoundary) {
5141
formatter = new Q3TextFormatterBreakWords;
5142
formatter->setAllowBreakInWords(policy == AtWordOrDocumentBoundary);
5144
formatter = new Q3TextFormatterBreakInWords;
5146
formatter->setWrapAtColumn(document()->formatter()->wrapAtColumn());
5147
formatter->setWrapEnabled(document()->formatter()->isWrapEnabled(0));
5148
document()->setFormatter(formatter);
5151
lastFormatted = doc->firstParagraph();
5156
Q3TextEdit::WrapPolicy Q3TextEdit::wrapPolicy() const
5162
Deletes all the text in the text edit.
5164
\sa cut() removeSelectedText() setText()
5167
void Q3TextEdit::clear()
5169
#ifdef QT_TEXTEDIT_OPTIMIZATION
5175
// make clear undoable
5176
doc->selectAll(Q3TextDocument::Temp);
5177
removeSelectedText(Q3TextDocument::Temp);
5178
setContentsPos(0, 0);
5179
if (cursor->isValid())
5180
cursor->restoreState();
5183
cursor = new Q3TextCursor(doc);
5188
emit cursorPositionChanged(cursor);
5189
emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
5192
int Q3TextEdit::undoDepth() const
5194
return document()->undoDepth();
5198
\property Q3TextEdit::length
5199
\brief the number of characters in the text
5202
int Q3TextEdit::length() const
5204
#ifdef QT_TEXTEDIT_OPTIMIZATION
5209
return document()->length();
5213
\property Q3TextEdit::tabStopWidth
5214
\brief the tab stop width in pixels
5217
int Q3TextEdit::tabStopWidth() const
5219
return document()->tabStopWidth();
5222
void Q3TextEdit::setUndoDepth(int d)
5224
document()->setUndoDepth(d);
5227
void Q3TextEdit::setTabStopWidth(int ts)
5229
document()->setTabStops(ts);
5231
lastFormatted = doc->firstParagraph();
5241
QSize Q3TextEdit::sizeHint() const
5243
// cf. Q3ScrollView::sizeHint()
5245
int f = 2 * frameWidth();
5246
int h = fontMetrics().height();
5248
return sz.expandedTo(QSize(12 * h, 8 * h));
5251
void Q3TextEdit::clearUndoRedo()
5255
undoRedoInfo.clear();
5256
emit undoAvailable(doc->commands()->isUndoAvailable());
5257
emit redoAvailable(doc->commands()->isRedoAvailable());
5261
\warning In Qt 3.1 we will provide a cleaer API for the
5262
functionality which is provided by this function and in Qt 4.0 this
5263
function will go away.
5265
This function gets the format of the character at position \a
5266
index in paragraph \a para. Sets \a font to the character's font, \a
5267
color to the character's color and \a verticalAlignment to the
5268
character's vertical alignment.
5270
Returns false if \a para or \a index is out of range otherwise
5274
bool Q3TextEdit::getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment)
5276
if (!font || !color)
5278
Q3TextParagraph *p = doc->paragAt(para);
5281
if (index < 0 || index >= p->length())
5283
*font = p->at(index)->format()->font();
5284
*color = p->at(index)->format()->color();
5285
*verticalAlignment = (VerticalAlignment)p->at(index)->format()->vAlign();
5290
\warning In Qt 3.1 we will provide a cleaer API for the
5291
functionality which is provided by this function and in Qt 4.0 this
5292
function will go away.
5294
This function gets the format of the paragraph \a para. Sets \a
5295
font to the paragraphs's font, \a color to the paragraph's color, \a
5296
verticalAlignment to the paragraph's vertical alignment, \a
5297
alignment to the paragraph's alignment, \a displayMode to the
5298
paragraph's display mode, \a listStyle to the paragraph's list style
5299
(if the display mode is Q3StyleSheetItem::DisplayListItem) and \a
5300
listDepth to the depth of the list (if the display mode is
5301
Q3StyleSheetItem::DisplayListItem).
5303
Returns false if \a para is out of range otherwise returns true.
5306
bool Q3TextEdit::getParagraphFormat(int para, QFont *font, QColor *color,
5307
VerticalAlignment *verticalAlignment, int *alignment,
5308
Q3StyleSheetItem::DisplayMode *displayMode,
5309
Q3StyleSheetItem::ListStyle *listStyle,
5312
if (!font || !color || !alignment || !displayMode || !listStyle)
5314
Q3TextParagraph *p = doc->paragAt(para);
5317
*font = p->at(0)->format()->font();
5318
*color = p->at(0)->format()->color();
5319
*verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign();
5320
*alignment = p->alignment();
5321
*displayMode = p->isListItem() ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::DisplayBlock;
5322
*listStyle = p->listStyle();
5323
*listDepth = p->listDepth();
5330
This function is called to create a right mouse button popup menu
5331
at the document position \a pos. If you want to create a custom
5332
popup menu, reimplement this function and return the created popup
5333
menu. Ownership of the popup menu is transferred to the caller.
5336
Q3PopupMenu *Q3TextEdit::createPopupMenu(const QPoint& pos)
5339
#ifndef QT_NO_POPUPMENU
5340
Q3PopupMenu *popup = new Q3PopupMenu(this, "qt_edit_menu");
5341
if (!isReadOnly()) {
5342
d->id[IdUndo] = popup->insertItem(tr("&Undo") + ACCEL_KEY(Z));
5343
d->id[IdRedo] = popup->insertItem(tr("&Redo") + ACCEL_KEY(Y));
5344
popup->addSeparator();
5346
#ifndef QT_NO_CLIPBOARD
5348
d->id[IdCut] = popup->insertItem(tr("Cu&t") + ACCEL_KEY(X));
5349
d->id[IdCopy] = popup->insertItem(tr("&Copy") + ACCEL_KEY(C));
5351
d->id[IdPaste] = popup->insertItem(tr("&Paste") + ACCEL_KEY(V));
5353
if (!isReadOnly()) {
5354
d->id[IdClear] = popup->insertItem(tr("Clear"));
5355
popup->addSeparator();
5357
#if defined(Q_WS_X11)
5358
d->id[IdSelectAll] = popup->insertItem(tr("Select All"));
5360
d->id[IdSelectAll] = popup->insertItem(tr("Select All") + ACCEL_KEY(A));
5362
popup->setItemEnabled(d->id[IdUndo], !isReadOnly() && doc->commands()->isUndoAvailable());
5363
popup->setItemEnabled(d->id[IdRedo], !isReadOnly() && doc->commands()->isRedoAvailable());
5364
#ifndef QT_NO_CLIPBOARD
5365
popup->setItemEnabled(d->id[IdCut], !isReadOnly() && doc->hasSelection(Q3TextDocument::Standard, true));
5366
#ifdef QT_TEXTEDIT_OPTIMIZATION
5367
popup->setItemEnabled(d->id[IdCopy], d->optimMode ? optimHasSelection() : doc->hasSelection(Q3TextDocument::Standard, true));
5369
popup->setItemEnabled(d->id[IdCopy], doc->hasSelection(Q3TextDocument::Standard, true));
5371
popup->setItemEnabled(d->id[IdPaste], !isReadOnly() && !QApplication::clipboard()->text(d->clipboard_mode).isEmpty());
5373
const bool isEmptyDocument = (length() == 0);
5374
popup->setItemEnabled(d->id[IdClear], !isReadOnly() && !isEmptyDocument);
5375
popup->setItemEnabled(d->id[IdSelectAll], !isEmptyDocument);
5383
This function is called to create a right mouse button popup menu.
5384
If you want to create a custom popup menu, reimplement this function
5385
and return the created popup menu. Ownership of the popup menu is
5386
transferred to the caller.
5388
This function is only called if createPopupMenu(const QPoint &)
5392
Q3PopupMenu *Q3TextEdit::createPopupMenu()
5398
\fn Q3TextEdit::zoomIn()
5402
Zooms in on the text by by making the base font size one point
5403
larger and recalculating all font sizes to be the new size. This
5404
does not change the size of any images.
5410
\fn Q3TextEdit::zoomOut()
5414
Zooms out on the text by by making the base font size one point
5415
smaller and recalculating all font sizes to be the new size. This
5416
does not change the size of any images.
5423
Zooms in on the text by by making the base font size \a range
5424
points larger and recalculating all font sizes to be the new size.
5425
This does not change the size of any images.
5430
void Q3TextEdit::zoomIn(int range)
5432
QFont f(Q3ScrollView::font());
5433
f.setPointSize(f.pointSize() + range);
5438
Zooms out on the text by making the base font size \a range points
5439
smaller and recalculating all font sizes to be the new size. This
5440
does not change the size of any images.
5445
void Q3TextEdit::zoomOut(int range)
5447
QFont f(Q3ScrollView::font());
5448
f.setPointSize(qMax(1, f.pointSize() - range));
5453
Zooms the text by making the base font size \a size points and
5454
recalculating all font sizes to be the new size. This does not
5455
change the size of any images.
5458
void Q3TextEdit::zoomTo(int size)
5460
QFont f(Q3ScrollView::font());
5461
f.setPointSize(size);
5466
Q3TextEdit is optimized for large amounts text. One of its
5467
optimizations is to format only the visible text, formatting the rest
5468
on demand, e.g. as the user scrolls, so you don't usually need to
5471
In some situations you may want to force the whole text
5472
to be formatted. For example, if after calling setText(), you wanted
5473
to know the height of the document (using contentsHeight()), you
5474
would call this function first.
5477
void Q3TextEdit::sync()
5479
#ifdef QT_TEXTEDIT_OPTIMIZATION
5481
QFontMetrics fm(Q3ScrollView::font());
5482
resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
5486
while (lastFormatted) {
5487
lastFormatted->format();
5488
lastFormatted = lastFormatted->next();
5490
resizeContents(contentsWidth(), doc->height());
5496
Sets the background color of selection number \a selNum to \a back
5497
and specifies whether the text of this selection should be
5498
inverted with \a invertText.
5500
This only works for \a selNum > 0. The default selection (\a
5501
selNum == 0) gets its attributes from the text edit's
5505
void Q3TextEdit::setSelectionAttributes(int selNum, const QColor &back, bool invertText)
5509
if (selNum > doc->numSelections())
5510
doc->addSelection(selNum);
5511
doc->setSelectionColor(selNum, back);
5513
doc->setSelectionTextColor(selNum, palette().color(QPalette::HighlightedText));
5519
void Q3TextEdit::changeEvent(QEvent *ev)
5521
if(ev->type() == QEvent::ActivationChange) {
5522
if (!isActiveWindow() && scrollTimer)
5523
scrollTimer->stop();
5524
if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
5528
#ifdef QT_TEXTEDIT_OPTIMIZATION
5529
if (d->optimMode && (ev->type() == QEvent::ApplicationFontChange
5530
|| ev->type() == QEvent::FontChange)) {
5531
Q3ScrollView::setFont(font());
5532
doc->setDefaultFormat(font(), doc->formatCollection()->defaultFormat()->color());
5533
// recalculate the max string width
5534
QFontMetrics fm(font());
5536
d->od->maxLineWidth = 0;
5537
for (i = 0; i < d->od->numLines; i++) {
5538
sw = fm.width(d->od->lines[LOGOFFSET(i)]);
5539
if (d->od->maxLineWidth < sw)
5540
d->od->maxLineWidth = sw;
5542
resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
5547
Q3ScrollView::changeEvent(ev);
5549
if (textFormat() == Qt::PlainText) {
5550
if (ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange
5551
|| ev->type() == QEvent::EnabledChange) {
5552
Q3TextFormat *f = doc->formatCollection()->defaultFormat();
5553
f->setColor(palette().text().color());
5558
if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) {
5559
doc->setMinimumWidth(-1);
5560
doc->setDefaultFormat(font(), doc->formatCollection()->defaultFormat()->color());
5561
lastFormatted = doc->firstParagraph();
5567
void Q3TextEdit::setReadOnly(bool b)
5572
d->cursorBlinkActive = !b;
5573
#ifndef QT_NO_CURSOR
5575
viewport()->setCursor(Qt::ArrowCursor);
5577
viewport()->setCursor(Qt::IBeamCursor);
5578
setInputMethodEnabled(!readonly);
5580
#ifdef QT_TEXTEDIT_OPTIMIZATION
5586
Scrolls to the bottom of the document and does formatting if
5590
void Q3TextEdit::scrollToBottom()
5593
setContentsPos(contentsX(), contentsHeight() - visibleHeight());
5597
Returns the rectangle of the paragraph \a para in contents
5598
coordinates, or an invalid rectangle if \a para is out of range.
5601
QRect Q3TextEdit::paragraphRect(int para) const
5603
Q3TextEdit *that = (Q3TextEdit *)this;
5605
Q3TextParagraph *p = doc->paragAt(para);
5607
return QRect(-1, -1, -1, -1);
5612
Returns the paragraph which is at position \a pos (in contents
5616
int Q3TextEdit::paragraphAt(const QPoint &pos) const
5618
#ifdef QT_TEXTEDIT_OPTIMIZATION
5620
QFontMetrics fm(Q3ScrollView::font());
5621
int parag = pos.y() / fm.lineSpacing();
5622
if (parag <= d->od->numLines)
5628
Q3TextCursor c(doc);
5629
c.place(pos, doc->firstParagraph());
5631
return c.paragraph()->paragId();
5632
return -1; // should never happen..
5636
Returns the index of the character (relative to its paragraph) at
5637
position \a pos (in contents coordinates). If \a para is not 0,
5638
\c{*}\a{para} is set to the character's paragraph.
5641
int Q3TextEdit::charAt(const QPoint &pos, int *para) const
5643
#ifdef QT_TEXTEDIT_OPTIMIZATION
5645
int par = paragraphAt(pos);
5648
return optimCharIndex(d->od->lines[LOGOFFSET(par)], pos.x());
5651
Q3TextCursor c(doc);
5652
c.place(pos, doc->firstParagraph());
5653
if (c.paragraph()) {
5655
*para = c.paragraph()->paragId();
5658
return -1; // should never happen..
5662
Sets the background color of the paragraph \a para to \a bg.
5665
void Q3TextEdit::setParagraphBackgroundColor(int para, const QColor &bg)
5667
Q3TextParagraph *p = doc->paragAt(para);
5670
p->setBackgroundColor(bg);
5675
Clears the background color of the paragraph \a para, so that the
5676
default color is used again.
5679
void Q3TextEdit::clearParagraphBackground(int para)
5681
Q3TextParagraph *p = doc->paragAt(para);
5684
p->clearBackgroundColor();
5689
Returns the background color of the paragraph \a para or an
5690
invalid color if \a para is out of range or the paragraph has no
5694
QColor Q3TextEdit::paragraphBackgroundColor(int para) const
5696
Q3TextParagraph *p = doc->paragAt(para);
5699
QColor *c = p->backgroundColor();
5706
\property Q3TextEdit::undoRedoEnabled
5707
\brief whether undo/redo is enabled
5709
When changing this property, the undo/redo history is cleared.
5711
The default is true.
5714
void Q3TextEdit::setUndoRedoEnabled(bool b)
5716
undoRedoInfo.clear();
5717
doc->commands()->clear();
5722
bool Q3TextEdit::isUndoRedoEnabled() const
5728
Returns true if undo is available; otherwise returns false.
5731
bool Q3TextEdit::isUndoAvailable() const
5733
return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid());
5737
Returns true if redo is available; otherwise returns false.
5740
bool Q3TextEdit::isRedoAvailable() const
5742
return undoEnabled && doc->commands()->isRedoAvailable();
5745
void Q3TextEdit::ensureFormatted(Q3TextParagraph *p)
5747
while (!p->isValid()) {
5755
void Q3TextEdit::updateCursor(const QPoint & pos)
5757
if (isReadOnly() && linksEnabled()) {
5758
Q3TextCursor c = *cursor;
5759
placeCursor(pos, &c, true);
5761
#ifndef QT_NO_NETWORKPROTOCOL
5762
bool insideParagRect = true;
5763
if (c.paragraph() == doc->lastParagraph()
5764
&& c.paragraph()->rect().y() + c.paragraph()->rect().height() < pos.y())
5765
insideParagRect = false;
5766
if (insideParagRect && c.paragraph() && c.paragraph()->at(c.index()) &&
5767
c.paragraph()->at(c.index())->isAnchor()) {
5768
if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()
5769
&& c.index() < c.paragraph()->length() - 1)
5770
onLink = c.paragraph()->at(c.index())->anchorHref();
5774
if (!c.paragraph()->at(c.index())->anchorName().isEmpty()
5775
&& c.index() < c.paragraph()->length() - 1)
5776
d->onName = c.paragraph()->at(c.index())->anchorName();
5780
if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()) {
5781
#ifndef QT_NO_CURSOR
5782
viewport()->setCursor(onLink.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
5784
QUrl u = QUrl(doc->context()).resolved(onLink);
5785
emitHighlighted(u.toString(QUrl::None));
5788
#ifndef QT_NO_CURSOR
5789
viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
5792
emitHighlighted(QString());
5799
Places the cursor \a c at the character which is closest to position
5800
\a pos (in contents coordinates). If \a c is 0, the default text
5803
\sa setCursorPosition()
5805
void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c)
5807
placeCursor(pos, c, false);
5811
void Q3TextEdit::clipboardChanged()
5813
#ifndef QT_NO_CLIPBOARD
5814
// don't listen to selection changes
5815
disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
5820
/*! \property Q3TextEdit::tabChangesFocus
5821
\brief whether TAB changes focus or is accepted as input
5823
In some occasions text edits should not allow the user to input
5824
tabulators or change indentation using the TAB key, as this breaks
5825
the focus chain. The default is false.
5829
void Q3TextEdit::setTabChangesFocus(bool b)
5831
d->tabChangesFocus = b;
5834
bool Q3TextEdit::tabChangesFocus() const
5836
return d->tabChangesFocus;
5839
#ifdef QT_TEXTEDIT_OPTIMIZATION
5840
/* Implementation of optimized Qt::LogText mode follows */
5842
static void qSwap(int * a, int * b)
5852
bool Q3TextEdit::checkOptimMode()
5854
bool oldMode = d->optimMode;
5855
if (textFormat() == Qt::LogText) {
5856
d->optimMode = true;
5859
d->optimMode = false;
5862
// when changing mode - try to keep selections and text
5863
if (oldMode != d->optimMode) {
5865
d->od = new Q3TextEditOptimPrivate;
5866
connect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
5867
disconnect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
5868
disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
5869
disconnect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
5870
optimSetText(doc->originalText());
5873
cursor = new Q3TextCursor(doc);
5875
disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll()));
5876
connect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int)));
5877
connect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone()));
5878
connect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore()));
5879
setText(optimText());
5884
return d->optimMode;
5888
QString Q3TextEdit::optimText() const
5892
if (d->od->len == 0)
5895
// concatenate all strings
5898
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
5899
Q3TextEditOptimPrivate::Tag * ftag = 0;
5900
for (i = 0; i < d->od->numLines; i++) {
5901
if (d->od->lines[LOGOFFSET(i)].isEmpty()) { // CR lines are empty
5904
tmp = d->od->lines[LOGOFFSET(i)] + "\n";
5905
// inject the tags for this line
5906
if ((it = d->od->tagIndex.find(LOGOFFSET(i))) != d->od->tagIndex.end())
5909
while (ftag && ftag->line == i) {
5910
tmp.insert(ftag->index + offset, "<" + ftag->tag + ">");
5911
offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars
5921
void Q3TextEdit::optimSetText(const QString &str)
5923
optimRemoveSelection();
5924
// this is just too slow - but may have to go in due to compatibility reasons
5925
// if (str == optimText())
5927
d->od->numLines = 0;
5928
d->od->lines.clear();
5929
d->od->maxLineWidth = 0;
5932
QFontMetrics fm(Q3ScrollView::font());
5933
if (!(str.isEmpty() || str.isNull() || d->maxLogLines == 0)) {
5934
QStringList strl = str.split('\n');
5936
for (QStringList::Iterator it = strl.begin(); it != strl.end(); ++it) {
5937
optimParseTags(&*it);
5938
optimCheckLimit(*it);
5939
lWidth = fm.width(*it);
5940
if (lWidth > d->od->maxLineWidth)
5941
d->od->maxLineWidth = lWidth;
5944
resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
5951
Append \a tag to the tag list.
5953
Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimAppendTag(int index, const QString & tag)
5955
Q3TextEditOptimPrivate::Tag * t = new Q3TextEditOptimPrivate::Tag, * tmp;
5957
if (d->od->tags == 0)
5959
t->bold = t->italic = t->underline = false;
5960
t->line = d->od->numLines;
5965
t->prev = d->od->lastTag;
5967
d->od->lastTag->next = t;
5970
tmp = d->od->tagIndex[LOGOFFSET(t->line)];
5971
if (!tmp || (tmp && tmp->index > t->index)) {
5972
d->od->tagIndex.insert(LOGOFFSET(t->line), t);
5979
Insert \a tag in the tag - according to line and index numbers
5981
Q3TextEditOptimPrivate::Tag *Q3TextEdit::optimInsertTag(int line, int index, const QString &tag)
5983
Q3TextEditOptimPrivate::Tag *t = new Q3TextEditOptimPrivate::Tag, *tmp;
5985
if (d->od->tags == 0)
5987
t->bold = t->italic = t->underline = false;
5996
// find insertion pt. in tag struct.
5997
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
5998
if ((it = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
6000
if (tmp->index >= index) { // the exisiting tag may be placed AFTER the one we want to insert
6003
while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index)
6008
while (tmp && tmp->next && tmp->next->line < line)
6010
if (tmp == d->od->tags)
6015
t->next = tmp ? tmp->next : 0;
6021
tmp = d->od->tagIndex[LOGOFFSET(t->line)];
6022
if (!tmp || (tmp && tmp->index >= t->index)) {
6023
d->od->tagIndex.insert(LOGOFFSET(t->line), t);
6030
Find tags in \a line, remove them from \a line and put them in a
6033
A tag is delimited by '<' and '>'. The characters '<', '>' and '&'
6034
are escaped by using '<', '>' and '&'. Left-tags marks
6035
the starting point for formatting, while right-tags mark the ending
6036
point. A right-tag is the same as a left-tag, but with a '/'
6037
appearing before the tag keyword. E.g a valid left-tag: <b>, and
6038
a valid right-tag: </b>. Tags can be nested, but they have to be
6039
closed in the same order as they are opened. E.g:
6040
<font color=red><font color=blue>blue</font>red</font> - is valid, while:
6041
<font color=red><b>bold red</font> just bold</b> - is invalid since the font tag is
6042
closed before the bold tag. Note that a tag does not have to be
6043
closed: '<font color=blue>Lots of text - and then some..' is perfectly valid for
6044
setting all text appearing after the tag to blue. A tag can be used
6045
to change the color of a piece of text, or set one of the following
6046
formatting attributes: bold, italic and underline. These attributes
6047
are set using the <b>, <i> and <u> tags. Example of valid tags:
6048
<font color=red>, </font>, <b>, <u>, <i>, </i>.
6049
Example of valid text:
6050
This is some <font color=red>red text</font>, while this is some <font color=green>green
6051
text</font>. <font color=blue><font color=yellow>This is yellow</font>, while this is
6054
Note that only the color attribute of the HTML font tag is supported.
6057
1. A tag cannot span several lines.
6058
2. Very limited error checking - mismatching left/right-tags is the
6059
only thing that is detected.
6062
void Q3TextEdit::optimParseTags(QString * line, int lineNo, int indexOffset)
6064
int len = line->length();
6065
int i, startIndex = -1, endIndex = -1, escIndex = -1;
6066
int state = 0; // 0 = outside tag, 1 = inside tag
6067
bool tagOpen, tagClose;
6068
int bold = 0, italic = 0, underline = 0;
6070
QStack<Q3TextEditOptimPrivate::Tag *> tagStack;
6072
for (i = 0; i < len; i++) {
6073
tagOpen = (*line)[i] == '<';
6074
tagClose = (*line)[i] == '>';
6076
// handle '<' and '>' and '&'
6077
if ((*line)[i] == '&') {
6080
} else if (escIndex != -1 && (*line)[i] == ';') {
6081
QString esc = line->mid(escIndex, i - escIndex + 1);
6085
else if (esc == ">")
6087
else if (esc == "&")
6089
line->replace(escIndex, i - escIndex + 1, c);
6090
len = line->length();
6096
if (state == 0 && tagOpen) {
6101
if (state == 1 && tagClose) {
6104
if (!tagStr.isEmpty()) {
6105
Q3TextEditOptimPrivate::Tag * tag, * cur, * tmp;
6110
else if (tagStr == "/b")
6112
else if (tagStr == "i")
6114
else if (tagStr == "/i")
6116
else if (tagStr == "u")
6118
else if (tagStr == "/u")
6123
tag = optimInsertTag(lineNo, startIndex + indexOffset, tagStr);
6125
tag = optimAppendTag(startIndex, tagStr);
6126
// everything that is not a b, u or i tag is considered
6127
// to be a color tag.
6128
tag->type = format ? Q3TextEditOptimPrivate::Format
6129
: Q3TextEditOptimPrivate::Color;
6130
if (tagStr[0] == '/') {
6131
// this is a right-tag - search for the left-tag
6132
// and possible parent tag
6135
qWarning("Q3TextEdit::optimParseTags: no left-tag for '<%s>' in line %d.",
6136
tag->tag.latin1(), tag->line + 1);
6137
return; // something is wrong - give up
6140
if (cur->leftTag) { // push right-tags encountered
6143
tmp = tagStack.isEmpty() ? 0 : tagStack.pop();
6145
if ((("/" + cur->tag) == tag->tag) ||
6146
(tag->tag == "/font" && cur->tag.left(4) == "font")) {
6147
// set up the left and parent of this tag
6150
if (tmp && tmp->parent) {
6151
tag->parent = tmp->parent;
6152
} else if (tmp && !tmp->leftTag) {
6156
} else if (!cur->leftTag) {
6157
qWarning("Q3TextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.", cur->tag[0] == '/' ? "left" : "right", cur->tag.latin1(), cur->line + 1);
6158
return; // something is amiss - give up
6165
tag->bold = bold > 0;
6166
tag->italic = italic > 0;
6167
tag->underline = underline > 0;
6169
while (tmp && tmp->leftTag) {
6170
tmp = tmp->leftTag->parent;
6173
tag->bold |= tmp->bold;
6174
tag->italic |= tmp->italic;
6175
tag->underline |= tmp->underline;
6179
if (startIndex != -1) {
6180
int l = (endIndex == -1) ?
6181
line->length() - startIndex : endIndex - startIndex;
6182
line->remove(startIndex, l+1);
6183
len = line->length();
6191
tagStr += (*line)[i];
6196
// calculate the width of a string in pixels inc. tabs
6197
static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm)
6199
int tabs = str.count('\t');
6202
return fm.width(str);
6208
for (tn = 1; tn <= tabs; ++tn) {
6209
newIdx = str.indexOf('\t', newIdx);
6210
strWidth += fm.width(str.mid(lastIdx, newIdx - lastIdx));
6211
if (strWidth >= tn * tabWidth) {
6213
while (strWidth >= u * tabWidth)
6215
strWidth = u * tabWidth;
6217
strWidth = tn * tabWidth;
6221
if ((int)str.length() > newIdx)
6222
strWidth += fm.width(str.mid(newIdx));
6226
bool Q3TextEdit::optimHasBoldMetrics(int line)
6228
Q3TextEditOptimPrivate::Tag *t;
6229
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
6230
if ((it = d->od->tagIndex.find(line)) != d->od->tagIndex.end()) {
6232
while (t && t->line == line) {
6237
} else if ((t = optimPreviousLeftTag(line)) && t->bold) {
6245
Append \a str to the current text buffer. Parses each line to find
6248
void Q3TextEdit::optimAppend(const QString &str)
6250
if (str.isEmpty() || str.isNull() || d->maxLogLines == 0)
6253
QStringList strl = str.split('\n');
6254
QStringList::Iterator it = strl.begin();
6256
QFontMetrics fm(Q3ScrollView::font());
6258
for (; it != strl.end(); ++it) {
6259
optimParseTags(&*it);
6260
optimCheckLimit(*it);
6261
if (optimHasBoldMetrics(d->od->numLines-1)) {
6262
QFont fnt = Q3ScrollView::font();
6264
fm = QFontMetrics(fnt);
6266
lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4;
6267
if (lWidth > d->od->maxLineWidth)
6268
d->od->maxLineWidth = lWidth;
6270
bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight();
6271
resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
6274
ensureVisible(contentsX(), contentsHeight(), 0, 0);
6276
// when a max log size is set, the text may not be redrawn because
6277
// the size of the viewport may not have changed
6278
if (d->maxLogLines > -1)
6279
viewport()->update();
6283
static void qStripTags(QString *line)
6285
int len = line->length();
6286
int i, startIndex = -1, endIndex = -1, escIndex = -1;
6287
int state = 0; // 0 = outside tag, 1 = inside tag
6288
bool tagOpen, tagClose;
6290
for (i = 0; i < len; i++) {
6291
tagOpen = (*line)[i] == '<';
6292
tagClose = (*line)[i] == '>';
6294
// handle '<' and '>' and '&'
6295
if ((*line)[i] == '&') {
6298
} else if (escIndex != -1 && (*line)[i] == ';') {
6299
QString esc = line->mid(escIndex, i - escIndex + 1);
6303
else if (esc == ">")
6305
else if (esc == "&")
6307
line->replace(escIndex, i - escIndex + 1, c);
6308
len = line->length();
6314
if (state == 0 && tagOpen) {
6319
if (state == 1 && tagClose) {
6322
if (startIndex != -1) {
6323
int l = (endIndex == -1) ?
6324
line->length() - startIndex : endIndex - startIndex;
6325
line->remove(startIndex, l+1);
6326
len = line->length();
6336
Inserts the text into \a line at index \a index.
6339
void Q3TextEdit::optimInsert(const QString& text, int line, int index)
6341
if (text.isEmpty() || d->maxLogLines == 0)
6345
if (line > d->od->numLines-1)
6346
line = d->od->numLines-1;
6349
if (index > d->od->lines[line].length())
6350
index = d->od->lines[line].length();
6352
QStringList strl = text.split('\n');
6353
int numNewLines = strl.count() - 1;
6354
Q3TextEditOptimPrivate::Tag *tag = 0;
6355
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator ii;
6358
if (numNewLines == 0) {
6359
// Case 1. Fast single line case - just inject it!
6360
QString stripped = text;
6361
qStripTags(&stripped);
6362
d->od->lines[LOGOFFSET(line)].insert(index, stripped);
6363
// move the tag indices following the insertion pt.
6364
if ((ii = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
6366
while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
6368
while (tag && (LOGOFFSET(tag->line) == line)) {
6369
tag->index += stripped.length();
6374
optimParseTags(&stripped, line, index);
6375
} else if (numNewLines > 0) {
6376
// Case 2. We have at least 1 newline char - split at
6377
// insertion pt. and make room for new lines - complex and slow!
6378
QString left = d->od->lines[LOGOFFSET(line)].left(index);
6379
QString right = d->od->lines[LOGOFFSET(line)].mid(index);
6381
// rearrange lines for insertion
6382
for (x = d->od->numLines - 1; x > line; x--)
6383
d->od->lines[x + numNewLines] = d->od->lines[x];
6384
d->od->numLines += numNewLines;
6386
// fix the tag index and the tag line/index numbers - this
6387
// might take a while..
6388
for (x = line; x < d->od->numLines; x++) {
6389
if ((ii = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) {
6391
if (LOGOFFSET(tag->line) == line)
6392
while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
6397
// relabel affected tags with new line numbers and new index
6400
if (LOGOFFSET(tag->line) == line)
6401
tag->index -= index;
6402
tag->line += numNewLines;
6406
// generate a new tag index
6407
d->od->tagIndex.clear();
6410
if (!((ii = d->od->tagIndex.find(LOGOFFSET(tag->line))) != d->od->tagIndex.end()))
6411
d->od->tagIndex[LOGOFFSET(tag->line)] = tag;
6415
// update the tag indices on the spliced line - needs to be done before new tags are added
6416
QString stripped = strl[strl.count() - 1];
6417
qStripTags(&stripped);
6418
if ((ii = d->od->tagIndex.find(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.end()) {
6420
while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) {
6421
tag->index += stripped.length();
6426
// inject the new lines
6427
QStringList::Iterator it = strl.begin();
6430
for (; it != strl.end(); ++it) {
6432
qStripTags(&stripped);
6434
stripped = left + stripped;
6439
d->od->lines[LOGOFFSET(x)] = stripped;
6440
optimParseTags(&*it, x++, idx);
6442
d->od->lines[LOGOFFSET(x - 1)] += right;
6444
// recalculate the pixel width of the longest injected line -
6445
QFontMetrics fm(Q3ScrollView::font());
6447
for (x = line; x < line + numNewLines; x++) {
6448
if (optimHasBoldMetrics(x)) {
6449
QFont fnt = Q3ScrollView::font();
6451
fm = QFontMetrics(fnt);
6453
lWidth = fm.width(d->od->lines[x]) + 4;
6454
if (lWidth > d->od->maxLineWidth)
6455
d->od->maxLineWidth = lWidth;
6457
resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
6465
Returns the first open left-tag appearing before line \a line.
6467
Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimPreviousLeftTag(int line)
6469
Q3TextEditOptimPrivate::Tag * ftag = 0;
6470
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
6471
if ((it = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end())
6474
// start searching for an open tag
6477
if (ftag->line > line || ftag->next == 0) {
6478
if (ftag->line > line)
6489
if (ftag && ftag->parent) // use the open parent tag
6490
ftag = ftag->parent;
6491
else if (ftag && ftag->leftTag) // this is a right-tag with no parent
6499
Set the format for the string starting at index \a start and ending
6500
at \a end according to \a tag. If \a tag is a Format tag, find the
6501
first open color tag appearing before \a tag and use that tag to
6504
void Q3TextEdit::optimSetTextFormat(Q3TextDocument * td, Q3TextCursor * cur,
6505
Q3TextFormat * f, int start, int end,
6506
Q3TextEditOptimPrivate::Tag * tag)
6508
int formatFlags = Q3TextFormat::Bold | Q3TextFormat::Italic |
6509
Q3TextFormat::Underline;
6510
cur->setIndex(start);
6511
td->setSelectionStart(0, *cur);
6513
td->setSelectionEnd(0, *cur);
6514
Q3StyleSheetItem * ssItem = styleSheet()->item(tag->tag);
6515
if (!ssItem || tag->type == Q3TextEditOptimPrivate::Format) {
6516
f->setBold(tag->bold);
6517
f->setItalic(tag->italic);
6518
f->setUnderline(tag->underline);
6519
if (tag->type == Q3TextEditOptimPrivate::Format) {
6520
// check to see if there are any open color tags prior to
6523
while (tag && (tag->type == Q3TextEditOptimPrivate::Format ||
6525
tag = tag->leftTag ? tag->parent : tag->prev;
6529
QString col = tag->tag.simplified();
6530
if (col.left(10) == "font color") {
6531
int i = col.indexOf('=', 10);
6532
col = col.mid(i + 1).simplified();
6534
col = col.mid(1, col.length() - 2);
6536
QColor color = QColor(col);
6537
if (color.isValid()) {
6538
formatFlags |= Q3TextFormat::Color;
6542
} else { // use the stylesheet tag definition
6543
if (ssItem->color().isValid()) {
6544
formatFlags |= Q3TextFormat::Color;
6545
f->setColor(ssItem->color());
6547
f->setBold(ssItem->fontWeight() == QFont::Bold);
6548
f->setItalic(ssItem->fontItalic());
6549
f->setUnderline(ssItem->fontUnderline());
6551
td->setFormat(0, f, formatFlags);
6552
td->removeSelection(0);
6556
void Q3TextEdit::optimDrawContents(QPainter * p, int clipx, int clipy,
6557
int clipw, int cliph)
6559
QFontMetrics fm(Q3ScrollView::font());
6560
int startLine = clipy / fm.lineSpacing();
6562
// we always have to fetch at least two lines for drawing because the
6563
// painter may be translated so that parts of two lines cover the area
6565
int nLines = (cliph / fm.lineSpacing()) + 2;
6566
int endLine = startLine + nLines;
6568
if (startLine >= d->od->numLines)
6570
if ((startLine + nLines) > d->od->numLines)
6571
nLines = d->od->numLines - startLine;
6575
for (i = startLine; i < (startLine + nLines); i++)
6576
str.append(d->od->lines[LOGOFFSET(i)] + "\n");
6578
Q3TextDocument * td = new Q3TextDocument(0);
6579
td->setDefaultFormat(Q3ScrollView::font(), QColor());
6580
td->setPlainText(str);
6581
td->setFormatter(new Q3TextFormatterBreakWords); // deleted by QTextDoc
6582
td->formatter()->setWrapEnabled(false);
6583
td->setTabStops(doc->tabStopWidth());
6585
// get the current text color from the current format
6586
td->selectAll(Q3TextDocument::Standard);
6588
f.setColor(palette().text().color());
6589
f.setFont(Q3ScrollView::font());
6590
td->setFormat(Q3TextDocument::Standard, &f,
6591
Q3TextFormat::Color | Q3TextFormat::Font);
6592
td->removeSelection(Q3TextDocument::Standard);
6594
// add tag formatting
6597
QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
6598
Q3TextEditOptimPrivate::Tag * tag = 0, * tmp = 0;
6599
Q3TextCursor cur(td);
6600
// Step 1 - find previous left-tag
6601
tmp = optimPreviousLeftTag(i);
6602
for (; i < startLine + nLines; i++) {
6603
if ((it = d->od->tagIndex.find(LOGOFFSET(i))) != d->od->tagIndex.end())
6605
// Step 2 - iterate over tags on the current line
6607
while (tag && tag->line == i) {
6609
if (tag->prev && !tag->prev->leftTag) {
6611
} else if (tag->prev && tag->prev->parent) {
6612
tmp = tag->prev->parent;
6614
if ((tag->index - lastIndex) > 0 && tmp) {
6615
optimSetTextFormat(td, &cur, &f, lastIndex, tag->index, tmp);
6617
lastIndex = tag->index;
6621
// Step 3 - color last part of the line - if necessary
6622
if (tmp && tmp->parent)
6624
if ((cur.paragraph()->length()-1 - lastIndex) > 0 && tmp && !tmp->leftTag) {
6625
optimSetTextFormat(td, &cur, &f, lastIndex,
6626
cur.paragraph()->length() - 1, tmp);
6628
cur.setParagraph(cur.paragraph()->next());
6630
// useful debug info
6632
// tag = d->od->tags;
6635
// qWarning("Tag: %p, parent: %09p, leftTag: %09p, Name: %-15s, ParentName: %s, %d%d%d", tag,
6636
// tag->parent, tag->leftTag, tag->tag.latin1(), tag->parent ? tag->parent->tag.latin1():"<none>",
6637
// tag->bold, tag->italic, tag->underline);
6642
// if there is a selection, make sure that the selection in the
6643
// part we need to redraw is set correctly
6644
if (optimHasSelection()) {
6645
Q3TextCursor c1(td);
6646
Q3TextCursor c2(td);
6647
int selStart = d->od->selStart.line;
6648
int idxStart = d->od->selStart.index;
6649
int selEnd = d->od->selEnd.line;
6650
int idxEnd = d->od->selEnd.index;
6651
if (selEnd < selStart) {
6652
qSwap(&selStart, &selEnd);
6653
qSwap(&idxStart, &idxEnd);
6655
if (selEnd > d->od->numLines-1) {
6656
selEnd = d->od->numLines-1;
6658
if (startLine <= selStart && endLine >= selEnd) {
6659
// case 1: area to paint covers entire selection
6660
int paragS = selStart - startLine;
6661
int paragE = paragS + (selEnd - selStart);
6662
Q3TextParagraph * parag = td->paragAt(paragS);
6664
c1.setParagraph(parag);
6665
if (td->text(paragS).length() >= idxStart)
6666
c1.setIndex(idxStart);
6668
parag = td->paragAt(paragE);
6670
c2.setParagraph(parag);
6671
if (td->text(paragE).length() >= idxEnd)
6672
c2.setIndex(idxEnd);
6674
} else if (startLine > selStart && endLine < selEnd) {
6675
// case 2: area to paint is all part of the selection
6676
td->selectAll(Q3TextDocument::Standard);
6677
} else if (startLine > selStart && endLine >= selEnd &&
6678
startLine <= selEnd) {
6679
// case 3: area to paint starts inside a selection, ends past it
6680
c1.setParagraph(td->firstParagraph());
6682
int paragE = selEnd - startLine;
6683
Q3TextParagraph * parag = td->paragAt(paragE);
6685
c2.setParagraph(parag);
6686
if (td->text(paragE).length() >= idxEnd)
6687
c2.setIndex(idxEnd);
6689
} else if (startLine <= selStart && endLine < selEnd &&
6690
endLine > selStart) {
6691
// case 4: area to paint starts before a selection, ends inside it
6692
int paragS = selStart - startLine;
6693
Q3TextParagraph * parag = td->paragAt(paragS);
6695
c1.setParagraph(parag);
6696
c1.setIndex(idxStart);
6698
c2.setParagraph(td->lastParagraph());
6699
c2.setIndex(td->lastParagraph()->string()->toString().length() - 1);
6702
// previously selected?
6703
if (!td->hasSelection(Q3TextDocument::Standard)) {
6704
td->setSelectionStart(Q3TextDocument::Standard, c1);
6705
td->setSelectionEnd(Q3TextDocument::Standard, c2);
6708
td->doLayout(p, contentsWidth());
6710
// have to align the painter so that partly visible lines are
6711
// drawn at the correct position within the area that needs to be
6713
int offset = clipy % fm.lineSpacing() + 2;
6714
QRect r(clipx, 0, clipw, cliph + offset);
6715
p->translate(0, clipy - offset);
6716
td->draw(p, r.x(), r.y(), r.width(), r.height(), palette());
6717
p->translate(0, -(clipy - offset));
6722
void Q3TextEdit::optimMousePressEvent(QMouseEvent * e)
6724
if (e->button() != Qt::LeftButton)
6727
QFontMetrics fm(Q3ScrollView::font());
6728
mousePressed = true;
6729
mousePos = e->pos();
6730
d->od->selStart.line = e->y() / fm.lineSpacing();
6731
if (d->od->selStart.line > d->od->numLines-1) {
6732
d->od->selStart.line = d->od->numLines-1;
6733
d->od->selStart.index = d->od->lines[LOGOFFSET(d->od->numLines-1)].length();
6735
QString str = d->od->lines[LOGOFFSET(d->od->selStart.line)];
6736
d->od->selStart.index = optimCharIndex(str, mousePos.x());
6738
d->od->selEnd.line = d->od->selStart.line;
6739
d->od->selEnd.index = d->od->selStart.index;
6740
oldMousePos = e->pos();
6745
void Q3TextEdit::optimMouseReleaseEvent(QMouseEvent * e)
6747
if (e->button() != Qt::LeftButton)
6750
if (scrollTimer->isActive())
6751
scrollTimer->stop();
6752
if (!inDoubleClick) {
6753
QFontMetrics fm(Q3ScrollView::font());
6754
d->od->selEnd.line = e->y() / fm.lineSpacing();
6755
if (d->od->selEnd.line > d->od->numLines-1) {
6756
d->od->selEnd.line = d->od->numLines-1;
6758
QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
6759
mousePos = e->pos();
6760
d->od->selEnd.index = optimCharIndex(str, mousePos.x());
6761
if (d->od->selEnd.line < d->od->selStart.line) {
6762
qSwap(&d->od->selStart.line, &d->od->selEnd.line);
6763
qSwap(&d->od->selStart.index, &d->od->selEnd.index);
6764
} else if (d->od->selStart.line == d->od->selEnd.line &&
6765
d->od->selStart.index > d->od->selEnd.index) {
6766
qSwap(&d->od->selStart.index, &d->od->selEnd.index);
6768
oldMousePos = e->pos();
6772
mousePressed = false;
6776
inDoubleClick = false;
6777
emit copyAvailable(optimHasSelection());
6778
emit selectionChanged();
6782
void Q3TextEdit::optimMouseMoveEvent(QMouseEvent * e)
6784
mousePos = e->pos();
6785
optimDoAutoScroll();
6786
oldMousePos = mousePos;
6790
void Q3TextEdit::optimDoAutoScroll()
6795
QFontMetrics fm(Q3ScrollView::font());
6796
QPoint pos(mapFromGlobal(QCursor::pos()));
6797
bool doScroll = false;
6798
int xx = contentsX() + pos.x();
6799
int yy = contentsY() + pos.y();
6801
// find out how much we have to scroll in either dir.
6802
if (pos.x() < 0 || pos.x() > viewport()->width() ||
6803
pos.y() < 0 || pos.y() > viewport()->height()) {
6806
xx = contentsX() - fm.width('w');
6807
else if (pos.x() > viewport()->width())
6808
xx = contentsX() + viewport()->width() + fm.width('w');
6811
my = contentsY() - 1;
6812
yy = (my / fm.lineSpacing()) * fm.lineSpacing() + 1;
6813
} else if (pos.y() > viewport()->height()) {
6814
my = contentsY() + viewport()->height() + 1;
6815
yy = (my / fm.lineSpacing() + 1) * fm.lineSpacing() - 1;
6817
d->od->selEnd.line = my / fm.lineSpacing();
6822
d->od->selEnd.line = mousePos.y() / fm.lineSpacing();
6825
if (d->od->selEnd.line < 0) {
6826
d->od->selEnd.line = 0;
6827
} else if (d->od->selEnd.line > d->od->numLines-1) {
6828
d->od->selEnd.line = d->od->numLines-1;
6831
QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
6832
d->od->selEnd.index = optimCharIndex(str, mousePos.x());
6834
// have to have a valid index before generating a paint event
6836
ensureVisible(xx, yy, 1, 1);
6838
// if the text document is smaller than the height of the viewport
6839
// - redraw the whole thing otherwise calculate the rect that
6841
if (d->od->numLines * fm.lineSpacing() < viewport()->height()) {
6842
repaintContents(contentsX(), contentsY(), width(), height());
6844
int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2;
6846
if (oldMousePos.y() < mousePos.y()) {
6847
y = oldMousePos.y() - fm.lineSpacing();
6849
// expand paint area for a fully selected line
6850
h += fm.lineSpacing();
6851
y = mousePos.y() - fm.lineSpacing()*2;
6855
repaintContents(contentsX(), y, width(), h);
6858
if (!scrollTimer->isActive() && pos.y() < 0 || pos.y() > height())
6859
scrollTimer->start(100, false);
6860
else if (scrollTimer->isActive() && pos.y() >= 0 && pos.y() <= height())
6861
scrollTimer->stop();
6866
Returns the index of the character in the string \a str that is
6867
currently under the mouse pointer.
6869
int Q3TextEdit::optimCharIndex(const QString &str, int mx) const
6871
QFontMetrics fm(Q3ScrollView::font());
6873
int dd, dist = 10000000;
6876
mx = mx - 4; // ### get the real margin from somewhere
6878
if (!str.contains('\t') && mx > fm.width(str))
6879
return str.length();
6881
while (i < str.length()) {
6882
strWidth = qStrWidth(str.left(i), tabStopWidth(), fm);
6884
if (QABS(dd) <= dist) {
6895
void Q3TextEdit::optimSelectAll()
6897
d->od->selStart.line = d->od->selStart.index = 0;
6898
d->od->selEnd.line = d->od->numLines - 1;
6899
d->od->selEnd.index = d->od->lines[LOGOFFSET(d->od->selEnd.line)].length();
6902
emit copyAvailable(optimHasSelection());
6903
emit selectionChanged();
6907
void Q3TextEdit::optimRemoveSelection()
6909
d->od->selStart.line = d->od->selEnd.line = -1;
6910
d->od->selStart.index = d->od->selEnd.index = -1;
6915
void Q3TextEdit::optimSetSelection(int startLine, int startIdx,
6916
int endLine, int endIdx)
6918
d->od->selStart.line = startLine;
6919
d->od->selEnd.line = endLine;
6920
d->od->selStart.index = startIdx;
6921
d->od->selEnd.index = endIdx;
6925
bool Q3TextEdit::optimHasSelection() const
6927
if (d->od->selStart.line != d->od->selEnd.line ||
6928
d->od->selStart.index != d->od->selEnd.index)
6934
QString Q3TextEdit::optimSelectedText() const
6938
if (!optimHasSelection())
6941
// concatenate all strings
6942
if (d->od->selStart.line == d->od->selEnd.line) {
6943
str = d->od->lines[LOGOFFSET(d->od->selEnd.line)].mid(d->od->selStart.index,
6944
d->od->selEnd.index - d->od->selStart.index);
6946
int i = d->od->selStart.line;
6947
str = d->od->lines[LOGOFFSET(i)].right(d->od->lines[LOGOFFSET(i)].length() -
6948
d->od->selStart.index) + "\n";
6950
for (; i < d->od->selEnd.line; i++) {
6951
if (d->od->lines[LOGOFFSET(i)].isEmpty()) // CR lines are empty
6954
str += d->od->lines[LOGOFFSET(i)] + "\n";
6956
str += d->od->lines[LOGOFFSET(d->od->selEnd.line)].left(d->od->selEnd.index);
6962
bool Q3TextEdit::optimFind(const QString & expr, bool cs, bool /*wo*/,
6963
bool fw, int * para, int * index)
6966
int parag = para ? *para : d->od->search.line,
6967
idx = index ? *index : d->od->search.index, i;
6969
if (d->od->len == 0)
6972
for (i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i--) {
6974
? d->od->lines[LOGOFFSET(i)].indexOf(expr, idx,
6975
cs ? Qt::CaseSensitive : Qt::CaseInsensitive)
6976
: d->od->lines[LOGOFFSET(i)].lastIndexOf(expr, idx,
6977
cs ? Qt::CaseSensitive : Qt::CaseInsensitive);
6990
d->od->search.index = idx;
6991
d->od->search.line = i;
6992
optimSetSelection(i, idx, i, idx + expr.length());
6993
QFontMetrics fm(Q3ScrollView::font());
6994
int h = fm.lineSpacing();
6995
int x = fm.width(d->od->lines[LOGOFFSET(i)].left(idx + expr.length())) + 4;
6996
ensureVisible(x, i * h + h / 2, 1, h / 2 + 2);
6997
repaintContents(); // could possibly be optimized
7003
void Q3TextEdit::polishEvent(QEvent*)
7005
// this will ensure that the last line is visible if text have
7006
// been added to the widget before it is shown
7012
Sets the maximum number of lines a Q3TextEdit can hold in \c
7013
Qt::LogText mode to \a limit. If \a limit is -1 (the default), this
7014
signifies an unlimited number of lines.
7016
\warning Never use formatting tags that span more than one line
7017
when the maximum log lines is set. When lines are removed from the
7018
top of the buffer it could result in an unbalanced tag pair, i.e.
7019
the left formatting tag is removed before the right one.
7021
void Q3TextEdit::setMaxLogLines(int limit)
7023
d->maxLogLines = limit;
7024
if (d->maxLogLines < -1)
7025
d->maxLogLines = -1;
7026
if (d->maxLogLines == -1)
7031
Returns the maximum number of lines Q3TextEdit can hold in \c
7032
Qt::LogText mode. By default the number of lines is unlimited, which
7033
is signified by a value of -1.
7035
int Q3TextEdit::maxLogLines() const
7037
return d->maxLogLines;
7041
Check if the number of lines in the buffer is limited, and uphold
7042
that limit when appending new lines.
7044
void Q3TextEdit::optimCheckLimit(const QString& str)
7046
if (d->maxLogLines > -1 && d->maxLogLines == d->od->numLines) {
7047
// NB! Removing the top line in the buffer will potentially
7048
// destroy the structure holding the formatting tags - if line
7049
// spanning tags are used.
7050
Q3TextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr;
7051
QList<Q3TextEditOptimPrivate::Tag *> lst;
7054
// unhook the ptr from the tag structure
7055
if (((uint) LOGOFFSET(t->line) < (uint) d->logOffset &&
7056
(uint) LOGOFFSET(t->line) < (uint) LOGOFFSET(d->od->numLines) &&
7057
(uint) LOGOFFSET(d->od->numLines) > (uint) d->logOffset))
7060
t->prev->next = t->next;
7062
t->next->prev = t->prev;
7063
if (d->od->tags == t)
7064
d->od->tags = t->next;
7065
if (d->od->lastTag == t) {
7067
d->od->lastTag = t->prev;
7069
d->od->lastTag = d->od->tags;
7079
// Remove all references to the ptrs we just deleted
7082
for (int i = 0; i < lst.size(); ++i) {
7084
if (itr->parent == tmp)
7086
if (itr->leftTag == tmp)
7091
// ...in the tag index as well
7092
QMap<int, Q3TextEditOptimPrivate::Tag *>::Iterator idx;
7093
if ((idx = d->od->tagIndex.find(d->logOffset)) != d->od->tagIndex.end())
7094
d->od->tagIndex.erase(idx);
7096
QMap<int,QString>::Iterator it;
7097
if ((it = d->od->lines.find(d->logOffset)) != d->od->lines.end()) {
7098
d->od->len -= (*it).length();
7099
d->od->lines.erase(it);
7101
d->logOffset = LOGOFFSET(1);
7104
d->od->len += str.length();
7105
d->od->lines[LOGOFFSET(d->od->numLines++)] = str;
7108
#endif // QT_TEXTEDIT_OPTIMIZATION
7111
\property Q3TextEdit::autoFormatting
7112
\brief the enabled set of auto formatting features
7114
The value can be any combination of the values in the \c
7115
AutoFormattingFlag enum. The default is \c AutoAll. Choose \c AutoNone
7116
to disable all automatic formatting.
7118
Currently, the only automatic formatting feature provided is \c
7119
AutoBulletList; future versions of Qt may offer more.
7122
void Q3TextEdit::setAutoFormatting(AutoFormatting features)
7124
d->autoFormatting = features;
7127
Q3TextEdit::AutoFormatting Q3TextEdit::autoFormatting() const
7129
return d->autoFormatting;
7133
Returns the QSyntaxHighlighter set on this Q3TextEdit. 0 is
7134
returned if no syntax highlighter is set.
7136
Q3SyntaxHighlighter * Q3TextEdit::syntaxHighlighter() const
7138
if (document()->preProcessor())
7139
return ((Q3SyntaxHighlighterInternal *) document()->preProcessor())->highlighter;
7144
#endif //QT_NO_TEXTEDIT