| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955 |
- #include <QtDebug>
- #include <QImage>
- #include <QVariant>
- #include <QMimeData>
- #include <QWidget>
- #include <QImageReader>
- #include <QDir>
- #include <QMessageBox>
- #include <QKeyEvent>
- #include <QTextCursor>
- #include <QTimer>
- #include <QGuiApplication>
- #include <QApplication>
- #include <QClipboard>
- #include "vmdeditoperations.h"
- #include "dialog/vinsertimagedialog.h"
- #include "dialog/vselectdialog.h"
- #include "utils/vutils.h"
- #include "veditor.h"
- #include "vdownloader.h"
- #include "vfile.h"
- #include "vmdeditor.h"
- #include "vconfigmanager.h"
- #include "utils/vvim.h"
- #include "utils/veditutils.h"
- extern VConfigManager *g_config;
- const QString VMdEditOperations::c_defaultImageTitle = "";
- VMdEditOperations::VMdEditOperations(VEditor *p_editor, VFile *p_file)
- : VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
- {
- }
- bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
- {
- QImage image = qvariant_cast<QImage>(source->imageData());
- if (image.isNull()) {
- return false;
- }
- VInsertImageDialog dialog(tr("Insert Image From Clipboard"),
- c_defaultImageTitle,
- "",
- m_editor->getEditor());
- dialog.setBrowseable(false);
- dialog.setImage(image);
- if (dialog.exec() == QDialog::Accepted) {
- insertImageFromQImage(dialog.getImageTitleInput(),
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- image);
- }
- return true;
- }
- void VMdEditOperations::insertImageFromQImage(const QString &title, const QString &path,
- const QString &folderInLink, const QImage &image)
- {
- QString fileName = VUtils::generateImageFileName(path, title);
- QString filePath = QDir(path).filePath(fileName);
- V_ASSERT(!QFile(filePath).exists());
- QString errStr;
- bool ret = VUtils::makePath(path);
- if (!ret) {
- errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
- .arg(g_config->c_dataTextStyle).arg(path);
- } else {
- ret = image.save(filePath);
- if (!ret) {
- errStr = tr("Fail to save image <span style=\"%1\">%2</span>.")
- .arg(g_config->c_dataTextStyle).arg(filePath);
- }
- }
- if (!ret) {
- VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
- tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(title),
- errStr,
- QMessageBox::Ok,
- QMessageBox::Ok,
- m_editor->getEditor());
- return;
- }
- QString md = QString("").arg(title).arg(folderInLink).arg(fileName);
- insertTextAtCurPos(md);
- qDebug() << "insert image" << title << filePath;
- VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
- Q_ASSERT(mdEditor);
- mdEditor->imageInserted(filePath);
- }
- void VMdEditOperations::insertImageFromPath(const QString &title, const QString &path,
- const QString &folderInLink, const QString &oriImagePath)
- {
- QString fileName = VUtils::generateImageFileName(path, title, QFileInfo(oriImagePath).suffix());
- QString filePath = QDir(path).filePath(fileName);
- V_ASSERT(!QFile(filePath).exists());
- QString errStr;
- bool ret = VUtils::makePath(path);
- if (!ret) {
- errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
- .arg(g_config->c_dataTextStyle).arg(path);
- } else {
- ret = QFile::copy(oriImagePath, filePath);
- if (!ret) {
- errStr = tr("Fail to copy image <span style=\"%1\">%2</span>.")
- .arg(g_config->c_dataTextStyle).arg(filePath);
- }
- }
- if (!ret) {
- VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
- tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(title),
- errStr,
- QMessageBox::Ok,
- QMessageBox::Ok,
- m_editor->getEditor());
- return;
- }
- QString md = QString("").arg(title).arg(folderInLink).arg(fileName);
- insertTextAtCurPos(md);
- qDebug() << "insert image" << title << filePath;
- VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
- Q_ASSERT(mdEditor);
- mdEditor->imageInserted(filePath);
- }
- bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
- {
- QString imagePath;
- QImage image;
- bool isLocal = imageUrl.isLocalFile();
- QString title;
- // Whether it is a local file or web URL
- if (isLocal) {
- imagePath = imageUrl.toLocalFile();
- image = QImage(imagePath);
- if (image.isNull()) {
- qWarning() << "image is null";
- return false;
- }
- title = "Insert Image From File";
- } else {
- imagePath = imageUrl.toString();
- title = "Insert Image From Network";
- }
- VInsertImageDialog dialog(title, c_defaultImageTitle,
- imagePath, m_editor->getEditor());
- dialog.setBrowseable(false, true);
- if (isLocal) {
- dialog.setImage(image);
- } else {
- // Download it to a QImage
- VDownloader *downloader = new VDownloader(&dialog);
- connect(downloader, &VDownloader::downloadFinished,
- &dialog, &VInsertImageDialog::imageDownloaded);
- downloader->download(imageUrl.toString());
- }
- if (dialog.exec() == QDialog::Accepted) {
- if (isLocal) {
- insertImageFromPath(dialog.getImageTitleInput(),
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- imagePath);
- } else {
- insertImageFromQImage(dialog.getImageTitleInput(),
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- dialog.getImage());
- }
- }
- return true;
- }
- bool VMdEditOperations::insertImage()
- {
- VInsertImageDialog dialog(tr("Insert Image From File"),
- c_defaultImageTitle, "", m_editor->getEditor());
- if (dialog.exec() == QDialog::Accepted) {
- QString title = dialog.getImageTitleInput();
- QString imagePath = dialog.getPathInput();
- qDebug() << "insert image from" << imagePath << "as" << title;
- insertImageFromPath(title,
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- imagePath);
- }
- return true;
- }
- bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
- {
- if (m_editConfig->m_enableVimMode
- && m_vim->handleKeyPressEvent(p_event, &m_autoIndentPos)) {
- return true;
- }
- bool ret = false;
- int key = p_event->key();
- int modifiers = p_event->modifiers();
- switch (key) {
- case Qt::Key_1:
- case Qt::Key_2:
- case Qt::Key_3:
- case Qt::Key_4:
- case Qt::Key_5:
- case Qt::Key_6:
- {
- if (modifiers == Qt::ControlModifier) {
- // Ctrl + <N>: insert title at level <N>.
- if (insertTitle(key - Qt::Key_0)) {
- p_event->accept();
- ret = true;
- goto exit;
- }
- }
- break;
- }
- case Qt::Key_Tab:
- {
- if (handleKeyTab(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_Backtab:
- {
- if (handleKeyBackTab(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_B:
- {
- if (modifiers == Qt::ControlModifier) {
- decorateBold();
- p_event->accept();
- ret = true;
- }
- break;
- }
- case Qt::Key_H:
- {
- if (handleKeyH(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_I:
- {
- if (modifiers == Qt::ControlModifier) {
- decorateItalic();
- p_event->accept();
- ret = true;
- }
- break;
- }
- case Qt::Key_L:
- {
- if (modifiers == Qt::ControlModifier) {
- m_editor->insertLink();
- p_event->accept();
- ret = true;
- }
- break;
- }
- case Qt::Key_M:
- {
- if (modifiers == Qt::ControlModifier) {
- decorateCodeBlock();
- p_event->accept();
- ret = true;
- }
- break;
- }
- case Qt::Key_K:
- {
- if (modifiers == Qt::ControlModifier) {
- decorateInlineCode();
- p_event->accept();
- ret = true;
- }
- break;
- }
- case Qt::Key_U:
- {
- if (handleKeyU(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_W:
- {
- if (handleKeyW(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_BracketLeft:
- {
- if (handleKeyBracketLeft(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_Escape:
- {
- if (handleKeyEsc(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_Enter:
- // Fall through.
- case Qt::Key_Return:
- {
- if (handleKeyReturn(p_event)) {
- ret = true;
- goto exit;
- }
- break;
- }
- case Qt::Key_D:
- {
- if (modifiers == Qt::ControlModifier) {
- decorateStrikethrough();
- p_event->accept();
- ret = true;
- }
- break;
- }
- default:
- break;
- }
- exit:
- // Qt::Key_Return, Qt::Key_Tab and Qt::Key_Backtab will handle m_autoIndentPos.
- if (key != Qt::Key_Return
- && key != Qt::Key_Enter
- && key != Qt::Key_Tab
- && key != Qt::Key_Backtab
- && key != Qt::Key_Shift
- && key != Qt::Key_Control) {
- m_autoIndentPos = -1;
- }
- return ret;
- }
- // Let Ctrl+[ behave exactly like ESC.
- bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
- {
- // 1. If there is any selection, clear it.
- // 2. Otherwise, ignore this event and let parent handles it.
- if (p_event->modifiers() == Qt::ControlModifier) {
- QTextCursor cursor = m_editor->textCursorW();
- if (cursor.hasSelection()) {
- cursor.clearSelection();
- m_editor->setTextCursorW(cursor);
- p_event->accept();
- return true;
- }
- }
- return false;
- }
- bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
- {
- QTextDocument *doc = m_editor->documentW();
- QString text(m_editConfig->m_tabSpaces);
- if (p_event->modifiers() == Qt::NoModifier) {
- QTextCursor cursor = m_editor->textCursorW();
- if (cursor.hasSelection()) {
- m_autoIndentPos = -1;
- cursor.beginEditBlock();
- // Indent each selected line.
- VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- } else {
- // If it is a Tab key following auto list, increase the indent level.
- QTextBlock block = cursor.block();
- int seq = -1;
- if (m_autoIndentPos == cursor.position()
- && VEditUtils::isListBlock(block, &seq)) {
- QTextCursor blockCursor(block);
- blockCursor.beginEditBlock();
- blockCursor.insertText(text);
- if (seq != -1) {
- changeListBlockSeqNumber(block, 1);
- }
- blockCursor.endEditBlock();
- // Change m_autoIndentPos to let it can be repeated.
- m_autoIndentPos = m_editor->textCursorW().position();
- } else {
- // Just insert "tab".
- insertTextAtCurPos(text);
- m_autoIndentPos = -1;
- }
- }
- } else {
- m_autoIndentPos = -1;
- return false;
- }
- p_event->accept();
- return true;
- }
- bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
- {
- if (p_event->modifiers() != Qt::ShiftModifier) {
- m_autoIndentPos = -1;
- return false;
- }
- QTextDocument *doc = m_editor->documentW();
- QTextCursor cursor = m_editor->textCursorW();
- QTextBlock block = doc->findBlock(cursor.selectionStart());
- bool continueAutoIndent = false;
- int seq = -1;
- if (cursor.position() == m_autoIndentPos
- && VEditUtils::isListBlock(block, &seq) &&
- !cursor.hasSelection()) {
- continueAutoIndent = true;
- }
- cursor.beginEditBlock();
- if (continueAutoIndent && seq != -1) {
- changeListBlockSeqNumber(block, 1);
- }
- VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
- cursor.endEditBlock();
- if (continueAutoIndent) {
- m_autoIndentPos = m_editor->textCursorW().position();
- } else {
- m_autoIndentPos = -1;
- }
- p_event->accept();
- return true;
- }
- bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
- {
- if (p_event->modifiers() == Qt::ControlModifier) {
- // Ctrl+H, equal to backspace.
- QTextCursor cursor = m_editor->textCursorW();
- cursor.deletePreviousChar();
- p_event->accept();
- return true;
- }
- return false;
- }
- bool VMdEditOperations::handleKeyU(QKeyEvent *p_event)
- {
- if (p_event->modifiers() == Qt::ControlModifier) {
- // Ctrl+U, delete till the start of line.
- QTextCursor cursor = m_editor->textCursorW();
- bool ret;
- if (cursor.atBlockStart()) {
- ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
- } else {
- ret = cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
- }
- if (ret) {
- cursor.removeSelectedText();
- }
- p_event->accept();
- return true;
- }
- return false;
- }
- bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
- {
- if (p_event->modifiers() == Qt::ControlModifier) {
- // Ctrl+W, delete till the start of previous word.
- QTextCursor cursor = m_editor->textCursorW();
- if (cursor.hasSelection()) {
- cursor.removeSelectedText();
- } else {
- bool ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
- if (ret) {
- cursor.removeSelectedText();
- }
- }
- p_event->accept();
- return true;
- }
- return false;
- }
- bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
- {
- // 1. If there is any selection, clear it.
- // 2. Otherwise, ignore this event and let parent handles it.
- QTextCursor cursor = m_editor->textCursorW();
- if (cursor.hasSelection()) {
- cursor.clearSelection();
- m_editor->setTextCursorW(cursor);
- p_event->accept();
- return true;
- }
- return false;
- }
- bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
- {
- bool autolist = true;
- if (p_event->modifiers() & Qt::ControlModifier) {
- m_autoIndentPos = -1;
- return false;
- } else if (p_event->modifiers() & Qt::ShiftModifier) {
- // Insert two spaces and a new line.
- m_autoIndentPos = -1;
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- cursor.removeSelectedText();
- cursor.insertText(" ");
- cursor.endEditBlock();
- // Let remaining logics handle inserting the new block except that we
- // do not need to insert auto list.
- autolist = false;
- }
- // See if we need to cancel auto indent.
- if (m_autoIndentPos > -1) {
- // Cancel the auto indent/list if the pos is the same and cursor is at
- // the end of a block.
- QTextCursor cursor = m_editor->textCursorW();
- if (VEditUtils::needToCancelAutoIndent(m_autoIndentPos, cursor)) {
- m_autoIndentPos = -1;
- VEditUtils::deleteIndentAndListMark(cursor);
- m_editor->setTextCursorW(cursor);
- return true;
- }
- }
- bool handled = false;
- m_autoIndentPos = -1;
- if (g_config->getAutoIndent()) {
- handled = true;
- QTextCursor cursor = m_editor->textCursorW();
- bool textInserted = false;
- cursor.beginEditBlock();
- cursor.removeSelectedText();
- // Indent the new line as previous line.
- textInserted = VEditUtils::insertBlockWithIndent(cursor);
- // Continue the list from previous line.
- if (g_config->getAutoList() && autolist) {
- textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor) || textInserted;
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- if (textInserted) {
- m_autoIndentPos = m_editor->textCursorW().position();
- }
- }
- return handled;
- }
- void VMdEditOperations::changeListBlockSeqNumber(QTextBlock &p_block, int p_seq)
- {
- QString text = p_block.text();
- QRegExp regExp("^(\\s*)(\\d+)\\.\\s");
- int idx = regExp.indexIn(text);
- if (idx == -1 || regExp.captureCount() != 2) {
- return;
- }
- int oriSeq = -1;
- bool ok = false;
- oriSeq = regExp.capturedTexts()[2].toInt(&ok);
- if (ok && oriSeq == p_seq) {
- return;
- }
- QTextCursor cursor(p_block);
- bool ret = cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,
- regExp.capturedTexts()[1].size());
- if (!ret) {
- return;
- }
- ret = cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor,
- regExp.capturedTexts()[2].size());
- if (!ret) {
- return;
- }
- cursor.removeSelectedText();
- cursor.insertText(QString::number(p_seq));
- }
- bool VMdEditOperations::insertTitle(int p_level)
- {
- QTextDocument *doc = m_editor->documentW();
- QTextCursor cursor = m_editor->textCursorW();
- int firstBlock = cursor.block().blockNumber();
- int lastBlock = firstBlock;
- if (cursor.hasSelection()) {
- // Insert title # in front of the selected blocks.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- firstBlock = doc->findBlock(start).blockNumber();
- lastBlock = doc->findBlock(end).blockNumber();
- }
- cursor.beginEditBlock();
- for (int i = firstBlock; i <= lastBlock; ++i) {
- VEditUtils::insertTitleMark(cursor, doc->findBlockByNumber(i), p_level);
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- return true;
- }
- void VMdEditOperations::decorateText(TextDecoration p_decoration)
- {
- if (p_decoration == TextDecoration::None) {
- return;
- }
- bool validDecoration = true;
- switch (p_decoration) {
- case TextDecoration::Bold:
- decorateBold();
- break;
- case TextDecoration::Italic:
- decorateItalic();
- break;
- case TextDecoration::Strikethrough:
- decorateStrikethrough();
- break;
- case TextDecoration::InlineCode:
- decorateInlineCode();
- break;
- case TextDecoration::CodeBlock:
- decorateCodeBlock();
- break;
- default:
- validDecoration = false;
- qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
- break;
- }
- if (validDecoration && m_editConfig->m_enableVimMode) {
- Q_ASSERT(m_vim);
- m_vim->setMode(VimMode::Insert, false);
- }
- }
- void VMdEditOperations::decorateBold()
- {
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- if (cursor.hasSelection()) {
- // Insert ** around the selected text.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- cursor.clearSelection();
- cursor.setPosition(start, QTextCursor::MoveAnchor);
- cursor.insertText("**");
- cursor.setPosition(end + 2, QTextCursor::MoveAnchor);
- cursor.insertText("**");
- } else {
- // Insert **** and place cursor in the middle.
- // Or if there are two * after current cursor, just skip them.
- int pos = cursor.positionInBlock();
- bool hasStars = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 2) {
- if (text[pos] == '*' && text[pos + 1] == '*') {
- hasStars = true;
- }
- }
- if (hasStars) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
- } else {
- cursor.insertText("****");
- cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
- }
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- }
- void VMdEditOperations::decorateItalic()
- {
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- if (cursor.hasSelection()) {
- // Insert * around the selected text.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- cursor.clearSelection();
- cursor.setPosition(start, QTextCursor::MoveAnchor);
- cursor.insertText("*");
- cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
- cursor.insertText("*");
- } else {
- // Insert ** and place cursor in the middle.
- // Or if there are one * after current cursor, just skip it.
- int pos = cursor.positionInBlock();
- bool hasStar = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '*') {
- hasStar = true;
- }
- }
- if (hasStar) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
- } else {
- cursor.insertText("**");
- cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
- }
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- }
- void VMdEditOperations::decorateInlineCode()
- {
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- if (cursor.hasSelection()) {
- // Insert ` around the selected text.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- cursor.clearSelection();
- cursor.setPosition(start, QTextCursor::MoveAnchor);
- cursor.insertText("`");
- cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
- cursor.insertText("`");
- } else {
- // Insert `` and place cursor in the middle.
- // Or if there are one ` after current cursor, just skip it.
- int pos = cursor.positionInBlock();
- bool hasBackquote = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '`') {
- hasBackquote = true;
- }
- }
- if (hasBackquote) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
- } else {
- cursor.insertText("``");
- cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
- }
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- }
- void VMdEditOperations::decorateCodeBlock()
- {
- const QString marker("```");
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- if (cursor.hasSelection()) {
- // Insert ``` around the selected text.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- QString indentation = VEditUtils::fetchIndentSpaces(cursor.block());
- // Insert the end marker first.
- cursor.setPosition(end, QTextCursor::MoveAnchor);
- VEditUtils::insertBlock(cursor, false);
- VEditUtils::indentBlock(cursor, indentation);
- cursor.insertText(marker);
- // Insert the start marker.
- cursor.setPosition(start, QTextCursor::MoveAnchor);
- VEditUtils::insertBlock(cursor, true);
- VEditUtils::indentBlock(cursor, indentation);
- cursor.insertText(marker);
- } else {
- // Insert ``` ``` and place cursor after the first marker.
- // Or if current block or next block is ```, we will skip it.
- QTextBlock block = cursor.block();
- int state = block.userState();
- if (state == HighlightBlockState::CodeBlock
- || state == HighlightBlockState::CodeBlockStart
- || state == HighlightBlockState::CodeBlockEnd) {
- // Find the block end.
- while (block.isValid()) {
- if (block.userState() == HighlightBlockState::CodeBlockEnd) {
- break;
- }
- block = block.next();
- }
- if (block.isValid()) {
- // It is CodeBlockEnd.
- cursor.setPosition(block.position());
- if (block.next().isValid()) {
- cursor.movePosition(QTextCursor::NextBlock);
- cursor.movePosition(QTextCursor::StartOfBlock);
- } else {
- cursor.movePosition(QTextCursor::EndOfBlock);
- }
- } else {
- // Reach the end of the document.
- cursor.movePosition(QTextCursor::End);
- }
- } else {
- bool insertInline = false;
- if (!cursor.atBlockEnd()) {
- cursor.insertBlock();
- cursor.movePosition(QTextCursor::PreviousBlock);
- } else if (cursor.atBlockStart() || VEditUtils::isSpaceBlock(block)) {
- insertInline = true;
- }
- if (!insertInline) {
- VEditUtils::insertBlock(cursor, false);
- VEditUtils::indentBlockAsBlock(cursor, false);
- }
- cursor.insertText(marker);
- VEditUtils::insertBlock(cursor, true);
- VEditUtils::indentBlockAsBlock(cursor, true);
- cursor.insertText(marker);
- }
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- }
- void VMdEditOperations::decorateStrikethrough()
- {
- QTextCursor cursor = m_editor->textCursorW();
- cursor.beginEditBlock();
- if (cursor.hasSelection()) {
- // Insert ~~ around the selected text.
- int start = cursor.selectionStart();
- int end = cursor.selectionEnd();
- cursor.clearSelection();
- cursor.setPosition(start, QTextCursor::MoveAnchor);
- cursor.insertText("~~");
- cursor.setPosition(end + 2, QTextCursor::MoveAnchor);
- cursor.insertText("~~");
- } else {
- // Insert ~~~~ and place cursor in the middle.
- // Or if there are one ~~ after current cursor, just skip it.
- int pos = cursor.positionInBlock();
- bool hasStrikethrough = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 2) {
- if (text[pos] == '~' && text[pos + 1] == '~') {
- hasStrikethrough = true;
- }
- }
- if (hasStrikethrough) {
- cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
- } else {
- cursor.insertText("~~~~");
- cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
- }
- }
- cursor.endEditBlock();
- m_editor->setTextCursorW(cursor);
- }
- bool VMdEditOperations::insertLink(const QString &p_linkText,
- const QString &p_linkUrl)
- {
- QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
- QTextCursor cursor = m_editor->textCursorW();
- cursor.insertText(link);
- m_editor->setTextCursorW(cursor);
- setVimMode(VimMode::Insert);
- return true;
- }
|