veditwindow.cpp 28 KB

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