|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "q3textedit.h" |
|
43 |
|
44 #ifndef QT_NO_TEXTEDIT |
|
45 |
|
46 #include <private/q3richtext_p.h> |
|
47 #include "qpainter.h" |
|
48 #include "qpen.h" |
|
49 #include "qbrush.h" |
|
50 #include "qpixmap.h" |
|
51 #include "qfont.h" |
|
52 #include "qcolor.h" |
|
53 #include "qstyle.h" |
|
54 #include "qsize.h" |
|
55 #include "qevent.h" |
|
56 #include "qtimer.h" |
|
57 #include "qapplication.h" |
|
58 #include "q3listbox.h" |
|
59 #include "qclipboard.h" |
|
60 #include "qcolordialog.h" |
|
61 #include "q3stylesheet.h" |
|
62 #include "q3dragobject.h" |
|
63 #include "qurl.h" |
|
64 #include "qcursor.h" |
|
65 #include "qregexp.h" |
|
66 #include "q3popupmenu.h" |
|
67 #include "qstack.h" |
|
68 #include "qmetaobject.h" |
|
69 #include "q3textbrowser.h" |
|
70 #include "private/q3syntaxhighlighter_p.h" |
|
71 #include "qtextformat.h" |
|
72 #ifndef QT_NO_IM |
|
73 #include <qinputcontext.h> |
|
74 #endif |
|
75 |
|
76 #ifndef QT_NO_ACCEL |
|
77 #include <qkeysequence.h> |
|
78 #define ACCEL_KEY(k) QLatin1Char('\t') + QString(QKeySequence(Qt::CTRL | Qt::Key_ ## k)) |
|
79 #else |
|
80 #define ACCEL_KEY(k) QLatin1Char('\t' )+ QString::fromLatin1("Ctrl+" #k) |
|
81 #endif |
|
82 |
|
83 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
84 #define LOGOFFSET(i) d->logOffset + i |
|
85 #endif |
|
86 |
|
87 QT_BEGIN_NAMESPACE |
|
88 |
|
89 struct QUndoRedoInfoPrivate |
|
90 { |
|
91 Q3TextString text; |
|
92 }; |
|
93 |
|
94 class Q3TextEditPrivate |
|
95 { |
|
96 public: |
|
97 Q3TextEditPrivate() |
|
98 :preeditStart(-1),preeditLength(-1),numPreeditSelections(0),ensureCursorVisibleInShowEvent(false), |
|
99 tabChangesFocus(false), |
|
100 #ifndef QT_NO_CLIPBOARD |
|
101 clipboard_mode(QClipboard::Clipboard), |
|
102 #endif |
|
103 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
104 od(0), optimMode(false), |
|
105 maxLogLines(-1), |
|
106 logOffset(0), |
|
107 #endif |
|
108 autoFormatting((uint)Q3TextEdit::AutoAll), |
|
109 cursorRepaintMode(false), |
|
110 cursorBlinkActive(false) |
|
111 |
|
112 { |
|
113 for (int i=0; i<7; i++) |
|
114 id[i] = 0; |
|
115 } |
|
116 int id[7]; |
|
117 int preeditStart; |
|
118 int preeditLength; |
|
119 int numPreeditSelections; |
|
120 uint ensureCursorVisibleInShowEvent : 1; |
|
121 uint tabChangesFocus : 1; |
|
122 QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized |
|
123 QString pressedName; |
|
124 QString onName; |
|
125 #ifndef QT_NO_CLIPBOARD |
|
126 QClipboard::Mode clipboard_mode; |
|
127 #endif |
|
128 QTimer *trippleClickTimer; |
|
129 QPoint trippleClickPoint; |
|
130 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
131 Q3TextEditOptimPrivate * od; |
|
132 bool optimMode : 1; |
|
133 int maxLogLines; |
|
134 int logOffset; |
|
135 #endif |
|
136 Q3TextEdit::AutoFormatting autoFormatting; |
|
137 uint cursorRepaintMode : 1; |
|
138 uint cursorBlinkActive : 1; |
|
139 }; |
|
140 |
|
141 #ifndef QT_NO_MIME |
|
142 class Q3RichTextDrag : public Q3TextDrag |
|
143 { |
|
144 public: |
|
145 Q3RichTextDrag(QWidget *dragSource = 0, const char *name = 0); |
|
146 |
|
147 void setPlainText(const QString &txt) { setText(txt); } |
|
148 void setRichText(const QString &txt) { richTxt = txt; } |
|
149 |
|
150 virtual QByteArray encodedData(const char *mime) const; |
|
151 virtual const char* format(int i) const; |
|
152 |
|
153 static bool decode(QMimeSource *e, QString &str, const QString &mimetype, |
|
154 const QString &subtype); |
|
155 static bool canDecode(QMimeSource* e); |
|
156 |
|
157 private: |
|
158 QString richTxt; |
|
159 |
|
160 }; |
|
161 |
|
162 Q3RichTextDrag::Q3RichTextDrag(QWidget *dragSource, const char *name) |
|
163 : Q3TextDrag(dragSource, name) |
|
164 { |
|
165 } |
|
166 |
|
167 QByteArray Q3RichTextDrag::encodedData(const char *mime) const |
|
168 { |
|
169 if (qstrcmp("application/x-qrichtext", mime) == 0) { |
|
170 return richTxt.toUtf8(); // #### perhaps we should use USC2 instead? |
|
171 } else |
|
172 return Q3TextDrag::encodedData(mime); |
|
173 } |
|
174 |
|
175 bool Q3RichTextDrag::decode(QMimeSource *e, QString &str, const QString &mimetype, |
|
176 const QString &subtype) |
|
177 { |
|
178 if (mimetype == QLatin1String("application/x-qrichtext")) { |
|
179 // do richtext decode |
|
180 const char *mime; |
|
181 int i; |
|
182 for (i = 0; (mime = e->format(i)); ++i) { |
|
183 if (qstrcmp("application/x-qrichtext", mime) != 0) |
|
184 continue; |
|
185 str = QString::fromUtf8(e->encodedData(mime)); |
|
186 return true; |
|
187 } |
|
188 return false; |
|
189 } |
|
190 |
|
191 // do a regular text decode |
|
192 QString st = subtype; |
|
193 return Q3TextDrag::decode(e, str, st); |
|
194 } |
|
195 |
|
196 bool Q3RichTextDrag::canDecode(QMimeSource* e) |
|
197 { |
|
198 if (e->provides("application/x-qrichtext")) |
|
199 return true; |
|
200 return Q3TextDrag::canDecode(e); |
|
201 } |
|
202 |
|
203 const char* Q3RichTextDrag::format(int i) const |
|
204 { |
|
205 if (Q3TextDrag::format(i)) |
|
206 return Q3TextDrag::format(i); |
|
207 if (Q3TextDrag::format(i-1)) |
|
208 return "application/x-qrichtext"; |
|
209 return 0; |
|
210 } |
|
211 |
|
212 #endif |
|
213 |
|
214 static bool block_set_alignment = false; |
|
215 |
|
216 /*! |
|
217 \class Q3TextEdit |
|
218 \brief The Q3TextEdit widget provides a powerful single-page rich text editor. |
|
219 |
|
220 \compat |
|
221 |
|
222 \tableofcontents |
|
223 |
|
224 \section1 Introduction and Concepts |
|
225 |
|
226 Q3TextEdit is an advanced WYSIWYG viewer/editor supporting rich |
|
227 text formatting using HTML-style tags. It is optimized to handle |
|
228 large documents and to respond quickly to user input. |
|
229 |
|
230 Q3TextEdit has four modes of operation: |
|
231 \table |
|
232 \header \i Mode \i Command \i Notes |
|
233 \row \i Plain Text Editor \i setTextFormat(Qt::PlainText) |
|
234 \i Set text with setText(); text() returns plain text. Text |
|
235 attributes (e.g. colors) can be set, but plain text is always |
|
236 returned. |
|
237 \row \i Rich Text Editor \i setTextFormat(Qt::RichText) |
|
238 \i Set text with setText(); text() returns rich text. Rich |
|
239 text editing is fairly limited. You can't set margins or |
|
240 insert images for example (although you can read and |
|
241 correctly display files that have margins set and that |
|
242 include images). This mode is mostly useful for editing small |
|
243 amounts of rich text. |
|
244 \row \i Text Viewer \i setReadOnly(true) |
|
245 \i Set text with setText() or append() (which has no undo |
|
246 history so is faster and uses less memory); text() returns |
|
247 plain or rich text depending on the textFormat(). This mode |
|
248 can correctly display a large subset of HTML tags. |
|
249 \row \i Log Viewer \i setTextFormat(Qt::LogText) |
|
250 \i Append text using append(). The widget is set to be read |
|
251 only and rich text support is disabled although a few HTML |
|
252 tags (for color, bold, italic and underline) may be used. |
|
253 (See \link #logtextmode Qt::LogText mode\endlink for details.) |
|
254 \endtable |
|
255 |
|
256 Q3TextEdit can be used as a syntax highlighting editor when used in |
|
257 conjunction with QSyntaxHighlighter. |
|
258 |
|
259 We recommend that you always call setTextFormat() to set the mode |
|
260 you want to use. If you use Qt::AutoText then setText() and |
|
261 append() will try to determine whether the text they are given is |
|
262 plain text or rich text. If you use Qt::RichText then setText() and |
|
263 append() will assume that the text they are given is rich text. |
|
264 insert() simply inserts the text it is given. |
|
265 |
|
266 Q3TextEdit works on paragraphs and characters. A paragraph is a |
|
267 formatted string which is word-wrapped to fit into the width of |
|
268 the widget. By default when reading plain text, one newline |
|
269 signify a paragraph. A document consists of zero or more |
|
270 paragraphs, indexed from 0. Characters are indexed on a |
|
271 per-paragraph basis, also indexed from 0. The words in the |
|
272 paragraph are aligned in accordance with the paragraph's |
|
273 alignment(). Paragraphs are separated by hard line breaks. Each |
|
274 character within a paragraph has its own attributes, for example, |
|
275 font and color. |
|
276 |
|
277 The text edit documentation uses the following concepts: |
|
278 \list |
|
279 \i \e{current format} -- |
|
280 this is the format at the current cursor position, \e and it |
|
281 is the format of the selected text if any. |
|
282 \i \e{current paragraph} -- the paragraph which contains the |
|
283 cursor. |
|
284 \endlist |
|
285 |
|
286 Q3TextEdit can display images (using Q3MimeSourceFactory), lists and |
|
287 tables. If the text is too large to view within the text edit's |
|
288 viewport, scroll bars will appear. The text edit can load both |
|
289 plain text and HTML files (a subset of HTML 3.2 and 4). The |
|
290 rendering style and the set of valid tags are defined by a |
|
291 styleSheet(). Custom tags can be created and placed in a custom |
|
292 style sheet. Change the style sheet with \l{setStyleSheet()}; see |
|
293 Q3StyleSheet for details. The images identified by image tags are |
|
294 displayed if they can be interpreted using the text edit's |
|
295 \l{Q3MimeSourceFactory}; see setMimeSourceFactory(). |
|
296 |
|
297 If you want a text browser with more navigation use QTextBrowser. |
|
298 If you just need to display a small piece of rich text use QLabel |
|
299 or QSimpleRichText. |
|
300 |
|
301 If you create a new Q3TextEdit, and want to allow the user to edit |
|
302 rich text, call setTextFormat(Qt::RichText) to ensure that the |
|
303 text is treated as rich text. (Rich text uses HTML tags to set |
|
304 text formatting attributes. See Q3StyleSheet for information on the |
|
305 HTML tags that are supported.). If you don't call setTextFormat() |
|
306 explicitly the text edit will guess from the text itself whether |
|
307 it is rich text or plain text. This means that if the text looks |
|
308 like HTML or XML it will probably be interpreted as rich text, so |
|
309 you should call setTextFormat(Qt::PlainText) to preserve such |
|
310 text. |
|
311 |
|
312 Note that we do not intend to add a full-featured web browser |
|
313 widget to Qt (because that would easily double Qt's size and only |
|
314 a few applications would benefit from it). The rich |
|
315 text support in Qt is designed to provide a fast, portable and |
|
316 efficient way to add reasonable online help facilities to |
|
317 applications, and to provide a basis for rich text editors. |
|
318 |
|
319 \section1 Using Q3TextEdit as a Display Widget |
|
320 |
|
321 Q3TextEdit can display a large HTML subset, including tables and |
|
322 images. |
|
323 |
|
324 The text is set or replaced using setText() which deletes any |
|
325 existing text and replaces it with the text passed in the |
|
326 setText() call. If you call setText() with legacy HTML (with |
|
327 setTextFormat(Qt::RichText) in force), and then call text(), the text |
|
328 that is returned may have different markup, but will render the |
|
329 same. Text can be inserted with insert(), paste(), pasteSubType() |
|
330 and append(). Text that is appended does not go into the undo |
|
331 history; this makes append() faster and consumes less memory. Text |
|
332 can also be cut(). The entire text is deleted with clear() and the |
|
333 selected text is deleted with removeSelectedText(). Selected |
|
334 (marked) text can also be deleted with del() (which will delete |
|
335 the character to the right of the cursor if no text is selected). |
|
336 |
|
337 Loading and saving text is achieved using setText() and text(), |
|
338 for example: |
|
339 \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 0 |
|
340 |
|
341 By default the text edit wraps words at whitespace to fit within |
|
342 the text edit widget. The setWordWrap() function is used to |
|
343 specify the kind of word wrap you want, or \c NoWrap if you don't |
|
344 want any wrapping. Call setWordWrap() to set a fixed pixel width |
|
345 \c FixedPixelWidth, or character column (e.g. 80 column) \c |
|
346 FixedColumnWidth with the pixels or columns specified with |
|
347 setWrapColumnOrWidth(). If you use word wrap to the widget's width |
|
348 \c WidgetWidth, you can specify whether to break on whitespace or |
|
349 anywhere with setWrapPolicy(). |
|
350 |
|
351 The background color is set differently than other widgets, using |
|
352 setPaper(). You specify a brush style which could be a plain color |
|
353 or a complex pixmap. |
|
354 |
|
355 Hypertext links are automatically underlined; this can be changed |
|
356 with setLinkUnderline(). The tab stop width is set with |
|
357 setTabStopWidth(). |
|
358 |
|
359 The zoomIn() and zoomOut() functions can be used to resize the |
|
360 text by increasing (decreasing for zoomOut()) the point size used. |
|
361 Images are not affected by the zoom functions. |
|
362 |
|
363 The lines() function returns the number of lines in the text and |
|
364 paragraphs() returns the number of paragraphs. The number of lines |
|
365 within a particular paragraph is returned by linesOfParagraph(). |
|
366 The length of the entire text in characters is returned by |
|
367 length(). |
|
368 |
|
369 You can scroll to an anchor in the text, e.g. |
|
370 \c{<a name="anchor">} with scrollToAnchor(). The find() function |
|
371 can be used to find and select a given string within the text. |
|
372 |
|
373 A read-only Q3TextEdit provides the same functionality as the |
|
374 (obsolete) QTextView. (QTextView is still supplied for |
|
375 compatibility with old code.) |
|
376 |
|
377 \section2 Read-only key bindings |
|
378 |
|
379 When Q3TextEdit is used read-only the key-bindings are limited to |
|
380 navigation, and text may only be selected with the mouse: |
|
381 \table |
|
382 \header \i Keypresses \i Action |
|
383 \row \i Up \i Move one line up |
|
384 \row \i Down \i Move one line down |
|
385 \row \i Left \i Move one character left |
|
386 \row \i Right \i Move one character right |
|
387 \row \i PageUp \i Move one (viewport) page up |
|
388 \row \i PageDown \i Move one (viewport) page down |
|
389 \row \i Home \i Move to the beginning of the text |
|
390 \row \i End \i Move to the end of the text |
|
391 \row \i Shift+Wheel |
|
392 \i Scroll the page horizontally (the Wheel is the mouse wheel) |
|
393 \row \i Ctrl+Wheel \i Zoom the text |
|
394 \endtable |
|
395 |
|
396 The text edit may be able to provide some meta-information. For |
|
397 example, the documentTitle() function will return the text from |
|
398 within HTML \c{<title>} tags. |
|
399 |
|
400 The text displayed in a text edit has a \e context. The context is |
|
401 a path which the text edit's Q3MimeSourceFactory uses to resolve |
|
402 the locations of files and images. It is passed to the |
|
403 mimeSourceFactory() when quering data. (See Q3TextEdit() and |
|
404 \l{context()}.) |
|
405 |
|
406 \target logtextmode |
|
407 \section2 Using Q3TextEdit in Qt::LogText Mode |
|
408 |
|
409 Setting the text format to Qt::LogText puts the widget in a special |
|
410 mode which is optimized for very large texts. In this mode editing |
|
411 and rich text support are disabled (the widget is explicitly set |
|
412 to read-only mode). This allows the text to be stored in a |
|
413 different, more memory efficient manner. However, a certain degree |
|
414 of text formatting is supported through the use of formatting |
|
415 tags. A tag is delimited by \c < and \c {>}. The characters \c |
|
416 {<}, \c > and \c & are escaped by using \c {<}, \c {>} and |
|
417 \c {&}. A tag pair consists of a left and a right tag (or |
|
418 open/close tags). Left-tags mark the starting point for |
|
419 formatting, while right-tags mark the ending point. A right-tag |
|
420 always start with a \c / before the tag keyword. For example \c |
|
421 <b> and \c </b> are a tag pair. Tags can be nested, but they |
|
422 have to be closed in the same order as they are opened. For |
|
423 example, \c <b><u></u></b> is valid, while \c |
|
424 <b><u></b></u> will output an error message. |
|
425 |
|
426 By using tags it is possible to change the color, bold, italic and |
|
427 underline settings for a piece of text. A color can be specified |
|
428 by using the HTML font tag \c {<font color=colorname>}. The color |
|
429 name can be one of the color names from the X11 color database, or |
|
430 a RGB hex value (e.g \c {#00ff00}). Example of valid color tags: |
|
431 \c {<font color=red>}, \c{<font color="light blue">},\c {<font |
|
432 color="#223344">}. Bold, italic and underline settings can be |
|
433 specified by the tags \c {<b>}, \c <i> and \c {<u>}. Note that a |
|
434 tag does not necessarily have to be closed. A valid example: |
|
435 \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 1 |
|
436 |
|
437 Stylesheets can also be used in Qt::LogText mode. To create and use a |
|
438 custom tag, you could do the following: |
|
439 \snippet doc/src/snippets/code/src_qt3support_text_q3textedit.cpp 2 |
|
440 Note that only the color, bold, underline and italic attributes of |
|
441 a Q3StyleSheetItem is used in Qt::LogText mode. |
|
442 |
|
443 Note that you can use setMaxLogLines() to limit the number of |
|
444 lines the widget can hold in Qt::LogText mode. |
|
445 |
|
446 There are a few things that you need to be aware of when the |
|
447 widget is in this mode: |
|
448 \list |
|
449 \i Functions that deal with rich text formatting and cursor |
|
450 movement will not work or return anything valid. |
|
451 \i Lines are equivalent to paragraphs. |
|
452 \endlist |
|
453 |
|
454 \section1 Using Q3TextEdit as an Editor |
|
455 |
|
456 All the information about using Q3TextEdit as a display widget also |
|
457 applies here. |
|
458 |
|
459 The current format's attributes are set with setItalic(), |
|
460 setBold(), setUnderline(), setFamily() (font family), |
|
461 setPointSize(), setColor() and setCurrentFont(). The current |
|
462 paragraph's alignment is set with setAlignment(). |
|
463 |
|
464 Use setSelection() to select text. The setSelectionAttributes() |
|
465 function is used to indicate how selected text should be |
|
466 displayed. Use hasSelectedText() to find out if any text is |
|
467 selected. The currently selected text's position is available |
|
468 using getSelection() and the selected text itself is returned by |
|
469 selectedText(). The selection can be copied to the clipboard with |
|
470 copy(), or cut to the clipboard with cut(). It can be deleted with |
|
471 removeSelectedText(). The entire text can be selected (or |
|
472 unselected) using selectAll(). Q3TextEdit supports multiple |
|
473 selections. Most of the selection functions operate on the default |
|
474 selection, selection 0. If the user presses a non-selecting key, |
|
475 e.g. a cursor key without also holding down Shift, all selections |
|
476 are cleared. |
|
477 |
|
478 Set and get the position of the cursor with setCursorPosition() |
|
479 and getCursorPosition() respectively. When the cursor is moved, |
|
480 the signals currentFontChanged(), currentColorChanged() and |
|
481 currentAlignmentChanged() are emitted to reflect the font, color |
|
482 and alignment at the new cursor position. |
|
483 |
|
484 If the text changes, the textChanged() signal is emitted, and if |
|
485 the user inserts a new line by pressing Return or Enter, |
|
486 returnPressed() is emitted. The isModified() function will return |
|
487 true if the text has been modified. |
|
488 |
|
489 Q3TextEdit provides command-based undo and redo. To set the depth |
|
490 of the command history use setUndoDepth() which defaults to 100 |
|
491 steps. To undo or redo the last operation call undo() or redo(). |
|
492 The signals undoAvailable() and redoAvailable() indicate whether |
|
493 the undo and redo operations can be executed. |
|
494 |
|
495 \section2 Editing key bindings |
|
496 |
|
497 The list of key-bindings which are implemented for editing: |
|
498 \table |
|
499 \header \i Keypresses \i Action |
|
500 \row \i Backspace \i Delete the character to the left of the cursor |
|
501 \row \i Delete \i Delete the character to the right of the cursor |
|
502 \row \i Ctrl+A \i Move the cursor to the beginning of the line |
|
503 \row \i Ctrl+B \i Move the cursor one character left |
|
504 \row \i Ctrl+C \i Copy the marked text to the clipboard (also |
|
505 Ctrl+Insert under Windows) |
|
506 \row \i Ctrl+D \i Delete the character to the right of the cursor |
|
507 \row \i Ctrl+E \i Move the cursor to the end of the line |
|
508 \row \i Ctrl+F \i Move the cursor one character right |
|
509 \row \i Ctrl+H \i Delete the character to the left of the cursor |
|
510 \row \i Ctrl+K \i Delete to end of line |
|
511 \row \i Ctrl+N \i Move the cursor one line down |
|
512 \row \i Ctrl+P \i Move the cursor one line up |
|
513 \row \i Ctrl+V \i Paste the clipboard text into line edit |
|
514 (also Shift+Insert under Windows) |
|
515 \row \i Ctrl+X \i Cut the marked text, copy to clipboard |
|
516 (also Shift+Delete under Windows) |
|
517 \row \i Ctrl+Z \i Undo the last operation |
|
518 \row \i Ctrl+Y \i Redo the last operation |
|
519 \row \i Left \i Move the cursor one character left |
|
520 \row \i Ctrl+Left \i Move the cursor one word left |
|
521 \row \i Right \i Move the cursor one character right |
|
522 \row \i Ctrl+Right \i Move the cursor one word right |
|
523 \row \i Up \i Move the cursor one line up |
|
524 \row \i Ctrl+Qt::Up \i Move the cursor one word up |
|
525 \row \i DownArrow \i Move the cursor one line down |
|
526 \row \i Ctrl+Down \i Move the cursor one word down |
|
527 \row \i PageUp \i Move the cursor one page up |
|
528 \row \i PageDown \i Move the cursor one page down |
|
529 \row \i Home \i Move the cursor to the beginning of the line |
|
530 \row \i Ctrl+Home \i Move the cursor to the beginning of the text |
|
531 \row \i End \i Move the cursor to the end of the line |
|
532 \row \i Ctrl+End \i Move the cursor to the end of the text |
|
533 \row \i Shift+Wheel \i Scroll the page horizontally |
|
534 (the Wheel is the mouse wheel) |
|
535 \row \i Ctrl+Wheel \i Zoom the text |
|
536 \endtable |
|
537 |
|
538 To select (mark) text hold down the Shift key whilst pressing one |
|
539 of the movement keystrokes, for example, \e{Shift+Right} |
|
540 will select the character to the right, and \e{Shift+Ctrl+Right} will select the word to the right, etc. |
|
541 |
|
542 By default the text edit widget operates in insert mode so all |
|
543 text that the user enters is inserted into the text edit and any |
|
544 text to the right of the cursor is moved out of the way. The mode |
|
545 can be changed to overwrite, where new text overwrites any text to |
|
546 the right of the cursor, using setOverwriteMode(). |
|
547 */ |
|
548 |
|
549 /*! |
|
550 \enum Q3TextEdit::AutoFormattingFlag |
|
551 |
|
552 \value AutoNone Do not perform any automatic formatting |
|
553 \value AutoBulletList Only automatically format bulletted lists |
|
554 \value AutoAll Apply all available autoformatting |
|
555 */ |
|
556 |
|
557 |
|
558 /*! |
|
559 \enum Q3TextEdit::KeyboardAction |
|
560 |
|
561 This enum is used by doKeyboardAction() to specify which action |
|
562 should be executed: |
|
563 |
|
564 \value ActionBackspace Delete the character to the left of the |
|
565 cursor. |
|
566 |
|
567 \value ActionDelete Delete the character to the right of the |
|
568 cursor. |
|
569 |
|
570 \value ActionReturn Split the paragraph at the cursor position. |
|
571 |
|
572 \value ActionKill If the cursor is not at the end of the |
|
573 paragraph, delete the text from the cursor position until the end |
|
574 of the paragraph. If the cursor is at the end of the paragraph, |
|
575 delete the hard line break at the end of the paragraph: this will |
|
576 cause this paragraph to be joined with the following paragraph. |
|
577 |
|
578 \value ActionWordBackspace Delete the word to the left of the |
|
579 cursor position. |
|
580 |
|
581 \value ActionWordDelete Delete the word to the right of the |
|
582 cursor position |
|
583 |
|
584 */ |
|
585 |
|
586 /*! |
|
587 \enum Q3TextEdit::VerticalAlignment |
|
588 |
|
589 This enum is used to set the vertical alignment of the text. |
|
590 |
|
591 \value AlignNormal Normal alignment |
|
592 \value AlignSuperScript Superscript |
|
593 \value AlignSubScript Subscript |
|
594 */ |
|
595 |
|
596 /*! |
|
597 \enum Q3TextEdit::TextInsertionFlags |
|
598 |
|
599 \internal |
|
600 |
|
601 \value RedoIndentation |
|
602 \value CheckNewLines |
|
603 \value RemoveSelected |
|
604 */ |
|
605 |
|
606 |
|
607 /*! |
|
608 \fn void Q3TextEdit::copyAvailable(bool yes) |
|
609 |
|
610 This signal is emitted when text is selected or de-selected in the |
|
611 text edit. |
|
612 |
|
613 When text is selected this signal will be emitted with \a yes set |
|
614 to true. If no text has been selected or if the selected text is |
|
615 de-selected this signal is emitted with \a yes set to false. |
|
616 |
|
617 If \a yes is true then copy() can be used to copy the selection to |
|
618 the clipboard. If \a yes is false then copy() does nothing. |
|
619 |
|
620 \sa selectionChanged() |
|
621 */ |
|
622 |
|
623 |
|
624 /*! |
|
625 \fn void Q3TextEdit::textChanged() |
|
626 |
|
627 This signal is emitted whenever the text in the text edit changes. |
|
628 |
|
629 \sa setText() append() |
|
630 */ |
|
631 |
|
632 /*! |
|
633 \fn void Q3TextEdit::selectionChanged() |
|
634 |
|
635 This signal is emitted whenever the selection changes. |
|
636 |
|
637 \sa setSelection() copyAvailable() |
|
638 */ |
|
639 |
|
640 /*! \fn Q3TextDocument *Q3TextEdit::document() const |
|
641 |
|
642 \internal |
|
643 |
|
644 This function returns the Q3TextDocument which is used by the text |
|
645 edit. |
|
646 */ |
|
647 |
|
648 /*! \fn void Q3TextEdit::setDocument(Q3TextDocument *doc) |
|
649 |
|
650 \internal |
|
651 |
|
652 This function sets the Q3TextDocument which should be used by the text |
|
653 edit to \a doc. This can be used, for example, if you want to |
|
654 display a document using multiple views. You would create a |
|
655 Q3TextDocument and set it to the text edits which should display it. |
|
656 You would need to connect to the textChanged() and |
|
657 selectionChanged() signals of all the text edits and update them all |
|
658 accordingly (preferably with a slight delay for efficiency reasons). |
|
659 */ |
|
660 |
|
661 /*! |
|
662 \enum Q3TextEdit::CursorAction |
|
663 |
|
664 This enum is used by moveCursor() to specify in which direction |
|
665 the cursor should be moved: |
|
666 |
|
667 \value MoveBackward Moves the cursor one character backward |
|
668 |
|
669 \value MoveWordBackward Moves the cursor one word backward |
|
670 |
|
671 \value MoveForward Moves the cursor one character forward |
|
672 |
|
673 \value MoveWordForward Moves the cursor one word forward |
|
674 |
|
675 \value MoveUp Moves the cursor up one line |
|
676 |
|
677 \value MoveDown Moves the cursor down one line |
|
678 |
|
679 \value MoveLineStart Moves the cursor to the beginning of the line |
|
680 |
|
681 \value MoveLineEnd Moves the cursor to the end of the line |
|
682 |
|
683 \value MoveHome Moves the cursor to the beginning of the document |
|
684 |
|
685 \value MoveEnd Moves the cursor to the end of the document |
|
686 |
|
687 \value MovePgUp Moves the cursor one viewport page up |
|
688 |
|
689 \value MovePgDown Moves the cursor one viewport page down |
|
690 */ |
|
691 |
|
692 /*! |
|
693 \property Q3TextEdit::overwriteMode |
|
694 \brief the text edit's overwrite mode |
|
695 |
|
696 If false (the default) characters entered by the user are inserted |
|
697 with any characters to the right being moved out of the way. If |
|
698 true, the editor is in overwrite mode, i.e. characters entered by |
|
699 the user overwrite any characters to the right of the cursor |
|
700 position. |
|
701 */ |
|
702 |
|
703 /*! |
|
704 \fn void Q3TextEdit::setCurrentFont(const QFont &f) |
|
705 |
|
706 Sets the font of the current format to \a f. |
|
707 |
|
708 If the widget is in Qt::LogText mode this function will do |
|
709 nothing. Use setFont() instead. |
|
710 |
|
711 \sa currentFont() setPointSize() setFamily() |
|
712 */ |
|
713 |
|
714 /*! |
|
715 \property Q3TextEdit::undoDepth |
|
716 \brief the depth of the undo history |
|
717 |
|
718 The maximum number of steps in the undo/redo history. The default |
|
719 is 100. |
|
720 |
|
721 \sa undo() redo() |
|
722 */ |
|
723 |
|
724 /*! |
|
725 \fn void Q3TextEdit::undoAvailable(bool yes) |
|
726 |
|
727 This signal is emitted when the availability of undo changes. If |
|
728 \a yes is true, then undo() will work until undoAvailable(false) |
|
729 is next emitted. |
|
730 |
|
731 \sa undo() undoDepth() |
|
732 */ |
|
733 |
|
734 /*! |
|
735 \fn void Q3TextEdit::modificationChanged(bool m) |
|
736 |
|
737 This signal is emitted when the modification status of the |
|
738 document has changed. If \a m is true, the document was modified, |
|
739 otherwise the modification state has been reset to unmodified. |
|
740 |
|
741 \sa modified |
|
742 */ |
|
743 |
|
744 /*! |
|
745 \fn void Q3TextEdit::redoAvailable(bool yes) |
|
746 |
|
747 This signal is emitted when the availability of redo changes. If |
|
748 \a yes is true, then redo() will work until redoAvailable(false) |
|
749 is next emitted. |
|
750 |
|
751 \sa redo() undoDepth() |
|
752 */ |
|
753 |
|
754 /*! |
|
755 \fn void Q3TextEdit::currentFontChanged(const QFont &f) |
|
756 |
|
757 This signal is emitted if the font of the current format has |
|
758 changed. |
|
759 |
|
760 The new font is \a f. |
|
761 |
|
762 \sa setCurrentFont() |
|
763 */ |
|
764 |
|
765 /*! |
|
766 \fn void Q3TextEdit::currentColorChanged(const QColor &c) |
|
767 |
|
768 This signal is emitted if the color of the current format has |
|
769 changed. |
|
770 |
|
771 The new color is \a c. |
|
772 |
|
773 \sa setColor() |
|
774 */ |
|
775 |
|
776 /*! |
|
777 \fn void Q3TextEdit::currentVerticalAlignmentChanged(Q3TextEdit::VerticalAlignment a) |
|
778 |
|
779 This signal is emitted if the vertical alignment of the current |
|
780 format has changed. |
|
781 |
|
782 The new vertical alignment is \a a. |
|
783 */ |
|
784 |
|
785 /*! |
|
786 \fn void Q3TextEdit::currentAlignmentChanged(int a) |
|
787 |
|
788 This signal is emitted if the alignment of the current paragraph |
|
789 has changed. |
|
790 |
|
791 The new alignment is \a a. |
|
792 |
|
793 \sa setAlignment() |
|
794 */ |
|
795 |
|
796 /*! |
|
797 \fn void Q3TextEdit::cursorPositionChanged(Q3TextCursor *c) |
|
798 |
|
799 \internal |
|
800 */ |
|
801 |
|
802 /*! |
|
803 \fn void Q3TextEdit::cursorPositionChanged(int para, int pos) |
|
804 |
|
805 \overload |
|
806 |
|
807 This signal is emitted if the position of the cursor has changed. |
|
808 \a para contains the paragraph index and \a pos contains the |
|
809 character position within the paragraph. |
|
810 |
|
811 \sa setCursorPosition() |
|
812 */ |
|
813 |
|
814 /*! |
|
815 \fn void Q3TextEdit::clicked(int para, int pos) |
|
816 |
|
817 This signal is emitted when the mouse is clicked on the paragraph |
|
818 \a para at character position \a pos. |
|
819 |
|
820 \sa doubleClicked() |
|
821 */ |
|
822 |
|
823 /*! \fn void Q3TextEdit::doubleClicked(int para, int pos) |
|
824 |
|
825 This signal is emitted when the mouse is double-clicked on the |
|
826 paragraph \a para at character position \a pos. |
|
827 |
|
828 \sa clicked() |
|
829 */ |
|
830 |
|
831 |
|
832 /*! |
|
833 \fn void Q3TextEdit::returnPressed() |
|
834 |
|
835 This signal is emitted if the user pressed the Return or the Enter |
|
836 key. |
|
837 */ |
|
838 |
|
839 /*! |
|
840 \fn Q3TextCursor *Q3TextEdit::textCursor() const |
|
841 |
|
842 Returns the text edit's text cursor. |
|
843 |
|
844 \warning Q3TextCursor is not in the public API, but in special |
|
845 circumstances you might wish to use it. |
|
846 */ |
|
847 |
|
848 /*! |
|
849 Constructs an empty Q3TextEdit called \a name, with parent \a |
|
850 parent. |
|
851 */ |
|
852 |
|
853 Q3TextEdit::Q3TextEdit(QWidget *parent, const char *name) |
|
854 : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase), |
|
855 doc(new Q3TextDocument(0)), undoRedoInfo(doc) |
|
856 { |
|
857 init(); |
|
858 } |
|
859 |
|
860 /*! |
|
861 Constructs a Q3TextEdit called \a name, with parent \a parent. The |
|
862 text edit will display the text \a text using context \a context. |
|
863 |
|
864 The \a context is a path which the text edit's Q3MimeSourceFactory |
|
865 uses to resolve the locations of files and images. It is passed to |
|
866 the mimeSourceFactory() when quering data. |
|
867 |
|
868 For example if the text contains an image tag, |
|
869 \c{<img src="image.png">}, and the context is "path/to/look/in", the |
|
870 Q3MimeSourceFactory will try to load the image from |
|
871 "path/to/look/in/image.png". If the tag was |
|
872 \c{<img src="/image.png">}, the context will not be used (because |
|
873 Q3MimeSourceFactory recognizes that we have used an absolute path) |
|
874 and will try to load "/image.png". The context is applied in exactly |
|
875 the same way to \e hrefs, for example, |
|
876 \c{<a href="target.html">Target</a>}, would resolve to |
|
877 "path/to/look/in/target.html". |
|
878 */ |
|
879 |
|
880 Q3TextEdit::Q3TextEdit(const QString& text, const QString& context, |
|
881 QWidget *parent, const char *name) |
|
882 : Q3ScrollView(parent, name, Qt::WStaticContents | Qt::WNoAutoErase), |
|
883 doc(new Q3TextDocument(0)), undoRedoInfo(doc) |
|
884 { |
|
885 init(); |
|
886 setText(text, context); |
|
887 } |
|
888 |
|
889 /*! |
|
890 Destructor. |
|
891 */ |
|
892 |
|
893 Q3TextEdit::~Q3TextEdit() |
|
894 { |
|
895 delete undoRedoInfo.d; |
|
896 undoRedoInfo.d = 0; |
|
897 delete cursor; |
|
898 delete doc; |
|
899 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
900 delete d->od; |
|
901 #endif |
|
902 delete d; |
|
903 } |
|
904 |
|
905 void Q3TextEdit::init() |
|
906 { |
|
907 d = new Q3TextEditPrivate; |
|
908 doc->formatCollection()->setPaintDevice(this); |
|
909 undoEnabled = true; |
|
910 readonly = true; |
|
911 setReadOnly(false); |
|
912 setFrameStyle(LineEditPanel | Sunken); |
|
913 connect(doc, SIGNAL(minimumWidthChanged(int)), |
|
914 this, SLOT(documentWidthChanged(int))); |
|
915 |
|
916 mousePressed = false; |
|
917 inDoubleClick = false; |
|
918 modified = false; |
|
919 mightStartDrag = false; |
|
920 onLink.clear(); |
|
921 d->onName.clear(); |
|
922 overWrite = false; |
|
923 wrapMode = WidgetWidth; |
|
924 wrapWidth = -1; |
|
925 wPolicy = AtWhiteSpace; |
|
926 inDnD = false; |
|
927 doc->setFormatter(new Q3TextFormatterBreakWords); |
|
928 QFont f = Q3ScrollView::font(); |
|
929 if (f.kerning()) |
|
930 f.setKerning(false); |
|
931 doc->formatCollection()->defaultFormat()->setFont(f); |
|
932 doc->formatCollection()->defaultFormat()->setColor(palette().color(QPalette::Text)); |
|
933 currentFormat = doc->formatCollection()->defaultFormat(); |
|
934 currentAlignment = Qt::AlignAuto; |
|
935 |
|
936 setBackgroundRole(QPalette::Base); |
|
937 viewport()->setBackgroundRole(QPalette::Base); |
|
938 |
|
939 viewport()->setAcceptDrops(true); |
|
940 resizeContents(0, doc->lastParagraph() ? |
|
941 (doc->lastParagraph()->paragId() + 1) * doc->formatCollection()->defaultFormat()->height() : 0); |
|
942 |
|
943 setAttribute(Qt::WA_KeyCompression, true); |
|
944 viewport()->setMouseTracking(true); |
|
945 #ifndef QT_NO_CURSOR |
|
946 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
947 #endif |
|
948 cursor = new Q3TextCursor(doc); |
|
949 |
|
950 formatTimer = new QTimer(this); |
|
951 connect(formatTimer, SIGNAL(timeout()), |
|
952 this, SLOT(formatMore())); |
|
953 lastFormatted = doc->firstParagraph(); |
|
954 |
|
955 scrollTimer = new QTimer(this); |
|
956 connect(scrollTimer, SIGNAL(timeout()), |
|
957 this, SLOT(autoScrollTimerDone())); |
|
958 |
|
959 interval = 0; |
|
960 changeIntervalTimer = new QTimer(this); |
|
961 connect(changeIntervalTimer, SIGNAL(timeout()), |
|
962 this, SLOT(doChangeInterval())); |
|
963 |
|
964 cursorVisible = true; |
|
965 blinkTimer = new QTimer(this); |
|
966 connect(blinkTimer, SIGNAL(timeout()), |
|
967 this, SLOT(blinkCursor())); |
|
968 |
|
969 #ifndef QT_NO_DRAGANDDROP |
|
970 dragStartTimer = new QTimer(this); |
|
971 connect(dragStartTimer, SIGNAL(timeout()), |
|
972 this, SLOT(startDrag())); |
|
973 #endif |
|
974 |
|
975 d->trippleClickTimer = new QTimer(this); |
|
976 |
|
977 formatMore(); |
|
978 |
|
979 blinkCursorVisible = false; |
|
980 |
|
981 viewport()->setFocusProxy(this); |
|
982 viewport()->setFocusPolicy(Qt::WheelFocus); |
|
983 setFocusPolicy(Qt::WheelFocus); |
|
984 setInputMethodEnabled(true); |
|
985 viewport()->installEventFilter(this); |
|
986 connect(this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased())); |
|
987 connect(this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased())); |
|
988 installEventFilter(this); |
|
989 } |
|
990 |
|
991 void Q3TextEdit::paintDocument(bool drawAll, QPainter *p, int cx, int cy, int cw, int ch) |
|
992 { |
|
993 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
994 Q_ASSERT(!d->optimMode); |
|
995 if (d->optimMode) |
|
996 return; |
|
997 #endif |
|
998 |
|
999 bool drawCur = blinkCursorVisible && (hasFocus() || viewport()->hasFocus()); |
|
1000 if ((hasSelectedText() && !style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, 0, this)) || |
|
1001 isReadOnly() || !cursorVisible) |
|
1002 drawCur = false; |
|
1003 QPalette pal = palette(); |
|
1004 if (doc->paper()) |
|
1005 pal.setBrush(QPalette::Base, *doc->paper()); |
|
1006 |
|
1007 if (contentsY() < doc->y()) { |
|
1008 p->fillRect(contentsX(), contentsY(), visibleWidth(), doc->y(), |
|
1009 pal.base()); |
|
1010 } |
|
1011 if (drawAll && doc->width() - contentsX() < cx + cw) { |
|
1012 p->fillRect(doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch, |
|
1013 pal.base()); |
|
1014 } |
|
1015 |
|
1016 p->setBrushOrigin(-contentsX(), -contentsY()); |
|
1017 |
|
1018 lastFormatted = doc->draw(p, cx, cy, cw, ch, pal, !drawAll, drawCur, cursor); |
|
1019 |
|
1020 if (lastFormatted == doc->lastParagraph()) |
|
1021 resizeContents(contentsWidth(), doc->height()); |
|
1022 |
|
1023 if (contentsHeight() < visibleHeight() && (!doc->lastParagraph() || doc->lastParagraph()->isValid()) && drawAll) |
|
1024 p->fillRect(0, contentsHeight(), visibleWidth(), |
|
1025 visibleHeight() - contentsHeight(), pal.base()); |
|
1026 } |
|
1027 |
|
1028 /*! |
|
1029 \reimp |
|
1030 */ |
|
1031 |
|
1032 void Q3TextEdit::drawContents(QPainter *p, int cx, int cy, int cw, int ch) |
|
1033 { |
|
1034 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
1035 if (d->optimMode) { |
|
1036 optimDrawContents(p, cx, cy, cw, ch); |
|
1037 return; |
|
1038 } |
|
1039 #endif |
|
1040 paintDocument(true, p, cx, cy, cw, ch); |
|
1041 int v; |
|
1042 p->setPen(palette().color(foregroundRole())); |
|
1043 if (document()->isPageBreakEnabled() && (v = document()->flow()->pageSize()) > 0) { |
|
1044 int l = int(cy / v) * v; |
|
1045 while (l < cy + ch) { |
|
1046 p->drawLine(cx, l, cx + cw - 1, l); |
|
1047 l += v; |
|
1048 } |
|
1049 } |
|
1050 } |
|
1051 |
|
1052 /*! |
|
1053 \internal |
|
1054 */ |
|
1055 |
|
1056 void Q3TextEdit::drawContents(QPainter *p) |
|
1057 { |
|
1058 if (horizontalScrollBar()->isVisible() && |
|
1059 verticalScrollBar()->isVisible()) { |
|
1060 const QRect verticalRect = verticalScrollBar()->geometry(); |
|
1061 const QRect horizontalRect = horizontalScrollBar()->geometry(); |
|
1062 |
|
1063 QRect cornerRect; |
|
1064 cornerRect.setTop(verticalRect.bottom()); |
|
1065 cornerRect.setBottom(horizontalRect.bottom()); |
|
1066 cornerRect.setLeft(verticalRect.left()); |
|
1067 cornerRect.setRight(verticalRect.right()); |
|
1068 |
|
1069 p->fillRect(cornerRect, palette().background()); |
|
1070 } |
|
1071 } |
|
1072 |
|
1073 /*! |
|
1074 \reimp |
|
1075 */ |
|
1076 |
|
1077 bool Q3TextEdit::event(QEvent *e) |
|
1078 { |
|
1079 if (e->type() == QEvent::AccelOverride && !isReadOnly()) { |
|
1080 QKeyEvent* ke = (QKeyEvent*) e; |
|
1081 switch(ke->state()) { |
|
1082 case Qt::NoButton: |
|
1083 case Qt::Keypad: |
|
1084 case Qt::ShiftButton: |
|
1085 if (ke->key() < Qt::Key_Escape) { |
|
1086 ke->accept(); |
|
1087 } else { |
|
1088 switch (ke->key()) { |
|
1089 case Qt::Key_Return: |
|
1090 case Qt::Key_Enter: |
|
1091 case Qt::Key_Delete: |
|
1092 case Qt::Key_Home: |
|
1093 case Qt::Key_End: |
|
1094 case Qt::Key_Backspace: |
|
1095 case Qt::Key_Left: |
|
1096 case Qt::Key_Right: |
|
1097 ke->accept(); |
|
1098 default: |
|
1099 break; |
|
1100 } |
|
1101 } |
|
1102 break; |
|
1103 |
|
1104 case Qt::ControlButton: |
|
1105 case Qt::ControlButton|Qt::ShiftButton: |
|
1106 case Qt::ControlButton|Qt::Keypad: |
|
1107 case Qt::ControlButton|Qt::ShiftButton|Qt::Keypad: |
|
1108 switch (ke->key()) { |
|
1109 case Qt::Key_Tab: |
|
1110 case Qt::Key_Backtab: |
|
1111 ke->ignore(); |
|
1112 break; |
|
1113 // Those are too frequently used for application functionality |
|
1114 /* case Qt::Key_A: |
|
1115 case Qt::Key_B: |
|
1116 case Qt::Key_D: |
|
1117 case Qt::Key_E: |
|
1118 case Qt::Key_F: |
|
1119 case Qt::Key_H: |
|
1120 case Qt::Key_I: |
|
1121 case Qt::Key_K: |
|
1122 case Qt::Key_N: |
|
1123 case Qt::Key_P: |
|
1124 case Qt::Key_T: |
|
1125 */ |
|
1126 case Qt::Key_C: |
|
1127 case Qt::Key_V: |
|
1128 case Qt::Key_X: |
|
1129 case Qt::Key_Y: |
|
1130 case Qt::Key_Z: |
|
1131 case Qt::Key_Left: |
|
1132 case Qt::Key_Right: |
|
1133 case Qt::Key_Up: |
|
1134 case Qt::Key_Down: |
|
1135 case Qt::Key_Home: |
|
1136 case Qt::Key_End: |
|
1137 #if defined (Q_WS_WIN) |
|
1138 case Qt::Key_Insert: |
|
1139 case Qt::Key_Delete: |
|
1140 #endif |
|
1141 ke->accept(); |
|
1142 default: |
|
1143 break; |
|
1144 } |
|
1145 break; |
|
1146 |
|
1147 default: |
|
1148 switch (ke->key()) { |
|
1149 #if defined (Q_WS_WIN) |
|
1150 case Qt::Key_Insert: |
|
1151 ke->accept(); |
|
1152 #endif |
|
1153 default: |
|
1154 break; |
|
1155 } |
|
1156 break; |
|
1157 } |
|
1158 } |
|
1159 |
|
1160 if (e->type() == QEvent::Show) { |
|
1161 if ( |
|
1162 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
1163 !d->optimMode && |
|
1164 #endif |
|
1165 d->ensureCursorVisibleInShowEvent ) { |
|
1166 ensureCursorVisible(); |
|
1167 d->ensureCursorVisibleInShowEvent = false; |
|
1168 } |
|
1169 if (!d->scrollToAnchor.isEmpty()) { |
|
1170 scrollToAnchor(d->scrollToAnchor); |
|
1171 d->scrollToAnchor.clear(); |
|
1172 } |
|
1173 } |
|
1174 return QWidget::event(e); |
|
1175 } |
|
1176 |
|
1177 /*! |
|
1178 Processes the key event, \a e. By default key events are used to |
|
1179 provide keyboard navigation and text editing. |
|
1180 */ |
|
1181 |
|
1182 void Q3TextEdit::keyPressEvent(QKeyEvent *e) |
|
1183 { |
|
1184 changeIntervalTimer->stop(); |
|
1185 interval = 10; |
|
1186 bool unknownKey = false; |
|
1187 if (isReadOnly()) { |
|
1188 if (!handleReadOnlyKeyEvent(e)) |
|
1189 Q3ScrollView::keyPressEvent(e); |
|
1190 changeIntervalTimer->start(100, true); |
|
1191 return; |
|
1192 } |
|
1193 |
|
1194 |
|
1195 bool selChanged = false; |
|
1196 for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection |
|
1197 selChanged = doc->removeSelection(i) || selChanged; |
|
1198 |
|
1199 if (selChanged) { |
|
1200 cursor->paragraph()->document()->nextDoubleBuffered = true; |
|
1201 repaintChanged(); |
|
1202 } |
|
1203 |
|
1204 bool clearUndoRedoInfo = true; |
|
1205 |
|
1206 |
|
1207 switch (e->key()) { |
|
1208 case Qt::Key_Left: |
|
1209 case Qt::Key_Right: { |
|
1210 // a bit hacky, but can't change this without introducing new enum values for move and keeping the |
|
1211 // correct semantics and movement for BiDi and non BiDi text. |
|
1212 CursorAction a; |
|
1213 if (cursor->paragraph()->string()->isRightToLeft() == (e->key() == Qt::Key_Right)) |
|
1214 a = e->state() & Qt::ControlButton ? MoveWordBackward : MoveBackward; |
|
1215 else |
|
1216 a = e->state() & Qt::ControlButton ? MoveWordForward : MoveForward; |
|
1217 moveCursor(a, e->state() & Qt::ShiftButton); |
|
1218 break; |
|
1219 } |
|
1220 case Qt::Key_Up: |
|
1221 moveCursor(e->state() & Qt::ControlButton ? MovePgUp : MoveUp, e->state() & Qt::ShiftButton); |
|
1222 break; |
|
1223 case Qt::Key_Down: |
|
1224 moveCursor(e->state() & Qt::ControlButton ? MovePgDown : MoveDown, e->state() & Qt::ShiftButton); |
|
1225 break; |
|
1226 case Qt::Key_Home: |
|
1227 moveCursor(e->state() & Qt::ControlButton ? MoveHome : MoveLineStart, e->state() & Qt::ShiftButton); |
|
1228 break; |
|
1229 case Qt::Key_End: |
|
1230 moveCursor(e->state() & Qt::ControlButton ? MoveEnd : MoveLineEnd, e->state() & Qt::ShiftButton); |
|
1231 break; |
|
1232 case Qt::Key_Prior: |
|
1233 moveCursor(MovePgUp, e->state() & Qt::ShiftButton); |
|
1234 break; |
|
1235 case Qt::Key_Next: |
|
1236 moveCursor(MovePgDown, e->state() & Qt::ShiftButton); |
|
1237 break; |
|
1238 case Qt::Key_Return: case Qt::Key_Enter: |
|
1239 if (doc->hasSelection(Q3TextDocument::Standard, false)) |
|
1240 removeSelectedText(); |
|
1241 if (textFormat() == Qt::RichText && (e->state() & Qt::ControlButton)) { |
|
1242 // Ctrl-Enter inserts a line break in rich text mode |
|
1243 insert(QString(QChar(QChar::LineSeparator)), true, false); |
|
1244 } else { |
|
1245 #ifndef QT_NO_CURSOR |
|
1246 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
1247 #endif |
|
1248 clearUndoRedoInfo = false; |
|
1249 doKeyboardAction(ActionReturn); |
|
1250 emit returnPressed(); |
|
1251 } |
|
1252 break; |
|
1253 case Qt::Key_Delete: |
|
1254 #if defined (Q_WS_WIN) |
|
1255 if (e->state() & Qt::ShiftButton) { |
|
1256 cut(); |
|
1257 break; |
|
1258 } else |
|
1259 #endif |
|
1260 if (doc->hasSelection(Q3TextDocument::Standard, true)) { |
|
1261 removeSelectedText(); |
|
1262 break; |
|
1263 } |
|
1264 doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordDelete |
|
1265 : ActionDelete); |
|
1266 clearUndoRedoInfo = false; |
|
1267 |
|
1268 break; |
|
1269 case Qt::Key_Insert: |
|
1270 if (e->state() & Qt::ShiftButton) |
|
1271 paste(); |
|
1272 #if defined (Q_WS_WIN) |
|
1273 else if (e->state() & Qt::ControlButton) |
|
1274 copy(); |
|
1275 #endif |
|
1276 else |
|
1277 setOverwriteMode(!isOverwriteMode()); |
|
1278 break; |
|
1279 case Qt::Key_Backspace: |
|
1280 #if defined (Q_WS_WIN) |
|
1281 if (e->state() & Qt::AltButton) { |
|
1282 if (e->state() & Qt::ControlButton) { |
|
1283 break; |
|
1284 } else if (e->state() & Qt::ShiftButton) { |
|
1285 redo(); |
|
1286 break; |
|
1287 } else { |
|
1288 undo(); |
|
1289 break; |
|
1290 } |
|
1291 } else |
|
1292 #endif |
|
1293 if (doc->hasSelection(Q3TextDocument::Standard, true)) { |
|
1294 removeSelectedText(); |
|
1295 break; |
|
1296 } |
|
1297 |
|
1298 doKeyboardAction(e->state() & Qt::ControlButton ? ActionWordBackspace |
|
1299 : ActionBackspace); |
|
1300 clearUndoRedoInfo = false; |
|
1301 break; |
|
1302 case Qt::Key_F16: // Copy key on Sun keyboards |
|
1303 copy(); |
|
1304 break; |
|
1305 case Qt::Key_F18: // Paste key on Sun keyboards |
|
1306 paste(); |
|
1307 break; |
|
1308 case Qt::Key_F20: // Cut key on Sun keyboards |
|
1309 cut(); |
|
1310 break; |
|
1311 case Qt::Key_Direction_L: |
|
1312 if (doc->textFormat() == Qt::PlainText) { |
|
1313 // change the whole doc |
|
1314 Q3TextParagraph *p = doc->firstParagraph(); |
|
1315 while (p) { |
|
1316 p->setDirection(QChar::DirL); |
|
1317 p->setAlignment(Qt::AlignLeft); |
|
1318 p->invalidate(0); |
|
1319 p = p->next(); |
|
1320 } |
|
1321 } else { |
|
1322 if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL) |
|
1323 return; |
|
1324 cursor->paragraph()->setDirection(QChar::DirL); |
|
1325 if (cursor->paragraph()->length() <= 1&& |
|
1326 ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0)) |
|
1327 setAlignment(Qt::AlignLeft); |
|
1328 } |
|
1329 repaintChanged(); |
|
1330 break; |
|
1331 case Qt::Key_Direction_R: |
|
1332 if (doc->textFormat() == Qt::PlainText) { |
|
1333 // change the whole doc |
|
1334 Q3TextParagraph *p = doc->firstParagraph(); |
|
1335 while (p) { |
|
1336 p->setDirection(QChar::DirR); |
|
1337 p->setAlignment(Qt::AlignRight); |
|
1338 p->invalidate(0); |
|
1339 p = p->next(); |
|
1340 } |
|
1341 } else { |
|
1342 if (!cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR) |
|
1343 return; |
|
1344 cursor->paragraph()->setDirection(QChar::DirR); |
|
1345 if (cursor->paragraph()->length() <= 1&& |
|
1346 ((cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight)) != 0)) |
|
1347 setAlignment(Qt::AlignRight); |
|
1348 } |
|
1349 repaintChanged(); |
|
1350 break; |
|
1351 default: { |
|
1352 unsigned char ascii = e->text().length() ? e->text().unicode()->latin1() : 0; |
|
1353 if (e->text().length() && |
|
1354 ((!(e->state() & Qt::ControlButton) && |
|
1355 #ifndef Q_OS_MAC |
|
1356 !(e->state() & Qt::AltButton) && |
|
1357 #endif |
|
1358 !(e->state() & Qt::MetaButton)) || |
|
1359 (((e->state() & (Qt::ControlButton | Qt::AltButton))) == (Qt::ControlButton|Qt::AltButton))) && |
|
1360 (!ascii || ascii >= 32 || e->text() == QString(QLatin1Char('\t')))) { |
|
1361 clearUndoRedoInfo = false; |
|
1362 if (e->key() == Qt::Key_Tab) { |
|
1363 if (d->tabChangesFocus) { |
|
1364 e->ignore(); |
|
1365 break; |
|
1366 } |
|
1367 if (textFormat() == Qt::RichText && cursor->index() == 0 |
|
1368 && (cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth())) { |
|
1369 clearUndoRedo(); |
|
1370 undoRedoInfo.type = UndoRedoInfo::Style; |
|
1371 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1372 undoRedoInfo.eid = undoRedoInfo.id; |
|
1373 undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid); |
|
1374 cursor->paragraph()->setListDepth(cursor->paragraph()->listDepth() +1); |
|
1375 clearUndoRedo(); |
|
1376 drawCursor(false); |
|
1377 repaintChanged(); |
|
1378 drawCursor(true); |
|
1379 break; |
|
1380 } |
|
1381 } else if (e->key() == Qt::Key_BackTab) { |
|
1382 if (d->tabChangesFocus) { |
|
1383 e->ignore(); |
|
1384 break; |
|
1385 } |
|
1386 } |
|
1387 |
|
1388 if ((autoFormatting() & AutoBulletList) && |
|
1389 textFormat() == Qt::RichText && cursor->index() == 0 |
|
1390 && !cursor->paragraph()->isListItem() |
|
1391 && (e->text()[0] == QLatin1Char('-') || e->text()[0] == QLatin1Char('*'))) { |
|
1392 clearUndoRedo(); |
|
1393 undoRedoInfo.type = UndoRedoInfo::Style; |
|
1394 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1395 undoRedoInfo.eid = undoRedoInfo.id; |
|
1396 undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid); |
|
1397 setParagType(Q3StyleSheetItem::DisplayListItem, Q3StyleSheetItem::ListDisc); |
|
1398 clearUndoRedo(); |
|
1399 drawCursor(false); |
|
1400 repaintChanged(); |
|
1401 drawCursor(true); |
|
1402 break; |
|
1403 } |
|
1404 if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(Q3TextDocument::Standard)) { |
|
1405 doKeyboardAction(ActionDelete); |
|
1406 clearUndoRedoInfo = false; |
|
1407 } |
|
1408 QString t = e->text(); |
|
1409 insert(t, true, false); |
|
1410 break; |
|
1411 } else if (e->state() & Qt::ControlButton) { |
|
1412 switch (e->key()) { |
|
1413 case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards |
|
1414 copy(); |
|
1415 break; |
|
1416 case Qt::Key_V: |
|
1417 paste(); |
|
1418 break; |
|
1419 case Qt::Key_X: |
|
1420 cut(); |
|
1421 break; |
|
1422 case Qt::Key_I: case Qt::Key_T: case Qt::Key_Tab: |
|
1423 if (!d->tabChangesFocus) |
|
1424 indent(); |
|
1425 break; |
|
1426 case Qt::Key_A: |
|
1427 #if defined(Q_WS_X11) |
|
1428 moveCursor(MoveLineStart, e->state() & Qt::ShiftButton); |
|
1429 #else |
|
1430 selectAll(true); |
|
1431 #endif |
|
1432 break; |
|
1433 case Qt::Key_B: |
|
1434 moveCursor(MoveBackward, e->state() & Qt::ShiftButton); |
|
1435 break; |
|
1436 case Qt::Key_F: |
|
1437 moveCursor(MoveForward, e->state() & Qt::ShiftButton); |
|
1438 break; |
|
1439 case Qt::Key_D: |
|
1440 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
1441 removeSelectedText(); |
|
1442 break; |
|
1443 } |
|
1444 doKeyboardAction(ActionDelete); |
|
1445 clearUndoRedoInfo = false; |
|
1446 break; |
|
1447 case Qt::Key_H: |
|
1448 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
1449 removeSelectedText(); |
|
1450 break; |
|
1451 } |
|
1452 if (!cursor->paragraph()->prev() && |
|
1453 cursor->atParagStart()) |
|
1454 break; |
|
1455 |
|
1456 doKeyboardAction(ActionBackspace); |
|
1457 clearUndoRedoInfo = false; |
|
1458 break; |
|
1459 case Qt::Key_E: |
|
1460 moveCursor(MoveLineEnd, e->state() & Qt::ShiftButton); |
|
1461 break; |
|
1462 case Qt::Key_N: |
|
1463 moveCursor(MoveDown, e->state() & Qt::ShiftButton); |
|
1464 break; |
|
1465 case Qt::Key_P: |
|
1466 moveCursor(MoveUp, e->state() & Qt::ShiftButton); |
|
1467 break; |
|
1468 case Qt::Key_Z: |
|
1469 if(e->state() & Qt::ShiftButton) |
|
1470 redo(); |
|
1471 else |
|
1472 undo(); |
|
1473 break; |
|
1474 case Qt::Key_Y: |
|
1475 redo(); |
|
1476 break; |
|
1477 case Qt::Key_K: |
|
1478 doKeyboardAction(ActionKill); |
|
1479 break; |
|
1480 #if defined(Q_WS_WIN) |
|
1481 case Qt::Key_Insert: |
|
1482 copy(); |
|
1483 break; |
|
1484 case Qt::Key_Delete: |
|
1485 del(); |
|
1486 break; |
|
1487 #endif |
|
1488 default: |
|
1489 unknownKey = false; |
|
1490 break; |
|
1491 } |
|
1492 } else { |
|
1493 unknownKey = true; |
|
1494 } |
|
1495 } |
|
1496 } |
|
1497 |
|
1498 emit cursorPositionChanged(cursor); |
|
1499 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
1500 if (clearUndoRedoInfo) |
|
1501 clearUndoRedo(); |
|
1502 changeIntervalTimer->start(100, true); |
|
1503 if (unknownKey) |
|
1504 e->ignore(); |
|
1505 } |
|
1506 |
|
1507 /*! |
|
1508 \reimp |
|
1509 */ |
|
1510 void Q3TextEdit::inputMethodEvent(QInputMethodEvent *e) |
|
1511 { |
|
1512 if (isReadOnly()) { |
|
1513 e->ignore(); |
|
1514 return; |
|
1515 } |
|
1516 |
|
1517 if (hasSelectedText()) |
|
1518 removeSelectedText(); |
|
1519 clearUndoRedo(); |
|
1520 undoRedoInfo.type = UndoRedoInfo::IME; |
|
1521 |
|
1522 bool oldupdate = updatesEnabled(); |
|
1523 if (oldupdate) |
|
1524 setUpdatesEnabled(false); |
|
1525 bool sigs_blocked = signalsBlocked(); |
|
1526 blockSignals(true); |
|
1527 const int preeditSelectionBase = 31900; |
|
1528 for (int i = 0; i < d->numPreeditSelections; ++i) |
|
1529 doc->removeSelection(preeditSelectionBase + i); |
|
1530 d->numPreeditSelections = 0; |
|
1531 |
|
1532 if (d->preeditLength > 0 && cursor->paragraph()) { |
|
1533 cursor->setIndex(d->preeditStart); |
|
1534 cursor->paragraph()->remove(d->preeditStart, d->preeditLength); |
|
1535 d->preeditStart = d->preeditLength = -1; |
|
1536 } |
|
1537 |
|
1538 if (!e->commitString().isEmpty() || e->replacementLength()) { |
|
1539 int c = cursor->index(); // cursor position after insertion of commit string |
|
1540 if (e->replacementStart() <= 0) |
|
1541 c += e->commitString().length() + qMin(-e->replacementStart(), e->replacementLength()); |
|
1542 cursor->setIndex(cursor->index() + e->replacementStart()); |
|
1543 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
1544 cursor->setIndex(cursor->index() + e->replacementLength()); |
|
1545 doc->setSelectionEnd(Q3TextDocument::Standard, *cursor); |
|
1546 removeSelectedText(); |
|
1547 if (undoRedoInfo.type == UndoRedoInfo::IME) |
|
1548 undoRedoInfo.type = UndoRedoInfo::Invalid; |
|
1549 insert(e->commitString()); |
|
1550 undoRedoInfo.type = UndoRedoInfo::IME; |
|
1551 cursor->setIndex(c); |
|
1552 } |
|
1553 |
|
1554 if (!e->preeditString().isEmpty()) { |
|
1555 d->preeditStart = cursor->index(); |
|
1556 d->preeditLength = e->preeditString().length(); |
|
1557 insert(e->preeditString()); |
|
1558 cursor->setIndex(d->preeditStart); |
|
1559 |
|
1560 Q3TextCursor c = *cursor; |
|
1561 for (int i = 0; i < e->attributes().size(); ++i) { |
|
1562 const QInputMethodEvent::Attribute &a = e->attributes().at(i); |
|
1563 if (a.type == QInputMethodEvent::Cursor) |
|
1564 cursor->setIndex(cursor->index() + a.start); |
|
1565 else if (a.type != QInputMethodEvent::TextFormat) |
|
1566 continue; |
|
1567 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); |
|
1568 if (f.isValid()) { |
|
1569 Q3TextCursor c2 = c; |
|
1570 c2.setIndex(c.index() + a.start); |
|
1571 doc->setSelectionStart(preeditSelectionBase + d->numPreeditSelections, c2); |
|
1572 c2.setIndex(c.index() + a.start + a.length); |
|
1573 doc->setSelectionEnd(preeditSelectionBase + d->numPreeditSelections, c2); |
|
1574 |
|
1575 QColor c = f.hasProperty(QTextFormat::BackgroundBrush) ? f.background().color() : QColor(); |
|
1576 doc->setSelectionColor(preeditSelectionBase + d->numPreeditSelections, c); |
|
1577 c = f.hasProperty(QTextFormat::ForegroundBrush) ? f.foreground().color() : QColor(); |
|
1578 doc->setSelectionTextColor(preeditSelectionBase + d->numPreeditSelections, c); |
|
1579 if (f.fontUnderline()) { |
|
1580 Q3TextParagraph *par = cursor->paragraph(); |
|
1581 Q3TextFormat f(*par->string()->at(d->preeditStart).format()); |
|
1582 f.setUnderline(true); |
|
1583 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
1584 par->setFormat(d->preeditStart + a.start, a.length, f2); |
|
1585 } |
|
1586 ++d->numPreeditSelections; |
|
1587 } |
|
1588 } |
|
1589 } else { |
|
1590 undoRedoInfo.type = UndoRedoInfo::Invalid; |
|
1591 } |
|
1592 blockSignals(sigs_blocked); |
|
1593 if (oldupdate) |
|
1594 setUpdatesEnabled(true); |
|
1595 if (!e->commitString().isEmpty()) |
|
1596 emit textChanged(); |
|
1597 repaintChanged(); |
|
1598 } |
|
1599 |
|
1600 |
|
1601 static bool qtextedit_ignore_readonly = false; |
|
1602 |
|
1603 /*! |
|
1604 Executes keyboard action \a action. This is normally called by a |
|
1605 key event handler. |
|
1606 */ |
|
1607 |
|
1608 void Q3TextEdit::doKeyboardAction(Q3TextEdit::KeyboardAction action) |
|
1609 { |
|
1610 if (isReadOnly() && !qtextedit_ignore_readonly) |
|
1611 return; |
|
1612 |
|
1613 if (cursor->nestedDepth() != 0) |
|
1614 return; |
|
1615 |
|
1616 lastFormatted = cursor->paragraph(); |
|
1617 drawCursor(false); |
|
1618 bool doUpdateCurrentFormat = true; |
|
1619 |
|
1620 switch (action) { |
|
1621 case ActionWordDelete: |
|
1622 case ActionDelete: |
|
1623 if (action == ActionDelete && !cursor->atParagEnd()) { |
|
1624 if (undoEnabled) { |
|
1625 checkUndoRedoInfo(UndoRedoInfo::Delete); |
|
1626 if (!undoRedoInfo.valid()) { |
|
1627 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1628 undoRedoInfo.index = cursor->index(); |
|
1629 undoRedoInfo.d->text.clear(); |
|
1630 } |
|
1631 int idx = cursor->index(); |
|
1632 do { |
|
1633 undoRedoInfo.d->text.insert(undoRedoInfo.d->text.length(), cursor->paragraph()->at(idx++), true); |
|
1634 } while (!cursor->paragraph()->string()->validCursorPosition(idx)); |
|
1635 } |
|
1636 cursor->remove(); |
|
1637 } else { |
|
1638 clearUndoRedo(); |
|
1639 doc->setSelectionStart(Q3TextDocument::Temp, *cursor); |
|
1640 if (action == ActionWordDelete && !cursor->atParagEnd()) { |
|
1641 cursor->gotoNextWord(); |
|
1642 } else { |
|
1643 cursor->gotoNextLetter(); |
|
1644 } |
|
1645 doc->setSelectionEnd(Q3TextDocument::Temp, *cursor); |
|
1646 removeSelectedText(Q3TextDocument::Temp); |
|
1647 } |
|
1648 break; |
|
1649 case ActionWordBackspace: |
|
1650 case ActionBackspace: |
|
1651 if (textFormat() == Qt::RichText |
|
1652 && (cursor->paragraph()->isListItem() |
|
1653 || cursor->paragraph()->listDepth()) |
|
1654 && cursor->index() == 0) { |
|
1655 if (undoEnabled) { |
|
1656 clearUndoRedo(); |
|
1657 undoRedoInfo.type = UndoRedoInfo::Style; |
|
1658 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1659 undoRedoInfo.eid = undoRedoInfo.id; |
|
1660 undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid); |
|
1661 } |
|
1662 int ldepth = cursor->paragraph()->listDepth(); |
|
1663 if (cursor->paragraph()->isListItem() && ldepth == 1) { |
|
1664 cursor->paragraph()->setListItem(false); |
|
1665 } else if (qMax(ldepth, 1) == 1) { |
|
1666 cursor->paragraph()->setListItem(false); |
|
1667 cursor->paragraph()->setListDepth(0); |
|
1668 } else { |
|
1669 cursor->paragraph()->setListDepth(ldepth - 1); |
|
1670 } |
|
1671 clearUndoRedo(); |
|
1672 lastFormatted = cursor->paragraph(); |
|
1673 repaintChanged(); |
|
1674 drawCursor(true); |
|
1675 return; |
|
1676 } |
|
1677 |
|
1678 if (action == ActionBackspace && !cursor->atParagStart()) { |
|
1679 if (undoEnabled) { |
|
1680 checkUndoRedoInfo(UndoRedoInfo::Delete); |
|
1681 if (!undoRedoInfo.valid()) { |
|
1682 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1683 undoRedoInfo.index = cursor->index(); |
|
1684 undoRedoInfo.d->text.clear(); |
|
1685 } |
|
1686 undoRedoInfo.d->text.insert(0, cursor->paragraph()->at(cursor->index()-1), true); |
|
1687 undoRedoInfo.index = cursor->index()-1; |
|
1688 } |
|
1689 cursor->removePreviousChar(); |
|
1690 lastFormatted = cursor->paragraph(); |
|
1691 } else if (cursor->paragraph()->prev() |
|
1692 || (action == ActionWordBackspace |
|
1693 && !cursor->atParagStart())) { |
|
1694 clearUndoRedo(); |
|
1695 doc->setSelectionStart(Q3TextDocument::Temp, *cursor); |
|
1696 if (action == ActionWordBackspace && !cursor->atParagStart()) { |
|
1697 cursor->gotoPreviousWord(); |
|
1698 } else { |
|
1699 cursor->gotoPreviousLetter(); |
|
1700 } |
|
1701 doc->setSelectionEnd(Q3TextDocument::Temp, *cursor); |
|
1702 removeSelectedText(Q3TextDocument::Temp); |
|
1703 } |
|
1704 break; |
|
1705 case ActionReturn: |
|
1706 if (undoEnabled) { |
|
1707 checkUndoRedoInfo(UndoRedoInfo::Return); |
|
1708 if (!undoRedoInfo.valid()) { |
|
1709 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
1710 undoRedoInfo.index = cursor->index(); |
|
1711 undoRedoInfo.d->text.clear(); |
|
1712 } |
|
1713 undoRedoInfo.d->text += QString(QLatin1Char('\n')); |
|
1714 } |
|
1715 cursor->splitAndInsertEmptyParagraph(); |
|
1716 if (cursor->paragraph()->prev()) { |
|
1717 lastFormatted = cursor->paragraph()->prev(); |
|
1718 lastFormatted->invalidate(0); |
|
1719 } |
|
1720 doUpdateCurrentFormat = false; |
|
1721 break; |
|
1722 case ActionKill: |
|
1723 clearUndoRedo(); |
|
1724 doc->setSelectionStart(Q3TextDocument::Temp, *cursor); |
|
1725 if (cursor->atParagEnd()) |
|
1726 cursor->gotoNextLetter(); |
|
1727 else |
|
1728 cursor->setIndex(cursor->paragraph()->length() - 1); |
|
1729 doc->setSelectionEnd(Q3TextDocument::Temp, *cursor); |
|
1730 removeSelectedText(Q3TextDocument::Temp); |
|
1731 break; |
|
1732 } |
|
1733 |
|
1734 formatMore(); |
|
1735 repaintChanged(); |
|
1736 ensureCursorVisible(); |
|
1737 drawCursor(true); |
|
1738 if (doUpdateCurrentFormat) |
|
1739 updateCurrentFormat(); |
|
1740 setModified(); |
|
1741 emit textChanged(); |
|
1742 } |
|
1743 |
|
1744 void Q3TextEdit::readFormats(Q3TextCursor &c1, Q3TextCursor &c2, Q3TextString &text, bool fillStyles) |
|
1745 { |
|
1746 #ifndef QT_NO_DATASTREAM |
|
1747 QDataStream styleStream(&undoRedoInfo.styleInformation, IO_WriteOnly); |
|
1748 #endif |
|
1749 c2.restoreState(); |
|
1750 c1.restoreState(); |
|
1751 int lastIndex = text.length(); |
|
1752 if (c1.paragraph() == c2.paragraph()) { |
|
1753 for (int i = c1.index(); i < c2.index(); ++i) |
|
1754 text.insert(lastIndex + i - c1.index(), c1.paragraph()->at(i), true); |
|
1755 #ifndef QT_NO_DATASTREAM |
|
1756 if (fillStyles) { |
|
1757 styleStream << (int) 1; |
|
1758 c1.paragraph()->writeStyleInformation(styleStream); |
|
1759 } |
|
1760 #endif |
|
1761 } else { |
|
1762 int i; |
|
1763 for (i = c1.index(); i < c1.paragraph()->length()-1; ++i) |
|
1764 text.insert(lastIndex++, c1.paragraph()->at(i), true); |
|
1765 int num = 2; // start and end, being different |
|
1766 text += QString(QLatin1Char('\n')); lastIndex++; |
|
1767 |
|
1768 if (c1.paragraph()->next() != c2.paragraph()) { |
|
1769 num += text.appendParagraphs(c1.paragraph()->next(), c2.paragraph()); |
|
1770 lastIndex = text.length(); |
|
1771 } |
|
1772 |
|
1773 for (i = 0; i < c2.index(); ++i) |
|
1774 text.insert(i + lastIndex, c2.paragraph()->at(i), true); |
|
1775 #ifndef QT_NO_DATASTREAM |
|
1776 if (fillStyles) { |
|
1777 styleStream << num; |
|
1778 for (Q3TextParagraph *p = c1.paragraph(); --num >= 0; p = p->next()) |
|
1779 p->writeStyleInformation(styleStream); |
|
1780 } |
|
1781 #endif |
|
1782 } |
|
1783 } |
|
1784 |
|
1785 /*! |
|
1786 Removes the selection \a selNum (by default 0). This does not |
|
1787 remove the selected text. |
|
1788 |
|
1789 \sa removeSelectedText() |
|
1790 */ |
|
1791 |
|
1792 void Q3TextEdit::removeSelection(int selNum) |
|
1793 { |
|
1794 doc->removeSelection(selNum); |
|
1795 repaintChanged(); |
|
1796 } |
|
1797 |
|
1798 /*! |
|
1799 Deletes the text of selection \a selNum (by default, the default |
|
1800 selection, 0). If there is no selected text nothing happens. |
|
1801 |
|
1802 \sa selectedText removeSelection() |
|
1803 */ |
|
1804 |
|
1805 void Q3TextEdit::removeSelectedText(int selNum) |
|
1806 { |
|
1807 Q3TextCursor c1 = doc->selectionStartCursor(selNum); |
|
1808 c1.restoreState(); |
|
1809 Q3TextCursor c2 = doc->selectionEndCursor(selNum); |
|
1810 c2.restoreState(); |
|
1811 |
|
1812 // ### no support for editing tables yet, plus security for broken selections |
|
1813 if (c1.nestedDepth() || c2.nestedDepth()) |
|
1814 return; |
|
1815 |
|
1816 for (int i = 0; i < (int)doc->numSelections(); ++i) { |
|
1817 if (i == selNum) |
|
1818 continue; |
|
1819 doc->removeSelection(i); |
|
1820 } |
|
1821 |
|
1822 drawCursor(false); |
|
1823 if (undoEnabled) { |
|
1824 checkUndoRedoInfo(UndoRedoInfo::RemoveSelected); |
|
1825 if (!undoRedoInfo.valid()) { |
|
1826 doc->selectionStart(selNum, undoRedoInfo.id, undoRedoInfo.index); |
|
1827 undoRedoInfo.d->text.clear(); |
|
1828 } |
|
1829 readFormats(c1, c2, undoRedoInfo.d->text, true); |
|
1830 } |
|
1831 |
|
1832 doc->removeSelectedText(selNum, cursor); |
|
1833 if (cursor->isValid()) { |
|
1834 lastFormatted = 0; // make sync a noop |
|
1835 ensureCursorVisible(); |
|
1836 lastFormatted = cursor->paragraph(); |
|
1837 formatMore(); |
|
1838 repaintContents(); |
|
1839 ensureCursorVisible(); |
|
1840 drawCursor(true); |
|
1841 clearUndoRedo(); |
|
1842 #if defined(Q_WS_WIN) |
|
1843 // there seems to be a problem with repainting or erasing the area |
|
1844 // of the scrollview which is not the contents on windows |
|
1845 if (contentsHeight() < visibleHeight()) |
|
1846 viewport()->repaint(0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight()); |
|
1847 #endif |
|
1848 #ifndef QT_NO_CURSOR |
|
1849 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
1850 #endif |
|
1851 } else { |
|
1852 lastFormatted = doc->firstParagraph(); |
|
1853 delete cursor; |
|
1854 cursor = new Q3TextCursor(doc); |
|
1855 drawCursor(true); |
|
1856 repaintContents(); |
|
1857 } |
|
1858 setModified(); |
|
1859 emit textChanged(); |
|
1860 emit selectionChanged(); |
|
1861 emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard)); |
|
1862 } |
|
1863 |
|
1864 /*! |
|
1865 Moves the text cursor according to \a action. This is normally |
|
1866 used by some key event handler. \a select specifies whether the |
|
1867 text between the current cursor position and the new position |
|
1868 should be selected. |
|
1869 */ |
|
1870 |
|
1871 void Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action, bool select) |
|
1872 { |
|
1873 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
1874 if (d->optimMode) |
|
1875 return; |
|
1876 #endif |
|
1877 #ifdef Q_WS_MAC |
|
1878 Q3TextCursor c1 = *cursor; |
|
1879 Q3TextCursor c2; |
|
1880 #endif |
|
1881 drawCursor(false); |
|
1882 if (select) { |
|
1883 if (!doc->hasSelection(Q3TextDocument::Standard)) |
|
1884 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
1885 moveCursor(action); |
|
1886 #ifdef Q_WS_MAC |
|
1887 c2 = *cursor; |
|
1888 if (c1 == c2) |
|
1889 if (action == MoveDown || action == MovePgDown) |
|
1890 moveCursor(MoveEnd); |
|
1891 else if (action == MoveUp || action == MovePgUp) |
|
1892 moveCursor(MoveHome); |
|
1893 #endif |
|
1894 if (doc->setSelectionEnd(Q3TextDocument::Standard, *cursor)) { |
|
1895 cursor->paragraph()->document()->nextDoubleBuffered = true; |
|
1896 repaintChanged(); |
|
1897 } else { |
|
1898 drawCursor(true); |
|
1899 } |
|
1900 ensureCursorVisible(); |
|
1901 emit selectionChanged(); |
|
1902 emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard)); |
|
1903 } else { |
|
1904 #ifdef Q_WS_MAC |
|
1905 Q3TextCursor cStart = doc->selectionStartCursor(Q3TextDocument::Standard); |
|
1906 Q3TextCursor cEnd = doc->selectionEndCursor(Q3TextDocument::Standard); |
|
1907 bool redraw = doc->removeSelection(Q3TextDocument::Standard); |
|
1908 if (redraw && action == MoveDown) |
|
1909 *cursor = cEnd; |
|
1910 else if (redraw && action == MoveUp) |
|
1911 *cursor = cStart; |
|
1912 if (redraw && action == MoveForward) |
|
1913 *cursor = cEnd; |
|
1914 else if (redraw && action == MoveBackward) |
|
1915 *cursor = cStart; |
|
1916 else |
|
1917 moveCursor(action); |
|
1918 c2 = *cursor; |
|
1919 if (c1 == c2) |
|
1920 if (action == MoveDown) |
|
1921 moveCursor(MoveEnd); |
|
1922 else if (action == MoveUp) |
|
1923 moveCursor(MoveHome); |
|
1924 #else |
|
1925 bool redraw = doc->removeSelection(Q3TextDocument::Standard); |
|
1926 moveCursor(action); |
|
1927 #endif |
|
1928 if (!redraw) { |
|
1929 ensureCursorVisible(); |
|
1930 drawCursor(true); |
|
1931 } else { |
|
1932 cursor->paragraph()->document()->nextDoubleBuffered = true; |
|
1933 repaintChanged(); |
|
1934 ensureCursorVisible(); |
|
1935 drawCursor(true); |
|
1936 #ifndef QT_NO_CURSOR |
|
1937 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
1938 #endif |
|
1939 } |
|
1940 if (redraw) { |
|
1941 emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard)); |
|
1942 emit selectionChanged(); |
|
1943 } |
|
1944 } |
|
1945 |
|
1946 drawCursor(true); |
|
1947 updateCurrentFormat(); |
|
1948 } |
|
1949 |
|
1950 /*! |
|
1951 \overload |
|
1952 */ |
|
1953 |
|
1954 void Q3TextEdit::moveCursor(Q3TextEdit::CursorAction action) |
|
1955 { |
|
1956 resetInputContext(); |
|
1957 switch (action) { |
|
1958 case MoveBackward: |
|
1959 cursor->gotoPreviousLetter(); |
|
1960 break; |
|
1961 case MoveWordBackward: |
|
1962 cursor->gotoPreviousWord(); |
|
1963 break; |
|
1964 case MoveForward: |
|
1965 cursor->gotoNextLetter(); |
|
1966 break; |
|
1967 case MoveWordForward: |
|
1968 cursor->gotoNextWord(); |
|
1969 break; |
|
1970 case MoveUp: |
|
1971 cursor->gotoUp(); |
|
1972 break; |
|
1973 case MovePgUp: |
|
1974 cursor->gotoPageUp(visibleHeight()); |
|
1975 break; |
|
1976 case MoveDown: |
|
1977 cursor->gotoDown(); |
|
1978 break; |
|
1979 case MovePgDown: |
|
1980 cursor->gotoPageDown(visibleHeight()); |
|
1981 break; |
|
1982 case MoveLineStart: |
|
1983 cursor->gotoLineStart(); |
|
1984 break; |
|
1985 case MoveHome: |
|
1986 cursor->gotoHome(); |
|
1987 break; |
|
1988 case MoveLineEnd: |
|
1989 cursor->gotoLineEnd(); |
|
1990 break; |
|
1991 case MoveEnd: |
|
1992 ensureFormatted(doc->lastParagraph()); |
|
1993 cursor->gotoEnd(); |
|
1994 break; |
|
1995 } |
|
1996 updateCurrentFormat(); |
|
1997 } |
|
1998 |
|
1999 /*! |
|
2000 \reimp |
|
2001 */ |
|
2002 |
|
2003 void Q3TextEdit::resizeEvent(QResizeEvent *e) |
|
2004 { |
|
2005 Q3ScrollView::resizeEvent(e); |
|
2006 if (doc->visibleWidth() == 0) |
|
2007 doResize(); |
|
2008 } |
|
2009 |
|
2010 /*! |
|
2011 \reimp |
|
2012 */ |
|
2013 |
|
2014 void Q3TextEdit::viewportResizeEvent(QResizeEvent *e) |
|
2015 { |
|
2016 Q3ScrollView::viewportResizeEvent(e); |
|
2017 if (e->oldSize().width() != e->size().width()) { |
|
2018 bool stayAtBottom = e->oldSize().height() != e->size().height() && |
|
2019 contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height(); |
|
2020 doResize(); |
|
2021 if (stayAtBottom) |
|
2022 scrollToBottom(); |
|
2023 } |
|
2024 } |
|
2025 |
|
2026 /*! |
|
2027 Ensures that the cursor is visible by scrolling the text edit if |
|
2028 necessary. |
|
2029 |
|
2030 \sa setCursorPosition() |
|
2031 */ |
|
2032 |
|
2033 void Q3TextEdit::ensureCursorVisible() |
|
2034 { |
|
2035 // Not visible or the user is dragging the window, so don't position to caret yet |
|
2036 if (!updatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed()) { |
|
2037 d->ensureCursorVisibleInShowEvent = true; |
|
2038 return; |
|
2039 } |
|
2040 sync(); |
|
2041 Q3TextStringChar *chr = cursor->paragraph()->at(cursor->index()); |
|
2042 int h = cursor->paragraph()->lineHeightOfChar(cursor->index()); |
|
2043 int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX(); |
|
2044 int y = 0; int dummy; |
|
2045 cursor->paragraph()->lineHeightOfChar(cursor->index(), &dummy, &y); |
|
2046 y += cursor->paragraph()->rect().y() + cursor->offsetY(); |
|
2047 int w = 1; |
|
2048 ensureVisible(x, y + h / 2, w, h / 2 + 2); |
|
2049 } |
|
2050 |
|
2051 /*! |
|
2052 \internal |
|
2053 */ |
|
2054 void Q3TextEdit::sliderReleased() |
|
2055 { |
|
2056 if (d->ensureCursorVisibleInShowEvent && isVisible()) { |
|
2057 d->ensureCursorVisibleInShowEvent = false; |
|
2058 ensureCursorVisible(); |
|
2059 } |
|
2060 } |
|
2061 |
|
2062 /*! |
|
2063 \internal |
|
2064 |
|
2065 If \a visible is true, the cursor is shown; otherwise it is |
|
2066 hidden. |
|
2067 */ |
|
2068 void Q3TextEdit::drawCursor(bool visible) |
|
2069 { |
|
2070 d->cursorRepaintMode = true; |
|
2071 blinkCursorVisible = visible; |
|
2072 QRect r(cursor->topParagraph()->rect()); |
|
2073 if (!cursor->nestedDepth()) { |
|
2074 int h = cursor->paragraph()->lineHeightOfChar(cursor->index()); |
|
2075 r = QRect(r.x(), r.y() + cursor->y(), r.width(), h); |
|
2076 } |
|
2077 r.moveBy(-contentsX(), -contentsY()); |
|
2078 viewport()->update(r); |
|
2079 } |
|
2080 |
|
2081 enum { |
|
2082 IdUndo = 0, |
|
2083 IdRedo = 1, |
|
2084 IdCut = 2, |
|
2085 IdCopy = 3, |
|
2086 IdPaste = 4, |
|
2087 IdClear = 5, |
|
2088 IdSelectAll = 6 |
|
2089 }; |
|
2090 |
|
2091 /*! |
|
2092 \reimp |
|
2093 */ |
|
2094 #ifndef QT_NO_WHEELEVENT |
|
2095 void Q3TextEdit::contentsWheelEvent(QWheelEvent *e) |
|
2096 { |
|
2097 if (isReadOnly()) { |
|
2098 if (e->state() & Qt::ControlButton) { |
|
2099 if (e->delta() > 0) |
|
2100 zoomOut(); |
|
2101 else if (e->delta() < 0) |
|
2102 zoomIn(); |
|
2103 return; |
|
2104 } |
|
2105 } |
|
2106 Q3ScrollView::contentsWheelEvent(e); |
|
2107 } |
|
2108 #endif |
|
2109 |
|
2110 /*! |
|
2111 \reimp |
|
2112 */ |
|
2113 |
|
2114 void Q3TextEdit::contentsMousePressEvent(QMouseEvent *e) |
|
2115 { |
|
2116 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2117 if (d->optimMode) { |
|
2118 optimMousePressEvent(e); |
|
2119 return; |
|
2120 } |
|
2121 #endif |
|
2122 |
|
2123 #if !defined(QT_NO_IM) |
|
2124 if (e->button() == Qt::LeftButton && d->preeditLength > 0 && cursor->paragraph()) { |
|
2125 Q3TextCursor c = *cursor; |
|
2126 placeCursor(e->pos(), &c, false); |
|
2127 inputContext()->mouseHandler(c.index() - d->preeditStart, e); |
|
2128 if (d->preeditLength > 0) |
|
2129 return; |
|
2130 } |
|
2131 #endif |
|
2132 |
|
2133 if (d->trippleClickTimer->isActive() && |
|
2134 (e->globalPos() - d->trippleClickPoint).manhattanLength() < |
|
2135 QApplication::startDragDistance()) { |
|
2136 Q3TextCursor c1 = *cursor; |
|
2137 Q3TextCursor c2 = *cursor; |
|
2138 c1.gotoLineStart(); |
|
2139 c2.gotoLineEnd(); |
|
2140 doc->setSelectionStart(Q3TextDocument::Standard, c1); |
|
2141 doc->setSelectionEnd(Q3TextDocument::Standard, c2); |
|
2142 *cursor = c2; |
|
2143 repaintChanged(); |
|
2144 mousePressed = true; |
|
2145 return; |
|
2146 } |
|
2147 |
|
2148 clearUndoRedo(); |
|
2149 Q3TextCursor oldCursor = *cursor; |
|
2150 Q3TextCursor c = *cursor; |
|
2151 mousePos = e->pos(); |
|
2152 mightStartDrag = false; |
|
2153 pressedLink.clear(); |
|
2154 d->pressedName.clear(); |
|
2155 |
|
2156 if (e->button() == Qt::LeftButton) { |
|
2157 mousePressed = true; |
|
2158 drawCursor(false); |
|
2159 placeCursor(e->pos()); |
|
2160 ensureCursorVisible(); |
|
2161 |
|
2162 if (isReadOnly() && linksEnabled()) { |
|
2163 Q3TextCursor c = *cursor; |
|
2164 placeCursor(e->pos(), &c, true); |
|
2165 if (c.paragraph() && c.paragraph()->at(c.index()) && |
|
2166 c.paragraph()->at(c.index())->isAnchor()) { |
|
2167 pressedLink = c.paragraph()->at(c.index())->anchorHref(); |
|
2168 d->pressedName = c.paragraph()->at(c.index())->anchorName(); |
|
2169 } |
|
2170 } |
|
2171 |
|
2172 #ifndef QT_NO_DRAGANDDROP |
|
2173 if (doc->inSelection(Q3TextDocument::Standard, e->pos())) { |
|
2174 mightStartDrag = true; |
|
2175 drawCursor(true); |
|
2176 dragStartTimer->start(QApplication::startDragTime(), true); |
|
2177 dragStartPos = e->pos(); |
|
2178 return; |
|
2179 } |
|
2180 #endif |
|
2181 |
|
2182 bool redraw = false; |
|
2183 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
2184 if (!(e->state() & Qt::ShiftButton)) { |
|
2185 redraw = doc->removeSelection(Q3TextDocument::Standard); |
|
2186 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
2187 } else { |
|
2188 redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw; |
|
2189 } |
|
2190 } else { |
|
2191 if (isReadOnly() || !(e->state() & Qt::ShiftButton)) { |
|
2192 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
2193 } else { |
|
2194 doc->setSelectionStart(Q3TextDocument::Standard, c); |
|
2195 redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw; |
|
2196 } |
|
2197 } |
|
2198 |
|
2199 for (int i = 1; i < doc->numSelections(); ++i) // start with 1 as we don't want to remove the Standard-Selection |
|
2200 redraw = doc->removeSelection(i) || redraw; |
|
2201 |
|
2202 if (!redraw) { |
|
2203 drawCursor(true); |
|
2204 } else { |
|
2205 repaintChanged(); |
|
2206 #ifndef QT_NO_CURSOR |
|
2207 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
2208 #endif |
|
2209 } |
|
2210 } else if (e->button() == Qt::MidButton) { |
|
2211 bool redraw = doc->removeSelection(Q3TextDocument::Standard); |
|
2212 if (!redraw) { |
|
2213 drawCursor(true); |
|
2214 } else { |
|
2215 repaintChanged(); |
|
2216 #ifndef QT_NO_CURSOR |
|
2217 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
2218 #endif |
|
2219 } |
|
2220 } |
|
2221 |
|
2222 if (*cursor != oldCursor) |
|
2223 updateCurrentFormat(); |
|
2224 } |
|
2225 |
|
2226 /*! |
|
2227 \reimp |
|
2228 */ |
|
2229 |
|
2230 void Q3TextEdit::contentsMouseMoveEvent(QMouseEvent *e) |
|
2231 { |
|
2232 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2233 if (d->optimMode) { |
|
2234 optimMouseMoveEvent(e); |
|
2235 return; |
|
2236 } |
|
2237 #endif |
|
2238 |
|
2239 #if !defined(QT_NO_IM) |
|
2240 if (d->preeditLength > 0) |
|
2241 return; |
|
2242 #endif |
|
2243 |
|
2244 if (mousePressed) { |
|
2245 #ifndef QT_NO_DRAGANDDROP |
|
2246 if (mightStartDrag) { |
|
2247 dragStartTimer->stop(); |
|
2248 if ((e->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) |
|
2249 startDrag(); |
|
2250 #ifndef QT_NO_CURSOR |
|
2251 if (!isReadOnly()) |
|
2252 viewport()->setCursor(Qt::IBeamCursor); |
|
2253 #endif |
|
2254 return; |
|
2255 } |
|
2256 #endif |
|
2257 mousePos = e->pos(); |
|
2258 handleMouseMove(mousePos); |
|
2259 oldMousePos = mousePos; |
|
2260 } |
|
2261 |
|
2262 #ifndef QT_NO_CURSOR |
|
2263 if (!isReadOnly() && !mousePressed) { |
|
2264 if (doc->hasSelection(Q3TextDocument::Standard) && doc->inSelection(Q3TextDocument::Standard, e->pos())) |
|
2265 viewport()->setCursor(Qt::ArrowCursor); |
|
2266 else |
|
2267 viewport()->setCursor(Qt::IBeamCursor); |
|
2268 } |
|
2269 #endif |
|
2270 updateCursor(e->pos()); |
|
2271 } |
|
2272 |
|
2273 void Q3TextEdit::copyToClipboard() |
|
2274 { |
|
2275 #ifndef QT_NO_CLIPBOARD |
|
2276 if (QApplication::clipboard()->supportsSelection()) { |
|
2277 d->clipboard_mode = QClipboard::Selection; |
|
2278 |
|
2279 // don't listen to selection changes |
|
2280 disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); |
|
2281 copy(); |
|
2282 // listen to selection changes |
|
2283 connect(QApplication::clipboard(), SIGNAL(selectionChanged()), |
|
2284 this, SLOT(clipboardChanged())); |
|
2285 |
|
2286 d->clipboard_mode = QClipboard::Clipboard; |
|
2287 } |
|
2288 #endif |
|
2289 } |
|
2290 |
|
2291 /*! |
|
2292 \reimp |
|
2293 */ |
|
2294 |
|
2295 void Q3TextEdit::contentsMouseReleaseEvent(QMouseEvent * e) |
|
2296 { |
|
2297 if (!inDoubleClick) { // could be the release of a dblclick |
|
2298 int para = 0; |
|
2299 int index = charAt(e->pos(), ¶); |
|
2300 emit clicked(para, index); |
|
2301 } |
|
2302 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2303 if (d->optimMode) { |
|
2304 optimMouseReleaseEvent(e); |
|
2305 return; |
|
2306 } |
|
2307 #endif |
|
2308 Q3TextCursor oldCursor = *cursor; |
|
2309 if (scrollTimer->isActive()) |
|
2310 scrollTimer->stop(); |
|
2311 #ifndef QT_NO_DRAGANDDROP |
|
2312 if (dragStartTimer->isActive()) |
|
2313 dragStartTimer->stop(); |
|
2314 if (mightStartDrag) { |
|
2315 selectAll(false); |
|
2316 mousePressed = false; |
|
2317 } |
|
2318 #endif |
|
2319 if (mousePressed) { |
|
2320 mousePressed = false; |
|
2321 copyToClipboard(); |
|
2322 } |
|
2323 #ifndef QT_NO_CLIPBOARD |
|
2324 else if (e->button() == Qt::MidButton && !isReadOnly()) { |
|
2325 // only do middle-click pasting on systems that have selections (ie. X11) |
|
2326 if (QApplication::clipboard()->supportsSelection()) { |
|
2327 drawCursor(false); |
|
2328 placeCursor(e->pos()); |
|
2329 ensureCursorVisible(); |
|
2330 doc->setSelectionStart(Q3TextDocument::Standard, oldCursor); |
|
2331 bool redraw = false; |
|
2332 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
2333 redraw = doc->removeSelection(Q3TextDocument::Standard); |
|
2334 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
2335 } else { |
|
2336 doc->setSelectionStart(Q3TextDocument::Standard, *cursor); |
|
2337 } |
|
2338 // start with 1 as we don't want to remove the Standard-Selection |
|
2339 for (int i = 1; i < doc->numSelections(); ++i) |
|
2340 redraw = doc->removeSelection(i) || redraw; |
|
2341 if (!redraw) { |
|
2342 drawCursor(true); |
|
2343 } else { |
|
2344 repaintChanged(); |
|
2345 #ifndef QT_NO_CURSOR |
|
2346 viewport()->setCursor(Qt::IBeamCursor); |
|
2347 #endif |
|
2348 } |
|
2349 d->clipboard_mode = QClipboard::Selection; |
|
2350 paste(); |
|
2351 d->clipboard_mode = QClipboard::Clipboard; |
|
2352 } |
|
2353 } |
|
2354 #endif |
|
2355 emit cursorPositionChanged(cursor); |
|
2356 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
2357 if (oldCursor != *cursor) |
|
2358 updateCurrentFormat(); |
|
2359 inDoubleClick = false; |
|
2360 |
|
2361 #ifndef QT_NO_NETWORKPROTOCOL |
|
2362 if (( (!onLink.isEmpty() && onLink == pressedLink) |
|
2363 || (!d->onName.isEmpty() && d->onName == d->pressedName)) |
|
2364 && linksEnabled()) { |
|
2365 if (!onLink.isEmpty()) { |
|
2366 QUrl u = QUrl(doc->context()).resolved(onLink); |
|
2367 emitLinkClicked(u.toString(QUrl::None)); |
|
2368 } |
|
2369 if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this)) |
|
2370 emit browser->anchorClicked(d->onName, onLink); |
|
2371 |
|
2372 // emitting linkClicked() may result in that the cursor winds |
|
2373 // up hovering over a different valid link - check this and |
|
2374 // set the appropriate cursor shape |
|
2375 updateCursor(e->pos()); |
|
2376 } |
|
2377 #endif |
|
2378 drawCursor(true); |
|
2379 if (!doc->hasSelection(Q3TextDocument::Standard, true)) |
|
2380 doc->removeSelection(Q3TextDocument::Standard); |
|
2381 |
|
2382 emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard)); |
|
2383 emit selectionChanged(); |
|
2384 } |
|
2385 |
|
2386 /*! |
|
2387 \reimp |
|
2388 */ |
|
2389 |
|
2390 void Q3TextEdit::contentsMouseDoubleClickEvent(QMouseEvent * e) |
|
2391 { |
|
2392 if (e->button() != Qt::LeftButton) { |
|
2393 e->ignore(); |
|
2394 return; |
|
2395 } |
|
2396 #if !defined(QT_NO_IM) |
|
2397 if (d->preeditLength > 0) |
|
2398 return; |
|
2399 #endif |
|
2400 |
|
2401 int para = 0; |
|
2402 int index = charAt(e->pos(), ¶); |
|
2403 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2404 if (d->optimMode) { |
|
2405 QString str = d->od->lines[LOGOFFSET(para)]; |
|
2406 int startIdx = index, endIdx = index, i; |
|
2407 if (!str[index].isSpace()) { |
|
2408 i = startIdx; |
|
2409 // find start of word |
|
2410 while (i >= 0 && !str[i].isSpace()) { |
|
2411 startIdx = i--; |
|
2412 } |
|
2413 i = endIdx; |
|
2414 // find end of word.. |
|
2415 while (i < str.length() && !str[i].isSpace()) { |
|
2416 endIdx = ++i; |
|
2417 } |
|
2418 // ..and start of next |
|
2419 while (i < str.length() && str[i].isSpace()) { |
|
2420 endIdx = ++i; |
|
2421 } |
|
2422 optimSetSelection(para, startIdx, para, endIdx); |
|
2423 repaintContents(); |
|
2424 } |
|
2425 } else |
|
2426 #endif |
|
2427 { |
|
2428 Q3TextCursor c1 = *cursor; |
|
2429 Q3TextCursor c2 = *cursor; |
|
2430 #if defined(Q_OS_MAC) |
|
2431 Q3TextParagraph *para = cursor->paragraph(); |
|
2432 if (cursor->isValid()) { |
|
2433 if (para->at(cursor->index())->c.isLetterOrNumber()) { |
|
2434 while (c1.index() > 0 && |
|
2435 c1.paragraph()->at(c1.index()-1)->c.isLetterOrNumber()) |
|
2436 c1.gotoPreviousLetter(); |
|
2437 while (c2.paragraph()->at(c2.index())->c.isLetterOrNumber() && |
|
2438 !c2.atParagEnd()) |
|
2439 c2.gotoNextLetter(); |
|
2440 } else if (para->at(cursor->index())->c.isSpace()) { |
|
2441 while (c1.index() > 0 && |
|
2442 c1.paragraph()->at(c1.index()-1)->c.isSpace()) |
|
2443 c1.gotoPreviousLetter(); |
|
2444 while (c2.paragraph()->at(c2.index())->c.isSpace() && |
|
2445 !c2.atParagEnd()) |
|
2446 c2.gotoNextLetter(); |
|
2447 } else if (!c2.atParagEnd()) { |
|
2448 c2.gotoNextLetter(); |
|
2449 } |
|
2450 } |
|
2451 #else |
|
2452 if (cursor->index() > 0 && !cursor->paragraph()->at(cursor->index()-1)->c.isSpace()) |
|
2453 c1.gotoPreviousWord(); |
|
2454 if (!cursor->paragraph()->at(cursor->index())->c.isSpace() && !cursor->atParagEnd()) |
|
2455 c2.gotoNextWord(); |
|
2456 #endif |
|
2457 doc->setSelectionStart(Q3TextDocument::Standard, c1); |
|
2458 doc->setSelectionEnd(Q3TextDocument::Standard, c2); |
|
2459 |
|
2460 *cursor = c2; |
|
2461 |
|
2462 repaintChanged(); |
|
2463 |
|
2464 d->trippleClickTimer->start(qApp->doubleClickInterval(), true); |
|
2465 d->trippleClickPoint = e->globalPos(); |
|
2466 } |
|
2467 inDoubleClick = true; |
|
2468 mousePressed = true; |
|
2469 emit doubleClicked(para, index); |
|
2470 } |
|
2471 |
|
2472 #ifndef QT_NO_DRAGANDDROP |
|
2473 |
|
2474 /*! |
|
2475 \reimp |
|
2476 */ |
|
2477 |
|
2478 void Q3TextEdit::contentsDragEnterEvent(QDragEnterEvent *e) |
|
2479 { |
|
2480 if (isReadOnly() || !Q3TextDrag::canDecode(e)) { |
|
2481 e->ignore(); |
|
2482 return; |
|
2483 } |
|
2484 e->acceptAction(); |
|
2485 inDnD = true; |
|
2486 } |
|
2487 |
|
2488 /*! |
|
2489 \reimp |
|
2490 */ |
|
2491 |
|
2492 void Q3TextEdit::contentsDragMoveEvent(QDragMoveEvent *e) |
|
2493 { |
|
2494 if (isReadOnly() || !Q3TextDrag::canDecode(e)) { |
|
2495 e->ignore(); |
|
2496 return; |
|
2497 } |
|
2498 drawCursor(false); |
|
2499 placeCursor(e->pos(), cursor); |
|
2500 drawCursor(true); |
|
2501 e->acceptAction(); |
|
2502 } |
|
2503 |
|
2504 /*! |
|
2505 \reimp |
|
2506 */ |
|
2507 |
|
2508 void Q3TextEdit::contentsDragLeaveEvent(QDragLeaveEvent *) |
|
2509 { |
|
2510 drawCursor(false); |
|
2511 inDnD = false; |
|
2512 } |
|
2513 |
|
2514 /*! |
|
2515 \reimp |
|
2516 */ |
|
2517 |
|
2518 void Q3TextEdit::contentsDropEvent(QDropEvent *e) |
|
2519 { |
|
2520 if (isReadOnly()) |
|
2521 return; |
|
2522 inDnD = false; |
|
2523 e->acceptAction(); |
|
2524 bool intern = false; |
|
2525 if (Q3RichTextDrag::canDecode(e)) { |
|
2526 bool hasSel = doc->hasSelection(Q3TextDocument::Standard); |
|
2527 bool internalDrag = e->source() == this || e->source() == viewport(); |
|
2528 int dropId, dropIndex; |
|
2529 Q3TextCursor insertCursor = *cursor; |
|
2530 dropId = cursor->paragraph()->paragId(); |
|
2531 dropIndex = cursor->index(); |
|
2532 if (hasSel && internalDrag) { |
|
2533 Q3TextCursor c1, c2; |
|
2534 int selStartId, selStartIndex; |
|
2535 int selEndId, selEndIndex; |
|
2536 c1 = doc->selectionStartCursor(Q3TextDocument::Standard); |
|
2537 c1.restoreState(); |
|
2538 c2 = doc->selectionEndCursor(Q3TextDocument::Standard); |
|
2539 c2.restoreState(); |
|
2540 selStartId = c1.paragraph()->paragId(); |
|
2541 selStartIndex = c1.index(); |
|
2542 selEndId = c2.paragraph()->paragId(); |
|
2543 selEndIndex = c2.index(); |
|
2544 if (((dropId > selStartId) || |
|
2545 (dropId == selStartId && dropIndex > selStartIndex)) && |
|
2546 ((dropId < selEndId) || |
|
2547 (dropId == selEndId && dropIndex <= selEndIndex))) |
|
2548 insertCursor = c1; |
|
2549 if (dropId == selEndId && dropIndex > selEndIndex) { |
|
2550 insertCursor = c1; |
|
2551 if (selStartId == selEndId) { |
|
2552 insertCursor.setIndex(dropIndex - |
|
2553 (selEndIndex - selStartIndex)); |
|
2554 } else { |
|
2555 insertCursor.setIndex(dropIndex - selEndIndex + |
|
2556 selStartIndex); |
|
2557 } |
|
2558 } |
|
2559 } |
|
2560 |
|
2561 if (internalDrag && e->action() == QDropEvent::Move) { |
|
2562 removeSelectedText(); |
|
2563 intern = true; |
|
2564 doc->removeSelection(Q3TextDocument::Standard); |
|
2565 } else { |
|
2566 doc->removeSelection(Q3TextDocument::Standard); |
|
2567 #ifndef QT_NO_CURSOR |
|
2568 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
2569 #endif |
|
2570 } |
|
2571 drawCursor(false); |
|
2572 cursor->setParagraph(insertCursor.paragraph()); |
|
2573 cursor->setIndex(insertCursor.index()); |
|
2574 drawCursor(true); |
|
2575 if (!cursor->nestedDepth()) { |
|
2576 QString subType = QLatin1String("plain"); |
|
2577 if (textFormat() != Qt::PlainText) { |
|
2578 if (e->provides("application/x-qrichtext")) |
|
2579 subType = QLatin1String("x-qrichtext"); |
|
2580 } |
|
2581 #ifndef QT_NO_CLIPBOARD |
|
2582 pasteSubType(subType.toLatin1(), e); |
|
2583 #endif |
|
2584 // emit appropriate signals. |
|
2585 emit selectionChanged(); |
|
2586 emit cursorPositionChanged(cursor); |
|
2587 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
2588 } else { |
|
2589 if (intern) |
|
2590 undo(); |
|
2591 e->ignore(); |
|
2592 } |
|
2593 } |
|
2594 } |
|
2595 |
|
2596 #endif |
|
2597 |
|
2598 /*! |
|
2599 \reimp |
|
2600 */ |
|
2601 void Q3TextEdit::contentsContextMenuEvent(QContextMenuEvent *e) |
|
2602 { |
|
2603 clearUndoRedo(); |
|
2604 mousePressed = false; |
|
2605 |
|
2606 e->accept(); |
|
2607 #ifndef QT_NO_POPUPMENU |
|
2608 Q3PopupMenu *popup = createPopupMenu(e->pos()); |
|
2609 if (!popup) |
|
2610 popup = createPopupMenu(); |
|
2611 if (!popup) |
|
2612 return; |
|
2613 int r = popup->exec(e->globalPos(), -1); |
|
2614 delete popup; |
|
2615 |
|
2616 if (r == d->id[IdClear]) |
|
2617 clear(); |
|
2618 else if (r == d->id[IdSelectAll]) { |
|
2619 selectAll(); |
|
2620 #ifndef QT_NO_CLIPBOARD |
|
2621 // if the clipboard support selections, put the newly selected text into |
|
2622 // the clipboard |
|
2623 if (QApplication::clipboard()->supportsSelection()) { |
|
2624 d->clipboard_mode = QClipboard::Selection; |
|
2625 |
|
2626 // don't listen to selection changes |
|
2627 disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); |
|
2628 copy(); |
|
2629 // listen to selection changes |
|
2630 connect(QApplication::clipboard(), SIGNAL(selectionChanged()), |
|
2631 this, SLOT(clipboardChanged())); |
|
2632 |
|
2633 d->clipboard_mode = QClipboard::Clipboard; |
|
2634 } |
|
2635 #endif |
|
2636 } else if (r == d->id[IdUndo]) |
|
2637 undo(); |
|
2638 else if (r == d->id[IdRedo]) |
|
2639 redo(); |
|
2640 #ifndef QT_NO_CLIPBOARD |
|
2641 else if (r == d->id[IdCut]) |
|
2642 cut(); |
|
2643 else if (r == d->id[IdCopy]) |
|
2644 copy(); |
|
2645 else if (r == d->id[IdPaste]) |
|
2646 paste(); |
|
2647 #endif |
|
2648 #endif |
|
2649 } |
|
2650 |
|
2651 |
|
2652 void Q3TextEdit::autoScrollTimerDone() |
|
2653 { |
|
2654 if (mousePressed) |
|
2655 handleMouseMove( viewportToContents(viewport()->mapFromGlobal(QCursor::pos()) )); |
|
2656 } |
|
2657 |
|
2658 void Q3TextEdit::handleMouseMove(const QPoint& pos) |
|
2659 { |
|
2660 if (!mousePressed) |
|
2661 return; |
|
2662 |
|
2663 if ((!scrollTimer->isActive() && pos.y() < contentsY()) || pos.y() > contentsY() + visibleHeight()) |
|
2664 scrollTimer->start(100, false); |
|
2665 else if (scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight()) |
|
2666 scrollTimer->stop(); |
|
2667 |
|
2668 drawCursor(false); |
|
2669 Q3TextCursor oldCursor = *cursor; |
|
2670 |
|
2671 placeCursor(pos); |
|
2672 |
|
2673 if (inDoubleClick) { |
|
2674 Q3TextCursor cl = *cursor; |
|
2675 cl.gotoPreviousWord(); |
|
2676 Q3TextCursor cr = *cursor; |
|
2677 cr.gotoNextWord(); |
|
2678 |
|
2679 int diff = QABS(oldCursor.paragraph()->at(oldCursor.index())->x - mousePos.x()); |
|
2680 int ldiff = QABS(cl.paragraph()->at(cl.index())->x - mousePos.x()); |
|
2681 int rdiff = QABS(cr.paragraph()->at(cr.index())->x - mousePos.x()); |
|
2682 |
|
2683 |
|
2684 if (cursor->paragraph()->lineStartOfChar(cursor->index()) != |
|
2685 oldCursor.paragraph()->lineStartOfChar(oldCursor.index())) |
|
2686 diff = 0xFFFFFF; |
|
2687 |
|
2688 if (rdiff < diff && rdiff < ldiff) |
|
2689 *cursor = cr; |
|
2690 else if (ldiff < diff && ldiff < rdiff) |
|
2691 *cursor = cl; |
|
2692 else |
|
2693 *cursor = oldCursor; |
|
2694 |
|
2695 } |
|
2696 ensureCursorVisible(); |
|
2697 |
|
2698 bool redraw = false; |
|
2699 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
2700 redraw = doc->setSelectionEnd(Q3TextDocument::Standard, *cursor) || redraw; |
|
2701 } |
|
2702 |
|
2703 if (!redraw) { |
|
2704 drawCursor(true); |
|
2705 } else { |
|
2706 repaintChanged(); |
|
2707 drawCursor(true); |
|
2708 } |
|
2709 |
|
2710 if (currentFormat && currentFormat->key() != cursor->paragraph()->at(cursor->index())->format()->key()) { |
|
2711 currentFormat->removeRef(); |
|
2712 currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(cursor->index())->format()); |
|
2713 if (currentFormat->isMisspelled()) { |
|
2714 currentFormat->removeRef(); |
|
2715 currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color()); |
|
2716 } |
|
2717 emit currentFontChanged(currentFormat->font()); |
|
2718 emit currentColorChanged(currentFormat->color()); |
|
2719 emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign()); |
|
2720 } |
|
2721 |
|
2722 if (currentAlignment != cursor->paragraph()->alignment()) { |
|
2723 currentAlignment = cursor->paragraph()->alignment(); |
|
2724 block_set_alignment = true; |
|
2725 emit currentAlignmentChanged(currentAlignment); |
|
2726 block_set_alignment = false; |
|
2727 } |
|
2728 } |
|
2729 |
|
2730 /*! \internal */ |
|
2731 |
|
2732 void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c, bool link) |
|
2733 { |
|
2734 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2735 if (d->optimMode) |
|
2736 return; |
|
2737 #endif |
|
2738 if (!c) |
|
2739 c = cursor; |
|
2740 |
|
2741 if(c == cursor) |
|
2742 resetInputContext(); |
|
2743 c->restoreState(); |
|
2744 Q3TextParagraph *s = doc->firstParagraph(); |
|
2745 c->place(pos, s, link); |
|
2746 } |
|
2747 |
|
2748 |
|
2749 QVariant Q3TextEdit::inputMethodQuery(Qt::InputMethodQuery query) const |
|
2750 { |
|
2751 Q3TextCursor c(*cursor); |
|
2752 |
|
2753 switch(query) { |
|
2754 case Qt::ImMicroFocus: { |
|
2755 int h = c.paragraph()->lineHeightOfChar(cursor->index()); |
|
2756 return QRect(c.x() - contentsX() + frameWidth(), |
|
2757 c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 1, h); |
|
2758 } |
|
2759 case Qt::ImFont: |
|
2760 return c.paragraph()->at(c.index())->format()->font(); |
|
2761 default: |
|
2762 // ##### fix the others! |
|
2763 return QWidget::inputMethodQuery(query); |
|
2764 } |
|
2765 } |
|
2766 |
|
2767 |
|
2768 |
|
2769 void Q3TextEdit::formatMore() |
|
2770 { |
|
2771 if (!lastFormatted) |
|
2772 return; |
|
2773 |
|
2774 int bottom = contentsHeight(); |
|
2775 int lastTop = -1; |
|
2776 int lastBottom = -1; |
|
2777 int to = 20; |
|
2778 bool firstVisible = false; |
|
2779 QRect cr(contentsX(), contentsY(), visibleWidth(), visibleHeight()); |
|
2780 for (int i = 0; lastFormatted && |
|
2781 (i < to || (firstVisible && lastTop < contentsY()+height())); |
|
2782 i++) { |
|
2783 lastFormatted->format(); |
|
2784 lastTop = lastFormatted->rect().top(); |
|
2785 lastBottom = lastFormatted->rect().bottom(); |
|
2786 if (i == 0) |
|
2787 firstVisible = lastBottom < cr.bottom(); |
|
2788 bottom = qMax(bottom, lastBottom); |
|
2789 lastFormatted = lastFormatted->next(); |
|
2790 } |
|
2791 |
|
2792 if (bottom > contentsHeight()) { |
|
2793 resizeContents(contentsWidth(), qMax(doc->height(), bottom)); |
|
2794 } else if (!lastFormatted && lastBottom < contentsHeight()) { |
|
2795 resizeContents(contentsWidth(), qMax(doc->height(), lastBottom)); |
|
2796 if (contentsHeight() < visibleHeight()) |
|
2797 updateContents(0, contentsHeight(), visibleWidth(), |
|
2798 visibleHeight() - contentsHeight()); |
|
2799 } |
|
2800 |
|
2801 if (lastFormatted) |
|
2802 formatTimer->start(interval, true); |
|
2803 else |
|
2804 interval = qMax(0, interval); |
|
2805 } |
|
2806 |
|
2807 void Q3TextEdit::doResize() |
|
2808 { |
|
2809 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2810 if (!d->optimMode) |
|
2811 #endif |
|
2812 { |
|
2813 if (wrapMode == FixedPixelWidth) |
|
2814 return; |
|
2815 doc->setMinimumWidth(-1); |
|
2816 resizeContents(0, 0); |
|
2817 doc->setWidth(visibleWidth()); |
|
2818 doc->invalidate(); |
|
2819 lastFormatted = doc->firstParagraph(); |
|
2820 interval = 0; |
|
2821 formatMore(); |
|
2822 } |
|
2823 repaintContents(); |
|
2824 } |
|
2825 |
|
2826 /*! \internal */ |
|
2827 |
|
2828 void Q3TextEdit::doChangeInterval() |
|
2829 { |
|
2830 interval = 0; |
|
2831 } |
|
2832 |
|
2833 /*! |
|
2834 \reimp |
|
2835 */ |
|
2836 |
|
2837 bool Q3TextEdit::eventFilter(QObject *o, QEvent *e) |
|
2838 { |
|
2839 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2840 if (!d->optimMode && (o == this || o == viewport())) { |
|
2841 #else |
|
2842 if (o == this || o == viewport()) { |
|
2843 #endif |
|
2844 if (d->cursorBlinkActive && e->type() == QEvent::FocusIn) { |
|
2845 if (QApplication::cursorFlashTime() > 0) |
|
2846 blinkTimer->start(QApplication::cursorFlashTime() / 2); |
|
2847 drawCursor(true); |
|
2848 } else if (e->type() == QEvent::FocusOut) { |
|
2849 blinkTimer->stop(); |
|
2850 drawCursor(false); |
|
2851 } |
|
2852 } |
|
2853 |
|
2854 if (o == this && e->type() == QEvent::PaletteChange) { |
|
2855 QColor old(viewport()->palette().color(QPalette::Text)); |
|
2856 if (old != palette().color(QPalette::Text)) { |
|
2857 QColor c(palette().color(QPalette::Text)); |
|
2858 doc->setMinimumWidth(-1); |
|
2859 doc->setDefaultFormat(doc->formatCollection()->defaultFormat()->font(), c); |
|
2860 lastFormatted = doc->firstParagraph(); |
|
2861 formatMore(); |
|
2862 repaintChanged(); |
|
2863 } |
|
2864 } |
|
2865 |
|
2866 return Q3ScrollView::eventFilter(o, e); |
|
2867 } |
|
2868 |
|
2869 /*! |
|
2870 Inserts the given \a text. If \a indent is true the paragraph that |
|
2871 contains the text is reindented; if \a checkNewLine is true the \a |
|
2872 text is checked for newlines and relaid out. If \a removeSelected |
|
2873 is true and there is a selection, the insertion replaces the |
|
2874 selected text. |
|
2875 */ |
|
2876 void Q3TextEdit::insert(const QString &text, bool indent, |
|
2877 bool checkNewLine, bool removeSelected) |
|
2878 { |
|
2879 uint f = 0; |
|
2880 if (indent) |
|
2881 f |= RedoIndentation; |
|
2882 if (checkNewLine) |
|
2883 f |= CheckNewLines; |
|
2884 if (removeSelected) |
|
2885 f |= RemoveSelected; |
|
2886 insert(text, f); |
|
2887 } |
|
2888 |
|
2889 /*! |
|
2890 Inserts \a text at the current cursor position. |
|
2891 |
|
2892 The \a insertionFlags define how the text is inserted. If \c |
|
2893 RedoIndentation is set, the paragraph is re-indented. If \c |
|
2894 CheckNewLines is set, newline characters in \a text result in hard |
|
2895 line breaks (i.e. new paragraphs). If \c checkNewLine is not set, |
|
2896 the behavior of the editor is undefined if the \a text contains |
|
2897 newlines. (It is not possible to change Q3TextEdit's newline handling |
|
2898 behavior, but you can use QString::replace() to preprocess text |
|
2899 before inserting it.) If \c RemoveSelected is set, any selected |
|
2900 text (in selection 0) is removed before the text is inserted. |
|
2901 |
|
2902 The default flags are \c CheckNewLines | \c RemoveSelected. |
|
2903 |
|
2904 If the widget is in Qt::LogText mode this function will do nothing. |
|
2905 |
|
2906 \sa paste() pasteSubType() |
|
2907 */ |
|
2908 |
|
2909 |
|
2910 void Q3TextEdit::insert(const QString &text, uint insertionFlags) |
|
2911 { |
|
2912 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2913 if (d->optimMode) |
|
2914 return; |
|
2915 #endif |
|
2916 |
|
2917 if (cursor->nestedDepth() != 0) // #### for 3.0, disable editing of tables as this is not advanced enough |
|
2918 return; |
|
2919 |
|
2920 bool indent = insertionFlags & RedoIndentation; |
|
2921 bool checkNewLine = insertionFlags & CheckNewLines; |
|
2922 bool removeSelected = insertionFlags & RemoveSelected; |
|
2923 QString txt(text); |
|
2924 drawCursor(false); |
|
2925 if (!isReadOnly() && doc->hasSelection(Q3TextDocument::Standard) && removeSelected) |
|
2926 removeSelectedText(); |
|
2927 Q3TextCursor c2 = *cursor; |
|
2928 int oldLen = 0; |
|
2929 |
|
2930 if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) { |
|
2931 checkUndoRedoInfo(UndoRedoInfo::Insert); |
|
2932 |
|
2933 // If we are inserting at the end of the previous insertion, we keep this in |
|
2934 // the same undo/redo command. Otherwise, we separate them in two different commands. |
|
2935 if (undoRedoInfo.valid() && undoRedoInfo.index + undoRedoInfo.d->text.length() != cursor->index()) { |
|
2936 clearUndoRedo(); |
|
2937 undoRedoInfo.type = UndoRedoInfo::Insert; |
|
2938 } |
|
2939 |
|
2940 if (!undoRedoInfo.valid()) { |
|
2941 undoRedoInfo.id = cursor->paragraph()->paragId(); |
|
2942 undoRedoInfo.index = cursor->index(); |
|
2943 undoRedoInfo.d->text.clear(); |
|
2944 } |
|
2945 oldLen = undoRedoInfo.d->text.length(); |
|
2946 } |
|
2947 |
|
2948 lastFormatted = checkNewLine && cursor->paragraph()->prev() ? |
|
2949 cursor->paragraph()->prev() : cursor->paragraph(); |
|
2950 Q3TextCursor oldCursor = *cursor; |
|
2951 cursor->insert(txt, checkNewLine); |
|
2952 if (doc->useFormatCollection() && !doc->preProcessor()) { |
|
2953 doc->setSelectionStart(Q3TextDocument::Temp, oldCursor); |
|
2954 doc->setSelectionEnd(Q3TextDocument::Temp, *cursor); |
|
2955 doc->setFormat(Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format); |
|
2956 doc->removeSelection(Q3TextDocument::Temp); |
|
2957 } |
|
2958 |
|
2959 if (indent && (txt == QString(QLatin1Char('{')) || txt == QString(QLatin1Char('}')) || txt == QString(QLatin1Char(':')) || txt == QString(QLatin1Char('#')))) |
|
2960 cursor->indent(); |
|
2961 formatMore(); |
|
2962 repaintChanged(); |
|
2963 ensureCursorVisible(); |
|
2964 drawCursor(true); |
|
2965 |
|
2966 if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) { |
|
2967 undoRedoInfo.d->text += txt; |
|
2968 if (!doc->preProcessor()) { |
|
2969 for (int i = 0; i < (int)txt.length(); ++i) { |
|
2970 if (txt[i] != QLatin1Char('\n') && c2.paragraph()->at(c2.index())->format()) { |
|
2971 c2.paragraph()->at(c2.index())->format()->addRef(); |
|
2972 undoRedoInfo.d->text. |
|
2973 setFormat(oldLen + i, |
|
2974 c2.paragraph()->at(c2.index())->format(), true); |
|
2975 } |
|
2976 c2.gotoNextLetter(); |
|
2977 } |
|
2978 } |
|
2979 } |
|
2980 |
|
2981 if (!removeSelected) { |
|
2982 doc->setSelectionStart(Q3TextDocument::Standard, oldCursor); |
|
2983 doc->setSelectionEnd(Q3TextDocument::Standard, *cursor); |
|
2984 repaintChanged(); |
|
2985 } |
|
2986 |
|
2987 setModified(); |
|
2988 emit textChanged(); |
|
2989 } |
|
2990 |
|
2991 /*! |
|
2992 Inserts \a text in the paragraph \a para at position \a index. |
|
2993 */ |
|
2994 |
|
2995 void Q3TextEdit::insertAt(const QString &text, int para, int index) |
|
2996 { |
|
2997 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
2998 if (d->optimMode) { |
|
2999 optimInsert(text, para, index); |
|
3000 return; |
|
3001 } |
|
3002 #endif |
|
3003 Q3TextParagraph *p = doc->paragAt(para); |
|
3004 if (!p) |
|
3005 return; |
|
3006 removeSelection(Q3TextDocument::Standard); |
|
3007 Q3TextCursor tmp = *cursor; |
|
3008 cursor->setParagraph(p); |
|
3009 cursor->setIndex(index); |
|
3010 insert(text, false, true, false); |
|
3011 *cursor = tmp; |
|
3012 removeSelection(Q3TextDocument::Standard); |
|
3013 } |
|
3014 |
|
3015 /*! |
|
3016 Inserts \a text as a new paragraph at position \a para. If \a para |
|
3017 is -1, the text is appended. Use append() if the append operation |
|
3018 is performance critical. |
|
3019 */ |
|
3020 |
|
3021 void Q3TextEdit::insertParagraph(const QString &text, int para) |
|
3022 { |
|
3023 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3024 if (d->optimMode) { |
|
3025 optimInsert(text + QLatin1Char('\n'), para, 0); |
|
3026 return; |
|
3027 } |
|
3028 #endif |
|
3029 for (int i = 0; i < (int)doc->numSelections(); ++i) |
|
3030 doc->removeSelection(i); |
|
3031 |
|
3032 Q3TextParagraph *p = doc->paragAt(para); |
|
3033 |
|
3034 bool append = !p; |
|
3035 if (!p) |
|
3036 p = doc->lastParagraph(); |
|
3037 |
|
3038 Q3TextCursor old = *cursor; |
|
3039 drawCursor(false); |
|
3040 |
|
3041 cursor->setParagraph(p); |
|
3042 cursor->setIndex(0); |
|
3043 clearUndoRedo(); |
|
3044 qtextedit_ignore_readonly = true; |
|
3045 if (append && cursor->paragraph()->length() > 1) { |
|
3046 cursor->setIndex(cursor->paragraph()->length() - 1); |
|
3047 doKeyboardAction(ActionReturn); |
|
3048 } |
|
3049 insert(text, false, true, true); |
|
3050 doKeyboardAction(ActionReturn); |
|
3051 qtextedit_ignore_readonly = false; |
|
3052 |
|
3053 drawCursor(false); |
|
3054 *cursor = old; |
|
3055 drawCursor(true); |
|
3056 |
|
3057 repaintChanged(); |
|
3058 } |
|
3059 |
|
3060 /*! |
|
3061 Removes the paragraph \a para. |
|
3062 */ |
|
3063 |
|
3064 void Q3TextEdit::removeParagraph(int para) |
|
3065 { |
|
3066 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3067 if (d->optimMode) |
|
3068 return; |
|
3069 #endif |
|
3070 Q3TextParagraph *p = doc->paragAt(para); |
|
3071 if (!p) |
|
3072 return; |
|
3073 |
|
3074 for (int i = 0; i < doc->numSelections(); ++i) |
|
3075 doc->removeSelection(i); |
|
3076 |
|
3077 Q3TextCursor start(doc); |
|
3078 Q3TextCursor end(doc); |
|
3079 start.setParagraph(p); |
|
3080 start.setIndex(0); |
|
3081 end.setParagraph(p); |
|
3082 end.setIndex(p->length() - 1); |
|
3083 |
|
3084 if (!(p == doc->firstParagraph() && p == doc->lastParagraph())) { |
|
3085 if (p->next()) { |
|
3086 end.setParagraph(p->next()); |
|
3087 end.setIndex(0); |
|
3088 } else if (p->prev()) { |
|
3089 start.setParagraph(p->prev()); |
|
3090 start.setIndex(p->prev()->length() - 1); |
|
3091 } |
|
3092 } |
|
3093 |
|
3094 doc->setSelectionStart(Q3TextDocument::Temp, start); |
|
3095 doc->setSelectionEnd(Q3TextDocument::Temp, end); |
|
3096 removeSelectedText(Q3TextDocument::Temp); |
|
3097 } |
|
3098 |
|
3099 /*! |
|
3100 Undoes the last operation. |
|
3101 |
|
3102 If there is no operation to undo, i.e. there is no undo step in |
|
3103 the undo/redo history, nothing happens. |
|
3104 |
|
3105 \sa undoAvailable() redo() undoDepth() |
|
3106 */ |
|
3107 |
|
3108 void Q3TextEdit::undo() |
|
3109 { |
|
3110 clearUndoRedo(); |
|
3111 if (isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled) |
|
3112 return; |
|
3113 |
|
3114 for (int i = 0; i < (int)doc->numSelections(); ++i) |
|
3115 doc->removeSelection(i); |
|
3116 |
|
3117 #ifndef QT_NO_CURSOR |
|
3118 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
3119 #endif |
|
3120 |
|
3121 clearUndoRedo(); |
|
3122 drawCursor(false); |
|
3123 Q3TextCursor *c = doc->undo(cursor); |
|
3124 if (!c) { |
|
3125 drawCursor(true); |
|
3126 return; |
|
3127 } |
|
3128 lastFormatted = 0; |
|
3129 repaintChanged(); |
|
3130 ensureCursorVisible(); |
|
3131 drawCursor(true); |
|
3132 setModified(); |
|
3133 // ### If we get back to a completely blank textedit, it |
|
3134 // is possible that cursor is invalid and further actions |
|
3135 // might not fix the problem, so reset the cursor here. |
|
3136 // This is copied from removeSeletedText(), it might be |
|
3137 // okay to just call that. |
|
3138 if (!cursor->isValid()) { |
|
3139 delete cursor; |
|
3140 cursor = new Q3TextCursor(doc); |
|
3141 drawCursor(true); |
|
3142 repaintContents(); |
|
3143 } |
|
3144 emit undoAvailable(isUndoAvailable()); |
|
3145 emit redoAvailable(isRedoAvailable()); |
|
3146 emit textChanged(); |
|
3147 } |
|
3148 |
|
3149 /*! |
|
3150 Redoes the last operation. |
|
3151 |
|
3152 If there is no operation to redo, i.e. there is no redo step in |
|
3153 the undo/redo history, nothing happens. |
|
3154 |
|
3155 \sa redoAvailable() undo() undoDepth() |
|
3156 */ |
|
3157 |
|
3158 void Q3TextEdit::redo() |
|
3159 { |
|
3160 if (isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled) |
|
3161 return; |
|
3162 |
|
3163 for (int i = 0; i < (int)doc->numSelections(); ++i) |
|
3164 doc->removeSelection(i); |
|
3165 |
|
3166 #ifndef QT_NO_CURSOR |
|
3167 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
3168 #endif |
|
3169 |
|
3170 clearUndoRedo(); |
|
3171 drawCursor(false); |
|
3172 Q3TextCursor *c = doc->redo(cursor); |
|
3173 if (!c) { |
|
3174 drawCursor(true); |
|
3175 return; |
|
3176 } |
|
3177 lastFormatted = 0; |
|
3178 ensureCursorVisible(); |
|
3179 repaintChanged(); |
|
3180 ensureCursorVisible(); |
|
3181 drawCursor(true); |
|
3182 setModified(); |
|
3183 emit undoAvailable(isUndoAvailable()); |
|
3184 emit redoAvailable(isRedoAvailable()); |
|
3185 emit textChanged(); |
|
3186 } |
|
3187 |
|
3188 /*! |
|
3189 Pastes the text from the clipboard into the text edit at the |
|
3190 current cursor position. Only plain text is pasted. |
|
3191 |
|
3192 If there is no text in the clipboard nothing happens. |
|
3193 |
|
3194 \sa pasteSubType() cut() Q3TextEdit::copy() |
|
3195 */ |
|
3196 |
|
3197 void Q3TextEdit::paste() |
|
3198 { |
|
3199 #ifndef QT_NO_MIMECLIPBOARD |
|
3200 if (isReadOnly()) |
|
3201 return; |
|
3202 QString subType = QLatin1String("plain"); |
|
3203 if (textFormat() != Qt::PlainText) { |
|
3204 QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode); |
|
3205 if (!m) |
|
3206 return; |
|
3207 if (m->provides("application/x-qrichtext")) |
|
3208 subType = QLatin1String("x-qrichtext"); |
|
3209 } |
|
3210 |
|
3211 pasteSubType(subType.toLatin1()); |
|
3212 #endif |
|
3213 } |
|
3214 |
|
3215 void Q3TextEdit::checkUndoRedoInfo(UndoRedoInfo::Type t) |
|
3216 { |
|
3217 if (undoRedoInfo.valid() && t != undoRedoInfo.type) { |
|
3218 clearUndoRedo(); |
|
3219 } |
|
3220 undoRedoInfo.type = t; |
|
3221 } |
|
3222 |
|
3223 /*! |
|
3224 Repaints any paragraphs that have changed. |
|
3225 |
|
3226 Although used extensively internally you shouldn't need to call |
|
3227 this yourself. |
|
3228 */ |
|
3229 void Q3TextEdit::repaintChanged() |
|
3230 { |
|
3231 if (!updatesEnabled() || !viewport()->updatesEnabled()) |
|
3232 return; |
|
3233 |
|
3234 if (doc->firstParagraph()) |
|
3235 lastFormatted = doc->firstParagraph(); |
|
3236 updateContents(); // good enough until this class is rewritten |
|
3237 } |
|
3238 |
|
3239 #ifndef QT_NO_MIME |
|
3240 Q3TextDrag *Q3TextEdit::dragObject(QWidget *parent) const |
|
3241 { |
|
3242 if (!doc->hasSelection(Q3TextDocument::Standard) || |
|
3243 doc->selectedText(Q3TextDocument::Standard).isEmpty()) |
|
3244 return 0; |
|
3245 if (textFormat() != Qt::RichText) |
|
3246 return new Q3TextDrag(doc->selectedText(Q3TextDocument::Standard), parent); |
|
3247 Q3RichTextDrag *drag = new Q3RichTextDrag(parent); |
|
3248 drag->setPlainText(doc->selectedText(Q3TextDocument::Standard)); |
|
3249 drag->setRichText(doc->selectedText(Q3TextDocument::Standard, true)); |
|
3250 return drag; |
|
3251 } |
|
3252 #endif |
|
3253 |
|
3254 /*! |
|
3255 Copies the selected text (from selection 0) to the clipboard and |
|
3256 deletes it from the text edit. |
|
3257 |
|
3258 If there is no selected text (in selection 0) nothing happens. |
|
3259 |
|
3260 \sa Q3TextEdit::copy() paste() pasteSubType() |
|
3261 */ |
|
3262 |
|
3263 void Q3TextEdit::cut() |
|
3264 { |
|
3265 if (isReadOnly()) |
|
3266 return; |
|
3267 normalCopy(); |
|
3268 removeSelectedText(); |
|
3269 } |
|
3270 |
|
3271 void Q3TextEdit::normalCopy() |
|
3272 { |
|
3273 #ifndef QT_NO_MIME |
|
3274 Q3TextDrag *drag = dragObject(); |
|
3275 if (!drag) |
|
3276 return; |
|
3277 #ifndef QT_NO_MIMECLIPBOARD |
|
3278 QApplication::clipboard()->setData(drag, d->clipboard_mode); |
|
3279 #endif // QT_NO_MIMECLIPBOARD |
|
3280 #endif // QT_NO_MIME |
|
3281 } |
|
3282 |
|
3283 /*! |
|
3284 Copies any selected text (from selection 0) to the clipboard. |
|
3285 |
|
3286 \sa hasSelectedText() copyAvailable() |
|
3287 */ |
|
3288 |
|
3289 void Q3TextEdit::copy() |
|
3290 { |
|
3291 #ifndef QT_NO_CLIPBOARD |
|
3292 # ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3293 if (d->optimMode && optimHasSelection()) |
|
3294 QApplication::clipboard()->setText(optimSelectedText(), d->clipboard_mode); |
|
3295 else |
|
3296 normalCopy(); |
|
3297 # else |
|
3298 normalCopy(); |
|
3299 # endif |
|
3300 #endif |
|
3301 } |
|
3302 |
|
3303 /*! |
|
3304 \internal |
|
3305 |
|
3306 Re-indents the current paragraph. |
|
3307 */ |
|
3308 |
|
3309 void Q3TextEdit::indent() |
|
3310 { |
|
3311 if (isReadOnly()) |
|
3312 return; |
|
3313 |
|
3314 drawCursor(false); |
|
3315 if (!doc->hasSelection(Q3TextDocument::Standard)) |
|
3316 cursor->indent(); |
|
3317 else |
|
3318 doc->indentSelection(Q3TextDocument::Standard); |
|
3319 repaintChanged(); |
|
3320 drawCursor(true); |
|
3321 setModified(); |
|
3322 emit textChanged(); |
|
3323 } |
|
3324 |
|
3325 /*! |
|
3326 Reimplemented to allow tabbing through links. If \a n is true the |
|
3327 tab moves the focus to the next child; if \a n is false the tab |
|
3328 moves the focus to the previous child. Returns true if the focus |
|
3329 was moved; otherwise returns false. |
|
3330 */ |
|
3331 |
|
3332 bool Q3TextEdit::focusNextPrevChild(bool n) |
|
3333 { |
|
3334 if (!isReadOnly() || !linksEnabled()) |
|
3335 return false; |
|
3336 bool b = doc->focusNextPrevChild(n); |
|
3337 repaintChanged(); |
|
3338 if (b) { |
|
3339 Q3TextParagraph *p = doc->focusIndicator.parag; |
|
3340 int start = doc->focusIndicator.start; |
|
3341 int len = doc->focusIndicator.len; |
|
3342 |
|
3343 int y = p->rect().y(); |
|
3344 while (p |
|
3345 && len == 0 |
|
3346 && p->at(start)->isCustom() |
|
3347 && p->at(start)->customItem()->isNested()) { |
|
3348 |
|
3349 Q3TextTable *t = (Q3TextTable*)p->at(start)->customItem(); |
|
3350 QList<Q3TextTableCell *> cells = t->tableCells(); |
|
3351 for (int idx = 0; idx < cells.count(); ++idx) { |
|
3352 Q3TextTableCell *c = cells.at(idx); |
|
3353 Q3TextDocument *cellDoc = c->richText(); |
|
3354 if ( cellDoc->hasFocusParagraph() ) { |
|
3355 y += c->geometry().y() + c->verticalAlignmentOffset(); |
|
3356 |
|
3357 p = cellDoc->focusIndicator.parag; |
|
3358 start = cellDoc->focusIndicator.start; |
|
3359 len = cellDoc->focusIndicator.len; |
|
3360 if ( p ) |
|
3361 y += p->rect().y(); |
|
3362 |
|
3363 break; |
|
3364 } |
|
3365 } |
|
3366 } |
|
3367 setContentsPos( contentsX(), QMIN( y, contentsHeight() - visibleHeight() ) ); |
|
3368 } |
|
3369 return b; |
|
3370 } |
|
3371 |
|
3372 /*! |
|
3373 \internal |
|
3374 |
|
3375 This functions sets the current format to \a f. Only the fields of \a |
|
3376 f which are specified by the \a flags are used. |
|
3377 */ |
|
3378 |
|
3379 void Q3TextEdit::setFormat(Q3TextFormat *f, int flags) |
|
3380 { |
|
3381 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
3382 drawCursor(false); |
|
3383 Q3TextCursor c1 = doc->selectionStartCursor(Q3TextDocument::Standard); |
|
3384 c1.restoreState(); |
|
3385 Q3TextCursor c2 = doc->selectionEndCursor(Q3TextDocument::Standard); |
|
3386 c2.restoreState(); |
|
3387 if (undoEnabled) { |
|
3388 clearUndoRedo(); |
|
3389 undoRedoInfo.type = UndoRedoInfo::Format; |
|
3390 undoRedoInfo.id = c1.paragraph()->paragId(); |
|
3391 undoRedoInfo.index = c1.index(); |
|
3392 undoRedoInfo.eid = c2.paragraph()->paragId(); |
|
3393 undoRedoInfo.eindex = c2.index(); |
|
3394 readFormats(c1, c2, undoRedoInfo.d->text); |
|
3395 undoRedoInfo.format = f; |
|
3396 undoRedoInfo.flags = flags; |
|
3397 clearUndoRedo(); |
|
3398 } |
|
3399 doc->setFormat(Q3TextDocument::Standard, f, flags); |
|
3400 repaintChanged(); |
|
3401 formatMore(); |
|
3402 drawCursor(true); |
|
3403 setModified(); |
|
3404 emit textChanged(); |
|
3405 } |
|
3406 if (currentFormat && currentFormat->key() != f->key()) { |
|
3407 currentFormat->removeRef(); |
|
3408 currentFormat = doc->formatCollection()->format(f); |
|
3409 if (currentFormat->isMisspelled()) { |
|
3410 currentFormat->removeRef(); |
|
3411 currentFormat = doc->formatCollection()->format(currentFormat->font(), |
|
3412 currentFormat->color()); |
|
3413 } |
|
3414 emit currentFontChanged(currentFormat->font()); |
|
3415 emit currentColorChanged(currentFormat->color()); |
|
3416 emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign()); |
|
3417 if (cursor->index() == cursor->paragraph()->length() - 1) { |
|
3418 currentFormat->addRef(); |
|
3419 cursor->paragraph()->string()->setFormat(cursor->index(), currentFormat, true); |
|
3420 if (cursor->paragraph()->length() == 1) { |
|
3421 cursor->paragraph()->invalidate(0); |
|
3422 cursor->paragraph()->format(); |
|
3423 repaintChanged(); |
|
3424 } |
|
3425 } |
|
3426 } |
|
3427 } |
|
3428 |
|
3429 /*! \internal |
|
3430 \warning In Qt 3.1 we will provide a cleaer API for the |
|
3431 functionality which is provided by this function and in Qt 4.0 this |
|
3432 function will go away. |
|
3433 |
|
3434 Sets the paragraph style of the current paragraph |
|
3435 to \a dm. If \a dm is Q3StyleSheetItem::DisplayListItem, the |
|
3436 type of the list item is set to \a listStyle. |
|
3437 |
|
3438 \sa setAlignment() |
|
3439 */ |
|
3440 |
|
3441 void Q3TextEdit::setParagType(Q3StyleSheetItem::DisplayMode dm, |
|
3442 Q3StyleSheetItem::ListStyle listStyle) |
|
3443 { |
|
3444 if (isReadOnly()) |
|
3445 return; |
|
3446 |
|
3447 drawCursor(false); |
|
3448 Q3TextParagraph *start = cursor->paragraph(); |
|
3449 Q3TextParagraph *end = start; |
|
3450 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
3451 start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph(); |
|
3452 end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph(); |
|
3453 if (end->paragId() < start->paragId()) |
|
3454 return; // do not trust our selections |
|
3455 } |
|
3456 |
|
3457 clearUndoRedo(); |
|
3458 undoRedoInfo.type = UndoRedoInfo::Style; |
|
3459 undoRedoInfo.id = start->paragId(); |
|
3460 undoRedoInfo.eid = end->paragId(); |
|
3461 undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid); |
|
3462 |
|
3463 while (start != end->next()) { |
|
3464 start->setListStyle(listStyle); |
|
3465 if (dm == Q3StyleSheetItem::DisplayListItem) { |
|
3466 start->setListItem(true); |
|
3467 if(start->listDepth() == 0) |
|
3468 start->setListDepth(1); |
|
3469 } else if (start->isListItem()) { |
|
3470 start->setListItem(false); |
|
3471 start->setListDepth(qMax(start->listDepth()-1, 0)); |
|
3472 } |
|
3473 start = start->next(); |
|
3474 } |
|
3475 |
|
3476 clearUndoRedo(); |
|
3477 repaintChanged(); |
|
3478 formatMore(); |
|
3479 drawCursor(true); |
|
3480 setModified(); |
|
3481 emit textChanged(); |
|
3482 } |
|
3483 |
|
3484 /*! |
|
3485 Sets the alignment of the current paragraph to \a a. Valid |
|
3486 alignments are Qt::AlignLeft, Qt::AlignRight, |
|
3487 Qt::AlignJustify and Qt::AlignCenter (which centers |
|
3488 horizontally). |
|
3489 */ |
|
3490 |
|
3491 void Q3TextEdit::setAlignment(int a) |
|
3492 { |
|
3493 if (isReadOnly() || block_set_alignment) |
|
3494 return; |
|
3495 |
|
3496 drawCursor(false); |
|
3497 Q3TextParagraph *start = cursor->paragraph(); |
|
3498 Q3TextParagraph *end = start; |
|
3499 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
3500 start = doc->selectionStartCursor(Q3TextDocument::Standard).topParagraph(); |
|
3501 end = doc->selectionEndCursor(Q3TextDocument::Standard).topParagraph(); |
|
3502 if (end->paragId() < start->paragId()) |
|
3503 return; // do not trust our selections |
|
3504 } |
|
3505 |
|
3506 clearUndoRedo(); |
|
3507 undoRedoInfo.type = UndoRedoInfo::Style; |
|
3508 undoRedoInfo.id = start->paragId(); |
|
3509 undoRedoInfo.eid = end->paragId(); |
|
3510 undoRedoInfo.styleInformation = Q3TextStyleCommand::readStyleInformation(doc, undoRedoInfo.id, undoRedoInfo.eid); |
|
3511 |
|
3512 while (start != end->next()) { |
|
3513 start->setAlignment(a); |
|
3514 start = start->next(); |
|
3515 } |
|
3516 |
|
3517 clearUndoRedo(); |
|
3518 repaintChanged(); |
|
3519 formatMore(); |
|
3520 drawCursor(true); |
|
3521 if (currentAlignment != a) { |
|
3522 currentAlignment = a; |
|
3523 emit currentAlignmentChanged(currentAlignment); |
|
3524 } |
|
3525 setModified(); |
|
3526 emit textChanged(); |
|
3527 } |
|
3528 |
|
3529 void Q3TextEdit::updateCurrentFormat() |
|
3530 { |
|
3531 int i = cursor->index(); |
|
3532 if (i > 0) |
|
3533 --i; |
|
3534 if (doc->useFormatCollection() && |
|
3535 (!currentFormat || currentFormat->key() != cursor->paragraph()->at(i)->format()->key())) { |
|
3536 if (currentFormat) |
|
3537 currentFormat->removeRef(); |
|
3538 currentFormat = doc->formatCollection()->format(cursor->paragraph()->at(i)->format()); |
|
3539 if (currentFormat->isMisspelled()) { |
|
3540 currentFormat->removeRef(); |
|
3541 currentFormat = doc->formatCollection()->format(currentFormat->font(), currentFormat->color()); |
|
3542 } |
|
3543 emit currentFontChanged(currentFormat->font()); |
|
3544 emit currentColorChanged(currentFormat->color()); |
|
3545 emit currentVerticalAlignmentChanged((VerticalAlignment)currentFormat->vAlign()); |
|
3546 } |
|
3547 |
|
3548 if (currentAlignment != cursor->paragraph()->alignment()) { |
|
3549 currentAlignment = cursor->paragraph()->alignment(); |
|
3550 block_set_alignment = true; |
|
3551 emit currentAlignmentChanged(currentAlignment); |
|
3552 block_set_alignment = false; |
|
3553 } |
|
3554 } |
|
3555 |
|
3556 /*! |
|
3557 If \a b is true sets the current format to italic; otherwise sets |
|
3558 the current format to non-italic. |
|
3559 |
|
3560 \sa italic() |
|
3561 */ |
|
3562 |
|
3563 void Q3TextEdit::setItalic(bool b) |
|
3564 { |
|
3565 Q3TextFormat f(*currentFormat); |
|
3566 f.setItalic(b); |
|
3567 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3568 setFormat(f2, Q3TextFormat::Italic); |
|
3569 } |
|
3570 |
|
3571 /*! |
|
3572 If \a b is true sets the current format to bold; otherwise sets |
|
3573 the current format to non-bold. |
|
3574 |
|
3575 \sa bold() |
|
3576 */ |
|
3577 |
|
3578 void Q3TextEdit::setBold(bool b) |
|
3579 { |
|
3580 Q3TextFormat f(*currentFormat); |
|
3581 f.setBold(b); |
|
3582 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3583 setFormat(f2, Q3TextFormat::Bold); |
|
3584 } |
|
3585 |
|
3586 /*! |
|
3587 If \a b is true sets the current format to underline; otherwise |
|
3588 sets the current format to non-underline. |
|
3589 |
|
3590 \sa underline() |
|
3591 */ |
|
3592 |
|
3593 void Q3TextEdit::setUnderline(bool b) |
|
3594 { |
|
3595 Q3TextFormat f(*currentFormat); |
|
3596 f.setUnderline(b); |
|
3597 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3598 setFormat(f2, Q3TextFormat::Underline); |
|
3599 } |
|
3600 |
|
3601 /*! |
|
3602 Sets the font family of the current format to \a fontFamily. |
|
3603 |
|
3604 \sa family() setCurrentFont() |
|
3605 */ |
|
3606 |
|
3607 void Q3TextEdit::setFamily(const QString &fontFamily) |
|
3608 { |
|
3609 Q3TextFormat f(*currentFormat); |
|
3610 f.setFamily(fontFamily); |
|
3611 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3612 setFormat(f2, Q3TextFormat::Family); |
|
3613 } |
|
3614 |
|
3615 /*! |
|
3616 Sets the point size of the current format to \a s. |
|
3617 |
|
3618 Note that if \a s is zero or negative, the behavior of this |
|
3619 function is not defined. |
|
3620 |
|
3621 \sa pointSize() setCurrentFont() setFamily() |
|
3622 */ |
|
3623 |
|
3624 void Q3TextEdit::setPointSize(int s) |
|
3625 { |
|
3626 Q3TextFormat f(*currentFormat); |
|
3627 f.setPointSize(s); |
|
3628 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3629 setFormat(f2, Q3TextFormat::Size); |
|
3630 } |
|
3631 |
|
3632 /*! |
|
3633 Sets the color of the current format, i.e. of the text, to \a c. |
|
3634 |
|
3635 \sa color() setPaper() |
|
3636 */ |
|
3637 |
|
3638 void Q3TextEdit::setColor(const QColor &c) |
|
3639 { |
|
3640 Q3TextFormat f(*currentFormat); |
|
3641 f.setColor(c); |
|
3642 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3643 setFormat(f2, Q3TextFormat::Color); |
|
3644 } |
|
3645 |
|
3646 /*! |
|
3647 Sets the vertical alignment of the current format, i.e. of the |
|
3648 text, to \a a. |
|
3649 |
|
3650 \sa color() setPaper() |
|
3651 */ |
|
3652 |
|
3653 void Q3TextEdit::setVerticalAlignment(Q3TextEdit::VerticalAlignment a) |
|
3654 { |
|
3655 Q3TextFormat f(*currentFormat); |
|
3656 f.setVAlign((Q3TextFormat::VerticalAlignment)a); |
|
3657 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3658 setFormat(f2, Q3TextFormat::VAlign); |
|
3659 } |
|
3660 |
|
3661 void Q3TextEdit::setFontInternal(const QFont &f_) |
|
3662 { |
|
3663 QFont font = f_; |
|
3664 if (font.kerning()) |
|
3665 font.setKerning(false); |
|
3666 Q3TextFormat f(*currentFormat); |
|
3667 f.setFont(font); |
|
3668 Q3TextFormat *f2 = doc->formatCollection()->format(&f); |
|
3669 setFormat(f2, Q3TextFormat::Font); |
|
3670 } |
|
3671 |
|
3672 |
|
3673 QString Q3TextEdit::text() const |
|
3674 { |
|
3675 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3676 if (d->optimMode) |
|
3677 return optimText(); |
|
3678 #endif |
|
3679 |
|
3680 Q3TextParagraph *p = doc->firstParagraph(); |
|
3681 if (!p || (!p->next() && p->length() <= 1)) |
|
3682 return QString::fromLatin1(""); |
|
3683 |
|
3684 if (isReadOnly()) |
|
3685 return doc->originalText(); |
|
3686 return doc->text(); |
|
3687 } |
|
3688 |
|
3689 /*! |
|
3690 \overload |
|
3691 |
|
3692 Returns the text of paragraph \a para. |
|
3693 |
|
3694 If textFormat() is Qt::RichText the text will contain HTML |
|
3695 formatting tags. |
|
3696 */ |
|
3697 |
|
3698 QString Q3TextEdit::text(int para) const |
|
3699 { |
|
3700 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3701 if (d->optimMode && (d->od->numLines >= para)) { |
|
3702 QString paraStr = d->od->lines[LOGOFFSET(para)]; |
|
3703 if (paraStr.isEmpty()) |
|
3704 paraStr = QLatin1Char('\n'); |
|
3705 return paraStr; |
|
3706 } else |
|
3707 #endif |
|
3708 return doc->text(para); |
|
3709 } |
|
3710 |
|
3711 /*! |
|
3712 \overload |
|
3713 |
|
3714 Changes the text of the text edit to the string \a text and the |
|
3715 context to \a context. Any previous text is removed. |
|
3716 |
|
3717 \a text may be interpreted either as plain text or as rich text, |
|
3718 depending on the textFormat(). The default setting is Qt::AutoText, |
|
3719 i.e. the text edit auto-detects the format from \a text. |
|
3720 |
|
3721 For rich text the rendering style and available tags are defined |
|
3722 by a styleSheet(); see Q3StyleSheet for details. |
|
3723 |
|
3724 The optional \a context is a path which the text edit's |
|
3725 Q3MimeSourceFactory uses to resolve the locations of files and |
|
3726 images. (See \l{Q3TextEdit::Q3TextEdit()}.) It is passed to the text |
|
3727 edit's Q3MimeSourceFactory when quering data. |
|
3728 |
|
3729 Note that the undo/redo history is cleared by this function. |
|
3730 |
|
3731 \sa text(), setTextFormat() |
|
3732 */ |
|
3733 |
|
3734 void Q3TextEdit::setText(const QString &text, const QString &context) |
|
3735 { |
|
3736 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3737 if (d->optimMode) { |
|
3738 optimSetText(text); |
|
3739 return; |
|
3740 } |
|
3741 #endif |
|
3742 if (!isModified() && isReadOnly() && |
|
3743 this->context() == context && this->text() == text) |
|
3744 return; |
|
3745 |
|
3746 emit undoAvailable(false); |
|
3747 emit redoAvailable(false); |
|
3748 undoRedoInfo.clear(); |
|
3749 doc->commands()->clear(); |
|
3750 |
|
3751 lastFormatted = 0; |
|
3752 int oldCursorPos = cursor->index(); |
|
3753 int oldCursorPar = cursor->paragraph()->paragId(); |
|
3754 cursor->restoreState(); |
|
3755 delete cursor; |
|
3756 doc->setText(text, context); |
|
3757 |
|
3758 if (wrapMode == FixedPixelWidth) { |
|
3759 resizeContents(wrapWidth, 0); |
|
3760 doc->setWidth(wrapWidth); |
|
3761 doc->setMinimumWidth(wrapWidth); |
|
3762 } else { |
|
3763 doc->setMinimumWidth(-1); |
|
3764 resizeContents(0, 0); |
|
3765 } |
|
3766 |
|
3767 lastFormatted = doc->firstParagraph(); |
|
3768 cursor = new Q3TextCursor(doc); |
|
3769 updateContents(); |
|
3770 |
|
3771 if (isModified()) |
|
3772 setModified(false); |
|
3773 emit textChanged(); |
|
3774 if (cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar) { |
|
3775 emit cursorPositionChanged(cursor); |
|
3776 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
3777 } |
|
3778 formatMore(); |
|
3779 updateCurrentFormat(); |
|
3780 d->scrollToAnchor.clear(); |
|
3781 } |
|
3782 |
|
3783 /*! |
|
3784 \property Q3TextEdit::text |
|
3785 \brief the text edit's text |
|
3786 |
|
3787 There is no default text. |
|
3788 |
|
3789 On setting, any previous text is deleted. |
|
3790 |
|
3791 The text may be interpreted either as plain text or as rich text, |
|
3792 depending on the textFormat(). The default setting is Qt::AutoText, |
|
3793 i.e. the text edit auto-detects the format of the text. |
|
3794 |
|
3795 For richtext, calling text() on an editable Q3TextEdit will cause |
|
3796 the text to be regenerated from the textedit. This may mean that |
|
3797 the QString returned may not be exactly the same as the one that |
|
3798 was set. |
|
3799 |
|
3800 \sa textFormat |
|
3801 */ |
|
3802 |
|
3803 |
|
3804 /*! |
|
3805 \property Q3TextEdit::readOnly |
|
3806 \brief whether the text edit is read-only |
|
3807 |
|
3808 In a read-only text edit the user can only navigate through the |
|
3809 text and select text; modifying the text is not possible. |
|
3810 |
|
3811 This property's default is false. |
|
3812 */ |
|
3813 |
|
3814 /*! |
|
3815 Finds the next occurrence of the string, \a expr. Returns true if |
|
3816 \a expr was found; otherwise returns false. |
|
3817 |
|
3818 If \a para and \a index are both 0 the search begins from the |
|
3819 current cursor position. If \a para and \a index are both not 0, |
|
3820 the search begins from the \c{*}\a{index} character position in the |
|
3821 \c{*}\a{para} paragraph. |
|
3822 |
|
3823 If \a cs is true the search is case sensitive, otherwise it is |
|
3824 case insensitive. If \a wo is true the search looks for whole word |
|
3825 matches only; otherwise it searches for any matching text. If \a |
|
3826 forward is true (the default) the search works forward from the |
|
3827 starting position to the end of the text, otherwise it works |
|
3828 backwards to the beginning of the text. |
|
3829 |
|
3830 If \a expr is found the function returns true. If \a index and \a |
|
3831 para are not 0, the number of the paragraph in which the first |
|
3832 character of the match was found is put into \c{*}\a{para}, and the |
|
3833 index position of that character within the paragraph is put into |
|
3834 \c{*}\a{index}. |
|
3835 |
|
3836 If \a expr is not found the function returns false. If \a index |
|
3837 and \a para are not 0 and \a expr is not found, \c{*}\a{index} |
|
3838 and \c{*}\a{para} are undefined. |
|
3839 |
|
3840 Please note that this function will make the next occurrence of |
|
3841 the string (if found) the current selection, and will thus |
|
3842 modify the cursor position. |
|
3843 |
|
3844 Using the \a para and \a index parameters will not work correctly |
|
3845 in case the document contains tables. |
|
3846 */ |
|
3847 |
|
3848 bool Q3TextEdit::find(const QString &expr, bool cs, bool wo, bool forward, |
|
3849 int *para, int *index) |
|
3850 { |
|
3851 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3852 if (d->optimMode) |
|
3853 return optimFind(expr, cs, wo, forward, para, index); |
|
3854 #endif |
|
3855 drawCursor(false); |
|
3856 #ifndef QT_NO_CURSOR |
|
3857 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
3858 #endif |
|
3859 Q3TextCursor findcur = *cursor; |
|
3860 if (para && index) { |
|
3861 if (doc->paragAt(*para)) |
|
3862 findcur.gotoPosition(doc->paragAt(*para), *index); |
|
3863 else |
|
3864 findcur.gotoEnd(); |
|
3865 } else if (doc->hasSelection(Q3TextDocument::Standard)){ |
|
3866 // maks sure we do not find the same selection again |
|
3867 if (forward) |
|
3868 findcur.gotoNextLetter(); |
|
3869 else |
|
3870 findcur.gotoPreviousLetter(); |
|
3871 } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) { |
|
3872 findcur.gotoEnd(); |
|
3873 } |
|
3874 removeSelection(Q3TextDocument::Standard); |
|
3875 bool found = doc->find(findcur, expr, cs, wo, forward); |
|
3876 if (found) { |
|
3877 if (para) |
|
3878 *para = findcur.paragraph()->paragId(); |
|
3879 if (index) |
|
3880 *index = findcur.index(); |
|
3881 *cursor = findcur; |
|
3882 repaintChanged(); |
|
3883 ensureCursorVisible(); |
|
3884 } |
|
3885 drawCursor(true); |
|
3886 if (found) { |
|
3887 emit cursorPositionChanged(cursor); |
|
3888 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
3889 } |
|
3890 return found; |
|
3891 } |
|
3892 |
|
3893 void Q3TextEdit::blinkCursor() |
|
3894 { |
|
3895 bool cv = cursorVisible; |
|
3896 blinkCursorVisible = !blinkCursorVisible; |
|
3897 drawCursor(blinkCursorVisible); |
|
3898 cursorVisible = cv; |
|
3899 } |
|
3900 |
|
3901 /*! |
|
3902 Sets the cursor to position \a index in paragraph \a para. |
|
3903 |
|
3904 \sa getCursorPosition() |
|
3905 */ |
|
3906 |
|
3907 void Q3TextEdit::setCursorPosition(int para, int index) |
|
3908 { |
|
3909 Q3TextParagraph *p = doc->paragAt(para); |
|
3910 if (!p) |
|
3911 return; |
|
3912 |
|
3913 if (index > p->length() - 1) |
|
3914 index = p->length() - 1; |
|
3915 |
|
3916 drawCursor(false); |
|
3917 cursor->setParagraph(p); |
|
3918 cursor->setIndex(index); |
|
3919 ensureCursorVisible(); |
|
3920 drawCursor(true); |
|
3921 updateCurrentFormat(); |
|
3922 emit cursorPositionChanged(cursor); |
|
3923 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
3924 } |
|
3925 |
|
3926 /*! |
|
3927 This function sets the \c{*}\a{para} and \c{*}\a{index} parameters to the |
|
3928 current cursor position. \a para and \a index must not be 0. |
|
3929 |
|
3930 \sa setCursorPosition() |
|
3931 */ |
|
3932 |
|
3933 void Q3TextEdit::getCursorPosition(int *para, int *index) const |
|
3934 { |
|
3935 if (!para || !index) |
|
3936 return; |
|
3937 *para = cursor->paragraph()->paragId(); |
|
3938 *index = cursor->index(); |
|
3939 } |
|
3940 |
|
3941 /*! |
|
3942 Sets a selection which starts at position \a indexFrom in |
|
3943 paragraph \a paraFrom and ends at position \a indexTo in paragraph |
|
3944 \a paraTo. |
|
3945 |
|
3946 Any existing selections which have a different id (\a selNum) are |
|
3947 left alone, but if an existing selection has the same id as \a |
|
3948 selNum it is removed and replaced by this selection. |
|
3949 |
|
3950 Uses the selection settings of selection \a selNum. If \a selNum |
|
3951 is 0, this is the default selection. |
|
3952 |
|
3953 The cursor is moved to the end of the selection if \a selNum is 0, |
|
3954 otherwise the cursor position remains unchanged. |
|
3955 |
|
3956 \sa getSelection() selectedText |
|
3957 */ |
|
3958 |
|
3959 void Q3TextEdit::setSelection(int paraFrom, int indexFrom, |
|
3960 int paraTo, int indexTo, int selNum) |
|
3961 { |
|
3962 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
3963 if (d->optimMode) { |
|
3964 optimSetSelection(paraFrom, indexFrom, paraTo, indexTo); |
|
3965 repaintContents(); |
|
3966 return; |
|
3967 } |
|
3968 #endif |
|
3969 if (doc->hasSelection(selNum)) { |
|
3970 doc->removeSelection(selNum); |
|
3971 repaintChanged(); |
|
3972 } |
|
3973 if (selNum > doc->numSelections() - 1) |
|
3974 doc->addSelection(selNum); |
|
3975 Q3TextParagraph *p1 = doc->paragAt(paraFrom); |
|
3976 if (!p1) |
|
3977 return; |
|
3978 Q3TextParagraph *p2 = doc->paragAt(paraTo); |
|
3979 if (!p2) |
|
3980 return; |
|
3981 |
|
3982 if (indexFrom > p1->length() - 1) |
|
3983 indexFrom = p1->length() - 1; |
|
3984 if (indexTo > p2->length() - 1) |
|
3985 indexTo = p2->length() - 1; |
|
3986 |
|
3987 drawCursor(false); |
|
3988 Q3TextCursor c = *cursor; |
|
3989 Q3TextCursor oldCursor = *cursor; |
|
3990 c.setParagraph(p1); |
|
3991 c.setIndex(indexFrom); |
|
3992 cursor->setParagraph(p2); |
|
3993 cursor->setIndex(indexTo); |
|
3994 doc->setSelectionStart(selNum, c); |
|
3995 doc->setSelectionEnd(selNum, *cursor); |
|
3996 repaintChanged(); |
|
3997 ensureCursorVisible(); |
|
3998 if (selNum != Q3TextDocument::Standard) |
|
3999 *cursor = oldCursor; |
|
4000 drawCursor(true); |
|
4001 } |
|
4002 |
|
4003 /*! |
|
4004 If there is a selection, \c{*}\a{paraFrom} is set to the number of the |
|
4005 paragraph in which the selection begins and \c{*}\a{paraTo} is set to |
|
4006 the number of the paragraph in which the selection ends. (They |
|
4007 could be the same.) \c{*}\a{indexFrom} is set to the index at which the |
|
4008 selection begins within \c{*}\a{paraFrom}, and \c{*}\a{indexTo} is set to |
|
4009 the index at which the selection ends within \c{*}\a{paraTo}. |
|
4010 |
|
4011 If there is no selection, \c{*}\a{paraFrom}, \c{*}\a{indexFrom}, |
|
4012 \c{*}\a{paraTo} and \c{*}\a{indexTo} are all set to -1. |
|
4013 |
|
4014 If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this |
|
4015 function does nothing. |
|
4016 |
|
4017 The \a selNum is the number of the selection (multiple selections |
|
4018 are supported). It defaults to 0 (the default selection). |
|
4019 |
|
4020 \sa setSelection() selectedText |
|
4021 */ |
|
4022 |
|
4023 void Q3TextEdit::getSelection(int *paraFrom, int *indexFrom, |
|
4024 int *paraTo, int *indexTo, int selNum) const |
|
4025 { |
|
4026 if (!paraFrom || !paraTo || !indexFrom || !indexTo) |
|
4027 return; |
|
4028 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4029 if (d->optimMode) { |
|
4030 *paraFrom = d->od->selStart.line; |
|
4031 *paraTo = d->od->selEnd.line; |
|
4032 *indexFrom = d->od->selStart.index; |
|
4033 *indexTo = d->od->selEnd.index; |
|
4034 return; |
|
4035 } |
|
4036 #endif |
|
4037 if (!doc->hasSelection(selNum)) { |
|
4038 *paraFrom = -1; |
|
4039 *indexFrom = -1; |
|
4040 *paraTo = -1; |
|
4041 *indexTo = -1; |
|
4042 return; |
|
4043 } |
|
4044 |
|
4045 doc->selectionStart(selNum, *paraFrom, *indexFrom); |
|
4046 doc->selectionEnd(selNum, *paraTo, *indexTo); |
|
4047 } |
|
4048 |
|
4049 /*! |
|
4050 \property Q3TextEdit::textFormat |
|
4051 \brief the text format: rich text, plain text, log text or auto text. |
|
4052 |
|
4053 The text format is one of the following: |
|
4054 \list |
|
4055 \i Qt::PlainText - all characters, except newlines, are displayed |
|
4056 verbatim, including spaces. Whenever a newline appears in the text |
|
4057 the text edit inserts a hard line break and begins a new |
|
4058 paragraph. |
|
4059 \i Qt::RichText - rich text rendering. The available styles are |
|
4060 defined in the default stylesheet Q3StyleSheet::defaultSheet(). |
|
4061 \i Qt::LogText - optimized mode for very large texts. Supports a very |
|
4062 limited set of formatting tags (color, bold, underline and italic |
|
4063 settings). |
|
4064 \i Qt::AutoText - this is the default. The text edit autodetects which |
|
4065 rendering style is best, Qt::PlainText or Qt::RichText. This is done |
|
4066 by using the Q3StyleSheet::mightBeRichText() function. |
|
4067 \endlist |
|
4068 */ |
|
4069 |
|
4070 void Q3TextEdit::setTextFormat(Qt::TextFormat format) |
|
4071 { |
|
4072 doc->setTextFormat(format); |
|
4073 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4074 checkOptimMode(); |
|
4075 #endif |
|
4076 } |
|
4077 |
|
4078 Qt::TextFormat Q3TextEdit::textFormat() const |
|
4079 { |
|
4080 return doc->textFormat(); |
|
4081 } |
|
4082 |
|
4083 /*! |
|
4084 Returns the number of paragraphs in the text; an empty textedit is always |
|
4085 considered to have one paragraph, so 1 is returned in this case. |
|
4086 */ |
|
4087 |
|
4088 int Q3TextEdit::paragraphs() const |
|
4089 { |
|
4090 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4091 if (d->optimMode) { |
|
4092 return d->od->numLines; |
|
4093 } |
|
4094 #endif |
|
4095 return doc->lastParagraph()->paragId() + 1; |
|
4096 } |
|
4097 |
|
4098 /*! |
|
4099 Returns the number of lines in paragraph \a para, or -1 if there |
|
4100 is no paragraph with index \a para. |
|
4101 */ |
|
4102 |
|
4103 int Q3TextEdit::linesOfParagraph(int para) const |
|
4104 { |
|
4105 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4106 if (d->optimMode) { |
|
4107 if (d->od->numLines >= para) |
|
4108 return 1; |
|
4109 else |
|
4110 return -1; |
|
4111 } |
|
4112 #endif |
|
4113 Q3TextParagraph *p = doc->paragAt(para); |
|
4114 if (!p) |
|
4115 return -1; |
|
4116 return p->lines(); |
|
4117 } |
|
4118 |
|
4119 /*! |
|
4120 Returns the length of the paragraph \a para (i.e. the number of |
|
4121 characters), or -1 if there is no paragraph with index \a para. |
|
4122 |
|
4123 This function ignores newlines. |
|
4124 */ |
|
4125 |
|
4126 int Q3TextEdit::paragraphLength(int para) const |
|
4127 { |
|
4128 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4129 if (d->optimMode) { |
|
4130 if (d->od->numLines >= para) { |
|
4131 if (d->od->lines[LOGOFFSET(para)].isEmpty()) // CR |
|
4132 return 1; |
|
4133 else |
|
4134 return d->od->lines[LOGOFFSET(para)].length(); |
|
4135 } |
|
4136 return -1; |
|
4137 } |
|
4138 #endif |
|
4139 Q3TextParagraph *p = doc->paragAt(para); |
|
4140 if (!p) |
|
4141 return -1; |
|
4142 return p->length() - 1; |
|
4143 } |
|
4144 |
|
4145 /*! |
|
4146 Returns the number of lines in the text edit; this could be 0. |
|
4147 |
|
4148 \warning This function may be slow. Lines change all the time |
|
4149 during word wrapping, so this function has to iterate over all the |
|
4150 paragraphs and get the number of lines from each one individually. |
|
4151 */ |
|
4152 |
|
4153 int Q3TextEdit::lines() const |
|
4154 { |
|
4155 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4156 if (d->optimMode) { |
|
4157 return d->od->numLines; |
|
4158 } |
|
4159 #endif |
|
4160 Q3TextParagraph *p = doc->firstParagraph(); |
|
4161 int l = 0; |
|
4162 while (p) { |
|
4163 l += p->lines(); |
|
4164 p = p->next(); |
|
4165 } |
|
4166 |
|
4167 return l; |
|
4168 } |
|
4169 |
|
4170 /*! |
|
4171 Returns the line number of the line in paragraph \a para in which |
|
4172 the character at position \a index appears. The \a index position is |
|
4173 relative to the beginning of the paragraph. If there is no such |
|
4174 paragraph or no such character at the \a index position (e.g. the |
|
4175 index is out of range) -1 is returned. |
|
4176 */ |
|
4177 |
|
4178 int Q3TextEdit::lineOfChar(int para, int index) |
|
4179 { |
|
4180 Q3TextParagraph *p = doc->paragAt(para); |
|
4181 if (!p) |
|
4182 return -1; |
|
4183 |
|
4184 int idx, line; |
|
4185 Q3TextStringChar *c = p->lineStartOfChar(index, &idx, &line); |
|
4186 if (!c) |
|
4187 return -1; |
|
4188 |
|
4189 return line; |
|
4190 } |
|
4191 |
|
4192 void Q3TextEdit::setModified(bool m) |
|
4193 { |
|
4194 bool oldModified = modified; |
|
4195 modified = m; |
|
4196 if (modified && doc->oTextValid) |
|
4197 doc->invalidateOriginalText(); |
|
4198 if (oldModified != modified) |
|
4199 emit modificationChanged(modified); |
|
4200 } |
|
4201 |
|
4202 /*! |
|
4203 \property Q3TextEdit::modified |
|
4204 \brief whether the document has been modified by the user |
|
4205 */ |
|
4206 |
|
4207 bool Q3TextEdit::isModified() const |
|
4208 { |
|
4209 return modified; |
|
4210 } |
|
4211 |
|
4212 void Q3TextEdit::setModified() |
|
4213 { |
|
4214 if (!isModified()) |
|
4215 setModified(true); |
|
4216 } |
|
4217 |
|
4218 /*! |
|
4219 Returns true if the current format is italic; otherwise returns false. |
|
4220 |
|
4221 \sa setItalic() |
|
4222 */ |
|
4223 |
|
4224 bool Q3TextEdit::italic() const |
|
4225 { |
|
4226 return currentFormat->font().italic(); |
|
4227 } |
|
4228 |
|
4229 /*! |
|
4230 Returns true if the current format is bold; otherwise returns false. |
|
4231 |
|
4232 \sa setBold() |
|
4233 */ |
|
4234 |
|
4235 bool Q3TextEdit::bold() const |
|
4236 { |
|
4237 return currentFormat->font().bold(); |
|
4238 } |
|
4239 |
|
4240 /*! |
|
4241 Returns true if the current format is underlined; otherwise returns |
|
4242 false. |
|
4243 |
|
4244 \sa setUnderline() |
|
4245 */ |
|
4246 |
|
4247 bool Q3TextEdit::underline() const |
|
4248 { |
|
4249 return currentFormat->font().underline(); |
|
4250 } |
|
4251 |
|
4252 /*! |
|
4253 Returns the font family of the current format. |
|
4254 |
|
4255 \sa setFamily() setCurrentFont() setPointSize() |
|
4256 */ |
|
4257 |
|
4258 QString Q3TextEdit::family() const |
|
4259 { |
|
4260 return currentFormat->font().family(); |
|
4261 } |
|
4262 |
|
4263 /*! |
|
4264 Returns the point size of the font of the current format. |
|
4265 |
|
4266 \sa setFamily() setCurrentFont() setPointSize() |
|
4267 */ |
|
4268 |
|
4269 int Q3TextEdit::pointSize() const |
|
4270 { |
|
4271 return currentFormat->font().pointSize(); |
|
4272 } |
|
4273 |
|
4274 /*! |
|
4275 Returns the color of the current format. |
|
4276 |
|
4277 \sa setColor() setPaper() |
|
4278 */ |
|
4279 |
|
4280 QColor Q3TextEdit::color() const |
|
4281 { |
|
4282 return currentFormat->color(); |
|
4283 } |
|
4284 |
|
4285 /*! |
|
4286 Returns Q3ScrollView::font() |
|
4287 |
|
4288 \warning In previous versions this function returned the font of |
|
4289 the current format. This lead to confusion. Please use |
|
4290 currentFont() instead. |
|
4291 */ |
|
4292 |
|
4293 QFont Q3TextEdit::font() const |
|
4294 { |
|
4295 return Q3ScrollView::font(); |
|
4296 } |
|
4297 |
|
4298 /*! |
|
4299 Returns the font of the current format. |
|
4300 |
|
4301 \sa setCurrentFont() setFamily() setPointSize() |
|
4302 */ |
|
4303 |
|
4304 QFont Q3TextEdit::currentFont() const |
|
4305 { |
|
4306 return currentFormat->font(); |
|
4307 } |
|
4308 |
|
4309 |
|
4310 /*! |
|
4311 Returns the alignment of the current paragraph. |
|
4312 |
|
4313 \sa setAlignment() |
|
4314 */ |
|
4315 |
|
4316 int Q3TextEdit::alignment() const |
|
4317 { |
|
4318 return currentAlignment; |
|
4319 } |
|
4320 |
|
4321 /*! |
|
4322 Returns the vertical alignment of the current format. |
|
4323 |
|
4324 \sa setVerticalAlignment() |
|
4325 */ |
|
4326 |
|
4327 Q3TextEdit::VerticalAlignment Q3TextEdit::verticalAlignment() const |
|
4328 { |
|
4329 return (Q3TextEdit::VerticalAlignment) currentFormat->vAlign(); |
|
4330 } |
|
4331 |
|
4332 void Q3TextEdit::startDrag() |
|
4333 { |
|
4334 #ifndef QT_NO_DRAGANDDROP |
|
4335 mousePressed = false; |
|
4336 inDoubleClick = false; |
|
4337 Q3DragObject *drag = dragObject(viewport()); |
|
4338 if (!drag) |
|
4339 return; |
|
4340 if (isReadOnly()) { |
|
4341 drag->dragCopy(); |
|
4342 } else { |
|
4343 if (drag->drag() && Q3DragObject::target() != this && Q3DragObject::target() != viewport()) |
|
4344 removeSelectedText(); |
|
4345 } |
|
4346 #endif |
|
4347 } |
|
4348 |
|
4349 /*! |
|
4350 If \a select is true (the default), all the text is selected as |
|
4351 selection 0. If \a select is false any selected text is |
|
4352 unselected, i.e. the default selection (selection 0) is cleared. |
|
4353 |
|
4354 \sa selectedText |
|
4355 */ |
|
4356 |
|
4357 void Q3TextEdit::selectAll(bool select) |
|
4358 { |
|
4359 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4360 if (d->optimMode) { |
|
4361 if (select) |
|
4362 optimSelectAll(); |
|
4363 else |
|
4364 optimRemoveSelection(); |
|
4365 return; |
|
4366 } |
|
4367 #endif |
|
4368 if (!select) |
|
4369 doc->removeSelection(Q3TextDocument::Standard); |
|
4370 else |
|
4371 doc->selectAll(Q3TextDocument::Standard); |
|
4372 repaintChanged(); |
|
4373 emit copyAvailable(doc->hasSelection(Q3TextDocument::Standard)); |
|
4374 emit selectionChanged(); |
|
4375 #ifndef QT_NO_CURSOR |
|
4376 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
4377 #endif |
|
4378 } |
|
4379 |
|
4380 void Q3TextEdit::UndoRedoInfo::clear() |
|
4381 { |
|
4382 if (valid()) { |
|
4383 if (type == Insert || type == Return) |
|
4384 doc->addCommand(new Q3TextInsertCommand(doc, id, index, d->text.rawData(), styleInformation)); |
|
4385 else if (type == Format) |
|
4386 doc->addCommand(new Q3TextFormatCommand(doc, id, index, eid, eindex, d->text.rawData(), format, flags)); |
|
4387 else if (type == Style) |
|
4388 doc->addCommand(new Q3TextStyleCommand(doc, id, eid, styleInformation)); |
|
4389 else if (type != Invalid) { |
|
4390 doc->addCommand(new Q3TextDeleteCommand(doc, id, index, d->text.rawData(), styleInformation)); |
|
4391 } |
|
4392 } |
|
4393 type = Invalid; |
|
4394 d->text.clear(); |
|
4395 id = -1; |
|
4396 index = -1; |
|
4397 styleInformation = QByteArray(); |
|
4398 } |
|
4399 |
|
4400 |
|
4401 /*! |
|
4402 If there is some selected text (in selection 0) it is deleted. If |
|
4403 there is no selected text (in selection 0) the character to the |
|
4404 right of the text cursor is deleted. |
|
4405 |
|
4406 \sa removeSelectedText() cut() |
|
4407 */ |
|
4408 |
|
4409 void Q3TextEdit::del() |
|
4410 { |
|
4411 if (doc->hasSelection(Q3TextDocument::Standard)) { |
|
4412 removeSelectedText(); |
|
4413 return; |
|
4414 } |
|
4415 |
|
4416 doKeyboardAction(ActionDelete); |
|
4417 } |
|
4418 |
|
4419 |
|
4420 Q3TextEdit::UndoRedoInfo::UndoRedoInfo(Q3TextDocument *dc) |
|
4421 : type(Invalid), doc(dc) |
|
4422 { |
|
4423 d = new QUndoRedoInfoPrivate; |
|
4424 d->text.clear(); |
|
4425 id = -1; |
|
4426 index = -1; |
|
4427 } |
|
4428 |
|
4429 Q3TextEdit::UndoRedoInfo::~UndoRedoInfo() |
|
4430 { |
|
4431 delete d; |
|
4432 } |
|
4433 |
|
4434 bool Q3TextEdit::UndoRedoInfo::valid() const |
|
4435 { |
|
4436 return id >= 0 && type != Invalid; |
|
4437 } |
|
4438 |
|
4439 /*! |
|
4440 \internal |
|
4441 |
|
4442 Resets the current format to the default format. |
|
4443 */ |
|
4444 |
|
4445 void Q3TextEdit::resetFormat() |
|
4446 { |
|
4447 setAlignment(Qt::AlignAuto); |
|
4448 setParagType(Q3StyleSheetItem::DisplayBlock, Q3StyleSheetItem::ListDisc); |
|
4449 setFormat(doc->formatCollection()->defaultFormat(), Q3TextFormat::Format); |
|
4450 } |
|
4451 |
|
4452 /*! |
|
4453 Returns the Q3StyleSheet which is being used by this text edit. |
|
4454 |
|
4455 \sa setStyleSheet() |
|
4456 */ |
|
4457 |
|
4458 Q3StyleSheet* Q3TextEdit::styleSheet() const |
|
4459 { |
|
4460 return doc->styleSheet(); |
|
4461 } |
|
4462 |
|
4463 /*! |
|
4464 Sets the stylesheet to use with this text edit to \a styleSheet. |
|
4465 Changes will only take effect for new text added with setText() or |
|
4466 append(). |
|
4467 |
|
4468 \sa styleSheet() |
|
4469 */ |
|
4470 |
|
4471 void Q3TextEdit::setStyleSheet(Q3StyleSheet* styleSheet) |
|
4472 { |
|
4473 doc->setStyleSheet(styleSheet); |
|
4474 } |
|
4475 |
|
4476 /*! |
|
4477 \property Q3TextEdit::paper |
|
4478 \brief the background (paper) brush. |
|
4479 |
|
4480 The brush that is currently used to draw the background of the |
|
4481 text edit. The initial setting is an empty brush. |
|
4482 */ |
|
4483 |
|
4484 void Q3TextEdit::setPaper(const QBrush& pap) |
|
4485 { |
|
4486 doc->setPaper(new QBrush(pap)); |
|
4487 if ( pap.pixmap() ) |
|
4488 viewport()->setBackgroundPixmap( *pap.pixmap() ); |
|
4489 QPalette pal = palette(); |
|
4490 pal.setColor(QPalette::Window, pap.color()); |
|
4491 setPalette(pal); |
|
4492 pal = viewport()->palette(); |
|
4493 pal.setColor(QPalette::Window, pap.color()); |
|
4494 viewport()->setPalette(pal); |
|
4495 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4496 // force a repaint of the entire viewport - using updateContents() |
|
4497 // would clip the coords to the content size |
|
4498 if (d->optimMode) |
|
4499 repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); |
|
4500 else |
|
4501 #endif |
|
4502 updateContents(); |
|
4503 } |
|
4504 |
|
4505 QBrush Q3TextEdit::paper() const |
|
4506 { |
|
4507 if (doc->paper()) |
|
4508 return *doc->paper(); |
|
4509 return QBrush(palette().base()); |
|
4510 } |
|
4511 |
|
4512 /*! |
|
4513 \property Q3TextEdit::linkUnderline |
|
4514 \brief whether hypertext links will be underlined |
|
4515 |
|
4516 If true (the default) hypertext links will be displayed |
|
4517 underlined. If false links will not be displayed underlined. |
|
4518 */ |
|
4519 |
|
4520 void Q3TextEdit::setLinkUnderline(bool b) |
|
4521 { |
|
4522 if (doc->underlineLinks() == b) |
|
4523 return; |
|
4524 doc->setUnderlineLinks(b); |
|
4525 repaintChanged(); |
|
4526 } |
|
4527 |
|
4528 bool Q3TextEdit::linkUnderline() const |
|
4529 { |
|
4530 return doc->underlineLinks(); |
|
4531 } |
|
4532 |
|
4533 /*! |
|
4534 Sets the text edit's mimesource factory to \a factory. See |
|
4535 Q3MimeSourceFactory for further details. |
|
4536 |
|
4537 \sa mimeSourceFactory() |
|
4538 */ |
|
4539 |
|
4540 #ifndef QT_NO_MIME |
|
4541 void Q3TextEdit::setMimeSourceFactory(Q3MimeSourceFactory* factory) |
|
4542 { |
|
4543 doc->setMimeSourceFactory(factory); |
|
4544 } |
|
4545 |
|
4546 /*! |
|
4547 Returns the Q3MimeSourceFactory which is being used by this text |
|
4548 edit. |
|
4549 |
|
4550 \sa setMimeSourceFactory() |
|
4551 */ |
|
4552 |
|
4553 Q3MimeSourceFactory* Q3TextEdit::mimeSourceFactory() const |
|
4554 { |
|
4555 return doc->mimeSourceFactory(); |
|
4556 } |
|
4557 #endif |
|
4558 |
|
4559 /*! |
|
4560 Returns how many pixels high the text edit needs to be to display |
|
4561 all the text if the text edit is \a w pixels wide. |
|
4562 */ |
|
4563 |
|
4564 int Q3TextEdit::heightForWidth(int w) const |
|
4565 { |
|
4566 int oldw = doc->width(); |
|
4567 doc->doLayout(0, w); |
|
4568 int h = doc->height(); |
|
4569 doc->setWidth(oldw); |
|
4570 doc->invalidate(); |
|
4571 ((Q3TextEdit*)this)->formatMore(); |
|
4572 return h; |
|
4573 } |
|
4574 |
|
4575 /*! |
|
4576 Appends a new paragraph with \a text to the end of the text edit. Note that |
|
4577 the undo/redo history is cleared by this function, and no undo |
|
4578 history is kept for appends which makes them faster than |
|
4579 insert()s. If you want to append text which is added to the |
|
4580 undo/redo history as well, use insertParagraph(). |
|
4581 */ |
|
4582 |
|
4583 void Q3TextEdit::append(const QString &text) |
|
4584 { |
|
4585 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4586 if (d->optimMode) { |
|
4587 optimAppend(text); |
|
4588 return; |
|
4589 } |
|
4590 #endif |
|
4591 // flush and clear the undo/redo stack if necessary |
|
4592 undoRedoInfo.clear(); |
|
4593 doc->commands()->clear(); |
|
4594 |
|
4595 doc->removeSelection(Q3TextDocument::Standard); |
|
4596 Qt::TextFormat f = doc->textFormat(); |
|
4597 if (f == Qt::AutoText) { |
|
4598 if (Q3StyleSheet::mightBeRichText(text)) |
|
4599 f = Qt::RichText; |
|
4600 else |
|
4601 f = Qt::PlainText; |
|
4602 } |
|
4603 |
|
4604 drawCursor(false); |
|
4605 Q3TextCursor oldc(*cursor); |
|
4606 ensureFormatted(doc->lastParagraph()); |
|
4607 bool atBottom = contentsY() >= contentsHeight() - visibleHeight(); |
|
4608 cursor->gotoEnd(); |
|
4609 if (cursor->index() > 0) |
|
4610 cursor->splitAndInsertEmptyParagraph(); |
|
4611 Q3TextCursor oldCursor2 = *cursor; |
|
4612 |
|
4613 if (f == Qt::PlainText) { |
|
4614 cursor->insert(text, true); |
|
4615 if (doc->useFormatCollection() && !doc->preProcessor() && |
|
4616 currentFormat != cursor->paragraph()->at( cursor->index() )->format()) { |
|
4617 doc->setSelectionStart( Q3TextDocument::Temp, oldCursor2 ); |
|
4618 doc->setSelectionEnd( Q3TextDocument::Temp, *cursor ); |
|
4619 doc->setFormat( Q3TextDocument::Temp, currentFormat, Q3TextFormat::Format ); |
|
4620 doc->removeSelection( Q3TextDocument::Temp ); |
|
4621 } |
|
4622 } else { |
|
4623 cursor->paragraph()->setListItem(false); |
|
4624 cursor->paragraph()->setListDepth(0); |
|
4625 if (cursor->paragraph()->prev()) |
|
4626 cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change |
|
4627 doc->setRichTextInternal(text); |
|
4628 } |
|
4629 formatMore(); |
|
4630 repaintChanged(); |
|
4631 if (atBottom) |
|
4632 scrollToBottom(); |
|
4633 *cursor = oldc; |
|
4634 if (!isReadOnly()) |
|
4635 cursorVisible = true; |
|
4636 setModified(); |
|
4637 emit textChanged(); |
|
4638 } |
|
4639 |
|
4640 /*! |
|
4641 \property Q3TextEdit::hasSelectedText |
|
4642 \brief whether some text is selected in selection 0 |
|
4643 */ |
|
4644 |
|
4645 bool Q3TextEdit::hasSelectedText() const |
|
4646 { |
|
4647 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4648 if (d->optimMode) |
|
4649 return optimHasSelection(); |
|
4650 else |
|
4651 #endif |
|
4652 return doc->hasSelection(Q3TextDocument::Standard); |
|
4653 } |
|
4654 |
|
4655 /*! |
|
4656 \property Q3TextEdit::selectedText |
|
4657 \brief The selected text (from selection 0) or an empty string if |
|
4658 there is no currently selected text (in selection 0). |
|
4659 |
|
4660 The text is always returned as Qt::PlainText if the textFormat() is |
|
4661 Qt::PlainText or Qt::AutoText, otherwise it is returned as HTML. |
|
4662 |
|
4663 \sa hasSelectedText |
|
4664 */ |
|
4665 |
|
4666 QString Q3TextEdit::selectedText() const |
|
4667 { |
|
4668 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
4669 if (d->optimMode) |
|
4670 return optimSelectedText(); |
|
4671 else |
|
4672 #endif |
|
4673 return doc->selectedText(Q3TextDocument::Standard, textFormat() == Qt::RichText); |
|
4674 } |
|
4675 |
|
4676 bool Q3TextEdit::handleReadOnlyKeyEvent(QKeyEvent *e) |
|
4677 { |
|
4678 switch(e->key()) { |
|
4679 case Qt::Key_Down: |
|
4680 setContentsPos(contentsX(), contentsY() + 10); |
|
4681 break; |
|
4682 case Qt::Key_Up: |
|
4683 setContentsPos(contentsX(), contentsY() - 10); |
|
4684 break; |
|
4685 case Qt::Key_Left: |
|
4686 setContentsPos(contentsX() - 10, contentsY()); |
|
4687 break; |
|
4688 case Qt::Key_Right: |
|
4689 setContentsPos(contentsX() + 10, contentsY()); |
|
4690 break; |
|
4691 case Qt::Key_PageUp: |
|
4692 setContentsPos(contentsX(), contentsY() - visibleHeight()); |
|
4693 break; |
|
4694 case Qt::Key_PageDown: |
|
4695 setContentsPos(contentsX(), contentsY() + visibleHeight()); |
|
4696 break; |
|
4697 case Qt::Key_Home: |
|
4698 setContentsPos(contentsX(), 0); |
|
4699 break; |
|
4700 case Qt::Key_End: |
|
4701 setContentsPos(contentsX(), contentsHeight() - visibleHeight()); |
|
4702 break; |
|
4703 case Qt::Key_F16: // Copy key on Sun keyboards |
|
4704 copy(); |
|
4705 break; |
|
4706 #ifndef QT_NO_NETWORKPROTOCOL |
|
4707 case Qt::Key_Return: |
|
4708 case Qt::Key_Enter: |
|
4709 case Qt::Key_Space: { |
|
4710 if (!doc->focusIndicator.href.isEmpty() |
|
4711 || !doc->focusIndicator.name.isEmpty()) { |
|
4712 if (!doc->focusIndicator.href.isEmpty()) { |
|
4713 QUrl u = QUrl(doc->context()).resolved(doc->focusIndicator.href); |
|
4714 emitLinkClicked(u.toString(QUrl::None)); |
|
4715 } |
|
4716 if (!doc->focusIndicator.name.isEmpty()) |
|
4717 if (Q3TextBrowser *browser = qobject_cast<Q3TextBrowser*>(this)) |
|
4718 emit browser->anchorClicked(doc->focusIndicator.name, doc->focusIndicator.href); |
|
4719 |
|
4720 #ifndef QT_NO_CURSOR |
|
4721 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
4722 #endif |
|
4723 } |
|
4724 } break; |
|
4725 #endif |
|
4726 default: |
|
4727 if (e->state() & Qt::ControlButton) { |
|
4728 switch (e->key()) { |
|
4729 case Qt::Key_C: case Qt::Key_F16: // Copy key on Sun keyboards |
|
4730 copy(); |
|
4731 break; |
|
4732 #ifdef Q_WS_WIN |
|
4733 case Qt::Key_Insert: |
|
4734 copy(); |
|
4735 break; |
|
4736 case Qt::Key_A: |
|
4737 selectAll(); |
|
4738 break; |
|
4739 #endif |
|
4740 } |
|
4741 |
|
4742 } |
|
4743 return false; |
|
4744 } |
|
4745 return true; |
|
4746 } |
|
4747 |
|
4748 /*! |
|
4749 Returns the context of the text edit. The context is a path which |
|
4750 the text edit's Q3MimeSourceFactory uses to resolve the locations |
|
4751 of files and images. |
|
4752 |
|
4753 \sa text |
|
4754 */ |
|
4755 |
|
4756 QString Q3TextEdit::context() const |
|
4757 { |
|
4758 return doc->context(); |
|
4759 } |
|
4760 |
|
4761 /*! |
|
4762 \property Q3TextEdit::documentTitle |
|
4763 \brief the title of the document parsed from the text. |
|
4764 |
|
4765 For Qt::PlainText the title will be an empty string. For \c |
|
4766 Qt::RichText the title will be the text between the \c{<title>} tags, |
|
4767 if present, otherwise an empty string. |
|
4768 */ |
|
4769 |
|
4770 QString Q3TextEdit::documentTitle() const |
|
4771 { |
|
4772 return doc->attributes()[QLatin1String("title")]; |
|
4773 } |
|
4774 |
|
4775 void Q3TextEdit::makeParagVisible(Q3TextParagraph *p) |
|
4776 { |
|
4777 setContentsPos(contentsX(), qMin(p->rect().y(), contentsHeight() - visibleHeight())); |
|
4778 } |
|
4779 |
|
4780 /*! |
|
4781 Scrolls the text edit to make the text at the anchor called \a |
|
4782 name visible, if it can be found in the document. If the anchor |
|
4783 isn't found no scrolling will occur. An anchor is defined using |
|
4784 the HTML anchor tag, e.g. \c{<a name="target">}. |
|
4785 */ |
|
4786 |
|
4787 void Q3TextEdit::scrollToAnchor(const QString& name) |
|
4788 { |
|
4789 if (!isVisible()) { |
|
4790 d->scrollToAnchor = name; |
|
4791 return; |
|
4792 } |
|
4793 if (name.isEmpty()) |
|
4794 return; |
|
4795 sync(); |
|
4796 Q3TextCursor cursor(doc); |
|
4797 Q3TextParagraph* last = doc->lastParagraph(); |
|
4798 for (;;) { |
|
4799 Q3TextStringChar* c = cursor.paragraph()->at(cursor.index()); |
|
4800 if(c->isAnchor()) { |
|
4801 QString a = c->anchorName(); |
|
4802 if (a == name || |
|
4803 (a.contains(QLatin1Char('#')) && a.split(QLatin1Char('#')).contains(name))) { |
|
4804 setContentsPos(contentsX(), qMin(cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight())); |
|
4805 break; |
|
4806 } |
|
4807 } |
|
4808 if (cursor.paragraph() == last && cursor.atParagEnd() ) |
|
4809 break; |
|
4810 cursor.gotoNextLetter(); |
|
4811 } |
|
4812 } |
|
4813 |
|
4814 /*! |
|
4815 Returns the text for the attribute \a attr (Qt::AnchorHref by |
|
4816 default) if there is an anchor at position \a pos (in contents |
|
4817 coordinates); otherwise returns an empty string. |
|
4818 */ |
|
4819 |
|
4820 QString Q3TextEdit::anchorAt(const QPoint& pos, Qt::AnchorAttribute attr) |
|
4821 { |
|
4822 Q3TextCursor c(doc); |
|
4823 placeCursor(pos, &c, true); |
|
4824 switch(attr) { |
|
4825 case Qt::AnchorName: |
|
4826 return c.paragraph()->at(c.index())->anchorName(); |
|
4827 case Qt::AnchorHref: |
|
4828 return c.paragraph()->at(c.index())->anchorHref(); |
|
4829 } |
|
4830 // incase the compiler is really dumb about determining if a function |
|
4831 // returns something :) |
|
4832 return QString(); |
|
4833 } |
|
4834 |
|
4835 void Q3TextEdit::documentWidthChanged(int w) |
|
4836 { |
|
4837 resizeContents(qMax(visibleWidth(), w), contentsHeight()); |
|
4838 } |
|
4839 |
|
4840 /*! \internal |
|
4841 |
|
4842 This function does nothing |
|
4843 */ |
|
4844 |
|
4845 void Q3TextEdit::updateStyles() |
|
4846 { |
|
4847 } |
|
4848 |
|
4849 void Q3TextEdit::setDocument(Q3TextDocument *dc) |
|
4850 { |
|
4851 if (dc == 0) { |
|
4852 qWarning("Q3TextEdit::setDocument() called with null Q3TextDocument pointer"); |
|
4853 return; |
|
4854 } |
|
4855 if (dc == doc) |
|
4856 return; |
|
4857 doc = dc; |
|
4858 delete cursor; |
|
4859 cursor = new Q3TextCursor(doc); |
|
4860 clearUndoRedo(); |
|
4861 undoRedoInfo.doc = doc; |
|
4862 lastFormatted = 0; |
|
4863 } |
|
4864 |
|
4865 #ifndef QT_NO_CLIPBOARD |
|
4866 |
|
4867 /*! |
|
4868 Pastes the text with format \a subtype from the clipboard into the |
|
4869 text edit at the current cursor position. The \a subtype can be |
|
4870 "plain" or "html". |
|
4871 |
|
4872 If there is no text with format \a subtype in the clipboard |
|
4873 nothing happens. |
|
4874 |
|
4875 \sa paste() cut() Q3TextEdit::copy() |
|
4876 */ |
|
4877 |
|
4878 void Q3TextEdit::pasteSubType(const QByteArray &subtype) |
|
4879 { |
|
4880 #ifndef QT_NO_MIMECLIPBOARD |
|
4881 QMimeSource *m = QApplication::clipboard()->data(d->clipboard_mode); |
|
4882 pasteSubType(subtype, m); |
|
4883 #endif |
|
4884 } |
|
4885 |
|
4886 /*! \internal */ |
|
4887 |
|
4888 void Q3TextEdit::pasteSubType(const QByteArray& subtype, QMimeSource *m) |
|
4889 { |
|
4890 #ifndef QT_NO_MIME |
|
4891 QByteArray st = subtype; |
|
4892 |
|
4893 if (subtype != "x-qrichtext") |
|
4894 st.prepend("text/"); |
|
4895 else |
|
4896 st.prepend("application/"); |
|
4897 if (!m) |
|
4898 return; |
|
4899 if (doc->hasSelection(Q3TextDocument::Standard)) |
|
4900 removeSelectedText(); |
|
4901 if (!Q3RichTextDrag::canDecode(m)) |
|
4902 return; |
|
4903 QString t; |
|
4904 if (!Q3RichTextDrag::decode(m, t, QString::fromLatin1(st), QString::fromLatin1(subtype))) |
|
4905 return; |
|
4906 if (st == "application/x-qrichtext") { |
|
4907 int start; |
|
4908 if ((start = t.indexOf(QLatin1String("<!--StartFragment-->"))) != -1) { |
|
4909 start += 20; |
|
4910 int end = t.indexOf(QLatin1String("<!--EndFragment-->")); |
|
4911 Q3TextCursor oldC = *cursor; |
|
4912 |
|
4913 // during the setRichTextInternal() call the cursors |
|
4914 // paragraph might get joined with the provious one, so |
|
4915 // the cursors one would get deleted and oldC.paragraph() |
|
4916 // would be a dnagling pointer. To avoid that try to go |
|
4917 // one letter back and later go one forward again. |
|
4918 oldC.gotoPreviousLetter(); |
|
4919 bool couldGoBack = oldC != *cursor; |
|
4920 // first para might get deleted, so remember to reset it |
|
4921 bool wasAtFirst = oldC.paragraph() == doc->firstParagraph(); |
|
4922 |
|
4923 if (start < end) |
|
4924 t = t.mid(start, end - start); |
|
4925 else |
|
4926 t = t.mid(start); |
|
4927 lastFormatted = cursor->paragraph(); |
|
4928 if (lastFormatted->prev()) |
|
4929 lastFormatted = lastFormatted->prev(); |
|
4930 doc->setRichTextInternal(t, cursor); |
|
4931 |
|
4932 // the first para might have been deleted in |
|
4933 // setRichTextInternal(). To be sure, reset it if |
|
4934 // necessary. |
|
4935 if (wasAtFirst) { |
|
4936 int index = oldC.index(); |
|
4937 oldC.setParagraph(doc->firstParagraph()); |
|
4938 oldC.setIndex(index); |
|
4939 } |
|
4940 |
|
4941 // if we went back one letter before (see last comment), |
|
4942 // go one forward to point to the right position |
|
4943 if (couldGoBack) |
|
4944 oldC.gotoNextLetter(); |
|
4945 |
|
4946 if (undoEnabled && !isReadOnly()) { |
|
4947 doc->setSelectionStart(Q3TextDocument::Temp, oldC); |
|
4948 doc->setSelectionEnd(Q3TextDocument::Temp, *cursor); |
|
4949 |
|
4950 checkUndoRedoInfo(UndoRedoInfo::Insert); |
|
4951 if (!undoRedoInfo.valid()) { |
|
4952 undoRedoInfo.id = oldC.paragraph()->paragId(); |
|
4953 undoRedoInfo.index = oldC.index(); |
|
4954 undoRedoInfo.d->text.clear(); |
|
4955 } |
|
4956 int oldLen = undoRedoInfo.d->text.length(); |
|
4957 if (!doc->preProcessor()) { |
|
4958 QString txt = doc->selectedText(Q3TextDocument::Temp); |
|
4959 undoRedoInfo.d->text += txt; |
|
4960 for (int i = 0; i < (int)txt.length(); ++i) { |
|
4961 if (txt[i] != QLatin1Char('\n') && oldC.paragraph()->at(oldC.index())->format()) { |
|
4962 oldC.paragraph()->at(oldC.index())->format()->addRef(); |
|
4963 undoRedoInfo.d->text. |
|
4964 setFormat(oldLen + i, oldC.paragraph()->at(oldC.index())->format(), true); |
|
4965 } |
|
4966 oldC.gotoNextLetter(); |
|
4967 } |
|
4968 } |
|
4969 undoRedoInfo.clear(); |
|
4970 removeSelection(Q3TextDocument::Temp); |
|
4971 } |
|
4972 |
|
4973 formatMore(); |
|
4974 setModified(); |
|
4975 emit textChanged(); |
|
4976 repaintChanged(); |
|
4977 ensureCursorVisible(); |
|
4978 return; |
|
4979 } |
|
4980 } else { |
|
4981 #if defined(Q_OS_WIN32) |
|
4982 // Need to convert CRLF to LF |
|
4983 t.replace(QLatin1String("\r\n"), QLatin1String("\n")); |
|
4984 #elif defined(Q_OS_MAC) |
|
4985 //need to convert CR to LF |
|
4986 t.replace(QLatin1Char('\r'), QLatin1Char('\n')); |
|
4987 #endif |
|
4988 QChar *uc = (QChar *)t.unicode(); |
|
4989 for (int i = 0; i < t.length(); i++) { |
|
4990 if (uc[i] < QLatin1Char(' ') && uc[i] != QLatin1Char('\n') && uc[i] != QLatin1Char('\t')) |
|
4991 uc[i] = QLatin1Char(' '); |
|
4992 } |
|
4993 if (!t.isEmpty()) |
|
4994 insert(t, false, true); |
|
4995 } |
|
4996 #endif //QT_NO_MIME |
|
4997 } |
|
4998 |
|
4999 #ifndef QT_NO_MIMECLIPBOARD |
|
5000 /*! |
|
5001 Prompts the user to choose a type from a list of text types |
|
5002 available, then copies text from the clipboard (if there is any) |
|
5003 into the text edit at the current text cursor position. Any |
|
5004 selected text (in selection 0) is first deleted. |
|
5005 */ |
|
5006 void Q3TextEdit::pasteSpecial(const QPoint& pt) |
|
5007 { |
|
5008 QByteArray st = pickSpecial(QApplication::clipboard()->data(d->clipboard_mode), |
|
5009 true, pt); |
|
5010 if (!st.isEmpty()) |
|
5011 pasteSubType(st); |
|
5012 } |
|
5013 #endif |
|
5014 #ifndef QT_NO_MIME |
|
5015 QByteArray Q3TextEdit::pickSpecial(QMimeSource* ms, bool always_ask, const QPoint& pt) |
|
5016 { |
|
5017 if (ms) { |
|
5018 #ifndef QT_NO_MENU |
|
5019 QMenu popup(this); |
|
5020 QString fmt; |
|
5021 int n = 0; |
|
5022 QHash<QString, bool> done; |
|
5023 for (int i = 0; !(fmt = QLatin1String(ms->format(i))).isNull(); i++) { |
|
5024 int semi = fmt.indexOf(QLatin1Char(';')); |
|
5025 if (semi >= 0) |
|
5026 fmt = fmt.left(semi); |
|
5027 if (fmt.left(5) == QLatin1String("text/")) { |
|
5028 fmt = fmt.mid(5); |
|
5029 if (!done.contains(fmt)) { |
|
5030 done.insert(fmt,true); |
|
5031 popup.insertItem(fmt, i); |
|
5032 n++; |
|
5033 } |
|
5034 } |
|
5035 } |
|
5036 if (n) { |
|
5037 QAction *action = (n == 1 && !always_ask) |
|
5038 ? popup.actions().at(0) |
|
5039 : popup.exec(pt); |
|
5040 if (action) |
|
5041 return action->text().toLatin1(); |
|
5042 } |
|
5043 #else |
|
5044 QString fmt; |
|
5045 for (int i = 0; !(fmt = ms->format(i)).isNull(); i++) { |
|
5046 int semi = fmt.indexOf(';'); |
|
5047 if (semi >= 0) |
|
5048 fmt = fmt.left(semi); |
|
5049 if (fmt.left(5) == "text/") { |
|
5050 fmt = fmt.mid(5); |
|
5051 return fmt.latin1(); |
|
5052 } |
|
5053 } |
|
5054 #endif |
|
5055 } |
|
5056 return QByteArray(); |
|
5057 } |
|
5058 #endif // QT_NO_MIME |
|
5059 #endif // QT_NO_CLIPBOARD |
|
5060 |
|
5061 /*! |
|
5062 \enum Q3TextEdit::WordWrap |
|
5063 |
|
5064 This enum defines the Q3TextEdit's word wrap modes. |
|
5065 |
|
5066 \value NoWrap Do not wrap the text. |
|
5067 |
|
5068 \value WidgetWidth Wrap the text at the current width of the |
|
5069 widget (this is the default). Wrapping is at whitespace by |
|
5070 default; this can be changed with setWrapPolicy(). |
|
5071 |
|
5072 \value FixedPixelWidth Wrap the text at a fixed number of pixels |
|
5073 from the widget's left side. The number of pixels is set with |
|
5074 wrapColumnOrWidth(). |
|
5075 |
|
5076 \value FixedColumnWidth Wrap the text at a fixed number of |
|
5077 character columns from the widget's left side. The number of |
|
5078 characters is set with wrapColumnOrWidth(). This is useful if you |
|
5079 need formatted text that can also be displayed gracefully on |
|
5080 devices with monospaced fonts, for example a standard VT100 |
|
5081 terminal, where you might set wrapColumnOrWidth() to 80. |
|
5082 |
|
5083 \sa setWordWrap() wordWrap() |
|
5084 */ |
|
5085 |
|
5086 /*! |
|
5087 \property Q3TextEdit::wordWrap |
|
5088 \brief the word wrap mode |
|
5089 |
|
5090 The default mode is \c WidgetWidth which causes words to be |
|
5091 wrapped at the right edge of the text edit. Wrapping occurs at |
|
5092 whitespace, keeping whole words intact. If you want wrapping to |
|
5093 occur within words use setWrapPolicy(). If you set a wrap mode of |
|
5094 \c FixedPixelWidth or \c FixedColumnWidth you should also call |
|
5095 setWrapColumnOrWidth() with the width you want. |
|
5096 |
|
5097 \sa WordWrap, wrapColumnOrWidth, wrapPolicy, |
|
5098 */ |
|
5099 |
|
5100 void Q3TextEdit::setWordWrap(WordWrap mode) |
|
5101 { |
|
5102 if (wrapMode == mode) |
|
5103 return; |
|
5104 wrapMode = mode; |
|
5105 switch (mode) { |
|
5106 case NoWrap: |
|
5107 document()->formatter()->setWrapEnabled(false); |
|
5108 document()->formatter()->setWrapAtColumn(-1); |
|
5109 doc->setWidth(visibleWidth()); |
|
5110 doc->setMinimumWidth(-1); |
|
5111 doc->invalidate(); |
|
5112 updateContents(); |
|
5113 lastFormatted = doc->firstParagraph(); |
|
5114 interval = 0; |
|
5115 formatMore(); |
|
5116 break; |
|
5117 case WidgetWidth: |
|
5118 document()->formatter()->setWrapEnabled(true); |
|
5119 document()->formatter()->setWrapAtColumn(-1); |
|
5120 doResize(); |
|
5121 break; |
|
5122 case FixedPixelWidth: |
|
5123 document()->formatter()->setWrapEnabled(true); |
|
5124 document()->formatter()->setWrapAtColumn(-1); |
|
5125 if (wrapWidth < 0) |
|
5126 wrapWidth = 200; |
|
5127 setWrapColumnOrWidth(wrapWidth); |
|
5128 break; |
|
5129 case FixedColumnWidth: |
|
5130 if (wrapWidth < 0) |
|
5131 wrapWidth = 80; |
|
5132 document()->formatter()->setWrapEnabled(true); |
|
5133 document()->formatter()->setWrapAtColumn(wrapWidth); |
|
5134 setWrapColumnOrWidth(wrapWidth); |
|
5135 break; |
|
5136 } |
|
5137 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5138 checkOptimMode(); |
|
5139 #endif |
|
5140 } |
|
5141 |
|
5142 Q3TextEdit::WordWrap Q3TextEdit::wordWrap() const |
|
5143 { |
|
5144 return wrapMode; |
|
5145 } |
|
5146 |
|
5147 /*! |
|
5148 \property Q3TextEdit::wrapColumnOrWidth |
|
5149 \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped |
|
5150 |
|
5151 If the wrap mode is \c FixedPixelWidth, the value is the number of |
|
5152 pixels from the left edge of the text edit at which text should be |
|
5153 wrapped. If the wrap mode is \c FixedColumnWidth, the value is the |
|
5154 column number (in character columns) from the left edge of the |
|
5155 text edit at which text should be wrapped. |
|
5156 |
|
5157 \sa wordWrap |
|
5158 */ |
|
5159 void Q3TextEdit::setWrapColumnOrWidth(int value) |
|
5160 { |
|
5161 wrapWidth = value; |
|
5162 if (wrapMode == FixedColumnWidth) { |
|
5163 document()->formatter()->setWrapAtColumn(wrapWidth); |
|
5164 resizeContents(0, 0); |
|
5165 doc->setWidth(visibleWidth()); |
|
5166 doc->setMinimumWidth(-1); |
|
5167 } else if (wrapMode == FixedPixelWidth) { |
|
5168 document()->formatter()->setWrapAtColumn(-1); |
|
5169 resizeContents(wrapWidth, 0); |
|
5170 doc->setWidth(wrapWidth); |
|
5171 doc->setMinimumWidth(wrapWidth); |
|
5172 } else { |
|
5173 return; |
|
5174 } |
|
5175 doc->invalidate(); |
|
5176 updateContents(); |
|
5177 lastFormatted = doc->firstParagraph(); |
|
5178 interval = 0; |
|
5179 formatMore(); |
|
5180 } |
|
5181 |
|
5182 int Q3TextEdit::wrapColumnOrWidth() const |
|
5183 { |
|
5184 if (wrapMode == WidgetWidth) |
|
5185 return visibleWidth(); |
|
5186 return wrapWidth; |
|
5187 } |
|
5188 |
|
5189 |
|
5190 /*! |
|
5191 \enum Q3TextEdit::WrapPolicy |
|
5192 |
|
5193 This enum defines where text can be wrapped in word wrap mode. |
|
5194 |
|
5195 \value AtWhiteSpace Don't use this deprecated value (it is a |
|
5196 synonym for \c AtWordBoundary which you should use instead). |
|
5197 \value Anywhere Break anywhere, including within words. |
|
5198 \value AtWordBoundary Break lines at word boundaries, e.g. spaces or |
|
5199 newlines |
|
5200 \value AtWordOrDocumentBoundary Break lines at whitespace, e.g. |
|
5201 spaces or newlines if possible. Break it anywhere otherwise. |
|
5202 |
|
5203 \sa setWrapPolicy() |
|
5204 */ |
|
5205 |
|
5206 /*! |
|
5207 \property Q3TextEdit::wrapPolicy |
|
5208 \brief the word wrap policy, at whitespace or anywhere |
|
5209 |
|
5210 Defines where text can be wrapped when word wrap mode is not \c |
|
5211 NoWrap. The choices are \c AtWordBoundary (the default), \c |
|
5212 Anywhere and \c AtWordOrDocumentBoundary |
|
5213 |
|
5214 \sa wordWrap |
|
5215 */ |
|
5216 |
|
5217 void Q3TextEdit::setWrapPolicy(WrapPolicy policy) |
|
5218 { |
|
5219 if (wPolicy == policy) |
|
5220 return; |
|
5221 wPolicy = policy; |
|
5222 Q3TextFormatter *formatter; |
|
5223 if (policy == AtWordBoundary || policy == AtWordOrDocumentBoundary) { |
|
5224 formatter = new Q3TextFormatterBreakWords; |
|
5225 formatter->setAllowBreakInWords(policy == AtWordOrDocumentBoundary); |
|
5226 } else { |
|
5227 formatter = new Q3TextFormatterBreakInWords; |
|
5228 } |
|
5229 formatter->setWrapAtColumn(document()->formatter()->wrapAtColumn()); |
|
5230 formatter->setWrapEnabled(document()->formatter()->isWrapEnabled(0)); |
|
5231 document()->setFormatter(formatter); |
|
5232 doc->invalidate(); |
|
5233 updateContents(); |
|
5234 lastFormatted = doc->firstParagraph(); |
|
5235 interval = 0; |
|
5236 formatMore(); |
|
5237 } |
|
5238 |
|
5239 Q3TextEdit::WrapPolicy Q3TextEdit::wrapPolicy() const |
|
5240 { |
|
5241 return wPolicy; |
|
5242 } |
|
5243 |
|
5244 /*! |
|
5245 Deletes all the text in the text edit. |
|
5246 |
|
5247 \sa cut() removeSelectedText() setText() |
|
5248 */ |
|
5249 |
|
5250 void Q3TextEdit::clear() |
|
5251 { |
|
5252 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5253 if (d->optimMode) { |
|
5254 optimSetText(QLatin1String("")); |
|
5255 } else |
|
5256 #endif |
|
5257 { |
|
5258 // make clear undoable |
|
5259 doc->selectAll(Q3TextDocument::Temp); |
|
5260 removeSelectedText(Q3TextDocument::Temp); |
|
5261 setContentsPos(0, 0); |
|
5262 if (cursor->isValid()) |
|
5263 cursor->restoreState(); |
|
5264 doc->clear(true); |
|
5265 delete cursor; |
|
5266 cursor = new Q3TextCursor(doc); |
|
5267 lastFormatted = 0; |
|
5268 } |
|
5269 updateContents(); |
|
5270 |
|
5271 emit cursorPositionChanged(cursor); |
|
5272 emit cursorPositionChanged(cursor->paragraph()->paragId(), cursor->index()); |
|
5273 } |
|
5274 |
|
5275 int Q3TextEdit::undoDepth() const |
|
5276 { |
|
5277 return document()->undoDepth(); |
|
5278 } |
|
5279 |
|
5280 /*! |
|
5281 \property Q3TextEdit::length |
|
5282 \brief the number of characters in the text |
|
5283 */ |
|
5284 |
|
5285 int Q3TextEdit::length() const |
|
5286 { |
|
5287 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5288 if (d->optimMode) |
|
5289 return d->od->len; |
|
5290 else |
|
5291 #endif |
|
5292 return document()->length(); |
|
5293 } |
|
5294 |
|
5295 /*! |
|
5296 \property Q3TextEdit::tabStopWidth |
|
5297 \brief the tab stop width in pixels |
|
5298 */ |
|
5299 |
|
5300 int Q3TextEdit::tabStopWidth() const |
|
5301 { |
|
5302 return document()->tabStopWidth(); |
|
5303 } |
|
5304 |
|
5305 void Q3TextEdit::setUndoDepth(int d) |
|
5306 { |
|
5307 document()->setUndoDepth(d); |
|
5308 } |
|
5309 |
|
5310 void Q3TextEdit::setTabStopWidth(int ts) |
|
5311 { |
|
5312 document()->setTabStops(ts); |
|
5313 doc->invalidate(); |
|
5314 lastFormatted = doc->firstParagraph(); |
|
5315 interval = 0; |
|
5316 formatMore(); |
|
5317 updateContents(); |
|
5318 } |
|
5319 |
|
5320 /*! |
|
5321 \reimp |
|
5322 */ |
|
5323 |
|
5324 QSize Q3TextEdit::sizeHint() const |
|
5325 { |
|
5326 // cf. Q3ScrollView::sizeHint() |
|
5327 ensurePolished(); |
|
5328 int f = 2 * frameWidth(); |
|
5329 int h = fontMetrics().height(); |
|
5330 QSize sz(f, f); |
|
5331 return sz.expandedTo(QSize(12 * h, 8 * h)); |
|
5332 } |
|
5333 |
|
5334 void Q3TextEdit::clearUndoRedo() |
|
5335 { |
|
5336 if (!undoEnabled) |
|
5337 return; |
|
5338 undoRedoInfo.clear(); |
|
5339 emit undoAvailable(doc->commands()->isUndoAvailable()); |
|
5340 emit redoAvailable(doc->commands()->isRedoAvailable()); |
|
5341 } |
|
5342 |
|
5343 /*! \internal |
|
5344 \warning In Qt 3.1 we will provide a cleaer API for the |
|
5345 functionality which is provided by this function and in Qt 4.0 this |
|
5346 function will go away. |
|
5347 |
|
5348 This function gets the format of the character at position \a |
|
5349 index in paragraph \a para. Sets \a font to the character's font, \a |
|
5350 color to the character's color and \a verticalAlignment to the |
|
5351 character's vertical alignment. |
|
5352 |
|
5353 Returns false if \a para or \a index is out of range otherwise |
|
5354 returns true. |
|
5355 */ |
|
5356 |
|
5357 bool Q3TextEdit::getFormat(int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment) |
|
5358 { |
|
5359 if (!font || !color) |
|
5360 return false; |
|
5361 Q3TextParagraph *p = doc->paragAt(para); |
|
5362 if (!p) |
|
5363 return false; |
|
5364 if (index < 0 || index >= p->length()) |
|
5365 return false; |
|
5366 *font = p->at(index)->format()->font(); |
|
5367 *color = p->at(index)->format()->color(); |
|
5368 *verticalAlignment = (VerticalAlignment)p->at(index)->format()->vAlign(); |
|
5369 return true; |
|
5370 } |
|
5371 |
|
5372 /*! \internal |
|
5373 \warning In Qt 3.1 we will provide a cleaer API for the |
|
5374 functionality which is provided by this function and in Qt 4.0 this |
|
5375 function will go away. |
|
5376 |
|
5377 This function gets the format of the paragraph \a para. Sets \a |
|
5378 font to the paragraphs's font, \a color to the paragraph's color, \a |
|
5379 verticalAlignment to the paragraph's vertical alignment, \a |
|
5380 alignment to the paragraph's alignment, \a displayMode to the |
|
5381 paragraph's display mode, \a listStyle to the paragraph's list style |
|
5382 (if the display mode is Q3StyleSheetItem::DisplayListItem) and \a |
|
5383 listDepth to the depth of the list (if the display mode is |
|
5384 Q3StyleSheetItem::DisplayListItem). |
|
5385 |
|
5386 Returns false if \a para is out of range otherwise returns true. |
|
5387 */ |
|
5388 |
|
5389 bool Q3TextEdit::getParagraphFormat(int para, QFont *font, QColor *color, |
|
5390 VerticalAlignment *verticalAlignment, int *alignment, |
|
5391 Q3StyleSheetItem::DisplayMode *displayMode, |
|
5392 Q3StyleSheetItem::ListStyle *listStyle, |
|
5393 int *listDepth) |
|
5394 { |
|
5395 if (!font || !color || !alignment || !displayMode || !listStyle) |
|
5396 return false; |
|
5397 Q3TextParagraph *p = doc->paragAt(para); |
|
5398 if (!p) |
|
5399 return false; |
|
5400 *font = p->at(0)->format()->font(); |
|
5401 *color = p->at(0)->format()->color(); |
|
5402 *verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign(); |
|
5403 *alignment = p->alignment(); |
|
5404 *displayMode = p->isListItem() ? Q3StyleSheetItem::DisplayListItem : Q3StyleSheetItem::DisplayBlock; |
|
5405 *listStyle = p->listStyle(); |
|
5406 *listDepth = p->listDepth(); |
|
5407 return true; |
|
5408 } |
|
5409 |
|
5410 |
|
5411 |
|
5412 /*! |
|
5413 This function is called to create a right mouse button popup menu |
|
5414 at the document position \a pos. If you want to create a custom |
|
5415 popup menu, reimplement this function and return the created popup |
|
5416 menu. Ownership of the popup menu is transferred to the caller. |
|
5417 |
|
5418 \warning The QPopupMenu ID values 0-7 are reserved, and they map to the |
|
5419 standard operations. When inserting items into your custom popup menu, be |
|
5420 sure to specify ID values larger than 7. |
|
5421 */ |
|
5422 |
|
5423 Q3PopupMenu *Q3TextEdit::createPopupMenu(const QPoint& pos) |
|
5424 { |
|
5425 Q_UNUSED(pos) |
|
5426 #ifndef QT_NO_POPUPMENU |
|
5427 Q3PopupMenu *popup = new Q3PopupMenu(this, "qt_edit_menu"); |
|
5428 if (!isReadOnly()) { |
|
5429 d->id[IdUndo] = popup->insertItem(tr("&Undo") + ACCEL_KEY(Z)); |
|
5430 d->id[IdRedo] = popup->insertItem(tr("&Redo") + ACCEL_KEY(Y)); |
|
5431 popup->addSeparator(); |
|
5432 } |
|
5433 #ifndef QT_NO_CLIPBOARD |
|
5434 if (!isReadOnly()) |
|
5435 d->id[IdCut] = popup->insertItem(tr("Cu&t") + ACCEL_KEY(X)); |
|
5436 d->id[IdCopy] = popup->insertItem(tr("&Copy") + ACCEL_KEY(C)); |
|
5437 if (!isReadOnly()) |
|
5438 d->id[IdPaste] = popup->insertItem(tr("&Paste") + ACCEL_KEY(V)); |
|
5439 #endif |
|
5440 if (!isReadOnly()) { |
|
5441 d->id[IdClear] = popup->insertItem(tr("Clear")); |
|
5442 popup->addSeparator(); |
|
5443 } |
|
5444 #if defined(Q_WS_X11) |
|
5445 d->id[IdSelectAll] = popup->insertItem(tr("Select All")); |
|
5446 #else |
|
5447 d->id[IdSelectAll] = popup->insertItem(tr("Select All") + ACCEL_KEY(A)); |
|
5448 #endif |
|
5449 popup->setItemEnabled(d->id[IdUndo], !isReadOnly() && doc->commands()->isUndoAvailable()); |
|
5450 popup->setItemEnabled(d->id[IdRedo], !isReadOnly() && doc->commands()->isRedoAvailable()); |
|
5451 #ifndef QT_NO_CLIPBOARD |
|
5452 popup->setItemEnabled(d->id[IdCut], !isReadOnly() && doc->hasSelection(Q3TextDocument::Standard, true)); |
|
5453 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5454 popup->setItemEnabled(d->id[IdCopy], d->optimMode ? optimHasSelection() : doc->hasSelection(Q3TextDocument::Standard, true)); |
|
5455 #else |
|
5456 popup->setItemEnabled(d->id[IdCopy], doc->hasSelection(Q3TextDocument::Standard, true)); |
|
5457 #endif |
|
5458 popup->setItemEnabled(d->id[IdPaste], !isReadOnly() && !QApplication::clipboard()->text(d->clipboard_mode).isEmpty()); |
|
5459 #endif |
|
5460 const bool isEmptyDocument = (length() == 0); |
|
5461 popup->setItemEnabled(d->id[IdClear], !isReadOnly() && !isEmptyDocument); |
|
5462 popup->setItemEnabled(d->id[IdSelectAll], !isEmptyDocument); |
|
5463 return popup; |
|
5464 #else |
|
5465 return 0; |
|
5466 #endif |
|
5467 } |
|
5468 |
|
5469 /*! \overload |
|
5470 This function is called to create a right mouse button popup menu. |
|
5471 If you want to create a custom popup menu, reimplement this function |
|
5472 and return the created popup menu. Ownership of the popup menu is |
|
5473 transferred to the caller. |
|
5474 |
|
5475 This function is only called if createPopupMenu(const QPoint &) |
|
5476 returns 0. |
|
5477 */ |
|
5478 |
|
5479 Q3PopupMenu *Q3TextEdit::createPopupMenu() |
|
5480 { |
|
5481 return 0; |
|
5482 } |
|
5483 |
|
5484 /*! |
|
5485 \fn Q3TextEdit::zoomIn() |
|
5486 |
|
5487 \overload |
|
5488 |
|
5489 Zooms in on the text by making the base font size one point |
|
5490 larger and recalculating all font sizes to be the new size. This |
|
5491 does not change the size of any images. |
|
5492 |
|
5493 \sa zoomOut() |
|
5494 */ |
|
5495 |
|
5496 /*! |
|
5497 \fn Q3TextEdit::zoomOut() |
|
5498 |
|
5499 \overload |
|
5500 |
|
5501 Zooms out on the text by making the base font size one point |
|
5502 smaller and recalculating all font sizes to be the new size. This |
|
5503 does not change the size of any images. |
|
5504 |
|
5505 \sa zoomIn() |
|
5506 */ |
|
5507 |
|
5508 |
|
5509 /*! |
|
5510 Zooms in on the text by making the base font size \a range |
|
5511 points larger and recalculating all font sizes to be the new size. |
|
5512 This does not change the size of any images. |
|
5513 |
|
5514 \sa zoomOut() |
|
5515 */ |
|
5516 |
|
5517 void Q3TextEdit::zoomIn(int range) |
|
5518 { |
|
5519 QFont f(Q3ScrollView::font()); |
|
5520 f.setPointSize(f.pointSize() + range); |
|
5521 setFont(f); |
|
5522 } |
|
5523 |
|
5524 /*! |
|
5525 Zooms out on the text by making the base font size \a range points |
|
5526 smaller and recalculating all font sizes to be the new size. This |
|
5527 does not change the size of any images. |
|
5528 |
|
5529 \sa zoomIn() |
|
5530 */ |
|
5531 |
|
5532 void Q3TextEdit::zoomOut(int range) |
|
5533 { |
|
5534 QFont f(Q3ScrollView::font()); |
|
5535 f.setPointSize(qMax(1, f.pointSize() - range)); |
|
5536 setFont(f); |
|
5537 } |
|
5538 |
|
5539 /*! |
|
5540 Zooms the text by making the base font size \a size points and |
|
5541 recalculating all font sizes to be the new size. This does not |
|
5542 change the size of any images. |
|
5543 */ |
|
5544 |
|
5545 void Q3TextEdit::zoomTo(int size) |
|
5546 { |
|
5547 QFont f(Q3ScrollView::font()); |
|
5548 f.setPointSize(size); |
|
5549 setFont(f); |
|
5550 } |
|
5551 |
|
5552 /*! |
|
5553 Q3TextEdit is optimized for large amounts text. One of its |
|
5554 optimizations is to format only the visible text, formatting the rest |
|
5555 on demand, e.g. as the user scrolls, so you don't usually need to |
|
5556 call this function. |
|
5557 |
|
5558 In some situations you may want to force the whole text |
|
5559 to be formatted. For example, if after calling setText(), you wanted |
|
5560 to know the height of the document (using contentsHeight()), you |
|
5561 would call this function first. |
|
5562 */ |
|
5563 |
|
5564 void Q3TextEdit::sync() |
|
5565 { |
|
5566 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5567 if (d->optimMode) { |
|
5568 QFontMetrics fm(Q3ScrollView::font()); |
|
5569 resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); |
|
5570 } else |
|
5571 #endif |
|
5572 { |
|
5573 while (lastFormatted) { |
|
5574 lastFormatted->format(); |
|
5575 lastFormatted = lastFormatted->next(); |
|
5576 } |
|
5577 resizeContents(contentsWidth(), doc->height()); |
|
5578 } |
|
5579 updateScrollBars(); |
|
5580 } |
|
5581 |
|
5582 /*! |
|
5583 Sets the background color of selection number \a selNum to \a back |
|
5584 and specifies whether the text of this selection should be |
|
5585 inverted with \a invertText. |
|
5586 |
|
5587 This only works for \a selNum > 0. The default selection (\a |
|
5588 selNum == 0) gets its attributes from the text edit's |
|
5589 palette(). |
|
5590 */ |
|
5591 |
|
5592 void Q3TextEdit::setSelectionAttributes(int selNum, const QColor &back, bool invertText) |
|
5593 { |
|
5594 if (selNum < 1) |
|
5595 return; |
|
5596 if (selNum > doc->numSelections()) |
|
5597 doc->addSelection(selNum); |
|
5598 doc->setSelectionColor(selNum, back); |
|
5599 if (invertText) |
|
5600 doc->setSelectionTextColor(selNum, palette().color(QPalette::HighlightedText)); |
|
5601 } |
|
5602 |
|
5603 /*! |
|
5604 \reimp |
|
5605 */ |
|
5606 void Q3TextEdit::changeEvent(QEvent *ev) |
|
5607 { |
|
5608 if(ev->type() == QEvent::ActivationChange) { |
|
5609 if (!isActiveWindow() && scrollTimer) |
|
5610 scrollTimer->stop(); |
|
5611 if (!palette().isEqual(QPalette::Active, QPalette::Inactive)) |
|
5612 updateContents(); |
|
5613 } |
|
5614 |
|
5615 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5616 if (d->optimMode && (ev->type() == QEvent::ApplicationFontChange |
|
5617 || ev->type() == QEvent::FontChange)) { |
|
5618 QFont f = font(); |
|
5619 if (f.kerning()) |
|
5620 f.setKerning(false); |
|
5621 |
|
5622 setFont(f); |
|
5623 |
|
5624 Q3ScrollView::setFont(f); |
|
5625 doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color()); |
|
5626 // recalculate the max string width |
|
5627 QFontMetrics fm(f); |
|
5628 int i, sw; |
|
5629 d->od->maxLineWidth = 0; |
|
5630 for (i = 0; i < d->od->numLines; i++) { |
|
5631 sw = fm.width(d->od->lines[LOGOFFSET(i)]); |
|
5632 if (d->od->maxLineWidth < sw) |
|
5633 d->od->maxLineWidth = sw; |
|
5634 } |
|
5635 resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); |
|
5636 return; |
|
5637 } |
|
5638 #endif |
|
5639 |
|
5640 Q3ScrollView::changeEvent(ev); |
|
5641 |
|
5642 if (textFormat() == Qt::PlainText) { |
|
5643 if (ev->type() == QEvent::ApplicationPaletteChange || ev->type() == QEvent::PaletteChange |
|
5644 || ev->type() == QEvent::EnabledChange) { |
|
5645 Q3TextFormat *f = doc->formatCollection()->defaultFormat(); |
|
5646 f->setColor(palette().text().color()); |
|
5647 updateContents(); |
|
5648 } |
|
5649 } |
|
5650 |
|
5651 if (ev->type() == QEvent::ApplicationFontChange || ev->type() == QEvent::FontChange) { |
|
5652 QFont f = font(); |
|
5653 if (f.kerning()) |
|
5654 f.setKerning(false); |
|
5655 doc->setMinimumWidth(-1); |
|
5656 doc->setDefaultFormat(f, doc->formatCollection()->defaultFormat()->color()); |
|
5657 lastFormatted = doc->firstParagraph(); |
|
5658 formatMore(); |
|
5659 repaintChanged(); |
|
5660 } |
|
5661 } |
|
5662 |
|
5663 void Q3TextEdit::setReadOnly(bool b) |
|
5664 { |
|
5665 if (readonly == b) |
|
5666 return; |
|
5667 readonly = b; |
|
5668 d->cursorBlinkActive = !b; |
|
5669 #ifndef QT_NO_CURSOR |
|
5670 if (readonly) |
|
5671 viewport()->setCursor(Qt::ArrowCursor); |
|
5672 else |
|
5673 viewport()->setCursor(Qt::IBeamCursor); |
|
5674 setInputMethodEnabled(!readonly); |
|
5675 #endif |
|
5676 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5677 checkOptimMode(); |
|
5678 #endif |
|
5679 } |
|
5680 |
|
5681 /*! |
|
5682 Scrolls to the bottom of the document and does formatting if |
|
5683 required. |
|
5684 */ |
|
5685 |
|
5686 void Q3TextEdit::scrollToBottom() |
|
5687 { |
|
5688 sync(); |
|
5689 setContentsPos(contentsX(), contentsHeight() - visibleHeight()); |
|
5690 } |
|
5691 |
|
5692 /*! |
|
5693 Returns the rectangle of the paragraph \a para in contents |
|
5694 coordinates, or an invalid rectangle if \a para is out of range. |
|
5695 */ |
|
5696 |
|
5697 QRect Q3TextEdit::paragraphRect(int para) const |
|
5698 { |
|
5699 Q3TextEdit *that = (Q3TextEdit *)this; |
|
5700 that->sync(); |
|
5701 Q3TextParagraph *p = doc->paragAt(para); |
|
5702 if (!p) |
|
5703 return QRect(-1, -1, -1, -1); |
|
5704 return p->rect(); |
|
5705 } |
|
5706 |
|
5707 /*! |
|
5708 Returns the paragraph which is at position \a pos (in contents |
|
5709 coordinates). |
|
5710 */ |
|
5711 |
|
5712 int Q3TextEdit::paragraphAt(const QPoint &pos) const |
|
5713 { |
|
5714 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5715 if (d->optimMode) { |
|
5716 QFontMetrics fm(Q3ScrollView::font()); |
|
5717 int parag = pos.y() / fm.lineSpacing(); |
|
5718 if (parag <= d->od->numLines) |
|
5719 return parag; |
|
5720 else |
|
5721 return 0; |
|
5722 } |
|
5723 #endif |
|
5724 Q3TextCursor c(doc); |
|
5725 c.place(pos, doc->firstParagraph()); |
|
5726 if (c.paragraph()) |
|
5727 return c.paragraph()->paragId(); |
|
5728 return -1; // should never happen.. |
|
5729 } |
|
5730 |
|
5731 /*! |
|
5732 Returns the index of the character (relative to its paragraph) at |
|
5733 position \a pos (in contents coordinates). If \a para is not 0, |
|
5734 \c{*}\a{para} is set to the character's paragraph. |
|
5735 */ |
|
5736 |
|
5737 int Q3TextEdit::charAt(const QPoint &pos, int *para) const |
|
5738 { |
|
5739 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5740 if (d->optimMode) { |
|
5741 int par = paragraphAt(pos); |
|
5742 if (para) |
|
5743 *para = par; |
|
5744 return optimCharIndex(d->od->lines[LOGOFFSET(par)], pos.x()); |
|
5745 } |
|
5746 #endif |
|
5747 Q3TextCursor c(doc); |
|
5748 c.place(pos, doc->firstParagraph()); |
|
5749 if (c.paragraph()) { |
|
5750 if (para) |
|
5751 *para = c.paragraph()->paragId(); |
|
5752 return c.index(); |
|
5753 } |
|
5754 return -1; // should never happen.. |
|
5755 } |
|
5756 |
|
5757 /*! |
|
5758 Sets the background color of the paragraph \a para to \a bg. |
|
5759 */ |
|
5760 |
|
5761 void Q3TextEdit::setParagraphBackgroundColor(int para, const QColor &bg) |
|
5762 { |
|
5763 Q3TextParagraph *p = doc->paragAt(para); |
|
5764 if (!p) |
|
5765 return; |
|
5766 p->setBackgroundColor(bg); |
|
5767 repaintChanged(); |
|
5768 } |
|
5769 |
|
5770 /*! |
|
5771 Clears the background color of the paragraph \a para, so that the |
|
5772 default color is used again. |
|
5773 */ |
|
5774 |
|
5775 void Q3TextEdit::clearParagraphBackground(int para) |
|
5776 { |
|
5777 Q3TextParagraph *p = doc->paragAt(para); |
|
5778 if (!p) |
|
5779 return; |
|
5780 p->clearBackgroundColor(); |
|
5781 repaintChanged(); |
|
5782 } |
|
5783 |
|
5784 /*! |
|
5785 Returns the background color of the paragraph \a para or an |
|
5786 invalid color if \a para is out of range or the paragraph has no |
|
5787 background set |
|
5788 */ |
|
5789 |
|
5790 QColor Q3TextEdit::paragraphBackgroundColor(int para) const |
|
5791 { |
|
5792 Q3TextParagraph *p = doc->paragAt(para); |
|
5793 if (!p) |
|
5794 return QColor(); |
|
5795 QColor *c = p->backgroundColor(); |
|
5796 if (c) |
|
5797 return *c; |
|
5798 return QColor(); |
|
5799 } |
|
5800 |
|
5801 /*! |
|
5802 \property Q3TextEdit::undoRedoEnabled |
|
5803 \brief whether undo/redo is enabled |
|
5804 |
|
5805 When changing this property, the undo/redo history is cleared. |
|
5806 |
|
5807 The default is true. |
|
5808 */ |
|
5809 |
|
5810 void Q3TextEdit::setUndoRedoEnabled(bool b) |
|
5811 { |
|
5812 undoRedoInfo.clear(); |
|
5813 doc->commands()->clear(); |
|
5814 |
|
5815 undoEnabled = b; |
|
5816 } |
|
5817 |
|
5818 bool Q3TextEdit::isUndoRedoEnabled() const |
|
5819 { |
|
5820 return undoEnabled; |
|
5821 } |
|
5822 |
|
5823 /*! |
|
5824 Returns true if undo is available; otherwise returns false. |
|
5825 */ |
|
5826 |
|
5827 bool Q3TextEdit::isUndoAvailable() const |
|
5828 { |
|
5829 return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid()); |
|
5830 } |
|
5831 |
|
5832 /*! |
|
5833 Returns true if redo is available; otherwise returns false. |
|
5834 */ |
|
5835 |
|
5836 bool Q3TextEdit::isRedoAvailable() const |
|
5837 { |
|
5838 return undoEnabled && doc->commands()->isRedoAvailable(); |
|
5839 } |
|
5840 |
|
5841 void Q3TextEdit::ensureFormatted(Q3TextParagraph *p) |
|
5842 { |
|
5843 while (!p->isValid()) { |
|
5844 if (!lastFormatted) |
|
5845 return; |
|
5846 formatMore(); |
|
5847 } |
|
5848 } |
|
5849 |
|
5850 /*! \internal */ |
|
5851 void Q3TextEdit::updateCursor(const QPoint & pos) |
|
5852 { |
|
5853 if (isReadOnly() && linksEnabled()) { |
|
5854 Q3TextCursor c = *cursor; |
|
5855 placeCursor(pos, &c, true); |
|
5856 |
|
5857 #ifndef QT_NO_NETWORKPROTOCOL |
|
5858 bool insideParagRect = true; |
|
5859 if (c.paragraph() == doc->lastParagraph() |
|
5860 && c.paragraph()->rect().y() + c.paragraph()->rect().height() < pos.y()) |
|
5861 insideParagRect = false; |
|
5862 if (insideParagRect && c.paragraph() && c.paragraph()->at(c.index()) && |
|
5863 c.paragraph()->at(c.index())->isAnchor()) { |
|
5864 if (!c.paragraph()->at(c.index())->anchorHref().isEmpty() |
|
5865 && c.index() < c.paragraph()->length() - 1) |
|
5866 onLink = c.paragraph()->at(c.index())->anchorHref(); |
|
5867 else |
|
5868 onLink.clear(); |
|
5869 |
|
5870 if (!c.paragraph()->at(c.index())->anchorName().isEmpty() |
|
5871 && c.index() < c.paragraph()->length() - 1) |
|
5872 d->onName = c.paragraph()->at(c.index())->anchorName(); |
|
5873 else |
|
5874 d->onName.clear(); |
|
5875 |
|
5876 if (!c.paragraph()->at(c.index())->anchorHref().isEmpty()) { |
|
5877 #ifndef QT_NO_CURSOR |
|
5878 viewport()->setCursor(onLink.isEmpty() ? Qt::ArrowCursor : Qt::PointingHandCursor); |
|
5879 #endif |
|
5880 QUrl u = QUrl(doc->context()).resolved(onLink); |
|
5881 emitHighlighted(u.toString(QUrl::None)); |
|
5882 } |
|
5883 } else { |
|
5884 #ifndef QT_NO_CURSOR |
|
5885 viewport()->setCursor(isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor); |
|
5886 #endif |
|
5887 onLink.clear(); |
|
5888 emitHighlighted(QString()); |
|
5889 } |
|
5890 #endif |
|
5891 } |
|
5892 } |
|
5893 |
|
5894 /*! |
|
5895 Places the cursor \a c at the character which is closest to position |
|
5896 \a pos (in contents coordinates). If \a c is 0, the default text |
|
5897 cursor is used. |
|
5898 |
|
5899 \sa setCursorPosition() |
|
5900 */ |
|
5901 void Q3TextEdit::placeCursor(const QPoint &pos, Q3TextCursor *c) |
|
5902 { |
|
5903 placeCursor(pos, c, false); |
|
5904 } |
|
5905 |
|
5906 /*! \internal */ |
|
5907 void Q3TextEdit::clipboardChanged() |
|
5908 { |
|
5909 #ifndef QT_NO_CLIPBOARD |
|
5910 // don't listen to selection changes |
|
5911 disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); |
|
5912 #endif |
|
5913 selectAll(false); |
|
5914 } |
|
5915 |
|
5916 /*! \property Q3TextEdit::tabChangesFocus |
|
5917 \brief whether TAB changes focus or is accepted as input |
|
5918 |
|
5919 In some occasions text edits should not allow the user to input |
|
5920 tabulators or change indentation using the TAB key, as this breaks |
|
5921 the focus chain. The default is false. |
|
5922 |
|
5923 */ |
|
5924 |
|
5925 void Q3TextEdit::setTabChangesFocus(bool b) |
|
5926 { |
|
5927 d->tabChangesFocus = b; |
|
5928 } |
|
5929 |
|
5930 bool Q3TextEdit::tabChangesFocus() const |
|
5931 { |
|
5932 return d->tabChangesFocus; |
|
5933 } |
|
5934 |
|
5935 #ifdef QT_TEXTEDIT_OPTIMIZATION |
|
5936 /* Implementation of optimized Qt::LogText mode follows */ |
|
5937 |
|
5938 static void qSwap(int * a, int * b) |
|
5939 { |
|
5940 if (!a || !b) |
|
5941 return; |
|
5942 int tmp = *a; |
|
5943 *a = *b; |
|
5944 *b = tmp; |
|
5945 } |
|
5946 |
|
5947 /*! \internal */ |
|
5948 bool Q3TextEdit::checkOptimMode() |
|
5949 { |
|
5950 bool oldMode = d->optimMode; |
|
5951 if (textFormat() == Qt::LogText) { |
|
5952 d->optimMode = true; |
|
5953 setReadOnly(true); |
|
5954 } else { |
|
5955 d->optimMode = false; |
|
5956 } |
|
5957 |
|
5958 // when changing mode - try to keep selections and text |
|
5959 if (oldMode != d->optimMode) { |
|
5960 if (d->optimMode) { |
|
5961 d->od = new Q3TextEditOptimPrivate; |
|
5962 connect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll())); |
|
5963 disconnect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int))); |
|
5964 disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone())); |
|
5965 disconnect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore())); |
|
5966 optimSetText(doc->originalText()); |
|
5967 doc->clear(true); |
|
5968 delete cursor; |
|
5969 cursor = new Q3TextCursor(doc); |
|
5970 } else { |
|
5971 disconnect(scrollTimer, SIGNAL(timeout()), this, SLOT(optimDoAutoScroll())); |
|
5972 connect(doc, SIGNAL(minimumWidthChanged(int)), this, SLOT(documentWidthChanged(int))); |
|
5973 connect(scrollTimer, SIGNAL(timeout()), this, SLOT(autoScrollTimerDone())); |
|
5974 connect(formatTimer, SIGNAL(timeout()), this, SLOT(formatMore())); |
|
5975 setText(optimText()); |
|
5976 delete d->od; |
|
5977 d->od = 0; |
|
5978 } |
|
5979 } |
|
5980 return d->optimMode; |
|
5981 } |
|
5982 |
|
5983 /*! \internal */ |
|
5984 QString Q3TextEdit::optimText() const |
|
5985 { |
|
5986 QString str, tmp; |
|
5987 |
|
5988 if (d->od->len == 0) |
|
5989 return str; |
|
5990 |
|
5991 // concatenate all strings |
|
5992 int i; |
|
5993 int offset; |
|
5994 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it; |
|
5995 Q3TextEditOptimPrivate::Tag * ftag = 0; |
|
5996 for (i = 0; i < d->od->numLines; i++) { |
|
5997 if (d->od->lines[LOGOFFSET(i)].isEmpty()) { // CR lines are empty |
|
5998 str += QLatin1Char('\n'); |
|
5999 } else { |
|
6000 tmp = d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n'); |
|
6001 // inject the tags for this line |
|
6002 if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd()) |
|
6003 ftag = it.value(); |
|
6004 offset = 0; |
|
6005 while (ftag && ftag->line == i) { |
|
6006 tmp.insert(ftag->index + offset, QLatin1Char('<') + ftag->tag + QLatin1Char('>')); |
|
6007 offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars |
|
6008 ftag = ftag->next; |
|
6009 } |
|
6010 str += tmp; |
|
6011 } |
|
6012 } |
|
6013 return str; |
|
6014 } |
|
6015 |
|
6016 /*! \internal */ |
|
6017 void Q3TextEdit::optimSetText(const QString &str) |
|
6018 { |
|
6019 optimRemoveSelection(); |
|
6020 // this is just too slow - but may have to go in due to compatibility reasons |
|
6021 // if (str == optimText()) |
|
6022 // return; |
|
6023 d->od->numLines = 0; |
|
6024 d->od->lines.clear(); |
|
6025 d->od->maxLineWidth = 0; |
|
6026 d->od->len = 0; |
|
6027 d->od->clearTags(); |
|
6028 QFontMetrics fm(Q3ScrollView::font()); |
|
6029 if (!(str.isEmpty() || str.isNull() || d->maxLogLines == 0)) { |
|
6030 QStringList strl = str.split(QLatin1Char('\n')); |
|
6031 int lWidth = 0; |
|
6032 for (QStringList::Iterator it = strl.begin(); it != strl.end(); ++it) { |
|
6033 optimParseTags(&*it); |
|
6034 optimCheckLimit(*it); |
|
6035 lWidth = fm.width(*it); |
|
6036 if (lWidth > d->od->maxLineWidth) |
|
6037 d->od->maxLineWidth = lWidth; |
|
6038 } |
|
6039 } |
|
6040 resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); |
|
6041 repaintContents(); |
|
6042 emit textChanged(); |
|
6043 } |
|
6044 |
|
6045 /*! \internal |
|
6046 |
|
6047 Append \a tag to the tag list. |
|
6048 */ |
|
6049 Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimAppendTag(int index, const QString & tag) |
|
6050 { |
|
6051 Q3TextEditOptimPrivate::Tag * t = new Q3TextEditOptimPrivate::Tag, * tmp; |
|
6052 |
|
6053 if (d->od->tags == 0) |
|
6054 d->od->tags = t; |
|
6055 t->bold = t->italic = t->underline = false; |
|
6056 t->line = d->od->numLines; |
|
6057 t->index = index; |
|
6058 t->tag = tag; |
|
6059 t->leftTag = 0; |
|
6060 t->parent = 0; |
|
6061 t->prev = d->od->lastTag; |
|
6062 if (d->od->lastTag) |
|
6063 d->od->lastTag->next = t; |
|
6064 t->next = 0; |
|
6065 d->od->lastTag = t; |
|
6066 tmp = d->od->tagIndex[LOGOFFSET(t->line)]; |
|
6067 if (!tmp || (tmp && tmp->index > t->index)) { |
|
6068 d->od->tagIndex.insert(LOGOFFSET(t->line), t); |
|
6069 } |
|
6070 return t; |
|
6071 } |
|
6072 |
|
6073 /*! \internal |
|
6074 |
|
6075 Insert \a tag in the tag - according to line and index numbers |
|
6076 */ |
|
6077 Q3TextEditOptimPrivate::Tag *Q3TextEdit::optimInsertTag(int line, int index, const QString &tag) |
|
6078 { |
|
6079 Q3TextEditOptimPrivate::Tag *t = new Q3TextEditOptimPrivate::Tag, *tmp; |
|
6080 |
|
6081 if (d->od->tags == 0) |
|
6082 d->od->tags = t; |
|
6083 t->bold = t->italic = t->underline = false; |
|
6084 t->line = line; |
|
6085 t->index = index; |
|
6086 t->tag = tag; |
|
6087 t->leftTag = 0; |
|
6088 t->parent = 0; |
|
6089 t->next = 0; |
|
6090 t->prev = 0; |
|
6091 |
|
6092 // find insertion pt. in tag struct. |
|
6093 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it; |
|
6094 if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) { |
|
6095 tmp = *it; |
|
6096 if (tmp->index >= index) { // the existing tag may be placed AFTER the one we want to insert |
|
6097 tmp = tmp->prev; |
|
6098 } else { |
|
6099 while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index) |
|
6100 tmp = tmp->next; |
|
6101 } |
|
6102 } else { |
|
6103 tmp = d->od->tags; |
|
6104 while (tmp && tmp->next && tmp->next->line < line) |
|
6105 tmp = tmp->next; |
|
6106 if (tmp == d->od->tags) |
|
6107 tmp = 0; |
|
6108 } |
|
6109 |
|
6110 t->prev = tmp; |
|
6111 t->next = tmp ? tmp->next : 0; |
|
6112 if (t->next) |
|
6113 t->next->prev = t; |
|
6114 if (tmp) |
|
6115 tmp->next = t; |
|
6116 |
|
6117 tmp = d->od->tagIndex[LOGOFFSET(t->line)]; |
|
6118 if (!tmp || (tmp && tmp->index >= t->index)) { |
|
6119 d->od->tagIndex.insert(LOGOFFSET(t->line), t); |
|
6120 } |
|
6121 return t; |
|
6122 } |
|
6123 |
|
6124 /*! \internal |
|
6125 |
|
6126 Find tags in \a line, remove them from \a line and put them in a |
|
6127 structure. |
|
6128 |
|
6129 A tag is delimited by '<' and '>'. The characters '<', '>' and '&' |
|
6130 are escaped by using '<', '>' and '&'. Left-tags marks |
|
6131 the starting point for formatting, while right-tags mark the ending |
|
6132 point. A right-tag is the same as a left-tag, but with a '/' |
|
6133 appearing before the tag keyword. E.g a valid left-tag: <b>, and |
|
6134 a valid right-tag: </b>. Tags can be nested, but they have to be |
|
6135 closed in the same order as they are opened. E.g: |
|
6136 <font color=red><font color=blue>blue</font>red</font> - is valid, while: |
|
6137 <font color=red><b>bold red</font> just bold</b> - is invalid since the font tag is |
|
6138 closed before the bold tag. Note that a tag does not have to be |
|
6139 closed: '<font color=blue>Lots of text - and then some..' is perfectly valid for |
|
6140 setting all text appearing after the tag to blue. A tag can be used |
|
6141 to change the color of a piece of text, or set one of the following |
|
6142 formatting attributes: bold, italic and underline. These attributes |
|
6143 are set using the <b>, <i> and <u> tags. Example of valid tags: |
|
6144 <font color=red>, </font>, <b>, <u>, <i>, </i>. |
|
6145 Example of valid text: |
|
6146 This is some <font color=red>red text</font>, while this is some <font color=green>green |
|
6147 text</font>. <font color=blue><font color=yellow>This is yellow</font>, while this is |
|
6148 blue.</font> |
|
6149 |
|
6150 Note that only the color attribute of the HTML font tag is supported. |
|
6151 |
|
6152 Limitations: |
|
6153 1. A tag cannot span several lines. |
|
6154 2. Very limited error checking - mismatching left/right-tags is the |
|
6155 only thing that is detected. |
|
6156 |
|
6157 */ |
|
6158 void Q3TextEdit::optimParseTags(QString * line, int lineNo, int indexOffset) |
|
6159 { |
|
6160 int len = line->length(); |
|
6161 int i, startIndex = -1, endIndex = -1, escIndex = -1; |
|
6162 int state = 0; // 0 = outside tag, 1 = inside tag |
|
6163 bool tagOpen, tagClose; |
|
6164 int bold = 0, italic = 0, underline = 0; |
|
6165 QString tagStr; |
|
6166 QStack<Q3TextEditOptimPrivate::Tag *> tagStack; |
|
6167 |
|
6168 for (i = 0; i < len; i++) { |
|
6169 tagOpen = (*line)[i] == QLatin1Char('<'); |
|
6170 tagClose = (*line)[i] == QLatin1Char('>'); |
|
6171 |
|
6172 // handle '<' and '>' and '&' |
|
6173 if ((*line)[i] == QLatin1Char('&')) { |
|
6174 escIndex = i; |
|
6175 continue; |
|
6176 } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) { |
|
6177 QString esc = line->mid(escIndex, i - escIndex + 1); |
|
6178 QString c; |
|
6179 if (esc == QLatin1String("<")) |
|
6180 c = QLatin1Char('<'); |
|
6181 else if (esc == QLatin1String(">")) |
|
6182 c = QLatin1Char('>'); |
|
6183 else if (esc == QLatin1String("&")) |
|
6184 c = QLatin1Char('&'); |
|
6185 line->replace(escIndex, i - escIndex + 1, c); |
|
6186 len = line->length(); |
|
6187 i -= i-escIndex; |
|
6188 escIndex = -1; |
|
6189 continue; |
|
6190 } |
|
6191 |
|
6192 if (state == 0 && tagOpen) { |
|
6193 state = 1; |
|
6194 startIndex = i; |
|
6195 continue; |
|
6196 } |
|
6197 if (state == 1 && tagClose) { |
|
6198 state = 0; |
|
6199 endIndex = i; |
|
6200 if (!tagStr.isEmpty()) { |
|
6201 Q3TextEditOptimPrivate::Tag * tag, * cur, * tmp; |
|
6202 bool format = true; |
|
6203 |
|
6204 if (tagStr == QLatin1String("b")) |
|
6205 bold++; |
|
6206 else if (tagStr == QLatin1String("/b")) |
|
6207 bold--; |
|
6208 else if (tagStr == QLatin1String("i")) |
|
6209 italic++; |
|
6210 else if (tagStr == QLatin1String("/i")) |
|
6211 italic--; |
|
6212 else if (tagStr == QLatin1String("u")) |
|
6213 underline++; |
|
6214 else if (tagStr == QLatin1String("/u")) |
|
6215 underline--; |
|
6216 else |
|
6217 format = false; |
|
6218 if (lineNo > -1) |
|
6219 tag = optimInsertTag(lineNo, startIndex + indexOffset, tagStr); |
|
6220 else |
|
6221 tag = optimAppendTag(startIndex, tagStr); |
|
6222 // everything that is not a b, u or i tag is considered |
|
6223 // to be a color tag. |
|
6224 tag->type = format ? Q3TextEditOptimPrivate::Format |
|
6225 : Q3TextEditOptimPrivate::Color; |
|
6226 if (tagStr[0] == QLatin1Char('/')) { |
|
6227 // this is a right-tag - search for the left-tag |
|
6228 // and possible parent tag |
|
6229 cur = tag->prev; |
|
6230 if (!cur) { |
|
6231 qWarning("Q3TextEdit::optimParseTags: no left-tag for '<%s>' in line %d.", |
|
6232 tag->tag.latin1(), tag->line + 1); |
|
6233 return; // something is wrong - give up |
|
6234 } |
|
6235 while (cur) { |
|
6236 if (cur->leftTag) { // push right-tags encountered |
|
6237 tagStack.push(cur); |
|
6238 } else { |
|
6239 tmp = tagStack.isEmpty() ? 0 : tagStack.pop(); |
|
6240 if (!tmp) { |
|
6241 if (((QLatin1Char('/') + cur->tag) == tag->tag) || |
|
6242 (tag->tag == QLatin1String("/font") && cur->tag.left(4) == QLatin1String("font"))) { |
|
6243 // set up the left and parent of this tag |
|
6244 tag->leftTag = cur; |
|
6245 tmp = cur->prev; |
|
6246 if (tmp && tmp->parent) { |
|
6247 tag->parent = tmp->parent; |
|
6248 } else if (tmp && !tmp->leftTag) { |
|
6249 tag->parent = tmp; |
|
6250 } |
|
6251 break; |
|
6252 } else if (!cur->leftTag) { |
|
6253 qWarning("Q3TextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.", |
|
6254 qPrintable(QString(cur->tag[0] == QLatin1Char('/') ? QLatin1String("left") : QLatin1String("right"))), |
|
6255 cur->tag.latin1(), cur->line + 1); |
|
6256 return; // something is amiss - give up |
|
6257 } |
|
6258 } |
|
6259 } |
|
6260 cur = cur->prev; |
|
6261 } |
|
6262 } else { |
|
6263 tag->bold = bold > 0; |
|
6264 tag->italic = italic > 0; |
|
6265 tag->underline = underline > 0; |
|
6266 tmp = tag->prev; |
|
6267 while (tmp && tmp->leftTag) { |
|
6268 tmp = tmp->leftTag->parent; |
|
6269 } |
|
6270 if (tmp) { |
|
6271 tag->bold |= tmp->bold; |
|
6272 tag->italic |= tmp->italic; |
|
6273 tag->underline |= tmp->underline; |
|
6274 } |
|
6275 } |
|
6276 } |
|
6277 if (startIndex != -1) { |
|
6278 int l = (endIndex == -1) ? |
|
6279 line->length() - startIndex : endIndex - startIndex; |
|
6280 line->remove(startIndex, l+1); |
|
6281 len = line->length(); |
|
6282 i -= l+1; |
|
6283 } |
|
6284 tagStr = QLatin1String(""); |
|
6285 continue; |
|
6286 } |
|
6287 |
|
6288 if (state == 1) { |
|
6289 tagStr += (*line)[i]; |
|
6290 } |
|
6291 } |
|
6292 } |
|
6293 |
|
6294 // calculate the width of a string in pixels inc. tabs |
|
6295 static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm) |
|
6296 { |
|
6297 int tabs = str.count(QLatin1Char('\t')); |
|
6298 |
|
6299 if (!tabs) |
|
6300 return fm.width(str); |
|
6301 |
|
6302 int newIdx = 0; |
|
6303 int lastIdx = 0; |
|
6304 int strWidth = 0; |
|
6305 int tn; |
|
6306 for (tn = 1; tn <= tabs; ++tn) { |
|
6307 newIdx = str.indexOf(QLatin1Char('\t'), newIdx); |
|
6308 strWidth += fm.width(str.mid(lastIdx, newIdx - lastIdx)); |
|
6309 if (strWidth >= tn * tabWidth) { |
|
6310 int u = tn; |
|
6311 while (strWidth >= u * tabWidth) |
|
6312 ++u; |
|
6313 strWidth = u * tabWidth; |
|
6314 } else { |
|
6315 strWidth = tn * tabWidth; |
|
6316 } |
|
6317 lastIdx = ++newIdx; |
|
6318 } |
|
6319 if ((int)str.length() > newIdx) |
|
6320 strWidth += fm.width(str.mid(newIdx)); |
|
6321 return strWidth; |
|
6322 } |
|
6323 |
|
6324 bool Q3TextEdit::optimHasBoldMetrics(int line) |
|
6325 { |
|
6326 Q3TextEditOptimPrivate::Tag *t; |
|
6327 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it; |
|
6328 if ((it = d->od->tagIndex.constFind(line)) != d->od->tagIndex.constEnd()) { |
|
6329 t = *it; |
|
6330 while (t && t->line == line) { |
|
6331 if (t->bold) |
|
6332 return true; |
|
6333 t = t->next; |
|
6334 } |
|
6335 } else if ((t = optimPreviousLeftTag(line)) && t->bold) { |
|
6336 return true; |
|
6337 } |
|
6338 return false; |
|
6339 } |
|
6340 |
|
6341 /*! \internal |
|
6342 |
|
6343 Append \a str to the current text buffer. Parses each line to find |
|
6344 formatting tags. |
|
6345 */ |
|
6346 void Q3TextEdit::optimAppend(const QString &str) |
|
6347 { |
|
6348 if (str.isEmpty() || str.isNull() || d->maxLogLines == 0) |
|
6349 return; |
|
6350 |
|
6351 QStringList strl = str.split(QLatin1Char('\n')); |
|
6352 QStringList::Iterator it = strl.begin(); |
|
6353 |
|
6354 QFontMetrics fm(Q3ScrollView::font()); |
|
6355 int lWidth = 0; |
|
6356 for (; it != strl.end(); ++it) { |
|
6357 optimParseTags(&*it); |
|
6358 optimCheckLimit(*it); |
|
6359 if (optimHasBoldMetrics(d->od->numLines-1)) { |
|
6360 QFont fnt = Q3ScrollView::font(); |
|
6361 fnt.setBold(true); |
|
6362 fm = QFontMetrics(fnt); |
|
6363 } |
|
6364 lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4; |
|
6365 if (lWidth > d->od->maxLineWidth) |
|
6366 d->od->maxLineWidth = lWidth; |
|
6367 } |
|
6368 bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight(); |
|
6369 resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); |
|
6370 if (scrollToEnd) { |
|
6371 updateScrollBars(); |
|
6372 ensureVisible(contentsX(), contentsHeight(), 0, 0); |
|
6373 } |
|
6374 // when a max log size is set, the text may not be redrawn because |
|
6375 // the size of the viewport may not have changed |
|
6376 if (d->maxLogLines > -1) |
|
6377 viewport()->update(); |
|
6378 emit textChanged(); |
|
6379 } |
|
6380 |
|
6381 static void qStripTags(QString *line) |
|
6382 { |
|
6383 int len = line->length(); |
|
6384 int i, startIndex = -1, endIndex = -1, escIndex = -1; |
|
6385 int state = 0; // 0 = outside tag, 1 = inside tag |
|
6386 bool tagOpen, tagClose; |
|
6387 |
|
6388 for (i = 0; i < len; i++) { |
|
6389 tagOpen = (*line)[i] == QLatin1Char('<'); |
|
6390 tagClose = (*line)[i] == QLatin1Char('>'); |
|
6391 |
|
6392 // handle '<' and '>' and '&' |
|
6393 if ((*line)[i] == QLatin1Char('&')) { |
|
6394 escIndex = i; |
|
6395 continue; |
|
6396 } else if (escIndex != -1 && (*line)[i] == QLatin1Char(';')) { |
|
6397 QString esc = line->mid(escIndex, i - escIndex + 1); |
|
6398 QString c; |
|
6399 if (esc == QLatin1String("<")) |
|
6400 c = QLatin1Char('<'); |
|
6401 else if (esc == QLatin1String(">")) |
|
6402 c = QLatin1Char('>'); |
|
6403 else if (esc == QLatin1String("&")) |
|
6404 c = QLatin1Char('&'); |
|
6405 line->replace(escIndex, i - escIndex + 1, c); |
|
6406 len = line->length(); |
|
6407 i -= i-escIndex; |
|
6408 escIndex = -1; |
|
6409 continue; |
|
6410 } |
|
6411 |
|
6412 if (state == 0 && tagOpen) { |
|
6413 state = 1; |
|
6414 startIndex = i; |
|
6415 continue; |
|
6416 } |
|
6417 if (state == 1 && tagClose) { |
|
6418 state = 0; |
|
6419 endIndex = i; |
|
6420 if (startIndex != -1) { |
|
6421 int l = (endIndex == -1) ? |
|
6422 line->length() - startIndex : endIndex - startIndex; |
|
6423 line->remove(startIndex, l+1); |
|
6424 len = line->length(); |
|
6425 i -= l+1; |
|
6426 } |
|
6427 continue; |
|
6428 } |
|
6429 } |
|
6430 } |
|
6431 |
|
6432 /*! \internal |
|
6433 |
|
6434 Inserts the text into \a line at index \a index. |
|
6435 */ |
|
6436 |
|
6437 void Q3TextEdit::optimInsert(const QString& text, int line, int index) |
|
6438 { |
|
6439 if (text.isEmpty() || d->maxLogLines == 0) |
|
6440 return; |
|
6441 if (line < 0) |
|
6442 line = 0; |
|
6443 if (line > d->od->numLines-1) |
|
6444 line = d->od->numLines-1; |
|
6445 if (index < 0) |
|
6446 index = 0; |
|
6447 if (index > d->od->lines[line].length()) |
|
6448 index = d->od->lines[line].length(); |
|
6449 |
|
6450 QStringList strl = text.split(QLatin1Char('\n')); |
|
6451 int numNewLines = strl.count() - 1; |
|
6452 Q3TextEditOptimPrivate::Tag *tag = 0; |
|
6453 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator ii; |
|
6454 int x; |
|
6455 |
|
6456 if (numNewLines == 0) { |
|
6457 // Case 1. Fast single line case - just inject it! |
|
6458 QString stripped = text; |
|
6459 qStripTags(&stripped); |
|
6460 d->od->lines[LOGOFFSET(line)].insert(index, stripped); |
|
6461 // move the tag indices following the insertion pt. |
|
6462 if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) { |
|
6463 tag = *ii; |
|
6464 while (tag && (LOGOFFSET(tag->line) == line && tag->index < index)) |
|
6465 tag = tag->next; |
|
6466 while (tag && (LOGOFFSET(tag->line) == line)) { |
|
6467 tag->index += stripped.length(); |
|
6468 tag = tag->next; |
|
6469 } |
|
6470 } |
|
6471 stripped = text; |
|
6472 optimParseTags(&stripped, line, index); |
|
6473 } else if (numNewLines > 0) { |
|
6474 // Case 2. We have at least 1 newline char - split at |
|
6475 // insertion pt. and make room for new lines - complex and slow! |
|
6476 QString left = d->od->lines[LOGOFFSET(line)].left(index); |
|
6477 QString right = d->od->lines[LOGOFFSET(line)].mid(index); |
|
6478 |
|
6479 // rearrange lines for insertion |
|
6480 for (x = d->od->numLines - 1; x > line; x--) |
|
6481 d->od->lines[x + numNewLines] = d->od->lines[x]; |
|
6482 d->od->numLines += numNewLines; |
|
6483 |
|
6484 // fix the tag index and the tag line/index numbers - this |
|
6485 // might take a while.. |
|
6486 for (x = line; x < d->od->numLines; x++) { |
|
6487 if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) { |
|
6488 tag = ii.value(); |
|
6489 if (LOGOFFSET(tag->line) == line) |
|
6490 while (tag && (LOGOFFSET(tag->line) == line && tag->index < index)) |
|
6491 tag = tag->next; |
|
6492 } |
|
6493 } |
|
6494 |
|
6495 // relabel affected tags with new line numbers and new index |
|
6496 // positions |
|
6497 while (tag) { |
|
6498 if (LOGOFFSET(tag->line) == line) |
|
6499 tag->index -= index; |
|
6500 tag->line += numNewLines; |
|
6501 tag = tag->next; |
|
6502 } |
|
6503 |
|
6504 // generate a new tag index |
|
6505 d->od->tagIndex.clear(); |
|
6506 tag = d->od->tags; |
|
6507 while (tag) { |
|
6508 if (!((ii = d->od->tagIndex.constFind(LOGOFFSET(tag->line))) != d->od->tagIndex.constEnd())) |
|
6509 d->od->tagIndex[LOGOFFSET(tag->line)] = tag; |
|
6510 tag = tag->next; |
|
6511 } |
|
6512 |
|
6513 // update the tag indices on the spliced line - needs to be done before new tags are added |
|
6514 QString stripped = strl[strl.count() - 1]; |
|
6515 qStripTags(&stripped); |
|
6516 if ((ii = d->od->tagIndex.constFind(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.constEnd()) { |
|
6517 tag = *ii; |
|
6518 while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) { |
|
6519 tag->index += stripped.length(); |
|
6520 tag = tag->next; |
|
6521 } |
|
6522 } |
|
6523 |
|
6524 // inject the new lines |
|
6525 QStringList::Iterator it = strl.begin(); |
|
6526 x = line; |
|
6527 int idx; |
|
6528 for (; it != strl.end(); ++it) { |
|
6529 stripped = *it; |
|
6530 qStripTags(&stripped); |
|
6531 if (x == line) { |
|
6532 stripped = left + stripped; |
|
6533 idx = index; |
|
6534 } else { |
|
6535 idx = 0; |
|
6536 } |
|
6537 d->od->lines[LOGOFFSET(x)] = stripped; |
|
6538 optimParseTags(&*it, x++, idx); |
|
6539 } |
|
6540 d->od->lines[LOGOFFSET(x - 1)] += right; |
|
6541 } |
|
6542 // recalculate the pixel width of the longest injected line - |
|
6543 QFontMetrics fm(Q3ScrollView::font()); |
|
6544 int lWidth = 0; |
|
6545 for (x = line; x < line + numNewLines; x++) { |
|
6546 if (optimHasBoldMetrics(x)) { |
|
6547 QFont fnt = Q3ScrollView::font(); |
|
6548 fnt.setBold(true); |
|
6549 fm = QFontMetrics(fnt); |
|
6550 } |
|
6551 lWidth = fm.width(d->od->lines[x]) + 4; |
|
6552 if (lWidth > d->od->maxLineWidth) |
|
6553 d->od->maxLineWidth = lWidth; |
|
6554 } |
|
6555 resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); |
|
6556 repaintContents(); |
|
6557 emit textChanged(); |
|
6558 } |
|
6559 |
|
6560 |
|
6561 /*! \internal |
|
6562 |
|
6563 Returns the first open left-tag appearing before line \a line. |
|
6564 */ |
|
6565 Q3TextEditOptimPrivate::Tag * Q3TextEdit::optimPreviousLeftTag(int line) |
|
6566 { |
|
6567 Q3TextEditOptimPrivate::Tag * ftag = 0; |
|
6568 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it; |
|
6569 if ((it = d->od->tagIndex.constFind(LOGOFFSET(line))) != d->od->tagIndex.constEnd()) |
|
6570 ftag = it.value(); |
|
6571 if (!ftag) { |
|
6572 // start searching for an open tag |
|
6573 ftag = d->od->tags; |
|
6574 while (ftag) { |
|
6575 if (ftag->line > line || ftag->next == 0) { |
|
6576 if (ftag->line > line) |
|
6577 ftag = ftag->prev; |
|
6578 break; |
|
6579 } |
|
6580 ftag = ftag->next; |
|
6581 } |
|
6582 } else { |
|
6583 ftag = ftag->prev; |
|
6584 } |
|
6585 |
|
6586 if (ftag) { |
|
6587 if (ftag && ftag->parent) // use the open parent tag |
|
6588 ftag = ftag->parent; |
|
6589 else if (ftag && ftag->leftTag) // this is a right-tag with no parent |
|
6590 ftag = 0; |
|
6591 } |
|
6592 return ftag; |
|
6593 } |
|
6594 |
|
6595 /*! \internal |
|
6596 |
|
6597 Set the format for the string starting at index \a start and ending |
|
6598 at \a end according to \a tag. If \a tag is a Format tag, find the |
|
6599 first open color tag appearing before \a tag and use that tag to |
|
6600 color the string. |
|
6601 */ |
|
6602 void Q3TextEdit::optimSetTextFormat(Q3TextDocument * td, Q3TextCursor * cur, |
|
6603 Q3TextFormat * f, int start, int end, |
|
6604 Q3TextEditOptimPrivate::Tag * tag) |
|
6605 { |
|
6606 int formatFlags = Q3TextFormat::Bold | Q3TextFormat::Italic | |
|
6607 Q3TextFormat::Underline; |
|
6608 cur->setIndex(start); |
|
6609 td->setSelectionStart(0, *cur); |
|
6610 cur->setIndex(end); |
|
6611 td->setSelectionEnd(0, *cur); |
|
6612 Q3StyleSheetItem * ssItem = styleSheet()->item(tag->tag); |
|
6613 if (!ssItem || tag->type == Q3TextEditOptimPrivate::Format) { |
|
6614 f->setBold(tag->bold); |
|
6615 f->setItalic(tag->italic); |
|
6616 f->setUnderline(tag->underline); |
|
6617 if (tag->type == Q3TextEditOptimPrivate::Format) { |
|
6618 // check to see if there are any open color tags prior to |
|
6619 // this format tag |
|
6620 tag = tag->prev; |
|
6621 while (tag && (tag->type == Q3TextEditOptimPrivate::Format || |
|
6622 tag->leftTag)) { |
|
6623 tag = tag->leftTag ? tag->parent : tag->prev; |
|
6624 } |
|
6625 } |
|
6626 if (tag) { |
|
6627 QString col = tag->tag.simplified(); |
|
6628 if (col.startsWith(QLatin1String("font color"))) { |
|
6629 int i = col.indexOf(QLatin1Char('='), 10); |
|
6630 col = col.mid(i + 1).simplified(); |
|
6631 if (col[0] == QLatin1Char('\"')) |
|
6632 col = col.mid(1, col.length() - 2); |
|
6633 } |
|
6634 QColor color = QColor(col); |
|
6635 if (color.isValid()) { |
|
6636 formatFlags |= Q3TextFormat::Color; |
|
6637 f->setColor(color); |
|
6638 } |
|
6639 } |
|
6640 } else { // use the stylesheet tag definition |
|
6641 if (ssItem->color().isValid()) { |
|
6642 formatFlags |= Q3TextFormat::Color; |
|
6643 f->setColor(ssItem->color()); |
|
6644 } |
|
6645 f->setBold(ssItem->fontWeight() == QFont::Bold); |
|
6646 f->setItalic(ssItem->fontItalic()); |
|
6647 f->setUnderline(ssItem->fontUnderline()); |
|
6648 } |
|
6649 td->setFormat(0, f, formatFlags); |
|
6650 td->removeSelection(0); |
|
6651 } |
|
6652 |
|
6653 /*! \internal */ |
|
6654 void Q3TextEdit::optimDrawContents(QPainter * p, int clipx, int clipy, |
|
6655 int clipw, int cliph) |
|
6656 { |
|
6657 QFontMetrics fm(Q3ScrollView::font()); |
|
6658 int startLine = clipy / fm.lineSpacing(); |
|
6659 |
|
6660 // we always have to fetch at least two lines for drawing because the |
|
6661 // painter may be translated so that parts of two lines cover the area |
|
6662 // of a single line |
|
6663 int nLines = (cliph / fm.lineSpacing()) + 2; |
|
6664 int endLine = startLine + nLines; |
|
6665 |
|
6666 if (startLine >= d->od->numLines) |
|
6667 return; |
|
6668 if ((startLine + nLines) > d->od->numLines) |
|
6669 nLines = d->od->numLines - startLine; |
|
6670 |
|
6671 int i = 0; |
|
6672 QString str; |
|
6673 for (i = startLine; i < (startLine + nLines); i++) |
|
6674 str.append(d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n')); |
|
6675 |
|
6676 Q3TextDocument * td = new Q3TextDocument(0); |
|
6677 td->setDefaultFormat(Q3ScrollView::font(), QColor()); |
|
6678 td->setPlainText(str); |
|
6679 td->setFormatter(new Q3TextFormatterBreakWords); // deleted by QTextDoc |
|
6680 td->formatter()->setWrapEnabled(false); |
|
6681 td->setTabStops(doc->tabStopWidth()); |
|
6682 |
|
6683 // get the current text color from the current format |
|
6684 td->selectAll(Q3TextDocument::Standard); |
|
6685 Q3TextFormat f; |
|
6686 f.setColor(palette().text().color()); |
|
6687 f.setFont(Q3ScrollView::font()); |
|
6688 td->setFormat(Q3TextDocument::Standard, &f, |
|
6689 Q3TextFormat::Color | Q3TextFormat::Font); |
|
6690 td->removeSelection(Q3TextDocument::Standard); |
|
6691 |
|
6692 // add tag formatting |
|
6693 if (d->od->tags) { |
|
6694 int i = startLine; |
|
6695 QMap<int,Q3TextEditOptimPrivate::Tag *>::ConstIterator it; |
|
6696 Q3TextEditOptimPrivate::Tag * tag = 0, * tmp = 0; |
|
6697 Q3TextCursor cur(td); |
|
6698 // Step 1 - find previous left-tag |
|
6699 tmp = optimPreviousLeftTag(i); |
|
6700 for (; i < startLine + nLines; i++) { |
|
6701 if ((it = d->od->tagIndex.constFind(LOGOFFSET(i))) != d->od->tagIndex.constEnd()) |
|
6702 tag = it.value(); |
|
6703 // Step 2 - iterate over tags on the current line |
|
6704 int lastIndex = 0; |
|
6705 while (tag && tag->line == i) { |
|
6706 tmp = 0; |
|
6707 if (tag->prev && !tag->prev->leftTag) { |
|
6708 tmp = tag->prev; |
|
6709 } else if (tag->prev && tag->prev->parent) { |
|
6710 tmp = tag->prev->parent; |
|
6711 } |
|
6712 if ((tag->index - lastIndex) > 0 && tmp) { |
|
6713 optimSetTextFormat(td, &cur, &f, lastIndex, tag->index, tmp); |
|
6714 } |
|
6715 lastIndex = tag->index; |
|
6716 tmp = tag; |
|
6717 tag = tag->next; |
|
6718 } |
|
6719 // Step 3 - color last part of the line - if necessary |
|
6720 if (tmp && tmp->parent) |
|
6721 tmp = tmp->parent; |
|
6722 if ((cur.paragraph()->length()-1 - lastIndex) > 0 && tmp && !tmp->leftTag) { |
|
6723 optimSetTextFormat(td, &cur, &f, lastIndex, |
|
6724 cur.paragraph()->length() - 1, tmp); |
|
6725 } |
|
6726 cur.setParagraph(cur.paragraph()->next()); |
|
6727 } |
|
6728 // useful debug info |
|
6729 // |
|
6730 // tag = d->od->tags; |
|
6731 // qWarning("###"); |
|
6732 // while (tag) { |
|
6733 // qWarning("Tag: %p, parent: %09p, leftTag: %09p, Name: %-15s, ParentName: %s, %d%d%d", tag, |
|
6734 // tag->parent, tag->leftTag, tag->tag.latin1(), tag->parent ? tag->parent->tag.latin1():"<none>", |
|
6735 // tag->bold, tag->italic, tag->underline); |
|
6736 // tag = tag->next; |
|
6737 // } |
|
6738 } |
|
6739 |
|
6740 // if there is a selection, make sure that the selection in the |
|
6741 // part we need to redraw is set correctly |
|
6742 if (optimHasSelection()) { |
|
6743 Q3TextCursor c1(td); |
|
6744 Q3TextCursor c2(td); |
|
6745 int selStart = d->od->selStart.line; |
|
6746 int idxStart = d->od->selStart.index; |
|
6747 int selEnd = d->od->selEnd.line; |
|
6748 int idxEnd = d->od->selEnd.index; |
|
6749 if (selEnd < selStart) { |
|
6750 qSwap(&selStart, &selEnd); |
|
6751 qSwap(&idxStart, &idxEnd); |
|
6752 } |
|
6753 if (selEnd > d->od->numLines-1) { |
|
6754 selEnd = d->od->numLines-1; |
|
6755 } |
|
6756 if (startLine <= selStart && endLine >= selEnd) { |
|
6757 // case 1: area to paint covers entire selection |
|
6758 int paragS = selStart - startLine; |
|
6759 int paragE = paragS + (selEnd - selStart); |
|
6760 Q3TextParagraph * parag = td->paragAt(paragS); |
|
6761 if (parag) { |
|
6762 c1.setParagraph(parag); |
|
6763 if (td->text(paragS).length() >= idxStart) |
|
6764 c1.setIndex(idxStart); |
|
6765 } |
|
6766 parag = td->paragAt(paragE); |
|
6767 if (parag) { |
|
6768 c2.setParagraph(parag); |
|
6769 if (td->text(paragE).length() >= idxEnd) |
|
6770 c2.setIndex(idxEnd); |
|
6771 } |
|
6772 } else if (startLine > selStart && endLine < selEnd) { |
|
6773 // case 2: area to paint is all part of the selection |
|
6774 td->selectAll(Q3TextDocument::Standard); |
|
6775 } else if (startLine > selStart && endLine >= selEnd && |
|
6776 startLine <= selEnd) { |
|
6777 // case 3: area to paint starts inside a selection, ends past it |
|
6778 c1.setParagraph(td->firstParagraph()); |
|
6779 c1.setIndex(0); |
|
6780 int paragE = selEnd - startLine; |
|
6781 Q3TextParagraph * parag = td->paragAt(paragE); |
|
6782 if (parag) { |
|
6783 c2.setParagraph(parag); |
|
6784 if (td->text(paragE).length() >= idxEnd) |
|
6785 c2.setIndex(idxEnd); |
|
6786 } |
|
6787 } else if (startLine <= selStart && endLine < selEnd && |
|
6788 endLine > selStart) { |
|
6789 // case 4: area to paint starts before a selection, ends inside it |
|
6790 int paragS = selStart - startLine; |
|
6791 Q3TextParagraph * parag = td->paragAt(paragS); |
|
6792 if (parag) { |
|
6793 c1.setParagraph(parag); |
|
6794 c1.setIndex(idxStart); |
|
6795 } |
|
6796 c2.setParagraph(td->lastParagraph()); |
|
6797 c2.setIndex(td->lastParagraph()->string()->toString().length() - 1); |
|
6798 |
|
6799 } |
|
6800 // previously selected? |
|
6801 if (!td->hasSelection(Q3TextDocument::Standard)) { |
|
6802 td->setSelectionStart(Q3TextDocument::Standard, c1); |
|
6803 td->setSelectionEnd(Q3TextDocument::Standard, c2); |
|
6804 } |
|
6805 } |
|
6806 td->doLayout(p, contentsWidth()); |
|
6807 |
|
6808 // have to align the painter so that partly visible lines are |
|
6809 // drawn at the correct position within the area that needs to be |
|
6810 // painted |
|
6811 int offset = clipy % fm.lineSpacing() + 2; |
|
6812 QRect r(clipx, 0, clipw, cliph + offset); |
|
6813 p->translate(0, clipy - offset); |
|
6814 td->draw(p, r.x(), r.y(), r.width(), r.height(), palette()); |
|
6815 p->translate(0, -(clipy - offset)); |
|
6816 delete td; |
|
6817 } |
|
6818 |
|
6819 /*! \internal */ |
|
6820 void Q3TextEdit::optimMousePressEvent(QMouseEvent * e) |
|
6821 { |
|
6822 if (e->button() != Qt::LeftButton) |
|
6823 return; |
|
6824 |
|
6825 QFontMetrics fm(Q3ScrollView::font()); |
|
6826 mousePressed = true; |
|
6827 mousePos = e->pos(); |
|
6828 d->od->selStart.line = e->y() / fm.lineSpacing(); |
|
6829 if (d->od->selStart.line > d->od->numLines-1) { |
|
6830 d->od->selStart.line = d->od->numLines-1; |
|
6831 d->od->selStart.index = d->od->lines[LOGOFFSET(d->od->numLines-1)].length(); |
|
6832 } else { |
|
6833 QString str = d->od->lines[LOGOFFSET(d->od->selStart.line)]; |
|
6834 d->od->selStart.index = optimCharIndex(str, mousePos.x()); |
|
6835 } |
|
6836 d->od->selEnd.line = d->od->selStart.line; |
|
6837 d->od->selEnd.index = d->od->selStart.index; |
|
6838 oldMousePos = e->pos(); |
|
6839 repaintContents(); |
|
6840 } |
|
6841 |
|
6842 /*! \internal */ |
|
6843 void Q3TextEdit::optimMouseReleaseEvent(QMouseEvent * e) |
|
6844 { |
|
6845 if (e->button() != Qt::LeftButton) |
|
6846 return; |
|
6847 |
|
6848 if (scrollTimer->isActive()) |
|
6849 scrollTimer->stop(); |
|
6850 if (!inDoubleClick) { |
|
6851 QFontMetrics fm(Q3ScrollView::font()); |
|
6852 d->od->selEnd.line = e->y() / fm.lineSpacing(); |
|
6853 if (d->od->selEnd.line > d->od->numLines-1) { |
|
6854 d->od->selEnd.line = d->od->numLines-1; |
|
6855 } |
|
6856 QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)]; |
|
6857 mousePos = e->pos(); |
|
6858 d->od->selEnd.index = optimCharIndex(str, mousePos.x()); |
|
6859 if (d->od->selEnd.line < d->od->selStart.line) { |
|
6860 qSwap(&d->od->selStart.line, &d->od->selEnd.line); |
|
6861 qSwap(&d->od->selStart.index, &d->od->selEnd.index); |
|
6862 } else if (d->od->selStart.line == d->od->selEnd.line && |
|
6863 d->od->selStart.index > d->od->selEnd.index) { |
|
6864 qSwap(&d->od->selStart.index, &d->od->selEnd.index); |
|
6865 } |
|
6866 oldMousePos = e->pos(); |
|
6867 repaintContents(); |
|
6868 } |
|
6869 if (mousePressed) { |
|
6870 mousePressed = false; |
|
6871 copyToClipboard(); |
|
6872 } |
|
6873 |
|
6874 inDoubleClick = false; |
|
6875 emit copyAvailable(optimHasSelection()); |
|
6876 emit selectionChanged(); |
|
6877 } |
|
6878 |
|
6879 /*! \internal */ |
|
6880 void Q3TextEdit::optimMouseMoveEvent(QMouseEvent * e) |
|
6881 { |
|
6882 mousePos = e->pos(); |
|
6883 optimDoAutoScroll(); |
|
6884 oldMousePos = mousePos; |
|
6885 } |
|
6886 |
|
6887 /*! \internal */ |
|
6888 void Q3TextEdit::optimDoAutoScroll() |
|
6889 { |
|
6890 if (!mousePressed) |
|
6891 return; |
|
6892 |
|
6893 QFontMetrics fm(Q3ScrollView::font()); |
|
6894 QPoint pos(mapFromGlobal(QCursor::pos())); |
|
6895 bool doScroll = false; |
|
6896 int xx = contentsX() + pos.x(); |
|
6897 int yy = contentsY() + pos.y(); |
|
6898 |
|
6899 // find out how much we have to scroll in either dir. |
|
6900 if (pos.x() < 0 || pos.x() > viewport()->width() || |
|
6901 pos.y() < 0 || pos.y() > viewport()->height()) { |
|
6902 int my = yy; |
|
6903 if (pos.x() < 0) |
|
6904 xx = contentsX() - fm.width(QLatin1Char('w')); |
|
6905 else if (pos.x() > viewport()->width()) |
|
6906 xx = contentsX() + viewport()->width() + fm.width(QLatin1Char('w')); |
|
6907 |
|
6908 if (pos.y() < 0) { |
|
6909 my = contentsY() - 1; |
|
6910 yy = (my / fm.lineSpacing()) * fm.lineSpacing() + 1; |
|
6911 } else if (pos.y() > viewport()->height()) { |
|
6912 my = contentsY() + viewport()->height() + 1; |
|
6913 yy = (my / fm.lineSpacing() + 1) * fm.lineSpacing() - 1; |
|
6914 } |
|
6915 d->od->selEnd.line = my / fm.lineSpacing(); |
|
6916 mousePos.setX(xx); |
|
6917 mousePos.setY(my); |
|
6918 doScroll = true; |
|
6919 } else { |
|
6920 d->od->selEnd.line = mousePos.y() / fm.lineSpacing(); |
|
6921 } |
|
6922 |
|
6923 if (d->od->selEnd.line < 0) { |
|
6924 d->od->selEnd.line = 0; |
|
6925 } else if (d->od->selEnd.line > d->od->numLines-1) { |
|
6926 d->od->selEnd.line = d->od->numLines-1; |
|
6927 } |
|
6928 |
|
6929 QString str = d->od->lines[LOGOFFSET(d->od->selEnd.line)]; |
|
6930 d->od->selEnd.index = optimCharIndex(str, mousePos.x()); |
|
6931 |
|
6932 // have to have a valid index before generating a paint event |
|
6933 if (doScroll) |
|
6934 ensureVisible(xx, yy, 1, 1); |
|
6935 |
|
6936 // if the text document is smaller than the height of the viewport |
|
6937 // - redraw the whole thing otherwise calculate the rect that |
|
6938 // needs drawing. |
|
6939 if (d->od->numLines * fm.lineSpacing() < viewport()->height()) { |
|
6940 repaintContents(contentsX(), contentsY(), width(), height()); |
|
6941 } else { |
|
6942 int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2; |
|
6943 int y; |
|
6944 if (oldMousePos.y() < mousePos.y()) { |
|
6945 y = oldMousePos.y() - fm.lineSpacing(); |
|
6946 } else { |
|
6947 // expand paint area for a fully selected line |
|
6948 h += fm.lineSpacing(); |
|
6949 y = mousePos.y() - fm.lineSpacing()*2; |
|
6950 } |
|
6951 if (y < 0) |
|
6952 y = 0; |
|
6953 repaintContents(contentsX(), y, width(), h); |
|
6954 } |
|
6955 |
|
6956 if ((!scrollTimer->isActive() && pos.y() < 0) || pos.y() > height()) |
|
6957 scrollTimer->start(100, false); |
|
6958 else if (scrollTimer->isActive() && pos.y() >= 0 && pos.y() <= height()) |
|
6959 scrollTimer->stop(); |
|
6960 } |
|
6961 |
|
6962 /*! \internal |
|
6963 |
|
6964 Returns the index of the character in the string \a str that is |
|
6965 currently under the mouse pointer. |
|
6966 */ |
|
6967 int Q3TextEdit::optimCharIndex(const QString &str, int mx) const |
|
6968 { |
|
6969 QFontMetrics fm(Q3ScrollView::font()); |
|
6970 int i = 0; |
|
6971 int dd, dist = 10000000; |
|
6972 int curpos = 0; |
|
6973 int strWidth; |
|
6974 mx = mx - 4; // ### get the real margin from somewhere |
|
6975 |
|
6976 if (!str.contains(QLatin1Char('\t')) && mx > fm.width(str)) |
|
6977 return str.length(); |
|
6978 |
|
6979 while (i < str.length()) { |
|
6980 strWidth = qStrWidth(str.left(i), tabStopWidth(), fm); |
|
6981 dd = strWidth - mx; |
|
6982 if (QABS(dd) <= dist) { |
|
6983 dist = QABS(dd); |
|
6984 if (mx >= strWidth) |
|
6985 curpos = i; |
|
6986 } |
|
6987 ++i; |
|
6988 } |
|
6989 return curpos; |
|
6990 } |
|
6991 |
|
6992 /*! \internal */ |
|
6993 void Q3TextEdit::optimSelectAll() |
|
6994 { |
|
6995 d->od->selStart.line = d->od->selStart.index = 0; |
|
6996 d->od->selEnd.line = d->od->numLines - 1; |
|
6997 d->od->selEnd.index = d->od->lines[LOGOFFSET(d->od->selEnd.line)].length(); |
|
6998 |
|
6999 repaintContents(); |
|
7000 emit copyAvailable(optimHasSelection()); |
|
7001 emit selectionChanged(); |
|
7002 } |
|
7003 |
|
7004 /*! \internal */ |
|
7005 void Q3TextEdit::optimRemoveSelection() |
|
7006 { |
|
7007 d->od->selStart.line = d->od->selEnd.line = -1; |
|
7008 d->od->selStart.index = d->od->selEnd.index = -1; |
|
7009 repaintContents(); |
|
7010 } |
|
7011 |
|
7012 /*! \internal */ |
|
7013 void Q3TextEdit::optimSetSelection(int startLine, int startIdx, |
|
7014 int endLine, int endIdx) |
|
7015 { |
|
7016 d->od->selStart.line = startLine; |
|
7017 d->od->selEnd.line = endLine; |
|
7018 d->od->selStart.index = startIdx; |
|
7019 d->od->selEnd.index = endIdx; |
|
7020 } |
|
7021 |
|
7022 /*! \internal */ |
|
7023 bool Q3TextEdit::optimHasSelection() const |
|
7024 { |
|
7025 if (d->od->selStart.line != d->od->selEnd.line || |
|
7026 d->od->selStart.index != d->od->selEnd.index) |
|
7027 return true; |
|
7028 return false; |
|
7029 } |
|
7030 |
|
7031 /*! \internal */ |
|
7032 QString Q3TextEdit::optimSelectedText() const |
|
7033 { |
|
7034 QString str; |
|
7035 |
|
7036 if (!optimHasSelection()) |
|
7037 return str; |
|
7038 |
|
7039 // concatenate all strings |
|
7040 if (d->od->selStart.line == d->od->selEnd.line) { |
|
7041 str = d->od->lines[LOGOFFSET(d->od->selEnd.line)].mid(d->od->selStart.index, |
|
7042 d->od->selEnd.index - d->od->selStart.index); |
|
7043 } else { |
|
7044 int i = d->od->selStart.line; |
|
7045 str = d->od->lines[LOGOFFSET(i)].right(d->od->lines[LOGOFFSET(i)].length() - |
|
7046 d->od->selStart.index) + QLatin1Char('\n'); |
|
7047 i++; |
|
7048 for (; i < d->od->selEnd.line; i++) { |
|
7049 if (d->od->lines[LOGOFFSET(i)].isEmpty()) // CR lines are empty |
|
7050 str += QLatin1Char('\n'); |
|
7051 else |
|
7052 str += d->od->lines[LOGOFFSET(i)] + QLatin1Char('\n'); |
|
7053 } |
|
7054 str += d->od->lines[LOGOFFSET(d->od->selEnd.line)].left(d->od->selEnd.index); |
|
7055 } |
|
7056 return str; |
|
7057 } |
|
7058 |
|
7059 /*! \internal */ |
|
7060 bool Q3TextEdit::optimFind(const QString & expr, bool cs, bool /*wo*/, |
|
7061 bool fw, int * para, int * index) |
|
7062 { |
|
7063 bool found = false; |
|
7064 int parag = para ? *para : d->od->search.line, |
|
7065 idx = index ? *index : d->od->search.index, i; |
|
7066 |
|
7067 if (d->od->len == 0) |
|
7068 return false; |
|
7069 |
|
7070 for (i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i--) { |
|
7071 idx = fw |
|
7072 ? d->od->lines[LOGOFFSET(i)].indexOf(expr, idx, |
|
7073 cs ? Qt::CaseSensitive : Qt::CaseInsensitive) |
|
7074 : d->od->lines[LOGOFFSET(i)].lastIndexOf(expr, idx, |
|
7075 cs ? Qt::CaseSensitive : Qt::CaseInsensitive); |
|
7076 if (idx != -1) { |
|
7077 found = true; |
|
7078 break; |
|
7079 } else if (fw) |
|
7080 idx = 0; |
|
7081 } |
|
7082 |
|
7083 if (found) { |
|
7084 if (index) |
|
7085 *index = idx; |
|
7086 if (para) |
|
7087 *para = i; |
|
7088 d->od->search.index = idx; |
|
7089 d->od->search.line = i; |
|
7090 optimSetSelection(i, idx, i, idx + expr.length()); |
|
7091 QFontMetrics fm(Q3ScrollView::font()); |
|
7092 int h = fm.lineSpacing(); |
|
7093 int x = fm.width(d->od->lines[LOGOFFSET(i)].left(idx + expr.length())) + 4; |
|
7094 ensureVisible(x, i * h + h / 2, 1, h / 2 + 2); |
|
7095 repaintContents(); // could possibly be optimized |
|
7096 } |
|
7097 return found; |
|
7098 } |
|
7099 |
|
7100 /*! \reimp */ |
|
7101 void Q3TextEdit::polishEvent(QEvent*) |
|
7102 { |
|
7103 // this will ensure that the last line is visible if text have |
|
7104 // been added to the widget before it is shown |
|
7105 if (d->optimMode) |
|
7106 scrollToBottom(); |
|
7107 } |
|
7108 |
|
7109 /*! |
|
7110 Sets the maximum number of lines a Q3TextEdit can hold in \c |
|
7111 Qt::LogText mode to \a limit. If \a limit is -1 (the default), this |
|
7112 signifies an unlimited number of lines. |
|
7113 |
|
7114 \warning Never use formatting tags that span more than one line |
|
7115 when the maximum log lines is set. When lines are removed from the |
|
7116 top of the buffer it could result in an unbalanced tag pair, i.e. |
|
7117 the left formatting tag is removed before the right one. |
|
7118 */ |
|
7119 void Q3TextEdit::setMaxLogLines(int limit) |
|
7120 { |
|
7121 d->maxLogLines = limit; |
|
7122 if (d->maxLogLines < -1) |
|
7123 d->maxLogLines = -1; |
|
7124 if (d->maxLogLines == -1) |
|
7125 d->logOffset = 0; |
|
7126 } |
|
7127 |
|
7128 /*! |
|
7129 Returns the maximum number of lines Q3TextEdit can hold in \c |
|
7130 Qt::LogText mode. By default the number of lines is unlimited, which |
|
7131 is signified by a value of -1. |
|
7132 */ |
|
7133 int Q3TextEdit::maxLogLines() const |
|
7134 { |
|
7135 return d->maxLogLines; |
|
7136 } |
|
7137 |
|
7138 /*! |
|
7139 Check if the number of lines in the buffer is limited, and uphold |
|
7140 that limit when appending new lines. |
|
7141 */ |
|
7142 void Q3TextEdit::optimCheckLimit(const QString& str) |
|
7143 { |
|
7144 if (d->maxLogLines > -1 && d->maxLogLines <= d->od->numLines) { |
|
7145 // NB! Removing the top line in the buffer will potentially |
|
7146 // destroy the structure holding the formatting tags - if line |
|
7147 // spanning tags are used. |
|
7148 Q3TextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr; |
|
7149 QList<Q3TextEditOptimPrivate::Tag *> lst; |
|
7150 while (t) { |
|
7151 t->line -= 1; |
|
7152 // unhook the ptr from the tag structure |
|
7153 if (((uint) LOGOFFSET(t->line) < (uint) d->logOffset && |
|
7154 (uint) LOGOFFSET(t->line) < (uint) LOGOFFSET(d->od->numLines) && |
|
7155 (uint) LOGOFFSET(d->od->numLines) > (uint) d->logOffset)) |
|
7156 { |
|
7157 if (t->prev) |
|
7158 t->prev->next = t->next; |
|
7159 if (t->next) |
|
7160 t->next->prev = t->prev; |
|
7161 if (d->od->tags == t) |
|
7162 d->od->tags = t->next; |
|
7163 if (d->od->lastTag == t) { |
|
7164 if (t->prev) |
|
7165 d->od->lastTag = t->prev; |
|
7166 else |
|
7167 d->od->lastTag = d->od->tags; |
|
7168 } |
|
7169 tmp = t; |
|
7170 t = t->next; |
|
7171 lst.append(tmp); |
|
7172 delete tmp; |
|
7173 } else { |
|
7174 t = t->next; |
|
7175 } |
|
7176 } |
|
7177 // Remove all references to the ptrs we just deleted |
|
7178 itr = d->od->tags; |
|
7179 while (itr) { |
|
7180 for (int i = 0; i < lst.size(); ++i) { |
|
7181 tmp = lst.at(i); |
|
7182 if (itr->parent == tmp) |
|
7183 itr->parent = 0; |
|
7184 if (itr->leftTag == tmp) |
|
7185 itr->leftTag = 0; |
|
7186 } |
|
7187 itr = itr->next; |
|
7188 } |
|
7189 // ...in the tag index as well |
|
7190 QMap<int, Q3TextEditOptimPrivate::Tag *>::Iterator idx; |
|
7191 if ((idx = d->od->tagIndex.find(d->logOffset)) != d->od->tagIndex.end()) |
|
7192 d->od->tagIndex.erase(idx); |
|
7193 |
|
7194 QMap<int,QString>::Iterator it; |
|
7195 if ((it = d->od->lines.find(d->logOffset)) != d->od->lines.end()) { |
|
7196 d->od->len -= (*it).length(); |
|
7197 d->od->lines.erase(it); |
|
7198 d->od->numLines--; |
|
7199 d->logOffset = LOGOFFSET(1); |
|
7200 } |
|
7201 } |
|
7202 d->od->len += str.length(); |
|
7203 d->od->lines[LOGOFFSET(d->od->numLines++)] = str; |
|
7204 } |
|
7205 |
|
7206 #endif // QT_TEXTEDIT_OPTIMIZATION |
|
7207 |
|
7208 /*! |
|
7209 \property Q3TextEdit::autoFormatting |
|
7210 \brief the enabled set of auto formatting features |
|
7211 |
|
7212 The value can be any combination of the values in the \c |
|
7213 AutoFormattingFlag enum. The default is \c AutoAll. Choose \c AutoNone |
|
7214 to disable all automatic formatting. |
|
7215 |
|
7216 Currently, the only automatic formatting feature provided is \c |
|
7217 AutoBulletList; future versions of Qt may offer more. |
|
7218 */ |
|
7219 |
|
7220 void Q3TextEdit::setAutoFormatting(AutoFormatting features) |
|
7221 { |
|
7222 d->autoFormatting = features; |
|
7223 } |
|
7224 |
|
7225 Q3TextEdit::AutoFormatting Q3TextEdit::autoFormatting() const |
|
7226 { |
|
7227 return d->autoFormatting; |
|
7228 } |
|
7229 |
|
7230 /*! |
|
7231 Returns the QSyntaxHighlighter set on this Q3TextEdit. 0 is |
|
7232 returned if no syntax highlighter is set. |
|
7233 */ |
|
7234 Q3SyntaxHighlighter * Q3TextEdit::syntaxHighlighter() const |
|
7235 { |
|
7236 if (document()->preProcessor()) |
|
7237 return ((Q3SyntaxHighlighterInternal *) document()->preProcessor())->highlighter; |
|
7238 else |
|
7239 return 0; |
|
7240 } |
|
7241 |
|
7242 QT_END_NAMESPACE |
|
7243 |
|
7244 #endif //QT_NO_TEXTEDIT |