#include "veditutils.h" #include #include #include #include #include "vutils.h" void VEditUtils::removeBlock(QTextBlock &p_block, QString *p_text) { QTextCursor cursor(p_block); removeBlock(cursor, p_text); } void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text) { const QTextDocument *doc = p_cursor.document(); int blockCount = doc->blockCount(); int blockNum = p_cursor.block().blockNumber(); p_cursor.select(QTextCursor::BlockUnderCursor); if (p_text) { *p_text = selectedText(p_cursor) + "\n"; } p_cursor.deleteChar(); // Deleting the first block will leave an empty block. // Deleting the last empty block will not work with deleteChar(). if (blockCount == doc->blockCount()) { if (blockNum == blockCount - 1) { // The last block. p_cursor.deletePreviousChar(); } else { p_cursor.deleteChar(); } } if (p_cursor.block().blockNumber() < blockNum) { p_cursor.movePosition(QTextCursor::NextBlock); } p_cursor.movePosition(QTextCursor::StartOfBlock); } bool VEditUtils::insertBlockWithIndent(QTextCursor &p_cursor) { V_ASSERT(!p_cursor.hasSelection()); p_cursor.insertBlock(); return indentBlockAsPreviousBlock(p_cursor); } bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor) { bool ret = false; QTextBlock block = p_cursor.block(); QTextBlock preBlock = block.previous(); if (!preBlock.isValid()) { return false; } QString text = preBlock.text(); QRegExp regExp("^\\s*(-|\\d+\\.)\\s"); int regIdx = regExp.indexIn(text); if (regIdx != -1) { ret = true; V_ASSERT(regExp.captureCount() == 1); QString markText = regExp.capturedTexts()[1]; if (markText == "-") { // Insert - in front. p_cursor.insertText("- "); } else { // markText is like "123.". V_ASSERT(markText.endsWith('.')); bool ok = false; int num = markText.left(markText.size() - 1).toInt(&ok, 10); V_ASSERT(ok); num++; p_cursor.insertText(QString::number(num, 10) + ". "); } } return ret; } bool VEditUtils::indentBlockAsPreviousBlock(QTextCursor &p_cursor) { bool changed = false; QTextBlock block = p_cursor.block(); if (block.blockNumber() == 0) { // The first block. return false; } QTextBlock preBlock = block.previous(); QString text = preBlock.text(); QRegExp regExp("(^\\s*)"); regExp.indexIn(text); V_ASSERT(regExp.captureCount() == 1); QString leadingSpaces = regExp.capturedTexts()[1]; moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor); if (!p_cursor.atBlockStart()) { p_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); p_cursor.removeSelectedText(); changed = true; } if (!leadingSpaces.isEmpty()) { p_cursor.insertText(leadingSpaces); changed = true; } return changed; } void VEditUtils::moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor, QTextCursor::MoveMode p_mode) { QTextBlock block = p_cursor.block(); QString text = block.text(); int idx = 0; for (; idx < text.size(); ++idx) { if (text[idx].isSpace()) { continue; } else { break; } } p_cursor.setPosition(block.position() + idx, p_mode); } void VEditUtils::removeObjectReplacementCharacter(QString &p_text) { QRegExp orcBlockExp(QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)")); p_text.remove(orcBlockExp); p_text.remove(QChar::ObjectReplacementCharacter); } QString VEditUtils::selectedText(const QTextCursor &p_cursor) { QString text = p_cursor.selectedText(); text.replace(QChar::ParagraphSeparator, '\n'); return text; } // Use another QTextCursor to remain the selection. void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc, const QTextCursor &p_cursor, const QString &p_indentationText, bool p_isIndent) { int nrBlocks = 1; int start = p_cursor.selectionStart(); int end = p_cursor.selectionEnd(); QTextBlock sBlock = p_doc->findBlock(start); if (start != end) { QTextBlock eBlock = p_doc->findBlock(end); nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1; } QTextCursor bCursor(sBlock); bCursor.beginEditBlock(); for (int i = 0; i < nrBlocks; ++i) { if (p_isIndent) { indentBlock(bCursor, p_indentationText); } else { unindentBlock(bCursor, p_indentationText); } bCursor.movePosition(QTextCursor::NextBlock); } bCursor.endEditBlock(); } void VEditUtils::indentBlock(QTextCursor &p_cursor, const QString &p_indentationText) { QTextBlock block = p_cursor.block(); if (block.length() > 1) { p_cursor.movePosition(QTextCursor::StartOfBlock); p_cursor.insertText(p_indentationText); } } void VEditUtils::unindentBlock(QTextCursor &p_cursor, const QString &p_indentationText) { QTextBlock block = p_cursor.block(); QString text = block.text(); if (text.isEmpty()) { return; } p_cursor.movePosition(QTextCursor::StartOfBlock); if (text[0] == '\t') { p_cursor.deleteChar(); } else if (text[0].isSpace()) { int width = p_indentationText.size(); for (int i = 0; i < width; ++i) { if (text[i] == ' ') { p_cursor.deleteChar(); } else { break; } } } } bool VEditUtils::findTargetWithinBlock(QTextCursor &p_cursor, QTextCursor::MoveMode p_mode, QChar p_target, bool p_forward, bool p_inclusive, int p_repeat) { if (p_repeat < 1) { return false; } QTextBlock block = p_cursor.block(); QString text = block.text(); int pib = p_cursor.positionInBlock(); int delta = p_forward ? 1 : -1; // The index to start searching. int idx = pib + (p_inclusive ? delta : 2 * delta); for (; idx < text.size() && idx >= 0; idx += delta) { if (text[idx] == p_target) { if (--p_repeat == 0) { break; } } } if (idx < 0 || idx >= text.size() || p_repeat > 0) { return false; } // text[idx] is the target character. if ((p_forward && p_inclusive && p_mode == QTextCursor::KeepAnchor) || (!p_forward && !p_inclusive)) { ++idx; } else if (p_forward && !p_inclusive && p_mode == QTextCursor::MoveAnchor) { --idx; } p_cursor.setPosition(block.position() + idx, p_mode); return true; } int VEditUtils::selectedBlockCount(const QTextCursor &p_cursor) { if (!p_cursor.hasSelection()) { return 0; } QTextDocument *doc = p_cursor.document(); int sbNum = doc->findBlock(p_cursor.selectionStart()).blockNumber(); int ebNum = doc->findBlock(p_cursor.selectionEnd()).blockNumber(); return ebNum - sbNum + 1; } void VEditUtils::scrollBlockInPage(QTextEdit *p_edit, int p_blockNum, int p_dest) { QTextDocument *doc = p_edit->document(); QTextCursor cursor = p_edit->textCursor(); if (p_blockNum >= doc->blockCount()) { p_blockNum = doc->blockCount() - 1; } QTextBlock block = doc->findBlockByNumber(p_blockNum); int pib = cursor.positionInBlock(); if (cursor.block().blockNumber() != p_blockNum) { // Move the cursor to the block. if (pib >= block.length()) { pib = block.length() - 1; } cursor.setPosition(block.position() + pib); p_edit->setTextCursor(cursor); } // Scroll to let current cursor locate in proper position. p_edit->ensureCursorVisible(); QScrollBar *vsbar = p_edit->verticalScrollBar(); if (!vsbar || !vsbar->isVisible()) { // No vertical scrollbar. No need to scrool. return; } QRect rect = p_edit->cursorRect(); int height = p_edit->rect().height(); QScrollBar *sbar = p_edit->horizontalScrollBar(); if (sbar && sbar->isVisible()) { height -= sbar->height(); } switch (p_dest) { case 0: { // Top. while (rect.y() > 0 && vsbar->value() < vsbar->maximum()) { vsbar->setValue(vsbar->value() + vsbar->singleStep()); rect = p_edit->cursorRect(); } break; } case 1: { // Center. height = qMax(height / 2, 1); if (rect.y() > height) { while (rect.y() > height && vsbar->value() < vsbar->maximum()) { vsbar->setValue(vsbar->value() + vsbar->singleStep()); rect = p_edit->cursorRect(); } } else if (rect.y() < height) { while (rect.y() < height && vsbar->value() > vsbar->minimum()) { vsbar->setValue(vsbar->value() - vsbar->singleStep()); rect = p_edit->cursorRect(); } } break; } case 2: // Bottom. while (rect.y() < height && vsbar->value() > vsbar->minimum()) { vsbar->setValue(vsbar->value() - vsbar->singleStep()); rect = p_edit->cursorRect(); } break; default: break; } p_edit->ensureCursorVisible(); } bool VEditUtils::isListBlock(const QTextBlock &p_block, int *p_seq) { QString text = p_block.text(); QRegExp regExp("^\\s*(-|\\d+\\.)\\s"); if (p_seq) { *p_seq = -1; } int regIdx = regExp.indexIn(text); if (regIdx == -1) { return false; } V_ASSERT(regExp.captureCount() == 1); QString markText = regExp.capturedTexts()[1]; if (markText != "-") { V_ASSERT(markText.endsWith('.')); bool ok = false; int num = markText.left(markText.size() - 1).toInt(&ok, 10); V_ASSERT(ok); if (p_seq) { *p_seq = num; } } return true; } bool VEditUtils::isSpaceToBlockStart(const QTextBlock &p_block, int p_posInBlock) { if (p_posInBlock <= 0) { return true; } QString text = p_block.text(); V_ASSERT(text.size() >= p_posInBlock); return text.left(p_posInBlock).trimmed().isEmpty(); } void VEditUtils::deleteIndentAndListMark(QTextCursor &p_cursor) { V_ASSERT(!p_cursor.hasSelection()); p_cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); p_cursor.removeSelectedText(); }