mainwindow.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. #include "mainwindow.h"
  2. #include <QFileInfo>
  3. #include <QDebug>
  4. #include <QResizeEvent>
  5. #include <QSplitter>
  6. #include <QDockWidget>
  7. #include <QVariant>
  8. #include <QTextEdit>
  9. #include <QStatusBar>
  10. #include <QCloseEvent>
  11. #include <QVBoxLayout>
  12. #include <QTabBar>
  13. #include <QVariant>
  14. #include <QCoreApplication>
  15. #include <QApplication>
  16. #include <QShortcut>
  17. #include <QSystemTrayIcon>
  18. #include <QWindowStateChangeEvent>
  19. #include <QTimer>
  20. #include <QProgressDialog>
  21. #include <QHotkey>
  22. #include <QWebEngineView>
  23. #include "notebookexplorer.h"
  24. #include "vnotex.h"
  25. #include "notebookmgr.h"
  26. #include "buffermgr.h"
  27. #include "viewarea.h"
  28. #include <core/configmgr.h>
  29. #include <core/sessionconfig.h>
  30. #include <core/coreconfig.h>
  31. #include <core/mainconfig.h>
  32. #include <core/widgetconfig.h>
  33. #include <core/events.h>
  34. #include <core/exception.h>
  35. #include <core/fileopenparameters.h>
  36. #include <widgets/dialogs/exportdialog.h>
  37. #include "viewwindow.h"
  38. #include "outlineviewer.h"
  39. #include <utils/widgetutils.h>
  40. #include "navigationmodemgr.h"
  41. #include "messageboxhelper.h"
  42. #include "systemtrayhelper.h"
  43. #include "titletoolbar.h"
  44. #include "locationlist.h"
  45. #include "searchpanel.h"
  46. #include "snippetpanel.h"
  47. #include "historypanel.h"
  48. #include "windowspanel.h"
  49. #include "windowsprovider.h"
  50. #include <notebook/notebook.h>
  51. #include "searchinfoprovider.h"
  52. #include <vtextedit/spellchecker.h>
  53. #include <utils/docsutils.h>
  54. #include <utils/iconutils.h>
  55. #include <core/thememgr.h>
  56. #include "dialogs/updater.h"
  57. #include "tagexplorer.h"
  58. #include "toolbarhelper.h"
  59. #include "statusbarhelper.h"
  60. #include "consoleviewer.h"
  61. using namespace vnotex;
  62. MainWindow::MainWindow(QWidget *p_parent)
  63. : FramelessMainWindowImpl(!ConfigMgr::getInst().getSessionConfig().getSystemTitleBarEnabled(), p_parent),
  64. m_dockWidgetHelper(this)
  65. {
  66. VNoteX::getInst().setMainWindow(this);
  67. NavigationModeMgr::init(this);
  68. setupUI();
  69. setupShortcuts();
  70. loadStateAndGeometry();
  71. m_dockWidgetHelper.postSetup();
  72. // The signal is particularly useful if your application has to do some last-second cleanup.
  73. // Note that no user interaction is possible in this state.
  74. connect(qApp, &QCoreApplication::aboutToQuit,
  75. this, &MainWindow::closeOnQuit);
  76. connect(&VNoteX::getInst(), &VNoteX::exportRequested,
  77. this, &MainWindow::exportNotes);
  78. }
  79. MainWindow::~MainWindow()
  80. {
  81. // Should be desturcted before status bar.
  82. delete m_viewArea;
  83. m_viewArea = nullptr;
  84. }
  85. void MainWindow::kickOffOnStart(const QStringList &p_paths)
  86. {
  87. QTimer::singleShot(300, [this, p_paths]() {
  88. if (m_dummyWebView) {
  89. delete m_dummyWebView;
  90. m_dummyWebView = nullptr;
  91. }
  92. loadStateAndGeometry();
  93. {
  94. QProgressDialog proDlg(tr("Initializing core components..."),
  95. QString(),
  96. 0,
  97. 0,
  98. this);
  99. proDlg.setWindowFlags(proDlg.windowFlags() & ~Qt::WindowCloseButtonHint);
  100. proDlg.setWindowModality(Qt::WindowModal);
  101. proDlg.setValue(0);
  102. VNoteX::getInst().initLoad();
  103. setupSpellCheck();
  104. }
  105. // Do necessary stuffs before emitting this signal.
  106. emit mainWindowStarted();
  107. emit layoutChanged();
  108. checkNotebooksFailedToLoad();
  109. loadWidgetsData();
  110. demoWidget();
  111. openFiles(p_paths);
  112. if (MainConfig::isVersionChanged()) {
  113. QString tips;
  114. try {
  115. tips = DocsUtils::getDocText("features_tips.txt");
  116. } catch (Exception &p_e) {
  117. // Just ignore it.
  118. Q_UNUSED(p_e);
  119. }
  120. if (!tips.isEmpty()) {
  121. MessageBoxHelper::notify(MessageBoxHelper::Information,
  122. tips,
  123. this);
  124. }
  125. const auto file = DocsUtils::getDocFile(QStringLiteral("welcome.md"));
  126. if (!file.isEmpty()) {
  127. auto paras = QSharedPointer<FileOpenParameters>::create();
  128. paras->m_readOnly = true;
  129. paras->m_sessionEnabled = false;
  130. emit VNoteX::getInst().openFileRequested(file, paras);
  131. }
  132. }
  133. if (ConfigMgr::getInst().getCoreConfig().isCheckForUpdatesOnStartEnabled()) {
  134. QTimer::singleShot(5 * 60 * 1000, this, &MainWindow::checkForUpdates);
  135. }
  136. });
  137. }
  138. void MainWindow::openFiles(const QStringList &p_files)
  139. {
  140. for (const auto &file : p_files) {
  141. emit VNoteX::getInst().openFileRequested(file, QSharedPointer<FileOpenParameters>::create());
  142. }
  143. }
  144. void MainWindow::setupUI()
  145. {
  146. setupCentralWidget();
  147. setupDocks();
  148. setupToolBar();
  149. setupStatusBar();
  150. setupTipsArea();
  151. setupSystemTray();
  152. m_dockWidgetHelper.activateDock(DockWidgetHelper::NavigationDock);
  153. #if defined(Q_OS_WIN)
  154. m_dummyWebView = new QWebEngineView(this);
  155. m_dummyWebView->setFixedSize(1, 1);
  156. #endif
  157. }
  158. void MainWindow::setupStatusBar()
  159. {
  160. StatusBarHelper::setupStatusBar(this);
  161. connect(&VNoteX::getInst(), &VNoteX::statusMessageRequested,
  162. statusBar(), &QStatusBar::showMessage);
  163. }
  164. void MainWindow::setupTipsArea()
  165. {
  166. connect(&VNoteX::getInst(), &VNoteX::tipsRequested,
  167. this, &MainWindow::showTips);
  168. }
  169. void MainWindow::createTipsArea()
  170. {
  171. if (m_tipsLabel) {
  172. return;
  173. }
  174. m_tipsLabel = new QLabel(this);
  175. m_tipsLabel->setObjectName("MainWindowTipsLabel");
  176. m_tipsLabel->hide();
  177. m_tipsTimer = new QTimer(this);
  178. m_tipsTimer->setSingleShot(true);
  179. m_tipsTimer->setInterval(3000);
  180. connect(m_tipsTimer, &QTimer::timeout,
  181. this, [this]() {
  182. setTipsAreaVisible(false);
  183. });
  184. }
  185. void MainWindow::setupCentralWidget()
  186. {
  187. m_viewArea = new ViewArea(this);
  188. NavigationModeMgr::getInst().registerNavigationTarget(m_viewArea);
  189. connect(&VNoteX::getInst().getBufferMgr(), &BufferMgr::bufferRequested,
  190. m_viewArea, &ViewArea::openBuffer);
  191. connect(m_viewArea, &ViewArea::statusWidgetChanged,
  192. this, [this](QWidget *p_widget) {
  193. if (m_viewAreaStatusWidget) {
  194. // Will hide it.
  195. statusBar()->removeWidget(m_viewAreaStatusWidget);
  196. }
  197. m_viewAreaStatusWidget = p_widget;
  198. if (m_viewAreaStatusWidget) {
  199. statusBar()->addPermanentWidget(m_viewAreaStatusWidget);
  200. m_viewAreaStatusWidget->show();
  201. }
  202. });
  203. connect(m_viewArea, &ViewArea::currentViewWindowChanged,
  204. this, [this]() {
  205. setWindowTitle(getViewAreaTitle());
  206. });
  207. connect(m_viewArea, &ViewArea::currentViewWindowUpdated,
  208. this, [this]() {
  209. setWindowTitle(getViewAreaTitle());
  210. });
  211. {
  212. auto &notebookMgr = VNoteX::getInst().getNotebookMgr();
  213. connect(&notebookMgr, &NotebookMgr::notebookAboutToClose,
  214. this, [this](const Notebook *p_notebook) {
  215. m_viewArea->close(p_notebook, true);
  216. });
  217. connect(&notebookMgr, &NotebookMgr::notebookAboutToRemove,
  218. this, [this](const Notebook *p_notebook) {
  219. m_viewArea->close(p_notebook, true);
  220. });
  221. }
  222. setCentralWidget(m_viewArea);
  223. }
  224. void MainWindow::setupDocks()
  225. {
  226. setupNotebookExplorer();
  227. setupTagExplorer();
  228. setupOutlineViewer();
  229. setupConsoleViewer();
  230. setupHistoryPanel();
  231. setupWindowsPanel();
  232. setupSearchPanel();
  233. setupSnippetPanel();
  234. setupLocationList();
  235. m_dockWidgetHelper.setupDocks();
  236. NavigationModeMgr::getInst().registerNavigationTarget(&m_dockWidgetHelper);
  237. }
  238. void MainWindow::setupSearchPanel()
  239. {
  240. m_searchPanel = new SearchPanel(
  241. QSharedPointer<SearchInfoProvider>::create(m_viewArea,
  242. m_notebookExplorer,
  243. &VNoteX::getInst().getNotebookMgr()),
  244. this);
  245. m_searchPanel->setObjectName("SearchPanel.vnotex");
  246. }
  247. void MainWindow::setupSnippetPanel()
  248. {
  249. m_snippetPanel = new SnippetPanel(this);
  250. m_snippetPanel->setObjectName("SnippetPanel.vnotex");
  251. connect(m_snippetPanel, &SnippetPanel::applySnippetRequested,
  252. this, [this](const QString &p_name) {
  253. auto viewWindow = m_viewArea->getCurrentViewWindow();
  254. if (viewWindow) {
  255. viewWindow->applySnippet(p_name);
  256. viewWindow->setFocus();
  257. }
  258. });
  259. }
  260. void MainWindow::setupHistoryPanel()
  261. {
  262. m_historyPanel = new HistoryPanel(this);
  263. m_historyPanel->setObjectName("HistoryPanel.vnotex");
  264. }
  265. void MainWindow::setupWindowsPanel()
  266. {
  267. m_windowsPanel = new WindowsPanel(QSharedPointer<WindowsProvider>::create(m_viewArea), this);
  268. m_windowsPanel->setObjectName("WindowsPanel.vnotex");
  269. }
  270. void MainWindow::setupLocationList()
  271. {
  272. m_locationList = new LocationList(this);
  273. m_locationList->setObjectName("LocationList.vnotex");
  274. }
  275. void MainWindow::setupNotebookExplorer()
  276. {
  277. m_notebookExplorer = new NotebookExplorer(this);
  278. connect(&VNoteX::getInst(), &VNoteX::newNotebookRequested,
  279. m_notebookExplorer, &NotebookExplorer::newNotebook);
  280. connect(&VNoteX::getInst(), &VNoteX::newNotebookFromFolderRequested,
  281. m_notebookExplorer, &NotebookExplorer::newNotebookFromFolder);
  282. connect(&VNoteX::getInst(), &VNoteX::importNotebookRequested,
  283. m_notebookExplorer, &NotebookExplorer::importNotebook);
  284. connect(&VNoteX::getInst(), &VNoteX::newFolderRequested,
  285. m_notebookExplorer, &NotebookExplorer::newFolder);
  286. connect(&VNoteX::getInst(), &VNoteX::newNoteRequested,
  287. m_notebookExplorer, &NotebookExplorer::newNote);
  288. connect(&VNoteX::getInst(), &VNoteX::newQuickNoteRequested,
  289. m_notebookExplorer, &NotebookExplorer::newQuickNote);
  290. connect(&VNoteX::getInst(), &VNoteX::importFileRequested,
  291. m_notebookExplorer, &NotebookExplorer::importFile);
  292. connect(&VNoteX::getInst(), &VNoteX::importFolderRequested,
  293. m_notebookExplorer, &NotebookExplorer::importFolder);
  294. connect(&VNoteX::getInst(), &VNoteX::importLegacyNotebookRequested,
  295. m_notebookExplorer, &NotebookExplorer::importLegacyNotebook);
  296. connect(&VNoteX::getInst(), &VNoteX::manageNotebooksRequested,
  297. m_notebookExplorer, &NotebookExplorer::manageNotebooks);
  298. connect(&VNoteX::getInst(), &VNoteX::locateNodeRequested,
  299. this, [this](Node *p_node) {
  300. m_dockWidgetHelper.activateDock(DockWidgetHelper::NavigationDock);
  301. m_notebookExplorer->locateNode(p_node);
  302. });
  303. auto &notebookMgr = VNoteX::getInst().getNotebookMgr();
  304. connect(&notebookMgr, &NotebookMgr::notebooksUpdated,
  305. m_notebookExplorer, &NotebookExplorer::loadNotebooks);
  306. connect(&notebookMgr, &NotebookMgr::notebookUpdated,
  307. m_notebookExplorer, &NotebookExplorer::reloadNotebook);
  308. connect(&notebookMgr, &NotebookMgr::currentNotebookChanged,
  309. m_notebookExplorer, &NotebookExplorer::setCurrentNotebook);
  310. connect(m_notebookExplorer, &NotebookExplorer::notebookActivated,
  311. &notebookMgr, &NotebookMgr::setCurrentNotebook);
  312. }
  313. void MainWindow::closeEvent(QCloseEvent *p_event)
  314. {
  315. const int toTray = ConfigMgr::getInst().getSessionConfig().getMinimizeToSystemTray();
  316. bool isExit = m_requestQuit > -1 || toTray == 0;
  317. const int exitCode = m_requestQuit;
  318. m_requestQuit = -1;
  319. #if defined(Q_OS_MACOS)
  320. // Do not support minimized to tray on macOS.
  321. isExit = true;
  322. #endif
  323. bool needShowMessage = false;
  324. if(!isExit && toTray == -1){
  325. int ret = MessageBoxHelper::questionYesNo(MessageBoxHelper::Question,
  326. tr("Do you want to minimize %1 to system tray "
  327. "instead of quitting when closed?").arg(qApp->applicationName()),
  328. tr("You could change the option in Settings later."),
  329. QString(),
  330. this);
  331. if (ret == QMessageBox::Yes) {
  332. ConfigMgr::getInst().getSessionConfig().setMinimizeToSystemTray(true);
  333. needShowMessage = true;
  334. } else if (ret == QMessageBox::No) {
  335. ConfigMgr::getInst().getSessionConfig().setMinimizeToSystemTray(false);
  336. isExit = true;
  337. } else {
  338. p_event->ignore();
  339. return;
  340. }
  341. }
  342. if (isVisible()) {
  343. // Avoid geometry corruption caused by fullscreen or minimized window.
  344. const auto state = windowState();
  345. if (state & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
  346. if (m_windowOldState & Qt::WindowMaximized) {
  347. showMaximized();
  348. } else {
  349. showNormal();
  350. }
  351. }
  352. // Do not expand the content area.
  353. setContentAreaExpanded(false);
  354. saveStateAndGeometry();
  355. }
  356. if (isExit || !m_trayIcon->isVisible()) {
  357. // Signal out the close event.
  358. auto event = QSharedPointer<Event>::create();
  359. event->m_response = true;
  360. emit mainWindowClosed(event);
  361. if (!event->m_response.toBool()) {
  362. // Stop the close.
  363. p_event->ignore();
  364. return;
  365. }
  366. m_trayIcon->hide();
  367. FramelessMainWindowImpl::closeEvent(p_event);
  368. qApp->exit(exitCode > -1 ? exitCode : 0);
  369. } else {
  370. emit minimizedToSystemTray();
  371. hide();
  372. p_event->ignore();
  373. if (needShowMessage) {
  374. m_trayIcon->showMessage(ConfigMgr::c_appName, tr("%1 is still running here.").arg(ConfigMgr::c_appName));
  375. }
  376. }
  377. }
  378. void MainWindow::saveStateAndGeometry()
  379. {
  380. if (m_layoutReset) {
  381. return;
  382. }
  383. SessionConfig::MainWindowStateGeometry sg;
  384. sg.m_mainState = saveState();
  385. sg.m_mainGeometry = saveGeometry();
  386. sg.m_visibleDocksBeforeExpand = m_visibleDocksBeforeExpand;
  387. sg.m_tagExplorerState = m_tagExplorer->saveState();
  388. sg.m_notebookExplorerState = m_notebookExplorer->saveState();
  389. sg.m_locationListState = m_locationList->saveState();
  390. auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
  391. sessionConfig.setMainWindowStateGeometry(sg);
  392. }
  393. void MainWindow::loadStateAndGeometry()
  394. {
  395. const auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
  396. const auto sg = sessionConfig.getMainWindowStateGeometry();
  397. if (!sg.m_mainGeometry.isEmpty()) {
  398. restoreGeometry(sg.m_mainGeometry);
  399. }
  400. if (!sg.m_mainState.isEmpty()) {
  401. // Will also restore the state of dock widgets.
  402. restoreState(sg.m_mainState);
  403. }
  404. m_visibleDocksBeforeExpand = sg.m_visibleDocksBeforeExpand;
  405. if (m_visibleDocksBeforeExpand.isEmpty()) {
  406. // Init (or init again if there is no visible dock).
  407. m_visibleDocksBeforeExpand = m_dockWidgetHelper.getVisibleDocks();
  408. }
  409. if (!sg.m_tagExplorerState.isEmpty()) {
  410. m_tagExplorer->restoreState(sg.m_tagExplorerState);
  411. }
  412. if (!sg.m_notebookExplorerState.isEmpty()) {
  413. m_notebookExplorer->restoreState(sg.m_notebookExplorerState);
  414. }
  415. if (!sg.m_locationListState.isEmpty()) {
  416. m_locationList->restoreState(sg.m_locationListState);
  417. }
  418. }
  419. void MainWindow::resetStateAndGeometry()
  420. {
  421. if (m_layoutReset) {
  422. return;
  423. }
  424. m_layoutReset = true;
  425. SessionConfig::MainWindowStateGeometry sg;
  426. auto& sessionConfig = ConfigMgr::getInst().getSessionConfig();
  427. sessionConfig.setMainWindowStateGeometry(sg);
  428. }
  429. void MainWindow::setContentAreaExpanded(bool p_expanded)
  430. {
  431. if (m_contentAreaExpanded == p_expanded) {
  432. return;
  433. }
  434. m_contentAreaExpanded = p_expanded;
  435. if (p_expanded) {
  436. // Store the state and hide.
  437. m_visibleDocksBeforeExpand = m_dockWidgetHelper.hideDocks();
  438. } else {
  439. // Restore the state.
  440. m_dockWidgetHelper.restoreDocks(m_visibleDocksBeforeExpand);
  441. }
  442. }
  443. bool MainWindow::isContentAreaExpanded() const
  444. {
  445. return m_contentAreaExpanded;
  446. }
  447. void MainWindow::demoWidget()
  448. {
  449. }
  450. QString MainWindow::getViewAreaTitle() const
  451. {
  452. QString title;
  453. const auto win = m_viewArea->getCurrentViewWindow();
  454. if (win) {
  455. title = win->getName();
  456. }
  457. return title.isEmpty() ? QString() : QStringLiteral("%1 - %2").arg(title, ConfigMgr::c_appName);
  458. }
  459. void MainWindow::setupOutlineViewer()
  460. {
  461. // Do not provide title here since there is one in the dock title.
  462. m_outlineViewer = new OutlineViewer(QString(), this);
  463. m_outlineViewer->setObjectName("OutlineViewer.vnotex");
  464. // There are OutlineViewers in each ViewWindow. We only need to register navigation mode for the outline panel.
  465. NavigationModeMgr::getInst().registerNavigationTarget(m_outlineViewer->getNavigationModeWrapper());
  466. connect(m_viewArea, &ViewArea::currentViewWindowChanged,
  467. this, [this]() {
  468. auto win = m_viewArea->getCurrentViewWindow();
  469. m_outlineViewer->setOutlineProvider(win ? win->getOutlineProvider() : nullptr);
  470. });
  471. connect(m_outlineViewer, &OutlineViewer::focusViewArea,
  472. this, &MainWindow::focusViewArea);
  473. }
  474. void MainWindow::setupConsoleViewer()
  475. {
  476. m_consoleViewer = new ConsoleViewer(this);
  477. m_consoleViewer->setObjectName("ConsoleViewer.vnotex");
  478. connect(&VNoteX::getInst(), &VNoteX::showOutputRequested,
  479. this, [this](const QString &p_text) {
  480. m_consoleViewer->append(p_text);
  481. m_dockWidgetHelper.activateDock(DockWidgetHelper::ConsoleDock);
  482. });
  483. }
  484. const QVector<QDockWidget *> &MainWindow::getDocks() const
  485. {
  486. return m_dockWidgetHelper.getDocks();
  487. }
  488. ViewArea *MainWindow::getViewArea() const
  489. {
  490. return m_viewArea;
  491. }
  492. void MainWindow::focusViewArea()
  493. {
  494. m_viewArea->focus();
  495. }
  496. NotebookExplorer *MainWindow::getNotebookExplorer() const
  497. {
  498. Q_ASSERT(m_notebookExplorer);
  499. return m_notebookExplorer;
  500. }
  501. void MainWindow::setupToolBar()
  502. {
  503. const int sz = ConfigMgr::getInst().getCoreConfig().getToolBarIconSize();
  504. if (isFrameless()) {
  505. auto toolBar = new TitleToolBar(tr("Global"), this);
  506. toolBar->setIconSize(QSize(sz + 4, sz + 4));
  507. ToolBarHelper::setupToolBars(this, toolBar);
  508. toolBar->addTitleBarIcons(ToolBarHelper::generateIcon(QStringLiteral("minimize.svg")),
  509. ToolBarHelper::generateIcon(QStringLiteral("maximize.svg")),
  510. ToolBarHelper::generateIcon(QStringLiteral("maximize_restore.svg")),
  511. ToolBarHelper::generateDangerousIcon(QStringLiteral("close.svg")));
  512. setTitleBar(toolBar);
  513. connect(this, &FramelessMainWindowImpl::windowStateChanged,
  514. toolBar, &TitleToolBar::updateMaximizeAct);
  515. } else {
  516. auto toolBar = new QToolBar(tr("Global"), this);
  517. toolBar->setIconSize(QSize(sz, sz));
  518. ToolBarHelper::setupToolBars(this, toolBar);
  519. }
  520. // Disable the context menu above tool bar.
  521. setContextMenuPolicy(Qt::NoContextMenu);
  522. }
  523. void MainWindow::closeOnQuit()
  524. {
  525. // No user interaction is available.
  526. emit mainWindowClosedOnQuit();
  527. VNoteX::getInst().getNotebookMgr().close();
  528. }
  529. void MainWindow::setupShortcuts()
  530. {
  531. const auto &coreConfig = ConfigMgr::getInst().getCoreConfig();
  532. // For cross-platform global shortcuts, the external library QHotkey is used.
  533. QKeySequence wakeUp(coreConfig.getShortcut(CoreConfig::Global_WakeUp));
  534. if (!wakeUp.isEmpty()) {
  535. auto qHotkey = new QHotkey(wakeUp, true, this);
  536. connect(qHotkey , &QHotkey::activated, this, &MainWindow::showMainWindow);
  537. }
  538. }
  539. void MainWindow::setStayOnTop(bool p_enabled)
  540. {
  541. bool shown = isVisible();
  542. Qt::WindowFlags flags = windowFlags();
  543. const Qt::WindowFlags magicFlag = Qt::WindowStaysOnTopHint;
  544. if (p_enabled) {
  545. setWindowFlags(flags | magicFlag);
  546. } else {
  547. setWindowFlags(flags ^ magicFlag);
  548. }
  549. setWindowFlagsOnUpdate();
  550. if (shown) {
  551. show();
  552. }
  553. }
  554. void MainWindow::setupSystemTray()
  555. {
  556. m_trayIcon = SystemTrayHelper::setupSystemTray(this);
  557. m_trayIcon->show();
  558. }
  559. void MainWindow::restart()
  560. {
  561. m_requestQuit = RESTART_EXIT_CODE;
  562. close();
  563. }
  564. void MainWindow::changeEvent(QEvent *p_event)
  565. {
  566. if (p_event->type() == QEvent::WindowStateChange) {
  567. QWindowStateChangeEvent *eve = static_cast<QWindowStateChangeEvent *>(p_event);
  568. m_windowOldState = eve->oldState();
  569. }
  570. FramelessMainWindowImpl::changeEvent(p_event);
  571. }
  572. void MainWindow::showMainWindow()
  573. {
  574. if (isMinimized()) {
  575. if (m_windowOldState & Qt::WindowMaximized) {
  576. showMaximized();
  577. } else if (m_windowOldState & Qt::WindowFullScreen) {
  578. showFullScreen();
  579. } else {
  580. showNormal();
  581. }
  582. } else {
  583. show();
  584. // Need to call raise() in macOS.
  585. raise();
  586. }
  587. activateWindow();
  588. }
  589. void MainWindow::quitApp()
  590. {
  591. m_requestQuit = 0;
  592. close();
  593. }
  594. void MainWindow::updateDockWidgetTabBar()
  595. {
  596. m_dockWidgetHelper.updateDockWidgetTabBar();
  597. }
  598. void MainWindow::exportNotes()
  599. {
  600. if (m_exportDialog) {
  601. MessageBoxHelper::notify(MessageBoxHelper::Information,
  602. tr("There is one export dialog running. Please close it first."),
  603. this);
  604. m_exportDialog->activateWindow();
  605. m_exportDialog->show();
  606. return;
  607. }
  608. auto currentNotebook = m_notebookExplorer->currentNotebook().data();
  609. auto viewWindow = m_viewArea->getCurrentViewWindow();
  610. auto folderNode = m_notebookExplorer->currentExploredFolderNode();
  611. if (folderNode && (folderNode->isRoot())) {
  612. folderNode = nullptr;
  613. }
  614. auto noteNode = m_notebookExplorer->currentExploredNode();
  615. if (noteNode && !noteNode->hasContent()) {
  616. noteNode = nullptr;
  617. }
  618. m_exportDialog = new ExportDialog(currentNotebook,
  619. folderNode,
  620. noteNode,
  621. viewWindow ? viewWindow->getBuffer() : nullptr,
  622. nullptr);
  623. connect(m_exportDialog, &QDialog::finished,
  624. this, [this]() {
  625. m_exportDialog->deleteLater();
  626. m_exportDialog = nullptr;
  627. });
  628. // Let it be able to run at background.
  629. m_exportDialog->show();
  630. }
  631. void MainWindow::showTips(const QString &p_message, int p_timeoutMilliseconds)
  632. {
  633. createTipsArea();
  634. m_tipsTimer->stop();
  635. setTipsAreaVisible(false);
  636. if (p_message.isEmpty()) {
  637. return;
  638. }
  639. m_tipsLabel->setText(p_message);
  640. setTipsAreaVisible(true);
  641. m_tipsTimer->start(p_timeoutMilliseconds);
  642. }
  643. void MainWindow::setTipsAreaVisible(bool p_visible)
  644. {
  645. Q_ASSERT(m_tipsLabel);
  646. if (p_visible) {
  647. m_tipsLabel->adjustSize();
  648. int labelW = m_tipsLabel->width();
  649. int labelH = m_tipsLabel->height();
  650. int x = (width() - labelW) / 2;
  651. int y = (height() - labelH) / 3;
  652. if (x < 0) {
  653. x = 0;
  654. }
  655. if (y < 0) {
  656. y = 0;
  657. }
  658. m_tipsLabel->move(x, y);
  659. m_tipsLabel->show();
  660. } else {
  661. m_tipsLabel->hide();
  662. }
  663. }
  664. LocationList *MainWindow::getLocationList() const
  665. {
  666. return m_locationList;
  667. }
  668. void MainWindow::setLocationListVisible(bool p_visible)
  669. {
  670. if (p_visible) {
  671. m_dockWidgetHelper.activateDock(DockWidgetHelper::LocationListDock);
  672. } else {
  673. m_dockWidgetHelper.getDock(DockWidgetHelper::LocationListDock)->hide();
  674. }
  675. }
  676. void MainWindow::toggleLocationListVisible()
  677. {
  678. bool visible = m_dockWidgetHelper.getDock(DockWidgetHelper::LocationListDock)->isVisible();
  679. setLocationListVisible(!visible);
  680. }
  681. void MainWindow::setupSpellCheck()
  682. {
  683. const auto &configMgr = ConfigMgr::getInst();
  684. vte::SpellChecker::addDictionaryCustomSearchPaths(
  685. QStringList() << configMgr.getUserDictsFolder() << configMgr.getAppDictsFolder());
  686. }
  687. void MainWindow::checkForUpdates()
  688. {
  689. Updater::checkForUpdates(this, [this](bool p_hasUpdate, const QString &p_version, const QString &p_errMsg) {
  690. if (p_version.isEmpty()) {
  691. statusBar()->showMessage(tr("Failed to check for updates (%1)").arg(p_errMsg), 3000);
  692. } else if (p_hasUpdate) {
  693. statusBar()->showMessage(tr("Updates available: %1").arg(p_version));
  694. }
  695. });
  696. }
  697. void MainWindow::checkNotebooksFailedToLoad()
  698. {
  699. auto &notebookMgr = VNoteX::getInst().getNotebookMgr();
  700. const auto &notebooks = notebookMgr.getNotebooksFailedToLoad();
  701. if (notebooks.isEmpty()) {
  702. return;
  703. }
  704. int ret = MessageBoxHelper::questionYesNo(MessageBoxHelper::Warning,
  705. tr("Failed to load %n notebook(s).", "", notebooks.size()),
  706. tr("These notebooks may be moved or deleted. It is recommended to remove "
  707. "them from configuration and open them with the correct root folder path later.\n"
  708. "Remove them from the configuration?"),
  709. notebooks.join(QLatin1Char('\n')),
  710. this);
  711. if (ret == QMessageBox::Yes) {
  712. notebookMgr.clearNotebooksFailedToLoad();
  713. }
  714. }
  715. void MainWindow::setupTagExplorer()
  716. {
  717. m_tagExplorer = new TagExplorer(this);
  718. connect(&VNoteX::getInst().getNotebookMgr(), &NotebookMgr::currentNotebookChanged,
  719. m_tagExplorer, &TagExplorer::setNotebook);
  720. }
  721. void MainWindow::loadWidgetsData()
  722. {
  723. m_historyPanel->initialize();
  724. m_snippetPanel->initialize();
  725. }