mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
403 lines
11 KiB
C++
403 lines
11 KiB
C++
#include "veditutils.h"
|
|
|
|
#include <QTextDocument>
|
|
#include <QDebug>
|
|
#include <QTextEdit>
|
|
#include <QScrollBar>
|
|
|
|
#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();
|
|
}
|
|
|