summaryrefslogtreecommitdiff
path: root/noncore/apps/opie-write/qtextedit.cpp
authorleseb <leseb>2002-07-14 21:21:35 (UTC)
committer leseb <leseb>2002-07-14 21:21:35 (UTC)
commit4feeec8b5b41cfd3d13274411f515524f687da09 (patch) (unidiff)
tree002bbfb9997713e5d5975855d3cfbba7a71b9104 /noncore/apps/opie-write/qtextedit.cpp
parentbdef9cf23ced569a9bc80c1d4f25d85861273b4a (diff)
downloadopie-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.cpp4516
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
67using namespace Qt3;
68
69struct QUndoRedoInfoPrivate
70{
71 QTextString text;
72};
73
74namespace Qt3 {
75
76class QTextEditPrivate
77{
78public:
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
89static 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
574QTextEdit::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
602QTextEdit::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
613QTextEdit::~QTextEdit()
614{
615 delete undoRedoInfo.d;
616 undoRedoInfo.d = 0;
617 delete cursor;
618 delete doc;
619 delete d;
620}
621
622void 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
694void 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
726void 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
743void QTextEdit::drawContents( QPainter * )
744{
745}
746
747/*! \reimp */
748
749bool 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
830void 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
1079void 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
1212void 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
1278void 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
1291void 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
1363void 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
1415void 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
1470void QTextEdit::resizeEvent( QResizeEvent *e )
1471{
1472 QScrollView::resizeEvent( e );
1473}
1474
1475/*! \reimp */
1476
1477void QTextEdit::viewportResizeEvent( QResizeEvent *e )
1478{
1479 QScrollView::viewportResizeEvent( e );
1480 if ( e->oldSize().width() != e->size().width() )
1481 doResize();
1482}
1483
1484static 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
1493void 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*/
1516void 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
1558enum {
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
1570void 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
1587void 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
1667void 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
1700void 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
1743void 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
1767void 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
1779void 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
1793void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * )
1794{
1795 inDnD = FALSE;
1796}
1797
1798/*! \reimp */
1799
1800void 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
1835void QTextEdit::autoScrollTimerDone()
1836{
1837 if ( mousePressed )
1838 handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) );
1839}
1840
1841void 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
1922void 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
1940void 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
1975void 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
1991void QTextEdit::doChangeInterval()
1992{
1993 interval = 0;
1994}
1995
1996/*! \reimp */
1997
1998bool 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
2035void 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
2106void 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
2123void 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
2140void 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
2193void 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
2241void 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
2286void 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
2303void 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
2317void 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
2335void 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
2359void 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
2369void 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
2391bool 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
2411void 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
2460void 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
2478void 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
2540void 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
2599void 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
2633void 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
2648void 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
2663void 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
2677void 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
2694void 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
2708void 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
2722void 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
2730void 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
2739QString 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
2754QString 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
2779void 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
2872bool 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
2887void 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
2903void 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
2929void 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
2952void 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
3009void 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
3043void QTextEdit::setTextFormat( TextFormat format )
3044{
3045 doc->setTextFormat( format );
3046}
3047
3048Qt::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
3057int 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
3067int 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
3080int 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
3096int 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
3116int 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
3130void 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
3144bool QTextEdit::isModified() const
3145{
3146 return modified;
3147}
3148
3149void 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
3161bool 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
3172bool 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
3184bool 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
3195QString 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
3207int 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
3218QColor 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
3230QFont 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
3241int QTextEdit::alignment() const
3242{
3243 return currentAlignment;
3244}
3245
3246void 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
3270void 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
3284void 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
3316void QTextEdit::del()
3317{
3318 if ( doc->hasSelection( QTextDocument::Standard ) ) {
3319 removeSelectedText();
3320 return;
3321 }
3322
3323 doKeyboardAction( ActionDelete );
3324}
3325
3326
3327QTextEdit::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
3336QTextEdit::UndoRedoInfo::~UndoRedoInfo()
3337{
3338 delete d;
3339}
3340
3341bool 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
3352void 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
3364QStyleSheet* 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
3375void 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
3388void 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
3395QBrush 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
3410void QTextEdit::setLinkUnderline( bool b )
3411{
3412 if ( b == doc->underlineLinks() )
3413 return;
3414 doc->setUnderlineLinks( b );
3415 updateStyles();
3416}
3417
3418bool 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
3429void 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
3440QMimeSourceFactory* 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
3450int 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
3465void 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
3517bool 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
3533QString QTextEdit::selectedText() const
3534{
3535 return doc->selectedText( QTextDocument::Standard );
3536}
3537
3538bool 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
3601QString 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
3615QString QTextEdit::documentTitle() const
3616{
3617 return doc->attributes()[ "title" ];
3618}
3619
3620void 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
3631void 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
3657QString QTextEdit::anchorAt( const QPoint& pos )
3658{
3659 QTextCursor c( doc );
3660 placeCursor( pos, &c );
3661 return c.parag()->at( c.index() )->anchorHref();
3662}
3663
3664void 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
3674void QTextEdit::updateStyles()
3675{
3676 doc->updateStyles();
3677 updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
3678}
3679
3680void 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*/
3702void 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*/
3736void 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
3744QCString 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
3828void 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
3867QTextEdit::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 */
3884void 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
3907int 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
3940void 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
3960QTextEdit::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
3972void 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
3992int 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
4003int 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
4014int QTextEdit::tabStopWidth() const
4015{
4016 return document()->tabStopWidth();
4017}
4018
4019void QTextEdit::setUndoDepth( int d )
4020{
4021 document()->setUndoDepth( d );
4022}
4023
4024void 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
4036QSize QTextEdit::sizeHint() const
4037{
4038 // ### calculate a reasonable one
4039 return QSize( 100, 100 );
4040}
4041
4042void 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
4058bool 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
4085bool 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
4117QPopupMenu *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
4164QPopupMenu *QTextEdit::createPopupMenu()
4165{
4166 return 0;
4167}
4168
4169/*! \reimp */
4170
4171void 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
4227void 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
4241void 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
4253void 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
4274void 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
4286void 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
4313void 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 */
4324void QTextEdit::windowActivationChange( bool )
4325{
4326 if ( !isVisible() )
4327 return;
4328
4329 if ( palette().active() != palette().inactive() )
4330 updateContents( contentsX(), contentsY(), visibleWidth(), visibleHeight() );
4331}
4332
4333void 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
4349void 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
4359QRect 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
4374int 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
4390int 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
4404void 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
4417void 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
4431QColor 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
4448void QTextEdit::setUndoRedoEnabled( bool b )
4449{
4450 undoEnabled = b;
4451}
4452
4453bool QTextEdit::isUndoRedoEnabled() const
4454{
4455 return undoEnabled;
4456}
4457
4458/*! Returns whether undo is available */
4459
4460bool QTextEdit::isUndoAvailable() const
4461{
4462 return doc->commands()->isUndoAvailable() || undoRedoInfo.valid();
4463}
4464
4465/*! Returns whether redo is available */
4466
4467bool QTextEdit::isRedoAvailable() const
4468{
4469 return doc->commands()->isRedoAvailable();
4470}
4471
4472void QTextEdit::ensureFormatted( QTextParag *p )
4473{
4474 while ( !p->isValid() ) {
4475 if ( !lastFormatted )
4476 return;
4477 formatMore();
4478 }
4479}
4480
4481/*! \internal */
4482void 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
4513void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c )
4514{
4515 placeCursor( pos, c, FALSE );
4516}