mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 22:09:52 +08:00
replace VEdit and VMdEdit with VEditor and VMdEditor
This commit is contained in:
parent
5abcb1a8d9
commit
404b5329a1
12
src/src.pro
12
src/src.pro
@ -84,7 +84,11 @@ SOURCES += main.cpp\
|
|||||||
dialog/vinsertlinkdialog.cpp \
|
dialog/vinsertlinkdialog.cpp \
|
||||||
vplaintextedit.cpp \
|
vplaintextedit.cpp \
|
||||||
vimageresourcemanager.cpp \
|
vimageresourcemanager.cpp \
|
||||||
vlinenumberarea.cpp
|
vlinenumberarea.cpp \
|
||||||
|
veditor.cpp \
|
||||||
|
vmdeditor.cpp \
|
||||||
|
veditconfig.cpp \
|
||||||
|
vpreviewmanager.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -156,7 +160,11 @@ HEADERS += vmainwindow.h \
|
|||||||
dialog/vinsertlinkdialog.h \
|
dialog/vinsertlinkdialog.h \
|
||||||
vplaintextedit.h \
|
vplaintextedit.h \
|
||||||
vimageresourcemanager.h \
|
vimageresourcemanager.h \
|
||||||
vlinenumberarea.h
|
vlinenumberarea.h \
|
||||||
|
veditor.h \
|
||||||
|
vmdeditor.h \
|
||||||
|
veditconfig.h \
|
||||||
|
vpreviewmanager.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
||||||
#include "vutils.h"
|
#include "vutils.h"
|
||||||
@ -418,6 +419,92 @@ void VEditUtils::scrollBlockInPage(QTextEdit *p_edit,
|
|||||||
p_edit->ensureCursorVisible();
|
p_edit->ensureCursorVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEditUtils::scrollBlockInPage(QPlainTextEdit *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 scroll.
|
||||||
|
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)
|
bool VEditUtils::isListBlock(const QTextBlock &p_block, int *p_seq)
|
||||||
{
|
{
|
||||||
QString text = p_block.text();
|
QString text = p_block.text();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
class QTextDocument;
|
class QTextDocument;
|
||||||
class QTextEdit;
|
class QTextEdit;
|
||||||
|
class QPlainTextEdit;
|
||||||
|
|
||||||
// Utils for text edit.
|
// Utils for text edit.
|
||||||
class VEditUtils
|
class VEditUtils
|
||||||
@ -113,6 +114,14 @@ public:
|
|||||||
int p_blockNum,
|
int p_blockNum,
|
||||||
int p_dest);
|
int p_dest);
|
||||||
|
|
||||||
|
// Scroll block @p_blockNum into the visual window.
|
||||||
|
// @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom.
|
||||||
|
// @p_blockNum is based on 0.
|
||||||
|
// Will set the cursor to the block.
|
||||||
|
static void scrollBlockInPage(QPlainTextEdit *p_edit,
|
||||||
|
int p_blockNum,
|
||||||
|
int p_dest);
|
||||||
|
|
||||||
// Check if @p_block is a auto list block.
|
// Check if @p_block is a auto list block.
|
||||||
// @p_seq will be the seq number of the ordered list, or -1.
|
// @p_seq will be the seq number of the ordered list, or -1.
|
||||||
// Returns true if it is an auto list block.
|
// Returns true if it is an auto list block.
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vedit.h"
|
#include "veditor.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
|
|
||||||
@ -106,8 +106,8 @@ static QString keyToString(int p_key, int p_modifiers)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VVim::VVim(VEdit *p_editor)
|
VVim::VVim(VEditor *p_editor)
|
||||||
: QObject(p_editor), m_editor(p_editor),
|
: QObject(p_editor->getEditor()), m_editor(p_editor),
|
||||||
m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Invalid),
|
m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Invalid),
|
||||||
m_resetPositionInBlock(true), m_regName(c_unnamedRegister),
|
m_resetPositionInBlock(true), m_regName(c_unnamedRegister),
|
||||||
m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false),
|
m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false),
|
||||||
@ -119,7 +119,7 @@ VVim::VVim(VEdit *p_editor)
|
|||||||
|
|
||||||
initRegisters();
|
initRegisters();
|
||||||
|
|
||||||
connect(m_editor, &VEdit::selectionChangedByMouse,
|
connect(m_editor->object(), &VEditorObject::selectionChangedByMouse,
|
||||||
this, &VVim::selectionToVisualMode);
|
this, &VVim::selectionToVisualMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,13 +472,13 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// See if we need to cancel auto indent.
|
// See if we need to cancel auto indent.
|
||||||
bool cancelAutoIndent = false;
|
bool cancelAutoIndent = false;
|
||||||
if (p_autoIndentPos && *p_autoIndentPos > -1) {
|
if (p_autoIndentPos && *p_autoIndentPos > -1) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cancelAutoIndent = VEditUtils::needToCancelAutoIndent(*p_autoIndentPos, cursor);
|
cancelAutoIndent = VEditUtils::needToCancelAutoIndent(*p_autoIndentPos, cursor);
|
||||||
|
|
||||||
if (cancelAutoIndent) {
|
if (cancelAutoIndent) {
|
||||||
autoIndentPos = -1;
|
autoIndentPos = -1;
|
||||||
VEditUtils::deleteIndentAndListMark(cursor);
|
VEditUtils::deleteIndentAndListMark(cursor);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,7 +501,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
QChar reg = keyToRegisterName(keyInfo);
|
QChar reg = keyToRegisterName(keyInfo);
|
||||||
if (!reg.isNull()) {
|
if (!reg.isNull()) {
|
||||||
// Insert register content.
|
// Insert register content.
|
||||||
m_editor->insertPlainText(getRegister(reg).read());
|
m_editor->insertPlainTextW(getRegister(reg).read());
|
||||||
}
|
}
|
||||||
|
|
||||||
goto clear_accept;
|
goto clear_accept;
|
||||||
@ -565,7 +565,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// Expecting a mark name to create a mark.
|
// Expecting a mark name to create a mark.
|
||||||
if (keyInfo.isAlphabet() && modifiers == Qt::NoModifier) {
|
if (keyInfo.isAlphabet() && modifiers == Qt::NoModifier) {
|
||||||
m_keys.clear();
|
m_keys.clear();
|
||||||
m_marks.setMark(keyToChar(key, modifiers), m_editor->textCursor());
|
m_marks.setMark(keyToChar(key, modifiers), m_editor->textCursorW());
|
||||||
}
|
}
|
||||||
|
|
||||||
goto clear_accept;
|
goto clear_accept;
|
||||||
@ -804,7 +804,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
setMode(VimMode::Insert, false);
|
setMode(VimMode::Insert, false);
|
||||||
}
|
}
|
||||||
} else if (modifiers == Qt::ShiftModifier) {
|
} else if (modifiers == Qt::ShiftModifier) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
// Insert at the first non-space character.
|
// Insert at the first non-space character.
|
||||||
VEditUtils::moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor);
|
VEditUtils::moveCursorFirstNonSpaceCharacter(cursor, QTextCursor::MoveAnchor);
|
||||||
@ -815,7 +815,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
1);
|
1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
} else if (isControlModifier(modifiers)) {
|
} else if (isControlModifier(modifiers)) {
|
||||||
// Ctrl+I, jump to next location.
|
// Ctrl+I, jump to next location.
|
||||||
@ -856,29 +856,29 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// Enter Insert mode.
|
// Enter Insert mode.
|
||||||
// Move cursor back one character.
|
// Move cursor back one character.
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
V_ASSERT(!cursor.hasSelection());
|
V_ASSERT(!cursor.hasSelection());
|
||||||
|
|
||||||
if (!cursor.atBlockEnd()) {
|
if (!cursor.atBlockEnd()) {
|
||||||
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
|
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
}
|
}
|
||||||
} else if (modifiers == Qt::ShiftModifier) {
|
} else if (modifiers == Qt::ShiftModifier) {
|
||||||
// Insert at the end of line.
|
// Insert at the end of line.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
cursor.movePosition(QTextCursor::EndOfBlock,
|
cursor.movePosition(QTextCursor::EndOfBlock,
|
||||||
QTextCursor::MoveAnchor,
|
QTextCursor::MoveAnchor,
|
||||||
1);
|
1);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
} else if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
} else if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
||||||
if (!cursor.atBlockEnd()) {
|
if (!cursor.atBlockEnd()) {
|
||||||
cursor.clearSelection();
|
cursor.clearSelection();
|
||||||
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
|
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,7 +894,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// Insert a new block under/above current block and enter insert mode.
|
// Insert a new block under/above current block and enter insert mode.
|
||||||
bool insertAbove = modifiers == Qt::ShiftModifier;
|
bool insertAbove = modifiers == Qt::ShiftModifier;
|
||||||
if (m_mode == VimMode::Normal) {
|
if (m_mode == VimMode::Normal) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
cursor.movePosition(insertAbove ? QTextCursor::StartOfBlock
|
cursor.movePosition(insertAbove ? QTextCursor::StartOfBlock
|
||||||
: QTextCursor::EndOfBlock,
|
: QTextCursor::EndOfBlock,
|
||||||
@ -919,7 +919,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
|
|
||||||
if (textInserted) {
|
if (textInserted) {
|
||||||
autoIndentPos = cursor.position();
|
autoIndentPos = cursor.position();
|
||||||
@ -1104,7 +1104,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
} else if (m_keys.isEmpty() && !hasActionToken()) {
|
} else if (m_keys.isEmpty() && !hasActionToken()) {
|
||||||
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
||||||
// u/U for tolower and toupper selected text.
|
// u/U for tolower and toupper selected text.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
// Different from Vim:
|
// Different from Vim:
|
||||||
// If there is no selection in Visual mode, we do nothing.
|
// If there is no selection in Visual mode, we do nothing.
|
||||||
@ -1116,7 +1116,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
|
|
||||||
convertCaseOfSelectedText(cursor, toLower);
|
convertCaseOfSelectedText(cursor, toLower);
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
|
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
break;
|
break;
|
||||||
@ -1143,7 +1143,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
} else if (checkPendingKey(Key(Qt::Key_G))) {
|
} else if (checkPendingKey(Key(Qt::Key_G))) {
|
||||||
// gu/gU, ToLower/ToUpper action.
|
// gu/gU, ToLower/ToUpper action.
|
||||||
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
// Different from Vim:
|
// Different from Vim:
|
||||||
// If there is no selection in Visual mode, we do nothing.
|
// If there is no selection in Visual mode, we do nothing.
|
||||||
@ -1155,7 +1155,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
|
|
||||||
convertCaseOfSelectedText(cursor, toLower);
|
convertCaseOfSelectedText(cursor, toLower);
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1338,7 +1338,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// Clear selection and enter normal mode.
|
// Clear selection and enter normal mode.
|
||||||
bool ret = clearSelection();
|
bool ret = clearSelection();
|
||||||
if (!ret && checkMode(VimMode::Normal)) {
|
if (!ret && checkMode(VimMode::Normal)) {
|
||||||
emit m_editor->requestCloseFindReplaceDialog();
|
emit m_editor->object()->requestCloseFindReplaceDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
@ -1367,9 +1367,9 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
setMode(mode);
|
setMode(mode);
|
||||||
|
|
||||||
if (m_mode == VimMode::VisualLine) {
|
if (m_mode == VimMode::VisualLine) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
expandSelectionToWholeLines(cursor);
|
expandSelectionToWholeLines(cursor);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1681,8 +1681,8 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
} else {
|
} else {
|
||||||
// The first >/<, an Action.
|
// The first >/<, an Action.
|
||||||
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
if (m_mode == VimMode::Visual || m_mode == VimMode::VisualLine) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
VEditUtils::indentSelectedBlocks(m_editor->document(),
|
VEditUtils::indentSelectedBlocks(m_editor->documentW(),
|
||||||
cursor,
|
cursor,
|
||||||
m_editConfig->m_tabSpaces,
|
m_editConfig->m_tabSpaces,
|
||||||
!unindent);
|
!unindent);
|
||||||
@ -1857,7 +1857,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
// xx%, jump to a certain line (percentage of the documents).
|
// xx%, jump to a certain line (percentage of the documents).
|
||||||
// Change the repeat from percentage to line number.
|
// Change the repeat from percentage to line number.
|
||||||
Token *token = getRepeatToken();
|
Token *token = getRepeatToken();
|
||||||
int bn = percentageToBlockNumber(m_editor->document(), token->m_repeat);
|
int bn = percentageToBlockNumber(m_editor->documentW(), token->m_repeat);
|
||||||
if (bn == -1) {
|
if (bn == -1) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
@ -2148,7 +2148,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
|
|||||||
|
|
||||||
clear_accept:
|
clear_accept:
|
||||||
resetState();
|
resetState();
|
||||||
m_editor->makeBlockVisible(m_editor->textCursor().block());
|
m_editor->makeBlockVisible(m_editor->textCursorW().block());
|
||||||
|
|
||||||
accept:
|
accept:
|
||||||
ret = true;
|
ret = true;
|
||||||
@ -2353,7 +2353,7 @@ void VVim::processMoveAction(QList<Token> &p_tokens)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (m_resetPositionInBlock) {
|
if (m_resetPositionInBlock) {
|
||||||
positionInBlock = cursor.positionInBlock();
|
positionInBlock = cursor.positionInBlock();
|
||||||
}
|
}
|
||||||
@ -2389,7 +2389,7 @@ void VVim::processMoveAction(QList<Token> &p_tokens)
|
|||||||
expandSelectionToWholeLines(cursor);
|
expandSelectionToWholeLines(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3383,8 +3383,8 @@ void VVim::processDeleteAction(QList<Token> &p_tokens)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
bool hasMoved = false;
|
bool hasMoved = false;
|
||||||
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
||||||
|
|
||||||
@ -3598,7 +3598,7 @@ void VVim::processDeleteAction(QList<Token> &p_tokens)
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (hasMoved) {
|
if (hasMoved) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3616,8 +3616,8 @@ void VVim::processCopyAction(QList<Token> &p_tokens)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
int oriPos = cursor.position();
|
int oriPos = cursor.position();
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
||||||
@ -3810,7 +3810,7 @@ void VVim::processCopyAction(QList<Token> &p_tokens)
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3853,7 +3853,7 @@ void VVim::processPasteAction(QList<Token> &p_tokens, bool p_pasteBefore)
|
|||||||
bool changed = false;
|
bool changed = false;
|
||||||
int nrBlock = 0;
|
int nrBlock = 0;
|
||||||
int restorePos = -1;
|
int restorePos = -1;
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
|
|
||||||
// Different from Vim:
|
// Different from Vim:
|
||||||
@ -3942,7 +3942,7 @@ void VVim::processPasteAction(QList<Token> &p_tokens, bool p_pasteBefore)
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "text pasted" << text;
|
qDebug() << "text pasted" << text;
|
||||||
@ -3962,8 +3962,8 @@ void VVim::processChangeAction(QList<Token> &p_tokens)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
bool hasMoved = false;
|
bool hasMoved = false;
|
||||||
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
||||||
|
|
||||||
@ -4226,7 +4226,7 @@ void VVim::processChangeAction(QList<Token> &p_tokens)
|
|||||||
int pos = cursor.selectionStart();
|
int pos = cursor.selectionStart();
|
||||||
bool allDeleted = false;
|
bool allDeleted = false;
|
||||||
if (pos == 0) {
|
if (pos == 0) {
|
||||||
QTextBlock block = m_editor->document()->lastBlock();
|
QTextBlock block = m_editor->documentW()->lastBlock();
|
||||||
if (block.position() + block.length() - 1 == cursor.selectionEnd()) {
|
if (block.position() + block.length() - 1 == cursor.selectionEnd()) {
|
||||||
allDeleted = true;
|
allDeleted = true;
|
||||||
}
|
}
|
||||||
@ -4243,7 +4243,7 @@ void VVim::processChangeAction(QList<Token> &p_tokens)
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (hasMoved) {
|
if (hasMoved) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(VimMode::Insert);
|
setMode(VimMode::Insert);
|
||||||
@ -4263,8 +4263,8 @@ void VVim::processIndentAction(QList<Token> &p_tokens, bool p_isIndent)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
|
|
||||||
if (to.isRange()) {
|
if (to.isRange()) {
|
||||||
bool changed = selectRange(cursor, doc, to.m_range, repeat);
|
bool changed = selectRange(cursor, doc, to.m_range, repeat);
|
||||||
@ -4402,8 +4402,8 @@ void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
QTextCursor::MoveMode moveMode = QTextCursor::KeepAnchor;
|
||||||
int oriPos = cursor.position();
|
int oriPos = cursor.position();
|
||||||
@ -4500,7 +4500,7 @@ void VVim::processToLowerAction(QList<Token> &p_tokens, bool p_toLower)
|
|||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4517,10 +4517,10 @@ void VVim::processUndoAction(QList<Token> &p_tokens)
|
|||||||
repeat = to.m_repeat;
|
repeat = to.m_repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (i = 0; i < repeat && doc->isUndoAvailable(); ++i) {
|
for (i = 0; i < repeat && doc->isUndoAvailable(); ++i) {
|
||||||
m_editor->undo();
|
m_editor->undoW();
|
||||||
}
|
}
|
||||||
|
|
||||||
message(tr("Undo %1 %2").arg(i).arg(i > 1 ? tr("changes") : tr("change")));
|
message(tr("Undo %1 %2").arg(i).arg(i > 1 ? tr("changes") : tr("change")));
|
||||||
@ -4539,10 +4539,10 @@ void VVim::processRedoAction(QList<Token> &p_tokens)
|
|||||||
repeat = to.m_repeat;
|
repeat = to.m_repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (i = 0; i < repeat && doc->isRedoAvailable(); ++i) {
|
for (i = 0; i < repeat && doc->isRedoAvailable(); ++i) {
|
||||||
m_editor->redo();
|
m_editor->redoW();
|
||||||
}
|
}
|
||||||
|
|
||||||
message(tr("Redo %1 %2").arg(i).arg(i > 1 ? tr("changes") : tr("change")));
|
message(tr("Redo %1 %2").arg(i).arg(i > 1 ? tr("changes") : tr("change")));
|
||||||
@ -4550,7 +4550,7 @@ void VVim::processRedoAction(QList<Token> &p_tokens)
|
|||||||
|
|
||||||
void VVim::processRedrawLineAction(QList<Token> &p_tokens, int p_dest)
|
void VVim::processRedrawLineAction(QList<Token> &p_tokens, int p_dest)
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
int repeat = cursor.block().blockNumber();
|
int repeat = cursor.block().blockNumber();
|
||||||
if (!p_tokens.isEmpty()) {
|
if (!p_tokens.isEmpty()) {
|
||||||
Token to = p_tokens.takeFirst();
|
Token to = p_tokens.takeFirst();
|
||||||
@ -4562,7 +4562,7 @@ void VVim::processRedrawLineAction(QList<Token> &p_tokens, int p_dest)
|
|||||||
repeat = to.m_repeat - 1;
|
repeat = to.m_repeat - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditUtils::scrollBlockInPage(m_editor, repeat, p_dest);
|
m_editor->scrollBlockInPage(repeat, p_dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
||||||
@ -4578,7 +4578,7 @@ void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
|||||||
repeat = to.m_repeat;
|
repeat = to.m_repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
Location loc;
|
Location loc;
|
||||||
if (p_next) {
|
if (p_next) {
|
||||||
while (m_locations.hasNext() && repeat > 0) {
|
while (m_locations.hasNext() && repeat > 0) {
|
||||||
@ -4593,7 +4593,7 @@ void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (loc.isValid()) {
|
if (loc.isValid()) {
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
if (loc.m_blockNumber >= doc->blockCount()) {
|
if (loc.m_blockNumber >= doc->blockCount()) {
|
||||||
message(tr("Mark has invalid line number"));
|
message(tr("Mark has invalid line number"));
|
||||||
return;
|
return;
|
||||||
@ -4607,11 +4607,11 @@ void VVim::processJumpLocationAction(QList<Token> &p_tokens, bool p_next)
|
|||||||
|
|
||||||
if (!m_editor->isBlockVisible(block)) {
|
if (!m_editor->isBlockVisible(block)) {
|
||||||
// Scroll the block to the center of screen.
|
// Scroll the block to the center of screen.
|
||||||
VEditUtils::scrollBlockInPage(m_editor, block.blockNumber(), 1);
|
m_editor->scrollBlockInPage(block.blockNumber(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.setPosition(block.position() + pib);
|
cursor.setPosition(block.position() + pib);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4643,7 +4643,7 @@ void VVim::processReplaceAction(QList<Token> &p_tokens)
|
|||||||
// If repeat is greater than the number of left characters in current line,
|
// If repeat is greater than the number of left characters in current line,
|
||||||
// do nothing.
|
// do nothing.
|
||||||
// In visual mode, repeat is ignored.
|
// In visual mode, repeat is ignored.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (checkMode(VimMode::Normal)) {
|
if (checkMode(VimMode::Normal)) {
|
||||||
// Select the characters to be replaced.
|
// Select the characters to be replaced.
|
||||||
@ -4659,7 +4659,7 @@ void VVim::processReplaceAction(QList<Token> &p_tokens)
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4683,7 +4683,7 @@ void VVim::processReverseCaseAction(QList<Token> &p_tokens)
|
|||||||
// If repeat is greater than the number of left characters in current line,
|
// If repeat is greater than the number of left characters in current line,
|
||||||
// just change the actual number of left characters.
|
// just change the actual number of left characters.
|
||||||
// In visual mode, repeat is ignored and reverse the selected text.
|
// In visual mode, repeat is ignored and reverse the selected text.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (checkMode(VimMode::Normal)) {
|
if (checkMode(VimMode::Normal)) {
|
||||||
// Select the characters to be replaced.
|
// Select the characters to be replaced.
|
||||||
@ -4699,7 +4699,7 @@ void VVim::processReverseCaseAction(QList<Token> &p_tokens)
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4726,12 +4726,12 @@ void VVim::processJoinAction(QList<Token> &p_tokens, bool p_modifySpaces)
|
|||||||
// In visual mode, repeat is ignored and join the highlighted lines.
|
// In visual mode, repeat is ignored and join the highlighted lines.
|
||||||
int firstBlock = -1;
|
int firstBlock = -1;
|
||||||
int blockCount = repeat;
|
int blockCount = repeat;
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (checkMode(VimMode::Normal)) {
|
if (checkMode(VimMode::Normal)) {
|
||||||
firstBlock = cursor.block().blockNumber();
|
firstBlock = cursor.block().blockNumber();
|
||||||
} else {
|
} else {
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
firstBlock = doc->findBlock(cursor.selectionStart()).blockNumber();
|
firstBlock = doc->findBlock(cursor.selectionStart()).blockNumber();
|
||||||
int lastBlock = doc->findBlock(cursor.selectionEnd()).blockNumber();
|
int lastBlock = doc->findBlock(cursor.selectionEnd()).blockNumber();
|
||||||
blockCount = lastBlock - firstBlock + 1;
|
blockCount = lastBlock - firstBlock + 1;
|
||||||
@ -4741,17 +4741,17 @@ void VVim::processJoinAction(QList<Token> &p_tokens, bool p_modifySpaces)
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
setMode(VimMode::Normal);
|
setMode(VimMode::Normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VVim::clearSelection()
|
bool VVim::clearSelection()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
cursor.clearSelection();
|
cursor.clearSelection();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4760,8 +4760,8 @@ bool VVim::clearSelection()
|
|||||||
|
|
||||||
int VVim::blockCountOfPageStep() const
|
int VVim::blockCountOfPageStep() const
|
||||||
{
|
{
|
||||||
int lineCount = m_editor->document()->blockCount();
|
int lineCount = m_editor->documentW()->blockCount();
|
||||||
QScrollBar *bar = m_editor->verticalScrollBar();
|
QScrollBar *bar = m_editor->verticalScrollBarW();
|
||||||
int steps = (bar->maximum() - bar->minimum() + bar->pageStep());
|
int steps = (bar->maximum() - bar->minimum() + bar->pageStep());
|
||||||
int pageLineCount = lineCount * (bar->pageStep() * 1.0 / steps);
|
int pageLineCount = lineCount * (bar->pageStep() * 1.0 / steps);
|
||||||
return pageLineCount;
|
return pageLineCount;
|
||||||
@ -4781,7 +4781,7 @@ void VVim::selectionToVisualMode(bool p_hasText)
|
|||||||
|
|
||||||
void VVim::expandSelectionToWholeLines(QTextCursor &p_cursor)
|
void VVim::expandSelectionToWholeLines(QTextCursor &p_cursor)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
int curPos = p_cursor.position();
|
int curPos = p_cursor.position();
|
||||||
int anchorPos = p_cursor.anchor();
|
int anchorPos = p_cursor.anchor();
|
||||||
QTextBlock curBlock = doc->findBlock(curPos);
|
QTextBlock curBlock = doc->findBlock(curPos);
|
||||||
@ -5031,12 +5031,12 @@ void VVim::deleteSelectedText(QTextCursor &p_cursor, bool p_clearEmptyBlock)
|
|||||||
|
|
||||||
void VVim::copySelectedText(bool p_addNewLine)
|
void VVim::copySelectedText(bool p_addNewLine)
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
copySelectedText(cursor, p_addNewLine);
|
copySelectedText(cursor, p_addNewLine);
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5318,15 +5318,15 @@ bool VVim::executeCommand(const QString &p_cmd)
|
|||||||
}else if (p_cmd.size() == 1) {
|
}else if (p_cmd.size() == 1) {
|
||||||
if (p_cmd == "w") {
|
if (p_cmd == "w") {
|
||||||
// :w, save current file.
|
// :w, save current file.
|
||||||
emit m_editor->saveNote();
|
emit m_editor->object()->saveNote();
|
||||||
msg = tr("Note has been saved");
|
msg = tr("Note has been saved");
|
||||||
} else if (p_cmd == "q") {
|
} else if (p_cmd == "q") {
|
||||||
// :q, quit edit mode.
|
// :q, quit edit mode.
|
||||||
emit m_editor->discardAndRead();
|
emit m_editor->object()->discardAndRead();
|
||||||
msg = tr("Quit");
|
msg = tr("Quit");
|
||||||
} else if (p_cmd == "x") {
|
} else if (p_cmd == "x") {
|
||||||
// :x, save if there is any change and quit edit mode.
|
// :x, save if there is any change and quit edit mode.
|
||||||
emit m_editor->saveAndRead();
|
emit m_editor->object()->saveAndRead();
|
||||||
msg = tr("Quit with note having been saved");
|
msg = tr("Quit with note having been saved");
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
@ -5335,11 +5335,11 @@ bool VVim::executeCommand(const QString &p_cmd)
|
|||||||
if (p_cmd == "wq") {
|
if (p_cmd == "wq") {
|
||||||
// :wq, save change and quit edit mode.
|
// :wq, save change and quit edit mode.
|
||||||
// We treat it same as :x.
|
// We treat it same as :x.
|
||||||
emit m_editor->saveAndRead();
|
emit m_editor->object()->saveAndRead();
|
||||||
msg = tr("Quit with note having been saved");
|
msg = tr("Quit with note having been saved");
|
||||||
} else if (p_cmd == "q!") {
|
} else if (p_cmd == "q!") {
|
||||||
// :q!, discard change and quit edit mode.
|
// :q!, discard change and quit edit mode.
|
||||||
emit m_editor->discardAndRead();
|
emit m_editor->object()->discardAndRead();
|
||||||
msg = tr("Quit");
|
msg = tr("Quit");
|
||||||
} else {
|
} else {
|
||||||
validCommand = false;
|
validCommand = false;
|
||||||
@ -5437,7 +5437,7 @@ bool VVim::processLeaderSequence(const Key &p_key)
|
|||||||
clearSearchHighlight();
|
clearSearchHighlight();
|
||||||
} else if (p_key == Key(Qt::Key_W)) {
|
} else if (p_key == Key(Qt::Key_W)) {
|
||||||
// <leader>w, save note
|
// <leader>w, save note
|
||||||
emit m_editor->saveNote();
|
emit m_editor->object()->saveNote();
|
||||||
message(tr("Note has been saved"));
|
message(tr("Note has been saved"));
|
||||||
} else {
|
} else {
|
||||||
validSequence = false;
|
validSequence = false;
|
||||||
@ -5624,7 +5624,7 @@ void VVim::processTitleJump(const QList<Token> &p_tokens, bool p_forward, int p_
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (m_editor->jumpTitle(p_forward, p_relativeLevel, repeat)) {
|
if (m_editor->jumpTitle(p_forward, p_relativeLevel, repeat)) {
|
||||||
// Record current location.
|
// Record current location.
|
||||||
m_locations.addLocation(cursor);
|
m_locations.addLocation(cursor);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "vutils.h"
|
#include "vutils.h"
|
||||||
|
|
||||||
class VEdit;
|
class VEditor;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
class VEditConfig;
|
class VEditConfig;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
@ -26,7 +26,7 @@ class VVim : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit VVim(VEdit *p_editor);
|
explicit VVim(VEditor *p_editor);
|
||||||
|
|
||||||
// Struct for a location.
|
// Struct for a location.
|
||||||
struct Location
|
struct Location
|
||||||
@ -801,7 +801,7 @@ private:
|
|||||||
Register &getRegister(QChar p_regName) const;
|
Register &getRegister(QChar p_regName) const;
|
||||||
void setRegister(QChar p_regName, const QString &p_val);
|
void setRegister(QChar p_regName, const QString &p_val);
|
||||||
|
|
||||||
VEdit *m_editor;
|
VEditor *m_editor;
|
||||||
const VEditConfig *m_editConfig;
|
const VEditConfig *m_editConfig;
|
||||||
VimMode m_mode;
|
VimMode m_mode;
|
||||||
|
|
||||||
|
@ -95,14 +95,6 @@ enum HighlightBlockState
|
|||||||
Comment
|
Comment
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LineNumberType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Absolute,
|
|
||||||
Relative,
|
|
||||||
CodeBlock
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pages to open on start up.
|
// Pages to open on start up.
|
||||||
enum class StartupPageType
|
enum class StartupPageType
|
||||||
{
|
{
|
||||||
|
@ -18,44 +18,6 @@ extern VNote *g_vnote;
|
|||||||
|
|
||||||
extern VMetaWordManager *g_mwMgr;
|
extern VMetaWordManager *g_mwMgr;
|
||||||
|
|
||||||
void VEditConfig::init(const QFontMetrics &p_metric,
|
|
||||||
bool p_enableHeadingSequence)
|
|
||||||
{
|
|
||||||
update(p_metric);
|
|
||||||
|
|
||||||
// Init configs that do not support update later.
|
|
||||||
m_enableVimMode = g_config->getEnableVimMode();
|
|
||||||
|
|
||||||
if (g_config->getLineDistanceHeight() <= 0) {
|
|
||||||
m_lineDistanceHeight = 0;
|
|
||||||
} else {
|
|
||||||
m_lineDistanceHeight = g_config->getLineDistanceHeight() * VUtils::calculateScaleFactor();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_highlightWholeBlock = m_enableVimMode;
|
|
||||||
|
|
||||||
m_enableHeadingSequence = p_enableHeadingSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VEditConfig::update(const QFontMetrics &p_metric)
|
|
||||||
{
|
|
||||||
if (g_config->getTabStopWidth() > 0) {
|
|
||||||
m_tabStopWidth = g_config->getTabStopWidth() * p_metric.width(' ');
|
|
||||||
} else {
|
|
||||||
m_tabStopWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_expandTab = g_config->getIsExpandTab();
|
|
||||||
|
|
||||||
if (m_expandTab && (g_config->getTabStopWidth() > 0)) {
|
|
||||||
m_tabSpaces = QString(g_config->getTabStopWidth(), ' ');
|
|
||||||
} else {
|
|
||||||
m_tabSpaces = "\t";
|
|
||||||
}
|
|
||||||
|
|
||||||
m_cursorLineBg = QColor(g_config->getEditorCurrentLineBg());
|
|
||||||
}
|
|
||||||
|
|
||||||
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
||||||
: QTextEdit(p_parent), m_file(p_file),
|
: QTextEdit(p_parent), m_file(p_file),
|
||||||
m_editOps(NULL), m_enableInputMethod(true)
|
m_editOps(NULL), m_enableInputMethod(true)
|
||||||
|
53
src/vedit.h
53
src/vedit.h
@ -11,6 +11,8 @@
|
|||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
|
#include "veditconfig.h"
|
||||||
|
#include "veditor.h"
|
||||||
|
|
||||||
class VEditOperations;
|
class VEditOperations;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
@ -21,55 +23,6 @@ class QResizeEvent;
|
|||||||
class QSize;
|
class QSize;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
|
|
||||||
enum class SelectionId {
|
|
||||||
CurrentLine = 0,
|
|
||||||
SelectedWord,
|
|
||||||
SearchedKeyword,
|
|
||||||
SearchedKeywordUnderCursor,
|
|
||||||
IncrementalSearchedKeyword,
|
|
||||||
TrailingSapce,
|
|
||||||
MaxSelection
|
|
||||||
};
|
|
||||||
|
|
||||||
class VEditConfig {
|
|
||||||
public:
|
|
||||||
VEditConfig() : m_tabStopWidth(0),
|
|
||||||
m_tabSpaces("\t"),
|
|
||||||
m_enableVimMode(false),
|
|
||||||
m_highlightWholeBlock(false),
|
|
||||||
m_lineDistanceHeight(0),
|
|
||||||
m_enableHeadingSequence(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void init(const QFontMetrics &p_metric,
|
|
||||||
bool p_enableHeadingSequence);
|
|
||||||
|
|
||||||
// Only update those configs which could be updated online.
|
|
||||||
void update(const QFontMetrics &p_metric);
|
|
||||||
|
|
||||||
// Width in pixels.
|
|
||||||
int m_tabStopWidth;
|
|
||||||
|
|
||||||
bool m_expandTab;
|
|
||||||
|
|
||||||
// The literal string for Tab. It is spaces if Tab is expanded.
|
|
||||||
QString m_tabSpaces;
|
|
||||||
|
|
||||||
bool m_enableVimMode;
|
|
||||||
|
|
||||||
// The background color of cursor line.
|
|
||||||
QColor m_cursorLineBg;
|
|
||||||
|
|
||||||
// Whether highlight a visual line or a whole block.
|
|
||||||
bool m_highlightWholeBlock;
|
|
||||||
|
|
||||||
// Line distance height in pixels.
|
|
||||||
int m_lineDistanceHeight;
|
|
||||||
|
|
||||||
// Whether enable auto heading sequence.
|
|
||||||
bool m_enableHeadingSequence;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LineNumberArea;
|
class LineNumberArea;
|
||||||
|
|
||||||
class VEdit : public QTextEdit
|
class VEdit : public QTextEdit
|
||||||
@ -98,7 +51,7 @@ public:
|
|||||||
// User has enter the content to search, but does not enter the "find" button yet.
|
// User has enter the content to search, but does not enter the "find" button yet.
|
||||||
bool peekText(const QString &p_text, uint p_options, bool p_forward = true);
|
bool peekText(const QString &p_text, uint p_options, bool p_forward = true);
|
||||||
|
|
||||||
// If @p_cursor is not now, set the position of @p_cursor instead of current
|
// If @p_cursor is not null, set the position of @p_cursor instead of current
|
||||||
// cursor.
|
// cursor.
|
||||||
bool findText(const QString &p_text, uint p_options, bool p_forward,
|
bool findText(const QString &p_text, uint p_options, bool p_forward,
|
||||||
QTextCursor *p_cursor = NULL,
|
QTextCursor *p_cursor = NULL,
|
||||||
|
44
src/veditconfig.cpp
Normal file
44
src/veditconfig.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "veditconfig.h"
|
||||||
|
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
void VEditConfig::init(const QFontMetrics &p_metric,
|
||||||
|
bool p_enableHeadingSequence)
|
||||||
|
{
|
||||||
|
update(p_metric);
|
||||||
|
|
||||||
|
// Init configs that do not support update later.
|
||||||
|
m_enableVimMode = g_config->getEnableVimMode();
|
||||||
|
|
||||||
|
if (g_config->getLineDistanceHeight() <= 0) {
|
||||||
|
m_lineDistanceHeight = 0;
|
||||||
|
} else {
|
||||||
|
m_lineDistanceHeight = g_config->getLineDistanceHeight() * VUtils::calculateScaleFactor();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_highlightWholeBlock = m_enableVimMode;
|
||||||
|
|
||||||
|
m_enableHeadingSequence = p_enableHeadingSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditConfig::update(const QFontMetrics &p_metric)
|
||||||
|
{
|
||||||
|
if (g_config->getTabStopWidth() > 0) {
|
||||||
|
m_tabStopWidth = g_config->getTabStopWidth() * p_metric.width(' ');
|
||||||
|
} else {
|
||||||
|
m_tabStopWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_expandTab = g_config->getIsExpandTab();
|
||||||
|
|
||||||
|
if (m_expandTab && (g_config->getTabStopWidth() > 0)) {
|
||||||
|
m_tabSpaces = QString(g_config->getTabStopWidth(), ' ');
|
||||||
|
} else {
|
||||||
|
m_tabSpaces = "\t";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cursorLineBg = QColor(g_config->getEditorCurrentLineBg());
|
||||||
|
}
|
48
src/veditconfig.h
Normal file
48
src/veditconfig.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef VEDITCONFIG_H
|
||||||
|
#define VEDITCONFIG_H
|
||||||
|
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QString>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
|
||||||
|
class VEditConfig {
|
||||||
|
public:
|
||||||
|
VEditConfig() : m_tabStopWidth(0),
|
||||||
|
m_tabSpaces("\t"),
|
||||||
|
m_enableVimMode(false),
|
||||||
|
m_highlightWholeBlock(false),
|
||||||
|
m_lineDistanceHeight(0),
|
||||||
|
m_enableHeadingSequence(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void init(const QFontMetrics &p_metric,
|
||||||
|
bool p_enableHeadingSequence);
|
||||||
|
|
||||||
|
// Only update those configs which could be updated online.
|
||||||
|
void update(const QFontMetrics &p_metric);
|
||||||
|
|
||||||
|
// Width in pixels.
|
||||||
|
int m_tabStopWidth;
|
||||||
|
|
||||||
|
bool m_expandTab;
|
||||||
|
|
||||||
|
// The literal string for Tab. It is spaces if Tab is expanded.
|
||||||
|
QString m_tabSpaces;
|
||||||
|
|
||||||
|
bool m_enableVimMode;
|
||||||
|
|
||||||
|
// The background color of cursor line.
|
||||||
|
QColor m_cursorLineBg;
|
||||||
|
|
||||||
|
// Whether highlight a visual line or a whole block.
|
||||||
|
bool m_highlightWholeBlock;
|
||||||
|
|
||||||
|
// Line distance height in pixels.
|
||||||
|
int m_lineDistanceHeight;
|
||||||
|
|
||||||
|
// Whether enable auto heading sequence.
|
||||||
|
bool m_enableHeadingSequence;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VEDITCONFIG_H
|
@ -1,18 +1,21 @@
|
|||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include "vedit.h"
|
#include "veditor.h"
|
||||||
#include "veditoperations.h"
|
#include "veditoperations.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
|
VEditOperations::VEditOperations(VEditor *p_editor, VFile *p_file)
|
||||||
: QObject(p_editor), m_editor(p_editor), m_file(p_file),
|
: QObject(p_editor->getEditor()),
|
||||||
m_editConfig(&p_editor->getConfig()), m_vim(NULL)
|
m_editor(p_editor),
|
||||||
|
m_file(p_file),
|
||||||
|
m_editConfig(&p_editor->getConfig()),
|
||||||
|
m_vim(NULL)
|
||||||
{
|
{
|
||||||
connect(m_editor, &VEdit::configUpdated,
|
connect(m_editor->object(), &VEditorObject::configUpdated,
|
||||||
this, &VEditOperations::handleEditConfigUpdated);
|
this, &VEditOperations::handleEditConfigUpdated);
|
||||||
|
|
||||||
if (m_editConfig->m_enableVimMode) {
|
if (m_editConfig->m_enableVimMode) {
|
||||||
@ -29,7 +32,7 @@ VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
|
|||||||
|
|
||||||
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
||||||
{
|
{
|
||||||
m_editor->insertPlainText(p_text);
|
m_editor->insertPlainTextW(p_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
VEditOperations::~VEditOperations()
|
VEditOperations::~VEditOperations()
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "utils/vvim.h"
|
#include "utils/vvim.h"
|
||||||
|
|
||||||
class VEdit;
|
class VEditor;
|
||||||
class VEditConfig;
|
class VEditConfig;
|
||||||
class QMimeData;
|
class QMimeData;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
@ -17,7 +17,7 @@ class VEditOperations: public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VEditOperations(VEdit *p_editor, VFile *p_file);
|
VEditOperations(VEditor *p_editor, VFile *p_file);
|
||||||
|
|
||||||
virtual ~VEditOperations();
|
virtual ~VEditOperations();
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
void insertTextAtCurPos(const QString &p_text);
|
void insertTextAtCurPos(const QString &p_text);
|
||||||
|
|
||||||
VEdit *m_editor;
|
VEditor *m_editor;
|
||||||
QPointer<VFile> m_file;
|
QPointer<VFile> m_file;
|
||||||
VEditConfig *m_editConfig;
|
VEditConfig *m_editConfig;
|
||||||
VVim *m_vim;
|
VVim *m_vim;
|
||||||
|
917
src/veditor.cpp
Normal file
917
src/veditor.cpp
Normal file
@ -0,0 +1,917 @@
|
|||||||
|
#include "veditor.h"
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
|
#include "veditoperations.h"
|
||||||
|
#include "dialog/vinsertlinkdialog.h"
|
||||||
|
#include "utils/vmetawordmanager.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
extern VMetaWordManager *g_mwMgr;
|
||||||
|
|
||||||
|
VEditor::VEditor(VFile *p_file, QWidget *p_editor)
|
||||||
|
: m_editor(p_editor),
|
||||||
|
m_object(new VEditorObject(this, p_editor)),
|
||||||
|
m_file(p_file),
|
||||||
|
m_editOps(nullptr),
|
||||||
|
m_document(nullptr),
|
||||||
|
m_enableInputMethod(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VEditor::~VEditor()
|
||||||
|
{
|
||||||
|
if (m_file && m_document) {
|
||||||
|
QObject::disconnect(m_document, &QTextDocument::modificationChanged,
|
||||||
|
(VFile *)m_file, &VFile::setModified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::init()
|
||||||
|
{
|
||||||
|
const int labelTimerInterval = 500;
|
||||||
|
const int extraSelectionHighlightTimer = 500;
|
||||||
|
const int labelSize = 64;
|
||||||
|
|
||||||
|
m_document = documentW();
|
||||||
|
|
||||||
|
m_selectedWordColor = QColor(g_config->getEditorSelectedWordBg());
|
||||||
|
m_searchedWordColor = QColor(g_config->getEditorSearchedWordBg());
|
||||||
|
m_searchedWordCursorColor = QColor(g_config->getEditorSearchedWordCursorBg());
|
||||||
|
m_incrementalSearchedWordColor = QColor(g_config->getEditorIncrementalSearchedWordBg());
|
||||||
|
m_trailingSpaceColor = QColor(g_config->getEditorTrailingSpaceBg());
|
||||||
|
|
||||||
|
QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
|
||||||
|
m_wrapLabel = new QLabel(m_editor);
|
||||||
|
m_wrapLabel->setPixmap(wrapPixmap.scaled(labelSize, labelSize));
|
||||||
|
m_wrapLabel->hide();
|
||||||
|
m_labelTimer = new QTimer(m_editor);
|
||||||
|
m_labelTimer->setSingleShot(true);
|
||||||
|
m_labelTimer->setInterval(labelTimerInterval);
|
||||||
|
QObject::connect(m_labelTimer, &QTimer::timeout,
|
||||||
|
m_object, &VEditorObject::labelTimerTimeout);
|
||||||
|
|
||||||
|
m_highlightTimer = new QTimer(m_editor);
|
||||||
|
m_highlightTimer->setSingleShot(true);
|
||||||
|
m_highlightTimer->setInterval(extraSelectionHighlightTimer);
|
||||||
|
QObject::connect(m_highlightTimer, &QTimer::timeout,
|
||||||
|
m_object, &VEditorObject::doHighlightExtraSelections);
|
||||||
|
|
||||||
|
m_extraSelections.resize((int)SelectionId::MaxSelection);
|
||||||
|
|
||||||
|
QObject::connect(m_document, &QTextDocument::modificationChanged,
|
||||||
|
(VFile *)m_file, &VFile::setModified);
|
||||||
|
|
||||||
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
m_config.init(QFontMetrics(m_editor->font()), false);
|
||||||
|
updateEditConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::labelTimerTimeout()
|
||||||
|
{
|
||||||
|
m_wrapLabel->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::doHighlightExtraSelections()
|
||||||
|
{
|
||||||
|
int nrExtra = m_extraSelections.size();
|
||||||
|
Q_ASSERT(nrExtra == (int)SelectionId::MaxSelection);
|
||||||
|
QList<QTextEdit::ExtraSelection> extraSelects;
|
||||||
|
for (int i = 0; i < nrExtra; ++i) {
|
||||||
|
extraSelects.append(m_extraSelections[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setExtraSelectionsW(extraSelects);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::updateEditConfig()
|
||||||
|
{
|
||||||
|
m_config.update(QFontMetrics(m_editor->font()));
|
||||||
|
|
||||||
|
if (m_config.m_tabStopWidth > 0) {
|
||||||
|
setTabStopWidthW(m_config.m_tabStopWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit m_object->configUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightOnCursorPositionChanged()
|
||||||
|
{
|
||||||
|
static QTextCursor lastCursor;
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
if (lastCursor.isNull() || cursor.blockNumber() != lastCursor.blockNumber()) {
|
||||||
|
highlightCurrentLine();
|
||||||
|
highlightTrailingSpace();
|
||||||
|
} else {
|
||||||
|
// Judge whether we have trailing space at current line.
|
||||||
|
QString text = cursor.block().text();
|
||||||
|
if (text.rbegin()->isSpace()) {
|
||||||
|
highlightTrailingSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle word-wrap in one block.
|
||||||
|
// Highlight current line if in different visual line.
|
||||||
|
if ((lastCursor.positionInBlock() - lastCursor.columnNumber()) !=
|
||||||
|
(cursor.positionInBlock() - cursor.columnNumber())) {
|
||||||
|
highlightCurrentLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCursor = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightCurrentLine()
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::CurrentLine];
|
||||||
|
if (g_config->getHighlightCursorLine()) {
|
||||||
|
// Need to highlight current line.
|
||||||
|
selects.clear();
|
||||||
|
|
||||||
|
// A long block maybe splited into multiple visual lines.
|
||||||
|
QTextEdit::ExtraSelection select;
|
||||||
|
select.format.setBackground(m_config.m_cursorLineBg);
|
||||||
|
select.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
if (m_config.m_highlightWholeBlock) {
|
||||||
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor, 1);
|
||||||
|
QTextBlock block = cursor.block();
|
||||||
|
int blockEnd = block.position() + block.length();
|
||||||
|
int pos = -1;
|
||||||
|
while (cursor.position() < blockEnd && pos != cursor.position()) {
|
||||||
|
QTextEdit::ExtraSelection newSelect = select;
|
||||||
|
newSelect.cursor = cursor;
|
||||||
|
selects.append(newSelect);
|
||||||
|
|
||||||
|
pos = cursor.position();
|
||||||
|
cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cursor.clearSelection();
|
||||||
|
select.cursor = cursor;
|
||||||
|
selects.append(select);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Need to clear current line highlight.
|
||||||
|
if (selects.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not highlight trailing spaces with current cursor right behind.
|
||||||
|
static void trailingSpaceFilter(VEditor *p_editor, QList<QTextEdit::ExtraSelection> &p_result)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = p_editor->textCursorW();
|
||||||
|
if (!cursor.atBlockEnd()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cursorPos = cursor.position();
|
||||||
|
for (auto it = p_result.begin(); it != p_result.end(); ++it) {
|
||||||
|
if (it->cursor.selectionEnd() == cursorPos) {
|
||||||
|
p_result.erase(it);
|
||||||
|
|
||||||
|
// There will be only one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightTrailingSpace()
|
||||||
|
{
|
||||||
|
if (!g_config->getEnableTrailingSpaceHighlight()) {
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::TrailingSapce];
|
||||||
|
if (!selects.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setBackground(m_trailingSpaceColor);
|
||||||
|
QString text("\\s+$");
|
||||||
|
highlightTextAll(text,
|
||||||
|
FindOption::RegularExpression,
|
||||||
|
SelectionId::TrailingSapce,
|
||||||
|
format,
|
||||||
|
trailingSpaceFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightExtraSelections(bool p_now)
|
||||||
|
{
|
||||||
|
m_highlightTimer->stop();
|
||||||
|
if (p_now) {
|
||||||
|
doHighlightExtraSelections();
|
||||||
|
} else {
|
||||||
|
m_highlightTimer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
SelectionId p_id,
|
||||||
|
QTextCharFormat p_format,
|
||||||
|
void (*p_filter)(VEditor *,
|
||||||
|
QList<QTextEdit::ExtraSelection> &))
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)p_id];
|
||||||
|
if (!p_text.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
|
||||||
|
QList<QTextCursor> occurs = findTextAll(p_text, p_options);
|
||||||
|
for (int i = 0; i < occurs.size(); ++i) {
|
||||||
|
QTextEdit::ExtraSelection select;
|
||||||
|
select.format = p_format;
|
||||||
|
select.cursor = occurs[i];
|
||||||
|
selects.append(select);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selects.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_filter) {
|
||||||
|
p_filter(this, selects);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightExtraSelections();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTextCursor> VEditor::findTextAll(const QString &p_text, uint p_options)
|
||||||
|
{
|
||||||
|
QList<QTextCursor> results;
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options
|
||||||
|
QTextDocument::FindFlags findFlags;
|
||||||
|
bool caseSensitive = false;
|
||||||
|
if (p_options & FindOption::CaseSensitive) {
|
||||||
|
findFlags |= QTextDocument::FindCaseSensitively;
|
||||||
|
caseSensitive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_options & FindOption::WholeWordOnly) {
|
||||||
|
findFlags |= QTextDocument::FindWholeWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use regular expression
|
||||||
|
bool useRegExp = false;
|
||||||
|
QRegExp exp;
|
||||||
|
if (p_options & FindOption::RegularExpression) {
|
||||||
|
useRegExp = true;
|
||||||
|
exp = QRegExp(p_text,
|
||||||
|
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
int startPos = 0;
|
||||||
|
QTextCursor cursor;
|
||||||
|
while (true) {
|
||||||
|
if (useRegExp) {
|
||||||
|
cursor = m_document->find(exp, startPos, findFlags);
|
||||||
|
} else {
|
||||||
|
cursor = m_document->find(p_text, startPos, findFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
results.append(cursor);
|
||||||
|
startPos = cursor.selectionEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightSelectedWord()
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
|
||||||
|
if (!g_config->getHighlightSelectedWord()) {
|
||||||
|
if (!selects.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = textCursorW().selectedText().trimmed();
|
||||||
|
if (text.isEmpty() || wordInSearchedSelection(text)) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setBackground(m_selectedWordColor);
|
||||||
|
highlightTextAll(text,
|
||||||
|
FindOption::CaseSensitive,
|
||||||
|
SelectionId::SelectedWord,
|
||||||
|
format);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::wordInSearchedSelection(const QString &p_text)
|
||||||
|
{
|
||||||
|
QString text = p_text.trimmed();
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
|
||||||
|
for (int i = 0; i < selects.size(); ++i) {
|
||||||
|
QString searchedWord = selects[i].cursor.selectedText();
|
||||||
|
if (text == searchedWord.trimmed()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::isModified() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_file ? (m_file->isModified() == m_document->isModified())
|
||||||
|
: true);
|
||||||
|
return m_document->isModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::setModified(bool p_modified)
|
||||||
|
{
|
||||||
|
m_document->setModified(p_modified);
|
||||||
|
if (m_file) {
|
||||||
|
m_file->setModified(p_modified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::insertImage()
|
||||||
|
{
|
||||||
|
if (m_editOps) {
|
||||||
|
m_editOps->insertImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::insertLink()
|
||||||
|
{
|
||||||
|
if (!m_editOps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
QString linkText, linkUrl;
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
if (cursor.hasSelection()) {
|
||||||
|
text = VEditUtils::selectedText(cursor).trimmed();
|
||||||
|
// Only pure space is accepted.
|
||||||
|
QRegExp reg("[\\S ]*");
|
||||||
|
if (reg.exactMatch(text)) {
|
||||||
|
QUrl url = QUrl::fromUserInput(text,
|
||||||
|
m_file->fetchBasePath());
|
||||||
|
QRegExp urlReg("[\\.\\\\/]");
|
||||||
|
if (url.isValid()
|
||||||
|
&& text.contains(urlReg)) {
|
||||||
|
// Url.
|
||||||
|
linkUrl = text;
|
||||||
|
} else {
|
||||||
|
// Text.
|
||||||
|
linkText = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VInsertLinkDialog dialog(QObject::tr("Insert Link"),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
linkText,
|
||||||
|
linkUrl,
|
||||||
|
m_editor);
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
linkText = dialog.getLinkText();
|
||||||
|
linkUrl = dialog.getLinkUrl();
|
||||||
|
Q_ASSERT(!linkText.isEmpty() && !linkUrl.isEmpty());
|
||||||
|
|
||||||
|
m_editOps->insertLink(linkText, linkUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::peekText(const QString &p_text, uint p_options, bool p_forward)
|
||||||
|
{
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
makeBlockVisible(m_document->findBlock(textCursorW().selectionStart()));
|
||||||
|
highlightIncrementalSearchedWord(QTextCursor());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wrapped = false;
|
||||||
|
QTextCursor retCursor;
|
||||||
|
bool found = findTextHelper(p_text,
|
||||||
|
p_options,
|
||||||
|
p_forward,
|
||||||
|
p_forward ? textCursorW().position() + 1
|
||||||
|
: textCursorW().position(),
|
||||||
|
wrapped,
|
||||||
|
retCursor);
|
||||||
|
if (found) {
|
||||||
|
makeBlockVisible(m_document->findBlock(retCursor.selectionStart()));
|
||||||
|
highlightIncrementalSearchedWord(retCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::findText(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
QTextCursor *p_cursor,
|
||||||
|
QTextCursor::MoveMode p_moveMode)
|
||||||
|
{
|
||||||
|
clearIncrementalSearchedWordHighlight();
|
||||||
|
|
||||||
|
if (p_text.isEmpty()) {
|
||||||
|
clearSearchedWordHighlight();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
bool wrapped = false;
|
||||||
|
QTextCursor retCursor;
|
||||||
|
int matches = 0;
|
||||||
|
int start = p_forward ? cursor.position() + 1 : cursor.position();
|
||||||
|
if (p_cursor) {
|
||||||
|
start = p_forward ? p_cursor->position() + 1 : p_cursor->position();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = findTextHelper(p_text, p_options, p_forward, start,
|
||||||
|
wrapped, retCursor);
|
||||||
|
if (found) {
|
||||||
|
Q_ASSERT(!retCursor.isNull());
|
||||||
|
if (wrapped) {
|
||||||
|
showWrapLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_cursor) {
|
||||||
|
p_cursor->setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
|
} else {
|
||||||
|
cursor.setPosition(retCursor.selectionStart(), p_moveMode);
|
||||||
|
setTextCursorW(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightSearchedWord(p_text, p_options);
|
||||||
|
highlightSearchedWordUnderCursor(retCursor);
|
||||||
|
matches = m_extraSelections[(int)SelectionId::SearchedKeyword].size();
|
||||||
|
} else {
|
||||||
|
clearSearchedWordHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matches == 0) {
|
||||||
|
emit m_object->statusMessage(QObject::tr("Found no match"));
|
||||||
|
} else {
|
||||||
|
emit m_object->statusMessage(QObject::tr("Found %1 %2").arg(matches)
|
||||||
|
.arg(matches > 1 ? QObject::tr("matches")
|
||||||
|
: QObject::tr("match")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
|
||||||
|
if (!g_config->getHighlightSearchedWord() || !p_cursor.hasSelection()) {
|
||||||
|
if (!selects.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
QTextEdit::ExtraSelection select;
|
||||||
|
select.format.setBackground(m_incrementalSearchedWordColor);
|
||||||
|
select.cursor = p_cursor;
|
||||||
|
selects.append(select);
|
||||||
|
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use QPlainTextEdit::find() instead of QTextDocument::find() because the later has
|
||||||
|
// bugs in searching backward.
|
||||||
|
bool VEditor::findTextHelper(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
int p_start,
|
||||||
|
bool &p_wrapped,
|
||||||
|
QTextCursor &p_cursor)
|
||||||
|
{
|
||||||
|
p_wrapped = false;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
// Options
|
||||||
|
QTextDocument::FindFlags findFlags;
|
||||||
|
bool caseSensitive = false;
|
||||||
|
if (p_options & FindOption::CaseSensitive) {
|
||||||
|
findFlags |= QTextDocument::FindCaseSensitively;
|
||||||
|
caseSensitive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_options & FindOption::WholeWordOnly) {
|
||||||
|
findFlags |= QTextDocument::FindWholeWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p_forward) {
|
||||||
|
findFlags |= QTextDocument::FindBackward;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use regular expression
|
||||||
|
bool useRegExp = false;
|
||||||
|
QRegExp exp;
|
||||||
|
if (p_options & FindOption::RegularExpression) {
|
||||||
|
useRegExp = true;
|
||||||
|
exp = QRegExp(p_text,
|
||||||
|
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store current state of the cursor.
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
if (cursor.position() != p_start) {
|
||||||
|
if (p_start < 0) {
|
||||||
|
p_start = 0;
|
||||||
|
} else if (p_start > m_document->characterCount()) {
|
||||||
|
p_start = m_document->characterCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor startCursor = cursor;
|
||||||
|
startCursor.setPosition(p_start);
|
||||||
|
setTextCursorW(startCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!found) {
|
||||||
|
if (useRegExp) {
|
||||||
|
found = findW(exp, findFlags);
|
||||||
|
} else {
|
||||||
|
found = findW(p_text, findFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_wrapped) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
// Wrap to the other end of the document to search again.
|
||||||
|
p_wrapped = true;
|
||||||
|
QTextCursor wrapCursor = textCursorW();
|
||||||
|
if (p_forward) {
|
||||||
|
wrapCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
||||||
|
} else {
|
||||||
|
wrapCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTextCursorW(wrapCursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
p_cursor = textCursorW();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the original cursor.
|
||||||
|
setTextCursorW(cursor);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::clearIncrementalSearchedWordHighlight(bool p_now)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
|
||||||
|
if (selects.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(p_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::clearSearchedWordHighlight()
|
||||||
|
{
|
||||||
|
clearIncrementalSearchedWordHighlight(false);
|
||||||
|
clearSearchedWordUnderCursorHighlight(false);
|
||||||
|
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
|
||||||
|
if (selects.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::clearSearchedWordUnderCursorHighlight(bool p_now)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeywordUnderCursor];
|
||||||
|
if (selects.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(p_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::showWrapLabel()
|
||||||
|
{
|
||||||
|
int labelW = m_wrapLabel->width();
|
||||||
|
int labelH = m_wrapLabel->height();
|
||||||
|
int x = (m_editor->width() - labelW) / 2;
|
||||||
|
int y = (m_editor->height() - labelH) / 2;
|
||||||
|
if (x < 0) {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < 0) {
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wrapLabel->move(x, y);
|
||||||
|
m_wrapLabel->show();
|
||||||
|
m_labelTimer->stop();
|
||||||
|
m_labelTimer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightSearchedWord(const QString &p_text, uint p_options)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
|
||||||
|
if (!g_config->getHighlightSearchedWord() || p_text.isEmpty()) {
|
||||||
|
if (!selects.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCharFormat format;
|
||||||
|
format.setBackground(m_searchedWordColor);
|
||||||
|
highlightTextAll(p_text, p_options, SelectionId::SearchedKeyword, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::highlightSearchedWordUnderCursor(const QTextCursor &p_cursor)
|
||||||
|
{
|
||||||
|
QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeywordUnderCursor];
|
||||||
|
if (!p_cursor.hasSelection()) {
|
||||||
|
if (!selects.isEmpty()) {
|
||||||
|
selects.clear();
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selects.clear();
|
||||||
|
QTextEdit::ExtraSelection select;
|
||||||
|
select.format.setBackground(m_searchedWordCursorColor);
|
||||||
|
select.cursor = p_cursor;
|
||||||
|
selects.append(select);
|
||||||
|
|
||||||
|
highlightExtraSelections(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::replaceText(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
const QString &p_replaceText,
|
||||||
|
bool p_findNext)
|
||||||
|
{
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
bool wrapped = false;
|
||||||
|
QTextCursor retCursor;
|
||||||
|
bool found = findTextHelper(p_text,
|
||||||
|
p_options, true,
|
||||||
|
cursor.position(),
|
||||||
|
wrapped,
|
||||||
|
retCursor);
|
||||||
|
if (found) {
|
||||||
|
if (retCursor.selectionStart() == cursor.position()) {
|
||||||
|
// Matched.
|
||||||
|
retCursor.beginEditBlock();
|
||||||
|
retCursor.insertText(p_replaceText);
|
||||||
|
retCursor.endEditBlock();
|
||||||
|
setTextCursorW(retCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_findNext) {
|
||||||
|
findText(p_text, p_options, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::replaceTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
const QString &p_replaceText)
|
||||||
|
{
|
||||||
|
// Replace from the start to the end and restore the cursor.
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
int nrReplaces = 0;
|
||||||
|
QTextCursor tmpCursor = cursor;
|
||||||
|
tmpCursor.setPosition(0);
|
||||||
|
setTextCursorW(tmpCursor);
|
||||||
|
int start = tmpCursor.position();
|
||||||
|
while (true) {
|
||||||
|
bool wrapped = false;
|
||||||
|
QTextCursor retCursor;
|
||||||
|
bool found = findTextHelper(p_text,
|
||||||
|
p_options,
|
||||||
|
true,
|
||||||
|
start,
|
||||||
|
wrapped,
|
||||||
|
retCursor);
|
||||||
|
if (!found) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (wrapped) {
|
||||||
|
// Wrap back.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrReplaces++;
|
||||||
|
retCursor.beginEditBlock();
|
||||||
|
retCursor.insertText(p_replaceText);
|
||||||
|
retCursor.endEditBlock();
|
||||||
|
setTextCursorW(retCursor);
|
||||||
|
start = retCursor.position();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore cursor position.
|
||||||
|
cursor.clearSelection();
|
||||||
|
setTextCursorW(cursor);
|
||||||
|
qDebug() << "replace all" << nrReplaces << "occurences";
|
||||||
|
|
||||||
|
emit m_object->statusMessage(QObject::tr("Replace %1 %2").arg(nrReplaces)
|
||||||
|
.arg(nrReplaces > 1 ? QObject::tr("occurences")
|
||||||
|
: QObject::tr("occurence")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::evaluateMagicWords()
|
||||||
|
{
|
||||||
|
QString text;
|
||||||
|
QTextCursor cursor = textCursorW();
|
||||||
|
if (!cursor.hasSelection()) {
|
||||||
|
// Get the WORD in current cursor.
|
||||||
|
int start, end;
|
||||||
|
VEditUtils::findCurrentWORD(cursor, start, end);
|
||||||
|
|
||||||
|
if (start == end) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
cursor.setPosition(start);
|
||||||
|
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text = VEditUtils::selectedText(cursor);
|
||||||
|
Q_ASSERT(!text.isEmpty());
|
||||||
|
QString evaText = g_mwMgr->evaluate(text);
|
||||||
|
if (text != evaText) {
|
||||||
|
qDebug() << "evaluateMagicWords" << text << evaText;
|
||||||
|
|
||||||
|
cursor.insertText(evaText);
|
||||||
|
|
||||||
|
if (m_editOps) {
|
||||||
|
m_editOps->setVimMode(VimMode::Insert);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTextCursorW(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::setReadOnlyAndHighlightCurrentLine(bool p_readonly)
|
||||||
|
{
|
||||||
|
setReadOnlyW(p_readonly);
|
||||||
|
highlightCurrentLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::handleMousePressEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
if (p_event->button() == Qt::LeftButton
|
||||||
|
&& p_event->modifiers() == Qt::ControlModifier
|
||||||
|
&& !textCursorW().hasSelection()) {
|
||||||
|
m_oriMouseX = p_event->x();
|
||||||
|
m_oriMouseY = p_event->y();
|
||||||
|
m_readyToScroll = true;
|
||||||
|
m_mouseMoveScrolled = false;
|
||||||
|
p_event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_readyToScroll = false;
|
||||||
|
m_mouseMoveScrolled = false;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::handleMouseReleaseEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
if (m_mouseMoveScrolled || m_readyToScroll) {
|
||||||
|
viewportW()->setCursor(Qt::IBeamCursor);
|
||||||
|
m_readyToScroll = false;
|
||||||
|
m_mouseMoveScrolled = false;
|
||||||
|
p_event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_readyToScroll = false;
|
||||||
|
m_mouseMoveScrolled = false;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::handleMouseMoveEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
const int threshold = 5;
|
||||||
|
|
||||||
|
if (m_readyToScroll) {
|
||||||
|
int deltaX = p_event->x() - m_oriMouseX;
|
||||||
|
int deltaY = p_event->y() - m_oriMouseY;
|
||||||
|
|
||||||
|
if (qAbs(deltaX) >= threshold || qAbs(deltaY) >= threshold) {
|
||||||
|
m_oriMouseX = p_event->x();
|
||||||
|
m_oriMouseY = p_event->y();
|
||||||
|
|
||||||
|
if (!m_mouseMoveScrolled) {
|
||||||
|
m_mouseMoveScrolled = true;
|
||||||
|
viewportW()->setCursor(Qt::SizeAllCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar *verBar = verticalScrollBarW();
|
||||||
|
QScrollBar *horBar = horizontalScrollBarW();
|
||||||
|
if (verBar->isVisible()) {
|
||||||
|
verBar->setValue(verBar->value() - deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horBar->isVisible()) {
|
||||||
|
horBar->setValue(horBar->value() - deltaX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p_event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::requestUpdateVimStatus()
|
||||||
|
{
|
||||||
|
if (m_editOps) {
|
||||||
|
m_editOps->requestUpdateVimStatus();
|
||||||
|
} else {
|
||||||
|
emit m_object->vimStatusUpdated(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VEditor::handleInputMethodQuery(Qt::InputMethodQuery p_query,
|
||||||
|
QVariant &p_var) const
|
||||||
|
{
|
||||||
|
if (p_query == Qt::ImEnabled) {
|
||||||
|
p_var = m_enableInputMethod;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::setInputMethodEnabled(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_enableInputMethod != p_enabled) {
|
||||||
|
m_enableInputMethod = p_enabled;
|
||||||
|
|
||||||
|
QInputMethod *im = QGuiApplication::inputMethod();
|
||||||
|
im->reset();
|
||||||
|
|
||||||
|
// Ask input method to query current state, which will call inputMethodQuery().
|
||||||
|
im->update(Qt::ImEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::decorateText(TextDecoration p_decoration)
|
||||||
|
{
|
||||||
|
if (m_editOps) {
|
||||||
|
m_editOps->decorateText(p_decoration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditor::updateConfig()
|
||||||
|
{
|
||||||
|
updateEditConfig();
|
||||||
|
}
|
372
src/veditor.h
Normal file
372
src/veditor.h
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
#ifndef VEDITOR_H
|
||||||
|
#define VEDITOR_H
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QVector>
|
||||||
|
#include <QList>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
|
#include "veditconfig.h"
|
||||||
|
#include "vfile.h"
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
class VEditorObject;
|
||||||
|
class VEditOperations;
|
||||||
|
class QTimer;
|
||||||
|
class QLabel;
|
||||||
|
class VVim;
|
||||||
|
|
||||||
|
|
||||||
|
enum class SelectionId {
|
||||||
|
CurrentLine = 0,
|
||||||
|
SelectedWord,
|
||||||
|
SearchedKeyword,
|
||||||
|
SearchedKeywordUnderCursor,
|
||||||
|
IncrementalSearchedKeyword,
|
||||||
|
TrailingSapce,
|
||||||
|
MaxSelection
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Abstract class for an edit.
|
||||||
|
// Should inherit this class as well as QPlainTextEdit or QTextEdit.
|
||||||
|
// Will replace VEdit eventually.
|
||||||
|
class VEditor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit VEditor(VFile *p_file, QWidget *p_editor);
|
||||||
|
|
||||||
|
virtual ~VEditor();
|
||||||
|
|
||||||
|
void highlightCurrentLine();
|
||||||
|
|
||||||
|
virtual void beginEdit() = 0;
|
||||||
|
|
||||||
|
virtual void endEdit() = 0;
|
||||||
|
|
||||||
|
// Save buffer content to VFile.
|
||||||
|
virtual void saveFile() = 0;
|
||||||
|
|
||||||
|
virtual void reloadFile() = 0;
|
||||||
|
|
||||||
|
virtual bool scrollToBlock(int p_blockNumber) = 0;
|
||||||
|
|
||||||
|
bool isModified() const;
|
||||||
|
|
||||||
|
void setModified(bool p_modified);
|
||||||
|
|
||||||
|
// User requests to insert an image.
|
||||||
|
void insertImage();
|
||||||
|
|
||||||
|
// User requests to insert a link.
|
||||||
|
void insertLink();
|
||||||
|
|
||||||
|
// Used for incremental search.
|
||||||
|
// User has enter the content to search, but does not enter the "find" button yet.
|
||||||
|
bool peekText(const QString &p_text, uint p_options, bool p_forward = true);
|
||||||
|
|
||||||
|
// If @p_cursor is not null, set the position of @p_cursor instead of current
|
||||||
|
// cursor.
|
||||||
|
bool findText(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
QTextCursor *p_cursor = nullptr,
|
||||||
|
QTextCursor::MoveMode p_moveMode = QTextCursor::MoveAnchor);
|
||||||
|
|
||||||
|
void replaceText(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
const QString &p_replaceText,
|
||||||
|
bool p_findNext);
|
||||||
|
|
||||||
|
void replaceTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
const QString &p_replaceText);
|
||||||
|
|
||||||
|
// Scroll the content to make @p_block visible.
|
||||||
|
// If the @p_block is too long to hold in one page, just let it occupy the
|
||||||
|
// whole page.
|
||||||
|
// Will not change current cursor.
|
||||||
|
virtual void makeBlockVisible(const QTextBlock &p_block) = 0;
|
||||||
|
|
||||||
|
// Clear IncrementalSearchedKeyword highlight.
|
||||||
|
void clearIncrementalSearchedWordHighlight(bool p_now = true);
|
||||||
|
|
||||||
|
// Clear SearchedKeyword highlight.
|
||||||
|
void clearSearchedWordHighlight();
|
||||||
|
|
||||||
|
// Clear SearchedKeywordUnderCursor Highlight.
|
||||||
|
void clearSearchedWordUnderCursorHighlight(bool p_now = true);
|
||||||
|
|
||||||
|
// Evaluate selected text or cursor word as magic words.
|
||||||
|
void evaluateMagicWords();
|
||||||
|
|
||||||
|
VFile *getFile() const;
|
||||||
|
|
||||||
|
VEditConfig &getConfig();
|
||||||
|
|
||||||
|
// Request to update Vim status.
|
||||||
|
void requestUpdateVimStatus();
|
||||||
|
|
||||||
|
// Jump to a title.
|
||||||
|
// @p_forward: jump forward or backward.
|
||||||
|
// @p_relativeLevel: 0 for the same level as current header;
|
||||||
|
// negative value for upper level;
|
||||||
|
// positive value is ignored.
|
||||||
|
// Returns true if the jump succeeded.
|
||||||
|
virtual bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) = 0;
|
||||||
|
|
||||||
|
void setInputMethodEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
// Insert decoration markers or decorate selected text.
|
||||||
|
void decorateText(TextDecoration p_decoration);
|
||||||
|
|
||||||
|
virtual bool isBlockVisible(const QTextBlock &p_block) = 0;
|
||||||
|
|
||||||
|
VEditorObject *object() const;
|
||||||
|
|
||||||
|
QWidget *getEditor() const;
|
||||||
|
|
||||||
|
// Scroll block @p_blockNum into the visual window.
|
||||||
|
// @p_dest is the position of the window: 0 for top, 1 for center, 2 for bottom.
|
||||||
|
// @p_blockNum is based on 0.
|
||||||
|
// Will set the cursor to the block.
|
||||||
|
virtual void scrollBlockInPage(int p_blockNum, int p_dest) = 0;
|
||||||
|
|
||||||
|
// Update config according to global configurations.
|
||||||
|
virtual void updateConfig();
|
||||||
|
|
||||||
|
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
||||||
|
// Ends with W to distinguish it from the original interfaces.
|
||||||
|
public:
|
||||||
|
virtual void setExtraSelectionsW(const QList<QTextEdit::ExtraSelection> &p_selections) = 0;
|
||||||
|
|
||||||
|
virtual QTextDocument *documentW() const = 0;
|
||||||
|
|
||||||
|
virtual void setTabStopWidthW(int p_width) = 0;
|
||||||
|
|
||||||
|
virtual QTextCursor textCursorW() const = 0;
|
||||||
|
|
||||||
|
virtual void setTextCursorW(const QTextCursor &p_cursor) = 0;
|
||||||
|
|
||||||
|
virtual void moveCursorW(QTextCursor::MoveOperation p_operation,
|
||||||
|
QTextCursor::MoveMode p_mode = QTextCursor::MoveAnchor) = 0;
|
||||||
|
|
||||||
|
virtual QScrollBar *verticalScrollBarW() const = 0;
|
||||||
|
|
||||||
|
virtual QScrollBar *horizontalScrollBarW() const = 0;
|
||||||
|
|
||||||
|
virtual bool findW(const QString &p_exp,
|
||||||
|
QTextDocument::FindFlags p_options = QTextDocument::FindFlags()) = 0;
|
||||||
|
|
||||||
|
virtual bool findW(const QRegExp &p_exp,
|
||||||
|
QTextDocument::FindFlags p_options = QTextDocument::FindFlags()) = 0;
|
||||||
|
|
||||||
|
virtual void setReadOnlyW(bool p_ro) = 0;
|
||||||
|
|
||||||
|
virtual QWidget *viewportW() const = 0;
|
||||||
|
|
||||||
|
virtual void insertPlainTextW(const QString &p_text) = 0;
|
||||||
|
|
||||||
|
virtual void undoW() = 0;
|
||||||
|
|
||||||
|
virtual void redoW() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void init();
|
||||||
|
|
||||||
|
virtual void updateFontAndPalette() = 0;
|
||||||
|
|
||||||
|
// Update m_config according to VConfigManager.
|
||||||
|
void updateEditConfig();
|
||||||
|
|
||||||
|
// Do some highlight on cursor position changed.
|
||||||
|
void highlightOnCursorPositionChanged();
|
||||||
|
|
||||||
|
// Highlight selected text.
|
||||||
|
void highlightSelectedWord();
|
||||||
|
|
||||||
|
bool wordInSearchedSelection(const QString &p_text);
|
||||||
|
|
||||||
|
// Set read-only property and highlight current line.
|
||||||
|
void setReadOnlyAndHighlightCurrentLine(bool p_readonly);
|
||||||
|
|
||||||
|
// Handle the mouse press event of m_editor.
|
||||||
|
// Returns true if no further process is needed.
|
||||||
|
bool handleMousePressEvent(QMouseEvent *p_event);
|
||||||
|
|
||||||
|
bool handleMouseReleaseEvent(QMouseEvent *p_event);
|
||||||
|
|
||||||
|
bool handleMouseMoveEvent(QMouseEvent *p_event);
|
||||||
|
|
||||||
|
bool handleInputMethodQuery(Qt::InputMethodQuery p_query,
|
||||||
|
QVariant &p_var) const;
|
||||||
|
|
||||||
|
QWidget *m_editor;
|
||||||
|
|
||||||
|
VEditorObject *m_object;
|
||||||
|
|
||||||
|
QPointer<VFile> m_file;
|
||||||
|
|
||||||
|
VEditOperations *m_editOps;
|
||||||
|
|
||||||
|
VEditConfig m_config;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class VEditorObject;
|
||||||
|
|
||||||
|
void highlightTrailingSpace();
|
||||||
|
|
||||||
|
// Trigger the timer to request highlight.
|
||||||
|
// If @p_now is true, stop the timer and highlight immediately.
|
||||||
|
void highlightExtraSelections(bool p_now = false);
|
||||||
|
|
||||||
|
// @p_fileter: a function to filter out highlight results.
|
||||||
|
void highlightTextAll(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
SelectionId p_id,
|
||||||
|
QTextCharFormat p_format,
|
||||||
|
void (*p_filter)(VEditor *,
|
||||||
|
QList<QTextEdit::ExtraSelection> &) = NULL);
|
||||||
|
|
||||||
|
// Find all the occurences of @p_text.
|
||||||
|
QList<QTextCursor> findTextAll(const QString &p_text, uint p_options);
|
||||||
|
|
||||||
|
// Highlight @p_cursor as the incremental searched keyword.
|
||||||
|
void highlightIncrementalSearchedWord(const QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
// Find @p_text in the document starting from @p_start.
|
||||||
|
// Returns true if @p_text is found and set @p_cursor to indicate
|
||||||
|
// the position.
|
||||||
|
// Will NOT change current cursor.
|
||||||
|
bool findTextHelper(const QString &p_text,
|
||||||
|
uint p_options,
|
||||||
|
bool p_forward,
|
||||||
|
int p_start,
|
||||||
|
bool &p_wrapped,
|
||||||
|
QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
void showWrapLabel();
|
||||||
|
|
||||||
|
void highlightSearchedWord(const QString &p_text, uint p_options);
|
||||||
|
|
||||||
|
// Highlight @p_cursor as the searched keyword under cursor.
|
||||||
|
void highlightSearchedWordUnderCursor(const QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
QLabel *m_wrapLabel;
|
||||||
|
QTimer *m_labelTimer;
|
||||||
|
|
||||||
|
QTextDocument *m_document;
|
||||||
|
|
||||||
|
// doHighlightExtraSelections() will highlight these selections.
|
||||||
|
// Selections are indexed by SelectionId.
|
||||||
|
QVector<QList<QTextEdit::ExtraSelection> > m_extraSelections;
|
||||||
|
|
||||||
|
QColor m_selectedWordColor;
|
||||||
|
QColor m_searchedWordColor;
|
||||||
|
QColor m_searchedWordCursorColor;
|
||||||
|
QColor m_incrementalSearchedWordColor;
|
||||||
|
QColor m_trailingSpaceColor;
|
||||||
|
|
||||||
|
// Timer for extra selections highlight.
|
||||||
|
QTimer *m_highlightTimer;
|
||||||
|
|
||||||
|
bool m_readyToScroll;
|
||||||
|
bool m_mouseMoveScrolled;
|
||||||
|
int m_oriMouseX;
|
||||||
|
int m_oriMouseY;
|
||||||
|
|
||||||
|
// Whether enable input method.
|
||||||
|
bool m_enableInputMethod;
|
||||||
|
|
||||||
|
// Functions for private slots.
|
||||||
|
private:
|
||||||
|
void labelTimerTimeout();
|
||||||
|
|
||||||
|
// Do the real work to highlight extra selections.
|
||||||
|
void doHighlightExtraSelections();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Since one class could not inherit QObject multiple times, we use this class
|
||||||
|
// for VEditor to signal/slot.
|
||||||
|
class VEditorObject : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VEditorObject(VEditor *p_editor, QObject *p_parent = nullptr)
|
||||||
|
: QObject(p_parent), m_editor(p_editor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emit when editor config has been updated.
|
||||||
|
void configUpdated();
|
||||||
|
|
||||||
|
// Emit when want to show message in status bar.
|
||||||
|
void statusMessage(const QString &p_msg);
|
||||||
|
|
||||||
|
// Request VEditTab to save and exit edit mode.
|
||||||
|
void saveAndRead();
|
||||||
|
|
||||||
|
// Request VEditTab to discard and exit edit mode.
|
||||||
|
void discardAndRead();
|
||||||
|
|
||||||
|
// Request VEditTab to edit current note.
|
||||||
|
void editNote();
|
||||||
|
|
||||||
|
// Request VEditTab to save this file.
|
||||||
|
void saveNote();
|
||||||
|
|
||||||
|
// Selection changed by mouse.
|
||||||
|
void selectionChangedByMouse(bool p_hasSelection);
|
||||||
|
|
||||||
|
// Emit when Vim status updated.
|
||||||
|
void vimStatusUpdated(const VVim *p_vim);
|
||||||
|
|
||||||
|
// Emit when all initialization is ready.
|
||||||
|
void ready();
|
||||||
|
|
||||||
|
// Request the edit tab to close find and replace dialog.
|
||||||
|
void requestCloseFindReplaceDialog();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// Timer for find-wrap label.
|
||||||
|
void labelTimerTimeout()
|
||||||
|
{
|
||||||
|
m_editor->labelTimerTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the real work to highlight extra selections.
|
||||||
|
void doHighlightExtraSelections()
|
||||||
|
{
|
||||||
|
m_editor->doHighlightExtraSelections();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class VEditor;
|
||||||
|
|
||||||
|
VEditor *m_editor;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline VFile *VEditor::getFile() const
|
||||||
|
{
|
||||||
|
return m_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VEditConfig &VEditor::getConfig()
|
||||||
|
{
|
||||||
|
return m_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VEditorObject *VEditor::object() const
|
||||||
|
{
|
||||||
|
return m_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QWidget *VEditor::getEditor() const
|
||||||
|
{
|
||||||
|
return m_editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VEDITOR_H
|
@ -10,7 +10,7 @@
|
|||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vnotefile.h"
|
#include "vnotefile.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "vmdedit.h"
|
#include "vmdeditor.h"
|
||||||
#include "vmdtab.h"
|
#include "vmdtab.h"
|
||||||
#include "dialog/vconfirmdeletiondialog.h"
|
#include "dialog/vconfirmdeletiondialog.h"
|
||||||
#include "dialog/vsortdialog.h"
|
#include "dialog/vsortdialog.h"
|
||||||
@ -376,7 +376,7 @@ void VFileList::newFile()
|
|||||||
if (contentInserted) {
|
if (contentInserted) {
|
||||||
const VMdTab *tab = dynamic_cast<VMdTab *>(editArea->getCurrentTab());
|
const VMdTab *tab = dynamic_cast<VMdTab *>(editArea->getCurrentTab());
|
||||||
if (tab) {
|
if (tab) {
|
||||||
VMdEdit *edit = dynamic_cast<VMdEdit *>(tab->getEditor());
|
VMdEditor *edit = tab->getEditor();
|
||||||
if (edit && edit->getFile() == file) {
|
if (edit && edit->getFile() == file) {
|
||||||
QTextCursor cursor = edit->textCursor();
|
QTextCursor cursor = edit->textCursor();
|
||||||
cursor.movePosition(QTextCursor::End);
|
cursor.movePosition(QTextCursor::End);
|
||||||
|
@ -84,7 +84,7 @@ private:
|
|||||||
int m_endPos;
|
int m_endPos;
|
||||||
QString m_linkUrl;
|
QString m_linkUrl;
|
||||||
|
|
||||||
// Whether it is a image block.
|
// Whether it is an image block.
|
||||||
bool m_isBlock;
|
bool m_isBlock;
|
||||||
|
|
||||||
// The previewed image ID if this link has been previewed.
|
// The previewed image ID if this link has been previewed.
|
||||||
|
@ -44,7 +44,7 @@ void VImageResourceManager::updateBlockInfos(const QVector<VBlockImageInfo> &p_b
|
|||||||
|
|
||||||
// Clear unused images.
|
// Clear unused images.
|
||||||
for (auto it = m_images.begin(); it != m_images.end();) {
|
for (auto it = m_images.begin(); it != m_images.end();) {
|
||||||
if (!m_images.contains(it.key())) {
|
if (!usedImages.contains(it.key())) {
|
||||||
// Remove the image.
|
// Remove the image.
|
||||||
it = m_images.erase(it);
|
it = m_images.erase(it);
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,7 +13,8 @@ enum class LineNumberType
|
|||||||
None = 0,
|
None = 0,
|
||||||
Absolute,
|
Absolute,
|
||||||
Relative,
|
Relative,
|
||||||
CodeBlock
|
CodeBlock,
|
||||||
|
Invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1630,6 +1630,7 @@ void VMainWindow::initEditorLineNumberMenu(QMenu *p_menu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_config->setEditorLineNumber(p_action->data().toInt());
|
g_config->setEditorLineNumber(p_action->data().toInt());
|
||||||
|
emit editorConfigUpdated();
|
||||||
});
|
});
|
||||||
|
|
||||||
int lineNumberMode = g_config->getEditorLineNumber();
|
int lineNumberMode = g_config->getEditorLineNumber();
|
||||||
@ -2280,6 +2281,8 @@ void VMainWindow::enableImagePreview(bool p_checked)
|
|||||||
void VMainWindow::enableImagePreviewConstraint(bool p_checked)
|
void VMainWindow::enableImagePreviewConstraint(bool p_checked)
|
||||||
{
|
{
|
||||||
g_config->setEnablePreviewImageConstraint(p_checked);
|
g_config->setEnablePreviewImageConstraint(p_checked);
|
||||||
|
|
||||||
|
emit editorConfigUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMainWindow::enableImageConstraint(bool p_checked)
|
void VMainWindow::enableImageConstraint(bool p_checked)
|
||||||
|
@ -86,6 +86,10 @@ public:
|
|||||||
// Prompt user for new notebook if there is no notebook.
|
// Prompt user for new notebook if there is no notebook.
|
||||||
void promptNewNotebookIfEmpty();
|
void promptNewNotebookIfEmpty();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emit when editor related configurations were changed by user.
|
||||||
|
void editorConfigUpdated();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void importNoteFromFile();
|
void importNoteFromFile();
|
||||||
void viewSettings();
|
void viewSettings();
|
||||||
|
@ -66,6 +66,8 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Comment out these lines since we use VMdEditor to replace VMdEdit.
|
||||||
|
/*
|
||||||
m_editOps = new VMdEditOperations(this, m_file);
|
m_editOps = new VMdEditOperations(this, m_file);
|
||||||
|
|
||||||
connect(m_editOps, &VEditOperations::statusMessage,
|
connect(m_editOps, &VEditOperations::statusMessage,
|
||||||
@ -78,6 +80,7 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
|
|
||||||
connect(QApplication::clipboard(), &QClipboard::changed,
|
connect(QApplication::clipboard(), &QClipboard::changed,
|
||||||
this, &VMdEdit::handleClipboardChanged);
|
this, &VMdEdit::handleClipboardChanged);
|
||||||
|
*/
|
||||||
|
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
#include "dialog/vinsertimagedialog.h"
|
#include "dialog/vinsertimagedialog.h"
|
||||||
#include "dialog/vselectdialog.h"
|
#include "dialog/vselectdialog.h"
|
||||||
#include "utils/vutils.h"
|
#include "utils/vutils.h"
|
||||||
#include "vedit.h"
|
#include "veditor.h"
|
||||||
#include "vdownloader.h"
|
#include "vdownloader.h"
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "vmdedit.h"
|
#include "vmdeditor.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
#include "utils/vvim.h"
|
#include "utils/vvim.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
@ -28,7 +28,7 @@ extern VConfigManager *g_config;
|
|||||||
|
|
||||||
const QString VMdEditOperations::c_defaultImageTitle = "";
|
const QString VMdEditOperations::c_defaultImageTitle = "";
|
||||||
|
|
||||||
VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
|
VMdEditOperations::VMdEditOperations(VEditor *p_editor, VFile *p_file)
|
||||||
: VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
|
: VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -40,7 +40,9 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
VInsertImageDialog dialog(tr("Insert Image From Clipboard"),
|
VInsertImageDialog dialog(tr("Insert Image From Clipboard"),
|
||||||
c_defaultImageTitle, "", (QWidget *)m_editor);
|
c_defaultImageTitle,
|
||||||
|
"",
|
||||||
|
m_editor->getEditor());
|
||||||
dialog.setBrowseable(false);
|
dialog.setBrowseable(false);
|
||||||
dialog.setImage(image);
|
dialog.setImage(image);
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
@ -78,7 +80,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
|
|||||||
errStr,
|
errStr,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
(QWidget *)m_editor);
|
m_editor->getEditor());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +89,7 @@ void VMdEditOperations::insertImageFromQImage(const QString &title, const QStrin
|
|||||||
|
|
||||||
qDebug() << "insert image" << title << filePath;
|
qDebug() << "insert image" << title << filePath;
|
||||||
|
|
||||||
VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
|
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
|
||||||
Q_ASSERT(mdEditor);
|
Q_ASSERT(mdEditor);
|
||||||
mdEditor->imageInserted(filePath);
|
mdEditor->imageInserted(filePath);
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
|
|||||||
errStr,
|
errStr,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
(QWidget *)m_editor);
|
m_editor->getEditor());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +129,7 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
|
|||||||
|
|
||||||
qDebug() << "insert image" << title << filePath;
|
qDebug() << "insert image" << title << filePath;
|
||||||
|
|
||||||
VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
|
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
|
||||||
Q_ASSERT(mdEditor);
|
Q_ASSERT(mdEditor);
|
||||||
mdEditor->imageInserted(filePath);
|
mdEditor->imageInserted(filePath);
|
||||||
}
|
}
|
||||||
@ -156,7 +158,7 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
|
|||||||
|
|
||||||
|
|
||||||
VInsertImageDialog dialog(title, c_defaultImageTitle,
|
VInsertImageDialog dialog(title, c_defaultImageTitle,
|
||||||
imagePath, (QWidget *)m_editor);
|
imagePath, m_editor->getEditor());
|
||||||
dialog.setBrowseable(false, true);
|
dialog.setBrowseable(false, true);
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
dialog.setImage(image);
|
dialog.setImage(image);
|
||||||
@ -186,7 +188,7 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
|
|||||||
bool VMdEditOperations::insertImage()
|
bool VMdEditOperations::insertImage()
|
||||||
{
|
{
|
||||||
VInsertImageDialog dialog(tr("Insert Image From File"),
|
VInsertImageDialog dialog(tr("Insert Image From File"),
|
||||||
c_defaultImageTitle, "", (QWidget *)m_editor);
|
c_defaultImageTitle, "", m_editor->getEditor());
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
QString title = dialog.getImageTitleInput();
|
QString title = dialog.getImageTitleInput();
|
||||||
QString imagePath = dialog.getPathInput();
|
QString imagePath = dialog.getPathInput();
|
||||||
@ -393,10 +395,10 @@ bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
|
|||||||
// 1. If there is any selection, clear it.
|
// 1. If there is any selection, clear it.
|
||||||
// 2. Otherwise, ignore this event and let parent handles it.
|
// 2. Otherwise, ignore this event and let parent handles it.
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
cursor.clearSelection();
|
cursor.clearSelection();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -407,18 +409,18 @@ bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
|
|||||||
|
|
||||||
bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
QString text(m_editConfig->m_tabSpaces);
|
QString text(m_editConfig->m_tabSpaces);
|
||||||
|
|
||||||
if (p_event->modifiers() == Qt::NoModifier) {
|
if (p_event->modifiers() == Qt::NoModifier) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
// Indent each selected line.
|
// Indent each selected line.
|
||||||
VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
|
VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
} else {
|
} else {
|
||||||
// If it is a Tab key following auto list, increase the indent level.
|
// If it is a Tab key following auto list, increase the indent level.
|
||||||
QTextBlock block = cursor.block();
|
QTextBlock block = cursor.block();
|
||||||
@ -433,7 +435,7 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
blockCursor.endEditBlock();
|
blockCursor.endEditBlock();
|
||||||
// Change m_autoIndentPos to let it can be repeated.
|
// Change m_autoIndentPos to let it can be repeated.
|
||||||
m_autoIndentPos = m_editor->textCursor().position();
|
m_autoIndentPos = m_editor->textCursorW().position();
|
||||||
} else {
|
} else {
|
||||||
// Just insert "tab".
|
// Just insert "tab".
|
||||||
insertTextAtCurPos(text);
|
insertTextAtCurPos(text);
|
||||||
@ -454,8 +456,8 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
|
|||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||||
bool continueAutoIndent = false;
|
bool continueAutoIndent = false;
|
||||||
int seq = -1;
|
int seq = -1;
|
||||||
@ -474,7 +476,7 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
|
|
||||||
if (continueAutoIndent) {
|
if (continueAutoIndent) {
|
||||||
m_autoIndentPos = m_editor->textCursor().position();
|
m_autoIndentPos = m_editor->textCursorW().position();
|
||||||
} else {
|
} else {
|
||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
}
|
}
|
||||||
@ -486,7 +488,7 @@ bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
|
|||||||
{
|
{
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
// Ctrl+H, equal to backspace.
|
// Ctrl+H, equal to backspace.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.deletePreviousChar();
|
cursor.deletePreviousChar();
|
||||||
|
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
@ -499,7 +501,7 @@ bool VMdEditOperations::handleKeyU(QKeyEvent *p_event)
|
|||||||
{
|
{
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
// Ctrl+U, delete till the start of line.
|
// Ctrl+U, delete till the start of line.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
bool ret;
|
bool ret;
|
||||||
if (cursor.atBlockStart()) {
|
if (cursor.atBlockStart()) {
|
||||||
ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
|
ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
|
||||||
@ -520,7 +522,7 @@ bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
|
|||||||
{
|
{
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
// Ctrl+W, delete till the start of previous word.
|
// Ctrl+W, delete till the start of previous word.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
cursor.removeSelectedText();
|
cursor.removeSelectedText();
|
||||||
} else {
|
} else {
|
||||||
@ -539,10 +541,10 @@ bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
|
|||||||
{
|
{
|
||||||
// 1. If there is any selection, clear it.
|
// 1. If there is any selection, clear it.
|
||||||
// 2. Otherwise, ignore this event and let parent handles it.
|
// 2. Otherwise, ignore this event and let parent handles it.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
cursor.clearSelection();
|
cursor.clearSelection();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -560,7 +562,7 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
// Insert two spaces and a new line.
|
// Insert two spaces and a new line.
|
||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
cursor.removeSelectedText();
|
cursor.removeSelectedText();
|
||||||
cursor.insertText(" ");
|
cursor.insertText(" ");
|
||||||
@ -575,11 +577,11 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
if (m_autoIndentPos > -1) {
|
if (m_autoIndentPos > -1) {
|
||||||
// Cancel the auto indent/list if the pos is the same and cursor is at
|
// Cancel the auto indent/list if the pos is the same and cursor is at
|
||||||
// the end of a block.
|
// the end of a block.
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
if (VEditUtils::needToCancelAutoIndent(m_autoIndentPos, cursor)) {
|
if (VEditUtils::needToCancelAutoIndent(m_autoIndentPos, cursor)) {
|
||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
VEditUtils::deleteIndentAndListMark(cursor);
|
VEditUtils::deleteIndentAndListMark(cursor);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,7 +591,7 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
if (g_config->getAutoIndent()) {
|
if (g_config->getAutoIndent()) {
|
||||||
handled = true;
|
handled = true;
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
bool textInserted = false;
|
bool textInserted = false;
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
cursor.removeSelectedText();
|
cursor.removeSelectedText();
|
||||||
@ -603,9 +605,9 @@ bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
if (textInserted) {
|
if (textInserted) {
|
||||||
m_autoIndentPos = m_editor->textCursor().position();
|
m_autoIndentPos = m_editor->textCursorW().position();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,8 +650,8 @@ void VMdEditOperations::changeListBlockSeqNumber(QTextBlock &p_block, int p_seq)
|
|||||||
|
|
||||||
bool VMdEditOperations::insertTitle(int p_level)
|
bool VMdEditOperations::insertTitle(int p_level)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->documentW();
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
int firstBlock = cursor.block().blockNumber();
|
int firstBlock = cursor.block().blockNumber();
|
||||||
int lastBlock = firstBlock;
|
int lastBlock = firstBlock;
|
||||||
|
|
||||||
@ -667,7 +669,7 @@ bool VMdEditOperations::insertTitle(int p_level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,7 +715,7 @@ void VMdEditOperations::decorateText(TextDecoration p_decoration)
|
|||||||
|
|
||||||
void VMdEditOperations::decorateBold()
|
void VMdEditOperations::decorateBold()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
// Insert ** around the selected text.
|
// Insert ** around the selected text.
|
||||||
@ -745,12 +747,12 @@ void VMdEditOperations::decorateBold()
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditOperations::decorateItalic()
|
void VMdEditOperations::decorateItalic()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
// Insert * around the selected text.
|
// Insert * around the selected text.
|
||||||
@ -782,12 +784,12 @@ void VMdEditOperations::decorateItalic()
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditOperations::decorateInlineCode()
|
void VMdEditOperations::decorateInlineCode()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
// Insert ` around the selected text.
|
// Insert ` around the selected text.
|
||||||
@ -819,14 +821,14 @@ void VMdEditOperations::decorateInlineCode()
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditOperations::decorateCodeBlock()
|
void VMdEditOperations::decorateCodeBlock()
|
||||||
{
|
{
|
||||||
const QString marker("```");
|
const QString marker("```");
|
||||||
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
// Insert ``` around the selected text.
|
// Insert ``` around the selected text.
|
||||||
@ -899,12 +901,12 @@ void VMdEditOperations::decorateCodeBlock()
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditOperations::decorateStrikethrough()
|
void VMdEditOperations::decorateStrikethrough()
|
||||||
{
|
{
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
// Insert ~~ around the selected text.
|
// Insert ~~ around the selected text.
|
||||||
@ -936,16 +938,16 @@ void VMdEditOperations::decorateStrikethrough()
|
|||||||
}
|
}
|
||||||
|
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::insertLink(const QString &p_linkText,
|
bool VMdEditOperations::insertLink(const QString &p_linkText,
|
||||||
const QString &p_linkUrl)
|
const QString &p_linkUrl)
|
||||||
{
|
{
|
||||||
QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
|
QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursorW();
|
||||||
cursor.insertText(link);
|
cursor.insertText(link);
|
||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursorW(cursor);
|
||||||
|
|
||||||
setVimMode(VimMode::Insert);
|
setVimMode(VimMode::Insert);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class VMdEditOperations : public VEditOperations
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
VMdEditOperations(VEdit *p_editor, VFile *p_file);
|
VMdEditOperations(VEditor *p_editor, VFile *p_file);
|
||||||
|
|
||||||
bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
|
bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
862
src/vmdeditor.cpp
Normal file
862
src/vmdeditor.cpp
Normal file
@ -0,0 +1,862 @@
|
|||||||
|
#include "vmdeditor.h"
|
||||||
|
|
||||||
|
#include <QtWidgets>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "vdocument.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
|
#include "vedittab.h"
|
||||||
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
#include "vcodeblockhighlighthelper.h"
|
||||||
|
#include "vmdeditoperations.h"
|
||||||
|
#include "vtableofcontent.h"
|
||||||
|
#include "utils/veditutils.h"
|
||||||
|
#include "dialog/vselectdialog.h"
|
||||||
|
#include "dialog/vconfirmdeletiondialog.h"
|
||||||
|
#include "vtextblockdata.h"
|
||||||
|
#include "vorphanfile.h"
|
||||||
|
#include "vnotefile.h"
|
||||||
|
#include "vpreviewmanager.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
VMdEditor::VMdEditor(VFile *p_file,
|
||||||
|
VDocument *p_doc,
|
||||||
|
MarkdownConverterType p_type,
|
||||||
|
QWidget *p_parent)
|
||||||
|
: VPlainTextEdit(p_parent),
|
||||||
|
VEditor(p_file, this),
|
||||||
|
m_mdHighlighter(NULL),
|
||||||
|
m_freshEdit(true)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_file->getDocType() == DocType::Markdown);
|
||||||
|
|
||||||
|
VEditor::init();
|
||||||
|
|
||||||
|
// Hook functions from VEditor.
|
||||||
|
connect(this, &VPlainTextEdit::cursorPositionChanged,
|
||||||
|
this, [this]() {
|
||||||
|
highlightOnCursorPositionChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &VPlainTextEdit::selectionChanged,
|
||||||
|
this, [this]() {
|
||||||
|
highlightSelectedWord();
|
||||||
|
});
|
||||||
|
// End.
|
||||||
|
|
||||||
|
m_mdHighlighter = new HGMarkdownHighlighter(g_config->getMdHighlightingStyles(),
|
||||||
|
g_config->getCodeBlockStyles(),
|
||||||
|
g_config->getMarkdownHighlightInterval(),
|
||||||
|
document());
|
||||||
|
|
||||||
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::headersUpdated,
|
||||||
|
this, &VMdEditor::updateHeaders);
|
||||||
|
|
||||||
|
// After highlight, the cursor may trun into non-visible. We should make it visible
|
||||||
|
// in this case.
|
||||||
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::highlightCompleted,
|
||||||
|
this, [this]() {
|
||||||
|
makeBlockVisible(textCursor().block());
|
||||||
|
});
|
||||||
|
|
||||||
|
m_cbHighlighter = new VCodeBlockHighlightHelper(m_mdHighlighter,
|
||||||
|
p_doc,
|
||||||
|
p_type);
|
||||||
|
|
||||||
|
m_previewMgr = new VPreviewManager(this);
|
||||||
|
connect(m_mdHighlighter, &HGMarkdownHighlighter::imageLinksUpdated,
|
||||||
|
m_previewMgr, &VPreviewManager::imageLinksUpdated);
|
||||||
|
connect(m_previewMgr, &VPreviewManager::requestUpdateImageLinks,
|
||||||
|
m_mdHighlighter, &HGMarkdownHighlighter::updateHighlight);
|
||||||
|
|
||||||
|
m_editOps = new VMdEditOperations(this, m_file);
|
||||||
|
connect(m_editOps, &VEditOperations::statusMessage,
|
||||||
|
m_object, &VEditorObject::statusMessage);
|
||||||
|
connect(m_editOps, &VEditOperations::vimStatusUpdated,
|
||||||
|
m_object, &VEditorObject::vimStatusUpdated);
|
||||||
|
|
||||||
|
connect(this, &VPlainTextEdit::cursorPositionChanged,
|
||||||
|
this, &VMdEditor::updateCurrentHeader);
|
||||||
|
|
||||||
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::updateFontAndPalette()
|
||||||
|
{
|
||||||
|
setFont(g_config->getMdEditFont());
|
||||||
|
setPalette(g_config->getMdEditPalette());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::beginEdit()
|
||||||
|
{
|
||||||
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
|
|
||||||
|
initInitImages();
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
|
||||||
|
setReadOnlyAndHighlightCurrentLine(false);
|
||||||
|
|
||||||
|
emit statusChanged();
|
||||||
|
|
||||||
|
updateHeaders(m_mdHighlighter->getHeaderRegions());
|
||||||
|
|
||||||
|
if (m_freshEdit) {
|
||||||
|
m_freshEdit = false;
|
||||||
|
emit m_object->ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::endEdit()
|
||||||
|
{
|
||||||
|
setReadOnlyAndHighlightCurrentLine(true);
|
||||||
|
clearUnusedImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::saveFile()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_file->isModifiable());
|
||||||
|
|
||||||
|
if (!document()->isModified()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_file->setContent(toPlainText());
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::reloadFile()
|
||||||
|
{
|
||||||
|
const QString &content = m_file->getContent();
|
||||||
|
setPlainText(content);
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::scrollToBlock(int p_blockNumber)
|
||||||
|
{
|
||||||
|
QTextBlock block = document()->findBlockByNumber(p_blockNumber);
|
||||||
|
if (block.isValid()) {
|
||||||
|
VEditUtils::scrollBlockInPage(this, block.blockNumber(), 0);
|
||||||
|
moveCursor(QTextCursor::EndOfBlock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the visual offset of a block.
|
||||||
|
#define GETVISUALOFFSETY ((int)(contentOffset().y() + rect.y()))
|
||||||
|
|
||||||
|
void VMdEditor::makeBlockVisible(const QTextBlock &p_block)
|
||||||
|
{
|
||||||
|
if (!p_block.isValid() || !p_block.isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar *vbar = verticalScrollBar();
|
||||||
|
if (!vbar || !vbar->isVisible()) {
|
||||||
|
// No vertical scrollbar. No need to scroll.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int height = rect().height();
|
||||||
|
QScrollBar *hbar = horizontalScrollBar();
|
||||||
|
if (hbar && hbar->isVisible()) {
|
||||||
|
height -= hbar->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool moved = false;
|
||||||
|
|
||||||
|
QRectF rect = blockBoundingGeometry(p_block);
|
||||||
|
int y = GETVISUALOFFSETY;
|
||||||
|
int rectHeight = (int)rect.height();
|
||||||
|
|
||||||
|
// Handle the case rectHeight >= height.
|
||||||
|
if (rectHeight >= height) {
|
||||||
|
if (y <= 0) {
|
||||||
|
if (y + rectHeight < height) {
|
||||||
|
// Need to scroll up.
|
||||||
|
while (y + rectHeight < height && vbar->value() > vbar->minimum()) {
|
||||||
|
moved = true;
|
||||||
|
vbar->setValue(vbar->value() - vbar->singleStep());
|
||||||
|
rect = blockBoundingGeometry(p_block);
|
||||||
|
rectHeight = (int)rect.height();
|
||||||
|
y = GETVISUALOFFSETY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Need to scroll down.
|
||||||
|
while (y > 0 && vbar->value() < vbar->maximum()) {
|
||||||
|
moved = true;
|
||||||
|
vbar->setValue(vbar->value() + vbar->singleStep());
|
||||||
|
rect = blockBoundingGeometry(p_block);
|
||||||
|
rectHeight = (int)rect.height();
|
||||||
|
y = GETVISUALOFFSETY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
qDebug() << "scroll to make huge block visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (y < 0 && vbar->value() > vbar->minimum()) {
|
||||||
|
qDebug() << y << vbar->value() << vbar->minimum() << rectHeight;
|
||||||
|
moved = true;
|
||||||
|
vbar->setValue(vbar->value() - vbar->singleStep());
|
||||||
|
rect = blockBoundingGeometry(p_block);
|
||||||
|
rectHeight = (int)rect.height();
|
||||||
|
y = GETVISUALOFFSETY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
qDebug() << "scroll page down to make block visible";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (y + rectHeight > height && vbar->value() < vbar->maximum()) {
|
||||||
|
moved = true;
|
||||||
|
vbar->setValue(vbar->value() + vbar->singleStep());
|
||||||
|
rect = blockBoundingGeometry(p_block);
|
||||||
|
rectHeight = (int)rect.height();
|
||||||
|
y = GETVISUALOFFSETY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
qDebug() << "scroll page up to make block visible";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::contextMenuEvent(QContextMenuEvent *p_event)
|
||||||
|
{
|
||||||
|
QMenu *menu = createStandardContextMenu();
|
||||||
|
menu->setToolTipsVisible(true);
|
||||||
|
|
||||||
|
const QList<QAction *> actions = menu->actions();
|
||||||
|
|
||||||
|
if (!textCursor().hasSelection()) {
|
||||||
|
VEditTab *editTab = dynamic_cast<VEditTab *>(parent());
|
||||||
|
Q_ASSERT(editTab);
|
||||||
|
if (editTab->isEditMode()) {
|
||||||
|
QAction *saveExitAct = new QAction(QIcon(":/resources/icons/save_exit.svg"),
|
||||||
|
tr("&Save Changes And Read"),
|
||||||
|
menu);
|
||||||
|
saveExitAct->setToolTip(tr("Save changes and exit edit mode"));
|
||||||
|
connect(saveExitAct, &QAction::triggered,
|
||||||
|
this, [this]() {
|
||||||
|
emit m_object->saveAndRead();
|
||||||
|
});
|
||||||
|
|
||||||
|
QAction *discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"),
|
||||||
|
tr("&Discard Changes And Read"),
|
||||||
|
menu);
|
||||||
|
discardExitAct->setToolTip(tr("Discard changes and exit edit mode"));
|
||||||
|
connect(discardExitAct, &QAction::triggered,
|
||||||
|
this, [this]() {
|
||||||
|
emit m_object->discardAndRead();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->insertAction(actions.isEmpty() ? NULL : actions[0], discardExitAct);
|
||||||
|
menu->insertAction(discardExitAct, saveExitAct);
|
||||||
|
if (!actions.isEmpty()) {
|
||||||
|
menu->insertSeparator(actions[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->exec(p_event->globalPos());
|
||||||
|
delete menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::mousePressEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
if (handleMousePressEvent(p_event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlainTextEdit::mousePressEvent(p_event);
|
||||||
|
|
||||||
|
emit m_object->selectionChangedByMouse(textCursor().hasSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::mouseReleaseEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
if (handleMouseReleaseEvent(p_event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlainTextEdit::mousePressEvent(p_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::mouseMoveEvent(QMouseEvent *p_event)
|
||||||
|
{
|
||||||
|
if (handleMouseMoveEvent(p_event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlainTextEdit::mouseMoveEvent(p_event);
|
||||||
|
|
||||||
|
emit m_object->selectionChangedByMouse(textCursor().hasSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant VMdEditor::inputMethodQuery(Qt::InputMethodQuery p_query) const
|
||||||
|
{
|
||||||
|
QVariant ret;
|
||||||
|
if (handleInputMethodQuery(p_query, ret)) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VPlainTextEdit::inputMethodQuery(p_query);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::isBlockVisible(const QTextBlock &p_block)
|
||||||
|
{
|
||||||
|
if (!p_block.isValid() || !p_block.isVisible()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar *vbar = verticalScrollBar();
|
||||||
|
if (!vbar || !vbar->isVisible()) {
|
||||||
|
// No vertical scrollbar.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int height = rect().height();
|
||||||
|
QScrollBar *hbar = horizontalScrollBar();
|
||||||
|
if (hbar && hbar->isVisible()) {
|
||||||
|
height -= hbar->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF rect = blockBoundingGeometry(p_block);
|
||||||
|
int y = GETVISUALOFFSETY;
|
||||||
|
int rectHeight = (int)rect.height();
|
||||||
|
|
||||||
|
return (y >= 0 && y < height) || (y < 0 && y + rectHeight > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addHeaderSequence(QVector<int> &p_sequence, int p_level, int p_baseLevel)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_level >= 1 && p_level < p_sequence.size());
|
||||||
|
if (p_level < p_baseLevel) {
|
||||||
|
p_sequence.fill(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
++p_sequence[p_level];
|
||||||
|
for (int i = p_level + 1; i < p_sequence.size(); ++i) {
|
||||||
|
p_sequence[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString headerSequenceStr(const QVector<int> &p_sequence)
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
for (int i = 1; i < p_sequence.size(); ++i) {
|
||||||
|
if (p_sequence[i] != 0) {
|
||||||
|
res = res + QString::number(p_sequence[i]) + '.';
|
||||||
|
} else if (res.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insertSequenceToHeader(QTextBlock p_block,
|
||||||
|
QRegExp &p_reg,
|
||||||
|
QRegExp &p_preReg,
|
||||||
|
const QString &p_seq)
|
||||||
|
{
|
||||||
|
if (!p_block.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = p_block.text();
|
||||||
|
bool matched = p_reg.exactMatch(text);
|
||||||
|
Q_ASSERT(matched);
|
||||||
|
|
||||||
|
matched = p_preReg.exactMatch(text);
|
||||||
|
Q_ASSERT(matched);
|
||||||
|
|
||||||
|
int start = p_reg.cap(1).length() + 1;
|
||||||
|
int end = p_preReg.cap(1).length();
|
||||||
|
|
||||||
|
Q_ASSERT(start <= end);
|
||||||
|
|
||||||
|
QTextCursor cursor(p_block);
|
||||||
|
cursor.setPosition(p_block.position() + start);
|
||||||
|
if (start != end) {
|
||||||
|
cursor.setPosition(p_block.position() + end, QTextCursor::KeepAnchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_seq.isEmpty()) {
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
} else {
|
||||||
|
cursor.insertText(p_seq + ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::updateHeaders(const QVector<VElementRegion> &p_headerRegions)
|
||||||
|
{
|
||||||
|
QTextDocument *doc = document();
|
||||||
|
|
||||||
|
QVector<VTableOfContentItem> headers;
|
||||||
|
QVector<int> headerBlockNumbers;
|
||||||
|
QVector<QString> headerSequences;
|
||||||
|
if (!p_headerRegions.isEmpty()) {
|
||||||
|
headers.reserve(p_headerRegions.size());
|
||||||
|
headerBlockNumbers.reserve(p_headerRegions.size());
|
||||||
|
headerSequences.reserve(p_headerRegions.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume that each block contains only one line
|
||||||
|
// Only support # syntax for now
|
||||||
|
QRegExp headerReg(VUtils::c_headerRegExp);
|
||||||
|
int baseLevel = -1;
|
||||||
|
for (auto const & reg : p_headerRegions) {
|
||||||
|
QTextBlock block = doc->findBlock(reg.m_startPos);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!block.contains(reg.m_endPos - 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((block.userState() == HighlightBlockState::Normal)
|
||||||
|
&& headerReg.exactMatch(block.text())) {
|
||||||
|
int level = headerReg.cap(1).length();
|
||||||
|
VTableOfContentItem header(headerReg.cap(2).trimmed(),
|
||||||
|
level,
|
||||||
|
block.blockNumber(),
|
||||||
|
headers.size());
|
||||||
|
headers.append(header);
|
||||||
|
headerBlockNumbers.append(block.blockNumber());
|
||||||
|
headerSequences.append(headerReg.cap(3));
|
||||||
|
|
||||||
|
if (baseLevel == -1) {
|
||||||
|
baseLevel = level;
|
||||||
|
} else if (baseLevel > level) {
|
||||||
|
baseLevel = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_headers.clear();
|
||||||
|
|
||||||
|
bool autoSequence = m_config.m_enableHeadingSequence
|
||||||
|
&& !isReadOnly()
|
||||||
|
&& m_file->isModifiable();
|
||||||
|
int headingSequenceBaseLevel = g_config->getHeadingSequenceBaseLevel();
|
||||||
|
if (headingSequenceBaseLevel < 1 || headingSequenceBaseLevel > 6) {
|
||||||
|
headingSequenceBaseLevel = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<int> seqs(7, 0);
|
||||||
|
QRegExp preReg(VUtils::c_headerPrefixRegExp);
|
||||||
|
int curLevel = baseLevel - 1;
|
||||||
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
VTableOfContentItem &item = headers[i];
|
||||||
|
while (item.m_level > curLevel + 1) {
|
||||||
|
curLevel += 1;
|
||||||
|
|
||||||
|
// Insert empty level which is an invalid header.
|
||||||
|
m_headers.append(VTableOfContentItem(c_emptyHeaderName,
|
||||||
|
curLevel,
|
||||||
|
-1,
|
||||||
|
m_headers.size()));
|
||||||
|
if (autoSequence) {
|
||||||
|
addHeaderSequence(seqs, curLevel, headingSequenceBaseLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.m_index = m_headers.size();
|
||||||
|
m_headers.append(item);
|
||||||
|
curLevel = item.m_level;
|
||||||
|
if (autoSequence) {
|
||||||
|
addHeaderSequence(seqs, item.m_level, headingSequenceBaseLevel);
|
||||||
|
|
||||||
|
QString seqStr = headerSequenceStr(seqs);
|
||||||
|
if (headerSequences[i] != seqStr) {
|
||||||
|
// Insert correct sequence.
|
||||||
|
insertSequenceToHeader(doc->findBlockByNumber(headerBlockNumbers[i]),
|
||||||
|
headerReg,
|
||||||
|
preReg,
|
||||||
|
seqStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit headersChanged(m_headers);
|
||||||
|
|
||||||
|
updateCurrentHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::updateCurrentHeader()
|
||||||
|
{
|
||||||
|
emit currentHeaderChanged(textCursor().block().blockNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::initInitImages()
|
||||||
|
{
|
||||||
|
m_initImages = VUtils::fetchImagesFromMarkdownFile(m_file,
|
||||||
|
ImageLink::LocalRelativeInternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::clearUnusedImages()
|
||||||
|
{
|
||||||
|
QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(m_file,
|
||||||
|
ImageLink::LocalRelativeInternal);
|
||||||
|
|
||||||
|
QVector<QString> unusedImages;
|
||||||
|
|
||||||
|
if (!m_insertedImages.isEmpty()) {
|
||||||
|
for (int i = 0; i < m_insertedImages.size(); ++i) {
|
||||||
|
const ImageLink &link = m_insertedImages[i];
|
||||||
|
|
||||||
|
if (link.m_type != ImageLink::LocalRelativeInternal) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < images.size(); ++j) {
|
||||||
|
if (VUtils::equalPath(link.m_path, images[j].m_path)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This inserted image is no longer in the file.
|
||||||
|
if (j == images.size()) {
|
||||||
|
unusedImages.push_back(link.m_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_insertedImages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_initImages.size(); ++i) {
|
||||||
|
const ImageLink &link = m_initImages[i];
|
||||||
|
|
||||||
|
V_ASSERT(link.m_type == ImageLink::LocalRelativeInternal);
|
||||||
|
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < images.size(); ++j) {
|
||||||
|
if (VUtils::equalPath(link.m_path, images[j].m_path)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original local relative image is no longer in the file.
|
||||||
|
if (j == images.size()) {
|
||||||
|
unusedImages.push_back(link.m_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!unusedImages.isEmpty()) {
|
||||||
|
if (g_config->getConfirmImagesCleanUp()) {
|
||||||
|
QVector<ConfirmItemInfo> items;
|
||||||
|
for (auto const & img : unusedImages) {
|
||||||
|
items.push_back(ConfirmItemInfo(img,
|
||||||
|
img,
|
||||||
|
img,
|
||||||
|
NULL));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = tr("Following images seems not to be used in this note anymore. "
|
||||||
|
"Please confirm the deletion of these images.");
|
||||||
|
|
||||||
|
QString info = tr("Deleted files could be found in the recycle "
|
||||||
|
"bin of this note.<br>"
|
||||||
|
"Click \"Cancel\" to leave them untouched.");
|
||||||
|
|
||||||
|
VConfirmDeletionDialog dialog(tr("Confirm Cleaning Up Unused Images"),
|
||||||
|
text,
|
||||||
|
info,
|
||||||
|
items,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
this);
|
||||||
|
|
||||||
|
unusedImages.clear();
|
||||||
|
if (dialog.exec()) {
|
||||||
|
items = dialog.getConfirmedItems();
|
||||||
|
g_config->setConfirmImagesCleanUp(dialog.getAskAgainEnabled());
|
||||||
|
|
||||||
|
for (auto const & item : items) {
|
||||||
|
unusedImages.push_back(item.m_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < unusedImages.size(); ++i) {
|
||||||
|
bool ret = false;
|
||||||
|
if (m_file->getType() == FileType::Note) {
|
||||||
|
const VNoteFile *tmpFile = dynamic_cast<const VNoteFile *>((VFile *)m_file);
|
||||||
|
ret = VUtils::deleteFile(tmpFile->getNotebook(), unusedImages[i], false);
|
||||||
|
} else if (m_file->getType() == FileType::Orphan) {
|
||||||
|
const VOrphanFile *tmpFile = dynamic_cast<const VOrphanFile *>((VFile *)m_file);
|
||||||
|
ret = VUtils::deleteFile(tmpFile, unusedImages[i], false);
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
qWarning() << "fail to delete unused original image" << unusedImages[i];
|
||||||
|
} else {
|
||||||
|
qDebug() << "delete unused image" << unusedImages[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initImages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::keyPressEvent(QKeyEvent *p_event)
|
||||||
|
{
|
||||||
|
if (m_editOps && m_editOps->handleKeyPressEvent(p_event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlainTextEdit::keyPressEvent(p_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::canInsertFromMimeData(const QMimeData *p_source) const
|
||||||
|
{
|
||||||
|
return p_source->hasImage()
|
||||||
|
|| p_source->hasUrls()
|
||||||
|
|| VPlainTextEdit::canInsertFromMimeData(p_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::insertFromMimeData(const QMimeData *p_source)
|
||||||
|
{
|
||||||
|
VSelectDialog dialog(tr("Insert From Clipboard"), this);
|
||||||
|
dialog.addSelection(tr("Insert As Image"), 0);
|
||||||
|
dialog.addSelection(tr("Insert As Text"), 1);
|
||||||
|
|
||||||
|
if (p_source->hasImage()) {
|
||||||
|
// Image data in the clipboard
|
||||||
|
if (p_source->hasText()) {
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
if (dialog.getSelection() == 1) {
|
||||||
|
// Insert as text.
|
||||||
|
Q_ASSERT(p_source->hasText() && p_source->hasImage());
|
||||||
|
VPlainTextEdit::insertFromMimeData(p_source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_editOps->insertImageFromMimeData(p_source);
|
||||||
|
return;
|
||||||
|
} else if (p_source->hasUrls()) {
|
||||||
|
QList<QUrl> urls = p_source->urls();
|
||||||
|
if (urls.size() == 1 && VUtils::isImageURL(urls[0])) {
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
// FIXME: After calling dialog.exec(), p_source->hasUrl() returns false.
|
||||||
|
if (dialog.getSelection() == 0) {
|
||||||
|
// Insert as image.
|
||||||
|
m_editOps->insertImageFromURL(urls[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMimeData newSource;
|
||||||
|
newSource.setUrls(urls);
|
||||||
|
VPlainTextEdit::insertFromMimeData(&newSource);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (p_source->hasText()) {
|
||||||
|
QString text = p_source->text();
|
||||||
|
if (VUtils::isImageURLText(text)) {
|
||||||
|
// The text is a URL to an image.
|
||||||
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
|
if (dialog.getSelection() == 0) {
|
||||||
|
// Insert as image.
|
||||||
|
QUrl url(text);
|
||||||
|
if (url.isValid()) {
|
||||||
|
m_editOps->insertImageFromURL(QUrl(text));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(p_source->hasText());
|
||||||
|
}
|
||||||
|
|
||||||
|
VPlainTextEdit::insertFromMimeData(p_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::imageInserted(const QString &p_path)
|
||||||
|
{
|
||||||
|
ImageLink link;
|
||||||
|
link.m_path = p_path;
|
||||||
|
if (m_file->useRelativeImageFolder()) {
|
||||||
|
link.m_type = ImageLink::LocalRelativeInternal;
|
||||||
|
} else {
|
||||||
|
link.m_type = ImageLink::LocalAbsolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_insertedImages.append(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::scrollToHeader(int p_blockNumber)
|
||||||
|
{
|
||||||
|
if (p_blockNumber < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scrollToBlock(p_blockNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VMdEditor::indexOfCurrentHeader() const
|
||||||
|
{
|
||||||
|
if (m_headers.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockNumber = textCursor().block().blockNumber();
|
||||||
|
for (int i = m_headers.size() - 1; i >= 0; --i) {
|
||||||
|
if (!m_headers[i].isEmpty()
|
||||||
|
&& m_headers[i].m_blockNumber <= blockNumber) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMdEditor::jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat)
|
||||||
|
{
|
||||||
|
if (m_headers.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor cursor = textCursor();
|
||||||
|
int cursorLine = cursor.block().blockNumber();
|
||||||
|
int targetIdx = -1;
|
||||||
|
// -1: skip level check.
|
||||||
|
int targetLevel = 0;
|
||||||
|
int idx = indexOfCurrentHeader();
|
||||||
|
if (idx == -1) {
|
||||||
|
// Cursor locates at the beginning, before any headers.
|
||||||
|
if (p_relativeLevel < 0 || !p_forward) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int delta = 1;
|
||||||
|
if (!p_forward) {
|
||||||
|
delta = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool firstHeader = true;
|
||||||
|
for (targetIdx = idx == -1 ? 0 : idx;
|
||||||
|
targetIdx >= 0 && targetIdx < m_headers.size();
|
||||||
|
targetIdx += delta) {
|
||||||
|
const VTableOfContentItem &header = m_headers[targetIdx];
|
||||||
|
if (header.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetLevel == 0) {
|
||||||
|
// The target level has not been init yet.
|
||||||
|
Q_ASSERT(firstHeader);
|
||||||
|
targetLevel = header.m_level;
|
||||||
|
if (p_relativeLevel < 0) {
|
||||||
|
targetLevel += p_relativeLevel;
|
||||||
|
if (targetLevel < 1) {
|
||||||
|
// Invalid level.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (p_relativeLevel > 0) {
|
||||||
|
targetLevel = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetLevel == -1 || header.m_level == targetLevel) {
|
||||||
|
if (firstHeader
|
||||||
|
&& (cursorLine == header.m_blockNumber
|
||||||
|
|| p_forward)
|
||||||
|
&& idx != -1) {
|
||||||
|
// This header is not counted for the repeat.
|
||||||
|
firstHeader = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--p_repeat == 0) {
|
||||||
|
// Found.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (header.m_level < targetLevel) {
|
||||||
|
// Stop by higher level.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstHeader = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetIdx < 0 || targetIdx >= m_headers.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump to target header.
|
||||||
|
int line = m_headers[targetIdx].m_blockNumber;
|
||||||
|
if (line > -1) {
|
||||||
|
QTextBlock block = document()->findBlockByNumber(line);
|
||||||
|
if (block.isValid()) {
|
||||||
|
cursor.setPosition(block.position());
|
||||||
|
setTextCursor(cursor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::scrollBlockInPage(int p_blockNum, int p_dest)
|
||||||
|
{
|
||||||
|
VEditUtils::scrollBlockInPage(this, p_blockNum, p_dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::updatePlainTextEditConfig()
|
||||||
|
{
|
||||||
|
m_previewMgr->setPreviewEnabled(g_config->getEnablePreviewImages());
|
||||||
|
setBlockImageEnabled(g_config->getEnablePreviewImages());
|
||||||
|
|
||||||
|
setImageWidthConstrainted(g_config->getEnablePreviewImageConstraint());
|
||||||
|
|
||||||
|
int lineNumber = g_config->getEditorLineNumber();
|
||||||
|
if (lineNumber < (int)LineNumberType::None || lineNumber >= (int)LineNumberType::Invalid) {
|
||||||
|
lineNumber = (int)LineNumberType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLineNumberType((LineNumberType)lineNumber);
|
||||||
|
setLineNumberColor(g_config->getEditorLineNumberFg(),
|
||||||
|
g_config->getEditorLineNumberBg());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMdEditor::updateConfig()
|
||||||
|
{
|
||||||
|
updatePlainTextEditConfig();
|
||||||
|
updateEditConfig();
|
||||||
|
}
|
212
src/vmdeditor.h
Normal file
212
src/vmdeditor.h
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
#ifndef VMDEDITOR_H
|
||||||
|
#define VMDEDITOR_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QString>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
#include "vplaintextedit.h"
|
||||||
|
#include "veditor.h"
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "vtableofcontent.h"
|
||||||
|
#include "veditoperations.h"
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
|
class HGMarkdownHighlighter;
|
||||||
|
class VCodeBlockHighlightHelper;
|
||||||
|
class VDocument;
|
||||||
|
class VPreviewManager;
|
||||||
|
|
||||||
|
class VMdEditor : public VPlainTextEdit, public VEditor
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VMdEditor(VFile *p_file,
|
||||||
|
VDocument *p_doc,
|
||||||
|
MarkdownConverterType p_type,
|
||||||
|
QWidget *p_parent = nullptr);
|
||||||
|
|
||||||
|
void beginEdit() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void endEdit() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void saveFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void reloadFile() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool scrollToBlock(int p_blockNumber) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void makeBlockVisible(const QTextBlock &p_block) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
QVariant inputMethodQuery(Qt::InputMethodQuery p_query) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool isBlockVisible(const QTextBlock &p_block) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
// An image has been inserted. The image is relative.
|
||||||
|
// @p_path is the absolute path of the inserted image.
|
||||||
|
void imageInserted(const QString &p_path);
|
||||||
|
|
||||||
|
// Scroll to header @p_blockNumber.
|
||||||
|
// Return true if @p_blockNumber is valid to scroll to.
|
||||||
|
bool scrollToHeader(int p_blockNumber);
|
||||||
|
|
||||||
|
void scrollBlockInPage(int p_blockNum, int p_dest) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void updateConfig() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool jumpTitle(bool p_forward, int p_relativeLevel, int p_repeat) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
// Wrapper functions for QPlainTextEdit/QTextEdit.
|
||||||
|
public:
|
||||||
|
void setExtraSelectionsW(const QList<QTextEdit::ExtraSelection> &p_selections) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
setExtraSelections(p_selections);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextDocument *documentW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return document();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTabStopWidthW(int p_width) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
setTabStopWidth(p_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextCursor textCursorW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return textCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveCursorW(QTextCursor::MoveOperation p_operation,
|
||||||
|
QTextCursor::MoveMode p_mode) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
moveCursor(p_operation, p_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar *verticalScrollBarW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return verticalScrollBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar *horizontalScrollBarW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return horizontalScrollBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTextCursorW(const QTextCursor &p_cursor) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
setTextCursor(p_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findW(const QString &p_exp,
|
||||||
|
QTextDocument::FindFlags p_options = QTextDocument::FindFlags()) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return find(p_exp, p_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findW(const QRegExp &p_exp,
|
||||||
|
QTextDocument::FindFlags p_options = QTextDocument::FindFlags()) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return find(p_exp, p_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReadOnlyW(bool p_ro) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
setReadOnly(p_ro);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *viewportW() const Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
return viewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertPlainTextW(const QString &p_text) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
insertPlainText(p_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void undoW() Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void redoW() Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
redo();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Signal when headers change.
|
||||||
|
void headersChanged(const QVector<VTableOfContentItem> &p_headers);
|
||||||
|
|
||||||
|
// Signal when current header change.
|
||||||
|
void currentHeaderChanged(int p_blockNumber);
|
||||||
|
|
||||||
|
// Signal when the status of VMdEdit changed.
|
||||||
|
// Will be emitted by VImagePreviewer for now.
|
||||||
|
void statusChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateFontAndPalette() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void contextMenuEvent(QContextMenuEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
// Used to implement dragging mouse with Ctrl and left button pressed to scroll.
|
||||||
|
void mousePressEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void mouseReleaseEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
bool canInsertFromMimeData(const QMimeData *p_source) const Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
void insertFromMimeData(const QMimeData *p_source) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// Update m_headers according to elements.
|
||||||
|
void updateHeaders(const QVector<VElementRegion> &p_headerRegions);
|
||||||
|
|
||||||
|
// Update current header according to cursor position.
|
||||||
|
// When there is no header in current cursor, will signal an invalid header.
|
||||||
|
void updateCurrentHeader();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Update the config of VPlainTextEdit according to global configurations.
|
||||||
|
void updatePlainTextEditConfig();
|
||||||
|
|
||||||
|
// Get the initial images from file before edit.
|
||||||
|
void initInitImages();
|
||||||
|
|
||||||
|
// Clear two kind of images according to initial images and current images:
|
||||||
|
// 1. Newly inserted images which are deleted later;
|
||||||
|
// 2. Initial images which are deleted;
|
||||||
|
void clearUnusedImages();
|
||||||
|
|
||||||
|
// Index in m_headers of current header which contains the cursor.
|
||||||
|
int indexOfCurrentHeader() const;
|
||||||
|
|
||||||
|
HGMarkdownHighlighter *m_mdHighlighter;
|
||||||
|
|
||||||
|
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||||
|
|
||||||
|
VPreviewManager *m_previewMgr;
|
||||||
|
|
||||||
|
// Image links inserted while editing.
|
||||||
|
QVector<ImageLink> m_insertedImages;
|
||||||
|
|
||||||
|
// Image links right at the beginning of the edit.
|
||||||
|
QVector<ImageLink> m_initImages;
|
||||||
|
|
||||||
|
// Mainly used for title jump.
|
||||||
|
QVector<VTableOfContentItem> m_headers;
|
||||||
|
|
||||||
|
bool m_freshEdit;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VMDEDITOR_H
|
@ -12,11 +12,14 @@
|
|||||||
#include "vmarkdownconverter.h"
|
#include "vmarkdownconverter.h"
|
||||||
#include "vnotebook.h"
|
#include "vnotebook.h"
|
||||||
#include "vtableofcontent.h"
|
#include "vtableofcontent.h"
|
||||||
#include "vmdedit.h"
|
|
||||||
#include "dialog/vfindreplacedialog.h"
|
#include "dialog/vfindreplacedialog.h"
|
||||||
#include "veditarea.h"
|
#include "veditarea.h"
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vwebview.h"
|
#include "vwebview.h"
|
||||||
|
#include "vmdeditor.h"
|
||||||
|
#include "vmainwindow.h"
|
||||||
|
|
||||||
|
extern VMainWindow *g_mainWin;
|
||||||
|
|
||||||
extern VConfigManager *g_config;
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
@ -124,7 +127,7 @@ bool VMdTab::scrollEditorToHeader(const VHeaderPointer &p_header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VMdEdit *mdEdit = dynamic_cast<VMdEdit *>(getEditor());
|
VMdEditor *mdEdit = getEditor();
|
||||||
|
|
||||||
int blockNumber = -1;
|
int blockNumber = -1;
|
||||||
if (p_header.isValid()) {
|
if (p_header.isValid()) {
|
||||||
@ -185,8 +188,7 @@ void VMdTab::showFileEditMode()
|
|||||||
|
|
||||||
m_isEditMode = true;
|
m_isEditMode = true;
|
||||||
|
|
||||||
VMdEdit *mdEdit = dynamic_cast<VMdEdit *>(getEditor());
|
VMdEditor *mdEdit = getEditor();
|
||||||
V_ASSERT(mdEdit);
|
|
||||||
|
|
||||||
mdEdit->beginEdit();
|
mdEdit->beginEdit();
|
||||||
m_stacks->setCurrentWidget(mdEdit);
|
m_stacks->setCurrentWidget(mdEdit);
|
||||||
@ -376,34 +378,35 @@ void VMdTab::setupMarkdownViewer()
|
|||||||
void VMdTab::setupMarkdownEditor()
|
void VMdTab::setupMarkdownEditor()
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_editor);
|
Q_ASSERT(!m_editor);
|
||||||
qDebug() << "create Markdown editor";
|
|
||||||
|
|
||||||
m_editor = new VMdEdit(m_file, m_document, m_mdConType, this);
|
m_editor = new VMdEditor(m_file, m_document, m_mdConType, this);
|
||||||
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::headersChanged,
|
connect(m_editor, &VMdEditor::headersChanged,
|
||||||
this, &VMdTab::updateOutlineFromHeaders);
|
this, &VMdTab::updateOutlineFromHeaders);
|
||||||
connect(dynamic_cast<VMdEdit *>(m_editor), SIGNAL(currentHeaderChanged(int)),
|
connect(m_editor, SIGNAL(currentHeaderChanged(int)),
|
||||||
this, SLOT(updateCurrentHeader(int)));
|
this, SLOT(updateCurrentHeader(int)));
|
||||||
connect(dynamic_cast<VMdEdit *>(m_editor), &VMdEdit::statusChanged,
|
connect(m_editor, &VMdEditor::statusChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::textChanged,
|
connect(m_editor, &VMdEditor::textChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::cursorPositionChanged,
|
connect(m_editor, &VMdEditor::cursorPositionChanged,
|
||||||
this, &VMdTab::updateStatus);
|
this, &VMdTab::updateStatus);
|
||||||
connect(m_editor, &VEdit::saveAndRead,
|
connect(g_mainWin, &VMainWindow::editorConfigUpdated,
|
||||||
|
m_editor, &VMdEditor::updateConfig);
|
||||||
|
connect(m_editor->object(), &VEditorObject::saveAndRead,
|
||||||
this, &VMdTab::saveAndRead);
|
this, &VMdTab::saveAndRead);
|
||||||
connect(m_editor, &VEdit::discardAndRead,
|
connect(m_editor->object(), &VEditorObject::discardAndRead,
|
||||||
this, &VMdTab::discardAndRead);
|
this, &VMdTab::discardAndRead);
|
||||||
connect(m_editor, &VEdit::saveNote,
|
connect(m_editor->object(), &VEditorObject::saveNote,
|
||||||
this, &VMdTab::saveFile);
|
this, &VMdTab::saveFile);
|
||||||
connect(m_editor, &VEdit::statusMessage,
|
connect(m_editor->object(), &VEditorObject::statusMessage,
|
||||||
this, &VEditTab::statusMessage);
|
this, &VEditTab::statusMessage);
|
||||||
connect(m_editor, &VEdit::vimStatusUpdated,
|
connect(m_editor->object(), &VEditorObject::vimStatusUpdated,
|
||||||
this, &VEditTab::vimStatusUpdated);
|
this, &VEditTab::vimStatusUpdated);
|
||||||
connect(m_editor, &VEdit::requestCloseFindReplaceDialog,
|
connect(m_editor->object(), &VEditorObject::requestCloseFindReplaceDialog,
|
||||||
this, [this]() {
|
this, [this]() {
|
||||||
this->m_editArea->getFindReplaceDialog()->closeDialog();
|
this->m_editArea->getFindReplaceDialog()->closeDialog();
|
||||||
});
|
});
|
||||||
connect(m_editor, SIGNAL(ready(void)),
|
connect(m_editor->object(), SIGNAL(ready(void)),
|
||||||
this, SLOT(restoreFromTabInfo(void)));
|
this, SLOT(restoreFromTabInfo(void)));
|
||||||
|
|
||||||
enableHeadingSequence(m_enableHeadingSequence);
|
enableHeadingSequence(m_enableHeadingSequence);
|
||||||
|
12
src/vmdtab.h
12
src/vmdtab.h
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
class VWebView;
|
class VWebView;
|
||||||
class QStackedLayout;
|
class QStackedLayout;
|
||||||
class VEdit;
|
|
||||||
class VDocument;
|
class VDocument;
|
||||||
|
class VMdEditor;
|
||||||
|
|
||||||
class VMdTab : public VEditTab
|
class VMdTab : public VEditTab
|
||||||
{
|
{
|
||||||
@ -55,7 +55,7 @@ public:
|
|||||||
|
|
||||||
VWebView *getWebViewer() const;
|
VWebView *getWebViewer() const;
|
||||||
|
|
||||||
VEdit *getEditor() const;
|
VMdEditor *getEditor() const;
|
||||||
|
|
||||||
MarkdownConverterType getMarkdownConverterType() const;
|
MarkdownConverterType getMarkdownConverterType() const;
|
||||||
|
|
||||||
@ -148,13 +148,13 @@ private:
|
|||||||
void focusChild() Q_DECL_OVERRIDE;
|
void focusChild() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
// Get the markdown editor. If not init yet, init and return it.
|
// Get the markdown editor. If not init yet, init and return it.
|
||||||
VEdit *getEditor();
|
VMdEditor *getEditor();
|
||||||
|
|
||||||
// Restore from @p_fino.
|
// Restore from @p_fino.
|
||||||
// Return true if succeed.
|
// Return true if succeed.
|
||||||
bool restoreFromTabInfo(const VEditTabInfo &p_info) Q_DECL_OVERRIDE;
|
bool restoreFromTabInfo(const VEditTabInfo &p_info) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
VEdit *m_editor;
|
VMdEditor *m_editor;
|
||||||
VWebView *m_webViewer;
|
VWebView *m_webViewer;
|
||||||
VDocument *m_document;
|
VDocument *m_document;
|
||||||
MarkdownConverterType m_mdConType;
|
MarkdownConverterType m_mdConType;
|
||||||
@ -165,7 +165,7 @@ private:
|
|||||||
QStackedLayout *m_stacks;
|
QStackedLayout *m_stacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline VEdit *VMdTab::getEditor()
|
inline VMdEditor *VMdTab::getEditor()
|
||||||
{
|
{
|
||||||
if (m_editor) {
|
if (m_editor) {
|
||||||
return m_editor;
|
return m_editor;
|
||||||
@ -175,7 +175,7 @@ inline VEdit *VMdTab::getEditor()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline VEdit *VMdTab::getEditor() const
|
inline VMdEditor *VMdTab::getEditor() const
|
||||||
{
|
{
|
||||||
return m_editor;
|
return m_editor;
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,11 @@ const int VPlainTextEdit::c_minimumImageWidth = 100;
|
|||||||
|
|
||||||
enum class BlockState
|
enum class BlockState
|
||||||
{
|
{
|
||||||
Normal = 1,
|
Normal = 0,
|
||||||
CodeBlockStart,
|
CodeBlockStart,
|
||||||
CodeBlock,
|
CodeBlock,
|
||||||
CodeBlockEnd
|
CodeBlockEnd,
|
||||||
|
Comment
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -79,6 +80,8 @@ void VPlainTextEdit::updateBlockImages(const QVector<VBlockImageInfo> &p_blocksI
|
|||||||
{
|
{
|
||||||
if (m_blockImageEnabled) {
|
if (m_blockImageEnabled) {
|
||||||
m_imageMgr->updateBlockInfos(p_blocksInfo, m_maximumImageWidth);
|
m_imageMgr->updateBlockInfos(p_blocksInfo, m_maximumImageWidth);
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,6 +301,9 @@ void VPlainTextEdit::drawImageOfBlock(const QTextBlock &p_block,
|
|||||||
qMax(info->m_imageHeight, tmpRect.height() - oriHeight));
|
qMax(info->m_imageHeight, tmpRect.height() - oriHeight));
|
||||||
|
|
||||||
p_painter->drawPixmap(targetRect, *image);
|
p_painter->drawPixmap(targetRect, *image);
|
||||||
|
|
||||||
|
auto *layout = getLayout();
|
||||||
|
emit layout->documentSizeChanged(layout->documentSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF VPlainTextEdit::originalBlockBoundingRect(const QTextBlock &p_block) const
|
QRectF VPlainTextEdit::originalBlockBoundingRect(const QTextBlock &p_block) const
|
||||||
@ -322,14 +328,23 @@ void VPlainTextEdit::setBlockImageEnabled(bool p_enabled)
|
|||||||
void VPlainTextEdit::setImageWidthConstrainted(bool p_enabled)
|
void VPlainTextEdit::setImageWidthConstrainted(bool p_enabled)
|
||||||
{
|
{
|
||||||
m_imageWidthConstrainted = p_enabled;
|
m_imageWidthConstrainted = p_enabled;
|
||||||
|
|
||||||
|
updateImageWidth();
|
||||||
|
|
||||||
|
auto *layout = getLayout();
|
||||||
|
emit layout->documentSizeChanged(layout->documentSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPlainTextEdit::resizeEvent(QResizeEvent *p_event)
|
void VPlainTextEdit::updateImageWidth()
|
||||||
{
|
{
|
||||||
bool needUpdate = false;
|
bool needUpdate = false;
|
||||||
if (m_imageWidthConstrainted) {
|
if (m_imageWidthConstrainted) {
|
||||||
const QSize &si = p_event->size();
|
int viewWidth = viewport()->size().width();
|
||||||
m_maximumImageWidth = si.width();
|
m_maximumImageWidth = viewWidth - 10;
|
||||||
|
if (m_maximumImageWidth < 0) {
|
||||||
|
m_maximumImageWidth = viewWidth;
|
||||||
|
}
|
||||||
|
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
} else if (m_maximumImageWidth != INT_MAX) {
|
} else if (m_maximumImageWidth != INT_MAX) {
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
@ -339,6 +354,11 @@ void VPlainTextEdit::resizeEvent(QResizeEvent *p_event)
|
|||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
m_imageMgr->updateImageWidth(m_maximumImageWidth);
|
m_imageMgr->updateImageWidth(m_maximumImageWidth);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPlainTextEdit::resizeEvent(QResizeEvent *p_event)
|
||||||
|
{
|
||||||
|
updateImageWidth();
|
||||||
|
|
||||||
QPlainTextEdit::resizeEvent(p_event);
|
QPlainTextEdit::resizeEvent(p_event);
|
||||||
|
|
||||||
@ -368,9 +388,8 @@ void VPlainTextEdit::paintLineNumberArea(QPaintEvent *p_event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int blockNumber = block.blockNumber();
|
int blockNumber = block.blockNumber();
|
||||||
int offsetY = (int)contentOffset().y();
|
QRectF rect = blockBoundingGeometry(block);
|
||||||
QRectF rect = blockBoundingRect(block);
|
int top = (int)(contentOffset().y() + rect.y());
|
||||||
int top = offsetY + (int)rect.y();
|
|
||||||
int bottom = top + (int)rect.height();
|
int bottom = top + (int)rect.height();
|
||||||
int eventTop = p_event->rect().top();
|
int eventTop = p_event->rect().top();
|
||||||
int eventBtm = p_event->rect().bottom();
|
int eventBtm = p_event->rect().bottom();
|
||||||
@ -496,7 +515,9 @@ void VPlainTextEdit::updateLineNumberAreaMargin()
|
|||||||
width = m_lineNumberArea->calculateWidth();
|
width = m_lineNumberArea->calculateWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewportMargins(width, 0, 0, 0);
|
if (width != viewportMargins().left()) {
|
||||||
|
setViewportMargins(width, 0, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VPlainTextEdit::updateLineNumberArea()
|
void VPlainTextEdit::updateLineNumberArea()
|
||||||
|
@ -83,6 +83,8 @@ public:
|
|||||||
|
|
||||||
void setLineNumberType(LineNumberType p_type);
|
void setLineNumberType(LineNumberType p_type);
|
||||||
|
|
||||||
|
void setLineNumberColor(const QColor &p_foreground, const QColor &p_background);
|
||||||
|
|
||||||
// The minimum width of an image in pixels.
|
// The minimum width of an image in pixels.
|
||||||
static const int c_minimumImageWidth;
|
static const int c_minimumImageWidth;
|
||||||
|
|
||||||
@ -111,6 +113,8 @@ private:
|
|||||||
|
|
||||||
VPlainTextDocumentLayout *getLayout() const;
|
VPlainTextDocumentLayout *getLayout() const;
|
||||||
|
|
||||||
|
void updateImageWidth();
|
||||||
|
|
||||||
// Widget to display line number area.
|
// Widget to display line number area.
|
||||||
VLineNumberArea *m_lineNumberArea;
|
VLineNumberArea *m_lineNumberArea;
|
||||||
|
|
||||||
@ -176,4 +180,11 @@ inline void VPlainTextEdit::setLineNumberType(LineNumberType p_type)
|
|||||||
updateLineNumberArea();
|
updateLineNumberArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void VPlainTextEdit::setLineNumberColor(const QColor &p_foreground,
|
||||||
|
const QColor &p_background)
|
||||||
|
{
|
||||||
|
m_lineNumberArea->setForegroundColor(p_foreground);
|
||||||
|
m_lineNumberArea->setBackgroundColor(p_background);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // VPLAINTEXTEDIT_H
|
#endif // VPLAINTEXTEDIT_H
|
||||||
|
316
src/vpreviewmanager.cpp
Normal file
316
src/vpreviewmanager.cpp
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
#include "vpreviewmanager.h"
|
||||||
|
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QVector>
|
||||||
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
#include "vdownloader.h"
|
||||||
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
#include "vtextblockdata.h"
|
||||||
|
|
||||||
|
extern VConfigManager *g_config;
|
||||||
|
|
||||||
|
VPreviewManager::VPreviewManager(VMdEditor *p_editor)
|
||||||
|
: QObject(p_editor),
|
||||||
|
m_editor(p_editor),
|
||||||
|
m_previewEnabled(false)
|
||||||
|
{
|
||||||
|
m_blockImageInfo.resize(PreviewSource::Invalid);
|
||||||
|
|
||||||
|
m_downloader = new VDownloader(this);
|
||||||
|
connect(m_downloader, &VDownloader::downloadFinished,
|
||||||
|
this, &VPreviewManager::imageDownloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions)
|
||||||
|
{
|
||||||
|
if (!m_previewEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imageRegions = p_imageRegions;
|
||||||
|
|
||||||
|
previewImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::imageDownloaded(const QByteArray &p_data, const QString &p_url)
|
||||||
|
{
|
||||||
|
if (!m_previewEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_urlToName.find(p_url);
|
||||||
|
if (it == m_urlToName.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = it.value();
|
||||||
|
m_urlToName.erase(it);
|
||||||
|
|
||||||
|
if (m_editor->containsImage(name) || name.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap image;
|
||||||
|
image.loadFromData(p_data);
|
||||||
|
|
||||||
|
if (!image.isNull()) {
|
||||||
|
m_editor->addImage(name, image);
|
||||||
|
qDebug() << "downloaded image inserted in resource manager" << p_url << name;
|
||||||
|
emit requestUpdateImageLinks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::setPreviewEnabled(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_previewEnabled != p_enabled) {
|
||||||
|
m_previewEnabled = p_enabled;
|
||||||
|
|
||||||
|
if (!m_previewEnabled) {
|
||||||
|
clearPreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::clearPreview()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_blockImageInfo.size(); ++i) {
|
||||||
|
m_blockImageInfo[i].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEditorBlockImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::previewImages()
|
||||||
|
{
|
||||||
|
QVector<ImageLinkInfo> imageLinks;
|
||||||
|
fetchImageLinksFromRegions(imageLinks);
|
||||||
|
|
||||||
|
updateBlockImageInfo(imageLinks);
|
||||||
|
|
||||||
|
updateEditorBlockImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if p_text[p_start, p_end) is all spaces.
|
||||||
|
static bool isAllSpaces(const QString &p_text, int p_start, int p_end)
|
||||||
|
{
|
||||||
|
int len = qMin(p_text.size(), p_end);
|
||||||
|
for (int i = p_start; i < len; ++i) {
|
||||||
|
if (!p_text[i].isSpace()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_imageLinks)
|
||||||
|
{
|
||||||
|
p_imageLinks.clear();
|
||||||
|
|
||||||
|
if (m_imageRegions.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_imageLinks.reserve(m_imageRegions.size());
|
||||||
|
|
||||||
|
QTextDocument *doc = m_editor->document();
|
||||||
|
|
||||||
|
for (int i = 0; i < m_imageRegions.size(); ++i) {
|
||||||
|
VElementRegion ® = m_imageRegions[i];
|
||||||
|
QTextBlock block = doc->findBlock(reg.m_startPos);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockStart = block.position();
|
||||||
|
int blockEnd = blockStart + block.length() - 1;
|
||||||
|
QString text = block.text();
|
||||||
|
Q_ASSERT(reg.m_endPos <= blockEnd);
|
||||||
|
ImageLinkInfo info(reg.m_startPos,
|
||||||
|
reg.m_endPos,
|
||||||
|
block.blockNumber(),
|
||||||
|
calculateBlockMargin(block));
|
||||||
|
if ((reg.m_startPos == blockStart
|
||||||
|
|| isAllSpaces(text, 0, reg.m_startPos - blockStart))
|
||||||
|
&& (reg.m_endPos == blockEnd
|
||||||
|
|| isAllSpaces(text, reg.m_endPos - blockStart, blockEnd - blockStart))) {
|
||||||
|
// Image block.
|
||||||
|
info.m_isBlock = true;
|
||||||
|
info.m_linkUrl = fetchImagePathToPreview(text, info.m_linkShortUrl);
|
||||||
|
} else {
|
||||||
|
// Inline image.
|
||||||
|
info.m_isBlock = false;
|
||||||
|
info.m_linkUrl = fetchImagePathToPreview(text.mid(reg.m_startPos - blockStart,
|
||||||
|
reg.m_endPos - reg.m_startPos),
|
||||||
|
info.m_linkShortUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.m_linkUrl.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_imageLinks.append(info);
|
||||||
|
|
||||||
|
qDebug() << "image region" << i
|
||||||
|
<< info.m_startPos << info.m_endPos << info.m_blockNumber
|
||||||
|
<< info.m_linkShortUrl << info.m_linkUrl << info.m_isBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VPreviewManager::fetchImageUrlToPreview(const QString &p_text)
|
||||||
|
{
|
||||||
|
QRegExp regExp(VUtils::c_imageLinkRegExp);
|
||||||
|
|
||||||
|
int index = regExp.indexIn(p_text);
|
||||||
|
if (index == -1) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastIndex = regExp.lastIndexIn(p_text);
|
||||||
|
if (lastIndex != index) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return regExp.capturedTexts()[2].trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VPreviewManager::fetchImagePathToPreview(const QString &p_text, QString &p_url)
|
||||||
|
{
|
||||||
|
p_url = fetchImageUrlToPreview(p_text);
|
||||||
|
if (p_url.isEmpty()) {
|
||||||
|
return p_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VFile *file = m_editor->getFile();
|
||||||
|
|
||||||
|
QString imagePath;
|
||||||
|
QFileInfo info(file->fetchBasePath(), p_url);
|
||||||
|
|
||||||
|
if (info.exists()) {
|
||||||
|
if (info.isNativePath()) {
|
||||||
|
// Local file.
|
||||||
|
imagePath = QDir::cleanPath(info.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
imagePath = p_url;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QString decodedUrl(p_url);
|
||||||
|
VUtils::decodeUrl(decodedUrl);
|
||||||
|
QFileInfo dinfo(file->fetchBasePath(), decodedUrl);
|
||||||
|
if (dinfo.exists()) {
|
||||||
|
if (dinfo.isNativePath()) {
|
||||||
|
// Local file.
|
||||||
|
imagePath = QDir::cleanPath(dinfo.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
imagePath = p_url;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QUrl url(p_url);
|
||||||
|
imagePath = url.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::updateBlockImageInfo(const QVector<ImageLinkInfo> &p_imageLinks)
|
||||||
|
{
|
||||||
|
QVector<VBlockImageInfo> &blockInfos = m_blockImageInfo[PreviewSource::ImageLink];
|
||||||
|
blockInfos.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < p_imageLinks.size(); ++i) {
|
||||||
|
const ImageLinkInfo &link = p_imageLinks[i];
|
||||||
|
|
||||||
|
// Skip inline images.
|
||||||
|
if (!link.m_isBlock) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = imageResourceName(link);
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VBlockImageInfo info(link.m_blockNumber, name, link.m_margin);
|
||||||
|
blockInfos.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString VPreviewManager::imageResourceName(const ImageLinkInfo &p_link)
|
||||||
|
{
|
||||||
|
QString name = p_link.m_linkShortUrl;
|
||||||
|
if (m_editor->containsImage(name)
|
||||||
|
|| name.isEmpty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add it to the resource.
|
||||||
|
QString imgPath = p_link.m_linkUrl;
|
||||||
|
QFileInfo info(imgPath);
|
||||||
|
QPixmap image;
|
||||||
|
if (info.exists()) {
|
||||||
|
// Local file.
|
||||||
|
image = QPixmap(imgPath);
|
||||||
|
} else {
|
||||||
|
// URL. Try to download it.
|
||||||
|
m_downloader->download(imgPath);
|
||||||
|
m_urlToName.insert(imgPath, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.isNull()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_editor->addImage(name, image);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VPreviewManager::updateEditorBlockImages()
|
||||||
|
{
|
||||||
|
// TODO: need to combine all preview sources.
|
||||||
|
Q_ASSERT(m_blockImageInfo.size() == 1);
|
||||||
|
|
||||||
|
m_editor->updateBlockImages(m_blockImageInfo[PreviewSource::ImageLink]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int VPreviewManager::calculateBlockMargin(const QTextBlock &p_block)
|
||||||
|
{
|
||||||
|
static QHash<QString, int> spaceWidthOfFonts;
|
||||||
|
|
||||||
|
if (!p_block.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString text = p_block.text();
|
||||||
|
int nrSpaces = 0;
|
||||||
|
for (int i = 0; i < text.size(); ++i) {
|
||||||
|
if (!text[i].isSpace()) {
|
||||||
|
break;
|
||||||
|
} else if (text[i] == ' ') {
|
||||||
|
++nrSpaces;
|
||||||
|
} else if (text[i] == '\t') {
|
||||||
|
nrSpaces += m_editor->tabStopWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nrSpaces == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spaceWidth = 0;
|
||||||
|
QFont font = p_block.charFormat().font();
|
||||||
|
QString fontName = font.toString();
|
||||||
|
auto it = spaceWidthOfFonts.find(fontName);
|
||||||
|
if (it != spaceWidthOfFonts.end()) {
|
||||||
|
spaceWidth = it.value();
|
||||||
|
} else {
|
||||||
|
spaceWidth = QFontMetrics(font).width(' ');
|
||||||
|
spaceWidthOfFonts.insert(fontName, spaceWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return spaceWidth * nrSpaces;
|
||||||
|
}
|
135
src/vpreviewmanager.h
Normal file
135
src/vpreviewmanager.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#ifndef VPREVIEWMANAGER_H
|
||||||
|
#define VPREVIEWMANAGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QVector>
|
||||||
|
#include "hgmarkdownhighlighter.h"
|
||||||
|
#include "vmdeditor.h"
|
||||||
|
|
||||||
|
class VDownloader;
|
||||||
|
|
||||||
|
|
||||||
|
class VPreviewManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VPreviewManager(VMdEditor *p_editor);
|
||||||
|
|
||||||
|
void setPreviewEnabled(bool p_enabled);
|
||||||
|
|
||||||
|
// Clear all the preview.
|
||||||
|
void clearPreview();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Image links were updated from the highlighter.
|
||||||
|
void imageLinksUpdated(const QVector<VElementRegion> &p_imageRegions);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Request highlighter to update image links.
|
||||||
|
void requestUpdateImageLinks();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// Non-local image downloaded for preview.
|
||||||
|
void imageDownloaded(const QByteArray &p_data, const QString &p_url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sources of the preview.
|
||||||
|
enum PreviewSource
|
||||||
|
{
|
||||||
|
ImageLink = 0,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImageLinkInfo
|
||||||
|
{
|
||||||
|
ImageLinkInfo()
|
||||||
|
: m_startPos(-1),
|
||||||
|
m_endPos(-1),
|
||||||
|
m_blockNumber(-1),
|
||||||
|
m_margin(0),
|
||||||
|
m_isBlock(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageLinkInfo(int p_startPos,
|
||||||
|
int p_endPos,
|
||||||
|
int p_blockNumber,
|
||||||
|
int p_margin)
|
||||||
|
: m_startPos(p_startPos),
|
||||||
|
m_endPos(p_endPos),
|
||||||
|
m_blockNumber(p_blockNumber),
|
||||||
|
m_margin(p_margin),
|
||||||
|
m_isBlock(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_startPos;
|
||||||
|
|
||||||
|
int m_endPos;
|
||||||
|
|
||||||
|
int m_blockNumber;
|
||||||
|
|
||||||
|
// Left margin of this block in pixels.
|
||||||
|
int m_margin;
|
||||||
|
|
||||||
|
// Short URL within the () of ![]().
|
||||||
|
// Used as the ID of the image.
|
||||||
|
QString m_linkShortUrl;
|
||||||
|
|
||||||
|
// Full URL of the link.
|
||||||
|
QString m_linkUrl;
|
||||||
|
|
||||||
|
// Whether it is an image block.
|
||||||
|
bool m_isBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start to preview images according to image links.
|
||||||
|
void previewImages();
|
||||||
|
|
||||||
|
// According to m_imageRegions, fetch the image link Url.
|
||||||
|
// @p_imageRegions: output.
|
||||||
|
void fetchImageLinksFromRegions(QVector<ImageLinkInfo> &p_imageLinks);
|
||||||
|
|
||||||
|
// Fetch the image link's URL if there is only one link.
|
||||||
|
QString fetchImageUrlToPreview(const QString &p_text);
|
||||||
|
|
||||||
|
// Fetch teh image's full path if there is only one image link.
|
||||||
|
// @p_url: contains the short URL in ![]().
|
||||||
|
QString fetchImagePathToPreview(const QString &p_text, QString &p_url);
|
||||||
|
|
||||||
|
void updateBlockImageInfo(const QVector<ImageLinkInfo> &p_imageLinks);
|
||||||
|
|
||||||
|
// Get the name of the image in the resource manager.
|
||||||
|
// Will add the image to the resource manager if not exists.
|
||||||
|
// Returns empty if fail to add the image to the resource manager.
|
||||||
|
QString imageResourceName(const ImageLinkInfo &p_link);
|
||||||
|
|
||||||
|
// Ask the editor to preview images.
|
||||||
|
void updateEditorBlockImages();
|
||||||
|
|
||||||
|
// Calculate the block margin (prefix spaces) in pixels.
|
||||||
|
int calculateBlockMargin(const QTextBlock &p_block);
|
||||||
|
|
||||||
|
VMdEditor *m_editor;
|
||||||
|
|
||||||
|
VDownloader *m_downloader;
|
||||||
|
|
||||||
|
// Whether preview is enabled.
|
||||||
|
bool m_previewEnabled;
|
||||||
|
|
||||||
|
// Regions of all the image links.
|
||||||
|
QVector<VElementRegion> m_imageRegions;
|
||||||
|
|
||||||
|
// All preview images and information.
|
||||||
|
// Each preview source corresponds to one vector.
|
||||||
|
QVector<QVector<VBlockImageInfo>> m_blockImageInfo;
|
||||||
|
|
||||||
|
// Map from URL to name in the resource manager.
|
||||||
|
// Used for downloading images.
|
||||||
|
QHash<QString, QString> m_urlToName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VPREVIEWMANAGER_H
|
Loading…
x
Reference in New Issue
Block a user