~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/qt3support/text/q3textedit.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the Qt 3 compatibility classes of the Qt Toolkit.
 
6
**
 
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.
 
10
**
 
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.
 
15
**
 
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.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
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.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "q3textedit.h"
 
30
 
 
31
#ifndef QT_NO_TEXTEDIT
 
32
 
 
33
#include <private/q3richtext_p.h>
 
34
#include "qpainter.h"
 
35
#include "qpen.h"
 
36
#include "qbrush.h"
 
37
#include "qpixmap.h"
 
38
#include "qfont.h"
 
39
#include "qcolor.h"
 
40
#include "qstyle.h"
 
41
#include "qsize.h"
 
42
#include "qevent.h"
 
43
#include "qtimer.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"
 
50
#include "qurl.h"
 
51
#include "qcursor.h"
 
52
#include "qregexp.h"
 
53
#include "q3popupmenu.h"
 
54
#include "qstack.h"
 
55
#include "qmetaobject.h"
 
56
#include "q3textbrowser.h"
 
57
#include "private/q3syntaxhighlighter_p.h"
 
58
#include "qtextformat.h"
 
59
 
 
60
#ifndef QT_NO_ACCEL
 
61
#include <qkeysequence.h>
 
62
#define ACCEL_KEY(k) "\t" + QString(QKeySequence(Qt::CTRL | Qt::Key_ ## k))
 
63
#else
 
64
#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
 
65
#endif
 
66
 
 
67
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
68
#define LOGOFFSET(i) d->logOffset + i
 
69
#endif
 
70
 
 
71
struct QUndoRedoInfoPrivate
 
72
{
 
73
    Q3TextString text;
 
74
};
 
75
 
 
76
class Q3TextEditPrivate
 
77
{
 
78
public:
 
79
    Q3TextEditPrivate()
 
80
        :preeditStart(-1),preeditLength(-1),numPreeditSelections(0),ensureCursorVisibleInShowEvent(false),
 
81
         tabChangesFocus(false),
 
82
#ifndef QT_NO_CLIPBOARD
 
83
         clipboard_mode(QClipboard::Clipboard),
 
84
#endif
 
85
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
86
         od(0), optimMode(false),
 
87
         maxLogLines(-1),
 
88
         logOffset(0),
 
89
#endif
 
90
         autoFormatting((uint)Q3TextEdit::AutoAll),
 
91
         cursorRepaintMode(false),
 
92
         cursorBlinkActive(false)
 
93
 
 
94
    {
 
95
        for (int i=0; i<7; i++)
 
96
            id[i] = 0;
 
97
    }
 
98
    int id[7];
 
99
    int preeditStart;
 
100
    int preeditLength;
 
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
 
105
    QString pressedName;
 
106
    QString onName;
 
107
#ifndef QT_NO_CLIPBOARD
 
108
    QClipboard::Mode clipboard_mode;
 
109
#endif
 
110
    QTimer *trippleClickTimer;
 
111
    QPoint trippleClickPoint;
 
112
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
113
    Q3TextEditOptimPrivate * od;
 
114
    bool optimMode : 1;
 
115
    int maxLogLines;
 
116
    int logOffset;
 
117
#endif
 
118
    Q3TextEdit::AutoFormatting autoFormatting;
 
119
    uint cursorRepaintMode : 1;
 
120
    uint cursorBlinkActive : 1;
 
121
};
 
122
 
 
123
#ifndef QT_NO_MIME
 
124
class Q3RichTextDrag : public Q3TextDrag
 
125
{
 
126
public:
 
127
    Q3RichTextDrag(QWidget *dragSource = 0, const char *name = 0);
 
128
 
 
129
    void setPlainText(const QString &txt) { setText(txt); }
 
130
    void setRichText(const QString &txt) { richTxt = txt; }
 
131
 
 
132
    virtual QByteArray encodedData(const char *mime) const;
 
133
    virtual const char* format(int i) const;
 
134
 
 
135
    static bool decode(QMimeSource *e, QString &str, const QString &mimetype,
 
136
                        const QString &subtype);
 
137
    static bool canDecode(QMimeSource* e);
 
138
 
 
139
private:
 
140
    QString richTxt;
 
141
 
 
142
};
 
143
 
 
144
Q3RichTextDrag::Q3RichTextDrag(QWidget *dragSource, const char *name)
 
145
    : Q3TextDrag(dragSource, name)
 
146
{
 
147
}
 
148
 
 
149
QByteArray Q3RichTextDrag::encodedData(const char *mime) const
 
150
{
 
151
    if (qstrcmp("application/x-qrichtext", mime) == 0) {
 
152
        return richTxt.toUtf8(); // #### perhaps we should use USC2 instead?
 
153
    } else
 
154
        return Q3TextDrag::encodedData(mime);
 
155
}
 
156
 
 
157
bool Q3RichTextDrag::decode(QMimeSource *e, QString &str, const QString &mimetype,
 
158
                            const QString &subtype)
 
159
{
 
160
    if (mimetype == "application/x-qrichtext") {
 
161
        // do richtext decode
 
162
        const char *mime;
 
163
        int i;
 
164
        for (i = 0; (mime = e->format(i)); ++i) {
 
165
            if (qstrcmp("application/x-qrichtext", mime) != 0)
 
166
                continue;
 
167
            str = QString::fromUtf8(e->encodedData(mime));
 
168
            return true;
 
169
        }
 
170
        return false;
 
171
    }
 
172
 
 
173
    // do a regular text decode
 
174
    QString st = subtype;
 
175
    return Q3TextDrag::decode(e, str, st);
 
176
}
 
177
 
 
178
bool Q3RichTextDrag::canDecode(QMimeSource* e)
 
179
{
 
180
    if (e->provides("application/x-qrichtext"))
 
181
        return true;
 
182
    return Q3TextDrag::canDecode(e);
 
183
}
 
184
 
 
185
const char* Q3RichTextDrag::format(int i) const
 
186
{
 
187
    if (Q3TextDrag::format(i))
 
188
        return Q3TextDrag::format(i);
 
189
    if (Q3TextDrag::format(i-1))
 
190
        return "application/x-qrichtext";
 
191
    return 0;
 
192
}
 
193
 
 
194
#endif
 
195
 
 
196
static bool block_set_alignment = false;
 
197
 
 
198
/*!
 
199
    \class Q3TextEdit qtextedit.h
 
200
    \brief The Q3TextEdit widget provides a powerful single-page rich text editor.
 
201
 
 
202
    \compat
 
203
 
 
204
    \tableofcontents
 
205
 
 
206
    \section1 Introduction and Concepts
 
207
 
 
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.
 
211
 
 
212
    Q3TextEdit has four modes of operation:
 
213
    \table
 
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
 
218
         returned.
 
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.)
 
236
    \endtable
 
237
 
 
238
    Q3TextEdit can be used as a syntax highlighting editor when used in
 
239
    conjunction with QSyntaxHighlighter.
 
240
 
 
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.
 
247
 
 
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,
 
257
    font and color.
 
258
 
 
259
    The text edit documentation uses the following concepts:
 
260
    \list
 
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
 
265
    cursor.
 
266
    \endlist
 
267
 
 
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().
 
278
 
 
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
 
281
    or QSimpleRichText.
 
282
 
 
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
 
292
    text.
 
293
 
 
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.
 
300
 
 
301
    \section1 Using Q3TextEdit as a Display Widget
 
302
 
 
303
    Q3TextEdit can display a large HTML subset, including tables and
 
304
    images.
 
305
 
 
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).
 
318
 
 
319
    Loading and saving text is achieved using setText() and text(),
 
320
    for example:
 
321
    \code
 
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());
 
326
    }
 
327
 
 
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);
 
333
    }
 
334
    \endcode
 
335
 
 
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().
 
345
 
 
346
    The background color is set differently than other widgets, using
 
347
    setPaper(). You specify a brush style which could be a plain color
 
348
    or a complex pixmap.
 
349
 
 
350
    Hypertext links are automatically underlined; this can be changed
 
351
    with setLinkUnderline(). The tab stop width is set with
 
352
    setTabStopWidth().
 
353
 
 
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.
 
357
 
 
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
 
362
    length().
 
363
 
 
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.
 
367
 
 
368
    A read-only Q3TextEdit provides the same functionality as the
 
369
    (obsolete) QTextView. (QTextView is still supplied for
 
370
    compatibility with old code.)
 
371
 
 
372
    \section2 Read-only key bindings
 
373
 
 
374
    When Q3TextEdit is used read-only the key-bindings are limited to
 
375
    navigation, and text may only be selected with the mouse:
 
376
    \table
 
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
 
386
    \row \i Shift+Wheel
 
387
         \i Scroll the page horizontally (the Wheel is the mouse wheel)
 
388
    \row \i Ctrl+Wheel        \i Zoom the text
 
389
    \endtable
 
390
 
 
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.
 
394
 
 
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
 
399
    \l{context()}.)
 
400
 
 
401
    \target logtextmode
 
402
    \section2 Using Q3TextEdit in Qt::LogText Mode
 
403
 
 
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 {&lt;}, \c {&gt;} and
 
412
    \c {&amp;}. 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.
 
420
 
 
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:
 
430
    \code
 
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>.
 
433
    \endcode
 
434
 
 
435
    Stylesheets can also be used in Qt::LogText mode. To create and use a
 
436
    custom tag, you could do the following:
 
437
    \code
 
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>!");
 
445
    \endcode
 
446
    Note that only the color, bold, underline and italic attributes of
 
447
    a Q3StyleSheetItem is used in Qt::LogText mode.
 
448
 
 
449
    Note that you can use setMaxLogLines() to limit the number of
 
450
    lines the widget can hold in Qt::LogText mode.
 
451
 
 
452
    There are a few things that you need to be aware of when the
 
453
    widget is in this mode:
 
454
    \list
 
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.
 
458
    \endlist
 
459
 
 
460
    \section1 Using Q3TextEdit as an Editor
 
461
 
 
462
    All the information about using Q3TextEdit as a display widget also
 
463
    applies here.
 
464
 
 
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().
 
469
 
 
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
 
482
    are cleared.
 
483
 
 
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.
 
489
 
 
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.
 
494
 
 
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.
 
500
 
 
501
    \section2 Editing key bindings
 
502
 
 
503
    The list of key-bindings which are implemented for editing:
 
504
    \table
 
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
 
542
    \endtable
 
543
 
 
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.
 
548
 
 
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().
 
554
*/
 
555
 
 
556
/*!
 
557
    \enum Q3TextEdit::AutoFormattingFlag
 
558
 
 
559
    \value AutoNone Do not perform any automatic formatting
 
560
    \value AutoBulletList Only automatically format bulletted lists
 
561
    \value AutoAll Apply all available autoformatting
 
562
*/
 
563
 
 
564
 
 
565
/*!
 
566
    \enum Q3TextEdit::KeyboardAction
 
567
 
 
568
    This enum is used by doKeyboardAction() to specify which action
 
569
    should be executed:
 
570
 
 
571
    \value ActionBackspace  Delete the character to the left of the
 
572
    cursor.
 
573
 
 
574
    \value ActionDelete  Delete the character to the right of the
 
575
    cursor.
 
576
 
 
577
    \value ActionReturn  Split the paragraph at the cursor position.
 
578
 
 
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.
 
584
 
 
585
    \value ActionWordBackspace Delete the word to the left of the
 
586
    cursor position.
 
587
 
 
588
    \value ActionWordDelete Delete the word to the right of the
 
589
    cursor position
 
590
 
 
591
*/
 
592
 
 
593
/*!
 
594
    \enum Q3TextEdit::VerticalAlignment
 
595
 
 
596
    This enum is used to set the vertical alignment of the text.
 
597
 
 
598
    \value AlignNormal Normal alignment
 
599
    \value AlignSuperScript Superscript
 
600
    \value AlignSubScript Subscript
 
601
*/
 
602
 
 
603
/*!
 
604
    \enum Q3TextEdit::TextInsertionFlags
 
605
 
 
606
    \internal
 
607
 
 
608
    \value RedoIndentation
 
609
    \value CheckNewLines
 
610
    \value RemoveSelected
 
611
*/
 
612
 
 
613
 
 
614
/*!
 
615
    \fn void Q3TextEdit::copyAvailable(bool yes)
 
616
 
 
617
    This signal is emitted when text is selected or de-selected in the
 
618
    text edit.
 
619
 
 
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.
 
623
 
 
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.
 
626
 
 
627
    \sa selectionChanged()
 
628
*/
 
629
 
 
630
 
 
631
/*!
 
632
    \fn void Q3TextEdit::textChanged()
 
633
 
 
634
    This signal is emitted whenever the text in the text edit changes.
 
635
 
 
636
    \sa setText() append()
 
637
*/
 
638
 
 
639
/*!
 
640
    \fn void Q3TextEdit::selectionChanged()
 
641
 
 
642
    This signal is emitted whenever the selection changes.
 
643
 
 
644
    \sa setSelection() copyAvailable()
 
645
*/
 
646
 
 
647
/*!  \fn Q3TextDocument *Q3TextEdit::document() const
 
648
 
 
649
    \internal
 
650
 
 
651
  This function returns the Q3TextDocument which is used by the text
 
652
  edit.
 
653
*/
 
654
 
 
655
/*!  \fn void Q3TextEdit::setDocument(Q3TextDocument *doc)
 
656
 
 
657
    \internal
 
658
 
 
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).
 
666
*/
 
667
 
 
668
/*!
 
669
    \enum Q3TextEdit::CursorAction
 
670
 
 
671
    This enum is used by moveCursor() to specify in which direction
 
672
    the cursor should be moved:
 
673
 
 
674
    \value MoveBackward  Moves the cursor one character backward
 
675
 
 
676
    \value MoveWordBackward Moves the cursor one word backward
 
677
 
 
678
    \value MoveForward  Moves the cursor one character forward
 
679
 
 
680
    \value MoveWordForward Moves the cursor one word forward
 
681
 
 
682
    \value MoveUp  Moves the cursor up one line
 
683
 
 
684
    \value MoveDown  Moves the cursor down one line
 
685
 
 
686
    \value MoveLineStart  Moves the cursor to the beginning of the line
 
687
 
 
688
    \value MoveLineEnd Moves the cursor to the end of the line
 
689
 
 
690
    \value MoveHome  Moves the cursor to the beginning of the document
 
691
 
 
692
    \value MoveEnd Moves the cursor to the end of the document
 
693
 
 
694
    \value MovePgUp  Moves the cursor one viewport page up
 
695
 
 
696
    \value MovePgDown  Moves the cursor one viewport page down
 
697
*/
 
698
 
 
699
/*!
 
700
    \property Q3TextEdit::overwriteMode
 
701
    \brief the text edit's overwrite mode
 
702
 
 
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
 
707
    position.
 
708
*/
 
709
 
 
710
/*!
 
711
    \fn void Q3TextEdit::setCurrentFont(const QFont &f)
 
712
 
 
713
    Sets the font of the current format to \a f.
 
714
 
 
715
    If the widget is in \c Qt::LogText mode this function will do
 
716
    nothing. Use setFont() instead.
 
717
 
 
718
    \sa currentFont() setPointSize() setFamily()
 
719
*/
 
720
 
 
721
/*!
 
722
    \property Q3TextEdit::undoDepth
 
723
    \brief the depth of the undo history
 
724
 
 
725
    The maximum number of steps in the undo/redo history. The default
 
726
    is 100.
 
727
 
 
728
    \sa undo() redo()
 
729
*/
 
730
 
 
731
/*!
 
732
    \fn void Q3TextEdit::undoAvailable(bool yes)
 
733
 
 
734
    This signal is emitted when the availability of undo changes. If
 
735
    \a yes is true, then undo() will work until undoAvailable(false)
 
736
    is next emitted.
 
737
 
 
738
    \sa undo() undoDepth()
 
739
*/
 
740
 
 
741
/*!
 
742
    \fn void Q3TextEdit::modificationChanged(bool m)
 
743
 
 
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.
 
747
 
 
748
    \sa modified
 
749
*/
 
750
 
 
751
/*!
 
752
    \fn void Q3TextEdit::redoAvailable(bool yes)
 
753
 
 
754
    This signal is emitted when the availability of redo changes. If
 
755
    \a yes is true, then redo() will work until redoAvailable(false)
 
756
    is next emitted.
 
757
 
 
758
    \sa redo() undoDepth()
 
759
*/
 
760
 
 
761
/*!
 
762
    \fn void Q3TextEdit::currentFontChanged(const QFont &f)
 
763
 
 
764
    This signal is emitted if the font of the current format has
 
765
    changed.
 
766
 
 
767
    The new font is \a f.
 
768
 
 
769
    \sa setCurrentFont()
 
770
*/
 
771
 
 
772
/*!
 
773
    \fn void Q3TextEdit::currentColorChanged(const QColor &c)
 
774
 
 
775
    This signal is emitted if the color of the current format has
 
776
    changed.
 
777
 
 
778
    The new color is \a c.
 
779
 
 
780
    \sa setColor()
 
781
*/
 
782
 
 
783
/*!
 
784
    \fn void Q3TextEdit::currentVerticalAlignmentChanged(VerticalAlignment a)
 
785
 
 
786
    This signal is emitted if the vertical alignment of the current
 
787
    format has changed.
 
788
 
 
789
    The new vertical alignment is \a a.
 
790
 
 
791
    \sa setVerticalAlignment()
 
792
*/
 
793
 
 
794
/*!
 
795
    \fn void Q3TextEdit::currentAlignmentChanged(int a)
 
796
 
 
797
    This signal is emitted if the alignment of the current paragraph
 
798
    has changed.
 
799
 
 
800
    The new alignment is \a a.
 
801
 
 
802
    \sa setAlignment()
 
803
*/
 
804
 
 
805
/*!
 
806
    \fn void Q3TextEdit::cursorPositionChanged(Q3TextCursor *c)
 
807
 
 
808
    \internal
 
809
*/
 
810
 
 
811
/*!
 
812
    \fn void Q3TextEdit::cursorPositionChanged(int para, int pos)
 
813
 
 
814
    \overload
 
815
 
 
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.
 
819
 
 
820
    \sa setCursorPosition()
 
821
*/
 
822
 
 
823
/*!
 
824
    \fn void Q3TextEdit::clicked(int para, int pos)
 
825
 
 
826
    This signal is emitted when the mouse is clicked on the paragraph
 
827
    \a para at character position \a pos.
 
828
 
 
829
    \sa doubleClicked()
 
830
*/
 
831
 
 
832
/*! \fn void Q3TextEdit::doubleClicked(int para, int pos)
 
833
 
 
834
  This signal is emitted when the mouse is double-clicked on the
 
835
  paragraph \a para at character position \a pos.
 
836
 
 
837
  \sa clicked()
 
838
*/
 
839
 
 
840
 
 
841
/*!
 
842
    \fn void Q3TextEdit::returnPressed()
 
843
 
 
844
    This signal is emitted if the user pressed the Return or the Enter
 
845
    key.
 
846
*/
 
847
 
 
848
/*!
 
849
    \fn Q3TextCursor *Q3TextEdit::textCursor() const
 
850
 
 
851
    Returns the text edit's text cursor.
 
852
 
 
853
    \warning Q3TextCursor is not in the public API, but in special
 
854
    circumstances you might wish to use it.
 
855
*/
 
856
 
 
857
/*!
 
858
    Constructs an empty Q3TextEdit called \a name, with parent \a
 
859
    parent.
 
860
*/
 
861
 
 
862
Q3TextEdit::Q3TextEdit(QWidget *parent, const char *name)
 
863
    : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase),
 
864
      doc(new Q3TextDocument(0)), undoRedoInfo(doc)
 
865
{
 
866
    init();
 
867
}
 
868
 
 
869
/*!
 
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.
 
872
 
 
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.
 
876
 
 
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".
 
887
*/
 
888
 
 
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)
 
893
{
 
894
    init();
 
895
    setText(text, context);
 
896
}
 
897
 
 
898
/*!
 
899
    Destructor.
 
900
*/
 
901
 
 
902
Q3TextEdit::~Q3TextEdit()
 
903
{
 
904
    delete undoRedoInfo.d;
 
905
    undoRedoInfo.d = 0;
 
906
    delete cursor;
 
907
    delete doc;
 
908
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
909
    delete d->od;
 
910
#endif
 
911
    delete d;
 
912
}
 
913
 
 
914
void Q3TextEdit::init()
 
915
{
 
916
    d = new Q3TextEditPrivate;
 
917
    doc->formatCollection()->setPaintDevice(this);
 
918
    undoEnabled = true;
 
919
    readonly = true;
 
920
    setReadOnly(false);
 
921
    setFrameStyle(LineEditPanel | Sunken);
 
922
    connect(doc, SIGNAL(minimumWidthChanged(int)),
 
923
             this, SLOT(documentWidthChanged(int)));
 
924
 
 
925
    mousePressed = false;
 
926
    inDoubleClick = false;
 
927
    modified = false;
 
928
    mightStartDrag = false;
 
929
    onLink.clear();
 
930
    d->onName.clear();
 
931
    overWrite = false;
 
932
    wrapMode = WidgetWidth;
 
933
    wrapWidth = -1;
 
934
    wPolicy = AtWhiteSpace;
 
935
    inDnD = false;
 
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;
 
941
 
 
942
    setBackgroundRole(QPalette::Base);
 
943
    viewport()->setBackgroundRole(QPalette::Base);
 
944
 
 
945
    viewport()->setAcceptDrops(true);
 
946
    resizeContents(0, doc->lastParagraph() ?
 
947
                    (doc->lastParagraph()->paragId() + 1) * doc->formatCollection()->defaultFormat()->height() : 0);
 
948
 
 
949
    setAttribute(Qt::WA_KeyCompression, true);
 
950
    viewport()->setMouseTracking(true);
 
951
#ifndef QT_NO_CURSOR
 
952
    viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
953
#endif
 
954
    cursor = new Q3TextCursor(doc);
 
955
 
 
956
    formatTimer = new QTimer(this);
 
957
    connect(formatTimer, SIGNAL(timeout()),
 
958
             this, SLOT(formatMore()));
 
959
    lastFormatted = doc->firstParagraph();
 
960
 
 
961
    scrollTimer = new QTimer(this);
 
962
    connect(scrollTimer, SIGNAL(timeout()),
 
963
             this, SLOT(autoScrollTimerDone()));
 
964
 
 
965
    interval = 0;
 
966
    changeIntervalTimer = new QTimer(this);
 
967
    connect(changeIntervalTimer, SIGNAL(timeout()),
 
968
             this, SLOT(doChangeInterval()));
 
969
 
 
970
    cursorVisible = true;
 
971
    blinkTimer = new QTimer(this);
 
972
    connect(blinkTimer, SIGNAL(timeout()),
 
973
             this, SLOT(blinkCursor()));
 
974
 
 
975
#ifndef QT_NO_DRAGANDDROP
 
976
    dragStartTimer = new QTimer(this);
 
977
    connect(dragStartTimer, SIGNAL(timeout()),
 
978
             this, SLOT(startDrag()));
 
979
#endif
 
980
 
 
981
    d->trippleClickTimer = new QTimer(this);
 
982
 
 
983
    formatMore();
 
984
 
 
985
    blinkCursorVisible = false;
 
986
 
 
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);
 
994
}
 
995
 
 
996
void Q3TextEdit::paintDocument(bool drawAll, QPainter *p, int cx, int cy, int cw, int ch)
 
997
{
 
998
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
999
    Q_ASSERT(!d->optimMode);
 
1000
    if (d->optimMode)
 
1001
        return;
 
1002
#endif
 
1003
 
 
1004
    bool drawCur = blinkCursorVisible && (hasFocus() || viewport()->hasFocus());
 
1005
    if ((hasSelectedText() && !style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, 0, this)) ||
 
1006
        isReadOnly() || !cursorVisible)
 
1007
        drawCur = false;
 
1008
    QPalette pal = palette();
 
1009
    if (doc->paper())
 
1010
        pal.setBrush(QPalette::Base, *doc->paper());
 
1011
 
 
1012
    if (contentsY() < doc->y()) {
 
1013
        p->fillRect(contentsX(), contentsY(), visibleWidth(), doc->y(),
 
1014
                     pal.base());
 
1015
    }
 
1016
    if (drawAll && doc->width() - contentsX() < cx + cw) {
 
1017
        p->fillRect(doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch,
 
1018
                     pal.base());
 
1019
    }
 
1020
 
 
1021
    p->setBrushOrigin(-contentsX(), -contentsY());
 
1022
 
 
1023
    lastFormatted = doc->draw(p, cx, cy, cw, ch, pal, !drawAll, drawCur, cursor);
 
1024
 
 
1025
    if (lastFormatted == doc->lastParagraph())
 
1026
        resizeContents(contentsWidth(), doc->height());
 
1027
 
 
1028
    if (contentsHeight() < visibleHeight() && (!doc->lastParagraph() || doc->lastParagraph()->isValid()) && drawAll)
 
1029
        p->fillRect(0, contentsHeight(), visibleWidth(),
 
1030
                     visibleHeight() - contentsHeight(), pal.base());
 
1031
}
 
1032
 
 
1033
/*!
 
1034
    \reimp
 
1035
*/
 
1036
 
 
1037
void Q3TextEdit::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
 
1038
{
 
1039
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
1040
    if (d->optimMode) {
 
1041
        optimDrawContents(p, cx, cy, cw, ch);
 
1042
        return;
 
1043
    }
 
1044
#endif
 
1045
    paintDocument(true, p, cx, cy, cw, ch);
 
1046
    int v;
 
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);
 
1052
            l += v;
 
1053
        }
 
1054
    }
 
1055
}
 
1056
 
 
1057
/*!
 
1058
    \internal
 
1059
*/
 
1060
 
 
1061
void Q3TextEdit::drawContents(QPainter *p)
 
1062
{
 
1063
    if (horizontalScrollBar()->isVisible() &&
 
1064
         verticalScrollBar()->isVisible()) {
 
1065
        const QRect verticalRect = verticalScrollBar()->geometry();
 
1066
        const QRect horizontalRect = horizontalScrollBar()->geometry();
 
1067
 
 
1068
        QRect cornerRect;
 
1069
        cornerRect.setTop(verticalRect.bottom());
 
1070
        cornerRect.setBottom(horizontalRect.bottom());
 
1071
        cornerRect.setLeft(verticalRect.left());
 
1072
        cornerRect.setRight(verticalRect.right());
 
1073
 
 
1074
        p->fillRect(cornerRect, palette().background());
 
1075
    }
 
1076
}
 
1077
 
 
1078
/*!
 
1079
    \reimp
 
1080
*/
 
1081
 
 
1082
bool Q3TextEdit::event(QEvent *e)
 
1083
{
 
1084
    if (e->type() == QEvent::AccelOverride && !isReadOnly()) {
 
1085
        QKeyEvent* ke = (QKeyEvent*) e;
 
1086
        switch(ke->state()) {
 
1087
        case Qt::NoButton:
 
1088
        case Qt::Keypad:
 
1089
        case Qt::ShiftButton:
 
1090
            if (ke->key() < Qt::Key_Escape) {
 
1091
                ke->accept();
 
1092
            } else if (ke->state() == Qt::NoButton
 
1093
                        || ke->state() == Qt::ShiftButton) {
 
1094
                switch (ke->key()) {
 
1095
                case Qt::Key_Return:
 
1096
                case Qt::Key_Enter:
 
1097
                case Qt::Key_Delete:
 
1098
                case Qt::Key_Home:
 
1099
                case Qt::Key_End:
 
1100
                case Qt::Key_Backspace:
 
1101
                case Qt::Key_Left:
 
1102
                case Qt::Key_Right:
 
1103
                    ke->accept();
 
1104
                default:
 
1105
                    break;
 
1106
                }
 
1107
            }
 
1108
            break;
 
1109
 
 
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()) {
 
1115
            case Qt::Key_Tab:
 
1116
            case Qt::Key_Backtab:
 
1117
                ke->ignore();
 
1118
                break;
 
1119
// Those are too frequently used for application functionality
 
1120
/*            case Qt::Key_A:
 
1121
            case Qt::Key_B:
 
1122
            case Qt::Key_D:
 
1123
            case Qt::Key_E:
 
1124
            case Qt::Key_F:
 
1125
            case Qt::Key_H:
 
1126
            case Qt::Key_I:
 
1127
            case Qt::Key_K:
 
1128
            case Qt::Key_N:
 
1129
            case Qt::Key_P:
 
1130
            case Qt::Key_T:
 
1131
*/
 
1132
            case Qt::Key_C:
 
1133
            case Qt::Key_V:
 
1134
            case Qt::Key_X:
 
1135
            case Qt::Key_Y:
 
1136
            case Qt::Key_Z:
 
1137
            case Qt::Key_Left:
 
1138
            case Qt::Key_Right:
 
1139
            case Qt::Key_Up:
 
1140
            case Qt::Key_Down:
 
1141
            case Qt::Key_Home:
 
1142
            case Qt::Key_End:
 
1143
#if defined (Q_WS_WIN)
 
1144
            case Qt::Key_Insert:
 
1145
            case Qt::Key_Delete:
 
1146
#endif
 
1147
                ke->accept();
 
1148
            default:
 
1149
                break;
 
1150
            }
 
1151
            break;
 
1152
 
 
1153
        default:
 
1154
            switch (ke->key()) {
 
1155
#if defined (Q_WS_WIN)
 
1156
            case Qt::Key_Insert:
 
1157
                ke->accept();
 
1158
#endif
 
1159
            default:
 
1160
                break;
 
1161
            }
 
1162
            break;
 
1163
        }
 
1164
    }
 
1165
 
 
1166
    if (e->type() == QEvent::Show) {
 
1167
        if (
 
1168
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
1169
             !d->optimMode &&
 
1170
#endif
 
1171
             d->ensureCursorVisibleInShowEvent ) {
 
1172
            ensureCursorVisible();
 
1173
            d->ensureCursorVisibleInShowEvent = false;
 
1174
        }
 
1175
        if (!d->scrollToAnchor.isEmpty()) {
 
1176
            scrollToAnchor(d->scrollToAnchor);
 
1177
            d->scrollToAnchor.clear();
 
1178
        }
 
1179
    }
 
1180
    return QWidget::event(e);
 
1181
}
 
1182
 
 
1183
/*!
 
1184
    Processes the key event, \a e. By default key events are used to
 
1185
    provide keyboard navigation and text editing.
 
1186
*/
 
1187
 
 
1188
void Q3TextEdit::keyPressEvent(QKeyEvent *e)
 
1189
{
 
1190
    changeIntervalTimer->stop();
 
1191
    interval = 10;
 
1192
    bool unknownKey = false;
 
1193
    if (isReadOnly()) {
 
1194
        if (!handleReadOnlyKeyEvent(e))
 
1195
            Q3ScrollView::keyPressEvent(e);
 
1196
        changeIntervalTimer->start(100, true);
 
1197
        return;
 
1198
    }
 
1199
 
 
1200
 
 
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;
 
1204
 
 
1205
    if (selChanged) {
 
1206
        cursor->paragraph()->document()->nextDoubleBuffered = true;
 
1207
        repaintChanged();
 
1208
    }
 
1209
 
 
1210
    bool clearUndoRedoInfo = true;
 
1211
 
 
1212
 
 
1213
    switch (e->key()) {
 
1214
    case Qt::Key_Left:
 
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.
 
1218
        CursorAction a;
 
1219
        if (cursor->paragraph()->string()->isRightToLeft() == (e->key() == Qt::Key_Right))
 
1220
            a = e->state() & Qt::ControlButton ? MoveWordBackward : MoveBackward;
 
1221
        else
 
1222
            a = e->state() & Qt::ControlButton ? MoveWordForward : MoveForward;
 
1223
        moveCursor(a, e->state() & Qt::ShiftButton);
 
1224
        break;
 
1225
    }
 
1226
    case Qt::Key_Up:
 
1227
        moveCursor(e->state() & Qt::ControlButton ? MovePgUp : MoveUp, e->state() & Qt::ShiftButton);
 
1228
        break;
 
1229
    case Qt::Key_Down:
 
1230
        moveCursor(e->state() & Qt::ControlButton ? MovePgDown : MoveDown, e->state() & Qt::ShiftButton);
 
1231
        break;
 
1232
    case Qt::Key_Home:
 
1233
        moveCursor(e->state() & Qt::ControlButton ? MoveHome : MoveLineStart, e->state() & Qt::ShiftButton);
 
1234
        break;
 
1235
    case Qt::Key_End:
 
1236
        moveCursor(e->state() & Qt::ControlButton ? MoveEnd : MoveLineEnd, e->state() & Qt::ShiftButton);
 
1237
        break;
 
1238
    case Qt::Key_Prior:
 
1239
        moveCursor(MovePgUp, e->state() & Qt::ShiftButton);
 
1240
        break;
 
1241
    case Qt::Key_Next:
 
1242
        moveCursor(MovePgDown, e->state() & Qt::ShiftButton);
 
1243
        break;
 
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);
 
1250
        } else {
 
1251
#ifndef QT_NO_CURSOR
 
1252
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
1253
#endif
 
1254
            clearUndoRedoInfo = false;
 
1255
            doKeyboardAction(ActionReturn);
 
1256
            emit returnPressed();
 
1257
        }
 
1258
        break;
 
1259
    case Qt::Key_Delete:
 
1260
#if defined (Q_WS_WIN)
 
1261
        if (e->state() & Qt::ShiftButton) {
 
1262
            cut();
 
1263
            break;
 
1264
        } else
 
1265
#endif
 
1266
        if (doc->hasSelection(Q3TextDocument::Standard, true)) {
 
1267
            removeSelectedText();
 
1268
            break;
 
1269
        }
 
1270
        doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordDelete
 
1271
                          : ActionDelete);
 
1272
        clearUndoRedoInfo = false;
 
1273
 
 
1274
        break;
 
1275
    case Qt::Key_Insert:
 
1276
        if (e->state() & Qt::ShiftButton)
 
1277
            paste();
 
1278
#if defined (Q_WS_WIN)
 
1279
        else if (e->state() & Qt::ControlButton)
 
1280
            copy();
 
1281
#endif
 
1282
        else
 
1283
            setOverwriteMode(!isOverwriteMode());
 
1284
        break;
 
1285
    case Qt::Key_Backspace:
 
1286
#if defined (Q_WS_WIN)
 
1287
        if (e->state() & Qt::AltButton) {
 
1288
            if (e->state() & Qt::ControlButton) {
 
1289
                break;
 
1290
            } else if (e->state() & Qt::ShiftButton) {
 
1291
                redo();
 
1292
                break;
 
1293
            } else {
 
1294
                undo();
 
1295
                break;
 
1296
            }
 
1297
        } else
 
1298
#endif
 
1299
        if (doc->hasSelection(Q3TextDocument::Standard, true)) {
 
1300
            removeSelectedText();
 
1301
            break;
 
1302
        }
 
1303
 
 
1304
        doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordBackspace
 
1305
                          : ActionBackspace);
 
1306
        clearUndoRedoInfo = false;
 
1307
        break;
 
1308
    case Qt::Key_F16: // Copy key on Sun keyboards
 
1309
        copy();
 
1310
        break;
 
1311
    case Qt::Key_F18:  // Paste key on Sun keyboards
 
1312
        paste();
 
1313
        break;
 
1314
    case Qt::Key_F20:  // Cut key on Sun keyboards
 
1315
        cut();
 
1316
        break;
 
1317
    case Qt::Key_Direction_L:
 
1318
        if (doc->textFormat() == Qt::PlainText) {
 
1319
            // change the whole doc
 
1320
            Q3TextParagraph *p = doc->firstParagraph();
 
1321
            while (p) {
 
1322
                p->setDirection(QChar::DirL);
 
1323
                p->setAlignment(Qt::AlignLeft);
 
1324
                p->invalidate(0);
 
1325
                p = p->next();
 
1326
            }
 
1327
        } else {
 
1328
            if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL)
 
1329
                return;
 
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);
 
1334
        }
 
1335
        repaintChanged();
 
1336
        break;
 
1337
    case Qt::Key_Direction_R:
 
1338
        if (doc->textFormat() == Qt::PlainText) {
 
1339
            // change the whole doc
 
1340
            Q3TextParagraph *p = doc->firstParagraph();
 
1341
            while (p) {
 
1342
                p->setDirection(QChar::DirR);
 
1343
                p->setAlignment(Qt::AlignRight);
 
1344
                p->invalidate(0);
 
1345
                p = p->next();
 
1346
            }
 
1347
        } else {
 
1348
            if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR)
 
1349
                return;
 
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);
 
1354
        }
 
1355
        repaintChanged();
 
1356
        break;
 
1357
    default: {
 
1358
            char ascii = e->text().length() ? e->text().unicode()->latin1() : 0;
 
1359
            if (e->text().length() &&
 
1360
                (!(e->state() & Qt::ControlButton) &&
 
1361
#ifndef Q_OS_MAC
 
1362
                  !(e->state() & Qt::AltButton) &&
 
1363
#endif
 
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) {
 
1370
                        e->ignore();
 
1371
                        break;
 
1372
                    }
 
1373
                    if (textFormat() == Qt::RichText && cursor->index() == 0
 
1374
                         && (cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth())) {
 
1375
                        clearUndoRedo();
 
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);
 
1381
                        clearUndoRedo();
 
1382
                        drawCursor(false);
 
1383
                        repaintChanged();
 
1384
                        drawCursor(true);
 
1385
                        break;
 
1386
                    }
 
1387
                } else if (e->key() == Qt::Key_BackTab) {
 
1388
                    if (d->tabChangesFocus) {
 
1389
                        e->ignore();
 
1390
                        break;
 
1391
                    }
 
1392
                }
 
1393
 
 
1394
                if ((autoFormatting() & AutoBulletList) &&
 
1395
                     textFormat() == Qt::RichText && cursor->index() == 0
 
1396
                     && !cursor->paragraph()->isListItem()
 
1397
                     && (e->text()[0] == '-' || e->text()[0] == '*')) {
 
1398
                        clearUndoRedo();
 
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);
 
1404
                        clearUndoRedo();
 
1405
                        drawCursor(false);
 
1406
                        repaintChanged();
 
1407
                        drawCursor(true);
 
1408
                        break;
 
1409
                }
 
1410
                if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(Q3TextDocument::Standard)) {
 
1411
                    doKeyboardAction(ActionDelete);
 
1412
                    clearUndoRedoInfo = false;
 
1413
                }
 
1414
                QString t = e->text();
 
1415
                insert(t, true, false);
 
1416
                break;
 
1417
            } else if (e->state() & Qt::ControlButton) {
 
1418
                switch (e->key()) {
 
1419
                case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
 
1420
                    copy();
 
1421
                    break;
 
1422
                case Qt::Key_V:
 
1423
                    paste();
 
1424
                    break;
 
1425
                case Qt::Key_X:
 
1426
                    cut();
 
1427
                    break;
 
1428
                case Qt::Key_I: case Qt::Key_T: case Qt::Key_Tab:
 
1429
                    if (!d->tabChangesFocus)
 
1430
                        indent();
 
1431
                    break;
 
1432
                case Qt::Key_A:
 
1433
#if defined(Q_WS_X11)
 
1434
                    moveCursor(MoveLineStart, e->state() & Qt::ShiftButton);
 
1435
#else
 
1436
                    selectAll(true);
 
1437
#endif
 
1438
                    break;
 
1439
                case Qt::Key_B:
 
1440
                    moveCursor(MoveBackward, e->state() & Qt::ShiftButton);
 
1441
                    break;
 
1442
                case Qt::Key_F:
 
1443
                    moveCursor(MoveForward, e->state() & Qt::ShiftButton);
 
1444
                    break;
 
1445
                case Qt::Key_D:
 
1446
                    if (doc->hasSelection(Q3TextDocument::Standard)) {
 
1447
                        removeSelectedText();
 
1448
                        break;
 
1449
                    }
 
1450
                    doKeyboardAction(ActionDelete);
 
1451
                    clearUndoRedoInfo = false;
 
1452
                    break;
 
1453
                case Qt::Key_H:
 
1454
                    if (doc->hasSelection(Q3TextDocument::Standard)) {
 
1455
                        removeSelectedText();
 
1456
                        break;
 
1457
                    }
 
1458
                    if (!cursor->paragraph()->prev() &&
 
1459
                         cursor->atParagStart())
 
1460
                        break;
 
1461
 
 
1462
                    doKeyboardAction(ActionBackspace);
 
1463
                    clearUndoRedoInfo = false;
 
1464
                    break;
 
1465
                case Qt::Key_E:
 
1466
                    moveCursor(MoveLineEnd, e->state() & Qt::ShiftButton);
 
1467
                    break;
 
1468
                case Qt::Key_N:
 
1469
                    moveCursor(MoveDown, e->state() & Qt::ShiftButton);
 
1470
                    break;
 
1471
                case Qt::Key_P:
 
1472
                    moveCursor(MoveUp, e->state() & Qt::ShiftButton);
 
1473
                    break;
 
1474
                case Qt::Key_Z:
 
1475
                    if(e->state() & Qt::ShiftButton)
 
1476
                        redo();
 
1477
                    else
 
1478
                        undo();
 
1479
                    break;
 
1480
                case Qt::Key_Y:
 
1481
                    redo();
 
1482
                    break;
 
1483
                case Qt::Key_K:
 
1484
                    doKeyboardAction(ActionKill);
 
1485
                    break;
 
1486
#if defined(Q_WS_WIN)
 
1487
                case Qt::Key_Insert:
 
1488
                    copy();
 
1489
                    break;
 
1490
                case Qt::Key_Delete:
 
1491
                    del();
 
1492
                    break;
 
1493
#endif
 
1494
                default:
 
1495
                    unknownKey = false;
 
1496
                    break;
 
1497
                }
 
1498
            } else {
 
1499
                unknownKey = true;
 
1500
            }
 
1501
        }
 
1502
    }
 
1503
 
 
1504
    emit cursorPositionChanged(cursor);
 
1505
    emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
1506
    if (clearUndoRedoInfo)
 
1507
        clearUndoRedo();
 
1508
    changeIntervalTimer->start(100, true);
 
1509
    if (unknownKey)
 
1510
        e->ignore();
 
1511
}
 
1512
 
 
1513
/*!
 
1514
    \reimp
 
1515
*/
 
1516
void Q3TextEdit::inputMethodEvent(QInputMethodEvent *e)
 
1517
{
 
1518
    if (isReadOnly()) {
 
1519
        e->ignore();
 
1520
        return;
 
1521
    }
 
1522
 
 
1523
    if (hasSelectedText())
 
1524
        removeSelectedText();
 
1525
 
 
1526
    bool oldupdate = updatesEnabled();
 
1527
    if (oldupdate)
 
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;
 
1533
 
 
1534
    if (d->preeditLength > 0 && cursor->paragraph()) {
 
1535
        cursor->paragraph()->remove(d->preeditStart, d->preeditLength);
 
1536
        d->preeditStart = d->preeditLength = -1;
 
1537
    }
 
1538
 
 
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);
 
1550
    }
 
1551
 
 
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);
 
1557
 
 
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)
 
1564
                continue;
 
1565
            QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
 
1566
            if (f.isValid()) {
 
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);
 
1572
 
 
1573
                doc->setSelectionColor(preeditSelectionBase + d->numPreeditSelections, f.background().color());
 
1574
                doc->setSelectionTextColor(preeditSelectionBase + d->numPreeditSelections, f.foreground().color());
 
1575
                ++d->numPreeditSelections;
 
1576
            }
 
1577
        }
 
1578
    }
 
1579
    if (oldupdate)
 
1580
        setUpdatesEnabled(true);
 
1581
    repaintChanged();
 
1582
}
 
1583
 
 
1584
 
 
1585
static bool qtextedit_ignore_readonly = false;
 
1586
 
 
1587
/*!
 
1588
    Executes keyboard action \a action. This is normally called by a
 
1589
    key event handler.
 
1590
*/
 
1591
 
 
1592
void Q3TextEdit::doKeyboardAction(KeyboardAction action)
 
1593
{
 
1594
    if (isReadOnly() && !qtextedit_ignore_readonly)
 
1595
        return;
 
1596
 
 
1597
    if (cursor->nestedDepth() != 0)
 
1598
        return;
 
1599
 
 
1600
    lastFormatted = cursor->paragraph();
 
1601
    drawCursor(false);
 
1602
    bool doUpdateCurrentFormat = true;
 
1603
 
 
1604
    switch (action) {
 
1605
    case ActionWordDelete:
 
1606
    case ActionDelete:
 
1607
        if (action == ActionDelete && !cursor->atParagEnd()) {
 
1608
            if (undoEnabled) {
 
1609
                checkUndoRedoInfo(UndoRedoInfo::Delete);
 
1610
                if (!undoRedoInfo.valid()) {
 
1611
                    undoRedoInfo.id = cursor->paragraph()->paragId();
 
1612
                    undoRedoInfo.index = cursor->index();
 
1613
                    undoRedoInfo.d->text.clear();
 
1614
                }
 
1615
                int idx = cursor->index();
 
1616
                do {
 
1617
                    undoRedoInfo.d->text.insert(undoRedoInfo.d->text.length(), cursor->paragraph()->at(idx++), true);
 
1618
                } while (!cursor->paragraph()->string()->validCursorPosition(idx));
 
1619
            }
 
1620
            cursor->remove();
 
1621
        } else {
 
1622
            clearUndoRedo();
 
1623
            doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
 
1624
            if (action == ActionWordDelete && !cursor->atParagEnd()) {
 
1625
                cursor->gotoNextWord();
 
1626
            } else {
 
1627
                cursor->gotoNextLetter();
 
1628
            }
 
1629
            doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
 
1630
            removeSelectedText(Q3TextDocument::Temp);
 
1631
        }
 
1632
        break;
 
1633
    case ActionWordBackspace:
 
1634
    case ActionBackspace:
 
1635
        if (textFormat() == Qt::RichText
 
1636
             && (cursor->paragraph()->isListItem()
 
1637
                 || cursor->paragraph()->listDepth())
 
1638
             && cursor->index() == 0) {
 
1639
            if (undoEnabled) {
 
1640
                clearUndoRedo();
 
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);
 
1645
            }
 
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);
 
1652
            } else {
 
1653
                cursor->paragraph()->setListDepth(ldepth - 1);
 
1654
            }
 
1655
            clearUndoRedo();
 
1656
            lastFormatted = cursor->paragraph();
 
1657
            repaintChanged();
 
1658
            drawCursor(true);
 
1659
            return;
 
1660
        }
 
1661
 
 
1662
        if (action == ActionBackspace && !cursor->atParagStart()) {
 
1663
            if (undoEnabled) {
 
1664
                checkUndoRedoInfo(UndoRedoInfo::Delete);
 
1665
                if (!undoRedoInfo.valid()) {
 
1666
                    undoRedoInfo.id = cursor->paragraph()->paragId();
 
1667
                    undoRedoInfo.index = cursor->index();
 
1668
                    undoRedoInfo.d->text.clear();
 
1669
                }
 
1670
                undoRedoInfo.d->text.insert(0, cursor->paragraph()->at(cursor->index()-1), true);
 
1671
                undoRedoInfo.index = cursor->index()-1;
 
1672
            }
 
1673
            cursor->removePreviousChar();
 
1674
            lastFormatted = cursor->paragraph();
 
1675
        } else if (cursor->paragraph()->prev()
 
1676
                    || (action == ActionWordBackspace
 
1677
                        && !cursor->atParagStart())) {
 
1678
            clearUndoRedo();
 
1679
            doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
 
1680
            if (action == ActionWordBackspace && !cursor->atParagStart()) {
 
1681
                cursor->gotoPreviousWord();
 
1682
            } else {
 
1683
                cursor->gotoPreviousLetter();
 
1684
            }
 
1685
            doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
 
1686
            removeSelectedText(Q3TextDocument::Temp);
 
1687
        }
 
1688
        break;
 
1689
    case ActionReturn:
 
1690
        if (undoEnabled) {
 
1691
            checkUndoRedoInfo(UndoRedoInfo::Return);
 
1692
            if (!undoRedoInfo.valid()) {
 
1693
                undoRedoInfo.id = cursor->paragraph()->paragId();
 
1694
                undoRedoInfo.index = cursor->index();
 
1695
                undoRedoInfo.d->text.clear();
 
1696
            }
 
1697
            undoRedoInfo.d->text += "\n";
 
1698
        }
 
1699
        cursor->splitAndInsertEmptyParagraph();
 
1700
        if (cursor->paragraph()->prev()) {
 
1701
            lastFormatted = cursor->paragraph()->prev();
 
1702
            lastFormatted->invalidate(0);
 
1703
        }
 
1704
        doUpdateCurrentFormat = false;
 
1705
        break;
 
1706
    case ActionKill:
 
1707
        clearUndoRedo();
 
1708
        doc->setSelectionStart(Q3TextDocument::Temp, *cursor);
 
1709
        if (cursor->atParagEnd())
 
1710
            cursor->gotoNextLetter();
 
1711
        else
 
1712
            cursor->setIndex(cursor->paragraph()->length() - 1);
 
1713
        doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
 
1714
        removeSelectedText(Q3TextDocument::Temp);
 
1715
        break;
 
1716
    }
 
1717
 
 
1718
    formatMore();
 
1719
    repaintChanged();
 
1720
    ensureCursorVisible();
 
1721
    drawCursor(true);
 
1722
    if (doUpdateCurrentFormat)
 
1723
        updateCurrentFormat();
 
1724
    setModified();
 
1725
    emit textChanged();
 
1726
}
 
1727
 
 
1728
void Q3TextEdit::readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles)
 
1729
{
 
1730
#ifndef QT_NO_DATASTREAM
 
1731
    QDataStream styleStream(&undoRedoInfo.styleInformation, IO_WriteOnly);
 
1732
#endif
 
1733
    c2.restoreState();
 
1734
    c1.restoreState();
 
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
 
1740
        if (fillStyles) {
 
1741
            styleStream << (int) 1;
 
1742
            c1.paragraph()->writeStyleInformation(styleStream);
 
1743
        }
 
1744
#endif
 
1745
    } else {
 
1746
        int i;
 
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++;
 
1756
            p = p->next();
 
1757
        }
 
1758
        for (i = 0; i < c2.index(); ++i)
 
1759
            text.insert(i + lastIndex, c2.paragraph()->at(i), true);
 
1760
#ifndef QT_NO_DATASTREAM
 
1761
        if (fillStyles) {
 
1762
            styleStream << num;
 
1763
            for (Q3TextParagraph *p = c1.paragraph(); --num >= 0; p = p->next())
 
1764
                p->writeStyleInformation(styleStream);
 
1765
        }
 
1766
#endif
 
1767
    }
 
1768
}
 
1769
 
 
1770
/*!
 
1771
    Removes the selection \a selNum (by default 0). This does not
 
1772
    remove the selected text.
 
1773
 
 
1774
    \sa removeSelectedText()
 
1775
*/
 
1776
 
 
1777
void Q3TextEdit::removeSelection(int selNum)
 
1778
{
 
1779
    doc->removeSelection(selNum);
 
1780
    repaintChanged();
 
1781
}
 
1782
 
 
1783
/*!
 
1784
    Deletes the text of selection \a selNum (by default, the default
 
1785
    selection, 0). If there is no selected text nothing happens.
 
1786
 
 
1787
    \sa selectedText removeSelection()
 
1788
*/
 
1789
 
 
1790
void Q3TextEdit::removeSelectedText(int selNum)
 
1791
{
 
1792
    Q3TextCursor c1 = doc->selectionStartCursor(selNum);
 
1793
    c1.restoreState();
 
1794
    Q3TextCursor c2 = doc->selectionEndCursor(selNum);
 
1795
    c2.restoreState();
 
1796
 
 
1797
    // ### no support for editing tables yet, plus security for broken selections
 
1798
    if (c1.nestedDepth() || c2.nestedDepth())
 
1799
        return;
 
1800
 
 
1801
    for (int i = 0; i < (int)doc->numSelections(); ++i) {
 
1802
        if (i == selNum)
 
1803
            continue;
 
1804
        doc->removeSelection(i);
 
1805
    }
 
1806
 
 
1807
    drawCursor(false);
 
1808
    if (undoEnabled) {
 
1809
        checkUndoRedoInfo(UndoRedoInfo::RemoveSelected);
 
1810
        if (!undoRedoInfo.valid()) {
 
1811
            doc->selectionStart(selNum, undoRedoInfo.id, undoRedoInfo.index);
 
1812
            undoRedoInfo.d->text.clear();
 
1813
        }
 
1814
        readFormats(c1, c2, undoRedoInfo.d->text, true);
 
1815
    }
 
1816
 
 
1817
    doc->removeSelectedText(selNum, cursor);
 
1818
    if (cursor->isValid()) {
 
1819
        lastFormatted = 0; // make sync a noop
 
1820
        ensureCursorVisible();
 
1821
        lastFormatted = cursor->paragraph();
 
1822
        formatMore();
 
1823
        repaintContents();
 
1824
        ensureCursorVisible();
 
1825
        drawCursor(true);
 
1826
        clearUndoRedo();
 
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());
 
1832
#endif
 
1833
#ifndef QT_NO_CURSOR
 
1834
        viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
1835
#endif
 
1836
    } else {
 
1837
        delete cursor;
 
1838
        cursor = new Q3TextCursor(doc);
 
1839
        drawCursor(true);
 
1840
        repaintContents();
 
1841
    }
 
1842
    setModified();
 
1843
    emit textChanged();
 
1844
    emit selectionChanged();
 
1845
    emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
 
1846
}
 
1847
 
 
1848
/*!
 
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
 
1852
    should be selected.
 
1853
*/
 
1854
 
 
1855
void Q3TextEdit::moveCursor(CursorAction action, bool select)
 
1856
{
 
1857
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
1858
    if (d->optimMode)
 
1859
        return;
 
1860
#endif
 
1861
#ifdef Q_WS_MAC
 
1862
    Q3TextCursor c1 = *cursor;
 
1863
    Q3TextCursor c2;
 
1864
#endif
 
1865
    drawCursor(false);
 
1866
    if (select) {
 
1867
        if (!doc->hasSelection(Q3TextDocument::Standard))
 
1868
            doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
 
1869
        moveCursor(action);
 
1870
#ifdef Q_WS_MAC
 
1871
        c2 = *cursor;
 
1872
        if (c1 == c2)
 
1873
            if (action == MoveDown || action == MovePgDown)
 
1874
                moveCursor(MoveEnd);
 
1875
            else if (action == MoveUp || action == MovePgUp)
 
1876
                moveCursor(MoveHome);
 
1877
#endif
 
1878
        if (doc->setSelectionEnd(Q3TextDocument::Standard, *cursor)) {
 
1879
            cursor->paragraph()->document()->nextDoubleBuffered = true;
 
1880
            repaintChanged();
 
1881
        } else {
 
1882
            drawCursor(true);
 
1883
        }
 
1884
        ensureCursorVisible();
 
1885
        emit selectionChanged();
 
1886
        emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
 
1887
    } else {
 
1888
#ifdef Q_WS_MAC
 
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)
 
1893
            *cursor = cEnd;
 
1894
        else if (redraw && action == MoveUp)
 
1895
            *cursor = cStart;
 
1896
        if (redraw && action == MoveForward)
 
1897
            *cursor = cEnd;
 
1898
        else if (redraw && action == MoveBackward)
 
1899
            *cursor = cStart;
 
1900
        else
 
1901
            moveCursor(action);
 
1902
        c2 = *cursor;
 
1903
        if (c1 == c2)
 
1904
            if (action == MoveDown)
 
1905
                moveCursor(MoveEnd);
 
1906
            else if (action == MoveUp)
 
1907
                moveCursor(MoveHome);
 
1908
#else
 
1909
        bool redraw = doc->removeSelection(Q3TextDocument::Standard);
 
1910
        moveCursor(action);
 
1911
#endif
 
1912
        if (!redraw) {
 
1913
            ensureCursorVisible();
 
1914
            drawCursor(true);
 
1915
        } else {
 
1916
            cursor->paragraph()->document()->nextDoubleBuffered = true;
 
1917
            repaintChanged();
 
1918
            ensureCursorVisible();
 
1919
            drawCursor(true);
 
1920
#ifndef QT_NO_CURSOR
 
1921
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
1922
#endif
 
1923
        }
 
1924
        if (redraw) {
 
1925
            emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
 
1926
            emit selectionChanged();
 
1927
        }
 
1928
    }
 
1929
 
 
1930
    drawCursor(true);
 
1931
    updateCurrentFormat();
 
1932
}
 
1933
 
 
1934
/*!
 
1935
    \overload
 
1936
*/
 
1937
 
 
1938
void Q3TextEdit::moveCursor(CursorAction action)
 
1939
{
 
1940
    resetInputContext();
 
1941
    switch (action) {
 
1942
    case MoveBackward:
 
1943
        cursor->gotoPreviousLetter();
 
1944
        break;
 
1945
    case MoveWordBackward:
 
1946
        cursor->gotoPreviousWord();
 
1947
        break;
 
1948
    case MoveForward:
 
1949
        cursor->gotoNextLetter();
 
1950
        break;
 
1951
    case MoveWordForward:
 
1952
        cursor->gotoNextWord();
 
1953
        break;
 
1954
    case MoveUp:
 
1955
        cursor->gotoUp();
 
1956
        break;
 
1957
    case MovePgUp:
 
1958
        cursor->gotoPageUp(visibleHeight());
 
1959
        break;
 
1960
    case MoveDown:
 
1961
        cursor->gotoDown();
 
1962
        break;
 
1963
    case MovePgDown:
 
1964
        cursor->gotoPageDown(visibleHeight());
 
1965
        break;
 
1966
    case MoveLineStart:
 
1967
        cursor->gotoLineStart();
 
1968
        break;
 
1969
    case MoveHome:
 
1970
        cursor->gotoHome();
 
1971
        break;
 
1972
    case MoveLineEnd:
 
1973
        cursor->gotoLineEnd();
 
1974
        break;
 
1975
    case MoveEnd:
 
1976
        ensureFormatted(doc->lastParagraph());
 
1977
        cursor->gotoEnd();
 
1978
        break;
 
1979
    }
 
1980
    updateCurrentFormat();
 
1981
}
 
1982
 
 
1983
/*!
 
1984
    \reimp
 
1985
*/
 
1986
 
 
1987
void Q3TextEdit::resizeEvent(QResizeEvent *e)
 
1988
{
 
1989
    Q3ScrollView::resizeEvent(e);
 
1990
    if (doc->visibleWidth() == 0)
 
1991
        doResize();
 
1992
}
 
1993
 
 
1994
/*!
 
1995
    \reimp
 
1996
*/
 
1997
 
 
1998
void Q3TextEdit::viewportResizeEvent(QResizeEvent *e)
 
1999
{
 
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();
 
2004
        doResize();
 
2005
        if (stayAtBottom)
 
2006
            scrollToBottom();
 
2007
    }
 
2008
}
 
2009
 
 
2010
/*!
 
2011
    Ensures that the cursor is visible by scrolling the text edit if
 
2012
    necessary.
 
2013
 
 
2014
    \sa setCursorPosition()
 
2015
*/
 
2016
 
 
2017
void Q3TextEdit::ensureCursorVisible()
 
2018
{
 
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;
 
2022
        return;
 
2023
    }
 
2024
    sync();
 
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();
 
2031
    int w = 1;
 
2032
    ensureVisible(x, y + h / 2, w, h / 2 + 2);
 
2033
}
 
2034
 
 
2035
/*!
 
2036
    \internal
 
2037
*/
 
2038
void Q3TextEdit::sliderReleased()
 
2039
{
 
2040
    if (d->ensureCursorVisibleInShowEvent && isVisible()) {
 
2041
        d->ensureCursorVisibleInShowEvent = false;
 
2042
        ensureCursorVisible();
 
2043
    }
 
2044
}
 
2045
 
 
2046
/*!
 
2047
    \internal
 
2048
 
 
2049
    If \a visible is true, the cursor is shown; otherwise it is
 
2050
    hidden.
 
2051
*/
 
2052
void Q3TextEdit::drawCursor(bool visible)
 
2053
{
 
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);
 
2060
    }
 
2061
    r.moveBy(-contentsX(), -contentsY());
 
2062
    viewport()->repaint(r);
 
2063
}
 
2064
 
 
2065
enum {
 
2066
    IdUndo = 0,
 
2067
    IdRedo = 1,
 
2068
    IdCut = 2,
 
2069
    IdCopy = 3,
 
2070
    IdPaste = 4,
 
2071
    IdClear = 5,
 
2072
    IdSelectAll = 6
 
2073
};
 
2074
 
 
2075
/*!
 
2076
    \reimp
 
2077
*/
 
2078
#ifndef QT_NO_WHEELEVENT
 
2079
void Q3TextEdit::contentsWheelEvent(QWheelEvent *e)
 
2080
{
 
2081
    if (isReadOnly()) {
 
2082
        if (e->state() & Qt::ControlButton) {
 
2083
            if (e->delta() > 0)
 
2084
                zoomOut();
 
2085
            else if (e->delta() < 0)
 
2086
                zoomIn();
 
2087
            return;
 
2088
        }
 
2089
    }
 
2090
    Q3ScrollView::contentsWheelEvent(e);
 
2091
}
 
2092
#endif
 
2093
 
 
2094
/*!
 
2095
    \reimp
 
2096
*/
 
2097
 
 
2098
void Q3TextEdit::contentsMousePressEvent(QMouseEvent *e)
 
2099
{
 
2100
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2101
    if (d->optimMode) {
 
2102
        optimMousePressEvent(e);
 
2103
        return;
 
2104
    }
 
2105
#endif
 
2106
 
 
2107
    if (d->trippleClickTimer->isActive() &&
 
2108
         (e->globalPos() - d->trippleClickPoint).manhattanLength() <
 
2109
         QApplication::startDragDistance()) {
 
2110
        Q3TextCursor c1 = *cursor;
 
2111
        Q3TextCursor c2 = *cursor;
 
2112
        c1.gotoLineStart();
 
2113
        c2.gotoLineEnd();
 
2114
        doc->setSelectionStart(Q3TextDocument::Standard, c1);
 
2115
        doc->setSelectionEnd(Q3TextDocument::Standard, c2);
 
2116
        *cursor = c2;
 
2117
        repaintChanged();
 
2118
        mousePressed = true;
 
2119
        return;
 
2120
    }
 
2121
 
 
2122
    clearUndoRedo();
 
2123
    Q3TextCursor oldCursor = *cursor;
 
2124
    Q3TextCursor c = *cursor;
 
2125
    mousePos = e->pos();
 
2126
    mightStartDrag = false;
 
2127
    pressedLink.clear();
 
2128
    d->pressedName.clear();
 
2129
 
 
2130
    if (e->button() == Qt::LeftButton) {
 
2131
        mousePressed = true;
 
2132
        drawCursor(false);
 
2133
        placeCursor(e->pos());
 
2134
        ensureCursorVisible();
 
2135
 
 
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();
 
2143
            }
 
2144
        }
 
2145
 
 
2146
#ifndef QT_NO_DRAGANDDROP
 
2147
        if (doc->inSelection(Q3TextDocument::Standard, e->pos())) {
 
2148
            mightStartDrag = true;
 
2149
            drawCursor(true);
 
2150
            dragStartTimer->start(QApplication::startDragTime(), true);
 
2151
            dragStartPos = e->pos();
 
2152
            return;
 
2153
        }
 
2154
#endif
 
2155
 
 
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);
 
2161
            } else {
 
2162
                redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
 
2163
            }
 
2164
        } else {
 
2165
            if (isReadOnly() || !(e->state() & Qt::ShiftButton)) {
 
2166
                doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
 
2167
            } else {
 
2168
                doc->setSelectionStart(Q3TextDocument::Standard, c);
 
2169
                redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
 
2170
            }
 
2171
        }
 
2172
 
 
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;
 
2175
 
 
2176
        if (!redraw) {
 
2177
            drawCursor(true);
 
2178
        } else {
 
2179
            repaintChanged();
 
2180
#ifndef QT_NO_CURSOR
 
2181
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
2182
#endif
 
2183
        }
 
2184
    } else if (e->button() == Qt::MidButton) {
 
2185
        bool redraw = doc->removeSelection(Q3TextDocument::Standard);
 
2186
        if (!redraw) {
 
2187
            drawCursor(true);
 
2188
        } else {
 
2189
            repaintChanged();
 
2190
#ifndef QT_NO_CURSOR
 
2191
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
2192
#endif
 
2193
        }
 
2194
    }
 
2195
 
 
2196
    if (*cursor != oldCursor)
 
2197
        updateCurrentFormat();
 
2198
}
 
2199
 
 
2200
/*!
 
2201
    \reimp
 
2202
*/
 
2203
 
 
2204
void Q3TextEdit::contentsMouseMoveEvent(QMouseEvent *e)
 
2205
{
 
2206
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2207
    if (d->optimMode) {
 
2208
        optimMouseMoveEvent(e);
 
2209
        return;
 
2210
    }
 
2211
#endif
 
2212
    if (mousePressed) {
 
2213
#ifndef QT_NO_DRAGANDDROP
 
2214
        if (mightStartDrag) {
 
2215
            dragStartTimer->stop();
 
2216
            if ((e->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance())
 
2217
                startDrag();
 
2218
#ifndef QT_NO_CURSOR
 
2219
            if (!isReadOnly())
 
2220
                viewport()->setCursor(Qt::IBeamCursor);
 
2221
#endif
 
2222
            return;
 
2223
        }
 
2224
#endif
 
2225
        mousePos = e->pos();
 
2226
        handleMouseMove(mousePos);
 
2227
        oldMousePos = mousePos;
 
2228
    }
 
2229
 
 
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);
 
2234
        else
 
2235
            viewport()->setCursor(Qt::IBeamCursor);
 
2236
    }
 
2237
#endif
 
2238
    updateCursor(e->pos());
 
2239
}
 
2240
 
 
2241
void Q3TextEdit::copyToClipboard()
 
2242
{
 
2243
#ifndef QT_NO_CLIPBOARD
 
2244
    if (QApplication::clipboard()->supportsSelection()) {
 
2245
        d->clipboard_mode = QClipboard::Selection;
 
2246
 
 
2247
        // don't listen to selection changes
 
2248
        disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
 
2249
        copy();
 
2250
        // listen to selection changes
 
2251
        connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
 
2252
                 this, SLOT(clipboardChanged()));
 
2253
 
 
2254
        d->clipboard_mode = QClipboard::Clipboard;
 
2255
    }
 
2256
#endif
 
2257
}
 
2258
 
 
2259
/*!
 
2260
    \reimp
 
2261
*/
 
2262
 
 
2263
void Q3TextEdit::contentsMouseReleaseEvent(QMouseEvent * e)
 
2264
{
 
2265
    if (!inDoubleClick) { // could be the release of a dblclick
 
2266
        int para = 0;
 
2267
        int index = charAt(e->pos(), &para);
 
2268
        emit clicked(para, index);
 
2269
    }
 
2270
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2271
    if (d->optimMode) {
 
2272
        optimMouseReleaseEvent(e);
 
2273
        return;
 
2274
    }
 
2275
#endif
 
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) {
 
2283
        selectAll(false);
 
2284
        mousePressed = false;
 
2285
    }
 
2286
#endif
 
2287
    if (mousePressed) {
 
2288
        mousePressed = false;
 
2289
        copyToClipboard();
 
2290
    }
 
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()) {
 
2295
            drawCursor(false);
 
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);
 
2303
            } else {
 
2304
                doc->setSelectionStart(Q3TextDocument::Standard, *cursor);
 
2305
            }
 
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;
 
2309
            if (!redraw) {
 
2310
                drawCursor(true);
 
2311
            } else {
 
2312
                repaintChanged();
 
2313
#ifndef QT_NO_CURSOR
 
2314
                viewport()->setCursor(Qt::IBeamCursor);
 
2315
#endif
 
2316
            }
 
2317
            d->clipboard_mode = QClipboard::Selection;
 
2318
            paste();
 
2319
            d->clipboard_mode = QClipboard::Clipboard;
 
2320
        }
 
2321
    }
 
2322
#endif
 
2323
    emit cursorPositionChanged(cursor);
 
2324
    emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
2325
    if (oldCursor != *cursor)
 
2326
        updateCurrentFormat();
 
2327
    inDoubleClick = false;
 
2328
 
 
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));
 
2336
        }
 
2337
        if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
 
2338
            emit browser->anchorClicked(d->onName, onLink);
 
2339
 
 
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());
 
2344
    }
 
2345
#endif
 
2346
    drawCursor(true);
 
2347
    if (!doc->hasSelection(Q3TextDocument::Standard, true))
 
2348
        doc->removeSelection(Q3TextDocument::Standard);
 
2349
 
 
2350
    emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
 
2351
    emit selectionChanged();
 
2352
}
 
2353
 
 
2354
/*!
 
2355
    \reimp
 
2356
*/
 
2357
 
 
2358
void Q3TextEdit::contentsMouseDoubleClickEvent(QMouseEvent * e)
 
2359
{
 
2360
    if (e->button() != Qt::LeftButton) {
 
2361
        e->ignore();
 
2362
        return;
 
2363
    }
 
2364
    int para = 0;
 
2365
    int index = charAt(e->pos(), &para);
 
2366
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2367
    if (d->optimMode) {
 
2368
        QString str = d->od->lines[LOGOFFSET(para)];
 
2369
        int startIdx = index, endIdx = index, i;
 
2370
        if (!str[index].isSpace()) {
 
2371
            i = startIdx;
 
2372
            // find start of word
 
2373
            while (i >= 0 && !str[i].isSpace()) {
 
2374
                startIdx = i--;
 
2375
            }
 
2376
            i = endIdx;
 
2377
            // find end of word..
 
2378
            while (i < str.length() && !str[i].isSpace()) {
 
2379
                endIdx = ++i;
 
2380
            }
 
2381
            // ..and start of next
 
2382
            while (i < str.length() && str[i].isSpace()) {
 
2383
                endIdx = ++i;
 
2384
            }
 
2385
            optimSetSelection(para, startIdx, para, endIdx);
 
2386
            repaintContents();
 
2387
        }
 
2388
    } else
 
2389
#endif
 
2390
    {
 
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() &&
 
2401
                        !c2.atParagEnd())
 
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() &&
 
2408
                        !c2.atParagEnd())
 
2409
                    c2.gotoNextLetter();
 
2410
            } else if (!c2.atParagEnd()) {
 
2411
                c2.gotoNextLetter();
 
2412
            }
 
2413
        }
 
2414
#else
 
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())
 
2418
            c2.gotoNextWord();
 
2419
#endif
 
2420
        doc->setSelectionStart(Q3TextDocument::Standard, c1);
 
2421
        doc->setSelectionEnd(Q3TextDocument::Standard, c2);
 
2422
 
 
2423
        *cursor = c2;
 
2424
 
 
2425
        repaintChanged();
 
2426
 
 
2427
        d->trippleClickTimer->start(qApp->doubleClickInterval(), true);
 
2428
        d->trippleClickPoint = e->globalPos();
 
2429
    }
 
2430
    inDoubleClick = true;
 
2431
    mousePressed = true;
 
2432
    emit doubleClicked(para, index);
 
2433
}
 
2434
 
 
2435
#ifndef QT_NO_DRAGANDDROP
 
2436
 
 
2437
/*!
 
2438
    \reimp
 
2439
*/
 
2440
 
 
2441
void Q3TextEdit::contentsDragEnterEvent(QDragEnterEvent *e)
 
2442
{
 
2443
    if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
 
2444
        e->ignore();
 
2445
        return;
 
2446
    }
 
2447
    e->acceptAction();
 
2448
    inDnD = true;
 
2449
}
 
2450
 
 
2451
/*!
 
2452
    \reimp
 
2453
*/
 
2454
 
 
2455
void Q3TextEdit::contentsDragMoveEvent(QDragMoveEvent *e)
 
2456
{
 
2457
    if (isReadOnly() || !Q3TextDrag::canDecode(e)) {
 
2458
        e->ignore();
 
2459
        return;
 
2460
    }
 
2461
    drawCursor(false);
 
2462
    placeCursor(e->pos(),  cursor);
 
2463
    drawCursor(true);
 
2464
    e->acceptAction();
 
2465
}
 
2466
 
 
2467
/*!
 
2468
    \reimp
 
2469
*/
 
2470
 
 
2471
void Q3TextEdit::contentsDragLeaveEvent(QDragLeaveEvent *)
 
2472
{
 
2473
    drawCursor(false);
 
2474
    inDnD = false;
 
2475
}
 
2476
 
 
2477
/*!
 
2478
    \reimp
 
2479
*/
 
2480
 
 
2481
void Q3TextEdit::contentsDropEvent(QDropEvent *e)
 
2482
{
 
2483
    if (isReadOnly())
 
2484
        return;
 
2485
    inDnD = false;
 
2486
    e->acceptAction();
 
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);
 
2500
            c1.restoreState();
 
2501
            c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
 
2502
            c2.restoreState();
 
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)))
 
2511
                insertCursor = c1;
 
2512
            if (dropId == selEndId && dropIndex > selEndIndex) {
 
2513
                insertCursor = c1;
 
2514
                if (selStartId == selEndId) {
 
2515
                    insertCursor.setIndex(dropIndex -
 
2516
                                           (selEndIndex - selStartIndex));
 
2517
                } else {
 
2518
                    insertCursor.setIndex(dropIndex - selEndIndex +
 
2519
                                           selStartIndex);
 
2520
                }
 
2521
            }
 
2522
         }
 
2523
 
 
2524
        if (internalDrag && e->action() == QDropEvent::Move) {
 
2525
            removeSelectedText();
 
2526
            intern = true;
 
2527
            doc->removeSelection(Q3TextDocument::Standard);
 
2528
        } else {
 
2529
            doc->removeSelection(Q3TextDocument::Standard);
 
2530
#ifndef QT_NO_CURSOR
 
2531
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
2532
#endif
 
2533
        }
 
2534
        drawCursor(false);
 
2535
        cursor->setParagraph(insertCursor.paragraph());
 
2536
        cursor->setIndex(insertCursor.index());
 
2537
        drawCursor(true);
 
2538
        if (!cursor->nestedDepth()) {
 
2539
            QString subType = "plain";
 
2540
            if (textFormat() != Qt::PlainText) {
 
2541
                if (e->provides("application/x-qrichtext"))
 
2542
                    subType = "x-qrichtext";
 
2543
            }
 
2544
#ifndef QT_NO_CLIPBOARD
 
2545
            pasteSubType(subType.toLatin1(), e);
 
2546
#endif
 
2547
            // emit appropriate signals.
 
2548
            emit selectionChanged();
 
2549
            emit cursorPositionChanged(cursor);
 
2550
            emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
2551
        } else {
 
2552
            if (intern)
 
2553
                undo();
 
2554
            e->ignore();
 
2555
        }
 
2556
    }
 
2557
}
 
2558
 
 
2559
#endif
 
2560
 
 
2561
/*!
 
2562
    \reimp
 
2563
*/
 
2564
void Q3TextEdit::contentsContextMenuEvent(QContextMenuEvent *e)
 
2565
{
 
2566
    clearUndoRedo();
 
2567
    mousePressed = false;
 
2568
 
 
2569
    e->accept();
 
2570
#ifndef QT_NO_POPUPMENU
 
2571
    Q3PopupMenu *popup = createPopupMenu(e->pos());
 
2572
    if (!popup)
 
2573
        popup = createPopupMenu();
 
2574
    if (!popup)
 
2575
        return;
 
2576
    int r = popup->exec(e->globalPos(), -1);
 
2577
    delete popup;
 
2578
 
 
2579
    if (r == d->id[IdClear])
 
2580
        clear();
 
2581
    else if (r == d->id[IdSelectAll]) {
 
2582
        selectAll();
 
2583
#ifndef QT_NO_CLIPBOARD
 
2584
        // if the clipboard support selections, put the newly selected text into
 
2585
        // the clipboard
 
2586
        if (QApplication::clipboard()->supportsSelection()) {
 
2587
            d->clipboard_mode = QClipboard::Selection;
 
2588
 
 
2589
            // don't listen to selection changes
 
2590
            disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
 
2591
            copy();
 
2592
            // listen to selection changes
 
2593
            connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
 
2594
                     this, SLOT(clipboardChanged()));
 
2595
 
 
2596
            d->clipboard_mode = QClipboard::Clipboard;
 
2597
        }
 
2598
#endif
 
2599
    } else if (r == d->id[IdUndo])
 
2600
        undo();
 
2601
    else if (r == d->id[IdRedo])
 
2602
        redo();
 
2603
#ifndef QT_NO_CLIPBOARD
 
2604
    else if (r == d->id[IdCut])
 
2605
        cut();
 
2606
    else if (r == d->id[IdCopy])
 
2607
        copy();
 
2608
    else if (r == d->id[IdPaste])
 
2609
        paste();
 
2610
#endif
 
2611
#endif
 
2612
}
 
2613
 
 
2614
 
 
2615
void Q3TextEdit::autoScrollTimerDone()
 
2616
{
 
2617
    if (mousePressed)
 
2618
        handleMouseMove( viewportToContents(viewport()->mapFromGlobal(QCursor::pos()) ));
 
2619
}
 
2620
 
 
2621
void Q3TextEdit::handleMouseMove(const QPoint& pos)
 
2622
{
 
2623
    if (!mousePressed)
 
2624
        return;
 
2625
 
 
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();
 
2630
 
 
2631
    drawCursor(false);
 
2632
    Q3TextCursor oldCursor = *cursor;
 
2633
 
 
2634
    placeCursor(pos);
 
2635
 
 
2636
    if (inDoubleClick) {
 
2637
        Q3TextCursor cl = *cursor;
 
2638
        cl.gotoPreviousWord();
 
2639
        Q3TextCursor cr = *cursor;
 
2640
        cr.gotoNextWord();
 
2641
 
 
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());
 
2645
 
 
2646
 
 
2647
        if (cursor->paragraph()->lineStartOfChar(cursor->index()) !=
 
2648
             oldCursor.paragraph()->lineStartOfChar(oldCursor.index()))
 
2649
            diff = 0xFFFFFF;
 
2650
 
 
2651
        if (rdiff < diff && rdiff < ldiff)
 
2652
            *cursor = cr;
 
2653
        else if (ldiff < diff && ldiff < rdiff)
 
2654
            *cursor = cl;
 
2655
        else
 
2656
            *cursor = oldCursor;
 
2657
 
 
2658
    }
 
2659
    ensureCursorVisible();
 
2660
 
 
2661
    bool redraw = false;
 
2662
    if (doc->hasSelection(Q3TextDocument::Standard)) {
 
2663
        redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw;
 
2664
    }
 
2665
 
 
2666
    if (!redraw) {
 
2667
        drawCursor(true);
 
2668
    } else {
 
2669
        repaintChanged();
 
2670
        drawCursor(true);
 
2671
    }
 
2672
 
 
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());
 
2679
        }
 
2680
        emit currentFontChanged(currentFormat->font());
 
2681
        emit currentColorChanged(currentFormat->color());
 
2682
        emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
 
2683
    }
 
2684
 
 
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;
 
2690
    }
 
2691
}
 
2692
 
 
2693
/*! \internal */
 
2694
 
 
2695
void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c, bool link)
 
2696
{
 
2697
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2698
    if (d->optimMode)
 
2699
        return;
 
2700
#endif
 
2701
    if (!c)
 
2702
        c = cursor;
 
2703
 
 
2704
    resetInputContext();
 
2705
    c->restoreState();
 
2706
    Q3TextParagraph *s = doc->firstParagraph();
 
2707
    c->place(pos, s, link);
 
2708
}
 
2709
 
 
2710
 
 
2711
QVariant Q3TextEdit::inputMethodQuery(Qt::InputMethodQuery query) const
 
2712
{
 
2713
    Q3TextCursor c(*cursor);
 
2714
    if (d->preeditStart != -1)
 
2715
        c.setIndex(d->preeditStart);
 
2716
 
 
2717
    switch(query) {
 
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);
 
2722
    }
 
2723
    case Qt::ImFont:
 
2724
            return c.paragraph()->at(c.index())->format()->font();
 
2725
    default:
 
2726
    // ##### fix the others!
 
2727
        return QWidget::inputMethodQuery(query);
 
2728
    }
 
2729
}
 
2730
 
 
2731
 
 
2732
 
 
2733
void Q3TextEdit::formatMore()
 
2734
{
 
2735
    if (!lastFormatted)
 
2736
        return;
 
2737
 
 
2738
    int bottom = contentsHeight();
 
2739
    int lastTop = -1;
 
2740
    int lastBottom = -1;
 
2741
    int to = 20;
 
2742
    bool firstVisible = false;
 
2743
    QRect cr(contentsX(), contentsY(), visibleWidth(), visibleHeight());
 
2744
    for (int i = 0; lastFormatted &&
 
2745
          (i < to || (firstVisible && lastTop < contentsY()+height()));
 
2746
          i++) {
 
2747
        lastFormatted->format();
 
2748
        lastTop = lastFormatted->rect().top();
 
2749
        lastBottom = lastFormatted->rect().bottom();
 
2750
        if (i == 0)
 
2751
            firstVisible = lastBottom < cr.bottom();
 
2752
        bottom = qMax(bottom, lastBottom);
 
2753
        lastFormatted = lastFormatted->next();
 
2754
    }
 
2755
 
 
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());
 
2763
    }
 
2764
 
 
2765
    if (lastFormatted)
 
2766
        formatTimer->start(interval, true);
 
2767
    else
 
2768
        interval = qMax(0, interval);
 
2769
}
 
2770
 
 
2771
void Q3TextEdit::doResize()
 
2772
{
 
2773
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2774
    if (!d->optimMode)
 
2775
#endif
 
2776
    {
 
2777
        if (wrapMode == FixedPixelWidth)
 
2778
            return;
 
2779
        doc->setMinimumWidth(-1);
 
2780
        resizeContents(0, 0);
 
2781
        doc->setWidth(visibleWidth());
 
2782
        doc->invalidate();
 
2783
        lastFormatted = doc->firstParagraph();
 
2784
        interval = 0;
 
2785
        formatMore();
 
2786
    }
 
2787
    repaintContents();
 
2788
}
 
2789
 
 
2790
/*! \internal */
 
2791
 
 
2792
void Q3TextEdit::doChangeInterval()
 
2793
{
 
2794
    interval = 0;
 
2795
}
 
2796
 
 
2797
/*!
 
2798
    \reimp
 
2799
*/
 
2800
 
 
2801
bool Q3TextEdit::eventFilter(QObject *o, QEvent *e)
 
2802
{
 
2803
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2804
    if (!d->optimMode && (o == this || o == viewport())) {
 
2805
#else
 
2806
    if (o == this || o == viewport()) {
 
2807
#endif
 
2808
        if (d->cursorBlinkActive && e->type() == QEvent::FocusIn) {
 
2809
            if (QApplication::cursorFlashTime() > 0)
 
2810
                blinkTimer->start(QApplication::cursorFlashTime() / 2);
 
2811
            drawCursor(true);
 
2812
        } else if (e->type() == QEvent::FocusOut) {
 
2813
            blinkTimer->stop();
 
2814
            drawCursor(false);
 
2815
        }
 
2816
    }
 
2817
 
 
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();
 
2825
            formatMore();
 
2826
            repaintChanged();
 
2827
        }
 
2828
    }
 
2829
 
 
2830
    return Q3ScrollView::eventFilter(o, e);
 
2831
}
 
2832
 
 
2833
/*!
 
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
 
2838
    selected text.
 
2839
 */
 
2840
void Q3TextEdit::insert(const QString &text, bool indent,
 
2841
                        bool checkNewLine, bool removeSelected)
 
2842
{
 
2843
    uint f = 0;
 
2844
    if (indent)
 
2845
        f |= RedoIndentation;
 
2846
    if (checkNewLine)
 
2847
        f |= CheckNewLines;
 
2848
    if (removeSelected)
 
2849
        f |= RemoveSelected;
 
2850
    insert(text, f);
 
2851
}
 
2852
 
 
2853
/*!
 
2854
    Inserts \a text at the current cursor position.
 
2855
 
 
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.
 
2865
 
 
2866
    The default flags are \c CheckNewLines | \c RemoveSelected.
 
2867
 
 
2868
    If the widget is in \c Qt::LogText mode this function will do nothing.
 
2869
 
 
2870
    \sa paste() pasteSubType()
 
2871
*/
 
2872
 
 
2873
 
 
2874
void Q3TextEdit::insert(const QString &text, uint insertionFlags)
 
2875
{
 
2876
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2877
    if (d->optimMode)
 
2878
        return;
 
2879
#endif
 
2880
 
 
2881
    if (cursor->nestedDepth() != 0) // #### for 3.0, disable editing of tables as this is not advanced enough
 
2882
        return;
 
2883
 
 
2884
    bool indent = insertionFlags & RedoIndentation;
 
2885
    bool checkNewLine = insertionFlags & CheckNewLines;
 
2886
    bool removeSelected = insertionFlags & RemoveSelected;
 
2887
    QString txt(text);
 
2888
    drawCursor(false);
 
2889
    if (!isReadOnly() && doc->hasSelection(Q3TextDocument::Standard) && removeSelected)
 
2890
        removeSelectedText();
 
2891
    Q3TextCursor c2 = *cursor;
 
2892
    int oldLen = 0;
 
2893
 
 
2894
    if (undoEnabled && !isReadOnly()) {
 
2895
        checkUndoRedoInfo(UndoRedoInfo::Insert);
 
2896
 
 
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()) {
 
2900
            clearUndoRedo();        
 
2901
            undoRedoInfo.type = UndoRedoInfo::Insert;
 
2902
        }
 
2903
               
 
2904
        if (!undoRedoInfo.valid()) {            
 
2905
            undoRedoInfo.id = cursor->paragraph()->paragId();
 
2906
            undoRedoInfo.index = cursor->index();
 
2907
            undoRedoInfo.d->text.clear();
 
2908
        } 
 
2909
        oldLen = undoRedoInfo.d->text.length();
 
2910
    }
 
2911
 
 
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);
 
2921
    }
 
2922
 
 
2923
    if (indent && (txt == "{" || txt == "}" || txt == ":" || txt == "#"))
 
2924
        cursor->indent();
 
2925
    formatMore();
 
2926
    repaintChanged();
 
2927
    ensureCursorVisible();
 
2928
    drawCursor(true);
 
2929
 
 
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);
 
2939
                }
 
2940
                c2.gotoNextLetter();
 
2941
            }
 
2942
        }
 
2943
    }
 
2944
 
 
2945
    if (!removeSelected) {
 
2946
        doc->setSelectionStart(Q3TextDocument::Standard, oldCursor);
 
2947
        doc->setSelectionEnd(Q3TextDocument::Standard, *cursor);
 
2948
        repaintChanged();
 
2949
    }
 
2950
 
 
2951
    setModified();
 
2952
    emit textChanged();
 
2953
}
 
2954
 
 
2955
/*!
 
2956
    Inserts \a text in the paragraph \a para at position \a index.
 
2957
*/
 
2958
 
 
2959
void Q3TextEdit::insertAt(const QString &text, int para, int index)
 
2960
{
 
2961
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2962
    if (d->optimMode) {
 
2963
        optimInsert(text, para, index);
 
2964
        return;
 
2965
    }
 
2966
#endif
 
2967
    Q3TextParagraph *p = doc->paragAt(para);
 
2968
    if (!p)
 
2969
        return;
 
2970
    removeSelection(Q3TextDocument::Standard);
 
2971
    Q3TextCursor tmp = *cursor;
 
2972
    cursor->setParagraph(p);
 
2973
    cursor->setIndex(index);
 
2974
    insert(text, false, true, false);
 
2975
    *cursor = tmp;
 
2976
    removeSelection(Q3TextDocument::Standard);
 
2977
}
 
2978
 
 
2979
/*!
 
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.
 
2983
*/
 
2984
 
 
2985
void Q3TextEdit::insertParagraph(const QString &text, int para)
 
2986
{
 
2987
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
2988
    if (d->optimMode) {
 
2989
        optimInsert(text + "\n", para, 0);
 
2990
        return;
 
2991
    }
 
2992
#endif
 
2993
    for (int i = 0; i < (int)doc->numSelections(); ++i)
 
2994
        doc->removeSelection(i);
 
2995
 
 
2996
    Q3TextParagraph *p = doc->paragAt(para);
 
2997
 
 
2998
    bool append = !p;
 
2999
    if (!p)
 
3000
        p = doc->lastParagraph();
 
3001
 
 
3002
    Q3TextCursor old = *cursor;
 
3003
    drawCursor(false);
 
3004
 
 
3005
    cursor->setParagraph(p);
 
3006
    cursor->setIndex(0);
 
3007
    clearUndoRedo();
 
3008
    qtextedit_ignore_readonly = true;
 
3009
    if (append && cursor->paragraph()->length() > 1) {
 
3010
        cursor->setIndex(cursor->paragraph()->length() - 1);
 
3011
        doKeyboardAction(ActionReturn);
 
3012
    }
 
3013
    insert(text, false, true, true);
 
3014
    doKeyboardAction(ActionReturn);
 
3015
    qtextedit_ignore_readonly = false;
 
3016
 
 
3017
    drawCursor(false);
 
3018
    *cursor = old;
 
3019
    drawCursor(true);
 
3020
 
 
3021
    repaintChanged();
 
3022
}
 
3023
 
 
3024
/*!
 
3025
    Removes the paragraph \a para.
 
3026
*/
 
3027
 
 
3028
void Q3TextEdit::removeParagraph(int para)
 
3029
{
 
3030
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3031
    if (d->optimMode)
 
3032
        return;
 
3033
#endif
 
3034
    Q3TextParagraph *p = doc->paragAt(para);
 
3035
    if (!p)
 
3036
        return;
 
3037
 
 
3038
    for (int i = 0; i < doc->numSelections(); ++i)
 
3039
        doc->removeSelection(i);
 
3040
 
 
3041
    Q3TextCursor start(doc);
 
3042
    Q3TextCursor end(doc);
 
3043
    start.setParagraph(p);
 
3044
    start.setIndex(0);
 
3045
    end.setParagraph(p);
 
3046
    end.setIndex(p->length() - 1);
 
3047
 
 
3048
    if (!(p == doc->firstParagraph() && p == doc->lastParagraph())) {
 
3049
        if (p->next()) {
 
3050
            end.setParagraph(p->next());
 
3051
            end.setIndex(0);
 
3052
        } else if (p->prev()) {
 
3053
            start.setParagraph(p->prev());
 
3054
            start.setIndex(p->prev()->length() - 1);
 
3055
        }
 
3056
    }
 
3057
 
 
3058
    doc->setSelectionStart(Q3TextDocument::Temp, start);
 
3059
    doc->setSelectionEnd(Q3TextDocument::Temp, end);
 
3060
    removeSelectedText(Q3TextDocument::Temp);
 
3061
}
 
3062
 
 
3063
/*!
 
3064
    Undoes the last operation.
 
3065
 
 
3066
    If there is no operation to undo, i.e. there is no undo step in
 
3067
    the undo/redo history, nothing happens.
 
3068
 
 
3069
    \sa undoAvailable() redo() undoDepth()
 
3070
*/
 
3071
 
 
3072
void Q3TextEdit::undo()
 
3073
{
 
3074
    clearUndoRedo();
 
3075
    if (isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled)
 
3076
        return;
 
3077
 
 
3078
    for (int i = 0; i < (int)doc->numSelections(); ++i)
 
3079
        doc->removeSelection(i);
 
3080
 
 
3081
#ifndef QT_NO_CURSOR
 
3082
    viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
3083
#endif
 
3084
 
 
3085
    clearUndoRedo();
 
3086
    drawCursor(false);
 
3087
    Q3TextCursor *c = doc->undo(cursor);
 
3088
    if (!c) {
 
3089
        drawCursor(true);
 
3090
        return;
 
3091
    }
 
3092
    lastFormatted = 0;
 
3093
    repaintChanged();
 
3094
    ensureCursorVisible();
 
3095
    drawCursor(true);
 
3096
    setModified();
 
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()) {
 
3103
        delete cursor;
 
3104
        cursor = new Q3TextCursor(doc);
 
3105
        drawCursor(true);
 
3106
        repaintContents();
 
3107
    }
 
3108
    emit undoAvailable(isUndoAvailable());
 
3109
    emit redoAvailable(isRedoAvailable());
 
3110
    emit textChanged();
 
3111
}
 
3112
 
 
3113
/*!
 
3114
    Redoes the last operation.
 
3115
 
 
3116
    If there is no operation to redo, i.e. there is no redo step in
 
3117
    the undo/redo history, nothing happens.
 
3118
 
 
3119
    \sa redoAvailable() undo() undoDepth()
 
3120
*/
 
3121
 
 
3122
void Q3TextEdit::redo()
 
3123
{
 
3124
    if (isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled)
 
3125
        return;
 
3126
 
 
3127
    for (int i = 0; i < (int)doc->numSelections(); ++i)
 
3128
        doc->removeSelection(i);
 
3129
 
 
3130
#ifndef QT_NO_CURSOR
 
3131
    viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
3132
#endif
 
3133
 
 
3134
    clearUndoRedo();
 
3135
    drawCursor(false);
 
3136
    Q3TextCursor *c = doc->redo(cursor);
 
3137
    if (!c) {
 
3138
        drawCursor(true);
 
3139
        return;
 
3140
    }
 
3141
    lastFormatted = 0;
 
3142
    ensureCursorVisible();
 
3143
    repaintChanged();
 
3144
    ensureCursorVisible();
 
3145
    drawCursor(true);
 
3146
    setModified();
 
3147
    emit undoAvailable(isUndoAvailable());
 
3148
    emit redoAvailable(isRedoAvailable());
 
3149
    emit textChanged();
 
3150
}
 
3151
 
 
3152
/*!
 
3153
    Pastes the text from the clipboard into the text edit at the
 
3154
    current cursor position. Only plain text is pasted.
 
3155
 
 
3156
    If there is no text in the clipboard nothing happens.
 
3157
 
 
3158
    \sa pasteSubType() cut() Q3TextEdit::copy()
 
3159
*/
 
3160
 
 
3161
void Q3TextEdit::paste()
 
3162
{
 
3163
#ifndef QT_NO_MIMECLIPBOARD
 
3164
    if (isReadOnly())
 
3165
        return;
 
3166
    QString subType = "plain";
 
3167
    if (textFormat() != Qt::PlainText) {
 
3168
        QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
 
3169
        if (!m)
 
3170
            return;
 
3171
        if (m->provides("application/x-qrichtext"))
 
3172
            subType = "x-qrichtext";
 
3173
    }
 
3174
 
 
3175
    pasteSubType(subType.toLatin1());
 
3176
#endif
 
3177
}
 
3178
 
 
3179
void Q3TextEdit::checkUndoRedoInfo(UndoRedoInfo::Type t)
 
3180
{
 
3181
    if (undoRedoInfo.valid() && t != undoRedoInfo.type) {
 
3182
        clearUndoRedo();
 
3183
    }
 
3184
    undoRedoInfo.type = t;
 
3185
}
 
3186
 
 
3187
/*!
 
3188
    Repaints any paragraphs that have changed.
 
3189
 
 
3190
    Although used extensively internally you shouldn't need to call
 
3191
    this yourself.
 
3192
*/
 
3193
void Q3TextEdit::repaintChanged()
 
3194
{
 
3195
    if (!updatesEnabled() || !viewport()->updatesEnabled())
 
3196
        return;
 
3197
 
 
3198
    updateContents(); // good enough until this class is rewritten
 
3199
}
 
3200
 
 
3201
#ifndef QT_NO_MIME
 
3202
Q3TextDrag *Q3TextEdit::dragObject(QWidget *parent) const
 
3203
{
 
3204
    if (!doc->hasSelection(Q3TextDocument::Standard) ||
 
3205
         doc->selectedText(Q3TextDocument::Standard).isEmpty())
 
3206
        return 0;
 
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));
 
3212
    return drag;
 
3213
}
 
3214
#endif
 
3215
 
 
3216
/*!
 
3217
    Copies the selected text (from selection 0) to the clipboard and
 
3218
    deletes it from the text edit.
 
3219
 
 
3220
    If there is no selected text (in selection 0) nothing happens.
 
3221
 
 
3222
    \sa Q3TextEdit::copy() paste() pasteSubType()
 
3223
*/
 
3224
 
 
3225
void Q3TextEdit::cut()
 
3226
{
 
3227
    if (isReadOnly())
 
3228
        return;
 
3229
    normalCopy();
 
3230
    removeSelectedText();
 
3231
}
 
3232
 
 
3233
void Q3TextEdit::normalCopy()
 
3234
{
 
3235
#ifndef QT_NO_MIME
 
3236
    Q3TextDrag *drag = dragObject();
 
3237
    if (!drag)
 
3238
        return;
 
3239
#ifndef QT_NO_MIMECLIPBOARD
 
3240
    QApplication::clipboard()->setData(drag, d->clipboard_mode);
 
3241
#endif // QT_NO_MIMECLIPBOARD
 
3242
#endif // QT_NO_MIME
 
3243
}
 
3244
 
 
3245
/*!
 
3246
    Copies any selected text (from selection 0) to the clipboard.
 
3247
 
 
3248
    \sa hasSelectedText() copyAvailable()
 
3249
*/
 
3250
 
 
3251
void Q3TextEdit::copy()
 
3252
{
 
3253
#ifndef QT_NO_CLIPBOARD
 
3254
# ifdef QT_TEXTEDIT_OPTIMIZATION
 
3255
    if (d->optimMode && optimHasSelection())
 
3256
        QApplication::clipboard()->setText(optimSelectedText(), d->clipboard_mode);
 
3257
    else
 
3258
        normalCopy();
 
3259
# else
 
3260
    normalCopy();
 
3261
# endif
 
3262
#endif
 
3263
}
 
3264
 
 
3265
/*!
 
3266
    \internal
 
3267
 
 
3268
    Re-indents the current paragraph.
 
3269
*/
 
3270
 
 
3271
void Q3TextEdit::indent()
 
3272
{
 
3273
    if (isReadOnly())
 
3274
        return;
 
3275
 
 
3276
    drawCursor(false);
 
3277
    if (!doc->hasSelection(Q3TextDocument::Standard))
 
3278
        cursor->indent();
 
3279
    else
 
3280
        doc->indentSelection(Q3TextDocument::Standard);
 
3281
    repaintChanged();
 
3282
    drawCursor(true);
 
3283
    setModified();
 
3284
    emit textChanged();
 
3285
}
 
3286
 
 
3287
/*!
 
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.
 
3292
 */
 
3293
 
 
3294
bool Q3TextEdit::focusNextPrevChild(bool n)
 
3295
{
 
3296
    if (!isReadOnly() || !linksEnabled())
 
3297
        return false;
 
3298
    bool b = doc->focusNextPrevChild(n);
 
3299
    repaintChanged();
 
3300
    if (b)
 
3301
        //##### this does not work with tables. The focusIndicator
 
3302
        //should really be a Q3TextCursor. Fix 3.1
 
3303
        makeParagVisible(doc->focusIndicator.parag);
 
3304
    return b;
 
3305
}
 
3306
 
 
3307
/*!
 
3308
    \internal
 
3309
 
 
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.
 
3312
*/
 
3313
 
 
3314
void Q3TextEdit::setFormat(Q3TextFormat *f, int flags)
 
3315
{
 
3316
    if (doc->hasSelection(Q3TextDocument::Standard)) {
 
3317
        drawCursor(false);
 
3318
        Q3TextCursor c1 = doc->selectionStartCursor(Q3TextDocument::Standard);
 
3319
        c1.restoreState();
 
3320
        Q3TextCursor c2 = doc->selectionEndCursor(Q3TextDocument::Standard);
 
3321
        c2.restoreState();
 
3322
        if (undoEnabled) {
 
3323
            clearUndoRedo();
 
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;
 
3332
            clearUndoRedo();
 
3333
        }
 
3334
        doc->setFormat(Q3TextDocument::Standard, f, flags);
 
3335
        repaintChanged();
 
3336
        formatMore();
 
3337
        drawCursor(true);
 
3338
        setModified();
 
3339
        emit textChanged();
 
3340
    }
 
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());
 
3348
        }
 
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();
 
3358
                repaintChanged();
 
3359
            }
 
3360
        }
 
3361
    }
 
3362
}
 
3363
 
 
3364
/*! \internal
 
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.
 
3368
 
 
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.
 
3372
 
 
3373
  \sa setAlignment()
 
3374
*/
 
3375
 
 
3376
void Q3TextEdit::setParagType(Q3StyleSheetItem::DisplayMode dm,
 
3377
                              Q3StyleSheetItem::ListStyle listStyle)
 
3378
{
 
3379
    if (isReadOnly())
 
3380
        return;
 
3381
 
 
3382
    drawCursor(false);
 
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
 
3390
    }
 
3391
 
 
3392
    clearUndoRedo();
 
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);
 
3397
 
 
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));
 
3407
        }
 
3408
        start = start->next();
 
3409
    }
 
3410
 
 
3411
    clearUndoRedo();
 
3412
    repaintChanged();
 
3413
    formatMore();
 
3414
    drawCursor(true);
 
3415
    setModified();
 
3416
    emit textChanged();
 
3417
}
 
3418
 
 
3419
/*!
 
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
 
3423
    horizontally).
 
3424
*/
 
3425
 
 
3426
void Q3TextEdit::setAlignment(int a)
 
3427
{
 
3428
    if (isReadOnly() || block_set_alignment)
 
3429
        return;
 
3430
 
 
3431
    drawCursor(false);
 
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
 
3439
    }
 
3440
 
 
3441
    clearUndoRedo();
 
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);
 
3446
 
 
3447
    while (start != end->next()) {
 
3448
        start->setAlignment(a);
 
3449
        start = start->next();
 
3450
    }
 
3451
 
 
3452
    clearUndoRedo();
 
3453
    repaintChanged();
 
3454
    formatMore();
 
3455
    drawCursor(true);
 
3456
    if (currentAlignment != a) {
 
3457
        currentAlignment = a;
 
3458
        emit currentAlignmentChanged(currentAlignment);
 
3459
    }
 
3460
    setModified();
 
3461
    emit textChanged();
 
3462
}
 
3463
 
 
3464
void Q3TextEdit::updateCurrentFormat()
 
3465
{
 
3466
    int i = cursor->index();
 
3467
    if (i > 0)
 
3468
        --i;
 
3469
    if (doc->useFormatCollection() &&
 
3470
         (!currentFormat || currentFormat->key() != cursor->paragraph()->at(i)->format()->key())) {
 
3471
        if (currentFormat)
 
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());
 
3477
        }
 
3478
        emit currentFontChanged(currentFormat->font());
 
3479
        emit currentColorChanged(currentFormat->color());
 
3480
        emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign());
 
3481
    }
 
3482
 
 
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;
 
3488
    }
 
3489
}
 
3490
 
 
3491
/*!
 
3492
    If \a b is true sets the current format to italic; otherwise sets
 
3493
    the current format to non-italic.
 
3494
 
 
3495
    \sa italic()
 
3496
*/
 
3497
 
 
3498
void Q3TextEdit::setItalic(bool b)
 
3499
{
 
3500
    Q3TextFormat f(*currentFormat);
 
3501
    f.setItalic(b);
 
3502
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3503
    setFormat(f2, Q3TextFormat::Italic);
 
3504
}
 
3505
 
 
3506
/*!
 
3507
    If \a b is true sets the current format to bold; otherwise sets
 
3508
    the current format to non-bold.
 
3509
 
 
3510
    \sa bold()
 
3511
*/
 
3512
 
 
3513
void Q3TextEdit::setBold(bool b)
 
3514
{
 
3515
    Q3TextFormat f(*currentFormat);
 
3516
    f.setBold(b);
 
3517
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3518
    setFormat(f2, Q3TextFormat::Bold);
 
3519
}
 
3520
 
 
3521
/*!
 
3522
    If \a b is true sets the current format to underline; otherwise
 
3523
    sets the current format to non-underline.
 
3524
 
 
3525
    \sa underline()
 
3526
*/
 
3527
 
 
3528
void Q3TextEdit::setUnderline(bool b)
 
3529
{
 
3530
    Q3TextFormat f(*currentFormat);
 
3531
    f.setUnderline(b);
 
3532
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3533
    setFormat(f2, Q3TextFormat::Underline);
 
3534
}
 
3535
 
 
3536
/*!
 
3537
    Sets the font family of the current format to \a fontFamily.
 
3538
 
 
3539
    \sa family() setCurrentFont()
 
3540
*/
 
3541
 
 
3542
void Q3TextEdit::setFamily(const QString &fontFamily)
 
3543
{
 
3544
    Q3TextFormat f(*currentFormat);
 
3545
    f.setFamily(fontFamily);
 
3546
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3547
    setFormat(f2, Q3TextFormat::Family);
 
3548
}
 
3549
 
 
3550
/*!
 
3551
    Sets the point size of the current format to \a s.
 
3552
 
 
3553
    Note that if \a s is zero or negative, the behavior of this
 
3554
    function is not defined.
 
3555
 
 
3556
    \sa pointSize() setCurrentFont() setFamily()
 
3557
*/
 
3558
 
 
3559
void Q3TextEdit::setPointSize(int s)
 
3560
{
 
3561
    Q3TextFormat f(*currentFormat);
 
3562
    f.setPointSize(s);
 
3563
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3564
    setFormat(f2, Q3TextFormat::Size);
 
3565
}
 
3566
 
 
3567
/*!
 
3568
    Sets the color of the current format, i.e. of the text, to \a c.
 
3569
 
 
3570
    \sa color() setPaper()
 
3571
*/
 
3572
 
 
3573
void Q3TextEdit::setColor(const QColor &c)
 
3574
{
 
3575
    Q3TextFormat f(*currentFormat);
 
3576
    f.setColor(c);
 
3577
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3578
    setFormat(f2, Q3TextFormat::Color);
 
3579
}
 
3580
 
 
3581
/*!
 
3582
    Sets the vertical alignment of the current format, i.e. of the
 
3583
    text, to \a a.
 
3584
 
 
3585
    \sa color() setPaper()
 
3586
*/
 
3587
 
 
3588
void Q3TextEdit::setVerticalAlignment(VerticalAlignment a)
 
3589
{
 
3590
    Q3TextFormat f(*currentFormat);
 
3591
    f.setVAlign((Q3TextFormat::VerticalAlignment)a);
 
3592
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3593
    setFormat(f2, Q3TextFormat::VAlign);
 
3594
}
 
3595
 
 
3596
void Q3TextEdit::setFontInternal(const QFont &f_)
 
3597
{
 
3598
    Q3TextFormat f(*currentFormat);
 
3599
    f.setFont(f_);
 
3600
    Q3TextFormat *f2 = doc->formatCollection()->format(&f);
 
3601
    setFormat(f2, Q3TextFormat::Font);
 
3602
}
 
3603
 
 
3604
 
 
3605
QString Q3TextEdit::text() const
 
3606
{
 
3607
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3608
    if (d->optimMode)
 
3609
        return optimText();
 
3610
#endif
 
3611
 
 
3612
    Q3TextParagraph *p = doc->firstParagraph();
 
3613
    if (!p || (!p->next() && p->length() <= 1))
 
3614
        return QString::fromLatin1("");
 
3615
 
 
3616
    if (isReadOnly())
 
3617
        return doc->originalText();
 
3618
    return doc->text();
 
3619
}
 
3620
 
 
3621
/*!
 
3622
    \overload
 
3623
 
 
3624
    Returns the text of paragraph \a para.
 
3625
 
 
3626
    If textFormat() is \c Qt::RichText the text will contain HTML
 
3627
    formatting tags.
 
3628
*/
 
3629
 
 
3630
QString Q3TextEdit::text(int para) const
 
3631
{
 
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())
 
3636
            paraStr = "\n";
 
3637
        return paraStr;
 
3638
    } else
 
3639
#endif
 
3640
    return doc->text(para);
 
3641
}
 
3642
 
 
3643
/*!
 
3644
    \overload
 
3645
 
 
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.
 
3648
 
 
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.
 
3652
 
 
3653
    For rich text the rendering style and available tags are defined
 
3654
    by a styleSheet(); see Q3StyleSheet for details.
 
3655
 
 
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.
 
3660
 
 
3661
    Note that the undo/redo history is cleared by this function.
 
3662
 
 
3663
    \sa text(), setTextFormat()
 
3664
*/
 
3665
 
 
3666
void Q3TextEdit::setText(const QString &text, const QString &context)
 
3667
{
 
3668
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3669
    if (d->optimMode) {
 
3670
        optimSetText(text);
 
3671
        return;
 
3672
    }
 
3673
#endif
 
3674
    if (!isModified() && isReadOnly() &&
 
3675
         this->context() == context && this->text() == text)
 
3676
        return;
 
3677
 
 
3678
    emit undoAvailable(false);
 
3679
    emit redoAvailable(false);
 
3680
    undoRedoInfo.clear();
 
3681
    doc->commands()->clear();
 
3682
 
 
3683
    lastFormatted = 0;
 
3684
    int oldCursorPos = cursor->index();
 
3685
    int oldCursorPar = cursor->paragraph()->paragId();
 
3686
    cursor->restoreState();
 
3687
    delete cursor;
 
3688
    doc->setText(text, context);
 
3689
 
 
3690
    if (wrapMode == FixedPixelWidth) {
 
3691
        resizeContents(wrapWidth, 0);
 
3692
        doc->setWidth(wrapWidth);
 
3693
        doc->setMinimumWidth(wrapWidth);
 
3694
    } else {
 
3695
        doc->setMinimumWidth(-1);
 
3696
        resizeContents(0, 0);
 
3697
    }
 
3698
 
 
3699
    lastFormatted = doc->firstParagraph();
 
3700
    cursor = new Q3TextCursor(doc);
 
3701
    updateContents();
 
3702
 
 
3703
    if (isModified())
 
3704
        setModified(false);
 
3705
    emit textChanged();
 
3706
    if (cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar) {
 
3707
        emit cursorPositionChanged(cursor);
 
3708
        emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
3709
    }
 
3710
    formatMore();
 
3711
    updateCurrentFormat();
 
3712
    d->scrollToAnchor.clear();
 
3713
}
 
3714
 
 
3715
/*!
 
3716
    \property Q3TextEdit::text
 
3717
    \brief the text edit's text
 
3718
 
 
3719
    There is no default text.
 
3720
 
 
3721
    On setting, any previous text is deleted.
 
3722
 
 
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.
 
3726
 
 
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
 
3730
    was set.
 
3731
 
 
3732
    \sa textFormat
 
3733
*/
 
3734
 
 
3735
 
 
3736
/*!
 
3737
    \property Q3TextEdit::readOnly
 
3738
    \brief whether the text edit is read-only
 
3739
 
 
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.
 
3742
 
 
3743
    This property's default is false.
 
3744
*/
 
3745
 
 
3746
/*!
 
3747
    Finds the next occurrence of the string, \a expr. Returns true if
 
3748
    \a expr was found; otherwise returns false.
 
3749
 
 
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.
 
3754
 
 
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.
 
3761
 
 
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
 
3766
    \c{*}\a{index}.
 
3767
 
 
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.
 
3771
 
 
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.
 
3775
*/
 
3776
 
 
3777
bool Q3TextEdit::find(const QString &expr, bool cs, bool wo, bool forward,
 
3778
                      int *para, int *index)
 
3779
{
 
3780
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3781
    if (d->optimMode)
 
3782
        return optimFind(expr, cs, wo, forward, para, index);
 
3783
#endif
 
3784
    drawCursor(false);
 
3785
#ifndef QT_NO_CURSOR
 
3786
    viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
3787
#endif
 
3788
    Q3TextCursor findcur = *cursor;
 
3789
    if (para && index) {
 
3790
        if (doc->paragAt(*para))
 
3791
            findcur.gotoPosition(doc->paragAt(*para), *index);
 
3792
        else
 
3793
            findcur.gotoEnd();
 
3794
    } else if (doc->hasSelection(Q3TextDocument::Standard)){
 
3795
        // maks sure we do not find the same selection again
 
3796
        if (forward)
 
3797
            findcur.gotoNextLetter();
 
3798
        else
 
3799
            findcur.gotoPreviousLetter();
 
3800
    } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) {
 
3801
        findcur.gotoEnd();
 
3802
    }
 
3803
    removeSelection(Q3TextDocument::Standard);
 
3804
    bool found = doc->find(findcur, expr, cs, wo, forward);
 
3805
    if (found) {
 
3806
        if (para)
 
3807
            *para = findcur.paragraph()->paragId();
 
3808
        if (index)
 
3809
            *index = findcur.index();
 
3810
        *cursor = findcur;
 
3811
        repaintChanged();
 
3812
        ensureCursorVisible();
 
3813
    }
 
3814
    drawCursor(true);
 
3815
    if (found) {
 
3816
        emit cursorPositionChanged(cursor);
 
3817
        emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
3818
    }
 
3819
    return found;
 
3820
}
 
3821
 
 
3822
void Q3TextEdit::blinkCursor()
 
3823
{
 
3824
    bool cv = cursorVisible;
 
3825
    blinkCursorVisible = !blinkCursorVisible;
 
3826
    drawCursor(blinkCursorVisible);
 
3827
    cursorVisible = cv;
 
3828
}
 
3829
 
 
3830
/*!
 
3831
    Sets the cursor to position \a index in paragraph \a para.
 
3832
 
 
3833
    \sa getCursorPosition()
 
3834
*/
 
3835
 
 
3836
void Q3TextEdit::setCursorPosition(int para, int index)
 
3837
{
 
3838
    Q3TextParagraph *p = doc->paragAt(para);
 
3839
    if (!p)
 
3840
        return;
 
3841
 
 
3842
    if (index > p->length() - 1)
 
3843
        index = p->length() - 1;
 
3844
 
 
3845
    drawCursor(false);
 
3846
    cursor->setParagraph(p);
 
3847
    cursor->setIndex(index);
 
3848
    ensureCursorVisible();
 
3849
    drawCursor(true);
 
3850
    updateCurrentFormat();
 
3851
    emit cursorPositionChanged(cursor);
 
3852
    emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
3853
}
 
3854
 
 
3855
/*!
 
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.
 
3858
 
 
3859
    \sa setCursorPosition()
 
3860
*/
 
3861
 
 
3862
void Q3TextEdit::getCursorPosition(int *para, int *index) const
 
3863
{
 
3864
    if (!para || !index)
 
3865
        return;
 
3866
    *para = cursor->paragraph()->paragId();
 
3867
    *index = cursor->index();
 
3868
}
 
3869
 
 
3870
/*!
 
3871
    Sets a selection which starts at position \a indexFrom in
 
3872
    paragraph \a paraFrom and ends at position \a indexTo in paragraph
 
3873
    \a paraTo.
 
3874
 
 
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.
 
3878
 
 
3879
    Uses the selection settings of selection \a selNum. If \a selNum
 
3880
    is 0, this is the default selection.
 
3881
 
 
3882
    The cursor is moved to the end of the selection if \a selNum is 0,
 
3883
    otherwise the cursor position remains unchanged.
 
3884
 
 
3885
    \sa getSelection() selectedText
 
3886
*/
 
3887
 
 
3888
void Q3TextEdit::setSelection(int paraFrom, int indexFrom,
 
3889
                              int paraTo, int indexTo, int selNum)
 
3890
{
 
3891
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3892
    if (d->optimMode) {
 
3893
        optimSetSelection(paraFrom, indexFrom, paraTo, indexTo);
 
3894
        repaintContents();
 
3895
        return;
 
3896
    }
 
3897
#endif
 
3898
    if (doc->hasSelection(selNum)) {
 
3899
        doc->removeSelection(selNum);
 
3900
        repaintChanged();
 
3901
    }
 
3902
    if (selNum > doc->numSelections() - 1)
 
3903
        doc->addSelection(selNum);
 
3904
    Q3TextParagraph *p1 = doc->paragAt(paraFrom);
 
3905
    if (!p1)
 
3906
        return;
 
3907
    Q3TextParagraph *p2 = doc->paragAt(paraTo);
 
3908
    if (!p2)
 
3909
        return;
 
3910
 
 
3911
    if (indexFrom > p1->length() - 1)
 
3912
        indexFrom = p1->length() - 1;
 
3913
    if (indexTo > p2->length() - 1)
 
3914
        indexTo = p2->length() - 1;
 
3915
 
 
3916
    drawCursor(false);
 
3917
    Q3TextCursor c = *cursor;
 
3918
    Q3TextCursor oldCursor = *cursor;
 
3919
    c.setParagraph(p1);
 
3920
    c.setIndex(indexFrom);
 
3921
    cursor->setParagraph(p2);
 
3922
    cursor->setIndex(indexTo);
 
3923
    doc->setSelectionStart(selNum, c);
 
3924
    doc->setSelectionEnd(selNum, *cursor);
 
3925
    repaintChanged();
 
3926
    ensureCursorVisible();
 
3927
    if (selNum != Q3TextDocument::Standard)
 
3928
        *cursor = oldCursor;
 
3929
    drawCursor(true);
 
3930
}
 
3931
 
 
3932
/*!
 
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}.
 
3939
 
 
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.
 
3942
 
 
3943
    If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this
 
3944
    function does nothing.
 
3945
 
 
3946
    The \a selNum is the number of the selection (multiple selections
 
3947
    are supported). It defaults to 0 (the default selection).
 
3948
 
 
3949
    \sa setSelection() selectedText
 
3950
*/
 
3951
 
 
3952
void Q3TextEdit::getSelection(int *paraFrom, int *indexFrom,
 
3953
                              int *paraTo, int *indexTo, int selNum) const
 
3954
{
 
3955
    if (!paraFrom || !paraTo || !indexFrom || !indexTo)
 
3956
        return;
 
3957
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
3958
    if (d->optimMode) {
 
3959
        *paraFrom = d->od->selStart.line;
 
3960
        *paraTo = d->od->selEnd.line;
 
3961
        *indexFrom = d->od->selStart.index;
 
3962
        *indexTo = d->od->selEnd.index;
 
3963
        return;
 
3964
    }
 
3965
#endif
 
3966
    if (!doc->hasSelection(selNum)) {
 
3967
        *paraFrom = -1;
 
3968
        *indexFrom = -1;
 
3969
        *paraTo = -1;
 
3970
        *indexTo = -1;
 
3971
        return;
 
3972
    }
 
3973
 
 
3974
    doc->selectionStart(selNum, *paraFrom, *indexFrom);
 
3975
    doc->selectionEnd(selNum, *paraTo, *indexTo);
 
3976
}
 
3977
 
 
3978
/*!
 
3979
    \property Q3TextEdit::textFormat
 
3980
    \brief the text format: rich text, plain text, log text or auto text.
 
3981
 
 
3982
    The text format is one of the following:
 
3983
    \list
 
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
 
3987
    paragraph.
 
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
 
3992
    settings).
 
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.
 
3996
    \endlist
 
3997
*/
 
3998
 
 
3999
void Q3TextEdit::setTextFormat(Qt::TextFormat format)
 
4000
{
 
4001
    doc->setTextFormat(format);
 
4002
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4003
    checkOptimMode();
 
4004
#endif
 
4005
}
 
4006
 
 
4007
Qt::TextFormat Q3TextEdit::textFormat() const
 
4008
{
 
4009
    return doc->textFormat();
 
4010
}
 
4011
 
 
4012
/*!
 
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.
 
4015
*/
 
4016
 
 
4017
int Q3TextEdit::paragraphs() const
 
4018
{
 
4019
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4020
    if (d->optimMode) {
 
4021
        return d->od->numLines;
 
4022
    }
 
4023
#endif
 
4024
    return doc->lastParagraph()->paragId() + 1;
 
4025
}
 
4026
 
 
4027
/*!
 
4028
    Returns the number of lines in paragraph \a para, or -1 if there
 
4029
    is no paragraph with index \a para.
 
4030
*/
 
4031
 
 
4032
int Q3TextEdit::linesOfParagraph(int para) const
 
4033
{
 
4034
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4035
    if (d->optimMode) {
 
4036
        if (d->od->numLines >= para)
 
4037
            return 1;
 
4038
        else
 
4039
            return -1;
 
4040
    }
 
4041
#endif
 
4042
    Q3TextParagraph *p = doc->paragAt(para);
 
4043
    if (!p)
 
4044
        return -1;
 
4045
    return p->lines();
 
4046
}
 
4047
 
 
4048
/*!
 
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.
 
4051
 
 
4052
    This function ignores newlines.
 
4053
*/
 
4054
 
 
4055
int Q3TextEdit::paragraphLength(int para) const
 
4056
{
 
4057
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4058
    if (d->optimMode) {
 
4059
        if (d->od->numLines >= para) {
 
4060
            if (d->od->lines[LOGOFFSET(para)].isEmpty()) // CR
 
4061
                return 1;
 
4062
            else
 
4063
                return d->od->lines[LOGOFFSET(para)].length();
 
4064
        }
 
4065
        return -1;
 
4066
    }
 
4067
#endif
 
4068
    Q3TextParagraph *p = doc->paragAt(para);
 
4069
    if (!p)
 
4070
        return -1;
 
4071
    return p->length() - 1;
 
4072
}
 
4073
 
 
4074
/*!
 
4075
    Returns the number of lines in the text edit; this could be 0.
 
4076
 
 
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.
 
4080
*/
 
4081
 
 
4082
int Q3TextEdit::lines() const
 
4083
{
 
4084
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4085
    if (d->optimMode) {
 
4086
        return d->od->numLines;
 
4087
    }
 
4088
#endif
 
4089
    Q3TextParagraph *p = doc->firstParagraph();
 
4090
    int l = 0;
 
4091
    while (p) {
 
4092
        l += p->lines();
 
4093
        p = p->next();
 
4094
    }
 
4095
 
 
4096
    return l;
 
4097
}
 
4098
 
 
4099
/*!
 
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.
 
4105
*/
 
4106
 
 
4107
int Q3TextEdit::lineOfChar(int para, int index)
 
4108
{
 
4109
    Q3TextParagraph *p = doc->paragAt(para);
 
4110
    if (!p)
 
4111
        return -1;
 
4112
 
 
4113
    int idx, line;
 
4114
    Q3TextStringChar *c = p->lineStartOfChar(index, &idx, &line);
 
4115
    if (!c)
 
4116
        return -1;
 
4117
 
 
4118
    return line;
 
4119
}
 
4120
 
 
4121
void Q3TextEdit::setModified(bool m)
 
4122
{
 
4123
    bool oldModified = modified;
 
4124
    modified = m;
 
4125
    if (modified && doc->oTextValid)
 
4126
        doc->invalidateOriginalText();
 
4127
    if (oldModified != modified)
 
4128
        emit modificationChanged(modified);
 
4129
}
 
4130
 
 
4131
/*!
 
4132
    \property Q3TextEdit::modified
 
4133
    \brief whether the document has been modified by the user
 
4134
*/
 
4135
 
 
4136
bool Q3TextEdit::isModified() const
 
4137
{
 
4138
    return modified;
 
4139
}
 
4140
 
 
4141
void Q3TextEdit::setModified()
 
4142
{
 
4143
    if (!isModified())
 
4144
        setModified(true);
 
4145
}
 
4146
 
 
4147
/*!
 
4148
    Returns true if the current format is italic; otherwise returns false.
 
4149
 
 
4150
    \sa setItalic()
 
4151
*/
 
4152
 
 
4153
bool Q3TextEdit::italic() const
 
4154
{
 
4155
    return currentFormat->font().italic();
 
4156
}
 
4157
 
 
4158
/*!
 
4159
    Returns true if the current format is bold; otherwise returns false.
 
4160
 
 
4161
    \sa setBold()
 
4162
*/
 
4163
 
 
4164
bool Q3TextEdit::bold() const
 
4165
{
 
4166
    return currentFormat->font().bold();
 
4167
}
 
4168
 
 
4169
/*!
 
4170
    Returns true if the current format is underlined; otherwise returns
 
4171
    false.
 
4172
 
 
4173
    \sa setUnderline()
 
4174
*/
 
4175
 
 
4176
bool Q3TextEdit::underline() const
 
4177
{
 
4178
    return currentFormat->font().underline();
 
4179
}
 
4180
 
 
4181
/*!
 
4182
    Returns the font family of the current format.
 
4183
 
 
4184
    \sa setFamily() setCurrentFont() setPointSize()
 
4185
*/
 
4186
 
 
4187
QString Q3TextEdit::family() const
 
4188
{
 
4189
    return currentFormat->font().family();
 
4190
}
 
4191
 
 
4192
/*!
 
4193
    Returns the point size of the font of the current format.
 
4194
 
 
4195
    \sa setFamily() setCurrentFont() setPointSize()
 
4196
*/
 
4197
 
 
4198
int Q3TextEdit::pointSize() const
 
4199
{
 
4200
    return currentFormat->font().pointSize();
 
4201
}
 
4202
 
 
4203
/*!
 
4204
    Returns the color of the current format.
 
4205
 
 
4206
    \sa setColor() setPaper()
 
4207
*/
 
4208
 
 
4209
QColor Q3TextEdit::color() const
 
4210
{
 
4211
    return currentFormat->color();
 
4212
}
 
4213
 
 
4214
/*!
 
4215
    Returns Q3ScrollView::font()
 
4216
 
 
4217
    \warning In previous versions this function returned the font of
 
4218
    the current format. This lead to confusion. Please use
 
4219
    currentFont() instead.
 
4220
*/
 
4221
 
 
4222
QFont Q3TextEdit::font() const
 
4223
{
 
4224
    return Q3ScrollView::font();
 
4225
}
 
4226
 
 
4227
/*!
 
4228
    Returns the font of the current format.
 
4229
 
 
4230
    \sa setCurrentFont() setFamily() setPointSize()
 
4231
*/
 
4232
 
 
4233
QFont Q3TextEdit::currentFont() const
 
4234
{
 
4235
    return currentFormat->font();
 
4236
}
 
4237
 
 
4238
 
 
4239
/*!
 
4240
    Returns the alignment of the current paragraph.
 
4241
 
 
4242
    \sa setAlignment()
 
4243
*/
 
4244
 
 
4245
int Q3TextEdit::alignment() const
 
4246
{
 
4247
    return currentAlignment;
 
4248
}
 
4249
 
 
4250
/*!
 
4251
    Returns the vertical alignment of the current format.
 
4252
 
 
4253
    \sa setVerticalAlignment()
 
4254
*/
 
4255
 
 
4256
Q3TextEdit::VerticalAlignment Q3TextEdit::verticalAlignment() const
 
4257
{
 
4258
    return (Q3TextEdit::VerticalAlignment) currentFormat->vAlign();
 
4259
}
 
4260
 
 
4261
void Q3TextEdit::startDrag()
 
4262
{
 
4263
#ifndef QT_NO_DRAGANDDROP
 
4264
    mousePressed = false;
 
4265
    inDoubleClick = false;
 
4266
    Q3DragObject *drag = dragObject(viewport());
 
4267
    if (!drag)
 
4268
        return;
 
4269
    if (isReadOnly()) {
 
4270
        drag->dragCopy();
 
4271
    } else {
 
4272
        if (drag->drag() && Q3DragObject::target() != this && Q3DragObject::target() != viewport())
 
4273
            removeSelectedText();
 
4274
    }
 
4275
#endif
 
4276
}
 
4277
 
 
4278
/*!
 
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.
 
4282
 
 
4283
    \sa selectedText
 
4284
*/
 
4285
 
 
4286
void Q3TextEdit::selectAll(bool select)
 
4287
{
 
4288
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4289
    if (d->optimMode) {
 
4290
        if (select)
 
4291
            optimSelectAll();
 
4292
        else
 
4293
            optimRemoveSelection();
 
4294
        return;
 
4295
    }
 
4296
#endif
 
4297
    if (!select)
 
4298
        doc->removeSelection(Q3TextDocument::Standard);
 
4299
    else
 
4300
        doc->selectAll(Q3TextDocument::Standard);
 
4301
    repaintChanged();
 
4302
    emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard));
 
4303
    emit selectionChanged();
 
4304
#ifndef QT_NO_CURSOR
 
4305
    viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
4306
#endif
 
4307
}
 
4308
 
 
4309
void Q3TextEdit::UndoRedoInfo::clear()
 
4310
{
 
4311
    if (valid()) {
 
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));
 
4320
        }
 
4321
    }
 
4322
    type = Invalid;
 
4323
    d->text.clear();
 
4324
    id = -1;
 
4325
    index = -1;
 
4326
    styleInformation = QByteArray();
 
4327
}
 
4328
 
 
4329
 
 
4330
/*!
 
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.
 
4334
 
 
4335
    \sa removeSelectedText() cut()
 
4336
*/
 
4337
 
 
4338
void Q3TextEdit::del()
 
4339
{
 
4340
    if (doc->hasSelection(Q3TextDocument::Standard)) {
 
4341
        removeSelectedText();
 
4342
        return;
 
4343
    }
 
4344
 
 
4345
    doKeyboardAction(ActionDelete);
 
4346
}
 
4347
 
 
4348
 
 
4349
Q3TextEdit::UndoRedoInfo::UndoRedoInfo(Q3TextDocument *dc)
 
4350
    : type(Invalid), doc(dc)
 
4351
{
 
4352
    d = new QUndoRedoInfoPrivate;
 
4353
    d->text.clear();
 
4354
    id = -1;
 
4355
    index = -1;
 
4356
}
 
4357
 
 
4358
Q3TextEdit::UndoRedoInfo::~UndoRedoInfo()
 
4359
{
 
4360
    delete d;
 
4361
}
 
4362
 
 
4363
bool Q3TextEdit::UndoRedoInfo::valid() const
 
4364
{
 
4365
    return id >= 0 &&  type != Invalid;
 
4366
}
 
4367
 
 
4368
/*!
 
4369
    \internal
 
4370
 
 
4371
  Resets the current format to the default format.
 
4372
*/
 
4373
 
 
4374
void Q3TextEdit::resetFormat()
 
4375
{
 
4376
    setAlignment(Qt::AlignAuto);
 
4377
    setParagType(Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc);
 
4378
    setFormat(doc->formatCollection()->defaultFormat(), Q3TextFormat::Format);
 
4379
}
 
4380
 
 
4381
/*!
 
4382
    Returns the Q3StyleSheet which is being used by this text edit.
 
4383
 
 
4384
    \sa setStyleSheet()
 
4385
*/
 
4386
 
 
4387
Q3StyleSheet* Q3TextEdit::styleSheet() const
 
4388
{
 
4389
    return doc->styleSheet();
 
4390
}
 
4391
 
 
4392
/*!
 
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
 
4395
    append().
 
4396
 
 
4397
    \sa styleSheet()
 
4398
*/
 
4399
 
 
4400
void Q3TextEdit::setStyleSheet(Q3StyleSheet* styleSheet)
 
4401
{
 
4402
    doc->setStyleSheet(styleSheet);
 
4403
}
 
4404
 
 
4405
/*!
 
4406
    \property Q3TextEdit::paper
 
4407
    \brief the background (paper) brush.
 
4408
 
 
4409
    The brush that is currently used to draw the background of the
 
4410
    text edit. The initial setting is an empty brush.
 
4411
*/
 
4412
 
 
4413
void Q3TextEdit::setPaper(const QBrush& pap)
 
4414
{
 
4415
    doc->setPaper(new QBrush(pap));
 
4416
    if ( pap.pixmap() )
 
4417
        viewport()->setBackgroundPixmap( *pap.pixmap() );
 
4418
    QPalette pal = palette();
 
4419
    pal.setColor(QPalette::Background, pap.color());
 
4420
    setPalette(pal);
 
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
 
4427
    if (d->optimMode)
 
4428
        repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height());
 
4429
    else
 
4430
#endif
 
4431
        updateContents();
 
4432
}
 
4433
 
 
4434
QBrush Q3TextEdit::paper() const
 
4435
{
 
4436
    if (doc->paper())
 
4437
        return *doc->paper();
 
4438
    return QBrush(palette().base());
 
4439
}
 
4440
 
 
4441
/*!
 
4442
    \property Q3TextEdit::linkUnderline
 
4443
    \brief whether hypertext links will be underlined
 
4444
 
 
4445
    If true (the default) hypertext links will be displayed
 
4446
    underlined. If false links will not be displayed underlined.
 
4447
*/
 
4448
 
 
4449
void Q3TextEdit::setLinkUnderline(bool b)
 
4450
{
 
4451
    if (doc->underlineLinks() == b)
 
4452
        return;
 
4453
    doc->setUnderlineLinks(b);
 
4454
    repaintChanged();
 
4455
}
 
4456
 
 
4457
bool Q3TextEdit::linkUnderline() const
 
4458
{
 
4459
    return doc->underlineLinks();
 
4460
}
 
4461
 
 
4462
/*!
 
4463
    Sets the text edit's mimesource factory to \a factory. See
 
4464
    Q3MimeSourceFactory for further details.
 
4465
 
 
4466
    \sa mimeSourceFactory()
 
4467
 */
 
4468
 
 
4469
#ifndef QT_NO_MIME
 
4470
void Q3TextEdit::setMimeSourceFactory(Q3MimeSourceFactory* factory)
 
4471
{
 
4472
    doc->setMimeSourceFactory(factory);
 
4473
}
 
4474
 
 
4475
/*!
 
4476
    Returns the Q3MimeSourceFactory which is being used by this text
 
4477
    edit.
 
4478
 
 
4479
    \sa setMimeSourceFactory()
 
4480
*/
 
4481
 
 
4482
Q3MimeSourceFactory* Q3TextEdit::mimeSourceFactory() const
 
4483
{
 
4484
    return doc->mimeSourceFactory();
 
4485
}
 
4486
#endif
 
4487
 
 
4488
/*!
 
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.
 
4491
*/
 
4492
 
 
4493
int Q3TextEdit::heightForWidth(int w) const
 
4494
{
 
4495
    int oldw = doc->width();
 
4496
    doc->doLayout(0, w);
 
4497
    int h = doc->height();
 
4498
    doc->setWidth(oldw);
 
4499
    doc->invalidate();
 
4500
    ((Q3TextEdit*)this)->formatMore();
 
4501
    return h;
 
4502
}
 
4503
 
 
4504
/*!
 
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().
 
4510
*/
 
4511
 
 
4512
void Q3TextEdit::append(const QString &text)
 
4513
{
 
4514
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4515
    if (d->optimMode) {
 
4516
        optimAppend(text);
 
4517
        return;
 
4518
    }
 
4519
#endif
 
4520
    // flush and clear the undo/redo stack if necessary
 
4521
    undoRedoInfo.clear();
 
4522
    doc->commands()->clear();
 
4523
 
 
4524
    doc->removeSelection(Q3TextDocument::Standard);
 
4525
    Qt::TextFormat f = doc->textFormat();
 
4526
    if (f == Qt::AutoText) {
 
4527
        if (Q3StyleSheet::mightBeRichText(text))
 
4528
            f = Qt::RichText;
 
4529
        else
 
4530
            f = Qt::PlainText;
 
4531
    }
 
4532
 
 
4533
    drawCursor(false);
 
4534
    Q3TextCursor oldc(*cursor);
 
4535
    ensureFormatted(doc->lastParagraph());
 
4536
    bool atBottom = contentsY() >= contentsHeight() - visibleHeight();
 
4537
    cursor->gotoEnd();
 
4538
    if (cursor->index() > 0)
 
4539
        cursor->splitAndInsertEmptyParagraph();
 
4540
 
 
4541
    if (f == Qt::PlainText) {
 
4542
        cursor->insert(text, true);
 
4543
    } else {
 
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);
 
4549
    }
 
4550
    formatMore();
 
4551
    repaintChanged();
 
4552
    if (atBottom)
 
4553
        scrollToBottom();
 
4554
    *cursor = oldc;
 
4555
    if (!isReadOnly())
 
4556
        cursorVisible = true;
 
4557
    setModified();
 
4558
    emit textChanged();
 
4559
}
 
4560
 
 
4561
/*!
 
4562
    \property Q3TextEdit::hasSelectedText
 
4563
    \brief whether some text is selected in selection 0
 
4564
*/
 
4565
 
 
4566
bool Q3TextEdit::hasSelectedText() const
 
4567
{
 
4568
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4569
    if (d->optimMode)
 
4570
        return optimHasSelection();
 
4571
    else
 
4572
#endif
 
4573
        return doc->hasSelection(Q3TextDocument::Standard);
 
4574
}
 
4575
 
 
4576
/*!
 
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).
 
4580
 
 
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.
 
4583
 
 
4584
    \sa hasSelectedText
 
4585
*/
 
4586
 
 
4587
QString Q3TextEdit::selectedText() const
 
4588
{
 
4589
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
4590
    if (d->optimMode)
 
4591
        return optimSelectedText();
 
4592
    else
 
4593
#endif
 
4594
        return doc->selectedText(Q3TextDocument::Standard, textFormat() == Qt::RichText);
 
4595
}
 
4596
 
 
4597
bool Q3TextEdit::handleReadOnlyKeyEvent(QKeyEvent *e)
 
4598
{
 
4599
    switch(e->key()) {
 
4600
    case Qt::Key_Down:
 
4601
        setContentsPos(contentsX(), contentsY() + 10);
 
4602
        break;
 
4603
    case Qt::Key_Up:
 
4604
        setContentsPos(contentsX(), contentsY() - 10);
 
4605
        break;
 
4606
    case Qt::Key_Left:
 
4607
        setContentsPos(contentsX() - 10, contentsY());
 
4608
        break;
 
4609
    case Qt::Key_Right:
 
4610
        setContentsPos(contentsX() + 10, contentsY());
 
4611
        break;
 
4612
    case Qt::Key_PageUp:
 
4613
        setContentsPos(contentsX(), contentsY() - visibleHeight());
 
4614
        break;
 
4615
    case Qt::Key_PageDown:
 
4616
        setContentsPos(contentsX(), contentsY() + visibleHeight());
 
4617
        break;
 
4618
    case Qt::Key_Home:
 
4619
        setContentsPos(contentsX(), 0);
 
4620
        break;
 
4621
    case Qt::Key_End:
 
4622
        setContentsPos(contentsX(), contentsHeight() - visibleHeight());
 
4623
        break;
 
4624
    case Qt::Key_F16: // Copy key on Sun keyboards
 
4625
        copy();
 
4626
        break;
 
4627
#ifndef QT_NO_NETWORKPROTOCOL
 
4628
    case Qt::Key_Return:
 
4629
    case Qt::Key_Enter:
 
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));
 
4636
            }
 
4637
            if (!doc->focusIndicator.name.isEmpty())
 
4638
                if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this))
 
4639
                    emit browser->anchorClicked(doc->focusIndicator.name, doc->focusIndicator.href);
 
4640
 
 
4641
#ifndef QT_NO_CURSOR
 
4642
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
4643
#endif
 
4644
        }
 
4645
    } break;
 
4646
#endif
 
4647
    default:
 
4648
        if (e->state() & Qt::ControlButton) {
 
4649
            switch (e->key()) {
 
4650
            case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards
 
4651
                copy();
 
4652
                break;
 
4653
#ifdef Q_WS_WIN
 
4654
            case Qt::Key_Insert:
 
4655
                copy();
 
4656
                break;
 
4657
            case Qt::Key_A:
 
4658
                selectAll();
 
4659
                break;
 
4660
#endif
 
4661
            }
 
4662
 
 
4663
        }
 
4664
        return false;
 
4665
    }
 
4666
    return true;
 
4667
}
 
4668
 
 
4669
/*!
 
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.
 
4673
 
 
4674
    \sa text
 
4675
*/
 
4676
 
 
4677
QString Q3TextEdit::context() const
 
4678
{
 
4679
    return doc->context();
 
4680
}
 
4681
 
 
4682
/*!
 
4683
    \property Q3TextEdit::documentTitle
 
4684
    \brief the title of the document parsed from the text.
 
4685
 
 
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.
 
4689
*/
 
4690
 
 
4691
QString Q3TextEdit::documentTitle() const
 
4692
{
 
4693
    return doc->attributes()["title"];
 
4694
}
 
4695
 
 
4696
void Q3TextEdit::makeParagVisible(Q3TextParagraph *p)
 
4697
{
 
4698
    setContentsPos(contentsX(), qMin(p->rect().y(), contentsHeight() - visibleHeight()));
 
4699
}
 
4700
 
 
4701
/*!
 
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">}.
 
4706
*/
 
4707
 
 
4708
void Q3TextEdit::scrollToAnchor(const QString& name)
 
4709
{
 
4710
    if (!isVisible()) {
 
4711
        d->scrollToAnchor = name;
 
4712
        return;
 
4713
    }
 
4714
    if (name.isEmpty())
 
4715
        return;
 
4716
    sync();
 
4717
    Q3TextCursor cursor(doc);
 
4718
    Q3TextParagraph* last = doc->lastParagraph();
 
4719
    for (;;) {
 
4720
        Q3TextStringChar* c = cursor.paragraph()->at(cursor.index());
 
4721
        if(c->isAnchor()) {
 
4722
            QString a = c->anchorName();
 
4723
            if (a == name ||
 
4724
                 (a.contains('#') && a.split('#').contains(name))) {
 
4725
                setContentsPos(contentsX(), qMin(cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight()));
 
4726
                break;
 
4727
            }
 
4728
        }
 
4729
        if (cursor.paragraph() == last && cursor.atParagEnd() )
 
4730
            break;
 
4731
        cursor.gotoNextLetter();
 
4732
    }
 
4733
}
 
4734
 
 
4735
/*!
 
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.
 
4739
*/
 
4740
 
 
4741
QString Q3TextEdit::anchorAt(const QPoint& pos, Qt::AnchorAttribute attr)
 
4742
{
 
4743
    Q3TextCursor c(doc);
 
4744
    placeCursor(pos, &c, true);
 
4745
    switch(attr) {
 
4746
        case Qt::AnchorName:
 
4747
            return c.paragraph()->at(c.index())->anchorName();
 
4748
        case Qt::AnchorHref:
 
4749
            return c.paragraph()->at(c.index())->anchorHref();
 
4750
    }
 
4751
    // incase the compiler is really dumb about determining if a function
 
4752
    // returns something :)
 
4753
    return QString();
 
4754
}
 
4755
 
 
4756
void Q3TextEdit::documentWidthChanged(int w)
 
4757
{
 
4758
    resizeContents(qMax(visibleWidth(), w), contentsHeight());
 
4759
}
 
4760
 
 
4761
/*! \internal
 
4762
 
 
4763
  This function does nothing
 
4764
*/
 
4765
 
 
4766
void Q3TextEdit::updateStyles()
 
4767
{
 
4768
}
 
4769
 
 
4770
void Q3TextEdit::setDocument(Q3TextDocument *dc)
 
4771
{
 
4772
    if (dc == doc)
 
4773
        return;
 
4774
    doc = dc;
 
4775
    delete cursor;
 
4776
    cursor = new Q3TextCursor(doc);
 
4777
    clearUndoRedo();
 
4778
    undoRedoInfo.doc = doc;
 
4779
    lastFormatted = 0;
 
4780
}
 
4781
 
 
4782
#ifndef QT_NO_CLIPBOARD
 
4783
 
 
4784
/*!
 
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
 
4787
    "plain" or "html".
 
4788
 
 
4789
    If there is no text with format \a subtype in the clipboard
 
4790
    nothing happens.
 
4791
 
 
4792
    \sa paste() cut() Q3TextEdit::copy()
 
4793
*/
 
4794
 
 
4795
void Q3TextEdit::pasteSubType(const QByteArray &subtype)
 
4796
{
 
4797
#ifndef QT_NO_MIMECLIPBOARD
 
4798
    QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode);
 
4799
    pasteSubType(subtype, m);
 
4800
#endif
 
4801
}
 
4802
 
 
4803
/*! \internal */
 
4804
 
 
4805
void Q3TextEdit::pasteSubType(const QByteArray& subtype, QMimeSource *m)
 
4806
{
 
4807
#ifndef QT_NO_MIME
 
4808
    QByteArray st = subtype;
 
4809
 
 
4810
    if (subtype != "x-qrichtext")
 
4811
        st.prepend("text/");
 
4812
    else
 
4813
        st.prepend("application/");
 
4814
    if (!m)
 
4815
        return;
 
4816
    if (doc->hasSelection(Q3TextDocument::Standard))
 
4817
        removeSelectedText();
 
4818
    if (!Q3RichTextDrag::canDecode(m))
 
4819
        return;
 
4820
    QString t;
 
4821
    if (!Q3RichTextDrag::decode(m, t, st, subtype))
 
4822
        return;
 
4823
    if (st == "application/x-qrichtext") {
 
4824
        int start;
 
4825
        if ((start = t.indexOf("<!--StartFragment-->")) != -1) {
 
4826
            start += 20;
 
4827
            int end = t.indexOf("<!--EndFragment-->");
 
4828
            Q3TextCursor oldC = *cursor;
 
4829
 
 
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();
 
4839
 
 
4840
            if (start < end)
 
4841
                t = t.mid(start, end - start);
 
4842
            else
 
4843
                t = t.mid(start);
 
4844
            lastFormatted = cursor->paragraph();
 
4845
            if (lastFormatted->prev())
 
4846
                lastFormatted = lastFormatted->prev();
 
4847
            doc->setRichTextInternal(t, cursor);
 
4848
 
 
4849
            // the first para might have been deleted in
 
4850
            // setRichTextInternal(). To be sure, reset it if
 
4851
            // necessary.
 
4852
            if (wasAtFirst) {
 
4853
                int index = oldC.index();
 
4854
                oldC.setParagraph(doc->firstParagraph());
 
4855
                oldC.setIndex(index);
 
4856
            }
 
4857
 
 
4858
            // if we went back one letter before (see last comment),
 
4859
            // go one forward to point to the right position
 
4860
            if (couldGoBack)
 
4861
                oldC.gotoNextLetter();
 
4862
 
 
4863
            if (undoEnabled && !isReadOnly()) {
 
4864
                doc->setSelectionStart(Q3TextDocument::Temp, oldC);
 
4865
                doc->setSelectionEnd(Q3TextDocument::Temp, *cursor);
 
4866
 
 
4867
                checkUndoRedoInfo(UndoRedoInfo::Insert);
 
4868
                if (!undoRedoInfo.valid()) {
 
4869
                    undoRedoInfo.id = oldC.paragraph()->paragId();
 
4870
                    undoRedoInfo.index = oldC.index();
 
4871
                    undoRedoInfo.d->text.clear();
 
4872
                }
 
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);
 
4882
                        }
 
4883
                        oldC.gotoNextLetter();
 
4884
                    }
 
4885
                }
 
4886
                undoRedoInfo.clear();
 
4887
                removeSelection(Q3TextDocument::Temp);
 
4888
            }
 
4889
 
 
4890
            formatMore();
 
4891
            setModified();
 
4892
            emit textChanged();
 
4893
            repaintChanged();
 
4894
            ensureCursorVisible();
 
4895
            return;
 
4896
        }
 
4897
    } else {
 
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');
 
4904
#endif
 
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')
 
4908
                uc[i] = ' ';
 
4909
        }
 
4910
        if (!t.isEmpty())
 
4911
            insert(t, false, true);
 
4912
    }
 
4913
#endif //QT_NO_MIME
 
4914
}
 
4915
 
 
4916
#ifndef QT_NO_MIMECLIPBOARD
 
4917
/*!
 
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.
 
4922
*/
 
4923
void Q3TextEdit::pasteSpecial(const QPoint& pt)
 
4924
{
 
4925
    QByteArray st = pickSpecial(QApplication::clipboard()->data(d->clipboard_mode),
 
4926
                               true, pt);
 
4927
    if (!st.isEmpty())
 
4928
        pasteSubType(st);
 
4929
}
 
4930
#endif
 
4931
#ifndef QT_NO_MIME
 
4932
QByteArray Q3TextEdit::pickSpecial(QMimeSource* ms, bool always_ask, const QPoint& pt)
 
4933
{
 
4934
    if (ms)  {
 
4935
#ifndef QT_NO_MENU
 
4936
        QMenu popup(this);
 
4937
        QString fmt;
 
4938
        int n = 0;
 
4939
        QHash<QString, bool> done;
 
4940
        for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
 
4941
            int semi = fmt.indexOf(';');
 
4942
            if (semi >= 0)
 
4943
                fmt = fmt.left(semi);
 
4944
            if (fmt.left(5) == "text/") {
 
4945
                fmt = fmt.mid(5);
 
4946
                if (!done.contains(fmt)) {
 
4947
                    done.insert(fmt,true);
 
4948
                    popup.insertItem(fmt, i);
 
4949
                    n++;
 
4950
                }
 
4951
            }
 
4952
        }
 
4953
        if (n) {
 
4954
            QAction *action = (n == 1 && !always_ask)
 
4955
                ? popup.actions().at(0)
 
4956
                : popup.exec(pt);
 
4957
            if (action)
 
4958
                return action->text().toLatin1();
 
4959
        }
 
4960
#else
 
4961
        QString fmt;
 
4962
        for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) {
 
4963
            int semi = fmt.indexOf(';');
 
4964
            if (semi >= 0)
 
4965
                fmt = fmt.left(semi);
 
4966
            if (fmt.left(5) == "text/") {
 
4967
                fmt = fmt.mid(5);
 
4968
                return fmt.latin1();
 
4969
            }
 
4970
        }
 
4971
#endif
 
4972
    }
 
4973
    return QByteArray();
 
4974
}
 
4975
#endif // QT_NO_MIME
 
4976
#endif // QT_NO_CLIPBOARD
 
4977
 
 
4978
/*!
 
4979
    \enum Q3TextEdit::WordWrap
 
4980
 
 
4981
    This enum defines the Q3TextEdit's word wrap modes.
 
4982
 
 
4983
    \value NoWrap Do not wrap the text.
 
4984
 
 
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().
 
4988
 
 
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().
 
4992
 
 
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.
 
4999
 
 
5000
    \sa setWordWrap() wordWrap()
 
5001
*/
 
5002
 
 
5003
/*!
 
5004
    \property Q3TextEdit::wordWrap
 
5005
    \brief the word wrap mode
 
5006
 
 
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.
 
5013
 
 
5014
    \sa WordWrap, wrapColumnOrWidth, wrapPolicy,
 
5015
*/
 
5016
 
 
5017
void Q3TextEdit::setWordWrap(WordWrap mode)
 
5018
{
 
5019
    if (wrapMode == mode)
 
5020
        return;
 
5021
    wrapMode = mode;
 
5022
    switch (mode) {
 
5023
    case NoWrap:
 
5024
        document()->formatter()->setWrapEnabled(false);
 
5025
        document()->formatter()->setWrapAtColumn(-1);
 
5026
        doc->setWidth(visibleWidth());
 
5027
        doc->setMinimumWidth(-1);
 
5028
        doc->invalidate();
 
5029
        updateContents();
 
5030
        lastFormatted = doc->firstParagraph();
 
5031
        interval = 0;
 
5032
        formatMore();
 
5033
        break;
 
5034
    case WidgetWidth:
 
5035
        document()->formatter()->setWrapEnabled(true);
 
5036
        document()->formatter()->setWrapAtColumn(-1);
 
5037
        doResize();
 
5038
        break;
 
5039
    case FixedPixelWidth:
 
5040
        document()->formatter()->setWrapEnabled(true);
 
5041
        document()->formatter()->setWrapAtColumn(-1);
 
5042
        if (wrapWidth < 0)
 
5043
            wrapWidth = 200;
 
5044
        setWrapColumnOrWidth(wrapWidth);
 
5045
        break;
 
5046
    case FixedColumnWidth:
 
5047
        if (wrapWidth < 0)
 
5048
            wrapWidth = 80;
 
5049
        document()->formatter()->setWrapEnabled(true);
 
5050
        document()->formatter()->setWrapAtColumn(wrapWidth);
 
5051
        setWrapColumnOrWidth(wrapWidth);
 
5052
        break;
 
5053
    }
 
5054
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5055
    checkOptimMode();
 
5056
#endif
 
5057
}
 
5058
 
 
5059
Q3TextEdit::WordWrap Q3TextEdit::wordWrap() const
 
5060
{
 
5061
    return wrapMode;
 
5062
}
 
5063
 
 
5064
/*!
 
5065
    \property Q3TextEdit::wrapColumnOrWidth
 
5066
    \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped
 
5067
 
 
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.
 
5073
 
 
5074
    \sa wordWrap
 
5075
*/
 
5076
void Q3TextEdit::setWrapColumnOrWidth(int value)
 
5077
{
 
5078
    wrapWidth = 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);
 
5089
    } else {
 
5090
        return;
 
5091
    }
 
5092
    doc->invalidate();
 
5093
    updateContents();
 
5094
    lastFormatted = doc->firstParagraph();
 
5095
    interval = 0;
 
5096
    formatMore();
 
5097
}
 
5098
 
 
5099
int Q3TextEdit::wrapColumnOrWidth() const
 
5100
{
 
5101
    if (wrapMode == WidgetWidth)
 
5102
        return visibleWidth();
 
5103
    return wrapWidth;
 
5104
}
 
5105
 
 
5106
 
 
5107
/*!
 
5108
    \enum Q3TextEdit::WrapPolicy
 
5109
 
 
5110
    This enum defines where text can be wrapped in word wrap mode.
 
5111
 
 
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
 
5116
    newlines
 
5117
    \value AtWordOrDocumentBoundary Break lines at whitespace, e.g.
 
5118
    spaces or newlines if possible. Break it anywhere otherwise.
 
5119
 
 
5120
    \sa setWrapPolicy()
 
5121
*/
 
5122
 
 
5123
/*!
 
5124
    \property Q3TextEdit::wrapPolicy
 
5125
    \brief the word wrap policy, at whitespace or anywhere
 
5126
 
 
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
 
5130
 
 
5131
    \sa wordWrap
 
5132
*/
 
5133
 
 
5134
void Q3TextEdit::setWrapPolicy(WrapPolicy policy)
 
5135
{
 
5136
    if (wPolicy == policy)
 
5137
        return;
 
5138
    wPolicy = policy;
 
5139
    Q3TextFormatter *formatter;
 
5140
    if (policy == AtWordBoundary || policy == AtWordOrDocumentBoundary) {
 
5141
        formatter = new Q3TextFormatterBreakWords;
 
5142
        formatter->setAllowBreakInWords(policy == AtWordOrDocumentBoundary);
 
5143
    } else {
 
5144
        formatter = new Q3TextFormatterBreakInWords;
 
5145
    }
 
5146
    formatter->setWrapAtColumn(document()->formatter()->wrapAtColumn());
 
5147
    formatter->setWrapEnabled(document()->formatter()->isWrapEnabled(0));
 
5148
    document()->setFormatter(formatter);
 
5149
    doc->invalidate();
 
5150
    updateContents();
 
5151
    lastFormatted = doc->firstParagraph();
 
5152
    interval = 0;
 
5153
    formatMore();
 
5154
}
 
5155
 
 
5156
Q3TextEdit::WrapPolicy Q3TextEdit::wrapPolicy() const
 
5157
{
 
5158
    return wPolicy;
 
5159
}
 
5160
 
 
5161
/*!
 
5162
    Deletes all the text in the text edit.
 
5163
 
 
5164
    \sa cut() removeSelectedText() setText()
 
5165
*/
 
5166
 
 
5167
void Q3TextEdit::clear()
 
5168
{
 
5169
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5170
    if (d->optimMode) {
 
5171
        optimSetText("");
 
5172
    } else
 
5173
#endif
 
5174
    {
 
5175
        // make clear undoable
 
5176
        doc->selectAll(Q3TextDocument::Temp);
 
5177
        removeSelectedText(Q3TextDocument::Temp);
 
5178
        setContentsPos(0, 0);
 
5179
        if (cursor->isValid())
 
5180
            cursor->restoreState();
 
5181
        doc->clear(true);
 
5182
        delete cursor;
 
5183
        cursor = new Q3TextCursor(doc);
 
5184
        lastFormatted = 0;
 
5185
    }
 
5186
    updateContents();
 
5187
 
 
5188
    emit cursorPositionChanged(cursor);
 
5189
    emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index());
 
5190
}
 
5191
 
 
5192
int Q3TextEdit::undoDepth() const
 
5193
{
 
5194
    return document()->undoDepth();
 
5195
}
 
5196
 
 
5197
/*!
 
5198
    \property Q3TextEdit::length
 
5199
    \brief the number of characters in the text
 
5200
*/
 
5201
 
 
5202
int Q3TextEdit::length() const
 
5203
{
 
5204
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5205
    if (d->optimMode)
 
5206
        return d->od->len;
 
5207
    else
 
5208
#endif
 
5209
        return document()->length();
 
5210
}
 
5211
 
 
5212
/*!
 
5213
    \property Q3TextEdit::tabStopWidth
 
5214
    \brief the tab stop width in pixels
 
5215
*/
 
5216
 
 
5217
int Q3TextEdit::tabStopWidth() const
 
5218
{
 
5219
    return document()->tabStopWidth();
 
5220
}
 
5221
 
 
5222
void Q3TextEdit::setUndoDepth(int d)
 
5223
{
 
5224
    document()->setUndoDepth(d);
 
5225
}
 
5226
 
 
5227
void Q3TextEdit::setTabStopWidth(int ts)
 
5228
{
 
5229
    document()->setTabStops(ts);
 
5230
    doc->invalidate();
 
5231
    lastFormatted = doc->firstParagraph();
 
5232
    interval = 0;
 
5233
    formatMore();
 
5234
    updateContents();
 
5235
}
 
5236
 
 
5237
/*!
 
5238
    \reimp
 
5239
*/
 
5240
 
 
5241
QSize Q3TextEdit::sizeHint() const
 
5242
{
 
5243
    // cf. Q3ScrollView::sizeHint()
 
5244
    ensurePolished();
 
5245
    int f = 2 * frameWidth();
 
5246
    int h = fontMetrics().height();
 
5247
    QSize sz(f, f);
 
5248
    return sz.expandedTo(QSize(12 * h, 8 * h));
 
5249
}
 
5250
 
 
5251
void Q3TextEdit::clearUndoRedo()
 
5252
{
 
5253
    if (!undoEnabled)
 
5254
        return;
 
5255
    undoRedoInfo.clear();
 
5256
    emit undoAvailable(doc->commands()->isUndoAvailable());
 
5257
    emit redoAvailable(doc->commands()->isRedoAvailable());
 
5258
}
 
5259
 
 
5260
/*!  \internal
 
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.
 
5264
 
 
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.
 
5269
 
 
5270
  Returns false if \a para or \a index is out of range otherwise
 
5271
  returns true.
 
5272
*/
 
5273
 
 
5274
bool Q3TextEdit::getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment)
 
5275
{
 
5276
    if (!font || !color)
 
5277
        return false;
 
5278
    Q3TextParagraph *p = doc->paragAt(para);
 
5279
    if (!p)
 
5280
        return false;
 
5281
    if (index < 0 || index >= p->length())
 
5282
        return false;
 
5283
    *font = p->at(index)->format()->font();
 
5284
    *color = p->at(index)->format()->color();
 
5285
    *verticalAlignment = (VerticalAlignment)p->at(index)->format()->vAlign();
 
5286
    return true;
 
5287
}
 
5288
 
 
5289
/*!  \internal
 
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.
 
5293
 
 
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).
 
5302
 
 
5303
  Returns false if \a para is out of range otherwise returns true.
 
5304
*/
 
5305
 
 
5306
bool Q3TextEdit::getParagraphFormat(int para, QFont *font, QColor *color,
 
5307
                                    VerticalAlignment *verticalAlignment, int *alignment,
 
5308
                                    Q3StyleSheetItem::DisplayMode *displayMode,
 
5309
                                    Q3StyleSheetItem::ListStyle *listStyle,
 
5310
                                    int *listDepth)
 
5311
{
 
5312
    if (!font || !color || !alignment || !displayMode || !listStyle)
 
5313
        return false;
 
5314
    Q3TextParagraph *p = doc->paragAt(para);
 
5315
    if (!p)
 
5316
        return false;
 
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();
 
5324
    return true;
 
5325
}
 
5326
 
 
5327
 
 
5328
 
 
5329
/*!
 
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.
 
5334
*/
 
5335
 
 
5336
Q3PopupMenu *Q3TextEdit::createPopupMenu(const QPoint& pos)
 
5337
{
 
5338
    Q_UNUSED(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();
 
5345
    }
 
5346
#ifndef QT_NO_CLIPBOARD
 
5347
    if (!isReadOnly())
 
5348
        d->id[IdCut] = popup->insertItem(tr("Cu&t") + ACCEL_KEY(X));
 
5349
    d->id[IdCopy] = popup->insertItem(tr("&Copy") + ACCEL_KEY(C));
 
5350
    if (!isReadOnly())
 
5351
        d->id[IdPaste] = popup->insertItem(tr("&Paste") + ACCEL_KEY(V));
 
5352
#endif
 
5353
    if (!isReadOnly()) {
 
5354
        d->id[IdClear] = popup->insertItem(tr("Clear"));
 
5355
        popup->addSeparator();
 
5356
    }
 
5357
#if defined(Q_WS_X11)
 
5358
    d->id[IdSelectAll] = popup->insertItem(tr("Select All"));
 
5359
#else
 
5360
    d->id[IdSelectAll] = popup->insertItem(tr("Select All") + ACCEL_KEY(A));
 
5361
#endif
 
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));
 
5368
#else
 
5369
    popup->setItemEnabled(d->id[IdCopy], doc->hasSelection(Q3TextDocument::Standard, true));
 
5370
#endif
 
5371
    popup->setItemEnabled(d->id[IdPaste], !isReadOnly() && !QApplication::clipboard()->text(d->clipboard_mode).isEmpty());
 
5372
#endif
 
5373
    const bool isEmptyDocument = (length() == 0);
 
5374
    popup->setItemEnabled(d->id[IdClear], !isReadOnly() && !isEmptyDocument);
 
5375
    popup->setItemEnabled(d->id[IdSelectAll], !isEmptyDocument);
 
5376
    return popup;
 
5377
#else
 
5378
    return 0;
 
5379
#endif
 
5380
}
 
5381
 
 
5382
/*! \overload
 
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.
 
5387
 
 
5388
    This function is only called if createPopupMenu(const QPoint &)
 
5389
    returns 0.
 
5390
*/
 
5391
 
 
5392
Q3PopupMenu *Q3TextEdit::createPopupMenu()
 
5393
{
 
5394
    return 0;
 
5395
}
 
5396
 
 
5397
/*!
 
5398
    \fn Q3TextEdit::zoomIn()
 
5399
 
 
5400
    \overload
 
5401
 
 
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.
 
5405
 
 
5406
    \sa zoomOut()
 
5407
*/
 
5408
 
 
5409
/*!
 
5410
    \fn Q3TextEdit::zoomOut()
 
5411
 
 
5412
    \overload
 
5413
 
 
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.
 
5417
 
 
5418
    \sa zoomIn()
 
5419
*/
 
5420
 
 
5421
 
 
5422
/*!
 
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.
 
5426
 
 
5427
    \sa zoomOut()
 
5428
*/
 
5429
 
 
5430
void Q3TextEdit::zoomIn(int range)
 
5431
{
 
5432
    QFont f(Q3ScrollView::font());
 
5433
    f.setPointSize(f.pointSize() + range);
 
5434
    setFont(f);
 
5435
}
 
5436
 
 
5437
/*!
 
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.
 
5441
 
 
5442
    \sa zoomIn()
 
5443
*/
 
5444
 
 
5445
void Q3TextEdit::zoomOut(int range)
 
5446
{
 
5447
    QFont f(Q3ScrollView::font());
 
5448
    f.setPointSize(qMax(1, f.pointSize() - range));
 
5449
    setFont(f);
 
5450
}
 
5451
 
 
5452
/*!
 
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.
 
5456
*/
 
5457
 
 
5458
void Q3TextEdit::zoomTo(int size)
 
5459
{
 
5460
    QFont f(Q3ScrollView::font());
 
5461
    f.setPointSize(size);
 
5462
    setFont(f);
 
5463
}
 
5464
 
 
5465
/*!
 
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
 
5469
   call this function.
 
5470
 
 
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.
 
5475
*/
 
5476
 
 
5477
void Q3TextEdit::sync()
 
5478
{
 
5479
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5480
    if (d->optimMode) {
 
5481
        QFontMetrics fm(Q3ScrollView::font());
 
5482
        resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
 
5483
    } else
 
5484
#endif
 
5485
    {
 
5486
        while (lastFormatted) {
 
5487
            lastFormatted->format();
 
5488
            lastFormatted = lastFormatted->next();
 
5489
        }
 
5490
        resizeContents(contentsWidth(), doc->height());
 
5491
    }
 
5492
    updateScrollBars();
 
5493
}
 
5494
 
 
5495
/*!
 
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.
 
5499
 
 
5500
    This only works for \a selNum > 0. The default selection (\a
 
5501
    selNum == 0) gets its attributes from the text edit's
 
5502
    palette().
 
5503
*/
 
5504
 
 
5505
void Q3TextEdit::setSelectionAttributes(int selNum, const QColor &back, bool invertText)
 
5506
{
 
5507
    if (selNum < 1)
 
5508
        return;
 
5509
    if (selNum > doc->numSelections())
 
5510
        doc->addSelection(selNum);
 
5511
    doc->setSelectionColor(selNum, back);
 
5512
    if (invertText)
 
5513
        doc->setSelectionTextColor(selNum, palette().color(QPalette::HighlightedText));
 
5514
}
 
5515
 
 
5516
/*!
 
5517
    \reimp
 
5518
*/
 
5519
void Q3TextEdit::changeEvent(QEvent *ev)
 
5520
{
 
5521
    if(ev->type() == QEvent::ActivationChange) {
 
5522
        if (!isActiveWindow() && scrollTimer)
 
5523
            scrollTimer->stop();
 
5524
        if (!palette().isEqual(QPalette::Active, QPalette::Inactive))
 
5525
            updateContents();
 
5526
    }
 
5527
 
 
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());
 
5535
        int i, sw;
 
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;
 
5541
        }
 
5542
        resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
 
5543
        return;
 
5544
    }
 
5545
#endif
 
5546
 
 
5547
    Q3ScrollView::changeEvent(ev);
 
5548
 
 
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());
 
5554
            updateContents();
 
5555
        }
 
5556
    }
 
5557
 
 
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();
 
5562
        formatMore();
 
5563
        repaintChanged();
 
5564
    }
 
5565
}
 
5566
 
 
5567
void Q3TextEdit::setReadOnly(bool b)
 
5568
{
 
5569
    if (readonly == b)
 
5570
        return;
 
5571
    readonly = b;
 
5572
    d->cursorBlinkActive = !b;
 
5573
#ifndef QT_NO_CURSOR
 
5574
    if (readonly)
 
5575
        viewport()->setCursor(Qt::ArrowCursor);
 
5576
    else
 
5577
        viewport()->setCursor(Qt::IBeamCursor);
 
5578
    setInputMethodEnabled(!readonly);
 
5579
#endif
 
5580
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5581
    checkOptimMode();
 
5582
#endif
 
5583
}
 
5584
 
 
5585
/*!
 
5586
    Scrolls to the bottom of the document and does formatting if
 
5587
    required.
 
5588
*/
 
5589
 
 
5590
void Q3TextEdit::scrollToBottom()
 
5591
{
 
5592
    sync();
 
5593
    setContentsPos(contentsX(), contentsHeight() - visibleHeight());
 
5594
}
 
5595
 
 
5596
/*!
 
5597
    Returns the rectangle of the paragraph \a para in contents
 
5598
    coordinates, or an invalid rectangle if \a para is out of range.
 
5599
*/
 
5600
 
 
5601
QRect Q3TextEdit::paragraphRect(int para) const
 
5602
{
 
5603
    Q3TextEdit *that = (Q3TextEdit *)this;
 
5604
    that->sync();
 
5605
    Q3TextParagraph *p = doc->paragAt(para);
 
5606
    if (!p)
 
5607
        return QRect(-1, -1, -1, -1);
 
5608
    return p->rect();
 
5609
}
 
5610
 
 
5611
/*!
 
5612
    Returns the paragraph which is at position \a pos (in contents
 
5613
    coordinates).
 
5614
*/
 
5615
 
 
5616
int Q3TextEdit::paragraphAt(const QPoint &pos) const
 
5617
{
 
5618
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5619
    if (d->optimMode) {
 
5620
        QFontMetrics fm(Q3ScrollView::font());
 
5621
        int parag = pos.y() / fm.lineSpacing();
 
5622
        if (parag <= d->od->numLines)
 
5623
            return parag;
 
5624
        else
 
5625
            return 0;
 
5626
    }
 
5627
#endif
 
5628
    Q3TextCursor c(doc);
 
5629
    c.place(pos, doc->firstParagraph());
 
5630
    if (c.paragraph())
 
5631
        return c.paragraph()->paragId();
 
5632
    return -1; // should never happen..
 
5633
}
 
5634
 
 
5635
/*!
 
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.
 
5639
*/
 
5640
 
 
5641
int Q3TextEdit::charAt(const QPoint &pos, int *para) const
 
5642
{
 
5643
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5644
    if (d->optimMode) {
 
5645
        int par = paragraphAt(pos);
 
5646
        if (para)
 
5647
            *para = par;
 
5648
        return optimCharIndex(d->od->lines[LOGOFFSET(par)], pos.x());
 
5649
    }
 
5650
#endif
 
5651
    Q3TextCursor c(doc);
 
5652
    c.place(pos, doc->firstParagraph());
 
5653
    if (c.paragraph()) {
 
5654
        if (para)
 
5655
            *para = c.paragraph()->paragId();
 
5656
        return c.index();
 
5657
    }
 
5658
    return -1; // should never happen..
 
5659
}
 
5660
 
 
5661
/*!
 
5662
    Sets the background color of the paragraph \a para to \a bg.
 
5663
*/
 
5664
 
 
5665
void Q3TextEdit::setParagraphBackgroundColor(int para, const QColor &bg)
 
5666
{
 
5667
    Q3TextParagraph *p = doc->paragAt(para);
 
5668
    if (!p)
 
5669
        return;
 
5670
    p->setBackgroundColor(bg);
 
5671
    repaintChanged();
 
5672
}
 
5673
 
 
5674
/*!
 
5675
    Clears the background color of the paragraph \a para, so that the
 
5676
    default color is used again.
 
5677
*/
 
5678
 
 
5679
void Q3TextEdit::clearParagraphBackground(int para)
 
5680
{
 
5681
    Q3TextParagraph *p = doc->paragAt(para);
 
5682
    if (!p)
 
5683
        return;
 
5684
    p->clearBackgroundColor();
 
5685
    repaintChanged();
 
5686
}
 
5687
 
 
5688
/*!
 
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
 
5691
    background set
 
5692
*/
 
5693
 
 
5694
QColor Q3TextEdit::paragraphBackgroundColor(int para) const
 
5695
{
 
5696
    Q3TextParagraph *p = doc->paragAt(para);
 
5697
    if (!p)
 
5698
        return QColor();
 
5699
    QColor *c = p->backgroundColor();
 
5700
    if (c)
 
5701
        return *c;
 
5702
    return QColor();
 
5703
}
 
5704
 
 
5705
/*!
 
5706
    \property Q3TextEdit::undoRedoEnabled
 
5707
    \brief whether undo/redo is enabled
 
5708
 
 
5709
    When changing this property, the undo/redo history is cleared.
 
5710
 
 
5711
    The default is true.
 
5712
*/
 
5713
 
 
5714
void Q3TextEdit::setUndoRedoEnabled(bool b)
 
5715
{
 
5716
    undoRedoInfo.clear();
 
5717
    doc->commands()->clear();
 
5718
 
 
5719
    undoEnabled = b;
 
5720
}
 
5721
 
 
5722
bool Q3TextEdit::isUndoRedoEnabled() const
 
5723
{
 
5724
    return undoEnabled;
 
5725
}
 
5726
 
 
5727
/*!
 
5728
    Returns true if undo is available; otherwise returns false.
 
5729
*/
 
5730
 
 
5731
bool Q3TextEdit::isUndoAvailable() const
 
5732
{
 
5733
    return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid());
 
5734
}
 
5735
 
 
5736
/*!
 
5737
    Returns true if redo is available; otherwise returns false.
 
5738
*/
 
5739
 
 
5740
bool Q3TextEdit::isRedoAvailable() const
 
5741
{
 
5742
    return undoEnabled && doc->commands()->isRedoAvailable();
 
5743
}
 
5744
 
 
5745
void Q3TextEdit::ensureFormatted(Q3TextParagraph *p)
 
5746
{
 
5747
    while (!p->isValid()) {
 
5748
        if (!lastFormatted)
 
5749
            return;
 
5750
        formatMore();
 
5751
    }
 
5752
}
 
5753
 
 
5754
/*! \internal */
 
5755
void Q3TextEdit::updateCursor(const QPoint & pos)
 
5756
{
 
5757
    if (isReadOnly() && linksEnabled()) {
 
5758
        Q3TextCursor c = *cursor;
 
5759
        placeCursor(pos, &c, true);
 
5760
 
 
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();
 
5771
            else
 
5772
                onLink.clear();
 
5773
 
 
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();
 
5777
            else
 
5778
                d->onName.clear();
 
5779
 
 
5780
            if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()) {
 
5781
#ifndef QT_NO_CURSOR
 
5782
                viewport()->setCursor(onLink.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor);
 
5783
#endif
 
5784
                QUrl u = QUrl(doc->context()).resolved(onLink);
 
5785
                emitHighlighted(u.toString(QUrl::None));
 
5786
            }
 
5787
        } else {
 
5788
#ifndef QT_NO_CURSOR
 
5789
            viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
 
5790
#endif
 
5791
            onLink.clear();
 
5792
            emitHighlighted(QString());
 
5793
        }
 
5794
#endif
 
5795
    }
 
5796
}
 
5797
 
 
5798
/*!
 
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
 
5801
  cursor is used.
 
5802
 
 
5803
  \sa setCursorPosition()
 
5804
*/
 
5805
void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c)
 
5806
{
 
5807
    placeCursor(pos, c, false);
 
5808
}
 
5809
 
 
5810
/*! \internal */
 
5811
void Q3TextEdit::clipboardChanged()
 
5812
{
 
5813
#ifndef QT_NO_CLIPBOARD
 
5814
    // don't listen to selection changes
 
5815
    disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
 
5816
#endif
 
5817
    selectAll(false);
 
5818
}
 
5819
 
 
5820
/*! \property Q3TextEdit::tabChangesFocus
 
5821
  \brief whether TAB changes focus or is accepted as input
 
5822
 
 
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.
 
5826
 
 
5827
*/
 
5828
 
 
5829
void Q3TextEdit::setTabChangesFocus(bool b)
 
5830
{
 
5831
    d->tabChangesFocus = b;
 
5832
}
 
5833
 
 
5834
bool Q3TextEdit::tabChangesFocus() const
 
5835
{
 
5836
    return d->tabChangesFocus;
 
5837
}
 
5838
 
 
5839
#ifdef QT_TEXTEDIT_OPTIMIZATION
 
5840
/* Implementation of optimized Qt::LogText mode follows */
 
5841
 
 
5842
static void qSwap(int * a, int * b)
 
5843
{
 
5844
    if (!a || !b)
 
5845
        return;
 
5846
    int tmp = *a;
 
5847
    *a = *b;
 
5848
    *b = tmp;
 
5849
}
 
5850
 
 
5851
/*! \internal */
 
5852
bool Q3TextEdit::checkOptimMode()
 
5853
{
 
5854
    bool oldMode = d->optimMode;
 
5855
    if (textFormat() == Qt::LogText) {
 
5856
        d->optimMode = true;
 
5857
        setReadOnly(true);
 
5858
    } else {
 
5859
        d->optimMode = false;
 
5860
    }
 
5861
 
 
5862
    // when changing mode - try to keep selections and text
 
5863
    if (oldMode != d->optimMode) {
 
5864
        if (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());
 
5871
            doc->clear(true);
 
5872
            delete cursor;
 
5873
            cursor = new Q3TextCursor(doc);
 
5874
        } else {
 
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());
 
5880
            delete d->od;
 
5881
            d->od = 0;
 
5882
        }
 
5883
    }
 
5884
    return d->optimMode;
 
5885
}
 
5886
 
 
5887
/*! \internal */
 
5888
QString Q3TextEdit::optimText() const
 
5889
{
 
5890
    QString str, tmp;
 
5891
 
 
5892
    if (d->od->len == 0)
 
5893
        return str;
 
5894
 
 
5895
    // concatenate all strings
 
5896
    int i;
 
5897
    int offset;
 
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
 
5902
            str += "\n";
 
5903
        } else {
 
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())
 
5907
                ftag = it.value();
 
5908
            offset = 0;
 
5909
            while (ftag && ftag->line == i) {
 
5910
                tmp.insert(ftag->index + offset, "<" + ftag->tag + ">");
 
5911
                offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars
 
5912
                ftag = ftag->next;
 
5913
            }
 
5914
            str += tmp;
 
5915
        }
 
5916
    }
 
5917
    return str;
 
5918
}
 
5919
 
 
5920
/*! \internal */
 
5921
void Q3TextEdit::optimSetText(const QString &str)
 
5922
{
 
5923
    optimRemoveSelection();
 
5924
// this is just too slow - but may have to go in due to compatibility reasons
 
5925
//     if (str == optimText())
 
5926
//         return;
 
5927
    d->od->numLines = 0;
 
5928
    d->od->lines.clear();
 
5929
    d->od->maxLineWidth = 0;
 
5930
    d->od->len = 0;
 
5931
    d->od->clearTags();
 
5932
    QFontMetrics fm(Q3ScrollView::font());
 
5933
    if (!(str.isEmpty() || str.isNull() || d->maxLogLines == 0)) {
 
5934
        QStringList strl = str.split('\n');
 
5935
        int lWidth = 0;
 
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;
 
5942
        }
 
5943
    }
 
5944
    resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
 
5945
    repaintContents();
 
5946
    emit textChanged();
 
5947
}
 
5948
 
 
5949
/*! \internal
 
5950
 
 
5951
  Append \a tag to the tag list.
 
5952
*/
 
5953
Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimAppendTag(int index, const QString & tag)
 
5954
{
 
5955
    Q3TextEditOptimPrivate::Tag * t = new Q3TextEditOptimPrivate::Tag, * tmp;
 
5956
 
 
5957
    if (d->od->tags == 0)
 
5958
        d->od->tags = t;
 
5959
    t->bold = t->italic = t->underline = false;
 
5960
    t->line  = d->od->numLines;
 
5961
    t->index = index;
 
5962
    t->tag   = tag;
 
5963
    t->leftTag = 0;
 
5964
    t->parent  = 0;
 
5965
    t->prev = d->od->lastTag;
 
5966
    if (d->od->lastTag)
 
5967
        d->od->lastTag->next = t;
 
5968
    t->next = 0;
 
5969
    d->od->lastTag = 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);
 
5973
    }
 
5974
    return t;
 
5975
}
 
5976
 
 
5977
/*! \internal
 
5978
 
 
5979
  Insert \a tag in the tag - according to line and index numbers
 
5980
*/
 
5981
Q3TextEditOptimPrivate::Tag *Q3TextEdit::optimInsertTag(int line, int index, const QString &tag)
 
5982
{
 
5983
    Q3TextEditOptimPrivate::Tag *t = new Q3TextEditOptimPrivate::Tag, *tmp;
 
5984
 
 
5985
    if (d->od->tags == 0)
 
5986
        d->od->tags = t;
 
5987
    t->bold = t->italic = t->underline = false;
 
5988
    t->line  = line;
 
5989
    t->index = index;
 
5990
    t->tag   = tag;
 
5991
    t->leftTag = 0;
 
5992
    t->parent  = 0;
 
5993
    t->next = 0;
 
5994
    t->prev = 0;
 
5995
 
 
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()) {
 
5999
        tmp = *it;
 
6000
        if (tmp->index >= index) { // the exisiting tag may be placed AFTER the one we want to insert
 
6001
            tmp = tmp->prev;
 
6002
        } else {
 
6003
            while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index)
 
6004
                tmp = tmp->next;
 
6005
        }
 
6006
    } else {
 
6007
        tmp = d->od->tags;
 
6008
        while (tmp && tmp->next && tmp->next->line < line)
 
6009
            tmp = tmp->next;
 
6010
        if (tmp == d->od->tags)
 
6011
            tmp = 0;
 
6012
    }
 
6013
 
 
6014
    t->prev = tmp;
 
6015
    t->next = tmp ? tmp->next : 0;
 
6016
    if (t->next)
 
6017
        t->next->prev = t;
 
6018
    if (tmp)
 
6019
        tmp->next = t;
 
6020
 
 
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);
 
6024
    }
 
6025
    return t;
 
6026
}
 
6027
 
 
6028
/*! \internal
 
6029
 
 
6030
  Find tags in \a line, remove them from \a line and put them in a
 
6031
  structure.
 
6032
 
 
6033
  A tag is delimited by '<' and '>'. The characters '<', '>' and '&'
 
6034
  are escaped by using '&lt;', '&gt;' and '&amp;'. 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
 
6052
  blue.</font>
 
6053
 
 
6054
  Note that only the color attribute of the HTML font tag is supported.
 
6055
 
 
6056
  Limitations:
 
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.
 
6060
 
 
6061
*/
 
6062
void Q3TextEdit::optimParseTags(QString * line, int lineNo, int indexOffset)
 
6063
{
 
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;
 
6069
    QString tagStr;
 
6070
    QStack<Q3TextEditOptimPrivate::Tag *> tagStack;
 
6071
 
 
6072
    for (i = 0; i < len; i++) {
 
6073
        tagOpen = (*line)[i] == '<';
 
6074
        tagClose = (*line)[i] == '>';
 
6075
 
 
6076
        // handle '&lt;' and '&gt;' and '&amp;'
 
6077
        if ((*line)[i] == '&') {
 
6078
            escIndex = i;
 
6079
            continue;
 
6080
        } else if (escIndex != -1 && (*line)[i] == ';') {
 
6081
            QString esc = line->mid(escIndex, i - escIndex + 1);
 
6082
            QString c;
 
6083
            if (esc == "&lt;")
 
6084
                c = '<';
 
6085
            else if (esc == "&gt;")
 
6086
                c = '>';
 
6087
            else if (esc == "&amp;")
 
6088
                c = '&';
 
6089
            line->replace(escIndex, i - escIndex + 1, c);
 
6090
            len = line->length();
 
6091
            i -= i-escIndex;
 
6092
            escIndex = -1;
 
6093
            continue;
 
6094
        }
 
6095
 
 
6096
        if (state == 0 && tagOpen) {
 
6097
            state = 1;
 
6098
            startIndex = i;
 
6099
            continue;
 
6100
        }
 
6101
        if (state == 1 && tagClose) {
 
6102
            state = 0;
 
6103
            endIndex = i;
 
6104
            if (!tagStr.isEmpty()) {
 
6105
                Q3TextEditOptimPrivate::Tag * tag, * cur, * tmp;
 
6106
                bool format = true;
 
6107
 
 
6108
                if (tagStr == "b")
 
6109
                    bold++;
 
6110
                else if (tagStr == "/b")
 
6111
                    bold--;
 
6112
                else if (tagStr == "i")
 
6113
                    italic++;
 
6114
                else if (tagStr == "/i")
 
6115
                    italic--;
 
6116
                else if (tagStr == "u")
 
6117
                    underline++;
 
6118
                else if (tagStr == "/u")
 
6119
                    underline--;
 
6120
                else
 
6121
                    format = false;
 
6122
                if (lineNo > -1)
 
6123
                    tag = optimInsertTag(lineNo, startIndex + indexOffset, tagStr);
 
6124
                else
 
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
 
6133
                    cur = tag->prev;
 
6134
                    if (!cur) {
 
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
 
6138
                    }
 
6139
                    while (cur) {
 
6140
                        if (cur->leftTag) { // push right-tags encountered
 
6141
                            tagStack.push(cur);
 
6142
                        } else {
 
6143
                            tmp = tagStack.isEmpty() ? 0 : tagStack.pop();
 
6144
                            if (!tmp) {
 
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
 
6148
                                    tag->leftTag = cur;
 
6149
                                    tmp = cur->prev;
 
6150
                                    if (tmp && tmp->parent) {
 
6151
                                        tag->parent = tmp->parent;
 
6152
                                    } else if (tmp && !tmp->leftTag) {
 
6153
                                        tag->parent = tmp;
 
6154
                                    }
 
6155
                                    break;
 
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
 
6159
                                }
 
6160
                            }
 
6161
                        }
 
6162
                        cur = cur->prev;
 
6163
                    }
 
6164
                } else {
 
6165
                    tag->bold = bold > 0;
 
6166
                    tag->italic = italic > 0;
 
6167
                    tag->underline = underline > 0;
 
6168
                    tmp = tag->prev;
 
6169
                    while (tmp && tmp->leftTag) {
 
6170
                        tmp = tmp->leftTag->parent;
 
6171
                    }
 
6172
                    if (tmp) {
 
6173
                        tag->bold |= tmp->bold;
 
6174
                        tag->italic |= tmp->italic;
 
6175
                        tag->underline |= tmp->underline;
 
6176
                    }
 
6177
                }
 
6178
            }
 
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();
 
6184
                i -= l+1;
 
6185
            }
 
6186
            tagStr = "";
 
6187
            continue;
 
6188
        }
 
6189
 
 
6190
        if (state == 1) {
 
6191
            tagStr += (*line)[i];
 
6192
        }
 
6193
    }
 
6194
}
 
6195
 
 
6196
// calculate the width of a string in pixels inc. tabs
 
6197
static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm)
 
6198
{
 
6199
    int tabs = str.count('\t');
 
6200
 
 
6201
    if (!tabs)
 
6202
        return fm.width(str);
 
6203
 
 
6204
    int newIdx = 0;
 
6205
    int lastIdx = 0;
 
6206
    int strWidth = 0;
 
6207
    int tn;
 
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) {
 
6212
            int u = tn;
 
6213
            while (strWidth >= u * tabWidth)
 
6214
                ++u;
 
6215
            strWidth = u * tabWidth;
 
6216
        } else {
 
6217
            strWidth = tn * tabWidth;
 
6218
        }
 
6219
        lastIdx = ++newIdx;
 
6220
    }
 
6221
    if ((int)str.length() > newIdx)
 
6222
        strWidth += fm.width(str.mid(newIdx));
 
6223
    return strWidth;
 
6224
}
 
6225
 
 
6226
bool Q3TextEdit::optimHasBoldMetrics(int line)
 
6227
{
 
6228
    Q3TextEditOptimPrivate::Tag *t;
 
6229
    QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it;
 
6230
    if ((it = d->od->tagIndex.find(line)) != d->od->tagIndex.end()) {
 
6231
        t = *it;
 
6232
        while (t && t->line == line) {
 
6233
            if (t->bold)
 
6234
                return true;
 
6235
            t = t->next;
 
6236
        }
 
6237
    } else if ((t = optimPreviousLeftTag(line)) && t->bold) {
 
6238
        return true;
 
6239
    }
 
6240
    return false;
 
6241
}
 
6242
 
 
6243
/*! \internal
 
6244
 
 
6245
  Append \a str to the current text buffer. Parses each line to find
 
6246
  formatting tags.
 
6247
*/
 
6248
void Q3TextEdit::optimAppend(const QString &str)
 
6249
{
 
6250
    if (str.isEmpty() || str.isNull() || d->maxLogLines == 0)
 
6251
        return;
 
6252
 
 
6253
    QStringList strl = str.split('\n');
 
6254
    QStringList::Iterator it = strl.begin();
 
6255
 
 
6256
    QFontMetrics fm(Q3ScrollView::font());
 
6257
    int lWidth = 0;
 
6258
    for (; it != strl.end(); ++it) {
 
6259
        optimParseTags(&*it);
 
6260
        optimCheckLimit(*it);
 
6261
        if (optimHasBoldMetrics(d->od->numLines-1)) {
 
6262
            QFont fnt = Q3ScrollView::font();
 
6263
            fnt.setBold(true);
 
6264
            fm = QFontMetrics(fnt);
 
6265
        }
 
6266
        lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4;
 
6267
        if (lWidth > d->od->maxLineWidth)
 
6268
            d->od->maxLineWidth = lWidth;
 
6269
    }
 
6270
    bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight();
 
6271
    resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
 
6272
    if (scrollToEnd) {
 
6273
        updateScrollBars();
 
6274
        ensureVisible(contentsX(), contentsHeight(), 0, 0);
 
6275
    }
 
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();
 
6280
    emit textChanged();
 
6281
}
 
6282
 
 
6283
static void qStripTags(QString *line)
 
6284
{
 
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;
 
6289
 
 
6290
    for (i = 0; i < len; i++) {
 
6291
        tagOpen = (*line)[i] == '<';
 
6292
        tagClose = (*line)[i] == '>';
 
6293
 
 
6294
        // handle '&lt;' and '&gt;' and '&amp;'
 
6295
        if ((*line)[i] == '&') {
 
6296
            escIndex = i;
 
6297
            continue;
 
6298
        } else if (escIndex != -1 && (*line)[i] == ';') {
 
6299
            QString esc = line->mid(escIndex, i - escIndex + 1);
 
6300
            QString c;
 
6301
            if (esc == "&lt;")
 
6302
                c = '<';
 
6303
            else if (esc == "&gt;")
 
6304
                c = '>';
 
6305
            else if (esc == "&amp;")
 
6306
                c = '&';
 
6307
            line->replace(escIndex, i - escIndex + 1, c);
 
6308
            len = line->length();
 
6309
            i -= i-escIndex;
 
6310
            escIndex = -1;
 
6311
            continue;
 
6312
        }
 
6313
 
 
6314
        if (state == 0 && tagOpen) {
 
6315
            state = 1;
 
6316
            startIndex = i;
 
6317
            continue;
 
6318
        }
 
6319
        if (state == 1 && tagClose) {
 
6320
            state = 0;
 
6321
            endIndex = i;
 
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();
 
6327
                i -= l+1;
 
6328
            }
 
6329
            continue;
 
6330
        }
 
6331
    }
 
6332
}
 
6333
 
 
6334
/*! \internal
 
6335
 
 
6336
    Inserts the text into \a line at index \a index.
 
6337
*/
 
6338
 
 
6339
void Q3TextEdit::optimInsert(const QString& text, int line, int index)
 
6340
{
 
6341
    if (text.isEmpty() || d->maxLogLines == 0)
 
6342
        return;
 
6343
    if (line < 0)
 
6344
        line = 0;
 
6345
    if (line > d->od->numLines-1)
 
6346
        line = d->od->numLines-1;
 
6347
    if (index < 0)
 
6348
        index = 0;
 
6349
    if (index > d->od->lines[line].length())
 
6350
        index = d->od->lines[line].length();
 
6351
 
 
6352
    QStringList strl = text.split('\n');
 
6353
    int numNewLines = strl.count() - 1;
 
6354
    Q3TextEditOptimPrivate::Tag *tag = 0;
 
6355
    QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator ii;
 
6356
    int x;
 
6357
 
 
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()) {
 
6365
            tag = *ii;
 
6366
            while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
 
6367
                tag = tag->next;
 
6368
            while (tag && (LOGOFFSET(tag->line) == line)) {
 
6369
                tag->index += stripped.length();
 
6370
                tag = tag->next;
 
6371
            }
 
6372
        }
 
6373
        stripped = text;
 
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);
 
6380
 
 
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;
 
6385
 
 
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()) {
 
6390
                tag = ii.value();
 
6391
                if (LOGOFFSET(tag->line) == line)
 
6392
                    while (tag && (LOGOFFSET(tag->line) == line && tag->index < index))
 
6393
                        tag = tag->next;
 
6394
            }
 
6395
        }
 
6396
 
 
6397
        // relabel affected tags with new line numbers and new index
 
6398
        // positions
 
6399
        while (tag) {
 
6400
            if (LOGOFFSET(tag->line) == line)
 
6401
                tag->index -= index;
 
6402
            tag->line += numNewLines;
 
6403
            tag = tag->next;
 
6404
        }
 
6405
 
 
6406
        // generate a new tag index
 
6407
        d->od->tagIndex.clear();
 
6408
        tag = d->od->tags;
 
6409
        while (tag) {
 
6410
            if (!((ii = d->od->tagIndex.find(LOGOFFSET(tag->line))) != d->od->tagIndex.end()))
 
6411
                d->od->tagIndex[LOGOFFSET(tag->line)] = tag;
 
6412
            tag = tag->next;
 
6413
        }
 
6414
 
 
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()) {
 
6419
            tag = *ii;
 
6420
            while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) {
 
6421
                tag->index += stripped.length();
 
6422
                tag = tag->next;
 
6423
            }
 
6424
        }
 
6425
 
 
6426
        // inject the new lines
 
6427
        QStringList::Iterator it = strl.begin();
 
6428
        x = line;
 
6429
        int idx;
 
6430
        for (; it != strl.end(); ++it) {
 
6431
            stripped = *it;
 
6432
            qStripTags(&stripped);
 
6433
            if (x == line) {
 
6434
                stripped = left + stripped;
 
6435
                idx = index;
 
6436
            } else {
 
6437
                idx = 0;
 
6438
            }
 
6439
            d->od->lines[LOGOFFSET(x)] = stripped;
 
6440
            optimParseTags(&*it, x++, idx);
 
6441
        }
 
6442
        d->od->lines[LOGOFFSET(x - 1)] += right;
 
6443
    }
 
6444
    // recalculate the pixel width of the longest injected line -
 
6445
    QFontMetrics fm(Q3ScrollView::font());
 
6446
    int lWidth = 0;
 
6447
    for (x = line; x < line + numNewLines; x++) {
 
6448
        if (optimHasBoldMetrics(x)) {
 
6449
            QFont fnt = Q3ScrollView::font();
 
6450
            fnt.setBold(true);
 
6451
            fm = QFontMetrics(fnt);
 
6452
        }
 
6453
        lWidth = fm.width(d->od->lines[x]) + 4;
 
6454
        if (lWidth > d->od->maxLineWidth)
 
6455
            d->od->maxLineWidth = lWidth;
 
6456
    }
 
6457
    resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1);
 
6458
    repaintContents();
 
6459
    emit textChanged();
 
6460
}
 
6461
 
 
6462
 
 
6463
/*! \internal
 
6464
 
 
6465
  Returns the first open left-tag appearing before line \a line.
 
6466
 */
 
6467
Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimPreviousLeftTag(int line)
 
6468
{
 
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())
 
6472
        ftag = it.value();
 
6473
    if (!ftag) {
 
6474
        // start searching for an open tag
 
6475
        ftag = d->od->tags;
 
6476
        while (ftag) {
 
6477
            if (ftag->line > line || ftag->next == 0) {
 
6478
                if (ftag->line > line)
 
6479
                    ftag = ftag->prev;
 
6480
                break;
 
6481
            }
 
6482
            ftag = ftag->next;
 
6483
        }
 
6484
    } else {
 
6485
        ftag = ftag->prev;
 
6486
    }
 
6487
 
 
6488
    if (ftag) {
 
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
 
6492
            ftag = 0;
 
6493
    }
 
6494
    return ftag;
 
6495
}
 
6496
 
 
6497
/*! \internal
 
6498
 
 
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
 
6502
  color the string.
 
6503
*/
 
6504
void Q3TextEdit::optimSetTextFormat(Q3TextDocument * td, Q3TextCursor * cur,
 
6505
                                    Q3TextFormat * f, int start, int end,
 
6506
                                    Q3TextEditOptimPrivate::Tag * tag)
 
6507
{
 
6508
    int formatFlags = Q3TextFormat::Bold | Q3TextFormat::Italic |
 
6509
                      Q3TextFormat::Underline;
 
6510
    cur->setIndex(start);
 
6511
    td->setSelectionStart(0, *cur);
 
6512
    cur->setIndex(end);
 
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
 
6521
            // this format tag
 
6522
            tag = tag->prev;
 
6523
            while (tag && (tag->type == Q3TextEditOptimPrivate::Format ||
 
6524
                            tag->leftTag)) {
 
6525
                tag = tag->leftTag ? tag->parent : tag->prev;
 
6526
            }
 
6527
        }
 
6528
        if (tag) {
 
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();
 
6533
                if (col[0] == '\"')
 
6534
                    col = col.mid(1, col.length() - 2);
 
6535
            }
 
6536
            QColor color = QColor(col);
 
6537
            if (color.isValid()) {
 
6538
                formatFlags |= Q3TextFormat::Color;
 
6539
                f->setColor(color);
 
6540
            }
 
6541
        }
 
6542
    } else { // use the stylesheet tag definition
 
6543
        if (ssItem->color().isValid()) {
 
6544
            formatFlags |= Q3TextFormat::Color;
 
6545
            f->setColor(ssItem->color());
 
6546
        }
 
6547
        f->setBold(ssItem->fontWeight() == QFont::Bold);
 
6548
        f->setItalic(ssItem->fontItalic());
 
6549
        f->setUnderline(ssItem->fontUnderline());
 
6550
    }
 
6551
    td->setFormat(0, f, formatFlags);
 
6552
    td->removeSelection(0);
 
6553
}
 
6554
 
 
6555
/*! \internal */
 
6556
void Q3TextEdit::optimDrawContents(QPainter * p, int clipx, int clipy,
 
6557
                                   int clipw, int cliph)
 
6558
{
 
6559
    QFontMetrics fm(Q3ScrollView::font());
 
6560
    int startLine = clipy / fm.lineSpacing();
 
6561
 
 
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
 
6564
    // of a single line
 
6565
    int nLines = (cliph / fm.lineSpacing()) + 2;
 
6566
    int endLine = startLine + nLines;
 
6567
 
 
6568
    if (startLine >= d->od->numLines)
 
6569
        return;
 
6570
    if ((startLine + nLines) > d->od->numLines)
 
6571
        nLines = d->od->numLines - startLine;
 
6572
 
 
6573
    int i = 0;
 
6574
    QString str;
 
6575
    for (i = startLine; i < (startLine + nLines); i++)
 
6576
        str.append(d->od->lines[LOGOFFSET(i)] + "\n");
 
6577
 
 
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());
 
6584
 
 
6585
    // get the current text color from the current format
 
6586
    td->selectAll(Q3TextDocument::Standard);
 
6587
    Q3TextFormat f;
 
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);
 
6593
 
 
6594
    // add tag formatting
 
6595
    if (d->od->tags) {
 
6596
        int i = startLine;
 
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())
 
6604
                tag = it.value();
 
6605
            // Step 2 - iterate over tags on the current line
 
6606
            int lastIndex = 0;
 
6607
            while (tag && tag->line == i) {
 
6608
                tmp = 0;
 
6609
                if (tag->prev && !tag->prev->leftTag) {
 
6610
                    tmp = tag->prev;
 
6611
                } else if (tag->prev && tag->prev->parent) {
 
6612
                    tmp = tag->prev->parent;
 
6613
                }
 
6614
                if ((tag->index - lastIndex) > 0 && tmp) {
 
6615
                    optimSetTextFormat(td, &cur, &f, lastIndex, tag->index, tmp);
 
6616
                }
 
6617
                lastIndex = tag->index;
 
6618
                tmp = tag;
 
6619
                tag = tag->next;
 
6620
            }
 
6621
            // Step 3 - color last part of the line - if necessary
 
6622
            if (tmp && tmp->parent)
 
6623
                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);
 
6627
            }
 
6628
            cur.setParagraph(cur.paragraph()->next());
 
6629
        }
 
6630
        // useful debug info
 
6631
        //
 
6632
//         tag = d->od->tags;
 
6633
//         qWarning("###");
 
6634
//         while (tag) {
 
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);
 
6638
//             tag = tag->next;
 
6639
//         }
 
6640
    }
 
6641
 
 
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);
 
6654
        }
 
6655
        if (selEnd > d->od->numLines-1) {
 
6656
            selEnd = d->od->numLines-1;
 
6657
        }
 
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);
 
6663
            if (parag) {
 
6664
                c1.setParagraph(parag);
 
6665
                if (td->text(paragS).length() >= idxStart)
 
6666
                    c1.setIndex(idxStart);
 
6667
            }
 
6668
            parag = td->paragAt(paragE);
 
6669
            if (parag) {
 
6670
                c2.setParagraph(parag);
 
6671
                if (td->text(paragE).length() >= idxEnd)
 
6672
                    c2.setIndex(idxEnd);
 
6673
            }
 
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());
 
6681
            c1.setIndex(0);
 
6682
            int paragE = selEnd - startLine;
 
6683
            Q3TextParagraph * parag = td->paragAt(paragE);
 
6684
            if (parag) {
 
6685
                c2.setParagraph(parag);
 
6686
                if (td->text(paragE).length() >= idxEnd)
 
6687
                    c2.setIndex(idxEnd);
 
6688
            }
 
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);
 
6694
            if (parag) {
 
6695
                c1.setParagraph(parag);
 
6696
                c1.setIndex(idxStart);
 
6697
            }
 
6698
            c2.setParagraph(td->lastParagraph());
 
6699
            c2.setIndex(td->lastParagraph()->string()->toString().length() - 1);
 
6700
 
 
6701
        }
 
6702
        // previously selected?
 
6703
        if (!td->hasSelection(Q3TextDocument::Standard)) {
 
6704
            td->setSelectionStart(Q3TextDocument::Standard, c1);
 
6705
            td->setSelectionEnd(Q3TextDocument::Standard, c2);
 
6706
        }
 
6707
    }
 
6708
    td->doLayout(p, contentsWidth());
 
6709
 
 
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
 
6712
    // painted
 
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));
 
6718
    delete td;
 
6719
}
 
6720
 
 
6721
/*! \internal */
 
6722
void Q3TextEdit::optimMousePressEvent(QMouseEvent * e)
 
6723
{
 
6724
    if (e->button() != Qt::LeftButton)
 
6725
        return;
 
6726
 
 
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();
 
6734
    } else {
 
6735
        QString str = d->od->lines[LOGOFFSET(d->od->selStart.line)];
 
6736
        d->od->selStart.index = optimCharIndex(str, mousePos.x());
 
6737
    }
 
6738
    d->od->selEnd.line = d->od->selStart.line;
 
6739
    d->od->selEnd.index = d->od->selStart.index;
 
6740
    oldMousePos = e->pos();
 
6741
    repaintContents();
 
6742
}
 
6743
 
 
6744
/*! \internal */
 
6745
void Q3TextEdit::optimMouseReleaseEvent(QMouseEvent * e)
 
6746
{
 
6747
    if (e->button() != Qt::LeftButton)
 
6748
        return;
 
6749
 
 
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;
 
6757
        }
 
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);
 
6767
        }
 
6768
        oldMousePos = e->pos();
 
6769
        repaintContents();
 
6770
    }
 
6771
    if (mousePressed) {
 
6772
        mousePressed = false;
 
6773
        copyToClipboard();
 
6774
    }
 
6775
 
 
6776
    inDoubleClick = false;
 
6777
    emit copyAvailable(optimHasSelection());
 
6778
    emit selectionChanged();
 
6779
}
 
6780
 
 
6781
/*! \internal */
 
6782
void Q3TextEdit::optimMouseMoveEvent(QMouseEvent * e)
 
6783
{
 
6784
    mousePos = e->pos();
 
6785
    optimDoAutoScroll();
 
6786
    oldMousePos = mousePos;
 
6787
}
 
6788
 
 
6789
/*! \internal */
 
6790
void Q3TextEdit::optimDoAutoScroll()
 
6791
{
 
6792
    if (!mousePressed)
 
6793
        return;
 
6794
 
 
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();
 
6800
 
 
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()) {
 
6804
        int my = yy;
 
6805
        if (pos.x() < 0)
 
6806
            xx = contentsX() - fm.width('w');
 
6807
        else if (pos.x() > viewport()->width())
 
6808
            xx = contentsX() + viewport()->width() + fm.width('w');
 
6809
 
 
6810
        if (pos.y() < 0) {
 
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;
 
6816
        }
 
6817
        d->od->selEnd.line = my / fm.lineSpacing();
 
6818
        mousePos.setX(xx);
 
6819
        mousePos.setY(my);
 
6820
        doScroll = true;
 
6821
    } else {
 
6822
        d->od->selEnd.line = mousePos.y() / fm.lineSpacing();
 
6823
    }
 
6824
 
 
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;
 
6829
    }
 
6830
 
 
6831
    QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)];
 
6832
    d->od->selEnd.index = optimCharIndex(str, mousePos.x());
 
6833
 
 
6834
    // have to have a valid index before generating a paint event
 
6835
    if (doScroll)
 
6836
        ensureVisible(xx, yy, 1, 1);
 
6837
 
 
6838
    // if the text document is smaller than the height of the viewport
 
6839
    // - redraw the whole thing otherwise calculate the rect that
 
6840
    // needs drawing.
 
6841
    if (d->od->numLines * fm.lineSpacing() < viewport()->height()) {
 
6842
        repaintContents(contentsX(), contentsY(), width(), height());
 
6843
    } else {
 
6844
        int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2;
 
6845
        int y;
 
6846
        if (oldMousePos.y() < mousePos.y()) {
 
6847
            y = oldMousePos.y() - fm.lineSpacing();
 
6848
        } else {
 
6849
            // expand paint area for a fully selected line
 
6850
            h += fm.lineSpacing();
 
6851
            y = mousePos.y() - fm.lineSpacing()*2;
 
6852
        }
 
6853
        if (y < 0)
 
6854
            y = 0;
 
6855
        repaintContents(contentsX(), y, width(), h);
 
6856
    }
 
6857
 
 
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();
 
6862
}
 
6863
 
 
6864
/*! \internal
 
6865
 
 
6866
  Returns the index of the character in the string \a str that is
 
6867
  currently under the mouse pointer.
 
6868
*/
 
6869
int Q3TextEdit::optimCharIndex(const QString &str, int mx) const
 
6870
{
 
6871
    QFontMetrics fm(Q3ScrollView::font());
 
6872
    int i = 0;
 
6873
    int dd, dist = 10000000;
 
6874
    int curpos = 0;
 
6875
    int strWidth;
 
6876
    mx = mx - 4; // ### get the real margin from somewhere
 
6877
 
 
6878
    if (!str.contains('\t') && mx > fm.width(str))
 
6879
        return str.length();
 
6880
 
 
6881
    while (i < str.length()) {
 
6882
        strWidth = qStrWidth(str.left(i), tabStopWidth(), fm);
 
6883
        dd = strWidth - mx;
 
6884
        if (QABS(dd) <= dist) {
 
6885
            dist = QABS(dd);
 
6886
            if (mx >= strWidth)
 
6887
                curpos = i;
 
6888
        }
 
6889
        ++i;
 
6890
    }
 
6891
    return curpos;
 
6892
}
 
6893
 
 
6894
/*! \internal */
 
6895
void Q3TextEdit::optimSelectAll()
 
6896
{
 
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();
 
6900
 
 
6901
    repaintContents();
 
6902
    emit copyAvailable(optimHasSelection());
 
6903
    emit selectionChanged();
 
6904
}
 
6905
 
 
6906
/*! \internal */
 
6907
void Q3TextEdit::optimRemoveSelection()
 
6908
{
 
6909
    d->od->selStart.line = d->od->selEnd.line = -1;
 
6910
    d->od->selStart.index = d->od->selEnd.index = -1;
 
6911
    repaintContents();
 
6912
}
 
6913
 
 
6914
/*! \internal */
 
6915
void Q3TextEdit::optimSetSelection(int startLine, int startIdx,
 
6916
                                       int endLine, int endIdx)
 
6917
{
 
6918
    d->od->selStart.line = startLine;
 
6919
    d->od->selEnd.line = endLine;
 
6920
    d->od->selStart.index = startIdx;
 
6921
    d->od->selEnd.index = endIdx;
 
6922
}
 
6923
 
 
6924
/*! \internal */
 
6925
bool Q3TextEdit::optimHasSelection() const
 
6926
{
 
6927
    if (d->od->selStart.line != d->od->selEnd.line ||
 
6928
         d->od->selStart.index != d->od->selEnd.index)
 
6929
        return true;
 
6930
    return false;
 
6931
}
 
6932
 
 
6933
/*! \internal */
 
6934
QString Q3TextEdit::optimSelectedText() const
 
6935
{
 
6936
    QString str;
 
6937
 
 
6938
    if (!optimHasSelection())
 
6939
        return str;
 
6940
 
 
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);
 
6945
    } else {
 
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";
 
6949
        i++;
 
6950
        for (; i < d->od->selEnd.line; i++) {
 
6951
            if (d->od->lines[LOGOFFSET(i)].isEmpty()) // CR lines are empty
 
6952
                str += "\n";
 
6953
            else
 
6954
                str += d->od->lines[LOGOFFSET(i)] + "\n";
 
6955
        }
 
6956
        str += d->od->lines[LOGOFFSET(d->od->selEnd.line)].left(d->od->selEnd.index);
 
6957
    }
 
6958
    return str;
 
6959
}
 
6960
 
 
6961
/*! \internal */
 
6962
bool Q3TextEdit::optimFind(const QString & expr, bool cs, bool /*wo*/,
 
6963
                               bool fw, int * para, int * index)
 
6964
{
 
6965
    bool found = false;
 
6966
    int parag = para ? *para : d->od->search.line,
 
6967
          idx = index ? *index : d->od->search.index, i;
 
6968
 
 
6969
    if (d->od->len == 0)
 
6970
        return false;
 
6971
 
 
6972
    for (i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i--) {
 
6973
        idx = fw
 
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);
 
6978
        if (idx != -1) {
 
6979
            found = true;
 
6980
            break;
 
6981
        } else if (fw)
 
6982
            idx = 0;
 
6983
    }
 
6984
 
 
6985
    if (found) {
 
6986
        if (index)
 
6987
            *index = idx;
 
6988
        if (para)
 
6989
            *para = i;
 
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
 
6998
    }
 
6999
    return found;
 
7000
}
 
7001
 
 
7002
/*! \reimp */
 
7003
void Q3TextEdit::polishEvent(QEvent*)
 
7004
{
 
7005
    // this will ensure that the last line is visible if text have
 
7006
    // been added to the widget before it is shown
 
7007
    if (d->optimMode)
 
7008
        scrollToBottom();
 
7009
}
 
7010
 
 
7011
/*!
 
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.
 
7015
 
 
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.
 
7020
 */
 
7021
void Q3TextEdit::setMaxLogLines(int limit)
 
7022
{
 
7023
    d->maxLogLines = limit;
 
7024
    if (d->maxLogLines < -1)
 
7025
        d->maxLogLines = -1;
 
7026
    if (d->maxLogLines == -1)
 
7027
        d->logOffset = 0;
 
7028
}
 
7029
 
 
7030
/*!
 
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.
 
7034
 */
 
7035
int Q3TextEdit::maxLogLines() const
 
7036
{
 
7037
    return d->maxLogLines;
 
7038
}
 
7039
 
 
7040
/*!
 
7041
    Check if the number of lines in the buffer is limited, and uphold
 
7042
    that limit when appending new lines.
 
7043
 */
 
7044
void Q3TextEdit::optimCheckLimit(const QString& str)
 
7045
{
 
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;
 
7052
        while (t) {
 
7053
            t->line -= 1;
 
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))
 
7058
            {
 
7059
                if (t->prev)
 
7060
                    t->prev->next = t->next;
 
7061
                if (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) {
 
7066
                    if (t->prev)
 
7067
                        d->od->lastTag = t->prev;
 
7068
                    else
 
7069
                        d->od->lastTag = d->od->tags;
 
7070
                }
 
7071
                tmp = t;
 
7072
                t = t->next;
 
7073
                lst.append(tmp);
 
7074
                delete tmp;
 
7075
            } else {
 
7076
                t = t->next;
 
7077
            }
 
7078
        }
 
7079
        // Remove all references to the ptrs we just deleted
 
7080
        itr = d->od->tags;
 
7081
        while (itr) {
 
7082
            for (int i = 0; i < lst.size(); ++i) {
 
7083
                tmp = lst.at(i);
 
7084
                if (itr->parent == tmp)
 
7085
                    itr->parent = 0;
 
7086
                if (itr->leftTag == tmp)
 
7087
                    itr->leftTag = 0;
 
7088
            }
 
7089
            itr = itr->next;
 
7090
        }
 
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);
 
7095
 
 
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);
 
7100
            d->od->numLines--;
 
7101
            d->logOffset = LOGOFFSET(1);
 
7102
        }
 
7103
    }
 
7104
    d->od->len += str.length();
 
7105
    d->od->lines[LOGOFFSET(d->od->numLines++)] = str;
 
7106
}
 
7107
 
 
7108
#endif // QT_TEXTEDIT_OPTIMIZATION
 
7109
 
 
7110
/*!
 
7111
    \property Q3TextEdit::autoFormatting
 
7112
    \brief the enabled set of auto formatting features
 
7113
 
 
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.
 
7117
 
 
7118
    Currently, the only automatic formatting feature provided is \c
 
7119
    AutoBulletList; future versions of Qt may offer more.
 
7120
*/
 
7121
 
 
7122
void Q3TextEdit::setAutoFormatting(AutoFormatting features)
 
7123
{
 
7124
    d->autoFormatting = features;
 
7125
}
 
7126
 
 
7127
Q3TextEdit::AutoFormatting Q3TextEdit::autoFormatting() const
 
7128
{
 
7129
    return d->autoFormatting;
 
7130
}
 
7131
 
 
7132
/*!
 
7133
    Returns the QSyntaxHighlighter set on this Q3TextEdit. 0 is
 
7134
    returned if no syntax highlighter is set.
 
7135
 */
 
7136
Q3SyntaxHighlighter * Q3TextEdit::syntaxHighlighter() const
 
7137
{
 
7138
    if (document()->preProcessor())
 
7139
        return ((Q3SyntaxHighlighterInternal *) document()->preProcessor())->highlighter;
 
7140
    else
 
7141
        return 0;
 
7142
}
 
7143
 
 
7144
#endif //QT_NO_TEXTEDIT