veditarea.cpp 37 KB

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