vmdeditoperations.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. #include <QtDebug>
  2. #include <QImage>
  3. #include <QVariant>
  4. #include <QMimeData>
  5. #include <QWidget>
  6. #include <QImageReader>
  7. #include <QDir>
  8. #include <QMessageBox>
  9. #include <QKeyEvent>
  10. #include <QTextCursor>
  11. #include <QTimer>
  12. #include <QGuiApplication>
  13. #include <QApplication>
  14. #include <QClipboard>
  15. #include "vmdeditoperations.h"
  16. #include "dialog/vinsertimagedialog.h"
  17. #include "dialog/vselectdialog.h"
  18. #include "utils/vutils.h"
  19. #include "vedit.h"
  20. #include "vdownloader.h"
  21. #include "vfile.h"
  22. #include "vmdedit.h"
  23. #include "vconfigmanager.h"
  24. #include "utils/vvim.h"
  25. #include "utils/veditutils.h"
  26. extern VConfigManager vconfig;
  27. const QString VMdEditOperations::c_defaultImageTitle = "image";
  28. VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
  29. : VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
  30. {
  31. }
  32. bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
  33. {
  34. QImage image = qvariant_cast<QImage>(source->imageData());
  35. if (image.isNull()) {
  36. return false;
  37. }
  38. VInsertImageDialog dialog(tr("Insert Image From Clipboard"),
  39. c_defaultImageTitle, "", (QWidget *)m_editor);
  40. dialog.setBrowseable(false);
  41. dialog.setImage(image);
  42. if (dialog.exec() == QDialog::Accepted) {
  43. insertImageFromQImage(dialog.getImageTitleInput(), m_file->retriveImagePath(), image);
  44. }
  45. return true;
  46. }
  47. void VMdEditOperations::insertImageFromQImage(const QString &title, const QString &path,
  48. const QImage &image)
  49. {
  50. QString fileName = VUtils::generateImageFileName(path, title);
  51. QString filePath = QDir(path).filePath(fileName);
  52. V_ASSERT(!QFile(filePath).exists());
  53. QString errStr;
  54. bool ret = VUtils::makePath(path);
  55. if (!ret) {
  56. errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
  57. .arg(vconfig.c_dataTextStyle).arg(path);
  58. } else {
  59. ret = image.save(filePath);
  60. if (!ret) {
  61. errStr = tr("Fail to save image <span style=\"%1\">%2</span>.")
  62. .arg(vconfig.c_dataTextStyle).arg(filePath);
  63. }
  64. }
  65. if (!ret) {
  66. VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
  67. tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(vconfig.c_dataTextStyle).arg(title),
  68. errStr,
  69. QMessageBox::Ok,
  70. QMessageBox::Ok,
  71. (QWidget *)m_editor);
  72. return;
  73. }
  74. QString md = QString("![%1](%2/%3)").arg(title).arg(VUtils::directoryNameFromPath(path)).arg(fileName);
  75. insertTextAtCurPos(md);
  76. qDebug() << "insert image" << title << filePath;
  77. VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
  78. Q_ASSERT(mdEditor);
  79. mdEditor->imageInserted(filePath);
  80. }
  81. void VMdEditOperations::insertImageFromPath(const QString &title,
  82. const QString &path, const QString &oriImagePath)
  83. {
  84. QString fileName = VUtils::generateImageFileName(path, title, QFileInfo(oriImagePath).suffix());
  85. QString filePath = QDir(path).filePath(fileName);
  86. V_ASSERT(!QFile(filePath).exists());
  87. QString errStr;
  88. bool ret = VUtils::makePath(path);
  89. if (!ret) {
  90. errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
  91. .arg(vconfig.c_dataTextStyle).arg(path);
  92. } else {
  93. ret = QFile::copy(oriImagePath, filePath);
  94. if (!ret) {
  95. errStr = tr("Fail to copy image <span style=\"%1\">%2</span>.")
  96. .arg(vconfig.c_dataTextStyle).arg(filePath);
  97. }
  98. }
  99. if (!ret) {
  100. VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
  101. tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(vconfig.c_dataTextStyle).arg(title),
  102. errStr,
  103. QMessageBox::Ok,
  104. QMessageBox::Ok,
  105. (QWidget *)m_editor);
  106. return;
  107. }
  108. QString md = QString("![%1](%2/%3)").arg(title).arg(VUtils::directoryNameFromPath(path)).arg(fileName);
  109. insertTextAtCurPos(md);
  110. qDebug() << "insert image" << title << filePath;
  111. VMdEdit *mdEditor = dynamic_cast<VMdEdit *>(m_editor);
  112. Q_ASSERT(mdEditor);
  113. mdEditor->imageInserted(filePath);
  114. }
  115. bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
  116. {
  117. QString imagePath;
  118. QImage image;
  119. bool isLocal = imageUrl.isLocalFile();
  120. QString title;
  121. // Whether it is a local file or web URL
  122. if (isLocal) {
  123. imagePath = imageUrl.toLocalFile();
  124. image = QImage(imagePath);
  125. if (image.isNull()) {
  126. qWarning() << "image is null";
  127. return false;
  128. }
  129. title = "Insert Image From File";
  130. } else {
  131. imagePath = imageUrl.toString();
  132. title = "Insert Image From Network";
  133. }
  134. VInsertImageDialog dialog(title, c_defaultImageTitle,
  135. imagePath, (QWidget *)m_editor);
  136. dialog.setBrowseable(false, true);
  137. if (isLocal) {
  138. dialog.setImage(image);
  139. } else {
  140. // Download it to a QImage
  141. VDownloader *downloader = new VDownloader(&dialog);
  142. connect(downloader, &VDownloader::downloadFinished,
  143. &dialog, &VInsertImageDialog::imageDownloaded);
  144. downloader->download(imageUrl.toString());
  145. }
  146. if (dialog.exec() == QDialog::Accepted) {
  147. if (isLocal) {
  148. insertImageFromPath(dialog.getImageTitleInput(),
  149. m_file->retriveImagePath(),
  150. imagePath);
  151. } else {
  152. insertImageFromQImage(dialog.getImageTitleInput(),
  153. m_file->retriveImagePath(),
  154. dialog.getImage());
  155. }
  156. }
  157. return true;
  158. }
  159. bool VMdEditOperations::insertImage()
  160. {
  161. VInsertImageDialog dialog(tr("Insert Image From File"),
  162. c_defaultImageTitle, "", (QWidget *)m_editor);
  163. if (dialog.exec() == QDialog::Accepted) {
  164. QString title = dialog.getImageTitleInput();
  165. QString imagePath = dialog.getPathInput();
  166. qDebug() << "insert image from" << imagePath << "as" << title;
  167. insertImageFromPath(title, m_file->retriveImagePath(), imagePath);
  168. }
  169. return true;
  170. }
  171. bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
  172. {
  173. if (m_editConfig->m_enableVimMode
  174. && m_vim->handleKeyPressEvent(p_event, &m_autoIndentPos)) {
  175. return true;
  176. }
  177. bool ret = false;
  178. int key = p_event->key();
  179. int modifiers = p_event->modifiers();
  180. switch (key) {
  181. case Qt::Key_1:
  182. case Qt::Key_2:
  183. case Qt::Key_3:
  184. case Qt::Key_4:
  185. case Qt::Key_5:
  186. case Qt::Key_6:
  187. {
  188. if (modifiers == Qt::ControlModifier) {
  189. // Ctrl + <N>: insert title at level <N>.
  190. if (insertTitle(key - Qt::Key_0)) {
  191. p_event->accept();
  192. ret = true;
  193. goto exit;
  194. }
  195. }
  196. break;
  197. }
  198. case Qt::Key_Tab:
  199. {
  200. if (handleKeyTab(p_event)) {
  201. ret = true;
  202. goto exit;
  203. }
  204. break;
  205. }
  206. case Qt::Key_Backtab:
  207. {
  208. if (handleKeyBackTab(p_event)) {
  209. ret = true;
  210. goto exit;
  211. }
  212. break;
  213. }
  214. case Qt::Key_B:
  215. {
  216. if (modifiers == Qt::ControlModifier) {
  217. decorateBold();
  218. p_event->accept();
  219. ret = true;
  220. }
  221. break;
  222. }
  223. case Qt::Key_H:
  224. {
  225. if (handleKeyH(p_event)) {
  226. ret = true;
  227. goto exit;
  228. }
  229. break;
  230. }
  231. case Qt::Key_I:
  232. {
  233. if (modifiers == Qt::ControlModifier) {
  234. decorateItalic();
  235. p_event->accept();
  236. ret = true;
  237. }
  238. break;
  239. }
  240. case Qt::Key_O:
  241. {
  242. if (modifiers == Qt::ControlModifier) {
  243. decorateInlineCode();
  244. p_event->accept();
  245. ret = true;
  246. }
  247. break;
  248. }
  249. case Qt::Key_U:
  250. {
  251. if (handleKeyU(p_event)) {
  252. ret = true;
  253. goto exit;
  254. }
  255. break;
  256. }
  257. case Qt::Key_W:
  258. {
  259. if (handleKeyW(p_event)) {
  260. ret = true;
  261. goto exit;
  262. }
  263. break;
  264. }
  265. case Qt::Key_BracketLeft:
  266. {
  267. if (handleKeyBracketLeft(p_event)) {
  268. ret = true;
  269. goto exit;
  270. }
  271. break;
  272. }
  273. case Qt::Key_Escape:
  274. {
  275. if (handleKeyEsc(p_event)) {
  276. ret = true;
  277. goto exit;
  278. }
  279. break;
  280. }
  281. case Qt::Key_Enter:
  282. // Fall through.
  283. case Qt::Key_Return:
  284. {
  285. if (handleKeyReturn(p_event)) {
  286. ret = true;
  287. goto exit;
  288. }
  289. break;
  290. }
  291. default:
  292. break;
  293. }
  294. exit:
  295. // Qt::Key_Return, Qt::Key_Tab and Qt::Key_Backtab will handle m_autoIndentPos.
  296. if (key != Qt::Key_Return
  297. && key != Qt::Key_Enter
  298. && key != Qt::Key_Tab
  299. && key != Qt::Key_Backtab
  300. && key != Qt::Key_Shift
  301. && key != Qt::Key_Control) {
  302. m_autoIndentPos = -1;
  303. }
  304. return ret;
  305. }
  306. // Let Ctrl+[ behave exactly like ESC.
  307. bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
  308. {
  309. // 1. If there is any selection, clear it.
  310. // 2. Otherwise, ignore this event and let parent handles it.
  311. if (p_event->modifiers() == Qt::ControlModifier) {
  312. QTextCursor cursor = m_editor->textCursor();
  313. if (cursor.hasSelection()) {
  314. cursor.clearSelection();
  315. m_editor->setTextCursor(cursor);
  316. p_event->accept();
  317. return true;
  318. }
  319. }
  320. return false;
  321. }
  322. bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
  323. {
  324. QTextDocument *doc = m_editor->document();
  325. QString text(m_editConfig->m_tabSpaces);
  326. if (p_event->modifiers() == Qt::NoModifier) {
  327. QTextCursor cursor = m_editor->textCursor();
  328. if (cursor.hasSelection()) {
  329. m_autoIndentPos = -1;
  330. cursor.beginEditBlock();
  331. // Indent each selected line.
  332. VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
  333. cursor.endEditBlock();
  334. m_editor->setTextCursor(cursor);
  335. } else {
  336. // If it is a Tab key following auto list, increase the indent level.
  337. QTextBlock block = cursor.block();
  338. int seq = -1;
  339. if (m_autoIndentPos == cursor.position()
  340. && VEditUtils::isListBlock(block, &seq)) {
  341. QTextCursor blockCursor(block);
  342. blockCursor.beginEditBlock();
  343. blockCursor.insertText(text);
  344. if (seq != -1) {
  345. changeListBlockSeqNumber(block, 1);
  346. }
  347. blockCursor.endEditBlock();
  348. // Change m_autoIndentPos to let it can be repeated.
  349. m_autoIndentPos = m_editor->textCursor().position();
  350. } else {
  351. // Just insert "tab".
  352. insertTextAtCurPos(text);
  353. m_autoIndentPos = -1;
  354. }
  355. }
  356. } else {
  357. m_autoIndentPos = -1;
  358. return false;
  359. }
  360. p_event->accept();
  361. return true;
  362. }
  363. bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
  364. {
  365. if (p_event->modifiers() != Qt::ShiftModifier) {
  366. m_autoIndentPos = -1;
  367. return false;
  368. }
  369. QTextDocument *doc = m_editor->document();
  370. QTextCursor cursor = m_editor->textCursor();
  371. QTextBlock block = doc->findBlock(cursor.selectionStart());
  372. bool continueAutoIndent = false;
  373. int seq = -1;
  374. if (cursor.position() == m_autoIndentPos
  375. && VEditUtils::isListBlock(block, &seq) &&
  376. !cursor.hasSelection()) {
  377. continueAutoIndent = true;
  378. }
  379. cursor.beginEditBlock();
  380. if (continueAutoIndent && seq != -1) {
  381. changeListBlockSeqNumber(block, 1);
  382. }
  383. VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
  384. cursor.endEditBlock();
  385. if (continueAutoIndent) {
  386. m_autoIndentPos = m_editor->textCursor().position();
  387. } else {
  388. m_autoIndentPos = -1;
  389. }
  390. p_event->accept();
  391. return true;
  392. }
  393. bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
  394. {
  395. if (p_event->modifiers() == Qt::ControlModifier) {
  396. // Ctrl+H, equal to backspace.
  397. QTextCursor cursor = m_editor->textCursor();
  398. cursor.deletePreviousChar();
  399. p_event->accept();
  400. return true;
  401. }
  402. return false;
  403. }
  404. bool VMdEditOperations::handleKeyU(QKeyEvent *p_event)
  405. {
  406. if (p_event->modifiers() == Qt::ControlModifier) {
  407. // Ctrl+U, delete till the start of line.
  408. QTextCursor cursor = m_editor->textCursor();
  409. bool ret;
  410. if (cursor.atBlockStart()) {
  411. ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
  412. } else {
  413. ret = cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
  414. }
  415. if (ret) {
  416. cursor.removeSelectedText();
  417. }
  418. p_event->accept();
  419. return true;
  420. }
  421. return false;
  422. }
  423. bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
  424. {
  425. if (p_event->modifiers() == Qt::ControlModifier) {
  426. // Ctrl+W, delete till the start of previous word.
  427. QTextCursor cursor = m_editor->textCursor();
  428. if (cursor.hasSelection()) {
  429. cursor.removeSelectedText();
  430. } else {
  431. bool ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
  432. if (ret) {
  433. cursor.removeSelectedText();
  434. }
  435. }
  436. p_event->accept();
  437. return true;
  438. }
  439. return false;
  440. }
  441. bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
  442. {
  443. // 1. If there is any selection, clear it.
  444. // 2. Otherwise, ignore this event and let parent handles it.
  445. QTextCursor cursor = m_editor->textCursor();
  446. if (cursor.hasSelection()) {
  447. cursor.clearSelection();
  448. m_editor->setTextCursor(cursor);
  449. p_event->accept();
  450. return true;
  451. }
  452. return false;
  453. }
  454. bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
  455. {
  456. if (p_event->modifiers() & Qt::ControlModifier) {
  457. m_autoIndentPos = -1;
  458. return false;
  459. } else if (p_event->modifiers() & Qt::ShiftModifier) {
  460. // Insert two spaces and a new line.
  461. m_autoIndentPos = -1;
  462. QTextCursor cursor = m_editor->textCursor();
  463. cursor.beginEditBlock();
  464. cursor.removeSelectedText();
  465. cursor.insertText(" ");
  466. cursor.endEditBlock();
  467. // Let remaining logics handle inserting the new block.
  468. }
  469. // See if we need to cancel auto indent.
  470. if (m_autoIndentPos > -1) {
  471. // Cancel the auto indent/list if the pos is the same and cursor is at
  472. // the end of a block.
  473. bool cancelAutoIndent = false;
  474. QTextCursor cursor = m_editor->textCursor();
  475. QTextBlock block = cursor.block();
  476. if (cursor.position() == m_autoIndentPos && !cursor.hasSelection()) {
  477. if (VEditUtils::isListBlock(block)) {
  478. if (cursor.atBlockEnd()) {
  479. cancelAutoIndent = true;
  480. }
  481. } else if (VEditUtils::isSpaceToBlockStart(block,
  482. cursor.positionInBlock())) {
  483. cancelAutoIndent = true;
  484. }
  485. }
  486. if (cancelAutoIndent) {
  487. m_autoIndentPos = -1;
  488. VEditUtils::deleteIndentAndListMark(cursor);
  489. m_editor->setTextCursor(cursor);
  490. return true;
  491. }
  492. }
  493. bool handled = false;
  494. m_autoIndentPos = -1;
  495. if (vconfig.getAutoIndent()) {
  496. handled = true;
  497. QTextCursor cursor = m_editor->textCursor();
  498. bool textInserted = false;
  499. cursor.beginEditBlock();
  500. cursor.removeSelectedText();
  501. // Indent the new line as previous line.
  502. textInserted = VEditUtils::insertBlockWithIndent(cursor);
  503. // Continue the list from previous line.
  504. if (vconfig.getAutoList()) {
  505. textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor) || textInserted;
  506. }
  507. cursor.endEditBlock();
  508. m_editor->setTextCursor(cursor);
  509. if (textInserted) {
  510. m_autoIndentPos = m_editor->textCursor().position();
  511. }
  512. }
  513. return handled;
  514. }
  515. void VMdEditOperations::changeListBlockSeqNumber(QTextBlock &p_block, int p_seq)
  516. {
  517. QString text = p_block.text();
  518. QRegExp regExp("^(\\s*)(\\d+)\\.\\s");
  519. int idx = regExp.indexIn(text);
  520. if (idx == -1 || regExp.captureCount() != 2) {
  521. return;
  522. }
  523. int oriSeq = -1;
  524. bool ok = false;
  525. oriSeq = regExp.capturedTexts()[2].toInt(&ok);
  526. if (ok && oriSeq == p_seq) {
  527. return;
  528. }
  529. QTextCursor cursor(p_block);
  530. bool ret = cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,
  531. regExp.capturedTexts()[1].size());
  532. if (!ret) {
  533. return;
  534. }
  535. ret = cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor,
  536. regExp.capturedTexts()[2].size());
  537. if (!ret) {
  538. return;
  539. }
  540. cursor.removeSelectedText();
  541. cursor.insertText(QString::number(p_seq));
  542. }
  543. bool VMdEditOperations::insertTitle(int p_level)
  544. {
  545. Q_ASSERT(p_level > 0 && p_level < 7);
  546. QTextDocument *doc = m_editor->document();
  547. QString titleMark(p_level, '#');
  548. QTextCursor cursor = m_editor->textCursor();
  549. if (cursor.hasSelection()) {
  550. // Insert title # in front of the selected lines.
  551. int start = cursor.selectionStart();
  552. int end = cursor.selectionEnd();
  553. int startBlock = doc->findBlock(start).blockNumber();
  554. int endBlock = doc->findBlock(end).blockNumber();
  555. cursor.beginEditBlock();
  556. cursor.clearSelection();
  557. for (int i = startBlock; i <= endBlock; ++i) {
  558. QTextBlock block = doc->findBlockByNumber(i);
  559. cursor.setPosition(block.position(), QTextCursor::MoveAnchor);
  560. cursor.insertText(titleMark + " ");
  561. }
  562. cursor.movePosition(QTextCursor::EndOfBlock);
  563. cursor.endEditBlock();
  564. } else {
  565. // Insert title # in front of current block.
  566. cursor.beginEditBlock();
  567. cursor.movePosition(QTextCursor::StartOfBlock);
  568. cursor.insertText(titleMark + " ");
  569. cursor.movePosition(QTextCursor::EndOfBlock);
  570. cursor.endEditBlock();
  571. }
  572. m_editor->setTextCursor(cursor);
  573. return true;
  574. }
  575. void VMdEditOperations::decorateText(TextDecoration p_decoration)
  576. {
  577. if (p_decoration == TextDecoration::None) {
  578. return;
  579. }
  580. m_vim->setMode(VimMode::Insert, false);
  581. switch (p_decoration) {
  582. case TextDecoration::Bold:
  583. decorateBold();
  584. break;
  585. case TextDecoration::Italic:
  586. decorateItalic();
  587. break;
  588. case TextDecoration::InlineCode:
  589. decorateInlineCode();
  590. break;
  591. default:
  592. qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
  593. break;
  594. }
  595. }
  596. void VMdEditOperations::decorateBold()
  597. {
  598. QTextCursor cursor = m_editor->textCursor();
  599. cursor.beginEditBlock();
  600. if (cursor.hasSelection()) {
  601. // Insert ** around the selected text.
  602. int start = cursor.selectionStart();
  603. int end = cursor.selectionEnd();
  604. cursor.clearSelection();
  605. cursor.setPosition(start, QTextCursor::MoveAnchor);
  606. cursor.insertText("**");
  607. cursor.setPosition(end + 2, QTextCursor::MoveAnchor);
  608. cursor.insertText("**");
  609. } else {
  610. // Insert **** and place cursor in the middle.
  611. // Or if there are two * after current cursor, just skip them.
  612. int pos = cursor.positionInBlock();
  613. bool hasStars = false;
  614. QString text = cursor.block().text();
  615. if (pos <= text.size() - 2) {
  616. if (text[pos] == '*' && text[pos + 1] == '*') {
  617. hasStars = true;
  618. }
  619. }
  620. if (hasStars) {
  621. cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
  622. } else {
  623. cursor.insertText("****");
  624. cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
  625. }
  626. }
  627. cursor.endEditBlock();
  628. m_editor->setTextCursor(cursor);
  629. }
  630. void VMdEditOperations::decorateItalic()
  631. {
  632. QTextCursor cursor = m_editor->textCursor();
  633. cursor.beginEditBlock();
  634. if (cursor.hasSelection()) {
  635. // Insert * around the selected text.
  636. int start = cursor.selectionStart();
  637. int end = cursor.selectionEnd();
  638. cursor.clearSelection();
  639. cursor.setPosition(start, QTextCursor::MoveAnchor);
  640. cursor.insertText("*");
  641. cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
  642. cursor.insertText("*");
  643. } else {
  644. // Insert ** and place cursor in the middle.
  645. // Or if there are one * after current cursor, just skip it.
  646. int pos = cursor.positionInBlock();
  647. bool hasStar = false;
  648. QString text = cursor.block().text();
  649. if (pos <= text.size() - 1) {
  650. if (text[pos] == '*') {
  651. hasStar = true;
  652. }
  653. }
  654. if (hasStar) {
  655. cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
  656. } else {
  657. cursor.insertText("**");
  658. cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
  659. }
  660. }
  661. cursor.endEditBlock();
  662. m_editor->setTextCursor(cursor);
  663. }
  664. void VMdEditOperations::decorateInlineCode()
  665. {
  666. QTextCursor cursor = m_editor->textCursor();
  667. cursor.beginEditBlock();
  668. if (cursor.hasSelection()) {
  669. // Insert ` around the selected text.
  670. int start = cursor.selectionStart();
  671. int end = cursor.selectionEnd();
  672. cursor.clearSelection();
  673. cursor.setPosition(start, QTextCursor::MoveAnchor);
  674. cursor.insertText("`");
  675. cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
  676. cursor.insertText("`");
  677. } else {
  678. // Insert `` and place cursor in the middle.
  679. // Or if there are one ` after current cursor, just skip it.
  680. int pos = cursor.positionInBlock();
  681. bool hasBackquote = false;
  682. QString text = cursor.block().text();
  683. if (pos <= text.size() - 1) {
  684. if (text[pos] == '`') {
  685. hasBackquote = true;
  686. }
  687. }
  688. if (hasBackquote) {
  689. cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
  690. } else {
  691. cursor.insertText("``");
  692. cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
  693. }
  694. }
  695. cursor.endEditBlock();
  696. m_editor->setTextCursor(cursor);
  697. }