veditor.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474
  1. #include "veditor.h"
  2. #include <QtWidgets>
  3. #include <QTextDocument>
  4. #include "vconfigmanager.h"
  5. #include "utils/vutils.h"
  6. #include "utils/veditutils.h"
  7. #include "veditoperations.h"
  8. #include "dialog/vinsertlinkdialog.h"
  9. #include "utils/vmetawordmanager.h"
  10. #include "utils/vvim.h"
  11. extern VConfigManager *g_config;
  12. extern VMetaWordManager *g_mwMgr;
  13. VEditor::VEditor(VFile *p_file,
  14. QWidget *p_editor,
  15. const QSharedPointer<VTextEditCompleter> &p_completer)
  16. : m_editor(p_editor),
  17. m_object(new VEditorObject(this, p_editor)),
  18. m_file(p_file),
  19. m_editOps(nullptr),
  20. m_document(nullptr),
  21. m_enableInputMethod(true),
  22. m_timeStamp(0),
  23. m_trailingSpaceSelectionTS(0),
  24. m_completer(p_completer)
  25. {
  26. }
  27. VEditor::~VEditor()
  28. {
  29. if (m_completer->widget() == m_editor) {
  30. m_completer->setWidget(NULL);
  31. }
  32. cleanUp();
  33. }
  34. void VEditor::cleanUp()
  35. {
  36. for (auto const & file : m_tempFiles) {
  37. VUtils::deleteFile(file);
  38. }
  39. m_tempFiles.clear();
  40. }
  41. void VEditor::init()
  42. {
  43. const int labelTimerInterval = 500;
  44. const int extraSelectionHighlightTimer = 500;
  45. const int trailingSpaceUpdateTimer = 500;
  46. const int labelSize = 64;
  47. m_document = documentW();
  48. QObject::connect(m_document, &QTextDocument::contentsChanged,
  49. m_object, &VEditorObject::clearFindCache);
  50. m_selectedWordFg = QColor(g_config->getEditorSelectedWordFg());
  51. m_selectedWordBg = QColor(g_config->getEditorSelectedWordBg());
  52. m_searchedWordFg = QColor(g_config->getEditorSearchedWordFg());
  53. m_searchedWordBg = QColor(g_config->getEditorSearchedWordBg());
  54. m_searchedWordCursorFg = QColor(g_config->getEditorSearchedWordCursorFg());
  55. m_searchedWordCursorBg = QColor(g_config->getEditorSearchedWordCursorBg());
  56. m_incrementalSearchedWordFg = QColor(g_config->getEditorIncrementalSearchedWordFg());
  57. m_incrementalSearchedWordBg = QColor(g_config->getEditorIncrementalSearchedWordBg());
  58. m_trailingSpaceColor = QColor(g_config->getEditorTrailingSpaceBg());
  59. QPixmap wrapPixmap(":/resources/icons/search_wrap.svg");
  60. m_wrapLabel = new QLabel(m_editor);
  61. m_wrapLabel->setPixmap(wrapPixmap.scaled(labelSize, labelSize));
  62. m_wrapLabel->hide();
  63. m_labelTimer = new QTimer(m_editor);
  64. m_labelTimer->setSingleShot(true);
  65. m_labelTimer->setInterval(labelTimerInterval);
  66. QObject::connect(m_labelTimer, &QTimer::timeout,
  67. m_object, &VEditorObject::labelTimerTimeout);
  68. m_highlightTimer = new QTimer(m_editor);
  69. m_highlightTimer->setSingleShot(true);
  70. m_highlightTimer->setInterval(extraSelectionHighlightTimer);
  71. QObject::connect(m_highlightTimer, &QTimer::timeout,
  72. m_object, &VEditorObject::doHighlightExtraSelections);
  73. m_trailingSpaceTimer = new QTimer(m_editor);
  74. m_trailingSpaceTimer->setSingleShot(true);
  75. m_trailingSpaceTimer->setInterval(trailingSpaceUpdateTimer);
  76. QObject::connect(m_trailingSpaceTimer, &QTimer::timeout,
  77. m_object, &VEditorObject::doUpdateTrailingSpaceHighlights);
  78. m_extraSelections.resize((int)SelectionId::MaxSelection);
  79. updateFontAndPalette();
  80. m_config.init(QFontMetrics(m_editor->font()), false);
  81. updateEditConfig();
  82. }
  83. void VEditor::labelTimerTimeout()
  84. {
  85. m_wrapLabel->hide();
  86. }
  87. void VEditor::updateTrailingSpaceHighlights()
  88. {
  89. if (m_trailingSpaceSelectionTS != m_timeStamp) {
  90. m_trailingSpaceTimer->start();
  91. } else {
  92. highlightExtraSelections(false);
  93. }
  94. }
  95. void VEditor::doHighlightExtraSelections()
  96. {
  97. int nrExtra = m_extraSelections.size();
  98. Q_ASSERT(nrExtra == (int)SelectionId::MaxSelection);
  99. QList<QTextEdit::ExtraSelection> extraSelects;
  100. for (int i = 0; i < nrExtra; ++i) {
  101. if (i == (int)SelectionId::TrailingSapce) {
  102. filterTrailingSpace(extraSelects, m_extraSelections[i]);
  103. } else {
  104. extraSelects.append(m_extraSelections[i]);
  105. }
  106. }
  107. setExtraSelectionsW(extraSelects);
  108. }
  109. void VEditor::doUpdateTrailingSpaceHighlights()
  110. {
  111. if (!g_config->getEnableTrailingSpaceHighlight()) {
  112. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::TrailingSapce];
  113. if (!selects.isEmpty()) {
  114. selects.clear();
  115. highlightExtraSelections(true);
  116. }
  117. m_trailingSpaceSelectionTS = m_timeStamp;
  118. return;
  119. }
  120. QTextCharFormat format;
  121. format.setBackground(m_trailingSpaceColor);
  122. QString text("\\s+$");
  123. highlightTextAll(text,
  124. FindOption::RegularExpression,
  125. SelectionId::TrailingSapce,
  126. format);
  127. m_trailingSpaceSelectionTS = m_timeStamp;
  128. highlightExtraSelections(true);
  129. }
  130. void VEditor::updateEditConfig()
  131. {
  132. m_config.update(QFontMetrics(m_editor->font()));
  133. if (m_config.m_tabStopWidth > 0) {
  134. setTabStopWidthW(m_config.m_tabStopWidth);
  135. }
  136. emit m_object->configUpdated();
  137. }
  138. void VEditor::highlightOnCursorPositionChanged()
  139. {
  140. static QTextCursor lastCursor;
  141. QTextCursor cursor = textCursorW();
  142. if (lastCursor.isNull() || cursor.blockNumber() != lastCursor.blockNumber()) {
  143. updateTrailingSpaceHighlights();
  144. highlightCurrentLine();
  145. } else {
  146. // Judge whether we have trailing space at current line.
  147. QString text = cursor.block().text();
  148. if (!text.isEmpty()) {
  149. auto it = text.rbegin();
  150. bool needUpdate = it->isSpace();
  151. if (!needUpdate
  152. && cursor.atBlockEnd()
  153. && text.size() > 1) {
  154. ++it;
  155. needUpdate = it->isSpace();
  156. // Input two chars at once in Chinese.
  157. if (!needUpdate && text.size() > 2) {
  158. ++it;
  159. needUpdate = it->isSpace();
  160. }
  161. }
  162. if (needUpdate) {
  163. updateTrailingSpaceHighlights();
  164. }
  165. }
  166. // Handle word-wrap in one block.
  167. // Highlight current line if in different visual line.
  168. if ((lastCursor.positionInBlock() - lastCursor.columnNumber()) !=
  169. (cursor.positionInBlock() - cursor.columnNumber())) {
  170. highlightCurrentLine();
  171. }
  172. }
  173. lastCursor = cursor;
  174. }
  175. void VEditor::updateTimeStamp()
  176. {
  177. ++m_timeStamp;
  178. }
  179. void VEditor::highlightCurrentLine()
  180. {
  181. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::CurrentLine];
  182. if (g_config->getHighlightCursorLine()) {
  183. // Need to highlight current line.
  184. selects.clear();
  185. // A long block maybe splited into multiple visual lines.
  186. QTextEdit::ExtraSelection select;
  187. select.format.setBackground(m_config.m_cursorLineBg);
  188. select.format.setProperty(QTextFormat::FullWidthSelection, true);
  189. QTextCursor cursor = textCursorW();
  190. if (m_config.m_highlightWholeBlock) {
  191. cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor, 1);
  192. QTextBlock block = cursor.block();
  193. int blockEnd = block.position() + block.length();
  194. int pos = -1;
  195. while (cursor.position() < blockEnd && pos != cursor.position()) {
  196. QTextEdit::ExtraSelection newSelect = select;
  197. newSelect.cursor = cursor;
  198. selects.append(newSelect);
  199. pos = cursor.position();
  200. cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, 1);
  201. }
  202. } else {
  203. cursor.clearSelection();
  204. select.cursor = cursor;
  205. selects.append(select);
  206. }
  207. } else {
  208. // Need to clear current line highlight.
  209. if (selects.isEmpty()) {
  210. return;
  211. }
  212. selects.clear();
  213. }
  214. highlightExtraSelections(true);
  215. }
  216. void VEditor::filterTrailingSpace(QList<QTextEdit::ExtraSelection> &p_selects,
  217. const QList<QTextEdit::ExtraSelection> &p_src)
  218. {
  219. QTextCursor cursor = textCursorW();
  220. bool blockEnd = cursor.atBlockEnd();
  221. int blockNum = cursor.blockNumber();
  222. for (auto it = p_src.begin(); it != p_src.end(); ++it) {
  223. if (it->cursor.blockNumber() == blockNum) {
  224. if (blockEnd) {
  225. // When cursor is at block end, we do not display any trailing space
  226. // at current line.
  227. continue;
  228. }
  229. QString text = cursor.block().text();
  230. if (text.isEmpty()) {
  231. continue;
  232. } else if (!text[text.size() - 1].isSpace()) {
  233. continue;
  234. }
  235. }
  236. p_selects.append(*it);
  237. }
  238. }
  239. void VEditor::highlightExtraSelections(bool p_now)
  240. {
  241. m_highlightTimer->stop();
  242. if (p_now) {
  243. doHighlightExtraSelections();
  244. } else {
  245. m_highlightTimer->start();
  246. }
  247. }
  248. void VEditor::highlightTextAll(const QString &p_text,
  249. uint p_options,
  250. SelectionId p_id,
  251. QTextCharFormat p_format,
  252. void (*p_filter)(VEditor *,
  253. QList<QTextEdit::ExtraSelection> &))
  254. {
  255. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)p_id];
  256. if (!p_text.isEmpty()) {
  257. selects.clear();
  258. QList<QTextCursor> occurs = findTextAll(p_text, p_options);
  259. for (int i = 0; i < occurs.size(); ++i) {
  260. QTextEdit::ExtraSelection select;
  261. select.format = p_format;
  262. select.cursor = occurs[i];
  263. selects.append(select);
  264. }
  265. } else {
  266. if (selects.isEmpty()) {
  267. return;
  268. }
  269. selects.clear();
  270. }
  271. if (p_filter) {
  272. p_filter(this, selects);
  273. }
  274. highlightExtraSelections();
  275. }
  276. static QTextDocument::FindFlags findOptionsToFlags(uint p_options, bool p_forward)
  277. {
  278. QTextDocument::FindFlags findFlags;
  279. if (p_options & FindOption::CaseSensitive) {
  280. findFlags |= QTextDocument::FindCaseSensitively;
  281. }
  282. if (p_options & FindOption::WholeWordOnly) {
  283. findFlags |= QTextDocument::FindWholeWords;
  284. }
  285. if (!p_forward) {
  286. findFlags |= QTextDocument::FindBackward;
  287. }
  288. return findFlags;
  289. }
  290. QList<QTextCursor> VEditor::findTextAll(const QString &p_text,
  291. uint p_options,
  292. int p_start,
  293. int p_end)
  294. {
  295. QList<QTextCursor> results;
  296. if (p_text.isEmpty()) {
  297. return results;
  298. }
  299. // Options
  300. bool caseSensitive = p_options & FindOption::CaseSensitive;
  301. QTextDocument::FindFlags findFlags = findOptionsToFlags(p_options, true);
  302. if (p_options & FindOption::RegularExpression) {
  303. QRegExp exp(p_text,
  304. caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
  305. results = findTextAllInRange(m_document, exp, findFlags, p_start, p_end);
  306. } else {
  307. results = findTextAllInRange(m_document, p_text, findFlags, p_start, p_end);
  308. }
  309. return results;
  310. }
  311. const QList<QTextCursor> &VEditor::findTextAllCached(const QString &p_text,
  312. uint p_options,
  313. int p_start,
  314. int p_end)
  315. {
  316. if (p_text.isEmpty()) {
  317. m_findInfo.clear();
  318. return m_findInfo.m_result;
  319. }
  320. if (m_findInfo.isCached(p_text, p_options, p_start, p_end)) {
  321. return m_findInfo.m_result;
  322. }
  323. QList<QTextCursor> result = findTextAll(p_text, p_options, p_start, p_end);
  324. m_findInfo.update(p_text, p_options, p_start, p_end, result);
  325. return m_findInfo.m_result;
  326. }
  327. void VEditor::highlightSelectedWord()
  328. {
  329. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SelectedWord];
  330. if (!g_config->getHighlightSelectedWord()) {
  331. if (!selects.isEmpty()) {
  332. selects.clear();
  333. highlightExtraSelections(true);
  334. }
  335. return;
  336. }
  337. QString text = textCursorW().selectedText().trimmed();
  338. if (text.isEmpty() || wordInSearchedSelection(text)) {
  339. selects.clear();
  340. highlightExtraSelections(true);
  341. return;
  342. }
  343. QTextCharFormat format;
  344. format.setForeground(m_selectedWordFg);
  345. format.setBackground(m_selectedWordBg);
  346. highlightTextAll(text,
  347. FindOption::CaseSensitive,
  348. SelectionId::SelectedWord,
  349. format);
  350. }
  351. bool VEditor::wordInSearchedSelection(const QString &p_text)
  352. {
  353. QString text = p_text.trimmed();
  354. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  355. for (int i = 0; i < selects.size(); ++i) {
  356. QString searchedWord = selects[i].cursor.selectedText();
  357. if (text == searchedWord.trimmed()) {
  358. return true;
  359. }
  360. }
  361. return false;
  362. }
  363. bool VEditor::isModified() const
  364. {
  365. return m_document->isModified();
  366. }
  367. void VEditor::setModified(bool p_modified)
  368. {
  369. m_document->setModified(p_modified);
  370. }
  371. void VEditor::insertImage()
  372. {
  373. if (m_editOps) {
  374. m_editOps->insertImage();
  375. }
  376. }
  377. void VEditor::insertLink()
  378. {
  379. if (!m_editOps) {
  380. return;
  381. }
  382. QString text;
  383. QString linkText, linkUrl;
  384. QTextCursor cursor = textCursorW();
  385. if (cursor.hasSelection()) {
  386. text = VEditUtils::selectedText(cursor).trimmed();
  387. // Only pure space is accepted.
  388. QRegExp reg("[\\S ]*");
  389. if (reg.exactMatch(text)) {
  390. QUrl url = QUrl::fromUserInput(text,
  391. m_file->fetchBasePath());
  392. QRegExp urlReg("[\\.\\\\/]");
  393. if (url.isValid()
  394. && text.contains(urlReg)) {
  395. // Url.
  396. linkUrl = text;
  397. } else {
  398. // Text.
  399. linkText = text;
  400. }
  401. }
  402. }
  403. VInsertLinkDialog dialog(QObject::tr("Insert Link"),
  404. "",
  405. "",
  406. linkText,
  407. linkUrl,
  408. false,
  409. m_editor);
  410. if (dialog.exec() == QDialog::Accepted) {
  411. linkText = dialog.getLinkText();
  412. linkUrl = dialog.getLinkUrl();
  413. Q_ASSERT(!linkText.isEmpty() && !linkUrl.isEmpty());
  414. m_editOps->insertLink(linkText, linkUrl);
  415. }
  416. }
  417. bool VEditor::peekText(const QString &p_text, uint p_options, bool p_forward)
  418. {
  419. if (p_text.isEmpty()) {
  420. makeBlockVisible(m_document->findBlock(textCursorW().selectionStart()));
  421. highlightIncrementalSearchedWord(QTextCursor());
  422. return false;
  423. }
  424. bool wrapped = false;
  425. QTextCursor retCursor;
  426. int start = textCursorW().position();
  427. int skipPosition = start;
  428. bool found = false;
  429. while (true) {
  430. found = findTextHelper(p_text, p_options, p_forward, start, wrapped, retCursor);
  431. if (found) {
  432. if (p_forward && retCursor.selectionStart() == skipPosition) {
  433. // Skip the first match.
  434. skipPosition = -1;
  435. start = retCursor.selectionEnd();
  436. continue;
  437. }
  438. makeBlockVisible(m_document->findBlock(retCursor.selectionStart()));
  439. highlightIncrementalSearchedWord(retCursor);
  440. }
  441. break;
  442. }
  443. return found;
  444. }
  445. // @p_cursors is in ascending order.
  446. // If @p_forward is true, find the smallest cursor whose selection start is greater
  447. // than @p_pos or the first cursor if wrapped.
  448. // Otherwise, find the largest cursor whose selection start is smaller than @p_pos
  449. // or the last cursor if wrapped.
  450. static int selectCursor(const QList<QTextCursor> &p_cursors,
  451. int p_pos,
  452. bool p_forward,
  453. bool &p_wrapped)
  454. {
  455. Q_ASSERT(!p_cursors.isEmpty());
  456. p_wrapped = false;
  457. int first = 0, last = p_cursors.size() - 1;
  458. int lastMatch = -1;
  459. while (first <= last) {
  460. int mid = (first + last) / 2;
  461. const QTextCursor &cur = p_cursors.at(mid);
  462. if (p_forward) {
  463. if (cur.selectionStart() < p_pos) {
  464. first = mid + 1;
  465. } else if (cur.selectionStart() == p_pos) {
  466. // Next one is the right one.
  467. if (mid < p_cursors.size() - 1) {
  468. lastMatch = mid + 1;
  469. } else {
  470. lastMatch = 0;
  471. p_wrapped = true;
  472. }
  473. break;
  474. } else {
  475. // It is a match.
  476. if (lastMatch == -1 || mid < lastMatch) {
  477. lastMatch = mid;
  478. }
  479. last = mid - 1;
  480. }
  481. } else {
  482. if (cur.selectionStart() > p_pos) {
  483. last = mid - 1;
  484. } else if (cur.selectionStart() == p_pos) {
  485. // Previous one is the right one.
  486. if (mid > 0) {
  487. lastMatch = mid - 1;
  488. } else {
  489. lastMatch = p_cursors.size() - 1;
  490. p_wrapped = true;
  491. }
  492. break;
  493. } else {
  494. // It is a match.
  495. if (lastMatch == -1 || mid > lastMatch) {
  496. lastMatch = mid;
  497. }
  498. first = mid + 1;
  499. }
  500. }
  501. }
  502. if (lastMatch == -1) {
  503. p_wrapped = true;
  504. lastMatch = p_forward ? 0 : (p_cursors.size() - 1);
  505. }
  506. return lastMatch;
  507. }
  508. bool VEditor::findText(const QString &p_text,
  509. uint p_options,
  510. bool p_forward,
  511. QTextCursor *p_cursor,
  512. QTextCursor::MoveMode p_moveMode,
  513. bool p_useLeftSideOfCursor)
  514. {
  515. return findTextInRange(p_text,
  516. p_options,
  517. p_forward,
  518. p_cursor,
  519. p_moveMode,
  520. p_useLeftSideOfCursor);
  521. }
  522. bool VEditor::findTextInRange(const QString &p_text,
  523. uint p_options,
  524. bool p_forward,
  525. QTextCursor *p_cursor,
  526. QTextCursor::MoveMode p_moveMode,
  527. bool p_useLeftSideOfCursor,
  528. int p_start,
  529. int p_end)
  530. {
  531. clearIncrementalSearchedWordHighlight();
  532. if (p_text.isEmpty()) {
  533. m_findInfo.clear();
  534. clearSearchedWordHighlight();
  535. return false;
  536. }
  537. const QList<QTextCursor> &result = findTextAllCached(p_text, p_options, p_start, p_end);
  538. if (result.isEmpty()) {
  539. clearSearchedWordHighlight();
  540. emit m_object->statusMessage(QObject::tr("No match found"));
  541. } else {
  542. // Locate to the right match and update current cursor.
  543. QTextCursor cursor = textCursorW();
  544. int pos = p_cursor ? p_cursor->position() : cursor.position();
  545. if (p_useLeftSideOfCursor) {
  546. --pos;
  547. }
  548. bool wrapped = false;
  549. int idx = selectCursor(result, pos, p_forward, wrapped);
  550. const QTextCursor &tcursor = result.at(idx);
  551. if (wrapped) {
  552. showWrapLabel();
  553. }
  554. if (p_cursor) {
  555. p_cursor->setPosition(tcursor.selectionStart(), p_moveMode);
  556. } else {
  557. cursor.setPosition(tcursor.selectionStart(), p_moveMode);
  558. setTextCursorW(cursor);
  559. }
  560. highlightSearchedWord(result);
  561. highlightSearchedWordUnderCursor(tcursor);
  562. emit m_object->statusMessage(QObject::tr("Match found: %2 of %3")
  563. .arg(idx + 1)
  564. .arg(result.size()));
  565. }
  566. return !result.isEmpty();
  567. }
  568. bool VEditor::findTextOne(const QString &p_text, uint p_options, bool p_forward)
  569. {
  570. clearIncrementalSearchedWordHighlight();
  571. if (p_text.isEmpty()) {
  572. clearSearchedWordHighlight();
  573. return false;
  574. }
  575. QTextCursor cursor = textCursorW();
  576. bool wrapped = false;
  577. QTextCursor retCursor;
  578. int start = cursor.position();
  579. bool found = findTextHelper(p_text, p_options, p_forward, start, wrapped, retCursor);
  580. if (found) {
  581. Q_ASSERT(!retCursor.isNull());
  582. if (wrapped) {
  583. showWrapLabel();
  584. }
  585. cursor.setPosition(retCursor.selectionStart(), QTextCursor::MoveAnchor);
  586. setTextCursorW(cursor);
  587. highlightSearchedWordUnderCursor(retCursor);
  588. } else {
  589. clearSearchedWordHighlight();
  590. }
  591. return found;
  592. }
  593. bool VEditor::findTextInRange(const QString &p_text,
  594. uint p_options,
  595. bool p_forward,
  596. int p_start,
  597. int p_end)
  598. {
  599. return findTextInRange(p_text,
  600. p_options,
  601. p_forward,
  602. nullptr,
  603. QTextCursor::MoveAnchor,
  604. false,
  605. p_start,
  606. p_end);
  607. }
  608. void VEditor::highlightIncrementalSearchedWord(const QTextCursor &p_cursor)
  609. {
  610. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
  611. if (!g_config->getHighlightSearchedWord() || !p_cursor.hasSelection()) {
  612. if (!selects.isEmpty()) {
  613. selects.clear();
  614. highlightExtraSelections(true);
  615. }
  616. return;
  617. }
  618. selects.clear();
  619. QTextEdit::ExtraSelection select;
  620. select.format.setForeground(m_incrementalSearchedWordFg);
  621. select.format.setBackground(m_incrementalSearchedWordBg);
  622. select.cursor = p_cursor;
  623. selects.append(select);
  624. highlightExtraSelections(true);
  625. }
  626. // Use QPlainTextEdit::find() instead of QTextDocument::find() because the later has
  627. // bugs in searching backward.
  628. bool VEditor::findTextHelper(const QString &p_text,
  629. uint p_options,
  630. bool p_forward,
  631. int p_start,
  632. bool &p_wrapped,
  633. QTextCursor &p_cursor)
  634. {
  635. p_wrapped = false;
  636. bool found = false;
  637. // Options
  638. bool caseSensitive = p_options & FindOption::CaseSensitive;
  639. QTextDocument::FindFlags findFlags = findOptionsToFlags(p_options, p_forward);
  640. // Use regular expression
  641. bool useRegExp = p_options & FindOption::RegularExpression;
  642. QRegExp exp;
  643. if (useRegExp) {
  644. useRegExp = true;
  645. // FIXME: hang bug in Qt's find().
  646. QRegExp test("[$^]+");
  647. if (test.exactMatch(p_text)) {
  648. return false;
  649. }
  650. exp = QRegExp(p_text,
  651. caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
  652. }
  653. // Store current state of the cursor.
  654. QTextCursor cursor = textCursorW();
  655. if (cursor.position() != p_start) {
  656. if (p_start < 0) {
  657. p_start = 0;
  658. } else if (p_start > m_document->characterCount()) {
  659. p_start = m_document->characterCount();
  660. }
  661. }
  662. QTextCursor startCursor = cursor;
  663. // Will clear the selection.
  664. startCursor.setPosition(p_start);
  665. setTextCursorW(startCursor);
  666. while (!found) {
  667. if (useRegExp) {
  668. found = findW(exp, findFlags);
  669. } else {
  670. found = findW(p_text, findFlags);
  671. }
  672. if (p_wrapped) {
  673. break;
  674. }
  675. if (!found) {
  676. // Wrap to the other end of the document to search again.
  677. p_wrapped = true;
  678. QTextCursor wrapCursor = textCursorW();
  679. if (p_forward) {
  680. wrapCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
  681. } else {
  682. wrapCursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
  683. }
  684. setTextCursorW(wrapCursor);
  685. }
  686. }
  687. if (found) {
  688. p_cursor = textCursorW();
  689. }
  690. // Restore the original cursor.
  691. setTextCursorW(cursor);
  692. return found;
  693. }
  694. void VEditor::clearIncrementalSearchedWordHighlight(bool p_now)
  695. {
  696. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::IncrementalSearchedKeyword];
  697. if (selects.isEmpty()) {
  698. return;
  699. }
  700. selects.clear();
  701. highlightExtraSelections(p_now);
  702. }
  703. void VEditor::clearSearchedWordHighlight()
  704. {
  705. clearIncrementalSearchedWordHighlight(false);
  706. clearSearchedWordUnderCursorHighlight(false);
  707. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  708. if (selects.isEmpty()) {
  709. return;
  710. }
  711. selects.clear();
  712. highlightExtraSelections(true);
  713. }
  714. void VEditor::clearSearchedWordUnderCursorHighlight(bool p_now)
  715. {
  716. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeywordUnderCursor];
  717. if (selects.isEmpty()) {
  718. return;
  719. }
  720. selects.clear();
  721. highlightExtraSelections(p_now);
  722. }
  723. void VEditor::showWrapLabel()
  724. {
  725. int labelW = m_wrapLabel->width();
  726. int labelH = m_wrapLabel->height();
  727. int x = (m_editor->width() - labelW) / 2;
  728. int y = (m_editor->height() - labelH) / 2;
  729. if (x < 0) {
  730. x = 0;
  731. }
  732. if (y < 0) {
  733. y = 0;
  734. }
  735. m_wrapLabel->move(x, y);
  736. m_wrapLabel->show();
  737. m_labelTimer->stop();
  738. m_labelTimer->start();
  739. }
  740. void VEditor::highlightSearchedWord(const QList<QTextCursor> &p_matches)
  741. {
  742. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeyword];
  743. if (!g_config->getHighlightSearchedWord() || p_matches.isEmpty()) {
  744. if (!selects.isEmpty()) {
  745. selects.clear();
  746. highlightExtraSelections(true);
  747. }
  748. return;
  749. }
  750. selects.clear();
  751. QTextCharFormat format;
  752. format.setForeground(m_searchedWordFg);
  753. format.setBackground(m_searchedWordBg);
  754. for (int i = 0; i < p_matches.size(); ++i) {
  755. QTextEdit::ExtraSelection select;
  756. select.format = format;
  757. select.cursor = p_matches[i];
  758. selects.append(select);
  759. }
  760. highlightExtraSelections();
  761. }
  762. void VEditor::highlightSearchedWordUnderCursor(const QTextCursor &p_cursor)
  763. {
  764. QList<QTextEdit::ExtraSelection> &selects = m_extraSelections[(int)SelectionId::SearchedKeywordUnderCursor];
  765. if (!p_cursor.hasSelection()) {
  766. if (!selects.isEmpty()) {
  767. selects.clear();
  768. highlightExtraSelections(true);
  769. }
  770. return;
  771. }
  772. selects.clear();
  773. QTextEdit::ExtraSelection select;
  774. select.format.setForeground(m_searchedWordCursorFg);
  775. select.format.setBackground(m_searchedWordCursorBg);
  776. select.cursor = p_cursor;
  777. selects.append(select);
  778. highlightExtraSelections(true);
  779. }
  780. static void fillReplaceTextWithBackReference(QString &p_replaceText,
  781. const QString &p_text,
  782. const QRegExp &p_exp)
  783. {
  784. int idx = p_exp.indexIn(p_text);
  785. if (idx != 0) {
  786. return;
  787. }
  788. QStringList caps = p_exp.capturedTexts();
  789. if (caps.size() < 2) {
  790. return;
  791. }
  792. QChar sla('\\');
  793. int pos = 0;
  794. while (pos < p_replaceText.size()) {
  795. int idx = p_replaceText.indexOf(sla, pos);
  796. if (idx == -1 || idx == p_replaceText.size() - 1) {
  797. break;
  798. }
  799. QChar ne(p_replaceText[idx + 1]);
  800. if (ne == sla) {
  801. // \\ to \.
  802. p_replaceText.remove(idx, 1);
  803. } else if (ne.isDigit()) {
  804. // TODO: for now, we only support 1 - 9.
  805. int num = ne.digitValue();
  806. if (num > 0 && num < caps.size()) {
  807. // Replace it.
  808. p_replaceText.replace(idx, 2, caps[num]);
  809. pos = idx + caps[num].size() - 2;
  810. continue;
  811. }
  812. }
  813. pos = idx + 1;
  814. }
  815. }
  816. void VEditor::replaceText(const QString &p_text,
  817. uint p_options,
  818. const QString &p_replaceText,
  819. bool p_findNext)
  820. {
  821. QTextCursor cursor = textCursorW();
  822. bool wrapped = false;
  823. QTextCursor retCursor;
  824. QString newText(p_replaceText);
  825. bool useRegExp = p_options & FindOption::RegularExpression;
  826. QRegExp exp;
  827. if (useRegExp) {
  828. exp = QRegExp(p_text,
  829. (p_options & FindOption::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive);
  830. }
  831. bool found = findTextHelper(p_text,
  832. p_options,
  833. true,
  834. cursor.position(),
  835. wrapped,
  836. retCursor);
  837. if (found) {
  838. if (retCursor.selectionStart() == cursor.position()) {
  839. // Matched.
  840. if (useRegExp) {
  841. // Handle \1, \2 in replace text.
  842. fillReplaceTextWithBackReference(newText,
  843. VEditUtils::selectedText(retCursor),
  844. exp);
  845. }
  846. retCursor.beginEditBlock();
  847. retCursor.insertText(newText);
  848. retCursor.endEditBlock();
  849. setTextCursorW(retCursor);
  850. }
  851. if (p_findNext) {
  852. findTextOne(p_text, p_options, true);
  853. }
  854. }
  855. }
  856. void VEditor::replaceTextAll(const QString &p_text,
  857. uint p_options,
  858. const QString &p_replaceText)
  859. {
  860. // Replace from the start to the end and restore the cursor.
  861. QTextCursor cursor = textCursorW();
  862. int nrReplaces = 0;
  863. QTextCursor tmpCursor = cursor;
  864. tmpCursor.setPosition(0);
  865. setTextCursorW(tmpCursor);
  866. int start = tmpCursor.position();
  867. QString newText;
  868. bool useRegExp = p_options & FindOption::RegularExpression;
  869. QRegExp exp;
  870. if (useRegExp) {
  871. exp = QRegExp(p_text,
  872. (p_options & FindOption::CaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive);
  873. } else {
  874. newText = p_replaceText;
  875. }
  876. while (true) {
  877. bool wrapped = false;
  878. QTextCursor retCursor;
  879. bool found = findTextHelper(p_text,
  880. p_options,
  881. true,
  882. start,
  883. wrapped,
  884. retCursor);
  885. if (!found) {
  886. break;
  887. } else {
  888. if (wrapped) {
  889. // Wrap back.
  890. break;
  891. }
  892. if (useRegExp) {
  893. newText = p_replaceText;
  894. fillReplaceTextWithBackReference(newText,
  895. VEditUtils::selectedText(retCursor),
  896. exp);
  897. }
  898. nrReplaces++;
  899. retCursor.beginEditBlock();
  900. retCursor.insertText(newText);
  901. retCursor.endEditBlock();
  902. setTextCursorW(retCursor);
  903. start = retCursor.position();
  904. }
  905. }
  906. // Restore cursor position.
  907. cursor.clearSelection();
  908. setTextCursorW(cursor);
  909. qDebug() << "replace all" << nrReplaces << "occurences";
  910. emit m_object->statusMessage(QObject::tr("Replace %1 %2").arg(nrReplaces)
  911. .arg(nrReplaces > 1 ? QObject::tr("occurences")
  912. : QObject::tr("occurence")));
  913. }
  914. void VEditor::evaluateMagicWords()
  915. {
  916. QString text;
  917. QTextCursor cursor = textCursorW();
  918. if (!cursor.hasSelection()) {
  919. // Get the WORD in current cursor.
  920. int start, end;
  921. VEditUtils::findCurrentWORD(cursor, start, end);
  922. if (start == end) {
  923. return;
  924. } else {
  925. cursor.setPosition(start);
  926. cursor.setPosition(end, QTextCursor::KeepAnchor);
  927. }
  928. }
  929. text = VEditUtils::selectedText(cursor);
  930. Q_ASSERT(!text.isEmpty());
  931. QString evaText = g_mwMgr->evaluate(text);
  932. if (text != evaText) {
  933. qDebug() << "evaluateMagicWords" << text << evaText;
  934. cursor.insertText(evaText);
  935. if (m_editOps) {
  936. m_editOps->setVimMode(VimMode::Insert);
  937. }
  938. setTextCursorW(cursor);
  939. }
  940. }
  941. void VEditor::setReadOnlyAndHighlightCurrentLine(bool p_readonly)
  942. {
  943. setReadOnlyW(p_readonly);
  944. highlightCurrentLine();
  945. }
  946. bool VEditor::handleMousePressEvent(QMouseEvent *p_event)
  947. {
  948. if (p_event->button() == Qt::LeftButton
  949. && p_event->modifiers() == Qt::ControlModifier
  950. && !textCursorW().hasSelection()) {
  951. m_oriMouseX = p_event->x();
  952. m_oriMouseY = p_event->y();
  953. m_readyToScroll = true;
  954. m_mouseMoveScrolled = false;
  955. p_event->accept();
  956. return true;
  957. }
  958. m_readyToScroll = false;
  959. m_mouseMoveScrolled = false;
  960. return false;
  961. }
  962. bool VEditor::handleMouseReleaseEvent(QMouseEvent *p_event)
  963. {
  964. if (m_mouseMoveScrolled || m_readyToScroll) {
  965. viewportW()->setCursor(Qt::IBeamCursor);
  966. m_readyToScroll = false;
  967. m_mouseMoveScrolled = false;
  968. p_event->accept();
  969. return true;
  970. }
  971. m_readyToScroll = false;
  972. m_mouseMoveScrolled = false;
  973. return false;
  974. }
  975. bool VEditor::handleMouseMoveEvent(QMouseEvent *p_event)
  976. {
  977. const int threshold = 5;
  978. if (m_readyToScroll) {
  979. int deltaX = p_event->x() - m_oriMouseX;
  980. int deltaY = p_event->y() - m_oriMouseY;
  981. if (qAbs(deltaX) >= threshold || qAbs(deltaY) >= threshold) {
  982. m_oriMouseX = p_event->x();
  983. m_oriMouseY = p_event->y();
  984. if (!m_mouseMoveScrolled) {
  985. m_mouseMoveScrolled = true;
  986. viewportW()->setCursor(Qt::SizeAllCursor);
  987. }
  988. QScrollBar *verBar = verticalScrollBarW();
  989. QScrollBar *horBar = horizontalScrollBarW();
  990. if (verBar->isVisible()) {
  991. verBar->setValue(verBar->value() - deltaY);
  992. }
  993. if (horBar->isVisible()) {
  994. horBar->setValue(horBar->value() - deltaX);
  995. }
  996. }
  997. p_event->accept();
  998. return true;
  999. }
  1000. return false;
  1001. }
  1002. bool VEditor::handleWheelEvent(QWheelEvent *p_event)
  1003. {
  1004. Qt::KeyboardModifiers modifiers = p_event->modifiers();
  1005. if (modifiers == Qt::ShiftModifier) {
  1006. // Scroll horizontally.
  1007. QPoint numPixels = p_event->pixelDelta();
  1008. QPoint numDegrees = p_event->angleDelta() / 8;
  1009. QScrollBar *horBar = horizontalScrollBarW();
  1010. int steps = 0;
  1011. if (!numPixels.isNull()) {
  1012. steps = numPixels.y();
  1013. } else if (!numDegrees.isNull()) {
  1014. QPoint numSteps = numDegrees / 15;
  1015. steps = numSteps.y() * horBar->singleStep();
  1016. }
  1017. if (horBar->minimum() != horBar->maximum()) {
  1018. horBar->setValue(horBar->value() - steps);
  1019. }
  1020. p_event->accept();
  1021. return true;
  1022. } else if (modifiers == Qt::ControlModifier) {
  1023. // Zoom in/out.
  1024. QPoint angle = p_event->angleDelta();
  1025. if (!angle.isNull() && (angle.y() != 0)) {
  1026. if (angle.y() > 0) {
  1027. zoomInW();
  1028. } else {
  1029. zoomOutW();
  1030. }
  1031. }
  1032. p_event->accept();
  1033. return true;
  1034. }
  1035. return false;
  1036. }
  1037. void VEditor::requestUpdateVimStatus()
  1038. {
  1039. if (m_editOps) {
  1040. m_editOps->requestUpdateVimStatus();
  1041. } else {
  1042. emit m_object->vimStatusUpdated(NULL);
  1043. }
  1044. }
  1045. bool VEditor::handleInputMethodQuery(Qt::InputMethodQuery p_query,
  1046. QVariant &p_var) const
  1047. {
  1048. if (p_query == Qt::ImEnabled) {
  1049. p_var = m_enableInputMethod;
  1050. return true;
  1051. }
  1052. return false;
  1053. }
  1054. void VEditor::setInputMethodEnabled(bool p_enabled)
  1055. {
  1056. if (m_enableInputMethod != p_enabled) {
  1057. m_enableInputMethod = p_enabled;
  1058. QInputMethod *im = QGuiApplication::inputMethod();
  1059. im->reset();
  1060. // Ask input method to query current state, which will call inputMethodQuery().
  1061. im->update(Qt::ImEnabled);
  1062. }
  1063. }
  1064. void VEditor::decorateText(TextDecoration p_decoration, int p_level)
  1065. {
  1066. if (m_editOps) {
  1067. m_editOps->decorateText(p_decoration, p_level);
  1068. }
  1069. }
  1070. void VEditor::updateConfig()
  1071. {
  1072. updateEditConfig();
  1073. }
  1074. void VEditor::setVimMode(VimMode p_mode)
  1075. {
  1076. if (m_editOps) {
  1077. m_editOps->setVimMode(p_mode);
  1078. }
  1079. }
  1080. VVim *VEditor::getVim() const
  1081. {
  1082. if (m_editOps) {
  1083. return m_editOps->getVim();
  1084. }
  1085. return NULL;
  1086. }
  1087. bool VEditor::setCursorPosition(int p_blockNumber, int p_posInBlock)
  1088. {
  1089. QTextDocument *doc = documentW();
  1090. QTextBlock block = doc->findBlockByNumber(p_blockNumber);
  1091. if (!block.isValid() || block.length() <= p_posInBlock) {
  1092. return false;
  1093. }
  1094. QTextCursor cursor = textCursorW();
  1095. cursor.setPosition(block.position() + p_posInBlock);
  1096. setTextCursorW(cursor);
  1097. return true;
  1098. }
  1099. static Qt::CaseSensitivity completionCaseSensitivity(const QString &p_text)
  1100. {
  1101. bool upperCase = false;
  1102. for (int i = 0; i < p_text.size(); ++i) {
  1103. if (p_text[i].isUpper()) {
  1104. upperCase = true;
  1105. break;
  1106. }
  1107. }
  1108. return upperCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
  1109. }
  1110. void VEditor::requestCompletion(bool p_reversed)
  1111. {
  1112. QTextCursor cursor = textCursorW();
  1113. cursor.clearSelection();
  1114. setTextCursorW(cursor);
  1115. QStringList words = generateCompletionCandidates();
  1116. QString prefix = fetchCompletionPrefix();
  1117. // Smart case.
  1118. Qt::CaseSensitivity cs = completionCaseSensitivity(prefix);
  1119. cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, prefix.size());
  1120. QRect popupRect = cursorRectW(cursor);
  1121. popupRect.setLeft(popupRect.left() + lineNumberAreaWidth());
  1122. m_completer->performCompletion(words, prefix, cs, p_reversed, popupRect, this);
  1123. }
  1124. bool VEditor::isCompletionActivated() const
  1125. {
  1126. if (m_completer->widget() == m_editor
  1127. && m_completer->isPopupVisible()) {
  1128. return true;
  1129. }
  1130. return false;
  1131. }
  1132. QStringList VEditor::generateCompletionCandidates() const
  1133. {
  1134. QString content = getContent();
  1135. QTextCursor cursor = textCursorW();
  1136. int start, end;
  1137. VEditUtils::findCurrentWord(cursor, start, end, true);
  1138. QRegExp reg("\\W+");
  1139. QStringList ret = content.mid(end).split(reg, QString::SkipEmptyParts);
  1140. ret.append(content.left(start).split(reg, QString::SkipEmptyParts));
  1141. ret.removeDuplicates();
  1142. return ret;
  1143. }
  1144. QString VEditor::fetchCompletionPrefix() const
  1145. {
  1146. QTextCursor cursor = textCursorW();
  1147. if (cursor.atBlockStart()) {
  1148. return QString();
  1149. }
  1150. int pos = cursor.position() - 1;
  1151. int blockPos = cursor.block().position();
  1152. QString prefix;
  1153. while (pos >= blockPos) {
  1154. QChar ch = m_document->characterAt(pos);
  1155. if (ch.isSpace() || VEditUtils::isWordSeparator(ch)) {
  1156. break;
  1157. }
  1158. prefix.prepend(ch);
  1159. --pos;
  1160. }
  1161. return prefix;
  1162. }
  1163. // @p_prefix may be longer than @p_completion.
  1164. void VEditor::insertCompletion(const QString &p_prefix, const QString &p_completion)
  1165. {
  1166. QTextCursor cursor = textCursorW();
  1167. cursor.joinPreviousEditBlock();
  1168. cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, p_prefix.size());
  1169. cursor.insertText(p_completion);
  1170. cursor.endEditBlock();
  1171. setTextCursorW(cursor);
  1172. }
  1173. QList<QTextCursor> VEditor::findTextAllInRange(const QTextDocument *p_doc,
  1174. const QString &p_text,
  1175. QTextDocument::FindFlags p_flags,
  1176. int p_start,
  1177. int p_end)
  1178. {
  1179. QList<QTextCursor> results;
  1180. if (p_text.isEmpty()) {
  1181. return results;
  1182. }
  1183. int start = p_start;
  1184. int end = p_end == -1 ? p_doc->characterCount() + 1 : p_end;
  1185. while (start < end) {
  1186. QTextCursor cursor = p_doc->find(p_text, start, p_flags);
  1187. if (cursor.isNull()) {
  1188. break;
  1189. } else {
  1190. start = cursor.selectionEnd();
  1191. if (start <= end) {
  1192. results.append(cursor);
  1193. }
  1194. }
  1195. }
  1196. return results;
  1197. }
  1198. QList<QTextCursor> VEditor::findTextAllInRange(const QTextDocument *p_doc,
  1199. const QRegExp &p_reg,
  1200. QTextDocument::FindFlags p_flags,
  1201. int p_start,
  1202. int p_end)
  1203. {
  1204. QList<QTextCursor> results;
  1205. if (!p_reg.isValid()) {
  1206. return results;
  1207. }
  1208. int start = p_start;
  1209. int end = p_end == -1 ? p_doc->characterCount() + 1 : p_end;
  1210. while (start < end) {
  1211. QTextCursor cursor = p_doc->find(p_reg, start, p_flags);
  1212. if (cursor.isNull()) {
  1213. break;
  1214. } else {
  1215. start = cursor.selectionEnd();
  1216. if (start <= end) {
  1217. results.append(cursor);
  1218. }
  1219. }
  1220. }
  1221. return results;
  1222. }
  1223. void VEditor::clearFindCache()
  1224. {
  1225. m_findInfo.clearResult();
  1226. }
  1227. void VEditor::nextMatch(bool p_forward)
  1228. {
  1229. if (m_findInfo.isNull()) {
  1230. return;
  1231. }
  1232. if (m_findInfo.m_useToken) {
  1233. // TODO
  1234. } else {
  1235. findTextInRange(m_findInfo.m_text,
  1236. m_findInfo.m_options,
  1237. p_forward,
  1238. m_findInfo.m_start,
  1239. m_findInfo.m_end);
  1240. }
  1241. }