vedit.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. #include <QtWidgets>
  2. #include <QVector>
  3. #include <QDebug>
  4. #include "vedit.h"
  5. #include "vnote.h"
  6. #include "vconfigmanager.h"
  7. #include "vtoc.h"
  8. #include "utils/vutils.h"
  9. #include "veditoperations.h"
  10. #include "dialog/vfindreplacedialog.h"
  11. #include "vedittab.h"
  12. extern VConfigManager vconfig;
  13. extern VNote *g_vnote;
  14. void VEditConfig::init(const QFontMetrics &p_metric)
  15. {
  16. if (vconfig.getTabStopWidth() > 0) {
  17. m_tabStopWidth = vconfig.getTabStopWidth() * p_metric.width(' ');
  18. } else {
  19. m_tabStopWidth = 0;
  20. }
  21. m_expandTab = vconfig.getIsExpandTab();
  22. if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
  23. m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
  24. } else {
  25. m_tabSpaces = "\t";
  26. }
  27. m_enableVimMode = vconfig.getEnableVimMode();
  28. m_cursorLineBg = QColor(vconfig.getEditorCurrentLineBg());
  29. }
  30. VEdit::VEdit(VFile *p_file, QWidget *p_parent)
  31. : QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
  32. {
  33. const int labelTimerInterval = 500;
  34. const int extraSelectionHighlightTimer = 500;
  35. const int labelSize = 64;
  36. m_selectedWordColor = QColor("Yellow");
  37. m_searchedWordColor = QColor(g_vnote->getColorFromPalette("Green4"));
  38. m_trailingSpaceColor = QColor(vconfig.getEditorTrailingSpaceBackground());
  39. QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
  40. m_wrapLabel = new QLabel(this);
  41. m_wrapLabel->setPixmap(wrapPixmap.scaled(labelSize, labelSize));
  42. m_wrapLabel->hide();
  43. m_labelTimer = new QTimer(this);
  44. m_labelTimer->setSingleShot(true);
  45. m_labelTimer->setInterval(labelTimerInterval);
  46. connect(m_labelTimer, &QTimer::timeout,
  47. this, &VEdit::labelTimerTimeout);
  48. m_highlightTimer = new QTimer(this);
  49. m_highlightTimer->setSingleShot(true);
  50. m_highlightTimer->setInterval(extraSelectionHighlightTimer);
  51. connect(m_highlightTimer, &QTimer::timeout,
  52. this, &VEdit::doHighlightExtraSelections);
  53. connect(document(), &QTextDocument::modificationChanged,
  54. (VFile *)m_file, &VFile::setModified);
  55. m_extraSelections.resize((int)SelectionId::MaxSelection);
  56. updateFontAndPalette();
  57. updateConfig();
  58. connect(this, &VEdit::cursorPositionChanged,
  59. this, &VEdit::handleCursorPositionChanged);
  60. connect(this, &VEdit::selectionChanged,
  61. this, &VEdit::highlightSelectedWord);
  62. }
  63. VEdit::~VEdit()
  64. {
  65. if (m_file) {
  66. disconnect(document(), &QTextDocument::modificationChanged,
  67. (VFile *)m_file, &VFile::setModified);
  68. }
  69. }
  70. void VEdit::updateConfig()
  71. {
  72. m_config.init(QFontMetrics(font()));
  73. if (m_config.m_tabStopWidth > 0) {
  74. setTabStopWidth(m_config.m_tabStopWidth);
  75. }
  76. emit configUpdated();
  77. }
  78. void VEdit::beginEdit()
  79. {
  80. updateFontAndPalette();
  81. updateConfig();
  82. setReadOnly(false);
  83. setModified(false);
  84. }
  85. void VEdit::endEdit()
  86. {
  87. setReadOnly(true);
  88. }
  89. void VEdit::saveFile()
  90. {
  91. if (!document()->isModified()) {
  92. return;
  93. }
  94. m_file->setContent(toHtml());
  95. document()->setModified(false);
  96. }
  97. void VEdit::reloadFile()
  98. {
  99. setHtml(m_file->getContent());
  100. setModified(false);
  101. }
  102. void VEdit::scrollToLine(int p_lineNumber)
  103. {
  104. Q_ASSERT(p_lineNumber >= 0);
  105. // Move the cursor to the end first
  106. moveCursor(QTextCursor::End);
  107. QTextCursor cursor(document()->findBlockByLineNumber(p_lineNumber));
  108. cursor.movePosition(QTextCursor::EndOfBlock);
  109. setTextCursor(cursor);
  110. }
  111. bool VEdit::isModified() const
  112. {
  113. return document()->isModified();
  114. }
  115. void VEdit::setModified(bool p_modified)
  116. {
  117. document()->setModified(p_modified);
  118. if (m_file) {
  119. m_file->setModified(p_modified);
  120. }
  121. }
  122. void VEdit::insertImage()
  123. {
  124. if (m_editOps) {
  125. m_editOps->insertImage();
  126. }
  127. }
  128. bool VEdit::peekText(const QString &p_text, uint p_options)
  129. {
  130. static int startPos = textCursor().selectionStart();
  131. static int lastPos = startPos;
  132. bool found = false;
  133. if (p_text.isEmpty()) {
  134. // Clear previous selection
  135. QTextCursor cursor = textCursor();
  136. cursor.clearSelection();
  137. cursor.setPosition(startPos);
  138. setTextCursor(cursor);
  139. } else {
  140. QTextCursor cursor = textCursor();
  141. int curPos = cursor.selectionStart();
  142. if (curPos != lastPos) {
  143. // Cursor has been moved. Just start at current potition.
  144. startPos = curPos;
  145. lastPos = curPos;
  146. } else {
  147. cursor.setPosition(startPos);
  148. setTextCursor(cursor);
  149. }
  150. }
  151. bool wrapped = false;
  152. found = findTextHelper(p_text, p_options, true, wrapped);
  153. if (found) {
  154. lastPos = textCursor().selectionStart();
  155. found = true;
  156. }
  157. return found;
  158. }
  159. // Use QTextEdit::find() instead of QTextDocument::find() because the later has
  160. // bugs in searching backward.
  161. bool VEdit::findTextHelper(const QString &p_text, uint p_options,
  162. bool p_forward, bool &p_wrapped)
  163. {
  164. p_wrapped = false;
  165. bool found = false;
  166. // Options
  167. QTextDocument::FindFlags findFlags;
  168. bool caseSensitive = false;
  169. if (p_options & FindOption::CaseSensitive) {
  170. findFlags |= QTextDocument::FindCaseSensitively;
  171. caseSensitive = true;
  172. }
  173. if (p_options & FindOption::WholeWordOnly) {
  174. findFlags |= QTextDocument::FindWholeWords;
  175. }
  176. if (!p_forward) {
  177. findFlags |= QTextDocument::FindBackward;
  178. }
  179. // Use regular expression
  180. bool useRegExp = false;
  181. QRegExp exp;
  182. if (p_options & FindOption::RegularExpression) {
  183. useRegExp = true;
  184. exp = QRegExp(p_text,
  185. caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
  186. }
  187. QTextCursor cursor = textCursor();
  188. while (!found) {
  189. if (useRegExp) {
  190. found = find(exp, findFlags);
  191. } else {
  192. found = find(p_text, findFlags);
  193. }
  194. if (p_wrapped) {
  195. if (!found) {
  196. setTextCursor(cursor);
  197. }
  198. break;
  199. }
  200. if (!found) {
  201. // Wrap to the other end of the document to search again.
  202. p_wrapped = true;
  203. QTextCursor wrapCursor = textCursor();
  204. wrapCursor.clearSelection();
  205. if (p_forward) {
  206. wrapCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  207. } else {
  208. wrapCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
  209. }
  210. setTextCursor(wrapCursor);
  211. }
  212. }
  213. return found;
  214. }
  215. QList<QTextCursor> VEdit::findTextAll(const QString &p_text, uint p_options)
  216. {
  217. QList<QTextCursor> results;
  218. if (p_text.isEmpty()) {
  219. return results;
  220. }
  221. // Options
  222. QTextDocument::FindFlags findFlags;
  223. bool caseSensitive = false;
  224. if (p_options & FindOption::CaseSensitive) {
  225. findFlags |= QTextDocument::FindCaseSensitively;
  226. caseSensitive = true;
  227. }
  228. if (p_options & FindOption::WholeWordOnly) {
  229. findFlags |= QTextDocument::FindWholeWords;
  230. }
  231. // Use regular expression
  232. bool useRegExp = false;
  233. QRegExp exp;
  234. if (p_options & FindOption::RegularExpression) {
  235. useRegExp = true;
  236. exp = QRegExp(p_text,
  237. caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
  238. }
  239. int startPos = 0;
  240. QTextCursor cursor;
  241. QTextDocument *doc = document();
  242. while (true) {
  243. if (useRegExp) {
  244. cursor = doc->find(exp, startPos, findFlags);
  245. } else {
  246. cursor = doc->find(p_text, startPos, findFlags);
  247. }
  248. if (cursor.isNull()) {
  249. break;
  250. } else {
  251. results.append(cursor);
  252. startPos = cursor.selectionEnd();
  253. }
  254. }
  255. return results;
  256. }
  257. bool VEdit::findText(const QString &p_text, uint p_options, bool p_forward)
  258. {
  259. bool found = false;
  260. if (p_text.isEmpty()) {
  261. QTextCursor cursor = textCursor();
  262. cursor.clearSelection();
  263. setTextCursor(cursor);
  264. } else {
  265. bool wrapped = false;
  266. found = findTextHelper(p_text, p_options, p_forward, wrapped);
  267. if (found) {
  268. if (wrapped) {
  269. showWrapLabel();
  270. }
  271. highlightSearchedWord(p_text, p_options);
  272. } else {
  273. // Simply clear previous highlight.
  274. highlightSearchedWord("", p_options);
  275. }
  276. }
  277. qDebug() << "findText" << p_text << p_options << p_forward
  278. << (found ? "Found" : "NotFound");
  279. return found;
  280. }
  281. void VEdit::replaceText(const QString &p_text, uint p_options,
  282. const QString &p_replaceText, bool p_findNext)
  283. {
  284. QTextCursor cursor = textCursor();
  285. if (cursor.hasSelection()) {
  286. // Replace occurs only if the selected text matches @p_text with @p_options.
  287. QTextCursor tmpCursor = cursor;
  288. tmpCursor.setPosition(tmpCursor.selectionStart());
  289. tmpCursor.clearSelection();
  290. setTextCursor(tmpCursor);
  291. bool wrapped = false;
  292. bool found = findTextHelper(p_text, p_options, true, wrapped);
  293. bool matched = false;
  294. if (found) {
  295. tmpCursor = textCursor();
  296. matched = (cursor.selectionStart() == tmpCursor.selectionStart())
  297. && (cursor.selectionEnd() == tmpCursor.selectionEnd());
  298. }
  299. if (matched) {
  300. cursor.beginEditBlock();
  301. cursor.removeSelectedText();
  302. cursor.insertText(p_replaceText);
  303. cursor.endEditBlock();
  304. setTextCursor(cursor);
  305. } else {
  306. setTextCursor(cursor);
  307. }
  308. }
  309. if (p_findNext) {
  310. findText(p_text, p_options, true);
  311. }
  312. }
  313. void VEdit::replaceTextAll(const QString &p_text, uint p_options,
  314. const QString &p_replaceText)
  315. {
  316. // Replace from the start to the end and resotre the cursor.
  317. QTextCursor cursor = textCursor();
  318. int nrReplaces = 0;
  319. QTextCursor tmpCursor = cursor;
  320. tmpCursor.setPosition(0);
  321. setTextCursor(tmpCursor);
  322. while (true) {
  323. bool wrapped = false;
  324. bool found = findTextHelper(p_text, p_options, true, wrapped);
  325. if (!found) {
  326. break;
  327. } else {
  328. if (wrapped) {
  329. // Wrap back.
  330. break;
  331. }
  332. nrReplaces++;
  333. tmpCursor = textCursor();
  334. tmpCursor.beginEditBlock();
  335. tmpCursor.removeSelectedText();
  336. tmpCursor.insertText(p_replaceText);
  337. tmpCursor.endEditBlock();
  338. setTextCursor(tmpCursor);
  339. }
  340. }
  341. // Restore cursor position.
  342. cursor.clearSelection();
  343. setTextCursor(cursor);
  344. qDebug() << "replace all" << nrReplaces << "occurences";
  345. }
  346. void VEdit::showWrapLabel()
  347. {
  348. int labelW = m_wrapLabel->width();
  349. int labelH = m_wrapLabel->height();
  350. int x = (width() - labelW) / 2;
  351. int y = (height() - labelH) / 2;
  352. if (x < 0) {
  353. x = 0;
  354. }
  355. if (y < 0) {
  356. y = 0;
  357. }
  358. m_wrapLabel->move(x, y);
  359. m_wrapLabel->show();
  360. m_labelTimer->stop();
  361. m_labelTimer->start();
  362. }
  363. void VEdit::labelTimerTimeout()
  364. {
  365. m_wrapLabel->hide();
  366. }
  367. void VEdit::updateFontAndPalette()
  368. {
  369. setFont(vconfig.getBaseEditFont());
  370. setPalette(vconfig.getBaseEditPalette());
  371. }
  372. void VEdit::highlightExtraSelections(bool p_now)
  373. {
  374. m_highlightTimer->stop();
  375. if (p_now) {
  376. doHighlightExtraSelections();
  377. } else {
  378. m_highlightTimer->start();
  379. }
  380. }
  381. void VEdit::doHighlightExtraSelections()
  382. {
  383. int nrExtra = m_extraSelections.size();
  384. Q_ASSERT(nrExtra == (int)SelectionId::MaxSelection);
  385. QList<QTextEdit::ExtraSelection> extraSelects;
  386. for (int i = 0; i < nrExtra; ++i) {
  387. extraSelects.append(m_extraSelections[i]);
  388. }
  389. setExtraSelections(extraSelects);
  390. }
  391. void VEdit::highlightCurrentLine()
  392. {
  393. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::CurrentLine];
  394. if (vconfig.getHighlightCursorLine() && !isReadOnly()) {
  395. // Need to highlight current line.
  396. QTextEdit::ExtraSelection select;
  397. select.format.setBackground(m_config.m_cursorLineBg);
  398. select.format.setProperty(QTextFormat::FullWidthSelection, true);
  399. select.cursor = textCursor();
  400. select.cursor.clearSelection();
  401. selects.clear();
  402. selects.append(select);
  403. } else {
  404. // Need to clear current line highlight.
  405. if (selects.isEmpty()) {
  406. return;
  407. }
  408. selects.clear();
  409. }
  410. highlightExtraSelections(true);
  411. }
  412. void VEdit::setReadOnly(bool p_ro)
  413. {
  414. QTextEdit::setReadOnly(p_ro);
  415. highlightCurrentLine();
  416. }
  417. void VEdit::highlightSelectedWord()
  418. {
  419. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
  420. if (!vconfig.getHighlightSelectedWord()) {
  421. if (!selects.isEmpty()) {
  422. selects.clear();
  423. highlightExtraSelections(true);
  424. }
  425. return;
  426. }
  427. QString text = textCursor().selectedText().trimmed();
  428. if (text.isEmpty() || wordInSearchedSelection(text)) {
  429. selects.clear();
  430. highlightExtraSelections(true);
  431. return;
  432. }
  433. QTextCharFormat format;
  434. format.setBackground(m_selectedWordColor);
  435. highlightTextAll(text, FindOption::CaseSensitive, SelectionId::SelectedWord,
  436. format);
  437. }
  438. // Do not highlight trailing spaces with current cursor right behind.
  439. static void trailingSpaceFilter(VEdit *p_editor, QList<QTextEdit::ExtraSelection> &p_result)
  440. {
  441. QTextCursor cursor = p_editor->textCursor();
  442. if (!cursor.atBlockEnd()) {
  443. return;
  444. }
  445. int cursorPos = cursor.position();
  446. for (auto it = p_result.begin(); it != p_result.end(); ++it) {
  447. if (it->cursor.selectionEnd() == cursorPos) {
  448. p_result.erase(it);
  449. // There will be only one.
  450. return;
  451. }
  452. }
  453. }
  454. void VEdit::highlightTrailingSpace()
  455. {
  456. if (!vconfig.getEnableTrailingSpaceHighlight()) {
  457. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::TrailingSapce];
  458. if (!selects.isEmpty()) {
  459. selects.clear();
  460. highlightExtraSelections(true);
  461. }
  462. return;
  463. }
  464. QTextCharFormat format;
  465. format.setBackground(m_trailingSpaceColor);
  466. QString text("\\s+$");
  467. highlightTextAll(text, FindOption::RegularExpression,
  468. SelectionId::TrailingSapce, format,
  469. trailingSpaceFilter);
  470. }
  471. bool VEdit::wordInSearchedSelection(const QString &p_text)
  472. {
  473. QString text = p_text.trimmed();
  474. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  475. for (int i = 0; i < selects.size(); ++i) {
  476. QString searchedWord = selects[i].cursor.selectedText();
  477. if (text == searchedWord.trimmed()) {
  478. return true;
  479. }
  480. }
  481. return false;
  482. }
  483. void VEdit::highlightTextAll(const QString &p_text, uint p_options,
  484. SelectionId p_id, QTextCharFormat p_format,
  485. void (*p_filter)(VEdit *, QList<QTextEdit::ExtraSelection> &))
  486. {
  487. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)p_id];
  488. if (!p_text.isEmpty()) {
  489. selects.clear();
  490. QList<QTextCursor> occurs = findTextAll(p_text, p_options);
  491. for (int i = 0; i < occurs.size(); ++i) {
  492. QTextEdit::ExtraSelection select;
  493. select.format = p_format;
  494. select.cursor = occurs[i];
  495. selects.append(select);
  496. }
  497. } else {
  498. if (selects.isEmpty()) {
  499. return;
  500. }
  501. selects.clear();
  502. }
  503. if (p_filter) {
  504. p_filter(this, selects);
  505. }
  506. highlightExtraSelections();
  507. }
  508. void VEdit::highlightSearchedWord(const QString &p_text, uint p_options)
  509. {
  510. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  511. if (!vconfig.getHighlightSearchedWord() || p_text.isEmpty()) {
  512. if (!selects.isEmpty()) {
  513. selects.clear();
  514. highlightExtraSelections(true);
  515. }
  516. return;
  517. }
  518. QTextCharFormat format;
  519. format.setBackground(m_searchedWordColor);
  520. highlightTextAll(p_text, p_options, SelectionId::SearchedKeyword, format);
  521. }
  522. void VEdit::clearSearchedWordHighlight()
  523. {
  524. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  525. selects.clear();
  526. highlightExtraSelections(true);
  527. }
  528. void VEdit::contextMenuEvent(QContextMenuEvent *p_event)
  529. {
  530. QMenu *menu = createStandardContextMenu();
  531. menu->setToolTipsVisible(true);
  532. const QList<QAction *> actions = menu->actions();
  533. if (!textCursor().hasSelection()) {
  534. VEditTab *editTab = dynamic_cast<VEditTab *>(parent());
  535. V_ASSERT(editTab);
  536. if (editTab->isEditMode()) {
  537. QAction *saveExitAct = new QAction(QIcon(":/resources/icons/save_exit.svg"),
  538. tr("&Save Changes And Read"), this);
  539. saveExitAct->setToolTip(tr("Save changes and exit edit mode"));
  540. connect(saveExitAct, &QAction::triggered,
  541. this, &VEdit::handleSaveExitAct);
  542. QAction *discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"),
  543. tr("&Discard Changes And Read"), this);
  544. discardExitAct->setToolTip(tr("Discard changes and exit edit mode"));
  545. connect(discardExitAct, &QAction::triggered,
  546. this, &VEdit::handleDiscardExitAct);
  547. menu->insertAction(actions.isEmpty() ? NULL : actions[0], discardExitAct);
  548. menu->insertAction(discardExitAct, saveExitAct);
  549. if (!actions.isEmpty()) {
  550. menu->insertSeparator(actions[0]);
  551. }
  552. } else if (m_file->isModifiable()) {
  553. // HTML.
  554. QAction *editAct= new QAction(QIcon(":/resources/icons/edit_note.svg"),
  555. tr("&Edit"), this);
  556. editAct->setToolTip(tr("Edit current note"));
  557. connect(editAct, &QAction::triggered,
  558. this, &VEdit::handleEditAct);
  559. menu->insertAction(actions.isEmpty() ? NULL : actions[0], editAct);
  560. // actions does not contain editAction.
  561. if (!actions.isEmpty()) {
  562. menu->insertSeparator(actions[0]);
  563. }
  564. }
  565. }
  566. menu->exec(p_event->globalPos());
  567. delete menu;
  568. }
  569. void VEdit::handleSaveExitAct()
  570. {
  571. emit saveAndRead();
  572. }
  573. void VEdit::handleDiscardExitAct()
  574. {
  575. emit discardAndRead();
  576. }
  577. void VEdit::handleEditAct()
  578. {
  579. emit editNote();
  580. }
  581. VFile *VEdit::getFile() const
  582. {
  583. return m_file;
  584. }
  585. void VEdit::handleCursorPositionChanged()
  586. {
  587. static QTextCursor lastCursor;
  588. QTextCursor cursor = textCursor();
  589. if (lastCursor.isNull() || cursor.blockNumber() != lastCursor.blockNumber()) {
  590. highlightCurrentLine();
  591. highlightTrailingSpace();
  592. } else {
  593. // Judge whether we have trailing space at current line.
  594. QString text = cursor.block().text();
  595. if (text.rbegin()->isSpace()) {
  596. highlightTrailingSpace();
  597. }
  598. // Handle word-wrap in one block.
  599. // Highlight current line if in different visual line.
  600. if ((lastCursor.positionInBlock() - lastCursor.columnNumber()) !=
  601. (cursor.positionInBlock() - cursor.columnNumber())) {
  602. highlightCurrentLine();
  603. }
  604. }
  605. lastCursor = cursor;
  606. }
  607. VEditConfig &VEdit::getConfig()
  608. {
  609. return m_config;
  610. }