src/gui/text/qtextcontrol.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qtextcontrol_p.h"
00025 #include "qtextcontrol_p_p.h"
00026 #include "qlineedit.h"
00027 
00028 #ifndef QT_NO_TEXTCONTROL
00029 
00030 #include <qfont.h>
00031 #include <qpainter.h>
00032 #include <qevent.h>
00033 #include <qdebug.h>
00034 #include <qmime.h>
00035 #include <qdrag.h>
00036 #include <qclipboard.h>
00037 #include <qmenu.h>
00038 #include <qstyle.h>
00039 #include <qtimer.h>
00040 #include "private/qtextdocumentlayout_p.h"
00041 #include "private/qtextedit_p.h"
00042 #include "qtextdocument.h"
00043 #include "private/qtextdocument_p.h"
00044 #include "qtextlist.h"
00045 #include "private/qtextcontrol_p.h"
00046 #include "qgraphicssceneevent.h"
00047 
00048 #include <qtextformat.h>
00049 #include <qdatetime.h>
00050 #include <qapplication.h>
00051 #include <limits.h>
00052 #include <qtexttable.h>
00053 #include <qvariant.h>
00054 #include <qurl.h>
00055 #include <qdesktopservices.h>
00056 #include <qinputcontext.h>
00057 
00058 #ifndef QT_NO_SHORTCUT
00059 #include <qkeysequence.h>
00060 #define ACCEL_KEY(k) QString::fromLatin1("\t") + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k ))
00061 #else
00062 #define ACCEL_KEY(k) QString("\tCtrl+" #k)
00063 #endif
00064 
00065 // could go into QTextCursor...
00066 static QTextLine currentTextLine(const QTextCursor &cursor)
00067 {
00068     const QTextBlock block = cursor.block();
00069     if (!block.isValid())
00070         return QTextLine();
00071 
00072     const QTextLayout *layout = block.layout();
00073     if (!layout)
00074         return QTextLine();
00075 
00076     const int relativePos = cursor.position() - block.position();
00077     return layout->lineForTextPosition(relativePos);
00078 }
00079 
00080 QTextControlPrivate::QTextControlPrivate()
00081     : doc(0), cursorOn(false), cursorIsFocusIndicator(false),
00082       interactionFlags(Qt::TextEditorInteraction),
00083 #ifndef QT_NO_DRAGANDDROP
00084       mousePressed(false), mightStartDrag(false),
00085 #endif
00086       lastSelectionState(false), ignoreAutomaticScrollbarAdjustement(false),
00087       overwriteMode(false),
00088       acceptRichText(true),
00089       preeditCursor(0), hideCursor(false),
00090       hasFocus(false),
00091       layoutDirection(QApplication::layoutDirection()),
00092       isEnabled(true),
00093       hadSelectionOnMousePress(false),
00094       openExternalLinks(false)
00095 {}
00096 
00097 bool QTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e)
00098 {
00099 #ifdef QT_NO_SHORTCUT
00100     Q_UNUSED(e);
00101 #endif
00102 
00103     Q_Q(QTextControl);
00104     if (cursor.isNull())
00105         return false;
00106 
00107     const QTextCursor oldSelection = cursor;
00108     const int oldCursorPos = cursor.position();
00109 
00110     QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
00111     QTextCursor::MoveOperation op = QTextCursor::NoMove;
00112 
00113     if (false) {
00114     }
00115 #ifndef QT_NO_SHORTCUT
00116     if (e == QKeySequence::MoveToNextChar) {
00117             op = QTextCursor::Right;
00118     }
00119     else if (e == QKeySequence::MoveToPreviousChar) {
00120             op = QTextCursor::Left;
00121     }
00122     else if (e == QKeySequence::SelectNextChar) {
00123            op = QTextCursor::Right;
00124            mode = QTextCursor::KeepAnchor;
00125     }
00126     else if (e == QKeySequence::SelectPreviousChar) {
00127             op = QTextCursor::Left;
00128             mode = QTextCursor::KeepAnchor;
00129     }
00130     else if (e == QKeySequence::SelectNextWord) {
00131             op = QTextCursor::WordRight;
00132             mode = QTextCursor::KeepAnchor;
00133     }
00134     else if (e == QKeySequence::SelectPreviousWord) {
00135             op = QTextCursor::WordLeft;
00136             mode = QTextCursor::KeepAnchor;
00137     }
00138     else if (e == QKeySequence::SelectStartOfLine) {
00139             op = QTextCursor::StartOfLine;
00140             mode = QTextCursor::KeepAnchor;
00141     }
00142     else if (e == QKeySequence::SelectEndOfLine) {
00143             op = QTextCursor::EndOfLine;
00144             mode = QTextCursor::KeepAnchor;
00145     }
00146     else if (e == QKeySequence::SelectStartOfBlock) {
00147             op = QTextCursor::StartOfBlock;
00148             mode = QTextCursor::KeepAnchor;
00149     }
00150     else if (e == QKeySequence::SelectEndOfBlock) {
00151             op = QTextCursor::EndOfBlock;
00152             mode = QTextCursor::KeepAnchor;
00153     }
00154     else if (e == QKeySequence::SelectStartOfDocument) {
00155             op = QTextCursor::Start;
00156             mode = QTextCursor::KeepAnchor;
00157     }
00158     else if (e == QKeySequence::SelectEndOfDocument) {
00159             op = QTextCursor::End;
00160             mode = QTextCursor::KeepAnchor;
00161     }
00162     else if (e == QKeySequence::SelectPreviousLine) {
00163             op = QTextCursor::Up;
00164             mode = QTextCursor::KeepAnchor;
00165     }
00166     else if (e == QKeySequence::SelectNextLine) {
00167             op = QTextCursor::Down;
00168             mode = QTextCursor::KeepAnchor;
00169             {
00170                 QTextBlock block = cursor.block();
00171                 QTextLine line = currentTextLine(cursor);
00172                 if (!block.next().isValid()
00173                     && line.isValid()
00174                     && line.lineNumber() == block.layout()->lineCount() - 1)
00175                     op = QTextCursor::End;
00176             }
00177     }
00178     else if (e == QKeySequence::SelectNextLine) {
00179             op = QTextCursor::Down;
00180             mode = QTextCursor::KeepAnchor;
00181             {
00182                 QTextBlock block = cursor.block();
00183                 QTextLine line = currentTextLine(cursor);
00184                 if (!block.next().isValid()
00185                     && line.isValid()
00186                     && line.lineNumber() == block.layout()->lineCount() - 1)
00187                     op = QTextCursor::End;
00188             }
00189     }
00190     else if (e == QKeySequence::MoveToNextWord) {
00191             op = QTextCursor::WordRight;
00192     }
00193     else if (e == QKeySequence::MoveToPreviousWord) {
00194             op = QTextCursor::WordLeft;
00195     }
00196     else if (e == QKeySequence::MoveToEndOfBlock) {
00197             op = QTextCursor::EndOfBlock;
00198     }
00199     else if (e == QKeySequence::MoveToStartOfBlock) {
00200             op = QTextCursor::StartOfBlock;
00201     }
00202     else if (e == QKeySequence::MoveToNextLine) {
00203             op = QTextCursor::Down;
00204     }
00205     else if (e == QKeySequence::MoveToPreviousLine) {
00206             op = QTextCursor::Up;
00207     }
00208     else if (e == QKeySequence::MoveToPreviousLine) {
00209             op = QTextCursor::Up;
00210     }
00211     else if (e == QKeySequence::MoveToStartOfLine) {
00212             op = QTextCursor::StartOfLine;
00213     }
00214     else if (e == QKeySequence::MoveToEndOfLine) {
00215             op = QTextCursor::EndOfLine;
00216     }
00217     else if (e == QKeySequence::MoveToStartOfDocument) {
00218             op = QTextCursor::Start;
00219     }
00220     else if (e == QKeySequence::MoveToEndOfDocument) {
00221             op = QTextCursor::End;
00222     }
00223 #endif // QT_NO_SHORTCUT
00224     else {
00225         return false;
00226     }
00227 
00228 // Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
00229 // here's the breakdown:
00230 // Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
00231 // Alt (Option), or Meta (Control).
00232 // Command/Control + Left/Right -- Move to left or right of the line
00233 //                 + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
00234 // Option + Left/Right -- Move one word Left/right.
00235 //        + Up/Down  -- Begin/End of Paragraph.
00236 // Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
00237 
00238     const bool moved = cursor.movePosition(op, mode);
00239     q->ensureCursorVisible();
00240 
00241     if (moved) {
00242         if (cursor.position() != oldCursorPos)
00243             emit q->cursorPositionChanged();
00244         emit q->microFocusChanged();
00245     }
00246 #ifdef QT_KEYPAD_NAVIGATION
00247     else if (QApplication::keypadNavigationEnabled()
00248         && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)) {
00249         return false;
00250     }
00251 #endif
00252 
00253     selectionChanged();
00254 
00255     repaintOldAndNewSelection(oldSelection);
00256 
00257     return true;
00258 }
00259 
00260 void QTextControlPrivate::updateCurrentCharFormat()
00261 {
00262     Q_Q(QTextControl);
00263 
00264     QTextCharFormat fmt = cursor.charFormat();
00265     if (fmt == lastCharFormat)
00266         return;
00267     lastCharFormat = fmt;
00268 
00269     emit q->currentCharFormatChanged(fmt);
00270     emit q->microFocusChanged();
00271 }
00272 
00273 void QTextControlPrivate::indent()
00274 {
00275     QTextBlockFormat blockFmt = cursor.blockFormat();
00276 
00277     QTextList *list = cursor.currentList();
00278     if (!list) {
00279         QTextBlockFormat modifier;
00280         modifier.setIndent(blockFmt.indent() + 1);
00281         cursor.mergeBlockFormat(modifier);
00282     } else {
00283         QTextListFormat format = list->format();
00284         format.setIndent(format.indent() + 1);
00285 
00286         if (list->itemNumber(cursor.block()) == 1)
00287             list->setFormat(format);
00288         else
00289             cursor.createList(format);
00290     }
00291 }
00292 
00293 void QTextControlPrivate::outdent()
00294 {
00295     QTextBlockFormat blockFmt = cursor.blockFormat();
00296 
00297     QTextList *list = cursor.currentList();
00298 
00299     if (!list) {
00300         QTextBlockFormat modifier;
00301         modifier.setIndent(blockFmt.indent() - 1);
00302         cursor.mergeBlockFormat(modifier);
00303     } else {
00304         QTextListFormat listFmt = list->format();
00305         listFmt.setIndent(listFmt.indent() - 1);
00306         list->setFormat(listFmt);
00307     }
00308 }
00309 
00310 void QTextControlPrivate::gotoNextTableCell()
00311 {
00312     QTextTable *table = cursor.currentTable();
00313     QTextTableCell cell = table->cellAt(cursor);
00314 
00315     int newColumn = cell.column() + cell.columnSpan();
00316     int newRow = cell.row();
00317 
00318     if (newColumn >= table->columns()) {
00319         newColumn = 0;
00320         ++newRow;
00321         if (newRow >= table->rows())
00322             table->insertRows(table->rows(), 1);
00323     }
00324 
00325     cell = table->cellAt(newRow, newColumn);
00326     cursor = cell.firstCursorPosition();
00327 }
00328 
00329 void QTextControlPrivate::gotoPreviousTableCell()
00330 {
00331     QTextTable *table = cursor.currentTable();
00332     QTextTableCell cell = table->cellAt(cursor);
00333 
00334     int newColumn = cell.column() - 1;
00335     int newRow = cell.row();
00336 
00337     if (newColumn < 0) {
00338         newColumn = table->columns() - 1;
00339         --newRow;
00340         if (newRow < 0)
00341             return;
00342     }
00343 
00344     cell = table->cellAt(newRow, newColumn);
00345     cursor = cell.firstCursorPosition();
00346 }
00347 
00348 void QTextControlPrivate::createAutoBulletList()
00349 {
00350     cursor.beginEditBlock();
00351 
00352     QTextBlockFormat blockFmt = cursor.blockFormat();
00353 
00354     QTextListFormat listFmt;
00355     listFmt.setStyle(QTextListFormat::ListDisc);
00356     listFmt.setIndent(blockFmt.indent() + 1);
00357 
00358     blockFmt.setIndent(0);
00359     cursor.setBlockFormat(blockFmt);
00360 
00361     cursor.createList(listFmt);
00362 
00363     cursor.endEditBlock();
00364 }
00365 
00366 void QTextControlPrivate::setContent(Qt::TextFormat format, const QString &text, QTextDocument *document)
00367 {
00368     Q_Q(QTextControl);
00369 
00370     // for use when called from setPlainText. we may want to re-use the currently
00371     // set char format then.
00372     const QTextCharFormat charFormatForInsertion = cursor.charFormat();
00373 
00374     bool clearDocument = true;
00375     if (!doc) {
00376         palette = QApplication::palette("QTextControl");
00377 
00378         if (document) {
00379             doc = document;
00380             clearDocument = false;
00381         } else {
00382             doc = new QTextDocument(q);
00383         }
00384 
00385         QObject::connect(doc->documentLayout(), SIGNAL(update(QRectF)), q, SIGNAL(updateRequest(QRectF)));
00386         QObject::connect(doc->documentLayout(), SIGNAL(documentSizeChanged(QSizeF)), q, SIGNAL(documentSizeChanged(QSizeF)));
00387         cursor = QTextCursor(doc);
00388 
00389 // ####        doc->documentLayout()->setPaintDevice(viewport);
00390 
00391         QObject::connect(doc, SIGNAL(contentsChanged()), q, SLOT(_q_updateCurrentCharFormatAndSelection()));
00392         QObject::connect(doc, SIGNAL(cursorPositionChanged(QTextCursor)), q, SLOT(_q_emitCursorPosChanged(QTextCursor)));
00393 
00394         // convenience signal forwards
00395         QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
00396         QObject::connect(doc, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
00397         QObject::connect(doc, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
00398     }
00399 
00400     doc->setUndoRedoEnabled(false);
00401 
00402     // avoid multiple textChanged() signals being emitted
00403     QObject::disconnect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
00404 
00405     if (!text.isEmpty()) {
00406         // clear 'our' cursor for insertion to prevent
00407         // the emission of the cursorPositionChanged() signal.
00408         // instead we emit it only once at the end instead of
00409         // at the end of the document after loading and when
00410         // positioning the cursor again to the start of the
00411         // document.
00412         cursor = QTextCursor();
00413         if (format == Qt::PlainText) {
00414             QTextCursor formatCursor(doc);
00415             // put the setPlainText and the setCharFormat into one edit block,
00416             // so that the syntax highlight triggers only /once/ for the entire
00417             // document, not twice.
00418             formatCursor.beginEditBlock();
00419             doc->setPlainText(text);
00420             doc->setUndoRedoEnabled(false);
00421             formatCursor.select(QTextCursor::Document);
00422             formatCursor.setCharFormat(charFormatForInsertion);
00423             formatCursor.endEditBlock();
00424         } else {
00425             doc->setHtml(text);
00426             doc->setUndoRedoEnabled(false);
00427         }
00428         cursor = QTextCursor(doc);
00429     } else if (clearDocument) {
00430         doc->clear();
00431         cursor.movePosition(QTextCursor::Start);
00432         QTextBlockFormat blockFmt;
00433         blockFmt.setLayoutDirection(layoutDirection);
00434         cursor.mergeBlockFormat(blockFmt);
00435         cursor.setCharFormat(charFormatForInsertion);
00436     }
00437 
00438     QObject::connect(doc, SIGNAL(contentsChanged()), q, SIGNAL(textChanged()));
00439     emit q->textChanged();
00440     doc->setUndoRedoEnabled(interactionFlags & Qt::TextEditable);
00441     _q_updateCurrentCharFormatAndSelection();
00442     doc->setModified(false);
00443     q->ensureCursorVisible();
00444     emit q->cursorPositionChanged();
00445 }
00446 
00447 void QTextControlPrivate::startDrag()
00448 {
00449 #ifndef QT_NO_DRAGANDDROP
00450     Q_Q(QTextControl);
00451     mousePressed = false;
00452     if (!contextWidget)
00453         return;
00454     QMimeData *data = q->createMimeDataFromSelection();
00455 
00456     QDrag *drag = new QDrag(contextWidget);
00457     drag->setMimeData(data);
00458 
00459     Qt::DropActions actions = Qt::CopyAction;
00460     if (interactionFlags & Qt::TextEditable)
00461         actions |= Qt::MoveAction;
00462     Qt::DropAction action = drag->start(actions);
00463 
00464     if (action == Qt::MoveAction && drag->target() != contextWidget)
00465         cursor.removeSelectedText();
00466 #endif
00467 }
00468 
00469 void QTextControlPrivate::setCursorPosition(const QPointF &pos)
00470 {
00471     const int cursorPos = doc->documentLayout()->hitTest(pos, Qt::FuzzyHit);
00472     if (cursorPos == -1)
00473         return;
00474     cursor.setPosition(cursorPos);
00475 }
00476 
00477 void QTextControlPrivate::setCursorPosition(int pos, QTextCursor::MoveMode mode)
00478 {
00479     cursor.setPosition(pos, mode);
00480 
00481     if (mode != QTextCursor::KeepAnchor) {
00482         selectedWordOnDoubleClick = QTextCursor();
00483         selectedLineOnDoubleClick = QTextCursor();
00484     }
00485 }
00486 
00487 void QTextControlPrivate::repaintCursor()
00488 {
00489     Q_Q(QTextControl);
00490     emit q->updateRequest(q->cursorRect());
00491 }
00492 
00493 void QTextControlPrivate::repaintOldAndNewSelection(const QTextCursor &oldSelection)
00494 {
00495     Q_Q(QTextControl);
00496     QRectF updateRect = selectionRect() | selectionRect(oldSelection);
00497 
00498     if (cursor.hasSelection()
00499         && oldSelection.hasSelection()
00500         && cursor.currentFrame() == oldSelection.currentFrame()
00501         && !cursor.hasComplexSelection()
00502         && !oldSelection.hasComplexSelection()
00503         && cursor.anchor() == oldSelection.anchor()) {
00504         QTextCursor differenceSelection(doc);
00505         differenceSelection.setPosition(oldSelection.position());
00506         differenceSelection.setPosition(cursor.position(), QTextCursor::KeepAnchor);
00507         emit q->updateRequest(selectionRect(differenceSelection));
00508     } else {
00509         if (!oldSelection.isNull())
00510             emit q->updateRequest(selectionRect(oldSelection));
00511         emit q->updateRequest(selectionRect());
00512     }
00513 }
00514 
00515 void QTextControlPrivate::selectionChanged()
00516 {
00517     Q_Q(QTextControl);
00518     bool current = cursor.hasSelection();
00519     if (current == lastSelectionState)
00520         return;
00521 
00522     lastSelectionState = current;
00523     emit q->copyAvailable(current);
00524     emit q->selectionChanged();
00525     emit q->microFocusChanged();
00526 }
00527 
00528 void QTextControlPrivate::_q_updateCurrentCharFormatAndSelection()
00529 {
00530     updateCurrentCharFormat();
00531     selectionChanged();
00532 }
00533 
00534 #ifndef QT_NO_CLIPBOARD
00535 void QTextControlPrivate::setClipboardSelection()
00536 {
00537     QClipboard *clipboard = QApplication::clipboard();
00538     if (!cursor.hasSelection() || !clipboard->supportsSelection())
00539         return;
00540     Q_Q(QTextControl);
00541     QMimeData *data = q->createMimeDataFromSelection();
00542     clipboard->setMimeData(data, QClipboard::Selection);
00543 }
00544 #endif
00545 
00546 void QTextControlPrivate::_q_emitCursorPosChanged(const QTextCursor &someCursor)
00547 {
00548     Q_Q(QTextControl);
00549     if (someCursor.isCopyOf(cursor)) {
00550         emit q->cursorPositionChanged();
00551         emit q->microFocusChanged();
00552     }
00553 }
00554 
00555 void QTextControlPrivate::setBlinkingCursorEnabled(bool enable)
00556 {
00557     Q_Q(QTextControl);
00558     if (enable && QApplication::cursorFlashTime() > 0) {
00559         cursorBlinkTimer.start(QApplication::cursorFlashTime() / 2, q);
00560         cursorOn = true;
00561     } else {
00562         cursorBlinkTimer.stop();
00563         cursorOn = false;
00564     }
00565     repaintCursor();
00566 }
00567 
00568 void QTextControlPrivate::extendWordwiseSelection(int suggestedNewPosition, qreal mouseXPosition)
00569 {
00570     Q_Q(QTextControl);
00571 
00572     // if inside the initial selected word keep that
00573     if (suggestedNewPosition >= selectedWordOnDoubleClick.selectionStart()
00574         && suggestedNewPosition <= selectedWordOnDoubleClick.selectionEnd()) {
00575         q->setTextCursor(selectedWordOnDoubleClick);
00576         return;
00577     }
00578 
00579     QTextCursor curs = selectedWordOnDoubleClick;
00580     curs.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
00581 
00582     if (!curs.movePosition(QTextCursor::StartOfWord))
00583         return;
00584     const int wordStartPos = curs.position();
00585 
00586     const int blockPos = curs.block().position();
00587     const QPointF blockCoordinates = doc->documentLayout()->blockBoundingRect(curs.block()).topLeft();
00588 
00589     QTextLine line = currentTextLine(curs);
00590     if (!line.isValid())
00591         return;
00592 
00593     const qreal wordStartX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
00594 
00595     if (!curs.movePosition(QTextCursor::EndOfWord))
00596         return;
00597     const int wordEndPos = curs.position();
00598 
00599     const QTextLine otherLine = currentTextLine(curs);
00600     if (otherLine.textStart() != line.textStart()
00601         || wordEndPos == wordStartPos)
00602         return;
00603 
00604     const qreal wordEndX = line.cursorToX(curs.position() - blockPos) + blockCoordinates.x();
00605 
00606     if (mouseXPosition < wordStartX || mouseXPosition > wordEndX)
00607         return;
00608 
00609     // keep the already selected word even when moving to the left
00610     // (#39164)
00611     if (suggestedNewPosition < selectedWordOnDoubleClick.position())
00612         cursor.setPosition(selectedWordOnDoubleClick.selectionEnd());
00613     else
00614         cursor.setPosition(selectedWordOnDoubleClick.selectionStart());
00615 
00616     const qreal differenceToStart = mouseXPosition - wordStartX;
00617     const qreal differenceToEnd = wordEndX - mouseXPosition;
00618 
00619     if (differenceToStart < differenceToEnd)
00620         setCursorPosition(wordStartPos, QTextCursor::KeepAnchor);
00621     else
00622         setCursorPosition(wordEndPos, QTextCursor::KeepAnchor);
00623 }
00624 
00625 void QTextControlPrivate::extendLinewiseSelection(int suggestedNewPosition)
00626 {
00627     Q_Q(QTextControl);
00628 
00629     // if inside the initial selected line keep that
00630     if (suggestedNewPosition >= selectedLineOnDoubleClick.selectionStart()
00631         && suggestedNewPosition <= selectedLineOnDoubleClick.selectionEnd()) {
00632         q->setTextCursor(selectedLineOnDoubleClick);
00633         return;
00634     }
00635 
00636     if (suggestedNewPosition < selectedLineOnDoubleClick.position()) {
00637         cursor.setPosition(selectedLineOnDoubleClick.selectionEnd());
00638         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
00639         cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
00640     } else {
00641         cursor.setPosition(selectedLineOnDoubleClick.selectionStart());
00642         cursor.setPosition(suggestedNewPosition, QTextCursor::KeepAnchor);
00643         cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
00644     }
00645 }
00646 
00647 void QTextControlPrivate::_q_deleteSelected()
00648 {
00649     if (!(interactionFlags & Qt::TextEditable) || !cursor.hasSelection())
00650   return;
00651     cursor.removeSelectedText();
00652 }
00653 
00654 void QTextControl::undo()
00655 {
00656     Q_D(QTextControl);
00657     d->doc->undo(&d->cursor);
00658     ensureCursorVisible();
00659 }
00660 
00661 void QTextControl::redo()
00662 {
00663     Q_D(QTextControl);
00664     d->doc->redo(&d->cursor);
00665     ensureCursorVisible();
00666 }
00667 
00668 QTextControl::QTextControl(QObject *parent)
00669     : QObject(*new QTextControlPrivate, parent)
00670 {
00671     Q_D(QTextControl);
00672     d->setContent(); // init
00673 }
00674 
00675 QTextControl::QTextControl(const QString &text, QObject *parent)
00676     : QObject(*new QTextControlPrivate, parent)
00677 {
00678     Q_D(QTextControl);
00679     d->setContent(Qt::RichText, text); // init
00680 }
00681 
00682 QTextControl::QTextControl(QTextDocument *doc, QObject *parent)
00683     : QObject(*new QTextControlPrivate, parent)
00684 {
00685     Q_D(QTextControl);
00686     d->setContent(Qt::RichText, QString(), doc); // init
00687 }
00688 
00689 QTextControl::~QTextControl()
00690 {
00691 }
00692 
00693 void QTextControl::setDocument(QTextDocument *document)
00694 {
00695     Q_D(QTextControl);
00696     if (d->doc == document)
00697         return;
00698 
00699     d->doc->disconnect(this);
00700     d->doc->documentLayout()->disconnect(this);
00701     d->doc->documentLayout()->setPaintDevice(0);
00702 
00703     if (d->doc->parent() == this)
00704         delete d->doc;
00705 
00706     d->doc = 0;
00707     d->setContent(Qt::RichText, QString(), document);
00708 }
00709 
00710 QTextDocument *QTextControl::document() const
00711 {
00712     Q_D(const QTextControl);
00713     return d->doc;
00714 }
00715 
00716 void QTextControl::setTextCursor(const QTextCursor &cursor)
00717 {
00718     Q_D(QTextControl);
00719     const bool posChanged = cursor.position() != d->cursor.position();
00720     const QTextCursor oldSelection = d->cursor;
00721     d->cursor = cursor;
00722     d->_q_updateCurrentCharFormatAndSelection();
00723     ensureCursorVisible();
00724     d->repaintOldAndNewSelection(oldSelection);
00725     if (posChanged)
00726         emit cursorPositionChanged();
00727 }
00728 
00729 QTextCursor QTextControl::textCursor() const
00730 {
00731     Q_D(const QTextControl);
00732     return d->cursor;
00733 }
00734 
00735 #ifndef QT_NO_CLIPBOARD
00736 
00737 void QTextControl::cut()
00738 {
00739     Q_D(QTextControl);
00740     if (!(d->interactionFlags & Qt::TextEditable) || !d->cursor.hasSelection())
00741   return;
00742     copy();
00743     d->cursor.removeSelectedText();
00744 }
00745 
00746 void QTextControl::copy()
00747 {
00748     Q_D(QTextControl);
00749     if (!d->cursor.hasSelection())
00750   return;
00751     QMimeData *data = createMimeDataFromSelection();
00752     QApplication::clipboard()->setMimeData(data);
00753 }
00754 
00755 void QTextControl::paste()
00756 {
00757     const QMimeData *md = QApplication::clipboard()->mimeData();
00758     if (md)
00759         insertFromMimeData(md);
00760 }
00761 #endif
00762 
00763 void QTextControl::clear()
00764 {
00765     Q_D(QTextControl);
00766     // clears and sets empty content
00767     d->setContent();
00768 }
00769 
00770 
00771 void QTextControl::selectAll()
00772 {
00773     Q_D(QTextControl);
00774     d->cursor.select(QTextCursor::Document);
00775     d->selectionChanged();
00776     emit updateRequest(QRectF(QPointF(), d->doc->documentLayout()->documentSize()));
00777 }
00778 
00779 void QTextControl::processEvent(QEvent *e, const QPointF &coordinateOffset, QWidget *contextWidget)
00780 {
00781     QMatrix m;
00782     m.translate(coordinateOffset.x(), coordinateOffset.y());
00783     processEvent(e, m, contextWidget);
00784 }
00785 
00786 void QTextControl::processEvent(QEvent *e, const QMatrix &matrix, QWidget *contextWidget)
00787 {
00788 
00789     Q_D(QTextControl);
00790     if (d->interactionFlags & Qt::NoTextInteraction)
00791         return;
00792 
00793     d->contextWidget = contextWidget;
00794 
00795     if (!d->contextWidget) {
00796         switch (e->type()) {
00797 #ifndef QT_NO_GRAPHICSVIEW
00798             case QEvent::GraphicsSceneMouseMove:
00799             case QEvent::GraphicsSceneMousePress:
00800             case QEvent::GraphicsSceneMouseRelease:
00801             case QEvent::GraphicsSceneMouseDoubleClick:
00802             case QEvent::GraphicsSceneContextMenu:
00803             case QEvent::GraphicsSceneHoverEnter:
00804             case QEvent::GraphicsSceneHoverMove:
00805             case QEvent::GraphicsSceneHoverLeave:
00806             case QEvent::GraphicsSceneHelp:
00807             case QEvent::GraphicsSceneDragEnter:
00808             case QEvent::GraphicsSceneDragMove:
00809             case QEvent::GraphicsSceneDragLeave:
00810             case QEvent::GraphicsSceneDrop: {
00811                 QGraphicsSceneEvent *ev = static_cast<QGraphicsSceneEvent *>(e);
00812                 d->contextWidget = ev->widget();
00813                 break;
00814             }
00815 #endif // QT_NO_GRAPHICSVIEW
00816             default: break;
00817         };
00818     }
00819 
00820     switch (e->type()) {
00821         case QEvent::KeyPress:
00822             d->keyPressEvent(static_cast<QKeyEvent *>(e));
00823             break;
00824         case QEvent::MouseButtonPress: {
00825             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
00826             d->mousePressEvent(ev->button(), matrix.map(ev->pos()), ev->modifiers(),
00827                                ev->buttons(), ev->globalPos());
00828             break; }
00829         case QEvent::MouseMove: {
00830             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
00831             d->mouseMoveEvent(ev->buttons(), matrix.map(ev->pos()));
00832             break; }
00833         case QEvent::MouseButtonRelease: {
00834             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
00835             d->mouseReleaseEvent(ev->button(), matrix.map(ev->pos()));
00836             break; }
00837         case QEvent::MouseButtonDblClick: {
00838             QMouseEvent *ev = static_cast<QMouseEvent *>(e);
00839             d->mouseDoubleClickEvent(e, ev->button(), matrix.map(ev->pos()));
00840             break; }
00841         case QEvent::InputMethod:
00842             d->inputMethodEvent(static_cast<QInputMethodEvent *>(e));
00843             break;
00844         case QEvent::ContextMenu: {
00845             QContextMenuEvent *ev = static_cast<QContextMenuEvent *>(e);
00846             d->contextMenuEvent(ev->globalPos(), matrix.map(ev->pos()), contextWidget);
00847             break; }
00848 
00849         case QEvent::FocusIn:
00850         case QEvent::FocusOut:
00851             d->focusEvent(static_cast<QFocusEvent *>(e));
00852             break;
00853 
00854         case QEvent::EnabledChange:
00855             d->isEnabled = e->isAccepted();
00856             break;
00857 
00858 #ifndef QT_NO_DRAGANDDROP
00859         case QEvent::DragEnter: {
00860             QDragEnterEvent *ev = static_cast<QDragEnterEvent *>(e);
00861             if (d->dragEnterEvent(e, ev->mimeData()))
00862                 ev->acceptProposedAction();
00863             break;
00864         }
00865         case QEvent::DragLeave:
00866             d->dragLeaveEvent();
00867             break;
00868         case QEvent::DragMove: {
00869             QDragMoveEvent *ev = static_cast<QDragMoveEvent *>(e);
00870             if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
00871                 ev->acceptProposedAction();
00872             break;
00873         }
00874         case QEvent::Drop: {
00875             QDropEvent *ev = static_cast<QDropEvent *>(e);
00876             if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->proposedAction(), ev->source()))
00877                 ev->acceptProposedAction();
00878             break;
00879         }
00880 #endif
00881 
00882 #ifndef QT_NO_GRAPHICSVIEW
00883         case QEvent::GraphicsSceneMousePress: {
00884             QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
00885             d->mousePressEvent(ev->button(), matrix.map(ev->pos()), ev->modifiers(), ev->buttons(),
00886                                ev->screenPos());
00887             break; }
00888         case QEvent::GraphicsSceneMouseMove: {
00889             QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
00890             d->mouseMoveEvent(ev->buttons(), matrix.map(ev->pos()));
00891             break; }
00892         case QEvent::GraphicsSceneMouseRelease: {
00893             QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
00894             d->mouseReleaseEvent(ev->button(), matrix.map(ev->pos()));
00895             break; }
00896         case QEvent::GraphicsSceneMouseDoubleClick: {
00897             QGraphicsSceneMouseEvent *ev = static_cast<QGraphicsSceneMouseEvent *>(e);
00898             d->mouseDoubleClickEvent(e, ev->button(), matrix.map(ev->pos()));
00899             break; }
00900         case QEvent::GraphicsSceneContextMenu: {
00901             QGraphicsSceneContextMenuEvent *ev = static_cast<QGraphicsSceneContextMenuEvent *>(e);
00902             d->contextMenuEvent(ev->screenPos(), matrix.map(ev->pos()), contextWidget);
00903             break; }
00904 
00905         case QEvent::GraphicsSceneHoverMove: {
00906             QGraphicsSceneHoverEvent *ev = static_cast<QGraphicsSceneHoverEvent *>(e);
00907             d->mouseMoveEvent(Qt::NoButton, matrix.map(ev->pos()));
00908             break; }
00909 
00910         case QEvent::GraphicsSceneDragEnter: {
00911             QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
00912             if (d->dragEnterEvent(e, ev->mimeData()))
00913                 ev->acceptProposedAction();
00914             break; }
00915         case QEvent::GraphicsSceneDragLeave:
00916             d->dragLeaveEvent();
00917             break;
00918         case QEvent::GraphicsSceneDragMove: {
00919             QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
00920             if (d->dragMoveEvent(e, ev->mimeData(), matrix.map(ev->pos())))
00921                 ev->acceptProposedAction();
00922             break; }
00923         case QEvent::GraphicsSceneDrop: {
00924             QGraphicsSceneDragDropEvent *ev = static_cast<QGraphicsSceneDragDropEvent *>(e);
00925             if (d->dropEvent(ev->mimeData(), matrix.map(ev->pos()), ev->proposedAction(), ev->source()))
00926                 ev->acceptProposedAction();
00927             break; }
00928 #endif // QT_NO_GRAPHICSVIEW
00929 
00930         case QEvent::ShortcutOverride:
00931             if (d->interactionFlags & Qt::TextEditable) {
00932                 QKeyEvent* ke = static_cast<QKeyEvent *>(e);
00933                 if (ke->modifiers() == Qt::NoModifier
00934                     || ke->modifiers() == Qt::ShiftModifier
00935                     || ke->modifiers() == Qt::KeypadModifier) {
00936                     if (ke->key() < Qt::Key_Escape) {
00937                         ke->accept();
00938                     } else {
00939                         switch (ke->key()) {
00940                             case Qt::Key_Return:
00941                             case Qt::Key_Enter:
00942                             case Qt::Key_Delete:
00943                             case Qt::Key_Home:
00944                             case Qt::Key_End:
00945                             case Qt::Key_Backspace:
00946                             case Qt::Key_Left:
00947                             case Qt::Key_Right:
00948                             ke->accept();
00949                         default:
00950                             break;
00951                         }
00952                     }
00953                 } else if (ke->modifiers() & Qt::ControlModifier) {
00954                     switch (ke->key()) {
00955                         case Qt::Key_C:
00956                         case Qt::Key_V:
00957                         case Qt::Key_X:
00958                         case Qt::Key_Y:
00959                         case Qt::Key_Z:
00960                         case Qt::Key_Left:
00961                         case Qt::Key_Right:
00962                         case Qt::Key_Up:
00963                         case Qt::Key_Down:
00964                         case Qt::Key_Home:
00965                         case Qt::Key_End:
00966 #if !defined(Q_WS_MAC)
00967                         case Qt::Key_Insert:
00968                         case Qt::Key_Delete:
00969 #endif
00970                         ke->accept();
00971                     default:
00972                         break;
00973                     }
00974                 }
00975             }
00976             // FALL THROUGH
00977         default:
00978             break;
00979     }
00980 }
00981 
00982 bool QTextControl::event(QEvent *e)
00983 {
00984     return QObject::event(e);
00985 }
00986 
00987 void QTextControl::timerEvent(QTimerEvent *e)
00988 {
00989     Q_D(QTextControl);
00990     if (e->timerId() == d->cursorBlinkTimer.timerId()) {
00991         d->cursorOn = !d->cursorOn;
00992 
00993         if (d->cursor.hasSelection())
00994             d->cursorOn &= (QApplication::style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected)
00995                             != 0);
00996 
00997         d->repaintCursor();
00998     } else if (e->timerId() == d->trippleClickTimer.timerId()) {
00999         d->trippleClickTimer.stop();
01000     }
01001 }
01002 
01003 void QTextControl::setPlainText(const QString &text)
01004 {
01005     Q_D(QTextControl);
01006     d->setContent(Qt::PlainText, text);
01007 }
01008 
01009 void QTextControl::setHtml(const QString &text)
01010 {
01011     Q_D(QTextControl);
01012     d->setContent(Qt::RichText, text);
01013 }
01014 
01015 void QTextControlPrivate::keyPressEvent(QKeyEvent *e)
01016 {
01017     Q_Q(QTextControl);
01018 #ifndef QT_NO_SHORTCUT
01019     if (e == QKeySequence::SelectAll) {
01020             e->accept();
01021             q->selectAll();
01022             return;
01023     }
01024 #ifndef QT_NO_CLIPBOARD
01025     else if (e == QKeySequence::Copy) {
01026             e->accept();
01027             q->copy();
01028             return;
01029     }
01030 #endif
01031 #endif // QT_NO_SHORTCUT
01032 
01033     if (interactionFlags & Qt::TextSelectableByKeyboard
01034         && cursorMoveKeyEvent(e))
01035         goto accept;
01036 
01037     if (interactionFlags & Qt::LinksAccessibleByKeyboard) {
01038         if ((e->key() == Qt::Key_Return
01039              || e->key() == Qt::Key_Enter
01040 #ifdef QT_KEYPAD_NAVIGATION
01041              || e->key() == Qt::Key_Select
01042 #endif
01043              )
01044             && cursor.hasSelection()) {
01045 
01046             e->accept();
01047 
01048             QTextCursor tmp = cursor;
01049             if (tmp.selectionStart() != tmp.position())
01050                 tmp.setPosition(tmp.selectionStart());
01051             tmp.movePosition(QTextCursor::NextCharacter);
01052             const QString href = tmp.charFormat().anchorHref();
01053             if (!href.isEmpty()) {
01054 #ifndef QT_NO_DESKTOPSERVICES
01055                 if (openExternalLinks)
01056                     QDesktopServices::openUrl(href);
01057                 else
01058 #endif
01059                     emit q->linkActivated(href);
01060             }
01061             return;
01062         }
01063     }
01064 
01065     if (!(interactionFlags & Qt::TextEditable)) {
01066         e->ignore();
01067         return;
01068     }
01069 
01070     if (e->key() == Qt::Key_Direction_L || e->key() == Qt::Key_Direction_R) {
01071         QTextBlockFormat fmt;
01072         fmt.setLayoutDirection((e->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
01073         cursor.mergeBlockFormat(fmt);
01074         goto accept;
01075     }
01076 
01077     // schedule a repaint of the region of the cursor, as when we move it we
01078     // want to make sure the old cursor disappears (not noticeable when moving
01079     // only a few pixels but noticeable when jumping between cells in tables for
01080     // example)
01081     repaintSelection();
01082 
01083     if (e->key() == Qt::Key_Backspace && !e->modifiers()) {
01084         QTextBlockFormat blockFmt = cursor.blockFormat();
01085         QTextList *list = cursor.currentList();
01086         if (list && cursor.atBlockStart()) {
01087             list->remove(cursor.block());
01088         } else if (cursor.atBlockStart() && blockFmt.indent() > 0) {
01089             blockFmt.setIndent(blockFmt.indent() - 1);
01090             cursor.setBlockFormat(blockFmt);
01091         } else {
01092             cursor.deletePreviousChar();
01093         }
01094         goto accept;
01095     } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
01096         if (e->modifiers() & Qt::ControlModifier)
01097             cursor.insertText(QString(QChar::LineSeparator));
01098         else
01099             cursor.insertBlock();
01100         e->accept();
01101         goto accept;
01102     }
01103 
01104     if (false) {
01105     }
01106 #ifndef QT_NO_SHORTCUT
01107     else if (e == QKeySequence::Undo) {
01108             q->undo();
01109     }
01110     else if (e == QKeySequence::Redo) {
01111            q->redo();
01112     }
01113 #ifndef QT_NO_CLIPBOARD
01114     else if (e == QKeySequence::Cut) {
01115            q->cut();
01116     }
01117     else if (e == QKeySequence::Paste) {
01118            q->paste();
01119     }
01120 #endif
01121     else if (e == QKeySequence::Delete) {
01122         cursor.deleteChar();
01123     }
01124     else if (e == QKeySequence::DeleteEndOfWord) {
01125         cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
01126         cursor.deleteChar();
01127     }
01128     else if (e == QKeySequence::DeleteStartOfWord) {
01129         cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
01130         cursor.deleteChar();
01131     }
01132     else if (e == QKeySequence::DeleteEndOfLine) {
01133         QTextBlock block = cursor.block();
01134         if (cursor.position() == block.position() + block.length() - 2)
01135             cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
01136         else
01137             cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
01138         cursor.deleteChar();
01139     }
01140 #endif // QT_NO_SHORTCUT
01141     else {
01142         goto process;
01143     }
01144     goto accept;
01145 
01146 process:
01147     {
01148         QString text = e->text();
01149         if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) {
01150             if (overwriteMode
01151                 // no need to call deleteChar() if we have a selection, insertText
01152                 // does it already
01153                 && !cursor.hasSelection()
01154                 && !cursor.atBlockEnd())
01155                 cursor.deleteChar();
01156 
01157             cursor.insertText(text);
01158             selectionChanged();
01159         } else {
01160             e->ignore();
01161             return;
01162         }
01163     }
01164 
01165  accept:
01166 
01167     e->accept();
01168     cursorOn = true;
01169 
01170     q->ensureCursorVisible();
01171 
01172     updateCurrentCharFormat();
01173 }
01174 
01175 QVariant QTextControl::loadResource(int type, const QUrl &name)
01176 {
01177 #ifdef QT_NO_TEXTEDIT
01178     Q_UNUSED(type);
01179     Q_UNUSED(name);
01180 #else
01181     if (QTextEdit *textEdit = qobject_cast<QTextEdit *>(parent()))
01182         return textEdit->loadResource(type, name);
01183 #endif
01184     return QVariant();
01185 }
01186 
01187 QRectF QTextControlPrivate::rectForPosition(int position) const
01188 {
01189     const QTextBlock block = doc->findBlock(position);
01190     if (!block.isValid())
01191         return QRectF();
01192     const QAbstractTextDocumentLayout *docLayout = doc->documentLayout();
01193     const QTextLayout *layout = block.layout();
01194     const QPointF layoutPos = docLayout->blockBoundingRect(block).topLeft();
01195     int relativePos = position - block.position();
01196     if (preeditCursor != 0) {
01197         int preeditPos = layout->preeditAreaPosition();
01198         if (relativePos == preeditPos)
01199             relativePos += preeditCursor;
01200         else if (relativePos > preeditPos)
01201             relativePos += layout->preeditAreaText().length();
01202     }
01203     QTextLine line = layout->lineForTextPosition(relativePos);
01204 
01205     int cursorWidth;
01206     {
01207         bool ok = false;
01208 #ifndef QT_NO_PROPERTIES
01209         cursorWidth = docLayout->property("cursorWidth").toInt(&ok);
01210 #endif
01211         if (!ok)
01212             cursorWidth = 1;
01213     }
01214 
01215     QRectF r;
01216 
01217     if (line.isValid())
01218         r = QRectF(layoutPos.x() + line.cursorToX(relativePos) - 5 - cursorWidth, layoutPos.y() + line.y(),
01219                   2 * cursorWidth + 10, line.ascent() + line.descent()+1.);
01220     else
01221         r = QRectF(layoutPos.x() - 5 - cursorWidth, layoutPos.y(), 2 * cursorWidth + 10, 10); // #### correct height
01222 
01223     return r;
01224 }
01225 
01226 QRectF QTextControlPrivate::selectionRect(const QTextCursor &cursor) const
01227 {
01228     QRectF r = rectForPosition(cursor.selectionStart());
01229 
01230     if (cursor.hasComplexSelection() && cursor.currentTable()) {
01231         QTextTable *table = cursor.currentTable();
01232 
01233         r = doc->documentLayout()->frameBoundingRect(table);
01234         /*
01235         int firstRow, numRows, firstColumn, numColumns;
01236         cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
01237 
01238         const QTextTableCell firstCell = table->cellAt(firstRow, firstColumn);
01239         const QTextTableCell lastCell = table->cellAt(firstRow + numRows - 1, firstColumn + numColumns - 1);
01240 
01241         const QAbstractTextDocumentLayout * const layout = doc->documentLayout();
01242 
01243         QRectF tableSelRect = layout->blockBoundingRect(firstCell.firstCursorPosition().block());
01244 
01245         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
01246             const QTextTableCell cell = table->cellAt(firstRow, col);
01247             const qreal y = layout->blockBoundingRect(cell.firstCursorPosition().block()).top();
01248 
01249             tableSelRect.setTop(qMin(tableSelRect.top(), y));
01250         }
01251 
01252         for (int row = firstRow; row < firstRow + numRows; ++row) {
01253             const QTextTableCell cell = table->cellAt(row, firstColumn);
01254             const qreal x = layout->blockBoundingRect(cell.firstCursorPosition().block()).left();
01255 
01256             tableSelRect.setLeft(qMin(tableSelRect.left(), x));
01257         }
01258 
01259         for (int col = firstColumn; col < firstColumn + numColumns; ++col) {
01260             const QTextTableCell cell = table->cellAt(firstRow + numRows - 1, col);
01261             const qreal y = layout->blockBoundingRect(cell.lastCursorPosition().block()).bottom();
01262 
01263             tableSelRect.setBottom(qMax(tableSelRect.bottom(), y));
01264         }
01265 
01266         for (int row = firstRow; row < firstRow + numRows; ++row) {
01267             const QTextTableCell cell = table->cellAt(row, firstColumn + numColumns - 1);
01268             const qreal x = layout->blockBoundingRect(cell.lastCursorPosition().block()).right();
01269 
01270             tableSelRect.setRight(qMax(tableSelRect.right(), x));
01271         }
01272 
01273         r = tableSelRect.toRect();
01274         */
01275     } else if (cursor.hasSelection()) {
01276         const int position = cursor.selectionStart();
01277         const int anchor = cursor.selectionEnd();
01278         const QTextBlock posBlock = doc->findBlock(position);
01279         const QTextBlock anchorBlock = doc->findBlock(anchor);
01280         if (posBlock == anchorBlock && posBlock.layout()->lineCount()) {
01281             const QTextLine posLine = posBlock.layout()->lineForTextPosition(position - posBlock.position());
01282             const QTextLine anchorLine = anchorBlock.layout()->lineForTextPosition(anchor - anchorBlock.position());
01283 
01284             const int firstLine = qMin(posLine.lineNumber(), anchorLine.lineNumber());
01285             const int lastLine = qMax(posLine.lineNumber(), anchorLine.lineNumber());
01286             r = QRectF();
01287             for (int i = firstLine; i <= lastLine; ++i) {
01288                 r |= posBlock.layout()->lineAt(i).rect().toRect();
01289             }
01290             r.translate(doc->documentLayout()->blockBoundingRect(posBlock).topLeft());
01291         } else {
01292             QRectF anchorRect = rectForPosition(cursor.selectionEnd());
01293             r |= anchorRect;
01294             QRectF frameRect(doc->documentLayout()->frameBoundingRect(cursor.currentFrame()));
01295             r.setLeft(frameRect.left());
01296             r.setRight(frameRect.right());
01297         }
01298         if (r.isValid())
01299             r.adjust(-1, -1, 1, 1);
01300     }
01301 
01302     return r;
01303 }
01304 
01305 void QTextControlPrivate::mousePressEvent(Qt::MouseButton button, const QPointF &pos, Qt::KeyboardModifiers modifiers,
01306                                           Qt::MouseButtons buttons, const QPoint &globalPos)
01307 {
01308     Q_Q(QTextControl);
01309     cursorIsFocusIndicator = false;
01310 
01311     if (interactionFlags & Qt::LinksAccessibleByMouse) {
01312         anchorOnMousePress = q->anchorAt(pos);
01313     }
01314     if (!(button & Qt::LeftButton))
01315         return;
01316 
01317     if (!((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable)))
01318         return;
01319 
01320     const QTextCursor oldSelection = cursor;
01321     const int oldCursorPos = cursor.position();
01322 
01323     mousePressed = true;
01324 #ifndef QT_NO_DRAGANDDROP
01325     mightStartDrag = false;
01326 #endif
01327 
01328     if (trippleClickTimer.isActive()
01329         && ((pos - trippleClickPoint).toPoint().manhattanLength() < QApplication::startDragDistance())) {
01330 
01331         cursor.movePosition(QTextCursor::StartOfBlock);
01332         cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
01333 
01334         trippleClickTimer.stop();
01335     } else {
01336         int cursorPos = doc->documentLayout()->hitTest(pos, Qt::FuzzyHit);
01337         if (cursorPos == -1)
01338             return;
01339 
01340 #if !defined(QT_NO_IM)
01341         QTextLayout *layout = cursor.block().layout();
01342         if (contextWidget && layout && !layout->preeditAreaText().isEmpty()) {
01343             QInputContext *ctx = contextWidget->inputContext();
01344             if (!ctx && contextWidget->parentWidget())
01345                 ctx = contextWidget->parentWidget()->inputContext();
01346             if (ctx) {
01347                 QMouseEvent ev(QEvent::MouseButtonPress, contextWidget->mapFromGlobal(globalPos), globalPos,
01348                                button, buttons, modifiers);
01349                 ctx->mouseHandler(cursorPos - cursor.position(), &ev);
01350             }
01351             if (!layout->preeditAreaText().isEmpty())
01352                 return;
01353         }
01354 #endif
01355         if (modifiers == Qt::ShiftModifier) {
01356             if (selectedWordOnDoubleClick.hasSelection())
01357                 extendWordwiseSelection(cursorPos, pos.x());
01358             else if (selectedLineOnDoubleClick.hasSelection())
01359                 extendLinewiseSelection(cursorPos);
01360             else
01361                 setCursorPosition(cursorPos, QTextCursor::KeepAnchor);
01362         } else {
01363 
01364             if (cursor.hasSelection()
01365                 && cursorPos >= cursor.selectionStart()
01366                 && cursorPos <= cursor.selectionEnd()
01367                 && doc->documentLayout()->hitTest(pos, Qt::ExactHit) != -1) {
01368 #ifndef QT_NO_DRAGANDDROP
01369                 mightStartDrag = true;
01370                 dragStartPos = pos.toPoint();
01371 #endif
01372                 return;
01373             }
01374 
01375             setCursorPosition(cursorPos);
01376         }
01377     }
01378 
01379     if (interactionFlags & Qt::TextEditable) {
01380         q->ensureCursorVisible();
01381         if (cursor.position() != oldCursorPos)
01382             emit q->cursorPositionChanged();
01383         _q_updateCurrentCharFormatAndSelection();
01384     } else {
01385         if (cursor.position() != oldCursorPos)
01386             emit q->cursorPositionChanged();
01387         selectionChanged();
01388     }
01389     repaintOldAndNewSelection(oldSelection);
01390     hadSelectionOnMousePress = cursor.hasSelection();
01391 }
01392 
01393 void QTextControlPrivate::mouseMoveEvent(Qt::MouseButtons buttons, const QPointF &mousePos)
01394 {
01395     Q_Q(QTextControl);
01396 
01397     if (interactionFlags & Qt::LinksAccessibleByMouse) {
01398         QString anchor = q->anchorAt(mousePos);
01399         if (anchor != highlightedAnchor) {
01400             highlightedAnchor = anchor;
01401             emit q->linkHovered(anchor);
01402         }
01403     }
01404 
01405     if (!(buttons & Qt::LeftButton))
01406         return;
01407 
01408     if (!((interactionFlags & Qt::TextSelectableByMouse) || (interactionFlags & Qt::TextEditable)))
01409         return;
01410 
01411     if (!(mousePressed
01412           || selectedWordOnDoubleClick.hasSelection()
01413           || selectedLineOnDoubleClick.hasSelection()))
01414         return;
01415 
01416     const QTextCursor oldSelection = cursor;
01417     const int oldCursorPos = cursor.position();
01418 
01419     if (mightStartDrag) {
01420         if ((mousePos.toPoint() - dragStartPos).manhattanLength() > QApplication::startDragDistance())
01421             startDrag();
01422         return;
01423     }
01424     const qreal mouseX = qreal(mousePos.x());
01425 
01426 #if !defined(QT_NO_IM)
01427     QTextLayout *layout = cursor.block().layout();
01428     if (layout && !layout->preeditAreaText().isEmpty())
01429         return;
01430 #endif
01431 
01432     int newCursorPos = doc->documentLayout()->hitTest(mousePos, Qt::FuzzyHit);
01433     if (newCursorPos == -1)
01434         return;
01435 
01436     if (selectedWordOnDoubleClick.hasSelection())
01437         extendWordwiseSelection(newCursorPos, mouseX);
01438     else if (selectedLineOnDoubleClick.hasSelection())
01439         extendLinewiseSelection(newCursorPos);
01440     else
01441         setCursorPosition(newCursorPos, QTextCursor::KeepAnchor);
01442 
01443     if (interactionFlags & Qt::TextEditable) {
01444         q->ensureCursorVisible();
01445         if (cursor.position() != oldCursorPos)
01446             emit q->cursorPositionChanged();
01447         _q_updateCurrentCharFormatAndSelection();
01448     } else {
01449         emit q->visibilityRequest(QRectF(mousePos, QSizeF(1, 1)));
01450         if (cursor.position() != oldCursorPos)
01451             emit q->cursorPositionChanged();
01452         selectionChanged();
01453     }
01454     repaintOldAndNewSelection(oldSelection);
01455 }
01456 
01457 void QTextControlPrivate::mouseReleaseEvent(Qt::MouseButton button, const QPointF &pos)
01458 {
01459     Q_Q(QTextControl);
01460 
01461     const QTextCursor oldSelection = cursor;
01462 
01463 #ifndef QT_NO_DRAGANDDROP
01464     if (mightStartDrag) {
01465         mousePressed = false;
01466         setCursorPosition(pos);
01467         cursor.clearSelection();
01468         selectionChanged();
01469     }
01470 #endif
01471     if (mousePressed) {
01472         mousePressed = false;
01473 #ifndef QT_NO_CLIPBOARD
01474         if (interactionFlags & Qt::TextSelectableByMouse)
01475             setClipboardSelection();
01476     } else if (button == Qt::MidButton
01477                && (interactionFlags & Qt::TextEditable)
01478                && QApplication::clipboard()->supportsSelection()) {
01479         setCursorPosition(pos);
01480         const QMimeData *md = QApplication::clipboard()->mimeData(QClipboard::Selection);
01481         if (md)
01482             q->insertFromMimeData(md);
01483 #endif
01484     }
01485 
01486     repaintOldAndNewSelection(oldSelection);
01487 
01488     if (interactionFlags & Qt::LinksAccessibleByMouse) {
01489         if (!(button & Qt::LeftButton))
01490             return;
01491 
01492         const QString anchor = q->anchorAt(pos);
01493 
01494         if (anchor.isEmpty())
01495             return;
01496 
01497         if (!cursor.hasSelection()
01498             || (anchor == anchorOnMousePress && hadSelectionOnMousePress))
01499 #ifndef QT_NO_DESKTOPSERVICES
01500             if (openExternalLinks)
01501                 QDesktopServices::openUrl(anchor);
01502             else
01503 #endif
01504                 emit q->linkActivated(anchor);
01505     }
01506 }
01507 
01508 void QTextControlPrivate::mouseDoubleClickEvent(QEvent *e, Qt::MouseButton button, const QPointF &pos)
01509 {
01510     Q_Q(QTextControl);
01511     if (button != Qt::LeftButton
01512         || !(interactionFlags & Qt::TextSelectableByMouse)) {
01513         e->ignore();
01514         return;
01515     }
01516 #if !defined(QT_NO_IM)
01517     QTextLayout *layout = cursor.block().layout();
01518     if (layout && !layout->preeditAreaText().isEmpty())
01519         return;
01520 #endif
01521 
01522 #ifndef QT_NO_DRAGANDDROP
01523     mightStartDrag = false;
01524 #endif
01525     const QTextCursor oldSelection = cursor;
01526     setCursorPosition(pos);
01527     QTextLine line = currentTextLine(cursor);
01528     if (line.isValid() && line.textLength()) {
01529         cursor.select(QTextCursor::WordUnderCursor);
01530         selectionChanged();
01531 #ifndef QT_NO_CLIPBOARD
01532         setClipboardSelection();
01533 #endif
01534     }
01535     repaintOldAndNewSelection(oldSelection);
01536 
01537     selectedWordOnDoubleClick = cursor;
01538 
01539     trippleClickPoint = pos;
01540     trippleClickTimer.start(qApp->doubleClickInterval(), q);
01541 }
01542 
01543 void QTextControlPrivate::contextMenuEvent(const QPoint &screenPos, const QPointF &docPos, QWidget *contextWidget)
01544 {
01545 #ifdef QT_NO_CONTEXTMENU
01546     Q_UNUSED(screenPos);
01547     Q_UNUSED(docPos);
01548 #else
01549     Q_Q(QTextControl);
01550     if (!hasFocus)
01551         return;
01552     QMenu *menu = q->createStandardContextMenu(docPos, contextWidget);
01553     if (!menu)
01554         return;
01555     menu->exec(screenPos);
01556     delete menu;
01557 #endif
01558 }
01559 
01560 bool QTextControlPrivate::dragEnterEvent(QEvent *e, const QMimeData *mimeData)
01561 {
01562     Q_Q(QTextControl);
01563     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
01564         e->ignore();
01565         return false;
01566     }
01567 
01568     dndFeedbackCursor = QTextCursor();
01569 
01570     return true; // accept proposed action
01571 }
01572 
01573 void QTextControlPrivate::dragLeaveEvent()
01574 {
01575     Q_Q(QTextControl);
01576 
01577     const QRectF crect = q->cursorRect(dndFeedbackCursor);
01578     dndFeedbackCursor = QTextCursor();
01579 
01580     if (crect.isValid())
01581         emit q->updateRequest(crect);
01582 }
01583 
01584 bool QTextControlPrivate::dragMoveEvent(QEvent *e, const QMimeData *mimeData, const QPointF &pos)
01585 {
01586     Q_Q(QTextControl);
01587     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData)) {
01588         e->ignore();
01589         return false;
01590     }
01591 
01592     const int cursorPos = doc->documentLayout()->hitTest(pos, Qt::FuzzyHit);
01593     if (cursorPos != -1) {
01594         QRectF crect = q->cursorRect(dndFeedbackCursor);
01595         if (crect.isValid())
01596             emit q->updateRequest(crect);
01597 
01598         dndFeedbackCursor = cursor;
01599         dndFeedbackCursor.setPosition(cursorPos);
01600 
01601         crect = q->cursorRect(dndFeedbackCursor);
01602         emit q->updateRequest(crect);
01603     }
01604 
01605     return true; // accept proposed action
01606 }
01607 
01608 bool QTextControlPrivate::dropEvent(const QMimeData *mimeData, const QPointF &pos, Qt::DropAction dropAction, QWidget *source)
01609 {
01610     Q_Q(QTextControl);
01611     dndFeedbackCursor = QTextCursor();
01612 
01613     if (!(interactionFlags & Qt::TextEditable) || !q->canInsertFromMimeData(mimeData))
01614         return false;
01615 
01616     repaintSelection();
01617 
01618     QTextCursor insertionCursor = q->cursorForPosition(pos);
01619     insertionCursor.beginEditBlock();
01620 
01621     if (dropAction == Qt::MoveAction && source == contextWidget)
01622         cursor.removeSelectedText();
01623 
01624     cursor = insertionCursor;
01625     q->insertFromMimeData(mimeData);
01626     insertionCursor.endEditBlock();
01627     q->ensureCursorVisible();
01628     return true; // accept proposed action
01629 }
01630 
01631 void QTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
01632 {
01633     if (!(interactionFlags & Qt::TextEditable) || cursor.isNull()) {
01634         e->ignore();
01635         return;
01636     }
01637     cursor.beginEditBlock();
01638 
01639     cursor.removeSelectedText();
01640 
01641     // insert commit string
01642     if (!e->commitString().isEmpty() || e->replacementLength()) {
01643         QTextCursor c = cursor;
01644         c.setPosition(c.position() + e->replacementStart());
01645         c.setPosition(c.position() + e->replacementLength(), QTextCursor::KeepAnchor);
01646         c.insertText(e->commitString());
01647     }
01648 
01649     QTextBlock block = cursor.block();
01650     QTextLayout *layout = block.layout();
01651     layout->setPreeditArea(cursor.position() - block.position(), e->preeditString());
01652     QList<QTextLayout::FormatRange> overrides;
01653     preeditCursor = e->preeditString().length();
01654     hideCursor = false;
01655     for (int i = 0; i < e->attributes().size(); ++i) {
01656         const QInputMethodEvent::Attribute &a = e->attributes().at(i);
01657         if (a.type == QInputMethodEvent::Cursor) {
01658             preeditCursor = a.start;
01659             hideCursor = !a.length;
01660         } else if (a.type == QInputMethodEvent::TextFormat) {
01661             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
01662             if (f.isValid()) {
01663                 QTextLayout::FormatRange o;
01664                 o.start = a.start + cursor.position() - block.position();
01665                 o.length = a.length;
01666                 o.format = f;
01667                 overrides.append(o);
01668             }
01669         }
01670     }
01671     layout->setAdditionalFormats(overrides);
01672     cursor.endEditBlock();
01673 }
01674 
01675 QVariant QTextControl::inputMethodQuery(Qt::InputMethodQuery property) const
01676 {
01677     Q_D(const QTextControl);
01678     QTextBlock block = d->cursor.block();
01679     switch(property) {
01680     case Qt::ImMicroFocus:
01681         return cursorRect();
01682     case Qt::ImFont:
01683         return QVariant(d->cursor.charFormat().font());
01684     case Qt::ImCursorPosition:
01685         return QVariant(d->cursor.position() - block.position());
01686     case Qt::ImSurroundingText:
01687         return QVariant(block.text());
01688     case Qt::ImCurrentSelection:
01689         return QVariant(d->cursor.selectedText());
01690     default:
01691         return QVariant();
01692     }
01693 }
01694 
01695 void QTextControl::setFocus(bool focus, Qt::FocusReason reason)
01696 {
01697     QFocusEvent ev(focus ? QEvent::FocusIn : QEvent::FocusOut,
01698                    reason);
01699     processEvent(&ev);
01700 }
01701 
01702 void QTextControlPrivate::focusEvent(QFocusEvent *e)
01703 {
01704     emit q_func()->updateRequest(selectionRect());
01705     if (e->gotFocus()) {
01706         cursorOn = (interactionFlags & Qt::TextSelectableByKeyboard);
01707         if (interactionFlags & Qt::TextEditable) {
01708             setBlinkingCursorEnabled(true);
01709         }
01710     } else {
01711         setBlinkingCursorEnabled(false);
01712 
01713         if (interactionFlags & Qt::LinksAccessibleByKeyboard
01714             && e->reason() != Qt::ActiveWindowFocusReason
01715             && e->reason() != Qt::PopupFocusReason
01716             && cursor.hasSelection()) {
01717             cursor.clearSelection();
01718         }
01719     }
01720     hasFocus = e->gotFocus();
01721 }
01722 
01723 #ifndef QT_NO_CONTEXTMENU
01724 QMenu *QTextControl::createStandardContextMenu(const QPointF &pos, QWidget *parent)
01725 {
01726     Q_D(QTextControl);
01727 
01728     const bool showTextSelectionActions = d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
01729 
01730     d->linkToCopy = QString();
01731     if (!pos.isNull())
01732         d->linkToCopy = anchorAt(pos);
01733 
01734     if (d->linkToCopy.isEmpty() && !showTextSelectionActions)
01735         return 0;
01736 
01737     QMenu *menu = new QMenu(parent);
01738     QAction *a;
01739 
01740     if (d->interactionFlags & Qt::TextEditable) {
01741         a = menu->addAction(tr("&Undo") + ACCEL_KEY(Z), this, SLOT(undo()));
01742         a->setEnabled(d->doc->isUndoAvailable());
01743         a = menu->addAction(tr("&Redo") + ACCEL_KEY(Y), this, SLOT(redo()));
01744         a->setEnabled(d->doc->isRedoAvailable());
01745         menu->addSeparator();
01746 
01747         a = menu->addAction(tr("Cu&t") + ACCEL_KEY(X), this, SLOT(cut()));
01748         a->setEnabled(d->cursor.hasSelection());
01749     }
01750 
01751     if (showTextSelectionActions) {
01752         a = menu->addAction(tr("&Copy") + ACCEL_KEY(C), this, SLOT(copy()));
01753         a->setEnabled(d->cursor.hasSelection());
01754     }
01755 
01756     if ((d->interactionFlags & Qt::LinksAccessibleByKeyboard)
01757             || (d->interactionFlags & Qt::LinksAccessibleByMouse)) {
01758 
01759         a = menu->addAction(tr("Copy &Link Location"), this, SLOT(_q_copyLink()));
01760         a->setEnabled(!d->linkToCopy.isEmpty());
01761     }
01762 
01763     if (d->interactionFlags & Qt::TextEditable) {
01764 #if !defined(QT_NO_CLIPBOARD)
01765         a = menu->addAction(tr("&Paste") + ACCEL_KEY(V), this, SLOT(paste()));
01766         a->setEnabled(canPaste());
01767 #endif
01768         a = menu->addAction(tr("Delete"), this, SLOT(_q_deleteSelected()));
01769         a->setEnabled(d->cursor.hasSelection());
01770     }
01771 
01772 
01773     if (showTextSelectionActions) {
01774         menu->addSeparator();
01775         a = menu->addAction(tr("Select All") + ACCEL_KEY(A), this, SLOT(selectAll()));
01776         a->setEnabled(!d->doc->isEmpty());
01777     }
01778 
01779     if (d->interactionFlags & Qt::TextEditable) {
01780         menu->addSeparator();
01781         QUnicodeControlCharacterMenu *ctrlCharacterMenu = new QUnicodeControlCharacterMenu(this, menu);
01782         menu->addMenu(ctrlCharacterMenu);
01783     }
01784 
01785     return menu;
01786 }
01787 #endif // QT_NO_CONTEXTMENU
01788 
01789 QTextCursor QTextControl::cursorForPosition(const QPointF &pos) const
01790 {
01791     Q_D(const QTextControl);
01792     int cursorPos = d->doc->documentLayout()->hitTest(pos, Qt::FuzzyHit);
01793     if (cursorPos == -1)
01794         cursorPos = 0;
01795     QTextCursor c(d->doc);
01796     c.setPosition(cursorPos);
01797     return c;
01798 }
01799 
01800 QRectF QTextControl::cursorRect(const QTextCursor &cursor) const
01801 {
01802     Q_D(const QTextControl);
01803     if (cursor.isNull())
01804         return QRectF();
01805 
01806    return d->rectForPosition(cursor.position());
01807 }
01808 
01809 QRectF QTextControl::cursorRect() const
01810 {
01811     Q_D(const QTextControl);
01812     return cursorRect(d->cursor);
01813 }
01814 
01815 QString QTextControl::anchorAt(const QPointF &pos) const
01816 {
01817     Q_D(const QTextControl);
01818     return d->doc->documentLayout()->anchorAt(pos);
01819 }
01820 
01821 bool QTextControl::overwriteMode() const
01822 {
01823     Q_D(const QTextControl);
01824     return d->overwriteMode;
01825 }
01826 
01827 void QTextControl::setOverwriteMode(bool overwrite)
01828 {
01829     Q_D(QTextControl);
01830     d->overwriteMode = overwrite;
01831 }
01832 
01833 int QTextControl::tabStopWidth() const
01834 {
01835 #ifndef QT_NO_PROPERTIES
01836     Q_D(const QTextControl);
01837     return qRound(d->doc->documentLayout()->property("tabStopWidth").toDouble());
01838 #else
01839     return 80;
01840 #endif
01841 }
01842 
01843 void QTextControl::setTabStopWidth(int width)
01844 {
01845 #ifdef QT_NO_PROPERTIES
01846     Q_UNUSED(width);
01847 #else
01848     Q_D(QTextControl);
01849     d->doc->documentLayout()->setProperty("tabStopWidth", QVariant(double(width)));
01850 #endif
01851 }
01852 
01853 int QTextControl::cursorWidth() const
01854 {
01855 #ifndef QT_NO_PROPERTIES
01856     Q_D(const QTextControl);
01857     return d->doc->documentLayout()->property("cursorWidth").toInt();
01858 #else
01859     return 1;
01860 #endif
01861 }
01862 
01863 void QTextControl::setCursorWidth(int width)
01864 {
01865     Q_D(QTextControl);
01866 #ifdef QT_NO_PROPERTIES
01867     Q_UNUSED(width);
01868 #else
01869     d->doc->documentLayout()->setProperty("cursorWidth", width);
01870 #endif
01871     d->repaintCursor();
01872 }
01873 
01874 bool QTextControl::acceptRichText() const
01875 {
01876     Q_D(const QTextControl);
01877     return d->acceptRichText;
01878 }
01879 
01880 void QTextControl::setAcceptRichText(bool accept)
01881 {
01882     Q_D(QTextControl);
01883     d->acceptRichText = accept;
01884 }
01885 
01886 #ifndef QT_NO_TEXTEDIT
01887 
01888 void QTextControl::setExtraSelections(const QList<QTextEdit::ExtraSelection> &selections)
01889 {
01890     Q_D(QTextControl);
01891     if (selections.count() == d->extraSelections.count()) {
01892         bool needUpdate = false;
01893         for (int i = 0; i < selections.count(); ++i)
01894             if (selections.at(i).cursor != d->extraSelections.at(i).cursor
01895                 || selections.at(i).format != d->extraSelections.at(i).format) {
01896                 needUpdate = true;
01897                 break;
01898             }
01899         if (!needUpdate)
01900             return;
01901     }
01902 
01903     for (int i = 0; i < d->extraSelections.count(); ++i) {
01904         QRectF r = d->selectionRect(d->extraSelections.at(i).cursor);
01905         if (d->extraSelections.at(i).format.boolProperty(QTextFormat::FullWidthSelection)) {
01906             r.setLeft(0);
01907             r.setWidth(INT_MAX);
01908         }
01909         emit updateRequest(r);
01910     }
01911 
01912     d->extraSelections.resize(selections.count());
01913     for (int i = 0; i < selections.count(); ++i) {
01914         d->extraSelections[i].cursor = selections.at(i).cursor;
01915         d->extraSelections[i].format = selections.at(i).format;
01916     }
01917 
01918     for (int i = 0; i < d->extraSelections.count(); ++i) {
01919         QRectF r = d->selectionRect(d->extraSelections.at(i).cursor);
01920         if (d->extraSelections.at(i).format.boolProperty(QTextFormat::FullWidthSelection)) {
01921             r.setLeft(0);
01922             r.setWidth(INT_MAX);
01923         }
01924         emit updateRequest(r);
01925     }
01926 }
01927 
01928 QList<QTextEdit::ExtraSelection> QTextControl::extraSelections() const
01929 {
01930     Q_D(const QTextControl);
01931     QList<QTextEdit::ExtraSelection> selections;
01932     for (int i = 0; i < d->extraSelections.count(); ++i) {
01933         QTextEdit::ExtraSelection sel;
01934         sel.cursor = d->extraSelections.at(i).cursor;
01935         sel.format = d->extraSelections.at(i).format;
01936         selections.append(sel);
01937     }
01938     return selections;
01939 }
01940 
01941 #endif // QT_NO_TEXTEDIT
01942 
01943 void QTextControl::setTextWidth(qreal width)
01944 {
01945     Q_D(QTextControl);
01946     d->doc->setTextWidth(width);
01947 }
01948 
01949 qreal QTextControl::textWidth() const
01950 {
01951     Q_D(const QTextControl);
01952     return d->doc->textWidth();
01953 }
01954 
01955 QSizeF QTextControl::size() const
01956 {
01957     Q_D(const QTextControl);
01958     return d->doc->size();
01959 }
01960 
01961 void QTextControl::setOpenExternalLinks(bool open)
01962 {
01963     Q_D(QTextControl);
01964     d->openExternalLinks = open;
01965 }
01966 
01967 bool QTextControl::openExternalLinks() const
01968 {
01969     Q_D(const QTextControl);
01970     return d->openExternalLinks;
01971 }
01972 
01973 void QTextControl::moveCursor(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode)
01974 {
01975     Q_D(QTextControl);
01976     const QTextCursor oldSelection = d->cursor;
01977     const bool moved = d->cursor.movePosition(op, mode);
01978     d->_q_updateCurrentCharFormatAndSelection();
01979     ensureCursorVisible();
01980     d->repaintOldAndNewSelection(oldSelection);
01981     if (moved)
01982         emit cursorPositionChanged();
01983 }
01984 
01985 bool QTextControl::canPaste() const
01986 {
01987 #ifndef QT_NO_CLIPBOARD
01988     Q_D(const QTextControl);
01989     if (d->interactionFlags & Qt::TextEditable) {
01990         const QMimeData *md = QApplication::clipboard()->mimeData();
01991         return md && canInsertFromMimeData(md);
01992     }
01993 #endif
01994     return false;
01995 }
01996 
01997 QMimeData *QTextControl::createMimeDataFromSelection() const
01998 {
01999     Q_D(const QTextControl);
02000     const QTextDocumentFragment fragment(d->cursor);
02001     return new QTextEditMimeData(fragment);
02002 }
02003 
02004 bool QTextControl::canInsertFromMimeData(const QMimeData *source) const
02005 {
02006     Q_D(const QTextControl);
02007     if (d->acceptRichText)
02008         return (source->hasText() && !source->text().isEmpty())
02009             || source->hasHtml()
02010             || source->hasFormat(QLatin1String("application/x-qrichtext"))
02011             || source->hasFormat(QLatin1String("application/x-qt-richtext"));
02012     else
02013         return source->hasText() && !source->text().isEmpty();
02014 }
02015 
02016 void QTextControl::insertFromMimeData(const QMimeData *source)
02017 {
02018     Q_D(QTextControl);
02019     if (!(d->interactionFlags & Qt::TextEditable) || !source)
02020   return;
02021 
02022     bool hasData = false;
02023     QTextDocumentFragment fragment;
02024     if (source->hasFormat(QLatin1String("application/x-qrichtext")) && d->acceptRichText) {
02025         // x-qrichtext is always UTF-8 (taken from Qt3 since we don't use it anymore).
02026         fragment = QTextDocumentFragment::fromHtml(QString::fromUtf8(source->data(QLatin1String("application/x-qrichtext"))));
02027         hasData = true;
02028     } else if (source->hasHtml() && d->acceptRichText) {
02029         fragment = QTextDocumentFragment::fromHtml(source->html());
02030         hasData = true;
02031     } else {
02032         QString text = source->text();
02033         if (!text.isNull()) {
02034             fragment = QTextDocumentFragment::fromPlainText(text);
02035             hasData = true;
02036         }
02037     }
02038 
02039     if (hasData)
02040         d->cursor.insertFragment(fragment);
02041     ensureCursorVisible();
02042 }
02043 
02044 bool QTextControlPrivate::findNextPrevAnchor(bool next, int &start, int &end)
02045 {
02046     if (!cursor.hasSelection()) {
02047         cursor = QTextCursor(doc);
02048         if (next)
02049             cursor .movePosition(QTextCursor::Start);
02050         else
02051             cursor.movePosition(QTextCursor::End);
02052     }
02053 
02054     Q_ASSERT(!cursor.isNull());
02055 
02056     int anchorStart = -1;
02057     QString anchorHref;
02058     int anchorEnd = -1;
02059 
02060     if (next) {
02061         const int startPos = cursor.selectionEnd();
02062 
02063         QTextBlock block = doc->findBlock(startPos);
02064         QTextBlock::Iterator it = block.begin();
02065 
02066         while (!it.atEnd() && it.fragment().position() < startPos)
02067             ++it;
02068 
02069         while (block.isValid()) {
02070             anchorStart = -1;
02071 
02072             // find next anchor
02073             for (; !it.atEnd(); ++it) {
02074                 const QTextFragment fragment = it.fragment();
02075                 const QTextCharFormat fmt = fragment.charFormat();
02076 
02077                 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
02078                     anchorStart = fragment.position();
02079                     anchorHref = fmt.anchorHref();
02080                     break;
02081                 }
02082             }
02083 
02084             if (anchorStart != -1) {
02085                 anchorEnd = -1;
02086 
02087                 // find next non-anchor fragment
02088                 for (; !it.atEnd(); ++it) {
02089                     const QTextFragment fragment = it.fragment();
02090                     const QTextCharFormat fmt = fragment.charFormat();
02091 
02092                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
02093                         anchorEnd = fragment.position();
02094                         break;
02095                     }
02096                 }
02097 
02098                 if (anchorEnd == -1)
02099                     anchorEnd = block.position() + block.length() - 1;
02100 
02101                 // make found selection
02102                 break;
02103             }
02104 
02105             block = block.next();
02106             it = block.begin();
02107         }
02108     } else {
02109         int startPos = cursor.selectionStart();
02110         if (startPos > 0)
02111             --startPos;
02112 
02113         QTextBlock block = doc->findBlock(startPos);
02114         QTextBlock::Iterator blockStart = block.begin();
02115         QTextBlock::Iterator it = block.end();
02116 
02117         if (startPos == block.position()) {
02118             it = block.begin();
02119         } else {
02120             do {
02121                 if (it == blockStart) {
02122                     it = QTextBlock::Iterator();
02123                     block = QTextBlock();
02124                 } else {
02125                     --it;
02126                 }
02127             } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
02128         }
02129 
02130         while (block.isValid()) {
02131             anchorStart = -1;
02132 
02133             if (!it.atEnd()) {
02134                 do {
02135                     const QTextFragment fragment = it.fragment();
02136                     const QTextCharFormat fmt = fragment.charFormat();
02137 
02138                     if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
02139                         anchorStart = fragment.position() + fragment.length();
02140                         anchorHref = fmt.anchorHref();
02141                         break;
02142                     }
02143 
02144                     if (it == blockStart)
02145                         it = QTextBlock::Iterator();
02146                     else
02147                         --it;
02148                 } while (!it.atEnd());
02149             }
02150 
02151             if (anchorStart != -1 && !it.atEnd()) {
02152                 anchorEnd = -1;
02153 
02154                 do {
02155                     const QTextFragment fragment = it.fragment();
02156                     const QTextCharFormat fmt = fragment.charFormat();
02157 
02158                     if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
02159                         anchorEnd = fragment.position() + fragment.length();
02160                         break;
02161                     }
02162 
02163                     if (it == blockStart)
02164                         it = QTextBlock::Iterator();
02165                     else
02166                         --it;
02167                 } while (!it.atEnd());
02168 
02169                 if (anchorEnd == -1)
02170                     anchorEnd = qMax(0, block.position());
02171 
02172                 break;
02173             }
02174 
02175             block = block.previous();
02176             it = block.end();
02177             if (it != block.begin())
02178                 --it;
02179             blockStart = block.begin();
02180         }
02181 
02182     }
02183 
02184     if (anchorStart != -1 && anchorEnd != -1) {
02185         start = anchorStart;
02186         end = anchorEnd;
02187         return true;
02188     }
02189 
02190     return false;
02191 }
02192 
02193 bool QTextControl::setFocusToNextOrPreviousAnchor(bool next)
02194 {
02195     Q_D(QTextControl);
02196 
02197     if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
02198         return false;
02199 
02200     QRectF crect = d->selectionRect(d->cursor);
02201     emit updateRequest(crect);
02202 
02203     int anchorStart, anchorEnd;
02204     if (d->findNextPrevAnchor(next, anchorStart, anchorEnd)) {
02205         d->cursor.setPosition(anchorStart);
02206         d->cursor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
02207         d->cursorIsFocusIndicator = true;
02208     } else {
02209         d->cursor.clearSelection();
02210     }
02211 
02212     if (d->cursor.hasSelection()) {
02213         crect = d->selectionRect(d->cursor);
02214         emit updateRequest(crect);
02215         emit visibilityRequest(crect);
02216         return true;
02217     } else {
02218         return false;
02219     }
02220 }
02221 
02222 void QTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags)
02223 {
02224     Q_D(QTextControl);
02225     if (flags == d->interactionFlags)
02226         return;
02227     d->interactionFlags = flags;
02228 
02229     if (d->hasFocus)
02230         d->setBlinkingCursorEnabled(flags & Qt::TextEditable);
02231 }
02232 
02233 Qt::TextInteractionFlags QTextControl::textInteractionFlags() const
02234 {
02235     Q_D(const QTextControl);
02236     return d->interactionFlags;
02237 }
02238 
02239 void QTextControl::mergeCurrentCharFormat(const QTextCharFormat &modifier)
02240 {
02241     Q_D(QTextControl);
02242     d->cursor.mergeCharFormat(modifier);
02243     d->lastCharFormat = d->cursor.charFormat();
02244 }
02245 
02246 void QTextControl::setCurrentCharFormat(const QTextCharFormat &format)
02247 {
02248     Q_D(QTextControl);
02249     d->cursor.setCharFormat(format);
02250     d->lastCharFormat = format;
02251 }
02252 
02253 QTextCharFormat QTextControl::currentCharFormat() const
02254 {
02255     Q_D(const QTextControl);
02256     return d->cursor.charFormat();
02257 }
02258 
02259 void QTextControl::insertPlainText(const QString &text)
02260 {
02261     Q_D(QTextControl);
02262     d->cursor.insertText(text);
02263 }
02264 
02265 void QTextControl::insertHtml(const QString &text)
02266 {
02267     Q_D(QTextControl);
02268     d->cursor.insertHtml(text);
02269 }
02270 
02271 QPointF QTextControl::anchorPosition(const QString &name) const
02272 {
02273     Q_D(const QTextControl);
02274     if (name.isEmpty())
02275         return QPointF();
02276 
02277     QRectF r;
02278     for (QTextBlock block = d->doc->begin(); block.isValid(); block = block.next()) {
02279         QTextCharFormat format = block.charFormat();
02280         if (format.isAnchor() && format.anchorName() == name) {
02281             r = d->rectForPosition(block.position());
02282             break;
02283         }
02284 
02285         for (QTextBlock::Iterator it = block.begin(); !it.atEnd(); ++it) {
02286             QTextFragment fragment = it.fragment();
02287             format = fragment.charFormat();
02288             if (format.isAnchor() && format.anchorName() == name) {
02289                 r = d->rectForPosition(fragment.position());
02290                 block = QTextBlock();
02291                 break;
02292             }
02293         }
02294     }
02295     if (!r.isValid())
02296         return QPointF();
02297     return QPointF(0, r.top());
02298 }
02299 
02300 void QTextControl::adjustSize()
02301 {
02302     Q_D(QTextControl);
02303     d->doc->adjustSize();
02304 }
02305 
02306 QTextOption::WrapMode QTextControl::wordWrapMode() const
02307 {
02308     Q_D(const QTextControl);
02309     if (QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(d->doc->documentLayout()))
02310         return layout->wordWrapMode();
02311     return QTextOption::WordWrap;
02312 }
02313 
02314 void QTextControl::setWordWrapMode(QTextOption::WrapMode mode)
02315 {
02316     Q_D(QTextControl);
02317     if (QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(d->doc->documentLayout()))
02318         layout->setWordWrapMode(mode);
02319 }
02320 
02321 bool QTextControl::find(const QString &exp, QTextDocument::FindFlags options)
02322 {
02323     Q_D(QTextControl);
02324     QTextCursor search = d->doc->find(exp, d->cursor, options);
02325     if (search.isNull())
02326         return false;
02327 
02328     setTextCursor(search);
02329     return true;
02330 }
02331 
02332 void QTextControl::append(const QString &text)
02333 {
02334     Q_D(QTextControl);
02335     QTextCursor cursor(d->doc);
02336     cursor.beginEditBlock();
02337     cursor.movePosition(QTextCursor::End);
02338 
02339     if (!d->doc->isEmpty())
02340         cursor.insertBlock(d->cursor.blockFormat(), d->cursor.charFormat());
02341     else
02342         cursor.setCharFormat(d->cursor.charFormat());
02343 
02344     // preserve the char format
02345     QTextCharFormat oldCharFormat = d->cursor.charFormat();
02346     if (Qt::mightBeRichText(text)) {
02347         cursor.insertHtml(text);
02348     } else {
02349         cursor.insertText(text);
02350     }
02351     if (!d->cursor.hasSelection())
02352         d->cursor.setCharFormat(oldCharFormat);
02353 
02354     cursor.endEditBlock();
02355 }
02356 
02357 void QTextControl::ensureCursorVisible()
02358 {
02359     Q_D(QTextControl);
02360     QRectF crect = d->rectForPosition(d->cursor.position());
02361     emit visibilityRequest(crect);
02362     emit microFocusChanged();
02363 }
02364 
02365 QPalette QTextControl::palette() const
02366 {
02367     Q_D(const QTextControl);
02368     return d->palette;
02369 }
02370 
02371 void QTextControl::setPalette(const QPalette &pal)
02372 {
02373     Q_D(QTextControl);
02374     d->palette = pal;
02375 }
02376 
02377 void QTextControl::drawContents(QPainter *p, const QRectF &rect)
02378 {
02379     Q_D(QTextControl);
02380     p->save();
02381     QAbstractTextDocumentLayout::PaintContext ctx;
02382     if (rect.isValid())
02383         p->setClipRect(rect);
02384     ctx.clip = rect;
02385     ctx.selections = d->extraSelections;
02386 
02387     for (int i = 0; i < ctx.selections.count(); ++i)
02388         if (ctx.selections.at(i).format.boolProperty(QTextFormat::FullWidthSelection)) {
02389             ctx.clip.setLeft(0);
02390             ctx.clip.setWidth(INT_MAX);
02391         }
02392 
02393     ctx.palette = d->palette;
02394     if (d->cursorOn && d->isEnabled) {
02395         if (d->hideCursor)
02396             ctx.cursorPosition = -1;
02397         else if (d->preeditCursor != 0)
02398             ctx.cursorPosition = - (d->preeditCursor + 2);
02399         else
02400             ctx.cursorPosition = d->cursor.position();
02401     }
02402 
02403     if (!d->dndFeedbackCursor.isNull())
02404         ctx.cursorPosition = d->dndFeedbackCursor.position();
02405     if (d->cursor.hasSelection()) {
02406         QAbstractTextDocumentLayout::Selection selection;
02407         selection.cursor = d->cursor;
02408         if (d->cursorIsFocusIndicator) {
02409             QPen outline(ctx.palette.color(QPalette::Text), 1, Qt::DotLine);
02410             selection.format.setProperty(QTextFormat::OutlinePen, outline);
02411         } else {
02412             QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive;
02413             selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight));
02414             selection.format.setForeground(ctx.palette.brush(cg, QPalette::HighlightedText));
02415         }
02416         ctx.selections.append(selection);
02417     }
02418 
02419     d->doc->documentLayout()->draw(p, ctx);
02420     p->restore();
02421 }
02422 
02423 void QTextControlPrivate::_q_copyLink()
02424 {
02425     QMimeData *md = new QMimeData;
02426     md->setText(linkToCopy);
02427 #ifndef QT_NO_CLIPBOARD
02428     QApplication::clipboard()->setMimeData(md);
02429 #endif
02430 }
02431 
02432 #include "moc_qtextcontrol_p.cpp"
02433 
02434 #endif // QT_NO_TEXTCONTROL

Generated on Thu Mar 15 11:56:21 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1