veditwindow.cpp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. #include <QtWidgets>
  2. #include <QtDebug>
  3. #include "veditwindow.h"
  4. #include "vedittab.h"
  5. #include "utils/vutils.h"
  6. #include "vorphanfile.h"
  7. #include "vmainwindow.h"
  8. #include "veditarea.h"
  9. #include "vopenedlistmenu.h"
  10. #include "vmdtab.h"
  11. #include "vhtmltab.h"
  12. #include "vfilelist.h"
  13. #include "vconfigmanager.h"
  14. extern VConfigManager *g_config;
  15. extern VMainWindow *g_mainWin;
  16. VEditWindow::VEditWindow(VEditArea *editArea, QWidget *parent)
  17. : QTabWidget(parent),
  18. m_editArea(editArea),
  19. m_curTabWidget(NULL),
  20. m_lastTabWidget(NULL)
  21. {
  22. setAcceptDrops(true);
  23. initTabActions();
  24. setupCornerWidget();
  25. // Explicit speficy in macOS.
  26. setUsesScrollButtons(true);
  27. setElideMode(Qt::ElideRight);
  28. setTabsClosable(true);
  29. setMovable(true);
  30. setContextMenuPolicy(Qt::CustomContextMenu);
  31. QTabBar *bar = tabBar();
  32. bar->setContextMenuPolicy(Qt::CustomContextMenu);
  33. connect(bar, &QTabBar::customContextMenuRequested,
  34. this, &VEditWindow::tabbarContextMenuRequested);
  35. connect(this, &VEditWindow::tabCloseRequested,
  36. this, &VEditWindow::closeTab);
  37. connect(this, &VEditWindow::tabBarClicked,
  38. this, &VEditWindow::handleTabbarClicked);
  39. connect(this, &VEditWindow::tabBarDoubleClicked,
  40. this, [this](int p_index) {
  41. if (p_index != -1 && g_config->getDoubleClickCloseTab()) {
  42. closeTab(p_index);
  43. }
  44. });
  45. connect(this, &VEditWindow::currentChanged,
  46. this, &VEditWindow::handleCurrentIndexChanged);
  47. connect(this, &VEditWindow::customContextMenuRequested,
  48. this, &VEditWindow::contextMenuRequested);
  49. }
  50. void VEditWindow::initTabActions()
  51. {
  52. m_locateAct = new QAction(QIcon(":/resources/icons/locate_note.svg"),
  53. tr("Locate To Folder"), this);
  54. m_locateAct->setToolTip(tr("Locate the folder of current note"));
  55. connect(m_locateAct, &QAction::triggered,
  56. this, &VEditWindow::handleLocateAct);
  57. m_moveLeftAct = new QAction(QIcon(":/resources/icons/move_tab_left.svg"),
  58. tr("Move One Split Left"), this);
  59. m_moveLeftAct->setToolTip(tr("Move current tab to the split on the left"));
  60. connect(m_moveLeftAct, &QAction::triggered,
  61. this, &VEditWindow::handleMoveLeftAct);
  62. m_moveRightAct = new QAction(QIcon(":/resources/icons/move_tab_right.svg"),
  63. tr("Move One Split Right"), this);
  64. m_moveRightAct->setToolTip(tr("Move current tab to the split on the right"));
  65. connect(m_moveRightAct, &QAction::triggered,
  66. this, &VEditWindow::handleMoveRightAct);
  67. m_closeTabAct = new QAction(QIcon(":/resources/icons/close.svg"),
  68. tr("Close Tab"), this);
  69. m_closeTabAct->setToolTip(tr("Close current note tab"));
  70. connect(m_closeTabAct, &QAction::triggered,
  71. this, [this](){
  72. int tab = this->m_closeTabAct->data().toInt();
  73. Q_ASSERT(tab != -1);
  74. closeTab(tab);
  75. });
  76. m_closeOthersAct = new QAction(tr("Close Other Tabs"), this);
  77. m_closeOthersAct->setToolTip(tr("Close all other note tabs"));
  78. connect(m_closeOthersAct, &QAction::triggered,
  79. this, [this](){
  80. int tab = this->m_closeTabAct->data().toInt();
  81. Q_ASSERT(tab != -1);
  82. for (int i = tab - 1; i >= 0; --i) {
  83. this->setCurrentIndex(i);
  84. this->updateTabStatus(i);
  85. if (this->closeTab(i)) {
  86. --tab;
  87. }
  88. }
  89. for (int i = tab + 1; i < this->count();) {
  90. this->setCurrentIndex(i);
  91. this->updateTabStatus(i);
  92. if (!this->closeTab(i)) {
  93. ++i;
  94. }
  95. }
  96. });
  97. m_closeRightAct = new QAction(tr("Close Tabs To The Right"), this);
  98. m_closeRightAct->setToolTip(tr("Close all the note tabs to the right of current tab"));
  99. connect(m_closeRightAct, &QAction::triggered,
  100. this, [this](){
  101. int tab = this->m_closeTabAct->data().toInt();
  102. Q_ASSERT(tab != -1);
  103. for (int i = tab + 1; i < this->count();) {
  104. this->setCurrentIndex(i);
  105. this->updateTabStatus(i);
  106. if (!this->closeTab(i)) {
  107. ++i;
  108. }
  109. }
  110. });
  111. m_noteInfoAct = new QAction(QIcon(":/resources/icons/note_info.svg"),
  112. tr("Note Info"), this);
  113. m_noteInfoAct->setToolTip(tr("View and edit information of the note"));
  114. connect(m_noteInfoAct, &QAction::triggered,
  115. this, [this](){
  116. int tab = this->m_closeTabAct->data().toInt();
  117. Q_ASSERT(tab != -1);
  118. VEditTab *editor = getTab(tab);
  119. QPointer<VFile> file = editor->getFile();
  120. Q_ASSERT(file);
  121. if (file->getType() == FileType::Note) {
  122. VNoteFile *tmpFile = dynamic_cast<VNoteFile *>((VFile *)file);
  123. g_mainWin->getFileList()->fileInfo(tmpFile);
  124. } else if (file->getType() == FileType::Orphan) {
  125. g_mainWin->editOrphanFileInfo(file);
  126. }
  127. });
  128. m_openLocationAct = new QAction(tr("Open Note Location"), this);
  129. m_openLocationAct->setToolTip(tr("Open the folder containing this note in operating system"));
  130. connect(m_openLocationAct, &QAction::triggered,
  131. this, [this](){
  132. int tab = this->m_closeTabAct->data().toInt();
  133. Q_ASSERT(tab != -1);
  134. VEditTab *editor = getTab(tab);
  135. QPointer<VFile> file = editor->getFile();
  136. Q_ASSERT(file);
  137. QUrl url = QUrl::fromLocalFile(file->fetchBasePath());
  138. QDesktopServices::openUrl(url);
  139. });
  140. m_recycleBinAct = new QAction(QIcon(":/resources/icons/recycle_bin.svg"),
  141. tr("&Recycle Bin"), this);
  142. m_recycleBinAct->setToolTip(tr("Open the recycle bin of this note"));
  143. connect(m_recycleBinAct, &QAction::triggered,
  144. this, [this]() {
  145. int tab = this->m_closeTabAct->data().toInt();
  146. Q_ASSERT(tab != -1);
  147. VEditTab *editor = getTab(tab);
  148. VFile *file = editor->getFile();
  149. Q_ASSERT(file);
  150. QString folderPath;
  151. if (file->getType() == FileType::Note) {
  152. const VNoteFile *tmpFile = dynamic_cast<const VNoteFile *>((VFile *)file);
  153. folderPath = tmpFile->getNotebook()->getRecycleBinFolderPath();
  154. } else if (file->getType() == FileType::Orphan) {
  155. const VOrphanFile *tmpFile = dynamic_cast<const VOrphanFile *>((VFile *)file);
  156. folderPath = tmpFile->fetchRecycleBinFolderPath();
  157. } else {
  158. Q_ASSERT(false);
  159. }
  160. QUrl url = QUrl::fromLocalFile(folderPath);
  161. QDesktopServices::openUrl(url);
  162. });
  163. }
  164. void VEditWindow::setupCornerWidget()
  165. {
  166. // Left button
  167. leftBtn = new QPushButton(QIcon(":/resources/icons/corner_tablist.svg"),
  168. "", this);
  169. leftBtn->setProperty("CornerBtn", true);
  170. leftBtn->setToolTip(tr("Opened Notes List"));
  171. VOpenedListMenu *leftMenu = new VOpenedListMenu(this);
  172. leftMenu->setToolTipsVisible(true);
  173. connect(leftMenu, &VOpenedListMenu::fileTriggered,
  174. this, &VEditWindow::tabListJump);
  175. leftBtn->setMenu(leftMenu);
  176. // Right button
  177. // Actions
  178. splitAct = new QAction(QIcon(":/resources/icons/split_window.svg"),
  179. tr("Split"), this);
  180. splitAct->setToolTip(tr("Split current window vertically"));
  181. connect(splitAct, &QAction::triggered,
  182. this, [this](){
  183. splitWindow(true);
  184. });
  185. removeSplitAct = new QAction(QIcon(":/resources/icons/remove_split.svg"),
  186. tr("Remove split"), this);
  187. removeSplitAct->setToolTip(tr("Remove current split window"));
  188. connect(removeSplitAct, &QAction::triggered,
  189. this, &VEditWindow::removeSplit);
  190. rightBtn = new QPushButton(QIcon(":/resources/icons/corner_menu.svg"),
  191. "", this);
  192. rightBtn->setProperty("CornerBtn", true);
  193. rightBtn->setToolTip(tr("Menu"));
  194. QMenu *rightMenu = new QMenu(this);
  195. rightMenu->setToolTipsVisible(true);
  196. rightMenu->addAction(splitAct);
  197. rightMenu->addAction(removeSplitAct);
  198. rightBtn->setMenu(rightMenu);
  199. connect(rightMenu, &QMenu::aboutToShow,
  200. this, &VEditWindow::updateSplitMenu);
  201. // Move all buttons to the right corner.
  202. QWidget *widget = new QWidget(this);
  203. QHBoxLayout *layout = new QHBoxLayout();
  204. layout->addWidget(leftBtn);
  205. layout->addWidget(rightBtn);
  206. layout->setContentsMargins(0, 0, 0, 0);
  207. layout->setSpacing(0);
  208. widget->setLayout(layout);
  209. setCornerWidget(widget, Qt::TopRightCorner);
  210. }
  211. void VEditWindow::splitWindow(bool p_right)
  212. {
  213. emit requestSplitWindow(this, p_right);
  214. }
  215. void VEditWindow::removeSplit()
  216. {
  217. // Close all the files one by one
  218. // If user do not want to close a file, just stop removing.
  219. // Otherwise, closeAllFiles() will emit requestRemoveSplit.
  220. closeAllFiles(false);
  221. }
  222. void VEditWindow::removeEditTab(int p_index)
  223. {
  224. Q_ASSERT(p_index > -1 && p_index < tabBar()->count());
  225. VEditTab *editor = getTab(p_index);
  226. removeTab(p_index);
  227. delete editor;
  228. }
  229. int VEditWindow::insertEditTab(int p_index, VFile *p_file, QWidget *p_page)
  230. {
  231. int idx = insertTab(p_index,
  232. p_page,
  233. p_file->getName());
  234. updateTabInfo(idx);
  235. return idx;
  236. }
  237. int VEditWindow::appendEditTab(VFile *p_file, QWidget *p_page)
  238. {
  239. return insertEditTab(count(), p_file, p_page);
  240. }
  241. int VEditWindow::openFile(VFile *p_file, OpenFileMode p_mode)
  242. {
  243. qDebug() << "open" << p_file->getName();
  244. // Find if it has been opened already
  245. int idx = findTabByFile(p_file);
  246. if (idx > -1) {
  247. goto out;
  248. }
  249. idx = openFileInTab(p_file, p_mode);
  250. out:
  251. setCurrentIndex(idx);
  252. focusWindow();
  253. return idx;
  254. }
  255. // Return true if we closed the file actually
  256. bool VEditWindow::closeFile(const VFile *p_file, bool p_forced)
  257. {
  258. // Find if it has been opened already
  259. int idx = findTabByFile(p_file);
  260. if (idx == -1) {
  261. return false;
  262. }
  263. VEditTab *editor = getTab(idx);
  264. Q_ASSERT(editor);
  265. if (!p_forced) {
  266. setCurrentIndex(idx);
  267. updateTabStatus(idx);
  268. }
  269. // Even p_forced is true we need to delete unused images.
  270. bool ok = editor->closeFile(p_forced);
  271. if (ok) {
  272. removeEditTab(idx);
  273. }
  274. if (count() == 0) {
  275. emit requestRemoveSplit(this);
  276. }
  277. return ok;
  278. }
  279. bool VEditWindow::closeFile(const VDirectory *p_dir, bool p_forced)
  280. {
  281. Q_ASSERT(p_dir);
  282. int i = 0;
  283. while (i < count()) {
  284. VEditTab *editor = getTab(i);
  285. QPointer<VFile> file = editor->getFile();
  286. if (p_dir->containsFile(file)) {
  287. if (!closeFile(file, p_forced)) {
  288. return false;
  289. }
  290. // Closed a file, so don't need to add i.
  291. } else {
  292. ++i;
  293. }
  294. }
  295. return true;
  296. }
  297. bool VEditWindow::closeFile(const VNotebook *p_notebook, bool p_forced)
  298. {
  299. Q_ASSERT(p_notebook);
  300. int i = 0;
  301. while (i < count()) {
  302. VEditTab *editor = getTab(i);
  303. QPointer<VFile> file = editor->getFile();
  304. if (p_notebook->containsFile(file)) {
  305. if (!closeFile(file, p_forced)) {
  306. return false;
  307. }
  308. // Closed a file, so don't need to add i.
  309. } else {
  310. ++i;
  311. }
  312. }
  313. return true;
  314. }
  315. bool VEditWindow::closeAllFiles(bool p_forced)
  316. {
  317. int nrTab = count();
  318. bool ret = true;
  319. for (int i = 0; i < nrTab; ++i) {
  320. VEditTab *editor = getTab(0);
  321. if (!p_forced) {
  322. setCurrentIndex(0);
  323. updateTabStatus(0);
  324. }
  325. // Even p_forced is true we need to delete unused images.
  326. bool ok = editor->closeFile(p_forced);
  327. if (ok) {
  328. removeEditTab(0);
  329. } else {
  330. ret = false;
  331. break;
  332. }
  333. }
  334. if (count() == 0) {
  335. emit requestRemoveSplit(this);
  336. }
  337. return ret;
  338. }
  339. int VEditWindow::openFileInTab(VFile *p_file, OpenFileMode p_mode)
  340. {
  341. VEditTab *editor = NULL;
  342. switch (p_file->getDocType()) {
  343. case DocType::Markdown:
  344. editor = new VMdTab(p_file, m_editArea, p_mode, this);
  345. break;
  346. case DocType::Html:
  347. editor = new VHtmlTab(p_file, m_editArea, p_mode, this);
  348. break;
  349. default:
  350. V_ASSERT(false);
  351. break;
  352. }
  353. // Connect the signals.
  354. connectEditTab(editor);
  355. int idx = appendEditTab(p_file, editor);
  356. return idx;
  357. }
  358. int VEditWindow::findTabByFile(const VFile *p_file) const
  359. {
  360. int nrTabs = count();
  361. for (int i = 0; i < nrTabs; ++i) {
  362. if (getTab(i)->getFile() == p_file) {
  363. return i;
  364. }
  365. }
  366. return -1;
  367. }
  368. bool VEditWindow::closeTab(int p_index)
  369. {
  370. VEditTab *editor = getTab(p_index);
  371. Q_ASSERT(editor);
  372. bool ok = editor->closeFile(false);
  373. if (ok) {
  374. removeEditTab(p_index);
  375. }
  376. // User clicks the close button. We should make this window
  377. // to be current window.
  378. emit getFocused();
  379. if (count() == 0) {
  380. emit requestRemoveSplit(this);
  381. }
  382. return ok;
  383. }
  384. void VEditWindow::readFile()
  385. {
  386. VEditTab *editor = getTab(currentIndex());
  387. Q_ASSERT(editor);
  388. editor->readFile();
  389. }
  390. void VEditWindow::saveAndReadFile()
  391. {
  392. saveFile();
  393. readFile();
  394. }
  395. void VEditWindow::editFile()
  396. {
  397. VEditTab *editor = getTab(currentIndex());
  398. Q_ASSERT(editor);
  399. editor->editFile();
  400. }
  401. void VEditWindow::saveFile()
  402. {
  403. VEditTab *editor = getTab(currentIndex());
  404. Q_ASSERT(editor);
  405. editor->saveFile();
  406. }
  407. void VEditWindow::updateTabStatus(int p_index)
  408. {
  409. if (p_index == -1) {
  410. p_index = currentIndex();
  411. }
  412. if (p_index == -1) {
  413. emit tabStatusUpdated(VEditTabInfo());
  414. emit outlineChanged(VTableOfContent());
  415. emit currentHeaderChanged(VHeaderPointer());
  416. return;
  417. }
  418. VEditTab *tab = getTab(p_index);
  419. emit tabStatusUpdated(tab->fetchTabInfo());
  420. emit outlineChanged(tab->getOutline());
  421. emit currentHeaderChanged(tab->getCurrentHeader());
  422. updateTabInfo(p_index);
  423. }
  424. void VEditWindow::updateTabInfo(int p_index)
  425. {
  426. VEditTab *editor = getTab(p_index);
  427. const VFile *file = editor->getFile();
  428. bool editMode = editor->isEditMode();
  429. setTabText(p_index, generateTabText(p_index, file));
  430. setTabToolTip(p_index, generateTooltip(file));
  431. QString iconUrl(":/resources/icons/reading.svg");
  432. if (editMode) {
  433. iconUrl = file->isModified() ? ":/resources/icons/editing_modified.svg"
  434. : ":/resources/icons/editing.svg";
  435. }
  436. setTabIcon(p_index, QIcon(iconUrl));
  437. }
  438. void VEditWindow::updateAllTabsSequence()
  439. {
  440. for (int i = 0; i < count(); ++i) {
  441. VEditTab *editor = getTab(i);
  442. const VFile *file = editor->getFile();
  443. setTabText(i, generateTabText(i, file));
  444. }
  445. }
  446. VTableOfContent VEditWindow::getOutline() const
  447. {
  448. int idx = currentIndex();
  449. if (idx == -1) {
  450. return VTableOfContent();
  451. }
  452. return getTab(idx)->getOutline();
  453. }
  454. VHeaderPointer VEditWindow::getCurrentHeader() const
  455. {
  456. int idx = currentIndex();
  457. if (idx == -1) {
  458. return VHeaderPointer();
  459. }
  460. return getTab(idx)->getCurrentHeader();
  461. }
  462. // Focus this windows. Try to focus current tab.
  463. void VEditWindow::focusWindow()
  464. {
  465. int idx = currentIndex();
  466. if (idx == -1) {
  467. setFocus();
  468. emit getFocused();
  469. return;
  470. }
  471. getTab(idx)->focusTab();
  472. }
  473. void VEditWindow::handleTabbarClicked(int p_index)
  474. {
  475. // Only handle the case when (p_index == currentIndex()) here
  476. // because currentIndex() is not changed yet. If we focus window
  477. // now, we may change the current index forcely.
  478. if (p_index == currentIndex()) {
  479. focusWindow();
  480. }
  481. }
  482. void VEditWindow::handleCurrentIndexChanged(int p_index)
  483. {
  484. focusWindow();
  485. QWidget *wid = widget(p_index);
  486. if (wid && (wid == m_curTabWidget)) {
  487. return;
  488. }
  489. m_lastTabWidget = m_curTabWidget;
  490. m_curTabWidget = wid;
  491. }
  492. void VEditWindow::mousePressEvent(QMouseEvent *event)
  493. {
  494. focusWindow();
  495. QTabWidget::mousePressEvent(event);
  496. }
  497. void VEditWindow::contextMenuRequested(QPoint pos)
  498. {
  499. QMenu menu(this);
  500. menu.setToolTipsVisible(true);
  501. if (canRemoveSplit()) {
  502. menu.addAction(removeSplitAct);
  503. menu.exec(this->mapToGlobal(pos));
  504. }
  505. }
  506. void VEditWindow::tabbarContextMenuRequested(QPoint p_pos)
  507. {
  508. QMenu menu(this);
  509. menu.setToolTipsVisible(true);
  510. QTabBar *bar = tabBar();
  511. int tab = bar->tabAt(p_pos);
  512. if (tab == -1) {
  513. return;
  514. }
  515. VEditTab *editor = getTab(tab);
  516. VFile *file = editor->getFile();
  517. if (file->getType() == FileType::Note) {
  518. // Locate to folder.
  519. m_locateAct->setData(tab);
  520. menu.addAction(m_locateAct);
  521. menu.addSeparator();
  522. m_recycleBinAct->setData(tab);
  523. menu.addAction(m_recycleBinAct);
  524. m_openLocationAct->setData(tab);
  525. menu.addAction(m_openLocationAct);
  526. m_noteInfoAct->setData(tab);
  527. menu.addAction(m_noteInfoAct);
  528. } else if (file->getType() == FileType::Orphan
  529. && !(dynamic_cast<VOrphanFile *>(file)->isSystemFile())) {
  530. m_recycleBinAct->setData(tab);
  531. menu.addAction(m_recycleBinAct);
  532. m_openLocationAct->setData(tab);
  533. menu.addAction(m_openLocationAct);
  534. m_noteInfoAct->setData(tab);
  535. menu.addAction(m_noteInfoAct);
  536. }
  537. int totalWin = m_editArea->windowCount();
  538. // When there is only one tab and one split window, there is no need to
  539. // display these two actions.
  540. if (totalWin > 1 || count() > 1) {
  541. menu.addSeparator();
  542. m_moveLeftAct->setData(tab);
  543. // Move one split left.
  544. menu.addAction(m_moveLeftAct);
  545. m_moveRightAct->setData(tab);
  546. // Move one split right.
  547. menu.addAction(m_moveRightAct);
  548. }
  549. // Close tab, or other tabs, or tabs to the right.
  550. menu.addSeparator();
  551. m_closeTabAct->setData(tab);
  552. menu.addAction(m_closeTabAct);
  553. if (count() > 1) {
  554. m_closeOthersAct->setData(tab);
  555. menu.addAction(m_closeOthersAct);
  556. if (tab < count() - 1) {
  557. m_closeRightAct->setData(tab);
  558. menu.addAction(m_closeRightAct);
  559. }
  560. }
  561. if (!menu.actions().isEmpty()) {
  562. menu.exec(bar->mapToGlobal(p_pos));
  563. }
  564. }
  565. void VEditWindow::tabListJump(VFile *p_file)
  566. {
  567. if (!p_file) {
  568. return;
  569. }
  570. int idx = findTabByFile(p_file);
  571. Q_ASSERT(idx >= 0);
  572. setCurrentIndex(idx);
  573. updateTabStatus(idx);
  574. }
  575. void VEditWindow::updateSplitMenu()
  576. {
  577. if (canRemoveSplit()) {
  578. removeSplitAct->setVisible(true);
  579. } else {
  580. removeSplitAct->setVisible(false);
  581. }
  582. }
  583. bool VEditWindow::canRemoveSplit()
  584. {
  585. QSplitter *splitter = dynamic_cast<QSplitter *>(parent());
  586. Q_ASSERT(splitter);
  587. return splitter->count() > 1;
  588. }
  589. void VEditWindow::handleTabOutlineChanged(const VTableOfContent &p_outline)
  590. {
  591. // Only propagate it if it is current tab.
  592. VEditTab *tab = getCurrentTab();
  593. if (tab) {
  594. if (tab->getFile() == p_outline.getFile()) {
  595. emit outlineChanged(p_outline);
  596. }
  597. } else {
  598. emit outlineChanged(VTableOfContent());
  599. return;
  600. }
  601. }
  602. void VEditWindow::handleTabCurrentHeaderChanged(const VHeaderPointer &p_header)
  603. {
  604. // Only propagate it if it is current tab.
  605. VEditTab *tab = getCurrentTab();
  606. if (tab) {
  607. if (tab->getFile() == p_header.m_file) {
  608. emit currentHeaderChanged(p_header);
  609. }
  610. } else {
  611. emit currentHeaderChanged(VHeaderPointer());
  612. return;
  613. }
  614. }
  615. void VEditWindow::scrollToHeader(const VHeaderPointer &p_header)
  616. {
  617. VEditTab *tab = getCurrentTab();
  618. if (tab) {
  619. tab->scrollToHeader(p_header);
  620. }
  621. }
  622. void VEditWindow::handleTabStatusUpdated(const VEditTabInfo &p_info)
  623. {
  624. int idx = indexOf(dynamic_cast<QWidget *>(sender()));
  625. updateTabInfo(idx);
  626. updateAllTabsSequence();
  627. if (idx == currentIndex()) {
  628. // Current tab. Propogate its status.
  629. emit tabStatusUpdated(p_info);
  630. }
  631. }
  632. void VEditWindow::handleTabStatusMessage(const QString &p_msg)
  633. {
  634. int idx = indexOf(dynamic_cast<QWidget *>(sender()));
  635. if (idx == currentIndex()) {
  636. emit statusMessage(p_msg);
  637. }
  638. }
  639. void VEditWindow::handleTabVimStatusUpdated(const VVim *p_vim)
  640. {
  641. int idx = indexOf(dynamic_cast<QWidget *>(sender()));
  642. if (idx == currentIndex()) {
  643. emit vimStatusUpdated(p_vim);
  644. }
  645. }
  646. void VEditWindow::updateFileInfo(const VFile *p_file)
  647. {
  648. if (!p_file) {
  649. return;
  650. }
  651. int idx = findTabByFile(p_file);
  652. if (idx > -1) {
  653. updateTabStatus(idx);
  654. }
  655. }
  656. void VEditWindow::updateDirectoryInfo(const VDirectory *p_dir)
  657. {
  658. if (!p_dir) {
  659. return;
  660. }
  661. int nrTab = count();
  662. for (int i = 0; i < nrTab; ++i) {
  663. VEditTab *editor = getTab(i);
  664. QPointer<VFile> file = editor->getFile();
  665. if (p_dir->containsFile(file)) {
  666. updateTabStatus(i);
  667. }
  668. }
  669. }
  670. void VEditWindow::updateNotebookInfo(const VNotebook *p_notebook)
  671. {
  672. if (!p_notebook) {
  673. return;
  674. }
  675. int nrTab = count();
  676. for (int i = 0; i < nrTab; ++i) {
  677. VEditTab *editor = getTab(i);
  678. QPointer<VFile> file = editor->getFile();
  679. if (p_notebook->containsFile(file)) {
  680. updateTabStatus(i);
  681. }
  682. }
  683. }
  684. VEditTab *VEditWindow::getCurrentTab() const
  685. {
  686. int idx = currentIndex();
  687. if (idx == -1) {
  688. return NULL;
  689. }
  690. return getTab(idx);
  691. }
  692. QVector<VEditTabInfo> VEditWindow::getAllTabsInfo() const
  693. {
  694. int nrTab = count();
  695. QVector<VEditTabInfo> tabs;
  696. tabs.reserve(nrTab);
  697. for (int i = 0; i < nrTab; ++i) {
  698. VEditTab *editTab = getTab(i);
  699. tabs.push_back(editTab->fetchTabInfo());
  700. }
  701. return tabs;
  702. }
  703. void VEditWindow::handleLocateAct()
  704. {
  705. int tab = m_locateAct->data().toInt();
  706. VEditTab *editor = getTab(tab);
  707. QPointer<VFile> file = editor->getFile();
  708. if (file->getType() == FileType::Note) {
  709. g_mainWin->locateFile(file);
  710. }
  711. }
  712. void VEditWindow::handleMoveLeftAct()
  713. {
  714. int tab = m_moveLeftAct->data().toInt();
  715. Q_ASSERT(tab != -1);
  716. moveTabOneSplit(tab, false);
  717. }
  718. void VEditWindow::handleMoveRightAct()
  719. {
  720. int tab = m_moveRightAct->data().toInt();
  721. Q_ASSERT(tab != -1);
  722. moveTabOneSplit(tab, true);
  723. }
  724. void VEditWindow::moveTabOneSplit(int p_tabIdx, bool p_right)
  725. {
  726. Q_ASSERT(p_tabIdx > -1 && p_tabIdx < count());
  727. // Add split window if needed.
  728. if (m_editArea->windowCount() < 2) {
  729. // Request VEditArea to split window.
  730. splitWindow(p_right);
  731. // Though the signal and slot will behave like a function call. We wait
  732. // here until the window split finished.
  733. while (m_editArea->windowCount() < 2) {
  734. VUtils::sleepWait(100);
  735. }
  736. }
  737. int totalWin = m_editArea->windowCount();
  738. int idx = m_editArea->windowIndex(this);
  739. int newIdx = p_right ? idx + 1 : idx - 1;
  740. if (newIdx >= totalWin) {
  741. newIdx = 0;
  742. } else if (newIdx < 0) {
  743. newIdx = totalWin - 1;
  744. }
  745. VEditTab *editor = getTab(p_tabIdx);
  746. // Remove it from current window. This won't close the split even if it is
  747. // the only tab.
  748. removeTab(p_tabIdx);
  749. // Disconnect all the signals.
  750. disconnect(editor, 0, this, 0);
  751. m_editArea->moveTab(editor, idx, newIdx);
  752. // If there is no tab, remove current split.
  753. if (count() == 0) {
  754. emit requestRemoveSplit(this);
  755. }
  756. }
  757. void VEditWindow::moveCurrentTabOneSplit(bool p_right)
  758. {
  759. int idx = currentIndex();
  760. if (idx == -1) {
  761. return;
  762. }
  763. moveTabOneSplit(idx, p_right);
  764. }
  765. bool VEditWindow::addEditTab(QWidget *p_widget)
  766. {
  767. if (!p_widget) {
  768. return false;
  769. }
  770. VEditTab *editor = dynamic_cast<VEditTab *>(p_widget);
  771. if (!editor) {
  772. return false;
  773. }
  774. // Connect the signals.
  775. connectEditTab(editor);
  776. int idx = appendEditTab(editor->getFile(), editor);
  777. setCurrentIndex(idx);
  778. updateTabStatus(idx);
  779. return true;
  780. }
  781. void VEditWindow::connectEditTab(const VEditTab *p_tab)
  782. {
  783. connect(p_tab, &VEditTab::getFocused,
  784. this, &VEditWindow::getFocused);
  785. connect(p_tab, &VEditTab::outlineChanged,
  786. this, &VEditWindow::handleTabOutlineChanged);
  787. connect(p_tab, &VEditTab::currentHeaderChanged,
  788. this, &VEditWindow::handleTabCurrentHeaderChanged);
  789. connect(p_tab, &VEditTab::statusUpdated,
  790. this, &VEditWindow::handleTabStatusUpdated);
  791. connect(p_tab, &VEditTab::statusMessage,
  792. this, &VEditWindow::handleTabStatusMessage);
  793. connect(p_tab, &VEditTab::vimStatusUpdated,
  794. this, &VEditWindow::handleTabVimStatusUpdated);
  795. }
  796. void VEditWindow::setCurrentWindow(bool p_current)
  797. {
  798. if (p_current) {
  799. rightBtn->setIcon(QIcon(":/resources/icons/corner_menu_cur.svg"));
  800. leftBtn->setIcon(QIcon(":/resources/icons/corner_tablist_cur.svg"));
  801. } else {
  802. rightBtn->setIcon(QIcon(":/resources/icons/corner_menu.svg"));
  803. leftBtn->setIcon(QIcon(":/resources/icons/corner_tablist.svg"));
  804. }
  805. }
  806. void VEditWindow::clearSearchedWordHighlight()
  807. {
  808. int nrTab = count();
  809. for (int i = 0; i < nrTab; ++i) {
  810. getTab(i)->clearSearchedWordHighlight();
  811. }
  812. }
  813. void VEditWindow::focusNextTab(bool p_right)
  814. {
  815. focusWindow();
  816. if (count() < 2) {
  817. return;
  818. }
  819. int idx = currentIndex();
  820. idx = p_right ? idx + 1 : idx - 1;
  821. if (idx < 0) {
  822. idx = count() - 1;
  823. } else if (idx >= count()) {
  824. idx = 0;
  825. }
  826. setCurrentIndex(idx);
  827. }
  828. bool VEditWindow::showOpenedFileList()
  829. {
  830. if (count() == 0) {
  831. return false;
  832. }
  833. leftBtn->showMenu();
  834. return true;
  835. }
  836. bool VEditWindow::activateTab(int p_sequence)
  837. {
  838. if (p_sequence < c_tabSequenceBase || p_sequence >= (c_tabSequenceBase + count())) {
  839. return false;
  840. }
  841. setCurrentIndex(p_sequence - c_tabSequenceBase);
  842. return true;
  843. }
  844. bool VEditWindow::alternateTab()
  845. {
  846. if (m_lastTabWidget) {
  847. if (-1 != indexOf(m_lastTabWidget)) {
  848. setCurrentWidget(m_lastTabWidget);
  849. return true;
  850. } else {
  851. m_lastTabWidget = NULL;
  852. }
  853. }
  854. return false;
  855. }
  856. VEditTab* VEditWindow::getTab(int tabIndex) const
  857. {
  858. return dynamic_cast<VEditTab *>(widget(tabIndex));
  859. }
  860. void VEditWindow::dragEnterEvent(QDragEnterEvent *p_event)
  861. {
  862. if (p_event->mimeData()->hasFormat("text/uri-list")) {
  863. p_event->acceptProposedAction();
  864. return;
  865. }
  866. QTabWidget::dragEnterEvent(p_event);
  867. }
  868. void VEditWindow::dropEvent(QDropEvent *p_event)
  869. {
  870. const QMimeData *mime = p_event->mimeData();
  871. if (mime->hasFormat("text/uri-list") && mime->hasUrls()) {
  872. // Open external files in this edit window.
  873. QStringList files;
  874. QList<QUrl> urls = mime->urls();
  875. for (int i = 0; i < urls.size(); ++i) {
  876. QString file;
  877. if (urls[i].isLocalFile()) {
  878. file = urls[i].toLocalFile();
  879. QFileInfo fi(file);
  880. if (fi.exists() && fi.isFile()) {
  881. file = QDir::cleanPath(fi.absoluteFilePath());
  882. files.append(file);
  883. }
  884. }
  885. }
  886. if (!files.isEmpty()) {
  887. focusWindow();
  888. g_mainWin->openFiles(files);
  889. }
  890. p_event->acceptProposedAction();
  891. return;
  892. }
  893. QTabWidget::dropEvent(p_event);
  894. }