veditarea.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. #include "veditarea.h"
  2. #include <QtWidgets>
  3. #include "veditwindow.h"
  4. #include "vedittab.h"
  5. #include "vnote.h"
  6. #include "vconfigmanager.h"
  7. #include "vfile.h"
  8. #include "dialog/vfindreplacedialog.h"
  9. #include "utils/vutils.h"
  10. #include "vfilesessioninfo.h"
  11. #include "vmainwindow.h"
  12. #include "vcaptain.h"
  13. #include "vfilelist.h"
  14. #include "vmathjaxpreviewhelper.h"
  15. extern VConfigManager *g_config;
  16. extern VNote *g_vnote;
  17. extern VMainWindow *g_mainWin;
  18. VEditArea::VEditArea(QWidget *parent)
  19. : QWidget(parent),
  20. VNavigationMode(),
  21. curWindowIndex(-1)
  22. {
  23. setupUI();
  24. insertSplitWindow(0);
  25. setCurrentWindow(0, false);
  26. registerCaptainTargets();
  27. QString keySeq = g_config->getShortcutKeySequence("ActivateNextTab");
  28. qDebug() << "set ActivateNextTab shortcut to" << keySeq;
  29. QShortcut *activateNextTab = new QShortcut(QKeySequence(keySeq), this);
  30. activateNextTab->setContext(Qt::ApplicationShortcut);
  31. connect(activateNextTab, &QShortcut::activated,
  32. this, [this]() {
  33. VEditWindow *win = getCurrentWindow();
  34. if (win) {
  35. win->focusNextTab(true);
  36. }
  37. });
  38. keySeq = g_config->getShortcutKeySequence("ActivatePreviousTab");
  39. qDebug() << "set ActivatePreviousTab shortcut to" << keySeq;
  40. QShortcut *activatePreviousTab = new QShortcut(QKeySequence(keySeq), this);
  41. activatePreviousTab->setContext(Qt::ApplicationShortcut);
  42. connect(activatePreviousTab, &QShortcut::activated,
  43. this, [this]() {
  44. VEditWindow *win = getCurrentWindow();
  45. if (win) {
  46. win->focusNextTab(false);
  47. }
  48. });
  49. QTimer *timer = new QTimer(this);
  50. timer->setSingleShot(false);
  51. timer->setInterval(g_config->getFileTimerInterval());
  52. connect(timer, &QTimer::timeout,
  53. this, &VEditArea::handleFileTimerTimeout);
  54. timer->start();
  55. m_autoSave = g_config->getEnableAutoSave();
  56. m_mathPreviewHelper = new VMathJaxPreviewHelper(this, this);
  57. }
  58. void VEditArea::setupUI()
  59. {
  60. splitter = new QSplitter(this);
  61. m_findReplace = new VFindReplaceDialog(this);
  62. m_findReplace->setOption(FindOption::CaseSensitive,
  63. g_config->getFindCaseSensitive());
  64. m_findReplace->setOption(FindOption::WholeWordOnly,
  65. g_config->getFindWholeWordOnly());
  66. m_findReplace->setOption(FindOption::RegularExpression,
  67. g_config->getFindRegularExpression());
  68. m_findReplace->setOption(FindOption::IncrementalSearch,
  69. g_config->getFindIncrementalSearch());
  70. QVBoxLayout *mainLayout = new QVBoxLayout();
  71. mainLayout->addWidget(splitter);
  72. mainLayout->addWidget(m_findReplace);
  73. mainLayout->setContentsMargins(0, 0, 0, 0);
  74. mainLayout->setSpacing(0);
  75. mainLayout->setStretch(0, 1);
  76. mainLayout->setStretch(1, 0);
  77. setLayout(mainLayout);
  78. connect(m_findReplace, &VFindReplaceDialog::findTextChanged,
  79. this, &VEditArea::handleFindTextChanged);
  80. connect(m_findReplace, &VFindReplaceDialog::findOptionChanged,
  81. this, &VEditArea::handleFindOptionChanged);
  82. connect(m_findReplace, SIGNAL(findNext(const QString &, uint, bool)),
  83. this, SLOT(handleFindNext(const QString &, uint, bool)));
  84. connect(m_findReplace,
  85. SIGNAL(replace(const QString &, uint, const QString &, bool)),
  86. this,
  87. SLOT(handleReplace(const QString &, uint, const QString &, bool)));
  88. connect(m_findReplace,
  89. SIGNAL(replaceAll(const QString &, uint, const QString &)),
  90. this,
  91. SLOT(handleReplaceAll(const QString &, uint, const QString &)));
  92. connect(m_findReplace, &VFindReplaceDialog::dialogClosed,
  93. this, &VEditArea::handleFindDialogClosed);
  94. m_findReplace->hide();
  95. // Shortcut Ctrl+Shift+T to open last closed file.
  96. QString keySeq = g_config->getShortcutKeySequence("LastClosedFile");
  97. qDebug() << "set LastClosedFile shortcut to" << keySeq;
  98. QShortcut *lastClosedFileShortcut = new QShortcut(QKeySequence(keySeq), this);
  99. lastClosedFileShortcut->setContext(Qt::ApplicationShortcut);
  100. connect(lastClosedFileShortcut, &QShortcut::activated,
  101. this, [this]() {
  102. if (!m_lastClosedFiles.isEmpty()) {
  103. const VFileSessionInfo &file = m_lastClosedFiles.top();
  104. QVector<VFileSessionInfo> files(1, file);
  105. m_lastClosedFiles.pop();
  106. openFiles(files);
  107. }
  108. });
  109. }
  110. void VEditArea::insertSplitWindow(int idx)
  111. {
  112. VEditWindow *win = new VEditWindow(this);
  113. splitter->insertWidget(idx, win);
  114. connect(win, &VEditWindow::tabStatusUpdated,
  115. this, &VEditArea::handleWindowTabStatusUpdated);
  116. connect(win, &VEditWindow::requestSplitWindow,
  117. this, &VEditArea::splitWindow);
  118. connect(win, &VEditWindow::requestRemoveSplit,
  119. this, &VEditArea::handleRemoveSplitRequest);
  120. connect(win, &VEditWindow::getFocused,
  121. this, &VEditArea::handleWindowFocused);
  122. connect(win, &VEditWindow::outlineChanged,
  123. this, &VEditArea::handleWindowOutlineChanged);
  124. connect(win, &VEditWindow::currentHeaderChanged,
  125. this, &VEditArea::handleWindowCurrentHeaderChanged);
  126. connect(win, &VEditWindow::statusMessage,
  127. this, &VEditArea::handleWindowStatusMessage);
  128. connect(win, &VEditWindow::vimStatusUpdated,
  129. this, &VEditArea::handleWindowVimStatusUpdated);
  130. }
  131. void VEditArea::handleWindowTabStatusUpdated(const VEditTabInfo &p_info)
  132. {
  133. if (splitter->widget(curWindowIndex) == sender()) {
  134. emit tabStatusUpdated(p_info);
  135. }
  136. }
  137. void VEditArea::handleWindowStatusMessage(const QString &p_msg)
  138. {
  139. if (splitter->widget(curWindowIndex) == sender()) {
  140. emit statusMessage(p_msg);
  141. }
  142. }
  143. void VEditArea::handleWindowVimStatusUpdated(const VVim *p_vim)
  144. {
  145. if (splitter->widget(curWindowIndex) == sender()) {
  146. emit vimStatusUpdated(p_vim);
  147. }
  148. }
  149. void VEditArea::removeSplitWindow(VEditWindow *win)
  150. {
  151. if (!win) {
  152. return;
  153. }
  154. win->hide();
  155. win->setParent(this);
  156. disconnect(win, 0, this, 0);
  157. // Should be deleted later
  158. win->deleteLater();
  159. }
  160. VEditTab *VEditArea::openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode)
  161. {
  162. if (!p_file) {
  163. return NULL;
  164. }
  165. // Update auto save settings.
  166. m_autoSave = g_config->getEnableAutoSave();
  167. // If it is DocType::Unknown, open it using system default method.
  168. if (p_file->getDocType() == DocType::Unknown) {
  169. QUrl url = QUrl::fromLocalFile(p_file->fetchPath());
  170. QDesktopServices::openUrl(url);
  171. return NULL;
  172. }
  173. // Find if it has been opened already
  174. int winIdx, tabIdx;
  175. bool existFile = false;
  176. bool setFocus = false;
  177. auto tabs = findTabsByFile(p_file);
  178. if (!tabs.empty()) {
  179. // Current window first
  180. winIdx = tabs[0].first;
  181. tabIdx = tabs[0].second;
  182. for (int i = 0; i < tabs.size(); ++i) {
  183. if (tabs[i].first == curWindowIndex) {
  184. winIdx = tabs[i].first;
  185. tabIdx = tabs[i].second;
  186. break;
  187. }
  188. }
  189. setFocus = true;
  190. existFile = true;
  191. goto out;
  192. }
  193. // Open it in current window
  194. if (curWindowIndex == -1) {
  195. // insert a new split
  196. insertSplitWindow(0);
  197. curWindowIndex = 0;
  198. }
  199. winIdx = curWindowIndex;
  200. tabIdx = openFileInWindow(winIdx, p_file, p_mode);
  201. out:
  202. VEditTab *tab = getTab(winIdx, tabIdx);
  203. setCurrentTab(winIdx, tabIdx, setFocus);
  204. if (existFile && p_forceMode) {
  205. if (p_mode == OpenFileMode::Read) {
  206. readFile();
  207. } else if (p_mode == OpenFileMode::Edit) {
  208. editFile();
  209. }
  210. }
  211. return tab;
  212. }
  213. QVector<QPair<int, int> > VEditArea::findTabsByFile(const VFile *p_file)
  214. {
  215. QVector<QPair<int, int> > tabs;
  216. int nrWin = splitter->count();
  217. for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
  218. VEditWindow *win = getWindow(winIdx);
  219. int tabIdx = win->findTabByFile(p_file);
  220. if (tabIdx != -1) {
  221. QPair<int, int> match;
  222. match.first = winIdx;
  223. match.second = tabIdx;
  224. tabs.append(match);
  225. }
  226. }
  227. return tabs;
  228. }
  229. int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode)
  230. {
  231. Q_ASSERT(windowIndex < splitter->count());
  232. VEditWindow *win = getWindow(windowIndex);
  233. return win->openFile(p_file, p_mode);
  234. }
  235. void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
  236. {
  237. VEditWindow *win = getWindow(windowIndex);
  238. win->setCurrentIndex(tabIndex);
  239. setCurrentWindow(windowIndex, setFocus);
  240. }
  241. void VEditArea::setCurrentWindow(int windowIndex, bool setFocus)
  242. {
  243. int nrWin = splitter->count();
  244. curWindowIndex = windowIndex;
  245. for (int i = 0; i < nrWin; ++i) {
  246. getWindow(i)->setCurrentWindow(false);
  247. }
  248. if (curWindowIndex > -1) {
  249. getWindow(curWindowIndex)->setCurrentWindow(true);
  250. if (setFocus) {
  251. getWindow(curWindowIndex)->focusWindow();
  252. }
  253. }
  254. // Update status
  255. updateWindowStatus();
  256. }
  257. void VEditArea::updateWindowStatus()
  258. {
  259. if (curWindowIndex == -1) {
  260. Q_ASSERT(splitter->count() == 0);
  261. emit tabStatusUpdated(VEditTabInfo());
  262. emit outlineChanged(VTableOfContent());
  263. emit currentHeaderChanged(VHeaderPointer());
  264. return;
  265. }
  266. VEditWindow *win = getWindow(curWindowIndex);
  267. win->updateTabStatus();
  268. }
  269. bool VEditArea::closeFile(const VFile *p_file, bool p_forced)
  270. {
  271. if (!p_file) {
  272. return true;
  273. }
  274. bool ret = false;
  275. int i = 0;
  276. while (i < splitter->count()) {
  277. VEditWindow *win = getWindow(i);
  278. int nrWin = splitter->count();
  279. ret = ret || win->closeFile(p_file, p_forced);
  280. if (nrWin == splitter->count()) {
  281. ++i;
  282. }
  283. }
  284. updateWindowStatus();
  285. return ret;
  286. }
  287. bool VEditArea::closeFile(const VDirectory *p_dir, bool p_forced)
  288. {
  289. if (!p_dir) {
  290. return true;
  291. }
  292. int i = 0;
  293. while (i < splitter->count()) {
  294. VEditWindow *win = getWindow(i);
  295. if (!p_forced) {
  296. setCurrentWindow(i, false);
  297. }
  298. int nrWin = splitter->count();
  299. if (!win->closeFile(p_dir, p_forced)) {
  300. return false;
  301. }
  302. // win may be removed after closeFile()
  303. if (nrWin == splitter->count()) {
  304. ++i;
  305. }
  306. }
  307. updateWindowStatus();
  308. return true;
  309. }
  310. bool VEditArea::closeFile(const VNotebook *p_notebook, bool p_forced)
  311. {
  312. if (!p_notebook) {
  313. return true;
  314. }
  315. int i = 0;
  316. while (i < splitter->count()) {
  317. VEditWindow *win = getWindow(i);
  318. if (!p_forced) {
  319. setCurrentWindow(i, false);
  320. }
  321. int nrWin = splitter->count();
  322. if (!win->closeFile(p_notebook, p_forced)) {
  323. return false;
  324. }
  325. // win may be removed after closeFile()
  326. if (nrWin == splitter->count()) {
  327. ++i;
  328. }
  329. }
  330. updateWindowStatus();
  331. return true;
  332. }
  333. bool VEditArea::closeAllFiles(bool p_forced)
  334. {
  335. int i = 0;
  336. while (i < splitter->count()) {
  337. VEditWindow *win = getWindow(i);
  338. if (!p_forced) {
  339. setCurrentWindow(i, false);
  340. }
  341. int nrWin = splitter->count();
  342. if (!win->closeAllFiles(p_forced)) {
  343. return false;
  344. }
  345. if (nrWin == splitter->count()) {
  346. ++i;
  347. }
  348. }
  349. updateWindowStatus();
  350. return true;
  351. }
  352. void VEditArea::editFile()
  353. {
  354. VEditWindow *win = getWindow(curWindowIndex);
  355. win->editFile();
  356. }
  357. void VEditArea::saveFile()
  358. {
  359. VEditWindow *win = getWindow(curWindowIndex);
  360. win->saveFile();
  361. }
  362. void VEditArea::readFile(bool p_discard)
  363. {
  364. VEditWindow *win = getWindow(curWindowIndex);
  365. win->readFile(p_discard);
  366. }
  367. void VEditArea::saveAndReadFile()
  368. {
  369. VEditWindow *win = getWindow(curWindowIndex);
  370. win->saveAndReadFile();
  371. }
  372. void VEditArea::splitWindow(VEditWindow *p_window, bool p_right)
  373. {
  374. if (!p_window) {
  375. return;
  376. }
  377. int idx = splitter->indexOf(p_window);
  378. Q_ASSERT(idx > -1);
  379. if (p_right) {
  380. ++idx;
  381. } else {
  382. --idx;
  383. if (idx < 0) {
  384. idx = 0;
  385. }
  386. }
  387. insertSplitWindow(idx);
  388. setCurrentWindow(idx, true);
  389. }
  390. void VEditArea::splitCurrentWindow()
  391. {
  392. if (curWindowIndex > -1) {
  393. splitWindow(getWindow(curWindowIndex));
  394. }
  395. }
  396. void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
  397. {
  398. if (!curWindow || splitter->count() <= 1) {
  399. return;
  400. }
  401. int idx = splitter->indexOf(curWindow);
  402. // curWindow will be deleted later
  403. removeSplitWindow(curWindow);
  404. if (idx >= splitter->count()) {
  405. idx = splitter->count() - 1;
  406. }
  407. setCurrentWindow(idx, true);
  408. }
  409. void VEditArea::removeCurrentWindow()
  410. {
  411. if (curWindowIndex > -1) {
  412. handleRemoveSplitRequest(getWindow(curWindowIndex));
  413. }
  414. }
  415. void VEditArea::mousePressEvent(QMouseEvent *event)
  416. {
  417. QPoint pos = event->pos();
  418. int nrWin = splitter->count();
  419. for (int i = 0; i < nrWin; ++i) {
  420. VEditWindow *win = getWindow(i);
  421. if (win->geometry().contains(pos, true)) {
  422. setCurrentWindow(i, true);
  423. break;
  424. }
  425. }
  426. QWidget::mousePressEvent(event);
  427. }
  428. void VEditArea::handleWindowFocused()
  429. {
  430. QObject *winObject = sender();
  431. int nrWin = splitter->count();
  432. for (int i = 0; i < nrWin; ++i) {
  433. if (splitter->widget(i) == winObject) {
  434. setCurrentWindow(i, false);
  435. break;
  436. }
  437. }
  438. }
  439. void VEditArea::handleWindowOutlineChanged(const VTableOfContent &p_outline)
  440. {
  441. QObject *winObject = sender();
  442. if (splitter->widget(curWindowIndex) == winObject) {
  443. emit outlineChanged(p_outline);
  444. }
  445. }
  446. void VEditArea::handleWindowCurrentHeaderChanged(const VHeaderPointer &p_header)
  447. {
  448. QObject *winObject = sender();
  449. if (splitter->widget(curWindowIndex) == winObject) {
  450. emit currentHeaderChanged(p_header);
  451. }
  452. }
  453. void VEditArea::scrollToHeader(const VHeaderPointer &p_header)
  454. {
  455. VEditWindow *win = getCurrentWindow();
  456. if (win) {
  457. win->scrollToHeader(p_header);
  458. }
  459. }
  460. bool VEditArea::isFileOpened(const VFile *p_file)
  461. {
  462. return !findTabsByFile(p_file).isEmpty();
  463. }
  464. void VEditArea::handleFileUpdated(const VFile *p_file, UpdateAction p_act)
  465. {
  466. int nrWin = splitter->count();
  467. for (int i = 0; i < nrWin; ++i) {
  468. getWindow(i)->updateFileInfo(p_file, p_act);
  469. }
  470. }
  471. void VEditArea::handleDirectoryUpdated(const VDirectory *p_dir, UpdateAction p_act)
  472. {
  473. int nrWin = splitter->count();
  474. for (int i = 0; i < nrWin; ++i) {
  475. getWindow(i)->updateDirectoryInfo(p_dir, p_act);
  476. }
  477. }
  478. void VEditArea::handleNotebookUpdated(const VNotebook *p_notebook)
  479. {
  480. int nrWin = splitter->count();
  481. for (int i = 0; i < nrWin; ++i) {
  482. getWindow(i)->updateNotebookInfo(p_notebook);
  483. }
  484. }
  485. VEditTab *VEditArea::getCurrentTab() const
  486. {
  487. if (curWindowIndex == -1) {
  488. return NULL;
  489. }
  490. VEditWindow *win = getWindow(curWindowIndex);
  491. return win->getCurrentTab();
  492. }
  493. VEditTab *VEditArea::getTab(int p_winIdx, int p_tabIdx) const
  494. {
  495. VEditWindow *win = getWindow(p_winIdx);
  496. if (!win) {
  497. return NULL;
  498. }
  499. return win->getTab(p_tabIdx);
  500. }
  501. VEditTab *VEditArea::getTab(const VFile *p_file) const
  502. {
  503. int nrWin = splitter->count();
  504. for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
  505. VEditWindow *win = getWindow(winIdx);
  506. int tabIdx = win->findTabByFile(p_file);
  507. if (tabIdx != -1) {
  508. return win->getTab(tabIdx);
  509. }
  510. }
  511. return NULL;
  512. }
  513. QVector<VEditTabInfo> VEditArea::getAllTabsInfo() const
  514. {
  515. QVector<VEditTabInfo> tabs;
  516. int nrWin = splitter->count();
  517. for (int i = 0; i < nrWin; ++i) {
  518. tabs.append(getWindow(i)->getAllTabsInfo());
  519. }
  520. return tabs;
  521. }
  522. int VEditArea::windowIndex(const VEditWindow *p_window) const
  523. {
  524. int nrWin = splitter->count();
  525. for (int i = 0; i < nrWin; ++i) {
  526. if (p_window == getWindow(i)) {
  527. return i;
  528. }
  529. }
  530. return -1;
  531. }
  532. void VEditArea::moveTab(QWidget *p_widget, int p_fromIdx, int p_toIdx)
  533. {
  534. int nrWin = splitter->count();
  535. if (!p_widget || p_fromIdx < 0 || p_fromIdx >= nrWin
  536. || p_toIdx < 0 || p_toIdx >= nrWin) {
  537. return;
  538. }
  539. qDebug() << "move widget" << p_widget << "from" << p_fromIdx << "to" << p_toIdx;
  540. if (!getWindow(p_toIdx)->addEditTab(p_widget)) {
  541. delete p_widget;
  542. }
  543. }
  544. // Only propogate the search in the IncrementalSearch case.
  545. void VEditArea::handleFindTextChanged(const QString &p_text, uint p_options)
  546. {
  547. VEditTab *tab = getCurrentTab();
  548. if (tab) {
  549. if (p_options & FindOption::IncrementalSearch) {
  550. tab->findText(p_text, p_options, true);
  551. }
  552. }
  553. }
  554. void VEditArea::handleFindOptionChanged(uint p_options)
  555. {
  556. qDebug() << "find option changed" << p_options;
  557. g_config->setFindCaseSensitive(p_options & FindOption::CaseSensitive);
  558. g_config->setFindWholeWordOnly(p_options & FindOption::WholeWordOnly);
  559. g_config->setFindRegularExpression(p_options & FindOption::RegularExpression);
  560. g_config->setFindIncrementalSearch(p_options & FindOption::IncrementalSearch);
  561. }
  562. void VEditArea::handleFindNext(const QString &p_text, uint p_options,
  563. bool p_forward)
  564. {
  565. qDebug() << "find next" << p_text << p_options << p_forward;
  566. VEditTab *tab = getCurrentTab();
  567. if (tab) {
  568. tab->findText(p_text, p_options, false, p_forward);
  569. }
  570. }
  571. void VEditArea::handleReplace(const QString &p_text, uint p_options,
  572. const QString &p_replaceText, bool p_findNext)
  573. {
  574. qDebug() << "replace" << p_text << p_options << "with" << p_replaceText
  575. << p_findNext;
  576. VEditTab *tab = getCurrentTab();
  577. if (tab) {
  578. tab->replaceText(p_text, p_options, p_replaceText, p_findNext);
  579. }
  580. }
  581. void VEditArea::handleReplaceAll(const QString &p_text, uint p_options,
  582. const QString &p_replaceText)
  583. {
  584. qDebug() << "replace all" << p_text << p_options << "with" << p_replaceText;
  585. VEditTab *tab = getCurrentTab();
  586. if (tab) {
  587. tab->replaceTextAll(p_text, p_options, p_replaceText);
  588. }
  589. }
  590. // Let VEditArea get focus after VFindReplaceDialog is closed.
  591. void VEditArea::handleFindDialogClosed()
  592. {
  593. if (curWindowIndex == -1) {
  594. setFocus();
  595. } else {
  596. getWindow(curWindowIndex)->focusWindow();
  597. }
  598. // Clear all the search highlight.
  599. int nrWin = splitter->count();
  600. for (int i = 0; i < nrWin; ++i) {
  601. getWindow(i)->clearSearchedWordHighlight();
  602. }
  603. }
  604. QString VEditArea::getSelectedText()
  605. {
  606. VEditTab *tab = getCurrentTab();
  607. if (tab) {
  608. return tab->getSelectedText();
  609. } else {
  610. return QString();
  611. }
  612. }
  613. int VEditArea::focusNextWindow(int p_biaIdx)
  614. {
  615. if (p_biaIdx == 0) {
  616. return curWindowIndex;
  617. }
  618. int newIdx = curWindowIndex + p_biaIdx;
  619. if (newIdx < 0) {
  620. newIdx = 0;
  621. } else if (newIdx >= splitter->count()) {
  622. newIdx = splitter->count() - 1;
  623. }
  624. if (newIdx >= 0 && newIdx < splitter->count()) {
  625. setCurrentWindow(newIdx, true);
  626. } else {
  627. newIdx = -1;
  628. }
  629. return newIdx;
  630. }
  631. void VEditArea::moveCurrentTabOneSplit(bool p_right)
  632. {
  633. getWindow(curWindowIndex)->moveCurrentTabOneSplit(p_right);
  634. }
  635. VEditWindow *VEditArea::getCurrentWindow() const
  636. {
  637. if (curWindowIndex < 0) {
  638. return NULL;
  639. }
  640. return getWindow(curWindowIndex);
  641. }
  642. void VEditArea::registerNavigation(QChar p_majorKey)
  643. {
  644. m_majorKey = p_majorKey;
  645. V_ASSERT(m_keyMap.empty());
  646. V_ASSERT(m_naviLabels.empty());
  647. }
  648. void VEditArea::showNavigation()
  649. {
  650. // Clean up.
  651. m_keyMap.clear();
  652. for (auto label : m_naviLabels) {
  653. delete label;
  654. }
  655. m_naviLabels.clear();
  656. if (!isVisible()) {
  657. return;
  658. }
  659. // Generate labels for VEDitTab.
  660. int charIdx = 0;
  661. for (int i = 0; charIdx < 26 && i < splitter->count(); ++i) {
  662. VEditWindow *win = getWindow(i);
  663. QVector<TabNavigationInfo> tabInfos = win->getTabsNavigationInfo();
  664. for (int j = 0; charIdx < 26 && j < tabInfos.size(); ++j, ++charIdx) {
  665. QChar key('a' + charIdx);
  666. m_keyMap[key] = tabInfos[j].m_tab;
  667. QString str = QString(m_majorKey) + key;
  668. QLabel *label = new QLabel(str, win);
  669. label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
  670. label->show();
  671. label->move(tabInfos[j].m_topLeft);
  672. m_naviLabels.append(label);
  673. }
  674. }
  675. }
  676. void VEditArea::hideNavigation()
  677. {
  678. m_keyMap.clear();
  679. for (auto label : m_naviLabels) {
  680. delete label;
  681. }
  682. m_naviLabels.clear();
  683. }
  684. bool VEditArea::handleKeyNavigation(int p_key, bool &p_succeed)
  685. {
  686. static bool secondKey = false;
  687. bool ret = false;
  688. p_succeed = false;
  689. QChar keyChar = VUtils::keyToChar(p_key);
  690. if (secondKey && !keyChar.isNull()) {
  691. secondKey = false;
  692. p_succeed = true;
  693. auto it = m_keyMap.find(keyChar);
  694. if (it != m_keyMap.end()) {
  695. ret = true;
  696. static_cast<VEditTab *>(it.value())->focusTab();
  697. }
  698. } else if (keyChar == m_majorKey) {
  699. // Major key pressed.
  700. // Need second key if m_keyMap is not empty.
  701. if (m_keyMap.isEmpty()) {
  702. p_succeed = true;
  703. } else {
  704. secondKey = true;
  705. }
  706. ret = true;
  707. }
  708. return ret;
  709. }
  710. int VEditArea::openFiles(const QVector<VFileSessionInfo> &p_files, bool p_oneByOne)
  711. {
  712. VFile *curFile = NULL;
  713. int nrOpened = 0;
  714. for (auto const & info : p_files) {
  715. QString filePath = VUtils::validFilePathToOpen(info.m_file);
  716. if (filePath.isEmpty()) {
  717. continue;
  718. }
  719. VFile *file = g_vnote->getFile(filePath);
  720. if (!file) {
  721. continue;
  722. }
  723. VEditTab *tab = openFile(file, info.m_mode, true);
  724. ++nrOpened;
  725. if (info.m_active) {
  726. curFile = file;
  727. }
  728. VEditTabInfo tabInfo;
  729. tabInfo.m_editTab = tab;
  730. info.toEditTabInfo(&tabInfo);
  731. tab->tryRestoreFromTabInfo(tabInfo);
  732. if (p_oneByOne) {
  733. QCoreApplication::sendPostedEvents();
  734. }
  735. }
  736. if (curFile) {
  737. openFile(curFile, OpenFileMode::Read, false);
  738. }
  739. return nrOpened;
  740. }
  741. void VEditArea::registerCaptainTargets()
  742. {
  743. using namespace std::placeholders;
  744. VCaptain *captain = g_mainWin->getCaptain();
  745. captain->registerCaptainTarget(tr("ActivateTab1"),
  746. g_config->getCaptainShortcutKeySequence("ActivateTab1"),
  747. this,
  748. std::bind(activateTabByCaptain, _1, _2, 1));
  749. captain->registerCaptainTarget(tr("ActivateTab2"),
  750. g_config->getCaptainShortcutKeySequence("ActivateTab2"),
  751. this,
  752. std::bind(activateTabByCaptain, _1, _2, 2));
  753. captain->registerCaptainTarget(tr("ActivateTab3"),
  754. g_config->getCaptainShortcutKeySequence("ActivateTab3"),
  755. this,
  756. std::bind(activateTabByCaptain, _1, _2, 3));
  757. captain->registerCaptainTarget(tr("ActivateTab4"),
  758. g_config->getCaptainShortcutKeySequence("ActivateTab4"),
  759. this,
  760. std::bind(activateTabByCaptain, _1, _2, 4));
  761. captain->registerCaptainTarget(tr("ActivateTab5"),
  762. g_config->getCaptainShortcutKeySequence("ActivateTab5"),
  763. this,
  764. std::bind(activateTabByCaptain, _1, _2, 5));
  765. captain->registerCaptainTarget(tr("ActivateTab6"),
  766. g_config->getCaptainShortcutKeySequence("ActivateTab6"),
  767. this,
  768. std::bind(activateTabByCaptain, _1, _2, 6));
  769. captain->registerCaptainTarget(tr("ActivateTab7"),
  770. g_config->getCaptainShortcutKeySequence("ActivateTab7"),
  771. this,
  772. std::bind(activateTabByCaptain, _1, _2, 7));
  773. captain->registerCaptainTarget(tr("ActivateTab8"),
  774. g_config->getCaptainShortcutKeySequence("ActivateTab8"),
  775. this,
  776. std::bind(activateTabByCaptain, _1, _2, 8));
  777. captain->registerCaptainTarget(tr("ActivateTab9"),
  778. g_config->getCaptainShortcutKeySequence("ActivateTab9"),
  779. this,
  780. std::bind(activateTabByCaptain, _1, _2, 9));
  781. captain->registerCaptainTarget(tr("AlternateTab"),
  782. g_config->getCaptainShortcutKeySequence("AlternateTab"),
  783. this,
  784. alternateTabByCaptain);
  785. captain->registerCaptainTarget(tr("OpenedFileList"),
  786. g_config->getCaptainShortcutKeySequence("OpenedFileList"),
  787. this,
  788. showOpenedFileListByCaptain);
  789. captain->registerCaptainTarget(tr("ActivateSplitLeft"),
  790. g_config->getCaptainShortcutKeySequence("ActivateSplitLeft"),
  791. this,
  792. activateSplitLeftByCaptain);
  793. captain->registerCaptainTarget(tr("ActivateSplitRight"),
  794. g_config->getCaptainShortcutKeySequence("ActivateSplitRight"),
  795. this,
  796. activateSplitRightByCaptain);
  797. captain->registerCaptainTarget(tr("MoveTabSplitLeft"),
  798. g_config->getCaptainShortcutKeySequence("MoveTabSplitLeft"),
  799. this,
  800. moveTabSplitLeftByCaptain);
  801. captain->registerCaptainTarget(tr("MoveTabSplitRight"),
  802. g_config->getCaptainShortcutKeySequence("MoveTabSplitRight"),
  803. this,
  804. moveTabSplitRightByCaptain);
  805. captain->registerCaptainTarget(tr("ActivateNextTab"),
  806. g_config->getCaptainShortcutKeySequence("ActivateNextTab"),
  807. this,
  808. activateNextTabByCaptain);
  809. captain->registerCaptainTarget(tr("ActivatePreviousTab"),
  810. g_config->getCaptainShortcutKeySequence("ActivatePreviousTab"),
  811. this,
  812. activatePreviousTabByCaptain);
  813. captain->registerCaptainTarget(tr("VerticalSplit"),
  814. g_config->getCaptainShortcutKeySequence("VerticalSplit"),
  815. this,
  816. verticalSplitByCaptain);
  817. captain->registerCaptainTarget(tr("RemoveSplit"),
  818. g_config->getCaptainShortcutKeySequence("RemoveSplit"),
  819. this,
  820. removeSplitByCaptain);
  821. captain->registerCaptainTarget(tr("MagicWord"),
  822. g_config->getCaptainShortcutKeySequence("MagicWord"),
  823. this,
  824. evaluateMagicWordsByCaptain);
  825. captain->registerCaptainTarget(tr("ApplySnippet"),
  826. g_config->getCaptainShortcutKeySequence("ApplySnippet"),
  827. this,
  828. applySnippetByCaptain);
  829. captain->registerCaptainTarget(tr("LivePreview"),
  830. g_config->getCaptainShortcutKeySequence("LivePreview"),
  831. this,
  832. toggleLivePreviewByCaptain);
  833. }
  834. bool VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
  835. {
  836. Q_UNUSED(p_data);
  837. VEditArea *obj = static_cast<VEditArea *>(p_target);
  838. VEditWindow *win = obj->getCurrentWindow();
  839. if (win) {
  840. if (win->activateTab(p_idx)) {
  841. return false;
  842. }
  843. }
  844. return true;
  845. }
  846. bool VEditArea::alternateTabByCaptain(void *p_target, void *p_data)
  847. {
  848. Q_UNUSED(p_data);
  849. VEditArea *obj = static_cast<VEditArea *>(p_target);
  850. VEditWindow *win = obj->getCurrentWindow();
  851. if (win) {
  852. if (win->alternateTab()) {
  853. return false;
  854. }
  855. }
  856. return true;
  857. }
  858. bool VEditArea::showOpenedFileListByCaptain(void *p_target, void *p_data)
  859. {
  860. Q_UNUSED(p_data);
  861. VEditArea *obj = static_cast<VEditArea *>(p_target);
  862. VEditWindow *win = obj->getCurrentWindow();
  863. if (win) {
  864. if (win->showOpenedFileList()) {
  865. return false;
  866. }
  867. }
  868. return true;
  869. }
  870. bool VEditArea::activateSplitLeftByCaptain(void *p_target, void *p_data)
  871. {
  872. Q_UNUSED(p_data);
  873. VEditArea *obj = static_cast<VEditArea *>(p_target);
  874. if (obj->focusNextWindow(-1) > -1) {
  875. return false;
  876. }
  877. return true;
  878. }
  879. bool VEditArea::activateSplitRightByCaptain(void *p_target, void *p_data)
  880. {
  881. Q_UNUSED(p_data);
  882. VEditArea *obj = static_cast<VEditArea *>(p_target);
  883. if (obj->focusNextWindow(1) > -1) {
  884. return false;
  885. }
  886. return true;
  887. }
  888. bool VEditArea::moveTabSplitLeftByCaptain(void *p_target, void *p_data)
  889. {
  890. Q_UNUSED(p_data);
  891. VEditArea *obj = static_cast<VEditArea *>(p_target);
  892. obj->moveCurrentTabOneSplit(false);
  893. return true;
  894. }
  895. bool VEditArea::moveTabSplitRightByCaptain(void *p_target, void *p_data)
  896. {
  897. Q_UNUSED(p_data);
  898. VEditArea *obj = static_cast<VEditArea *>(p_target);
  899. obj->moveCurrentTabOneSplit(true);
  900. return true;
  901. }
  902. bool VEditArea::activateNextTabByCaptain(void *p_target, void *p_data)
  903. {
  904. Q_UNUSED(p_data);
  905. VEditArea *obj = static_cast<VEditArea *>(p_target);
  906. VEditWindow *win = obj->getCurrentWindow();
  907. if (win) {
  908. win->focusNextTab(true);
  909. return false;
  910. }
  911. return true;
  912. }
  913. bool VEditArea::activatePreviousTabByCaptain(void *p_target, void *p_data)
  914. {
  915. Q_UNUSED(p_data);
  916. VEditArea *obj = static_cast<VEditArea *>(p_target);
  917. VEditWindow *win = obj->getCurrentWindow();
  918. if (win) {
  919. win->focusNextTab(false);
  920. return false;
  921. }
  922. return true;
  923. }
  924. bool VEditArea::verticalSplitByCaptain(void *p_target, void *p_data)
  925. {
  926. Q_UNUSED(p_data);
  927. VEditArea *obj = static_cast<VEditArea *>(p_target);
  928. obj->splitCurrentWindow();
  929. return false;
  930. }
  931. bool VEditArea::removeSplitByCaptain(void *p_target, void *p_data)
  932. {
  933. Q_UNUSED(p_data);
  934. VEditArea *obj = static_cast<VEditArea *>(p_target);
  935. obj->removeCurrentWindow();
  936. QWidget *nextFocus = obj->getCurrentTab();
  937. if (nextFocus) {
  938. nextFocus->setFocus();
  939. } else {
  940. g_mainWin->getFileList()->setFocus();
  941. }
  942. return false;
  943. }
  944. bool VEditArea::evaluateMagicWordsByCaptain(void *p_target, void *p_data)
  945. {
  946. VEditArea *obj = static_cast<VEditArea *>(p_target);
  947. CaptainData *data = static_cast<CaptainData *>(p_data);
  948. VEditTab *tab = obj->getCurrentTab();
  949. if (tab
  950. && (data->m_focusWidgetBeforeCaptain == tab
  951. || tab->isAncestorOf(data->m_focusWidgetBeforeCaptain))) {
  952. tab->evaluateMagicWords();
  953. }
  954. return true;
  955. }
  956. bool VEditArea::applySnippetByCaptain(void *p_target, void *p_data)
  957. {
  958. VEditArea *obj = static_cast<VEditArea *>(p_target);
  959. CaptainData *data = static_cast<CaptainData *>(p_data);
  960. VEditTab *tab = obj->getCurrentTab();
  961. if (tab
  962. && (data->m_focusWidgetBeforeCaptain == tab
  963. || tab->isAncestorOf(data->m_focusWidgetBeforeCaptain))) {
  964. tab->applySnippet();
  965. }
  966. return true;
  967. }
  968. bool VEditArea::toggleLivePreviewByCaptain(void *p_target, void *p_data)
  969. {
  970. Q_UNUSED(p_data);
  971. VEditArea *obj = static_cast<VEditArea *>(p_target);
  972. VEditTab *tab = obj->getCurrentTab();
  973. if (tab) {
  974. tab->toggleLivePreview();
  975. }
  976. return true;
  977. }
  978. void VEditArea::recordClosedFile(const VFileSessionInfo &p_file)
  979. {
  980. for (auto it = m_lastClosedFiles.begin(); it != m_lastClosedFiles.end(); ++it) {
  981. if (it->m_file == p_file.m_file) {
  982. // Remove it.
  983. m_lastClosedFiles.erase(it);
  984. break;
  985. }
  986. }
  987. m_lastClosedFiles.push(p_file);
  988. emit fileClosed(p_file.m_file);
  989. }
  990. void VEditArea::handleFileTimerTimeout()
  991. {
  992. int nrWin = splitter->count();
  993. for (int i = 0; i < nrWin; ++i) {
  994. // Check whether opened files have been changed outside.
  995. VEditWindow *win = getWindow(i);
  996. win->checkFileChangeOutside();
  997. if (m_autoSave) {
  998. win->saveAll();
  999. }
  1000. }
  1001. }
  1002. QRect VEditArea::editAreaRect() const
  1003. {
  1004. QRect rt = rect();
  1005. int nrWin = splitter->count();
  1006. if (nrWin > 0) {
  1007. rt.setTopLeft(QPoint(0, getWindow(0)->tabBarHeight()));
  1008. }
  1009. return rt;
  1010. }