author | leseb <leseb> | 2002-07-14 21:21:35 (UTC) |
---|---|---|
committer | leseb <leseb> | 2002-07-14 21:21:35 (UTC) |
commit | 4feeec8b5b41cfd3d13274411f515524f687da09 (patch) (unidiff) | |
tree | 002bbfb9997713e5d5975855d3cfbba7a71b9104 /noncore/apps/opie-write/qtextedit.cpp | |
parent | bdef9cf23ced569a9bc80c1d4f25d85861273b4a (diff) | |
download | opie-4feeec8b5b41cfd3d13274411f515524f687da09.zip opie-4feeec8b5b41cfd3d13274411f515524f687da09.tar.gz opie-4feeec8b5b41cfd3d13274411f515524f687da09.tar.bz2 |
opie-write first draft
Diffstat (limited to 'noncore/apps/opie-write/qtextedit.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/apps/opie-write/qtextedit.cpp | 4516 |
1 files changed, 4516 insertions, 0 deletions
diff --git a/noncore/apps/opie-write/qtextedit.cpp b/noncore/apps/opie-write/qtextedit.cpp new file mode 100644 index 0000000..9c5ea79 --- a/dev/null +++ b/noncore/apps/opie-write/qtextedit.cpp | |||
@@ -0,0 +1,4516 @@ | |||
1 | /**************************************************************************** | ||
2 | ** $Id$ | ||
3 | ** | ||
4 | ** Implementation of the QTextEdit class | ||
5 | ** | ||
6 | ** Created : 990101 | ||
7 | ** | ||
8 | ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. | ||
9 | ** | ||
10 | ** This file is part of the widgets module of the Qt GUI Toolkit. | ||
11 | ** | ||
12 | ** This file may be distributed under the terms of the Q Public License | ||
13 | ** as defined by Trolltech AS of Norway and appearing in the file | ||
14 | ** LICENSE.QPL included in the packaging of this file. | ||
15 | ** | ||
16 | ** This file may be distributed and/or modified under the terms of the | ||
17 | ** GNU General Public License version 2 as published by the Free Software | ||
18 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
19 | ** packaging of this file. | ||
20 | ** | ||
21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition | ||
22 | ** licenses may use this file in accordance with the Qt Commercial License | ||
23 | ** Agreement provided with the Software. | ||
24 | ** | ||
25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
27 | ** | ||
28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for | ||
29 | ** information about Qt Commercial License Agreements. | ||
30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information. | ||
31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
32 | ** | ||
33 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
34 | ** not clear to you. | ||
35 | ** | ||
36 | **********************************************************************/ | ||
37 | |||
38 | #include "qtextedit.h" | ||
39 | |||
40 | #include "qrichtext_p.h" | ||
41 | #include "qpainter.h" | ||
42 | #include "qpen.h" | ||
43 | #include "qbrush.h" | ||
44 | #include "qpixmap.h" | ||
45 | #include "qfont.h" | ||
46 | #include "qcolor.h" | ||
47 | #include "qstyle.h" | ||
48 | #include "qsize.h" | ||
49 | #include "qevent.h" | ||
50 | #include "qtimer.h" | ||
51 | #include "qapplication.h" | ||
52 | #include "qlistbox.h" | ||
53 | #include "qvbox.h" | ||
54 | #include "qapplication.h" | ||
55 | #include "qclipboard.h" | ||
56 | #include "qcolordialog.h" | ||
57 | #include "qfontdialog.h" | ||
58 | #include "qstylesheet.h" | ||
59 | #include "qdragobject.h" | ||
60 | #include "qurl.h" | ||
61 | #include "qcursor.h" | ||
62 | #include "qregexp.h" | ||
63 | #include "qpopupmenu.h" | ||
64 | |||
65 | #define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k) | ||
66 | |||
67 | using namespace Qt3; | ||
68 | |||
69 | struct QUndoRedoInfoPrivate | ||
70 | { | ||
71 | QTextString text; | ||
72 | }; | ||
73 | |||
74 | namespace Qt3 { | ||
75 | |||
76 | class QTextEditPrivate | ||
77 | { | ||
78 | public: | ||
79 | QTextEditPrivate() | ||
80 | :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE) {} | ||
81 | int id[ 7 ]; | ||
82 | int preeditStart; | ||
83 | int preeditLength; | ||
84 | bool ensureCursorVisibleInShowEvent; | ||
85 | }; | ||
86 | |||
87 | } | ||
88 | |||
89 | static bool block_set_alignment = FALSE; | ||
90 | |||
91 | /*! | ||
92 | \class QTextEdit qtextedit.h | ||
93 | \brief The QTextEdit widget provides a sophisticated single-page rich text editor. | ||
94 | |||
95 | \ingroup basic | ||
96 | \ingroup text | ||
97 | \mainclass | ||
98 | |||
99 | QTextEdit is an advanced WYSIWYG editor supporting rich text | ||
100 | formatting. It is optimized to handle large documents and to | ||
101 | respond quickly to user input. | ||
102 | |||
103 | QTextEdit works on paragraphs and characters. A paragraph is a | ||
104 | formatted string which is word-wrapped to fit into the width of | ||
105 | the widget. A document consists of zero or more paragraphs, | ||
106 | indexed from 0. Characters are indexed on a per-paragraph basis, | ||
107 | also indexed from 0. The words in the paragraph are aligned in | ||
108 | accordance with the paragraph's alignment(). Paragraphs are | ||
109 | separated by hard line breaks. Each character within a paragraph | ||
110 | has its own attributes, for example, font and color. | ||
111 | |||
112 | QTextEdit can display images (using QMimeSourceFactory), lists and | ||
113 | tables. If the text is too large to view within the text edit's | ||
114 | viewport, scrollbars will appear. The text edit can load both | ||
115 | plain text and HTML files (a subset of HTML 3.2 and 4). The | ||
116 | rendering style and the set of valid tags are defined by a | ||
117 | styleSheet(). Change the style sheet with \l{setStyleSheet()}; see | ||
118 | QStyleSheet for details. The images identified by image tags are | ||
119 | displayed if they can be interpreted using the text edit's | ||
120 | \l{QMimeSourceFactory}; see setMimeSourceFactory(). | ||
121 | |||
122 | If you want a text browser with more navigation use QTextBrowser. | ||
123 | If you just need to display a small piece of rich text use QLabel | ||
124 | or QSimpleRichText. | ||
125 | |||
126 | If you create a new QTextEdit, and want to allow the user to edit | ||
127 | rich text, call setTextFormat(Qt::RichText) to ensure that the | ||
128 | text is treated as rich text. (Rich text uses HTML tags to set | ||
129 | text formatting attributes. See QStyleSheet for information on the | ||
130 | HTML tags that are supported.). If you don't call setTextFormat() | ||
131 | explicitly the text edit will guess from the text itself whether | ||
132 | it is rich text or plain text. This means that if the text looks | ||
133 | like HTML or XML it will probably be interpreted as rich text, so | ||
134 | you should call setTextFormat(Qt::PlainText) to preserve such | ||
135 | text. | ||
136 | |||
137 | The text edit documentation uses the following concepts: | ||
138 | \list | ||
139 | \i <i>current format</i> -- | ||
140 | this is the format at the current cursor position, \e and it | ||
141 | is the format of the selected text if any. | ||
142 | \i <i>current paragraph</i> -- the paragraph which contains the | ||
143 | cursor. | ||
144 | \endlist | ||
145 | |||
146 | The text is set or replaced using setText() which deletes any | ||
147 | existing text and replaces it with the text passed in the | ||
148 | setText() call. Text can be inserted with insert(), paste() and | ||
149 | pasteSubType(). Text can also be cut(). The entire text is deleted | ||
150 | with clear() and the selected text is deleted with | ||
151 | removeSelectedText(). Selected (marked) text can also be deleted | ||
152 | with del() (which will delete the character to the right of the | ||
153 | cursor if no text is selected). | ||
154 | |||
155 | The current format's attributes are set with setItalic(), | ||
156 | setBold(), setUnderline(), setFamily() (font family), | ||
157 | setPointSize(), setColor() and setCurrentFont(). The current | ||
158 | paragraph's style is set with setParagType() and its alignment is | ||
159 | set with setAlignment(). | ||
160 | |||
161 | Use setSelection() to select text. The setSelectionAttributes() | ||
162 | function is used to indicate how selected text should be | ||
163 | displayed. Use hasSelectedText() to find out if any text is | ||
164 | selected. The currently selected text's position is available | ||
165 | using getSelection() and the selected text itself is returned by | ||
166 | selectedText(). The selection can be copied to the clipboard with | ||
167 | copy(), or cut to the clipboard with cut(). It can be deleted with | ||
168 | removeSelectedText(). The entire text can be selected (or | ||
169 | unselected) using selectAll(). QTextEdit supports multiple | ||
170 | selections. Most of the selection functions operate on the default | ||
171 | selection, selection 0. If the user presses a non-selecting key, | ||
172 | e.g. a cursor key without also holding down Shift, all selections | ||
173 | are cleared. | ||
174 | |||
175 | Set and get the position of the cursor with setCursorPosition() | ||
176 | and getCursorPosition() respectively. When the cursor is moved, | ||
177 | the signals currentFontChanged(), currentColorChanged() and | ||
178 | currentAlignmentChanged() are emitted to reflect the font, color | ||
179 | and alignment at the new cursor position. | ||
180 | |||
181 | If the text changes, the textChanged() signal is emitted, and if | ||
182 | the user inserts a new line by pressing Return or Enter, | ||
183 | returnPressed() is emitted. The isModified() function will return | ||
184 | TRUE if the text has been modified. | ||
185 | |||
186 | QTextEdit provides command-based undo and redo. To set the depth | ||
187 | of the command history use setUndoDepth() which defaults to 100 | ||
188 | steps. To undo or redo the last operation call undo() or redo(). | ||
189 | The signals undoAvailable() and redoAvailable() indicate whether | ||
190 | the undo and redo operations can be executed. | ||
191 | |||
192 | The indent() function is used to reindent a paragraph. It is | ||
193 | useful for code editors, for example in <em>Qt Designer</em>'s | ||
194 | code editor \e{Ctrl+I} invokes the indent() function. | ||
195 | |||
196 | Loading and saving text is achieved using setText() and text(), | ||
197 | for example: | ||
198 | \code | ||
199 | QFile file( fileName ); // Read the text from a file | ||
200 | if ( file.open( IO_ReadOnly ) ) { | ||
201 | QTextStream ts( &file ); | ||
202 | textEdit->setText( ts.read() ); | ||
203 | } | ||
204 | \endcode | ||
205 | \code | ||
206 | QFile file( fileName ); // Write the text to a file | ||
207 | if ( file.open( IO_WriteOnly ) ) { | ||
208 | QTextStream ts( &file ); | ||
209 | ts << textEdit->text(); | ||
210 | textEdit->setModified( FALSE ); | ||
211 | } | ||
212 | \endcode | ||
213 | |||
214 | By default the text edit wraps words at whitespace to fit within | ||
215 | the text edit widget. The setWordWrap() function is used to | ||
216 | specify the kind of word wrap you want, or \c NoWrap if you don't | ||
217 | want any wrapping. Call setWordWrap() to set a fixed pixel width | ||
218 | \c FixedPixelWidth, or character column (e.g. 80 column) \c | ||
219 | FixedColumnWidth with the pixels or columns specified with | ||
220 | setWrapColumnOrWidth(). If you use word wrap to the widget's width | ||
221 | \c WidgetWidth, you can specify whether to break on whitespace or | ||
222 | anywhere with setWrapPolicy(). | ||
223 | |||
224 | The background color is set differently than other widgets, using | ||
225 | setPaper(). You specify a brush style which could be a plain color | ||
226 | or a complex pixmap. | ||
227 | |||
228 | Hypertext links are automatically underlined; this can be changed | ||
229 | with setLinkUnderline(). The tab stop width is set with | ||
230 | setTabStopWidth(). | ||
231 | |||
232 | The zoomIn() and zoomOut() functions can be used to resize the | ||
233 | text by increasing (decreasing for zoomOut()) the point size used. | ||
234 | Images are not affected by the zoom functions. | ||
235 | |||
236 | The lines() function returns the number of lines in the text and | ||
237 | paragraphs() returns the number of paragraphs. The number of lines | ||
238 | within a particular paragraph is returned by linesOfParagraph(). | ||
239 | The length of the entire text in characters is returned by | ||
240 | length(). | ||
241 | |||
242 | You can scroll to an anchor in the text, e.g. \c{<a | ||
243 | name="anchor">} with scrollToAnchor(). The find() function can be | ||
244 | used to find and select a given string within the text. | ||
245 | |||
246 | The list of key-bindings which are implemented for editing: | ||
247 | \table | ||
248 | \header \i Keypresses \i Action | ||
249 | \row \i \e{Backspace} \i Delete the character to the left of the cursor | ||
250 | \row \i \e{Delete} \i Delete the character to the right of the cursor | ||
251 | \row \i \e{Ctrl+A} \i Move the cursor to the beginning of the line | ||
252 | \row \i \e{Ctrl+B} \i Move the cursor one character left | ||
253 | \row \i \e{Ctrl+C} \i Copy the marked text to the clipboard (also | ||
254 | \e{Ctrl+Insert} under Windows) | ||
255 | \row \i \e{Ctrl+D} \i Delete the character to the right of the cursor | ||
256 | \row \i \e{Ctrl+E} \i Move the cursor to the end of the line | ||
257 | \row \i \e{Ctrl+F} \i Move the cursor one character right | ||
258 | \row \i \e{Ctrl+H} \i Delete the character to the left of the cursor | ||
259 | \row \i \e{Ctrl+K} \i Delete to end of line | ||
260 | \row \i \e{Ctrl+N} \i Move the cursor one line down | ||
261 | \row \i \e{Ctrl+P} \i Move the cursor one line up | ||
262 | \row \i \e{Ctrl+V} \i Paste the clipboard text into line edit | ||
263 | (also \e{Shift+Insert} under Windows) | ||
264 | \row \i \e{Ctrl+X} \i Cut the marked text, copy to clipboard | ||
265 | (also \e{Shift+Delete} under Windows) | ||
266 | \row \i \e{Ctrl+Z} \i Undo the last operation | ||
267 | \row \i \e{Ctrl+Y} \i Redo the last operation | ||
268 | \row \i \e{LeftArrow} \i Move the cursor one character left | ||
269 | \row \i \e{Ctrl+LeftArrow} \i Move the cursor one word left | ||
270 | \row \i \e{RightArrow} \i Move the cursor one character right | ||
271 | \row \i \e{Ctrl+RightArrow} \i Move the cursor one word right | ||
272 | \row \i \e{UpArrow} \i Move the cursor one line up | ||
273 | \row \i \e{Ctrl+UpArrow} \i Move the cursor one word up | ||
274 | \row \i \e{DownArrow} \i Move the cursor one line down | ||
275 | \row \i \e{Ctrl+Down Arrow} \i Move the cursor one word down | ||
276 | \row \i \e{PageUp} \i Move the cursor one page up | ||
277 | \row \i \e{PageDown} \i Move the cursor one page down | ||
278 | \row \i \e{Home} \i Move the cursor to the beginning of the line | ||
279 | \row \i \e{Ctrl+Home} \i Move the cursor to the beginning of the text | ||
280 | \row \i \e{End} \i Move the cursor to the end of the line | ||
281 | \row \i \e{Ctrl+End} \i Move the cursor to the end of the text | ||
282 | \row \i \e{Shift+Wheel} \i Scroll the page horizontally | ||
283 | (the Wheel is the mouse wheel) | ||
284 | \row \i \e{Ctrl+Wheel} \i Zoom the text | ||
285 | \endtable | ||
286 | |||
287 | To select (mark) text hold down the Shift key whilst pressing one | ||
288 | of the movement keystrokes, for example, <i>Shift+Right Arrow</i> | ||
289 | will select the character to the right, and <i>Shift+Ctrl+Right | ||
290 | Arrow</i> will select the word to the right, etc. | ||
291 | |||
292 | By default the text edit widget operates in insert mode so all | ||
293 | text that the user enters is inserted into the text edit and any | ||
294 | text to the right of the cursor is moved out of the way. The mode | ||
295 | can be changed to overwrite, where new text overwrites any text to | ||
296 | the right of the cursor, using setOverwriteMode(). | ||
297 | |||
298 | QTextEdit can also be used as read-only text viewer. Call | ||
299 | setReadOnly( TRUE ) to disable editing. A read-only QTextEdit | ||
300 | provides the same functionality as the (obsolete) QTextView. | ||
301 | (QTextView is still supplied for compatibility with old code.) | ||
302 | |||
303 | When QTextEdit is used read-only the key-bindings are limited to | ||
304 | navigation, and text may only be selected with the mouse: | ||
305 | \table | ||
306 | \header \i Keypresses \i Action | ||
307 | \row \i \e{UpArrow} \i Move one line up | ||
308 | \row \i \e{DownArrow} \i Move one line down | ||
309 | \row \i \e{LeftArrow} \i Move one character left | ||
310 | \row \i \e{RightArrow} \i Move one character right | ||
311 | \row \i \e{PageUp} \i Move one (viewport) page up | ||
312 | \row \i \e{PageDown} \i Move one (viewport) page down | ||
313 | \row \i \e{Home} \i Move to the beginning of the text | ||
314 | \row \i \e{End} \i Move to the end of the text | ||
315 | \row \i \e{Shift+Wheel} \i Scroll the page horizontally (the Wheel is the mouse wheel) | ||
316 | \row \i \e{Ctrl+Wheel} \i Zoom the text | ||
317 | \endtable | ||
318 | |||
319 | The text edit may be able to provide some meta-information. For | ||
320 | example, the documentTitle() function will return the text from | ||
321 | within HTML \c{<title>} tags. | ||
322 | |||
323 | The text displayed in a text edit has a \e context. The context is | ||
324 | a path which the text edit's QMimeSourceFactory uses to resolve | ||
325 | the locations of files and images. It is passed to the | ||
326 | mimeSourceFactory() when quering data. (See QTextEdit() and | ||
327 | \l{context()}.) | ||
328 | |||
329 | Note that we do not intend to add a full-featured web browser | ||
330 | widget to Qt (because that would easily double Qt's size and only | ||
331 | a few applications would benefit from it). The rich | ||
332 | text support in Qt is designed to provide a fast, portable and | ||
333 | efficient way to add reasonable online help facilities to | ||
334 | applications, and to provide a basis for rich text editors. | ||
335 | */ | ||
336 | |||
337 | /*! \enum QTextEdit::KeyboardAction | ||
338 | |||
339 | This enum is used by doKeyboardAction() to specify which action | ||
340 | should be executed: | ||
341 | |||
342 | \value ActionBackspace Delete the character to the left of the | ||
343 | cursor. | ||
344 | |||
345 | \value ActionDelete Delete the character to the right of the cursor. | ||
346 | |||
347 | \value ActionReturn Split the paragraph at the cursor position. | ||
348 | |||
349 | \value ActionKill If the cursor is not at the end of the paragraph, | ||
350 | delete the text from the cursor position until the end of the | ||
351 | paragraph. If the cursor is at the end of the paragraph, delete the | ||
352 | hard line break at the end of the paragraph - this will cause this | ||
353 | paragraph to be joined with the following paragraph. | ||
354 | */ | ||
355 | |||
356 | /*! \enum QTextEdit::VerticalAlignment | ||
357 | |||
358 | This enum is used to set the vertical alignment of the text. | ||
359 | |||
360 | \value AlignNormal Normal alignment | ||
361 | \value AlignSuperScript Superscript | ||
362 | \value AlignSubScript Subscript | ||
363 | */ | ||
364 | |||
365 | /*! \fn void QTextEdit::copyAvailable (bool yes) | ||
366 | |||
367 | This signal is emitted when text is selected or de-selected in the text | ||
368 | edit. | ||
369 | |||
370 | When text is selected this signal will be emitted with \a yes set to | ||
371 | TRUE. If no text has been selected or if the selected text is | ||
372 | de-selected this signal is emitted with \a yes set to FALSE. | ||
373 | |||
374 | If \a yes is TRUE then copy() can be used to copy the selection to the | ||
375 | clipboard. If \a yes is FALSE then copy() does nothing. | ||
376 | |||
377 | \sa selectionChanged() | ||
378 | */ | ||
379 | |||
380 | |||
381 | /*! \fn void QTextEdit::textChanged() | ||
382 | |||
383 | This signal is emitted whenever the text in the text edit changes. | ||
384 | |||
385 | \sa setText() append() | ||
386 | */ | ||
387 | |||
388 | /*! \fn void QTextEdit::selectionChanged() | ||
389 | |||
390 | This signal is emitted whenever the selection changes. | ||
391 | |||
392 | \sa setSelection() copyAvailable() | ||
393 | */ | ||
394 | |||
395 | /*! \fn QTextDocument *QTextEdit::document() const | ||
396 | |||
397 | \internal | ||
398 | |||
399 | This function returns the QTextDocument which is used by the text | ||
400 | edit. | ||
401 | */ | ||
402 | |||
403 | /*! \fn void QTextEdit::setDocument( QTextDocument *doc ) | ||
404 | |||
405 | \internal | ||
406 | |||
407 | This function sets the QTextDocument which should be used by the text | ||
408 | edit to \a doc. This can be used, for example, if you want to | ||
409 | display a document using multiple views. You would create a | ||
410 | QTextDocument and set it to the text edits which should display it. | ||
411 | You would need to connect to the textChanged() and | ||
412 | selectionChanged() signals of all the text edits and update them all | ||
413 | accordingly (preferably with a slight delay for efficiency reasons). | ||
414 | */ | ||
415 | |||
416 | /*! \enum QTextEdit::CursorAction | ||
417 | |||
418 | This enum is used by moveCursor() to specify in which direction | ||
419 | the cursor should be moved: | ||
420 | |||
421 | \value MoveBackward Moves the cursor one character backward | ||
422 | |||
423 | \value MoveWordBackward Moves the cursor one word backward | ||
424 | |||
425 | \value MoveForward Moves the cursor one character forward | ||
426 | |||
427 | \value MoveWordForward Moves the cursor one word forward | ||
428 | |||
429 | \value MoveUp Moves the cursor up one line | ||
430 | |||
431 | \value MoveDown Moves the cursor down one line | ||
432 | |||
433 | \value MoveLineStart Moves the cursor to the beginning of the line | ||
434 | |||
435 | \value MoveLineEnd Moves the cursor to the end of the line | ||
436 | |||
437 | \value MoveHome Moves the cursor to the beginning of the document | ||
438 | |||
439 | \value MoveEnd Moves the cursor to the end of the document | ||
440 | |||
441 | \value MovePgUp Moves the cursor one page up | ||
442 | |||
443 | \value MovePgDown Moves the cursor one page down | ||
444 | */ | ||
445 | |||
446 | |||
447 | /*! | ||
448 | \property QTextEdit::overwriteMode | ||
449 | \brief the text edit's overwrite mode | ||
450 | |||
451 | If FALSE (the default) characters entered by the user are inserted | ||
452 | with any characters to the right being moved out of the way. | ||
453 | If TRUE, the editor is in overwrite mode, i.e. characters entered by | ||
454 | the user overwrite any characters to the right of the cursor position. | ||
455 | */ | ||
456 | |||
457 | /*! \fn void QTextEdit::setCurrentFont( const QFont &f ) | ||
458 | |||
459 | Sets the font of the current format to \a f. | ||
460 | |||
461 | \sa font() setPointSize() setFamily() | ||
462 | */ | ||
463 | |||
464 | /*! | ||
465 | \property QTextEdit::undoDepth | ||
466 | \brief the depth of the undo history | ||
467 | |||
468 | The maximum number of steps in the undo/redo history. | ||
469 | The default is 100. | ||
470 | |||
471 | \sa undo() redo() | ||
472 | */ | ||
473 | |||
474 | /*! \fn void QTextEdit::undoAvailable( bool yes ) | ||
475 | |||
476 | This signal is emitted when the availability of undo changes. If \a | ||
477 | yes is TRUE, then undo() will work until undoAvailable( FALSE ) is | ||
478 | next emitted. | ||
479 | |||
480 | \sa undo() undoDepth() | ||
481 | */ | ||
482 | |||
483 | /*! \fn void QTextEdit::modificationChanged( bool m ) | ||
484 | |||
485 | This signal is emitted when the modification of the document | ||
486 | changed. If \a m is TRUE, the document was modified, otherwise the | ||
487 | modification state has been reset to unmodified. | ||
488 | |||
489 | \sa modified | ||
490 | */ | ||
491 | |||
492 | /*! \fn void QTextEdit::redoAvailable( bool yes ) | ||
493 | |||
494 | This signal is emitted when the availability of redo changes. If \a | ||
495 | yes is TRUE, then redo() will work until redoAvailable( FALSE ) is | ||
496 | next emitted. | ||
497 | |||
498 | \sa redo() undoDepth() | ||
499 | */ | ||
500 | |||
501 | /*! \fn void QTextEdit::currentFontChanged( const QFont &f ) | ||
502 | |||
503 | This signal is emitted if the font of the current format has changed. | ||
504 | |||
505 | The new font is \a f. | ||
506 | |||
507 | \sa setCurrentFont() | ||
508 | */ | ||
509 | |||
510 | /*! \fn void QTextEdit::currentColorChanged( const QColor &c ) | ||
511 | |||
512 | This signal is emitted if the color of the current format has changed. | ||
513 | |||
514 | The new color is \a c. | ||
515 | |||
516 | \sa setColor() | ||
517 | */ | ||
518 | |||
519 | /*! \fn void QTextEdit::currentVerticalAlignmentChanged( VerticalAlignment a ) | ||
520 | |||
521 | This signal is emitted if the vertical alignment of the current | ||
522 | format has changed. | ||
523 | |||
524 | The new vertical alignment is \a a. | ||
525 | |||
526 | \sa setVerticalAlignment() | ||
527 | */ | ||
528 | |||
529 | /*! \fn void QTextEdit::currentAlignmentChanged( int a ) | ||
530 | |||
531 | This signal is emitted if the alignment of the current paragraph | ||
532 | has changed. | ||
533 | |||
534 | The new alignment is \a a. | ||
535 | |||
536 | \sa setAlignment() | ||
537 | */ | ||
538 | |||
539 | /*! \fn void QTextEdit::cursorPositionChanged( QTextCursor *c ) | ||
540 | |||
541 | This signal is emitted if the position of the cursor changed. \a c | ||
542 | points to the text cursor object. | ||
543 | |||
544 | \sa setCursorPosition() | ||
545 | */ | ||
546 | |||
547 | /*! \overload void QTextEdit::cursorPositionChanged( int para, int pos ) | ||
548 | |||
549 | This signal is emitted if the position of the cursor changed. \a | ||
550 | para contains the paragraph index and \a pos contains the character | ||
551 | position within the paragraph. | ||
552 | |||
553 | \sa setCursorPosition() | ||
554 | */ | ||
555 | |||
556 | /*! \fn void QTextEdit::returnPressed() | ||
557 | |||
558 | This signal is emitted if the user pressed the Return or the Enter key. | ||
559 | */ | ||
560 | |||
561 | /*! | ||
562 | \fn QTextCursor *QTextEdit::textCursor() const | ||
563 | |||
564 | Returns the text edit's text cursor. | ||
565 | |||
566 | \warning QTextCursor is not in the public API, but in special | ||
567 | circumstances you might wish to use it. | ||
568 | */ | ||
569 | |||
570 | /*! Constructs an empty QTextEdit with parent \a parent and name \a | ||
571 | name. | ||
572 | */ | ||
573 | |||
574 | QTextEdit::QTextEdit( QWidget *parent, const char *name ) | ||
575 | : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ), | ||
576 | doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) | ||
577 | { | ||
578 | init(); | ||
579 | } | ||
580 | |||
581 | /*! | ||
582 | Constructs a QTextEdit with parent \a parent and name \a name. The | ||
583 | text edit will display the text \a text using context \a context. | ||
584 | |||
585 | The \a context is a path which the text edit's QMimeSourceFactory | ||
586 | uses to resolve the locations of files and images. It is passed to | ||
587 | the mimeSourceFactory() when quering data. | ||
588 | |||
589 | For example if the text contains an image tag, | ||
590 | \c{<img src="image.png">}, and the context is "path/to/look/in", the | ||
591 | QMimeSourceFactory will try to load the image from | ||
592 | "path/to/look/in/image.png". If the tag was | ||
593 | \c{<img src="/image.png">}, the context will not be used (because | ||
594 | QMimeSourceFactory recognizes that we have used an absolute path) | ||
595 | and will try to load "/image.png". The context is applied in exactly | ||
596 | the same way to \e hrefs, for example, | ||
597 | \c{<a href="target.html">Target</a>}, would resolve to | ||
598 | "path/to/look/in/target.html". | ||
599 | |||
600 | */ | ||
601 | |||
602 | QTextEdit::QTextEdit( const QString& text, const QString& context, | ||
603 | QWidget *parent, const char *name) | ||
604 | : QScrollView( parent, name, WStaticContents | WRepaintNoErase | WResizeNoErase ), | ||
605 | doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) | ||
606 | { | ||
607 | init(); | ||
608 | setText( text, context ); | ||
609 | } | ||
610 | |||
611 | /*! \reimp */ | ||
612 | |||
613 | QTextEdit::~QTextEdit() | ||
614 | { | ||
615 | delete undoRedoInfo.d; | ||
616 | undoRedoInfo.d = 0; | ||
617 | delete cursor; | ||
618 | delete doc; | ||
619 | delete d; | ||
620 | } | ||
621 | |||
622 | void QTextEdit::init() | ||
623 | { | ||
624 | setFrameStyle( Sunken ); | ||
625 | undoEnabled = TRUE; | ||
626 | readonly = TRUE; | ||
627 | setReadOnly( FALSE ); | ||
628 | d = new QTextEditPrivate; | ||
629 | connect( doc, SIGNAL( minimumWidthChanged( int ) ), | ||
630 | this, SLOT( documentWidthChanged( int ) ) ); | ||
631 | |||
632 | mousePressed = FALSE; | ||
633 | inDoubleClick = FALSE; | ||
634 | modified = FALSE; | ||
635 | onLink = QString::null; | ||
636 | overWrite = FALSE; | ||
637 | wrapMode = WidgetWidth; | ||
638 | wrapWidth = -1; | ||
639 | wPolicy = AtWhiteSpace; | ||
640 | inDnD = FALSE; | ||
641 | |||
642 | doc->setFormatter( new QTextFormatterBreakWords ); | ||
643 | currentFormat = doc->formatCollection()->defaultFormat(); | ||
644 | currentAlignment = Qt3::AlignAuto; | ||
645 | |||
646 | viewport()->setBackgroundMode( PaletteBase ); | ||
647 | viewport()->setAcceptDrops( TRUE ); | ||
648 | resizeContents( 0, doc->lastParag() ? | ||
649 | ( doc->lastParag()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 ); | ||
650 | |||
651 | setKeyCompression( TRUE ); | ||
652 | viewport()->setMouseTracking( TRUE ); | ||
653 | #ifndef QT_NO_CURSOR | ||
654 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
655 | #endif | ||
656 | cursor = new QTextCursor( doc ); | ||
657 | |||
658 | formatTimer = new QTimer( this ); | ||
659 | connect( formatTimer, SIGNAL( timeout() ), | ||
660 | this, SLOT( formatMore() ) ); | ||
661 | lastFormatted = doc->firstParag(); | ||
662 | |||
663 | scrollTimer = new QTimer( this ); | ||
664 | connect( scrollTimer, SIGNAL( timeout() ), | ||
665 | this, SLOT( autoScrollTimerDone() ) ); | ||
666 | |||
667 | interval = 0; | ||
668 | changeIntervalTimer = new QTimer( this ); | ||
669 | connect( changeIntervalTimer, SIGNAL( timeout() ), | ||
670 | this, SLOT( doChangeInterval() ) ); | ||
671 | |||
672 | cursorVisible = TRUE; | ||
673 | blinkTimer = new QTimer( this ); | ||
674 | connect( blinkTimer, SIGNAL( timeout() ), | ||
675 | this, SLOT( blinkCursor() ) ); | ||
676 | |||
677 | #ifndef QT_NO_DRAGANDDROP | ||
678 | dragStartTimer = new QTimer( this ); | ||
679 | connect( dragStartTimer, SIGNAL( timeout() ), | ||
680 | this, SLOT( startDrag() ) ); | ||
681 | #endif | ||
682 | |||
683 | |||
684 | formatMore(); | ||
685 | |||
686 | blinkCursorVisible = FALSE; | ||
687 | |||
688 | viewport()->setFocusProxy( this ); | ||
689 | viewport()->setFocusPolicy( WheelFocus ); | ||
690 | viewport()->installEventFilter( this ); | ||
691 | installEventFilter( this ); | ||
692 | } | ||
693 | |||
694 | void QTextEdit::paintDocument( bool drawAll, QPainter *p, int cx, int cy, int cw, int ch ) | ||
695 | { | ||
696 | bool drawCur = hasFocus() || viewport()->hasFocus(); | ||
697 | if ( hasSelectedText() || isReadOnly() || !cursorVisible ) | ||
698 | drawCur = FALSE; | ||
699 | QColorGroup g = colorGroup(); | ||
700 | if ( doc->paper() ) | ||
701 | g.setBrush( QColorGroup::Base, *doc->paper() ); | ||
702 | |||
703 | if ( contentsY() < doc->y() ) { | ||
704 | p->fillRect( contentsX(), contentsY(), visibleWidth(), doc->y(), | ||
705 | g.brush( QColorGroup::Base ) ); | ||
706 | } | ||
707 | if ( drawAll && doc->width() - contentsX() < cx + cw ) { | ||
708 | p->fillRect( doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch, | ||
709 | g.brush( QColorGroup::Base ) ); | ||
710 | } | ||
711 | |||
712 | p->setBrushOrigin( -contentsX(), -contentsY() ); | ||
713 | |||
714 | lastFormatted = doc->draw( p, cx, cy, cw, ch, g, !drawAll, drawCur, cursor ); | ||
715 | |||
716 | if ( lastFormatted == doc->lastParag() ) | ||
717 | resizeContents( contentsWidth(), doc->height() ); | ||
718 | |||
719 | if ( contentsHeight() < visibleHeight() && ( !doc->lastParag() || doc->lastParag()->isValid() ) && drawAll ) | ||
720 | p->fillRect( 0, contentsHeight(), visibleWidth(), | ||
721 | visibleHeight() - contentsHeight(), g.brush( QColorGroup::Base ) ); | ||
722 | } | ||
723 | |||
724 | /*! \reimp */ | ||
725 | |||
726 | void QTextEdit::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) | ||
727 | { | ||
728 | paintDocument( TRUE, p, cx, cy, cw, ch ); | ||
729 | int v; | ||
730 | p->setPen( foregroundColor() ); | ||
731 | if ( document()->isPageBreakEnabled() && ( v = document()->flow()->pageSize() ) > 0 ) { | ||
732 | int l = int(cy / v) * v; | ||
733 | while ( l < cy + ch ) { | ||
734 | p->drawLine( cx, l, cx + cw - 1, l ); | ||
735 | l += v; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | } | ||
740 | |||
741 | /*! \reimp */ | ||
742 | |||
743 | void QTextEdit::drawContents( QPainter * ) | ||
744 | { | ||
745 | } | ||
746 | |||
747 | /*! \reimp */ | ||
748 | |||
749 | bool QTextEdit::event( QEvent *e ) | ||
750 | { | ||
751 | if ( e->type() == QEvent::AccelOverride && !isReadOnly() ) { | ||
752 | QKeyEvent* ke = (QKeyEvent*) e; | ||
753 | if ( ke->state() == NoButton || ke->state() == Keypad ) { | ||
754 | if ( ke->key() < Key_Escape ) { | ||
755 | ke->accept(); | ||
756 | } else { | ||
757 | switch ( ke->key() ) { | ||
758 | case Key_Return: | ||
759 | case Key_Enter: | ||
760 | case Key_Delete: | ||
761 | case Key_Home: | ||
762 | case Key_End: | ||
763 | case Key_Backspace: | ||
764 | ke->accept(); | ||
765 | default: | ||
766 | break; | ||
767 | } | ||
768 | } | ||
769 | } else if ( ke->state() & ControlButton ) { | ||
770 | switch ( ke->key() ) { | ||
771 | // Those are too frequently used for application functionality | ||
772 | /* case Key_A: | ||
773 | case Key_B: | ||
774 | case Key_D: | ||
775 | case Key_E: | ||
776 | case Key_F: | ||
777 | case Key_H: | ||
778 | case Key_I: | ||
779 | case Key_K: | ||
780 | case Key_N: | ||
781 | case Key_P: | ||
782 | case Key_T: | ||
783 | */ | ||
784 | case Key_C: | ||
785 | case Key_V: | ||
786 | case Key_X: | ||
787 | case Key_Y: | ||
788 | case Key_Z: | ||
789 | case Key_Left: | ||
790 | case Key_Right: | ||
791 | case Key_Up: | ||
792 | case Key_Down: | ||
793 | case Key_Home: | ||
794 | case Key_End: | ||
795 | case Key_Tab: | ||
796 | #if defined (Q_WS_WIN) | ||
797 | case Key_Insert: | ||
798 | case Key_Delete: | ||
799 | #endif | ||
800 | ke->accept(); | ||
801 | default: | ||
802 | break; | ||
803 | } | ||
804 | } else { | ||
805 | switch ( ke->key() ) { | ||
806 | #if defined (Q_WS_WIN) | ||
807 | case Key_Insert: | ||
808 | ke->accept(); | ||
809 | #endif | ||
810 | default: | ||
811 | break; | ||
812 | } | ||
813 | } | ||
814 | } | ||
815 | |||
816 | if ( e->type() == QEvent::Show && d->ensureCursorVisibleInShowEvent ) { | ||
817 | sync(); | ||
818 | ensureCursorVisible(); | ||
819 | d->ensureCursorVisibleInShowEvent = FALSE; | ||
820 | } | ||
821 | return QWidget::event( e ); | ||
822 | } | ||
823 | |||
824 | /*! | ||
825 | Processes the key event, \a e. | ||
826 | By default key events are used to provide keyboard navigation and | ||
827 | text editing. | ||
828 | */ | ||
829 | |||
830 | void QTextEdit::keyPressEvent( QKeyEvent *e ) | ||
831 | { | ||
832 | changeIntervalTimer->stop(); | ||
833 | interval = 10; | ||
834 | bool unknown = FALSE; | ||
835 | if ( isReadOnly() ) { | ||
836 | if ( !handleReadOnlyKeyEvent( e ) ) | ||
837 | QScrollView::keyPressEvent( e ); | ||
838 | changeIntervalTimer->start( 100, TRUE ); | ||
839 | return; | ||
840 | } | ||
841 | |||
842 | |||
843 | bool selChanged = FALSE; | ||
844 | for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection | ||
845 | selChanged = doc->removeSelection( i ) || selChanged; | ||
846 | |||
847 | if ( selChanged ) { | ||
848 | cursor->parag()->document()->nextDoubleBuffered = TRUE; | ||
849 | repaintChanged(); | ||
850 | } | ||
851 | |||
852 | bool clearUndoRedoInfo = TRUE; | ||
853 | |||
854 | |||
855 | switch ( e->key() ) { | ||
856 | case Key_Left: | ||
857 | case Key_Right: { | ||
858 | // a bit hacky, but can't change this without introducing new enum values for move and keeping the | ||
859 | // correct semantics and movement for BiDi and non BiDi text. | ||
860 | CursorAction a; | ||
861 | if ( cursor->parag()->string()->isRightToLeft() == (e->key() == Key_Right) ) | ||
862 | a = e->state() & ControlButton ? MoveWordBackward : MoveBackward; | ||
863 | else | ||
864 | a = e->state() & ControlButton ? MoveWordForward : MoveForward; | ||
865 | moveCursor( a, e->state() & ShiftButton ); | ||
866 | break; | ||
867 | } | ||
868 | case Key_Up: | ||
869 | moveCursor( e->state() & ControlButton ? MovePgUp : MoveUp, e->state() & ShiftButton ); | ||
870 | break; | ||
871 | case Key_Down: | ||
872 | moveCursor( e->state() & ControlButton ? MovePgDown : MoveDown, e->state() & ShiftButton ); | ||
873 | break; | ||
874 | case Key_Home: | ||
875 | moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton ); | ||
876 | break; | ||
877 | case Key_End: | ||
878 | moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton ); | ||
879 | break; | ||
880 | case Key_Prior: | ||
881 | moveCursor( MovePgUp, e->state() & ShiftButton ); | ||
882 | break; | ||
883 | case Key_Next: | ||
884 | moveCursor( MovePgDown, e->state() & ShiftButton ); | ||
885 | break; | ||
886 | case Key_Return: case Key_Enter: | ||
887 | if ( doc->hasSelection( QTextDocument::Standard, FALSE ) ) | ||
888 | removeSelectedText(); | ||
889 | #ifndef QT_NO_CURSOR | ||
890 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
891 | #endif | ||
892 | clearUndoRedoInfo = FALSE; | ||
893 | doKeyboardAction( ActionReturn ); | ||
894 | emit returnPressed(); | ||
895 | break; | ||
896 | case Key_Delete: | ||
897 | #if defined (Q_WS_WIN) | ||
898 | if ( e->state() & ShiftButton ) { | ||
899 | cut(); | ||
900 | break; | ||
901 | } else | ||
902 | #endif | ||
903 | if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { | ||
904 | removeSelectedText(); | ||
905 | break; | ||
906 | } | ||
907 | doKeyboardAction( ActionDelete ); | ||
908 | clearUndoRedoInfo = FALSE; | ||
909 | |||
910 | break; | ||
911 | case Key_Insert: | ||
912 | if ( e->state() & ShiftButton ) | ||
913 | paste(); | ||
914 | break; | ||
915 | case Key_Backspace: | ||
916 | if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { | ||
917 | removeSelectedText(); | ||
918 | break; | ||
919 | } | ||
920 | |||
921 | if ( !cursor->parag()->prev() && | ||
922 | cursor->atParagStart() ) | ||
923 | break; | ||
924 | |||
925 | doKeyboardAction( ActionBackspace ); | ||
926 | clearUndoRedoInfo = FALSE; | ||
927 | |||
928 | break; | ||
929 | case Key_F16: // Copy key on Sun keyboards | ||
930 | copy(); | ||
931 | break; | ||
932 | case Key_F18: // Paste key on Sun keyboards | ||
933 | paste(); | ||
934 | break; | ||
935 | case Key_F20: // Cut key on Sun keyboards | ||
936 | cut(); | ||
937 | break; | ||
938 | default: { | ||
939 | if ( e->text().length() && | ||
940 | ( !( e->state() & ControlButton ) && | ||
941 | !( e->state() & AltButton ) || | ||
942 | ( ( e->state() & ControlButton | AltButton ) == (ControlButton|AltButton) ) ) && | ||
943 | ( !e->ascii() || e->ascii() >= 32 || e->text() == "\t" ) ) { | ||
944 | clearUndoRedoInfo = FALSE; | ||
945 | if ( e->key() == Key_Tab ) { | ||
946 | if ( textFormat() == Qt::RichText && | ||
947 | cursor->index() == 0 && cursor->parag()->style() && | ||
948 | cursor->parag()->style()->displayMode() == | ||
949 | QStyleSheetItem::DisplayListItem ) { | ||
950 | cursor->parag()->incDepth(); | ||
951 | drawCursor( FALSE ); | ||
952 | repaintChanged(); | ||
953 | drawCursor( TRUE ); | ||
954 | break; | ||
955 | } | ||
956 | } | ||
957 | if ( textFormat() == Qt::RichText && ( !cursor->parag()->style() || | ||
958 | cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayBlock ) && | ||
959 | cursor->index() == 0 && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) { | ||
960 | setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc ); | ||
961 | cursor->parag()->incDepth(); | ||
962 | drawCursor( FALSE ); | ||
963 | repaintChanged(); | ||
964 | drawCursor( TRUE ); | ||
965 | } else { | ||
966 | if ( overWrite && !cursor->atParagEnd() ) | ||
967 | cursor->remove(); | ||
968 | QString t = e->text(); | ||
969 | QTextParag *p = cursor->parag(); | ||
970 | if ( p && p->string() && p->string()->isRightToLeft() ) { | ||
971 | QChar *c = (QChar *)t.unicode(); | ||
972 | int l = t.length(); | ||
973 | while( l-- ) { | ||
974 | if ( c->mirrored() ) | ||
975 | *c = c->mirroredChar(); | ||
976 | c++; | ||
977 | } | ||
978 | } | ||
979 | insert( t, TRUE, FALSE, TRUE ); | ||
980 | } | ||
981 | break; | ||
982 | } else if ( e->state() & ControlButton ) { | ||
983 | switch ( e->key() ) { | ||
984 | case Key_C: case Key_F16: // Copy key on Sun keyboards | ||
985 | copy(); | ||
986 | break; | ||
987 | case Key_V: | ||
988 | paste(); | ||
989 | break; | ||
990 | case Key_X: | ||
991 | cut(); | ||
992 | break; | ||
993 | case Key_I: case Key_T: case Key_Tab: | ||
994 | indent(); | ||
995 | break; | ||
996 | case Key_A: | ||
997 | #if defined(Q_WS_X11) | ||
998 | moveCursor( MoveLineStart, e->state() & ShiftButton ); | ||
999 | #else | ||
1000 | selectAll( TRUE ); | ||
1001 | #endif | ||
1002 | break; | ||
1003 | case Key_B: | ||
1004 | moveCursor( MoveBackward, e->state() & ShiftButton ); | ||
1005 | break; | ||
1006 | case Key_F: | ||
1007 | moveCursor( MoveForward, e->state() & ShiftButton ); | ||
1008 | break; | ||
1009 | case Key_D: | ||
1010 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
1011 | removeSelectedText(); | ||
1012 | break; | ||
1013 | } | ||
1014 | doKeyboardAction( ActionDelete ); | ||
1015 | clearUndoRedoInfo = FALSE; | ||
1016 | break; | ||
1017 | case Key_H: | ||
1018 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
1019 | removeSelectedText(); | ||
1020 | break; | ||
1021 | } | ||
1022 | if ( !cursor->parag()->prev() && | ||
1023 | cursor->atParagStart() ) | ||
1024 | break; | ||
1025 | |||
1026 | doKeyboardAction( ActionBackspace ); | ||
1027 | clearUndoRedoInfo = FALSE; | ||
1028 | break; | ||
1029 | case Key_E: | ||
1030 | moveCursor( MoveLineEnd, e->state() & ShiftButton ); | ||
1031 | break; | ||
1032 | case Key_N: | ||
1033 | moveCursor( MoveDown, e->state() & ShiftButton ); | ||
1034 | break; | ||
1035 | case Key_P: | ||
1036 | moveCursor( MoveUp, e->state() & ShiftButton ); | ||
1037 | break; | ||
1038 | case Key_Z: | ||
1039 | undo(); | ||
1040 | break; | ||
1041 | case Key_Y: | ||
1042 | redo(); | ||
1043 | break; | ||
1044 | case Key_K: | ||
1045 | doKeyboardAction( ActionKill ); | ||
1046 | break; | ||
1047 | #if defined(Q_WS_WIN) | ||
1048 | case Key_Insert: | ||
1049 | copy(); | ||
1050 | break; | ||
1051 | case Key_Delete: | ||
1052 | del(); | ||
1053 | break; | ||
1054 | #endif | ||
1055 | default: | ||
1056 | unknown = FALSE; | ||
1057 | break; | ||
1058 | } | ||
1059 | } else { | ||
1060 | unknown = TRUE; | ||
1061 | } | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | emit cursorPositionChanged( cursor ); | ||
1066 | emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); | ||
1067 | if ( clearUndoRedoInfo ) | ||
1068 | clearUndoRedo(); | ||
1069 | changeIntervalTimer->start( 100, TRUE ); | ||
1070 | if ( unknown ) | ||
1071 | e->ignore(); | ||
1072 | } | ||
1073 | |||
1074 | /*! | ||
1075 | Executes keyboard action \a action. This is normally called by | ||
1076 | a key event handler. | ||
1077 | */ | ||
1078 | |||
1079 | void QTextEdit::doKeyboardAction( KeyboardAction action ) | ||
1080 | { | ||
1081 | if ( isReadOnly() ) | ||
1082 | return; | ||
1083 | |||
1084 | if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough | ||
1085 | return; | ||
1086 | |||
1087 | lastFormatted = cursor->parag(); | ||
1088 | drawCursor( FALSE ); | ||
1089 | bool doUpdateCurrentFormat = TRUE; | ||
1090 | |||
1091 | switch ( action ) { | ||
1092 | case ActionDelete: { | ||
1093 | checkUndoRedoInfo( UndoRedoInfo::Delete ); | ||
1094 | if ( !undoRedoInfo.valid() ) { | ||
1095 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
1096 | undoRedoInfo.index = cursor->index(); | ||
1097 | undoRedoInfo.d->text = QString::null; | ||
1098 | } | ||
1099 | undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c; | ||
1100 | if ( cursor->parag()->at( cursor->index() )->format() ) { | ||
1101 | cursor->parag()->at( cursor->index() )->format()->addRef(); | ||
1102 | undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); | ||
1103 | } | ||
1104 | QTextParag *old = cursor->parag(); | ||
1105 | if ( cursor->remove() ) { | ||
1106 | if ( old != cursor->parag() && lastFormatted == old ) | ||
1107 | lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0; | ||
1108 | undoRedoInfo.d->text += "\n"; | ||
1109 | } | ||
1110 | } break; | ||
1111 | case ActionBackspace: | ||
1112 | if ( textFormat() == Qt::RichText && | ||
1113 | cursor->parag()->style() && | ||
1114 | cursor->parag()->style()->displayMode() == QStyleSheetItem::DisplayListItem && | ||
1115 | cursor->index() == 0 ) { | ||
1116 | cursor->parag()->decDepth(); | ||
1117 | lastFormatted = cursor->parag(); | ||
1118 | repaintChanged(); | ||
1119 | drawCursor( TRUE ); | ||
1120 | return; | ||
1121 | } | ||
1122 | checkUndoRedoInfo( UndoRedoInfo::Delete ); | ||
1123 | if ( !undoRedoInfo.valid() ) { | ||
1124 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
1125 | undoRedoInfo.index = cursor->index(); | ||
1126 | undoRedoInfo.d->text = QString::null; | ||
1127 | } | ||
1128 | cursor->gotoPreviousLetter(); | ||
1129 | undoRedoInfo.d->text.prepend( QString( cursor->parag()->at( cursor->index() )->c ) ); | ||
1130 | if ( cursor->parag()->at( cursor->index() )->format() ) { | ||
1131 | cursor->parag()->at( cursor->index() )->format()->addRef(); | ||
1132 | undoRedoInfo.d->text.at( 0 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); | ||
1133 | } | ||
1134 | undoRedoInfo.index = cursor->index(); | ||
1135 | if ( cursor->remove() ) { | ||
1136 | undoRedoInfo.d->text.remove( 0, 1 ); | ||
1137 | undoRedoInfo.d->text.prepend( "\n" ); | ||
1138 | undoRedoInfo.index = cursor->index(); | ||
1139 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
1140 | } | ||
1141 | lastFormatted = cursor->parag(); | ||
1142 | break; | ||
1143 | case ActionReturn: { | ||
1144 | checkUndoRedoInfo( UndoRedoInfo::Return ); | ||
1145 | if ( !undoRedoInfo.valid() ) { | ||
1146 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
1147 | undoRedoInfo.index = cursor->index(); | ||
1148 | undoRedoInfo.d->text = QString::null; | ||
1149 | } | ||
1150 | undoRedoInfo.d->text += "\n"; | ||
1151 | cursor->splitAndInsertEmptyParag(); | ||
1152 | if ( cursor->parag()->prev() ) { | ||
1153 | lastFormatted = cursor->parag()->prev(); | ||
1154 | lastFormatted->invalidate( 0 ); | ||
1155 | } | ||
1156 | doUpdateCurrentFormat = FALSE; | ||
1157 | } break; | ||
1158 | case ActionKill: | ||
1159 | checkUndoRedoInfo( UndoRedoInfo::Delete ); | ||
1160 | if ( !undoRedoInfo.valid() ) { | ||
1161 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
1162 | undoRedoInfo.index = cursor->index(); | ||
1163 | undoRedoInfo.d->text = QString::null; | ||
1164 | } | ||
1165 | if ( cursor->atParagEnd() ) { | ||
1166 | undoRedoInfo.d->text += cursor->parag()->at( cursor->index() )->c; | ||
1167 | if ( cursor->parag()->at( cursor->index() )->format() ) { | ||
1168 | cursor->parag()->at( cursor->index() )->format()->addRef(); | ||
1169 | undoRedoInfo.d->text.at( undoRedoInfo.d->text.length() - 1 ).setFormat( cursor->parag()->at( cursor->index() )->format() ); | ||
1170 | } | ||
1171 | QTextParag *old = cursor->parag(); | ||
1172 | if ( cursor->remove() ) { | ||
1173 | if ( old != cursor->parag() && lastFormatted == old ) | ||
1174 | lastFormatted = cursor->parag() ? cursor->parag()->prev() : 0; | ||
1175 | undoRedoInfo.d->text += "\n"; | ||
1176 | } | ||
1177 | } else { | ||
1178 | int oldLen = undoRedoInfo.d->text.length(); | ||
1179 | undoRedoInfo.d->text += cursor->parag()->string()->toString().mid( cursor->index() ); | ||
1180 | for ( int i = cursor->index(); i < cursor->parag()->length(); ++i ) { | ||
1181 | if ( cursor->parag()->at( i )->format() ) { | ||
1182 | cursor->parag()->at( i )->format()->addRef(); | ||
1183 | undoRedoInfo.d->text.at( oldLen + i - cursor->index() ).setFormat( cursor->parag()->at( i )->format() ); | ||
1184 | } | ||
1185 | } | ||
1186 | undoRedoInfo.d->text.remove( undoRedoInfo.d->text.length() - 1, 1 ); | ||
1187 | cursor->killLine(); | ||
1188 | } | ||
1189 | break; | ||
1190 | } | ||
1191 | |||
1192 | formatMore(); | ||
1193 | repaintChanged(); | ||
1194 | ensureCursorVisible(); | ||
1195 | drawCursor( TRUE ); | ||
1196 | |||
1197 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
1198 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1199 | if ( !readonly ) { | ||
1200 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
1201 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
1202 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | if ( doUpdateCurrentFormat ) | ||
1207 | updateCurrentFormat(); | ||
1208 | setModified(); | ||
1209 | emit textChanged(); | ||
1210 | } | ||
1211 | |||
1212 | void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, int oldLen, QTextString &text, bool fillStyles ) | ||
1213 | { | ||
1214 | c2.restoreState(); | ||
1215 | c1.restoreState(); | ||
1216 | if ( c1.parag() == c2.parag() ) { | ||
1217 | for ( int i = c1.index(); i < c2.index(); ++i ) { | ||
1218 | if ( c1.parag()->at( i )->format() ) { | ||
1219 | c1.parag()->at( i )->format()->addRef(); | ||
1220 | text.at( oldLen + i - c1.index() ).setFormat( c1.parag()->at( i )->format() ); | ||
1221 | } | ||
1222 | } | ||
1223 | if ( fillStyles ) { | ||
1224 | undoRedoInfo.oldAligns[ 0 ] = c1.parag()->alignment(); | ||
1225 | undoRedoInfo.oldStyles << c1.parag()->styleSheetItems(); | ||
1226 | undoRedoInfo.oldListStyles << c1.parag()->listStyle(); | ||
1227 | } | ||
1228 | } else { | ||
1229 | int lastIndex = oldLen; | ||
1230 | int i; | ||
1231 | for ( i = c1.index(); i < c1.parag()->length(); ++i ) { | ||
1232 | if ( c1.parag()->at( i )->format() ) { | ||
1233 | c1.parag()->at( i )->format()->addRef(); | ||
1234 | text.at( lastIndex ).setFormat( c1.parag()->at( i )->format() ); | ||
1235 | lastIndex++; | ||
1236 | } | ||
1237 | } | ||
1238 | QTextParag *p = c1.parag()->next(); | ||
1239 | while ( p && p != c2.parag() ) { | ||
1240 | for ( int i = 0; i < p->length(); ++i ) { | ||
1241 | if ( p->at( i )->format() ) { | ||
1242 | p->at( i )->format()->addRef(); | ||
1243 | text.at( i + lastIndex ).setFormat( p->at( i )->format() ); | ||
1244 | } | ||
1245 | } | ||
1246 | lastIndex += p->length(); | ||
1247 | p = p->next(); | ||
1248 | } | ||
1249 | for ( i = 0; i < c2.index(); ++i ) { | ||
1250 | if ( c2.parag()->at( i )->format() ) { | ||
1251 | c2.parag()->at( i )->format()->addRef(); | ||
1252 | text.at( i + lastIndex ).setFormat( c2.parag()->at( i )->format() ); | ||
1253 | } | ||
1254 | } | ||
1255 | if ( fillStyles ) { | ||
1256 | QTextParag *p = c1.parag(); | ||
1257 | i = 0; | ||
1258 | while ( p ) { | ||
1259 | if ( i < (int)undoRedoInfo.oldAligns.size() ) | ||
1260 | undoRedoInfo.oldAligns[ i ] = p->alignment(); | ||
1261 | undoRedoInfo.oldStyles << p->styleSheetItems(); | ||
1262 | undoRedoInfo.oldListStyles << p->listStyle(); | ||
1263 | if ( p == c2.parag() ) | ||
1264 | break; | ||
1265 | p = p->next(); | ||
1266 | ++i; | ||
1267 | } | ||
1268 | } | ||
1269 | } | ||
1270 | } | ||
1271 | |||
1272 | /*! Removes the selection \a selNum (by default 0). This does not | ||
1273 | remove the selected text. | ||
1274 | |||
1275 | \sa removeSelectedText() | ||
1276 | */ | ||
1277 | |||
1278 | void QTextEdit::removeSelection( int selNum ) | ||
1279 | { | ||
1280 | doc->removeSelection( selNum ); | ||
1281 | repaintChanged(); | ||
1282 | } | ||
1283 | |||
1284 | /*! Deletes the selected text (i.e. the default selection's text) of | ||
1285 | the selection \a selNum (by default, 0). If there is no selected text | ||
1286 | nothing happens. | ||
1287 | |||
1288 | \sa selectedText removeSelection() | ||
1289 | */ | ||
1290 | |||
1291 | void QTextEdit::removeSelectedText( int selNum ) | ||
1292 | { | ||
1293 | if ( isReadOnly() ) | ||
1294 | return; | ||
1295 | |||
1296 | QTextCursor c1 = doc->selectionStartCursor( selNum ); | ||
1297 | QTextCursor c2 = doc->selectionEndCursor( selNum ); | ||
1298 | |||
1299 | // ### no support for editing tables yet | ||
1300 | if ( c1.nestedDepth() || c2.nestedDepth() ) | ||
1301 | return; | ||
1302 | |||
1303 | for ( int i = 0; i < (int)doc->numSelections(); ++i ) { | ||
1304 | if ( i == selNum ) | ||
1305 | continue; | ||
1306 | doc->removeSelection( i ); | ||
1307 | } | ||
1308 | |||
1309 | drawCursor( FALSE ); | ||
1310 | checkUndoRedoInfo( UndoRedoInfo::RemoveSelected ); | ||
1311 | if ( !undoRedoInfo.valid() ) { | ||
1312 | doc->selectionStart( selNum, undoRedoInfo.id, undoRedoInfo.index ); | ||
1313 | undoRedoInfo.d->text = QString::null; | ||
1314 | } | ||
1315 | int oldLen = undoRedoInfo.d->text.length(); | ||
1316 | undoRedoInfo.d->text = doc->selectedText( selNum, FALSE ); | ||
1317 | undoRedoInfo.oldAligns.resize( undoRedoInfo.oldAligns.size() + QMAX( 0, c2.parag()->paragId() - c1.parag()->paragId() + 1 ) ); | ||
1318 | readFormats( c1, c2, oldLen, undoRedoInfo.d->text, TRUE ); | ||
1319 | doc->removeSelectedText( selNum, cursor ); | ||
1320 | if ( cursor->isValid() ) { | ||
1321 | ensureCursorVisible(); | ||
1322 | lastFormatted = cursor->parag(); | ||
1323 | formatMore(); | ||
1324 | repaintChanged(); | ||
1325 | ensureCursorVisible(); | ||
1326 | drawCursor( TRUE ); | ||
1327 | clearUndoRedo(); | ||
1328 | #if defined(Q_WS_WIN) | ||
1329 | // there seems to be a problem with repainting or erasing the area | ||
1330 | // of the scrollview which is not the contents on windows | ||
1331 | if ( contentsHeight() < visibleHeight() ) | ||
1332 | viewport()->repaint( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), TRUE ); | ||
1333 | #endif | ||
1334 | #ifndef QT_NO_CURSOR | ||
1335 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
1336 | #endif | ||
1337 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
1338 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1339 | if ( !readonly ) { | ||
1340 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
1341 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
1342 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
1343 | } | ||
1344 | } | ||
1345 | } else { | ||
1346 | cursor->setDocument( doc ); | ||
1347 | cursor->setParag( doc->firstParag() ); | ||
1348 | cursor->setIndex( 0 ); | ||
1349 | drawCursor( TRUE ); | ||
1350 | viewport()->repaint( TRUE ); | ||
1351 | } | ||
1352 | setModified(); | ||
1353 | emit textChanged(); | ||
1354 | emit selectionChanged(); | ||
1355 | } | ||
1356 | |||
1357 | /*! Moves the text cursor according to \a action. This is normally | ||
1358 | used by some key event handler. \a select specifies whether the text | ||
1359 | between the current cursor position and the new position should be | ||
1360 | selected. | ||
1361 | */ | ||
1362 | |||
1363 | void QTextEdit::moveCursor( CursorAction action, bool select ) | ||
1364 | { | ||
1365 | drawCursor( FALSE ); | ||
1366 | if ( select ) { | ||
1367 | if ( !doc->hasSelection( QTextDocument::Standard ) ) | ||
1368 | doc->setSelectionStart( QTextDocument::Standard, cursor ); | ||
1369 | moveCursor( action ); | ||
1370 | if ( doc->setSelectionEnd( QTextDocument::Standard, cursor ) ) { | ||
1371 | cursor->parag()->document()->nextDoubleBuffered = TRUE; | ||
1372 | repaintChanged(); | ||
1373 | } else { | ||
1374 | drawCursor( TRUE ); | ||
1375 | } | ||
1376 | ensureCursorVisible(); | ||
1377 | emit selectionChanged(); | ||
1378 | emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); | ||
1379 | } else { | ||
1380 | bool redraw = doc->removeSelection( QTextDocument::Standard ); | ||
1381 | moveCursor( action ); | ||
1382 | if ( !redraw ) { | ||
1383 | ensureCursorVisible(); | ||
1384 | drawCursor( TRUE ); | ||
1385 | } else { | ||
1386 | cursor->parag()->document()->nextDoubleBuffered = TRUE; | ||
1387 | repaintChanged(); | ||
1388 | ensureCursorVisible(); | ||
1389 | drawCursor( TRUE ); | ||
1390 | #ifndef QT_NO_CURSOR | ||
1391 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
1392 | #endif | ||
1393 | } | ||
1394 | if ( redraw ) { | ||
1395 | emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); | ||
1396 | emit selectionChanged(); | ||
1397 | } | ||
1398 | } | ||
1399 | |||
1400 | drawCursor( TRUE ); | ||
1401 | updateCurrentFormat(); | ||
1402 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
1403 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1404 | if ( !readonly ) { | ||
1405 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
1406 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
1407 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
1408 | } | ||
1409 | } | ||
1410 | } | ||
1411 | |||
1412 | /*! \overload | ||
1413 | */ | ||
1414 | |||
1415 | void QTextEdit::moveCursor( CursorAction action ) | ||
1416 | { | ||
1417 | switch ( action ) { | ||
1418 | case MoveBackward: | ||
1419 | cursor->gotoPreviousLetter(); | ||
1420 | break; | ||
1421 | case MoveWordBackward: | ||
1422 | cursor->gotoPreviousWord(); | ||
1423 | break; | ||
1424 | case MoveForward: | ||
1425 | cursor->gotoNextLetter(); | ||
1426 | break; | ||
1427 | case MoveWordForward: | ||
1428 | cursor->gotoNextWord(); | ||
1429 | break; | ||
1430 | case MoveUp: | ||
1431 | cursor->gotoUp(); | ||
1432 | break; | ||
1433 | case MovePgUp: | ||
1434 | cursor->gotoPageUp( visibleHeight() ); | ||
1435 | break; | ||
1436 | case MoveDown: | ||
1437 | cursor->gotoDown(); | ||
1438 | break; | ||
1439 | case MovePgDown: | ||
1440 | cursor->gotoPageDown( visibleHeight() ); | ||
1441 | break; | ||
1442 | case MoveLineStart: | ||
1443 | cursor->gotoLineStart(); | ||
1444 | break; | ||
1445 | case MoveHome: | ||
1446 | cursor->gotoHome(); | ||
1447 | break; | ||
1448 | case MoveLineEnd: | ||
1449 | cursor->gotoLineEnd(); | ||
1450 | break; | ||
1451 | case MoveEnd: | ||
1452 | ensureFormatted( doc->lastParag() ); | ||
1453 | cursor->gotoEnd(); | ||
1454 | break; | ||
1455 | } | ||
1456 | |||
1457 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
1458 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1459 | if ( !readonly ) { | ||
1460 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
1461 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
1462 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
1463 | } | ||
1464 | } | ||
1465 | updateCurrentFormat(); | ||
1466 | } | ||
1467 | |||
1468 | /*! \reimp */ | ||
1469 | |||
1470 | void QTextEdit::resizeEvent( QResizeEvent *e ) | ||
1471 | { | ||
1472 | QScrollView::resizeEvent( e ); | ||
1473 | } | ||
1474 | |||
1475 | /*! \reimp */ | ||
1476 | |||
1477 | void QTextEdit::viewportResizeEvent( QResizeEvent *e ) | ||
1478 | { | ||
1479 | QScrollView::viewportResizeEvent( e ); | ||
1480 | if ( e->oldSize().width() != e->size().width() ) | ||
1481 | doResize(); | ||
1482 | } | ||
1483 | |||
1484 | static bool blockEnsureCursorVisible = FALSE; | ||
1485 | |||
1486 | /*! | ||
1487 | Ensures that the cursor is visible by scrolling the text edit if | ||
1488 | necessary. | ||
1489 | |||
1490 | \sa setCursorPosition() | ||
1491 | */ | ||
1492 | |||
1493 | void QTextEdit::ensureCursorVisible() | ||
1494 | { | ||
1495 | if ( blockEnsureCursorVisible ) | ||
1496 | return; | ||
1497 | if ( !isVisible() ) { | ||
1498 | d->ensureCursorVisibleInShowEvent = TRUE; | ||
1499 | return; | ||
1500 | } | ||
1501 | lastFormatted = cursor->parag(); | ||
1502 | formatMore(); | ||
1503 | QTextStringChar *chr = cursor->parag()->at( cursor->index() ); | ||
1504 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1505 | int x = cursor->parag()->rect().x() + chr->x + cursor->offsetX(); | ||
1506 | int y = 0; int dummy; | ||
1507 | cursor->parag()->lineHeightOfChar( cursor->index(), &dummy, &y ); | ||
1508 | y += cursor->parag()->rect().y() + cursor->offsetY(); | ||
1509 | int w = 1; | ||
1510 | ensureVisible( x, y + h / 2, w, h / 2 + 2 ); | ||
1511 | } | ||
1512 | |||
1513 | /*! | ||
1514 | \internal | ||
1515 | */ | ||
1516 | void QTextEdit::drawCursor( bool visible ) | ||
1517 | { | ||
1518 | if ( !isUpdatesEnabled() || | ||
1519 | !viewport()->isUpdatesEnabled() || | ||
1520 | !cursor->parag() || | ||
1521 | !cursor->parag()->isValid() || | ||
1522 | !selectedText().isEmpty() || | ||
1523 | ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) || | ||
1524 | isReadOnly() ) | ||
1525 | return; | ||
1526 | |||
1527 | QPainter p( viewport() ); | ||
1528 | QRect r( cursor->topParag()->rect() ); | ||
1529 | cursor->parag()->setChanged( TRUE ); | ||
1530 | p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() ); | ||
1531 | QPixmap *pix = 0; | ||
1532 | QColorGroup cg( colorGroup() ); | ||
1533 | if ( cursor->parag()->background() ) | ||
1534 | cg.setBrush( QColorGroup::Base, *cursor->parag()->background() ); | ||
1535 | else if ( doc->paper() ) | ||
1536 | cg.setBrush( QColorGroup::Base, *doc->paper() ); | ||
1537 | p.setBrushOrigin( -contentsX(), -contentsY() ); | ||
1538 | cursor->parag()->document()->nextDoubleBuffered = TRUE; | ||
1539 | if ( !cursor->nestedDepth() ) { | ||
1540 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1541 | int dist = 5; | ||
1542 | if ( ( cursor->parag()->alignment() & Qt3::AlignJustify ) == Qt3::AlignJustify ) | ||
1543 | dist = 50; | ||
1544 | int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist; | ||
1545 | x = QMAX( x, 0 ); | ||
1546 | p.setClipRect( QRect( x - contentsX(), | ||
1547 | r.y() - cursor->totalOffsetY() + cursor->y() - contentsY(), 2 * dist, h ) ); | ||
1548 | doc->drawParag( &p, cursor->parag(), x, | ||
1549 | r.y() - cursor->totalOffsetY() + cursor->y(), 2 * dist, h, pix, cg, visible, cursor ); | ||
1550 | } else { | ||
1551 | doc->drawParag( &p, cursor->parag(), r.x() - cursor->totalOffsetX(), | ||
1552 | r.y() - cursor->totalOffsetY(), r.width(), r.height(), | ||
1553 | pix, cg, visible, cursor ); | ||
1554 | } | ||
1555 | cursorVisible = visible; | ||
1556 | } | ||
1557 | |||
1558 | enum { | ||
1559 | IdUndo = 0, | ||
1560 | IdRedo = 1, | ||
1561 | IdCut = 2, | ||
1562 | IdCopy = 3, | ||
1563 | IdPaste = 4, | ||
1564 | IdClear = 5, | ||
1565 | IdSelectAll = 6 | ||
1566 | }; | ||
1567 | |||
1568 | /*! \reimp */ | ||
1569 | #ifndef QT_NO_WHEELEVENT | ||
1570 | void QTextEdit::contentsWheelEvent( QWheelEvent *e ) | ||
1571 | { | ||
1572 | if ( isReadOnly() ) { | ||
1573 | if ( e->state() & ControlButton ) { | ||
1574 | if ( e->delta() > 0 ) | ||
1575 | zoomOut(); | ||
1576 | else if ( e->delta() < 0 ) | ||
1577 | zoomIn(); | ||
1578 | return; | ||
1579 | } | ||
1580 | } | ||
1581 | QScrollView::contentsWheelEvent( e ); | ||
1582 | } | ||
1583 | #endif | ||
1584 | |||
1585 | /*! \reimp */ | ||
1586 | |||
1587 | void QTextEdit::contentsMousePressEvent( QMouseEvent *e ) | ||
1588 | { | ||
1589 | clearUndoRedo(); | ||
1590 | QTextCursor oldCursor = *cursor; | ||
1591 | QTextCursor c = *cursor; | ||
1592 | mousePos = e->pos(); | ||
1593 | mightStartDrag = FALSE; | ||
1594 | pressedLink = QString::null; | ||
1595 | |||
1596 | if ( e->button() == LeftButton ) { | ||
1597 | mousePressed = TRUE; | ||
1598 | drawCursor( FALSE ); | ||
1599 | placeCursor( e->pos() ); | ||
1600 | ensureCursorVisible(); | ||
1601 | |||
1602 | if ( isReadOnly() && linksEnabled() ) { | ||
1603 | QTextCursor c = *cursor; | ||
1604 | placeCursor( e->pos(), &c, TRUE ); | ||
1605 | if ( c.parag() && c.parag()->at( c.index() ) && | ||
1606 | c.parag()->at( c.index() )->isAnchor() ) { | ||
1607 | pressedLink = c.parag()->at( c.index() )->anchorHref(); | ||
1608 | } | ||
1609 | } | ||
1610 | |||
1611 | #ifndef QT_NO_DRAGANDDROP | ||
1612 | if ( doc->inSelection( QTextDocument::Standard, e->pos() ) ) { | ||
1613 | mightStartDrag = TRUE; | ||
1614 | drawCursor( TRUE ); | ||
1615 | dragStartTimer->start( QApplication::startDragTime(), TRUE ); | ||
1616 | dragStartPos = e->pos(); | ||
1617 | return; | ||
1618 | } | ||
1619 | #endif | ||
1620 | |||
1621 | bool redraw = FALSE; | ||
1622 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
1623 | if ( !( e->state() & ShiftButton ) ) { | ||
1624 | redraw = doc->removeSelection( QTextDocument::Standard ); | ||
1625 | doc->setSelectionStart( QTextDocument::Standard, cursor ); | ||
1626 | } else { | ||
1627 | redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; | ||
1628 | } | ||
1629 | } else { | ||
1630 | if ( isReadOnly() || !( e->state() & ShiftButton ) ) { | ||
1631 | doc->setSelectionStart( QTextDocument::Standard, cursor ); | ||
1632 | } else { | ||
1633 | doc->setSelectionStart( QTextDocument::Standard, &c ); | ||
1634 | redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; | ||
1635 | } | ||
1636 | } | ||
1637 | |||
1638 | for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection | ||
1639 | redraw = doc->removeSelection( i ) || redraw; | ||
1640 | |||
1641 | if ( !redraw ) { | ||
1642 | drawCursor( TRUE ); | ||
1643 | } else { | ||
1644 | repaintChanged(); | ||
1645 | #ifndef QT_NO_CURSOR | ||
1646 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
1647 | #endif | ||
1648 | } | ||
1649 | } else if ( e->button() == MidButton ) { | ||
1650 | bool redraw = doc->removeSelection( QTextDocument::Standard ); | ||
1651 | if ( !redraw ) { | ||
1652 | drawCursor( TRUE ); | ||
1653 | } else { | ||
1654 | repaintChanged(); | ||
1655 | #ifndef QT_NO_CURSOR | ||
1656 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
1657 | #endif | ||
1658 | } | ||
1659 | } | ||
1660 | |||
1661 | if ( *cursor != oldCursor ) | ||
1662 | updateCurrentFormat(); | ||
1663 | } | ||
1664 | |||
1665 | /*! \reimp */ | ||
1666 | |||
1667 | void QTextEdit::contentsMouseMoveEvent( QMouseEvent *e ) | ||
1668 | { | ||
1669 | if ( mousePressed ) { | ||
1670 | #ifndef QT_NO_DRAGANDDROP | ||
1671 | if ( mightStartDrag ) { | ||
1672 | dragStartTimer->stop(); | ||
1673 | if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() ) | ||
1674 | startDrag(); | ||
1675 | #ifndef QT_NO_CURSOR | ||
1676 | if ( !isReadOnly() ) | ||
1677 | viewport()->setCursor( ibeamCursor ); | ||
1678 | #endif | ||
1679 | return; | ||
1680 | } | ||
1681 | #endif | ||
1682 | mousePos = e->pos(); | ||
1683 | handleMouseMove( mousePos ); | ||
1684 | oldMousePos = mousePos; | ||
1685 | } | ||
1686 | |||
1687 | #ifndef QT_NO_CURSOR | ||
1688 | if ( !isReadOnly() && !mousePressed ) { | ||
1689 | if ( doc->hasSelection( QTextDocument::Standard ) && doc->inSelection( QTextDocument::Standard, e->pos() ) ) | ||
1690 | viewport()->setCursor( arrowCursor ); | ||
1691 | else | ||
1692 | viewport()->setCursor( ibeamCursor ); | ||
1693 | } | ||
1694 | #endif | ||
1695 | updateCursor( e->pos() ); | ||
1696 | } | ||
1697 | |||
1698 | /*! \reimp */ | ||
1699 | |||
1700 | void QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e ) | ||
1701 | { | ||
1702 | QTextCursor oldCursor = *cursor; | ||
1703 | if ( scrollTimer->isActive() ) | ||
1704 | scrollTimer->stop(); | ||
1705 | #ifndef QT_NO_DRAGANDDROP | ||
1706 | if ( dragStartTimer->isActive() ) | ||
1707 | dragStartTimer->stop(); | ||
1708 | if ( mightStartDrag ) { | ||
1709 | selectAll( FALSE ); | ||
1710 | mousePressed = FALSE; | ||
1711 | } | ||
1712 | #endif | ||
1713 | if ( mousePressed ) { | ||
1714 | mousePressed = FALSE; | ||
1715 | } | ||
1716 | emit cursorPositionChanged( cursor ); | ||
1717 | emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); | ||
1718 | if ( oldCursor != *cursor ) | ||
1719 | updateCurrentFormat(); | ||
1720 | inDoubleClick = FALSE; | ||
1721 | |||
1722 | #ifndef QT_NO_NETWORKPROTOCOL | ||
1723 | if ( !onLink.isEmpty() && onLink == pressedLink && linksEnabled() ) { | ||
1724 | QUrl u( doc->context(), onLink, TRUE ); | ||
1725 | emitLinkClicked( u.toString( FALSE, FALSE ) ); | ||
1726 | |||
1727 | // emitting linkClicked() may result in that the cursor winds | ||
1728 | // up hovering over a different valid link - check this and | ||
1729 | // set the appropriate cursor shape | ||
1730 | updateCursor( e->pos() ); | ||
1731 | } | ||
1732 | #endif | ||
1733 | drawCursor( TRUE ); | ||
1734 | if ( !doc->hasSelection( QTextDocument::Standard, TRUE ) ) | ||
1735 | doc->removeSelection( QTextDocument::Standard ); | ||
1736 | |||
1737 | emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); | ||
1738 | emit selectionChanged(); | ||
1739 | } | ||
1740 | |||
1741 | /*! \reimp */ | ||
1742 | |||
1743 | void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * ) | ||
1744 | { | ||
1745 | QTextCursor c1 = *cursor; | ||
1746 | QTextCursor c2 = *cursor; | ||
1747 | if ( cursor->index() > 0 && !cursor->parag()->at( cursor->index()-1 )->c.isSpace() ) | ||
1748 | c1.gotoPreviousWord(); | ||
1749 | if ( !cursor->parag()->at( cursor->index() )->c.isSpace() && !cursor->atParagEnd() ) | ||
1750 | c2.gotoNextWord(); | ||
1751 | |||
1752 | doc->setSelectionStart( QTextDocument::Standard, &c1 ); | ||
1753 | doc->setSelectionEnd( QTextDocument::Standard, &c2 ); | ||
1754 | |||
1755 | *cursor = c2; | ||
1756 | |||
1757 | repaintChanged(); | ||
1758 | |||
1759 | inDoubleClick = TRUE; | ||
1760 | mousePressed = TRUE; | ||
1761 | } | ||
1762 | |||
1763 | #ifndef QT_NO_DRAGANDDROP | ||
1764 | |||
1765 | /*! \reimp */ | ||
1766 | |||
1767 | void QTextEdit::contentsDragEnterEvent( QDragEnterEvent *e ) | ||
1768 | { | ||
1769 | if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { | ||
1770 | e->ignore(); | ||
1771 | return; | ||
1772 | } | ||
1773 | e->acceptAction(); | ||
1774 | inDnD = TRUE; | ||
1775 | } | ||
1776 | |||
1777 | /*! \reimp */ | ||
1778 | |||
1779 | void QTextEdit::contentsDragMoveEvent( QDragMoveEvent *e ) | ||
1780 | { | ||
1781 | if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { | ||
1782 | e->ignore(); | ||
1783 | return; | ||
1784 | } | ||
1785 | drawCursor( FALSE ); | ||
1786 | placeCursor( e->pos(), cursor ); | ||
1787 | drawCursor( TRUE ); | ||
1788 | e->acceptAction(); | ||
1789 | } | ||
1790 | |||
1791 | /*! \reimp */ | ||
1792 | |||
1793 | void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * ) | ||
1794 | { | ||
1795 | inDnD = FALSE; | ||
1796 | } | ||
1797 | |||
1798 | /*! \reimp */ | ||
1799 | |||
1800 | void QTextEdit::contentsDropEvent( QDropEvent *e ) | ||
1801 | { | ||
1802 | if ( isReadOnly() ) | ||
1803 | return; | ||
1804 | inDnD = FALSE; | ||
1805 | e->acceptAction(); | ||
1806 | QString text; | ||
1807 | bool intern = FALSE; | ||
1808 | if ( QTextDrag::decode( e, text ) ) { | ||
1809 | if ( ( e->source() == this || | ||
1810 | e->source() == viewport() ) && | ||
1811 | e->action() == QDropEvent::Move ) { | ||
1812 | removeSelectedText(); | ||
1813 | intern = TRUE; | ||
1814 | } else { | ||
1815 | doc->removeSelection( QTextDocument::Standard ); | ||
1816 | #ifndef QT_NO_CURSOR | ||
1817 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
1818 | #endif | ||
1819 | } | ||
1820 | drawCursor( FALSE ); | ||
1821 | placeCursor( e->pos(), cursor ); | ||
1822 | drawCursor( TRUE ); | ||
1823 | if ( !cursor->nestedDepth() ) { | ||
1824 | insert( text, FALSE, TRUE, FALSE ); | ||
1825 | } else { | ||
1826 | if ( intern ) | ||
1827 | undo(); | ||
1828 | e->ignore(); | ||
1829 | } | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | #endif | ||
1834 | |||
1835 | void QTextEdit::autoScrollTimerDone() | ||
1836 | { | ||
1837 | if ( mousePressed ) | ||
1838 | handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) ); | ||
1839 | } | ||
1840 | |||
1841 | void QTextEdit::handleMouseMove( const QPoint& pos ) | ||
1842 | { | ||
1843 | if ( !mousePressed ) | ||
1844 | return; | ||
1845 | |||
1846 | if ( !scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight() ) | ||
1847 | scrollTimer->start( 100, FALSE ); | ||
1848 | else if ( scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight() ) | ||
1849 | scrollTimer->stop(); | ||
1850 | |||
1851 | drawCursor( FALSE ); | ||
1852 | QTextCursor oldCursor = *cursor; | ||
1853 | |||
1854 | placeCursor( pos ); | ||
1855 | |||
1856 | if ( inDoubleClick ) { | ||
1857 | QTextCursor cl = *cursor; | ||
1858 | cl.gotoPreviousWord(); | ||
1859 | QTextCursor cr = *cursor; | ||
1860 | cr.gotoNextWord(); | ||
1861 | |||
1862 | int diff = QABS( oldCursor.parag()->at( oldCursor.index() )->x - mousePos.x() ); | ||
1863 | int ldiff = QABS( cl.parag()->at( cl.index() )->x - mousePos.x() ); | ||
1864 | int rdiff = QABS( cr.parag()->at( cr.index() )->x - mousePos.x() ); | ||
1865 | |||
1866 | |||
1867 | if ( cursor->parag()->lineStartOfChar( cursor->index() ) != | ||
1868 | oldCursor.parag()->lineStartOfChar( oldCursor.index() ) ) | ||
1869 | diff = 0xFFFFFF; | ||
1870 | |||
1871 | if ( rdiff < diff && rdiff < ldiff ) | ||
1872 | *cursor = cr; | ||
1873 | else if ( ldiff < diff && ldiff < rdiff ) | ||
1874 | *cursor = cl; | ||
1875 | else | ||
1876 | *cursor = oldCursor; | ||
1877 | |||
1878 | } | ||
1879 | ensureCursorVisible(); | ||
1880 | |||
1881 | bool redraw = FALSE; | ||
1882 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
1883 | redraw = doc->setSelectionEnd( QTextDocument::Standard, cursor ) || redraw; | ||
1884 | } | ||
1885 | |||
1886 | if ( !redraw ) { | ||
1887 | drawCursor( TRUE ); | ||
1888 | } else { | ||
1889 | repaintChanged(); | ||
1890 | drawCursor( TRUE ); | ||
1891 | } | ||
1892 | |||
1893 | if ( currentFormat && currentFormat->key() != cursor->parag()->at( cursor->index() )->format()->key() ) { | ||
1894 | currentFormat->removeRef(); | ||
1895 | currentFormat = doc->formatCollection()->format( cursor->parag()->at( cursor->index() )->format() ); | ||
1896 | if ( currentFormat->isMisspelled() ) { | ||
1897 | currentFormat->removeRef(); | ||
1898 | currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); | ||
1899 | } | ||
1900 | emit currentFontChanged( currentFormat->font() ); | ||
1901 | emit currentColorChanged( currentFormat->color() ); | ||
1902 | emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); | ||
1903 | } | ||
1904 | |||
1905 | if ( currentAlignment != cursor->parag()->alignment() ) { | ||
1906 | currentAlignment = cursor->parag()->alignment(); | ||
1907 | block_set_alignment = TRUE; | ||
1908 | emit currentAlignmentChanged( currentAlignment ); | ||
1909 | block_set_alignment = FALSE; | ||
1910 | } | ||
1911 | } | ||
1912 | |||
1913 | /*! | ||
1914 | \fn void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c ) | ||
1915 | Places the cursor \a c at the character which is closest to position | ||
1916 | \a pos (in contents coordinates). If \a c is 0, the default text | ||
1917 | cursor is used. | ||
1918 | |||
1919 | \sa setCursorPosition() | ||
1920 | */ | ||
1921 | |||
1922 | void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c, bool link ) | ||
1923 | { | ||
1924 | if ( !c ) | ||
1925 | c = cursor; | ||
1926 | |||
1927 | c->restoreState(); | ||
1928 | QTextParag *s = doc->firstParag(); | ||
1929 | c->place( pos, s, link ); | ||
1930 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
1931 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
1932 | if ( !readonly ) { | ||
1933 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
1934 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
1935 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
1936 | } | ||
1937 | } | ||
1938 | } | ||
1939 | |||
1940 | void QTextEdit::formatMore() | ||
1941 | { | ||
1942 | if ( !lastFormatted ) | ||
1943 | return; | ||
1944 | |||
1945 | int bottom = contentsHeight(); | ||
1946 | int lastBottom = -1; | ||
1947 | int to = !sender() ? 2 : 20; | ||
1948 | bool firstVisible = FALSE; | ||
1949 | QRect cr( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
1950 | for ( int i = 0; ( i < to || firstVisible ) && lastFormatted; ++i ) { | ||
1951 | lastFormatted->format(); | ||
1952 | if ( i == 0 ) | ||
1953 | firstVisible = lastFormatted->rect().intersects( cr ); | ||
1954 | else if ( firstVisible ) | ||
1955 | firstVisible = lastFormatted->rect().intersects( cr ); | ||
1956 | bottom = QMAX( bottom, lastFormatted->rect().top() + | ||
1957 | lastFormatted->rect().height() ); | ||
1958 | lastBottom = lastFormatted->rect().top() + lastFormatted->rect().height(); | ||
1959 | lastFormatted = lastFormatted->next(); | ||
1960 | if ( lastFormatted ) | ||
1961 | lastBottom = -1; | ||
1962 | } | ||
1963 | |||
1964 | if ( bottom > contentsHeight() ) | ||
1965 | resizeContents( contentsWidth(), QMAX( doc->height(), bottom ) ); | ||
1966 | else if ( lastBottom != -1 && lastBottom < contentsHeight() ) | ||
1967 | resizeContents( contentsWidth(), QMAX( doc->height(), lastBottom ) ); | ||
1968 | |||
1969 | if ( lastFormatted ) | ||
1970 | formatTimer->start( interval, TRUE ); | ||
1971 | else | ||
1972 | interval = QMAX( 0, interval ); | ||
1973 | } | ||
1974 | |||
1975 | void QTextEdit::doResize() | ||
1976 | { | ||
1977 | if ( wrapMode == FixedPixelWidth ) | ||
1978 | return; | ||
1979 | doc->setMinimumWidth( -1 ); | ||
1980 | resizeContents( 0, 0 ); | ||
1981 | doc->setWidth( visibleWidth() ); | ||
1982 | doc->invalidate(); | ||
1983 | lastFormatted = doc->firstParag(); | ||
1984 | interval = 0; | ||
1985 | formatMore(); | ||
1986 | repaintContents( contentsX(), contentsY(), visibleWidth(), visibleHeight(), FALSE ); | ||
1987 | } | ||
1988 | |||
1989 | /*! \internal */ | ||
1990 | |||
1991 | void QTextEdit::doChangeInterval() | ||
1992 | { | ||
1993 | interval = 0; | ||
1994 | } | ||
1995 | |||
1996 | /*! \reimp */ | ||
1997 | |||
1998 | bool QTextEdit::eventFilter( QObject *o, QEvent *e ) | ||
1999 | { | ||
2000 | if ( o == this || o == viewport() ) { | ||
2001 | if ( e->type() == QEvent::FocusIn ) { | ||
2002 | blinkTimer->start( QApplication::cursorFlashTime() / 2 ); | ||
2003 | drawCursor( TRUE ); | ||
2004 | |||
2005 | if ( !readonly ) { | ||
2006 | // make sure the micro focus hint is updated... | ||
2007 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2008 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2009 | cursor->y() + cursor->parag()->rect().y() - | ||
2010 | contentsY() + frameWidth(), 0, | ||
2011 | cursor->parag()->lineHeightOfChar( cursor->index() ), | ||
2012 | TRUE ); | ||
2013 | } | ||
2014 | } else if ( e->type() == QEvent::FocusOut ) { | ||
2015 | blinkTimer->stop(); | ||
2016 | drawCursor( FALSE ); | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | return QScrollView::eventFilter( o, e ); | ||
2021 | } | ||
2022 | |||
2023 | /*! | ||
2024 | Inserts \a text at the current cursor position. If \a indent is TRUE, | ||
2025 | the paragraph is re-indented. If \a checkNewLine is TRUE, newline | ||
2026 | characters in \a text result in hard line breaks (i.e. new | ||
2027 | paragraphs). If \a checkNewLine is FALSE the behaviour of the editor | ||
2028 | is undefined if the \a text contains newlines. If \a removeSelected is | ||
2029 | TRUE, any selected text (in selection 0) is removed before the text is | ||
2030 | inserted. | ||
2031 | |||
2032 | \sa paste() pasteSubType() | ||
2033 | */ | ||
2034 | |||
2035 | void QTextEdit::insert( const QString &text, bool indent, bool checkNewLine, bool removeSelected ) | ||
2036 | { | ||
2037 | if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough | ||
2038 | return; | ||
2039 | QString txt( text ); | ||
2040 | drawCursor( FALSE ); | ||
2041 | if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected ) | ||
2042 | removeSelectedText(); | ||
2043 | QTextCursor c2 = *cursor; | ||
2044 | int oldLen = 0; | ||
2045 | |||
2046 | if ( undoEnabled && !isReadOnly() ) { | ||
2047 | checkUndoRedoInfo( UndoRedoInfo::Insert ); | ||
2048 | if ( !undoRedoInfo.valid() ) { | ||
2049 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
2050 | undoRedoInfo.index = cursor->index(); | ||
2051 | undoRedoInfo.d->text = QString::null; | ||
2052 | } | ||
2053 | oldLen = undoRedoInfo.d->text.length(); | ||
2054 | } | ||
2055 | |||
2056 | lastFormatted = checkNewLine && cursor->parag()->prev() ? | ||
2057 | cursor->parag()->prev() : cursor->parag(); | ||
2058 | QTextCursor oldCursor = *cursor; | ||
2059 | cursor->insert( txt, checkNewLine ); | ||
2060 | if ( doc->useFormatCollection() ) { | ||
2061 | doc->setSelectionStart( QTextDocument::Temp, &oldCursor ); | ||
2062 | doc->setSelectionEnd( QTextDocument::Temp, cursor ); | ||
2063 | doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); | ||
2064 | doc->removeSelection( QTextDocument::Temp ); | ||
2065 | } | ||
2066 | |||
2067 | if ( indent && ( txt == "{" || txt == "}" || txt == ":" || txt == "#" ) ) | ||
2068 | cursor->indent(); | ||
2069 | formatMore(); | ||
2070 | repaintChanged(); | ||
2071 | ensureCursorVisible(); | ||
2072 | drawCursor( TRUE ); | ||
2073 | |||
2074 | if ( undoEnabled && !isReadOnly() ) { | ||
2075 | undoRedoInfo.d->text += txt; | ||
2076 | if ( !doc->preProcessor() ) { | ||
2077 | for ( int i = 0; i < (int)txt.length(); ++i ) { | ||
2078 | if ( txt[ i ] != '\n' && c2.parag()->at( c2.index() )->format() ) { | ||
2079 | c2.parag()->at( c2.index() )->format()->addRef(); | ||
2080 | undoRedoInfo.d->text.setFormat( oldLen + i, c2.parag()->at( c2.index() )->format(), TRUE ); | ||
2081 | } | ||
2082 | c2.gotoNextLetter(); | ||
2083 | } | ||
2084 | } | ||
2085 | } | ||
2086 | |||
2087 | setModified(); | ||
2088 | emit textChanged(); | ||
2089 | if ( !removeSelected ) { | ||
2090 | doc->setSelectionStart( QTextDocument::Standard, &oldCursor ); | ||
2091 | doc->setSelectionEnd( QTextDocument::Standard, cursor ); | ||
2092 | repaintChanged(); | ||
2093 | } | ||
2094 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
2095 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
2096 | if ( !readonly ) { | ||
2097 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2098 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2099 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
2100 | } | ||
2101 | } | ||
2102 | } | ||
2103 | |||
2104 | /*! Inserts \a text in the paragraph \a para and position \a index */ | ||
2105 | |||
2106 | void QTextEdit::insertAt( const QString &text, int para, int index ) | ||
2107 | { | ||
2108 | QTextParag *p = doc->paragAt( para ); | ||
2109 | if ( !p ) | ||
2110 | return; | ||
2111 | QTextCursor tmp = *cursor; | ||
2112 | cursor->setParag( p ); | ||
2113 | cursor->setIndex( index ); | ||
2114 | insert( text, FALSE, TRUE, FALSE ); | ||
2115 | *cursor = tmp; | ||
2116 | removeSelection( QTextDocument::Standard ); | ||
2117 | } | ||
2118 | |||
2119 | /*! Inserts \a text as the paragraph at position \a para. If \a para | ||
2120 | is -1, the text is appended. | ||
2121 | */ | ||
2122 | |||
2123 | void QTextEdit::insertParagraph( const QString &text, int para ) | ||
2124 | { | ||
2125 | QTextParag *p = doc->paragAt( para ); | ||
2126 | if ( p ) { | ||
2127 | QTextCursor tmp( doc ); | ||
2128 | tmp.setParag( p ); | ||
2129 | tmp.setIndex( 0 ); | ||
2130 | tmp.insert( text, TRUE ); | ||
2131 | tmp.splitAndInsertEmptyParag(); | ||
2132 | repaintChanged(); | ||
2133 | } else { | ||
2134 | append( text ); | ||
2135 | } | ||
2136 | } | ||
2137 | |||
2138 | /*! Removes the paragraph \a para */ | ||
2139 | |||
2140 | void QTextEdit::removeParagraph( int para ) | ||
2141 | { | ||
2142 | QTextParag *p = doc->paragAt( para ); | ||
2143 | if ( !p ) | ||
2144 | return; | ||
2145 | for ( int i = 0; i < doc->numSelections(); ++i ) | ||
2146 | doc->removeSelection( i ); | ||
2147 | |||
2148 | if ( p == doc->firstParag() && p == doc->lastParag() ) { | ||
2149 | p->remove( 0, p->length() - 1 ); | ||
2150 | repaintChanged(); | ||
2151 | return; | ||
2152 | } | ||
2153 | drawCursor( FALSE ); | ||
2154 | bool resetCursor = cursor->parag() == p; | ||
2155 | if ( p->prev() ) | ||
2156 | p->prev()->setNext( p->next() ); | ||
2157 | else | ||
2158 | doc->setFirstParag( p->next() ); | ||
2159 | if ( p->next() ) | ||
2160 | p->next()->setPrev( p->prev() ); | ||
2161 | else | ||
2162 | doc->setLastParag( p->prev() ); | ||
2163 | QTextParag *start = p->next(); | ||
2164 | int h = p->rect().height(); | ||
2165 | delete p; | ||
2166 | p = start; | ||
2167 | int dy = -h; | ||
2168 | while ( p ) { | ||
2169 | p->setParagId( p->prev() ? p->prev()->paragId() + 1 : 0 ); | ||
2170 | p->move( dy ); | ||
2171 | p->invalidate( 0 ); | ||
2172 | p->setEndState( -1 ); | ||
2173 | p = p->next(); | ||
2174 | } | ||
2175 | |||
2176 | if ( resetCursor ) { | ||
2177 | cursor->setParag( doc->firstParag() ); | ||
2178 | cursor->setIndex( 0 ); | ||
2179 | } | ||
2180 | repaintChanged(); | ||
2181 | drawCursor( TRUE ); | ||
2182 | } | ||
2183 | |||
2184 | /*! | ||
2185 | Undoes the last operation. | ||
2186 | |||
2187 | If there is no operation to undo, e.g. there is no undo step in the | ||
2188 | undo/redo history, nothing happens. | ||
2189 | |||
2190 | \sa undoAvailable() redo() undoDepth() | ||
2191 | */ | ||
2192 | |||
2193 | void QTextEdit::undo() | ||
2194 | { | ||
2195 | // XXX FIXME The next line is here because there may be a command | ||
2196 | // that needs to be 'flushed'. The FIXME is because I am not | ||
2197 | // 100% certain this is the right call to do this. | ||
2198 | clearUndoRedo(); | ||
2199 | if ( isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled ) | ||
2200 | return; | ||
2201 | |||
2202 | for ( int i = 0; i < (int)doc->numSelections(); ++i ) | ||
2203 | doc->removeSelection( i ); | ||
2204 | |||
2205 | #ifndef QT_NO_CURSOR | ||
2206 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
2207 | #endif | ||
2208 | |||
2209 | clearUndoRedo(); | ||
2210 | drawCursor( FALSE ); | ||
2211 | QTextCursor *c = doc->undo( cursor ); | ||
2212 | if ( !c ) { | ||
2213 | drawCursor( TRUE ); | ||
2214 | return; | ||
2215 | } | ||
2216 | lastFormatted = 0; | ||
2217 | ensureCursorVisible(); | ||
2218 | repaintChanged(); | ||
2219 | drawCursor( TRUE ); | ||
2220 | setModified(); | ||
2221 | emit textChanged(); | ||
2222 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
2223 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
2224 | if ( !readonly ) { | ||
2225 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2226 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2227 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
2228 | } | ||
2229 | } | ||
2230 | } | ||
2231 | |||
2232 | /*! | ||
2233 | Redoes the last operation. | ||
2234 | |||
2235 | If there is no operation to redo, e.g. there is no redo step in the | ||
2236 | undo/redo history, nothing happens. | ||
2237 | |||
2238 | \sa redoAvailable() undo() undoDepth() | ||
2239 | */ | ||
2240 | |||
2241 | void QTextEdit::redo() | ||
2242 | { | ||
2243 | if ( isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled ) | ||
2244 | return; | ||
2245 | |||
2246 | for ( int i = 0; i < (int)doc->numSelections(); ++i ) | ||
2247 | doc->removeSelection( i ); | ||
2248 | |||
2249 | #ifndef QT_NO_CURSOR | ||
2250 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
2251 | #endif | ||
2252 | |||
2253 | clearUndoRedo(); | ||
2254 | drawCursor( FALSE ); | ||
2255 | QTextCursor *c = doc->redo( cursor ); | ||
2256 | if ( !c ) { | ||
2257 | drawCursor( TRUE ); | ||
2258 | return; | ||
2259 | } | ||
2260 | lastFormatted = 0; | ||
2261 | ensureCursorVisible(); | ||
2262 | repaintChanged(); | ||
2263 | ensureCursorVisible(); | ||
2264 | drawCursor( TRUE ); | ||
2265 | setModified(); | ||
2266 | emit textChanged(); | ||
2267 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
2268 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
2269 | if ( !readonly ) { | ||
2270 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2271 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2272 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
2273 | } | ||
2274 | } | ||
2275 | } | ||
2276 | |||
2277 | /*! | ||
2278 | Pastes the text from the clipboard into the text edit at the current | ||
2279 | cursor position. Only plain text is pasted. | ||
2280 | |||
2281 | If there is no text in the clipboard nothing happens. | ||
2282 | |||
2283 | \sa pasteSubType() cut() QTextEdit::copy() | ||
2284 | */ | ||
2285 | |||
2286 | void QTextEdit::paste() | ||
2287 | { | ||
2288 | #ifndef QT_NO_CLIPBOARD | ||
2289 | if ( isReadOnly() ) | ||
2290 | return; | ||
2291 | pasteSubType( "plain" ); | ||
2292 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
2293 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
2294 | if ( !readonly ) { | ||
2295 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2296 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2297 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
2298 | } | ||
2299 | } | ||
2300 | #endif | ||
2301 | } | ||
2302 | |||
2303 | void QTextEdit::checkUndoRedoInfo( UndoRedoInfo::Type t ) | ||
2304 | { | ||
2305 | if ( undoRedoInfo.valid() && t != undoRedoInfo.type ) { | ||
2306 | clearUndoRedo(); | ||
2307 | } | ||
2308 | undoRedoInfo.type = t; | ||
2309 | } | ||
2310 | |||
2311 | /*! Repaints any paragraphs that have changed. | ||
2312 | |||
2313 | Although used extensively internally you shouldn't need to call this | ||
2314 | yourself. | ||
2315 | */ | ||
2316 | |||
2317 | void QTextEdit::repaintChanged() | ||
2318 | { | ||
2319 | if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() ) | ||
2320 | return; | ||
2321 | QPainter p( viewport() ); | ||
2322 | p.translate( -contentsX(), -contentsY() ); | ||
2323 | paintDocument( FALSE, &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
2324 | } | ||
2325 | |||
2326 | /*! | ||
2327 | Copies the selected text (from selection 0) to the clipboard and | ||
2328 | deletes it from the text edit. | ||
2329 | |||
2330 | If there is no selected text (in selection 0) nothing happens. | ||
2331 | |||
2332 | \sa QTextEdit::copy() paste() pasteSubType() | ||
2333 | */ | ||
2334 | |||
2335 | void QTextEdit::cut() | ||
2336 | { | ||
2337 | if ( isReadOnly() ) | ||
2338 | return; | ||
2339 | |||
2340 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
2341 | doc->copySelectedText( QTextDocument::Standard ); | ||
2342 | removeSelectedText(); | ||
2343 | } | ||
2344 | if ( hasFocus() || viewport()->hasFocus() ) { | ||
2345 | int h = cursor->parag()->lineHeightOfChar( cursor->index() ); | ||
2346 | if ( !readonly ) { | ||
2347 | QFont f = cursor->parag()->at( cursor->index() )->format()->font(); | ||
2348 | setMicroFocusHint( cursor->x() - contentsX() + frameWidth(), | ||
2349 | cursor->y() + cursor->parag()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE ); | ||
2350 | } | ||
2351 | } | ||
2352 | } | ||
2353 | |||
2354 | /*! Copies any selected text (from selection 0) to the clipboard. | ||
2355 | |||
2356 | \sa hasSelectedText() copyAvailable() | ||
2357 | */ | ||
2358 | |||
2359 | void QTextEdit::copy() | ||
2360 | { | ||
2361 | if ( !doc->selectedText( QTextDocument::Standard ).isEmpty() ) | ||
2362 | doc->copySelectedText( QTextDocument::Standard ); | ||
2363 | } | ||
2364 | |||
2365 | /*! | ||
2366 | Re-indents the current paragraph. | ||
2367 | */ | ||
2368 | |||
2369 | void QTextEdit::indent() | ||
2370 | { | ||
2371 | if ( isReadOnly() ) | ||
2372 | return; | ||
2373 | |||
2374 | drawCursor( FALSE ); | ||
2375 | if ( !doc->hasSelection( QTextDocument::Standard ) ) | ||
2376 | cursor->indent(); | ||
2377 | else | ||
2378 | doc->indentSelection( QTextDocument::Standard ); | ||
2379 | repaintChanged(); | ||
2380 | drawCursor( TRUE ); | ||
2381 | setModified(); | ||
2382 | emit textChanged(); | ||
2383 | } | ||
2384 | |||
2385 | /*! Reimplemented to allow tabbing through links. | ||
2386 | If \a n is TRUE the tab moves the focus to the next child; if \a n | ||
2387 | is FALSE the tab moves the focus to the previous child. | ||
2388 | Returns TRUE if the focus was moved; otherwise returns FALSE. | ||
2389 | */ | ||
2390 | |||
2391 | bool QTextEdit::focusNextPrevChild( bool n ) | ||
2392 | { | ||
2393 | if ( !isReadOnly() || !linksEnabled() ) | ||
2394 | return FALSE; | ||
2395 | bool b = doc->focusNextPrevChild( n ); | ||
2396 | repaintChanged(); | ||
2397 | if ( b ) | ||
2398 | //##### this does not work with tables. The focusIndicator | ||
2399 | //should really be a QTextCursor. Fix 3.1 | ||
2400 | makeParagVisible( doc->focusIndicator.parag ); | ||
2401 | return b; | ||
2402 | } | ||
2403 | |||
2404 | /*! | ||
2405 | \internal | ||
2406 | |||
2407 | This functions sets the current format to \a f. Only the fields of \a | ||
2408 | f which are specified by the \a flags are used. | ||
2409 | */ | ||
2410 | |||
2411 | void QTextEdit::setFormat( QTextFormat *f, int flags ) | ||
2412 | { | ||
2413 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
2414 | drawCursor( FALSE ); | ||
2415 | QString str = doc->selectedText( QTextDocument::Standard ); | ||
2416 | QTextCursor c1 = doc->selectionStartCursor( QTextDocument::Standard ); | ||
2417 | QTextCursor c2 = doc->selectionEndCursor( QTextDocument::Standard ); | ||
2418 | clearUndoRedo(); | ||
2419 | undoRedoInfo.type = UndoRedoInfo::Format; | ||
2420 | undoRedoInfo.id = c1.parag()->paragId(); | ||
2421 | undoRedoInfo.index = c1.index(); | ||
2422 | undoRedoInfo.eid = c2.parag()->paragId(); | ||
2423 | undoRedoInfo.eindex = c2.index(); | ||
2424 | undoRedoInfo.d->text = str; | ||
2425 | readFormats( c1, c2, 0, undoRedoInfo.d->text ); | ||
2426 | undoRedoInfo.format = f; | ||
2427 | undoRedoInfo.flags = flags; | ||
2428 | clearUndoRedo(); | ||
2429 | doc->setFormat( QTextDocument::Standard, f, flags ); | ||
2430 | repaintChanged(); | ||
2431 | formatMore(); | ||
2432 | drawCursor( TRUE ); | ||
2433 | setModified(); | ||
2434 | emit textChanged(); | ||
2435 | } | ||
2436 | if ( currentFormat && currentFormat->key() != f->key() ) { | ||
2437 | currentFormat->removeRef(); | ||
2438 | currentFormat = doc->formatCollection()->format( f ); | ||
2439 | if ( currentFormat->isMisspelled() ) { | ||
2440 | currentFormat->removeRef(); | ||
2441 | currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); | ||
2442 | } | ||
2443 | emit currentFontChanged( currentFormat->font() ); | ||
2444 | emit currentColorChanged( currentFormat->color() ); | ||
2445 | emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); | ||
2446 | if ( cursor->index() == cursor->parag()->length() - 1 ) { | ||
2447 | currentFormat->addRef(); | ||
2448 | cursor->parag()->string()->setFormat( cursor->index(), currentFormat, TRUE ); | ||
2449 | if ( cursor->parag()->length() == 1 ) { | ||
2450 | cursor->parag()->invalidate( 0 ); | ||
2451 | cursor->parag()->format(); | ||
2452 | repaintChanged(); | ||
2453 | } | ||
2454 | } | ||
2455 | } | ||
2456 | } | ||
2457 | |||
2458 | /*! \reimp */ | ||
2459 | |||
2460 | void QTextEdit::setPalette( const QPalette &p ) | ||
2461 | { | ||
2462 | QScrollView::setPalette( p ); | ||
2463 | if ( textFormat() == PlainText ) { | ||
2464 | QTextFormat *f = doc->formatCollection()->defaultFormat(); | ||
2465 | f->setColor( colorGroup().text() ); | ||
2466 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
2467 | } | ||
2468 | } | ||
2469 | |||
2470 | /*! | ||
2471 | Sets the paragraph style of the current paragraph | ||
2472 | to \a dm. If \a dm is QStyleSheetItem::DisplayListItem, the | ||
2473 | type of the list item is set to \a listStyle. | ||
2474 | |||
2475 | \sa setAlignment() | ||
2476 | */ | ||
2477 | |||
2478 | void QTextEdit::setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle ) | ||
2479 | { | ||
2480 | if ( isReadOnly() ) | ||
2481 | return; | ||
2482 | |||
2483 | drawCursor( FALSE ); | ||
2484 | if ( !doc->hasSelection( QTextDocument::Standard ) ) { | ||
2485 | clearUndoRedo(); | ||
2486 | undoRedoInfo.type = UndoRedoInfo::ParagType; | ||
2487 | QValueList< QPtrVector<QStyleSheetItem> > oldStyles; | ||
2488 | undoRedoInfo.oldStyles.clear(); | ||
2489 | undoRedoInfo.oldStyles << cursor->parag()->styleSheetItems(); | ||
2490 | undoRedoInfo.oldListStyles.clear(); | ||
2491 | undoRedoInfo.oldListStyles << cursor->parag()->listStyle(); | ||
2492 | undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem; | ||
2493 | undoRedoInfo.listStyle = listStyle; | ||
2494 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
2495 | undoRedoInfo.eid = cursor->parag()->paragId(); | ||
2496 | undoRedoInfo.d->text = " "; | ||
2497 | undoRedoInfo.index = 1; | ||
2498 | clearUndoRedo(); | ||
2499 | cursor->parag()->setList( dm == QStyleSheetItem::DisplayListItem, listStyle ); | ||
2500 | repaintChanged(); | ||
2501 | } else { | ||
2502 | QTextParag *start = doc->selectionStart( QTextDocument::Standard ); | ||
2503 | QTextParag *end = doc->selectionEnd( QTextDocument::Standard ); | ||
2504 | lastFormatted = start; | ||
2505 | clearUndoRedo(); | ||
2506 | undoRedoInfo.type = UndoRedoInfo::ParagType; | ||
2507 | undoRedoInfo.id = start->paragId(); | ||
2508 | undoRedoInfo.eid = end->paragId(); | ||
2509 | undoRedoInfo.list = dm == QStyleSheetItem::DisplayListItem; | ||
2510 | undoRedoInfo.listStyle = listStyle; | ||
2511 | undoRedoInfo.oldStyles.clear(); | ||
2512 | undoRedoInfo.oldListStyles.clear(); | ||
2513 | while ( start ) { | ||
2514 | undoRedoInfo.oldStyles << start->styleSheetItems(); | ||
2515 | undoRedoInfo.oldListStyles << start->listStyle(); | ||
2516 | start->setList( dm == QStyleSheetItem::DisplayListItem, listStyle ); | ||
2517 | if ( start == end ) | ||
2518 | break; | ||
2519 | start = start->next(); | ||
2520 | } | ||
2521 | undoRedoInfo.d->text = " "; | ||
2522 | undoRedoInfo.index = 1; | ||
2523 | clearUndoRedo(); | ||
2524 | repaintChanged(); | ||
2525 | formatMore(); | ||
2526 | } | ||
2527 | drawCursor( TRUE ); | ||
2528 | setModified(); | ||
2529 | emit textChanged(); | ||
2530 | } | ||
2531 | |||
2532 | /*! | ||
2533 | Sets the alignment of the current paragraph to \a a. Valid alignments | ||
2534 | are \c Qt::AlignLeft, \c Qt::AlignRight, Qt::AlignJustify and | ||
2535 | Qt::AlignCenter (which centers horizontally). | ||
2536 | |||
2537 | \sa setParagType() | ||
2538 | */ | ||
2539 | |||
2540 | void QTextEdit::setAlignment( int a ) | ||
2541 | { | ||
2542 | if ( isReadOnly() || block_set_alignment ) | ||
2543 | return; | ||
2544 | |||
2545 | drawCursor( FALSE ); | ||
2546 | if ( !doc->hasSelection( QTextDocument::Standard ) ) { | ||
2547 | if ( cursor->parag()->alignment() != a ) { | ||
2548 | clearUndoRedo(); | ||
2549 | undoRedoInfo.type = UndoRedoInfo::Alignment; | ||
2550 | QMemArray<int> oa( 1 ); | ||
2551 | oa[ 0 ] = cursor->parag()->alignment(); | ||
2552 | undoRedoInfo.oldAligns = oa; | ||
2553 | undoRedoInfo.newAlign = a; | ||
2554 | undoRedoInfo.id = cursor->parag()->paragId(); | ||
2555 | undoRedoInfo.eid = cursor->parag()->paragId(); | ||
2556 | undoRedoInfo.d->text = " "; | ||
2557 | undoRedoInfo.index = 1; | ||
2558 | clearUndoRedo(); | ||
2559 | cursor->parag()->setAlignment( a ); | ||
2560 | repaintChanged(); | ||
2561 | } | ||
2562 | } else { | ||
2563 | QTextParag *start = doc->selectionStart( QTextDocument::Standard ); | ||
2564 | QTextParag *end = doc->selectionEnd( QTextDocument::Standard ); | ||
2565 | lastFormatted = start; | ||
2566 | int len = end->paragId() - start->paragId() + 1; | ||
2567 | clearUndoRedo(); | ||
2568 | undoRedoInfo.type = UndoRedoInfo::Alignment; | ||
2569 | undoRedoInfo.id = start->paragId(); | ||
2570 | undoRedoInfo.eid = end->paragId(); | ||
2571 | QMemArray<int> oa( QMAX( 0, len ) ); | ||
2572 | int i = 0; | ||
2573 | while ( start ) { | ||
2574 | if ( i < (int)oa.size() ) | ||
2575 | oa[ i ] = start->alignment(); | ||
2576 | start->setAlignment( a ); | ||
2577 | if ( start == end ) | ||
2578 | break; | ||
2579 | start = start->next(); | ||
2580 | ++i; | ||
2581 | } | ||
2582 | undoRedoInfo.oldAligns = oa; | ||
2583 | undoRedoInfo.newAlign = a; | ||
2584 | undoRedoInfo.d->text = " "; | ||
2585 | undoRedoInfo.index = 1; | ||
2586 | clearUndoRedo(); | ||
2587 | repaintChanged(); | ||
2588 | formatMore(); | ||
2589 | } | ||
2590 | drawCursor( TRUE ); | ||
2591 | if ( currentAlignment != a ) { | ||
2592 | currentAlignment = a; | ||
2593 | emit currentAlignmentChanged( currentAlignment ); | ||
2594 | } | ||
2595 | setModified(); | ||
2596 | emit textChanged(); | ||
2597 | } | ||
2598 | |||
2599 | void QTextEdit::updateCurrentFormat() | ||
2600 | { | ||
2601 | int i = cursor->index(); | ||
2602 | if ( i > 0 ) | ||
2603 | --i; | ||
2604 | if ( doc->useFormatCollection() && | ||
2605 | ( !currentFormat || currentFormat->key() != cursor->parag()->at( i )->format()->key() ) ) { | ||
2606 | if ( currentFormat ) | ||
2607 | currentFormat->removeRef(); | ||
2608 | currentFormat = doc->formatCollection()->format( cursor->parag()->at( i )->format() ); | ||
2609 | if ( currentFormat->isMisspelled() ) { | ||
2610 | currentFormat->removeRef(); | ||
2611 | currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); | ||
2612 | } | ||
2613 | emit currentFontChanged( currentFormat->font() ); | ||
2614 | emit currentColorChanged( currentFormat->color() ); | ||
2615 | emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); | ||
2616 | } | ||
2617 | |||
2618 | if ( currentAlignment != cursor->parag()->alignment() ) { | ||
2619 | currentAlignment = cursor->parag()->alignment(); | ||
2620 | block_set_alignment = TRUE; | ||
2621 | emit currentAlignmentChanged( currentAlignment ); | ||
2622 | block_set_alignment = FALSE; | ||
2623 | } | ||
2624 | } | ||
2625 | |||
2626 | /*! | ||
2627 | If \a b is TRUE sets the current format to italic; otherwise sets | ||
2628 | the current format to non-italic. | ||
2629 | |||
2630 | \sa italic() | ||
2631 | */ | ||
2632 | |||
2633 | void QTextEdit::setItalic( bool b ) | ||
2634 | { | ||
2635 | QTextFormat f( *currentFormat ); | ||
2636 | f.setItalic( b ); | ||
2637 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2638 | setFormat( f2, QTextFormat::Italic ); | ||
2639 | } | ||
2640 | |||
2641 | /*! | ||
2642 | If \a b is TRUE sets the current format to bold; otherwise sets the | ||
2643 | current format to non-bold. | ||
2644 | |||
2645 | \sa bold() | ||
2646 | */ | ||
2647 | |||
2648 | void QTextEdit::setBold( bool b ) | ||
2649 | { | ||
2650 | QTextFormat f( *currentFormat ); | ||
2651 | f.setBold( b ); | ||
2652 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2653 | setFormat( f2, QTextFormat::Bold ); | ||
2654 | } | ||
2655 | |||
2656 | /*! | ||
2657 | If \a b is TRUE sets the current format to underline; otherwise sets | ||
2658 | the current format to non-underline. | ||
2659 | |||
2660 | \sa underline() | ||
2661 | */ | ||
2662 | |||
2663 | void QTextEdit::setUnderline( bool b ) | ||
2664 | { | ||
2665 | QTextFormat f( *currentFormat ); | ||
2666 | f.setUnderline( b ); | ||
2667 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2668 | setFormat( f2, QTextFormat::Underline ); | ||
2669 | } | ||
2670 | |||
2671 | /*! | ||
2672 | Sets the font family of the current format to \a fontFamily. | ||
2673 | |||
2674 | \sa family() setCurrentFont() | ||
2675 | */ | ||
2676 | |||
2677 | void QTextEdit::setFamily( const QString &fontFamily ) | ||
2678 | { | ||
2679 | QTextFormat f( *currentFormat ); | ||
2680 | f.setFamily( fontFamily ); | ||
2681 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2682 | setFormat( f2, QTextFormat::Family ); | ||
2683 | } | ||
2684 | |||
2685 | /*! | ||
2686 | Sets the point size of the current format to \a s. | ||
2687 | |||
2688 | Note that if \a s is zero or negative, the behaviour of this | ||
2689 | function is not defined. | ||
2690 | |||
2691 | \sa pointSize() setCurrentFont() setFamily() | ||
2692 | */ | ||
2693 | |||
2694 | void QTextEdit::setPointSize( int s ) | ||
2695 | { | ||
2696 | QTextFormat f( *currentFormat ); | ||
2697 | f.setPointSize( s ); | ||
2698 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2699 | setFormat( f2, QTextFormat::Size ); | ||
2700 | } | ||
2701 | |||
2702 | /*! | ||
2703 | Sets the color of the current format, i.e. of the text, to \a c. | ||
2704 | |||
2705 | \sa color() setPaper() | ||
2706 | */ | ||
2707 | |||
2708 | void QTextEdit::setColor( const QColor &c ) | ||
2709 | { | ||
2710 | QTextFormat f( *currentFormat ); | ||
2711 | f.setColor( c ); | ||
2712 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2713 | setFormat( f2, QTextFormat::Color ); | ||
2714 | } | ||
2715 | |||
2716 | /*! | ||
2717 | Sets the vertical alignment of the current format, i.e. of the text, to \a a. | ||
2718 | |||
2719 | \sa color() setPaper() | ||
2720 | */ | ||
2721 | |||
2722 | void QTextEdit::setVerticalAlignment( VerticalAlignment a ) | ||
2723 | { | ||
2724 | QTextFormat f( *currentFormat ); | ||
2725 | f.setVAlign( (QTextFormat::VerticalAlignment)a ); | ||
2726 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2727 | setFormat( f2, QTextFormat::VAlign ); | ||
2728 | } | ||
2729 | |||
2730 | void QTextEdit::setFontInternal( const QFont &f_ ) | ||
2731 | { | ||
2732 | QTextFormat f( *currentFormat ); | ||
2733 | f.setFont( f_ ); | ||
2734 | QTextFormat *f2 = doc->formatCollection()->format( &f ); | ||
2735 | setFormat( f2, QTextFormat::Font ); | ||
2736 | } | ||
2737 | |||
2738 | |||
2739 | QString QTextEdit::text() const | ||
2740 | { | ||
2741 | if ( isReadOnly() ) | ||
2742 | return doc->originalText(); | ||
2743 | return doc->text(); | ||
2744 | } | ||
2745 | |||
2746 | /*! | ||
2747 | \overload | ||
2748 | Returns the text of paragraph \a para. | ||
2749 | |||
2750 | If textFormat() is \c RichText the text will contain HTML | ||
2751 | formatting tags. | ||
2752 | */ | ||
2753 | |||
2754 | QString QTextEdit::text( int para ) const | ||
2755 | { | ||
2756 | return doc->text( para ); | ||
2757 | } | ||
2758 | |||
2759 | /*! | ||
2760 | \overload | ||
2761 | |||
2762 | Changes the text of the text edit to the string \a text and the | ||
2763 | context to \a context. Any previous text is removed. | ||
2764 | |||
2765 | \a text may be interpreted either as plain text or as rich text, | ||
2766 | depending on the textFormat(). The default setting is \c AutoText, | ||
2767 | i.e. the text edit autodetects the format from \a text. | ||
2768 | |||
2769 | The optional \a context is a path which the text edit's | ||
2770 | QMimeSourceFactory uses to resolve the locations of files and images. | ||
2771 | (See \l{QTextEdit::QTextEdit()}.) It is passed to the text edit's | ||
2772 | QMimeSourceFactory when quering data. | ||
2773 | |||
2774 | Note that the undo/redo history is cleared by this function. | ||
2775 | |||
2776 | \sa text(), setTextFormat() | ||
2777 | */ | ||
2778 | |||
2779 | void QTextEdit::setText( const QString &text, const QString &context ) | ||
2780 | { | ||
2781 | if ( !isModified() && this->context() == context && this->text() == text ) | ||
2782 | return; | ||
2783 | |||
2784 | emit undoAvailable( FALSE ); | ||
2785 | emit redoAvailable( FALSE ); | ||
2786 | undoRedoInfo.clear(); | ||
2787 | doc->commands()->clear(); | ||
2788 | |||
2789 | lastFormatted = 0; | ||
2790 | cursor->restoreState(); | ||
2791 | doc->setText( text, context ); | ||
2792 | |||
2793 | if ( wrapMode == FixedPixelWidth ) { | ||
2794 | resizeContents( wrapWidth, 0 ); | ||
2795 | doc->setWidth( wrapWidth ); | ||
2796 | doc->setMinimumWidth( wrapWidth ); | ||
2797 | } else { | ||
2798 | doc->setMinimumWidth( -1 ); | ||
2799 | resizeContents( 0, 0 ); | ||
2800 | } | ||
2801 | |||
2802 | cursor->setDocument( doc ); | ||
2803 | lastFormatted = doc->firstParag(); | ||
2804 | cursor->setParag( doc->firstParag() ); | ||
2805 | cursor->setIndex( 0 ); | ||
2806 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
2807 | |||
2808 | if ( isModified() ) | ||
2809 | setModified( FALSE ); | ||
2810 | emit textChanged(); | ||
2811 | formatMore(); | ||
2812 | updateCurrentFormat(); | ||
2813 | } | ||
2814 | |||
2815 | /*! | ||
2816 | \property QTextEdit::text | ||
2817 | \brief the text edit's text | ||
2818 | |||
2819 | There is no default text. | ||
2820 | |||
2821 | On setting, any previous text is deleted. | ||
2822 | |||
2823 | The text may be interpreted either as plain text or as rich text, | ||
2824 | depending on the textFormat(). The default setting is \c AutoText, | ||
2825 | i.e. the text edit autodetects the format of the text. | ||
2826 | |||
2827 | For richtext, calling text() on an editable QTextEdit will cause the text | ||
2828 | to be regenerated from the textedit. This may mean that the QString returned | ||
2829 | may not be exactly the same as the one that was set. | ||
2830 | |||
2831 | \sa textFormat | ||
2832 | */ | ||
2833 | |||
2834 | |||
2835 | /*! | ||
2836 | \property QTextEdit::readOnly | ||
2837 | \brief whether the text edit is read-only | ||
2838 | |||
2839 | In a read-only text edit the user can only navigate through the text | ||
2840 | and select text; modifying the text is not possible. | ||
2841 | |||
2842 | This property's default is FALSE. | ||
2843 | */ | ||
2844 | |||
2845 | /*! | ||
2846 | Finds the next occurrence of the string, \a expr. Returns TRUE if | ||
2847 | \a expr is found; otherwise returns FALSE. | ||
2848 | |||
2849 | If \a para and \a index are both null the search begins from the | ||
2850 | start of the text. If \a para and \a index are both not null, the | ||
2851 | search begins from the \e *\a index character position in the \e | ||
2852 | *\a para paragraph. | ||
2853 | |||
2854 | If \a cs is TRUE the search is case sensitive, otherwise it is | ||
2855 | case insensitive. If \a wo is TRUE the search looks for whole word | ||
2856 | matches only; otherwise it searches for any matching text. If \a | ||
2857 | forward is TRUE (the default) the search works forward from the | ||
2858 | starting position to the end of the text, otherwise it works | ||
2859 | backwards to the beginning of the text. | ||
2860 | |||
2861 | If \a expr is found the function returns TRUE. If \a index and \a | ||
2862 | para are not null, the number of the paragraph in which the first | ||
2863 | character of the match was found is put into \e *\a para, and the | ||
2864 | index position of that character within the paragraph is put into | ||
2865 | \e *\a index. | ||
2866 | |||
2867 | If \a expr is not found the function returns FALSE. If \a index | ||
2868 | and \a para are not null and \a expr is not found, \e *\a index | ||
2869 | and \e *\a para are undefined. | ||
2870 | */ | ||
2871 | |||
2872 | bool QTextEdit::find( const QString &expr, bool cs, bool wo, bool forward, | ||
2873 | int *para, int *index ) | ||
2874 | { | ||
2875 | drawCursor( FALSE ); | ||
2876 | doc->removeSelection( QTextDocument::Standard ); | ||
2877 | #ifndef QT_NO_CURSOR | ||
2878 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
2879 | #endif | ||
2880 | bool found = doc->find( expr, cs, wo, forward, para, index, cursor ); | ||
2881 | ensureCursorVisible(); | ||
2882 | drawCursor( TRUE ); | ||
2883 | repaintChanged(); | ||
2884 | return found; | ||
2885 | } | ||
2886 | |||
2887 | void QTextEdit::blinkCursor() | ||
2888 | { | ||
2889 | if ( !cursorVisible ) | ||
2890 | return; | ||
2891 | bool cv = cursorVisible; | ||
2892 | blinkCursorVisible = !blinkCursorVisible; | ||
2893 | drawCursor( blinkCursorVisible ); | ||
2894 | cursorVisible = cv; | ||
2895 | } | ||
2896 | |||
2897 | /*! | ||
2898 | Sets the cursor to position \a index in paragraph \a para. | ||
2899 | |||
2900 | \sa getCursorPosition() | ||
2901 | */ | ||
2902 | |||
2903 | void QTextEdit::setCursorPosition( int para, int index ) | ||
2904 | { | ||
2905 | QTextParag *p = doc->paragAt( para ); | ||
2906 | if ( !p ) | ||
2907 | return; | ||
2908 | |||
2909 | if ( index > p->length() - 1 ) | ||
2910 | index = p->length() - 1; | ||
2911 | |||
2912 | drawCursor( FALSE ); | ||
2913 | cursor->setParag( p ); | ||
2914 | cursor->setIndex( index ); | ||
2915 | ensureCursorVisible(); | ||
2916 | drawCursor( TRUE ); | ||
2917 | emit cursorPositionChanged( cursor ); | ||
2918 | emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); | ||
2919 | } | ||
2920 | |||
2921 | /*! | ||
2922 | This function sets the \e *\a para and \e *\a index parameters to the | ||
2923 | current cursor position. \a para and \a index must be non-null int | ||
2924 | pointers. | ||
2925 | |||
2926 | \sa setCursorPosition() | ||
2927 | */ | ||
2928 | |||
2929 | void QTextEdit::getCursorPosition( int *para, int *index ) const | ||
2930 | { | ||
2931 | if ( !para || !index ) | ||
2932 | return; | ||
2933 | *para = cursor->parag()->paragId(); | ||
2934 | *index = cursor->index(); | ||
2935 | } | ||
2936 | |||
2937 | /*! Sets a selection which starts at position \a indexFrom in | ||
2938 | paragraph \a paraFrom and ends at position \a indexTo in paragraph | ||
2939 | \a paraTo. Existing selections which have a different id (selNum) | ||
2940 | are not removed, existing selections which have the same id as \a | ||
2941 | selNum are removed. | ||
2942 | |||
2943 | Uses the selection settings of selection \a selNum. If \a selNum is 0, | ||
2944 | this is the default selection. | ||
2945 | |||
2946 | The cursor is moved to the end of the selection if \a selNum is 0, | ||
2947 | otherwise the cursor position remains unchanged. | ||
2948 | |||
2949 | \sa getSelection() selectedText | ||
2950 | */ | ||
2951 | |||
2952 | void QTextEdit::setSelection( int paraFrom, int indexFrom, | ||
2953 | int paraTo, int indexTo, int selNum ) | ||
2954 | { | ||
2955 | if ( doc->hasSelection( selNum ) ) { | ||
2956 | doc->removeSelection( selNum ); | ||
2957 | repaintChanged(); | ||
2958 | } | ||
2959 | if ( selNum > doc->numSelections() - 1 ) | ||
2960 | doc->addSelection( selNum ); | ||
2961 | QTextParag *p1 = doc->paragAt( paraFrom ); | ||
2962 | if ( !p1 ) | ||
2963 | return; | ||
2964 | QTextParag *p2 = doc->paragAt( paraTo ); | ||
2965 | if ( !p2 ) | ||
2966 | return; | ||
2967 | |||
2968 | if ( indexFrom > p1->length() - 1 ) | ||
2969 | indexFrom = p1->length() - 1; | ||
2970 | if ( indexTo > p2->length() - 1 ) | ||
2971 | indexTo = p2->length() - 1; | ||
2972 | |||
2973 | drawCursor( FALSE ); | ||
2974 | QTextCursor c = *cursor; | ||
2975 | QTextCursor oldCursor = *cursor; | ||
2976 | c.setParag( p1 ); | ||
2977 | c.setIndex( indexFrom ); | ||
2978 | cursor->setParag( p2 ); | ||
2979 | cursor->setIndex( indexTo ); | ||
2980 | doc->setSelectionStart( selNum, &c ); | ||
2981 | doc->setSelectionEnd( selNum, cursor ); | ||
2982 | repaintChanged(); | ||
2983 | ensureCursorVisible(); | ||
2984 | if ( selNum != QTextDocument::Standard ) | ||
2985 | *cursor = oldCursor; | ||
2986 | drawCursor( TRUE ); | ||
2987 | } | ||
2988 | |||
2989 | /*! | ||
2990 | If there is a selection, \e *\a paraFrom is set to the number of the | ||
2991 | paragraph in which the selection begins and \e *\a paraTo is set to | ||
2992 | the number of the paragraph in which the selection ends. (They could | ||
2993 | be the same.) \e *\a indexFrom is set to the index at which the | ||
2994 | selection begins within \e *\a paraFrom, and \e *\a indexTo is set to | ||
2995 | the index at which the selection ends within \e *\a paraTo. | ||
2996 | |||
2997 | If there is no selection, \e *\a paraFrom, \e *\a indexFrom, \e *\a | ||
2998 | paraTo and \e *\a indexTo are all set to -1. | ||
2999 | |||
3000 | \a paraFrom, \a indexFrom, \a paraTo and \a indexTo must be non-null | ||
3001 | int pointers. | ||
3002 | |||
3003 | The \a selNum is the number of the selection (multiple selections | ||
3004 | are supported). It defaults to 0 (the default selection). | ||
3005 | |||
3006 | \sa setSelection() selectedText | ||
3007 | */ | ||
3008 | |||
3009 | void QTextEdit::getSelection( int *paraFrom, int *indexFrom, | ||
3010 | int *paraTo, int *indexTo, int selNum ) const | ||
3011 | { | ||
3012 | if ( !paraFrom || !paraTo || !indexFrom || !indexTo ) | ||
3013 | return; | ||
3014 | if ( !doc->hasSelection( selNum ) ) { | ||
3015 | *paraFrom = -1; | ||
3016 | *indexFrom = -1; | ||
3017 | *paraTo = -1; | ||
3018 | *indexTo = -1; | ||
3019 | return; | ||
3020 | } | ||
3021 | |||
3022 | doc->selectionStart( selNum, *paraFrom, *indexFrom ); | ||
3023 | doc->selectionEnd( selNum, *paraTo, *indexTo ); | ||
3024 | } | ||
3025 | |||
3026 | /*! | ||
3027 | \property QTextEdit::textFormat | ||
3028 | \brief the text format: rich text, plain text or auto text | ||
3029 | |||
3030 | The text format is one of the following: | ||
3031 | \list | ||
3032 | \i PlainText - all characters, except newlines, are displayed | ||
3033 | verbatim, including spaces. Whenever a newline appears in the text the | ||
3034 | text edit inserts a hard line break and begins a new paragraph. | ||
3035 | \i RichText - rich text rendering. The available styles are | ||
3036 | defined in the default stylesheet QStyleSheet::defaultSheet(). | ||
3037 | \i AutoText - this is the default. The text edit autodetects | ||
3038 | which rendering style is best, \c PlainText or \c RichText. This is | ||
3039 | done by using the QStyleSheet::mightBeRichText() function. | ||
3040 | \endlist | ||
3041 | */ | ||
3042 | |||
3043 | void QTextEdit::setTextFormat( TextFormat format ) | ||
3044 | { | ||
3045 | doc->setTextFormat( format ); | ||
3046 | } | ||
3047 | |||
3048 | Qt::TextFormat QTextEdit::textFormat() const | ||
3049 | { | ||
3050 | return doc->textFormat(); | ||
3051 | } | ||
3052 | |||
3053 | /*! | ||
3054 | Returns the number of paragraphs in the text; this could be 0. | ||
3055 | */ | ||
3056 | |||
3057 | int QTextEdit::paragraphs() const | ||
3058 | { | ||
3059 | return doc->lastParag()->paragId() + 1; | ||
3060 | } | ||
3061 | |||
3062 | /*! | ||
3063 | Returns the number of lines in paragraph \a para, or -1 if there | ||
3064 | is no paragraph with index \a para. | ||
3065 | */ | ||
3066 | |||
3067 | int QTextEdit::linesOfParagraph( int para ) const | ||
3068 | { | ||
3069 | QTextParag *p = doc->paragAt( para ); | ||
3070 | if ( !p ) | ||
3071 | return -1; | ||
3072 | return p->lines(); | ||
3073 | } | ||
3074 | |||
3075 | /*! | ||
3076 | Returns the length of the paragraph \a para (number of | ||
3077 | characters), or -1 if there is no paragraph with index \a para | ||
3078 | */ | ||
3079 | |||
3080 | int QTextEdit::paragraphLength( int para ) const | ||
3081 | { | ||
3082 | QTextParag *p = doc->paragAt( para ); | ||
3083 | if ( !p ) | ||
3084 | return -1; | ||
3085 | return p->length() - 1; | ||
3086 | } | ||
3087 | |||
3088 | /*! | ||
3089 | Returns the number of lines in the text edit; this could be 0. | ||
3090 | |||
3091 | \warning This function may be slow. Lines change all the time | ||
3092 | during word wrapping, so this function has to iterate over all the | ||
3093 | paragraphs and get the number of lines from each one individually. | ||
3094 | */ | ||
3095 | |||
3096 | int QTextEdit::lines() const | ||
3097 | { | ||
3098 | QTextParag *p = doc->firstParag(); | ||
3099 | int l = 0; | ||
3100 | while ( p ) { | ||
3101 | l += p->lines(); | ||
3102 | p = p->next(); | ||
3103 | } | ||
3104 | |||
3105 | return l; | ||
3106 | } | ||
3107 | |||
3108 | /*! | ||
3109 | Returns the line number of the line in paragraph \a para in which | ||
3110 | the character at position \a index appears. The \a index position is | ||
3111 | relative to the beginning of the paragraph. If there is no such | ||
3112 | paragraph or no such character at the \a index position (e.g. the | ||
3113 | index is out of range) -1 is returned. | ||
3114 | */ | ||
3115 | |||
3116 | int QTextEdit::lineOfChar( int para, int index ) | ||
3117 | { | ||
3118 | QTextParag *p = doc->paragAt( para ); | ||
3119 | if ( !p ) | ||
3120 | return -1; | ||
3121 | |||
3122 | int idx, line; | ||
3123 | QTextStringChar *c = p->lineStartOfChar( index, &idx, &line ); | ||
3124 | if ( !c ) | ||
3125 | return -1; | ||
3126 | |||
3127 | return line; | ||
3128 | } | ||
3129 | |||
3130 | void QTextEdit::setModified( bool m ) | ||
3131 | { | ||
3132 | bool oldModified = modified; | ||
3133 | modified = m; | ||
3134 | if ( modified && doc->oTextValid ) | ||
3135 | doc->invalidateOriginalText(); | ||
3136 | if ( oldModified != modified ) | ||
3137 | emit modificationChanged( modified ); | ||
3138 | } | ||
3139 | |||
3140 | /*! \property QTextEdit::modified | ||
3141 | \brief whether the document has been modified by the user | ||
3142 | */ | ||
3143 | |||
3144 | bool QTextEdit::isModified() const | ||
3145 | { | ||
3146 | return modified; | ||
3147 | } | ||
3148 | |||
3149 | void QTextEdit::setModified() | ||
3150 | { | ||
3151 | if ( !isModified() ) | ||
3152 | setModified( TRUE ); | ||
3153 | } | ||
3154 | |||
3155 | /*! | ||
3156 | Returns TRUE if the current format is italic; otherwise returns FALSE. | ||
3157 | |||
3158 | \sa setItalic() | ||
3159 | */ | ||
3160 | |||
3161 | bool QTextEdit::italic() const | ||
3162 | { | ||
3163 | return currentFormat->font().italic(); | ||
3164 | } | ||
3165 | |||
3166 | /*! | ||
3167 | Returns TRUE if the current format is bold; otherwise returns FALSE. | ||
3168 | |||
3169 | \sa setBold() | ||
3170 | */ | ||
3171 | |||
3172 | bool QTextEdit::bold() const | ||
3173 | { | ||
3174 | return currentFormat->font().bold(); | ||
3175 | } | ||
3176 | |||
3177 | /*! | ||
3178 | Returns TRUE if the current format is underlined; otherwise returns | ||
3179 | FALSE. | ||
3180 | |||
3181 | \sa setUnderline() | ||
3182 | */ | ||
3183 | |||
3184 | bool QTextEdit::underline() const | ||
3185 | { | ||
3186 | return currentFormat->font().underline(); | ||
3187 | } | ||
3188 | |||
3189 | /*! | ||
3190 | Returns the font family of the current format. | ||
3191 | |||
3192 | \sa setFamily() setCurrentFont() setPointSize() | ||
3193 | */ | ||
3194 | |||
3195 | QString QTextEdit::family() const | ||
3196 | { | ||
3197 | return currentFormat->font().family(); | ||
3198 | } | ||
3199 | |||
3200 | /*! | ||
3201 | Returns the point size of the font of the current format. | ||
3202 | |||
3203 | \sa setFamily() setCurrentFont() setPointSize() | ||
3204 | |||
3205 | */ | ||
3206 | |||
3207 | int QTextEdit::pointSize() const | ||
3208 | { | ||
3209 | return currentFormat->font().pointSize(); | ||
3210 | } | ||
3211 | |||
3212 | /*! | ||
3213 | Returns the color of the current format. | ||
3214 | |||
3215 | \sa setColor() setPaper() | ||
3216 | */ | ||
3217 | |||
3218 | QColor QTextEdit::color() const | ||
3219 | { | ||
3220 | return currentFormat->color(); | ||
3221 | } | ||
3222 | |||
3223 | /*! | ||
3224 | Returns the font of the current format. | ||
3225 | |||
3226 | \sa setCurrentFont() setFamily() setPointSize() | ||
3227 | |||
3228 | */ | ||
3229 | |||
3230 | QFont QTextEdit::font() const | ||
3231 | { | ||
3232 | return currentFormat->font(); | ||
3233 | } | ||
3234 | |||
3235 | /*! | ||
3236 | Returns the alignment of the current paragraph. | ||
3237 | |||
3238 | \sa setAlignment() | ||
3239 | */ | ||
3240 | |||
3241 | int QTextEdit::alignment() const | ||
3242 | { | ||
3243 | return currentAlignment; | ||
3244 | } | ||
3245 | |||
3246 | void QTextEdit::startDrag() | ||
3247 | { | ||
3248 | #ifndef QT_NO_DRAGANDDROP | ||
3249 | mousePressed = FALSE; | ||
3250 | inDoubleClick = FALSE; | ||
3251 | QDragObject *drag = new QTextDrag( doc->selectedText( QTextDocument::Standard ), viewport() ); | ||
3252 | if ( isReadOnly() ) { | ||
3253 | drag->dragCopy(); | ||
3254 | } else { | ||
3255 | if ( drag->drag() && QDragObject::target() != this && QDragObject::target() != viewport() ) | ||
3256 | removeSelectedText(); | ||
3257 | } | ||
3258 | #endif | ||
3259 | } | ||
3260 | |||
3261 | /*! | ||
3262 | If \a select is TRUE (the default), all the text is selected as | ||
3263 | selection 0. | ||
3264 | If \a select is FALSE any selected text is unselected, i.e., the | ||
3265 | default selection (selection 0) is cleared. | ||
3266 | |||
3267 | \sa selectedText | ||
3268 | */ | ||
3269 | |||
3270 | void QTextEdit::selectAll( bool select ) | ||
3271 | { | ||
3272 | if ( !select ) | ||
3273 | doc->removeSelection( QTextDocument::Standard ); | ||
3274 | else | ||
3275 | doc->selectAll( QTextDocument::Standard ); | ||
3276 | repaintChanged(); | ||
3277 | emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); | ||
3278 | emit selectionChanged(); | ||
3279 | #ifndef QT_NO_CURSOR | ||
3280 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
3281 | #endif | ||
3282 | } | ||
3283 | |||
3284 | void QTextEdit::UndoRedoInfo::clear() | ||
3285 | { | ||
3286 | if ( valid() ) { | ||
3287 | if ( type == Insert || type == Return ) | ||
3288 | doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) ); | ||
3289 | else if ( type == Format ) | ||
3290 | doc->addCommand( new QTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) ); | ||
3291 | else if ( type == Alignment ) | ||
3292 | doc->addCommand( new QTextAlignmentCommand( doc, id, eid, newAlign, oldAligns ) ); | ||
3293 | else if ( type == ParagType ) | ||
3294 | doc->addCommand( new QTextParagTypeCommand( doc, id, eid, list, listStyle, oldStyles, oldListStyles ) ); | ||
3295 | else if ( type != Invalid ) | ||
3296 | doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), oldStyles, oldListStyles, oldAligns ) ); | ||
3297 | } | ||
3298 | d->text = QString::null; | ||
3299 | id = -1; | ||
3300 | index = -1; | ||
3301 | oldStyles.clear(); | ||
3302 | oldListStyles.clear(); | ||
3303 | oldAligns.resize( 0 ); | ||
3304 | } | ||
3305 | |||
3306 | |||
3307 | /*! | ||
3308 | If there is some selected text (in selection 0) it is deleted. If | ||
3309 | there is no selected text (in selection 0) the character to the | ||
3310 | right of the text cursor is deleted. | ||
3311 | |||
3312 | \sa removeSelectedText() cut() | ||
3313 | |||
3314 | */ | ||
3315 | |||
3316 | void QTextEdit::del() | ||
3317 | { | ||
3318 | if ( doc->hasSelection( QTextDocument::Standard ) ) { | ||
3319 | removeSelectedText(); | ||
3320 | return; | ||
3321 | } | ||
3322 | |||
3323 | doKeyboardAction( ActionDelete ); | ||
3324 | } | ||
3325 | |||
3326 | |||
3327 | QTextEdit::UndoRedoInfo::UndoRedoInfo( QTextDocument *dc ) | ||
3328 | : type( Invalid ), doc( dc ) | ||
3329 | { | ||
3330 | d = new QUndoRedoInfoPrivate; | ||
3331 | d->text = QString::null; | ||
3332 | id = -1; | ||
3333 | index = -1; | ||
3334 | } | ||
3335 | |||
3336 | QTextEdit::UndoRedoInfo::~UndoRedoInfo() | ||
3337 | { | ||
3338 | delete d; | ||
3339 | } | ||
3340 | |||
3341 | bool QTextEdit::UndoRedoInfo::valid() const | ||
3342 | { | ||
3343 | return d->text.length() > 0 && id >= 0 && index >= 0; | ||
3344 | } | ||
3345 | |||
3346 | /*! | ||
3347 | \internal | ||
3348 | |||
3349 | Resets the current format to the default format. | ||
3350 | */ | ||
3351 | |||
3352 | void QTextEdit::resetFormat() | ||
3353 | { | ||
3354 | setAlignment( Qt3::AlignAuto ); | ||
3355 | setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc ); | ||
3356 | setFormat( doc->formatCollection()->defaultFormat(), QTextFormat::Format ); | ||
3357 | } | ||
3358 | |||
3359 | /*! Returns the QStyleSheet which is currently used in this text edit. | ||
3360 | |||
3361 | \sa setStyleSheet() | ||
3362 | */ | ||
3363 | |||
3364 | QStyleSheet* QTextEdit::styleSheet() const | ||
3365 | { | ||
3366 | return doc->styleSheet(); | ||
3367 | } | ||
3368 | |||
3369 | /*! Sets the stylesheet to use with this text edit to \a styleSheet. Changes | ||
3370 | will only take effect for new text added with setText() or append(). | ||
3371 | |||
3372 | \sa styleSheet() | ||
3373 | */ | ||
3374 | |||
3375 | void QTextEdit::setStyleSheet( QStyleSheet* styleSheet ) | ||
3376 | { | ||
3377 | doc->setStyleSheet( styleSheet ); | ||
3378 | } | ||
3379 | |||
3380 | /*! | ||
3381 | \property QTextEdit::paper | ||
3382 | \brief the background (paper) brush. | ||
3383 | |||
3384 | The brush that is currently used to draw the background of the | ||
3385 | text edit. The initial setting is an empty brush. | ||
3386 | */ | ||
3387 | |||
3388 | void QTextEdit::setPaper( const QBrush& pap ) | ||
3389 | { | ||
3390 | doc->setPaper( new QBrush( pap ) ); | ||
3391 | viewport()->setBackgroundColor( pap.color() ); | ||
3392 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3393 | } | ||
3394 | |||
3395 | QBrush QTextEdit::paper() const | ||
3396 | { | ||
3397 | if ( doc->paper() ) | ||
3398 | return *doc->paper(); | ||
3399 | return QBrush(); | ||
3400 | } | ||
3401 | |||
3402 | /*! | ||
3403 | \property QTextEdit::linkUnderline | ||
3404 | \brief whether hypertext links will be underlined | ||
3405 | |||
3406 | If TRUE (the default) hypertext links will be displayed underlined. | ||
3407 | If FALSE links will not be displayed underlined. | ||
3408 | */ | ||
3409 | |||
3410 | void QTextEdit::setLinkUnderline( bool b ) | ||
3411 | { | ||
3412 | if ( b == doc->underlineLinks() ) | ||
3413 | return; | ||
3414 | doc->setUnderlineLinks( b ); | ||
3415 | updateStyles(); | ||
3416 | } | ||
3417 | |||
3418 | bool QTextEdit::linkUnderline() const | ||
3419 | { | ||
3420 | return doc->underlineLinks(); | ||
3421 | } | ||
3422 | |||
3423 | /*! Sets the text edit's mimesource factory to \a factory. See | ||
3424 | QMimeSourceFactory for further details. | ||
3425 | |||
3426 | \sa mimeSourceFactory() | ||
3427 | */ | ||
3428 | |||
3429 | void QTextEdit::setMimeSourceFactory( QMimeSourceFactory* factory ) | ||
3430 | { | ||
3431 | doc->setMimeSourceFactory( factory ); | ||
3432 | } | ||
3433 | |||
3434 | /*! Returns the QMimeSourceFactory which is currently used by this | ||
3435 | text edit. | ||
3436 | |||
3437 | \sa setMimeSourceFactory() | ||
3438 | */ | ||
3439 | |||
3440 | QMimeSourceFactory* QTextEdit::mimeSourceFactory() const | ||
3441 | { | ||
3442 | return doc->mimeSourceFactory(); | ||
3443 | } | ||
3444 | |||
3445 | /*! | ||
3446 | Returns how many pixels high the text edit needs to be to display | ||
3447 | all the text if the text edit is \a w pixels wide. | ||
3448 | */ | ||
3449 | |||
3450 | int QTextEdit::heightForWidth( int w ) const | ||
3451 | { | ||
3452 | int oldw = doc->width(); | ||
3453 | doc->doLayout( 0, w ); | ||
3454 | int h = doc->height(); | ||
3455 | doc->setWidth( oldw ); | ||
3456 | doc->invalidate(); | ||
3457 | ( (QTextEdit*)this )->formatMore(); | ||
3458 | return h; | ||
3459 | } | ||
3460 | |||
3461 | /*! Appends the text \a text to the end of the text edit. | ||
3462 | Note that the undo/redo history is cleared by this function. | ||
3463 | */ | ||
3464 | |||
3465 | void QTextEdit::append( const QString &text ) | ||
3466 | { | ||
3467 | // flush and clear the undo/redo stack if necessary | ||
3468 | if ( isReadOnly() && undoRedoInfo.valid() ) { | ||
3469 | undoRedoInfo.clear(); | ||
3470 | doc->commands()->clear(); | ||
3471 | } | ||
3472 | doc->removeSelection( QTextDocument::Standard ); | ||
3473 | TextFormat f = doc->textFormat(); | ||
3474 | if ( f == AutoText ) { | ||
3475 | if ( QStyleSheet::mightBeRichText( text ) ) | ||
3476 | f = RichText; | ||
3477 | else | ||
3478 | f = PlainText; | ||
3479 | } | ||
3480 | if ( f == PlainText ) { | ||
3481 | QTextCursor oldc( *cursor ); | ||
3482 | ensureFormatted( doc->lastParag() ); | ||
3483 | bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight() - | ||
3484 | ( horizontalScrollBar()->isVisible() ? horizontalScrollBar()->height() : 0 ); | ||
3485 | if ( !scrollToEnd ) | ||
3486 | blockEnsureCursorVisible = TRUE; | ||
3487 | cursor->gotoEnd(); | ||
3488 | if ( cursor->index() > 0 ) | ||
3489 | cursor->splitAndInsertEmptyParag(); | ||
3490 | QTextCursor oldCursor2 = *cursor; | ||
3491 | cursor->insert( text, TRUE ); | ||
3492 | if ( doc->useFormatCollection() && currentFormat != cursor->parag()->at( cursor->index() )->format() ) { | ||
3493 | doc->setSelectionStart( QTextDocument::Temp, &oldCursor2 ); | ||
3494 | doc->setSelectionEnd( QTextDocument::Temp, cursor ); | ||
3495 | doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); | ||
3496 | doc->removeSelection( QTextDocument::Temp ); | ||
3497 | } | ||
3498 | formatMore(); | ||
3499 | repaintChanged(); | ||
3500 | ensureCursorVisible(); | ||
3501 | drawCursor( TRUE ); | ||
3502 | *cursor = oldc; | ||
3503 | if ( !scrollToEnd ) | ||
3504 | blockEnsureCursorVisible = FALSE; | ||
3505 | } else if ( f == RichText ) { | ||
3506 | doc->setRichTextInternal( text ); | ||
3507 | repaintChanged(); | ||
3508 | } | ||
3509 | setModified(); | ||
3510 | emit textChanged(); | ||
3511 | } | ||
3512 | |||
3513 | /*! \property QTextEdit::hasSelectedText | ||
3514 | \brief whether some text is selected in selection 0 | ||
3515 | */ | ||
3516 | |||
3517 | bool QTextEdit::hasSelectedText() const | ||
3518 | { | ||
3519 | return doc->hasSelection( QTextDocument::Standard ); | ||
3520 | } | ||
3521 | |||
3522 | /*!\property QTextEdit::selectedText | ||
3523 | \brief The selected text (from selection 0) or an empty string if | ||
3524 | there is no currently selected text (in selection 0). | ||
3525 | |||
3526 | The text is always returned as \c PlainText regardless of the text | ||
3527 | format. In a future version of Qt an HTML subset \e may be returned | ||
3528 | depending on the text format. | ||
3529 | |||
3530 | \sa hasSelectedText | ||
3531 | */ | ||
3532 | |||
3533 | QString QTextEdit::selectedText() const | ||
3534 | { | ||
3535 | return doc->selectedText( QTextDocument::Standard ); | ||
3536 | } | ||
3537 | |||
3538 | bool QTextEdit::handleReadOnlyKeyEvent( QKeyEvent *e ) | ||
3539 | { | ||
3540 | switch( e->key() ) { | ||
3541 | case Key_Down: | ||
3542 | setContentsPos( contentsX(), contentsY() + 10 ); | ||
3543 | break; | ||
3544 | case Key_Up: | ||
3545 | setContentsPos( contentsX(), contentsY() - 10 ); | ||
3546 | break; | ||
3547 | case Key_Left: | ||
3548 | setContentsPos( contentsX() - 10, contentsY() ); | ||
3549 | break; | ||
3550 | case Key_Right: | ||
3551 | setContentsPos( contentsX() + 10, contentsY() ); | ||
3552 | break; | ||
3553 | case Key_PageUp: | ||
3554 | setContentsPos( contentsX(), contentsY() - visibleHeight() ); | ||
3555 | break; | ||
3556 | case Key_PageDown: | ||
3557 | setContentsPos( contentsX(), contentsY() + visibleHeight() ); | ||
3558 | break; | ||
3559 | case Key_Home: | ||
3560 | setContentsPos( contentsX(), 0 ); | ||
3561 | break; | ||
3562 | case Key_End: | ||
3563 | setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); | ||
3564 | break; | ||
3565 | case Key_F16: // Copy key on Sun keyboards | ||
3566 | copy(); | ||
3567 | break; | ||
3568 | #ifndef QT_NO_NETWORKPROTOCOL | ||
3569 | case Key_Return: | ||
3570 | case Key_Enter: | ||
3571 | case Key_Space: { | ||
3572 | if ( !doc->focusIndicator.href.isEmpty() ) { | ||
3573 | QUrl u( doc->context(), doc->focusIndicator.href, TRUE ); | ||
3574 | emitLinkClicked( u.toString( FALSE, FALSE ) ); | ||
3575 | #ifndef QT_NO_CURSOR | ||
3576 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
3577 | #endif | ||
3578 | } | ||
3579 | } break; | ||
3580 | #endif | ||
3581 | default: | ||
3582 | if ( e->state() & ControlButton ) { | ||
3583 | switch ( e->key() ) { | ||
3584 | case Key_C: case Key_F16: // Copy key on Sun keyboards | ||
3585 | copy(); | ||
3586 | break; | ||
3587 | } | ||
3588 | } | ||
3589 | return FALSE; | ||
3590 | } | ||
3591 | return TRUE; | ||
3592 | } | ||
3593 | |||
3594 | /*! Returns the context of the edit. | ||
3595 | The context is a path which the text edit's QMimeSourceFactory | ||
3596 | uses to resolve the locations of files and images. | ||
3597 | |||
3598 | \sa text | ||
3599 | */ | ||
3600 | |||
3601 | QString QTextEdit::context() const | ||
3602 | { | ||
3603 | return doc->context(); | ||
3604 | } | ||
3605 | |||
3606 | /*! | ||
3607 | \property QTextEdit::documentTitle | ||
3608 | \brief the title of the document parsed from the text. | ||
3609 | |||
3610 | For \c PlainText the title will be an empty string. For \c RichText | ||
3611 | the title will be the text between the \c{<title>} tags, if present, | ||
3612 | otherwise an empty string. | ||
3613 | */ | ||
3614 | |||
3615 | QString QTextEdit::documentTitle() const | ||
3616 | { | ||
3617 | return doc->attributes()[ "title" ]; | ||
3618 | } | ||
3619 | |||
3620 | void QTextEdit::makeParagVisible( QTextParag *p ) | ||
3621 | { | ||
3622 | setContentsPos( contentsX(), QMIN( p->rect().y(), contentsHeight() - visibleHeight() ) ); | ||
3623 | } | ||
3624 | |||
3625 | /*! Scrolls the text edit to make the text at the anchor called \a name | ||
3626 | visible, if it can be found in the document. If the anchor isn't found | ||
3627 | no scrolling will occur. An anchor is defined using the HTML anchor | ||
3628 | tag, e.g. \c{<a name="target">}. | ||
3629 | */ | ||
3630 | |||
3631 | void QTextEdit::scrollToAnchor( const QString& name ) | ||
3632 | { | ||
3633 | if ( name.isEmpty() ) | ||
3634 | return; | ||
3635 | sync(); | ||
3636 | QTextCursor cursor( doc ); | ||
3637 | QTextParag* last = doc->lastParag(); | ||
3638 | do { | ||
3639 | QTextStringChar* c = cursor.parag()->at( cursor.index() ); | ||
3640 | if( c->isAnchor() ) { | ||
3641 | QString a = c->anchorName(); | ||
3642 | if ( a == name || | ||
3643 | (a.contains( '#' ) && QStringList::split( '#', a ).contains( name ) ) ) { | ||
3644 | setContentsPos( contentsX(), QMIN( cursor.parag()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) ); | ||
3645 | return; | ||
3646 | } | ||
3647 | } | ||
3648 | cursor.gotoNextLetter(); | ||
3649 | } while( cursor.parag() != last || !cursor.atParagEnd() ); | ||
3650 | } | ||
3651 | |||
3652 | /*! If there is an anchor at position \a pos (in contents | ||
3653 | coordinates), its name is returned, otherwise an empty string is | ||
3654 | returned. | ||
3655 | */ | ||
3656 | |||
3657 | QString QTextEdit::anchorAt( const QPoint& pos ) | ||
3658 | { | ||
3659 | QTextCursor c( doc ); | ||
3660 | placeCursor( pos, &c ); | ||
3661 | return c.parag()->at( c.index() )->anchorHref(); | ||
3662 | } | ||
3663 | |||
3664 | void QTextEdit::documentWidthChanged( int w ) | ||
3665 | { | ||
3666 | resizeContents( QMAX( visibleWidth(), w), contentsHeight() ); | ||
3667 | } | ||
3668 | |||
3669 | /*! | ||
3670 | Updates all the rendering styles used to display the text. You will | ||
3671 | probably want to call this function after calling setStyleSheet(). | ||
3672 | */ | ||
3673 | |||
3674 | void QTextEdit::updateStyles() | ||
3675 | { | ||
3676 | doc->updateStyles(); | ||
3677 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3678 | } | ||
3679 | |||
3680 | void QTextEdit::setDocument( QTextDocument *dc ) | ||
3681 | { | ||
3682 | if ( dc == doc ) | ||
3683 | return; | ||
3684 | doc = dc; | ||
3685 | cursor->setDocument( doc ); | ||
3686 | clearUndoRedo(); | ||
3687 | lastFormatted = 0; | ||
3688 | } | ||
3689 | |||
3690 | #ifndef QT_NO_CLIPBOARD | ||
3691 | |||
3692 | /*! | ||
3693 | Pastes the text with format \a subtype from the clipboard into the | ||
3694 | text edit at the current cursor position. The \a subtype can be | ||
3695 | "plain" or "html". | ||
3696 | |||
3697 | If there is no text with format \a subtype in the clipboard nothing | ||
3698 | happens. | ||
3699 | |||
3700 | \sa paste() cut() QTextEdit::copy() | ||
3701 | */ | ||
3702 | void QTextEdit::pasteSubType( const QCString& subtype ) | ||
3703 | { | ||
3704 | QCString st = subtype; | ||
3705 | QString t = QApplication::clipboard()->text(st); | ||
3706 | if ( !t.isEmpty() ) { | ||
3707 | #if defined(Q_OS_WIN32) | ||
3708 | // Need to convert CRLF to LF | ||
3709 | int index = t.find( QString::fromLatin1("\r\n"), 0 ); | ||
3710 | while ( index != -1 ) { | ||
3711 | t.replace( index, 2, QChar('\n') ); | ||
3712 | index = t.find( "\r\n", index ); | ||
3713 | } | ||
3714 | #elif defined(Q_OS_MAC) | ||
3715 | //need to convert CR to LF | ||
3716 | for( unsigned int index = 0; index < t.length(); index++ ) | ||
3717 | if(t[index] == '\r') | ||
3718 | t[index] = '\n'; | ||
3719 | #endif | ||
3720 | for ( int i=0; (uint) i<t.length(); i++ ) { | ||
3721 | if ( t[ i ] < ' ' && t[ i ] != '\n' && t[ i ] != '\t' ) | ||
3722 | t[ i ] = ' '; | ||
3723 | } | ||
3724 | if ( !t.isEmpty() ) | ||
3725 | insert( t, FALSE, TRUE, TRUE ); | ||
3726 | } | ||
3727 | } | ||
3728 | |||
3729 | #ifndef QT_NO_MIMECLIPBOARD | ||
3730 | /*! | ||
3731 | Prompts the user to choose a type from a list of text types available, | ||
3732 | then copies text from the clipboard (if there is any) into the text | ||
3733 | edit at the current text cursor position. Any selected text (in | ||
3734 | selection 0) is first deleted. | ||
3735 | */ | ||
3736 | void QTextEdit::pasteSpecial( const QPoint& pt ) | ||
3737 | { | ||
3738 | QCString st = pickSpecial( QApplication::clipboard()->data(), TRUE, pt ); | ||
3739 | if ( !st.isEmpty() ) | ||
3740 | pasteSubType( st ); | ||
3741 | } | ||
3742 | #endif | ||
3743 | #ifndef QT_NO_MIME | ||
3744 | QCString QTextEdit::pickSpecial( QMimeSource* ms, bool always_ask, const QPoint& pt ) | ||
3745 | { | ||
3746 | if ( ms ) { | ||
3747 | #ifndef QT_NO_POPUPMENU | ||
3748 | QPopupMenu popup( this, "qt_pickspecial_menu" ); | ||
3749 | QString fmt; | ||
3750 | int n = 0; | ||
3751 | QDict<void> done; | ||
3752 | for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { | ||
3753 | int semi = fmt.find( ";" ); | ||
3754 | if ( semi >= 0 ) | ||
3755 | fmt = fmt.left( semi ); | ||
3756 | if ( fmt.left( 5 ) == "text/" ) { | ||
3757 | fmt = fmt.mid( 5 ); | ||
3758 | if ( !done.find( fmt ) ) { | ||
3759 | done.insert( fmt,(void*)1 ); | ||
3760 | popup.insertItem( fmt, i ); | ||
3761 | n++; | ||
3762 | } | ||
3763 | } | ||
3764 | } | ||
3765 | if ( n ) { | ||
3766 | int i = n ==1 && !always_ask ? popup.idAt( 0 ) : popup.exec( pt ); | ||
3767 | if ( i >= 0 ) | ||
3768 | return popup.text(i).latin1(); | ||
3769 | } | ||
3770 | #else | ||
3771 | QString fmt; | ||
3772 | for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { | ||
3773 | int semi = fmt.find( ";" ); | ||
3774 | if ( semi >= 0 ) | ||
3775 | fmt = fmt.left( semi ); | ||
3776 | if ( fmt.left( 5 ) == "text/" ) { | ||
3777 | fmt = fmt.mid( 5 ); | ||
3778 | return fmt.latin1(); | ||
3779 | } | ||
3780 | } | ||
3781 | #endif | ||
3782 | } | ||
3783 | return QCString(); | ||
3784 | } | ||
3785 | #endif // QT_NO_MIME | ||
3786 | #endif // QT_NO_CLIPBOARD | ||
3787 | |||
3788 | /*! \enum QTextEdit::WordWrap | ||
3789 | |||
3790 | This enum defines the QTextEdit's word wrap modes. The following | ||
3791 | values are valid: | ||
3792 | |||
3793 | \value NoWrap Do not wrap the text. | ||
3794 | |||
3795 | \value WidgetWidth Wrap the text at the current width of the | ||
3796 | widget (this is the default). Wrapping is at whitespace by default; | ||
3797 | this can be changed with setWrapPolicy(). | ||
3798 | |||
3799 | \value FixedPixelWidth Wrap the text at a fixed number of pixels from | ||
3800 | the widget's left side. The number of pixels is set with | ||
3801 | wrapColumnOrWidth(). | ||
3802 | |||
3803 | \value FixedColumnWidth Wrap the text at a fixed number of character | ||
3804 | columns from the widget's left side. The number of characters is set | ||
3805 | with wrapColumnOrWidth(). | ||
3806 | This is useful if you need formatted text that can also be | ||
3807 | displayed gracefully on devices with monospaced fonts, for example a | ||
3808 | standard VT100 terminal, where you might set wrapColumnOrWidth() to | ||
3809 | 80. | ||
3810 | |||
3811 | \sa setWordWrap() wordWrap() | ||
3812 | */ | ||
3813 | |||
3814 | /*! | ||
3815 | \property QTextEdit::wordWrap | ||
3816 | \brief the word wrap mode | ||
3817 | |||
3818 | The default mode is \c WidgetWidth which causes words to be wrapped | ||
3819 | at the right edge of the text edit. Wrapping occurs at whitespace, | ||
3820 | keeping whole words intact. If you want wrapping to occur within | ||
3821 | words use setWrapPolicy(). If you set a wrap mode of \c | ||
3822 | FixedPixelWidth or \c FixedColumnWidth you should also call | ||
3823 | setWrapColumnOrWidth() with the width you want. | ||
3824 | |||
3825 | \sa WordWrap, wrapColumnOrWidth, wrapPolicy, | ||
3826 | */ | ||
3827 | |||
3828 | void QTextEdit::setWordWrap( WordWrap mode ) | ||
3829 | { | ||
3830 | if ( wrapMode == mode ) | ||
3831 | return; | ||
3832 | wrapMode = mode; | ||
3833 | switch ( mode ) { | ||
3834 | case NoWrap: | ||
3835 | document()->formatter()->setWrapEnabled( FALSE ); | ||
3836 | document()->formatter()->setWrapAtColumn( -1 ); | ||
3837 | doc->setWidth( visibleWidth() ); | ||
3838 | doc->setMinimumWidth( -1 ); | ||
3839 | doc->invalidate(); | ||
3840 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3841 | lastFormatted = doc->firstParag(); | ||
3842 | interval = 0; | ||
3843 | formatMore(); | ||
3844 | break; | ||
3845 | case WidgetWidth: | ||
3846 | document()->formatter()->setWrapEnabled( TRUE ); | ||
3847 | document()->formatter()->setWrapAtColumn( -1 ); | ||
3848 | doResize(); | ||
3849 | break; | ||
3850 | case FixedPixelWidth: | ||
3851 | document()->formatter()->setWrapEnabled( TRUE ); | ||
3852 | document()->formatter()->setWrapAtColumn( -1 ); | ||
3853 | if ( wrapWidth < 0 ) | ||
3854 | wrapWidth = 200; | ||
3855 | setWrapColumnOrWidth( wrapWidth ); | ||
3856 | break; | ||
3857 | case FixedColumnWidth: | ||
3858 | if ( wrapWidth < 0 ) | ||
3859 | wrapWidth = 80; | ||
3860 | document()->formatter()->setWrapEnabled( TRUE ); | ||
3861 | document()->formatter()->setWrapAtColumn( wrapWidth ); | ||
3862 | setWrapColumnOrWidth( wrapWidth ); | ||
3863 | break; | ||
3864 | } | ||
3865 | } | ||
3866 | |||
3867 | QTextEdit::WordWrap QTextEdit::wordWrap() const | ||
3868 | { | ||
3869 | return wrapMode; | ||
3870 | } | ||
3871 | |||
3872 | /*! | ||
3873 | \property QTextEdit::wrapColumnOrWidth | ||
3874 | \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped | ||
3875 | |||
3876 | If the wrap mode is \c FixedPixelWidth, the value is the number | ||
3877 | of pixels from the left edge of the text edit at which text should | ||
3878 | be wrapped. If the wrap mode is \c FixedColumnWidth, the value is | ||
3879 | the column number (in character columns) from the left edge of the | ||
3880 | text edit at which text should be wrapped. | ||
3881 | |||
3882 | \sa wordWrap | ||
3883 | */ | ||
3884 | void QTextEdit::setWrapColumnOrWidth( int value ) | ||
3885 | { | ||
3886 | wrapWidth = value; | ||
3887 | if ( wrapMode == FixedColumnWidth ) { | ||
3888 | document()->formatter()->setWrapAtColumn( wrapWidth ); | ||
3889 | resizeContents( 0, 0 ); | ||
3890 | doc->setWidth( visibleWidth() ); | ||
3891 | doc->setMinimumWidth( -1 ); | ||
3892 | } else if (wrapMode == FixedPixelWidth ) { | ||
3893 | document()->formatter()->setWrapAtColumn( -1 ); | ||
3894 | resizeContents( wrapWidth, 0 ); | ||
3895 | doc->setWidth( wrapWidth ); | ||
3896 | doc->setMinimumWidth( wrapWidth ); | ||
3897 | } else { | ||
3898 | return; | ||
3899 | } | ||
3900 | doc->invalidate(); | ||
3901 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3902 | lastFormatted = doc->firstParag(); | ||
3903 | interval = 0; | ||
3904 | formatMore(); | ||
3905 | } | ||
3906 | |||
3907 | int QTextEdit::wrapColumnOrWidth() const | ||
3908 | { | ||
3909 | if ( wrapMode == WidgetWidth ) | ||
3910 | return visibleWidth(); | ||
3911 | return wrapWidth; | ||
3912 | } | ||
3913 | |||
3914 | |||
3915 | /*! \enum QTextEdit::WrapPolicy | ||
3916 | |||
3917 | This enum defines where text can be wrapped in word wrap mode. | ||
3918 | |||
3919 | The following values are valid: | ||
3920 | \value AtWhiteSpace Break lines at whitespace, e.g. spaces or | ||
3921 | newlines. | ||
3922 | \value Anywhere Break anywhere, including within words. | ||
3923 | \value AtWordBoundary Don't use this deprecated value (it is a | ||
3924 | synonym for AtWhiteSpace which you should use instead). | ||
3925 | |||
3926 | \sa setWrapPolicy() | ||
3927 | */ | ||
3928 | |||
3929 | /*! | ||
3930 | \property QTextEdit::wrapPolicy | ||
3931 | \brief the word wrap policy, at whitespace or anywhere | ||
3932 | |||
3933 | Defines where text can be wrapped when word wrap mode is not | ||
3934 | \c NoWrap. The choices are \c AtWhiteSpace (the default) and \c | ||
3935 | Anywhere. | ||
3936 | |||
3937 | \sa wordWrap | ||
3938 | */ | ||
3939 | |||
3940 | void QTextEdit::setWrapPolicy( WrapPolicy policy ) | ||
3941 | { | ||
3942 | if ( wPolicy == policy ) | ||
3943 | return; | ||
3944 | wPolicy = policy; | ||
3945 | QTextFormatter *formatter; | ||
3946 | if ( policy == AtWhiteSpace ) | ||
3947 | formatter = new QTextFormatterBreakWords; | ||
3948 | else | ||
3949 | formatter = new QTextFormatterBreakInWords; | ||
3950 | formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() ); | ||
3951 | formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) ); | ||
3952 | document()->setFormatter( formatter ); | ||
3953 | doc->invalidate(); | ||
3954 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3955 | lastFormatted = doc->firstParag(); | ||
3956 | interval = 0; | ||
3957 | formatMore(); | ||
3958 | } | ||
3959 | |||
3960 | QTextEdit::WrapPolicy QTextEdit::wrapPolicy() const | ||
3961 | { | ||
3962 | return wPolicy; | ||
3963 | } | ||
3964 | |||
3965 | /*! | ||
3966 | Deletes all the text in the text edit. | ||
3967 | |||
3968 | \sa cut() removeSelectedText() setText() | ||
3969 | |||
3970 | */ | ||
3971 | |||
3972 | void QTextEdit::clear() | ||
3973 | { | ||
3974 | // make clear undoable | ||
3975 | doc->selectAll( QTextDocument::Temp ); | ||
3976 | removeSelectedText( QTextDocument::Temp ); | ||
3977 | |||
3978 | setContentsPos( 0, 0 ); | ||
3979 | if ( cursor->isValid() ) | ||
3980 | cursor->restoreState(); | ||
3981 | doc->clear( TRUE ); | ||
3982 | cursor->setDocument( doc ); | ||
3983 | cursor->setParag( doc->firstParag() ); | ||
3984 | cursor->setIndex( 0 ); | ||
3985 | lastFormatted = 0; | ||
3986 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
3987 | |||
3988 | emit cursorPositionChanged( cursor ); | ||
3989 | emit cursorPositionChanged( cursor->parag()->paragId(), cursor->index() ); | ||
3990 | } | ||
3991 | |||
3992 | int QTextEdit::undoDepth() const | ||
3993 | { | ||
3994 | return document()->undoDepth(); | ||
3995 | } | ||
3996 | |||
3997 | /*! | ||
3998 | \property QTextEdit::length | ||
3999 | \brief the number of characters in the text | ||
4000 | |||
4001 | */ | ||
4002 | |||
4003 | int QTextEdit::length() const | ||
4004 | { | ||
4005 | return document()->length(); | ||
4006 | } | ||
4007 | |||
4008 | /*! | ||
4009 | \property QTextEdit::tabStopWidth | ||
4010 | \brief the tab stop width in pixels | ||
4011 | |||
4012 | */ | ||
4013 | |||
4014 | int QTextEdit::tabStopWidth() const | ||
4015 | { | ||
4016 | return document()->tabStopWidth(); | ||
4017 | } | ||
4018 | |||
4019 | void QTextEdit::setUndoDepth( int d ) | ||
4020 | { | ||
4021 | document()->setUndoDepth( d ); | ||
4022 | } | ||
4023 | |||
4024 | void QTextEdit::setTabStopWidth( int ts ) | ||
4025 | { | ||
4026 | document()->setTabStops( ts ); | ||
4027 | doc->invalidate(); | ||
4028 | lastFormatted = doc->firstParag(); | ||
4029 | interval = 0; | ||
4030 | formatMore(); | ||
4031 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
4032 | } | ||
4033 | |||
4034 | /*! \reimp */ | ||
4035 | |||
4036 | QSize QTextEdit::sizeHint() const | ||
4037 | { | ||
4038 | // ### calculate a reasonable one | ||
4039 | return QSize( 100, 100 ); | ||
4040 | } | ||
4041 | |||
4042 | void QTextEdit::clearUndoRedo() | ||
4043 | { | ||
4044 | undoRedoInfo.clear(); | ||
4045 | emit undoAvailable( doc->commands()->isUndoAvailable() ); | ||
4046 | emit redoAvailable( doc->commands()->isRedoAvailable() ); | ||
4047 | } | ||
4048 | |||
4049 | /*! This function gets the format of the character at position \a | ||
4050 | index in paragraph \a para. Sets \a font to the character's font, \a | ||
4051 | color to the character's color and \a verticalAlignment to the | ||
4052 | character's vertical alignment. | ||
4053 | |||
4054 | Returns FALSE if \a para or \a index is out of range otherwise | ||
4055 | returns TRUE. | ||
4056 | */ | ||
4057 | |||
4058 | bool QTextEdit::getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment ) | ||
4059 | { | ||
4060 | if ( !font || !color ) | ||
4061 | return FALSE; | ||
4062 | QTextParag *p = doc->paragAt( para ); | ||
4063 | if ( !p ) | ||
4064 | return FALSE; | ||
4065 | if ( index < 0 || index >= p->length() ) | ||
4066 | return FALSE; | ||
4067 | *font = p->at( index )->format()->font(); | ||
4068 | *color = p->at( index )->format()->color(); | ||
4069 | *verticalAlignment = (VerticalAlignment)p->at( index )->format()->vAlign(); | ||
4070 | return TRUE; | ||
4071 | } | ||
4072 | |||
4073 | /*! This function gets the format of the paragraph \a para. Sets \a | ||
4074 | font to the paragraphs's font, \a color to the paragraph's color, \a | ||
4075 | verticalAlignment to the paragraph's vertical alignment, \a | ||
4076 | alignment to the paragraph's alignment, \a displayMode to the | ||
4077 | paragraph's display mode, \a listStyle to the paragraph's list style | ||
4078 | (if the display mode is QStyleSheetItem::DisplayListItem) and \a | ||
4079 | listDepth to the depth of the list (if the display mode is | ||
4080 | QStyleSheetItem::DisplayListItem). | ||
4081 | |||
4082 | Returns FALSE if \a para is out of range otherwise returns TRUE. | ||
4083 | */ | ||
4084 | |||
4085 | bool QTextEdit::getParagraphFormat( int para, QFont *font, QColor *color, | ||
4086 | VerticalAlignment *verticalAlignment, int *alignment, | ||
4087 | QStyleSheetItem::DisplayMode *displayMode, | ||
4088 | QStyleSheetItem::ListStyle *listStyle, | ||
4089 | int *listDepth ) | ||
4090 | { | ||
4091 | if ( !font || !color || !alignment || !displayMode || !listStyle ) | ||
4092 | return FALSE; | ||
4093 | QTextParag *p = doc->paragAt( para ); | ||
4094 | if ( !p ) | ||
4095 | return FALSE; | ||
4096 | *font = p->paragFormat()->font(); | ||
4097 | *color = p->paragFormat()->color(); | ||
4098 | *verticalAlignment = (VerticalAlignment)p->paragFormat()->vAlign(); | ||
4099 | *alignment = p->alignment(); | ||
4100 | *displayMode = p->style() ? p->style()->displayMode() : QStyleSheetItem::DisplayBlock; | ||
4101 | *listStyle = p->listStyle(); | ||
4102 | *listDepth = p->listDepth(); | ||
4103 | return TRUE; | ||
4104 | } | ||
4105 | |||
4106 | |||
4107 | |||
4108 | /*! | ||
4109 | |||
4110 | This function is called to create a right mouse button popup menu | ||
4111 | at the document position \a pos. If you want to create a custom | ||
4112 | popup menu, reimplement this function and return the created | ||
4113 | popup menu. Ownership of the popup menu is transferred to the | ||
4114 | caller. | ||
4115 | */ | ||
4116 | |||
4117 | QPopupMenu *QTextEdit::createPopupMenu( const QPoint& pos ) | ||
4118 | { | ||
4119 | #ifndef QT_NO_POPUPMENU | ||
4120 | QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" ); | ||
4121 | if ( !isReadOnly() ) { | ||
4122 | d->id[ IdUndo ] = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) ); | ||
4123 | d->id[ IdRedo ] = popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) ); | ||
4124 | popup->insertSeparator(); | ||
4125 | } | ||
4126 | #ifndef QT_NO_CLIPBOARD | ||
4127 | if ( !isReadOnly() ) | ||
4128 | d->id[ IdCut ] = popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) ); | ||
4129 | d->id[ IdCopy ] = popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) ); | ||
4130 | if ( !isReadOnly() ) | ||
4131 | d->id[ IdPaste ] = popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) ); | ||
4132 | #endif | ||
4133 | if ( !isReadOnly() ) { | ||
4134 | d->id[ IdClear ] = popup->insertItem( tr( "Clear" ) ); | ||
4135 | popup->insertSeparator(); | ||
4136 | } | ||
4137 | #if defined(Q_WS_X11) | ||
4138 | d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) ); | ||
4139 | #else | ||
4140 | d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) + ACCEL_KEY( A ) ); | ||
4141 | #endif | ||
4142 | popup->setItemEnabled( d->id[ IdUndo ], !isReadOnly() && doc->commands()->isUndoAvailable() ); | ||
4143 | popup->setItemEnabled( d->id[ IdRedo ], !isReadOnly() && doc->commands()->isRedoAvailable() ); | ||
4144 | #ifndef QT_NO_CLIPBOARD | ||
4145 | popup->setItemEnabled( d->id[ IdCut ], !isReadOnly() && doc->hasSelection( QTextDocument::Standard, TRUE ) ); | ||
4146 | popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( QTextDocument::Standard, TRUE ) ); | ||
4147 | popup->setItemEnabled( d->id[ IdPaste ], !isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); | ||
4148 | #endif | ||
4149 | popup->setItemEnabled( d->id[ IdClear ], !isReadOnly() && !text().isEmpty() ); | ||
4150 | popup->setItemEnabled( d->id[ IdSelectAll ], (bool)text().length() ); | ||
4151 | return popup; | ||
4152 | #else | ||
4153 | return 0; | ||
4154 | #endif | ||
4155 | } | ||
4156 | |||
4157 | /*! \overload | ||
4158 | This function is called to create a right mouse button popup menu. | ||
4159 | If you want to create a custom popup menu, reimplement this function | ||
4160 | and return the created popup menu. Ownership of the popup menu is | ||
4161 | transferred to the caller. | ||
4162 | */ | ||
4163 | |||
4164 | QPopupMenu *QTextEdit::createPopupMenu() | ||
4165 | { | ||
4166 | return 0; | ||
4167 | } | ||
4168 | |||
4169 | /*! \reimp */ | ||
4170 | |||
4171 | void QTextEdit::setFont( const QFont &f ) | ||
4172 | { | ||
4173 | QFont old( QScrollView::font() ); | ||
4174 | QScrollView::setFont( f ); | ||
4175 | doc->setMinimumWidth( -1 ); | ||
4176 | |||
4177 | // ### that is a bit hacky | ||
4178 | static short diff = 1; | ||
4179 | diff *= -1; | ||
4180 | doc->setWidth( visibleWidth() + diff ); | ||
4181 | |||
4182 | int s = f.pointSize(); | ||
4183 | bool usePixels = FALSE; | ||
4184 | if ( s == -1 ) { | ||
4185 | s = f.pixelSize(); | ||
4186 | usePixels = TRUE; | ||
4187 | } | ||
4188 | doc->updateFontSizes( s, usePixels ); | ||
4189 | doc->updateFontAttributes( f, old ); | ||
4190 | lastFormatted = doc->firstParag(); | ||
4191 | formatMore(); | ||
4192 | repaintChanged(); | ||
4193 | } | ||
4194 | |||
4195 | /*! \fn QTextEdit::zoomIn() | ||
4196 | |||
4197 | \overload | ||
4198 | |||
4199 | Zooms in on the text by by making the base font size one | ||
4200 | point larger and recalculating all font sizes. This does not change | ||
4201 | the size of any images. | ||
4202 | |||
4203 | \sa zoomOut() | ||
4204 | |||
4205 | */ | ||
4206 | |||
4207 | /*! \fn QTextEdit::zoomOut() | ||
4208 | |||
4209 | \overload | ||
4210 | |||
4211 | Zooms out on the text by by making the base font size one | ||
4212 | point smaller and recalculating all font sizes. This does not change | ||
4213 | the size of any images. | ||
4214 | |||
4215 | \sa zoomIn() | ||
4216 | */ | ||
4217 | |||
4218 | |||
4219 | /*! | ||
4220 | Zooms in on the text by by making the base font size \a range | ||
4221 | points larger and recalculating all font sizes. This does not change | ||
4222 | the size of any images. | ||
4223 | |||
4224 | \sa zoomOut() | ||
4225 | */ | ||
4226 | |||
4227 | void QTextEdit::zoomIn( int range ) | ||
4228 | { | ||
4229 | QFont f( QScrollView::font() ); | ||
4230 | f.setPointSize( f.pointSize() + range ); | ||
4231 | setFont( f ); | ||
4232 | } | ||
4233 | |||
4234 | /*! Zooms out on the text by making the base font size \a range | ||
4235 | points smaller and recalculating all font sizes. This does not | ||
4236 | change the size of any images. | ||
4237 | |||
4238 | \sa zoomIn() | ||
4239 | */ | ||
4240 | |||
4241 | void QTextEdit::zoomOut( int range ) | ||
4242 | { | ||
4243 | QFont f( QScrollView::font() ); | ||
4244 | f.setPointSize( QMAX( 1, f.pointSize() - range ) ); | ||
4245 | setFont( f ); | ||
4246 | } | ||
4247 | |||
4248 | /*! Zooms the text by making the base font size \a size points and | ||
4249 | recalculating all font sizes. This does not change the size of any | ||
4250 | images. | ||
4251 | */ | ||
4252 | |||
4253 | void QTextEdit::zoomTo( int size ) | ||
4254 | { | ||
4255 | QFont f( QScrollView::font() ); | ||
4256 | f.setPointSize( size ); | ||
4257 | setFont( f ); | ||
4258 | } | ||
4259 | |||
4260 | /*! | ||
4261 | \internal | ||
4262 | |||
4263 | QTextEdit is optimized for large amounts text. One of its | ||
4264 | optimizations is to format only the visible text, formatting the rest | ||
4265 | on demand, e.g. as the user scrolls, so you don't usually need to | ||
4266 | call this function. | ||
4267 | |||
4268 | In some situations you may want to force the whole text | ||
4269 | to be formatted. For example, if after calling setText(), you wanted | ||
4270 | to know the height of the document (using contentsHeight()), you | ||
4271 | would call this function first. | ||
4272 | */ | ||
4273 | |||
4274 | void QTextEdit::sync() | ||
4275 | { | ||
4276 | QTextParag *p = lastFormatted; | ||
4277 | while ( p ) { | ||
4278 | p->format(); | ||
4279 | p = p->next(); | ||
4280 | } | ||
4281 | resizeContents( contentsWidth(), doc->height() ); | ||
4282 | } | ||
4283 | |||
4284 | /*! \reimp */ | ||
4285 | |||
4286 | void QTextEdit::setEnabled( bool b ) | ||
4287 | { | ||
4288 | QScrollView::setEnabled( b ); | ||
4289 | if ( !b ) { | ||
4290 | blinkTimer->stop(); | ||
4291 | drawCursor( FALSE ); | ||
4292 | } | ||
4293 | if ( textFormat() == PlainText ) { | ||
4294 | QTextFormat *f = doc->formatCollection()->defaultFormat(); | ||
4295 | f->setColor( colorGroup().text() ); | ||
4296 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
4297 | } | ||
4298 | if ( b ) { | ||
4299 | blinkTimer->start( QApplication::cursorFlashTime() / 2 ); | ||
4300 | drawCursor( TRUE ); | ||
4301 | } | ||
4302 | } | ||
4303 | |||
4304 | /*! | ||
4305 | Sets the background color of selection number \a selNum to \a back and | ||
4306 | specifies whether the text of this selection should be inverted with \a | ||
4307 | invertText. | ||
4308 | |||
4309 | This only works for \a selNum > 0. The default selection (\a selNum == | ||
4310 | 0) gets its attributes from the colorGroup() of this widget. | ||
4311 | */ | ||
4312 | |||
4313 | void QTextEdit::setSelectionAttributes( int selNum, const QColor &back, bool invertText ) | ||
4314 | { | ||
4315 | if ( selNum < 1 ) | ||
4316 | return; | ||
4317 | if ( selNum > doc->numSelections() ) | ||
4318 | doc->addSelection( selNum ); | ||
4319 | doc->setSelectionColor( selNum, back ); | ||
4320 | doc->setInvertSelectionText( selNum, invertText ); | ||
4321 | } | ||
4322 | |||
4323 | /*! \reimp */ | ||
4324 | void QTextEdit::windowActivationChange( bool ) | ||
4325 | { | ||
4326 | if ( !isVisible() ) | ||
4327 | return; | ||
4328 | |||
4329 | if ( palette().active() != palette().inactive() ) | ||
4330 | updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); | ||
4331 | } | ||
4332 | |||
4333 | void QTextEdit::setReadOnly( bool b ) | ||
4334 | { | ||
4335 | if ( readonly == b ) | ||
4336 | return; | ||
4337 | readonly = b; | ||
4338 | #ifndef QT_NO_CURSOR | ||
4339 | if ( readonly ) | ||
4340 | viewport()->setCursor( arrowCursor ); | ||
4341 | else | ||
4342 | viewport()->setCursor( ibeamCursor ); | ||
4343 | #endif | ||
4344 | } | ||
4345 | |||
4346 | /*! Scrolls to the bottom of the document and does formatting if | ||
4347 | required */ | ||
4348 | |||
4349 | void QTextEdit::scrollToBottom() | ||
4350 | { | ||
4351 | sync(); | ||
4352 | setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); | ||
4353 | } | ||
4354 | |||
4355 | /*! Returns the rectangle of the paragraph \a para in contents | ||
4356 | coordinates, or an invalid rectangle if \a para is out of range. | ||
4357 | */ | ||
4358 | |||
4359 | QRect QTextEdit::paragraphRect( int para ) const | ||
4360 | { | ||
4361 | QTextEdit *that = (QTextEdit *)this; | ||
4362 | that->sync(); | ||
4363 | QTextParag *p = doc->paragAt( para ); | ||
4364 | if ( !p ) | ||
4365 | return QRect( -1, -1, -1, -1 ); | ||
4366 | return p->rect(); | ||
4367 | } | ||
4368 | |||
4369 | /*! | ||
4370 | Returns the paragraph which is at position \a pos (in contents | ||
4371 | coordinates), or -1 if there is no paragraph with index \a pos. | ||
4372 | */ | ||
4373 | |||
4374 | int QTextEdit::paragraphAt( const QPoint &pos ) const | ||
4375 | { | ||
4376 | QTextCursor c( doc ); | ||
4377 | c.place( pos, doc->firstParag() ); | ||
4378 | if ( c.parag() ) | ||
4379 | return c.parag()->paragId(); | ||
4380 | return -1; | ||
4381 | } | ||
4382 | |||
4383 | /*! | ||
4384 | Returns the index of the character (relative to its paragraph) at | ||
4385 | position \a pos (in contents coordinates). If \a para is not null, | ||
4386 | \e *\a para is set to this paragraph. If there is no character at | ||
4387 | \a pos, -1 is returned. | ||
4388 | */ | ||
4389 | |||
4390 | int QTextEdit::charAt( const QPoint &pos, int *para ) const | ||
4391 | { | ||
4392 | QTextCursor c( doc ); | ||
4393 | c.place( pos, doc->firstParag() ); | ||
4394 | if ( c.parag() ) { | ||
4395 | if ( para ) | ||
4396 | *para = c.parag()->paragId(); | ||
4397 | return c.index(); | ||
4398 | } | ||
4399 | return -1; | ||
4400 | } | ||
4401 | |||
4402 | /*! Sets the background color of the paragraph \a para to \a bg */ | ||
4403 | |||
4404 | void QTextEdit::setParagraphBackgroundColor( int para, const QColor &bg ) | ||
4405 | { | ||
4406 | QTextParag *p = doc->paragAt( para ); | ||
4407 | if ( !p ) | ||
4408 | return; | ||
4409 | p->setBackgroundColor( bg ); | ||
4410 | repaintChanged(); | ||
4411 | } | ||
4412 | |||
4413 | /*! Clears the background color of the paragraph \a para, so that the | ||
4414 | default color is used again. | ||
4415 | */ | ||
4416 | |||
4417 | void QTextEdit::clearParagraphBackground( int para ) | ||
4418 | { | ||
4419 | QTextParag *p = doc->paragAt( para ); | ||
4420 | if ( !p ) | ||
4421 | return; | ||
4422 | p->clearBackgroundColor(); | ||
4423 | repaintChanged(); | ||
4424 | } | ||
4425 | |||
4426 | /*! Returns the background color of the paragraph \a para or an | ||
4427 | invalid color if \a para is out of range or the paragraph has no | ||
4428 | background set | ||
4429 | */ | ||
4430 | |||
4431 | QColor QTextEdit::paragraphBackgroundColor( int para ) const | ||
4432 | { | ||
4433 | QTextParag *p = doc->paragAt( para ); | ||
4434 | if ( !p ) | ||
4435 | return QColor(); | ||
4436 | QColor *c = p->backgroundColor(); | ||
4437 | if ( c ) | ||
4438 | return *c; | ||
4439 | return QColor(); | ||
4440 | } | ||
4441 | |||
4442 | /*! \property QTextEdit::undoRedoEnabled | ||
4443 | \brief whether undo/redo is enabled | ||
4444 | |||
4445 | The default is TRUE. | ||
4446 | */ | ||
4447 | |||
4448 | void QTextEdit::setUndoRedoEnabled( bool b ) | ||
4449 | { | ||
4450 | undoEnabled = b; | ||
4451 | } | ||
4452 | |||
4453 | bool QTextEdit::isUndoRedoEnabled() const | ||
4454 | { | ||
4455 | return undoEnabled; | ||
4456 | } | ||
4457 | |||
4458 | /*! Returns whether undo is available */ | ||
4459 | |||
4460 | bool QTextEdit::isUndoAvailable() const | ||
4461 | { | ||
4462 | return doc->commands()->isUndoAvailable() || undoRedoInfo.valid(); | ||
4463 | } | ||
4464 | |||
4465 | /*! Returns whether redo is available */ | ||
4466 | |||
4467 | bool QTextEdit::isRedoAvailable() const | ||
4468 | { | ||
4469 | return doc->commands()->isRedoAvailable(); | ||
4470 | } | ||
4471 | |||
4472 | void QTextEdit::ensureFormatted( QTextParag *p ) | ||
4473 | { | ||
4474 | while ( !p->isValid() ) { | ||
4475 | if ( !lastFormatted ) | ||
4476 | return; | ||
4477 | formatMore(); | ||
4478 | } | ||
4479 | } | ||
4480 | |||
4481 | /*! \internal */ | ||
4482 | void QTextEdit::updateCursor( const QPoint & pos ) | ||
4483 | { | ||
4484 | if ( isReadOnly() && linksEnabled() ) { | ||
4485 | QTextCursor c = *cursor; | ||
4486 | placeCursor( pos, &c, TRUE ); | ||
4487 | |||
4488 | #ifndef QT_NO_NETWORKPROTOCOL | ||
4489 | if ( c.parag() && c.parag()->at( c.index() ) && | ||
4490 | c.parag()->at( c.index() )->isAnchor() && | ||
4491 | !c.parag()->at( c.index() )->anchorHref().isEmpty() ) { | ||
4492 | if ( c.index() < c.parag()->length() - 1 ) | ||
4493 | onLink = c.parag()->at( c.index() )->anchorHref(); | ||
4494 | else | ||
4495 | onLink = QString::null; | ||
4496 | |||
4497 | #ifndef QT_NO_CURSOR | ||
4498 | viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor ); | ||
4499 | #endif | ||
4500 | QUrl u( doc->context(), onLink, TRUE ); | ||
4501 | emitHighlighted( u.toString( FALSE, FALSE ) ); | ||
4502 | } else { | ||
4503 | #ifndef QT_NO_CURSOR | ||
4504 | viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); | ||
4505 | #endif | ||
4506 | onLink = QString::null; | ||
4507 | emitHighlighted( QString::null ); | ||
4508 | } | ||
4509 | #endif | ||
4510 | } | ||
4511 | } | ||
4512 | |||
4513 | void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c ) | ||
4514 | { | ||
4515 | placeCursor( pos, c, FALSE ); | ||
4516 | } | ||