| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066 |
- #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,
- "",
- false,
- m_editor->getEditor());
- 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 url = QString("%1/%2").arg(folderInLink).arg(fileName);
- QString md = QString("").arg(title).arg(url);
- insertTextAtCurPos(md);
- qDebug() << "insert image" << title << filePath;
- VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
- Q_ASSERT(mdEditor);
- mdEditor->imageInserted(filePath, url);
- }
- 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 url = QString("%1/%2").arg(folderInLink).arg(fileName);
- QString md = QString("").arg(title).arg(url);
- insertTextAtCurPos(md);
- qDebug() << "insert image" << title << filePath;
- VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
- Q_ASSERT(mdEditor);
- mdEditor->imageInserted(filePath, url);
- }
- 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,
- false,
- m_editor->getEditor());
- 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()
- {
- // Use empty title and path to let the dialog auto complete.
- VInsertImageDialog dialog(tr("Insert Image"),
- "",
- "",
- true,
- m_editor->getEditor());
- if (dialog.exec() == QDialog::Accepted) {
- VInsertImageDialog::ImageType type = dialog.getImageType();
- if (type == VInsertImageDialog::ImageType::LocalFile) {
- insertImageFromPath(dialog.getImageTitleInput(),
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- dialog.getPathInput());
- } else {
- QImage img = dialog.getImage();
- if (!img.isNull()) {
- insertImageFromQImage(dialog.getImageTitleInput(),
- m_file->fetchImageFolderPath(),
- m_file->getImageFolderInLink(),
- img);
- }
- }
- }
- 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:
- case Qt::Key_7:
- {
- if (modifiers == Qt::ControlModifier) {
- // Ctrl + <N>: insert title at level <N>.
- if (decorateHeading(key == Qt::Key_7 ? 0 : 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_Apostrophe:
- {
- if (modifiers == Qt::ControlModifier) {
- m_editor->insertImage();
- 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.
- V_FALLTHROUGH;
- 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)
- {
- 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(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;
- }
- QTextCursor cursor = m_editor->textCursorW();
- QTextBlock block = m_editor->documentW()->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(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::decorateHeading(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, int p_level)
- {
- 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;
- case TextDecoration::Heading:
- decorateHeading(p_level);
- 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 or delete
- // them if four * appear.
- int pos = cursor.positionInBlock();
- bool hasStars = false;
- bool emptyMarkers = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 2) {
- if (text[pos] == '*' && text[pos + 1] == '*') {
- hasStars = true;
- if (pos >= 2
- && text[pos - 1] == '*'
- && text[pos - 2] == '*') {
- emptyMarkers = true;
- }
- }
- }
- if (hasStars) {
- if (emptyMarkers) {
- cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
- cursor.removeSelectedText();
- } else {
- 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 them or delete
- // them if two * appear.
- int pos = cursor.positionInBlock();
- bool hasStar = false;
- bool emptyMarkers = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '*'
- && (pos == text.size() - 1 || text[pos + 1] != '*')) {
- hasStar = true;
- if (pos >= 1 && text[pos - 1] == '*') {
- emptyMarkers = true;
- }
- }
- }
- if (hasStar) {
- if (emptyMarkers) {
- cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
- cursor.removeSelectedText();
- } else {
- 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 them or delete
- // them if two ` appear.
- int pos = cursor.positionInBlock();
- bool hasBackquote = false;
- bool emptyMarkers = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 1) {
- if (text[pos] == '`') {
- hasBackquote = true;
- if (pos >= 1 && text[pos - 1] == '`') {
- emptyMarkers = true;
- }
- }
- }
- if (hasBackquote) {
- if (emptyMarkers) {
- cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
- cursor.removeSelectedText();
- } else {
- 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.
- QTextBlock endBlock = block;
- while (endBlock.isValid()) {
- if (endBlock.userState() == HighlightBlockState::CodeBlockEnd) {
- break;
- }
- endBlock = endBlock.next();
- }
- if (endBlock.isValid()) {
- // It is CodeBlockEnd.
- if (endBlock.previous().isValid()
- && endBlock.previous().userState() == HighlightBlockState::CodeBlockStart) {
- // Delete empty code blocks.
- cursor.setPosition(endBlock.previous().position());
- cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
- cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
- cursor.removeSelectedText();
- } else {
- cursor.setPosition(endBlock.position());
- if (endBlock.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 or delete
- // it if for ~ appear.
- int pos = cursor.positionInBlock();
- bool hasStrikethrough = false;
- bool emptyMarkers = false;
- QString text = cursor.block().text();
- if (pos <= text.size() - 2) {
- if (text[pos] == '~' && text[pos + 1] == '~') {
- hasStrikethrough = true;
- if (pos >= 2
- && text[pos - 1] == '~'
- && text[pos - 2] == '~') {
- emptyMarkers = true;
- }
- }
- }
- if (hasStrikethrough) {
- if (emptyMarkers) {
- cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
- cursor.removeSelectedText();
- } else {
- 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;
- }
- bool VMdEditOperations::insertImageLink(const QString &p_linkText,
- const QString &p_linkUrl)
- {
- QString link = QString("").arg(p_linkText).arg(p_linkUrl);
- QTextCursor cursor = m_editor->textCursorW();
- cursor.insertText(link);
- m_editor->setTextCursorW(cursor);
- setVimMode(VimMode::Insert);
- return true;
- }
|