veditarea.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. #include <QtWidgets>
  2. #include "veditarea.h"
  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. extern VConfigManager *g_config;
  11. extern VNote *g_vnote;
  12. VEditArea::VEditArea(VNote *vnote, QWidget *parent)
  13. : QWidget(parent), VNavigationMode(),
  14. vnote(vnote), curWindowIndex(-1)
  15. {
  16. setupUI();
  17. insertSplitWindow(0);
  18. setCurrentWindow(0, false);
  19. }
  20. void VEditArea::setupUI()
  21. {
  22. splitter = new QSplitter(this);
  23. m_findReplace = new VFindReplaceDialog(this);
  24. m_findReplace->setOption(FindOption::CaseSensitive,
  25. g_config->getFindCaseSensitive());
  26. m_findReplace->setOption(FindOption::WholeWordOnly,
  27. g_config->getFindWholeWordOnly());
  28. m_findReplace->setOption(FindOption::RegularExpression,
  29. g_config->getFindRegularExpression());
  30. m_findReplace->setOption(FindOption::IncrementalSearch,
  31. g_config->getFindIncrementalSearch());
  32. QVBoxLayout *mainLayout = new QVBoxLayout();
  33. mainLayout->addWidget(splitter);
  34. mainLayout->addWidget(m_findReplace);
  35. mainLayout->setContentsMargins(0, 0, 0, 0);
  36. mainLayout->setSpacing(0);
  37. mainLayout->setStretch(0, 1);
  38. mainLayout->setStretch(1, 0);
  39. setLayout(mainLayout);
  40. connect(m_findReplace, &VFindReplaceDialog::findTextChanged,
  41. this, &VEditArea::handleFindTextChanged);
  42. connect(m_findReplace, &VFindReplaceDialog::findOptionChanged,
  43. this, &VEditArea::handleFindOptionChanged);
  44. connect(m_findReplace, SIGNAL(findNext(const QString &, uint, bool)),
  45. this, SLOT(handleFindNext(const QString &, uint, bool)));
  46. connect(m_findReplace,
  47. SIGNAL(replace(const QString &, uint, const QString &, bool)),
  48. this,
  49. SLOT(handleReplace(const QString &, uint, const QString &, bool)));
  50. connect(m_findReplace,
  51. SIGNAL(replaceAll(const QString &, uint, const QString &)),
  52. this,
  53. SLOT(handleReplaceAll(const QString &, uint, const QString &)));
  54. connect(m_findReplace, &VFindReplaceDialog::dialogClosed,
  55. this, &VEditArea::handleFindDialogClosed);
  56. m_findReplace->hide();
  57. }
  58. void VEditArea::insertSplitWindow(int idx)
  59. {
  60. VEditWindow *win = new VEditWindow(vnote, this);
  61. splitter->insertWidget(idx, win);
  62. connect(win, &VEditWindow::tabStatusUpdated,
  63. this, &VEditArea::handleWindowTabStatusUpdated);
  64. connect(win, &VEditWindow::requestSplitWindow,
  65. this, &VEditArea::splitWindow);
  66. connect(win, &VEditWindow::requestRemoveSplit,
  67. this, &VEditArea::handleRemoveSplitRequest);
  68. connect(win, &VEditWindow::getFocused,
  69. this, &VEditArea::handleWindowFocused);
  70. connect(win, &VEditWindow::outlineChanged,
  71. this, &VEditArea::handleOutlineChanged);
  72. connect(win, &VEditWindow::curHeaderChanged,
  73. this, &VEditArea::handleCurHeaderChanged);
  74. connect(win, &VEditWindow::statusMessage,
  75. this, &VEditArea::handleWindowStatusMessage);
  76. connect(win, &VEditWindow::vimStatusUpdated,
  77. this, &VEditArea::handleWindowVimStatusUpdated);
  78. }
  79. void VEditArea::handleWindowTabStatusUpdated(const VEditTabInfo &p_info)
  80. {
  81. if (splitter->widget(curWindowIndex) == sender()) {
  82. emit tabStatusUpdated(p_info);
  83. }
  84. }
  85. void VEditArea::handleWindowStatusMessage(const QString &p_msg)
  86. {
  87. if (splitter->widget(curWindowIndex) == sender()) {
  88. emit statusMessage(p_msg);
  89. }
  90. }
  91. void VEditArea::handleWindowVimStatusUpdated(const VVim *p_vim)
  92. {
  93. if (splitter->widget(curWindowIndex) == sender()) {
  94. emit vimStatusUpdated(p_vim);
  95. }
  96. }
  97. void VEditArea::removeSplitWindow(VEditWindow *win)
  98. {
  99. if (!win) {
  100. return;
  101. }
  102. win->hide();
  103. win->setParent(this);
  104. disconnect(win, 0, this, 0);
  105. // Should be deleted later
  106. win->deleteLater();
  107. }
  108. // A given file can be opened in multiple split windows. A given file could be
  109. // opened at most in one tab inside a window.
  110. void VEditArea::openFile(VFile *p_file, OpenFileMode p_mode)
  111. {
  112. if (!p_file) {
  113. return;
  114. }
  115. // If it is DocType::Unknown, open it using system default method.
  116. if (p_file->getDocType() == DocType::Unknown) {
  117. QUrl url = QUrl::fromLocalFile(p_file->fetchPath());
  118. QDesktopServices::openUrl(url);
  119. return;
  120. }
  121. // Find if it has been opened already
  122. int winIdx, tabIdx;
  123. bool existFile = false;
  124. bool setFocus = false;
  125. auto tabs = findTabsByFile(p_file);
  126. if (!tabs.empty()) {
  127. // Current window first
  128. winIdx = tabs[0].first;
  129. tabIdx = tabs[0].second;
  130. for (int i = 0; i < tabs.size(); ++i) {
  131. if (tabs[i].first == curWindowIndex) {
  132. winIdx = tabs[i].first;
  133. tabIdx = tabs[i].second;
  134. break;
  135. }
  136. }
  137. setFocus = true;
  138. existFile = true;
  139. goto out;
  140. }
  141. // Open it in current window
  142. if (curWindowIndex == -1) {
  143. // insert a new split
  144. insertSplitWindow(0);
  145. curWindowIndex = 0;
  146. }
  147. winIdx = curWindowIndex;
  148. tabIdx = openFileInWindow(winIdx, p_file, p_mode);
  149. out:
  150. setCurrentTab(winIdx, tabIdx, setFocus);
  151. if (existFile) {
  152. if (p_mode == OpenFileMode::Read) {
  153. readFile();
  154. } else if (p_mode == OpenFileMode::Edit) {
  155. editFile();
  156. }
  157. }
  158. }
  159. QVector<QPair<int, int> > VEditArea::findTabsByFile(const VFile *p_file)
  160. {
  161. QVector<QPair<int, int> > tabs;
  162. int nrWin = splitter->count();
  163. for (int winIdx = 0; winIdx < nrWin; ++winIdx) {
  164. VEditWindow *win = getWindow(winIdx);
  165. int tabIdx = win->findTabByFile(p_file);
  166. if (tabIdx != -1) {
  167. QPair<int, int> match;
  168. match.first = winIdx;
  169. match.second = tabIdx;
  170. tabs.append(match);
  171. }
  172. }
  173. return tabs;
  174. }
  175. int VEditArea::openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode)
  176. {
  177. Q_ASSERT(windowIndex < splitter->count());
  178. VEditWindow *win = getWindow(windowIndex);
  179. return win->openFile(p_file, p_mode);
  180. }
  181. void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus)
  182. {
  183. VEditWindow *win = getWindow(windowIndex);
  184. win->setCurrentIndex(tabIndex);
  185. setCurrentWindow(windowIndex, setFocus);
  186. }
  187. void VEditArea::setCurrentWindow(int windowIndex, bool setFocus)
  188. {
  189. int nrWin = splitter->count();
  190. curWindowIndex = windowIndex;
  191. for (int i = 0; i < nrWin; ++i) {
  192. getWindow(i)->setCurrentWindow(false);
  193. }
  194. if (curWindowIndex > -1) {
  195. getWindow(curWindowIndex)->setCurrentWindow(true);
  196. if (setFocus) {
  197. getWindow(curWindowIndex)->focusWindow();
  198. }
  199. }
  200. // Update status
  201. updateWindowStatus();
  202. }
  203. void VEditArea::updateWindowStatus()
  204. {
  205. if (curWindowIndex == -1) {
  206. Q_ASSERT(splitter->count() == 0);
  207. emit tabStatusUpdated(VEditTabInfo());
  208. emit outlineChanged(VToc());
  209. emit curHeaderChanged(VAnchor());
  210. return;
  211. }
  212. VEditWindow *win = getWindow(curWindowIndex);
  213. win->updateTabStatus();
  214. win->requestUpdateOutline();
  215. win->requestUpdateCurHeader();
  216. }
  217. bool VEditArea::closeFile(const VFile *p_file, bool p_forced)
  218. {
  219. if (!p_file) {
  220. return true;
  221. }
  222. bool ret = false;
  223. int i = 0;
  224. while (i < splitter->count()) {
  225. VEditWindow *win = getWindow(i);
  226. int nrWin = splitter->count();
  227. ret = ret || win->closeFile(p_file, p_forced);
  228. if (nrWin == splitter->count()) {
  229. ++i;
  230. }
  231. }
  232. updateWindowStatus();
  233. return ret;
  234. }
  235. bool VEditArea::closeFile(const VDirectory *p_dir, bool p_forced)
  236. {
  237. if (!p_dir) {
  238. return true;
  239. }
  240. int i = 0;
  241. while (i < splitter->count()) {
  242. VEditWindow *win = getWindow(i);
  243. if (!p_forced) {
  244. setCurrentWindow(i, false);
  245. }
  246. int nrWin = splitter->count();
  247. if (!win->closeFile(p_dir, p_forced)) {
  248. return false;
  249. }
  250. // win may be removed after closeFile()
  251. if (nrWin == splitter->count()) {
  252. ++i;
  253. }
  254. }
  255. updateWindowStatus();
  256. return true;
  257. }
  258. bool VEditArea::closeFile(const VNotebook *p_notebook, bool p_forced)
  259. {
  260. if (!p_notebook) {
  261. return true;
  262. }
  263. int i = 0;
  264. while (i < splitter->count()) {
  265. VEditWindow *win = getWindow(i);
  266. if (!p_forced) {
  267. setCurrentWindow(i, false);
  268. }
  269. int nrWin = splitter->count();
  270. if (!win->closeFile(p_notebook, p_forced)) {
  271. return false;
  272. }
  273. // win may be removed after closeFile()
  274. if (nrWin == splitter->count()) {
  275. ++i;
  276. }
  277. }
  278. updateWindowStatus();
  279. return true;
  280. }
  281. bool VEditArea::closeAllFiles(bool p_forced)
  282. {
  283. int i = 0;
  284. while (i < splitter->count()) {
  285. VEditWindow *win = getWindow(i);
  286. if (!p_forced) {
  287. setCurrentWindow(i, false);
  288. }
  289. int nrWin = splitter->count();
  290. if (!win->closeAllFiles(p_forced)) {
  291. return false;
  292. }
  293. if (nrWin == splitter->count()) {
  294. ++i;
  295. }
  296. }
  297. updateWindowStatus();
  298. return true;
  299. }
  300. void VEditArea::editFile()
  301. {
  302. VEditWindow *win = getWindow(curWindowIndex);
  303. win->editFile();
  304. }
  305. void VEditArea::saveFile()
  306. {
  307. VEditWindow *win = getWindow(curWindowIndex);
  308. win->saveFile();
  309. }
  310. void VEditArea::readFile()
  311. {
  312. VEditWindow *win = getWindow(curWindowIndex);
  313. win->readFile();
  314. }
  315. void VEditArea::saveAndReadFile()
  316. {
  317. VEditWindow *win = getWindow(curWindowIndex);
  318. win->saveAndReadFile();
  319. }
  320. void VEditArea::splitWindow(VEditWindow *p_window, bool p_right)
  321. {
  322. if (!p_window) {
  323. return;
  324. }
  325. int idx = splitter->indexOf(p_window);
  326. Q_ASSERT(idx > -1);
  327. if (p_right) {
  328. ++idx;
  329. } else {
  330. --idx;
  331. if (idx < 0) {
  332. idx = 0;
  333. }
  334. }
  335. insertSplitWindow(idx);
  336. setCurrentWindow(idx, true);
  337. }
  338. void VEditArea::splitCurrentWindow()
  339. {
  340. if (curWindowIndex > -1) {
  341. splitWindow(getWindow(curWindowIndex));
  342. }
  343. }
  344. void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow)
  345. {
  346. if (!curWindow || splitter->count() <= 1) {
  347. return;
  348. }
  349. int idx = splitter->indexOf(curWindow);
  350. // curWindow will be deleted later
  351. removeSplitWindow(curWindow);
  352. if (idx >= splitter->count()) {
  353. idx = splitter->count() - 1;
  354. }
  355. setCurrentWindow(idx, true);
  356. }
  357. void VEditArea::removeCurrentWindow()
  358. {
  359. if (curWindowIndex > -1) {
  360. handleRemoveSplitRequest(getWindow(curWindowIndex));
  361. }
  362. }
  363. void VEditArea::mousePressEvent(QMouseEvent *event)
  364. {
  365. QPoint pos = event->pos();
  366. int nrWin = splitter->count();
  367. for (int i = 0; i < nrWin; ++i) {
  368. VEditWindow *win = getWindow(i);
  369. if (win->geometry().contains(pos, true)) {
  370. setCurrentWindow(i, true);
  371. break;
  372. }
  373. }
  374. QWidget::mousePressEvent(event);
  375. }
  376. void VEditArea::handleWindowFocused()
  377. {
  378. QObject *winObject = sender();
  379. int nrWin = splitter->count();
  380. for (int i = 0; i < nrWin; ++i) {
  381. if (splitter->widget(i) == winObject) {
  382. setCurrentWindow(i, false);
  383. break;
  384. }
  385. }
  386. }
  387. void VEditArea::handleOutlineChanged(const VToc &toc)
  388. {
  389. QObject *winObject = sender();
  390. if (splitter->widget(curWindowIndex) == winObject) {
  391. emit outlineChanged(toc);
  392. }
  393. }
  394. void VEditArea::handleCurHeaderChanged(const VAnchor &anchor)
  395. {
  396. QObject *winObject = sender();
  397. if (splitter->widget(curWindowIndex) == winObject) {
  398. emit curHeaderChanged(anchor);
  399. }
  400. }
  401. void VEditArea::handleOutlineItemActivated(const VAnchor &anchor)
  402. {
  403. // Notice current window
  404. getWindow(curWindowIndex)->scrollCurTab(anchor);
  405. }
  406. bool VEditArea::isFileOpened(const VFile *p_file)
  407. {
  408. return !findTabsByFile(p_file).isEmpty();
  409. }
  410. void VEditArea::handleFileUpdated(const VFile *p_file)
  411. {
  412. int nrWin = splitter->count();
  413. for (int i = 0; i < nrWin; ++i) {
  414. getWindow(i)->updateFileInfo(p_file);
  415. }
  416. }
  417. void VEditArea::handleDirectoryUpdated(const VDirectory *p_dir)
  418. {
  419. int nrWin = splitter->count();
  420. for (int i = 0; i < nrWin; ++i) {
  421. getWindow(i)->updateDirectoryInfo(p_dir);
  422. }
  423. }
  424. void VEditArea::handleNotebookUpdated(const VNotebook *p_notebook)
  425. {
  426. int nrWin = splitter->count();
  427. for (int i = 0; i < nrWin; ++i) {
  428. getWindow(i)->updateNotebookInfo(p_notebook);
  429. }
  430. }
  431. VEditTab *VEditArea::currentEditTab()
  432. {
  433. if (curWindowIndex == -1) {
  434. return NULL;
  435. }
  436. VEditWindow *win = getWindow(curWindowIndex);
  437. return win->currentEditTab();
  438. }
  439. int VEditArea::windowIndex(const VEditWindow *p_window) const
  440. {
  441. int nrWin = splitter->count();
  442. for (int i = 0; i < nrWin; ++i) {
  443. if (p_window == getWindow(i)) {
  444. return i;
  445. }
  446. }
  447. return -1;
  448. }
  449. void VEditArea::moveTab(QWidget *p_widget, int p_fromIdx, int p_toIdx)
  450. {
  451. int nrWin = splitter->count();
  452. if (!p_widget || p_fromIdx < 0 || p_fromIdx >= nrWin
  453. || p_toIdx < 0 || p_toIdx >= nrWin) {
  454. return;
  455. }
  456. qDebug() << "move widget" << p_widget << "from" << p_fromIdx << "to" << p_toIdx;
  457. if (!getWindow(p_toIdx)->addEditTab(p_widget)) {
  458. delete p_widget;
  459. }
  460. }
  461. // Only propogate the search in the IncrementalSearch case.
  462. void VEditArea::handleFindTextChanged(const QString &p_text, uint p_options)
  463. {
  464. VEditTab *tab = currentEditTab();
  465. if (tab) {
  466. if (p_options & FindOption::IncrementalSearch) {
  467. tab->findText(p_text, p_options, true);
  468. }
  469. }
  470. }
  471. void VEditArea::handleFindOptionChanged(uint p_options)
  472. {
  473. qDebug() << "find option changed" << p_options;
  474. g_config->setFindCaseSensitive(p_options & FindOption::CaseSensitive);
  475. g_config->setFindWholeWordOnly(p_options & FindOption::WholeWordOnly);
  476. g_config->setFindRegularExpression(p_options & FindOption::RegularExpression);
  477. g_config->setFindIncrementalSearch(p_options & FindOption::IncrementalSearch);
  478. }
  479. void VEditArea::handleFindNext(const QString &p_text, uint p_options,
  480. bool p_forward)
  481. {
  482. qDebug() << "find next" << p_text << p_options << p_forward;
  483. VEditTab *tab = currentEditTab();
  484. if (tab) {
  485. tab->findText(p_text, p_options, false, p_forward);
  486. }
  487. }
  488. void VEditArea::handleReplace(const QString &p_text, uint p_options,
  489. const QString &p_replaceText, bool p_findNext)
  490. {
  491. qDebug() << "replace" << p_text << p_options << "with" << p_replaceText
  492. << p_findNext;
  493. VEditTab *tab = currentEditTab();
  494. if (tab) {
  495. tab->replaceText(p_text, p_options, p_replaceText, p_findNext);
  496. }
  497. }
  498. void VEditArea::handleReplaceAll(const QString &p_text, uint p_options,
  499. const QString &p_replaceText)
  500. {
  501. qDebug() << "replace all" << p_text << p_options << "with" << p_replaceText;
  502. VEditTab *tab = currentEditTab();
  503. if (tab) {
  504. tab->replaceTextAll(p_text, p_options, p_replaceText);
  505. }
  506. }
  507. // Let VEditArea get focus after VFindReplaceDialog is closed.
  508. void VEditArea::handleFindDialogClosed()
  509. {
  510. if (curWindowIndex == -1) {
  511. setFocus();
  512. } else {
  513. getWindow(curWindowIndex)->focusWindow();
  514. }
  515. // Clear all the search highlight.
  516. int nrWin = splitter->count();
  517. for (int i = 0; i < nrWin; ++i) {
  518. getWindow(i)->clearSearchedWordHighlight();
  519. }
  520. }
  521. QString VEditArea::getSelectedText()
  522. {
  523. VEditTab *tab = currentEditTab();
  524. if (tab) {
  525. return tab->getSelectedText();
  526. } else {
  527. return QString();
  528. }
  529. }
  530. int VEditArea::focusNextWindow(int p_biaIdx)
  531. {
  532. if (p_biaIdx == 0) {
  533. return curWindowIndex;
  534. }
  535. int newIdx = curWindowIndex + p_biaIdx;
  536. if (newIdx < 0) {
  537. newIdx = 0;
  538. } else if (newIdx >= splitter->count()) {
  539. newIdx = splitter->count() - 1;
  540. }
  541. if (newIdx >= 0 && newIdx < splitter->count()) {
  542. setCurrentWindow(newIdx, true);
  543. } else {
  544. newIdx = -1;
  545. }
  546. return newIdx;
  547. }
  548. void VEditArea::moveCurrentTabOneSplit(bool p_right)
  549. {
  550. getWindow(curWindowIndex)->moveCurrentTabOneSplit(p_right);
  551. }
  552. VEditWindow *VEditArea::getCurrentWindow() const
  553. {
  554. if (curWindowIndex < 0) {
  555. return NULL;
  556. }
  557. return getWindow(curWindowIndex);
  558. }
  559. void VEditArea::registerNavigation(QChar p_majorKey)
  560. {
  561. m_majorKey = p_majorKey;
  562. V_ASSERT(m_keyMap.empty());
  563. V_ASSERT(m_naviLabels.empty());
  564. }
  565. void VEditArea::showNavigation()
  566. {
  567. // Clean up.
  568. m_keyMap.clear();
  569. for (auto label : m_naviLabels) {
  570. delete label;
  571. }
  572. m_naviLabels.clear();
  573. if (!isVisible()) {
  574. return;
  575. }
  576. // Generate labels for VEditWindow.
  577. for (int i = 0; i < 26 && i < splitter->count(); ++i) {
  578. QChar key('a' + i);
  579. m_keyMap[key] = getWindow(i);
  580. QString str = QString(m_majorKey) + key;
  581. QLabel *label = new QLabel(str, this);
  582. label->setStyleSheet(g_vnote->getNavigationLabelStyle(str));
  583. label->move(getWindow(i)->geometry().topLeft());
  584. label->show();
  585. m_naviLabels.append(label);
  586. }
  587. }
  588. void VEditArea::hideNavigation()
  589. {
  590. m_keyMap.clear();
  591. for (auto label : m_naviLabels) {
  592. delete label;
  593. }
  594. m_naviLabels.clear();
  595. }
  596. bool VEditArea::handleKeyNavigation(int p_key, bool &p_succeed)
  597. {
  598. static bool secondKey = false;
  599. bool ret = false;
  600. p_succeed = false;
  601. QChar keyChar = VUtils::keyToChar(p_key);
  602. if (secondKey && !keyChar.isNull()) {
  603. secondKey = false;
  604. p_succeed = true;
  605. ret = true;
  606. auto it = m_keyMap.find(keyChar);
  607. if (it != m_keyMap.end()) {
  608. setCurrentWindow(splitter->indexOf(it.value()), true);
  609. }
  610. } else if (keyChar == m_majorKey) {
  611. // Major key pressed.
  612. // Need second key if m_keyMap is not empty.
  613. if (m_keyMap.isEmpty()) {
  614. p_succeed = true;
  615. } else {
  616. secondKey = true;
  617. }
  618. ret = true;
  619. }
  620. return ret;
  621. }