📄 qtextcursor.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qtextcursor.h"#include "qtextcursor_p.h"#include "qglobal.h"#include "qtextdocumentfragment.h"#include "qtextdocumentfragment_p.h"#include "qtextlist.h"#include "qtexttable.h"#include "qtexttable_p.h"#include "qtextengine_p.h"#include <qtextlayout.h>#include <qdebug.h>enum { AdjustPrev = 0x1, AdjustUp = 0x3, AdjustNext = 0x4, AdjustDown = 0x12};QTextCursorPrivate::QTextCursorPrivate(QTextDocumentPrivate *p) : priv(p), x(0), position(0), anchor(0), adjusted_anchor(0), currentCharFormat(-1){ priv->addCursor(this);}QTextCursorPrivate::QTextCursorPrivate(const QTextCursorPrivate &rhs) : QSharedData(rhs){ position = rhs.position; anchor = rhs.anchor; adjusted_anchor = rhs.adjusted_anchor; priv = rhs.priv; x = rhs.x; currentCharFormat = rhs.currentCharFormat; priv->addCursor(this);}QTextCursorPrivate::~QTextCursorPrivate(){ if (priv) priv->removeCursor(this);}QTextCursorPrivate::AdjustResult QTextCursorPrivate::adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op){ QTextCursorPrivate::AdjustResult result = QTextCursorPrivate::CursorMoved; // not(!) <= , so that inserting text adjusts the cursor correctly if (position < positionOfChange || (position == positionOfChange && op == QTextUndoCommand::KeepCursor)) { result = CursorUnchanged; } else { if (charsAddedOrRemoved < 0 && position < positionOfChange - charsAddedOrRemoved) position = positionOfChange; else position += charsAddedOrRemoved; currentCharFormat = -1; } if (anchor >= positionOfChange && (anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) { if (charsAddedOrRemoved < 0 && anchor < positionOfChange - charsAddedOrRemoved) anchor = positionOfChange; else anchor += charsAddedOrRemoved; } if (adjusted_anchor >= positionOfChange && (adjusted_anchor != positionOfChange || op != QTextUndoCommand::KeepCursor)) { if (charsAddedOrRemoved < 0 && adjusted_anchor < positionOfChange - charsAddedOrRemoved) adjusted_anchor = positionOfChange; else adjusted_anchor += charsAddedOrRemoved; } return result;}void QTextCursorPrivate::setX(){ QTextBlock block = priv->blocksFind(position); const QTextLayout *layout = block.layout(); int pos = position - block.position(); QTextLine line = layout->lineForTextPosition(pos); if (line.isValid()) x = line.cursorToX(pos);}void QTextCursorPrivate::remove(){ if (anchor == position) return; priv->beginEditBlock(); currentCharFormat = -1; int pos1 = position; int pos2 = adjusted_anchor; QTextUndoCommand::Operation op = QTextUndoCommand::KeepCursor; if (pos1 > pos2) { pos1 = adjusted_anchor; pos2 = position; op = QTextUndoCommand::MoveCursor; } // deleting inside table? -> delete only content QTextTable *table = complexSelectionTable(); if (table) { int startRow, startCol, numRows, numCols; selectedTableCells(&startRow, &numRows, &startCol, &numCols); clearCells(table, startRow, startCol, numRows, numCols, op); } else { priv->remove(pos1, pos2-pos1, op); } adjusted_anchor = anchor = position; priv->endEditBlock();}void QTextCursorPrivate::clearCells(QTextTable *table, int startRow, int startCol, int numRows, int numCols, QTextUndoCommand::Operation op){ priv->beginEditBlock(); for (int row = startRow; row < startRow + numRows; ++row) for (int col = startCol; col < startCol + numCols; ++col) { QTextTableCell cell = table->cellAt(row, col); const int startPos = cell.firstPosition(); const int endPos = cell.lastPosition(); Q_ASSERT(startPos <= endPos); priv->remove(startPos, endPos - startPos, op); } priv->endEditBlock();}bool QTextCursorPrivate::canDelete(int pos) const{ QTextDocumentPrivate::FragmentIterator fit = priv->find(pos); QTextCharFormat fmt = priv->formatCollection()->charFormat((*fit)->format); return (fmt.objectIndex() == -1 || fmt.objectType() == QTextFormat::ImageObject);}void QTextCursorPrivate::insertBlock(const QTextBlockFormat &format, const QTextCharFormat &charFormat){ QTextFormatCollection *formats = priv->formatCollection(); int idx = formats->indexForFormat(format); Q_ASSERT(formats->format(idx).isBlockFormat()); priv->insertBlock(position, idx, formats->indexForFormat(charFormat)); currentCharFormat = -1;}void QTextCursorPrivate::adjustCursor(QTextCursor::MoveOperation m){ adjusted_anchor = anchor; if (position == anchor) return; QTextFrame *f_position = priv->frameAt(position); QTextFrame *f_anchor = priv->frameAt(adjusted_anchor); if (f_position != f_anchor) { // find common parent frame QList<QTextFrame *> positionChain; QList<QTextFrame *> anchorChain; QTextFrame *f = f_position; while (f) { positionChain.prepend(f); f = f->parentFrame(); } f = f_anchor; while (f) { anchorChain.prepend(f); f = f->parentFrame(); } Q_ASSERT(positionChain.at(0) == anchorChain.at(0)); int i = 1; int l = qMin(positionChain.size(), anchorChain.size()); for (; i < l; ++i) { if (positionChain.at(i) != anchorChain.at(i)) break; } if (m <= QTextCursor::WordLeft) { if (i < positionChain.size()) position = positionChain.at(i)->firstPosition() - 1; } else { if (i < positionChain.size()) position = positionChain.at(i)->lastPosition() + 1; } if (position < adjusted_anchor) { if (i < anchorChain.size()) adjusted_anchor = anchorChain.at(i)->lastPosition() + 1; } else { if (i < anchorChain.size()) adjusted_anchor = anchorChain.at(i)->firstPosition() - 1; } f_position = positionChain.at(i-1); } // same frame, either need to adjust to cell boundaries or return QTextTable *table = qobject_cast<QTextTable *>(f_position); if (!table) return; QTextTableCell c_position = table->cellAt(position); QTextTableCell c_anchor = table->cellAt(adjusted_anchor); if (c_position != c_anchor) { bool before; int col_position = c_position.column(); int col_anchor = c_anchor.column(); if (col_position == col_anchor) { before = c_position.row() < c_anchor.row(); } else { before = col_position < col_anchor; } // adjust to cell boundaries if (m <= QTextCursor::WordLeft) { position = c_position.firstPosition(); if (!before) --position; } else { position = c_position.lastPosition(); if (before) ++position; } if (position < adjusted_anchor) adjusted_anchor = c_anchor.lastPosition(); else adjusted_anchor = c_anchor.firstPosition(); } currentCharFormat = -1;}bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor::MoveMode mode){ currentCharFormat = -1; bool adjustX = true; QTextBlock blockIt = block(); if (op >= QTextCursor::Left && op <= QTextCursor::WordRight && blockIt.blockFormat().layoutDirection() == Qt::RightToLeft) { if (op == QTextCursor::Left) op = QTextCursor::NextCharacter; else if (op == QTextCursor::Right) op = QTextCursor::PreviousCharacter; else if (op == QTextCursor::WordLeft) op = QTextCursor::NextWord; else if (op == QTextCursor::WordRight) op = QTextCursor::PreviousWord; } const QTextLayout *layout = blockIt.layout(); int relativePos = position - blockIt.position(); QTextLine line = layout->lineForTextPosition(relativePos); Q_ASSERT(priv->frameAt(position) == priv->frameAt(adjusted_anchor)); int newPosition = position; switch(op) { case QTextCursor::NoMove: return true; case QTextCursor::Start: newPosition = 0; break; case QTextCursor::StartOfLine: { if (!line.isValid()) break; newPosition = blockIt.position() + line.textStart(); break; } case QTextCursor::StartOfBlock: { newPosition = blockIt.position(); break; } case QTextCursor::PreviousBlock: { if (blockIt == priv->blocksBegin()) return false; blockIt = blockIt.previous(); newPosition = blockIt.position(); break; } case QTextCursor::PreviousCharacter: case QTextCursor::Left: newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters); break; case QTextCursor::StartOfWord: { QTextEngine *engine = layout->engine(); const QCharAttributes *attributes = engine->attributes(); if (relativePos == 0) return false; // skip if already at word start if (attributes[relativePos - 1].whiteSpace && !attributes[relativePos].whiteSpace) return false; // FALL THROUGH! } case QTextCursor::PreviousWord: case QTextCursor::WordLeft: newPosition = priv->previousCursorPosition(position, QTextLayout::SkipWords); break; case QTextCursor::Up: { int i = line.lineNumber() - 1; if (i == -1) { if (blockIt == priv->blocksBegin()) return false; int blockPosition = blockIt.position(); QTextTable *table = qobject_cast<QTextTable *>(priv->frameAt(blockPosition)); if (table) { QTextTableCell cell = table->cellAt(blockPosition); if (cell.firstPosition() == blockPosition) { int row = cell.row() - 1; if (row >= 0) { blockPosition = table->cellAt(row, cell.column()).lastPosition(); } else { // move to line above the table blockPosition = table->firstPosition() - 1; } blockIt = priv->blocksFind(blockPosition); } else { blockIt = blockIt.previous(); } } else { blockIt = blockIt.previous(); } layout = blockIt.layout(); i = layout->lineCount()-1; } if (layout->lineCount()) { QTextLine line = layout->lineAt(i); newPosition = line.xToCursor(x) + blockIt.position(); } else { newPosition = blockIt.position(); } adjustX = false; break; } case QTextCursor::End: newPosition = priv->length() - 1; break; case QTextCursor::EndOfLine: { if (!line.isValid() || line.textLength() == 0) break; newPosition = blockIt.position() + line.textStart() + line.textLength(); if (line.lineNumber() < layout->lineCount() - 1) { const QString text = blockIt.text(); // ###### this relies on spaces being the cause for linebreaks. // this doesn't work with japanese if (text.at(line.textStart() + line.textLength() - 1).isSpace()) --newPosition; } break; } case QTextCursor::EndOfWord: { QTextEngine *engine = layout->engine(); const QCharAttributes *attributes = engine->attributes(); const QString string = engine->layoutData->string; const int len = layout->engine()->layoutData->string.length(); if (relativePos >= len) return false; relativePos++; while (relativePos < len && !attributes[relativePos].whiteSpace && !engine->atWordSeparator(relativePos)) relativePos++; newPosition = blockIt.position() + relativePos; break; } case QTextCursor::EndOfBlock: if (blockIt.length() >= 1) // position right before the block separator newPosition = blockIt.position() + blockIt.length() - 1; break; case QTextCursor::NextBlock: { blockIt = blockIt.next(); if (!blockIt.isValid()) return false; newPosition = blockIt.position(); break; } case QTextCursor::NextCharacter: case QTextCursor::Right: newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters); break; case QTextCursor::NextWord: case QTextCursor::WordRight: newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -